From c24cc8a33bacb72f2022d99c69dfcd7b59e90f41 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Thu, 22 Aug 2024 07:48:31 -0400 Subject: [PATCH 001/160] chore: ignore sql queries when building docker (#11933) --- .dockerignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.dockerignore b/.dockerignore index a3096e7d40883..e182865ae0afd 100644 --- a/.dockerignore +++ b/.dockerignore @@ -22,6 +22,7 @@ open-api/typescript-sdk/node_modules/ server/coverage/ server/node_modules/ server/upload/ +server/src/queries server/dist/ server/www/ From 296bbeb2fc79ccdae2f3db3d7100f4ae4236ec93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carles=20Alb=C3=A0s=20Boix?= <43018489+carlesalbasboix@users.noreply.github.com> Date: Thu, 22 Aug 2024 16:40:15 +0200 Subject: [PATCH 002/160] feat(web): Left hand navigation for memories (#11913) --- web/src/lib/components/memory-page/memory-viewer.svelte | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/src/lib/components/memory-page/memory-viewer.svelte b/web/src/lib/components/memory-page/memory-viewer.svelte index 250cb379cc896..77dbf5614c05f 100644 --- a/web/src/lib/components/memory-page/memory-viewer.svelte +++ b/web/src/lib/components/memory-page/memory-viewer.svelte @@ -153,7 +153,9 @@ canGoForward && toNext() }, + { shortcut: { key: 'd' }, onShortcut: () => canGoForward && toNext() }, { shortcut: { key: 'ArrowLeft' }, onShortcut: () => canGoBack && toPrevious() }, + { shortcut: { key: 'a' }, onShortcut: () => canGoBack && toPrevious() }, { shortcut: { key: 'Escape' }, onShortcut: () => goto(AppRoute.PHOTOS) }, ]} /> From f69ce6ad8a486f8c80b6b03986f7f54ea2702039 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Thu, 22 Aug 2024 11:38:19 -0400 Subject: [PATCH 003/160] refactor(web): folder view (#11967) refactor(web): tree view --- .../components/folder-tree/folder-tree.svelte | 65 --------------- .../layouts/user-page-layout.svelte | 4 - .../side-bar/folder-browser-sidebar.svelte | 32 -------- .../side-bar/folder-side-bar.svelte | 8 -- .../tree/tree-item-thumbnails.svelte | 25 ++++++ .../shared-components/tree/tree-items.svelte | 17 ++++ .../shared-components/tree/tree.svelte | 39 +++++++++ web/src/lib/constants.ts | 1 + .../utils/{folder-utils.ts => tree-utils.ts} | 4 +- .../[[assetId=id]]/+page.svelte | 82 +++++++++---------- .../[[photos=photos]]/[[assetId=id]]/+page.ts | 17 ++-- 11 files changed, 135 insertions(+), 159 deletions(-) delete mode 100644 web/src/lib/components/folder-tree/folder-tree.svelte delete mode 100644 web/src/lib/components/shared-components/side-bar/folder-browser-sidebar.svelte delete mode 100644 web/src/lib/components/shared-components/side-bar/folder-side-bar.svelte create mode 100644 web/src/lib/components/shared-components/tree/tree-item-thumbnails.svelte create mode 100644 web/src/lib/components/shared-components/tree/tree-items.svelte create mode 100644 web/src/lib/components/shared-components/tree/tree.svelte rename web/src/lib/utils/{folder-utils.ts => tree-utils.ts} (71%) diff --git a/web/src/lib/components/folder-tree/folder-tree.svelte b/web/src/lib/components/folder-tree/folder-tree.svelte deleted file mode 100644 index 7f8289ce74a20..0000000000000 --- a/web/src/lib/components/folder-tree/folder-tree.svelte +++ /dev/null @@ -1,65 +0,0 @@ - - - - - -{#if isExpanded} -
    - {#each Object.entries(content) as [subFolderName, subContent], index (index)} -
  • - -
  • - {/each} -
-{/if} diff --git a/web/src/lib/components/layouts/user-page-layout.svelte b/web/src/lib/components/layouts/user-page-layout.svelte index 495c1aae30f94..8222007d57a4b 100644 --- a/web/src/lib/components/layouts/user-page-layout.svelte +++ b/web/src/lib/components/layouts/user-page-layout.svelte @@ -3,7 +3,6 @@ import NavigationBar from '../shared-components/navigation-bar/navigation-bar.svelte'; import SideBar from '../shared-components/side-bar/side-bar.svelte'; import AdminSideBar from '../shared-components/side-bar/admin-side-bar.svelte'; - import FolderSideBar from '$lib/components/shared-components/side-bar/folder-side-bar.svelte'; export let hideNavbar = false; export let showUploadButton = false; @@ -11,7 +10,6 @@ export let description: string | undefined = undefined; export let scrollbar = true; export let admin = false; - export let isFolderView = false; $: scrollbarClass = scrollbar ? 'immich-scrollbar p-2 pb-8' : 'scrollbar-hidden'; $: hasTitleClass = title ? 'top-16 h-[calc(100%-theme(spacing.16))]' : 'top-0 h-full'; @@ -31,8 +29,6 @@ {#if admin} - {:else if isFolderView} - {:else} {/if} diff --git a/web/src/lib/components/shared-components/side-bar/folder-browser-sidebar.svelte b/web/src/lib/components/shared-components/side-bar/folder-browser-sidebar.svelte deleted file mode 100644 index 8e744c23aa715..0000000000000 --- a/web/src/lib/components/shared-components/side-bar/folder-browser-sidebar.svelte +++ /dev/null @@ -1,32 +0,0 @@ - - -
-
{$t('explorer').toUpperCase()}
-
- {#each Object.entries(folderTree) as [folderName, content]} - - {/each} -
-
diff --git a/web/src/lib/components/shared-components/side-bar/folder-side-bar.svelte b/web/src/lib/components/shared-components/side-bar/folder-side-bar.svelte deleted file mode 100644 index ff1cd514e6b3e..0000000000000 --- a/web/src/lib/components/shared-components/side-bar/folder-side-bar.svelte +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/web/src/lib/components/shared-components/tree/tree-item-thumbnails.svelte b/web/src/lib/components/shared-components/tree/tree-item-thumbnails.svelte new file mode 100644 index 0000000000000..759a3e5e6579e --- /dev/null +++ b/web/src/lib/components/shared-components/tree/tree-item-thumbnails.svelte @@ -0,0 +1,25 @@ + + +{#if items.length > 0} +
+ {#each items as item} + + {/each} +
+{/if} diff --git a/web/src/lib/components/shared-components/tree/tree-items.svelte b/web/src/lib/components/shared-components/tree/tree-items.svelte new file mode 100644 index 0000000000000..bf04e6ae1fbce --- /dev/null +++ b/web/src/lib/components/shared-components/tree/tree-items.svelte @@ -0,0 +1,17 @@ + + +
    + {#each Object.entries(items) as [path, tree], index (index)} +
  • + +
  • + {/each} +
diff --git a/web/src/lib/components/shared-components/tree/tree.svelte b/web/src/lib/components/shared-components/tree/tree.svelte new file mode 100644 index 0000000000000..7975825c5ea88 --- /dev/null +++ b/web/src/lib/components/shared-components/tree/tree.svelte @@ -0,0 +1,39 @@ + + + + +
+ +
+ {value} +
+ +{#if isOpen} + +{/if} diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts index 184e913d9e5fc..34d64098487fe 100644 --- a/web/src/lib/constants.ts +++ b/web/src/lib/constants.ts @@ -80,6 +80,7 @@ export enum QueryParameter { SEARCHED_PEOPLE = 'searchedPeople', SMART_SEARCH = 'smartSearch', PAGE = 'page', + PATH = 'path', } export enum OpenSettingQueryParameterValue { diff --git a/web/src/lib/utils/folder-utils.ts b/web/src/lib/utils/tree-utils.ts similarity index 71% rename from web/src/lib/utils/folder-utils.ts rename to web/src/lib/utils/tree-utils.ts index 0305f89672648..cc17784eb64f3 100644 --- a/web/src/lib/utils/folder-utils.ts +++ b/web/src/lib/utils/tree-utils.ts @@ -2,7 +2,9 @@ export interface RecursiveObject { [key: string]: RecursiveObject; } -export function buildFolderTree(paths: string[]) { +export const normalizeTreePath = (path: string) => path.replace(/^\//, '').replace(/\/$/, ''); + +export function buildTree(paths: string[]) { const root: RecursiveObject = {}; for (const path of paths) { const parts = path.split('/'); diff --git a/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte index bf914ff8f934b..b5301843427ed 100644 --- a/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -1,14 +1,22 @@ - + + +
+
{$t('explorer').toUpperCase()}
+
+ +
+
+
+
{#if data.path} @@ -71,42 +93,20 @@
-
- - {#if data.currentFolders.length > 0} -
- {#each data.currentFolders as folder} - - {/each} -
- {/if} +
+ -
0} - > - {#if data.pathAssets && data.pathAssets.length > 0} + {#if data.pathAssets && data.pathAssets.length > 0} +
- {/if} -
+
+ {/if}
diff --git a/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.ts b/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.ts index f04d7840e524d..41800c1a7df89 100644 --- a/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.ts +++ b/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.ts @@ -1,7 +1,9 @@ +import { QueryParameter } from '$lib/constants'; import { foldersStore } from '$lib/stores/folders.store'; import { authenticate } from '$lib/utils/auth'; import { getFormatter } from '$lib/utils/i18n'; import { getAssetInfoFromParam } from '$lib/utils/navigation'; +import { buildTree, normalizeTreePath } from '$lib/utils/tree-utils'; import { get } from 'svelte/store'; import type { PageLoad } from './$types'; @@ -14,25 +16,24 @@ export const load = (async ({ params, url }) => { const { uniquePaths } = get(foldersStore); let pathAssets = null; - const path = url.searchParams.get('folder'); + const path = url.searchParams.get(QueryParameter.PATH); if (path) { await foldersStore.fetchAssetsByPath(path); const { assets } = get(foldersStore); pathAssets = assets[path] || null; } - const currentPath = path ? `${path}/`.replaceAll('//', '/') : ''; - - const currentFolders = (uniquePaths || []) - .filter((path) => path.startsWith(currentPath) && path !== currentPath) - .map((path) => path.replaceAll(currentPath, '').split('/')[0]) - .filter((value, index, self) => self.indexOf(value) === index); + let tree = buildTree(uniquePaths || []); + const parts = normalizeTreePath(path || '').split('/'); + for (const part of parts) { + tree = tree?.[part]; + } return { asset, path, - currentFolders, + currentFolders: Object.keys(tree || {}), pathAssets, meta: { title: $t('folders'), From 7fbf50a75e567ed4a9c5243532499efd383576ce Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Thu, 22 Aug 2024 23:24:49 -0400 Subject: [PATCH 004/160] fix: remove `asset.resized` (#11983) fix: remove resized --- e2e/src/api/specs/asset.e2e-spec.ts | 10 ----- .../openapi/lib/model/asset_response_dto.dart | 10 +---- open-api/immich-openapi-specs.json | 4 -- open-api/typescript-sdk/src/fetch-client.ts | 1 - server/src/dtos/asset-response.dto.ts | 4 -- server/src/queries/asset.repository.sql | 45 ------------------- server/src/repositories/asset.repository.ts | 5 +-- server/test/fixtures/shared-link.stub.ts | 2 - .../asset-viewer/asset-viewer.svelte | 21 +++------ .../asset-viewer/photo-viewer.svelte | 10 ++--- .../lib/components/assets/broken-asset.svelte | 25 +++++++++++ .../assets/thumbnail/image-thumbnail.svelte | 16 +++---- .../assets/thumbnail/thumbnail.svelte | 23 ++++------ .../covers/__tests__/share-cover.spec.ts | 6 ++- .../covers/asset-cover.svelte | 25 +++++++---- .../covers/share-cover.svelte | 4 +- .../sharedlinks-page/shared-link-card.svelte | 2 +- web/src/test-data/factories/asset-factory.ts | 1 - 18 files changed, 78 insertions(+), 136 deletions(-) create mode 100644 web/src/lib/components/assets/broken-asset.svelte diff --git a/e2e/src/api/specs/asset.e2e-spec.ts b/e2e/src/api/specs/asset.e2e-spec.ts index 99b33dfed8e93..82ce17865a565 100644 --- a/e2e/src/api/specs/asset.e2e-spec.ts +++ b/e2e/src/api/specs/asset.e2e-spec.ts @@ -843,7 +843,6 @@ describe('/asset', () => { expected: { type: AssetTypeEnum.Image, originalFileName: '8bit-sRGB.avif', - resized: true, exifInfo: { description: '', exifImageHeight: 1080, @@ -859,7 +858,6 @@ describe('/asset', () => { expected: { type: AssetTypeEnum.Image, originalFileName: 'el_torcal_rocks.jpg', - resized: true, exifInfo: { dateTimeOriginal: '2012-08-05T11:39:59.000Z', exifImageWidth: 512, @@ -883,7 +881,6 @@ describe('/asset', () => { expected: { type: AssetTypeEnum.Image, originalFileName: '8bit-sRGB.jxl', - resized: true, exifInfo: { description: '', exifImageHeight: 1080, @@ -899,7 +896,6 @@ describe('/asset', () => { expected: { type: AssetTypeEnum.Image, originalFileName: 'IMG_2682.heic', - resized: true, fileCreatedAt: '2019-03-21T16:04:22.348Z', exifInfo: { dateTimeOriginal: '2019-03-21T16:04:22.348Z', @@ -924,7 +920,6 @@ describe('/asset', () => { expected: { type: AssetTypeEnum.Image, originalFileName: 'density_plot.png', - resized: true, exifInfo: { exifImageWidth: 800, exifImageHeight: 800, @@ -939,7 +934,6 @@ describe('/asset', () => { expected: { type: AssetTypeEnum.Image, originalFileName: 'glarus.nef', - resized: true, fileCreatedAt: '2010-07-20T17:27:12.000Z', exifInfo: { make: 'NIKON CORPORATION', @@ -961,7 +955,6 @@ describe('/asset', () => { expected: { type: AssetTypeEnum.Image, originalFileName: 'philadelphia.nef', - resized: true, fileCreatedAt: '2016-09-22T22:10:29.060Z', exifInfo: { make: 'NIKON CORPORATION', @@ -984,7 +977,6 @@ describe('/asset', () => { expected: { type: AssetTypeEnum.Image, originalFileName: '4_3.rw2', - resized: true, fileCreatedAt: '2018-05-10T08:42:37.842Z', exifInfo: { make: 'Panasonic', @@ -1008,7 +1000,6 @@ describe('/asset', () => { expected: { type: AssetTypeEnum.Image, originalFileName: '12bit-compressed-(3_2).arw', - resized: true, fileCreatedAt: '2016-09-27T10:51:44.000Z', exifInfo: { make: 'SONY', @@ -1033,7 +1024,6 @@ describe('/asset', () => { expected: { type: AssetTypeEnum.Image, originalFileName: '14bit-uncompressed-(3_2).arw', - resized: true, fileCreatedAt: '2016-01-08T14:08:01.000Z', exifInfo: { make: 'SONY', diff --git a/mobile/openapi/lib/model/asset_response_dto.dart b/mobile/openapi/lib/model/asset_response_dto.dart index 561a42cc852cf..4217e133b8c34 100644 --- a/mobile/openapi/lib/model/asset_response_dto.dart +++ b/mobile/openapi/lib/model/asset_response_dto.dart @@ -36,7 +36,6 @@ class AssetResponseDto { this.owner, required this.ownerId, this.people = const [], - required this.resized, this.smartInfo, this.stack, this.tags = const [], @@ -112,8 +111,6 @@ class AssetResponseDto { List people; - bool resized; - /// /// Please note: This property should have been non-nullable! Since the specification file /// does not include a default value (using the "default:" property), however, the generated @@ -159,7 +156,6 @@ class AssetResponseDto { other.owner == owner && other.ownerId == ownerId && _deepEquality.equals(other.people, people) && - other.resized == resized && other.smartInfo == smartInfo && other.stack == stack && _deepEquality.equals(other.tags, tags) && @@ -194,7 +190,6 @@ class AssetResponseDto { (owner == null ? 0 : owner!.hashCode) + (ownerId.hashCode) + (people.hashCode) + - (resized.hashCode) + (smartInfo == null ? 0 : smartInfo!.hashCode) + (stack == null ? 0 : stack!.hashCode) + (tags.hashCode) + @@ -204,7 +199,7 @@ class AssetResponseDto { (updatedAt.hashCode); @override - String toString() => 'AssetResponseDto[checksum=$checksum, deviceAssetId=$deviceAssetId, deviceId=$deviceId, duplicateId=$duplicateId, duration=$duration, exifInfo=$exifInfo, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, hasMetadata=$hasMetadata, id=$id, isArchived=$isArchived, isFavorite=$isFavorite, isOffline=$isOffline, isTrashed=$isTrashed, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, originalMimeType=$originalMimeType, originalPath=$originalPath, owner=$owner, ownerId=$ownerId, people=$people, resized=$resized, smartInfo=$smartInfo, stack=$stack, tags=$tags, thumbhash=$thumbhash, type=$type, unassignedFaces=$unassignedFaces, updatedAt=$updatedAt]'; + String toString() => 'AssetResponseDto[checksum=$checksum, deviceAssetId=$deviceAssetId, deviceId=$deviceId, duplicateId=$duplicateId, duration=$duration, exifInfo=$exifInfo, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, hasMetadata=$hasMetadata, id=$id, isArchived=$isArchived, isFavorite=$isFavorite, isOffline=$isOffline, isTrashed=$isTrashed, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, originalMimeType=$originalMimeType, originalPath=$originalPath, owner=$owner, ownerId=$ownerId, people=$people, smartInfo=$smartInfo, stack=$stack, tags=$tags, thumbhash=$thumbhash, type=$type, unassignedFaces=$unassignedFaces, updatedAt=$updatedAt]'; Map toJson() { final json = {}; @@ -255,7 +250,6 @@ class AssetResponseDto { } json[r'ownerId'] = this.ownerId; json[r'people'] = this.people; - json[r'resized'] = this.resized; if (this.smartInfo != null) { json[r'smartInfo'] = this.smartInfo; } else { @@ -309,7 +303,6 @@ class AssetResponseDto { owner: UserResponseDto.fromJson(json[r'owner']), ownerId: mapValueOfType(json, r'ownerId')!, people: PersonWithFacesResponseDto.listFromJson(json[r'people']), - resized: mapValueOfType(json, r'resized')!, smartInfo: SmartInfoResponseDto.fromJson(json[r'smartInfo']), stack: AssetStackResponseDto.fromJson(json[r'stack']), tags: TagResponseDto.listFromJson(json[r'tags']), @@ -380,7 +373,6 @@ class AssetResponseDto { 'originalFileName', 'originalPath', 'ownerId', - 'resized', 'thumbhash', 'type', 'updatedAt', diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 02a887370af02..2137bf7b11ff1 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -8335,9 +8335,6 @@ }, "type": "array" }, - "resized": { - "type": "boolean" - }, "smartInfo": { "$ref": "#/components/schemas/SmartInfoResponseDto" }, @@ -8390,7 +8387,6 @@ "originalFileName", "originalPath", "ownerId", - "resized", "thumbhash", "type", "updatedAt" diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 9642f4c8171d4..bf0c63c2b8c9a 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -229,7 +229,6 @@ export type AssetResponseDto = { owner?: UserResponseDto; ownerId: string; people?: PersonWithFacesResponseDto[]; - resized: boolean; smartInfo?: SmartInfoResponseDto; stack?: (AssetStackResponseDto) | null; tags?: TagResponseDto[]; diff --git a/server/src/dtos/asset-response.dto.ts b/server/src/dtos/asset-response.dto.ts index 332f258d49590..caeae2971a228 100644 --- a/server/src/dtos/asset-response.dto.ts +++ b/server/src/dtos/asset-response.dto.ts @@ -14,7 +14,6 @@ import { AssetFaceEntity } from 'src/entities/asset-face.entity'; import { AssetEntity } from 'src/entities/asset.entity'; import { SmartInfoEntity } from 'src/entities/smart-info.entity'; import { AssetType } from 'src/enum'; -import { getAssetFiles } from 'src/utils/asset.util'; import { mimeTypes } from 'src/utils/mime-types'; export class SanitizedAssetResponseDto { @@ -23,7 +22,6 @@ export class SanitizedAssetResponseDto { type!: AssetType; thumbhash!: string | null; originalMimeType?: string; - resized!: boolean; localDateTime!: Date; duration!: string; livePhotoVideoId?: string | null; @@ -112,7 +110,6 @@ export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): As originalMimeType: mimeTypes.lookup(entity.originalFileName), thumbhash: entity.thumbhash?.toString('base64') ?? null, localDateTime: entity.localDateTime, - resized: !!getAssetFiles(entity.files).previewFile, duration: entity.duration ?? '0:00:00.00000', livePhotoVideoId: entity.livePhotoVideoId, hasMetadata: false, @@ -131,7 +128,6 @@ export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): As originalPath: entity.originalPath, originalFileName: entity.originalFileName, originalMimeType: mimeTypes.lookup(entity.originalFileName), - resized: !!getAssetFiles(entity.files).previewFile, thumbhash: entity.thumbhash?.toString('base64') ?? null, fileCreatedAt: entity.fileCreatedAt, fileModifiedAt: entity.fileModifiedAt, diff --git a/server/src/queries/asset.repository.sql b/server/src/queries/asset.repository.sql index fd5dc15c0a647..b08130b183eb0 100644 --- a/server/src/queries/asset.repository.sql +++ b/server/src/queries/asset.repository.sql @@ -598,12 +598,6 @@ SELECT "asset"."sidecarPath" AS "asset_sidecarPath", "asset"."stackId" AS "asset_stackId", "asset"."duplicateId" AS "asset_duplicateId", - "files"."id" AS "files_id", - "files"."assetId" AS "files_assetId", - "files"."createdAt" AS "files_createdAt", - "files"."updatedAt" AS "files_updatedAt", - "files"."type" AS "files_type", - "files"."path" AS "files_path", "exifInfo"."assetId" AS "exifInfo_assetId", "exifInfo"."description" AS "exifInfo_description", "exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth", @@ -665,7 +659,6 @@ SELECT "stackedAssets"."duplicateId" AS "stackedAssets_duplicateId" FROM "assets" "asset" - LEFT JOIN "asset_files" "files" ON "files"."assetId" = "asset"."id" LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id" LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId" LEFT JOIN "assets" "stackedAssets" ON "stackedAssets"."stackId" = "stack"."id" @@ -692,7 +685,6 @@ SELECT )::timestamptz AS "timeBucket" FROM "assets" "asset" - LEFT JOIN "asset_files" "files" ON "files"."assetId" = "asset"."id" LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id" LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId" LEFT JOIN "assets" "stackedAssets" ON "stackedAssets"."stackId" = "stack"."id" @@ -744,12 +736,6 @@ SELECT "asset"."sidecarPath" AS "asset_sidecarPath", "asset"."stackId" AS "asset_stackId", "asset"."duplicateId" AS "asset_duplicateId", - "files"."id" AS "files_id", - "files"."assetId" AS "files_assetId", - "files"."createdAt" AS "files_createdAt", - "files"."updatedAt" AS "files_updatedAt", - "files"."type" AS "files_type", - "files"."path" AS "files_path", "exifInfo"."assetId" AS "exifInfo_assetId", "exifInfo"."description" AS "exifInfo_description", "exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth", @@ -811,7 +797,6 @@ SELECT "stackedAssets"."duplicateId" AS "stackedAssets_duplicateId" FROM "assets" "asset" - LEFT JOIN "asset_files" "files" ON "files"."assetId" = "asset"."id" LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id" LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId" LEFT JOIN "assets" "stackedAssets" ON "stackedAssets"."stackId" = "stack"."id" @@ -865,12 +850,6 @@ SELECT "asset"."sidecarPath" AS "asset_sidecarPath", "asset"."stackId" AS "asset_stackId", "asset"."duplicateId" AS "asset_duplicateId", - "files"."id" AS "files_id", - "files"."assetId" AS "files_assetId", - "files"."createdAt" AS "files_createdAt", - "files"."updatedAt" AS "files_updatedAt", - "files"."type" AS "files_type", - "files"."path" AS "files_path", "exifInfo"."assetId" AS "exifInfo_assetId", "exifInfo"."description" AS "exifInfo_description", "exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth", @@ -932,7 +911,6 @@ SELECT "stackedAssets"."duplicateId" AS "stackedAssets_duplicateId" FROM "assets" "asset" - LEFT JOIN "asset_files" "files" ON "files"."assetId" = "asset"."id" LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id" LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId" LEFT JOIN "assets" "stackedAssets" ON "stackedAssets"."stackId" = "stack"."id" @@ -964,7 +942,6 @@ SELECT DISTINCT c.city AS "value" FROM "assets" "asset" - LEFT JOIN "asset_files" "files" ON "files"."assetId" = "asset"."id" INNER JOIN "exif" "e" ON "asset"."id" = e."assetId" INNER JOIN "cities" "c" ON c.city = "e"."city" WHERE @@ -995,7 +972,6 @@ SELECT DISTINCT unnest("si"."tags") AS "value" FROM "assets" "asset" - LEFT JOIN "asset_files" "files" ON "files"."assetId" = "asset"."id" INNER JOIN "smart_info" "si" ON "asset"."id" = si."assetId" INNER JOIN "random_tags" "t" ON "si"."tags" @> ARRAY[t.tag] WHERE @@ -1038,12 +1014,6 @@ SELECT "asset"."sidecarPath" AS "asset_sidecarPath", "asset"."stackId" AS "asset_stackId", "asset"."duplicateId" AS "asset_duplicateId", - "files"."id" AS "files_id", - "files"."assetId" AS "files_assetId", - "files"."createdAt" AS "files_createdAt", - "files"."updatedAt" AS "files_updatedAt", - "files"."type" AS "files_type", - "files"."path" AS "files_path", "exifInfo"."assetId" AS "exifInfo_assetId", "exifInfo"."description" AS "exifInfo_description", "exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth", @@ -1078,7 +1048,6 @@ SELECT "stack"."primaryAssetId" AS "stack_primaryAssetId" FROM "assets" "asset" - LEFT JOIN "asset_files" "files" ON "files"."assetId" = "asset"."id" LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id" LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId" WHERE @@ -1120,12 +1089,6 @@ SELECT "asset"."sidecarPath" AS "asset_sidecarPath", "asset"."stackId" AS "asset_stackId", "asset"."duplicateId" AS "asset_duplicateId", - "files"."id" AS "files_id", - "files"."assetId" AS "files_assetId", - "files"."createdAt" AS "files_createdAt", - "files"."updatedAt" AS "files_updatedAt", - "files"."type" AS "files_type", - "files"."path" AS "files_path", "exifInfo"."assetId" AS "exifInfo_assetId", "exifInfo"."description" AS "exifInfo_description", "exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth", @@ -1160,7 +1123,6 @@ SELECT "stack"."primaryAssetId" AS "stack_primaryAssetId" FROM "assets" "asset" - LEFT JOIN "asset_files" "files" ON "files"."assetId" = "asset"."id" LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id" LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId" WHERE @@ -1197,12 +1159,6 @@ SELECT "asset"."sidecarPath" AS "asset_sidecarPath", "asset"."stackId" AS "asset_stackId", "asset"."duplicateId" AS "asset_duplicateId", - "files"."id" AS "files_id", - "files"."assetId" AS "files_assetId", - "files"."createdAt" AS "files_createdAt", - "files"."updatedAt" AS "files_updatedAt", - "files"."type" AS "files_type", - "files"."path" AS "files_path", "exifInfo"."assetId" AS "exifInfo_assetId", "exifInfo"."description" AS "exifInfo_description", "exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth", @@ -1264,7 +1220,6 @@ SELECT "stackedAssets"."duplicateId" AS "stackedAssets_duplicateId" FROM "assets" "asset" - LEFT JOIN "asset_files" "files" ON "files"."assetId" = "asset"."id" LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id" LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId" LEFT JOIN "assets" "stackedAssets" ON "stackedAssets"."stackId" = "stack"."id" diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index 50ed724f9f01e..b95db5f3a8e62 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -710,10 +710,7 @@ export class AssetRepository implements IAssetRepository { } private getBuilder(options: AssetBuilderOptions) { - const builder = this.repository - .createQueryBuilder('asset') - .where('asset.isVisible = true') - .leftJoinAndSelect('asset.files', 'files'); + const builder = this.repository.createQueryBuilder('asset').where('asset.isVisible = true'); if (options.assetType !== undefined) { builder.andWhere('asset.type = :assetType', { assetType: options.assetType }); diff --git a/server/test/fixtures/shared-link.stub.ts b/server/test/fixtures/shared-link.stub.ts index 9ea252b5f7ec3..54898d8693e75 100644 --- a/server/test/fixtures/shared-link.stub.ts +++ b/server/test/fixtures/shared-link.stub.ts @@ -54,7 +54,6 @@ const assetResponse: AssetResponseDto = { originalMimeType: 'image/jpeg', originalPath: 'fake_path/jpeg', originalFileName: 'asset_1.jpeg', - resized: false, thumbhash: null, fileModifiedAt: today, isOffline: false, @@ -82,7 +81,6 @@ const assetResponseWithoutMetadata = { id: 'id_1', type: AssetType.VIDEO, originalMimeType: 'image/jpeg', - resized: false, thumbhash: null, localDateTime: today, duration: '0:00:00.00000', diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte index 3ed955848b347..4e98546069dd7 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -4,9 +4,9 @@ import MotionPhotoAction from '$lib/components/asset-viewer/actions/motion-photo-action.svelte'; import NextAssetAction from '$lib/components/asset-viewer/actions/next-asset-action.svelte'; import PreviousAssetAction from '$lib/components/asset-viewer/actions/previous-asset-action.svelte'; - import Icon from '$lib/components/elements/icon.svelte'; import { AssetAction, ProjectionType } from '$lib/constants'; import { updateNumberOfComments } from '$lib/stores/activity.store'; + import { closeEditorCofirm } from '$lib/stores/asset-editor.store'; import { assetViewingStore } from '$lib/stores/asset-viewing.store'; import type { AssetStore } from '$lib/stores/assets.store'; import { isShowDetail } from '$lib/stores/preferences.store'; @@ -25,14 +25,13 @@ getActivities, getActivityStatistics, getAllAlbums, + getStack, runAssetJobs, type ActivityResponseDto, type AlbumResponseDto, type AssetResponseDto, - getStack, type StackResponseDto, } from '@immich/sdk'; - import { mdiImageBrokenVariant } from '@mdi/js'; import { createEventDispatcher, onDestroy, onMount } from 'svelte'; import { t } from 'svelte-i18n'; import { fly } from 'svelte/transition'; @@ -42,13 +41,13 @@ import ActivityViewer from './activity-viewer.svelte'; import AssetViewerNavBar from './asset-viewer-nav-bar.svelte'; import DetailPanel from './detail-panel.svelte'; + import CropArea from './editor/crop-tool/crop-area.svelte'; + import EditorPanel from './editor/editor-panel.svelte'; import PanoramaViewer from './panorama-viewer.svelte'; import PhotoViewer from './photo-viewer.svelte'; import SlideshowBar from './slideshow-bar.svelte'; import VideoViewer from './video-wrapper-viewer.svelte'; - import EditorPanel from './editor/editor-panel.svelte'; - import CropArea from './editor/crop-tool/crop-area.svelte'; - import { closeEditorCofirm } from '$lib/stores/asset-editor.store'; + export let assetStore: AssetStore | null = null; export let asset: AssetResponseDto; export let preloadAssets: AssetResponseDto[] = []; @@ -481,15 +480,7 @@ {/key} {:else} {#key asset.id} - {#if !asset.resized} -
-
- -
-
- {:else if asset.type === AssetTypeEnum.Image} + {#if asset.type === AssetTypeEnum.Image} {#if shouldPlayMotionPhoto && asset.livePhotoVideoId} import { shortcuts } from '$lib/actions/shortcut'; + import { zoomImageAction, zoomed } from '$lib/actions/zoom-image'; + import BrokenAsset from '$lib/components/assets/broken-asset.svelte'; import { photoViewer } from '$lib/stores/assets.store'; import { boundingBoxesArray } from '$lib/stores/people.store'; import { alwaysLoadOriginalFile } from '$lib/stores/preferences.store'; @@ -9,15 +11,13 @@ import { isWebCompatibleImage } from '$lib/utils/asset-utils'; import { getBoundingBox } from '$lib/utils/people-utils'; import { getAltText } from '$lib/utils/thumbnail-util'; - import { AssetTypeEnum, type AssetResponseDto, AssetMediaSize, type SharedLinkResponseDto } from '@immich/sdk'; - import { zoomImageAction, zoomed } from '$lib/actions/zoom-image'; + import { AssetMediaSize, AssetTypeEnum, type AssetResponseDto, type SharedLinkResponseDto } from '@immich/sdk'; import { canCopyImagesToClipboard, copyImageToClipboard } from 'copy-image-clipboard'; import { onDestroy, onMount } from 'svelte'; - + import { t } from 'svelte-i18n'; import { fade } from 'svelte/transition'; import LoadingSpinner from '../shared-components/loading-spinner.svelte'; import { NotificationType, notificationController } from '../shared-components/notification/notification'; - import { t } from 'svelte-i18n'; export let asset: AssetResponseDto; export let preloadAssets: AssetResponseDto[] | undefined = undefined; @@ -137,7 +137,7 @@ ]} /> {#if imageError} -
{$t('error_loading_image')}
+ {/if} diff --git a/web/src/lib/components/assets/broken-asset.svelte b/web/src/lib/components/assets/broken-asset.svelte new file mode 100644 index 0000000000000..216a8f6f848b0 --- /dev/null +++ b/web/src/lib/components/assets/broken-asset.svelte @@ -0,0 +1,25 @@ + + +
+
+ + {#if !noMessage} +
{$t('error_loading_image')}
+ {/if} +
+
+ +
+
+
diff --git a/web/src/lib/components/assets/thumbnail/image-thumbnail.svelte b/web/src/lib/components/assets/thumbnail/image-thumbnail.svelte index e03dd35653290..38f2ff4dbb506 100644 --- a/web/src/lib/components/assets/thumbnail/image-thumbnail.svelte +++ b/web/src/lib/components/assets/thumbnail/image-thumbnail.svelte @@ -1,12 +1,12 @@ {#if errored} -
- -
+ +
{$t('error_loading_image')}
+
{:else} {/if} - {#if asset.resized} - (loaded = true)} - /> - {:else} -
- -
- {/if} + (loaded = true)} + /> {#if asset.type === AssetTypeEnum.Video}
diff --git a/web/src/lib/components/sharedlinks-page/covers/__tests__/share-cover.spec.ts b/web/src/lib/components/sharedlinks-page/covers/__tests__/share-cover.spec.ts index 1f1fa65cf8361..2952498b1ad64 100644 --- a/web/src/lib/components/sharedlinks-page/covers/__tests__/share-cover.spec.ts +++ b/web/src/lib/components/sharedlinks-page/covers/__tests__/share-cover.spec.ts @@ -47,13 +47,15 @@ describe('ShareCover component', () => { expect(img.className).toBe('z-0 rounded-xl object-cover aspect-square text'); }); - it('renders fallback image when asset is not resized', () => { - const link = sharedLinkFactory.build({ assets: [assetFactory.build({ resized: false })] }); + it.skip('renders fallback image when asset is not resized', () => { + const link = sharedLinkFactory.build({ assets: [assetFactory.build()] }); render(ShareCover, { link: link, preload: false, }); + // TODO emit image error event and check if fallback image is rendered + const img = screen.getByTestId('album-image'); expect(img.alt).toBe('unnamed_share'); }); diff --git a/web/src/lib/components/sharedlinks-page/covers/asset-cover.svelte b/web/src/lib/components/sharedlinks-page/covers/asset-cover.svelte index b8335be6b0632..69c11e079c51b 100644 --- a/web/src/lib/components/sharedlinks-page/covers/asset-cover.svelte +++ b/web/src/lib/components/sharedlinks-page/covers/asset-cover.svelte @@ -1,16 +1,25 @@ - +{#if isBroken} + +{:else} + (isBroken = true)} + class="z-0 rounded-xl object-cover aspect-square {className}" + data-testid="album-image" + draggable="false" + loading={preload ? 'eager' : 'lazy'} + {src} + /> +{/if} diff --git a/web/src/lib/components/sharedlinks-page/covers/share-cover.svelte b/web/src/lib/components/sharedlinks-page/covers/share-cover.svelte index 3a21a60989ce2..09f32d7dacebb 100644 --- a/web/src/lib/components/sharedlinks-page/covers/share-cover.svelte +++ b/web/src/lib/components/sharedlinks-page/covers/share-cover.svelte @@ -12,10 +12,10 @@ export { className as class }; -
+
{#if link?.album} - {:else if link.assets[0]?.resized} + {:else if link.assets[0]} - +
diff --git a/web/src/test-data/factories/asset-factory.ts b/web/src/test-data/factories/asset-factory.ts index 5f31b8af447f2..700b98c180e46 100644 --- a/web/src/test-data/factories/asset-factory.ts +++ b/web/src/test-data/factories/asset-factory.ts @@ -12,7 +12,6 @@ export const assetFactory = Sync.makeFactory({ originalPath: Sync.each(() => faker.system.filePath()), originalFileName: Sync.each(() => faker.system.fileName()), originalMimeType: Sync.each(() => faker.system.mimeType()), - resized: true, thumbhash: Sync.each(() => faker.string.alphanumeric(28)), fileCreatedAt: Sync.each(() => faker.date.past().toISOString()), fileModifiedAt: Sync.each(() => faker.date.past().toISOString()), From c14e2914f89b378d7ddfde08d9240af4882c2b59 Mon Sep 17 00:00:00 2001 From: Ben <45583362+ben-basten@users.noreply.github.com> Date: Fri, 23 Aug 2024 12:34:12 -0400 Subject: [PATCH 005/160] fix(web): rating stars accessibility (#11966) * fix(web): exif ratings accessibility * chore: add tests * fix: eslint errors * fix: clean up issues from changes in use:focusOutside --- web/src/lib/actions/focus-outside.ts | 5 +- .../detail-panel-star-rating.svelte | 2 +- .../__test__/star-rating.spec.ts | 78 ++++++++++++ .../shared-components/combobox.svelte | 2 - .../search-bar/search-bar.svelte | 5 +- .../shared-components/star-rating.svelte | 117 ++++++++++++++---- web/src/lib/i18n/en.json | 2 + 7 files changed, 180 insertions(+), 31 deletions(-) create mode 100644 web/src/lib/components/shared-components/__test__/star-rating.spec.ts diff --git a/web/src/lib/actions/focus-outside.ts b/web/src/lib/actions/focus-outside.ts index 07a85b021eb4f..2266ea8f0ff83 100644 --- a/web/src/lib/actions/focus-outside.ts +++ b/web/src/lib/actions/focus-outside.ts @@ -6,7 +6,10 @@ export function focusOutside(node: HTMLElement, options: Options = {}) { const { onFocusOut } = options; const handleFocusOut = (event: FocusEvent) => { - if (onFocusOut && event.relatedTarget instanceof Node && !node.contains(event.relatedTarget as Node)) { + if ( + onFocusOut && + (!event.relatedTarget || (event.relatedTarget instanceof Node && !node.contains(event.relatedTarget as Node))) + ) { onFocusOut(event); } }; diff --git a/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte b/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte index 131d2ca43675f..8b18d14f03d52 100644 --- a/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte @@ -21,7 +21,7 @@ {#if !isSharedLink() && $preferences?.rating?.enabled} -
+
handlePromiseError(handleChangeRating(rating))} />
{/if} diff --git a/web/src/lib/components/shared-components/__test__/star-rating.spec.ts b/web/src/lib/components/shared-components/__test__/star-rating.spec.ts new file mode 100644 index 0000000000000..cf33573b771b9 --- /dev/null +++ b/web/src/lib/components/shared-components/__test__/star-rating.spec.ts @@ -0,0 +1,78 @@ +import StarRating from '$lib/components/shared-components/star-rating.svelte'; +import { render } from '@testing-library/svelte'; + +describe('StarRating component', () => { + it('renders correctly', () => { + const component = render(StarRating, { + count: 3, + rating: 2, + readOnly: false, + onRating: vi.fn(), + }); + const container = component.getByTestId('star-container') as HTMLImageElement; + expect(container.className).toBe('flex flex-row'); + + const radioButtons = component.getAllByRole('radio') as HTMLInputElement[]; + expect(radioButtons.length).toBe(3); + const labels = component.getAllByTestId('star') as HTMLLabelElement[]; + expect(labels.length).toBe(3); + const labelText = component.getAllByText('rating_count') as HTMLSpanElement[]; + expect(labelText.length).toBe(3); + const clearButton = component.getByRole('button') as HTMLButtonElement; + expect(clearButton).toBeInTheDocument(); + + // Check the clear button content + expect(clearButton.textContent).toBe('rating_clear'); + + // Check the initial state + expect(radioButtons[0].checked).toBe(false); + expect(radioButtons[1].checked).toBe(true); + expect(radioButtons[2].checked).toBe(false); + + // Check the radio button attributes + for (const [index, radioButton] of radioButtons.entries()) { + expect(radioButton.id).toBe(labels[index].htmlFor); + expect(radioButton.name).toBe('stars'); + expect(radioButton.value).toBe((index + 1).toString()); + expect(radioButton.disabled).toBe(false); + expect(radioButton.className).toBe('sr-only'); + } + + // Check the label attributes + for (const label of labels) { + expect(label.className).toBe('cursor-pointer'); + expect(label.tabIndex).toBe(-1); + } + }); + + it('renders correctly with readOnly', () => { + const component = render(StarRating, { + count: 3, + rating: 2, + readOnly: true, + onRating: vi.fn(), + }); + const radioButtons = component.getAllByRole('radio') as HTMLInputElement[]; + expect(radioButtons.length).toBe(3); + const labels = component.getAllByTestId('star') as HTMLLabelElement[]; + expect(labels.length).toBe(3); + const clearButton = component.queryByRole('button'); + expect(clearButton).toBeNull(); + + // Check the initial state + expect(radioButtons[0].checked).toBe(false); + expect(radioButtons[1].checked).toBe(true); + expect(radioButtons[2].checked).toBe(false); + + // Check the radio button attributes + for (const [index, radioButton] of radioButtons.entries()) { + expect(radioButton.id).toBe(labels[index].htmlFor); + expect(radioButton.disabled).toBe(true); + } + + // Check the label attributes + for (const label of labels) { + expect(label.className).toBe(''); + } + }); +}); diff --git a/web/src/lib/components/shared-components/combobox.svelte b/web/src/lib/components/shared-components/combobox.svelte index 7cdcef9e40681..64ec16fda6541 100644 --- a/web/src/lib/components/shared-components/combobox.svelte +++ b/web/src/lib/components/shared-components/combobox.svelte @@ -23,7 +23,6 @@ import { createEventDispatcher, tick } from 'svelte'; import type { FormEventHandler } from 'svelte/elements'; import { shortcuts } from '$lib/actions/shortcut'; - import { clickOutside } from '$lib/actions/click-outside'; import { focusOutside } from '$lib/actions/focus-outside'; import { generateId } from '$lib/utils/generate-id'; import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte'; @@ -124,7 +123,6 @@
-
+
-
+
+ import { focusOutside } from '$lib/actions/focus-outside'; + import { shortcuts } from '$lib/actions/shortcut'; import Icon from '$lib/components/elements/icon.svelte'; + import { generateId } from '$lib/utils/generate-id'; + import { t } from 'svelte-i18n'; export let count = 5; export let rating: number; export let readOnly = false; export let onRating: (rating: number) => void | undefined; + let ratingSelection = 0; let hoverRating = 0; + let focusRating = 0; + let timeoutId: ReturnType | undefined; + + $: ratingSelection = rating; const starIcon = 'M10.788 3.21c.448-1.077 1.976-1.077 2.424 0l2.082 5.007 5.404.433c1.164.093 1.636 1.545.749 2.305l-4.117 3.527 1.257 5.273c.271 1.136-.964 2.033-1.96 1.425L12 18.354 7.373 21.18c-.996.608-2.231-.29-1.96-1.425l1.257-5.273-4.117-3.527c-.887-.76-.415-2.212.749-2.305l5.404-.433 2.082-5.006z'; + const id = generateId(); const handleSelect = (newRating: number) => { if (readOnly) { @@ -17,34 +27,93 @@ } if (newRating === rating) { - newRating = 0; + return; } - rating = newRating; + onRating(newRating); + }; - onRating?.(rating); + const setHoverRating = (value: number) => { + if (readOnly) { + return; + } + hoverRating = value; + }; + + const reset = () => { + setHoverRating(0); + focusRating = 0; + }; + + const handleSelectDebounced = (value: number) => { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + handleSelect(value); + }, 300); }; -
(hoverRating = 0)} on:blur|preventDefault> - {#each { length: count } as _, index} - {@const value = index + 1} - {@const filled = hoverRating >= value || (hoverRating === 0 && rating >= value)} - - {/each} -
+ {/each} +
+ +{#if ratingSelection > 0 && !readOnly} + +{/if} diff --git a/web/src/lib/i18n/en.json b/web/src/lib/i18n/en.json index 91fb1aba43c12..3609b9c274cb5 100644 --- a/web/src/lib/i18n/en.json +++ b/web/src/lib/i18n/en.json @@ -972,6 +972,8 @@ "purchase_server_title": "Server", "purchase_settings_server_activated": "The server product key is managed by the admin", "rating": "Star rating", + "rating_clear": "Clear rating", + "rating_count": "{count, plural, one {# star} other {# stars}}", "rating_description": "Display the exif rating in the info panel", "reaction_options": "Reaction options", "read_changelog": "Read Changelog", From da12d5f567d000e288e5db0cd68d9dcd848d94a4 Mon Sep 17 00:00:00 2001 From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Sat, 24 Aug 2024 01:03:36 +0200 Subject: [PATCH 006/160] feat(web): my immich shortcut (#12007) feat: my immich shortcut in web --- web/src/routes/+layout.svelte | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index d086129d7fde3..1ad9066c4e1db 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -13,13 +13,14 @@ import { loadConfig, serverConfig } from '$lib/stores/server-config.store'; import { user } from '$lib/stores/user.store'; import { closeWebsocketConnection, openWebsocketConnection } from '$lib/stores/websocket'; - import { setKey } from '$lib/utils'; + import { copyToClipboard, setKey } from '$lib/utils'; import { handleError } from '$lib/utils/handle-error'; import { onDestroy, onMount } from 'svelte'; import '../app.css'; import { isAssetViewerRoute, isSharedLinkRoute } from '$lib/utils/navigation'; import DialogWrapper from '$lib/components/shared-components/dialog/dialog-wrapper.svelte'; import { t } from 'svelte-i18n'; + import { shortcut } from '$lib/actions/shortcut'; let showNavigationLoadingBar = false; $: changeTheme($colorTheme); @@ -49,6 +50,10 @@ } }; + const getMyImmichLink = () => { + return new URL($page.url.pathname + $page.url.search, 'https://my.immich.app'); + }; + onMount(() => { // if the browser theme changes, changes the Immich theme too window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', handleChangeTheme); @@ -123,6 +128,12 @@ + copyToClipboard(getMyImmichLink().toString()), + }} +/> {#if showNavigationLoadingBar} From 00a7b801844a16caf5ec40ac48141d1cc0446992 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 24 Aug 2024 17:50:05 +0000 Subject: [PATCH 007/160] fix(deps): update machine-learning (#11921) --- machine-learning/poetry.lock | 230 ++++++++++++++++++----------------- 1 file changed, 117 insertions(+), 113 deletions(-) diff --git a/machine-learning/poetry.lock b/machine-learning/poetry.lock index 9d19b671d1a04..31949aee84ccb 100644 --- a/machine-learning/poetry.lock +++ b/machine-learning/poetry.lock @@ -680,18 +680,18 @@ test = ["pytest (>=6)"] [[package]] name = "fastapi-slim" -version = "0.112.0" +version = "0.112.1" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi_slim-0.112.0-py3-none-any.whl", hash = "sha256:7663edfbb5036d641aa45b4f5dad341cf78d98885216e78743a8cdd39a38883e"}, - {file = "fastapi_slim-0.112.0.tar.gz", hash = "sha256:2420f700b7dc2d1a6d02c7230f7aa2ae9fa0320d8d481094062ff717659c0843"}, + {file = "fastapi_slim-0.112.1-py3-none-any.whl", hash = "sha256:cc227cf9402d0ba54a24f80eb205c33bcb25d3ea18d53fdac3fd76ea5af8e76d"}, + {file = "fastapi_slim-0.112.1.tar.gz", hash = "sha256:876ebd24e72273986709db2d469b75dc18f04c3ab9140ffd78b29d7785d26687"}, ] [package.dependencies] pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.37.2,<0.38.0" +starlette = ">=0.37.2,<0.39.0" typing-extensions = ">=4.8.0" [package.extras] @@ -1236,13 +1236,13 @@ socks = ["socksio (==1.*)"] [[package]] name = "huggingface-hub" -version = "0.24.5" +version = "0.24.6" description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" optional = false python-versions = ">=3.8.0" files = [ - {file = "huggingface_hub-0.24.5-py3-none-any.whl", hash = "sha256:d93fb63b1f1a919a22ce91a14518974e81fc4610bf344dfe7572343ce8d3aced"}, - {file = "huggingface_hub-0.24.5.tar.gz", hash = "sha256:7b45d6744dd53ce9cbf9880957de00e9d10a9ae837f1c9b7255fc8fa4e8264f3"}, + {file = "huggingface_hub-0.24.6-py3-none-any.whl", hash = "sha256:a990f3232aa985fe749bc9474060cbad75e8b2f115f6665a9fda5b9c97818970"}, + {file = "huggingface_hub-0.24.6.tar.gz", hash = "sha256:cc2579e761d070713eaa9c323e3debe39d5b464ae3a7261c39a9195b27bb8000"}, ] [package.dependencies] @@ -1530,13 +1530,13 @@ test = ["pytest (>=7.4)", "pytest-cov (>=4.1)"] [[package]] name = "locust" -version = "2.31.2" +version = "2.31.3" description = "Developer-friendly load testing framework" optional = false python-versions = ">=3.9" files = [ - {file = "locust-2.31.2-py3-none-any.whl", hash = "sha256:9bcb8b777d9844ac9498d6eebe17a0afa21712419c42da27b1d1cac5895cd182"}, - {file = "locust-2.31.2.tar.gz", hash = "sha256:a31f8e1d24535494eb809bd8dfd545ada9514df4581b69bdc2ecf3e109b7a1dd"}, + {file = "locust-2.31.3-py3-none-any.whl", hash = "sha256:03122e007519b371a5a553d578af502826755de83551d79ea8a412ea1c660115"}, + {file = "locust-2.31.3.tar.gz", hash = "sha256:25f4603f24afa11ef1ee1f26b1c86a232eb9a1140be30b2a4642c12d7a7af8ae"}, ] [package.dependencies] @@ -1962,42 +1962,42 @@ reference = ["Pillow", "google-re2"] [[package]] name = "onnxruntime" -version = "1.18.1" +version = "1.19.0" description = "ONNX Runtime is a runtime accelerator for Machine Learning models" optional = false python-versions = "*" files = [ - {file = "onnxruntime-1.18.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:29ef7683312393d4ba04252f1b287d964bd67d5e6048b94d2da3643986c74d80"}, - {file = "onnxruntime-1.18.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fc706eb1df06ddf55776e15a30519fb15dda7697f987a2bbda4962845e3cec05"}, - {file = "onnxruntime-1.18.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7de69f5ced2a263531923fa68bbec52a56e793b802fcd81a03487b5e292bc3a"}, - {file = "onnxruntime-1.18.1-cp310-cp310-win32.whl", hash = "sha256:221e5b16173926e6c7de2cd437764492aa12b6811f45abd37024e7cf2ae5d7e3"}, - {file = "onnxruntime-1.18.1-cp310-cp310-win_amd64.whl", hash = "sha256:75211b619275199c861ee94d317243b8a0fcde6032e5a80e1aa9ded8ab4c6060"}, - {file = "onnxruntime-1.18.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:f26582882f2dc581b809cfa41a125ba71ad9e715738ec6402418df356969774a"}, - {file = "onnxruntime-1.18.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef36f3a8b768506d02be349ac303fd95d92813ba3ba70304d40c3cd5c25d6a4c"}, - {file = "onnxruntime-1.18.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:170e711393e0618efa8ed27b59b9de0ee2383bd2a1f93622a97006a5ad48e434"}, - {file = "onnxruntime-1.18.1-cp311-cp311-win32.whl", hash = "sha256:9b6a33419b6949ea34e0dc009bc4470e550155b6da644571ecace4b198b0d88f"}, - {file = "onnxruntime-1.18.1-cp311-cp311-win_amd64.whl", hash = "sha256:5c1380a9f1b7788da742c759b6a02ba771fe1ce620519b2b07309decbd1a2fe1"}, - {file = "onnxruntime-1.18.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:31bd57a55e3f983b598675dfc7e5d6f0877b70ec9864b3cc3c3e1923d0a01919"}, - {file = "onnxruntime-1.18.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9e03c4ba9f734500691a4d7d5b381cd71ee2f3ce80a1154ac8f7aed99d1ecaa"}, - {file = "onnxruntime-1.18.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:781aa9873640f5df24524f96f6070b8c550c66cb6af35710fd9f92a20b4bfbf6"}, - {file = "onnxruntime-1.18.1-cp312-cp312-win32.whl", hash = "sha256:3a2d9ab6254ca62adbb448222e630dc6883210f718065063518c8f93a32432be"}, - {file = "onnxruntime-1.18.1-cp312-cp312-win_amd64.whl", hash = "sha256:ad93c560b1c38c27c0275ffd15cd7f45b3ad3fc96653c09ce2931179982ff204"}, - {file = "onnxruntime-1.18.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:3b55dc9d3c67626388958a3eb7ad87eb7c70f75cb0f7ff4908d27b8b42f2475c"}, - {file = "onnxruntime-1.18.1-cp38-cp38-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f80dbcfb6763cc0177a31168b29b4bd7662545b99a19e211de8c734b657e0669"}, - {file = "onnxruntime-1.18.1-cp38-cp38-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1ff2c61a16d6c8631796c54139bafea41ee7736077a0fc64ee8ae59432f5c58"}, - {file = "onnxruntime-1.18.1-cp38-cp38-win32.whl", hash = "sha256:219855bd272fe0c667b850bf1a1a5a02499269a70d59c48e6f27f9c8bcb25d02"}, - {file = "onnxruntime-1.18.1-cp38-cp38-win_amd64.whl", hash = "sha256:afdf16aa607eb9a2c60d5ca2d5abf9f448e90c345b6b94c3ed14f4fb7e6a2d07"}, - {file = "onnxruntime-1.18.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:128df253ade673e60cea0955ec9d0e89617443a6d9ce47c2d79eb3f72a3be3de"}, - {file = "onnxruntime-1.18.1-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9839491e77e5c5a175cab3621e184d5a88925ee297ff4c311b68897197f4cde9"}, - {file = "onnxruntime-1.18.1-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad3187c1faff3ac15f7f0e7373ef4788c582cafa655a80fdbb33eaec88976c66"}, - {file = "onnxruntime-1.18.1-cp39-cp39-win32.whl", hash = "sha256:34657c78aa4e0b5145f9188b550ded3af626651b15017bf43d280d7e23dbf195"}, - {file = "onnxruntime-1.18.1-cp39-cp39-win_amd64.whl", hash = "sha256:9c14fd97c3ddfa97da5feef595e2c73f14c2d0ec1d4ecbea99c8d96603c89589"}, + {file = "onnxruntime-1.19.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6ce22a98dfec7b646ae305f52d0ce14a189a758b02ea501860ca719f4b0ae04b"}, + {file = "onnxruntime-1.19.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19019c72873f26927aa322c54cf2bf7312b23451b27451f39b88f57016c94f8b"}, + {file = "onnxruntime-1.19.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8eaa16df99171dc636e30108d15597aed8c4c2dd9dbfdd07cc464d57d73fb275"}, + {file = "onnxruntime-1.19.0-cp310-cp310-win32.whl", hash = "sha256:0eb0f8dbe596fd0f4737fe511fdbb17603853a7d204c5b2ca38d3c7808fc556b"}, + {file = "onnxruntime-1.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:616092d54ba8023b7bc0a5f6d900a07a37cc1cfcc631873c15f8c1d6e9e184d4"}, + {file = "onnxruntime-1.19.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:a2b53b3c287cd933e5eb597273926e899082d8c84ab96e1b34035764a1627e17"}, + {file = "onnxruntime-1.19.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e94984663963e74fbb468bde9ec6f19dcf890b594b35e249c4dc8789d08993c5"}, + {file = "onnxruntime-1.19.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f379d1f050cfb55ce015d53727b78ee362febc065c38eed81512b22b757da73"}, + {file = "onnxruntime-1.19.0-cp311-cp311-win32.whl", hash = "sha256:4ccb48faea02503275ae7e79e351434fc43c294c4cb5c4d8bcb7479061396614"}, + {file = "onnxruntime-1.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:9cdc8d311289a84e77722de68bd22b8adfb94eea26f4be6f9e017350faac8b18"}, + {file = "onnxruntime-1.19.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:1b59eaec1be9a8613c5fdeaafe67f73a062edce3ac03bbbdc9e2d98b58a30617"}, + {file = "onnxruntime-1.19.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be4144d014a4b25184e63ce7a463a2e7796e2f3df931fccc6a6aefa6f1365dc5"}, + {file = "onnxruntime-1.19.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10d7e7d4ca7021ce7f29a66dbc6071addf2de5839135339bd855c6d9c2bba371"}, + {file = "onnxruntime-1.19.0-cp312-cp312-win32.whl", hash = "sha256:87f2c58b577a1fb31dc5d92b647ecc588fd5f1ea0c3ad4526f5f80a113357c8d"}, + {file = "onnxruntime-1.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:8a1f50d49676d7b69566536ff039d9e4e95fc482a55673719f46528218ecbb94"}, + {file = "onnxruntime-1.19.0-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:71423c8c4b2d7a58956271534302ec72721c62a41efd0c4896343249b8399ab0"}, + {file = "onnxruntime-1.19.0-cp38-cp38-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9d63630d45e9498f96e75bbeb7fd4a56acb10155de0de4d0e18d1b6cbb0b358a"}, + {file = "onnxruntime-1.19.0-cp38-cp38-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3bfd15db1e8794d379a86c1a9116889f47f2cca40cc82208fc4f7e8c38e8522"}, + {file = "onnxruntime-1.19.0-cp38-cp38-win32.whl", hash = "sha256:3b098003b6b4cb37cc84942e5f1fe27f945dd857cbd2829c824c26b0ba4a247e"}, + {file = "onnxruntime-1.19.0-cp38-cp38-win_amd64.whl", hash = "sha256:cea067a6541d6787d903ee6843401c5b1332a266585160d9700f9f0939443886"}, + {file = "onnxruntime-1.19.0-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:c4fcff12dc5ca963c5f76b9822bb404578fa4a98c281e8c666b429192799a099"}, + {file = "onnxruntime-1.19.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f6dcad8a4db908fbe70b98c79cea1c8b6ac3316adf4ce93453136e33a524ac59"}, + {file = "onnxruntime-1.19.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bc449907c6e8d99eee5ae5cc9c8fdef273d801dcd195393d3f9ab8ad3f49522"}, + {file = "onnxruntime-1.19.0-cp39-cp39-win32.whl", hash = "sha256:947febd48405afcf526e45ccff97ff23b15e530434705f734870d22ae7fcf236"}, + {file = "onnxruntime-1.19.0-cp39-cp39-win_amd64.whl", hash = "sha256:f60be47eff5ee77fd28a466b0fd41d7debc42a32179d1ddb21e05d6067d7b48b"}, ] [package.dependencies] coloredlogs = "*" flatbuffers = "*" -numpy = ">=1.21.6,<2.0" +numpy = ">=1.21.6" packaging = "*" protobuf = "*" sympy = "*" @@ -2082,64 +2082,68 @@ numpy = [ [[package]] name = "orjson" -version = "3.10.6" +version = "3.10.7" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.10.6-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:fb0ee33124db6eaa517d00890fc1a55c3bfe1cf78ba4a8899d71a06f2d6ff5c7"}, - {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c1c4b53b24a4c06547ce43e5fee6ec4e0d8fe2d597f4647fc033fd205707365"}, - {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eadc8fd310edb4bdbd333374f2c8fec6794bbbae99b592f448d8214a5e4050c0"}, - {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61272a5aec2b2661f4fa2b37c907ce9701e821b2c1285d5c3ab0207ebd358d38"}, - {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57985ee7e91d6214c837936dc1608f40f330a6b88bb13f5a57ce5257807da143"}, - {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:633a3b31d9d7c9f02d49c4ab4d0a86065c4a6f6adc297d63d272e043472acab5"}, - {file = "orjson-3.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1c680b269d33ec444afe2bdc647c9eb73166fa47a16d9a75ee56a374f4a45f43"}, - {file = "orjson-3.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f759503a97a6ace19e55461395ab0d618b5a117e8d0fbb20e70cfd68a47327f2"}, - {file = "orjson-3.10.6-cp310-none-win32.whl", hash = "sha256:95a0cce17f969fb5391762e5719575217bd10ac5a189d1979442ee54456393f3"}, - {file = "orjson-3.10.6-cp310-none-win_amd64.whl", hash = "sha256:df25d9271270ba2133cc88ee83c318372bdc0f2cd6f32e7a450809a111efc45c"}, - {file = "orjson-3.10.6-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b1ec490e10d2a77c345def52599311849fc063ae0e67cf4f84528073152bb2ba"}, - {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d43d3feb8f19d07e9f01e5b9be4f28801cf7c60d0fa0d279951b18fae1932b"}, - {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac3045267e98fe749408eee1593a142e02357c5c99be0802185ef2170086a863"}, - {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c27bc6a28ae95923350ab382c57113abd38f3928af3c80be6f2ba7eb8d8db0b0"}, - {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d27456491ca79532d11e507cadca37fb8c9324a3976294f68fb1eff2dc6ced5a"}, - {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05ac3d3916023745aa3b3b388e91b9166be1ca02b7c7e41045da6d12985685f0"}, - {file = "orjson-3.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1335d4ef59ab85cab66fe73fd7a4e881c298ee7f63ede918b7faa1b27cbe5212"}, - {file = "orjson-3.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4bbc6d0af24c1575edc79994c20e1b29e6fb3c6a570371306db0993ecf144dc5"}, - {file = "orjson-3.10.6-cp311-none-win32.whl", hash = "sha256:450e39ab1f7694465060a0550b3f6d328d20297bf2e06aa947b97c21e5241fbd"}, - {file = "orjson-3.10.6-cp311-none-win_amd64.whl", hash = "sha256:227df19441372610b20e05bdb906e1742ec2ad7a66ac8350dcfd29a63014a83b"}, - {file = "orjson-3.10.6-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ea2977b21f8d5d9b758bb3f344a75e55ca78e3ff85595d248eee813ae23ecdfb"}, - {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6f3d167d13a16ed263b52dbfedff52c962bfd3d270b46b7518365bcc2121eed"}, - {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f710f346e4c44a4e8bdf23daa974faede58f83334289df80bc9cd12fe82573c7"}, - {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7275664f84e027dcb1ad5200b8b18373e9c669b2a9ec33d410c40f5ccf4b257e"}, - {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0943e4c701196b23c240b3d10ed8ecd674f03089198cf503105b474a4f77f21f"}, - {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:446dee5a491b5bc7d8f825d80d9637e7af43f86a331207b9c9610e2f93fee22a"}, - {file = "orjson-3.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:64c81456d2a050d380786413786b057983892db105516639cb5d3ee3c7fd5148"}, - {file = "orjson-3.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:960db0e31c4e52fa0fc3ecbaea5b2d3b58f379e32a95ae6b0ebeaa25b93dfd34"}, - {file = "orjson-3.10.6-cp312-none-win32.whl", hash = "sha256:a6ea7afb5b30b2317e0bee03c8d34c8181bc5a36f2afd4d0952f378972c4efd5"}, - {file = "orjson-3.10.6-cp312-none-win_amd64.whl", hash = "sha256:874ce88264b7e655dde4aeaacdc8fd772a7962faadfb41abe63e2a4861abc3dc"}, - {file = "orjson-3.10.6-cp313-none-win32.whl", hash = "sha256:efdf2c5cde290ae6b83095f03119bdc00303d7a03b42b16c54517baa3c4ca3d0"}, - {file = "orjson-3.10.6-cp313-none-win_amd64.whl", hash = "sha256:8e190fe7888e2e4392f52cafb9626113ba135ef53aacc65cd13109eb9746c43e"}, - {file = "orjson-3.10.6-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:66680eae4c4e7fc193d91cfc1353ad6d01b4801ae9b5314f17e11ba55e934183"}, - {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:caff75b425db5ef8e8f23af93c80f072f97b4fb3afd4af44482905c9f588da28"}, - {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3722fddb821b6036fd2a3c814f6bd9b57a89dc6337b9924ecd614ebce3271394"}, - {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2c116072a8533f2fec435fde4d134610f806bdac20188c7bd2081f3e9e0133f"}, - {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6eeb13218c8cf34c61912e9df2de2853f1d009de0e46ea09ccdf3d757896af0a"}, - {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:965a916373382674e323c957d560b953d81d7a8603fbeee26f7b8248638bd48b"}, - {file = "orjson-3.10.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:03c95484d53ed8e479cade8628c9cea00fd9d67f5554764a1110e0d5aa2de96e"}, - {file = "orjson-3.10.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:e060748a04cccf1e0a6f2358dffea9c080b849a4a68c28b1b907f272b5127e9b"}, - {file = "orjson-3.10.6-cp38-none-win32.whl", hash = "sha256:738dbe3ef909c4b019d69afc19caf6b5ed0e2f1c786b5d6215fbb7539246e4c6"}, - {file = "orjson-3.10.6-cp38-none-win_amd64.whl", hash = "sha256:d40f839dddf6a7d77114fe6b8a70218556408c71d4d6e29413bb5f150a692ff7"}, - {file = "orjson-3.10.6-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:697a35a083c4f834807a6232b3e62c8b280f7a44ad0b759fd4dce748951e70db"}, - {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd502f96bf5ea9a61cbc0b2b5900d0dd68aa0da197179042bdd2be67e51a1e4b"}, - {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f215789fb1667cdc874c1b8af6a84dc939fd802bf293a8334fce185c79cd359b"}, - {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2debd8ddce948a8c0938c8c93ade191d2f4ba4649a54302a7da905a81f00b56"}, - {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5410111d7b6681d4b0d65e0f58a13be588d01b473822483f77f513c7f93bd3b2"}, - {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb1f28a137337fdc18384079fa5726810681055b32b92253fa15ae5656e1dddb"}, - {file = "orjson-3.10.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bf2fbbce5fe7cd1aa177ea3eab2b8e6a6bc6e8592e4279ed3db2d62e57c0e1b2"}, - {file = "orjson-3.10.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:79b9b9e33bd4c517445a62b90ca0cc279b0f1f3970655c3df9e608bc3f91741a"}, - {file = "orjson-3.10.6-cp39-none-win32.whl", hash = "sha256:30b0a09a2014e621b1adf66a4f705f0809358350a757508ee80209b2d8dae219"}, - {file = "orjson-3.10.6-cp39-none-win_amd64.whl", hash = "sha256:49e3bc615652617d463069f91b867a4458114c5b104e13b7ae6872e5f79d0844"}, - {file = "orjson-3.10.6.tar.gz", hash = "sha256:e54b63d0a7c6c54a5f5f726bc93a2078111ef060fec4ecbf34c5db800ca3b3a7"}, + {file = "orjson-3.10.7-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:74f4544f5a6405b90da8ea724d15ac9c36da4d72a738c64685003337401f5c12"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34a566f22c28222b08875b18b0dfbf8a947e69df21a9ed5c51a6bf91cfb944ac"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf6ba8ebc8ef5792e2337fb0419f8009729335bb400ece005606336b7fd7bab7"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac7cf6222b29fbda9e3a472b41e6a5538b48f2c8f99261eecd60aafbdb60690c"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de817e2f5fc75a9e7dd350c4b0f54617b280e26d1631811a43e7e968fa71e3e9"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:348bdd16b32556cf8d7257b17cf2bdb7ab7976af4af41ebe79f9796c218f7e91"}, + {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:479fd0844ddc3ca77e0fd99644c7fe2de8e8be1efcd57705b5c92e5186e8a250"}, + {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fdf5197a21dd660cf19dfd2a3ce79574588f8f5e2dbf21bda9ee2d2b46924d84"}, + {file = "orjson-3.10.7-cp310-none-win32.whl", hash = "sha256:d374d36726746c81a49f3ff8daa2898dccab6596864ebe43d50733275c629175"}, + {file = "orjson-3.10.7-cp310-none-win_amd64.whl", hash = "sha256:cb61938aec8b0ffb6eef484d480188a1777e67b05d58e41b435c74b9d84e0b9c"}, + {file = "orjson-3.10.7-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7db8539039698ddfb9a524b4dd19508256107568cdad24f3682d5773e60504a2"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:480f455222cb7a1dea35c57a67578848537d2602b46c464472c995297117fa09"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8a9c9b168b3a19e37fe2778c0003359f07822c90fdff8f98d9d2a91b3144d8e0"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8de062de550f63185e4c1c54151bdddfc5625e37daf0aa1e75d2a1293e3b7d9a"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b0dd04483499d1de9c8f6203f8975caf17a6000b9c0c54630cef02e44ee624e"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b58d3795dafa334fc8fd46f7c5dc013e6ad06fd5b9a4cc98cb1456e7d3558bd6"}, + {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:33cfb96c24034a878d83d1a9415799a73dc77480e6c40417e5dda0710d559ee6"}, + {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e724cebe1fadc2b23c6f7415bad5ee6239e00a69f30ee423f319c6af70e2a5c0"}, + {file = "orjson-3.10.7-cp311-none-win32.whl", hash = "sha256:82763b46053727a7168d29c772ed5c870fdae2f61aa8a25994c7984a19b1021f"}, + {file = "orjson-3.10.7-cp311-none-win_amd64.whl", hash = "sha256:eb8d384a24778abf29afb8e41d68fdd9a156cf6e5390c04cc07bbc24b89e98b5"}, + {file = "orjson-3.10.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44a96f2d4c3af51bfac6bc4ef7b182aa33f2f054fd7f34cc0ee9a320d051d41f"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ac14cd57df0572453543f8f2575e2d01ae9e790c21f57627803f5e79b0d3c3"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bdbb61dcc365dd9be94e8f7df91975edc9364d6a78c8f7adb69c1cdff318ec93"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b48b3db6bb6e0a08fa8c83b47bc169623f801e5cc4f24442ab2b6617da3b5313"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23820a1563a1d386414fef15c249040042b8e5d07b40ab3fe3efbfbbcbcb8864"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0c6a008e91d10a2564edbb6ee5069a9e66df3fbe11c9a005cb411f441fd2c09"}, + {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d352ee8ac1926d6193f602cbe36b1643bbd1bbcb25e3c1a657a4390f3000c9a5"}, + {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2d9f990623f15c0ae7ac608103c33dfe1486d2ed974ac3f40b693bad1a22a7b"}, + {file = "orjson-3.10.7-cp312-none-win32.whl", hash = "sha256:7c4c17f8157bd520cdb7195f75ddbd31671997cbe10aee559c2d613592e7d7eb"}, + {file = "orjson-3.10.7-cp312-none-win_amd64.whl", hash = "sha256:1d9c0e733e02ada3ed6098a10a8ee0052dd55774de3d9110d29868d24b17faa1"}, + {file = "orjson-3.10.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:77d325ed866876c0fa6492598ec01fe30e803272a6e8b10e992288b009cbe149"}, + {file = "orjson-3.10.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ea2c232deedcb605e853ae1db2cc94f7390ac776743b699b50b071b02bea6fe"}, + {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3dcfbede6737fdbef3ce9c37af3fb6142e8e1ebc10336daa05872bfb1d87839c"}, + {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11748c135f281203f4ee695b7f80bb1358a82a63905f9f0b794769483ea854ad"}, + {file = "orjson-3.10.7-cp313-none-win32.whl", hash = "sha256:a7e19150d215c7a13f39eb787d84db274298d3f83d85463e61d277bbd7f401d2"}, + {file = "orjson-3.10.7-cp313-none-win_amd64.whl", hash = "sha256:eef44224729e9525d5261cc8d28d6b11cafc90e6bd0be2157bde69a52ec83024"}, + {file = "orjson-3.10.7-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6ea2b2258eff652c82652d5e0f02bd5e0463a6a52abb78e49ac288827aaa1469"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:430ee4d85841e1483d487e7b81401785a5dfd69db5de01314538f31f8fbf7ee1"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4b6146e439af4c2472c56f8540d799a67a81226e11992008cb47e1267a9b3225"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:084e537806b458911137f76097e53ce7bf5806dda33ddf6aaa66a028f8d43a23"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4829cf2195838e3f93b70fd3b4292156fc5e097aac3739859ac0dcc722b27ac0"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1193b2416cbad1a769f868b1749535d5da47626ac29445803dae7cc64b3f5c98"}, + {file = "orjson-3.10.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:4e6c3da13e5a57e4b3dca2de059f243ebec705857522f188f0180ae88badd354"}, + {file = "orjson-3.10.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c31008598424dfbe52ce8c5b47e0752dca918a4fdc4a2a32004efd9fab41d866"}, + {file = "orjson-3.10.7-cp38-none-win32.whl", hash = "sha256:7122a99831f9e7fe977dc45784d3b2edc821c172d545e6420c375e5a935f5a1c"}, + {file = "orjson-3.10.7-cp38-none-win_amd64.whl", hash = "sha256:a763bc0e58504cc803739e7df040685816145a6f3c8a589787084b54ebc9f16e"}, + {file = "orjson-3.10.7-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e76be12658a6fa376fcd331b1ea4e58f5a06fd0220653450f0d415b8fd0fbe20"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed350d6978d28b92939bfeb1a0570c523f6170efc3f0a0ef1f1df287cd4f4960"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:144888c76f8520e39bfa121b31fd637e18d4cc2f115727865fdf9fa325b10412"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09b2d92fd95ad2402188cf51573acde57eb269eddabaa60f69ea0d733e789fe9"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b24a579123fa884f3a3caadaed7b75eb5715ee2b17ab5c66ac97d29b18fe57f"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591bcfe7512353bd609875ab38050efe3d55e18934e2f18950c108334b4ff"}, + {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f4db56635b58cd1a200b0a23744ff44206ee6aa428185e2b6c4a65b3197abdcd"}, + {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0fa5886854673222618638c6df7718ea7fe2f3f2384c452c9ccedc70b4a510a5"}, + {file = "orjson-3.10.7-cp39-none-win32.whl", hash = "sha256:8272527d08450ab16eb405f47e0f4ef0e5ff5981c3d82afe0efd25dcbef2bcd2"}, + {file = "orjson-3.10.7-cp39-none-win_amd64.whl", hash = "sha256:974683d4618c0c7dbf4f69c95a979734bf183d0658611760017f6e70a145af58"}, + {file = "orjson-3.10.7.tar.gz", hash = "sha256:75ef0640403f945f3a1f9f6400686560dbfb0fb5b16589ad62cd477043c4eee3"}, ] [[package]] @@ -2829,29 +2833,29 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruff" -version = "0.5.7" +version = "0.6.2" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.5.7-py3-none-linux_armv6l.whl", hash = "sha256:548992d342fc404ee2e15a242cdbea4f8e39a52f2e7752d0e4cbe88d2d2f416a"}, - {file = "ruff-0.5.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:00cc8872331055ee017c4f1071a8a31ca0809ccc0657da1d154a1d2abac5c0be"}, - {file = "ruff-0.5.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eaf3d86a1fdac1aec8a3417a63587d93f906c678bb9ed0b796da7b59c1114a1e"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a01c34400097b06cf8a6e61b35d6d456d5bd1ae6961542de18ec81eaf33b4cb8"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcc8054f1a717e2213500edaddcf1dbb0abad40d98e1bd9d0ad364f75c763eea"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f70284e73f36558ef51602254451e50dd6cc479f8b6f8413a95fcb5db4a55fc"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a78ad870ae3c460394fc95437d43deb5c04b5c29297815a2a1de028903f19692"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ccd078c66a8e419475174bfe60a69adb36ce04f8d4e91b006f1329d5cd44bcf"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e31c9bad4ebf8fdb77b59cae75814440731060a09a0e0077d559a556453acbb"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d796327eed8e168164346b769dd9a27a70e0298d667b4ecee6877ce8095ec8e"}, - {file = "ruff-0.5.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a09ea2c3f7778cc635e7f6edf57d566a8ee8f485f3c4454db7771efb692c499"}, - {file = "ruff-0.5.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a36d8dcf55b3a3bc353270d544fb170d75d2dff41eba5df57b4e0b67a95bb64e"}, - {file = "ruff-0.5.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9369c218f789eefbd1b8d82a8cf25017b523ac47d96b2f531eba73770971c9e5"}, - {file = "ruff-0.5.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b88ca3db7eb377eb24fb7c82840546fb7acef75af4a74bd36e9ceb37a890257e"}, - {file = "ruff-0.5.7-py3-none-win32.whl", hash = "sha256:33d61fc0e902198a3e55719f4be6b375b28f860b09c281e4bdbf783c0566576a"}, - {file = "ruff-0.5.7-py3-none-win_amd64.whl", hash = "sha256:083bbcbe6fadb93cd86709037acc510f86eed5a314203079df174c40bbbca6b3"}, - {file = "ruff-0.5.7-py3-none-win_arm64.whl", hash = "sha256:2dca26154ff9571995107221d0aeaad0e75a77b5a682d6236cf89a58c70b76f4"}, - {file = "ruff-0.5.7.tar.gz", hash = "sha256:8dfc0a458797f5d9fb622dd0efc52d796f23f0a1493a9527f4e49a550ae9a7e5"}, + {file = "ruff-0.6.2-py3-none-linux_armv6l.whl", hash = "sha256:5c8cbc6252deb3ea840ad6a20b0f8583caab0c5ef4f9cca21adc5a92b8f79f3c"}, + {file = "ruff-0.6.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:17002fe241e76544448a8e1e6118abecbe8cd10cf68fde635dad480dba594570"}, + {file = "ruff-0.6.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3dbeac76ed13456f8158b8f4fe087bf87882e645c8e8b606dd17b0b66c2c1158"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:094600ee88cda325988d3f54e3588c46de5c18dae09d683ace278b11f9d4d534"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:316d418fe258c036ba05fbf7dfc1f7d3d4096db63431546163b472285668132b"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d72b8b3abf8a2d51b7b9944a41307d2f442558ccb3859bbd87e6ae9be1694a5d"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2aed7e243be68487aa8982e91c6e260982d00da3f38955873aecd5a9204b1d66"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d371f7fc9cec83497fe7cf5eaf5b76e22a8efce463de5f775a1826197feb9df8"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8f310d63af08f583363dfb844ba8f9417b558199c58a5999215082036d795a1"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7db6880c53c56addb8638fe444818183385ec85eeada1d48fc5abe045301b2f1"}, + {file = "ruff-0.6.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1175d39faadd9a50718f478d23bfc1d4da5743f1ab56af81a2b6caf0a2394f23"}, + {file = "ruff-0.6.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5b939f9c86d51635fe486585389f54582f0d65b8238e08c327c1534844b3bb9a"}, + {file = "ruff-0.6.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d0d62ca91219f906caf9b187dea50d17353f15ec9bb15aae4a606cd697b49b4c"}, + {file = "ruff-0.6.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7438a7288f9d67ed3c8ce4d059e67f7ed65e9fe3aa2ab6f5b4b3610e57e3cb56"}, + {file = "ruff-0.6.2-py3-none-win32.whl", hash = "sha256:279d5f7d86696df5f9549b56b9b6a7f6c72961b619022b5b7999b15db392a4da"}, + {file = "ruff-0.6.2-py3-none-win_amd64.whl", hash = "sha256:d9f3469c7dd43cd22eb1c3fc16926fb8258d50cb1b216658a07be95dd117b0f2"}, + {file = "ruff-0.6.2-py3-none-win_arm64.whl", hash = "sha256:f28fcd2cd0e02bdf739297516d5643a945cc7caf09bd9bcb4d932540a5ea4fa9"}, + {file = "ruff-0.6.2.tar.gz", hash = "sha256:239ee6beb9e91feb8e0ec384204a763f36cb53fb895a1a364618c6abb076b3be"}, ] [[package]] @@ -3264,13 +3268,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uvicorn" -version = "0.30.5" +version = "0.30.6" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.8" files = [ - {file = "uvicorn-0.30.5-py3-none-any.whl", hash = "sha256:b2d86de274726e9878188fa07576c9ceeff90a839e2b6e25c917fe05f5a6c835"}, - {file = "uvicorn-0.30.5.tar.gz", hash = "sha256:ac6fdbd4425c5fd17a9fe39daf4d4d075da6fdc80f653e5894cdc2fd98752bee"}, + {file = "uvicorn-0.30.6-py3-none-any.whl", hash = "sha256:65fd46fe3fda5bdc1b03b94eb634923ff18cd35b2f084813ea79d1f103f711b5"}, + {file = "uvicorn-0.30.6.tar.gz", hash = "sha256:4b15decdda1e72be08209e860a1e10e92439ad5b97cf44cc945fcbee66fc5788"}, ] [package.dependencies] From 843345df4ffc45f6808d47973e2c02da1d30b015 Mon Sep 17 00:00:00 2001 From: Yuvraj P Date: Sat, 24 Aug 2024 16:30:31 -0400 Subject: [PATCH 008/160] fix(mobile): Fix for incorrectly naming edited files and structure change (#11741) * Fix null name * Fix null name and Fix button * Remove extension correctly * Refactoring the code and formatting * formatting * Fix for the extension name --- mobile/lib/pages/editing/crop.page.dart | 12 ++- mobile/lib/pages/editing/edit.page.dart | 100 +++++++++--------- mobile/lib/routing/router.gr.dart | 35 +++--- .../asset_viewer/bottom_gallery_bar.dart | 9 +- 4 files changed, 88 insertions(+), 68 deletions(-) diff --git a/mobile/lib/pages/editing/crop.page.dart b/mobile/lib/pages/editing/crop.page.dart index 8a21cdf76908f..a3ac34dfa0a67 100644 --- a/mobile/lib/pages/editing/crop.page.dart +++ b/mobile/lib/pages/editing/crop.page.dart @@ -3,6 +3,7 @@ import 'package:crop_image/crop_image.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/utils/hooks/crop_controller_hook.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; import 'edit.page.dart'; import 'package:auto_route/auto_route.dart'; @@ -14,7 +15,8 @@ import 'package:auto_route/auto_route.dart'; @RoutePage() class CropImagePage extends HookWidget { final Image image; - const CropImagePage({super.key, required this.image}); + final Asset asset; + const CropImagePage({super.key, required this.image, required this.asset}); @override Widget build(BuildContext context) { @@ -34,7 +36,13 @@ class CropImagePage extends HookWidget { ), onPressed: () async { final croppedImage = await cropController.croppedImage(); - context.pushRoute(EditImageRoute(image: croppedImage)); + context.pushRoute( + EditImageRoute( + asset: asset, + image: croppedImage, + isEdited: true, + ), + ); }, ), ], diff --git a/mobile/lib/pages/editing/edit.page.dart b/mobile/lib/pages/editing/edit.page.dart index 22fb345e0f706..b9017e940bb41 100644 --- a/mobile/lib/pages/editing/edit.page.dart +++ b/mobile/lib/pages/editing/edit.page.dart @@ -12,6 +12,7 @@ import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:auto_route/auto_route.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:photo_manager/photo_manager.dart'; +import 'package:path/path.dart' as p; import 'package:immich_mobile/providers/album/album.provider.dart'; /// A stateless widget that provides functionality for editing an image. @@ -24,18 +25,16 @@ import 'package:immich_mobile/providers/album/album.provider.dart'; @immutable @RoutePage() class EditImagePage extends ConsumerWidget { - final Asset? asset; - final Image? image; + final Asset asset; + final Image image; + final bool isEdited; const EditImagePage({ super.key, - this.image, - this.asset, - }) : assert( - (image != null && asset == null) || (image == null && asset != null), - 'Must supply one of asset or image', - ); - + required this.asset, + required this.image, + required this.isEdited, + }); Future _imageToUint8List(Image image) async { final Completer completer = Completer(); image.image.resolve(const ImageConfiguration()).addListener( @@ -58,19 +57,34 @@ class EditImagePage extends ConsumerWidget { return completer.future; } + Future _saveEditedImage( + BuildContext context, + Asset asset, + Image image, + WidgetRef ref, + ) async { + try { + final Uint8List imageData = await _imageToUint8List(image); + await PhotoManager.editor.saveImage( + imageData, + title: "${p.withoutExtension(asset.fileName)}_edited.jpg", + ); + await ref.read(albumProvider.notifier).getDeviceAlbums(); + Navigator.of(context).popUntil((route) => route.isFirst); + } catch (e) { + ImmichToast.show( + durationInSecond: 6, + context: context, + msg: 'Error: $e', + gravity: ToastGravity.CENTER, + ); + } + } + @override Widget build(BuildContext context, WidgetRef ref) { - final ImageProvider provider = (asset != null) - ? ImmichImage.imageProvider(asset: asset!) - : (image != null) - ? image!.image - : throw Exception('Invalid image source type'); - - final Image imageWidget = (asset != null) - ? Image(image: ImmichImage.imageProvider(asset: asset!)) - : (image != null) - ? image! - : throw Exception('Invalid image source type'); + final Image imageWidget = + Image(image: ImmichImage.imageProvider(asset: asset)); return Scaffold( appBar: AppBar( @@ -85,44 +99,24 @@ class EditImagePage extends ConsumerWidget { Navigator.of(context).popUntil((route) => route.isFirst), ), actions: [ - if (image != null) - TextButton( - onPressed: () async { - try { - final Uint8List imageData = await _imageToUint8List(image!); - ImmichToast.show( - durationInSecond: 3, - context: context, - msg: 'Image Saved!', - gravity: ToastGravity.CENTER, - ); - - await PhotoManager.editor.saveImage( - imageData, - title: '${asset!.fileName}_edited.jpg', - ); - await ref.read(albumProvider.notifier).getDeviceAlbums(); - Navigator.of(context).popUntil((route) => route.isFirst); - } catch (e) { - ImmichToast.show( - durationInSecond: 6, - context: context, - msg: 'Error: ${e.toString()}', - gravity: ToastGravity.BOTTOM, - ); - } - }, - child: Text( - 'Save to gallery', - style: Theme.of(context).textTheme.displayMedium, + TextButton( + onPressed: isEdited + ? () => _saveEditedImage(context, asset, image, ref) + : null, + child: Text( + 'Save to gallery', + style: TextStyle( + color: + isEdited ? Theme.of(context).iconTheme.color : Colors.grey, ), ), + ), ], ), body: Column( children: [ Expanded( - child: Image(image: provider), + child: image, ), Container( height: 80, @@ -148,7 +142,9 @@ class EditImagePage extends ConsumerWidget { color: Theme.of(context).iconTheme.color, ), onPressed: () { - context.pushRoute(CropImageRoute(image: imageWidget)); + context.pushRoute( + CropImageRoute(asset: asset, image: imageWidget), + ); }, ), Text('Crop', style: Theme.of(context).textTheme.displayMedium), diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index a4259676c7a6d..90fc4cb0fe96c 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -613,12 +613,14 @@ class CropImageRoute extends PageRouteInfo { CropImageRoute({ Key? key, required Image image, + required Asset asset, List? children, }) : super( CropImageRoute.name, args: CropImageRouteArgs( key: key, image: image, + asset: asset, ), initialChildren: children, ); @@ -632,6 +634,7 @@ class CropImageRoute extends PageRouteInfo { return CropImagePage( key: args.key, image: args.image, + asset: args.asset, ); }, ); @@ -641,15 +644,18 @@ class CropImageRouteArgs { const CropImageRouteArgs({ this.key, required this.image, + required this.asset, }); final Key? key; final Image image; + final Asset asset; + @override String toString() { - return 'CropImageRouteArgs{key: $key, image: $image}'; + return 'CropImageRouteArgs{key: $key, image: $image, asset: $asset}'; } } @@ -658,15 +664,17 @@ class CropImageRouteArgs { class EditImageRoute extends PageRouteInfo { EditImageRoute({ Key? key, - Image? image, - Asset? asset, + required Asset asset, + required Image image, + required bool isEdited, List? children, }) : super( EditImageRoute.name, args: EditImageRouteArgs( key: key, - image: image, asset: asset, + image: image, + isEdited: isEdited, ), initialChildren: children, ); @@ -676,12 +684,12 @@ class EditImageRoute extends PageRouteInfo { static PageInfo page = PageInfo( name, builder: (data) { - final args = data.argsAs( - orElse: () => const EditImageRouteArgs()); + final args = data.argsAs(); return EditImagePage( key: args.key, - image: args.image, asset: args.asset, + image: args.image, + isEdited: args.isEdited, ); }, ); @@ -690,19 +698,22 @@ class EditImageRoute extends PageRouteInfo { class EditImageRouteArgs { const EditImageRouteArgs({ this.key, - this.image, - this.asset, + required this.asset, + required this.image, + required this.isEdited, }); final Key? key; - final Image? image; + final Asset asset; - final Asset? asset; + final Image image; + + final bool isEdited; @override String toString() { - return 'EditImageRouteArgs{key: $key, image: $image, asset: $asset}'; + return 'EditImageRouteArgs{key: $key, asset: $asset, image: $image, isEdited: $isEdited}'; } } diff --git a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart index 7d9e49bd29305..7e6136c256192 100644 --- a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart +++ b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart @@ -16,6 +16,7 @@ import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart' import 'package:immich_mobile/widgets/asset_viewer/video_controls.dart'; import 'package:immich_mobile/widgets/asset_grid/delete_dialog.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/widgets/common/immich_image.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; @@ -184,6 +185,7 @@ class BottomGalleryBar extends ConsumerWidget { } void handleEdit() async { + final image = Image(image: ImmichImage.imageProvider(asset: asset)); if (asset.isOffline) { ImmichToast.show( durationInSecond: 1, @@ -195,8 +197,11 @@ class BottomGalleryBar extends ConsumerWidget { } Navigator.of(context).push( MaterialPageRoute( - builder: (context) => - EditImagePage(asset: asset), // Send the Asset object + builder: (context) => EditImagePage( + asset: asset, + image: image, + isEdited: false, + ), ), ); } From 7a4fccb1b2a3f48286a2cd8babc49ef8fc644963 Mon Sep 17 00:00:00 2001 From: Snowknight26 Date: Sat, 24 Aug 2024 23:59:18 -0500 Subject: [PATCH 009/160] fix(web): show a clearer confirmation message when deleting an unnamed album (#11988) * fix(web): show a different confirmation message when deleting an unnamed album * Rename the function * Fix formatting --- .../lib/components/album-page/albums-list.svelte | 7 ++----- web/src/lib/i18n/en.json | 4 +++- web/src/lib/utils/album-utils.ts | 14 ++++++++++++++ .../[[photos=photos]]/[[assetId=id]]/+page.svelte | 6 ++---- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/web/src/lib/components/album-page/albums-list.svelte b/web/src/lib/components/album-page/albums-list.svelte index dcecd01d9eaa2..4355aca94d58b 100644 --- a/web/src/lib/components/album-page/albums-list.svelte +++ b/web/src/lib/components/album-page/albums-list.svelte @@ -17,7 +17,7 @@ import { handleError } from '$lib/utils/handle-error'; import { downloadAlbum } from '$lib/utils/asset-utils'; import { normalizeSearchString } from '$lib/utils/string-utils'; - import { getSelectedAlbumGroupOption, type AlbumGroup } from '$lib/utils/album-utils'; + import { getSelectedAlbumGroupOption, type AlbumGroup, confirmAlbumDelete } from '$lib/utils/album-utils'; import type { ContextMenuPosition } from '$lib/utils/context-menu'; import { user } from '$lib/stores/user.store'; import { @@ -31,7 +31,6 @@ } from '$lib/stores/preferences.store'; import { goto } from '$app/navigation'; import { AppRoute } from '$lib/constants'; - import { dialogController } from '$lib/components/shared-components/dialog/dialog'; import { t } from 'svelte-i18n'; export let ownedAlbums: AlbumResponseDto[] = []; @@ -302,9 +301,7 @@ return; } - const isConfirmed = await dialogController.show({ - prompt: $t('album_delete_confirmation', { values: { album: albumToDelete.albumName } }), - }); + const isConfirmed = await confirmAlbumDelete(albumToDelete); if (!isConfirmed) { return; diff --git a/web/src/lib/i18n/en.json b/web/src/lib/i18n/en.json index 3609b9c274cb5..43050fabdc2b3 100644 --- a/web/src/lib/i18n/en.json +++ b/web/src/lib/i18n/en.json @@ -335,7 +335,8 @@ "album_added": "Album added", "album_added_notification_setting_description": "Receive an email notification when you are added to a shared album", "album_cover_updated": "Album cover updated", - "album_delete_confirmation": "Are you sure you want to delete the album {album}?\nIf this album is shared, other users will not be able to access it anymore.", + "album_delete_confirmation": "Are you sure you want to delete the album {album}?", + "album_delete_confirmation_description": "If this album is shared, other users will not be able to access it anymore.", "album_info_updated": "Album info updated", "album_leave": "Leave album?", "album_leave_confirmation": "Are you sure you want to leave {album}?", @@ -1189,6 +1190,7 @@ "unlink_oauth": "Unlink OAuth", "unlinked_oauth_account": "Unlinked OAuth account", "unnamed_album": "Unnamed Album", + "unnamed_album_delete_confirmation": "Are you sure you want to delete this album?", "unnamed_share": "Unnamed Share", "unsaved_change": "Unsaved change", "unselect_all": "Unselect all", diff --git a/web/src/lib/utils/album-utils.ts b/web/src/lib/utils/album-utils.ts index aff76ef88e2aa..028aa721c744c 100644 --- a/web/src/lib/utils/album-utils.ts +++ b/web/src/lib/utils/album-utils.ts @@ -1,4 +1,5 @@ import { goto } from '$app/navigation'; +import { dialogController } from '$lib/components/shared-components/dialog/dialog'; import { AppRoute } from '$lib/constants'; import { AlbumFilter, @@ -199,3 +200,16 @@ export const collapseAllAlbumGroups = (groupIds: string[]) => { export const expandAllAlbumGroups = () => { collapseAllAlbumGroups([]); }; + +export const confirmAlbumDelete = async (album: AlbumResponseDto) => { + const $t = get(t); + const confirmation = + album.albumName.length > 0 + ? $t('album_delete_confirmation', { values: { album: album.albumName } }) + : $t('unnamed_album_delete_confirmation'); + + const description = $t('album_delete_confirmation_description'); + const prompt = `${confirmation} ${description}`; + + return dialogController.show({ prompt }); +}; diff --git a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte index ff5709df99f3c..1dfc494f5ec52 100644 --- a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -82,9 +82,9 @@ } from '@mdi/js'; import { fly } from 'svelte/transition'; import type { PageData } from './$types'; - import { dialogController } from '$lib/components/shared-components/dialog/dialog'; import { t } from 'svelte-i18n'; import { onDestroy } from 'svelte'; + import { confirmAlbumDelete } from '$lib/utils/album-utils'; export let data: PageData; @@ -365,9 +365,7 @@ }; const handleRemoveAlbum = async () => { - const isConfirmed = await dialogController.show({ - prompt: $t('album_delete_confirmation', { values: { album: album.albumName } }), - }); + const isConfirmed = await confirmAlbumDelete(album); if (!isConfirmed) { viewMode = ViewMode.VIEW; From b41af659972ce0c1b9aad14e32f76494d58614ab Mon Sep 17 00:00:00 2001 From: Christopher Makarem <23037854+x24git@users.noreply.github.com> Date: Sat, 24 Aug 2024 22:00:15 -0700 Subject: [PATCH 010/160] fix: align camera model drop down behavior with other drop downs on web and mobile (#11951) * fix(web): align search filter behavior to show all camera models * fix(mobile): align search filter behavior to clear camera model when make is set * (mobile) correctly clear the model controller * fix(mobile) re-add text controller to dropdown --------- Co-authored-by: Alex --- mobile/lib/widgets/search/search_filter/camera_picker.dart | 6 +++++- .../lib/widgets/search/search_filter/common/dropdown.dart | 1 + .../search-bar/search-camera-section.svelte | 5 ++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/mobile/lib/widgets/search/search_filter/camera_picker.dart b/mobile/lib/widgets/search/search_filter/camera_picker.dart index 2e5618c9e03bf..e2110c9c295f1 100644 --- a/mobile/lib/widgets/search/search_filter/camera_picker.dart +++ b/mobile/lib/widgets/search/search_filter/camera_picker.dart @@ -51,10 +51,14 @@ class CameraPicker extends HookConsumerWidget { controller: makeTextController, leadingIcon: const Icon(Icons.photo_camera_rounded), onSelected: (value) { + if (value.toString() == selectedMake.value) { + return; + } selectedMake.value = value.toString(); + modelTextController.value = TextEditingValue.empty; onSelect({ 'make': selectedMake.value, - 'model': selectedModel.value, + 'model': null, }); }, ); diff --git a/mobile/lib/widgets/search/search_filter/common/dropdown.dart b/mobile/lib/widgets/search/search_filter/common/dropdown.dart index 230d7dd4daa5d..dd8785459f7fb 100644 --- a/mobile/lib/widgets/search/search_filter/common/dropdown.dart +++ b/mobile/lib/widgets/search/search_filter/common/dropdown.dart @@ -29,6 +29,7 @@ class SearchDropdown extends StatelessWidget { return LayoutBuilder( builder: (context, constraints) { return DropdownMenu( + controller: controller, leadingIcon: leadingIcon, width: constraints.maxWidth, dropdownMenuEntries: dropdownMenuEntries, diff --git a/web/src/lib/components/shared-components/search-bar/search-camera-section.svelte b/web/src/lib/components/shared-components/search-bar/search-camera-section.svelte index 839c17eccecec..f1cd0c85964cf 100644 --- a/web/src/lib/components/shared-components/search-bar/search-camera-section.svelte +++ b/web/src/lib/components/shared-components/search-bar/search-camera-section.svelte @@ -18,13 +18,12 @@ $: makeFilter = filters.make; $: modelFilter = filters.model; - $: handlePromiseError(updateMakes(modelFilter)); + $: handlePromiseError(updateMakes()); $: handlePromiseError(updateModels(makeFilter)); - async function updateMakes(model?: string) { + async function updateMakes() { const results: Array = await getSearchSuggestions({ $type: SearchSuggestionType.CameraMake, - model, includeNull: true, }); From e457d8d62eb15cdb75fa9982744fbc1734767fc0 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 25 Aug 2024 00:09:37 -0500 Subject: [PATCH 011/160] chore(mobile): patch download > includeEmbeddedVideos user preferences (#11910) * chore(mobile): patch download > includeEmbeddedVideos user preferences * correct patch --- mobile/lib/providers/authentication.provider.dart | 3 +++ mobile/lib/utils/openapi_patching.dart | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/mobile/lib/providers/authentication.provider.dart b/mobile/lib/providers/authentication.provider.dart index 5846bb78cc3e8..5d3ae5bc22677 100644 --- a/mobile/lib/providers/authentication.provider.dart +++ b/mobile/lib/providers/authentication.provider.dart @@ -190,6 +190,9 @@ class AuthenticationNotifier extends StateNotifier { error, stackTrace, ); + debugPrint( + "Error getting user information from the server [CATCH ALL] $error $stackTrace", + ); } // If the user information is successfully retrieved, update the store diff --git a/mobile/lib/utils/openapi_patching.dart b/mobile/lib/utils/openapi_patching.dart index 7b27f59aee8c3..7a2f7396eb3ab 100644 --- a/mobile/lib/utils/openapi_patching.dart +++ b/mobile/lib/utils/openapi_patching.dart @@ -7,6 +7,11 @@ dynamic upgradeDto(dynamic value, String targetType) { if (value['rating'] == null) { value['rating'] = RatingResponse().toJson(); } + + if (value['download']['includeEmbeddedVideos'] == null) { + value['download']['includeEmbeddedVideos'] = false; + } } + break; } } From 868aedd2120da5899598b85fe9eea53e5e6deae7 Mon Sep 17 00:00:00 2001 From: Thomas Clarke <43609027+Tonux599@users.noreply.github.com> Date: Sun, 25 Aug 2024 18:54:12 +0100 Subject: [PATCH 012/160] fix: docs link to breaking changes (#12027) Fix link to breaking changes --- docs/docs/install/docker-compose.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/install/docker-compose.mdx b/docs/docs/install/docker-compose.mdx index 0b69bd8639838..9ef63523a05ec 100644 --- a/docs/docs/install/docker-compose.mdx +++ b/docs/docs/install/docker-compose.mdx @@ -109,7 +109,7 @@ Immich is currently under heavy development, which means you can expect [breakin [compose-file]: https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml [env-file]: https://github.com/immich-app/immich/releases/latest/download/example.env [watchtower]: https://containrrr.dev/watchtower/ -[breaking]: https://github.com/immich-app/immich/discussions?discussions_q=label%3Abreaking-change+sort%3Adate_created +[breaking]: https://github.com/immich-app/immich/discussions?discussions_q=label%3Achangelog%3Abreaking-change+sort%3Adate_created [container-auth]: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#authenticating-to-the-container-registry [releases]: https://github.com/immich-app/immich/releases [docker-repo]: https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository From b653a20d1562c389a3e5bac99306a104ce4d6c1c Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 25 Aug 2024 16:53:14 -0500 Subject: [PATCH 013/160] fix(web): sort folders (#12038) chore(web): sort folders --- web/src/lib/utils/tree-utils.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/src/lib/utils/tree-utils.ts b/web/src/lib/utils/tree-utils.ts index cc17784eb64f3..13fb6c1605c5b 100644 --- a/web/src/lib/utils/tree-utils.ts +++ b/web/src/lib/utils/tree-utils.ts @@ -6,6 +6,9 @@ export const normalizeTreePath = (path: string) => path.replace(/^\//, '').repla export function buildTree(paths: string[]) { const root: RecursiveObject = {}; + + paths.sort(); + for (const path of paths) { const parts = path.split('/'); let current = root; From b2dd5a3152982d173e5200438e1f12341b932453 Mon Sep 17 00:00:00 2001 From: Min Idzelis Date: Sun, 25 Aug 2024 18:34:08 -0400 Subject: [PATCH 014/160] feat: loading screen, initSDK on bootstrap, fix FOUC for theme (#10350) * feat: loading screen, initSDK on bootstrap, fix FOUC for theme * pulsate immich logo, don't set localstorage * Make it spin * Rework error handling a bit * Cleanup * fix test * rename, memoize --------- Co-authored-by: Alex Tran --- web/src/app.html | 156 +++++++++++++++--- .../admin-page/settings/admin-settings.svelte | 4 +- web/src/lib/components/error.svelte | 105 ++++++++++++ .../forms/admin-registration-form.svelte | 2 + .../lib/components/forms/login-form.svelte | 6 +- web/src/lib/stores/server-config.store.ts | 2 +- web/src/lib/utils.ts | 2 +- web/src/lib/utils/server.ts | 21 +++ web/src/routes/+error.svelte | 104 +----------- web/src/routes/+layout.svelte | 26 +-- web/src/routes/+layout.ts | 16 +- web/src/routes/+page.ts | 36 ++-- web/src/routes/auth/login/+page.ts | 11 +- web/src/routes/auth/onboarding/+page.ts | 2 - web/src/routes/auth/register/+page.ts | 8 +- 15 files changed, 328 insertions(+), 173 deletions(-) create mode 100644 web/src/lib/components/error.svelte create mode 100644 web/src/lib/utils/server.ts diff --git a/web/src/app.html b/web/src/app.html index d1db02f493483..aa8450e9be4ba 100644 --- a/web/src/app.html +++ b/web/src/app.html @@ -1,5 +1,5 @@ - + @@ -14,35 +14,96 @@ %sveltekit.head% + + +
+
+
+ + + +
+
+ +
+
+
+
+
+

+ 🚨 {$t('error_title')} +

+
+ handleCopy()} + /> +
+
+ +
+ +
+
+

{error?.message} ({error?.code})

+ {#if error?.stack} + +
{error?.stack || 'No stack'}
+ {/if} +
+
+ +
+ + +
+
+
+
+
diff --git a/web/src/lib/components/forms/admin-registration-form.svelte b/web/src/lib/components/forms/admin-registration-form.svelte index c66b09040fb77..d49ab554397c1 100644 --- a/web/src/lib/components/forms/admin-registration-form.svelte +++ b/web/src/lib/components/forms/admin-registration-form.svelte @@ -6,6 +6,7 @@ import Button from '../elements/buttons/button.svelte'; import PasswordField from '../shared-components/password-field.svelte'; import { t } from 'svelte-i18n'; + import { retrieveServerConfig } from '$lib/stores/server-config.store'; let email = ''; let password = ''; @@ -31,6 +32,7 @@ try { await signUpAdmin({ signUpDto: { email, password, name } }); + await retrieveServerConfig(); await goto(AppRoute.AUTH_LOGIN); } catch (error) { handleError(error, $t('errors.unable_to_create_admin_account')); diff --git a/web/src/lib/components/forms/login-form.svelte b/web/src/lib/components/forms/login-form.svelte index 828927a13abab..b1af7a01f4b1a 100644 --- a/web/src/lib/components/forms/login-form.svelte +++ b/web/src/lib/components/forms/login-form.svelte @@ -5,7 +5,7 @@ import { featureFlags, serverConfig } from '$lib/stores/server-config.store'; import { oauth } from '$lib/utils'; import { getServerErrorMessage, handleError } from '$lib/utils/handle-error'; - import { getServerConfig, login } from '@immich/sdk'; + import { login } from '@immich/sdk'; import { onMount } from 'svelte'; import { fade } from 'svelte/transition'; import Button from '../elements/buttons/button.svelte'; @@ -58,11 +58,9 @@ try { errorMessage = ''; loading = true; - const user = await login({ loginCredentialDto: { email, password } }); - const serverConfig = await getServerConfig(); - if (user.isAdmin && !serverConfig.isOnboarded) { + if (user.isAdmin && !$serverConfig.isOnboarded) { await onOnboarding(); return; } diff --git a/web/src/lib/stores/server-config.store.ts b/web/src/lib/stores/server-config.store.ts index 40670df25fb6f..1d3c4bc00eb26 100644 --- a/web/src/lib/stores/server-config.store.ts +++ b/web/src/lib/stores/server-config.store.ts @@ -33,7 +33,7 @@ export const serverConfig = writable({ externalDomain: '', }); -export const loadConfig = async () => { +export const retrieveServerConfig = async () => { const [flags, config] = await Promise.all([getServerFeatures(), getServerConfig()]); featureFlags.update(() => ({ ...flags, loaded: true })); diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts index b805cf8132a00..6c3add70ce562 100644 --- a/web/src/lib/utils.ts +++ b/web/src/lib/utils.ts @@ -33,7 +33,7 @@ interface DownloadRequestOptions { onDownloadProgress?: (event: ProgressEvent) => void; } -export const initApp = async () => { +export const initLanguage = async () => { const preferenceLang = get(lang); for (const { code, loader } of langs) { register(code, loader); diff --git a/web/src/lib/utils/server.ts b/web/src/lib/utils/server.ts new file mode 100644 index 0000000000000..d2c5ab185119c --- /dev/null +++ b/web/src/lib/utils/server.ts @@ -0,0 +1,21 @@ +import { retrieveServerConfig } from '$lib/stores/server-config.store'; +import { initLanguage } from '$lib/utils'; +import { defaults } from '@immich/sdk'; +import { memoize } from 'lodash-es'; + +type fetchType = typeof fetch; + +export function initSDK(fetch: fetchType) { + // set event.fetch on the fetch-client used by @immich/sdk + // https://kit.svelte.dev/docs/load#making-fetch-requests + // https://github.com/oazapfts/oazapfts/blob/main/README.md#fetch-options + defaults.fetch = fetch; +} + +async function _init(fetch: fetchType) { + initSDK(fetch); + await initLanguage(); + await retrieveServerConfig(); +} + +export const init = memoize(_init, () => 'singlevalue'); diff --git a/web/src/routes/+error.svelte b/web/src/routes/+error.svelte index e82605d83ef9a..23e8fd3ff191e 100644 --- a/web/src/routes/+error.svelte +++ b/web/src/routes/+error.svelte @@ -1,106 +1,6 @@ -
-
-
- - - -
-
- -
-
-
-
-
-

- 🚨 {$t('error_title')} -

-
- handleCopy()} - /> -
-
- -
- -
-
-

{$page.error?.message} ({$page.error?.code})

- {#if $page.error?.stack} - -
{$page.error?.stack || 'No stack'}
- {/if} -
-
- -
- - -
-
-
-
-
+ diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index 1ad9066c4e1db..b7335dea595d8 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -10,16 +10,18 @@ import VersionAnnouncementBox from '$lib/components/shared-components/version-announcement-box.svelte'; import { Theme } from '$lib/constants'; import { colorTheme, handleToggleTheme, type ThemeSetting } from '$lib/stores/preferences.store'; - import { loadConfig, serverConfig } from '$lib/stores/server-config.store'; + + import { serverConfig } from '$lib/stores/server-config.store'; + import { user } from '$lib/stores/user.store'; import { closeWebsocketConnection, openWebsocketConnection } from '$lib/stores/websocket'; import { copyToClipboard, setKey } from '$lib/utils'; - import { handleError } from '$lib/utils/handle-error'; import { onDestroy, onMount } from 'svelte'; import '../app.css'; import { isAssetViewerRoute, isSharedLinkRoute } from '$lib/utils/navigation'; import DialogWrapper from '$lib/components/shared-components/dialog/dialog-wrapper.svelte'; import { t } from 'svelte-i18n'; + import Error from '$lib/components/error.svelte'; import { shortcut } from '$lib/actions/shortcut'; let showNavigationLoadingBar = false; @@ -33,8 +35,7 @@ const changeTheme = (theme: ThemeSetting) => { if (theme.system) { - theme.value = - window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? Theme.DARK : Theme.LIGHT; + theme.value = window.matchMedia('(prefers-color-scheme: dark)').matches ? Theme.DARK : Theme.LIGHT; } if (theme.value === Theme.LIGHT) { @@ -55,6 +56,8 @@ }; onMount(() => { + const element = document.querySelector('#stencil'); + element?.remove(); // if the browser theme changes, changes the Immich theme too window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', handleChangeTheme); }); @@ -77,14 +80,6 @@ afterNavigate(() => { showNavigationLoadingBar = false; }); - - onMount(async () => { - try { - await loadConfig(); - } catch (error) { - handleError(error, $t('errors.unable_to_connect_to_server')); - } - }); @@ -134,7 +129,12 @@ onShortcut: () => copyToClipboard(getMyImmichLink().toString()), }} /> - + +{#if $page.data.error} + +{:else} + +{/if} {#if showNavigationLoadingBar} diff --git a/web/src/routes/+layout.ts b/web/src/routes/+layout.ts index e8f665e0e44af..b5edece09e58a 100644 --- a/web/src/routes/+layout.ts +++ b/web/src/routes/+layout.ts @@ -1,19 +1,19 @@ -import { initApp } from '$lib/utils'; -import { defaults } from '@immich/sdk'; +import { init } from '$lib/utils/server'; import type { LayoutLoad } from './$types'; export const ssr = false; export const csr = true; export const load = (async ({ fetch }) => { - // set event.fetch on the fetch-client used by @immich/sdk - // https://kit.svelte.dev/docs/load#making-fetch-requests - // https://github.com/oazapfts/oazapfts/blob/main/README.md#fetch-options - defaults.fetch = fetch; - - await initApp(); + let error; + try { + await init(fetch); + } catch (initError) { + error = initError; + } return { + error, meta: { title: 'Immich', }, diff --git a/web/src/routes/+page.ts b/web/src/routes/+page.ts index f9897336af75c..bcc854cc3cd4d 100644 --- a/web/src/routes/+page.ts +++ b/web/src/routes/+page.ts @@ -1,26 +1,38 @@ import { AppRoute } from '$lib/constants'; +import { serverConfig } from '$lib/stores/server-config.store'; import { getFormatter } from '$lib/utils/i18n'; -import { getServerConfig } from '@immich/sdk'; +import { init } from '$lib/utils/server'; + import { redirect } from '@sveltejs/kit'; +import { get } from 'svelte/store'; import { loadUser } from '../lib/utils/auth'; import type { PageLoad } from './$types'; export const ssr = false; export const csr = true; -export const load = (async () => { - const authenticated = await loadUser(); - if (authenticated) { - redirect(302, AppRoute.PHOTOS); - } +export const load = (async ({ fetch }) => { + let $t = (arg: string) => arg; + try { + await init(fetch); + const authenticated = await loadUser(); + if (authenticated) { + redirect(302, AppRoute.PHOTOS); + } - const { isInitialized } = await getServerConfig(); - if (isInitialized) { - // Redirect to login page if there exists an admin account (i.e. server is initialized) - redirect(302, AppRoute.AUTH_LOGIN); - } + const { isInitialized } = get(serverConfig); + if (isInitialized) { + // Redirect to login page if there exists an admin account (i.e. server is initialized) + redirect(302, AppRoute.AUTH_LOGIN); + } - const $t = await getFormatter(); + $t = await getFormatter(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (redirectError: any) { + if (redirectError?.status === 302) { + throw redirectError; + } + } return { meta: { diff --git a/web/src/routes/auth/login/+page.ts b/web/src/routes/auth/login/+page.ts index 427287c8eafb9..847992ab20098 100644 --- a/web/src/routes/auth/login/+page.ts +++ b/web/src/routes/auth/login/+page.ts @@ -1,12 +1,15 @@ import { AppRoute } from '$lib/constants'; +import { serverConfig } from '$lib/stores/server-config.store'; import { getFormatter } from '$lib/utils/i18n'; -import { defaults, getServerConfig } from '@immich/sdk'; + import { redirect } from '@sveltejs/kit'; +import { get } from 'svelte/store'; import type { PageLoad } from './$types'; -export const load = (async ({ fetch }) => { - defaults.fetch = fetch; - const { isInitialized } = await getServerConfig(); +export const load = (async ({ parent }) => { + await parent(); + const { isInitialized } = get(serverConfig); + if (!isInitialized) { // Admin not registered redirect(302, AppRoute.AUTH_REGISTER); diff --git a/web/src/routes/auth/onboarding/+page.ts b/web/src/routes/auth/onboarding/+page.ts index 7bd307a3ee9ef..db16c8e51419e 100644 --- a/web/src/routes/auth/onboarding/+page.ts +++ b/web/src/routes/auth/onboarding/+page.ts @@ -1,11 +1,9 @@ -import { loadConfig } from '$lib/stores/server-config.store'; import { authenticate } from '$lib/utils/auth'; import { getFormatter } from '$lib/utils/i18n'; import type { PageLoad } from './$types'; export const load = (async () => { await authenticate({ admin: true }); - await loadConfig(); const $t = await getFormatter(); diff --git a/web/src/routes/auth/register/+page.ts b/web/src/routes/auth/register/+page.ts index 00574043c1381..88b56caa47b6a 100644 --- a/web/src/routes/auth/register/+page.ts +++ b/web/src/routes/auth/register/+page.ts @@ -1,11 +1,13 @@ import { AppRoute } from '$lib/constants'; +import { serverConfig } from '$lib/stores/server-config.store'; import { getFormatter } from '$lib/utils/i18n'; -import { getServerConfig } from '@immich/sdk'; import { redirect } from '@sveltejs/kit'; +import { get } from 'svelte/store'; import type { PageLoad } from './$types'; -export const load = (async () => { - const { isInitialized } = await getServerConfig(); +export const load = (async ({ parent }) => { + await parent(); + const { isInitialized } = get(serverConfig); if (isInitialized) { // Admin has been registered, redirect to login redirect(302, AppRoute.AUTH_LOGIN); From 96056208fc823e81051482a8189bca8d5aed97fd Mon Sep 17 00:00:00 2001 From: Ben <45583362+ben-basten@users.noreply.github.com> Date: Sun, 25 Aug 2024 19:50:54 -0400 Subject: [PATCH 015/160] fix(web): announce current theme to screen reader users (#12039) --- .../components/shared-components/theme-button.svelte | 10 +++++++++- web/src/lib/i18n/en.json | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/web/src/lib/components/shared-components/theme-button.svelte b/web/src/lib/components/shared-components/theme-button.svelte index 7fc823f8ed995..8376b7235990a 100644 --- a/web/src/lib/components/shared-components/theme-button.svelte +++ b/web/src/lib/components/shared-components/theme-button.svelte @@ -7,8 +7,16 @@ $: icon = $colorTheme.value === Theme.LIGHT ? moonPath : sunPath; $: viewBox = $colorTheme.value === Theme.LIGHT ? moonViewBox : sunViewBox; + $: isDark = $colorTheme.value === Theme.DARK; {#if !$colorTheme.system} - + {/if} diff --git a/web/src/lib/i18n/en.json b/web/src/lib/i18n/en.json index 43050fabdc2b3..d8d0c3f8c87b9 100644 --- a/web/src/lib/i18n/en.json +++ b/web/src/lib/i18n/en.json @@ -1171,7 +1171,7 @@ "to_login": "Login", "to_trash": "Trash", "toggle_settings": "Toggle settings", - "toggle_theme": "Toggle theme", + "toggle_theme": "Toggle dark theme", "total_usage": "Total usage", "trash": "Trash", "trash_all": "Trash All", From 4f02412493a5758e32ee3830a35fc112de1c32dd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2024 22:50:51 -0400 Subject: [PATCH 016/160] chore(deps): update dependency node to v20.17.0 (#12040) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- cli/.nvmrc | 2 +- cli/package.json | 2 +- docs/.nvmrc | 2 +- docs/package.json | 2 +- e2e/.nvmrc | 2 +- e2e/package.json | 2 +- open-api/typescript-sdk/.nvmrc | 2 +- open-api/typescript-sdk/package.json | 2 +- server/.nvmrc | 2 +- server/package.json | 2 +- web/.nvmrc | 2 +- web/package.json | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cli/.nvmrc b/cli/.nvmrc index 8ce7030825b5e..3516580bbbc04 100644 --- a/cli/.nvmrc +++ b/cli/.nvmrc @@ -1 +1 @@ -20.16.0 +20.17.0 diff --git a/cli/package.json b/cli/package.json index ddd67308873d3..cce73afa37d1b 100644 --- a/cli/package.json +++ b/cli/package.json @@ -67,6 +67,6 @@ "lodash-es": "^4.17.21" }, "volta": { - "node": "20.16.0" + "node": "20.17.0" } } diff --git a/docs/.nvmrc b/docs/.nvmrc index 8ce7030825b5e..3516580bbbc04 100644 --- a/docs/.nvmrc +++ b/docs/.nvmrc @@ -1 +1 @@ -20.16.0 +20.17.0 diff --git a/docs/package.json b/docs/package.json index e32fe094996a6..cdcdf53446884 100644 --- a/docs/package.json +++ b/docs/package.json @@ -56,6 +56,6 @@ "node": ">=20" }, "volta": { - "node": "20.16.0" + "node": "20.17.0" } } diff --git a/e2e/.nvmrc b/e2e/.nvmrc index 8ce7030825b5e..3516580bbbc04 100644 --- a/e2e/.nvmrc +++ b/e2e/.nvmrc @@ -1 +1 @@ -20.16.0 +20.17.0 diff --git a/e2e/package.json b/e2e/package.json index 1c19526e83dc2..be072e44f3e23 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -53,6 +53,6 @@ "vitest": "^2.0.5" }, "volta": { - "node": "20.16.0" + "node": "20.17.0" } } diff --git a/open-api/typescript-sdk/.nvmrc b/open-api/typescript-sdk/.nvmrc index 8ce7030825b5e..3516580bbbc04 100644 --- a/open-api/typescript-sdk/.nvmrc +++ b/open-api/typescript-sdk/.nvmrc @@ -1 +1 @@ -20.16.0 +20.17.0 diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index 6f54670789dc7..90fa525fa01cd 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -28,6 +28,6 @@ "directory": "open-api/typescript-sdk" }, "volta": { - "node": "20.16.0" + "node": "20.17.0" } } diff --git a/server/.nvmrc b/server/.nvmrc index 8ce7030825b5e..3516580bbbc04 100644 --- a/server/.nvmrc +++ b/server/.nvmrc @@ -1 +1 @@ -20.16.0 +20.17.0 diff --git a/server/package.json b/server/package.json index d918582a58831..8a9149bf845b3 100644 --- a/server/package.json +++ b/server/package.json @@ -137,6 +137,6 @@ "vitest": "^2.0.5" }, "volta": { - "node": "20.16.0" + "node": "20.17.0" } } diff --git a/web/.nvmrc b/web/.nvmrc index 8ce7030825b5e..3516580bbbc04 100644 --- a/web/.nvmrc +++ b/web/.nvmrc @@ -1 +1 @@ -20.16.0 +20.17.0 diff --git a/web/package.json b/web/package.json index 7d7751b67f2ff..7163b04788c73 100644 --- a/web/package.json +++ b/web/package.json @@ -86,6 +86,6 @@ "thumbhash": "^0.1.1" }, "volta": { - "node": "20.16.0" + "node": "20.17.0" } } From fe672d4f35d1899ee09d58f773cc1f2b46af5d36 Mon Sep 17 00:00:00 2001 From: Anil Madhavapeddy Date: Mon, 26 Aug 2024 13:16:24 +0100 Subject: [PATCH 017/160] feat(format): nrw format (#12048) --- server/src/utils/mime-types.spec.ts | 1 + server/src/utils/mime-types.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/server/src/utils/mime-types.spec.ts b/server/src/utils/mime-types.spec.ts index 996ea6c744569..50fe760a04b82 100644 --- a/server/src/utils/mime-types.spec.ts +++ b/server/src/utils/mime-types.spec.ts @@ -30,6 +30,7 @@ describe('mimeTypes', () => { { mimetype: 'image/kdc', extension: '.kdc' }, { mimetype: 'image/mrw', extension: '.mrw' }, { mimetype: 'image/nef', extension: '.nef' }, + { mimetype: 'image/nrw', extension: '.nrw' }, { mimetype: 'image/orf', extension: '.orf' }, { mimetype: 'image/ori', extension: '.ori' }, { mimetype: 'image/pef', extension: '.pef' }, diff --git a/server/src/utils/mime-types.ts b/server/src/utils/mime-types.ts index 6b59d2cd41dc5..cbf6e5b489069 100644 --- a/server/src/utils/mime-types.ts +++ b/server/src/utils/mime-types.ts @@ -19,6 +19,7 @@ const raw: Record = { '.kdc': ['image/kdc', 'image/x-kodak-kdc'], '.mrw': ['image/mrw', 'image/x-minolta-mrw'], '.nef': ['image/nef', 'image/x-nikon-nef'], + '.nrw': ['image/nrw', 'image/x-nikon-nrw'], '.orf': ['image/orf', 'image/x-olympus-orf'], '.ori': ['image/ori', 'image/x-olympus-ori'], '.pef': ['image/pef', 'image/x-pentax-pef'], From 129e5eae66974055cbdaed90f694019939dca746 Mon Sep 17 00:00:00 2001 From: Carsten Otto Date: Mon, 26 Aug 2024 17:33:01 +0200 Subject: [PATCH 018/160] fix: do not code format repro steps in issue template (#12054) issue template: do not use "bash" to render a list of text items --- .github/ISSUE_TEMPLATE/bug_report.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 12ffc89ea2b5a..346c6e60f2e21 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -83,7 +83,6 @@ body: 2. 3. ... - render: bash validations: required: true From 3ac42edc74c0cc713a99505ecf907d261870eddc Mon Sep 17 00:00:00 2001 From: Matt Tyree Date: Mon, 26 Aug 2024 12:06:21 -0400 Subject: [PATCH 019/160] docs: add Immich Kiosk and Immich Power Tools to Community Projects (#12055) Add Immich Kiosk and Immich Power Tools Added Immich Kiosk and Immich Power Tools to Community Projects --- docs/src/components/community-projects.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/src/components/community-projects.tsx b/docs/src/components/community-projects.tsx index 0fd4cc25c1f01..0f9b2b2413a77 100644 --- a/docs/src/components/community-projects.tsx +++ b/docs/src/components/community-projects.tsx @@ -68,6 +68,16 @@ const projects: CommunityProjectProps[] = [ description: 'Snap package for easy install and zero-care auto updates of Immich. Self-hosted photo management.', url: 'https://immich-distribution.nsg.cc', }, + { + title: 'Immich Kiosk', + description: 'Lightweight slideshow to run on kiosk devices and browsers.', + url: 'https://github.com/damongolding/immich-kiosk', + }, + { + title: 'Immich Power Tools', + description: 'An unofficial immich client providing tools to speed up your workflows in Immich to organize your people and albums.', + url: 'https://github.com/varun-raj/immich-power-tools', + }, ]; function CommunityProject({ title, description, url }: CommunityProjectProps): JSX.Element { From edf47dbbd008811ad0bf46e87286c1cadaf669bf Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 26 Aug 2024 11:26:23 -0500 Subject: [PATCH 020/160] feat(web): restore scroll position on navigating back to search page (#12042) * feat(web): restore scroll position on navigating back to search page * set 0 for scroll X * lint * simplify --- .../[[assetId=id]]/+page.svelte | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte index cd4def17655a3..da85eb49c8267 100644 --- a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -40,6 +40,7 @@ import AlbumCardGroup from '$lib/components/album-page/album-card-group.svelte'; import { isAlbumsRoute, isPeopleRoute } from '$lib/utils/navigation'; import { t } from 'svelte-i18n'; + import { afterUpdate, tick } from 'svelte'; const MAX_ASSET_COUNT = 5000; let { isViewing: showAssetViewer } = assetViewingStore; @@ -54,6 +55,8 @@ let searchResultAlbums: AlbumResponseDto[] = []; let searchResultAssets: AssetResponseDto[] = []; let isLoading = true; + let scrollY = 0; + let scrollYHistory = 0; const onEscape = () => { if ($showAssetViewer) { @@ -70,6 +73,13 @@ $preventRaceConditionSearchBar = false; }; + // save and restore scroll position + afterUpdate(() => { + if (scrollY) { + scrollYHistory = scrollY; + } + }); + afterNavigate(({ from }) => { // Prevent setting previousRoute to the current page. if (from?.url && from.route.id !== $page.route.id) { @@ -84,6 +94,14 @@ if (isAlbumsRoute(route)) { previousRoute = AppRoute.EXPLORE; } + + tick() + .then(() => { + window.scrollTo(0, scrollYHistory); + }) + .catch(() => { + // do nothing + }); }); let selectedAssets: Set = new Set(); @@ -203,7 +221,7 @@ } - +
{#if isMultiSelectionMode} From f4371578f5b7be2ff06b3c1c6d651514d1d83a18 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 26 Aug 2024 12:20:50 -0500 Subject: [PATCH 021/160] fix(web): show supporter badge for account less than 14 days (#12058) --- .../side-bar/purchase-info.svelte | 78 +++++++++---------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/web/src/lib/components/shared-components/side-bar/purchase-info.svelte b/web/src/lib/components/shared-components/side-bar/purchase-info.svelte index 6f40dc4923885..a284c7efc1b95 100644 --- a/web/src/lib/components/shared-components/side-bar/purchase-info.svelte +++ b/web/src/lib/components/shared-components/side-bar/purchase-info.svelte @@ -76,48 +76,46 @@ (isOpen = false)} /> {/if} -{#if getAccountAge() > 14} - -{/if} + +
+ +
+
+ + {/if} +
{#if showMessage} From 6b6d2a6621ff68e342a3879af274f18b860ee7ce Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 26 Aug 2024 13:21:19 -0500 Subject: [PATCH 022/160] feat(mobile): preserve mobile album info on upload (#11965) * curating assets with albums to upload * sorting for background backup * background upload works * transform fields string array to javascript array * send json array * generate sql * refactor upload callback * remove albums info from upload payload * mechanism to create album on album selection * album creation * Sync to upload album * Remove unused service * unify name changes * Add mechanism to sync uploaded assets to albums * Put add to album operation after updating the UI state * clean up * background album sync * add to album in background context * remove add to album in callback * refactor * refactor * refactor * fix: make sure all selected albums are selected for building upload candidate * clean up * add manual sync button * lint * revert server changes * pr feedback * revert time filtering * const * sync album on manual upload * linting * pr feedback and proper time filtering * wording --- mobile/assets/i18n/en-US.json | 6 +- mobile/lib/entities/store.entity.dart | 2 + .../models/backup/backup_candidate.model.dart | 19 + .../lib/models/backup/backup_state.model.dart | 6 +- .../backup/success_upload_asset.model.dart | 42 +++ .../backup/backup_album_selection.page.dart | 70 ++-- .../lib/providers/album/album.provider.dart | 18 + .../lib/providers/backup/backup.provider.dart | 85 +++-- .../backup/manual_upload.provider.dart | 44 ++- mobile/lib/services/album.service.dart | 44 ++- mobile/lib/services/app_settings.service.dart | 1 + mobile/lib/services/asset.service.dart | 71 ++++ mobile/lib/services/background.service.dart | 64 +++- mobile/lib/services/backup.service.dart | 324 ++++++++++++------ .../lib/widgets/backup/album_info_card.dart | 14 +- .../widgets/backup/album_info_list_tile.dart | 11 +- .../backup_settings/backup_settings.dart | 34 ++ .../settings/settings_button_list_tile.dart | 5 +- .../settings/settings_switch_list_tile.dart | 30 +- 19 files changed, 657 insertions(+), 233 deletions(-) create mode 100644 mobile/lib/models/backup/backup_candidate.model.dart create mode 100644 mobile/lib/models/backup/success_upload_asset.model.dart diff --git a/mobile/assets/i18n/en-US.json b/mobile/assets/i18n/en-US.json index decb0a72e1eda..c092b79bd1d6e 100644 --- a/mobile/assets/i18n/en-US.json +++ b/mobile/assets/i18n/en-US.json @@ -573,5 +573,9 @@ "version_announcement_overlay_text_2": "please take your time to visit the ", "version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.", "version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89", - "viewer_unstack": "Un-Stack" + "viewer_unstack": "Un-Stack", + "sync_albums": "Sync albums", + "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", + "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", + "sync": "Sync" } diff --git a/mobile/lib/entities/store.entity.dart b/mobile/lib/entities/store.entity.dart index a84f9800019c3..1dda2b9a12a03 100644 --- a/mobile/lib/entities/store.entity.dart +++ b/mobile/lib/entities/store.entity.dart @@ -234,6 +234,8 @@ enum StoreKey { primaryColor(128, type: String), dynamicTheme(129, type: bool), colorfulInterface(130, type: bool), + + syncAlbums(131, type: bool), ; const StoreKey( diff --git a/mobile/lib/models/backup/backup_candidate.model.dart b/mobile/lib/models/backup/backup_candidate.model.dart new file mode 100644 index 0000000000000..5ef15167455df --- /dev/null +++ b/mobile/lib/models/backup/backup_candidate.model.dart @@ -0,0 +1,19 @@ +import 'package:photo_manager/photo_manager.dart'; + +class BackupCandidate { + BackupCandidate({required this.asset, required this.albumNames}); + + AssetEntity asset; + List albumNames; + + @override + int get hashCode => asset.hashCode; + + @override + bool operator ==(Object other) { + if (other is! BackupCandidate) { + return false; + } + return asset == other.asset; + } +} diff --git a/mobile/lib/models/backup/backup_state.model.dart b/mobile/lib/models/backup/backup_state.model.dart index bb693a5b75f7a..d829f411fc355 100644 --- a/mobile/lib/models/backup/backup_state.model.dart +++ b/mobile/lib/models/backup/backup_state.model.dart @@ -2,7 +2,7 @@ import 'package:cancellation_token_http/http.dart'; import 'package:collection/collection.dart'; -import 'package:photo_manager/photo_manager.dart'; +import 'package:immich_mobile/models/backup/backup_candidate.model.dart'; import 'package:immich_mobile/models/backup/available_album.model.dart'; import 'package:immich_mobile/models/backup/current_upload_asset.model.dart'; @@ -41,7 +41,7 @@ class BackUpState { final Set excludedBackupAlbums; /// Assets that are not overlapping in selected backup albums and excluded backup albums - final Set allUniqueAssets; + final Set allUniqueAssets; /// All assets from the selected albums that have been backup final Set selectedAlbumsBackupAssetsIds; @@ -94,7 +94,7 @@ class BackUpState { List? availableAlbums, Set? selectedBackupAlbums, Set? excludedBackupAlbums, - Set? allUniqueAssets, + Set? allUniqueAssets, Set? selectedAlbumsBackupAssetsIds, CurrentUploadAsset? currentUploadAsset, }) { diff --git a/mobile/lib/models/backup/success_upload_asset.model.dart b/mobile/lib/models/backup/success_upload_asset.model.dart new file mode 100644 index 0000000000000..045715e8cbbda --- /dev/null +++ b/mobile/lib/models/backup/success_upload_asset.model.dart @@ -0,0 +1,42 @@ +import 'package:immich_mobile/models/backup/backup_candidate.model.dart'; + +class SuccessUploadAsset { + final BackupCandidate candidate; + final String remoteAssetId; + final bool isDuplicate; + + SuccessUploadAsset({ + required this.candidate, + required this.remoteAssetId, + required this.isDuplicate, + }); + + SuccessUploadAsset copyWith({ + BackupCandidate? candidate, + String? remoteAssetId, + bool? isDuplicate, + }) { + return SuccessUploadAsset( + candidate: candidate ?? this.candidate, + remoteAssetId: remoteAssetId ?? this.remoteAssetId, + isDuplicate: isDuplicate ?? this.isDuplicate, + ); + } + + @override + String toString() => + 'SuccessUploadAsset(asset: $candidate, remoteAssetId: $remoteAssetId, isDuplicate: $isDuplicate)'; + + @override + bool operator ==(covariant SuccessUploadAsset other) { + if (identical(this, other)) return true; + + return other.candidate == candidate && + other.remoteAssetId == remoteAssetId && + other.isDuplicate == isDuplicate; + } + + @override + int get hashCode => + candidate.hashCode ^ remoteAssetId.hashCode ^ isDuplicate.hashCode; +} diff --git a/mobile/lib/pages/backup/backup_album_selection.page.dart b/mobile/lib/pages/backup/backup_album_selection.page.dart index 9f3e387755e85..8dccece325d8f 100644 --- a/mobile/lib/pages/backup/backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/backup_album_selection.page.dart @@ -4,19 +4,24 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/backup/backup.provider.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; import 'package:immich_mobile/widgets/backup/album_info_card.dart'; import 'package:immich_mobile/widgets/backup/album_info_list_tile.dart'; import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart'; +import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; @RoutePage() class BackupAlbumSelectionPage extends HookConsumerWidget { const BackupAlbumSelectionPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - // final availableAlbums = ref.watch(backupProvider).availableAlbums; final selectedBackupAlbums = ref.watch(backupProvider).selectedBackupAlbums; final excludedBackupAlbums = ref.watch(backupProvider).excludedBackupAlbums; + final enableSyncUploadAlbum = + useAppSettingsState(AppSettingsEnum.syncAlbums); final isDarkTheme = context.isDarkTheme; final albums = ref.watch(backupProvider).availableAlbums; @@ -144,47 +149,14 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { }).toSet(); } - // buildSearchBar() { - // return Padding( - // padding: const EdgeInsets.only(left: 16.0, right: 16, bottom: 8.0), - // child: TextFormField( - // onChanged: (searchValue) { - // // if (searchValue.isEmpty) { - // // albums = availableAlbums; - // // } else { - // // albums.value = availableAlbums - // // .where( - // // (album) => album.name - // // .toLowerCase() - // // .contains(searchValue.toLowerCase()), - // // ) - // // .toList(); - // // } - // }, - // decoration: InputDecoration( - // contentPadding: const EdgeInsets.symmetric( - // horizontal: 8.0, - // vertical: 8.0, - // ), - // hintText: "Search", - // hintStyle: TextStyle( - // color: isDarkTheme ? Colors.white : Colors.grey, - // fontSize: 14.0, - // ), - // prefixIcon: const Icon( - // Icons.search, - // color: Colors.grey, - // ), - // border: OutlineInputBorder( - // borderRadius: BorderRadius.circular(10), - // borderSide: BorderSide.none, - // ), - // filled: true, - // fillColor: isDarkTheme ? Colors.white30 : Colors.grey[200], - // ), - // ), - // ); - // } + handleSyncAlbumToggle(bool isEnable) async { + if (isEnable) { + await ref.read(albumProvider.notifier).getAllAlbums(); + for (final album in selectedBackupAlbums) { + await ref.read(albumProvider.notifier).createSyncAlbum(album.name); + } + } + } return Scaffold( appBar: AppBar( @@ -226,6 +198,20 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { ), ), + SettingsSwitchListTile( + valueNotifier: enableSyncUploadAlbum, + title: "sync_albums".tr(), + subtitle: "sync_upload_album_setting_subtitle".tr(), + contentPadding: const EdgeInsets.symmetric(horizontal: 16), + titleStyle: context.textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.bold, + ), + subtitleStyle: context.textTheme.labelLarge?.copyWith( + color: context.colorScheme.primary, + ), + onChanged: handleSyncAlbumToggle, + ), + ListTile( title: Text( "backup_album_selection_page_albums_device".tr( diff --git a/mobile/lib/providers/album/album.provider.dart b/mobile/lib/providers/album/album.provider.dart index 8251d5e66bf33..ed9dc07f5e5c0 100644 --- a/mobile/lib/providers/album/album.provider.dart +++ b/mobile/lib/providers/album/album.provider.dart @@ -23,6 +23,7 @@ class AlbumNotifier extends StateNotifier> { }); _streamSub = query.watch().listen((data) => state = data); } + final AlbumService _albumService; late final StreamSubscription> _streamSub; @@ -41,6 +42,23 @@ class AlbumNotifier extends StateNotifier> { ) => _albumService.createAlbum(albumTitle, assets, []); + Future getAlbumByName(String albumName, {bool remoteOnly = false}) => + _albumService.getAlbumByName(albumName, remoteOnly); + + /// Create an album on the server with the same name as the selected album for backup + /// First this will check if the album already exists on the server with name + /// If it does not exist, it will create the album on the server + Future createSyncAlbum( + String albumName, + ) async { + final album = await getAlbumByName(albumName, remoteOnly: true); + if (album != null) { + return; + } + + await createAlbum(albumName, {}); + } + @override void dispose() { _streamSub.cancel(); diff --git a/mobile/lib/providers/backup/backup.provider.dart b/mobile/lib/providers/backup/backup.provider.dart index 58027e3b941e0..02f1f07904f97 100644 --- a/mobile/lib/providers/backup/backup.provider.dart +++ b/mobile/lib/providers/backup/backup.provider.dart @@ -2,13 +2,16 @@ import 'dart:io'; import 'package:cancellation_token_http/http.dart'; import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/backup/available_album.model.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; +import 'package:immich_mobile/models/backup/backup_candidate.model.dart'; import 'package:immich_mobile/models/backup/backup_state.model.dart'; import 'package:immich_mobile/models/backup/current_upload_asset.model.dart'; import 'package:immich_mobile/models/backup/error_upload_asset.model.dart'; +import 'package:immich_mobile/models/backup/success_upload_asset.model.dart'; import 'package:immich_mobile/providers/backup/error_backup_list.provider.dart'; import 'package:immich_mobile/services/background.service.dart'; import 'package:immich_mobile/services/backup.service.dart'; @@ -290,8 +293,8 @@ class BackupNotifier extends StateNotifier { /// Future _updateBackupAssetCount() async { final duplicatedAssetIds = await _backupService.getDuplicatedAssetIds(); - final Set assetsFromSelectedAlbums = {}; - final Set assetsFromExcludedAlbums = {}; + final Set assetsFromSelectedAlbums = {}; + final Set assetsFromExcludedAlbums = {}; for (final album in state.selectedBackupAlbums) { final assetCount = await album.albumEntity.assetCountAsync; @@ -304,7 +307,27 @@ class BackupNotifier extends StateNotifier { start: 0, end: assetCount, ); - assetsFromSelectedAlbums.addAll(assets); + + // Add album's name to the asset info + for (final asset in assets) { + List albumNames = [album.name]; + + final existingAsset = assetsFromSelectedAlbums.firstWhereOrNull( + (a) => a.asset.id == asset.id, + ); + + if (existingAsset != null) { + albumNames.addAll(existingAsset.albumNames); + assetsFromSelectedAlbums.remove(existingAsset); + } + + assetsFromSelectedAlbums.add( + BackupCandidate( + asset: asset, + albumNames: albumNames, + ), + ); + } } for (final album in state.excludedBackupAlbums) { @@ -318,11 +341,17 @@ class BackupNotifier extends StateNotifier { start: 0, end: assetCount, ); - assetsFromExcludedAlbums.addAll(assets); + + for (final asset in assets) { + assetsFromExcludedAlbums.add( + BackupCandidate(asset: asset, albumNames: [album.name]), + ); + } } - final Set allUniqueAssets = + final Set allUniqueAssets = assetsFromSelectedAlbums.difference(assetsFromExcludedAlbums); + final allAssetsInDatabase = await _backupService.getDeviceBackupAsset(); if (allAssetsInDatabase == null) { @@ -331,14 +360,14 @@ class BackupNotifier extends StateNotifier { // Find asset that were backup from selected albums final Set selectedAlbumsBackupAssets = - Set.from(allUniqueAssets.map((e) => e.id)); + Set.from(allUniqueAssets.map((e) => e.asset.id)); selectedAlbumsBackupAssets .removeWhere((assetId) => !allAssetsInDatabase.contains(assetId)); // Remove duplicated asset from all unique assets allUniqueAssets.removeWhere( - (asset) => duplicatedAssetIds.contains(asset.id), + (candidate) => duplicatedAssetIds.contains(candidate.asset.id), ); if (allUniqueAssets.isEmpty) { @@ -433,10 +462,10 @@ class BackupNotifier extends StateNotifier { return; } - Set assetsWillBeBackup = Set.from(state.allUniqueAssets); + Set assetsWillBeBackup = Set.from(state.allUniqueAssets); // Remove item that has already been backed up for (final assetId in state.allAssetsInDatabase) { - assetsWillBeBackup.removeWhere((e) => e.id == assetId); + assetsWillBeBackup.removeWhere((e) => e.asset.id == assetId); } if (assetsWillBeBackup.isEmpty) { @@ -456,11 +485,11 @@ class BackupNotifier extends StateNotifier { await _backupService.backupAsset( assetsWillBeBackup, state.cancelToken, - pmProgressHandler, - _onAssetUploaded, - _onUploadProgress, - _onSetCurrentBackupAsset, - _onBackupError, + pmProgressHandler: pmProgressHandler, + onSuccess: _onAssetUploaded, + onProgress: _onUploadProgress, + onCurrentAsset: _onSetCurrentBackupAsset, + onError: _onBackupError, ); await notifyBackgroundServiceCanRun(); } else { @@ -497,34 +526,36 @@ class BackupNotifier extends StateNotifier { ); } - void _onAssetUploaded( - String deviceAssetId, - String deviceId, - bool isDuplicated, - ) { - if (isDuplicated) { + void _onAssetUploaded(SuccessUploadAsset result) async { + if (result.isDuplicate) { state = state.copyWith( allUniqueAssets: state.allUniqueAssets - .where((asset) => asset.id != deviceAssetId) + .where( + (candidate) => candidate.asset.id != result.candidate.asset.id, + ) .toSet(), ); } else { state = state.copyWith( selectedAlbumsBackupAssetsIds: { ...state.selectedAlbumsBackupAssetsIds, - deviceAssetId, + result.candidate.asset.id, }, - allAssetsInDatabase: [...state.allAssetsInDatabase, deviceAssetId], + allAssetsInDatabase: [ + ...state.allAssetsInDatabase, + result.candidate.asset.id, + ], ); } if (state.allUniqueAssets.length - state.selectedAlbumsBackupAssetsIds.length == 0) { - final latestAssetBackup = - state.allUniqueAssets.map((e) => e.modifiedDateTime).reduce( - (v, e) => e.isAfter(v) ? e : v, - ); + final latestAssetBackup = state.allUniqueAssets + .map((candidate) => candidate.asset.modifiedDateTime) + .reduce( + (v, e) => e.isAfter(v) ? e : v, + ); state = state.copyWith( selectedBackupAlbums: state.selectedBackupAlbums .map((e) => e.copyWith(lastBackup: latestAssetBackup)) diff --git a/mobile/lib/providers/backup/manual_upload.provider.dart b/mobile/lib/providers/backup/manual_upload.provider.dart index b446711226324..a76b56fea7f8a 100644 --- a/mobile/lib/providers/backup/manual_upload.provider.dart +++ b/mobile/lib/providers/backup/manual_upload.provider.dart @@ -6,6 +6,8 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/widgets.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/models/backup/backup_candidate.model.dart'; +import 'package:immich_mobile/models/backup/success_upload_asset.model.dart'; import 'package:immich_mobile/services/background.service.dart'; import 'package:immich_mobile/models/backup/backup_state.model.dart'; import 'package:immich_mobile/models/backup/current_upload_asset.model.dart'; @@ -22,6 +24,7 @@ import 'package:immich_mobile/providers/app_life_cycle.provider.dart'; import 'package:immich_mobile/services/local_notification.service.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:immich_mobile/utils/backup_progress.dart'; +import 'package:isar/isar.dart'; import 'package:logging/logging.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:photo_manager/photo_manager.dart'; @@ -31,6 +34,7 @@ final manualUploadProvider = return ManualUploadNotifier( ref.watch(localNotificationService), ref.watch(backupProvider.notifier), + ref.watch(backupServiceProvider), ref, ); }); @@ -39,11 +43,13 @@ class ManualUploadNotifier extends StateNotifier { final Logger _log = Logger("ManualUploadNotifier"); final LocalNotificationService _localNotificationService; final BackupNotifier _backupProvider; + final BackupService _backupService; final Ref ref; ManualUploadNotifier( this._localNotificationService, this._backupProvider, + this._backupService, this.ref, ) : super( ManualUploadState( @@ -115,11 +121,7 @@ class ManualUploadNotifier extends StateNotifier { } } - void _onAssetUploaded( - String deviceAssetId, - String deviceId, - bool isDuplicated, - ) { + void _onAssetUploaded(SuccessUploadAsset result) { state = state.copyWith(successfulUploads: state.successfulUploads + 1); _backupProvider.updateDiskInfo(); } @@ -209,9 +211,23 @@ class ManualUploadNotifier extends StateNotifier { ); } - Set allUploadAssets = allAssetsFromDevice.nonNulls.toSet(); + final selectedBackupAlbums = + _backupService.selectedAlbumsQuery().findAllSync(); + final excludedBackupAlbums = + _backupService.excludedAlbumsQuery().findAllSync(); - if (allUploadAssets.isEmpty) { + // Get candidates from selected albums and excluded albums + Set candidates = + await _backupService.buildUploadCandidates( + selectedBackupAlbums, + excludedBackupAlbums, + ); + + // Extrack candidate from allAssetsFromDevice.nonNulls + final uploadAssets = candidates + .where((e) => allAssetsFromDevice.nonNulls.contains(e.asset)); + + if (uploadAssets.isEmpty) { debugPrint("[_startUpload] No Assets to upload - Abort Process"); _backupProvider.updateBackupProgress(BackUpProgressEnum.idle); return false; @@ -221,7 +237,7 @@ class ManualUploadNotifier extends StateNotifier { progressInPercentage: 0, progressInFileSize: "0 B / 0 B", progressInFileSpeed: 0, - totalAssetsToUpload: allUploadAssets.length, + totalAssetsToUpload: uploadAssets.length, successfulUploads: 0, currentAssetIndex: 0, currentUploadAsset: CurrentUploadAsset( @@ -250,13 +266,13 @@ class ManualUploadNotifier extends StateNotifier { final pmProgressHandler = Platform.isIOS ? PMProgressHandler() : null; final bool ok = await ref.read(backupServiceProvider).backupAsset( - allUploadAssets, + uploadAssets, state.cancelToken, - pmProgressHandler, - _onAssetUploaded, - _onProgress, - _onSetCurrentBackupAsset, - _onAssetUploadError, + pmProgressHandler: pmProgressHandler, + onSuccess: _onAssetUploaded, + onProgress: _onProgress, + onCurrentAsset: _onSetCurrentBackupAsset, + onError: _onAssetUploadError, ); // Close detailed notification diff --git a/mobile/lib/services/album.service.dart b/mobile/lib/services/album.service.dart index c2494680c7da5..ef56f9bf6c12a 100644 --- a/mobile/lib/services/album.service.dart +++ b/mobile/lib/services/album.service.dart @@ -7,7 +7,6 @@ import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/albums/album_add_asset_response.model.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; -import 'package:immich_mobile/services/backup.service.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; @@ -28,7 +27,6 @@ final albumServiceProvider = Provider( ref.watch(userServiceProvider), ref.watch(syncServiceProvider), ref.watch(dbProvider), - ref.watch(backupServiceProvider), ), ); @@ -37,7 +35,6 @@ class AlbumService { final UserService _userService; final SyncService _syncService; final Isar _db; - final BackupService _backupService; final Logger _log = Logger('AlbumService'); Completer _localCompleter = Completer()..complete(false); Completer _remoteCompleter = Completer()..complete(false); @@ -47,9 +44,15 @@ class AlbumService { this._userService, this._syncService, this._db, - this._backupService, ); + QueryBuilder + selectedAlbumsQuery() => + _db.backupAlbums.filter().selectionEqualTo(BackupSelection.select); + QueryBuilder + excludedAlbumsQuery() => + _db.backupAlbums.filter().selectionEqualTo(BackupSelection.exclude); + /// Checks all selected device albums for changes of albums and their assets /// Updates the local database and returns `true` if there were any changes Future refreshDeviceAlbums() async { @@ -63,9 +66,9 @@ class AlbumService { bool changes = false; try { final List excludedIds = - await _backupService.excludedAlbumsQuery().idProperty().findAll(); + await excludedAlbumsQuery().idProperty().findAll(); final List selectedIds = - await _backupService.selectedAlbumsQuery().idProperty().findAll(); + await selectedAlbumsQuery().idProperty().findAll(); if (selectedIds.isEmpty) { final numLocal = await _db.albums.where().localIdIsNotNull().count(); if (numLocal > 0) { @@ -441,4 +444,33 @@ class AlbumService { return false; } } + + Future getAlbumByName(String name, bool remoteOnly) async { + return _db.albums + .filter() + .optional(remoteOnly, (q) => q.localIdIsNull()) + .nameEqualTo(name) + .sharedEqualTo(false) + .findFirst(); + } + + /// + /// Add the uploaded asset to the selected albums + /// + Future syncUploadAlbums( + List albumNames, + List assetIds, + ) async { + for (final albumName in albumNames) { + Album? album = await getAlbumByName(albumName, true); + album ??= await createAlbum(albumName, []); + + if (album != null && album.remoteId != null) { + await _apiService.albumsApi.addAssetsToAlbum( + album.remoteId!, + BulkIdsDto(ids: assetIds), + ); + } + } + } } diff --git a/mobile/lib/services/app_settings.service.dart b/mobile/lib/services/app_settings.service.dart index bd254032159c0..8f773e1bb33a9 100644 --- a/mobile/lib/services/app_settings.service.dart +++ b/mobile/lib/services/app_settings.service.dart @@ -76,6 +76,7 @@ enum AppSettingsEnum { false, ), enableHapticFeedback(StoreKey.enableHapticFeedback, null, true), + syncAlbums(StoreKey.syncAlbums, null, false), ; const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue); diff --git a/mobile/lib/services/asset.service.dart b/mobile/lib/services/asset.service.dart index d37133a63b9c7..17508cba5153e 100644 --- a/mobile/lib/services/asset.service.dart +++ b/mobile/lib/services/asset.service.dart @@ -2,15 +2,20 @@ import 'dart:async'; +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/exif_info.entity.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/models/backup/backup_candidate.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/providers/db.provider.dart'; +import 'package:immich_mobile/services/album.service.dart'; import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/services/backup.service.dart'; import 'package:immich_mobile/services/sync.service.dart'; import 'package:immich_mobile/services/user.service.dart'; import 'package:isar/isar.dart'; @@ -23,6 +28,8 @@ final assetServiceProvider = Provider( ref.watch(apiServiceProvider), ref.watch(syncServiceProvider), ref.watch(userServiceProvider), + ref.watch(backupServiceProvider), + ref.watch(albumServiceProvider), ref.watch(dbProvider), ), ); @@ -31,6 +38,8 @@ class AssetService { final ApiService _apiService; final SyncService _syncService; final UserService _userService; + final BackupService _backupService; + final AlbumService _albumService; final log = Logger('AssetService'); final Isar _db; @@ -38,6 +47,8 @@ class AssetService { this._apiService, this._syncService, this._userService, + this._backupService, + this._albumService, this._db, ); @@ -284,4 +295,64 @@ class AssetService { return Future.value(null); } } + + Future syncUploadedAssetToAlbums() async { + try { + final [selectedAlbums, excludedAlbums] = await Future.wait([ + _backupService.selectedAlbumsQuery().findAll(), + _backupService.excludedAlbumsQuery().findAll(), + ]); + + final candidates = await _backupService.buildUploadCandidates( + selectedAlbums, + excludedAlbums, + useTimeFilter: false, + ); + + final duplicates = await _apiService.assetsApi.checkExistingAssets( + CheckExistingAssetsDto( + deviceAssetIds: candidates.map((c) => c.asset.id).toList(), + deviceId: Store.get(StoreKey.deviceId), + ), + ); + + if (duplicates != null) { + candidates + .removeWhere((c) => !duplicates.existingIds.contains(c.asset.id)); + } + + await refreshRemoteAssets(); + final remoteAssets = await _db.assets + .where() + .localIdIsNotNull() + .filter() + .remoteIdIsNotNull() + .findAll(); + + /// Map + Map> assetToAlbums = {}; + + for (BackupCandidate candidate in candidates) { + final asset = remoteAssets.firstWhereOrNull( + (a) => a.localId == candidate.asset.id, + ); + + if (asset != null) { + for (final albumName in candidate.albumNames) { + assetToAlbums.putIfAbsent(albumName, () => []).add(asset.remoteId!); + } + } + } + + // Upload assets to albums + for (final entry in assetToAlbums.entries) { + final albumName = entry.key; + final assetIds = entry.value; + + await _albumService.syncUploadAlbums([albumName], assetIds); + } + } catch (error, stack) { + log.severe("Error while syncing uploaded asset to albums", error, stack); + } + } } diff --git a/mobile/lib/services/background.service.dart b/mobile/lib/services/background.service.dart index ba8f5c01ed963..b27ed34b946ce 100644 --- a/mobile/lib/services/background.service.dart +++ b/mobile/lib/services/background.service.dart @@ -10,6 +10,10 @@ import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/main.dart'; +import 'package:immich_mobile/models/backup/backup_candidate.model.dart'; +import 'package:immich_mobile/models/backup/success_upload_asset.model.dart'; +import 'package:immich_mobile/services/album.service.dart'; +import 'package:immich_mobile/services/hash.service.dart'; import 'package:immich_mobile/services/localization.service.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; import 'package:immich_mobile/models/backup/current_upload_asset.model.dart'; @@ -18,6 +22,9 @@ import 'package:immich_mobile/services/backup.service.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/services/partner.service.dart'; +import 'package:immich_mobile/services/sync.service.dart'; +import 'package:immich_mobile/services/user.service.dart'; import 'package:immich_mobile/utils/backup_progress.dart'; import 'package:immich_mobile/utils/diff.dart'; import 'package:immich_mobile/utils/http_ssl_cert_override.dart'; @@ -345,8 +352,16 @@ class BackgroundService { ApiService apiService = ApiService(); apiService.setAccessToken(Store.get(StoreKey.accessToken)); AppSettingsService settingService = AppSettingsService(); - BackupService backupService = BackupService(apiService, db, settingService); AppSettingsService settingsService = AppSettingsService(); + PartnerService partnerService = PartnerService(apiService, db); + HashService hashService = HashService(db, this); + SyncService syncSerive = SyncService(db, hashService); + UserService userService = + UserService(apiService, db, syncSerive, partnerService); + AlbumService albumService = + AlbumService(apiService, userService, syncSerive, db); + BackupService backupService = + BackupService(apiService, db, settingService, albumService); final selectedAlbums = backupService.selectedAlbumsQuery().findAllSync(); final excludedAlbums = backupService.excludedAlbumsQuery().findAllSync(); @@ -416,7 +431,7 @@ class BackgroundService { return false; } - List toUpload = await backupService.buildUploadCandidates( + Set toUpload = await backupService.buildUploadCandidates( selectedAlbums, excludedAlbums, ); @@ -460,29 +475,47 @@ class BackgroundService { final bool ok = await backupService.backupAsset( toUpload, _cancellationToken!, - pmProgressHandler, - notifyTotalProgress ? _onAssetUploaded : (assetId, deviceId, isDup) {}, - notifySingleProgress ? _onProgress : (sent, total) {}, - notifySingleProgress ? _onSetCurrentBackupAsset : (asset) {}, - _onBackupError, - sortAssets: true, + pmProgressHandler: pmProgressHandler, + onSuccess: (result) => _onAssetUploaded( + result: result, + shouldNotify: notifyTotalProgress, + ), + onProgress: (bytes, totalBytes) => + _onProgress(bytes, totalBytes, shouldNotify: notifySingleProgress), + onCurrentAsset: (asset) => + _onSetCurrentBackupAsset(asset, shouldNotify: notifySingleProgress), + onError: _onBackupError, + isBackground: true, ); + if (!ok && !_cancellationToken!.isCancelled) { _showErrorNotification( title: "backup_background_service_error_title".tr(), content: "backup_background_service_backup_failed_message".tr(), ); } + return ok; } - void _onAssetUploaded(String deviceAssetId, String deviceId, bool isDup) { + void _onAssetUploaded({ + required SuccessUploadAsset result, + bool shouldNotify = false, + }) async { + if (!shouldNotify) { + return; + } + _uploadedAssetsCount++; _throttledNotifiy(); } - void _onProgress(int sent, int total) { - _throttledDetailNotify(progress: sent, total: total); + void _onProgress(int bytes, int totalBytes, {bool shouldNotify = false}) { + if (!shouldNotify) { + return; + } + + _throttledDetailNotify(progress: bytes, total: totalBytes); } void _updateDetailProgress(String? title, int progress, int total) { @@ -522,7 +555,14 @@ class BackgroundService { ); } - void _onSetCurrentBackupAsset(CurrentUploadAsset currentUploadAsset) { + void _onSetCurrentBackupAsset( + CurrentUploadAsset currentUploadAsset, { + bool shouldNotify = false, + }) { + if (!shouldNotify) { + return; + } + _throttledDetailNotify.title = "backup_background_service_current_upload_notification" .tr(args: [currentUploadAsset.fileName]); diff --git a/mobile/lib/services/backup.service.dart b/mobile/lib/services/backup.service.dart index 64d683dc2ae83..12edd14d609ca 100644 --- a/mobile/lib/services/backup.service.dart +++ b/mobile/lib/services/backup.service.dart @@ -9,11 +9,14 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; import 'package:immich_mobile/entities/duplicated_asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/models/backup/backup_candidate.model.dart'; import 'package:immich_mobile/models/backup/current_upload_asset.model.dart'; import 'package:immich_mobile/models/backup/error_upload_asset.model.dart'; +import 'package:immich_mobile/models/backup/success_upload_asset.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/db.provider.dart'; +import 'package:immich_mobile/services/album.service.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:isar/isar.dart'; @@ -28,6 +31,7 @@ final backupServiceProvider = Provider( ref.watch(apiServiceProvider), ref.watch(dbProvider), ref.watch(appSettingsServiceProvider), + ref.watch(albumServiceProvider), ), ); @@ -37,8 +41,14 @@ class BackupService { final Isar _db; final Logger _log = Logger("BackupService"); final AppSettingsService _appSetting; + final AlbumService _albumService; - BackupService(this._apiService, this._db, this._appSetting); + BackupService( + this._apiService, + this._db, + this._appSetting, + this._albumService, + ); Future?> getDeviceBackupAsset() async { final String deviceId = Store.get(StoreKey.deviceId); @@ -70,10 +80,12 @@ class BackupService { _db.backupAlbums.filter().selectionEqualTo(BackupSelection.exclude); /// Returns all assets newer than the last successful backup per album - Future> buildUploadCandidates( + /// if `useTimeFilter` is set to true, all assets will be returned + Future> buildUploadCandidates( List selectedBackupAlbums, - List excludedBackupAlbums, - ) async { + List excludedBackupAlbums, { + bool useTimeFilter = true, + }) async { final filter = FilterOptionGroup( containsPathModified: true, orders: [const OrderOption(type: OrderOptionType.updateDate)], @@ -82,105 +94,156 @@ class BackupService { videoOption: const FilterOption(needTitle: true), ); final now = DateTime.now(); + final List selectedAlbums = - await _loadAlbumsWithTimeFilter(selectedBackupAlbums, filter, now); + await _loadAlbumsWithTimeFilter( + selectedBackupAlbums, + filter, + now, + useTimeFilter: useTimeFilter, + ); + if (selectedAlbums.every((e) => e == null)) { - return []; - } - final int allIdx = selectedAlbums.indexWhere((e) => e != null && e.isAll); - if (allIdx != -1) { - final List excludedAlbums = - await _loadAlbumsWithTimeFilter(excludedBackupAlbums, filter, now); - final List toAdd = await _fetchAssetsAndUpdateLastBackup( - selectedAlbums.slice(allIdx, allIdx + 1), - selectedBackupAlbums.slice(allIdx, allIdx + 1), - now, - ); - final List toRemove = await _fetchAssetsAndUpdateLastBackup( - excludedAlbums, - excludedBackupAlbums, - now, - ); - return toAdd.toSet().difference(toRemove.toSet()).toList(); - } else { - return await _fetchAssetsAndUpdateLastBackup( - selectedAlbums, - selectedBackupAlbums, - now, - ); + return {}; } + + final List excludedAlbums = + await _loadAlbumsWithTimeFilter( + excludedBackupAlbums, + filter, + now, + useTimeFilter: useTimeFilter, + ); + + final Set toAdd = await _fetchAssetsAndUpdateLastBackup( + selectedAlbums, + selectedBackupAlbums, + now, + useTimeFilter: useTimeFilter, + ); + + final Set toRemove = await _fetchAssetsAndUpdateLastBackup( + excludedAlbums, + excludedBackupAlbums, + now, + useTimeFilter: useTimeFilter, + ); + + return toAdd.difference(toRemove); } Future> _loadAlbumsWithTimeFilter( List albums, FilterOptionGroup filter, - DateTime now, - ) async { + DateTime now, { + bool useTimeFilter = true, + }) async { List result = []; - for (BackupAlbum a in albums) { + for (BackupAlbum backupAlbum in albums) { try { + final optionGroup = useTimeFilter + ? filter.copyWith( + updateTimeCond: DateTimeCond( + // subtract 2 seconds to prevent missing assets due to rounding issues + min: backupAlbum.lastBackup + .subtract(const Duration(seconds: 2)), + max: now, + ), + ) + : filter; + final AssetPathEntity album = await AssetPathEntity.obtainPathFromProperties( - id: a.id, - optionGroup: filter.copyWith( - updateTimeCond: DateTimeCond( - // subtract 2 seconds to prevent missing assets due to rounding issues - min: a.lastBackup.subtract(const Duration(seconds: 2)), - max: now, - ), - ), + id: backupAlbum.id, + optionGroup: optionGroup, maxDateTimeToNow: false, ); + result.add(album); } on StateError { // either there are no assets matching the filter criteria OR the album no longer exists } } + return result; } - Future> _fetchAssetsAndUpdateLastBackup( - List albums, + Future> _fetchAssetsAndUpdateLastBackup( + List localAlbums, List backupAlbums, - DateTime now, - ) async { - List result = []; - for (int i = 0; i < albums.length; i++) { - final AssetPathEntity? a = albums[i]; - if (a != null && - a.lastModified?.isBefore(backupAlbums[i].lastBackup) != true) { - result.addAll( - await a.getAssetListRange(start: 0, end: await a.assetCountAsync), - ); - backupAlbums[i].lastBackup = now; + DateTime now, { + bool useTimeFilter = true, + }) async { + Set candidate = {}; + + for (int i = 0; i < localAlbums.length; i++) { + final localAlbum = localAlbums[i]; + if (localAlbum == null) { + continue; } + + if (useTimeFilter && + localAlbum.lastModified?.isBefore(backupAlbums[i].lastBackup) == + true) { + continue; + } + + final assets = await localAlbum.getAssetListRange( + start: 0, + end: await localAlbum.assetCountAsync, + ); + + // Add album's name to the asset info + for (final asset in assets) { + List albumNames = [localAlbum.name]; + + final existingAsset = candidate.firstWhereOrNull( + (a) => a.asset.id == asset.id, + ); + + if (existingAsset != null) { + albumNames.addAll(existingAsset.albumNames); + candidate.remove(existingAsset); + } + + candidate.add( + BackupCandidate( + asset: asset, + albumNames: albumNames, + ), + ); + } + + backupAlbums[i].lastBackup = now; } - return result; + + return candidate; } /// Returns a new list of assets not yet uploaded - Future> removeAlreadyUploadedAssets( - List candidates, + Future> removeAlreadyUploadedAssets( + Set candidates, ) async { if (candidates.isEmpty) { return candidates; } + final Set duplicatedAssetIds = await getDuplicatedAssetIds(); - candidates = duplicatedAssetIds.isEmpty - ? candidates - : candidates - .whereNot((asset) => duplicatedAssetIds.contains(asset.id)) - .toList(); + candidates.removeWhere( + (candidate) => duplicatedAssetIds.contains(candidate.asset.id), + ); + if (candidates.isEmpty) { return candidates; } + final Set existing = {}; try { final String deviceId = Store.get(StoreKey.deviceId); final CheckExistingAssetsResponseDto? duplicates = await _apiService.assetsApi.checkExistingAssets( CheckExistingAssetsDto( - deviceAssetIds: candidates.map((e) => e.id).toList(), + deviceAssetIds: candidates.map((c) => c.asset.id).toList(), deviceId: deviceId, ), ); @@ -194,55 +257,75 @@ class BackupService { existing.addAll(allAssetsInDatabase); } } - return existing.isEmpty - ? candidates - : candidates.whereNot((e) => existing.contains(e.id)).toList(); + + if (existing.isNotEmpty) { + candidates.removeWhere((c) => existing.contains(c.asset.id)); + } + + return candidates; } - Future backupAsset( - Iterable assetList, - http.CancellationToken cancelToken, - PMProgressHandler? pmProgressHandler, - Function(String, String, bool) uploadSuccessCb, - Function(int, int) uploadProgressCb, - Function(CurrentUploadAsset) setCurrentUploadAssetCb, - Function(ErrorUploadAsset) errorCb, { - bool sortAssets = false, - }) async { - final bool isIgnoreIcloudAssets = - _appSetting.getSetting(AppSettingsEnum.ignoreIcloudAssets); - + Future _checkPermissions() async { if (Platform.isAndroid && !(await pm.Permission.accessMediaLocation.status).isGranted) { // double check that permission is granted here, to guard against // uploading corrupt assets without EXIF information _log.warning("Media location permission is not granted. " "Cannot access original assets for backup."); + return false; } - final String deviceId = Store.get(StoreKey.deviceId); - final String savedEndpoint = Store.get(StoreKey.serverEndpoint); - bool anyErrors = false; - final List duplicatedAssetIds = []; // DON'T KNOW WHY BUT THIS HELPS BACKGROUND BACKUP TO WORK ON IOS if (Platform.isIOS) { await PhotoManager.requestPermissionExtend(); } - List assetsToUpload = sortAssets - // Upload images before video assets - // these are further sorted by using their creation date - ? assetList.sorted( - (a, b) { - final cmp = a.typeInt - b.typeInt; - if (cmp != 0) return cmp; - return a.createDateTime.compareTo(b.createDateTime); - }, - ) - : assetList.toList(); + return true; + } - for (var entity in assetsToUpload) { + /// Upload images before video assets for background tasks + /// these are further sorted by using their creation date + List _sortPhotosFirst(List candidates) { + return candidates.sorted( + (a, b) { + final cmp = a.asset.typeInt - b.asset.typeInt; + if (cmp != 0) return cmp; + return a.asset.createDateTime.compareTo(b.asset.createDateTime); + }, + ); + } + + Future backupAsset( + Iterable assets, + http.CancellationToken cancelToken, { + bool isBackground = false, + PMProgressHandler? pmProgressHandler, + required void Function(SuccessUploadAsset result) onSuccess, + required void Function(int bytes, int totalBytes) onProgress, + required void Function(CurrentUploadAsset asset) onCurrentAsset, + required void Function(ErrorUploadAsset error) onError, + }) async { + final bool isIgnoreIcloudAssets = + _appSetting.getSetting(AppSettingsEnum.ignoreIcloudAssets); + final shouldSyncAlbums = _appSetting.getSetting(AppSettingsEnum.syncAlbums); + final String deviceId = Store.get(StoreKey.deviceId); + final String savedEndpoint = Store.get(StoreKey.serverEndpoint); + final List duplicatedAssetIds = []; + bool anyErrors = false; + + final hasPermission = await _checkPermissions(); + if (!hasPermission) { + return false; + } + + List candidates = assets.toList(); + if (isBackground) { + candidates = _sortPhotosFirst(candidates); + } + + for (final candidate in candidates) { + final AssetEntity entity = candidate.asset; File? file; File? livePhotoFile; @@ -257,7 +340,7 @@ class BackupService { continue; } - setCurrentUploadAssetCb( + onCurrentAsset( CurrentUploadAsset( id: entity.id, fileCreatedAt: entity.createDateTime.year == 1970 @@ -299,23 +382,22 @@ class BackupService { } } - var fileStream = file.openRead(); - var assetRawUploadData = http.MultipartFile( + final fileStream = file.openRead(); + final assetRawUploadData = http.MultipartFile( "assetData", fileStream, file.lengthSync(), filename: originalFileName, ); - var baseRequest = MultipartRequest( + final baseRequest = MultipartRequest( 'POST', Uri.parse('$savedEndpoint/assets'), - onProgress: ((bytes, totalBytes) => - uploadProgressCb(bytes, totalBytes)), + onProgress: ((bytes, totalBytes) => onProgress(bytes, totalBytes)), ); + baseRequest.headers.addAll(ApiService.getRequestHeaders()); baseRequest.headers["Transfer-Encoding"] = "chunked"; - baseRequest.fields['deviceAssetId'] = entity.id; baseRequest.fields['deviceId'] = deviceId; baseRequest.fields['fileCreatedAt'] = @@ -324,12 +406,9 @@ class BackupService { entity.modifiedDateTime.toUtc().toIso8601String(); baseRequest.fields['isFavorite'] = entity.isFavorite.toString(); baseRequest.fields['duration'] = entity.videoDuration.toString(); - baseRequest.files.add(assetRawUploadData); - var fileSize = file.lengthSync(); - - setCurrentUploadAssetCb( + onCurrentAsset( CurrentUploadAsset( id: entity.id, fileCreatedAt: entity.createDateTime.year == 1970 @@ -337,7 +416,7 @@ class BackupService { : entity.createDateTime, fileName: originalFileName, fileType: _getAssetType(entity.type), - fileSize: fileSize, + fileSize: file.lengthSync(), iCloudAsset: false, ), ); @@ -356,22 +435,23 @@ class BackupService { baseRequest.fields['livePhotoVideoId'] = livePhotoVideoId; } - var response = await httpClient.send( + final response = await httpClient.send( baseRequest, cancellationToken: cancelToken, ); - var responseBody = jsonDecode(await response.stream.bytesToString()); + final responseBody = + jsonDecode(await response.stream.bytesToString()); if (![200, 201].contains(response.statusCode)) { - var error = responseBody; - var errorMessage = error['message'] ?? error['error']; + final error = responseBody; + final errorMessage = error['message'] ?? error['error']; debugPrint( "Error(${error['statusCode']}) uploading ${entity.id} | $originalFileName | Created on ${entity.createDateTime} | ${error['error']}", ); - errorCb( + onError( ErrorUploadAsset( asset: entity, id: entity.id, @@ -386,23 +466,37 @@ class BackupService { anyErrors = true; break; } + continue; } - var isDuplicate = false; + bool isDuplicate = false; if (response.statusCode == 200) { isDuplicate = true; duplicatedAssetIds.add(entity.id); } - uploadSuccessCb(entity.id, deviceId, isDuplicate); + onSuccess( + SuccessUploadAsset( + candidate: candidate, + remoteAssetId: responseBody['id'] as String, + isDuplicate: isDuplicate, + ), + ); + + if (shouldSyncAlbums && !isDuplicate) { + await _albumService.syncUploadAlbums( + candidate.albumNames, + [responseBody['id'] as String], + ); + } } } on http.CancelledException { debugPrint("Backup was cancelled by the user"); anyErrors = true; break; - } catch (e) { - debugPrint("ERROR backupAsset: ${e.toString()}"); + } catch (error, stackTrace) { + debugPrint("Error backup asset: ${error.toString()}: $stackTrace"); anyErrors = true; continue; } finally { @@ -416,9 +510,11 @@ class BackupService { } } } + if (duplicatedAssetIds.isNotEmpty) { await _saveDuplicatedAssetIds(duplicatedAssetIds); } + return !anyErrors; } diff --git a/mobile/lib/widgets/backup/album_info_card.dart b/mobile/lib/widgets/backup/album_info_card.dart index e9349bd69eccf..0c9cd2d89d33c 100644 --- a/mobile/lib/widgets/backup/album_info_card.dart +++ b/mobile/lib/widgets/backup/album_info_card.dart @@ -5,15 +5,21 @@ import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/models/backup/available_album.model.dart'; +import 'package:immich_mobile/providers/album/album.provider.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/backup/backup.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; class AlbumInfoCard extends HookConsumerWidget { final AvailableAlbum album; - const AlbumInfoCard({super.key, required this.album}); + const AlbumInfoCard({ + super.key, + required this.album, + }); @override Widget build(BuildContext context, WidgetRef ref) { @@ -21,6 +27,9 @@ class AlbumInfoCard extends HookConsumerWidget { ref.watch(backupProvider).selectedBackupAlbums.contains(album); final bool isExcluded = ref.watch(backupProvider).excludedBackupAlbums.contains(album); + final syncAlbum = ref + .watch(appSettingsServiceProvider) + .getSetting(AppSettingsEnum.syncAlbums); final isDarkTheme = context.isDarkTheme; @@ -85,6 +94,9 @@ class AlbumInfoCard extends HookConsumerWidget { ref.read(backupProvider.notifier).removeAlbumForBackup(album); } else { ref.read(backupProvider.notifier).addAlbumForBackup(album); + if (syncAlbum) { + ref.read(albumProvider.notifier).createSyncAlbum(album.name); + } } }, onDoubleTap: () { diff --git a/mobile/lib/widgets/backup/album_info_list_tile.dart b/mobile/lib/widgets/backup/album_info_list_tile.dart index 7cdc595c7fc53..d326bad3e0fc7 100644 --- a/mobile/lib/widgets/backup/album_info_list_tile.dart +++ b/mobile/lib/widgets/backup/album_info_list_tile.dart @@ -5,9 +5,12 @@ import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/models/backup/available_album.model.dart'; +import 'package:immich_mobile/providers/album/album.provider.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/backup/backup.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; class AlbumInfoListTile extends HookConsumerWidget { @@ -21,7 +24,10 @@ class AlbumInfoListTile extends HookConsumerWidget { ref.watch(backupProvider).selectedBackupAlbums.contains(album); final bool isExcluded = ref.watch(backupProvider).excludedBackupAlbums.contains(album); - var assetCount = useState(0); + final assetCount = useState(0); + final syncAlbum = ref + .watch(appSettingsServiceProvider) + .getSetting(AppSettingsEnum.syncAlbums); useEffect( () { @@ -98,6 +104,9 @@ class AlbumInfoListTile extends HookConsumerWidget { ref.read(backupProvider.notifier).removeAlbumForBackup(album); } else { ref.read(backupProvider.notifier).addAlbumForBackup(album); + if (syncAlbum) { + ref.read(albumProvider.notifier).createSyncAlbum(album.name); + } } }, leading: buildIcon(), diff --git a/mobile/lib/widgets/settings/backup_settings/backup_settings.dart b/mobile/lib/widgets/settings/backup_settings/backup_settings.dart index 25bcf2d06e507..c093e8f1e3c98 100644 --- a/mobile/lib/widgets/settings/backup_settings/backup_settings.dart +++ b/mobile/lib/widgets/settings/backup_settings/backup_settings.dart @@ -1,9 +1,12 @@ import 'dart:io'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/providers/backup/backup_verification.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/services/asset.service.dart'; import 'package:immich_mobile/widgets/settings/backup_settings/background_settings.dart'; import 'package:immich_mobile/widgets/settings/backup_settings/foreground_settings.dart'; import 'package:immich_mobile/widgets/settings/settings_button_list_tile.dart'; @@ -23,7 +26,21 @@ class BackupSettings extends HookConsumerWidget { useAppSettingsState(AppSettingsEnum.ignoreIcloudAssets); final isAdvancedTroubleshooting = useAppSettingsState(AppSettingsEnum.advancedTroubleshooting); + final albumSync = useAppSettingsState(AppSettingsEnum.syncAlbums); final isCorruptCheckInProgress = ref.watch(backupVerificationProvider); + final isAlbumSyncInProgress = useState(false); + + syncAlbums() async { + isAlbumSyncInProgress.value = true; + try { + await ref.read(assetServiceProvider).syncUploadedAssetToAlbums(); + } catch (_) { + } finally { + Future.delayed(const Duration(seconds: 1), () { + isAlbumSyncInProgress.value = false; + }); + } + } final backupSettings = [ const ForegroundBackupSettings(), @@ -58,6 +75,23 @@ class BackupSettings extends HookConsumerWidget { .performBackupCheck(context) : null, ), + if (albumSync.value) + SettingsButtonListTile( + icon: Icons.photo_album_outlined, + title: 'sync_albums'.tr(), + subtitle: Text( + "sync_albums_manual_subtitle".tr(), + ), + buttonText: 'sync_albums'.tr(), + child: isAlbumSyncInProgress.value + ? const CircularProgressIndicator.adaptive( + strokeWidth: 2, + ) + : ElevatedButton( + onPressed: syncAlbums, + child: Text('sync'.tr()), + ), + ), ]; return SettingsSubPageScaffold( diff --git a/mobile/lib/widgets/settings/settings_button_list_tile.dart b/mobile/lib/widgets/settings/settings_button_list_tile.dart index 196e3d170feaf..c8bd8e4b588c9 100644 --- a/mobile/lib/widgets/settings/settings_button_list_tile.dart +++ b/mobile/lib/widgets/settings/settings_button_list_tile.dart @@ -9,6 +9,7 @@ class SettingsButtonListTile extends StatelessWidget { final Widget? subtitle; final String? subtileText; final String buttonText; + final Widget? child; final void Function()? onButtonTap; const SettingsButtonListTile({ @@ -18,6 +19,7 @@ class SettingsButtonListTile extends StatelessWidget { this.subtileText, this.subtitle, required this.buttonText, + this.child, this.onButtonTap, super.key, }); @@ -48,7 +50,8 @@ class SettingsButtonListTile extends StatelessWidget { ), if (subtitle != null) subtitle!, const SizedBox(height: 6), - ElevatedButton(onPressed: onButtonTap, child: Text(buttonText)), + child ?? + ElevatedButton(onPressed: onButtonTap, child: Text(buttonText)), ], ), ); diff --git a/mobile/lib/widgets/settings/settings_switch_list_tile.dart b/mobile/lib/widgets/settings/settings_switch_list_tile.dart index 78f1738266a31..8aa4ec0a60ec0 100644 --- a/mobile/lib/widgets/settings/settings_switch_list_tile.dart +++ b/mobile/lib/widgets/settings/settings_switch_list_tile.dart @@ -9,6 +9,9 @@ class SettingsSwitchListTile extends StatelessWidget { final String? subtitle; final IconData? icon; final Function(bool)? onChanged; + final EdgeInsets? contentPadding; + final TextStyle? titleStyle; + final TextStyle? subtitleStyle; const SettingsSwitchListTile({ required this.valueNotifier, @@ -17,6 +20,9 @@ class SettingsSwitchListTile extends StatelessWidget { this.icon, this.enabled = true, this.onChanged, + this.contentPadding = const EdgeInsets.symmetric(horizontal: 20), + this.titleStyle, + this.subtitleStyle, super.key, }); @@ -30,7 +36,7 @@ class SettingsSwitchListTile extends StatelessWidget { } return SwitchListTile.adaptive( - contentPadding: const EdgeInsets.symmetric(horizontal: 20), + contentPadding: contentPadding, selectedTileColor: enabled ? null : context.themeData.disabledColor, value: valueNotifier.value, onChanged: onSwitchChanged, @@ -45,20 +51,22 @@ class SettingsSwitchListTile extends StatelessWidget { : null, title: Text( title, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - color: enabled ? null : context.themeData.disabledColor, - height: 1.5, - ), + style: titleStyle ?? + context.textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.w500, + color: enabled ? null : context.themeData.disabledColor, + height: 1.5, + ), ), subtitle: subtitle != null ? Text( subtitle!, - style: context.textTheme.bodyMedium?.copyWith( - color: enabled - ? context.colorScheme.onSurfaceSecondary - : context.themeData.disabledColor, - ), + style: subtitleStyle ?? + context.textTheme.bodyMedium?.copyWith( + color: enabled + ? context.colorScheme.onSurfaceSecondary + : context.themeData.disabledColor, + ), ) : null, ); From 9894b9513bcc27615d2656a0afcad6c65028bef3 Mon Sep 17 00:00:00 2001 From: Ben <45583362+ben-basten@users.noreply.github.com> Date: Mon, 26 Aug 2024 21:05:23 -0400 Subject: [PATCH 023/160] fix(web): shared link expiration date accessibility (#12060) - use native select - shows focus, automatically has keyboard navigation, accessible for screen readers - remove DropdownButton component - fix dropdown styling in Safari --- .../create-shared-link-modal.svelte | 47 ++++++------ .../shared-components/dropdown-button.svelte | 74 ------------------- .../settings/setting-select.svelte | 38 ++++++---- 3 files changed, 46 insertions(+), 113 deletions(-) delete mode 100644 web/src/lib/components/shared-components/dropdown-button.svelte diff --git a/web/src/lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte b/web/src/lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte index 97c3aaf17e62c..c50a07ad37413 100644 --- a/web/src/lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte +++ b/web/src/lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte @@ -8,7 +8,6 @@ import { SharedLinkType, createSharedLink, updateSharedLink, type SharedLinkResponseDto } from '@immich/sdk'; import { mdiContentCopy, mdiLink } from '@mdi/js'; import { createEventDispatcher } from 'svelte'; - import DropdownButton, { type DropDownOption } from '../dropdown-button.svelte'; import { NotificationType, notificationController } from '../notification/notification'; import SettingInputField, { SettingInputFieldType } from '../settings/setting-input-field.svelte'; import SettingSwitch from '../settings/setting-switch.svelte'; @@ -16,6 +15,7 @@ import { t } from 'svelte-i18n'; import { locale } from '$lib/stores/preferences.store'; import { DateTime, Duration } from 'luxon'; + import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte'; export let onClose: () => void; export let albumId: string | undefined = undefined; @@ -27,7 +27,7 @@ let allowDownload = true; let allowUpload = false; let showMetadata = true; - let expirationOption: DropDownOption | undefined; + let expirationOption: number = 0; let password = ''; let shouldChangeExpirationTime = false; let enablePassword = false; @@ -48,14 +48,12 @@ ]; $: relativeTime = new Intl.RelativeTimeFormat($locale); - $: expiredDateOption = [ - { label: $t('never'), value: 0 }, - ...expirationOptions.map( - ([value, unit]): DropDownOption => ({ - label: relativeTime.format(value, unit), - value: Duration.fromObject({ [unit]: value }).toMillis(), - }), - ), + $: expiredDateOptions = [ + { text: $t('never'), value: 0 }, + ...expirationOptions.map(([value, unit]) => ({ + text: relativeTime.format(value, unit), + value: Duration.fromObject({ [unit]: value }).toMillis(), + })), ]; $: shareType = albumId ? SharedLinkType.Album : SharedLinkType.Individual; @@ -82,8 +80,7 @@ } const handleCreateSharedLink = async () => { - const expirationDate = - expirationOption && expirationOption.value > 0 ? DateTime.now().plus(expirationOption.value).toISO() : undefined; + const expirationDate = expirationOption > 0 ? DateTime.now().plus(expirationOption).toISO() : undefined; try { const data = await createSharedLink({ @@ -112,8 +109,7 @@ } try { - const expirationDate = - expirationOption && expirationOption.value > 0 ? DateTime.now().plus(expirationOption.value).toISO() : null; + const expirationDate = expirationOption > 0 ? DateTime.now().plus(expirationOption).toISO() : null; await updateSharedLink({ id: editingLink.id, @@ -212,19 +208,18 @@
-
- {#if editingLink} -

- -

- {:else} -

{$t('expire_after')}

- {/if} - - + +
+ {/if} +
+
diff --git a/web/src/lib/components/shared-components/dropdown-button.svelte b/web/src/lib/components/shared-components/dropdown-button.svelte deleted file mode 100644 index 450b3d5ce6381..0000000000000 --- a/web/src/lib/components/shared-components/dropdown-button.svelte +++ /dev/null @@ -1,74 +0,0 @@ - - - - -
- - - {#if isOpen} -
- {#each options as option} - - {/each} -
- {/if} -
- - diff --git a/web/src/lib/components/shared-components/settings/setting-select.svelte b/web/src/lib/components/shared-components/settings/setting-select.svelte index b4efd90056386..c5b9e2c02e17d 100644 --- a/web/src/lib/components/shared-components/settings/setting-select.svelte +++ b/web/src/lib/components/shared-components/settings/setting-select.svelte @@ -3,6 +3,8 @@ import { fly } from 'svelte/transition'; import { createEventDispatcher } from 'svelte'; import { t } from 'svelte-i18n'; + import Icon from '$lib/components/elements/icon.svelte'; + import { mdiChevronDown } from '@mdi/js'; export let value: string | number; export let options: { value: string | number; text: string }[]; @@ -46,17 +48,27 @@

{/if} - +
+ + +
From b051b29eca418bb867edba58af13df3bccb8230c Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 27 Aug 2024 03:48:39 +0200 Subject: [PATCH 024/160] feat(server): Storage template support album condition (#12000) feat(server): Storage template support album condition ([Request](https://github.com/immich-app/immich/discussions/11999)) --- .../services/storage-template.service.spec.ts | 44 ++++++++++++++++++- .../src/services/storage-template.service.ts | 4 +- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/server/src/services/storage-template.service.spec.ts b/server/src/services/storage-template.service.spec.ts index c1e0410a3d89c..92d11eaa125f7 100644 --- a/server/src/services/storage-template.service.spec.ts +++ b/server/src/services/storage-template.service.spec.ts @@ -15,6 +15,7 @@ import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; import { StorageTemplateService } from 'src/services/storage-template.service'; +import { albumStub } from 'test/fixtures/album.stub'; import { assetStub } from 'test/fixtures/asset.stub'; import { userStub } from 'test/fixtures/user.stub'; import { newAlbumRepositoryMock } from 'test/repositories/album.repository.mock'; @@ -83,7 +84,7 @@ describe(StorageTemplateService.name, () => { newConfig: { storageTemplate: { template: - '{{y}}{{M}}{{W}}{{d}}{{h}}{{m}}{{s}}{{filename}}{{ext}}{{filetype}}{{filetypefull}}{{assetId}}{{album}}', + '{{y}}{{M}}{{W}}{{d}}{{h}}{{m}}{{s}}{{filename}}{{ext}}{{filetype}}{{filetypefull}}{{assetId}}{{#if album}}{{album}}{{else}}other{{/if}}', }, } as SystemConfig, oldConfig: {} as SystemConfig, @@ -163,6 +164,47 @@ describe(StorageTemplateService.name, () => { originalPath: newMotionPicturePath, }); }); + it('Should use handlebar if condition for album', async () => { + const asset = assetStub.image; + const user = userStub.user1; + const album = albumStub.oneAsset; + const config = structuredClone(defaults); + config.storageTemplate.template = '{{y}}/{{#if album}}{{album}}{{else}}other/{{MM}}{{/if}}/{{filename}}'; + SystemConfigCore.create(systemMock, loggerMock).config$.next(config); + + userMock.get.mockResolvedValue(user); + assetMock.getByIds.mockResolvedValueOnce([asset]); + albumMock.getByAssetId.mockResolvedValueOnce([album]); + + expect(await sut.handleMigrationSingle({ id: asset.id })).toBe(JobStatus.SUCCESS); + + expect(moveMock.create).toHaveBeenCalledWith({ + entityId: asset.id, + newPath: `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${album.albumName}/${asset.originalFileName}`, + oldPath: asset.originalPath, + pathType: AssetPathType.ORIGINAL, + }); + }); + it('Should use handlebar else condition for album', async () => { + const asset = assetStub.image; + const user = userStub.user1; + const config = structuredClone(defaults); + config.storageTemplate.template = '{{y}}/{{#if album}}{{album}}{{else}}other//{{MM}}{{/if}}/{{filename}}'; + SystemConfigCore.create(systemMock, loggerMock).config$.next(config); + + userMock.get.mockResolvedValue(user); + assetMock.getByIds.mockResolvedValueOnce([asset]); + + expect(await sut.handleMigrationSingle({ id: asset.id })).toBe(JobStatus.SUCCESS); + + const month = (asset.fileCreatedAt.getMonth() + 1).toString().padStart(2, '0'); + expect(moveMock.create).toHaveBeenCalledWith({ + entityId: asset.id, + newPath: `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/other/${month}/${asset.originalFileName}`, + oldPath: asset.originalPath, + pathType: AssetPathType.ORIGINAL, + }); + }); it('should migrate previously failed move from original path when it still exists', async () => { userMock.get.mockResolvedValue(userStub.user1); const previousFailedNewPath = `upload/library/${userStub.user1.id}/2023/Feb/${assetStub.image.id}.jpg`; diff --git a/server/src/services/storage-template.service.ts b/server/src/services/storage-template.service.ts index 0ee5bdd3b56de..4855d602d70d0 100644 --- a/server/src/services/storage-template.service.ts +++ b/server/src/services/storage-template.service.ts @@ -308,7 +308,7 @@ export class StorageTemplateService { filetypefull: asset.type == AssetType.IMAGE ? 'IMAGE' : 'VIDEO', assetId: asset.id, //just throw into the root if it doesn't belong to an album - album: (albumName && sanitize(albumName.replaceAll(/\.+/g, ''))) || '.', + album: (albumName && sanitize(albumName.replaceAll(/\.+/g, ''))) || '', }; const systemTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; @@ -329,6 +329,6 @@ export class StorageTemplateService { substitutions[token] = dt.toFormat(token); } - return template(substitutions); + return template(substitutions).replaceAll(/\/{2,}/gm, '/'); } } From f70dcaa6cc460cf7f76df97198666eec536fc2eb Mon Sep 17 00:00:00 2001 From: Matthew Momjian <50788000+mmomjian@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:54:53 -0400 Subject: [PATCH 025/160] docs: mTLS/self signed FAQ entry (#12074) mTLS/self signed --- docs/docs/FAQ.mdx | 5 +++++ docs/src/components/community-projects.tsx | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/docs/FAQ.mdx b/docs/docs/FAQ.mdx index 117ca74c037ca..a5d9b6e3d310c 100644 --- a/docs/docs/FAQ.mdx +++ b/docs/docs/FAQ.mdx @@ -52,6 +52,11 @@ On iOS (iPhone and iPad), the operating system determines if a particular app ca - Disable Background App Refresh for apps that don't need background tasks to run. This will reduce the competition for background task invocation for Immich. - Use the Immich app more often. +### Why are features not working with a self-signed cert or mTLS? + +Due to limitations in the upstream app/video library, using a self-signed TLS certificate or mutual TLS may break video playback or asset upload (both foreground and/or background). +We recommend using a real SSL certificate from a free provider, for example [Let's Encrypt](https://letsencrypt.org/). + --- ## Assets diff --git a/docs/src/components/community-projects.tsx b/docs/src/components/community-projects.tsx index 0f9b2b2413a77..0f30bac60f66e 100644 --- a/docs/src/components/community-projects.tsx +++ b/docs/src/components/community-projects.tsx @@ -75,7 +75,8 @@ const projects: CommunityProjectProps[] = [ }, { title: 'Immich Power Tools', - description: 'An unofficial immich client providing tools to speed up your workflows in Immich to organize your people and albums.', + description: + 'An unofficial immich client providing tools to speed up your workflows in Immich to organize your people and albums.', url: 'https://github.com/varun-raj/immich-power-tools', }, ]; From 3e970bc2d333d470edec42c5af7e12a895de6aed Mon Sep 17 00:00:00 2001 From: Yuvraj P Date: Tue, 27 Aug 2024 12:06:16 -0400 Subject: [PATCH 026/160] fix(mobile): Changes in the UI for the image editor pages (#12018) * Ui enchancements and fixes * Reruning the github review thing * conflicts fix, apparently * conflicts fix, apparently * Fixed edit.page.dart * Fixed crop page; localization etc * Updated es-US.json; for Localization * Formatting * Changing the es-US.json back * Update en-US.json * localization --------- Co-authored-by: Alex --- mobile/assets/i18n/en-US.json | 5 ++ mobile/assets/i18n/es-US.json | 2 +- mobile/lib/pages/editing/crop.page.dart | 18 +++-- mobile/lib/pages/editing/edit.page.dart | 65 +++++++++++++------ .../lib/utils/hooks/crop_controller_hook.dart | 2 +- 5 files changed, 64 insertions(+), 28 deletions(-) diff --git a/mobile/assets/i18n/en-US.json b/mobile/assets/i18n/en-US.json index c092b79bd1d6e..d8aa678e337fc 100644 --- a/mobile/assets/i18n/en-US.json +++ b/mobile/assets/i18n/en-US.json @@ -574,6 +574,11 @@ "version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.", "version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89", "viewer_unstack": "Un-Stack", + "edit_image_title": "Edit", + "crop": "Crop", + "save_to_gallery": "Save to gallery", + "error_saving_image": "Error: {}", + "image_saved_successfully": "Image saved", "sync_albums": "Sync albums", "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", diff --git a/mobile/assets/i18n/es-US.json b/mobile/assets/i18n/es-US.json index 9a17fba78749c..394139767e248 100644 --- a/mobile/assets/i18n/es-US.json +++ b/mobile/assets/i18n/es-US.json @@ -575,4 +575,4 @@ "viewer_remove_from_stack": "Eliminar de la pila", "viewer_stack_use_as_main_asset": "Utilizar como recurso principal", "viewer_unstack": "Desapilar" -} \ No newline at end of file +} diff --git a/mobile/lib/pages/editing/crop.page.dart b/mobile/lib/pages/editing/crop.page.dart index a3ac34dfa0a67..729b59ded5911 100644 --- a/mobile/lib/pages/editing/crop.page.dart +++ b/mobile/lib/pages/editing/crop.page.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; import 'package:crop_image/crop_image.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/utils/hooks/crop_controller_hook.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'edit.page.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:auto_route/auto_route.dart'; /// A widget for cropping an image. @@ -25,13 +27,14 @@ class CropImagePage extends HookWidget { return Scaffold( appBar: AppBar( - backgroundColor: Theme.of(context).bottomAppBarTheme.color, - leading: CloseButton(color: Theme.of(context).iconTheme.color), + backgroundColor: context.scaffoldBackgroundColor, + title: Text("crop".tr()), + leading: CloseButton(color: context.primaryColor), actions: [ IconButton( icon: Icon( Icons.done_rounded, - color: Theme.of(context).iconTheme.color, + color: context.primaryColor, size: 24, ), onPressed: () async { @@ -47,13 +50,14 @@ class CropImagePage extends HookWidget { ), ], ), + backgroundColor: context.scaffoldBackgroundColor, body: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { return Column( children: [ Container( padding: const EdgeInsets.only(top: 20), - width: double.infinity, + width: constraints.maxWidth * 0.9, height: constraints.maxHeight * 0.6, child: CropImage( controller: cropController, @@ -65,7 +69,7 @@ class CropImagePage extends HookWidget { child: Container( width: double.infinity, decoration: BoxDecoration( - color: Theme.of(context).bottomAppBarTheme.color, + color: context.scaffoldBackgroundColor, borderRadius: const BorderRadius.only( topLeft: Radius.circular(20), topRight: Radius.circular(20), @@ -196,7 +200,7 @@ class _AspectRatioButton extends StatelessWidget { icon: Icon( iconData, color: aspectRatio.value == ratio - ? Colors.indigo + ? context.primaryColor : Theme.of(context).iconTheme.color, ), onPressed: () { @@ -205,7 +209,7 @@ class _AspectRatioButton extends StatelessWidget { cropController.aspectRatio = ratio; }, ), - Text(label, style: Theme.of(context).textTheme.bodyMedium), + Text(label, style: context.textTheme.displayMedium), ], ); } diff --git a/mobile/lib/pages/editing/edit.page.dart b/mobile/lib/pages/editing/edit.page.dart index b9017e940bb41..c81e84877b208 100644 --- a/mobile/lib/pages/editing/edit.page.dart +++ b/mobile/lib/pages/editing/edit.page.dart @@ -7,13 +7,15 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/widgets/common/immich_image.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:auto_route/auto_route.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:photo_manager/photo_manager.dart'; -import 'package:path/path.dart' as p; import 'package:immich_mobile/providers/album/album.provider.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:path/path.dart' as p; /// A stateless widget that provides functionality for editing an image. /// @@ -71,11 +73,17 @@ class EditImagePage extends ConsumerWidget { ); await ref.read(albumProvider.notifier).getDeviceAlbums(); Navigator.of(context).popUntil((route) => route.isFirst); + ImmichToast.show( + durationInSecond: 3, + context: context, + msg: 'Image Saved!', + gravity: ToastGravity.CENTER, + ); } catch (e) { ImmichToast.show( durationInSecond: 6, context: context, - msg: 'Error: $e', + msg: "error_saving_image".tr(args: [e.toString()]), gravity: ToastGravity.CENTER, ); } @@ -88,11 +96,12 @@ class EditImagePage extends ConsumerWidget { return Scaffold( appBar: AppBar( - backgroundColor: Theme.of(context).appBarTheme.backgroundColor, + title: Text("edit_image_title".tr()), + backgroundColor: context.scaffoldBackgroundColor, leading: IconButton( icon: Icon( Icons.close_rounded, - color: Theme.of(context).iconTheme.color, + color: context.primaryColor, size: 24, ), onPressed: () => @@ -104,31 +113,48 @@ class EditImagePage extends ConsumerWidget { ? () => _saveEditedImage(context, asset, image, ref) : null, child: Text( - 'Save to gallery', + "save_to_gallery".tr(), style: TextStyle( - color: - isEdited ? Theme.of(context).iconTheme.color : Colors.grey, + color: isEdited ? context.primaryColor : Colors.grey, ), ), ), ], ), - body: Column( - children: [ - Expanded( - child: image, + backgroundColor: context.scaffoldBackgroundColor, + body: Center( + child: ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height * 0.7, + maxWidth: MediaQuery.of(context).size.width * 0.9, ), - Container( - height: 80, - color: Theme.of(context).bottomAppBarTheme.color, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(7), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.2), + spreadRadius: 2, + blurRadius: 10, + offset: const Offset(0, 3), + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(7), + child: Image( + image: image.image, + fit: BoxFit.contain, + ), + ), ), - ], + ), ), bottomNavigationBar: Container( - height: 80, - margin: const EdgeInsets.only(bottom: 20, right: 10, left: 10, top: 10), + height: 70, + margin: const EdgeInsets.only(bottom: 60, right: 10, left: 10, top: 10), decoration: BoxDecoration( - color: Theme.of(context).bottomAppBarTheme.color, + color: context.scaffoldBackgroundColor, borderRadius: BorderRadius.circular(30), ), child: Column( @@ -140,6 +166,7 @@ class EditImagePage extends ConsumerWidget { ? Icons.crop_rotate_rounded : Icons.crop_rotate_rounded, color: Theme.of(context).iconTheme.color, + size: 25, ), onPressed: () { context.pushRoute( @@ -147,7 +174,7 @@ class EditImagePage extends ConsumerWidget { ); }, ), - Text('Crop', style: Theme.of(context).textTheme.displayMedium), + Text("crop".tr(), style: context.textTheme.displayMedium), ], ), ), diff --git a/mobile/lib/utils/hooks/crop_controller_hook.dart b/mobile/lib/utils/hooks/crop_controller_hook.dart index b03d9ccdb0917..04bc9787548eb 100644 --- a/mobile/lib/utils/hooks/crop_controller_hook.dart +++ b/mobile/lib/utils/hooks/crop_controller_hook.dart @@ -6,7 +6,7 @@ import 'dart:ui'; // Import the dart:ui library for Rect CropController useCropController() { return useMemoized( () => CropController( - defaultCrop: const Rect.fromLTRB(0.1, 0.1, 0.9, 0.9), + defaultCrop: const Rect.fromLTRB(0, 0, 1, 1), ), ); } From 16d5996f773e725ca2cf3f6754fafc53e3fa077d Mon Sep 17 00:00:00 2001 From: Matthew Momjian <50788000+mmomjian@users.noreply.github.com> Date: Tue, 27 Aug 2024 15:30:01 -0400 Subject: [PATCH 027/160] docs: external library deletion/edits (#12079) * external lib * edit 2 * Update FAQ.mdx * fixes --- docs/docs/FAQ.mdx | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/docs/FAQ.mdx b/docs/docs/FAQ.mdx index a5d9b6e3d310c..501a67d5f2a58 100644 --- a/docs/docs/FAQ.mdx +++ b/docs/docs/FAQ.mdx @@ -63,8 +63,9 @@ We recommend using a real SSL certificate from a free provider, for example [Let ### Does Immich change the file? -No, Immich does not touch the original file under any circumstances, -all edited metadata are saved in the companion sidecar file and the database. +No, Immich does not modify the original files. +All edited metadata is saved in companion `.xmp` sidecar files and the database. +However, Immich will delete original files that have been trashed when the trash is emptied in the Immich UI. ### Can I add my existing photo library? @@ -162,6 +163,19 @@ We haven't implemented an official mechanism for creating albums from external l Duplicate checking only exists for upload libraries, using the file hash. Furthermore, duplicate checking is not global, but _per library_. Therefore, a situation where the same file appears twice in the timeline is possible, especially for external libraries. +### Why are my edits to files not being saved in read-only external libraries? + +Images in read-write external libraries (the default) can be edited as normal. +In read-only libraries (`:ro` in the `docker-compose.yml`), Immich is unable to create the `.xmp` sidecar files to store edited file metadata. +For this reason, the metadata (timestamp, location, description, star rating, etc.) cannot be edited for files in read-only external libraries. + +### How are deletions of files handled in external libraries? + +Immich will attempt to delete original files that have been trashed when the trash is emptied. +In read-write external libraries (the default), Immich will delete the original file. +In read-only libraries (`:ro` in the `docker-compose.yml`), files can still be trashed in the UI. +However, when the trash is emptied, the files will re-appear in the main timeline since Immich is unable to delete the original file. + --- ## Machine Learning From aac6a4b0524ac19a47e4f6632e161f6e166800b7 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 27 Aug 2024 16:50:25 -0500 Subject: [PATCH 028/160] chore(web): ignore shortcut toggle when entering email and password (#12082) --- web/src/lib/actions/shortcut.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/src/lib/actions/shortcut.ts b/web/src/lib/actions/shortcut.ts index fca1ed7ef8f20..d28c294a8996b 100644 --- a/web/src/lib/actions/shortcut.ts +++ b/web/src/lib/actions/shortcut.ts @@ -20,7 +20,7 @@ export const shouldIgnoreShortcut = (event: KeyboardEvent): boolean => { return false; } const type = (event.target as HTMLInputElement).type; - return ['textarea', 'text', 'date', 'datetime-local'].includes(type); + return ['textarea', 'text', 'date', 'datetime-local', 'email', 'password'].includes(type); }; export const matchesShortcut = (event: KeyboardEvent, shortcut: Shortcut) => { @@ -53,7 +53,6 @@ export const shortcuts = ( ): ActionReturn[]> => { function onKeydown(event: KeyboardEvent) { const ignoreShortcut = shouldIgnoreShortcut(event); - for (const { shortcut, onShortcut, ignoreInputFields = true, preventDefault = true } of options) { if (ignoreInputFields && ignoreShortcut) { continue; From 0be3c4472f6eee522babe5c7d917aa604ac90f53 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Tue, 27 Aug 2024 18:06:50 -0400 Subject: [PATCH 029/160] refactor(server): event names (#12084) --- server/src/app.module.ts | 8 ++++---- server/src/interfaces/event.interface.ts | 14 +++++++------- server/src/services/album.service.spec.ts | 8 ++++---- server/src/services/album.service.ts | 6 +++--- server/src/services/database.service.ts | 2 +- server/src/services/library.service.ts | 7 ++++--- server/src/services/metadata.service.ts | 10 +++++----- server/src/services/microservices.service.ts | 4 ++-- server/src/services/notification.service.ts | 16 ++++++++-------- server/src/services/server.service.ts | 2 +- server/src/services/smart-info.service.ts | 12 ++++++------ server/src/services/storage-template.service.ts | 4 ++-- server/src/services/storage.service.ts | 2 +- server/src/services/system-config.service.ts | 10 +++++----- server/src/services/user-admin.service.ts | 2 +- server/src/services/version.service.ts | 2 +- 16 files changed, 55 insertions(+), 54 deletions(-) diff --git a/server/src/app.module.ts b/server/src/app.module.ts index 1a8a05fd4d77a..c6cd68a96ff77 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -62,7 +62,7 @@ export class ApiModule implements OnModuleInit, OnModuleDestroy { async onModuleInit() { const items = setupEventHandlers(this.moduleRef); - await this.eventRepository.emit('onBootstrap', 'api'); + await this.eventRepository.emit('app.bootstrap', 'api'); this.logger.setContext('EventLoader'); const eventMap = _.groupBy(items, 'event'); @@ -74,7 +74,7 @@ export class ApiModule implements OnModuleInit, OnModuleDestroy { } async onModuleDestroy() { - await this.eventRepository.emit('onShutdown'); + await this.eventRepository.emit('app.shutdown'); } } @@ -90,11 +90,11 @@ export class MicroservicesModule implements OnModuleInit, OnModuleDestroy { async onModuleInit() { setupEventHandlers(this.moduleRef); - await this.eventRepository.emit('onBootstrap', 'microservices'); + await this.eventRepository.emit('app.bootstrap', 'microservices'); } async onModuleDestroy() { - await this.eventRepository.emit('onShutdown'); + await this.eventRepository.emit('app.shutdown'); } } diff --git a/server/src/interfaces/event.interface.ts b/server/src/interfaces/event.interface.ts index 613a6423a4534..609f42cc32016 100644 --- a/server/src/interfaces/event.interface.ts +++ b/server/src/interfaces/event.interface.ts @@ -6,19 +6,19 @@ export const IEventRepository = 'IEventRepository'; type EmitEventMap = { // app events - onBootstrap: ['api' | 'microservices']; - onShutdown: []; + 'app.bootstrap': ['api' | 'microservices']; + 'app.shutdown': []; // config events - onConfigUpdate: [{ newConfig: SystemConfig; oldConfig: SystemConfig }]; - onConfigValidate: [{ newConfig: SystemConfig; oldConfig: SystemConfig }]; + 'config.update': [{ newConfig: SystemConfig; oldConfig: SystemConfig }]; + 'config.validate': [{ newConfig: SystemConfig; oldConfig: SystemConfig }]; // album events - onAlbumUpdate: [{ id: string; updatedBy: string }]; - onAlbumInvite: [{ id: string; userId: string }]; + 'album.update': [{ id: string; updatedBy: string }]; + 'album.invite': [{ id: string; userId: string }]; // user events - onUserSignup: [{ notify: boolean; id: string; tempPassword?: string }]; + 'user.signup': [{ notify: boolean; id: string; tempPassword?: string }]; }; export type EmitEvent = keyof EmitEventMap; diff --git a/server/src/services/album.service.spec.ts b/server/src/services/album.service.spec.ts index 16b2d97fdd4f4..164e823336878 100644 --- a/server/src/services/album.service.spec.ts +++ b/server/src/services/album.service.spec.ts @@ -205,7 +205,7 @@ describe(AlbumService.name, () => { expect(userMock.get).toHaveBeenCalledWith('user-id', {}); expect(accessMock.asset.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['123'])); - expect(eventMock.emit).toHaveBeenCalledWith('onAlbumInvite', { + expect(eventMock.emit).toHaveBeenCalledWith('album.invite', { id: albumStub.empty.id, userId: 'user-id', }); @@ -384,7 +384,7 @@ describe(AlbumService.name, () => { userId: authStub.user2.user.id, albumId: albumStub.sharedWithAdmin.id, }); - expect(eventMock.emit).toHaveBeenCalledWith('onAlbumInvite', { + expect(eventMock.emit).toHaveBeenCalledWith('album.invite', { id: albumStub.sharedWithAdmin.id, userId: userStub.user2.id, }); @@ -572,7 +572,7 @@ describe(AlbumService.name, () => { albumThumbnailAssetId: 'asset-1', }); expect(albumMock.addAssetIds).toHaveBeenCalledWith('album-123', ['asset-1', 'asset-2', 'asset-3']); - expect(eventMock.emit).toHaveBeenCalledWith('onAlbumUpdate', { + expect(eventMock.emit).toHaveBeenCalledWith('album.update', { id: 'album-123', updatedBy: authStub.admin.user.id, }); @@ -616,7 +616,7 @@ describe(AlbumService.name, () => { albumThumbnailAssetId: 'asset-1', }); expect(albumMock.addAssetIds).toHaveBeenCalledWith('album-123', ['asset-1', 'asset-2', 'asset-3']); - expect(eventMock.emit).toHaveBeenCalledWith('onAlbumUpdate', { + expect(eventMock.emit).toHaveBeenCalledWith('album.update', { id: 'album-123', updatedBy: authStub.user1.user.id, }); diff --git a/server/src/services/album.service.ts b/server/src/services/album.service.ts index b2b5ea32a2c93..1cd5237b7ae39 100644 --- a/server/src/services/album.service.ts +++ b/server/src/services/album.service.ts @@ -140,7 +140,7 @@ export class AlbumService { }); for (const { userId } of albumUsers) { - await this.eventRepository.emit('onAlbumInvite', { id: album.id, userId }); + await this.eventRepository.emit('album.invite', { id: album.id, userId }); } return mapAlbumWithAssets(album); @@ -192,7 +192,7 @@ export class AlbumService { albumThumbnailAssetId: album.albumThumbnailAssetId ?? firstNewAssetId, }); - await this.eventRepository.emit('onAlbumUpdate', { id, updatedBy: auth.user.id }); + await this.eventRepository.emit('album.update', { id, updatedBy: auth.user.id }); } return results; @@ -240,7 +240,7 @@ export class AlbumService { } await this.albumUserRepository.create({ userId: userId, albumId: id, role }); - await this.eventRepository.emit('onAlbumInvite', { id, userId }); + await this.eventRepository.emit('album.invite', { id, userId }); } return this.findOrFail(id, { withAssets: true }).then(mapAlbumWithoutAssets); diff --git a/server/src/services/database.service.ts b/server/src/services/database.service.ts index b6d61c578d79c..d2a2813a0550c 100644 --- a/server/src/services/database.service.ts +++ b/server/src/services/database.service.ts @@ -68,7 +68,7 @@ export class DatabaseService { this.logger.setContext(DatabaseService.name); } - @OnEmit({ event: 'onBootstrap', priority: -200 }) + @OnEmit({ event: 'app.bootstrap', priority: -200 }) async onBootstrap() { const version = await this.databaseRepository.getPostgresVersion(); const current = semver.coerce(version); diff --git a/server/src/services/library.service.ts b/server/src/services/library.service.ts index 1bee2d32c3a41..4b82c9811d8d3 100644 --- a/server/src/services/library.service.ts +++ b/server/src/services/library.service.ts @@ -66,7 +66,7 @@ export class LibraryService { this.configCore = SystemConfigCore.create(systemMetadataRepository, this.logger); } - @OnEmit({ event: 'onBootstrap' }) + @OnEmit({ event: 'app.bootstrap' }) async onBootstrap() { const config = await this.configCore.getConfig({ withCache: false }); @@ -104,7 +104,8 @@ export class LibraryService { }); } - onConfigValidate({ newConfig }: ArgOf<'onConfigValidate'>) { + @OnEmit({ event: 'config.validate' }) + onConfigValidate({ newConfig }: ArgOf<'config.validate'>) { const { scan } = newConfig.library; if (!validateCronExpression(scan.cronExpression)) { throw new Error(`Invalid cron expression ${scan.cronExpression}`); @@ -189,7 +190,7 @@ export class LibraryService { } } - @OnEmit({ event: 'onShutdown' }) + @OnEmit({ event: 'app.shutdown' }) async onShutdown() { await this.unwatchAll(); } diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index dcdf07b8c3f1a..3c938a4e59701 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -121,8 +121,8 @@ export class MetadataService { ); } - @OnEmit({ event: 'onBootstrap' }) - async onBootstrap(app: ArgOf<'onBootstrap'>) { + @OnEmit({ event: 'app.bootstrap' }) + async onBootstrap(app: ArgOf<'app.bootstrap'>) { if (app !== 'microservices') { return; } @@ -130,8 +130,8 @@ export class MetadataService { await this.init(config); } - @OnEmit({ event: 'onConfigUpdate' }) - async onConfigUpdate({ newConfig }: ArgOf<'onConfigUpdate'>) { + @OnEmit({ event: 'config.update' }) + async onConfigUpdate({ newConfig }: ArgOf<'config.update'>) { await this.init(newConfig); } @@ -153,7 +153,7 @@ export class MetadataService { } } - @OnEmit({ event: 'onShutdown' }) + @OnEmit({ event: 'app.shutdown' }) async onShutdown() { await this.repository.teardown(); } diff --git a/server/src/services/microservices.service.ts b/server/src/services/microservices.service.ts index 46ca4118d1954..5b28e6a00a189 100644 --- a/server/src/services/microservices.service.ts +++ b/server/src/services/microservices.service.ts @@ -39,8 +39,8 @@ export class MicroservicesService { private versionService: VersionService, ) {} - @OnEmit({ event: 'onBootstrap' }) - async onBootstrap(app: ArgOf<'onBootstrap'>) { + @OnEmit({ event: 'app.bootstrap' }) + async onBootstrap(app: ArgOf<'app.bootstrap'>) { if (app !== 'microservices') { return; } diff --git a/server/src/services/notification.service.ts b/server/src/services/notification.service.ts index 31701013b70fd..fa4f79f6d6b63 100644 --- a/server/src/services/notification.service.ts +++ b/server/src/services/notification.service.ts @@ -42,8 +42,8 @@ export class NotificationService { this.configCore = SystemConfigCore.create(systemMetadataRepository, logger); } - @OnEmit({ event: 'onConfigValidate', priority: -100 }) - async onConfigValidate({ oldConfig, newConfig }: ArgOf<'onConfigValidate'>) { + @OnEmit({ event: 'config.validate', priority: -100 }) + async onConfigValidate({ oldConfig, newConfig }: ArgOf<'config.validate'>) { try { if ( newConfig.notifications.smtp.enabled && @@ -57,20 +57,20 @@ export class NotificationService { } } - @OnEmit({ event: 'onUserSignup' }) - async onUserSignup({ notify, id, tempPassword }: ArgOf<'onUserSignup'>) { + @OnEmit({ event: 'user.signup' }) + async onUserSignup({ notify, id, tempPassword }: ArgOf<'user.signup'>) { if (notify) { await this.jobRepository.queue({ name: JobName.NOTIFY_SIGNUP, data: { id, tempPassword } }); } } - @OnEmit({ event: 'onAlbumUpdate' }) - async onAlbumUpdate({ id, updatedBy }: ArgOf<'onAlbumUpdate'>) { + @OnEmit({ event: 'album.update' }) + async onAlbumUpdate({ id, updatedBy }: ArgOf<'album.update'>) { await this.jobRepository.queue({ name: JobName.NOTIFY_ALBUM_UPDATE, data: { id, senderId: updatedBy } }); } - @OnEmit({ event: 'onAlbumInvite' }) - async onAlbumInvite({ id, userId }: ArgOf<'onAlbumInvite'>) { + @OnEmit({ event: 'album.invite' }) + async onAlbumInvite({ id, userId }: ArgOf<'album.invite'>) { await this.jobRepository.queue({ name: JobName.NOTIFY_ALBUM_INVITE, data: { id, recipientId: userId } }); } diff --git a/server/src/services/server.service.ts b/server/src/services/server.service.ts index faf4d981644a3..5ea8a3e45921f 100644 --- a/server/src/services/server.service.ts +++ b/server/src/services/server.service.ts @@ -42,7 +42,7 @@ export class ServerService { this.configCore = SystemConfigCore.create(systemMetadataRepository, this.logger); } - @OnEmit({ event: 'onBootstrap' }) + @OnEmit({ event: 'app.bootstrap' }) async onBootstrap(): Promise { const featureFlags = await this.getFeatures(); if (featureFlags.configFile) { diff --git a/server/src/services/smart-info.service.ts b/server/src/services/smart-info.service.ts index d57b5fb54ff82..a75594100f231 100644 --- a/server/src/services/smart-info.service.ts +++ b/server/src/services/smart-info.service.ts @@ -39,8 +39,8 @@ export class SmartInfoService { this.configCore = SystemConfigCore.create(systemMetadataRepository, this.logger); } - @OnEmit({ event: 'onBootstrap' }) - async onBootstrap(app: ArgOf<'onBootstrap'>) { + @OnEmit({ event: 'app.bootstrap' }) + async onBootstrap(app: ArgOf<'app.bootstrap'>) { if (app !== 'microservices') { return; } @@ -49,8 +49,8 @@ export class SmartInfoService { await this.init(config); } - @OnEmit({ event: 'onConfigValidate' }) - onConfigValidate({ newConfig }: ArgOf<'onConfigValidate'>) { + @OnEmit({ event: 'config.validate' }) + onConfigValidate({ newConfig }: ArgOf<'config.validate'>) { try { getCLIPModelInfo(newConfig.machineLearning.clip.modelName); } catch { @@ -60,8 +60,8 @@ export class SmartInfoService { } } - @OnEmit({ event: 'onConfigUpdate' }) - async onConfigUpdate({ oldConfig, newConfig }: ArgOf<'onConfigUpdate'>) { + @OnEmit({ event: 'config.update' }) + async onConfigUpdate({ oldConfig, newConfig }: ArgOf<'config.update'>) { await this.init(newConfig, oldConfig); } diff --git a/server/src/services/storage-template.service.ts b/server/src/services/storage-template.service.ts index 4855d602d70d0..829863e228e73 100644 --- a/server/src/services/storage-template.service.ts +++ b/server/src/services/storage-template.service.ts @@ -89,8 +89,8 @@ export class StorageTemplateService { ); } - @OnEmit({ event: 'onConfigValidate' }) - onConfigValidate({ newConfig }: ArgOf<'onConfigValidate'>) { + @OnEmit({ event: 'config.validate' }) + onConfigValidate({ newConfig }: ArgOf<'config.validate'>) { try { const { compiled } = this.compile(newConfig.storageTemplate.template); this.render(compiled, { diff --git a/server/src/services/storage.service.ts b/server/src/services/storage.service.ts index 1535d53d95e23..c3f2c06438340 100644 --- a/server/src/services/storage.service.ts +++ b/server/src/services/storage.service.ts @@ -14,7 +14,7 @@ export class StorageService { this.logger.setContext(StorageService.name); } - @OnEmit({ event: 'onBootstrap' }) + @OnEmit({ event: 'app.bootstrap' }) onBootstrap() { const libraryBase = StorageCore.getBaseFolder(StorageFolder.LIBRARY); this.storageRepository.mkdirSync(libraryBase); diff --git a/server/src/services/system-config.service.ts b/server/src/services/system-config.service.ts index b4e6f903b1a03..26a91f1d09e85 100644 --- a/server/src/services/system-config.service.ts +++ b/server/src/services/system-config.service.ts @@ -33,7 +33,7 @@ export class SystemConfigService { this.core.config$.subscribe((config) => this.setLogLevel(config)); } - @OnEmit({ event: 'onBootstrap', priority: -100 }) + @OnEmit({ event: 'app.bootstrap', priority: -100 }) async onBootstrap() { const config = await this.core.getConfig({ withCache: false }); this.core.config$.next(config); @@ -48,8 +48,8 @@ export class SystemConfigService { return mapConfig(defaults); } - @OnEmit({ event: 'onConfigValidate' }) - onConfigValidate({ newConfig, oldConfig }: ArgOf<'onConfigValidate'>) { + @OnEmit({ event: 'config.validate' }) + onConfigValidate({ newConfig, oldConfig }: ArgOf<'config.validate'>) { if (!_.isEqual(instanceToPlain(newConfig.logging), oldConfig.logging) && this.getEnvLogLevel()) { throw new Error('Logging cannot be changed while the environment variable IMMICH_LOG_LEVEL is set.'); } @@ -63,7 +63,7 @@ export class SystemConfigService { const oldConfig = await this.core.getConfig({ withCache: false }); try { - await this.eventRepository.emit('onConfigValidate', { newConfig: dto, oldConfig }); + await this.eventRepository.emit('config.validate', { newConfig: dto, oldConfig }); } catch (error) { this.logger.warn(`Unable to save system config due to a validation error: ${error}`); throw new BadRequestException(error instanceof Error ? error.message : error); @@ -74,7 +74,7 @@ export class SystemConfigService { // TODO probably move web socket emits to a separate service this.eventRepository.clientBroadcast(ClientEvent.CONFIG_UPDATE, {}); this.eventRepository.serverSend(ServerEvent.CONFIG_UPDATE, null); - await this.eventRepository.emit('onConfigUpdate', { newConfig, oldConfig }); + await this.eventRepository.emit('config.update', { newConfig, oldConfig }); return mapConfig(newConfig); } diff --git a/server/src/services/user-admin.service.ts b/server/src/services/user-admin.service.ts index 95eeed0475b7f..6a5b6ea06e520 100644 --- a/server/src/services/user-admin.service.ts +++ b/server/src/services/user-admin.service.ts @@ -45,7 +45,7 @@ export class UserAdminService { const { notify, ...rest } = dto; const user = await this.userCore.createUser(rest); - await this.eventRepository.emit('onUserSignup', { + await this.eventRepository.emit('user.signup', { notify: !!notify, id: user.id, tempPassword: user.shouldChangePassword ? rest.password : undefined, diff --git a/server/src/services/version.service.ts b/server/src/services/version.service.ts index 2f04a510146cc..468e8c9bdd52c 100644 --- a/server/src/services/version.service.ts +++ b/server/src/services/version.service.ts @@ -37,7 +37,7 @@ export class VersionService { this.configCore = SystemConfigCore.create(systemMetadataRepository, this.logger); } - @OnEmit({ event: 'onBootstrap' }) + @OnEmit({ event: 'app.bootstrap' }) async onBootstrap(): Promise { await this.handleVersionCheck(); } From 98b3441cb1457c009d434fc0fcf64fe9a69652e6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 18:08:01 -0400 Subject: [PATCH 030/160] chore(deps): update prom/prometheus docker digest to f663933 (#12072) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docker/docker-compose.prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index 2fec915a42c1f..733905e01dad7 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -79,7 +79,7 @@ services: container_name: immich_prometheus ports: - 9090:9090 - image: prom/prometheus@sha256:cafe963e591c872d38f3ea41ff8eb22cee97917b7c97b5c0ccd43a419f11f613 + image: prom/prometheus@sha256:f6639335d34a77d9d9db382b92eeb7fc00934be8eae81dbc03b31cfe90411a94 volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - prometheus-data:/prometheus From 72ab664936926a62c82b1ec46a0c51dc663991d8 Mon Sep 17 00:00:00 2001 From: Ben <45583362+ben-basten@users.noreply.github.com> Date: Tue, 27 Aug 2024 18:13:17 -0400 Subject: [PATCH 031/160] feat(web): announce notifications to screen readers (#12071) --- e2e/src/web/specs/photo-viewer.e2e-spec.ts | 2 +- .../context-menu/context-menu.svelte | 2 +- .../shared-components/loading-spinner.svelte | 1 + .../__tests__/notification-card.spec.ts | 23 +++++++++++++++++++ .../__tests__/notification-list.spec.ts | 6 +++-- .../notification/notification-card.svelte | 4 ++++ .../notification/notification-list.svelte | 22 ++++++++++-------- 7 files changed, 46 insertions(+), 14 deletions(-) diff --git a/e2e/src/web/specs/photo-viewer.e2e-spec.ts b/e2e/src/web/specs/photo-viewer.e2e-spec.ts index bc3f6843ca750..09340e98cbfb3 100644 --- a/e2e/src/web/specs/photo-viewer.e2e-spec.ts +++ b/e2e/src/web/specs/photo-viewer.e2e-spec.ts @@ -33,7 +33,7 @@ test.describe('Photo Viewer', () => { await page.waitForLoadState('load'); // this is the spinner await page.waitForSelector('svg[role=status]'); - await expect(page.getByRole('status')).toBeVisible(); + await expect(page.getByTestId('loading-spinner')).toBeVisible(); }); test('loads high resolution photo when zoomed', async ({ page }) => { diff --git a/web/src/lib/components/shared-components/context-menu/context-menu.svelte b/web/src/lib/components/shared-components/context-menu/context-menu.svelte index c6975fdc195e3..8f5ebfa2cfedc 100644 --- a/web/src/lib/components/shared-components/context-menu/context-menu.svelte +++ b/web/src/lib/components/shared-components/context-menu/context-menu.svelte @@ -50,7 +50,7 @@ bind:this={menuElement} class:max-h-[100vh]={isVisible} class:max-h-0={!isVisible} - class="flex flex-col transition-all duration-[250ms] ease-in-out" + class="flex flex-col transition-all duration-[250ms] ease-in-out outline-none" role="menu" tabindex="-1" > diff --git a/web/src/lib/components/shared-components/loading-spinner.svelte b/web/src/lib/components/shared-components/loading-spinner.svelte index 7835e17310234..48626a50f485a 100644 --- a/web/src/lib/components/shared-components/loading-spinner.svelte +++ b/web/src/lib/components/shared-components/loading-spinner.svelte @@ -11,6 +11,7 @@ viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg" + data-testid="loading-spinner" > { expect(sut.getByTestId('message')).toHaveTextContent('Notification message'); }); + it('makes all buttons non-focusable and hidden from screen readers', () => { + sut = render(NotificationCard, { + notification: { + id: 1234, + message: 'Notification message', + timeout: 1000, + type: NotificationType.Info, + action: { type: 'discard' }, + button: { + text: 'button', + onClick: vi.fn(), + }, + }, + }); + const buttons = sut.container.querySelectorAll('button'); + + expect(buttons).toHaveLength(2); + for (const button of buttons) { + expect(button.getAttribute('tabindex')).toBe('-1'); + expect(button.getAttribute('aria-hidden')).toBe('true'); + } + }); + it('shows title and renders component', () => { sut = render(NotificationCard, { notification: { diff --git a/web/src/lib/components/shared-components/notification/__tests__/notification-list.spec.ts b/web/src/lib/components/shared-components/notification/__tests__/notification-list.spec.ts index 44634d6b20038..669b7d75bd855 100644 --- a/web/src/lib/components/shared-components/notification/__tests__/notification-list.spec.ts +++ b/web/src/lib/components/shared-components/notification/__tests__/notification-list.spec.ts @@ -9,8 +9,6 @@ function _getNotificationListElement(sut: RenderResult): HTMLA } describe('NotificationList component', () => { - const sut: RenderResult = render(NotificationList); - beforeAll(() => { // https://testing-library.com/docs/svelte-testing-library/faq#why-arent-transition-events-running vi.stubGlobal('requestAnimationFrame', (fn: FrameRequestCallback) => { @@ -23,6 +21,10 @@ describe('NotificationList component', () => { }); it('shows a notification when added and closes it automatically after the delay timeout', async () => { + const sut: RenderResult = render(NotificationList); + const status = await sut.findAllByRole('status'); + + expect(status).toHaveLength(1); expect(_getNotificationListElement(sut)).not.toBeInTheDocument(); notificationController.show({ diff --git a/web/src/lib/components/shared-components/notification/notification-card.svelte b/web/src/lib/components/shared-components/notification/notification-card.svelte index aac0823bf563b..61e710a1707d2 100644 --- a/web/src/lib/components/shared-components/notification/notification-card.svelte +++ b/web/src/lib/components/shared-components/notification/notification-card.svelte @@ -91,6 +91,8 @@ size="20" padding="2" on:click={discard} + aria-hidden="true" + tabindex={-1} />
@@ -108,6 +110,8 @@ type="button" class="{buttonStyle[notification.type]} rounded px-3 pt-1.5 pb-1 transition-all duration-200" on:click={handleButtonClick} + aria-hidden="true" + tabindex={-1} > {notification.button.text} diff --git a/web/src/lib/components/shared-components/notification/notification-list.svelte b/web/src/lib/components/shared-components/notification/notification-list.svelte index d94ff5c14dd97..c7c54be26720c 100644 --- a/web/src/lib/components/shared-components/notification/notification-list.svelte +++ b/web/src/lib/components/shared-components/notification/notification-list.svelte @@ -1,7 +1,7 @@ -{#if $notificationList.length > 0} -
- {#each $notificationList as notification (notification.id)} -
- -
- {/each} -
-{/if} +
+ {#if $notificationList.length > 0} +
+ {#each $notificationList as notification (notification.id)} +
+ +
+ {/each} +
+ {/if} +
From 028be6738e4fa0a4e4cc3a1e2006cc51d7cb8661 Mon Sep 17 00:00:00 2001 From: Zack Pollard Date: Tue, 27 Aug 2024 23:19:04 +0100 Subject: [PATCH 032/160] ci: use push-o-matic app for release process (#12075) ci: use push-o-matic for release process --- .github/workflows/prepare-release.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 9d50f6f8f9100..6668976bcf0fe 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -40,16 +40,22 @@ jobs: - name: Bump version run: misc/release/pump-version.sh -s "${{ inputs.serverBump }}" -m "${{ inputs.mobileBump }}" + - name: Generate a token + id: generate-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }} + private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} + - name: Commit and tag id: push-tag uses: EndBug/add-and-commit@v9 with: - author_name: Alex The Bot - author_email: alex.tran1502@gmail.com - default_author: user_info - message: 'Version ${{ env.IMMICH_VERSION }}' + default_author: github_actions + message: 'chore: version ${{ env.IMMICH_VERSION }}' tag: ${{ env.IMMICH_VERSION }} push: true + github-token: ${{ steps.generate-token.outputs.token }} build_mobile: uses: ./.github/workflows/build-mobile.yml From be476d7982c1ec77baac44acf6b59e5a72112d99 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 27 Aug 2024 17:29:50 -0500 Subject: [PATCH 033/160] chore(web): ensure goto is awaited for login page (#12087) * chore(web): ensure goto is await for login page * ensure server config is updated after onboarding is finished --- web/src/routes/auth/login/+page.svelte | 6 +++--- web/src/routes/auth/onboarding/+page.svelte | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/web/src/routes/auth/login/+page.svelte b/web/src/routes/auth/login/+page.svelte index 9c22439c56fbb..dd0f64c5a844e 100644 --- a/web/src/routes/auth/login/+page.svelte +++ b/web/src/routes/auth/login/+page.svelte @@ -17,9 +17,9 @@

goto(AppRoute.PHOTOS, { invalidateAll: true })} - onFirstLogin={() => goto(AppRoute.AUTH_CHANGE_PASSWORD)} - onOnboarding={() => goto(AppRoute.AUTH_ONBOARDING)} + onSuccess={async () => await goto(AppRoute.PHOTOS, { invalidateAll: true })} + onFirstLogin={async () => await goto(AppRoute.AUTH_CHANGE_PASSWORD)} + onOnboarding={async () => await goto(AppRoute.AUTH_ONBOARDING)} /> {/if} diff --git a/web/src/routes/auth/onboarding/+page.svelte b/web/src/routes/auth/onboarding/+page.svelte index 0fe2c68c84c0a..ddb30d1b45eff 100644 --- a/web/src/routes/auth/onboarding/+page.svelte +++ b/web/src/routes/auth/onboarding/+page.svelte @@ -6,6 +6,7 @@ import OnboadingStorageTemplate from '$lib/components/onboarding-page/onboarding-storage-template.svelte'; import OnboardingTheme from '$lib/components/onboarding-page/onboarding-theme.svelte'; import { AppRoute, QueryParameter } from '$lib/constants'; + import { retrieveServerConfig } from '$lib/stores/server-config.store'; import { updateAdminOnboarding } from '@immich/sdk'; let index = 0; @@ -35,6 +36,7 @@ const handleDoneClicked = async () => { if (index >= onboardingSteps.length - 1) { await updateAdminOnboarding({ adminOnboardingUpdateDto: { isOnboarded: true } }); + await retrieveServerConfig(); await goto(AppRoute.PHOTOS); } else { index++; From d4cdd590bd1491c4725a5d585c9793dc5b9ce1de Mon Sep 17 00:00:00 2001 From: Matthew Momjian <50788000+mmomjian@users.noreply.github.com> Date: Tue, 27 Aug 2024 20:48:23 -0400 Subject: [PATCH 034/160] docs: sql query for duplicate files (#12086) --- docs/docs/guides/database-queries.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/docs/guides/database-queries.md b/docs/docs/guides/database-queries.md index 20b841f4027dc..2b4f27cfceaa5 100644 --- a/docs/docs/guides/database-queries.md +++ b/docs/docs/guides/database-queries.md @@ -23,7 +23,7 @@ SELECT * FROM "assets" WHERE "originalFileName" LIKE '%_2023_%'; -- all files wi ``` ```sql title="Find by path" -SELECT * FROM "assets" WHERE "originalPath" = 'upload/library/admin/2023/2023-09-03/PXL_20230903_232542848.jpg'; +SELECT * FROM "assets" WHERE "originalPath" = 'upload/library/admin/2023/2023-09-03/PXL_2023.jpg'; SELECT * FROM "assets" WHERE "originalPath" LIKE 'upload/library/admin/2023/%'; ``` @@ -37,6 +37,12 @@ SELECT * FROM "assets" WHERE "checksum" = decode('69de19c87658c4c15d9cacb9967b8e SELECT * FROM "assets" WHERE "checksum" = '\x69de19c87658c4c15d9cacb9967b8e033bf74dd1'; -- alternate notation ``` +```sql title="Find duplicate assets with identical checksum (SHA-1) (excluding trashed files)" +SELECT T1."checksum", array_agg(T2."id") ids FROM "assets" T1 + INNER JOIN "assets" T2 ON T1."checksum" = T2."checksum" AND T1."id" != T2."id" AND T2."deletedAt" IS NULL + WHERE T1."deletedAt" IS NULL GROUP BY T1."checksum"; +``` + ```sql title="Live photos" SELECT * FROM "assets" WHERE "livePhotoVideoId" IS NOT NULL; ``` @@ -79,8 +85,7 @@ SELECT "assets"."type", COUNT(*) FROM "assets" GROUP BY "assets"."type"; ```sql title="Count by type (per user)" SELECT "users"."email", "assets"."type", COUNT(*) FROM "assets" JOIN "users" ON "assets"."ownerId" = "users"."id" - GROUP BY "assets"."type", "users"."email" - ORDER BY "users"."email"; + GROUP BY "assets"."type", "users"."email" ORDER BY "users"."email"; ``` ```sql title="Failed file movements" From 1fd00d8262338a89151595ed25f84d0a6539180d Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 27 Aug 2024 22:31:32 -0500 Subject: [PATCH 035/160] chore(web): resolve timeline flashing temporarily (#12088) --- web/src/lib/stores/assets.store.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/src/lib/stores/assets.store.ts b/web/src/lib/stores/assets.store.ts index 7fd82b4c3a203..763d5b1874c6e 100644 --- a/web/src/lib/stores/assets.store.ts +++ b/web/src/lib/stores/assets.store.ts @@ -253,8 +253,9 @@ export class AssetStore { connect() { this.unsubscribers.push( - websocketEvents.on('on_upload_success', (asset) => { - this.addPendingChanges({ type: 'add', values: [asset] }); + websocketEvents.on('on_upload_success', (_) => { + // TODO!: Temporarily disable to avoid flashing effect of the timeline + // this.addPendingChanges({ type: 'add', values: [asset] }); }), websocketEvents.on('on_asset_trash', (ids) => { this.addPendingChanges({ type: 'trash', values: ids }); From 1239066adaad1ba85d4155027a7e83da9ce837d7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 08:51:02 -0400 Subject: [PATCH 036/160] chore(deps): update base-image to v20240827 (major) (#12073) chore(deps): update base-image to v20240827 Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- server/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/Dockerfile b/server/Dockerfile index 45c68e65e0b94..1c671f2332c8b 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,5 +1,5 @@ # dev build -FROM ghcr.io/immich-app/base-server-dev:20240820@sha256:a28296b40c1247e539894ac4013e6a3e20588d5aefe697fe2ada15f1bd23f6e5 AS dev +FROM ghcr.io/immich-app/base-server-dev:20240827@sha256:c882c0a354faaac4f7256d30ecc2c45435eafa9b64d60793a171abb74ec5ca95 AS dev RUN apt-get install --no-install-recommends -yqq tini WORKDIR /usr/src/app @@ -41,7 +41,7 @@ RUN npm run build # prod build -FROM ghcr.io/immich-app/base-server-prod:20240813@sha256:51537e98ac601aa8401604a6aa9421e94aa55e03c303f355cc5870142adcc471 +FROM ghcr.io/immich-app/base-server-prod:20240827@sha256:72a419dd703b0f530c43f3e00f3aa56be9efb61b4eb9fe911bae8c6f98237967 WORKDIR /usr/src/app ENV NODE_ENV=production \ From d8aec81ae05ddb547d52c0b52e4e5f0639221d24 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 08:52:24 -0400 Subject: [PATCH 037/160] fix(deps): update dependency react-email to v3 (#12077) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- server/package-lock.json | 2807 +++++++------------------------------- server/package.json | 2 +- 2 files changed, 476 insertions(+), 2333 deletions(-) diff --git a/server/package-lock.json b/server/package-lock.json index 05c1469d1ed7e..33a4cd51ad902 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -53,7 +53,7 @@ "openid-client": "^5.4.3", "pg": "^8.11.3", "picomatch": "^4.0.0", - "react-email": "^2.1.2", + "react-email": "^3.0.0", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", "sanitize-filename": "^1.6.3", @@ -115,6 +115,7 @@ "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -123,6 +124,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "peer": true, "engines": { "node": ">=10" }, @@ -598,17 +600,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/runtime": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", - "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/template": { "version": "7.24.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.6.tgz", @@ -740,21 +731,6 @@ "tslib": "^2.4.0" } }, - "node_modules/@emotion/is-prop-valid": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", - "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", - "optional": true, - "dependencies": { - "@emotion/memoize": "0.7.4" - } - }, - "node_modules/@emotion/memoize": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", - "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", - "optional": true - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", @@ -1127,6 +1103,7 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, "dependencies": { "eslint-visitor-keys": "^3.3.0" }, @@ -1141,6 +1118,7 @@ "version": "4.11.0", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -1149,6 +1127,7 @@ "version": "0.17.1", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.1.tgz", "integrity": "sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==", + "dev": true, "dependencies": { "@eslint/object-schema": "^2.1.4", "debug": "^4.3.1", @@ -1162,6 +1141,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, "license": "MIT", "dependencies": { "ajv": "^6.12.4", @@ -1185,6 +1165,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1200,6 +1181,7 @@ "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -1211,12 +1193,14 @@ "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/@eslint/js": { "version": "9.8.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.8.0.tgz", "integrity": "sha512-MfluB7EUfxXtv3i/++oh89uzAr4PDI4nn201hsp+qaXqsjAWzinlZEHEfPgAX4doIlKvPG/i0A9dpKxOLII8yA==", + "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1226,6 +1210,7 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -1239,40 +1224,6 @@ "node": ">=14" } }, - "node_modules/@floating-ui/core": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.4.tgz", - "integrity": "sha512-a4IowK4QkXl4SCWTGUR0INAfEOX3wtsYw3rKK5InQEHMGObkR8Xk44qYQD9P4r6HHw0iIfK6GUKECmY8sTkqRA==", - "dependencies": { - "@floating-ui/utils": "^0.2.4" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.7.tgz", - "integrity": "sha512-wmVfPG5o2xnKDU4jx/m4w5qva9FWHcnZ8BvzEe90D/RpwsJaTAVYPEPdQ8sbr/N8zZTAHlZUTQdqg8ZUbzHmng==", - "dependencies": { - "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.4" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.1.tgz", - "integrity": "sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==", - "dependencies": { - "@floating-ui/dom": "^1.0.0" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.4.tgz", - "integrity": "sha512-dWO2pw8hhi+WrXq1YJy2yCuWoL20PddgGaqTgVe4cOS9Q6qklXCiA1tJEqX6BEwRNSCP84/afac9hd4MS+zEUA==" - }, "node_modules/@golevelup/nestjs-discovery": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@golevelup/nestjs-discovery/-/nestjs-discovery-4.0.1.tgz", @@ -1377,6 +1328,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, "engines": { "node": ">=12.22" }, @@ -1389,6 +1341,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "dev": true, "engines": { "node": ">=18.18" }, @@ -1964,6 +1917,7 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -2457,14 +2411,14 @@ } }, "node_modules/@next/env": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.4.tgz", - "integrity": "sha512-e7X7bbn3Z6DWnDi75UWn+REgAbLEqxI8Tq2pkFOFAMpWAWApz/YCUhtWMWn410h8Q2fYiYL7Yg5OlxMOCfFjJQ==" + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz", + "integrity": "sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==" }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.4.tgz", - "integrity": "sha512-ubmUkbmW65nIAOmoxT1IROZdmmJMmdYvXIe8211send9ZYJu+SqxSnJM4TrPj9wmL6g9Atvj0S/2cFmMSS99jg==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.3.tgz", + "integrity": "sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==", "cpu": [ "arm64" ], @@ -2477,9 +2431,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.4.tgz", - "integrity": "sha512-b0Xo1ELj3u7IkZWAKcJPJEhBop117U78l70nfoQGo4xUSvv0PJSTaV4U9xQBLvZlnjsYkc8RwQN1HoH/oQmLlQ==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz", + "integrity": "sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==", "cpu": [ "x64" ], @@ -2492,9 +2446,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.4.tgz", - "integrity": "sha512-457G0hcLrdYA/u1O2XkRMsDKId5VKe3uKPvrKVOyuARa6nXrdhJOOYU9hkKKyQTMru1B8qEP78IAhf/1XnVqKA==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz", + "integrity": "sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==", "cpu": [ "arm64" ], @@ -2507,9 +2461,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.4.tgz", - "integrity": "sha512-l/kMG+z6MB+fKA9KdtyprkTQ1ihlJcBh66cf0HvqGP+rXBbOXX0dpJatjZbHeunvEHoBBS69GYQG5ry78JMy3g==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz", + "integrity": "sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==", "cpu": [ "arm64" ], @@ -2522,9 +2476,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.4.tgz", - "integrity": "sha512-BapIFZ3ZRnvQ1uWbmqEGJuPT9cgLwvKtxhK/L2t4QYO7l+/DxXuIGjvp1x8rvfa/x1FFSsipERZK70pewbtJtw==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz", + "integrity": "sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==", "cpu": [ "x64" ], @@ -2537,9 +2491,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.4.tgz", - "integrity": "sha512-mqVxTwk4XuBl49qn2A5UmzFImoL1iLm0KQQwtdRJRKl21ylQwwGCxJtIYo2rbfkZHoSKlh/YgztY0qH3wG1xIg==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz", + "integrity": "sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==", "cpu": [ "x64" ], @@ -2552,9 +2506,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.4.tgz", - "integrity": "sha512-xzxF4ErcumXjO2Pvg/wVGrtr9QQJLk3IyQX1ddAC/fi6/5jZCZ9xpuL9Tzc4KPWMFq8GGWFVDMshZOdHGdkvag==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz", + "integrity": "sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==", "cpu": [ "arm64" ], @@ -2567,9 +2521,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.4.tgz", - "integrity": "sha512-WZiz8OdbkpRw6/IU/lredZWKKZopUMhcI2F+XiMAcPja0uZYdMTZQRoQ0WZcvinn9xZAidimE7tN9W5v9Yyfyw==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz", + "integrity": "sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==", "cpu": [ "ia32" ], @@ -2582,9 +2536,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.4.tgz", - "integrity": "sha512-4Rto21sPfw555sZ/XNLqfxDUNeLhNYGO2dlPqsnuCg8N8a2a9u1ltqBOPQ4vj1Gf7eJC0W2hHG2eYUHuiXgY2w==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz", + "integrity": "sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==", "cpu": [ "x64" ], @@ -4317,92 +4271,6 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, - "node_modules/@radix-ui/colors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/colors/-/colors-1.0.1.tgz", - "integrity": "sha512-xySw8f0ZVsAEP+e7iLl3EvcBXX7gsIlC1Zso/sPBW9gIWerBTgz6axrjU+MZ39wD+WFi5h5zdWpsg3+hwt2Qsg==" - }, - "node_modules/@radix-ui/primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", - "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==" - }, - "node_modules/@radix-ui/react-arrow": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz", - "integrity": "sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==", - "dependencies": { - "@radix-ui/react-primitive": "2.0.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-collapsible": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.0.tgz", - "integrity": "sha512-zQY7Epa8sTL0mq4ajSJpjgn2YmCgyrG7RsQgLp3C0LQVkG7+Tf6Pv1CeNWZLyqMjhdPkBa5Lx7wYBeSu7uCSTA==", - "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-presence": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-collection": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", - "integrity": "sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-slot": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", @@ -4417,280 +4285,6 @@ } } }, - "node_modules/@radix-ui/react-context": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", - "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-direction": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", - "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.0.tgz", - "integrity": "sha512-/UovfmmXGptwGcBQawLzvn2jOfM0t4z3/uKffoBlj724+n3FvBbZ7M0aaBOmkp6pqFYpO4yx8tSVJjx3Fl2jig==", - "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-escape-keydown": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.0.tgz", - "integrity": "sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz", - "integrity": "sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-id": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", - "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-popover": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.1.tgz", - "integrity": "sha512-3y1A3isulwnWhvTTwmIreiB8CF4L+qRjZnK1wYLO7pplddzXKby/GnZ2M7OZY3qgnl6p9AodUIHRYGXNah8Y7g==", - "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-dismissable-layer": "1.1.0", - "@radix-ui/react-focus-guards": "1.1.0", - "@radix-ui/react-focus-scope": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.0", - "@radix-ui/react-portal": "1.1.1", - "@radix-ui/react-presence": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-slot": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.7" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-popper": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz", - "integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==", - "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-use-rect": "1.1.0", - "@radix-ui/react-use-size": "1.1.0", - "@radix-ui/rect": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-portal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.1.tgz", - "integrity": "sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g==", - "dependencies": { - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-presence": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.0.tgz", - "integrity": "sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz", - "integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==", - "dependencies": { - "@radix-ui/react-slot": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz", - "integrity": "sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==", - "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-collection": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", @@ -4708,214 +4302,6 @@ } } }, - "node_modules/@radix-ui/react-toggle": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.0.tgz", - "integrity": "sha512-gwoxaKZ0oJ4vIgzsfESBuSgJNdc0rv12VhHgcqN0TEJmmZixXG/2XpsLK8kzNWYcnaoRIEEQc0bEi3dIvdUpjw==", - "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-controllable-state": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-toggle-group": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.0.tgz", - "integrity": "sha512-PpTJV68dZU2oqqgq75Uzto5o/XfOVgkrJ9rulVmfTKxWp3HfUjHE6CP/WLRR4AzPX9HWxw7vFow2me85Yu+Naw==", - "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-roving-focus": "1.1.0", - "@radix-ui/react-toggle": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.1.tgz", - "integrity": "sha512-LLE8nzNE4MzPMw3O2zlVlkLFid3y9hMUs7uCbSHyKSo+tCN4yMCf+ZCCcfrYgsOC0TiHBPQ1mtpJ2liY3ZT3SQ==", - "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-dismissable-layer": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.0", - "@radix-ui/react-portal": "1.1.1", - "@radix-ui/react-presence": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-slot": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-visually-hidden": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", - "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", - "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", - "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", - "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==", - "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", - "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-rect": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", - "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", - "dependencies": { - "@radix-ui/rect": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-size": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", - "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.0.tgz", - "integrity": "sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==", - "dependencies": { - "@radix-ui/react-primitive": "2.0.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/rect": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", - "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==" - }, "node_modules/@react-email/body": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/@react-email/body/-/body-0.0.9.tgz", @@ -5686,10 +5072,11 @@ "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" }, "node_modules/@swc/helpers": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", - "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", "dependencies": { + "@swc/counter": "^0.1.3", "tslib": "^2.4.0" } }, @@ -5697,6 +5084,7 @@ "version": "0.1.12", "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.12.tgz", "integrity": "sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==", + "devOptional": true, "dependencies": { "@swc/counter": "^0.1.3" } @@ -5877,6 +5265,7 @@ "version": "8.44.3", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.3.tgz", "integrity": "sha512-iM/WfkwAhwmPff3wZuPLYiHX18HI24jU8k1ZSH7P8FHwxTjZ2P6CoX2wnF43oprR+YXJM6UUxATkNvyv/JHd+g==", + "dev": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -5886,6 +5275,7 @@ "version": "3.7.5", "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.5.tgz", "integrity": "sha512-JNvhIEyxVW6EoMIFIvj93ZOywYFatlpu9deeH6eSx6PE3WHYvHaQtmHmQeNw7aA81bYGBPPQqdtBm6b1SsQMmA==", + "dev": true, "dependencies": { "@types/eslint": "*", "@types/estree": "*" @@ -5894,7 +5284,8 @@ "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true }, "node_modules/@types/express": { "version": "4.17.21", @@ -5954,7 +5345,8 @@ "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true }, "node_modules/@types/lodash": { "version": "4.17.7", @@ -6112,15 +5504,12 @@ "integrity": "sha512-1MRgzpzY0hOp9pW/kLRxeQhUWwil6gnrUYd3oEpeYBqp/FexhaCPv3F8LsYr47gtUU45fO2cm1dbwkSrHEo8Uw==", "dev": true }, - "node_modules/@types/prismjs": { - "version": "1.26.3", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.3.tgz", - "integrity": "sha512-A0D0aTXvjlqJ5ZILMz3rNfDBOx9hHxLZYv2by47Sm/pqW35zzjusrZTryatjN/Rf8Us2gZrJD+KeHbUSTux1Cw==" - }, "node_modules/@types/prop-types": { "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", + "optional": true, + "peer": true }, "node_modules/@types/qs": { "version": "6.9.8", @@ -6138,19 +5527,13 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz", "integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==", + "optional": true, + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, - "node_modules/@types/react-dom": { - "version": "18.3.0", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", - "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/readdir-glob": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.2.tgz", @@ -6160,11 +5543,6 @@ "@types/node": "*" } }, - "node_modules/@types/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==" - }, "node_modules/@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", @@ -6265,16 +5643,6 @@ "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.8.tgz", "integrity": "sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ==" }, - "node_modules/@types/webpack": { - "version": "5.28.5", - "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.5.tgz", - "integrity": "sha512-wR87cgvxj3p6D0Crt1r5avwqffqPXUkNlnQ1mjU93G7gCuFjufZR4I6j8cz5g1F1tTYpfOOFvly+cmIQwL9wvw==", - "dependencies": { - "@types/node": "*", - "tapable": "^2.2.0", - "webpack": "^5" - } - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.1.tgz", @@ -6610,6 +5978,7 @@ "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "dev": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6" @@ -6618,22 +5987,26 @@ "node_modules/@webassemblyjs/floating-point-hex-parser": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==" + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "dev": true }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dev": true, "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.11.6", "@webassemblyjs/helper-api-error": "1.11.6", @@ -6643,12 +6016,14 @@ "node_modules/@webassemblyjs/helper-wasm-bytecode": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "dev": true, "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", @@ -6660,6 +6035,7 @@ "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -6668,6 +6044,7 @@ "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dev": true, "dependencies": { "@xtuc/long": "4.2.2" } @@ -6675,12 +6052,14 @@ "node_modules/@webassemblyjs/utf8": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "dev": true, "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", @@ -6696,6 +6075,7 @@ "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "dev": true, "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", @@ -6708,6 +6088,7 @@ "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "dev": true, "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", @@ -6719,6 +6100,7 @@ "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "dev": true, "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-api-error": "1.11.6", @@ -6732,6 +6114,7 @@ "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "dev": true, "dependencies": { "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" @@ -6740,12 +6123,14 @@ "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true }, "node_modules/abbrev": { "version": "1.1.1", @@ -6798,6 +6183,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -7100,17 +6486,6 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, - "node_modules/aria-hidden": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", - "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -7164,38 +6539,6 @@ "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==" }, - "node_modules/autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - } - ], - "dependencies": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", - "fraction.js": "^4.2.0", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, "node_modules/b4a": { "version": "1.6.4", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz", @@ -7596,6 +6939,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "peer": true, "engines": { "node": ">= 6" } @@ -7699,6 +7043,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, "engines": { "node": ">=6.0" } @@ -7863,14 +7208,6 @@ "node": ">=0.8" } }, - "node_modules/clsx": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", - "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", - "engines": { - "node": ">=6" - } - }, "node_modules/cluster-key-slot": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", @@ -8271,6 +7608,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "peer": true, "bin": { "cssesc": "bin/cssesc" }, @@ -8281,7 +7619,9 @@ "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "optional": true, + "peer": true }, "node_modules/dayjs": { "version": "1.11.10", @@ -8327,7 +7667,8 @@ "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true }, "node_modules/deepmerge": { "version": "4.3.1", @@ -8403,11 +7744,6 @@ "node": ">=8" } }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" - }, "node_modules/diacritics": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/diacritics/-/diacritics-1.3.0.tgz", @@ -8416,7 +7752,8 @@ "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "peer": true }, "node_modules/diff": { "version": "4.0.2", @@ -8449,7 +7786,8 @@ "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "peer": true }, "node_modules/docker-compose": { "version": "0.24.8", @@ -8700,18 +8038,6 @@ "node": ">=10.2.0" } }, - "node_modules/engine.io-client": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", - "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.11.0", - "xmlhttprequest-ssl": "~2.0.0" - } - }, "node_modules/engine.io-parser": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", @@ -8724,6 +8050,7 @@ "version": "5.17.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", + "dev": true, "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -8773,7 +8100,8 @@ "node_modules/es-module-lexer": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", - "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==" + "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==", + "dev": true }, "node_modules/esbuild": { "version": "0.20.2", @@ -8830,6 +8158,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, "engines": { "node": ">=10" }, @@ -8841,6 +8170,7 @@ "version": "9.8.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.8.0.tgz", "integrity": "sha512-K8qnZ/QJzT2dLKdZJVX6W4XOwBzutMYmt0lqUS+JdXgd+HTYFlonFgkJ8s44d/zMPPCnOOk0kMWCApCPhiOy9A==", + "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", @@ -8899,17 +8229,6 @@ "eslint": ">=7.0.0" } }, - "node_modules/eslint-config-turbo": { - "version": "1.10.12", - "resolved": "https://registry.npmjs.org/eslint-config-turbo/-/eslint-config-turbo-1.10.12.tgz", - "integrity": "sha512-z3jfh+D7UGYlzMWGh+Kqz++hf8LOE96q3o5R8X4HTjmxaBWlLAWG+0Ounr38h+JLR2TJno0hU9zfzoPNkR9BdA==", - "dependencies": { - "eslint-plugin-turbo": "1.10.12" - }, - "peerDependencies": { - "eslint": ">6.6.0" - } - }, "node_modules/eslint-plugin-prettier": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", @@ -8940,25 +8259,6 @@ } } }, - "node_modules/eslint-plugin-turbo": { - "version": "1.10.12", - "resolved": "https://registry.npmjs.org/eslint-plugin-turbo/-/eslint-plugin-turbo-1.10.12.tgz", - "integrity": "sha512-uNbdj+ohZaYo4tFJ6dStRXu2FZigwulR1b3URPXe0Q8YaE7thuekKNP+54CHtZPH9Zey9dmDx5btAQl9mfzGOw==", - "dependencies": { - "dotenv": "16.0.3" - }, - "peerDependencies": { - "eslint": ">6.6.0" - } - }, - "node_modules/eslint-plugin-turbo/node_modules/dotenv": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", - "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", - "engines": { - "node": ">=12" - } - }, "node_modules/eslint-plugin-unicorn": { "version": "55.0.0", "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-55.0.0.tgz", @@ -8996,6 +8296,7 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", + "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -9011,6 +8312,7 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -9022,6 +8324,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -9037,6 +8340,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -9048,6 +8352,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -9058,12 +8363,14 @@ "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/espree": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", + "dev": true, "dependencies": { "acorn": "^8.12.0", "acorn-jsx": "^5.3.2", @@ -9080,6 +8387,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -9104,6 +8412,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, "dependencies": { "estraverse": "^5.1.0" }, @@ -9115,6 +8424,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -9126,6 +8436,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, "engines": { "node": ">=4.0" } @@ -9143,6 +8454,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -9360,7 +8672,8 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, "node_modules/fast-diff": { "version": "1.3.0", @@ -9391,12 +8704,14 @@ "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true }, "node_modules/fast-safe-stringify": { "version": "2.1.1", @@ -9437,6 +8752,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, "dependencies": { "flat-cache": "^4.0.0" }, @@ -9497,6 +8813,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -9512,6 +8829,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" @@ -9523,7 +8841,8 @@ "node_modules/flatted": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true }, "node_modules/fluent-ffmpeg": { "version": "2.1.3", @@ -9604,41 +8923,6 @@ "node": ">= 0.6" } }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/framer-motion": { - "version": "10.17.4", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.17.4.tgz", - "integrity": "sha512-CYBSs6cWfzcasAX8aofgKFZootmkQtR4qxbfTOksBLny/lbUfkGbQAFOS3qnl6Uau1N9y8tUpI7mVIrHgkFjLQ==", - "dependencies": { - "tslib": "^2.4.0" - }, - "optionalDependencies": { - "@emotion/is-prop-valid": "^0.8.2" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -9874,14 +9158,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "engines": { - "node": ">=6" - } - }, "node_modules/get-port": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", @@ -9954,7 +9230,8 @@ "node_modules/glob-to-regexp": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true }, "node_modules/glob/node_modules/brace-expansion": { "version": "2.0.1", @@ -10303,6 +9580,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, "engines": { "node": ">= 4" } @@ -10337,6 +9615,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, "engines": { "node": ">=0.8.19" } @@ -10394,14 +9673,6 @@ "node": ">=12.0.0" } }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/ioredis": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.1.tgz", @@ -10522,6 +9793,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, "engines": { "node": ">=8" } @@ -10652,6 +9924,7 @@ "version": "1.21.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -10765,7 +10038,8 @@ "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", @@ -10781,7 +10055,8 @@ "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true }, "node_modules/json5": { "version": "2.2.3", @@ -10816,6 +10091,7 @@ "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, "dependencies": { "json-buffer": "3.0.1" } @@ -10870,6 +10146,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -10887,6 +10164,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "peer": true, "engines": { "node": ">=10" } @@ -10909,6 +10187,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, "engines": { "node": ">=6.11.5" } @@ -10917,6 +10196,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -10976,6 +10256,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "peer": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -11110,7 +10391,8 @@ "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true }, "node_modules/merge2": { "version": "1.4.1", @@ -11440,7 +10722,8 @@ "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true }, "node_modules/nearley": { "version": "2.20.1", @@ -11548,12 +10831,12 @@ } }, "node_modules/next": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/next/-/next-14.1.4.tgz", - "integrity": "sha512-1WTaXeSrUwlz/XcnhGTY7+8eiaFvdet5z9u3V2jb+Ek1vFo0VhHKSAIJvDWfQpttWjnyw14kBeq28TPq7bTeEQ==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.3.tgz", + "integrity": "sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==", "dependencies": { - "@next/env": "14.1.4", - "@swc/helpers": "0.5.2", + "@next/env": "14.2.3", + "@swc/helpers": "0.5.5", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "graceful-fs": "^4.2.11", @@ -11567,18 +10850,19 @@ "node": ">=18.17.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.1.4", - "@next/swc-darwin-x64": "14.1.4", - "@next/swc-linux-arm64-gnu": "14.1.4", - "@next/swc-linux-arm64-musl": "14.1.4", - "@next/swc-linux-x64-gnu": "14.1.4", - "@next/swc-linux-x64-musl": "14.1.4", - "@next/swc-win32-arm64-msvc": "14.1.4", - "@next/swc-win32-ia32-msvc": "14.1.4", - "@next/swc-win32-x64-msvc": "14.1.4" + "@next/swc-darwin-arm64": "14.2.3", + "@next/swc-darwin-x64": "14.2.3", + "@next/swc-linux-arm64-gnu": "14.2.3", + "@next/swc-linux-arm64-musl": "14.2.3", + "@next/swc-linux-x64-gnu": "14.2.3", + "@next/swc-linux-x64-musl": "14.2.3", + "@next/swc-win32-arm64-msvc": "14.2.3", + "@next/swc-win32-ia32-msvc": "14.2.3", + "@next/swc-win32-x64-msvc": "14.2.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", "react": "^18.2.0", "react-dom": "^18.2.0", "sass": "^1.3.0" @@ -11587,6 +10871,9 @@ "@opentelemetry/api": { "optional": true }, + "@playwright/test": { + "optional": true + }, "sass": { "optional": true } @@ -11719,14 +11006,6 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/notepack.io": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/notepack.io/-/notepack.io-3.0.1.tgz", @@ -11895,6 +11174,7 @@ "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, "dependencies": { "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", @@ -11941,6 +11221,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -11955,6 +11236,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -12049,6 +11331,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, "engines": { "node": ">=8" } @@ -12263,6 +11546,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -12271,6 +11555,7 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "peer": true, "engines": { "node": ">= 6" } @@ -12315,6 +11600,7 @@ "version": "15.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "peer": true, "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", @@ -12331,6 +11617,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "peer": true, "dependencies": { "camelcase-css": "^2.0.1" }, @@ -12359,6 +11646,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "lilconfig": "^3.0.0", "yaml": "^2.3.4" @@ -12383,6 +11671,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "peer": true, "engines": { "node": ">=14" }, @@ -12394,6 +11683,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "peer": true, "dependencies": { "postcss-selector-parser": "^6.0.11" }, @@ -12412,6 +11702,7 @@ "version": "6.0.16", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -12423,7 +11714,8 @@ "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "peer": true }, "node_modules/postgres-array": { "version": "2.0.0", @@ -12469,6 +11761,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, "engines": { "node": ">= 0.8.0" } @@ -12519,26 +11812,6 @@ } } }, - "node_modules/prism-react-renderer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.1.0.tgz", - "integrity": "sha512-I5cvXHjA1PVGbGm1MsWCpvBCRrYyxEri0MC7/JbfIfYfcXAxHyO5PaUjs3A8H5GW6kJcLhTHxxMaOZZpRZD2iQ==", - "dependencies": { - "@types/prismjs": "^1.26.0", - "clsx": "^1.2.1" - }, - "peerDependencies": { - "react": ">=16.0.0" - } - }, - "node_modules/prism-react-renderer/node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "engines": { - "node": ">=6" - } - }, "node_modules/prismjs": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", @@ -12664,6 +11937,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, "engines": { "node": ">=6" } @@ -12729,6 +12003,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, "dependencies": { "safe-buffer": "^5.1.0" } @@ -12759,6 +12034,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -12770,6 +12046,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -12779,53 +12056,27 @@ } }, "node_modules/react-email": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/react-email/-/react-email-2.1.6.tgz", - "integrity": "sha512-BtR9VI1CMq4953wfiBmzupKlWcRThaWG2dDgl1vWAllK3tNNmJNerwY4VlmASRDQZE3LpLXU3+lf8N/VAKdbZQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/react-email/-/react-email-3.0.1.tgz", + "integrity": "sha512-G4Bkx2ULIScy/0Z8nnWywHt0W1iTkaYCdh9rWNuQ3eVZ6B3ttTUDE9uUy3VNQ8dtQbmG0cpt8+XmImw7mMBW6Q==", "dependencies": { "@babel/core": "7.24.5", "@babel/parser": "7.24.5", - "@radix-ui/colors": "1.0.1", - "@radix-ui/react-collapsible": "1.1.0", - "@radix-ui/react-popover": "1.1.1", - "@radix-ui/react-slot": "1.1.0", - "@radix-ui/react-toggle-group": "1.1.0", - "@radix-ui/react-tooltip": "1.1.1", - "@swc/core": "1.3.101", - "@types/react": "18.2.47", - "@types/react-dom": "^18.2.0", - "@types/webpack": "5.28.5", - "autoprefixer": "10.4.14", "chalk": "4.1.2", - "chokidar": "3.5.3", - "clsx": "2.1.0", + "chokidar": "3.6.0", "commander": "11.1.0", "debounce": "2.0.0", "esbuild": "0.19.11", - "eslint-config-prettier": "9.0.0", - "eslint-config-turbo": "1.10.12", - "framer-motion": "10.17.4", "glob": "10.3.4", "log-symbols": "4.1.0", "mime-types": "2.1.35", - "next": "14.1.4", + "next": "14.2.3", "normalize-path": "3.0.0", "ora": "5.4.1", - "postcss": "8.4.38", - "prism-react-renderer": "2.1.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "socket.io": "4.7.3", - "socket.io-client": "4.7.3", - "sonner": "1.3.1", - "source-map-js": "1.0.2", - "stacktrace-parser": "0.1.10", - "tailwind-merge": "2.2.0", - "tailwindcss": "3.4.0", - "typescript": "5.1.6" + "socket.io": "4.7.5" }, "bin": { - "email": "cli/index.js" + "email": "dist/cli/index.js" }, "engines": { "node": ">=18.0.0" @@ -13176,208 +12427,6 @@ "node": ">=12" } }, - "node_modules/react-email/node_modules/@swc/core": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.101.tgz", - "integrity": "sha512-w5aQ9qYsd/IYmXADAnkXPGDMTqkQalIi+kfFf/MHRKTpaOL7DHjMXwPp/n8hJ0qNjRvchzmPtOqtPBiER50d8A==", - "hasInstallScript": true, - "dependencies": { - "@swc/counter": "^0.1.1", - "@swc/types": "^0.1.5" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/swc" - }, - "optionalDependencies": { - "@swc/core-darwin-arm64": "1.3.101", - "@swc/core-darwin-x64": "1.3.101", - "@swc/core-linux-arm-gnueabihf": "1.3.101", - "@swc/core-linux-arm64-gnu": "1.3.101", - "@swc/core-linux-arm64-musl": "1.3.101", - "@swc/core-linux-x64-gnu": "1.3.101", - "@swc/core-linux-x64-musl": "1.3.101", - "@swc/core-win32-arm64-msvc": "1.3.101", - "@swc/core-win32-ia32-msvc": "1.3.101", - "@swc/core-win32-x64-msvc": "1.3.101" - }, - "peerDependencies": { - "@swc/helpers": "^0.5.0" - }, - "peerDependenciesMeta": { - "@swc/helpers": { - "optional": true - } - } - }, - "node_modules/react-email/node_modules/@swc/core-darwin-arm64": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.101.tgz", - "integrity": "sha512-mNFK+uHNPRXSnfTOG34zJOeMl2waM4hF4a2NY7dkMXrPqw9CoJn4MwTXJcyMiSz1/BnNjjTCHF3Yhj0jPxmkzQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/react-email/node_modules/@swc/core-darwin-x64": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.101.tgz", - "integrity": "sha512-B085j8XOx73Fg15KsHvzYWG262bRweGr3JooO1aW5ec5pYbz5Ew9VS5JKYS03w2UBSxf2maWdbPz2UFAxg0whw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/react-email/node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.101.tgz", - "integrity": "sha512-9xLKRb6zSzRGPqdz52Hy5GuB1lSjmLqa0lST6MTFads3apmx4Vgs8Y5NuGhx/h2I8QM4jXdLbpqQlifpzTlSSw==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/react-email/node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.101.tgz", - "integrity": "sha512-oE+r1lo7g/vs96Weh2R5l971dt+ZLuhaUX+n3BfDdPxNHfObXgKMjO7E+QS5RbGjv/AwiPCxQmbdCp/xN5ICJA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/react-email/node_modules/@swc/core-linux-arm64-musl": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.101.tgz", - "integrity": "sha512-OGjYG3H4BMOTnJWJyBIovCez6KiHF30zMIu4+lGJTCrxRI2fAjGLml3PEXj8tC3FMcud7U2WUn6TdG0/te2k6g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/react-email/node_modules/@swc/core-linux-x64-gnu": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.101.tgz", - "integrity": "sha512-/kBMcoF12PRO/lwa8Z7w4YyiKDcXQEiLvM+S3G9EvkoKYGgkkz4Q6PSNhF5rwg/E3+Hq5/9D2R+6nrkF287ihg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/react-email/node_modules/@swc/core-linux-x64-musl": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.101.tgz", - "integrity": "sha512-kDN8lm4Eew0u1p+h1l3JzoeGgZPQ05qDE0czngnjmfpsH2sOZxVj1hdiCwS5lArpy7ktaLu5JdRnx70MkUzhXw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/react-email/node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.101.tgz", - "integrity": "sha512-9Wn8TTLWwJKw63K/S+jjrZb9yoJfJwCE2RV5vPCCWmlMf3U1AXj5XuWOLUX+Rp2sGKau7wZKsvywhheWm+qndQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/react-email/node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.101.tgz", - "integrity": "sha512-onO5KvICRVlu2xmr4//V2je9O2XgS1SGKpbX206KmmjcJhXN5EYLSxW9qgg+kgV5mip+sKTHTAu7IkzkAtElYA==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/react-email/node_modules/@swc/core-win32-x64-msvc": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.101.tgz", - "integrity": "sha512-T3GeJtNQV00YmiVw/88/nxJ/H43CJvFnpvBHCVn17xbahiVUOPOduh3rc9LgAkKiNt/aV8vU3OJR+6PhfMR7UQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/react-email/node_modules/@types/react": { - "version": "18.2.47", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.47.tgz", - "integrity": "sha512-xquNkkOirwyCgoClNk85BjP+aqnIS+ckAJ8i37gAbDs14jfW/J23f2GItAf33oiUPQnqNMALiFeoM9Y5mbjpVQ==", - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/react-email/node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" - }, "node_modules/react-email/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -13386,32 +12435,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/react-email/node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, "node_modules/react-email/node_modules/commander": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", @@ -13457,17 +12480,6 @@ "@esbuild/win32-x64": "0.19.11" } }, - "node_modules/react-email/node_modules/eslint-config-prettier": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", - "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, "node_modules/react-email/node_modules/glob": { "version": "10.3.4", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.4.tgz", @@ -13503,90 +12515,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/react-email/node_modules/socket.io": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.3.tgz", - "integrity": "sha512-SE+UIQXBQE+GPG2oszWMlsEmWtHVqw/h1VrYJGK5/MC7CH5p58N448HwIrtREcvR4jfdOJAY4ieQfxMr55qbbw==", - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.5.2", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/react-email/node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-email/node_modules/tailwindcss": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.0.tgz", - "integrity": "sha512-VigzymniH77knD1dryXbyxR+ePHihHociZbXnLZHUyzf2MMs2ZVqlUrZ3FvpXP8pno9JzmILt1sZPD19M3IxtA==", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.0", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.19.1", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/react-email/node_modules/tailwindcss/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/react-email/node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/react-promise-suspense": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/react-promise-suspense/-/react-promise-suspense-0.3.4.tgz", @@ -13600,77 +12528,11 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==" }, - "node_modules/react-remove-scroll": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz", - "integrity": "sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==", - "dependencies": { - "react-remove-scroll-bar": "^2.3.4", - "react-style-singleton": "^2.2.1", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-remove-scroll-bar": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", - "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", - "dependencies": { - "react-style-singleton": "^2.2.1", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-style-singleton": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", - "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", - "dependencies": { - "get-nonce": "^1.0.0", - "invariant": "^2.2.4", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "peer": true, "dependencies": { "pify": "^2.3.0" } @@ -13863,11 +12725,6 @@ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, "node_modules/regexp-tree": { "version": "0.1.27", "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", @@ -14251,6 +13108,7 @@ "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" } @@ -14259,6 +13117,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -14276,6 +13135,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -14291,6 +13151,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, "peerDependencies": { "ajv": "^6.9.1" } @@ -14298,7 +13159,8 @@ "node_modules/schema-utils/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/selderee": { "version": "0.11.0", @@ -14367,6 +13229,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, "dependencies": { "randombytes": "^2.1.0" } @@ -14624,20 +13487,6 @@ } } }, - "node_modules/socket.io-client": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.3.tgz", - "integrity": "sha512-nU+ywttCyBitXIl9Xe0RSEfek4LneYkJxCeNnKCuhwoH4jGXO1ipIUw/VA/+Vvv2G1MTym11fzFC0SxkrcfXDw==", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.2", - "engine.io-client": "~6.5.2", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", @@ -14650,15 +13499,6 @@ "node": ">=10.0.0" } }, - "node_modules/sonner": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.3.1.tgz", - "integrity": "sha512-+rOAO56b2eI3q5BtgljERSn2umRk63KFIvgb2ohbZ5X+Eb5u+a/7/0ZgswYqgBMg8dyl7n6OXd9KasA8QF9ToA==", - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, "node_modules/source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -14680,6 +13520,7 @@ "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -14689,6 +13530,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -14787,25 +13629,6 @@ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true }, - "node_modules/stacktrace-parser": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", - "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", - "dependencies": { - "type-fest": "^0.7.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/stacktrace-parser/node_modules/type-fest": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", - "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", - "engines": { - "node": ">=8" - } - }, "node_modules/standard-as-callback": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", @@ -14937,6 +13760,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, "engines": { "node": ">=8" }, @@ -14970,6 +13794,7 @@ "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", @@ -15064,18 +13889,6 @@ "url": "https://www.buymeacoffee.com/systeminfo" } }, - "node_modules/tailwind-merge": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.0.tgz", - "integrity": "sha512-SqqhhaL0T06SW59+JVNfAqKdqLs0497esifRrZ7jOaefP3o64fdFNDMrAQWZFMxTLJPiHVjRLUywT8uFz1xNWQ==", - "dependencies": { - "@babel/runtime": "^7.23.5" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" - } - }, "node_modules/tailwindcss": { "version": "3.4.6", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.6.tgz", @@ -15166,6 +13979,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, "engines": { "node": ">=6" } @@ -15238,6 +14052,7 @@ "version": "5.27.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==", + "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -15255,6 +14070,7 @@ "version": "5.3.10", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", @@ -15288,6 +14104,7 @@ "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -15301,6 +14118,7 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -15314,7 +14132,8 @@ "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true }, "node_modules/test-exclude": { "version": "7.0.1", @@ -15403,7 +14222,8 @@ "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true }, "node_modules/thenify": { "version": "3.3.1", @@ -15550,7 +14370,8 @@ "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "peer": true }, "node_modules/ts-node": { "version": "10.9.2", @@ -15668,6 +14489,7 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -16042,51 +14864,11 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "dependencies": { "punycode": "^2.1.0" } }, - "node_modules/use-callback-ref": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", - "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-sidecar": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", - "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/utf8-byte-length": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", @@ -16343,6 +15125,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", + "dev": true, "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -16368,6 +15151,7 @@ "version": "5.92.1", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.92.1.tgz", "integrity": "sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==", + "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", @@ -16423,6 +15207,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, "engines": { "node": ">=10.13.0" } @@ -16437,6 +15222,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -16449,6 +15235,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, "engines": { "node": ">=4.0" } @@ -16560,14 +15347,6 @@ } } }, - "node_modules/xmlhttprequest-ssl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", - "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -16644,6 +15423,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, "engines": { "node": ">=10" }, @@ -16707,12 +15487,14 @@ "@aashutoshrathi/word-wrap": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==" + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true }, "@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==" + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "peer": true }, "@ampproject/remapping": { "version": "2.3.0", @@ -17050,14 +15832,6 @@ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==" }, - "@babel/runtime": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", - "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", - "requires": { - "regenerator-runtime": "^0.14.0" - } - }, "@babel/template": { "version": "7.24.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.6.tgz", @@ -17165,21 +15939,6 @@ "tslib": "^2.4.0" } }, - "@emotion/is-prop-valid": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", - "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", - "optional": true, - "requires": { - "@emotion/memoize": "0.7.4" - } - }, - "@emotion/memoize": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", - "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", - "optional": true - }, "@esbuild/aix-ppc64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", @@ -17345,6 +16104,7 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, "requires": { "eslint-visitor-keys": "^3.3.0" } @@ -17352,12 +16112,14 @@ "@eslint-community/regexpp": { "version": "4.11.0", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==" + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "dev": true }, "@eslint/config-array": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.1.tgz", "integrity": "sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==", + "dev": true, "requires": { "@eslint/object-schema": "^2.1.4", "debug": "^4.3.1", @@ -17368,6 +16130,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -17384,6 +16147,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -17394,24 +16158,28 @@ "globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==" + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true } } }, "@eslint/js": { "version": "9.8.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.8.0.tgz", - "integrity": "sha512-MfluB7EUfxXtv3i/++oh89uzAr4PDI4nn201hsp+qaXqsjAWzinlZEHEfPgAX4doIlKvPG/i0A9dpKxOLII8yA==" + "integrity": "sha512-MfluB7EUfxXtv3i/++oh89uzAr4PDI4nn201hsp+qaXqsjAWzinlZEHEfPgAX4doIlKvPG/i0A9dpKxOLII8yA==", + "dev": true }, "@eslint/object-schema": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", - "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==" + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true }, "@fastify/busboy": { "version": "2.1.1", @@ -17419,36 +16187,6 @@ "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", "dev": true }, - "@floating-ui/core": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.4.tgz", - "integrity": "sha512-a4IowK4QkXl4SCWTGUR0INAfEOX3wtsYw3rKK5InQEHMGObkR8Xk44qYQD9P4r6HHw0iIfK6GUKECmY8sTkqRA==", - "requires": { - "@floating-ui/utils": "^0.2.4" - } - }, - "@floating-ui/dom": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.7.tgz", - "integrity": "sha512-wmVfPG5o2xnKDU4jx/m4w5qva9FWHcnZ8BvzEe90D/RpwsJaTAVYPEPdQ8sbr/N8zZTAHlZUTQdqg8ZUbzHmng==", - "requires": { - "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.4" - } - }, - "@floating-ui/react-dom": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.1.tgz", - "integrity": "sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==", - "requires": { - "@floating-ui/dom": "^1.0.0" - } - }, - "@floating-ui/utils": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.4.tgz", - "integrity": "sha512-dWO2pw8hhi+WrXq1YJy2yCuWoL20PddgGaqTgVe4cOS9Q6qklXCiA1tJEqX6BEwRNSCP84/afac9hd4MS+zEUA==" - }, "@golevelup/nestjs-discovery": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@golevelup/nestjs-discovery/-/nestjs-discovery-4.0.1.tgz", @@ -17529,12 +16267,14 @@ "@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==" + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true }, "@humanwhocodes/retry": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", - "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==" + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "dev": true }, "@img/sharp-darwin-arm64": { "version": "0.33.4", @@ -17770,6 +16510,7 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "dev": true, "requires": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -18056,62 +16797,62 @@ } }, "@next/env": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.4.tgz", - "integrity": "sha512-e7X7bbn3Z6DWnDi75UWn+REgAbLEqxI8Tq2pkFOFAMpWAWApz/YCUhtWMWn410h8Q2fYiYL7Yg5OlxMOCfFjJQ==" + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz", + "integrity": "sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==" }, "@next/swc-darwin-arm64": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.4.tgz", - "integrity": "sha512-ubmUkbmW65nIAOmoxT1IROZdmmJMmdYvXIe8211send9ZYJu+SqxSnJM4TrPj9wmL6g9Atvj0S/2cFmMSS99jg==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.3.tgz", + "integrity": "sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==", "optional": true }, "@next/swc-darwin-x64": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.4.tgz", - "integrity": "sha512-b0Xo1ELj3u7IkZWAKcJPJEhBop117U78l70nfoQGo4xUSvv0PJSTaV4U9xQBLvZlnjsYkc8RwQN1HoH/oQmLlQ==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz", + "integrity": "sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==", "optional": true }, "@next/swc-linux-arm64-gnu": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.4.tgz", - "integrity": "sha512-457G0hcLrdYA/u1O2XkRMsDKId5VKe3uKPvrKVOyuARa6nXrdhJOOYU9hkKKyQTMru1B8qEP78IAhf/1XnVqKA==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz", + "integrity": "sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==", "optional": true }, "@next/swc-linux-arm64-musl": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.4.tgz", - "integrity": "sha512-l/kMG+z6MB+fKA9KdtyprkTQ1ihlJcBh66cf0HvqGP+rXBbOXX0dpJatjZbHeunvEHoBBS69GYQG5ry78JMy3g==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz", + "integrity": "sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==", "optional": true }, "@next/swc-linux-x64-gnu": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.4.tgz", - "integrity": "sha512-BapIFZ3ZRnvQ1uWbmqEGJuPT9cgLwvKtxhK/L2t4QYO7l+/DxXuIGjvp1x8rvfa/x1FFSsipERZK70pewbtJtw==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz", + "integrity": "sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==", "optional": true }, "@next/swc-linux-x64-musl": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.4.tgz", - "integrity": "sha512-mqVxTwk4XuBl49qn2A5UmzFImoL1iLm0KQQwtdRJRKl21ylQwwGCxJtIYo2rbfkZHoSKlh/YgztY0qH3wG1xIg==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz", + "integrity": "sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==", "optional": true }, "@next/swc-win32-arm64-msvc": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.4.tgz", - "integrity": "sha512-xzxF4ErcumXjO2Pvg/wVGrtr9QQJLk3IyQX1ddAC/fi6/5jZCZ9xpuL9Tzc4KPWMFq8GGWFVDMshZOdHGdkvag==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz", + "integrity": "sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==", "optional": true }, "@next/swc-win32-ia32-msvc": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.4.tgz", - "integrity": "sha512-WZiz8OdbkpRw6/IU/lredZWKKZopUMhcI2F+XiMAcPja0uZYdMTZQRoQ0WZcvinn9xZAidimE7tN9W5v9Yyfyw==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz", + "integrity": "sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==", "optional": true }, "@next/swc-win32-x64-msvc": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.4.tgz", - "integrity": "sha512-4Rto21sPfw555sZ/XNLqfxDUNeLhNYGO2dlPqsnuCg8N8a2a9u1ltqBOPQ4vj1Gf7eJC0W2hHG2eYUHuiXgY2w==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz", + "integrity": "sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==", "optional": true }, "@nodelib/fs.scandir": { @@ -19267,185 +18008,12 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, - "@radix-ui/colors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/colors/-/colors-1.0.1.tgz", - "integrity": "sha512-xySw8f0ZVsAEP+e7iLl3EvcBXX7gsIlC1Zso/sPBW9gIWerBTgz6axrjU+MZ39wD+WFi5h5zdWpsg3+hwt2Qsg==" - }, - "@radix-ui/primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", - "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==" - }, - "@radix-ui/react-arrow": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz", - "integrity": "sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==", - "requires": { - "@radix-ui/react-primitive": "2.0.0" - } - }, - "@radix-ui/react-collapsible": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.0.tgz", - "integrity": "sha512-zQY7Epa8sTL0mq4ajSJpjgn2YmCgyrG7RsQgLp3C0LQVkG7+Tf6Pv1CeNWZLyqMjhdPkBa5Lx7wYBeSu7uCSTA==", - "requires": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-presence": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0" - } - }, - "@radix-ui/react-collection": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", - "integrity": "sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==", - "requires": { - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-slot": "1.1.0" - } - }, "@radix-ui/react-compose-refs": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", "integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==", "requires": {} }, - "@radix-ui/react-context": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", - "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", - "requires": {} - }, - "@radix-ui/react-direction": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", - "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", - "requires": {} - }, - "@radix-ui/react-dismissable-layer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.0.tgz", - "integrity": "sha512-/UovfmmXGptwGcBQawLzvn2jOfM0t4z3/uKffoBlj724+n3FvBbZ7M0aaBOmkp6pqFYpO4yx8tSVJjx3Fl2jig==", - "requires": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-escape-keydown": "1.1.0" - } - }, - "@radix-ui/react-focus-guards": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.0.tgz", - "integrity": "sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==", - "requires": {} - }, - "@radix-ui/react-focus-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz", - "integrity": "sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==", - "requires": { - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0" - } - }, - "@radix-ui/react-id": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", - "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", - "requires": { - "@radix-ui/react-use-layout-effect": "1.1.0" - } - }, - "@radix-ui/react-popover": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.1.tgz", - "integrity": "sha512-3y1A3isulwnWhvTTwmIreiB8CF4L+qRjZnK1wYLO7pplddzXKby/GnZ2M7OZY3qgnl6p9AodUIHRYGXNah8Y7g==", - "requires": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-dismissable-layer": "1.1.0", - "@radix-ui/react-focus-guards": "1.1.0", - "@radix-ui/react-focus-scope": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.0", - "@radix-ui/react-portal": "1.1.1", - "@radix-ui/react-presence": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-slot": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.7" - } - }, - "@radix-ui/react-popper": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz", - "integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==", - "requires": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-use-rect": "1.1.0", - "@radix-ui/react-use-size": "1.1.0", - "@radix-ui/rect": "1.1.0" - } - }, - "@radix-ui/react-portal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.1.tgz", - "integrity": "sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g==", - "requires": { - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-layout-effect": "1.1.0" - } - }, - "@radix-ui/react-presence": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.0.tgz", - "integrity": "sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==", - "requires": { - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0" - } - }, - "@radix-ui/react-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz", - "integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==", - "requires": { - "@radix-ui/react-slot": "1.1.0" - } - }, - "@radix-ui/react-roving-focus": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz", - "integrity": "sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==", - "requires": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-collection": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0" - } - }, "@radix-ui/react-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", @@ -19454,106 +18022,6 @@ "@radix-ui/react-compose-refs": "1.1.0" } }, - "@radix-ui/react-toggle": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.0.tgz", - "integrity": "sha512-gwoxaKZ0oJ4vIgzsfESBuSgJNdc0rv12VhHgcqN0TEJmmZixXG/2XpsLK8kzNWYcnaoRIEEQc0bEi3dIvdUpjw==", - "requires": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-controllable-state": "1.1.0" - } - }, - "@radix-ui/react-toggle-group": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.0.tgz", - "integrity": "sha512-PpTJV68dZU2oqqgq75Uzto5o/XfOVgkrJ9rulVmfTKxWp3HfUjHE6CP/WLRR4AzPX9HWxw7vFow2me85Yu+Naw==", - "requires": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-roving-focus": "1.1.0", - "@radix-ui/react-toggle": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0" - } - }, - "@radix-ui/react-tooltip": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.1.tgz", - "integrity": "sha512-LLE8nzNE4MzPMw3O2zlVlkLFid3y9hMUs7uCbSHyKSo+tCN4yMCf+ZCCcfrYgsOC0TiHBPQ1mtpJ2liY3ZT3SQ==", - "requires": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-dismissable-layer": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.0", - "@radix-ui/react-portal": "1.1.1", - "@radix-ui/react-presence": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-slot": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-visually-hidden": "1.1.0" - } - }, - "@radix-ui/react-use-callback-ref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", - "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", - "requires": {} - }, - "@radix-ui/react-use-controllable-state": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", - "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", - "requires": { - "@radix-ui/react-use-callback-ref": "1.1.0" - } - }, - "@radix-ui/react-use-escape-keydown": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", - "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==", - "requires": { - "@radix-ui/react-use-callback-ref": "1.1.0" - } - }, - "@radix-ui/react-use-layout-effect": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", - "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", - "requires": {} - }, - "@radix-ui/react-use-rect": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", - "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", - "requires": { - "@radix-ui/rect": "1.1.0" - } - }, - "@radix-ui/react-use-size": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", - "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", - "requires": { - "@radix-ui/react-use-layout-effect": "1.1.0" - } - }, - "@radix-ui/react-visually-hidden": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.0.tgz", - "integrity": "sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==", - "requires": { - "@radix-ui/react-primitive": "2.0.0" - } - }, - "@radix-ui/rect": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", - "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==" - }, "@react-email/body": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/@react-email/body/-/body-0.0.9.tgz", @@ -19991,10 +18459,11 @@ "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" }, "@swc/helpers": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", - "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", "requires": { + "@swc/counter": "^0.1.3", "tslib": "^2.4.0" } }, @@ -20002,6 +18471,7 @@ "version": "0.1.12", "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.12.tgz", "integrity": "sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==", + "devOptional": true, "requires": { "@swc/counter": "^0.1.3" } @@ -20173,6 +18643,7 @@ "version": "8.44.3", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.3.tgz", "integrity": "sha512-iM/WfkwAhwmPff3wZuPLYiHX18HI24jU8k1ZSH7P8FHwxTjZ2P6CoX2wnF43oprR+YXJM6UUxATkNvyv/JHd+g==", + "dev": true, "requires": { "@types/estree": "*", "@types/json-schema": "*" @@ -20182,6 +18653,7 @@ "version": "3.7.5", "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.5.tgz", "integrity": "sha512-JNvhIEyxVW6EoMIFIvj93ZOywYFatlpu9deeH6eSx6PE3WHYvHaQtmHmQeNw7aA81bYGBPPQqdtBm6b1SsQMmA==", + "dev": true, "requires": { "@types/eslint": "*", "@types/estree": "*" @@ -20190,7 +18662,8 @@ "@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true }, "@types/express": { "version": "4.17.21", @@ -20250,7 +18723,8 @@ "@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true }, "@types/lodash": { "version": "4.17.7", @@ -20395,15 +18869,12 @@ "integrity": "sha512-1MRgzpzY0hOp9pW/kLRxeQhUWwil6gnrUYd3oEpeYBqp/FexhaCPv3F8LsYr47gtUU45fO2cm1dbwkSrHEo8Uw==", "dev": true }, - "@types/prismjs": { - "version": "1.26.3", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.3.tgz", - "integrity": "sha512-A0D0aTXvjlqJ5ZILMz3rNfDBOx9hHxLZYv2by47Sm/pqW35zzjusrZTryatjN/Rf8Us2gZrJD+KeHbUSTux1Cw==" - }, "@types/prop-types": { "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", + "optional": true, + "peer": true }, "@types/qs": { "version": "6.9.8", @@ -20421,19 +18892,13 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz", "integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==", + "optional": true, + "peer": true, "requires": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, - "@types/react-dom": { - "version": "18.3.0", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", - "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", - "requires": { - "@types/react": "*" - } - }, "@types/readdir-glob": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.2.tgz", @@ -20443,11 +18908,6 @@ "@types/node": "*" } }, - "@types/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==" - }, "@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", @@ -20548,16 +19008,6 @@ "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.8.tgz", "integrity": "sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ==" }, - "@types/webpack": { - "version": "5.28.5", - "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.5.tgz", - "integrity": "sha512-wR87cgvxj3p6D0Crt1r5avwqffqPXUkNlnQ1mjU93G7gCuFjufZR4I6j8cz5g1F1tTYpfOOFvly+cmIQwL9wvw==", - "requires": { - "@types/node": "*", - "tapable": "^2.2.0", - "webpack": "^5" - } - }, "@typescript-eslint/eslint-plugin": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.1.tgz", @@ -20783,6 +19233,7 @@ "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "dev": true, "requires": { "@webassemblyjs/helper-numbers": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6" @@ -20791,22 +19242,26 @@ "@webassemblyjs/floating-point-hex-parser": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true }, "@webassemblyjs/helper-api-error": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true }, "@webassemblyjs/helper-buffer": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==" + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "dev": true }, "@webassemblyjs/helper-numbers": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dev": true, "requires": { "@webassemblyjs/floating-point-hex-parser": "1.11.6", "@webassemblyjs/helper-api-error": "1.11.6", @@ -20816,12 +19271,14 @@ "@webassemblyjs/helper-wasm-bytecode": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true }, "@webassemblyjs/helper-wasm-section": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "dev": true, "requires": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", @@ -20833,6 +19290,7 @@ "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, "requires": { "@xtuc/ieee754": "^1.2.0" } @@ -20841,6 +19299,7 @@ "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dev": true, "requires": { "@xtuc/long": "4.2.2" } @@ -20848,12 +19307,14 @@ "@webassemblyjs/utf8": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true }, "@webassemblyjs/wasm-edit": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "dev": true, "requires": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", @@ -20869,6 +19330,7 @@ "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "dev": true, "requires": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", @@ -20881,6 +19343,7 @@ "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "dev": true, "requires": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", @@ -20892,6 +19355,7 @@ "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "dev": true, "requires": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-api-error": "1.11.6", @@ -20905,6 +19369,7 @@ "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "dev": true, "requires": { "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" @@ -20913,12 +19378,14 @@ "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true }, "@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true }, "abbrev": { "version": "1.1.1", @@ -20957,6 +19424,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, "requires": {} }, "acorn-walk": { @@ -21165,14 +19633,6 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, - "aria-hidden": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", - "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", - "requires": { - "tslib": "^2.0.0" - } - }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -21220,19 +19680,6 @@ "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==" }, - "autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", - "requires": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", - "fraction.js": "^4.2.0", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - } - }, "b4a": { "version": "1.6.4", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz", @@ -21528,7 +19975,8 @@ "camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "peer": true }, "caniuse-lite": { "version": "1.0.30001618", @@ -21591,7 +20039,8 @@ "chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true }, "ci-info": { "version": "4.0.0", @@ -21709,11 +20158,6 @@ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==" }, - "clsx": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", - "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==" - }, "cluster-key-slot": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", @@ -22006,12 +20450,15 @@ "cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "peer": true }, "csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "optional": true, + "peer": true }, "dayjs": { "version": "1.11.10", @@ -22040,7 +20487,8 @@ "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true }, "deepmerge": { "version": "4.3.1", @@ -22091,11 +20539,6 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==" }, - "detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" - }, "diacritics": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/diacritics/-/diacritics-1.3.0.tgz", @@ -22104,7 +20547,8 @@ "didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "peer": true }, "diff": { "version": "4.0.2", @@ -22131,7 +20575,8 @@ "dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "peer": true }, "docker-compose": { "version": "0.24.8", @@ -22326,18 +20771,6 @@ "ws": "~8.11.0" } }, - "engine.io-client": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", - "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", - "requires": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.11.0", - "xmlhttprequest-ssl": "~2.0.0" - } - }, "engine.io-parser": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", @@ -22347,6 +20780,7 @@ "version": "5.17.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", + "dev": true, "requires": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -22381,7 +20815,8 @@ "es-module-lexer": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", - "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==" + "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==", + "dev": true }, "esbuild": { "version": "0.20.2", @@ -22427,12 +20862,14 @@ "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true }, "eslint": { "version": "9.8.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.8.0.tgz", "integrity": "sha512-K8qnZ/QJzT2dLKdZJVX6W4XOwBzutMYmt0lqUS+JdXgd+HTYFlonFgkJ8s44d/zMPPCnOOk0kMWCApCPhiOy9A==", + "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", @@ -22474,6 +20911,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -22484,12 +20922,14 @@ "eslint-visitor-keys": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==" + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true }, "glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, "requires": { "is-glob": "^4.0.3" } @@ -22497,7 +20937,8 @@ "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true } } }, @@ -22508,14 +20949,6 @@ "dev": true, "requires": {} }, - "eslint-config-turbo": { - "version": "1.10.12", - "resolved": "https://registry.npmjs.org/eslint-config-turbo/-/eslint-config-turbo-1.10.12.tgz", - "integrity": "sha512-z3jfh+D7UGYlzMWGh+Kqz++hf8LOE96q3o5R8X4HTjmxaBWlLAWG+0Ounr38h+JLR2TJno0hU9zfzoPNkR9BdA==", - "requires": { - "eslint-plugin-turbo": "1.10.12" - } - }, "eslint-plugin-prettier": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", @@ -22526,21 +20959,6 @@ "synckit": "^0.9.1" } }, - "eslint-plugin-turbo": { - "version": "1.10.12", - "resolved": "https://registry.npmjs.org/eslint-plugin-turbo/-/eslint-plugin-turbo-1.10.12.tgz", - "integrity": "sha512-uNbdj+ohZaYo4tFJ6dStRXu2FZigwulR1b3URPXe0Q8YaE7thuekKNP+54CHtZPH9Zey9dmDx5btAQl9mfzGOw==", - "requires": { - "dotenv": "16.0.3" - }, - "dependencies": { - "dotenv": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", - "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" - } - } - }, "eslint-plugin-unicorn": { "version": "55.0.0", "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-55.0.0.tgz", @@ -22569,6 +20987,7 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", + "dev": true, "requires": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -22577,12 +20996,14 @@ "eslint-visitor-keys": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==" + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true }, "espree": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", + "dev": true, "requires": { "acorn": "^8.12.0", "acorn-jsx": "^5.3.2", @@ -22592,7 +21013,8 @@ "eslint-visitor-keys": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==" + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true } } }, @@ -22606,6 +21028,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, "requires": { "estraverse": "^5.1.0" } @@ -22614,6 +21037,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, "requires": { "estraverse": "^5.2.0" } @@ -22621,7 +21045,8 @@ "estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true }, "estree-walker": { "version": "3.0.3", @@ -22635,7 +21060,8 @@ "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true }, "etag": { "version": "1.8.1", @@ -22804,7 +21230,8 @@ "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, "fast-diff": { "version": "1.3.0", @@ -22832,12 +21259,14 @@ "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true }, "fast-safe-stringify": { "version": "2.1.1", @@ -22871,6 +21300,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, "requires": { "flat-cache": "^4.0.0" } @@ -22924,6 +21354,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, "requires": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -22933,6 +21364,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, "requires": { "flatted": "^3.2.9", "keyv": "^4.5.4" @@ -22941,7 +21373,8 @@ "flatted": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true }, "fluent-ffmpeg": { "version": "2.1.3", @@ -23001,20 +21434,6 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" }, - "fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==" - }, - "framer-motion": { - "version": "10.17.4", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.17.4.tgz", - "integrity": "sha512-CYBSs6cWfzcasAX8aofgKFZootmkQtR4qxbfTOksBLny/lbUfkGbQAFOS3qnl6Uau1N9y8tUpI7mVIrHgkFjLQ==", - "requires": { - "@emotion/is-prop-valid": "^0.8.2", - "tslib": "^2.4.0" - } - }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -23193,11 +21612,6 @@ "hasown": "^2.0.0" } }, - "get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==" - }, "get-port": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", @@ -23267,7 +21681,8 @@ "glob-to-regexp": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true }, "globals": { "version": "15.9.0", @@ -23479,7 +21894,8 @@ "ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==" + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true }, "import-fresh": { "version": "3.3.0", @@ -23504,7 +21920,8 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true }, "indent-string": { "version": "4.0.0", @@ -23553,14 +21970,6 @@ "wrap-ansi": "^6.0.1" } }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "requires": { - "loose-envify": "^1.0.0" - } - }, "ioredis": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.1.tgz", @@ -23643,7 +22052,8 @@ "is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true }, "is-stream": { "version": "2.0.1", @@ -23731,7 +22141,8 @@ "jiti": { "version": "1.21.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", - "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==" + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "peer": true }, "joi": { "version": "17.13.3", @@ -23812,7 +22223,8 @@ "json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true }, "json-parse-even-better-errors": { "version": "2.3.1", @@ -23828,7 +22240,8 @@ "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true }, "json5": { "version": "2.2.3", @@ -23855,6 +22268,7 @@ "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, "requires": { "json-buffer": "3.0.1" } @@ -23905,6 +22319,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, "requires": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -23918,7 +22333,8 @@ "lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==" + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "peer": true }, "lines-and-columns": { "version": "1.2.4", @@ -23934,12 +22350,14 @@ "loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==" + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, "requires": { "p-locate": "^5.0.0" } @@ -23987,6 +22405,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "peer": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" } @@ -24090,7 +22509,8 @@ "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true }, "merge2": { "version": "1.4.1", @@ -24345,7 +22765,8 @@ "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true }, "nearley": { "version": "2.20.1", @@ -24421,21 +22842,21 @@ } }, "next": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/next/-/next-14.1.4.tgz", - "integrity": "sha512-1WTaXeSrUwlz/XcnhGTY7+8eiaFvdet5z9u3V2jb+Ek1vFo0VhHKSAIJvDWfQpttWjnyw14kBeq28TPq7bTeEQ==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.3.tgz", + "integrity": "sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==", "requires": { - "@next/env": "14.1.4", - "@next/swc-darwin-arm64": "14.1.4", - "@next/swc-darwin-x64": "14.1.4", - "@next/swc-linux-arm64-gnu": "14.1.4", - "@next/swc-linux-arm64-musl": "14.1.4", - "@next/swc-linux-x64-gnu": "14.1.4", - "@next/swc-linux-x64-musl": "14.1.4", - "@next/swc-win32-arm64-msvc": "14.1.4", - "@next/swc-win32-ia32-msvc": "14.1.4", - "@next/swc-win32-x64-msvc": "14.1.4", - "@swc/helpers": "0.5.2", + "@next/env": "14.2.3", + "@next/swc-darwin-arm64": "14.2.3", + "@next/swc-darwin-x64": "14.2.3", + "@next/swc-linux-arm64-gnu": "14.2.3", + "@next/swc-linux-arm64-musl": "14.2.3", + "@next/swc-linux-x64-gnu": "14.2.3", + "@next/swc-linux-x64-musl": "14.2.3", + "@next/swc-win32-arm64-msvc": "14.2.3", + "@next/swc-win32-ia32-msvc": "14.2.3", + "@next/swc-win32-x64-msvc": "14.2.3", + "@swc/helpers": "0.5.5", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "graceful-fs": "^4.2.11", @@ -24526,11 +22947,6 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" - }, "notepack.io": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/notepack.io/-/notepack.io-3.0.1.tgz", @@ -24658,6 +23074,7 @@ "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, "requires": { "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", @@ -24692,6 +23109,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, "requires": { "yocto-queue": "^0.1.0" } @@ -24700,6 +23118,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, "requires": { "p-limit": "^3.0.2" } @@ -24771,7 +23190,8 @@ "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true }, "path-is-absolute": { "version": "1.0.1", @@ -24927,12 +23347,14 @@ "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "peer": true }, "pirates": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==" + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "peer": true }, "pluralize": { "version": "8.0.0", @@ -24954,6 +23376,7 @@ "version": "15.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "peer": true, "requires": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", @@ -24964,6 +23387,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "peer": true, "requires": { "camelcase-css": "^2.0.1" } @@ -24972,6 +23396,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "peer": true, "requires": { "lilconfig": "^3.0.0", "yaml": "^2.3.4" @@ -24980,7 +23405,8 @@ "lilconfig": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", - "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==" + "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "peer": true } } }, @@ -24988,6 +23414,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "peer": true, "requires": { "postcss-selector-parser": "^6.0.11" } @@ -24996,6 +23423,7 @@ "version": "6.0.16", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", + "peer": true, "requires": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -25004,7 +23432,8 @@ "postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "peer": true }, "postgres-array": { "version": "2.0.0", @@ -25037,7 +23466,8 @@ "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true }, "prettier": { "version": "3.3.3", @@ -25060,22 +23490,6 @@ "dev": true, "requires": {} }, - "prism-react-renderer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.1.0.tgz", - "integrity": "sha512-I5cvXHjA1PVGbGm1MsWCpvBCRrYyxEri0MC7/JbfIfYfcXAxHyO5PaUjs3A8H5GW6kJcLhTHxxMaOZZpRZD2iQ==", - "requires": { - "@types/prismjs": "^1.26.0", - "clsx": "^1.2.1" - }, - "dependencies": { - "clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" - } - } - }, "prismjs": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", @@ -25178,7 +23592,8 @@ "punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true }, "qs": { "version": "6.11.0", @@ -25218,6 +23633,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, "requires": { "safe-buffer": "^5.1.0" } @@ -25242,6 +23658,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "peer": true, "requires": { "loose-envify": "^1.1.0" } @@ -25250,56 +23667,31 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "peer": true, "requires": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" } }, "react-email": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/react-email/-/react-email-2.1.6.tgz", - "integrity": "sha512-BtR9VI1CMq4953wfiBmzupKlWcRThaWG2dDgl1vWAllK3tNNmJNerwY4VlmASRDQZE3LpLXU3+lf8N/VAKdbZQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/react-email/-/react-email-3.0.1.tgz", + "integrity": "sha512-G4Bkx2ULIScy/0Z8nnWywHt0W1iTkaYCdh9rWNuQ3eVZ6B3ttTUDE9uUy3VNQ8dtQbmG0cpt8+XmImw7mMBW6Q==", "requires": { "@babel/core": "7.24.5", "@babel/parser": "7.24.5", - "@radix-ui/colors": "1.0.1", - "@radix-ui/react-collapsible": "1.1.0", - "@radix-ui/react-popover": "1.1.1", - "@radix-ui/react-slot": "1.1.0", - "@radix-ui/react-toggle-group": "1.1.0", - "@radix-ui/react-tooltip": "1.1.1", - "@swc/core": "1.3.101", - "@types/react": "18.2.47", - "@types/react-dom": "^18.2.0", - "@types/webpack": "5.28.5", - "autoprefixer": "10.4.14", "chalk": "4.1.2", - "chokidar": "3.5.3", - "clsx": "2.1.0", + "chokidar": "3.6.0", "commander": "11.1.0", "debounce": "2.0.0", "esbuild": "0.19.11", - "eslint-config-prettier": "9.0.0", - "eslint-config-turbo": "1.10.12", - "framer-motion": "10.17.4", "glob": "10.3.4", "log-symbols": "4.1.0", "mime-types": "2.1.35", - "next": "14.1.4", + "next": "14.2.3", "normalize-path": "3.0.0", "ora": "5.4.1", - "postcss": "8.4.38", - "prism-react-renderer": "2.1.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "socket.io": "4.7.3", - "socket.io-client": "4.7.3", - "sonner": "1.3.1", - "source-map-js": "1.0.2", - "stacktrace-parser": "0.1.10", - "tailwind-merge": "2.2.0", - "tailwindcss": "3.4.0", - "typescript": "5.1.6" + "socket.io": "4.7.5" }, "dependencies": { "@esbuild/aix-ppc64": { @@ -25440,100 +23832,6 @@ "integrity": "sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==", "optional": true }, - "@swc/core": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.101.tgz", - "integrity": "sha512-w5aQ9qYsd/IYmXADAnkXPGDMTqkQalIi+kfFf/MHRKTpaOL7DHjMXwPp/n8hJ0qNjRvchzmPtOqtPBiER50d8A==", - "requires": { - "@swc/core-darwin-arm64": "1.3.101", - "@swc/core-darwin-x64": "1.3.101", - "@swc/core-linux-arm-gnueabihf": "1.3.101", - "@swc/core-linux-arm64-gnu": "1.3.101", - "@swc/core-linux-arm64-musl": "1.3.101", - "@swc/core-linux-x64-gnu": "1.3.101", - "@swc/core-linux-x64-musl": "1.3.101", - "@swc/core-win32-arm64-msvc": "1.3.101", - "@swc/core-win32-ia32-msvc": "1.3.101", - "@swc/core-win32-x64-msvc": "1.3.101", - "@swc/counter": "^0.1.1", - "@swc/types": "^0.1.5" - } - }, - "@swc/core-darwin-arm64": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.101.tgz", - "integrity": "sha512-mNFK+uHNPRXSnfTOG34zJOeMl2waM4hF4a2NY7dkMXrPqw9CoJn4MwTXJcyMiSz1/BnNjjTCHF3Yhj0jPxmkzQ==", - "optional": true - }, - "@swc/core-darwin-x64": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.101.tgz", - "integrity": "sha512-B085j8XOx73Fg15KsHvzYWG262bRweGr3JooO1aW5ec5pYbz5Ew9VS5JKYS03w2UBSxf2maWdbPz2UFAxg0whw==", - "optional": true - }, - "@swc/core-linux-arm-gnueabihf": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.101.tgz", - "integrity": "sha512-9xLKRb6zSzRGPqdz52Hy5GuB1lSjmLqa0lST6MTFads3apmx4Vgs8Y5NuGhx/h2I8QM4jXdLbpqQlifpzTlSSw==", - "optional": true - }, - "@swc/core-linux-arm64-gnu": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.101.tgz", - "integrity": "sha512-oE+r1lo7g/vs96Weh2R5l971dt+ZLuhaUX+n3BfDdPxNHfObXgKMjO7E+QS5RbGjv/AwiPCxQmbdCp/xN5ICJA==", - "optional": true - }, - "@swc/core-linux-arm64-musl": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.101.tgz", - "integrity": "sha512-OGjYG3H4BMOTnJWJyBIovCez6KiHF30zMIu4+lGJTCrxRI2fAjGLml3PEXj8tC3FMcud7U2WUn6TdG0/te2k6g==", - "optional": true - }, - "@swc/core-linux-x64-gnu": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.101.tgz", - "integrity": "sha512-/kBMcoF12PRO/lwa8Z7w4YyiKDcXQEiLvM+S3G9EvkoKYGgkkz4Q6PSNhF5rwg/E3+Hq5/9D2R+6nrkF287ihg==", - "optional": true - }, - "@swc/core-linux-x64-musl": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.101.tgz", - "integrity": "sha512-kDN8lm4Eew0u1p+h1l3JzoeGgZPQ05qDE0czngnjmfpsH2sOZxVj1hdiCwS5lArpy7ktaLu5JdRnx70MkUzhXw==", - "optional": true - }, - "@swc/core-win32-arm64-msvc": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.101.tgz", - "integrity": "sha512-9Wn8TTLWwJKw63K/S+jjrZb9yoJfJwCE2RV5vPCCWmlMf3U1AXj5XuWOLUX+Rp2sGKau7wZKsvywhheWm+qndQ==", - "optional": true - }, - "@swc/core-win32-ia32-msvc": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.101.tgz", - "integrity": "sha512-onO5KvICRVlu2xmr4//V2je9O2XgS1SGKpbX206KmmjcJhXN5EYLSxW9qgg+kgV5mip+sKTHTAu7IkzkAtElYA==", - "optional": true - }, - "@swc/core-win32-x64-msvc": { - "version": "1.3.101", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.101.tgz", - "integrity": "sha512-T3GeJtNQV00YmiVw/88/nxJ/H43CJvFnpvBHCVn17xbahiVUOPOduh3rc9LgAkKiNt/aV8vU3OJR+6PhfMR7UQ==", - "optional": true - }, - "@types/react": { - "version": "18.2.47", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.47.tgz", - "integrity": "sha512-xquNkkOirwyCgoClNk85BjP+aqnIS+ckAJ8i37gAbDs14jfW/J23f2GItAf33oiUPQnqNMALiFeoM9Y5mbjpVQ==", - "requires": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" - }, "brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -25542,21 +23840,6 @@ "balanced-match": "^1.0.0" } }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, "commander": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", @@ -25592,12 +23875,6 @@ "@esbuild/win32-x64": "0.19.11" } }, - "eslint-config-prettier": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", - "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", - "requires": {} - }, "glob": { "version": "10.3.4", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.4.tgz", @@ -25617,69 +23894,6 @@ "requires": { "brace-expansion": "^2.0.1" } - }, - "socket.io": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.3.tgz", - "integrity": "sha512-SE+UIQXBQE+GPG2oszWMlsEmWtHVqw/h1VrYJGK5/MC7CH5p58N448HwIrtREcvR4jfdOJAY4ieQfxMr55qbbw==", - "requires": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.5.2", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" - } - }, - "source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" - }, - "tailwindcss": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.0.tgz", - "integrity": "sha512-VigzymniH77knD1dryXbyxR+ePHihHociZbXnLZHUyzf2MMs2ZVqlUrZ3FvpXP8pno9JzmILt1sZPD19M3IxtA==", - "requires": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.0", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.19.1", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" - }, - "dependencies": { - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "requires": { - "is-glob": "^4.0.3" - } - } - } - }, - "typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==" } } }, @@ -25698,41 +23912,11 @@ } } }, - "react-remove-scroll": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz", - "integrity": "sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==", - "requires": { - "react-remove-scroll-bar": "^2.3.4", - "react-style-singleton": "^2.2.1", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" - } - }, - "react-remove-scroll-bar": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", - "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", - "requires": { - "react-style-singleton": "^2.2.1", - "tslib": "^2.0.0" - } - }, - "react-style-singleton": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", - "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", - "requires": { - "get-nonce": "^1.0.0", - "invariant": "^2.2.4", - "tslib": "^2.0.0" - } - }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "peer": true, "requires": { "pify": "^2.3.0" } @@ -25882,11 +24066,6 @@ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" }, - "regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, "regexp-tree": { "version": "0.1.27", "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", @@ -26148,6 +24327,7 @@ "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "peer": true, "requires": { "loose-envify": "^1.1.0" } @@ -26156,6 +24336,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, "requires": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -26166,6 +24347,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -26177,12 +24359,14 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, "requires": {} }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true } } }, @@ -26245,6 +24429,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, "requires": { "randombytes": "^2.1.0" } @@ -26447,17 +24632,6 @@ } } }, - "socket.io-client": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.3.tgz", - "integrity": "sha512-nU+ywttCyBitXIl9Xe0RSEfek4LneYkJxCeNnKCuhwoH4jGXO1ipIUw/VA/+Vvv2G1MTym11fzFC0SxkrcfXDw==", - "requires": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.2", - "engine.io-client": "~6.5.2", - "socket.io-parser": "~4.2.4" - } - }, "socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", @@ -26467,12 +24641,6 @@ "debug": "~4.3.1" } }, - "sonner": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.3.1.tgz", - "integrity": "sha512-+rOAO56b2eI3q5BtgljERSn2umRk63KFIvgb2ohbZ5X+Eb5u+a/7/0ZgswYqgBMg8dyl7n6OXd9KasA8QF9ToA==", - "requires": {} - }, "source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -26488,6 +24656,7 @@ "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -26496,7 +24665,8 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true } } }, @@ -26582,21 +24752,6 @@ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true }, - "stacktrace-parser": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", - "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", - "requires": { - "type-fest": "^0.7.1" - }, - "dependencies": { - "type-fest": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", - "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==" - } - } - }, "standard-as-callback": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", @@ -26696,7 +24851,8 @@ "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true }, "styled-jsx": { "version": "5.1.1", @@ -26710,6 +24866,7 @@ "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "peer": true, "requires": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", @@ -26759,14 +24916,6 @@ "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.22.0.tgz", "integrity": "sha512-oAP80ymt8ssrAzjX8k3frbL7ys6AotqC35oikG6/SG15wBw+tG9nCk4oPaXIhEaAOAZ8XngxUv3ORq2IuR3r4Q==" }, - "tailwind-merge": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.0.tgz", - "integrity": "sha512-SqqhhaL0T06SW59+JVNfAqKdqLs0497esifRrZ7jOaefP3o64fdFNDMrAQWZFMxTLJPiHVjRLUywT8uFz1xNWQ==", - "requires": { - "@babel/runtime": "^7.23.5" - } - }, "tailwindcss": { "version": "3.4.6", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.6.tgz", @@ -26838,7 +24987,8 @@ "tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true }, "tar": { "version": "6.2.0", @@ -26896,6 +25046,7 @@ "version": "5.27.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==", + "dev": true, "requires": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -26906,7 +25057,8 @@ "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true } } }, @@ -26914,6 +25066,7 @@ "version": "5.3.10", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "dev": true, "requires": { "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", @@ -26926,6 +25079,7 @@ "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, "requires": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -26936,6 +25090,7 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -27020,7 +25175,8 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true }, "thenify": { "version": "3.3.1", @@ -27132,7 +25288,8 @@ "ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "peer": true }, "ts-node": { "version": "10.9.2", @@ -27208,6 +25365,7 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, "requires": { "prelude-ls": "^1.2.1" } @@ -27389,27 +25547,11 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "requires": { "punycode": "^2.1.0" } }, - "use-callback-ref": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", - "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", - "requires": { - "tslib": "^2.0.0" - } - }, - "use-sidecar": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", - "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", - "requires": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - } - }, "utf8-byte-length": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", @@ -27553,6 +25695,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", + "dev": true, "requires": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -27575,6 +25718,7 @@ "version": "5.92.1", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.92.1.tgz", "integrity": "sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==", + "dev": true, "requires": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", @@ -27606,6 +25750,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, "requires": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -27614,7 +25759,8 @@ "estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true } } }, @@ -27627,7 +25773,8 @@ "webpack-sources": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==" + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true }, "webpack-virtual-modules": { "version": "0.6.1", @@ -27706,11 +25853,6 @@ "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", "requires": {} }, - "xmlhttprequest-ssl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", - "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==" - }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -27767,7 +25909,8 @@ "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true }, "zip-stream": { "version": "6.0.1", diff --git a/server/package.json b/server/package.json index 8a9149bf845b3..55f80a071868d 100644 --- a/server/package.json +++ b/server/package.json @@ -79,7 +79,7 @@ "openid-client": "^5.4.3", "pg": "^8.11.3", "picomatch": "^4.0.0", - "react-email": "^2.1.2", + "react-email": "^3.0.0", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", "sanitize-filename": "^1.6.3", From 365facfc5173044b6c3f3de1a457819222cab4ca Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 08:52:49 -0400 Subject: [PATCH 038/160] chore(deps): update node (#12063) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- cli/Dockerfile | 2 +- cli/package-lock.json | 4 ++-- cli/package.json | 2 +- e2e/package-lock.json | 6 +++--- e2e/package.json | 2 +- open-api/typescript-sdk/package-lock.json | 2 +- open-api/typescript-sdk/package.json | 2 +- server/Dockerfile | 2 +- server/package-lock.json | 2 +- server/package.json | 2 +- web/Dockerfile | 2 +- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cli/Dockerfile b/cli/Dockerfile index 2c4aaf87186c0..e3cce6d448249 100644 --- a/cli/Dockerfile +++ b/cli/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20.16.0-alpine3.20@sha256:eb8101caae9ac02229bd64c024919fe3d4504ff7f329da79ca60a04db08cef52 AS core +FROM node:20.17.0-alpine3.20@sha256:1a526b97cace6b4006256570efa1a29cd1fe4b96a5301f8d48e87c5139438a45 AS core WORKDIR /usr/src/open-api/typescript-sdk COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./ diff --git a/cli/package-lock.json b/cli/package-lock.json index 6044069672878..2fdb1a5d5935f 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -24,7 +24,7 @@ "@types/cli-progress": "^3.11.0", "@types/lodash-es": "^4.17.12", "@types/mock-fs": "^4.13.1", - "@types/node": "^20.14.15", + "@types/node": "^20.16.1", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", "@vitest/coverage-v8": "^2.0.5", @@ -59,7 +59,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^20.14.15", + "@types/node": "^20.16.1", "typescript": "^5.3.3" } }, diff --git a/cli/package.json b/cli/package.json index cce73afa37d1b..d739cc3895c61 100644 --- a/cli/package.json +++ b/cli/package.json @@ -20,7 +20,7 @@ "@types/cli-progress": "^3.11.0", "@types/lodash-es": "^4.17.12", "@types/mock-fs": "^4.13.1", - "@types/node": "^20.14.15", + "@types/node": "^20.16.1", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", "@vitest/coverage-v8": "^2.0.5", diff --git a/e2e/package-lock.json b/e2e/package-lock.json index bc08cb0f9218d..5b85eb0147da2 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -15,7 +15,7 @@ "@immich/sdk": "file:../open-api/typescript-sdk", "@playwright/test": "^1.44.1", "@types/luxon": "^3.4.2", - "@types/node": "^20.14.15", + "@types/node": "^20.16.1", "@types/oidc-provider": "^8.5.1", "@types/pg": "^8.11.0", "@types/pngjs": "^6.0.4", @@ -64,7 +64,7 @@ "@types/cli-progress": "^3.11.0", "@types/lodash-es": "^4.17.12", "@types/mock-fs": "^4.13.1", - "@types/node": "^20.14.15", + "@types/node": "^20.16.1", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", "@vitest/coverage-v8": "^2.0.5", @@ -99,7 +99,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^20.14.15", + "@types/node": "^20.16.1", "typescript": "^5.3.3" } }, diff --git a/e2e/package.json b/e2e/package.json index be072e44f3e23..add072df84441 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -25,7 +25,7 @@ "@immich/sdk": "file:../open-api/typescript-sdk", "@playwright/test": "^1.44.1", "@types/luxon": "^3.4.2", - "@types/node": "^20.14.15", + "@types/node": "^20.16.1", "@types/oidc-provider": "^8.5.1", "@types/pg": "^8.11.0", "@types/pngjs": "^6.0.4", diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index 89322e1e07860..afa002a5a3b9f 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -12,7 +12,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^20.14.15", + "@types/node": "^20.16.1", "typescript": "^5.3.3" } }, diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index 90fa525fa01cd..d7d6ba6cc5e30 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -19,7 +19,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^20.14.15", + "@types/node": "^20.16.1", "typescript": "^5.3.3" }, "repository": { diff --git a/server/Dockerfile b/server/Dockerfile index 1c671f2332c8b..c961f0db64a5e 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -25,7 +25,7 @@ COPY --from=dev /usr/src/app/node_modules/@img ./node_modules/@img COPY --from=dev /usr/src/app/node_modules/exiftool-vendored.pl ./node_modules/exiftool-vendored.pl # web build -FROM node:20.16.0-alpine3.20@sha256:eb8101caae9ac02229bd64c024919fe3d4504ff7f329da79ca60a04db08cef52 AS web +FROM node:20.17.0-alpine3.20@sha256:1a526b97cace6b4006256570efa1a29cd1fe4b96a5301f8d48e87c5139438a45 AS web WORKDIR /usr/src/open-api/typescript-sdk COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./ diff --git a/server/package-lock.json b/server/package-lock.json index 33a4cd51ad902..780ff2a63e923 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -83,7 +83,7 @@ "@types/lodash": "^4.14.197", "@types/mock-fs": "^4.13.1", "@types/multer": "^1.4.7", - "@types/node": "^20.14.15", + "@types/node": "^20.16.1", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^3.0.0", "@types/semver": "^7.5.8", diff --git a/server/package.json b/server/package.json index 55f80a071868d..9f82378c1ad9d 100644 --- a/server/package.json +++ b/server/package.json @@ -109,7 +109,7 @@ "@types/lodash": "^4.14.197", "@types/mock-fs": "^4.13.1", "@types/multer": "^1.4.7", - "@types/node": "^20.14.15", + "@types/node": "^20.16.1", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^3.0.0", "@types/semver": "^7.5.8", diff --git a/web/Dockerfile b/web/Dockerfile index 5e1dd28020ac3..4bc711e15ece5 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20.16.0-alpine3.20@sha256:eb8101caae9ac02229bd64c024919fe3d4504ff7f329da79ca60a04db08cef52 +FROM node:20.17.0-alpine3.20@sha256:1a526b97cace6b4006256570efa1a29cd1fe4b96a5301f8d48e87c5139438a45 RUN apk add --no-cache tini USER node From cf272fc7fd927680e4629db1db7b383080ccffde Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 14:15:20 +0100 Subject: [PATCH 039/160] chore(deps): update terraform cloudflare to v4.40.0 (#11740) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .../docs-release/.terraform.lock.hcl | 60 +++++++++---------- .../modules/cloudflare/docs-release/config.tf | 2 +- .../cloudflare/docs/.terraform.lock.hcl | 60 +++++++++---------- deployment/modules/cloudflare/docs/config.tf | 2 +- 4 files changed, 62 insertions(+), 62 deletions(-) diff --git a/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl b/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl index 4774e1cacfe40..096177bb05366 100644 --- a/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl +++ b/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl @@ -2,37 +2,37 @@ # Manual edits may be lost in future updates. provider "registry.opentofu.org/cloudflare/cloudflare" { - version = "4.38.0" - constraints = "4.38.0" + version = "4.40.0" + constraints = "4.40.0" hashes = [ - "h1:+27KAHKHBDvv3dqyJv5vhtdKQZJzoZXoMqIyronlHNw=", - "h1:/uV9RgOUhkxElkHhWs8fs5ZbX9vj6RCBfP0oJO0JF30=", - "h1:1DNAdMugJJOAWD/XYiZenYYZLy7fw2ctjT4YZmkRCVQ=", - "h1:1wn4PmCLdT7mvd74JkCGmJDJxTQDkcxc+1jNbmwnMHA=", - "h1:BIHB4fBxHg2bA9KbL92njhyctxKC8b6hNDp60y5QBss=", - "h1:HCQpvKPsMsR4HO5eDqt+Kao7T7CYeEH7KZIO7xMcC6M=", - "h1:HTomuzocukpNLwtWzeSF3yteCVsyVKbwKmN66u9iPac=", - "h1:YDxsUBhBAwHSXLzVwrSlSBOwv1NvLyry7s5SfCV7VqQ=", - "h1:dchVhxo+Acd1l2RuZ88tW9lWj4422QMfgtxKvKCjYrw=", - "h1:eypa+P4ZpsEGMPFuCE+6VkRefu0TZRFmVBOpK+PDOPY=", - "h1:f3yjse2OsRZj7ZhR7BLintJMlI4fpyt8HyDP/zcEavw=", - "h1:mSJ7xj8K+xcnEmGg7lH0jjzyQb157wH94ULTAlIV+HQ=", - "h1:tt+2J2Ze8VIdDq2Hr6uHlTJzAMBRpErBwTYx0uD5ilE=", - "h1:uQW8SKxmulqrAisO+365mIf2FueINAp5PY28bqCPCug=", - "zh:171ab67cccceead4514fafb2d39e4e708a90cce79000aaf3c29aab7ed4457071", - "zh:18aa7228447baaaefc49a43e8eff970817a7491a63d8937e796357a3829dd979", - "zh:2cbaab6092e81ba6f41fa60a50f14e980c8ec327ee11d0b21f16a478be4b7567", - "zh:53b8e49c06f5b31a8c681f8c0669cf43e78abe71657b8182a221d096bb514965", - "zh:6037cfc60b4b647aabae155fcb46d649ed7c650e0287f05db52b2068f1e27c8a", - "zh:62460982ce1a869eebfca675603fbbd50416cf6b69459fb855bfbe5ae2b97607", - "zh:65f6f3a8470917b6398baa5eb4f74b3932b213eac7c0202798bfad6fd1ee17df", + "h1:GP2N1tXrmpxu+qEDvFAmkfv9aeZNhag3bchyJpGpYbU=", + "h1:HDJKZBQkVU0kQl4gViQ5L7EcFLn9hB0iuvO+ORJiDS4=", + "h1:KrbeEsZoCJOnnX68yNI5h3QhMjc5bBCQW4yvYaEFq3s=", + "h1:LelwnzU0OVn6g2+T9Ub9XdpC+vbheraIL/qgXhWBs/k=", + "h1:TIq9CynfWrKgCxKL97Akj89cYlvJKn/AL4UXogd8/FM=", + "h1:Uoy5oPdm1ipDG7yIMCUN1IXMpsTGXahPw3I0rVA/6wA=", + "h1:Wunfpm+IZhENdoimrh4iXiakVnCsfKOHo80yJUjMQXM=", + "h1:cRdCuahMOFrNyldnCInqGQRBT1DTkRPSfPnaf5r05iw=", + "h1:k+zpXg8BO7gdbTIfSGyQisHhs5aVWQVbPLa5uUdr2UA=", + "h1:kWNrzZ8Rh0OpHikexkmwJIIucD6SMZPi4oGyDsKJitw=", + "h1:lomfTTjK78BdSEVTFcJUBQRy7IQHuGQImMaPWaYpfgQ=", + "h1:oWcWlZe52ZRyLQciNe94RaWzhHifSTu03nlK0uL7rlM=", + "h1:p3JJrhGEPlPQP7Uwy9FNMdvqCyD8tuT4lnXuJ+pSF/M=", + "h1:wtB0sKxG2K/H41hWJI4uJdImWquuaP34Sip5LmfE410=", + "zh:01742e5946f936548f8e42120287ffc757abf97e7cbbe34e25c266a438fb54fd", + "zh:08d81f5a5aab4cc269f983b8c6b5be0e278105136aca9681740802619577371f", + "zh:0d75131ba70902cfc94a7a5900369bdde56528b2aad6e10b164449cc97d57396", + "zh:3890a715a012e197541daacdacb8cceec6d364814daa4640ddfe98a8ba9036cb", + "zh:58254ce5ebe1faed4664df86210c39d660bcdc60280f17b25fe4d4dbea21ea8c", + "zh:6b0abc1adbc2edee79368ce9f7338ebcb5d0bf941e8d7d9ac505b750f20f80a2", + "zh:81cc415d1477174a1ca288d25fdb57e5ee488c2d7f61f265ef995b255a53b0ce", + "zh:8680140c7fe5beaefe61c5cfa471bf88422dc0c0f05dad6d3cb482d4ffd22be4", "zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f", - "zh:8b5cebe64bf04105a49178a165b6a8800a9a33bae6767143a47fe4977755f805", - "zh:a5596635db0993ee3c3060fbc2227d91b239466e96d2d82642625a5aa2486988", - "zh:b3a9c63038441f13c311fd4b2c7e69e571445e5a7365a20c7cc9046b7e6c8aba", - "zh:b585e7e4d7648a540b14b9182819214896ca9337729eeb1f2034833b17db754d", - "zh:d2c3c545318ac8542369e9fc8228e29ee585febdf203a450fad3e0eded71ce02", - "zh:e95dd2d6c3525073af47d47b763cb81b6a51b20cabf76f789c69328922da9ecf", - "zh:eee6e590b36d6c6168a7daae8afa74a8721fd7aa9f62a710f04a311975100722", + "zh:a491d26236122ccb83dac8cb490d2c0aa1f4d3a0b4abe99300fd49b1a624f42f", + "zh:a70d9c469dc8d55715ba77c9d1a4ede1fdebf79e60ee18438a0844868db54e0d", + "zh:a7fcb7d5c4222e14ec6d9a15adf8b9a083d84b102c3d0e4a0d102df5a1360b62", + "zh:b4f9677174fabd199c8ebd2e9e5eb3528cf887e700569a4fb61eef4e070cec5e", + "zh:c27f0f7519221d75dae4a3787a59e05acd5cc9a0d30a390eff349a77d20d52e6", + "zh:db00d8605dbf43ca42fe1481a6c67fdcaa73debb7d2a0f613cb95ae5c5e7150e", ] } diff --git a/deployment/modules/cloudflare/docs-release/config.tf b/deployment/modules/cloudflare/docs-release/config.tf index b7c70f1c21719..63c96fc49805b 100644 --- a/deployment/modules/cloudflare/docs-release/config.tf +++ b/deployment/modules/cloudflare/docs-release/config.tf @@ -5,7 +5,7 @@ terraform { required_providers { cloudflare = { source = "cloudflare/cloudflare" - version = "4.38.0" + version = "4.40.0" } } } diff --git a/deployment/modules/cloudflare/docs/.terraform.lock.hcl b/deployment/modules/cloudflare/docs/.terraform.lock.hcl index 4774e1cacfe40..096177bb05366 100644 --- a/deployment/modules/cloudflare/docs/.terraform.lock.hcl +++ b/deployment/modules/cloudflare/docs/.terraform.lock.hcl @@ -2,37 +2,37 @@ # Manual edits may be lost in future updates. provider "registry.opentofu.org/cloudflare/cloudflare" { - version = "4.38.0" - constraints = "4.38.0" + version = "4.40.0" + constraints = "4.40.0" hashes = [ - "h1:+27KAHKHBDvv3dqyJv5vhtdKQZJzoZXoMqIyronlHNw=", - "h1:/uV9RgOUhkxElkHhWs8fs5ZbX9vj6RCBfP0oJO0JF30=", - "h1:1DNAdMugJJOAWD/XYiZenYYZLy7fw2ctjT4YZmkRCVQ=", - "h1:1wn4PmCLdT7mvd74JkCGmJDJxTQDkcxc+1jNbmwnMHA=", - "h1:BIHB4fBxHg2bA9KbL92njhyctxKC8b6hNDp60y5QBss=", - "h1:HCQpvKPsMsR4HO5eDqt+Kao7T7CYeEH7KZIO7xMcC6M=", - "h1:HTomuzocukpNLwtWzeSF3yteCVsyVKbwKmN66u9iPac=", - "h1:YDxsUBhBAwHSXLzVwrSlSBOwv1NvLyry7s5SfCV7VqQ=", - "h1:dchVhxo+Acd1l2RuZ88tW9lWj4422QMfgtxKvKCjYrw=", - "h1:eypa+P4ZpsEGMPFuCE+6VkRefu0TZRFmVBOpK+PDOPY=", - "h1:f3yjse2OsRZj7ZhR7BLintJMlI4fpyt8HyDP/zcEavw=", - "h1:mSJ7xj8K+xcnEmGg7lH0jjzyQb157wH94ULTAlIV+HQ=", - "h1:tt+2J2Ze8VIdDq2Hr6uHlTJzAMBRpErBwTYx0uD5ilE=", - "h1:uQW8SKxmulqrAisO+365mIf2FueINAp5PY28bqCPCug=", - "zh:171ab67cccceead4514fafb2d39e4e708a90cce79000aaf3c29aab7ed4457071", - "zh:18aa7228447baaaefc49a43e8eff970817a7491a63d8937e796357a3829dd979", - "zh:2cbaab6092e81ba6f41fa60a50f14e980c8ec327ee11d0b21f16a478be4b7567", - "zh:53b8e49c06f5b31a8c681f8c0669cf43e78abe71657b8182a221d096bb514965", - "zh:6037cfc60b4b647aabae155fcb46d649ed7c650e0287f05db52b2068f1e27c8a", - "zh:62460982ce1a869eebfca675603fbbd50416cf6b69459fb855bfbe5ae2b97607", - "zh:65f6f3a8470917b6398baa5eb4f74b3932b213eac7c0202798bfad6fd1ee17df", + "h1:GP2N1tXrmpxu+qEDvFAmkfv9aeZNhag3bchyJpGpYbU=", + "h1:HDJKZBQkVU0kQl4gViQ5L7EcFLn9hB0iuvO+ORJiDS4=", + "h1:KrbeEsZoCJOnnX68yNI5h3QhMjc5bBCQW4yvYaEFq3s=", + "h1:LelwnzU0OVn6g2+T9Ub9XdpC+vbheraIL/qgXhWBs/k=", + "h1:TIq9CynfWrKgCxKL97Akj89cYlvJKn/AL4UXogd8/FM=", + "h1:Uoy5oPdm1ipDG7yIMCUN1IXMpsTGXahPw3I0rVA/6wA=", + "h1:Wunfpm+IZhENdoimrh4iXiakVnCsfKOHo80yJUjMQXM=", + "h1:cRdCuahMOFrNyldnCInqGQRBT1DTkRPSfPnaf5r05iw=", + "h1:k+zpXg8BO7gdbTIfSGyQisHhs5aVWQVbPLa5uUdr2UA=", + "h1:kWNrzZ8Rh0OpHikexkmwJIIucD6SMZPi4oGyDsKJitw=", + "h1:lomfTTjK78BdSEVTFcJUBQRy7IQHuGQImMaPWaYpfgQ=", + "h1:oWcWlZe52ZRyLQciNe94RaWzhHifSTu03nlK0uL7rlM=", + "h1:p3JJrhGEPlPQP7Uwy9FNMdvqCyD8tuT4lnXuJ+pSF/M=", + "h1:wtB0sKxG2K/H41hWJI4uJdImWquuaP34Sip5LmfE410=", + "zh:01742e5946f936548f8e42120287ffc757abf97e7cbbe34e25c266a438fb54fd", + "zh:08d81f5a5aab4cc269f983b8c6b5be0e278105136aca9681740802619577371f", + "zh:0d75131ba70902cfc94a7a5900369bdde56528b2aad6e10b164449cc97d57396", + "zh:3890a715a012e197541daacdacb8cceec6d364814daa4640ddfe98a8ba9036cb", + "zh:58254ce5ebe1faed4664df86210c39d660bcdc60280f17b25fe4d4dbea21ea8c", + "zh:6b0abc1adbc2edee79368ce9f7338ebcb5d0bf941e8d7d9ac505b750f20f80a2", + "zh:81cc415d1477174a1ca288d25fdb57e5ee488c2d7f61f265ef995b255a53b0ce", + "zh:8680140c7fe5beaefe61c5cfa471bf88422dc0c0f05dad6d3cb482d4ffd22be4", "zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f", - "zh:8b5cebe64bf04105a49178a165b6a8800a9a33bae6767143a47fe4977755f805", - "zh:a5596635db0993ee3c3060fbc2227d91b239466e96d2d82642625a5aa2486988", - "zh:b3a9c63038441f13c311fd4b2c7e69e571445e5a7365a20c7cc9046b7e6c8aba", - "zh:b585e7e4d7648a540b14b9182819214896ca9337729eeb1f2034833b17db754d", - "zh:d2c3c545318ac8542369e9fc8228e29ee585febdf203a450fad3e0eded71ce02", - "zh:e95dd2d6c3525073af47d47b763cb81b6a51b20cabf76f789c69328922da9ecf", - "zh:eee6e590b36d6c6168a7daae8afa74a8721fd7aa9f62a710f04a311975100722", + "zh:a491d26236122ccb83dac8cb490d2c0aa1f4d3a0b4abe99300fd49b1a624f42f", + "zh:a70d9c469dc8d55715ba77c9d1a4ede1fdebf79e60ee18438a0844868db54e0d", + "zh:a7fcb7d5c4222e14ec6d9a15adf8b9a083d84b102c3d0e4a0d102df5a1360b62", + "zh:b4f9677174fabd199c8ebd2e9e5eb3528cf887e700569a4fb61eef4e070cec5e", + "zh:c27f0f7519221d75dae4a3787a59e05acd5cc9a0d30a390eff349a77d20d52e6", + "zh:db00d8605dbf43ca42fe1481a6c67fdcaa73debb7d2a0f613cb95ae5c5e7150e", ] } diff --git a/deployment/modules/cloudflare/docs/config.tf b/deployment/modules/cloudflare/docs/config.tf index b7c70f1c21719..63c96fc49805b 100644 --- a/deployment/modules/cloudflare/docs/config.tf +++ b/deployment/modules/cloudflare/docs/config.tf @@ -5,7 +5,7 @@ terraform { required_providers { cloudflare = { source = "cloudflare/cloudflare" - version = "4.38.0" + version = "4.40.0" } } } From c44280a50b28cba041ebf00cae7e06ec47472019 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 28 Aug 2024 08:20:56 -0500 Subject: [PATCH 040/160] chore(web): subtler spinner FOUC animation (#12090) --- web/src/app.html | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/web/src/app.html b/web/src/app.html index aa8450e9be4ba..778375c1e142b 100644 --- a/web/src/app.html +++ b/web/src/app.html @@ -20,43 +20,27 @@ height: 100%; width: 100%; } + body, html { margin: 0; padding: 0; } + @keyframes delayedVisibility { to { visibility: visible; } } - @keyframes stencil-pulse { - 0% { - transform: scale(0.93); - filter: drop-shadow(0 0 0 rgba(0, 0, 0, 0.7)); - } - 70% { - transform: scale(1); - filter: drop-shadow(0 0 8px rgba(0, 0, 0, 0)); - } - - 100% { - transform: scale(0.93); - filter: drop-shadow(0 0 0 rgba(0, 0, 0, 0)); - } - } @keyframes loadspin { 100% { transform: rotate(360deg); } } - #stencil svg { - height: 35%; - animation: stencil-pulse 1s linear infinite; - } + #stencil { - --stencil-width: 25vw; + --stencil-width: 150px; display: flex; width: var(--stencil-width); margin-left: auto; @@ -69,11 +53,13 @@ visibility: hidden; animation: 0s linear 0.3s forwards delayedVisibility, - loadspin 2s linear infinite; + loadspin 8s linear infinite; } + .bg-immich-bg { background-color: white; } + .dark .dark\:bg-immich-dark-bg { background-color: black; } From 6867bae770a2fe180d358ca4196cc0fd1a118d47 Mon Sep 17 00:00:00 2001 From: Lena Tauchner <48085877+Tiefseetauchner@users.noreply.github.com> Date: Wed, 28 Aug 2024 15:25:58 +0200 Subject: [PATCH 041/160] fix(cli): Update build instructions for CLI (#11874) Update build instructions for CLI --- cli/README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cli/README.md b/cli/README.md index a570a55239af1..8fa2ace483251 100644 --- a/cli/README.md +++ b/cli/README.md @@ -4,8 +4,18 @@ Please see the [Immich CLI documentation](https://immich.app/docs/features/comma # For developers +Before building the CLI, you must build the immich server and the open-api client. To build the server run the following in the server folder: + + $ npm install + $ npm run build + +Then, to build the open-api client run the following in the open-api folder: + + $ ./bin/generate-open-api.sh + To run the Immich CLI from source, run the following in the cli folder: + $ npm install $ npm run build $ ts-node . @@ -17,3 +27,4 @@ You can also build and install the CLI using $ npm run build $ npm install -g . +**** From e705831e67ffd290c983cc871904d66585cda2c9 Mon Sep 17 00:00:00 2001 From: Zack Pollard Date: Wed, 28 Aug 2024 16:33:21 +0100 Subject: [PATCH 042/160] ci: fix permissions when pr-label-validation runs from fork (#12093) --- .github/workflows/pr-label-validation.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr-label-validation.yml b/.github/workflows/pr-label-validation.yml index 510995aa549ef..1557b3d15cfba 100644 --- a/.github/workflows/pr-label-validation.yml +++ b/.github/workflows/pr-label-validation.yml @@ -1,12 +1,15 @@ name: PR Label Validation on: - pull_request: + pull_request_target: types: [opened, labeled, unlabeled, synchronize] jobs: validate-release-label: runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: read steps: - name: Require PR to have a changelog label uses: mheap/github-action-required-labels@v5 From cc4e5298ffc91e1f5ee36873979e3a3bb8652da0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 12:00:10 -0400 Subject: [PATCH 043/160] fix(deps): update typescript-projects (#11927) * fix(deps): update typescript-projects * chore: clean up --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jason Rasmussen --- cli/package-lock.json | 285 ++-- docs/package-lock.json | 466 +++--- docs/src/components/version-switcher.tsx | 1 - docs/tailwind.config.js | 2 +- e2e/package-lock.json | 148 +- server/package-lock.json | 1487 ++++++++--------- server/package.json | 4 +- server/src/emails/album-invite.email.tsx | 4 +- server/src/emails/album-update.email.tsx | 4 +- .../src/emails/components/immich.layout.tsx | 3 +- server/src/emails/license.email.tsx | 21 +- server/src/emails/test.email.tsx | 2 +- server/src/emails/welcome.email.tsx | 4 +- .../src/interfaces/notification.interface.ts | 2 +- .../repositories/notification.repository.ts | 6 +- server/src/services/notification.service.ts | 8 +- web/package-lock.json | 331 ++-- 17 files changed, 1414 insertions(+), 1364 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 2fdb1a5d5935f..fa38bd275e7fc 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -825,9 +825,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.8.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.8.0.tgz", - "integrity": "sha512-MfluB7EUfxXtv3i/++oh89uzAr4PDI4nn201hsp+qaXqsjAWzinlZEHEfPgAX4doIlKvPG/i0A9dpKxOLII8yA==", + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.0.tgz", + "integrity": "sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug==", "dev": true, "license": "MIT", "engines": { @@ -1054,169 +1054,224 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", - "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.1.tgz", + "integrity": "sha512-2thheikVEuU7ZxFXubPDOtspKn1x0yqaYQwvALVtEcvFhMifPADBrgRPyHV0TF3b+9BgvgjgagVyvA/UqPZHmg==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", - "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.1.tgz", + "integrity": "sha512-t1lLYn4V9WgnIFHXy1d2Di/7gyzBWS8G5pQSXdZqfrdCGTwi1VasRMSS81DTYb+avDs/Zz4A6dzERki5oRYz1g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", - "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.1.tgz", + "integrity": "sha512-AH/wNWSEEHvs6t4iJ3RANxW5ZCK3fUnmf0gyMxWCesY1AlUj8jY7GC+rQE4wd3gwmZ9XDOpL0kcFnCjtN7FXlA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", - "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.1.tgz", + "integrity": "sha512-dO0BIz/+5ZdkLZrVgQrDdW7m2RkrLwYTh2YMFG9IpBtlC1x1NPNSXkfczhZieOlOLEqgXOFH3wYHB7PmBtf+Bg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", - "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.1.tgz", + "integrity": "sha512-sWWgdQ1fq+XKrlda8PsMCfut8caFwZBmhYeoehJ05FdI0YZXk6ZyUjWLrIgbR/VgiGycrFKMMgp7eJ69HOF2pQ==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.1.tgz", + "integrity": "sha512-9OIiSuj5EsYQlmwhmFRA0LRO0dRRjdCVZA3hnmZe1rEwRk11Jy3ECGGq3a7RrVEZ0/pCsYWx8jG3IvcrJ6RCew==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", - "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.1.tgz", + "integrity": "sha512-0kuAkRK4MeIUbzQYu63NrJmfoUVicajoRAL1bpwdYIYRcs57iyIV9NLcuyDyDXE2GiZCL4uhKSYAnyWpjZkWow==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", - "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.1.tgz", + "integrity": "sha512-/6dYC9fZtfEY0vozpc5bx1RP4VrtEOhNQGb0HwvYNwXD1BBbwQ5cKIbUVVU7G2d5WRE90NfB922elN8ASXAJEA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.1.tgz", + "integrity": "sha512-ltUWy+sHeAh3YZ91NUsV4Xg3uBXAlscQe8ZOXRCVAKLsivGuJsrkawYPUEyCV3DYa9urgJugMLn8Z3Z/6CeyRQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", - "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.1.tgz", + "integrity": "sha512-BggMndzI7Tlv4/abrgLwa/dxNEMn2gC61DCLrTzw8LkpSKel4o+O+gtjbnkevZ18SKkeN3ihRGPuBxjaetWzWg==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.1.tgz", + "integrity": "sha512-z/9rtlGd/OMv+gb1mNSjElasMf9yXusAxnRDrBaYB+eS1shFm6/4/xDH1SAISO5729fFKUkJ88TkGPRUh8WSAA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", - "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.1.tgz", + "integrity": "sha512-kXQVcWqDcDKw0S2E0TmhlTLlUgAmMVqPrJZR+KpH/1ZaZhLSl23GZpQVmawBQGVhyP5WXIsIQ/zqbDBBYmxm5w==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", - "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.1.tgz", + "integrity": "sha512-CbFv/WMQsSdl+bpX6rVbzR4kAjSSBuDgCqb1l4J68UYsQNalz5wOqLGYj4ZI0thGpyX5kc+LLZ9CL+kpqDovZA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", - "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.1.tgz", + "integrity": "sha512-3Q3brDgA86gHXWHklrwdREKIrIbxC0ZgU8lwpj0eEKGBQH+31uPqr0P2v11pn0tSIxHvcdOWxa4j+YvLNx1i6g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", - "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.1.tgz", + "integrity": "sha512-tNg+jJcKR3Uwe4L0/wY3Ro0H+u3nrb04+tcq1GSYzBEmKLeOQF2emk1whxlzNqb6MMrQ2JOcQEpuuiPLyRcSIw==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", - "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.1.tgz", + "integrity": "sha512-xGiIH95H1zU7naUyTKEyOA/I0aexNMUdO9qRv0bLKN3qu25bBdrxZHqA3PTJ24YNN/GdMzG4xkDcd/GvjuhfLg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -1285,17 +1340,17 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.1.tgz", - "integrity": "sha512-5g3Y7GDFsJAnY4Yhvk8sZtFfV6YNF2caLzjrRPUBzewjPCaj0yokePB4LJSobyCzGMzjZZYFbwuzbfDHlimXbQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.2.0.tgz", + "integrity": "sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.0.1", - "@typescript-eslint/type-utils": "8.0.1", - "@typescript-eslint/utils": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1", + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/type-utils": "8.2.0", + "@typescript-eslint/utils": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1319,16 +1374,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.1.tgz", - "integrity": "sha512-5IgYJ9EO/12pOUwiBKFkpU7rS3IU21mtXzB81TNwq2xEybcmAZrE9qwDtsb5uQd9aVO9o0fdabFyAmKveXyujg==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.2.0.tgz", + "integrity": "sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.0.1", - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/typescript-estree": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1", + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/typescript-estree": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", "debug": "^4.3.4" }, "engines": { @@ -1348,14 +1403,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.1.tgz", - "integrity": "sha512-NpixInP5dm7uukMiRyiHjRKkom5RIFA4dfiHvalanD2cF0CLUuQqxfg8PtEUo9yqJI2bBhF+pcSafqnG3UBnRQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.2.0.tgz", + "integrity": "sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1" + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1366,14 +1421,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.1.tgz", - "integrity": "sha512-+/UT25MWvXeDX9YaHv1IS6KI1fiuTto43WprE7pgSMswHbn1Jm9GEM4Txp+X74ifOWV8emu2AWcbLhpJAvD5Ng==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.2.0.tgz", + "integrity": "sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.0.1", - "@typescript-eslint/utils": "8.0.1", + "@typescript-eslint/typescript-estree": "8.2.0", + "@typescript-eslint/utils": "8.2.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1391,9 +1446,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.1.tgz", - "integrity": "sha512-PpqTVT3yCA/bIgJ12czBuE3iBlM3g4inRSC5J0QOdQFAn07TYrYEQBBKgXH1lQpglup+Zy6c1fxuwTk4MTNKIw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.2.0.tgz", + "integrity": "sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==", "dev": true, "license": "MIT", "engines": { @@ -1405,14 +1460,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.1.tgz", - "integrity": "sha512-8V9hriRvZQXPWU3bbiUV4Epo7EvgM6RTs+sUmxp5G//dBGy402S7Fx0W0QkB2fb4obCF8SInoUzvTYtc3bkb5w==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.2.0.tgz", + "integrity": "sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1434,16 +1489,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.1.tgz", - "integrity": "sha512-CBFR0G0sCt0+fzfnKaciu9IBsKvEKYwN9UZ+eeogK1fYHg4Qxk1yf/wLQkLXlq8wbU2dFlgAesxt8Gi76E8RTA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.2.0.tgz", + "integrity": "sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.0.1", - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/typescript-estree": "8.0.1" + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/typescript-estree": "8.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1457,13 +1512,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.1.tgz", - "integrity": "sha512-W5E+o0UfUcK5EgchLZsyVWqARmsM7v54/qEq6PY3YI5arkgmCzHiuk0zKSJJbm71V0xdRna4BGomkCTXz2/LkQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.2.0.tgz", + "integrity": "sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.0.1", + "@typescript-eslint/types": "8.2.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -2080,9 +2135,9 @@ } }, "node_modules/eslint": { - "version": "9.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.8.0.tgz", - "integrity": "sha512-K8qnZ/QJzT2dLKdZJVX6W4XOwBzutMYmt0lqUS+JdXgd+HTYFlonFgkJ8s44d/zMPPCnOOk0kMWCApCPhiOy9A==", + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.0.tgz", + "integrity": "sha512-JfiKJrbx0506OEerjK2Y1QlldtBxkAlLxT5OEcRF8uaQ86noDe2k31Vw9rnSWv+MXZHj7OOUV/dA0AhdLFcyvA==", "dev": true, "license": "MIT", "dependencies": { @@ -2090,7 +2145,7 @@ "@eslint-community/regexpp": "^4.11.0", "@eslint/config-array": "^0.17.1", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.8.0", + "@eslint/js": "9.9.0", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", @@ -2129,6 +2184,14 @@ }, "funding": { "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-config-prettier": { @@ -3709,10 +3772,11 @@ } }, "node_modules/rollup": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", - "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.1.tgz", + "integrity": "sha512-ZnYyKvscThhgd3M5+Qt3pmhO4jIRR5RGzaSovB6Q7rGNrK5cUncrtLmcTTJVSdcKXyZjW8X8MB0JMSuH9bcAJg==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "1.0.5" }, @@ -3724,19 +3788,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.13.0", - "@rollup/rollup-android-arm64": "4.13.0", - "@rollup/rollup-darwin-arm64": "4.13.0", - "@rollup/rollup-darwin-x64": "4.13.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", - "@rollup/rollup-linux-arm64-gnu": "4.13.0", - "@rollup/rollup-linux-arm64-musl": "4.13.0", - "@rollup/rollup-linux-riscv64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-musl": "4.13.0", - "@rollup/rollup-win32-arm64-msvc": "4.13.0", - "@rollup/rollup-win32-ia32-msvc": "4.13.0", - "@rollup/rollup-win32-x64-msvc": "4.13.0", + "@rollup/rollup-android-arm-eabi": "4.21.1", + "@rollup/rollup-android-arm64": "4.21.1", + "@rollup/rollup-darwin-arm64": "4.21.1", + "@rollup/rollup-darwin-x64": "4.21.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.21.1", + "@rollup/rollup-linux-arm-musleabihf": "4.21.1", + "@rollup/rollup-linux-arm64-gnu": "4.21.1", + "@rollup/rollup-linux-arm64-musl": "4.21.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.21.1", + "@rollup/rollup-linux-riscv64-gnu": "4.21.1", + "@rollup/rollup-linux-s390x-gnu": "4.21.1", + "@rollup/rollup-linux-x64-gnu": "4.21.1", + "@rollup/rollup-linux-x64-musl": "4.21.1", + "@rollup/rollup-win32-arm64-msvc": "4.21.1", + "@rollup/rollup-win32-ia32-msvc": "4.21.1", + "@rollup/rollup-win32-x64-msvc": "4.21.1", "fsevents": "~2.3.2" } }, @@ -4207,15 +4274,15 @@ } }, "node_modules/vite": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.0.tgz", - "integrity": "sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz", + "integrity": "sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.40", - "rollup": "^4.13.0" + "postcss": "^8.4.41", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" diff --git a/docs/package-lock.json b/docs/package-lock.json index e5fb9f8b2aae7..c67c2b64fcb4e 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -2155,9 +2155,9 @@ } }, "node_modules/@docusaurus/core": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.4.0.tgz", - "integrity": "sha512-g+0wwmN2UJsBqy2fQRQ6fhXruoEa62JDeEa5d8IdTJlMoaDaEDfHh7WjwGRn4opuTQWpjAwP/fbcgyHKlE+64w==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.5.2.tgz", + "integrity": "sha512-4Z1WkhCSkX4KO0Fw5m/Vuc7Q3NxBG53NE5u59Rs96fWkMPZVSrzEPP16/Nk6cWb/shK7xXPndTmalJtw7twL/w==", "license": "MIT", "dependencies": { "@babel/core": "^7.23.3", @@ -2170,12 +2170,12 @@ "@babel/runtime": "^7.22.6", "@babel/runtime-corejs3": "^7.22.6", "@babel/traverse": "^7.22.8", - "@docusaurus/cssnano-preset": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/cssnano-preset": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "autoprefixer": "^10.4.14", "babel-loader": "^9.1.3", "babel-plugin-dynamic-import-node": "^2.3.3", @@ -2236,14 +2236,15 @@ "node": ">=18.0" }, "peerDependencies": { + "@mdx-js/react": "^3.0.0", "react": "^18.0.0", "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/cssnano-preset": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.4.0.tgz", - "integrity": "sha512-qwLFSz6v/pZHy/UP32IrprmH5ORce86BGtN0eBtG75PpzQJAzp9gefspox+s8IEOr0oZKuQ/nhzZ3xwyc3jYJQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.5.2.tgz", + "integrity": "sha512-D3KiQXOMA8+O0tqORBrTOEQyQxNIfPm9jEaJoALjjSjc2M/ZAWcUfPQEnwr2JB2TadHw2gqWgpZckQmrVWkytA==", "license": "MIT", "dependencies": { "cssnano-preset-advanced": "^6.1.2", @@ -2256,9 +2257,9 @@ } }, "node_modules/@docusaurus/logger": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.4.0.tgz", - "integrity": "sha512-bZwkX+9SJ8lB9kVRkXw+xvHYSMGG4bpYHKGXeXFvyVc79NMeeBSGgzd4TQLHH+DYeOJoCdl8flrFJVxlZ0wo/Q==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.5.2.tgz", + "integrity": "sha512-LHC540SGkeLfyT3RHK3gAMK6aS5TRqOD4R72BEU/DE2M/TY8WwEUAMY576UUc/oNJXv8pGhBmQB6N9p3pt8LQw==", "license": "MIT", "dependencies": { "chalk": "^4.1.2", @@ -2269,14 +2270,14 @@ } }, "node_modules/@docusaurus/mdx-loader": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.4.0.tgz", - "integrity": "sha512-kSSbrrk4nTjf4d+wtBA9H+FGauf2gCax89kV8SUSJu3qaTdSIKdWERlngsiHaCFgZ7laTJ8a67UFf+xlFPtuTw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.5.2.tgz", + "integrity": "sha512-ku3xO9vZdwpiMIVd8BzWV0DCqGEbCP5zs1iHfKX50vw6jX8vQo0ylYo1YJMZyz6e+JFJ17HYHT5FzVidz2IflA==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/logger": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "@mdx-js/mdx": "^3.0.0", "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", @@ -2308,12 +2309,12 @@ } }, "node_modules/@docusaurus/module-type-aliases": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.4.0.tgz", - "integrity": "sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.5.2.tgz", + "integrity": "sha512-Z+Xu3+2rvKef/YKTMxZHsEXp1y92ac0ngjDiExRdqGTmEKtCUpkbNYH8v5eXo5Ls+dnW88n6WTa+Q54kLOkwPg==", "license": "MIT", "dependencies": { - "@docusaurus/types": "3.4.0", + "@docusaurus/types": "3.5.2", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2326,52 +2327,21 @@ "react-dom": "*" } }, - "node_modules/@docusaurus/plugin-content-blog": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.4.0.tgz", - "integrity": "sha512-vv6ZAj78ibR5Jh7XBUT4ndIjmlAxkijM3Sx5MAAzC1gyv0vupDQNhzuFg1USQmQVj3P5I6bquk12etPV3LJ+Xw==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "cheerio": "^1.0.0-rc.12", - "feed": "^4.2.2", - "fs-extra": "^11.1.1", - "lodash": "^4.17.21", - "reading-time": "^1.5.0", - "srcset": "^4.0.0", - "tslib": "^2.6.0", - "unist-util-visit": "^5.0.0", - "utility-types": "^3.10.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.4.0.tgz", - "integrity": "sha512-HkUCZffhBo7ocYheD9oZvMcDloRnGhBMOZRyVcAQRFmZPmNqSyISlXA1tQCIxW+r478fty97XXAGjNYzBjpCsg==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.5.2.tgz", + "integrity": "sha512-Bt+OXn/CPtVqM3Di44vHjE7rPCEsRCB/DMo2qoOuozB9f7+lsdrHvD0QCHdBs0uhz6deYJDppAr2VgqybKPlVQ==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/module-type-aliases": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", @@ -2389,38 +2359,15 @@ "react-dom": "^18.0.0" } }, - "node_modules/@docusaurus/plugin-content-pages": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.4.0.tgz", - "integrity": "sha512-h2+VN/0JjpR8fIkDEAoadNjfR3oLzB+v1qSXbIAKjQ46JAHx3X22n9nqS+BWSQnTnp1AjkjSvZyJMekmcwxzxg==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "fs-extra": "^11.1.1", - "tslib": "^2.6.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, "node_modules/@docusaurus/plugin-debug": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.4.0.tgz", - "integrity": "sha512-uV7FDUNXGyDSD3PwUaf5YijX91T5/H9SX4ErEcshzwgzWwBtK37nUWPU3ZLJfeTavX3fycTOqk9TglpOLaWkCg==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.5.2.tgz", + "integrity": "sha512-kBK6GlN0itCkrmHuCS6aX1wmoWc5wpd5KJlqQ1FyrF0cLDnvsYSnh7+ftdwzt7G6lGBho8lrVwkkL9/iQvaSOA==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", "fs-extra": "^11.1.1", "react-json-view-lite": "^1.2.0", "tslib": "^2.6.0" @@ -2434,14 +2381,14 @@ } }, "node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.4.0.tgz", - "integrity": "sha512-mCArluxEGi3cmYHqsgpGGt3IyLCrFBxPsxNZ56Mpur0xSlInnIHoeLDH7FvVVcPJRPSQ9/MfRqLsainRw+BojA==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.5.2.tgz", + "integrity": "sha512-rjEkJH/tJ8OXRE9bwhV2mb/WP93V441rD6XnM6MIluu7rk8qg38iSxS43ga2V2Q/2ib53PcqbDEJDG/yWQRJhQ==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "tslib": "^2.6.0" }, "engines": { @@ -2453,14 +2400,14 @@ } }, "node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.4.0.tgz", - "integrity": "sha512-Dsgg6PLAqzZw5wZ4QjUYc8Z2KqJqXxHxq3vIoyoBWiLEEfigIs7wHR+oiWUQy3Zk9MIk6JTYj7tMoQU0Jm3nqA==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.5.2.tgz", + "integrity": "sha512-lm8XL3xLkTPHFKKjLjEEAHUrW0SZBSHBE1I+i/tmYMBsjCcUB5UJ52geS5PSiOCFVR74tbPGcPHEV/gaaxFeSA==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "@types/gtag.js": "^0.0.12", "tslib": "^2.6.0" }, @@ -2473,14 +2420,14 @@ } }, "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.4.0.tgz", - "integrity": "sha512-O9tX1BTwxIhgXpOLpFDueYA9DWk69WCbDRrjYoMQtFHSkTyE7RhNgyjSPREUWJb9i+YUg3OrsvrBYRl64FCPCQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.5.2.tgz", + "integrity": "sha512-QkpX68PMOMu10Mvgvr5CfZAzZQFx8WLlOiUQ/Qmmcl6mjGK6H21WLT5x7xDmcpCoKA/3CegsqIqBR+nA137lQg==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "tslib": "^2.6.0" }, "engines": { @@ -2492,17 +2439,17 @@ } }, "node_modules/@docusaurus/plugin-sitemap": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.4.0.tgz", - "integrity": "sha512-+0VDvx9SmNrFNgwPoeoCha+tRoAjopwT0+pYO1xAbyLcewXSemq+eLxEa46Q1/aoOaJQ0qqHELuQM7iS2gp33Q==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.5.2.tgz", + "integrity": "sha512-DnlqYyRAdQ4NHY28TfHuVk414ft2uruP4QWCH//jzpHjqvKyXjj2fmDtI8RPUBh9K8iZKFMHRnLtzJKySPWvFA==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "fs-extra": "^11.1.1", "sitemap": "^7.1.1", "tslib": "^2.6.0" @@ -2516,24 +2463,81 @@ } }, "node_modules/@docusaurus/preset-classic": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.4.0.tgz", - "integrity": "sha512-Ohj6KB7siKqZaQhNJVMBBUzT3Nnp6eTKqO+FXO3qu/n1hJl3YLwVKTWBg28LF7MWrKu46UuYavwMRxud0VyqHg==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.5.2.tgz", + "integrity": "sha512-3ihfXQ95aOHiLB5uCu+9PRy2gZCeSZoDcqpnDvf3B+sTrMvMTr8qRUzBvWkoIqc82yG5prCboRjk1SVILKx6sg==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/plugin-content-blog": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/plugin-content-pages": "3.4.0", - "@docusaurus/plugin-debug": "3.4.0", - "@docusaurus/plugin-google-analytics": "3.4.0", - "@docusaurus/plugin-google-gtag": "3.4.0", - "@docusaurus/plugin-google-tag-manager": "3.4.0", - "@docusaurus/plugin-sitemap": "3.4.0", - "@docusaurus/theme-classic": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/theme-search-algolia": "3.4.0", - "@docusaurus/types": "3.4.0" + "@docusaurus/core": "3.5.2", + "@docusaurus/plugin-content-blog": "3.5.2", + "@docusaurus/plugin-content-docs": "3.5.2", + "@docusaurus/plugin-content-pages": "3.5.2", + "@docusaurus/plugin-debug": "3.5.2", + "@docusaurus/plugin-google-analytics": "3.5.2", + "@docusaurus/plugin-google-gtag": "3.5.2", + "@docusaurus/plugin-google-tag-manager": "3.5.2", + "@docusaurus/plugin-sitemap": "3.5.2", + "@docusaurus/theme-classic": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/theme-search-algolia": "3.5.2", + "@docusaurus/types": "3.5.2" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/preset-classic/node_modules/@docusaurus/plugin-content-blog": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.5.2.tgz", + "integrity": "sha512-R7ghWnMvjSf+aeNDH0K4fjyQnt5L0KzUEnUhmf1e3jZrv3wogeytZNN6n7X8yHcMsuZHPOrctQhXWnmxu+IRRg==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", + "cheerio": "1.0.0-rc.12", + "feed": "^4.2.2", + "fs-extra": "^11.1.1", + "lodash": "^4.17.21", + "reading-time": "^1.5.0", + "srcset": "^4.0.0", + "tslib": "^2.6.0", + "unist-util-visit": "^5.0.0", + "utility-types": "^3.10.0", + "webpack": "^5.88.1" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "@docusaurus/plugin-content-docs": "*", + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/preset-classic/node_modules/@docusaurus/plugin-content-pages": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.5.2.tgz", + "integrity": "sha512-WzhHjNpoQAUz/ueO10cnundRz+VUtkjFhhaQ9jApyv1a46FPURO4cef89pyNIOMny1fjDz/NUN2z6Yi+5WUrCw==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", + "fs-extra": "^11.1.1", + "tslib": "^2.6.0", + "webpack": "^5.88.1" }, "engines": { "node": ">=18.0" @@ -2544,27 +2548,27 @@ } }, "node_modules/@docusaurus/theme-classic": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.4.0.tgz", - "integrity": "sha512-0IPtmxsBYv2adr1GnZRdMkEQt1YW6tpzrUPj02YxNpvJ5+ju4E13J5tB4nfdaen/tfR1hmpSPlTFPvTf4kwy8Q==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.5.2.tgz", + "integrity": "sha512-XRpinSix3NBv95Rk7xeMF9k4safMkwnpSgThn0UNQNumKvmcIYjfkwfh2BhwYh/BxMXQHJ/PdmNh22TQFpIaYg==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/plugin-content-blog": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/plugin-content-pages": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/theme-translations": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/module-type-aliases": "3.5.2", + "@docusaurus/plugin-content-blog": "3.5.2", + "@docusaurus/plugin-content-docs": "3.5.2", + "@docusaurus/plugin-content-pages": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/theme-translations": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "copy-text-to-clipboard": "^3.2.0", - "infima": "0.2.0-alpha.43", + "infima": "0.2.0-alpha.44", "lodash": "^4.17.21", "nprogress": "^0.2.0", "postcss": "^8.4.26", @@ -2583,19 +2587,73 @@ "react-dom": "^18.0.0" } }, - "node_modules/@docusaurus/theme-common": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.4.0.tgz", - "integrity": "sha512-0A27alXuv7ZdCg28oPE8nH/Iz73/IUejVaCazqu9elS4ypjiLhK3KfzdSQBnL/g7YfHSlymZKdiOHEo8fJ0qMA==", + "node_modules/@docusaurus/theme-classic/node_modules/@docusaurus/plugin-content-blog": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.5.2.tgz", + "integrity": "sha512-R7ghWnMvjSf+aeNDH0K4fjyQnt5L0KzUEnUhmf1e3jZrv3wogeytZNN6n7X8yHcMsuZHPOrctQhXWnmxu+IRRg==", "license": "MIT", "dependencies": { - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/plugin-content-blog": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/plugin-content-pages": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", + "cheerio": "1.0.0-rc.12", + "feed": "^4.2.2", + "fs-extra": "^11.1.1", + "lodash": "^4.17.21", + "reading-time": "^1.5.0", + "srcset": "^4.0.0", + "tslib": "^2.6.0", + "unist-util-visit": "^5.0.0", + "utility-types": "^3.10.0", + "webpack": "^5.88.1" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "@docusaurus/plugin-content-docs": "*", + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/theme-classic/node_modules/@docusaurus/plugin-content-pages": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.5.2.tgz", + "integrity": "sha512-WzhHjNpoQAUz/ueO10cnundRz+VUtkjFhhaQ9jApyv1a46FPURO4cef89pyNIOMny1fjDz/NUN2z6Yi+5WUrCw==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", + "fs-extra": "^11.1.1", + "tslib": "^2.6.0", + "webpack": "^5.88.1" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/theme-common": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.5.2.tgz", + "integrity": "sha512-QXqlm9S6x9Ibwjs7I2yEDgsCocp708DrCrgHgKwg2n2AY0YQ6IjU0gAK35lHRLOvAoJUfCKpQAwUykB0R7+Eew==", + "license": "MIT", + "dependencies": { + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/module-type-aliases": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2609,24 +2667,25 @@ "node": ">=18.0" }, "peerDependencies": { + "@docusaurus/plugin-content-docs": "*", "react": "^18.0.0", "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.4.0.tgz", - "integrity": "sha512-aiHFx7OCw4Wck1z6IoShVdUWIjntC8FHCw9c5dR8r3q4Ynh+zkS8y2eFFunN/DL6RXPzpnvKCg3vhLQYJDmT9Q==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.5.2.tgz", + "integrity": "sha512-qW53kp3VzMnEqZGjakaV90sst3iN1o32PH+nawv1uepROO8aEGxptcq2R5rsv7aBShSRbZwIobdvSYKsZ5pqvA==", "license": "MIT", "dependencies": { "@docsearch/react": "^3.5.2", - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/theme-translations": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/plugin-content-docs": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/theme-translations": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "algoliasearch": "^4.18.0", "algoliasearch-helper": "^3.13.3", "clsx": "^2.0.0", @@ -2645,9 +2704,9 @@ } }, "node_modules/@docusaurus/theme-translations": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.4.0.tgz", - "integrity": "sha512-zSxCSpmQCCdQU5Q4CnX/ID8CSUUI3fvmq4hU/GNP/XoAWtXo9SAVnM3TzpU8Gb//H3WCsT8mJcTfyOk3d9ftNg==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.5.2.tgz", + "integrity": "sha512-GPZLcu4aT1EmqSTmbdpVrDENGR2yObFEX8ssEFYTCiAIVc0EihNSdOIBTazUvgNqwvnoU1A8vIs1xyzc3LITTw==", "license": "MIT", "dependencies": { "fs-extra": "^11.1.1", @@ -2658,9 +2717,9 @@ } }, "node_modules/@docusaurus/types": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.4.0.tgz", - "integrity": "sha512-4jcDO8kXi5Cf9TcyikB/yKmz14f2RZ2qTRerbHAsS+5InE9ZgSLBNLsewtFTcTOXSVcbU3FoGOzcNWAmU1TR0A==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.5.2.tgz", + "integrity": "sha512-N6GntLXoLVUwkZw7zCxwy9QiuEXIcTVzA9AkmNw16oc0AP3SXLrMmDMMBIfgqwuKWa6Ox6epHol9kMtJqekACw==", "license": "MIT", "dependencies": { "@mdx-js/mdx": "^3.0.0", @@ -2679,13 +2738,13 @@ } }, "node_modules/@docusaurus/utils": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.4.0.tgz", - "integrity": "sha512-fRwnu3L3nnWaXOgs88BVBmG1yGjcQqZNHG+vInhEa2Sz2oQB+ZjbEMO5Rh9ePFpZ0YDiDUhpaVjwmS+AU2F14g==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.5.2.tgz", + "integrity": "sha512-33QvcNFh+Gv+C2dP9Y9xWEzMgf3JzrpL2nW9PopidiohS1nDcyknKRx2DWaFvyVTTYIkkABVSr073VTj/NITNA==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.4.0", - "@docusaurus/utils-common": "3.4.0", + "@docusaurus/logger": "3.5.2", + "@docusaurus/utils-common": "3.5.2", "@svgr/webpack": "^8.1.0", "escape-string-regexp": "^4.0.0", "file-loader": "^6.2.0", @@ -2718,9 +2777,9 @@ } }, "node_modules/@docusaurus/utils-common": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.4.0.tgz", - "integrity": "sha512-NVx54Wr4rCEKsjOH5QEVvxIqVvm+9kh7q8aYTU5WzUU9/Hctd6aTrcZ3G0Id4zYJ+AeaG5K5qHA4CY5Kcm2iyQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.5.2.tgz", + "integrity": "sha512-i0AZjHiRgJU6d7faQngIhuHKNrszpL/SHQPgF1zH4H+Ij6E9NBYGy6pkcGWToIv7IVPbs+pQLh1P3whn0gWXVg==", "license": "MIT", "dependencies": { "tslib": "^2.6.0" @@ -2738,14 +2797,14 @@ } }, "node_modules/@docusaurus/utils-validation": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.4.0.tgz", - "integrity": "sha512-hYQ9fM+AXYVTWxJOT1EuNaRnrR2WGpRdLDQG07O8UOpsvCPWUVOeo26Rbm0JWY2sGLfzAb+tvJ62yF+8F+TV0g==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.5.2.tgz", + "integrity": "sha512-m+Foq7augzXqB6HufdS139PFxDC5d5q2QKZy8q0qYYvGdI6nnlNsGH4cIGsgBnV7smz+mopl3g4asbSDvMV0jA==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", + "@docusaurus/logger": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", "fs-extra": "^11.2.0", "joi": "^17.9.2", "js-yaml": "^4.1.0", @@ -8767,9 +8826,10 @@ } }, "node_modules/infima": { - "version": "0.2.0-alpha.43", - "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.43.tgz", - "integrity": "sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ==", + "version": "0.2.0-alpha.44", + "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.44.tgz", + "integrity": "sha512-tuRkUSO/lB3rEhLJk25atwAjgLuzq070+pOW8XcvpHky/YbENnRRdPd85IBkyeTgttmOy5ah+yHYsK1HhUd4lQ==", + "license": "MIT", "engines": { "node": ">=12" } @@ -16020,9 +16080,9 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.9.tgz", - "integrity": "sha512-1SEOvRr6sSdV5IDf9iC+NU4dhwdqzF4zKKq3sAbasUWHEM6lsMhX+eNN5gkPx1BvLFEnZQEUFbXnGj8Qlp83Pg==", + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz", + "integrity": "sha512-KWZkVPm7yJRhdu4SRSl9d4AK2wM3a50UsvgHZO7xY77NQr2V+fIrEuoDGQcbvswWvFGbS2f6e+jC/6WJm1Dl0w==", "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", diff --git a/docs/src/components/version-switcher.tsx b/docs/src/components/version-switcher.tsx index dae822f4f7303..b89a65c6e4ae9 100644 --- a/docs/src/components/version-switcher.tsx +++ b/docs/src/components/version-switcher.tsx @@ -1,4 +1,3 @@ -import '@docusaurus/theme-classic/lib/theme/Unlisted/index'; import { useWindowSize } from '@docusaurus/theme-common'; import DropdownNavbarItem from '@theme/NavbarItem/DropdownNavbarItem'; import React, { useEffect, useState } from 'react'; diff --git a/docs/tailwind.config.js b/docs/tailwind.config.js index d3ed1f3cda916..1ef26facbb621 100644 --- a/docs/tailwind.config.js +++ b/docs/tailwind.config.js @@ -4,7 +4,7 @@ module.exports = { corePlugins: { preflight: false, // disable Tailwind's reset }, - content: ['./src/**/*.{js,jsx,ts,tsx}', '../docs/**/*.mdx'], // my markdown stuff is in ../docs, not /src + content: ['./src/**/*.{js,jsx,ts,tsx}', './{docs,blog}/**/*.{md,mdx}'], // my markdown stuff is in ../docs, not /src darkMode: ['class', '[data-theme="dark"]'], // hooks into docusaurus' dark mode settigns theme: { extend: { diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 5b85eb0147da2..cd591270db147 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -799,9 +799,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.8.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.8.0.tgz", - "integrity": "sha512-MfluB7EUfxXtv3i/++oh89uzAr4PDI4nn201hsp+qaXqsjAWzinlZEHEfPgAX4doIlKvPG/i0A9dpKxOLII8yA==", + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.0.tgz", + "integrity": "sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug==", "dev": true, "license": "MIT", "engines": { @@ -1113,13 +1113,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.46.0.tgz", - "integrity": "sha512-/QYft5VArOrGRP5pgkrfKksqsKA6CEFyGQ/gjNe6q0y4tZ1aaPfq4gIjudr1s3D+pXyrPRdsy4opKDrjBabE5w==", + "version": "1.46.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.46.1.tgz", + "integrity": "sha512-Fq6SwLujA/DOIvNC2EL/SojJnkKf/rAwJ//APpJJHRyMi1PdKrY3Az+4XNQ51N4RTbItbIByQ0jgd1tayq1aeA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.46.0" + "playwright": "1.46.1" }, "bin": { "playwright": "cli.js" @@ -1532,10 +1532,11 @@ "dev": true }, "node_modules/@types/oidc-provider": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/@types/oidc-provider/-/oidc-provider-8.5.1.tgz", - "integrity": "sha512-NS8tBPOj9GG6SxyrUHWBzglOtAYNDX41J4cRE45oeK0iSqI6V6tDW70aPWg25pJFNSC1evccXFm9evfwjxm7HQ==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@types/oidc-provider/-/oidc-provider-8.5.2.tgz", + "integrity": "sha512-NiD3VG49+cRCAAe8+uZLM4onOcX8y9+cwaml8JG1qlgc98rWoCRgsnOB4Ypx+ysays5jiwzfUgT0nWyXPB/9uQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/koa": "*", "@types/node": "*" @@ -1673,17 +1674,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.1.tgz", - "integrity": "sha512-5g3Y7GDFsJAnY4Yhvk8sZtFfV6YNF2caLzjrRPUBzewjPCaj0yokePB4LJSobyCzGMzjZZYFbwuzbfDHlimXbQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.2.0.tgz", + "integrity": "sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.0.1", - "@typescript-eslint/type-utils": "8.0.1", - "@typescript-eslint/utils": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1", + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/type-utils": "8.2.0", + "@typescript-eslint/utils": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1707,16 +1708,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.1.tgz", - "integrity": "sha512-5IgYJ9EO/12pOUwiBKFkpU7rS3IU21mtXzB81TNwq2xEybcmAZrE9qwDtsb5uQd9aVO9o0fdabFyAmKveXyujg==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.2.0.tgz", + "integrity": "sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.0.1", - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/typescript-estree": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1", + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/typescript-estree": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", "debug": "^4.3.4" }, "engines": { @@ -1736,14 +1737,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.1.tgz", - "integrity": "sha512-NpixInP5dm7uukMiRyiHjRKkom5RIFA4dfiHvalanD2cF0CLUuQqxfg8PtEUo9yqJI2bBhF+pcSafqnG3UBnRQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.2.0.tgz", + "integrity": "sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1" + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1754,14 +1755,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.1.tgz", - "integrity": "sha512-+/UT25MWvXeDX9YaHv1IS6KI1fiuTto43WprE7pgSMswHbn1Jm9GEM4Txp+X74ifOWV8emu2AWcbLhpJAvD5Ng==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.2.0.tgz", + "integrity": "sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.0.1", - "@typescript-eslint/utils": "8.0.1", + "@typescript-eslint/typescript-estree": "8.2.0", + "@typescript-eslint/utils": "8.2.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1779,9 +1780,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.1.tgz", - "integrity": "sha512-PpqTVT3yCA/bIgJ12czBuE3iBlM3g4inRSC5J0QOdQFAn07TYrYEQBBKgXH1lQpglup+Zy6c1fxuwTk4MTNKIw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.2.0.tgz", + "integrity": "sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==", "dev": true, "license": "MIT", "engines": { @@ -1793,14 +1794,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.1.tgz", - "integrity": "sha512-8V9hriRvZQXPWU3bbiUV4Epo7EvgM6RTs+sUmxp5G//dBGy402S7Fx0W0QkB2fb4obCF8SInoUzvTYtc3bkb5w==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.2.0.tgz", + "integrity": "sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1848,16 +1849,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.1.tgz", - "integrity": "sha512-CBFR0G0sCt0+fzfnKaciu9IBsKvEKYwN9UZ+eeogK1fYHg4Qxk1yf/wLQkLXlq8wbU2dFlgAesxt8Gi76E8RTA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.2.0.tgz", + "integrity": "sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.0.1", - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/typescript-estree": "8.0.1" + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/typescript-estree": "8.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1871,13 +1872,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.1.tgz", - "integrity": "sha512-W5E+o0UfUcK5EgchLZsyVWqARmsM7v54/qEq6PY3YI5arkgmCzHiuk0zKSJJbm71V0xdRna4BGomkCTXz2/LkQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.2.0.tgz", + "integrity": "sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.0.1", + "@typescript-eslint/types": "8.2.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -2888,9 +2889,9 @@ } }, "node_modules/eslint": { - "version": "9.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.8.0.tgz", - "integrity": "sha512-K8qnZ/QJzT2dLKdZJVX6W4XOwBzutMYmt0lqUS+JdXgd+HTYFlonFgkJ8s44d/zMPPCnOOk0kMWCApCPhiOy9A==", + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.0.tgz", + "integrity": "sha512-JfiKJrbx0506OEerjK2Y1QlldtBxkAlLxT5OEcRF8uaQ86noDe2k31Vw9rnSWv+MXZHj7OOUV/dA0AhdLFcyvA==", "dev": true, "license": "MIT", "dependencies": { @@ -2898,7 +2899,7 @@ "@eslint-community/regexpp": "^4.11.0", "@eslint/config-array": "^0.17.1", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.8.0", + "@eslint/js": "9.9.0", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", @@ -2937,6 +2938,14 @@ }, "funding": { "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-config-prettier": { @@ -4125,10 +4134,11 @@ } }, "node_modules/jose": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.6.3.tgz", - "integrity": "sha512-1Jh//hEEwMhNYPDDLwXHa2ePWgWiFNNUadVmguAAw2IJ6sj9mNxV5tGXJNqlMkJAybF6Lgw1mISDxTePP/187g==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.7.0.tgz", + "integrity": "sha512-3P9qfTYDVnNn642LCAqIKbTGb9a1TBxZ9ti5zEVEr48aDdflgRjhspWFb6WM4PzAfFbGMJYC4+803v8riCRAKw==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" } @@ -4436,9 +4446,9 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", "dependencies": { @@ -5178,13 +5188,13 @@ } }, "node_modules/playwright": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.0.tgz", - "integrity": "sha512-XYJ5WvfefWONh1uPAUAi0H2xXV5S3vrtcnXe6uAOgdGi3aSpqOSXX08IAjXW34xitfuOJsvXU5anXZxPSEQiJw==", + "version": "1.46.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.1.tgz", + "integrity": "sha512-oPcr1yqoXLCkgKtD5eNUPLiN40rYEM39odNpIb6VE6S7/15gJmA1NzVv6zJYusV0e7tzvkU/utBFNa/Kpxmwng==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.46.0" + "playwright-core": "1.46.1" }, "bin": { "playwright": "cli.js" @@ -5197,9 +5207,9 @@ } }, "node_modules/playwright-core": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.0.tgz", - "integrity": "sha512-9Y/d5UIwuJk8t3+lhmMSAJyNP1BUC/DqP3cQJDQQL/oWqAiuPTLgy7Q5dzglmTLwcBRdetzgNM/gni7ckfTr6A==", + "version": "1.46.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.1.tgz", + "integrity": "sha512-h9LqIQaAv+CYvWzsZ+h3RsrqCStkBHlgo6/TJlFst3cOTlLghBQlJwPOZKQJTKNaD3QIB7aAVQ+gfWbN3NXB7A==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/server/package-lock.json b/server/package-lock.json index 780ff2a63e923..972d1164633ba 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -24,7 +24,7 @@ "@opentelemetry/context-async-hooks": "^1.24.0", "@opentelemetry/exporter-prometheus": "^0.52.0", "@opentelemetry/sdk-node": "^0.52.0", - "@react-email/components": "^0.0.22", + "@react-email/components": "^0.0.23", "@socket.io/redis-adapter": "^8.3.0", "archiver": "^7.0.0", "async-lock": "^1.4.0", @@ -53,6 +53,7 @@ "openid-client": "^5.4.3", "pg": "^8.11.3", "picomatch": "^4.0.0", + "react": "^18.3.1", "react-email": "^3.0.0", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", @@ -86,6 +87,7 @@ "@types/node": "^20.16.1", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^3.0.0", + "@types/react": "^18.3.4", "@types/semver": "^7.5.8", "@types/supertest": "^6.0.0", "@types/ua-parser-js": "^0.7.36", @@ -723,9 +725,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.1.1.tgz", - "integrity": "sha512-3bfqkzuR1KLx57nZfjr2NLnFOobvyS0aTszaEGCGqmYMVDRaGvgIZbjGSV/MHSSmLgQ/b9JFHQ5xm5WRZYd+XQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.2.0.tgz", + "integrity": "sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==", "optional": true, "dependencies": { "tslib": "^2.4.0" @@ -1197,11 +1199,10 @@ "dev": true }, "node_modules/@eslint/js": { - "version": "9.8.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.8.0.tgz", - "integrity": "sha512-MfluB7EUfxXtv3i/++oh89uzAr4PDI4nn201hsp+qaXqsjAWzinlZEHEfPgAX4doIlKvPG/i0A9dpKxOLII8yA==", + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.0.tgz", + "integrity": "sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug==", "dev": true, - "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -1351,9 +1352,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.4.tgz", - "integrity": "sha512-p0suNqXufJs9t3RqLBO6vvrgr5OhgbWp76s5gTRvdmxmuv9E1rcaqGUsl3l4mKVmXPkTkTErXediAui4x+8PSA==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", "cpu": [ "arm64" ], @@ -1362,23 +1363,19 @@ "darwin" ], "engines": { - "glibc": ">=2.26", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.2" + "@img/sharp-libvips-darwin-arm64": "1.0.4" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.4.tgz", - "integrity": "sha512-0l7yRObwtTi82Z6ebVI2PnHT8EB2NxBgpK2MiKJZJ7cz32R4lxd001ecMhzzsZig3Yv9oclvqqdV93jo9hy+Dw==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", "cpu": [ "x64" ], @@ -1387,23 +1384,19 @@ "darwin" ], "engines": { - "glibc": ">=2.26", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.2" + "@img/sharp-libvips-darwin-x64": "1.0.4" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.2.tgz", - "integrity": "sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", "cpu": [ "arm64" ], @@ -1411,20 +1404,14 @@ "os": [ "darwin" ], - "engines": { - "macos": ">=11", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.2.tgz", - "integrity": "sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", "cpu": [ "x64" ], @@ -1432,20 +1419,14 @@ "os": [ "darwin" ], - "engines": { - "macos": ">=10.13", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.2.tgz", - "integrity": "sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", "cpu": [ "arm" ], @@ -1453,20 +1434,14 @@ "os": [ "linux" ], - "engines": { - "glibc": ">=2.28", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.2.tgz", - "integrity": "sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", "cpu": [ "arm64" ], @@ -1474,20 +1449,14 @@ "os": [ "linux" ], - "engines": { - "glibc": ">=2.26", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.2.tgz", - "integrity": "sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", "cpu": [ "s390x" ], @@ -1495,20 +1464,14 @@ "os": [ "linux" ], - "engines": { - "glibc": ">=2.28", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.2.tgz", - "integrity": "sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", "cpu": [ "x64" ], @@ -1516,20 +1479,14 @@ "os": [ "linux" ], - "engines": { - "glibc": ">=2.26", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.2.tgz", - "integrity": "sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", "cpu": [ "arm64" ], @@ -1537,20 +1494,14 @@ "os": [ "linux" ], - "engines": { - "musl": ">=1.2.2", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.2.tgz", - "integrity": "sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", "cpu": [ "x64" ], @@ -1558,20 +1509,14 @@ "os": [ "linux" ], - "engines": { - "musl": ">=1.2.2", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.4.tgz", - "integrity": "sha512-RUgBD1c0+gCYZGCCe6mMdTiOFS0Zc/XrN0fYd6hISIKcDUbAW5NtSQW9g/powkrXYm6Vzwd6y+fqmExDuCdHNQ==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", "cpu": [ "arm" ], @@ -1580,23 +1525,19 @@ "linux" ], "engines": { - "glibc": ">=2.28", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.2" + "@img/sharp-libvips-linux-arm": "1.0.5" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.4.tgz", - "integrity": "sha512-2800clwVg1ZQtxwSoTlHvtm9ObgAax7V6MTAB/hDT945Tfyy3hVkmiHpeLPCKYqYR1Gcmv1uDZ3a4OFwkdBL7Q==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", "cpu": [ "arm64" ], @@ -1605,23 +1546,19 @@ "linux" ], "engines": { - "glibc": ">=2.26", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.2" + "@img/sharp-libvips-linux-arm64": "1.0.4" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.4.tgz", - "integrity": "sha512-h3RAL3siQoyzSoH36tUeS0PDmb5wINKGYzcLB5C6DIiAn2F3udeFAum+gj8IbA/82+8RGCTn7XW8WTFnqag4tQ==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", "cpu": [ "s390x" ], @@ -1630,23 +1567,19 @@ "linux" ], "engines": { - "glibc": ">=2.31", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.0.2" + "@img/sharp-libvips-linux-s390x": "1.0.4" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.4.tgz", - "integrity": "sha512-GoR++s0XW9DGVi8SUGQ/U4AeIzLdNjHka6jidVwapQ/JebGVQIpi52OdyxCNVRE++n1FCLzjDovJNozif7w/Aw==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", "cpu": [ "x64" ], @@ -1655,23 +1588,19 @@ "linux" ], "engines": { - "glibc": ">=2.26", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.0.2" + "@img/sharp-libvips-linux-x64": "1.0.4" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.4.tgz", - "integrity": "sha512-nhr1yC3BlVrKDTl6cO12gTpXMl4ITBUZieehFvMntlCXFzH2bvKG76tBL2Y/OqhupZt81pR7R+Q5YhJxW0rGgQ==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", "cpu": [ "arm64" ], @@ -1680,23 +1609,19 @@ "linux" ], "engines": { - "musl": ">=1.2.2", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.2" + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.4.tgz", - "integrity": "sha512-uCPTku0zwqDmZEOi4ILyGdmW76tH7dm8kKlOIV1XC5cLyJ71ENAAqarOHQh0RLfpIpbV5KOpXzdU6XkJtS0daw==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", "cpu": [ "x64" ], @@ -1705,44 +1630,37 @@ "linux" ], "engines": { - "musl": ">=1.2.2", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.2" + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.4.tgz", - "integrity": "sha512-Bmmauh4sXUsUqkleQahpdNXKvo+wa1V9KhT2pDA4VJGKwnKMJXiSTGphn0gnJrlooda0QxCtXc6RX1XAU6hMnQ==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", "cpu": [ "wasm32" ], "optional": true, "dependencies": { - "@emnapi/runtime": "^1.1.1" + "@emnapi/runtime": "^1.2.0" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.4.tgz", - "integrity": "sha512-99SJ91XzUhYHbx7uhK3+9Lf7+LjwMGQZMDlO/E/YVJ7Nc3lyDFZPGhjwiYdctoH2BOzW9+TnfqcaMKt0jHLdqw==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", "cpu": [ "ia32" ], @@ -1751,19 +1669,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.4.tgz", - "integrity": "sha512-3QLocdTRVIrFNye5YocZl+KKpYKP+fksi1QhmOArgx7GyhIbQp/WrJRu176jm8IxromS7RIkzMiMINVdBtC8Aw==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", "cpu": [ "x64" ], @@ -1772,10 +1687,7 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" @@ -2036,9 +1948,9 @@ ] }, "node_modules/@nestjs/bull-shared": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@nestjs/bull-shared/-/bull-shared-10.2.0.tgz", - "integrity": "sha512-cSi6CyPECHDFumnHWWfwLCnbc6hm5jXt7FqzJ0Id6EhGqdz5ja0FmgRwXoS4xoMA2RRjlxn2vGXr4YOaHBAeig==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/@nestjs/bull-shared/-/bull-shared-10.2.1.tgz", + "integrity": "sha512-zvnTvSq6OJ92omcsFUwaUmPbM3PRgWkIusHPB5TE3IFS7nNdM3OwF+kfe56sgKjMtQQMe/56lok0S04OtPMX5Q==", "dependencies": { "tslib": "2.6.3" }, @@ -2048,11 +1960,11 @@ } }, "node_modules/@nestjs/bullmq": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@nestjs/bullmq/-/bullmq-10.2.0.tgz", - "integrity": "sha512-lHXWDocXh1Yl6unsUzGFEKmK02mu0DdI35cdBp3Fq/9D5V3oLuWjwAPFnTztedshIjlFmNW6x5mdaT5WZ0AV1Q==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/@nestjs/bullmq/-/bullmq-10.2.1.tgz", + "integrity": "sha512-nDR0hDabmtXt5gsb5R786BJsGIJoWh/79sVmRETXf4S45+fvdqG1XkCKAeHF9TO9USodw9m+XBNKysTnkY41gw==", "dependencies": { - "@nestjs/bull-shared": "^10.2.0", + "@nestjs/bull-shared": "^10.2.1", "tslib": "2.6.3" }, "peerDependencies": { @@ -2062,9 +1974,9 @@ } }, "node_modules/@nestjs/cli": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.4.2.tgz", - "integrity": "sha512-fQexIfLHfp6GUgX+CO4fOg+AEwV5ox/LHotQhyZi9wXUQDyIqS0NTTbumr//62EcX35qV4nU0359nYnuEdzG+A==", + "version": "10.4.4", + "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.4.4.tgz", + "integrity": "sha512-WKERbSZJGof0+9XeeMmWnb/9FpNxogcB5eTJTHjc9no0ymdTw3jTzT+KZL9iC/hGqBpuomDLaNFCYbAOt29nBw==", "dev": true, "dependencies": { "@angular-devkit/core": "17.3.8", @@ -2084,7 +1996,7 @@ "tsconfig-paths": "4.2.0", "tsconfig-paths-webpack-plugin": "4.1.0", "typescript": "5.3.3", - "webpack": "5.92.1", + "webpack": "5.93.0", "webpack-node-externals": "3.0.0" }, "bin": { @@ -2120,9 +2032,9 @@ } }, "node_modules/@nestjs/common": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.3.10.tgz", - "integrity": "sha512-H8k0jZtxk1IdtErGDmxFRy0PfcOAUg41Prrqpx76DQusGGJjsaovs1zjXVD1rZWaVYchfT1uczJ6L4Kio10VNg==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.1.tgz", + "integrity": "sha512-4CkrDx0s4XuWqFjX8WvOFV7Y6RGJd0P2OBblkhZS7nwoctoSuW5pyEa8SWak6YHNGrHRpFb6ymm5Ai4LncwRVA==", "dependencies": { "iterare": "1.2.1", "tslib": "2.6.3", @@ -2162,9 +2074,9 @@ } }, "node_modules/@nestjs/core": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.3.10.tgz", - "integrity": "sha512-ZbQ4jovQyzHtCGCrzK5NdtW1SYO2fHSsgSY1+/9WdruYCUra+JDkWEXgZ4M3Hv480Dl3OXehAmY1wCOojeMyMQ==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.1.tgz", + "integrity": "sha512-9I1WdfOBCCHdUm+ClBJupOuZQS6UxzIWHIq6Vp1brAA5ZKl/Wq6BVwSsbnUJGBy3J3PM2XHmR0EQ4fwX3nR7lA==", "hasInstallScript": true, "dependencies": { "@nuxtjs/opencollective": "0.3.2", @@ -2230,9 +2142,9 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.3.10.tgz", - "integrity": "sha512-wK2ow3CZI2KFqWeEpPmoR300OB6BcBLxARV1EiClJLCj4S1mZsoCmS0YWgpk3j1j6mo0SI8vNLi/cC2iZPEPQA==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.1.tgz", + "integrity": "sha512-ccfqIDAq/bg1ShLI5KGtaLaYGykuAdvCi57ohewH7eKJSIpWY1DQjbgKlFfXokALYUq1YOMGqjeZ244OWHfDQg==", "dependencies": { "body-parser": "1.20.2", "cors": "2.8.5", @@ -2250,9 +2162,9 @@ } }, "node_modules/@nestjs/platform-socket.io": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.3.10.tgz", - "integrity": "sha512-LRd+nGWhUu9hND1txCLPZd78Hea+qKJVENb+c9aDU04T24GRjsInDF2RANMR16JLQFcI9mclktDWX4plE95SHg==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.4.1.tgz", + "integrity": "sha512-cxn5vKBAbqtEVPl0qVcJpR4sC12+hzcY/mYXGW6ippOKQDBNc2OF8oZXu6V3O1MvAl+VM7eNNEsLmP9DRKQlnw==", "dependencies": { "socket.io": "4.7.5", "tslib": "2.6.3" @@ -2293,9 +2205,9 @@ } }, "node_modules/@nestjs/schematics": { - "version": "10.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.1.3.tgz", - "integrity": "sha512-aLJ4Nl/K/u6ZlgLa0NjKw5CuBOIgc6vudF42QvmGueu5FaMGM6IJrAuEvB5T2kr0PAfVwYmDFBBHCWdYhTw4Tg==", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.1.4.tgz", + "integrity": "sha512-QpY8ez9cTvXXPr3/KBrtSgXQHMSV6BkOUYy2c2TTe6cBqriEdGnCYqGl8cnfrQl3632q3lveQPaZ/c127dHsEw==", "dev": true, "dependencies": { "@angular-devkit/core": "17.3.8", @@ -2347,9 +2259,9 @@ } }, "node_modules/@nestjs/testing": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.3.10.tgz", - "integrity": "sha512-i3HAtVQJijxNxJq1k39aelyJlyEIBRONys7IipH/4r8W0J+M1V+y5EKDOyi4j1SdNSb/vmNyWpZ2/ewZjl3kRA==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.1.tgz", + "integrity": "sha512-pR+su5+YGqCLH0RhhVkPowQK7FCORU0/PWAywPK7LScAOtD67ZoviZ7hAU4vnGdwkg4HCB0D7W8Bkg19CGU8Xw==", "dev": true, "dependencies": { "tslib": "2.6.3" @@ -2389,9 +2301,9 @@ } }, "node_modules/@nestjs/websockets": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.3.10.tgz", - "integrity": "sha512-F/fhAC0ylAhjfCZj4Xrgc0yTJ/qltooDCa+fke7BFZLofLmE0yj7WzBVrBHsk/46kppyRcs5XrYjIQLqcDze8g==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.4.1.tgz", + "integrity": "sha512-p0Eq94WneczV2bnLEu9hl24iCIfH5eUCGgBuYOkVDySBwvya5L+gD4wUoqIqGoX1c6rkhQa+pMR7pi1EY4t93w==", "dependencies": { "iterare": "1.2.1", "object-hash": "3.0.0", @@ -4271,60 +4183,29 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", - "integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", - "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@react-email/body": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@react-email/body/-/body-0.0.9.tgz", - "integrity": "sha512-bSGF6j+MbfQKYnnN+Kf57lGp/J+ci+435OMIv/BKAtfmNzHL+ptRrsINJELiO8QzwnZmQjTGKSMAMMJiQS+xwQ==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@react-email/body/-/body-0.0.10.tgz", + "integrity": "sha512-dMJyL9aU25ieatdPtVjCyQ/WHZYHwNc+Hy/XpF8Cc18gu21cUynVEeYQzFSeigDRMeBQ3PGAyjVDPIob7YlGwA==", "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/button": { - "version": "0.0.16", - "resolved": "https://registry.npmjs.org/@react-email/button/-/button-0.0.16.tgz", - "integrity": "sha512-paptUerzDhKHEUmBuT0UecCoqo3N6ZQSyDKC1hFALTwKReGW2xQATisinho9Ybh9ZGw6IZ3n1nGtmX5k2sX70Q==", + "version": "0.0.17", + "resolved": "https://registry.npmjs.org/@react-email/button/-/button-0.0.17.tgz", + "integrity": "sha512-ioHdsk+BpGS/PqjU6JS7tUrVy9yvbUx92Z+Cem2+MbYp55oEwQ9VHf7u4f5NoM0gdhfKSehBwRdYlHt/frEMcg==", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/code-block": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/@react-email/code-block/-/code-block-0.0.6.tgz", - "integrity": "sha512-i+TEeI7AyG1pmtO2Mr+TblV08zQnOtTlYB/v45kFMlDWWKTkvIV33oLRqLYOFhCIvoO5fDZA9T+4m6PvhmcNwQ==", + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@react-email/code-block/-/code-block-0.0.7.tgz", + "integrity": "sha512-3lYLwn9rK16I4JmTR/sTzAJMVHzUmmcT1PT27+TXnQyBCfpfDV+VockSg1qhsgCusA/u6j0C97BMsa96AWEbbw==", "dependencies": { "prismjs": "1.29.0" }, @@ -4332,156 +4213,153 @@ "node": ">=18.0.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/code-inline": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@react-email/code-inline/-/code-inline-0.0.3.tgz", - "integrity": "sha512-SY5Nn4KhjcqqEBHvUwFlOLNmUT78elIGR+Y14eg02LrVKQJ38mFCfXNGDLk4wbP/2dnidkLYq9+60nf7mFMhnQ==", + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@react-email/code-inline/-/code-inline-0.0.4.tgz", + "integrity": "sha512-zj3oMQiiUCZbddSNt3k0zNfIBFK0ZNDIzzDyBaJKy6ZASTtWfB+1WFX0cpTX8q0gUiYK+A94rk5Qp68L6YXjXQ==", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/column": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@react-email/column/-/column-0.0.11.tgz", - "integrity": "sha512-KvrPuQFn0hlItRRL3vmRuOJgKG+8I0oO9HM5ReLMi5Ns313JSEQogCJaXuOEFkOVeuu5YyY6zy/+5Esccc1AxQ==", + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@react-email/column/-/column-0.0.12.tgz", + "integrity": "sha512-Rsl7iSdDaeHZO938xb+0wR5ud0Z3MVfdtPbNKJNojZi2hApwLAQXmDrnn/AcPDM5Lpl331ZljJS8vHTWxxkvKw==", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/components": { - "version": "0.0.22", - "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.0.22.tgz", - "integrity": "sha512-GO6F+fS3c3aQ6OnqL8esQ/KqtrPGwz80U6uQ8Nd/ETpgFt7y1PXvSGfr8v12wyLffAagdowc/JjoThfIr0L6aA==", + "version": "0.0.23", + "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.0.23.tgz", + "integrity": "sha512-RcBoffx2IZG6quLBXo5sj3fF47rKmmkiMhG1ZBua4nFjHYlmW8j1uUMyO5HNglxIF9E52NYq4sF7XeZRp9jYjg==", "dependencies": { - "@react-email/body": "0.0.9", - "@react-email/button": "0.0.16", - "@react-email/code-block": "0.0.6", - "@react-email/code-inline": "0.0.3", - "@react-email/column": "0.0.11", - "@react-email/container": "0.0.13", - "@react-email/font": "0.0.7", - "@react-email/head": "0.0.10", - "@react-email/heading": "0.0.13", - "@react-email/hr": "0.0.9", - "@react-email/html": "0.0.9", - "@react-email/img": "0.0.9", - "@react-email/link": "0.0.9", - "@react-email/markdown": "0.0.11", - "@react-email/preview": "0.0.10", - "@react-email/render": "0.0.17", - "@react-email/row": "0.0.9", - "@react-email/section": "0.0.13", - "@react-email/tailwind": "0.0.19", - "@react-email/text": "0.0.9" + "@react-email/body": "0.0.10", + "@react-email/button": "0.0.17", + "@react-email/code-block": "0.0.7", + "@react-email/code-inline": "0.0.4", + "@react-email/column": "0.0.12", + "@react-email/container": "0.0.14", + "@react-email/font": "0.0.8", + "@react-email/head": "0.0.11", + "@react-email/heading": "0.0.14", + "@react-email/hr": "0.0.10", + "@react-email/html": "0.0.10", + "@react-email/img": "0.0.10", + "@react-email/link": "0.0.10", + "@react-email/markdown": "0.0.12", + "@react-email/preview": "0.0.11", + "@react-email/render": "1.0.0", + "@react-email/row": "0.0.10", + "@react-email/section": "0.0.14", + "@react-email/tailwind": "0.1.0", + "@react-email/text": "0.0.10" }, "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/container": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@react-email/container/-/container-0.0.13.tgz", - "integrity": "sha512-ftke0N1FZl8MX3XXxXiiOaiJOnrQz7ZXUyqNj81K+BK+DePWIVaSmgK6Bu8fFnsgwdKuBdqjZTEtF4sIkU3FuQ==", + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@react-email/container/-/container-0.0.14.tgz", + "integrity": "sha512-NgoaJJd9tTtsrveL86Ocr/AYLkGyN3prdXKd/zm5fQpfDhy/NXezyT3iF6VlwAOEUIu64ErHpAJd+P6ygR+vjg==", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/font": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/@react-email/font/-/font-0.0.7.tgz", - "integrity": "sha512-R0/mfUV/XcUQIALjZUFT9GP+XGmIP1KPz20h9rpS5e4ji6VkQ3ENWlisxrdK5U+KA9iZQrlan+/6tUoTJ9bFsg==", + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@react-email/font/-/font-0.0.8.tgz", + "integrity": "sha512-fSBEqYyVPAyyACBBHcs3wEYzNknpHMuwcSAAKE8fOoDfGqURr/vSxKPdh4tOa9z7G4hlcEfgGrCYEa2iPT22cw==", "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/head": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/@react-email/head/-/head-0.0.10.tgz", - "integrity": "sha512-VoH399w0/i3dJFnwH0Ixf9BTuiWhSA/y8PpsCJ7CPw8Mv8WNBqMAAsw0rmrITYI8uPd15LZ2zk2uwRDvqasMRw==", + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@react-email/head/-/head-0.0.11.tgz", + "integrity": "sha512-skw5FUgyamIMK+LN+fZQ5WIKQYf0dPiRAvsUAUR2eYoZp9oRsfkIpFHr0GWPkKAYjFEj+uJjaxQ/0VzQH7svVg==", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/heading": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@react-email/heading/-/heading-0.0.13.tgz", - "integrity": "sha512-MYDzjJwljKHBLueLuyqkaHxu6N4aGOL1ms2NNyJ9WXC9mmBnLs4Y/QEf9SjE4Df3AW4iT9uyfVHuaNUb7uq5QA==", - "dependencies": { - "@radix-ui/react-slot": "1.1.0" - }, + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@react-email/heading/-/heading-0.0.14.tgz", + "integrity": "sha512-jZM7IVuZOXa0G110ES8OkxajPTypIKlzlO1K1RIe1auk76ukQRiCg1IRV4HZlWk1GGUbec5hNxsvZa2kU8cb9w==", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/hr": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@react-email/hr/-/hr-0.0.9.tgz", - "integrity": "sha512-Rte+EZL3ptH3rkVU3a7fh8/06mZ6Q679tDaWDjsw3878RQC9afWqUPp5lwgA/1pTouLmJlDs2BjRnV6H84O7iw==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@react-email/hr/-/hr-0.0.10.tgz", + "integrity": "sha512-3AA4Yjgl3zEid/KVx6uf6TuLJHVZvUc2cG9Wm9ZpWeAX4ODA+8g9HyuC0tfnjbRsVMhMcCGiECuWWXINi+60vA==", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/html": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@react-email/html/-/html-0.0.9.tgz", - "integrity": "sha512-NB74xwWaOJZxhpiy6pzkhHvugBa2vvmUa0KKnSwOEIX+WEQH8wj5UUhRN4F+Pmkiqz3QBTETUJiSsNWWFtrHgA==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@react-email/html/-/html-0.0.10.tgz", + "integrity": "sha512-06uiuSKJBWQJfhCKv4MPupELei4Lepyz9Sth7Yq7Fq29CAeB1ejLgKkGqn1I+FZ72hQxPLdYF4iq4yloKv3JCg==", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/img": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@react-email/img/-/img-0.0.9.tgz", - "integrity": "sha512-zDlQWmlSANb2dBYhDaKD12Z4xaGD5mEf3peawBYHGxYySzMLwRT2ANGvFqpDNd7iT0C5po+/9EWR8fS1dLy0QQ==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@react-email/img/-/img-0.0.10.tgz", + "integrity": "sha512-pJ8glJjDNaJ53qoM95pvX9SK05yh0bNQY/oyBKmxlBDdUII6ixuMc3SCwYXPMl+tgkQUyDgwEBpSTrLAnjL3hA==", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/link": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@react-email/link/-/link-0.0.9.tgz", - "integrity": "sha512-rRqWGPUTGFwwtMCtsdCHNh0ewOsd4UBG/D12UcwJYFKRb0U6hUG/6VJZE3tB1QYZpLIESdvOLL6ztznh+D749g==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@react-email/link/-/link-0.0.10.tgz", + "integrity": "sha512-tva3wvAWSR10lMJa9fVA09yRn7pbEki0ZZpHE6GD1jKbFhmzt38VgLO9B797/prqoDZdAr4rVK7LJFcdPx3GwA==", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/markdown": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@react-email/markdown/-/markdown-0.0.11.tgz", - "integrity": "sha512-KeDTS0bAvvtgavYAIAmxKpRxWUSr1/jufckDzu9g4QsQtth8wYaSR5wCPXuTPmhFgJMIlNSlOiBnVp+oRbDtKA==", + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@react-email/markdown/-/markdown-0.0.12.tgz", + "integrity": "sha512-wsuvj1XAb6O63aizCLNEeqVgKR3oFjAwt9vjfg2y2oh4G1dZeo8zonZM2x1fmkEkBZhzwSHraNi70jSXhA3A9w==", "dependencies": { "md-to-react-email": "5.0.2" }, @@ -4489,24 +4367,24 @@ "node": ">=18.0.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/preview": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/@react-email/preview/-/preview-0.0.10.tgz", - "integrity": "sha512-bRrv8teMMBlF7ttLp1zZUejkPUzrwMQXrigdagtEBOqsB8HxvJU2MR6Yyb3XOqBYldaIDOQJ1z61zyD2wRlKAw==", + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@react-email/preview/-/preview-0.0.11.tgz", + "integrity": "sha512-7O/CT4b16YlSGrj18htTPx3Vbhu2suCGv/cSe5c+fuSrIM/nMiBSZ3Js16Vj0XJbAmmmlVmYFZw9L20wXJ+LjQ==", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/render": { - "version": "0.0.17", - "resolved": "https://registry.npmjs.org/@react-email/render/-/render-0.0.17.tgz", - "integrity": "sha512-xBQ+/73+WsGuXKY7r1U73zMBNV28xdV0cp9cFjhNYipBReDHhV97IpA6v7Hl0dDtDzt+yS/72dY5vYXrF1v8NA==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.0.0.tgz", + "integrity": "sha512-seN2p3JRUSZhwIUiymh9N6ZfhRZ14ywOraQqAokY63DkDeHZW2pA2a6nWpNc/igfOcNyt09Wsoi1Aj0esxhdzw==", "dependencies": { "html-to-text": "9.0.5", "js-beautify": "^1.14.11", @@ -4516,52 +4394,52 @@ "node": ">=18.0.0" }, "peerDependencies": { - "react": "^18.2.0", - "react-dom": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/row": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@react-email/row/-/row-0.0.9.tgz", - "integrity": "sha512-ZDASHVvyKrWBS00o5pSH5khfMf46UtZhrHcSAfPSiC4nj7R8A0bf+3Wmbk8YmsaV+qWXUCUSHWwIAAlMRnJoAA==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@react-email/row/-/row-0.0.10.tgz", + "integrity": "sha512-jPyEhG3gsLX+Eb9U+A30fh0gK6hXJwF4ghJ+ZtFQtlKAKqHX+eCpWlqB3Xschd/ARJLod8WAswg0FB+JD9d0/A==", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/section": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@react-email/section/-/section-0.0.13.tgz", - "integrity": "sha512-McsCQ5NQlNWEMEAR3EtCxHgRhxGmLD+jPvj7A3FD7y2X3fXG0hbmUGX12B63rIywSWjJoQi6tojx/8RpzbyeTA==", + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@react-email/section/-/section-0.0.14.tgz", + "integrity": "sha512-+fYWLb4tPU1A/+GE5J1+SEMA7/wR3V30lQ+OR9t2kAJqNrARDbMx0bLnYnR1QL5TiFRz0pCF05SQUobk6gHEDQ==", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/tailwind": { - "version": "0.0.19", - "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-0.0.19.tgz", - "integrity": "sha512-bA0w4D7mSNowxWhcO0jBJauFIPf2Ok7QuKlrHwCcxyX35L2pb5D6ZmXYOrD9C6ADQuVz5oEX+oed3zpSLROgPg==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-0.1.0.tgz", + "integrity": "sha512-qysVUEY+M3SKUvu35XDpzn7yokhqFOT3tPU6Mj/pgc62TL5tQFj6msEbBtwoKs2qO3WZvai0DIHdLhaOxBQSow==", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@react-email/text": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@react-email/text/-/text-0.0.9.tgz", - "integrity": "sha512-UNFPGerER3zywpb1ODOS2VgHP7rgOmiTxMHn75pjvQf/gi3/jN9edEQLYvRgPv/mNn4IpJFkOrlP8jcammLeew==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@react-email/text/-/text-0.0.10.tgz", + "integrity": "sha512-wNAnxeEAiFs6N+SxS0y6wTJWfewEzUETuyS2aZmT00xk50VijwyFRuhm4sYSjusMyshevomFwz5jNISCxRsGWw==", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "node_modules/@rollup/pluginutils": { @@ -4869,9 +4747,9 @@ "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" }, "node_modules/@swc/core": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.6.tgz", - "integrity": "sha512-FZxyao9eQks1MRmUshgsZTmlg/HB2oXK5fghkoWJm/1CU2q2kaJlVDll2as5j+rmWiwkp0Gidlq8wlXcEEAO+g==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.14.tgz", + "integrity": "sha512-9aeXeifnyuvc2pcuuhPQgVUwdpGEzZ+9nJu0W8/hNl/aESFsJGR5i9uQJRGu0atoNr01gK092fvmqMmQAPcKow==", "devOptional": true, "hasInstallScript": true, "dependencies": { @@ -4886,16 +4764,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.7.6", - "@swc/core-darwin-x64": "1.7.6", - "@swc/core-linux-arm-gnueabihf": "1.7.6", - "@swc/core-linux-arm64-gnu": "1.7.6", - "@swc/core-linux-arm64-musl": "1.7.6", - "@swc/core-linux-x64-gnu": "1.7.6", - "@swc/core-linux-x64-musl": "1.7.6", - "@swc/core-win32-arm64-msvc": "1.7.6", - "@swc/core-win32-ia32-msvc": "1.7.6", - "@swc/core-win32-x64-msvc": "1.7.6" + "@swc/core-darwin-arm64": "1.7.14", + "@swc/core-darwin-x64": "1.7.14", + "@swc/core-linux-arm-gnueabihf": "1.7.14", + "@swc/core-linux-arm64-gnu": "1.7.14", + "@swc/core-linux-arm64-musl": "1.7.14", + "@swc/core-linux-x64-gnu": "1.7.14", + "@swc/core-linux-x64-musl": "1.7.14", + "@swc/core-win32-arm64-msvc": "1.7.14", + "@swc/core-win32-ia32-msvc": "1.7.14", + "@swc/core-win32-x64-msvc": "1.7.14" }, "peerDependencies": { "@swc/helpers": "*" @@ -4907,9 +4785,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.6.tgz", - "integrity": "sha512-6lYHey84ZzsdtC7UuPheM4Rm0Inzxm6Sb8U6dmKc4eCx8JL0LfWG4LC5RsdsrTxnjTsbriWlnhZBffh8ijUHIQ==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.14.tgz", + "integrity": "sha512-V0OUXjOH+hdGxDYG8NkQzy25mKOpcNKFpqtZEzLe5V/CpLJPnpg1+pMz70m14s9ZFda9OxsjlvPbg1FLUwhgIQ==", "cpu": [ "arm64" ], @@ -4923,9 +4801,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.6.tgz", - "integrity": "sha512-Fyl+8aH9O5rpx4O7r2KnsPpoi32iWoKOYKiipeTbGjQ/E95tNPxbmsz4yqE8Ovldcga60IPJ5OKQA3HWRiuzdw==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.14.tgz", + "integrity": "sha512-9iFvUnxG6FC3An5ogp5jbBfQuUmTTwy8KMB+ZddUoPB3NR1eV+Y9vOh/tfWcenSJbgOKDLgYC5D/b1mHAprsrQ==", "cpu": [ "x64" ], @@ -4939,9 +4817,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.6.tgz", - "integrity": "sha512-2WxYTqFaOx48GKC2cbO1/IntA+w+kfCFy436Ij7qRqqtV/WAvTM9TC1OmiFbqq436rSot52qYmX8fkwdB5UcLQ==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.14.tgz", + "integrity": "sha512-zGJsef9qPivKSH8Vv4F/HiBXBTHZ5Hs3ZjVGo/UIdWPJF8fTL9OVADiRrl34Q7zOZEtGXRwEKLUW1SCQcbDvZA==", "cpu": [ "arm" ], @@ -4955,9 +4833,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.6.tgz", - "integrity": "sha512-TBEGMSe0LhvPe4S7E68c7VzgT3OMu4VTmBLS7B2aHv4v8uZO92Khpp7L0WqgYU1y5eMjk+XLDLi4kokiNHv/Hg==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.14.tgz", + "integrity": "sha512-AxV3MPsoI7i4B8FXOew3dx3N8y00YoJYvIPfxelw07RegeCEH3aHp2U2DtgbP/NV1ugZMx0TL2Z2DEvocmA51g==", "cpu": [ "arm64" ], @@ -4971,9 +4849,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.6.tgz", - "integrity": "sha512-QI8QGL0HGT42tj7F1A+YAzhGkJjUcvvTfI1e2m704W0Enl2/UIK9v5D1zvQzYwusRyKuaQfbeBRYDh0NcLOGLg==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.14.tgz", + "integrity": "sha512-JDLdNjUj3zPehd4+DrQD8Ltb3B5lD8D05IwePyDWw+uR/YPc7w/TX1FUVci5h3giJnlMCJRvi1IQYV7K1n7KtQ==", "cpu": [ "arm64" ], @@ -4987,9 +4865,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.6.tgz", - "integrity": "sha512-61AYVzhjuNQAVIKKWOJu3H0/pFD28RYJGxnGg3YMhvRLRyuWNyY5Nyyj2WkKcz/ON+g38Arlz00NT1LDIViRLg==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.14.tgz", + "integrity": "sha512-Siy5OvPCLLWmMdx4msnEs8HvEVUEigSn0+3pbLjv78iwzXd0qSBNHUPZyC1xeurVaUbpNDxZTpPRIwpqNE2+Og==", "cpu": [ "x64" ], @@ -5003,9 +4881,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.6.tgz", - "integrity": "sha512-hQFznpfLK8XajfAAN9Cjs0w/aVmO7iu9VZvInyrTCRcPqxV5O+rvrhRxKvC1LRMZXr5M6JRSRtepp5w+TK4kAw==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.14.tgz", + "integrity": "sha512-FtEGm9mwtRYQNK43WMtUIadxHs/ja2rnDurB99os0ZoFTGG2IHuht2zD97W0wB8JbqEabT1XwSG9Y5wmN+ciEQ==", "cpu": [ "x64" ], @@ -5019,9 +4897,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.6.tgz", - "integrity": "sha512-Aqsd9afykVMuekzjm4X4TDqwxmG4CrzoOSFe0hZrn9SMio72l5eAPnMtYoe5LsIqtjV8MNprLfXaNbjHjTegmA==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.14.tgz", + "integrity": "sha512-Jp8KDlfq7Ntt2/BXr0y344cYgB1zf0DaLzDZ1ZJR6rYlAzWYSccLYcxHa97VGnsYhhPspMpmCvHid97oe2hl4A==", "cpu": [ "arm64" ], @@ -5035,9 +4913,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.6.tgz", - "integrity": "sha512-9h0hYnOeRVNeQgHQTvD1Im67faNSSzBZ7Adtxyu9urNLfBTJilMllFd2QuGHlKW5+uaT6ZH7ZWDb+c/enx7Lcg==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.14.tgz", + "integrity": "sha512-I+cFsXF0OU0J9J4zdWiQKKLURO5dvCujH9Jr8N0cErdy54l9d4gfIxdctfTF+7FyXtWKLTCkp+oby9BQhkFGWA==", "cpu": [ "ia32" ], @@ -5051,9 +4929,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.6.tgz", - "integrity": "sha512-izeoB8glCSe6IIDQmrVm6bvR9muk9TeKgmtY7b6l1BwL4BFnTUk4dMmpbntT90bEVQn3JPCaPtUG4HfL8VuyuA==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.14.tgz", + "integrity": "sha512-NNrprQCK6d28mG436jVo2TD+vACHseUECacEBGZ9Ef0qfOIWS1XIt2MisQKG0Oea2VvLFl6tF/V4Lnx/H0Sn3Q==", "cpu": [ "x64" ], @@ -5508,8 +5386,7 @@ "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", - "optional": true, - "peer": true + "dev": true }, "node_modules/@types/qs": { "version": "6.9.8", @@ -5524,11 +5401,10 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz", - "integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==", - "optional": true, - "peer": true, + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.4.tgz", + "integrity": "sha512-J7W30FTdfCxDDjmfRM+/JqLHBIyl7xUIp9kwK637FGmY7+mkSFSe6L4jpZzhj5QMfLssSDP4/i75AKkrdC7/Jw==", + "dev": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -5644,16 +5520,16 @@ "integrity": "sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.1.tgz", - "integrity": "sha512-5g3Y7GDFsJAnY4Yhvk8sZtFfV6YNF2caLzjrRPUBzewjPCaj0yokePB4LJSobyCzGMzjZZYFbwuzbfDHlimXbQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.2.0.tgz", + "integrity": "sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.0.1", - "@typescript-eslint/type-utils": "8.0.1", - "@typescript-eslint/utils": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1", + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/type-utils": "8.2.0", + "@typescript-eslint/utils": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -5677,15 +5553,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.1.tgz", - "integrity": "sha512-5IgYJ9EO/12pOUwiBKFkpU7rS3IU21mtXzB81TNwq2xEybcmAZrE9qwDtsb5uQd9aVO9o0fdabFyAmKveXyujg==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.2.0.tgz", + "integrity": "sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.0.1", - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/typescript-estree": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1", + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/typescript-estree": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", "debug": "^4.3.4" }, "engines": { @@ -5705,13 +5581,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.1.tgz", - "integrity": "sha512-NpixInP5dm7uukMiRyiHjRKkom5RIFA4dfiHvalanD2cF0CLUuQqxfg8PtEUo9yqJI2bBhF+pcSafqnG3UBnRQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.2.0.tgz", + "integrity": "sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1" + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5722,13 +5598,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.1.tgz", - "integrity": "sha512-+/UT25MWvXeDX9YaHv1IS6KI1fiuTto43WprE7pgSMswHbn1Jm9GEM4Txp+X74ifOWV8emu2AWcbLhpJAvD5Ng==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.2.0.tgz", + "integrity": "sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.0.1", - "@typescript-eslint/utils": "8.0.1", + "@typescript-eslint/typescript-estree": "8.2.0", + "@typescript-eslint/utils": "8.2.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -5746,9 +5622,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.1.tgz", - "integrity": "sha512-PpqTVT3yCA/bIgJ12czBuE3iBlM3g4inRSC5J0QOdQFAn07TYrYEQBBKgXH1lQpglup+Zy6c1fxuwTk4MTNKIw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.2.0.tgz", + "integrity": "sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5759,13 +5635,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.1.tgz", - "integrity": "sha512-8V9hriRvZQXPWU3bbiUV4Epo7EvgM6RTs+sUmxp5G//dBGy402S7Fx0W0QkB2fb4obCF8SInoUzvTYtc3bkb5w==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.2.0.tgz", + "integrity": "sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -5811,15 +5687,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.1.tgz", - "integrity": "sha512-CBFR0G0sCt0+fzfnKaciu9IBsKvEKYwN9UZ+eeogK1fYHg4Qxk1yf/wLQkLXlq8wbU2dFlgAesxt8Gi76E8RTA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.2.0.tgz", + "integrity": "sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.0.1", - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/typescript-estree": "8.0.1" + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/typescript-estree": "8.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5833,12 +5709,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.1.tgz", - "integrity": "sha512-W5E+o0UfUcK5EgchLZsyVWqARmsM7v54/qEq6PY3YI5arkgmCzHiuk0zKSJJbm71V0xdRna4BGomkCTXz2/LkQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.2.0.tgz", + "integrity": "sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.0.1", + "@typescript-eslint/types": "8.2.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -7620,8 +7496,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "optional": true, - "peer": true + "dev": true }, "node_modules/dayjs": { "version": "1.11.10", @@ -8167,16 +8042,16 @@ } }, "node_modules/eslint": { - "version": "9.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.8.0.tgz", - "integrity": "sha512-K8qnZ/QJzT2dLKdZJVX6W4XOwBzutMYmt0lqUS+JdXgd+HTYFlonFgkJ8s44d/zMPPCnOOk0kMWCApCPhiOy9A==", + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.0.tgz", + "integrity": "sha512-JfiKJrbx0506OEerjK2Y1QlldtBxkAlLxT5OEcRF8uaQ86noDe2k31Vw9rnSWv+MXZHj7OOUV/dA0AhdLFcyvA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", "@eslint/config-array": "^0.17.1", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.8.0", + "@eslint/js": "9.9.0", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", @@ -8215,6 +8090,14 @@ }, "funding": { "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-config-prettier": { @@ -10256,7 +10139,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "peer": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -10767,9 +10649,9 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "node_modules/nest-commander": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/nest-commander/-/nest-commander-3.14.0.tgz", - "integrity": "sha512-3HEfsEzoKEZ/5/cptkXlL8/31qohPxtMevoFo4j9NMe3q5PgI/0TgTYN/6py9GnFD51jSasEfFGChs1BJ+Enag==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/nest-commander/-/nest-commander-3.15.0.tgz", + "integrity": "sha512-o9VEfFj/w2nm+hQi6fnkxL1GAFZW/KmuGcIE7/B/TX0gwm0QVy8svAF75EQm8wrDjcvWS7Cx/ArnkFn2C+iM2w==", "dependencies": { "@fig/complete-commander": "^3.0.0", "@golevelup/nestjs-discovery": "4.0.1", @@ -12034,7 +11916,6 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -13309,42 +13190,41 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, "node_modules/sharp": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.4.tgz", - "integrity": "sha512-7i/dt5kGl7qR4gwPRD2biwD2/SvBn3O04J77XKFgL2OnZtQw+AG9wnuS/csmu80nPRHLYE9E41fyEiG8nhH6/Q==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", "hasInstallScript": true, "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", - "semver": "^7.6.0" + "semver": "^7.6.3" }, "engines": { - "libvips": ">=8.15.2", "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.33.4", - "@img/sharp-darwin-x64": "0.33.4", - "@img/sharp-libvips-darwin-arm64": "1.0.2", - "@img/sharp-libvips-darwin-x64": "1.0.2", - "@img/sharp-libvips-linux-arm": "1.0.2", - "@img/sharp-libvips-linux-arm64": "1.0.2", - "@img/sharp-libvips-linux-s390x": "1.0.2", - "@img/sharp-libvips-linux-x64": "1.0.2", - "@img/sharp-libvips-linuxmusl-arm64": "1.0.2", - "@img/sharp-libvips-linuxmusl-x64": "1.0.2", - "@img/sharp-linux-arm": "0.33.4", - "@img/sharp-linux-arm64": "0.33.4", - "@img/sharp-linux-s390x": "0.33.4", - "@img/sharp-linux-x64": "0.33.4", - "@img/sharp-linuxmusl-arm64": "0.33.4", - "@img/sharp-linuxmusl-x64": "0.33.4", - "@img/sharp-wasm32": "0.33.4", - "@img/sharp-win32-ia32": "0.33.4", - "@img/sharp-win32-x64": "0.33.4" + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" } }, "node_modules/shebang-command": { @@ -13582,9 +13462,9 @@ } }, "node_modules/sql-formatter": { - "version": "15.3.2", - "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.3.2.tgz", - "integrity": "sha512-pNxSMf5DtwhpZ8gUcOGCGZIWtCcyAUx9oLgAtlO4ag7DvlfnETL0BGqXaISc84pNrXvTWmt8Wal1FWKxdTsL3Q==", + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.4.0.tgz", + "integrity": "sha512-h3uVulRmOfARvDejuSzs9GMbua/UmGCKiP08zyHT1PnG376zk9CHVsDAcKIc9TcIwIrDH3YULWwI4PrXdmLRVw==", "dev": true, "dependencies": { "argparse": "^2.0.1", @@ -15148,9 +15028,9 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/webpack": { - "version": "5.92.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.92.1.tgz", - "integrity": "sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==", + "version": "5.93.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz", + "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", @@ -15931,9 +15811,9 @@ } }, "@emnapi/runtime": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.1.1.tgz", - "integrity": "sha512-3bfqkzuR1KLx57nZfjr2NLnFOobvyS0aTszaEGCGqmYMVDRaGvgIZbjGSV/MHSSmLgQ/b9JFHQ5xm5WRZYd+XQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.2.0.tgz", + "integrity": "sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==", "optional": true, "requires": { "tslib": "^2.4.0" @@ -16170,9 +16050,9 @@ } }, "@eslint/js": { - "version": "9.8.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.8.0.tgz", - "integrity": "sha512-MfluB7EUfxXtv3i/++oh89uzAr4PDI4nn201hsp+qaXqsjAWzinlZEHEfPgAX4doIlKvPG/i0A9dpKxOLII8yA==", + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.0.tgz", + "integrity": "sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug==", "dev": true }, "@eslint/object-schema": { @@ -16277,144 +16157,144 @@ "dev": true }, "@img/sharp-darwin-arm64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.4.tgz", - "integrity": "sha512-p0suNqXufJs9t3RqLBO6vvrgr5OhgbWp76s5gTRvdmxmuv9E1rcaqGUsl3l4mKVmXPkTkTErXediAui4x+8PSA==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", "optional": true, "requires": { - "@img/sharp-libvips-darwin-arm64": "1.0.2" + "@img/sharp-libvips-darwin-arm64": "1.0.4" } }, "@img/sharp-darwin-x64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.4.tgz", - "integrity": "sha512-0l7yRObwtTi82Z6ebVI2PnHT8EB2NxBgpK2MiKJZJ7cz32R4lxd001ecMhzzsZig3Yv9oclvqqdV93jo9hy+Dw==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", "optional": true, "requires": { - "@img/sharp-libvips-darwin-x64": "1.0.2" + "@img/sharp-libvips-darwin-x64": "1.0.4" } }, "@img/sharp-libvips-darwin-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.2.tgz", - "integrity": "sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", "optional": true }, "@img/sharp-libvips-darwin-x64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.2.tgz", - "integrity": "sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", "optional": true }, "@img/sharp-libvips-linux-arm": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.2.tgz", - "integrity": "sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", "optional": true }, "@img/sharp-libvips-linux-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.2.tgz", - "integrity": "sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", "optional": true }, "@img/sharp-libvips-linux-s390x": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.2.tgz", - "integrity": "sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", "optional": true }, "@img/sharp-libvips-linux-x64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.2.tgz", - "integrity": "sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", "optional": true }, "@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.2.tgz", - "integrity": "sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", "optional": true }, "@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.2.tgz", - "integrity": "sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", "optional": true }, "@img/sharp-linux-arm": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.4.tgz", - "integrity": "sha512-RUgBD1c0+gCYZGCCe6mMdTiOFS0Zc/XrN0fYd6hISIKcDUbAW5NtSQW9g/powkrXYm6Vzwd6y+fqmExDuCdHNQ==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", "optional": true, "requires": { - "@img/sharp-libvips-linux-arm": "1.0.2" + "@img/sharp-libvips-linux-arm": "1.0.5" } }, "@img/sharp-linux-arm64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.4.tgz", - "integrity": "sha512-2800clwVg1ZQtxwSoTlHvtm9ObgAax7V6MTAB/hDT945Tfyy3hVkmiHpeLPCKYqYR1Gcmv1uDZ3a4OFwkdBL7Q==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", "optional": true, "requires": { - "@img/sharp-libvips-linux-arm64": "1.0.2" + "@img/sharp-libvips-linux-arm64": "1.0.4" } }, "@img/sharp-linux-s390x": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.4.tgz", - "integrity": "sha512-h3RAL3siQoyzSoH36tUeS0PDmb5wINKGYzcLB5C6DIiAn2F3udeFAum+gj8IbA/82+8RGCTn7XW8WTFnqag4tQ==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", "optional": true, "requires": { - "@img/sharp-libvips-linux-s390x": "1.0.2" + "@img/sharp-libvips-linux-s390x": "1.0.4" } }, "@img/sharp-linux-x64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.4.tgz", - "integrity": "sha512-GoR++s0XW9DGVi8SUGQ/U4AeIzLdNjHka6jidVwapQ/JebGVQIpi52OdyxCNVRE++n1FCLzjDovJNozif7w/Aw==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", "optional": true, "requires": { - "@img/sharp-libvips-linux-x64": "1.0.2" + "@img/sharp-libvips-linux-x64": "1.0.4" } }, "@img/sharp-linuxmusl-arm64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.4.tgz", - "integrity": "sha512-nhr1yC3BlVrKDTl6cO12gTpXMl4ITBUZieehFvMntlCXFzH2bvKG76tBL2Y/OqhupZt81pR7R+Q5YhJxW0rGgQ==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", "optional": true, "requires": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.2" + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" } }, "@img/sharp-linuxmusl-x64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.4.tgz", - "integrity": "sha512-uCPTku0zwqDmZEOi4ILyGdmW76tH7dm8kKlOIV1XC5cLyJ71ENAAqarOHQh0RLfpIpbV5KOpXzdU6XkJtS0daw==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", "optional": true, "requires": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.2" + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" } }, "@img/sharp-wasm32": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.4.tgz", - "integrity": "sha512-Bmmauh4sXUsUqkleQahpdNXKvo+wa1V9KhT2pDA4VJGKwnKMJXiSTGphn0gnJrlooda0QxCtXc6RX1XAU6hMnQ==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", "optional": true, "requires": { - "@emnapi/runtime": "^1.1.1" + "@emnapi/runtime": "^1.2.0" } }, "@img/sharp-win32-ia32": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.4.tgz", - "integrity": "sha512-99SJ91XzUhYHbx7uhK3+9Lf7+LjwMGQZMDlO/E/YVJ7Nc3lyDFZPGhjwiYdctoH2BOzW9+TnfqcaMKt0jHLdqw==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", "optional": true }, "@img/sharp-win32-x64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.4.tgz", - "integrity": "sha512-3QLocdTRVIrFNye5YocZl+KKpYKP+fksi1QhmOArgx7GyhIbQp/WrJRu176jm8IxromS7RIkzMiMINVdBtC8Aw==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", "optional": true }, "@ioredis/commands": { @@ -16600,26 +16480,26 @@ "optional": true }, "@nestjs/bull-shared": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@nestjs/bull-shared/-/bull-shared-10.2.0.tgz", - "integrity": "sha512-cSi6CyPECHDFumnHWWfwLCnbc6hm5jXt7FqzJ0Id6EhGqdz5ja0FmgRwXoS4xoMA2RRjlxn2vGXr4YOaHBAeig==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/@nestjs/bull-shared/-/bull-shared-10.2.1.tgz", + "integrity": "sha512-zvnTvSq6OJ92omcsFUwaUmPbM3PRgWkIusHPB5TE3IFS7nNdM3OwF+kfe56sgKjMtQQMe/56lok0S04OtPMX5Q==", "requires": { "tslib": "2.6.3" } }, "@nestjs/bullmq": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@nestjs/bullmq/-/bullmq-10.2.0.tgz", - "integrity": "sha512-lHXWDocXh1Yl6unsUzGFEKmK02mu0DdI35cdBp3Fq/9D5V3oLuWjwAPFnTztedshIjlFmNW6x5mdaT5WZ0AV1Q==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/@nestjs/bullmq/-/bullmq-10.2.1.tgz", + "integrity": "sha512-nDR0hDabmtXt5gsb5R786BJsGIJoWh/79sVmRETXf4S45+fvdqG1XkCKAeHF9TO9USodw9m+XBNKysTnkY41gw==", "requires": { - "@nestjs/bull-shared": "^10.2.0", + "@nestjs/bull-shared": "^10.2.1", "tslib": "2.6.3" } }, "@nestjs/cli": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.4.2.tgz", - "integrity": "sha512-fQexIfLHfp6GUgX+CO4fOg+AEwV5ox/LHotQhyZi9wXUQDyIqS0NTTbumr//62EcX35qV4nU0359nYnuEdzG+A==", + "version": "10.4.4", + "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.4.4.tgz", + "integrity": "sha512-WKERbSZJGof0+9XeeMmWnb/9FpNxogcB5eTJTHjc9no0ymdTw3jTzT+KZL9iC/hGqBpuomDLaNFCYbAOt29nBw==", "dev": true, "requires": { "@angular-devkit/core": "17.3.8", @@ -16639,7 +16519,7 @@ "tsconfig-paths": "4.2.0", "tsconfig-paths-webpack-plugin": "4.1.0", "typescript": "5.3.3", - "webpack": "5.92.1", + "webpack": "5.93.0", "webpack-node-externals": "3.0.0" }, "dependencies": { @@ -16652,9 +16532,9 @@ } }, "@nestjs/common": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.3.10.tgz", - "integrity": "sha512-H8k0jZtxk1IdtErGDmxFRy0PfcOAUg41Prrqpx76DQusGGJjsaovs1zjXVD1rZWaVYchfT1uczJ6L4Kio10VNg==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.1.tgz", + "integrity": "sha512-4CkrDx0s4XuWqFjX8WvOFV7Y6RGJd0P2OBblkhZS7nwoctoSuW5pyEa8SWak6YHNGrHRpFb6ymm5Ai4LncwRVA==", "requires": { "iterare": "1.2.1", "tslib": "2.6.3", @@ -16672,9 +16552,9 @@ } }, "@nestjs/core": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.3.10.tgz", - "integrity": "sha512-ZbQ4jovQyzHtCGCrzK5NdtW1SYO2fHSsgSY1+/9WdruYCUra+JDkWEXgZ4M3Hv480Dl3OXehAmY1wCOojeMyMQ==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.1.tgz", + "integrity": "sha512-9I1WdfOBCCHdUm+ClBJupOuZQS6UxzIWHIq6Vp1brAA5ZKl/Wq6BVwSsbnUJGBy3J3PM2XHmR0EQ4fwX3nR7lA==", "requires": { "@nuxtjs/opencollective": "0.3.2", "fast-safe-stringify": "2.1.1", @@ -16699,9 +16579,9 @@ "requires": {} }, "@nestjs/platform-express": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.3.10.tgz", - "integrity": "sha512-wK2ow3CZI2KFqWeEpPmoR300OB6BcBLxARV1EiClJLCj4S1mZsoCmS0YWgpk3j1j6mo0SI8vNLi/cC2iZPEPQA==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.1.tgz", + "integrity": "sha512-ccfqIDAq/bg1ShLI5KGtaLaYGykuAdvCi57ohewH7eKJSIpWY1DQjbgKlFfXokALYUq1YOMGqjeZ244OWHfDQg==", "requires": { "body-parser": "1.20.2", "cors": "2.8.5", @@ -16711,9 +16591,9 @@ } }, "@nestjs/platform-socket.io": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.3.10.tgz", - "integrity": "sha512-LRd+nGWhUu9hND1txCLPZd78Hea+qKJVENb+c9aDU04T24GRjsInDF2RANMR16JLQFcI9mclktDWX4plE95SHg==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.4.1.tgz", + "integrity": "sha512-cxn5vKBAbqtEVPl0qVcJpR4sC12+hzcY/mYXGW6ippOKQDBNc2OF8oZXu6V3O1MvAl+VM7eNNEsLmP9DRKQlnw==", "requires": { "socket.io": "4.7.5", "tslib": "2.6.3" @@ -16736,9 +16616,9 @@ } }, "@nestjs/schematics": { - "version": "10.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.1.3.tgz", - "integrity": "sha512-aLJ4Nl/K/u6ZlgLa0NjKw5CuBOIgc6vudF42QvmGueu5FaMGM6IJrAuEvB5T2kr0PAfVwYmDFBBHCWdYhTw4Tg==", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.1.4.tgz", + "integrity": "sha512-QpY8ez9cTvXXPr3/KBrtSgXQHMSV6BkOUYy2c2TTe6cBqriEdGnCYqGl8cnfrQl3632q3lveQPaZ/c127dHsEw==", "dev": true, "requires": { "@angular-devkit/core": "17.3.8", @@ -16770,9 +16650,9 @@ } }, "@nestjs/testing": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.3.10.tgz", - "integrity": "sha512-i3HAtVQJijxNxJq1k39aelyJlyEIBRONys7IipH/4r8W0J+M1V+y5EKDOyi4j1SdNSb/vmNyWpZ2/ewZjl3kRA==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.1.tgz", + "integrity": "sha512-pR+su5+YGqCLH0RhhVkPowQK7FCORU0/PWAywPK7LScAOtD67ZoviZ7hAU4vnGdwkg4HCB0D7W8Bkg19CGU8Xw==", "dev": true, "requires": { "tslib": "2.6.3" @@ -16787,9 +16667,9 @@ } }, "@nestjs/websockets": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.3.10.tgz", - "integrity": "sha512-F/fhAC0ylAhjfCZj4Xrgc0yTJ/qltooDCa+fke7BFZLofLmE0yj7WzBVrBHsk/46kppyRcs5XrYjIQLqcDze8g==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.4.1.tgz", + "integrity": "sha512-p0Eq94WneczV2bnLEu9hl24iCIfH5eUCGgBuYOkVDySBwvya5L+gD4wUoqIqGoX1c6rkhQa+pMR7pi1EY4t93w==", "requires": { "iterare": "1.2.1", "object-hash": "3.0.0", @@ -18008,147 +17888,131 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, - "@radix-ui/react-compose-refs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", - "integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==", - "requires": {} - }, - "@radix-ui/react-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", - "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", - "requires": { - "@radix-ui/react-compose-refs": "1.1.0" - } - }, "@react-email/body": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@react-email/body/-/body-0.0.9.tgz", - "integrity": "sha512-bSGF6j+MbfQKYnnN+Kf57lGp/J+ci+435OMIv/BKAtfmNzHL+ptRrsINJELiO8QzwnZmQjTGKSMAMMJiQS+xwQ==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@react-email/body/-/body-0.0.10.tgz", + "integrity": "sha512-dMJyL9aU25ieatdPtVjCyQ/WHZYHwNc+Hy/XpF8Cc18gu21cUynVEeYQzFSeigDRMeBQ3PGAyjVDPIob7YlGwA==", "requires": {} }, "@react-email/button": { - "version": "0.0.16", - "resolved": "https://registry.npmjs.org/@react-email/button/-/button-0.0.16.tgz", - "integrity": "sha512-paptUerzDhKHEUmBuT0UecCoqo3N6ZQSyDKC1hFALTwKReGW2xQATisinho9Ybh9ZGw6IZ3n1nGtmX5k2sX70Q==", + "version": "0.0.17", + "resolved": "https://registry.npmjs.org/@react-email/button/-/button-0.0.17.tgz", + "integrity": "sha512-ioHdsk+BpGS/PqjU6JS7tUrVy9yvbUx92Z+Cem2+MbYp55oEwQ9VHf7u4f5NoM0gdhfKSehBwRdYlHt/frEMcg==", "requires": {} }, "@react-email/code-block": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/@react-email/code-block/-/code-block-0.0.6.tgz", - "integrity": "sha512-i+TEeI7AyG1pmtO2Mr+TblV08zQnOtTlYB/v45kFMlDWWKTkvIV33oLRqLYOFhCIvoO5fDZA9T+4m6PvhmcNwQ==", + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@react-email/code-block/-/code-block-0.0.7.tgz", + "integrity": "sha512-3lYLwn9rK16I4JmTR/sTzAJMVHzUmmcT1PT27+TXnQyBCfpfDV+VockSg1qhsgCusA/u6j0C97BMsa96AWEbbw==", "requires": { "prismjs": "1.29.0" } }, "@react-email/code-inline": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@react-email/code-inline/-/code-inline-0.0.3.tgz", - "integrity": "sha512-SY5Nn4KhjcqqEBHvUwFlOLNmUT78elIGR+Y14eg02LrVKQJ38mFCfXNGDLk4wbP/2dnidkLYq9+60nf7mFMhnQ==", + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@react-email/code-inline/-/code-inline-0.0.4.tgz", + "integrity": "sha512-zj3oMQiiUCZbddSNt3k0zNfIBFK0ZNDIzzDyBaJKy6ZASTtWfB+1WFX0cpTX8q0gUiYK+A94rk5Qp68L6YXjXQ==", "requires": {} }, "@react-email/column": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@react-email/column/-/column-0.0.11.tgz", - "integrity": "sha512-KvrPuQFn0hlItRRL3vmRuOJgKG+8I0oO9HM5ReLMi5Ns313JSEQogCJaXuOEFkOVeuu5YyY6zy/+5Esccc1AxQ==", + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@react-email/column/-/column-0.0.12.tgz", + "integrity": "sha512-Rsl7iSdDaeHZO938xb+0wR5ud0Z3MVfdtPbNKJNojZi2hApwLAQXmDrnn/AcPDM5Lpl331ZljJS8vHTWxxkvKw==", "requires": {} }, "@react-email/components": { - "version": "0.0.22", - "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.0.22.tgz", - "integrity": "sha512-GO6F+fS3c3aQ6OnqL8esQ/KqtrPGwz80U6uQ8Nd/ETpgFt7y1PXvSGfr8v12wyLffAagdowc/JjoThfIr0L6aA==", + "version": "0.0.23", + "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.0.23.tgz", + "integrity": "sha512-RcBoffx2IZG6quLBXo5sj3fF47rKmmkiMhG1ZBua4nFjHYlmW8j1uUMyO5HNglxIF9E52NYq4sF7XeZRp9jYjg==", "requires": { - "@react-email/body": "0.0.9", - "@react-email/button": "0.0.16", - "@react-email/code-block": "0.0.6", - "@react-email/code-inline": "0.0.3", - "@react-email/column": "0.0.11", - "@react-email/container": "0.0.13", - "@react-email/font": "0.0.7", - "@react-email/head": "0.0.10", - "@react-email/heading": "0.0.13", - "@react-email/hr": "0.0.9", - "@react-email/html": "0.0.9", - "@react-email/img": "0.0.9", - "@react-email/link": "0.0.9", - "@react-email/markdown": "0.0.11", - "@react-email/preview": "0.0.10", - "@react-email/render": "0.0.17", - "@react-email/row": "0.0.9", - "@react-email/section": "0.0.13", - "@react-email/tailwind": "0.0.19", - "@react-email/text": "0.0.9" + "@react-email/body": "0.0.10", + "@react-email/button": "0.0.17", + "@react-email/code-block": "0.0.7", + "@react-email/code-inline": "0.0.4", + "@react-email/column": "0.0.12", + "@react-email/container": "0.0.14", + "@react-email/font": "0.0.8", + "@react-email/head": "0.0.11", + "@react-email/heading": "0.0.14", + "@react-email/hr": "0.0.10", + "@react-email/html": "0.0.10", + "@react-email/img": "0.0.10", + "@react-email/link": "0.0.10", + "@react-email/markdown": "0.0.12", + "@react-email/preview": "0.0.11", + "@react-email/render": "1.0.0", + "@react-email/row": "0.0.10", + "@react-email/section": "0.0.14", + "@react-email/tailwind": "0.1.0", + "@react-email/text": "0.0.10" } }, "@react-email/container": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@react-email/container/-/container-0.0.13.tgz", - "integrity": "sha512-ftke0N1FZl8MX3XXxXiiOaiJOnrQz7ZXUyqNj81K+BK+DePWIVaSmgK6Bu8fFnsgwdKuBdqjZTEtF4sIkU3FuQ==", + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@react-email/container/-/container-0.0.14.tgz", + "integrity": "sha512-NgoaJJd9tTtsrveL86Ocr/AYLkGyN3prdXKd/zm5fQpfDhy/NXezyT3iF6VlwAOEUIu64ErHpAJd+P6ygR+vjg==", "requires": {} }, "@react-email/font": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/@react-email/font/-/font-0.0.7.tgz", - "integrity": "sha512-R0/mfUV/XcUQIALjZUFT9GP+XGmIP1KPz20h9rpS5e4ji6VkQ3ENWlisxrdK5U+KA9iZQrlan+/6tUoTJ9bFsg==", + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@react-email/font/-/font-0.0.8.tgz", + "integrity": "sha512-fSBEqYyVPAyyACBBHcs3wEYzNknpHMuwcSAAKE8fOoDfGqURr/vSxKPdh4tOa9z7G4hlcEfgGrCYEa2iPT22cw==", "requires": {} }, "@react-email/head": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/@react-email/head/-/head-0.0.10.tgz", - "integrity": "sha512-VoH399w0/i3dJFnwH0Ixf9BTuiWhSA/y8PpsCJ7CPw8Mv8WNBqMAAsw0rmrITYI8uPd15LZ2zk2uwRDvqasMRw==", + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@react-email/head/-/head-0.0.11.tgz", + "integrity": "sha512-skw5FUgyamIMK+LN+fZQ5WIKQYf0dPiRAvsUAUR2eYoZp9oRsfkIpFHr0GWPkKAYjFEj+uJjaxQ/0VzQH7svVg==", "requires": {} }, "@react-email/heading": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@react-email/heading/-/heading-0.0.13.tgz", - "integrity": "sha512-MYDzjJwljKHBLueLuyqkaHxu6N4aGOL1ms2NNyJ9WXC9mmBnLs4Y/QEf9SjE4Df3AW4iT9uyfVHuaNUb7uq5QA==", - "requires": { - "@radix-ui/react-slot": "1.1.0" - } + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@react-email/heading/-/heading-0.0.14.tgz", + "integrity": "sha512-jZM7IVuZOXa0G110ES8OkxajPTypIKlzlO1K1RIe1auk76ukQRiCg1IRV4HZlWk1GGUbec5hNxsvZa2kU8cb9w==", + "requires": {} }, "@react-email/hr": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@react-email/hr/-/hr-0.0.9.tgz", - "integrity": "sha512-Rte+EZL3ptH3rkVU3a7fh8/06mZ6Q679tDaWDjsw3878RQC9afWqUPp5lwgA/1pTouLmJlDs2BjRnV6H84O7iw==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@react-email/hr/-/hr-0.0.10.tgz", + "integrity": "sha512-3AA4Yjgl3zEid/KVx6uf6TuLJHVZvUc2cG9Wm9ZpWeAX4ODA+8g9HyuC0tfnjbRsVMhMcCGiECuWWXINi+60vA==", "requires": {} }, "@react-email/html": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@react-email/html/-/html-0.0.9.tgz", - "integrity": "sha512-NB74xwWaOJZxhpiy6pzkhHvugBa2vvmUa0KKnSwOEIX+WEQH8wj5UUhRN4F+Pmkiqz3QBTETUJiSsNWWFtrHgA==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@react-email/html/-/html-0.0.10.tgz", + "integrity": "sha512-06uiuSKJBWQJfhCKv4MPupELei4Lepyz9Sth7Yq7Fq29CAeB1ejLgKkGqn1I+FZ72hQxPLdYF4iq4yloKv3JCg==", "requires": {} }, "@react-email/img": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@react-email/img/-/img-0.0.9.tgz", - "integrity": "sha512-zDlQWmlSANb2dBYhDaKD12Z4xaGD5mEf3peawBYHGxYySzMLwRT2ANGvFqpDNd7iT0C5po+/9EWR8fS1dLy0QQ==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@react-email/img/-/img-0.0.10.tgz", + "integrity": "sha512-pJ8glJjDNaJ53qoM95pvX9SK05yh0bNQY/oyBKmxlBDdUII6ixuMc3SCwYXPMl+tgkQUyDgwEBpSTrLAnjL3hA==", "requires": {} }, "@react-email/link": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@react-email/link/-/link-0.0.9.tgz", - "integrity": "sha512-rRqWGPUTGFwwtMCtsdCHNh0ewOsd4UBG/D12UcwJYFKRb0U6hUG/6VJZE3tB1QYZpLIESdvOLL6ztznh+D749g==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@react-email/link/-/link-0.0.10.tgz", + "integrity": "sha512-tva3wvAWSR10lMJa9fVA09yRn7pbEki0ZZpHE6GD1jKbFhmzt38VgLO9B797/prqoDZdAr4rVK7LJFcdPx3GwA==", "requires": {} }, "@react-email/markdown": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@react-email/markdown/-/markdown-0.0.11.tgz", - "integrity": "sha512-KeDTS0bAvvtgavYAIAmxKpRxWUSr1/jufckDzu9g4QsQtth8wYaSR5wCPXuTPmhFgJMIlNSlOiBnVp+oRbDtKA==", + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@react-email/markdown/-/markdown-0.0.12.tgz", + "integrity": "sha512-wsuvj1XAb6O63aizCLNEeqVgKR3oFjAwt9vjfg2y2oh4G1dZeo8zonZM2x1fmkEkBZhzwSHraNi70jSXhA3A9w==", "requires": { "md-to-react-email": "5.0.2" } }, "@react-email/preview": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/@react-email/preview/-/preview-0.0.10.tgz", - "integrity": "sha512-bRrv8teMMBlF7ttLp1zZUejkPUzrwMQXrigdagtEBOqsB8HxvJU2MR6Yyb3XOqBYldaIDOQJ1z61zyD2wRlKAw==", + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@react-email/preview/-/preview-0.0.11.tgz", + "integrity": "sha512-7O/CT4b16YlSGrj18htTPx3Vbhu2suCGv/cSe5c+fuSrIM/nMiBSZ3Js16Vj0XJbAmmmlVmYFZw9L20wXJ+LjQ==", "requires": {} }, "@react-email/render": { - "version": "0.0.17", - "resolved": "https://registry.npmjs.org/@react-email/render/-/render-0.0.17.tgz", - "integrity": "sha512-xBQ+/73+WsGuXKY7r1U73zMBNV28xdV0cp9cFjhNYipBReDHhV97IpA6v7Hl0dDtDzt+yS/72dY5vYXrF1v8NA==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.0.0.tgz", + "integrity": "sha512-seN2p3JRUSZhwIUiymh9N6ZfhRZ14ywOraQqAokY63DkDeHZW2pA2a6nWpNc/igfOcNyt09Wsoi1Aj0esxhdzw==", "requires": { "html-to-text": "9.0.5", "js-beautify": "^1.14.11", @@ -18156,27 +18020,27 @@ } }, "@react-email/row": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@react-email/row/-/row-0.0.9.tgz", - "integrity": "sha512-ZDASHVvyKrWBS00o5pSH5khfMf46UtZhrHcSAfPSiC4nj7R8A0bf+3Wmbk8YmsaV+qWXUCUSHWwIAAlMRnJoAA==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@react-email/row/-/row-0.0.10.tgz", + "integrity": "sha512-jPyEhG3gsLX+Eb9U+A30fh0gK6hXJwF4ghJ+ZtFQtlKAKqHX+eCpWlqB3Xschd/ARJLod8WAswg0FB+JD9d0/A==", "requires": {} }, "@react-email/section": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@react-email/section/-/section-0.0.13.tgz", - "integrity": "sha512-McsCQ5NQlNWEMEAR3EtCxHgRhxGmLD+jPvj7A3FD7y2X3fXG0hbmUGX12B63rIywSWjJoQi6tojx/8RpzbyeTA==", + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@react-email/section/-/section-0.0.14.tgz", + "integrity": "sha512-+fYWLb4tPU1A/+GE5J1+SEMA7/wR3V30lQ+OR9t2kAJqNrARDbMx0bLnYnR1QL5TiFRz0pCF05SQUobk6gHEDQ==", "requires": {} }, "@react-email/tailwind": { - "version": "0.0.19", - "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-0.0.19.tgz", - "integrity": "sha512-bA0w4D7mSNowxWhcO0jBJauFIPf2Ok7QuKlrHwCcxyX35L2pb5D6ZmXYOrD9C6ADQuVz5oEX+oed3zpSLROgPg==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-0.1.0.tgz", + "integrity": "sha512-qysVUEY+M3SKUvu35XDpzn7yokhqFOT3tPU6Mj/pgc62TL5tQFj6msEbBtwoKs2qO3WZvai0DIHdLhaOxBQSow==", "requires": {} }, "@react-email/text": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@react-email/text/-/text-0.0.9.tgz", - "integrity": "sha512-UNFPGerER3zywpb1ODOS2VgHP7rgOmiTxMHn75pjvQf/gi3/jN9edEQLYvRgPv/mNn4IpJFkOrlP8jcammLeew==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@react-email/text/-/text-0.0.10.tgz", + "integrity": "sha512-wNAnxeEAiFs6N+SxS0y6wTJWfewEzUETuyS2aZmT00xk50VijwyFRuhm4sYSjusMyshevomFwz5jNISCxRsGWw==", "requires": {} }, "@rollup/pluginutils": { @@ -18364,92 +18228,92 @@ "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" }, "@swc/core": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.6.tgz", - "integrity": "sha512-FZxyao9eQks1MRmUshgsZTmlg/HB2oXK5fghkoWJm/1CU2q2kaJlVDll2as5j+rmWiwkp0Gidlq8wlXcEEAO+g==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.14.tgz", + "integrity": "sha512-9aeXeifnyuvc2pcuuhPQgVUwdpGEzZ+9nJu0W8/hNl/aESFsJGR5i9uQJRGu0atoNr01gK092fvmqMmQAPcKow==", "devOptional": true, "requires": { - "@swc/core-darwin-arm64": "1.7.6", - "@swc/core-darwin-x64": "1.7.6", - "@swc/core-linux-arm-gnueabihf": "1.7.6", - "@swc/core-linux-arm64-gnu": "1.7.6", - "@swc/core-linux-arm64-musl": "1.7.6", - "@swc/core-linux-x64-gnu": "1.7.6", - "@swc/core-linux-x64-musl": "1.7.6", - "@swc/core-win32-arm64-msvc": "1.7.6", - "@swc/core-win32-ia32-msvc": "1.7.6", - "@swc/core-win32-x64-msvc": "1.7.6", + "@swc/core-darwin-arm64": "1.7.14", + "@swc/core-darwin-x64": "1.7.14", + "@swc/core-linux-arm-gnueabihf": "1.7.14", + "@swc/core-linux-arm64-gnu": "1.7.14", + "@swc/core-linux-arm64-musl": "1.7.14", + "@swc/core-linux-x64-gnu": "1.7.14", + "@swc/core-linux-x64-musl": "1.7.14", + "@swc/core-win32-arm64-msvc": "1.7.14", + "@swc/core-win32-ia32-msvc": "1.7.14", + "@swc/core-win32-x64-msvc": "1.7.14", "@swc/counter": "^0.1.3", "@swc/types": "^0.1.12" } }, "@swc/core-darwin-arm64": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.6.tgz", - "integrity": "sha512-6lYHey84ZzsdtC7UuPheM4Rm0Inzxm6Sb8U6dmKc4eCx8JL0LfWG4LC5RsdsrTxnjTsbriWlnhZBffh8ijUHIQ==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.14.tgz", + "integrity": "sha512-V0OUXjOH+hdGxDYG8NkQzy25mKOpcNKFpqtZEzLe5V/CpLJPnpg1+pMz70m14s9ZFda9OxsjlvPbg1FLUwhgIQ==", "dev": true, "optional": true }, "@swc/core-darwin-x64": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.6.tgz", - "integrity": "sha512-Fyl+8aH9O5rpx4O7r2KnsPpoi32iWoKOYKiipeTbGjQ/E95tNPxbmsz4yqE8Ovldcga60IPJ5OKQA3HWRiuzdw==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.14.tgz", + "integrity": "sha512-9iFvUnxG6FC3An5ogp5jbBfQuUmTTwy8KMB+ZddUoPB3NR1eV+Y9vOh/tfWcenSJbgOKDLgYC5D/b1mHAprsrQ==", "dev": true, "optional": true }, "@swc/core-linux-arm-gnueabihf": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.6.tgz", - "integrity": "sha512-2WxYTqFaOx48GKC2cbO1/IntA+w+kfCFy436Ij7qRqqtV/WAvTM9TC1OmiFbqq436rSot52qYmX8fkwdB5UcLQ==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.14.tgz", + "integrity": "sha512-zGJsef9qPivKSH8Vv4F/HiBXBTHZ5Hs3ZjVGo/UIdWPJF8fTL9OVADiRrl34Q7zOZEtGXRwEKLUW1SCQcbDvZA==", "dev": true, "optional": true }, "@swc/core-linux-arm64-gnu": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.6.tgz", - "integrity": "sha512-TBEGMSe0LhvPe4S7E68c7VzgT3OMu4VTmBLS7B2aHv4v8uZO92Khpp7L0WqgYU1y5eMjk+XLDLi4kokiNHv/Hg==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.14.tgz", + "integrity": "sha512-AxV3MPsoI7i4B8FXOew3dx3N8y00YoJYvIPfxelw07RegeCEH3aHp2U2DtgbP/NV1ugZMx0TL2Z2DEvocmA51g==", "dev": true, "optional": true }, "@swc/core-linux-arm64-musl": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.6.tgz", - "integrity": "sha512-QI8QGL0HGT42tj7F1A+YAzhGkJjUcvvTfI1e2m704W0Enl2/UIK9v5D1zvQzYwusRyKuaQfbeBRYDh0NcLOGLg==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.14.tgz", + "integrity": "sha512-JDLdNjUj3zPehd4+DrQD8Ltb3B5lD8D05IwePyDWw+uR/YPc7w/TX1FUVci5h3giJnlMCJRvi1IQYV7K1n7KtQ==", "dev": true, "optional": true }, "@swc/core-linux-x64-gnu": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.6.tgz", - "integrity": "sha512-61AYVzhjuNQAVIKKWOJu3H0/pFD28RYJGxnGg3YMhvRLRyuWNyY5Nyyj2WkKcz/ON+g38Arlz00NT1LDIViRLg==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.14.tgz", + "integrity": "sha512-Siy5OvPCLLWmMdx4msnEs8HvEVUEigSn0+3pbLjv78iwzXd0qSBNHUPZyC1xeurVaUbpNDxZTpPRIwpqNE2+Og==", "dev": true, "optional": true }, "@swc/core-linux-x64-musl": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.6.tgz", - "integrity": "sha512-hQFznpfLK8XajfAAN9Cjs0w/aVmO7iu9VZvInyrTCRcPqxV5O+rvrhRxKvC1LRMZXr5M6JRSRtepp5w+TK4kAw==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.14.tgz", + "integrity": "sha512-FtEGm9mwtRYQNK43WMtUIadxHs/ja2rnDurB99os0ZoFTGG2IHuht2zD97W0wB8JbqEabT1XwSG9Y5wmN+ciEQ==", "dev": true, "optional": true }, "@swc/core-win32-arm64-msvc": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.6.tgz", - "integrity": "sha512-Aqsd9afykVMuekzjm4X4TDqwxmG4CrzoOSFe0hZrn9SMio72l5eAPnMtYoe5LsIqtjV8MNprLfXaNbjHjTegmA==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.14.tgz", + "integrity": "sha512-Jp8KDlfq7Ntt2/BXr0y344cYgB1zf0DaLzDZ1ZJR6rYlAzWYSccLYcxHa97VGnsYhhPspMpmCvHid97oe2hl4A==", "dev": true, "optional": true }, "@swc/core-win32-ia32-msvc": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.6.tgz", - "integrity": "sha512-9h0hYnOeRVNeQgHQTvD1Im67faNSSzBZ7Adtxyu9urNLfBTJilMllFd2QuGHlKW5+uaT6ZH7ZWDb+c/enx7Lcg==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.14.tgz", + "integrity": "sha512-I+cFsXF0OU0J9J4zdWiQKKLURO5dvCujH9Jr8N0cErdy54l9d4gfIxdctfTF+7FyXtWKLTCkp+oby9BQhkFGWA==", "dev": true, "optional": true }, "@swc/core-win32-x64-msvc": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.6.tgz", - "integrity": "sha512-izeoB8glCSe6IIDQmrVm6bvR9muk9TeKgmtY7b6l1BwL4BFnTUk4dMmpbntT90bEVQn3JPCaPtUG4HfL8VuyuA==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.14.tgz", + "integrity": "sha512-NNrprQCK6d28mG436jVo2TD+vACHseUECacEBGZ9Ef0qfOIWS1XIt2MisQKG0Oea2VvLFl6tF/V4Lnx/H0Sn3Q==", "dev": true, "optional": true }, @@ -18873,8 +18737,7 @@ "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", - "optional": true, - "peer": true + "dev": true }, "@types/qs": { "version": "6.9.8", @@ -18889,11 +18752,10 @@ "dev": true }, "@types/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz", - "integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==", - "optional": true, - "peer": true, + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.4.tgz", + "integrity": "sha512-J7W30FTdfCxDDjmfRM+/JqLHBIyl7xUIp9kwK637FGmY7+mkSFSe6L4jpZzhj5QMfLssSDP4/i75AKkrdC7/Jw==", + "dev": true, "requires": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -19009,16 +18871,16 @@ "integrity": "sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ==" }, "@typescript-eslint/eslint-plugin": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.1.tgz", - "integrity": "sha512-5g3Y7GDFsJAnY4Yhvk8sZtFfV6YNF2caLzjrRPUBzewjPCaj0yokePB4LJSobyCzGMzjZZYFbwuzbfDHlimXbQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.2.0.tgz", + "integrity": "sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.0.1", - "@typescript-eslint/type-utils": "8.0.1", - "@typescript-eslint/utils": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1", + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/type-utils": "8.2.0", + "@typescript-eslint/utils": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -19026,54 +18888,54 @@ } }, "@typescript-eslint/parser": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.1.tgz", - "integrity": "sha512-5IgYJ9EO/12pOUwiBKFkpU7rS3IU21mtXzB81TNwq2xEybcmAZrE9qwDtsb5uQd9aVO9o0fdabFyAmKveXyujg==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.2.0.tgz", + "integrity": "sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "8.0.1", - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/typescript-estree": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1", + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/typescript-estree": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.1.tgz", - "integrity": "sha512-NpixInP5dm7uukMiRyiHjRKkom5RIFA4dfiHvalanD2cF0CLUuQqxfg8PtEUo9yqJI2bBhF+pcSafqnG3UBnRQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.2.0.tgz", + "integrity": "sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==", "dev": true, "requires": { - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1" + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0" } }, "@typescript-eslint/type-utils": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.1.tgz", - "integrity": "sha512-+/UT25MWvXeDX9YaHv1IS6KI1fiuTto43WprE7pgSMswHbn1Jm9GEM4Txp+X74ifOWV8emu2AWcbLhpJAvD5Ng==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.2.0.tgz", + "integrity": "sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "8.0.1", - "@typescript-eslint/utils": "8.0.1", + "@typescript-eslint/typescript-estree": "8.2.0", + "@typescript-eslint/utils": "8.2.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" } }, "@typescript-eslint/types": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.1.tgz", - "integrity": "sha512-PpqTVT3yCA/bIgJ12czBuE3iBlM3g4inRSC5J0QOdQFAn07TYrYEQBBKgXH1lQpglup+Zy6c1fxuwTk4MTNKIw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.2.0.tgz", + "integrity": "sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.1.tgz", - "integrity": "sha512-8V9hriRvZQXPWU3bbiUV4Epo7EvgM6RTs+sUmxp5G//dBGy402S7Fx0W0QkB2fb4obCF8SInoUzvTYtc3bkb5w==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.2.0.tgz", + "integrity": "sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==", "dev": true, "requires": { - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -19103,24 +18965,24 @@ } }, "@typescript-eslint/utils": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.1.tgz", - "integrity": "sha512-CBFR0G0sCt0+fzfnKaciu9IBsKvEKYwN9UZ+eeogK1fYHg4Qxk1yf/wLQkLXlq8wbU2dFlgAesxt8Gi76E8RTA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.2.0.tgz", + "integrity": "sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.0.1", - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/typescript-estree": "8.0.1" + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/typescript-estree": "8.2.0" } }, "@typescript-eslint/visitor-keys": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.1.tgz", - "integrity": "sha512-W5E+o0UfUcK5EgchLZsyVWqARmsM7v54/qEq6PY3YI5arkgmCzHiuk0zKSJJbm71V0xdRna4BGomkCTXz2/LkQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.2.0.tgz", + "integrity": "sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==", "dev": true, "requires": { - "@typescript-eslint/types": "8.0.1", + "@typescript-eslint/types": "8.2.0", "eslint-visitor-keys": "^3.4.3" } }, @@ -20457,8 +20319,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "optional": true, - "peer": true + "dev": true }, "dayjs": { "version": "1.11.10", @@ -20866,16 +20727,16 @@ "dev": true }, "eslint": { - "version": "9.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.8.0.tgz", - "integrity": "sha512-K8qnZ/QJzT2dLKdZJVX6W4XOwBzutMYmt0lqUS+JdXgd+HTYFlonFgkJ8s44d/zMPPCnOOk0kMWCApCPhiOy9A==", + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.0.tgz", + "integrity": "sha512-JfiKJrbx0506OEerjK2Y1QlldtBxkAlLxT5OEcRF8uaQ86noDe2k31Vw9rnSWv+MXZHj7OOUV/dA0AhdLFcyvA==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", "@eslint/config-array": "^0.17.1", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.8.0", + "@eslint/js": "9.9.0", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", @@ -22405,7 +22266,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "peer": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" } @@ -22799,9 +22659,9 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "nest-commander": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/nest-commander/-/nest-commander-3.14.0.tgz", - "integrity": "sha512-3HEfsEzoKEZ/5/cptkXlL8/31qohPxtMevoFo4j9NMe3q5PgI/0TgTYN/6py9GnFD51jSasEfFGChs1BJ+Enag==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/nest-commander/-/nest-commander-3.15.0.tgz", + "integrity": "sha512-o9VEfFj/w2nm+hQi6fnkxL1GAFZW/KmuGcIE7/B/TX0gwm0QVy8svAF75EQm8wrDjcvWS7Cx/ArnkFn2C+iM2w==", "requires": { "@fig/complete-commander": "^3.0.0", "@golevelup/nestjs-discovery": "4.0.1", @@ -23658,7 +23518,6 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "peer": true, "requires": { "loose-envify": "^1.1.0" } @@ -24498,32 +24357,32 @@ } }, "sharp": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.4.tgz", - "integrity": "sha512-7i/dt5kGl7qR4gwPRD2biwD2/SvBn3O04J77XKFgL2OnZtQw+AG9wnuS/csmu80nPRHLYE9E41fyEiG8nhH6/Q==", + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", "requires": { - "@img/sharp-darwin-arm64": "0.33.4", - "@img/sharp-darwin-x64": "0.33.4", - "@img/sharp-libvips-darwin-arm64": "1.0.2", - "@img/sharp-libvips-darwin-x64": "1.0.2", - "@img/sharp-libvips-linux-arm": "1.0.2", - "@img/sharp-libvips-linux-arm64": "1.0.2", - "@img/sharp-libvips-linux-s390x": "1.0.2", - "@img/sharp-libvips-linux-x64": "1.0.2", - "@img/sharp-libvips-linuxmusl-arm64": "1.0.2", - "@img/sharp-libvips-linuxmusl-x64": "1.0.2", - "@img/sharp-linux-arm": "0.33.4", - "@img/sharp-linux-arm64": "0.33.4", - "@img/sharp-linux-s390x": "0.33.4", - "@img/sharp-linux-x64": "0.33.4", - "@img/sharp-linuxmusl-arm64": "0.33.4", - "@img/sharp-linuxmusl-x64": "0.33.4", - "@img/sharp-wasm32": "0.33.4", - "@img/sharp-win32-ia32": "0.33.4", - "@img/sharp-win32-x64": "0.33.4", + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5", "color": "^4.2.3", "detect-libc": "^2.0.3", - "semver": "^7.6.0" + "semver": "^7.6.3" } }, "shebang-command": { @@ -24714,9 +24573,9 @@ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" }, "sql-formatter": { - "version": "15.3.2", - "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.3.2.tgz", - "integrity": "sha512-pNxSMf5DtwhpZ8gUcOGCGZIWtCcyAUx9oLgAtlO4ag7DvlfnETL0BGqXaISc84pNrXvTWmt8Wal1FWKxdTsL3Q==", + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.4.0.tgz", + "integrity": "sha512-h3uVulRmOfARvDejuSzs9GMbua/UmGCKiP08zyHT1PnG376zk9CHVsDAcKIc9TcIwIrDH3YULWwI4PrXdmLRVw==", "dev": true, "requires": { "argparse": "^2.0.1", @@ -25715,9 +25574,9 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "webpack": { - "version": "5.92.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.92.1.tgz", - "integrity": "sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==", + "version": "5.93.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz", + "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.3", diff --git a/server/package.json b/server/package.json index 9f82378c1ad9d..f58ad98b0868a 100644 --- a/server/package.json +++ b/server/package.json @@ -50,7 +50,7 @@ "@opentelemetry/context-async-hooks": "^1.24.0", "@opentelemetry/exporter-prometheus": "^0.52.0", "@opentelemetry/sdk-node": "^0.52.0", - "@react-email/components": "^0.0.22", + "@react-email/components": "^0.0.23", "@socket.io/redis-adapter": "^8.3.0", "archiver": "^7.0.0", "async-lock": "^1.4.0", @@ -79,6 +79,7 @@ "openid-client": "^5.4.3", "pg": "^8.11.3", "picomatch": "^4.0.0", + "react": "^18.3.1", "react-email": "^3.0.0", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", @@ -112,6 +113,7 @@ "@types/node": "^20.16.1", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^3.0.0", + "@types/react": "^18.3.4", "@types/semver": "^7.5.8", "@types/supertest": "^6.0.0", "@types/ua-parser-js": "^0.7.36", diff --git a/server/src/emails/album-invite.email.tsx b/server/src/emails/album-invite.email.tsx index b804be0898d62..232ef5290d6db 100644 --- a/server/src/emails/album-invite.email.tsx +++ b/server/src/emails/album-invite.email.tsx @@ -1,8 +1,8 @@ import { Img, Link, Section, Text } from '@react-email/components'; import * as React from 'react'; +import { ImmichButton } from 'src/emails/components/button.component'; +import ImmichLayout from 'src/emails/components/immich.layout'; import { AlbumInviteEmailProps } from 'src/interfaces/notification.interface'; -import { ImmichButton } from './components/button.component'; -import ImmichLayout from './components/immich.layout'; export const AlbumInviteEmail = ({ baseUrl, diff --git a/server/src/emails/album-update.email.tsx b/server/src/emails/album-update.email.tsx index d05631a772496..0fb5ad931c9f5 100644 --- a/server/src/emails/album-update.email.tsx +++ b/server/src/emails/album-update.email.tsx @@ -1,8 +1,8 @@ import { Img, Link, Section, Text } from '@react-email/components'; import * as React from 'react'; +import { ImmichButton } from 'src/emails/components/button.component'; +import ImmichLayout from 'src/emails/components/immich.layout'; import { AlbumUpdateEmailProps } from 'src/interfaces/notification.interface'; -import { ImmichButton } from './components/button.component'; -import ImmichLayout from './components/immich.layout'; export const AlbumUpdateEmail = ({ baseUrl, albumName, recipientName, albumId, cid }: AlbumUpdateEmailProps) => ( diff --git a/server/src/emails/components/immich.layout.tsx b/server/src/emails/components/immich.layout.tsx index 8e6de2eebc068..bb7a2aab65a3f 100644 --- a/server/src/emails/components/immich.layout.tsx +++ b/server/src/emails/components/immich.layout.tsx @@ -1,6 +1,6 @@ import { Body, Container, Font, Head, Hr, Html, Img, Preview, Section, Tailwind, Text } from '@react-email/components'; import * as React from 'react'; -import { ImmichFooter } from './footer.template'; +import { ImmichFooter } from 'src/emails/components/footer.template'; interface ImmichLayoutProps { children: React.ReactNode; @@ -11,6 +11,7 @@ export const ImmichLayout = ({ children, preview }: ImmichLayoutProps) => ( ( diff --git a/server/src/emails/welcome.email.tsx b/server/src/emails/welcome.email.tsx index d6b3fc13e7ebd..e031ac6b97137 100644 --- a/server/src/emails/welcome.email.tsx +++ b/server/src/emails/welcome.email.tsx @@ -1,8 +1,8 @@ import { Link, Section, Text } from '@react-email/components'; import * as React from 'react'; +import { ImmichButton } from 'src/emails/components/button.component'; +import ImmichLayout from 'src/emails/components/immich.layout'; import { WelcomeEmailProps } from 'src/interfaces/notification.interface'; -import { ImmichButton } from './components/button.component'; -import ImmichLayout from './components/immich.layout'; export const WelcomeEmail = ({ baseUrl, displayName, username, password }: WelcomeEmailProps) => ( diff --git a/server/src/interfaces/notification.interface.ts b/server/src/interfaces/notification.interface.ts index c0ba4e209d052..ec0ecc534b6b1 100644 --- a/server/src/interfaces/notification.interface.ts +++ b/server/src/interfaces/notification.interface.ts @@ -90,7 +90,7 @@ export type SendEmailResponse = { }; export interface INotificationRepository { - renderEmail(request: EmailRenderRequest): { html: string; text: string }; + renderEmail(request: EmailRenderRequest): Promise<{ html: string; text: string }>; sendEmail(options: SendEmailOptions): Promise; verifySmtp(options: SmtpOptions): Promise; } diff --git a/server/src/repositories/notification.repository.ts b/server/src/repositories/notification.repository.ts index ef6c8c2f39603..9814a7bd5e72f 100644 --- a/server/src/repositories/notification.repository.ts +++ b/server/src/repositories/notification.repository.ts @@ -33,10 +33,10 @@ export class NotificationRepository implements INotificationRepository { } } - renderEmail(request: EmailRenderRequest): { html: string; text: string } { + async renderEmail(request: EmailRenderRequest): Promise<{ html: string; text: string }> { const component = this.render(request); - const html = render(component, { pretty: true }); - const text = render(component, { plainText: true }); + const html = await render(component, { pretty: true }); + const text = await render(component, { plainText: true }); return { html, text }; } diff --git a/server/src/services/notification.service.ts b/server/src/services/notification.service.ts index fa4f79f6d6b63..ace8240b39c57 100644 --- a/server/src/services/notification.service.ts +++ b/server/src/services/notification.service.ts @@ -87,7 +87,7 @@ export class NotificationService { } const { server } = await this.configCore.getConfig({ withCache: false }); - const { html, text } = this.notificationRepository.renderEmail({ + const { html, text } = await this.notificationRepository.renderEmail({ template: EmailTemplate.TEST_EMAIL, data: { baseUrl: server.externalDomain || DEFAULT_EXTERNAL_DOMAIN, @@ -113,7 +113,7 @@ export class NotificationService { } const { server } = await this.configCore.getConfig({ withCache: true }); - const { html, text } = this.notificationRepository.renderEmail({ + const { html, text } = await this.notificationRepository.renderEmail({ template: EmailTemplate.WELCOME, data: { baseUrl: server.externalDomain || DEFAULT_EXTERNAL_DOMAIN, @@ -156,7 +156,7 @@ export class NotificationService { const attachment = await this.getAlbumThumbnailAttachment(album); const { server } = await this.configCore.getConfig({ withCache: false }); - const { html, text } = this.notificationRepository.renderEmail({ + const { html, text } = await this.notificationRepository.renderEmail({ template: EmailTemplate.ALBUM_INVITE, data: { baseUrl: server.externalDomain || DEFAULT_EXTERNAL_DOMAIN, @@ -211,7 +211,7 @@ export class NotificationService { continue; } - const { html, text } = this.notificationRepository.renderEmail({ + const { html, text } = await this.notificationRepository.renderEmail({ template: EmailTemplate.ALBUM_UPDATE, data: { baseUrl: server.externalDomain || DEFAULT_EXTERNAL_DOMAIN, diff --git a/web/package-lock.json b/web/package-lock.json index 73682c06cb670..5670cf2cc99d2 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -79,7 +79,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^20.14.15", + "@types/node": "^20.16.1", "typescript": "^5.3.3" } }, @@ -984,9 +984,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.8.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.8.0.tgz", - "integrity": "sha512-MfluB7EUfxXtv3i/++oh89uzAr4PDI4nn201hsp+qaXqsjAWzinlZEHEfPgAX4doIlKvPG/i0A9dpKxOLII8yA==", + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.0.tgz", + "integrity": "sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug==", "dev": true, "license": "MIT", "engines": { @@ -1882,169 +1882,224 @@ "dev": true }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", - "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.1.tgz", + "integrity": "sha512-2thheikVEuU7ZxFXubPDOtspKn1x0yqaYQwvALVtEcvFhMifPADBrgRPyHV0TF3b+9BgvgjgagVyvA/UqPZHmg==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", - "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.1.tgz", + "integrity": "sha512-t1lLYn4V9WgnIFHXy1d2Di/7gyzBWS8G5pQSXdZqfrdCGTwi1VasRMSS81DTYb+avDs/Zz4A6dzERki5oRYz1g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", - "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.1.tgz", + "integrity": "sha512-AH/wNWSEEHvs6t4iJ3RANxW5ZCK3fUnmf0gyMxWCesY1AlUj8jY7GC+rQE4wd3gwmZ9XDOpL0kcFnCjtN7FXlA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", - "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.1.tgz", + "integrity": "sha512-dO0BIz/+5ZdkLZrVgQrDdW7m2RkrLwYTh2YMFG9IpBtlC1x1NPNSXkfczhZieOlOLEqgXOFH3wYHB7PmBtf+Bg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", - "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.1.tgz", + "integrity": "sha512-sWWgdQ1fq+XKrlda8PsMCfut8caFwZBmhYeoehJ05FdI0YZXk6ZyUjWLrIgbR/VgiGycrFKMMgp7eJ69HOF2pQ==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.1.tgz", + "integrity": "sha512-9OIiSuj5EsYQlmwhmFRA0LRO0dRRjdCVZA3hnmZe1rEwRk11Jy3ECGGq3a7RrVEZ0/pCsYWx8jG3IvcrJ6RCew==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", - "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.1.tgz", + "integrity": "sha512-0kuAkRK4MeIUbzQYu63NrJmfoUVicajoRAL1bpwdYIYRcs57iyIV9NLcuyDyDXE2GiZCL4uhKSYAnyWpjZkWow==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", - "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.1.tgz", + "integrity": "sha512-/6dYC9fZtfEY0vozpc5bx1RP4VrtEOhNQGb0HwvYNwXD1BBbwQ5cKIbUVVU7G2d5WRE90NfB922elN8ASXAJEA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.1.tgz", + "integrity": "sha512-ltUWy+sHeAh3YZ91NUsV4Xg3uBXAlscQe8ZOXRCVAKLsivGuJsrkawYPUEyCV3DYa9urgJugMLn8Z3Z/6CeyRQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", - "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.1.tgz", + "integrity": "sha512-BggMndzI7Tlv4/abrgLwa/dxNEMn2gC61DCLrTzw8LkpSKel4o+O+gtjbnkevZ18SKkeN3ihRGPuBxjaetWzWg==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.1.tgz", + "integrity": "sha512-z/9rtlGd/OMv+gb1mNSjElasMf9yXusAxnRDrBaYB+eS1shFm6/4/xDH1SAISO5729fFKUkJ88TkGPRUh8WSAA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", - "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.1.tgz", + "integrity": "sha512-kXQVcWqDcDKw0S2E0TmhlTLlUgAmMVqPrJZR+KpH/1ZaZhLSl23GZpQVmawBQGVhyP5WXIsIQ/zqbDBBYmxm5w==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", - "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.1.tgz", + "integrity": "sha512-CbFv/WMQsSdl+bpX6rVbzR4kAjSSBuDgCqb1l4J68UYsQNalz5wOqLGYj4ZI0thGpyX5kc+LLZ9CL+kpqDovZA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", - "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.1.tgz", + "integrity": "sha512-3Q3brDgA86gHXWHklrwdREKIrIbxC0ZgU8lwpj0eEKGBQH+31uPqr0P2v11pn0tSIxHvcdOWxa4j+YvLNx1i6g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", - "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.1.tgz", + "integrity": "sha512-tNg+jJcKR3Uwe4L0/wY3Ro0H+u3nrb04+tcq1GSYzBEmKLeOQF2emk1whxlzNqb6MMrQ2JOcQEpuuiPLyRcSIw==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", - "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.1.tgz", + "integrity": "sha512-xGiIH95H1zU7naUyTKEyOA/I0aexNMUdO9qRv0bLKN3qu25bBdrxZHqA3PTJ24YNN/GdMzG4xkDcd/GvjuhfLg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -2056,9 +2111,9 @@ "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" }, "node_modules/@sveltejs/adapter-static": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.2.tgz", - "integrity": "sha512-/EBFydZDwfwFfFEuF1vzUseBoRziwKP7AoHAwv+Ot3M084sE/HTVBHf9mCmXfdM9ijprY5YEugZjleflncX5fQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.4.tgz", + "integrity": "sha512-Qm4GAHCnRXwfWG9/AtnQ7mqjyjTs7i0Opyb8H2KH9rMR7fLxqiPx/tXeoE6HHo66+72CjyOb4nFH3lrejY4vzA==", "dev": true, "license": "MIT", "peerDependencies": { @@ -2066,9 +2121,9 @@ } }, "node_modules/@sveltejs/enhanced-img": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.3.1.tgz", - "integrity": "sha512-75A4YiXQp+GRc54EyiNOlhHnHt9O8e0CdCHLm3RWESLRaazd5OIciSa4SbKIo9DM84yGwSVShU0buyUmNJvgWg==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.3.3.tgz", + "integrity": "sha512-nsqJkVuYLUXARDLjMoGKAt4oLzwtY8X2E8rIl/TJl7ueLjpTISxrAhVRN3r8yMO+R+so4G6Taiix2mpiPpqZeg==", "dev": true, "license": "MIT", "dependencies": { @@ -2082,9 +2137,9 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.5.20", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.20.tgz", - "integrity": "sha512-47rJ5BoYwURE/Rp7FNMLp3NzdbWC9DQ/PmKd0mebxT2D/PrPxZxcLImcD3zsWdX2iS6oJk8ITJbO/N2lWnnUqA==", + "version": "2.5.24", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.24.tgz", + "integrity": "sha512-Nr2oxsCsDfEkdS/zzQQQbsPYTbu692Qs3/iE3L7VHzCVjG2+WujF9oMUozWI7GuX98KxYSoPMlAsfmDLSg44hQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2109,15 +2164,15 @@ "node": ">=18.13" }, "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3" } }, "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.1.tgz", - "integrity": "sha512-rimpFEAboBBHIlzISibg94iP09k/KYdHgVhJlcsTfn7KMBhc70jFX/GRWkRdFCc2fdnk+4+Bdfej23cMDnJS6A==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.2.tgz", + "integrity": "sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA==", "dev": true, "license": "MIT", "dependencies": { @@ -2506,17 +2561,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.1.tgz", - "integrity": "sha512-5g3Y7GDFsJAnY4Yhvk8sZtFfV6YNF2caLzjrRPUBzewjPCaj0yokePB4LJSobyCzGMzjZZYFbwuzbfDHlimXbQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.2.0.tgz", + "integrity": "sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.0.1", - "@typescript-eslint/type-utils": "8.0.1", - "@typescript-eslint/utils": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1", + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/type-utils": "8.2.0", + "@typescript-eslint/utils": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -2540,16 +2595,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.1.tgz", - "integrity": "sha512-5IgYJ9EO/12pOUwiBKFkpU7rS3IU21mtXzB81TNwq2xEybcmAZrE9qwDtsb5uQd9aVO9o0fdabFyAmKveXyujg==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.2.0.tgz", + "integrity": "sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.0.1", - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/typescript-estree": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1", + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/typescript-estree": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", "debug": "^4.3.4" }, "engines": { @@ -2569,14 +2624,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.1.tgz", - "integrity": "sha512-NpixInP5dm7uukMiRyiHjRKkom5RIFA4dfiHvalanD2cF0CLUuQqxfg8PtEUo9yqJI2bBhF+pcSafqnG3UBnRQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.2.0.tgz", + "integrity": "sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1" + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2587,14 +2642,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.1.tgz", - "integrity": "sha512-+/UT25MWvXeDX9YaHv1IS6KI1fiuTto43WprE7pgSMswHbn1Jm9GEM4Txp+X74ifOWV8emu2AWcbLhpJAvD5Ng==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.2.0.tgz", + "integrity": "sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.0.1", - "@typescript-eslint/utils": "8.0.1", + "@typescript-eslint/typescript-estree": "8.2.0", + "@typescript-eslint/utils": "8.2.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -2612,9 +2667,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.1.tgz", - "integrity": "sha512-PpqTVT3yCA/bIgJ12czBuE3iBlM3g4inRSC5J0QOdQFAn07TYrYEQBBKgXH1lQpglup+Zy6c1fxuwTk4MTNKIw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.2.0.tgz", + "integrity": "sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==", "dev": true, "license": "MIT", "engines": { @@ -2626,14 +2681,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.1.tgz", - "integrity": "sha512-8V9hriRvZQXPWU3bbiUV4Epo7EvgM6RTs+sUmxp5G//dBGy402S7Fx0W0QkB2fb4obCF8SInoUzvTYtc3bkb5w==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.2.0.tgz", + "integrity": "sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/visitor-keys": "8.0.1", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2694,16 +2749,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.1.tgz", - "integrity": "sha512-CBFR0G0sCt0+fzfnKaciu9IBsKvEKYwN9UZ+eeogK1fYHg4Qxk1yf/wLQkLXlq8wbU2dFlgAesxt8Gi76E8RTA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.2.0.tgz", + "integrity": "sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.0.1", - "@typescript-eslint/types": "8.0.1", - "@typescript-eslint/typescript-estree": "8.0.1" + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/typescript-estree": "8.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2717,13 +2772,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.1.tgz", - "integrity": "sha512-W5E+o0UfUcK5EgchLZsyVWqARmsM7v54/qEq6PY3YI5arkgmCzHiuk0zKSJJbm71V0xdRna4BGomkCTXz2/LkQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.2.0.tgz", + "integrity": "sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.0.1", + "@typescript-eslint/types": "8.2.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -3966,9 +4021,9 @@ } }, "node_modules/eslint": { - "version": "9.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.8.0.tgz", - "integrity": "sha512-K8qnZ/QJzT2dLKdZJVX6W4XOwBzutMYmt0lqUS+JdXgd+HTYFlonFgkJ8s44d/zMPPCnOOk0kMWCApCPhiOy9A==", + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.0.tgz", + "integrity": "sha512-JfiKJrbx0506OEerjK2Y1QlldtBxkAlLxT5OEcRF8uaQ86noDe2k31Vw9rnSWv+MXZHj7OOUV/dA0AhdLFcyvA==", "dev": true, "license": "MIT", "dependencies": { @@ -3976,7 +4031,7 @@ "@eslint-community/regexpp": "^4.11.0", "@eslint/config-array": "^0.17.1", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.8.0", + "@eslint/js": "9.9.0", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", @@ -4015,6 +4070,14 @@ }, "funding": { "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-compat-utils": { @@ -6956,10 +7019,11 @@ } }, "node_modules/rollup": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", - "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.1.tgz", + "integrity": "sha512-ZnYyKvscThhgd3M5+Qt3pmhO4jIRR5RGzaSovB6Q7rGNrK5cUncrtLmcTTJVSdcKXyZjW8X8MB0JMSuH9bcAJg==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "1.0.5" }, @@ -6971,19 +7035,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.13.0", - "@rollup/rollup-android-arm64": "4.13.0", - "@rollup/rollup-darwin-arm64": "4.13.0", - "@rollup/rollup-darwin-x64": "4.13.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", - "@rollup/rollup-linux-arm64-gnu": "4.13.0", - "@rollup/rollup-linux-arm64-musl": "4.13.0", - "@rollup/rollup-linux-riscv64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-musl": "4.13.0", - "@rollup/rollup-win32-arm64-msvc": "4.13.0", - "@rollup/rollup-win32-ia32-msvc": "4.13.0", - "@rollup/rollup-win32-x64-msvc": "4.13.0", + "@rollup/rollup-android-arm-eabi": "4.21.1", + "@rollup/rollup-android-arm64": "4.21.1", + "@rollup/rollup-darwin-arm64": "4.21.1", + "@rollup/rollup-darwin-x64": "4.21.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.21.1", + "@rollup/rollup-linux-arm-musleabihf": "4.21.1", + "@rollup/rollup-linux-arm64-gnu": "4.21.1", + "@rollup/rollup-linux-arm64-musl": "4.21.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.21.1", + "@rollup/rollup-linux-riscv64-gnu": "4.21.1", + "@rollup/rollup-linux-s390x-gnu": "4.21.1", + "@rollup/rollup-linux-x64-gnu": "4.21.1", + "@rollup/rollup-linux-x64-musl": "4.21.1", + "@rollup/rollup-win32-arm64-msvc": "4.21.1", + "@rollup/rollup-win32-ia32-msvc": "4.21.1", + "@rollup/rollup-win32-x64-msvc": "4.21.1", "fsevents": "~2.3.2" } }, @@ -7676,9 +7743,9 @@ } }, "node_modules/svelte-check": { - "version": "3.8.5", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.8.5.tgz", - "integrity": "sha512-3OGGgr9+bJ/+1nbPgsvulkLC48xBsqsgtc8Wam281H4G9F5v3mYGa2bHRsPuwHC5brKl4AxJH95QF73kmfihGQ==", + "version": "3.8.6", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.8.6.tgz", + "integrity": "sha512-ij0u4Lw/sOTREP13BdWZjiXD/BlHE6/e2e34XzmVmsp5IN4kVa3PWP65NM32JAgwjZlwBg/+JtiNV1MM8khu0Q==", "dev": true, "license": "MIT", "dependencies": { @@ -8158,9 +8225,9 @@ } }, "node_modules/svelte-maplibre": { - "version": "0.9.10", - "resolved": "https://registry.npmjs.org/svelte-maplibre/-/svelte-maplibre-0.9.10.tgz", - "integrity": "sha512-MYTMogRPzzgXDZGub4ivfdY1/P0uPxZfo/REQhne0zdBLc6cd4n1U4SqY9SoEGNN0CGW1KvSLfc7acx0kxzXlw==", + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/svelte-maplibre/-/svelte-maplibre-0.9.12.tgz", + "integrity": "sha512-92kYWgR+/qkO3lrsPoNFPpgULhcpKeOQ+IqqVsduDY7lOkhWKgCmx4r8i8UTfFZ6KGezSN0y7pweHEhBdhV3Xw==", "license": "MIT", "dependencies": { "d3-geo": "^3.1.0", @@ -8272,9 +8339,9 @@ "peer": true }, "node_modules/tailwindcss": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.9.tgz", - "integrity": "sha512-1SEOvRr6sSdV5IDf9iC+NU4dhwdqzF4zKKq3sAbasUWHEM6lsMhX+eNN5gkPx1BvLFEnZQEUFbXnGj8Qlp83Pg==", + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz", + "integrity": "sha512-KWZkVPm7yJRhdu4SRSl9d4AK2wM3a50UsvgHZO7xY77NQr2V+fIrEuoDGQcbvswWvFGbS2f6e+jC/6WJm1Dl0w==", "dev": true, "license": "MIT", "dependencies": { @@ -8758,15 +8825,15 @@ } }, "node_modules/vite": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.0.tgz", - "integrity": "sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz", + "integrity": "sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.40", - "rollup": "^4.13.0" + "postcss": "^8.4.41", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" From 2297d86569fdd0c0a806ef1b0211f4e831bc23c0 Mon Sep 17 00:00:00 2001 From: Kenneth Bingham Date: Wed, 28 Aug 2024 12:30:06 -0400 Subject: [PATCH 044/160] fix(mobile): use a valid OAuth callback URL (#10832) * add root resource path '/' to mobile oauth scheme * chore: add oauth-callback path * add root resource path '/' to mobile oauth scheme * chore: add oauth-callback path * fix: make sure there are three forward slash in callback URL --------- Co-authored-by: Jason Rasmussen Co-authored-by: Alex --- docs/docs/administration/oauth.md | 12 ++--- .../android/app/src/main/AndroidManifest.xml | 4 +- mobile/lib/pages/login/login.page.dart | 2 +- mobile/lib/services/oauth.service.dart | 42 +++++++++++------- .../lib/widgets/forms/login/login_form.dart | 44 +++++++++++++------ server/src/constants.ts | 2 +- server/src/services/auth.service.spec.ts | 40 ++++++++--------- server/src/services/auth.service.ts | 2 +- .../settings/auth/auth-settings.svelte | 4 +- web/src/lib/i18n/en.json | 2 +- 10 files changed, 92 insertions(+), 62 deletions(-) diff --git a/docs/docs/administration/oauth.md b/docs/docs/administration/oauth.md index ab317787bc09c..96dca68e4fa9d 100644 --- a/docs/docs/administration/oauth.md +++ b/docs/docs/administration/oauth.md @@ -3,7 +3,7 @@ This page contains details about using OAuth in Immich. :::tip -Unable to set `app.immich:/` as a valid redirect URI? See [Mobile Redirect URI](#mobile-redirect-uri) for an alternative solution. +Unable to set `app.immich:///oauth-callback` as a valid redirect URI? See [Mobile Redirect URI](#mobile-redirect-uri) for an alternative solution. ::: ## Overview @@ -30,7 +30,7 @@ Before enabling OAuth in Immich, a new client application needs to be configured The **Sign-in redirect URIs** should include: - - `app.immich:/` - for logging in with OAuth from the [Mobile App](/docs/features/mobile-app.mdx) + - `app.immich:///oauth-callback` - for logging in with OAuth from the [Mobile App](/docs/features/mobile-app.mdx) - `http://DOMAIN:PORT/auth/login` - for logging in with OAuth from the Web Client - `http://DOMAIN:PORT/user-settings` - for manually linking OAuth in the Web Client @@ -38,7 +38,7 @@ Before enabling OAuth in Immich, a new client application needs to be configured Mobile - - `app.immich:/` (You **MUST** include this for iOS and Android mobile apps to work properly) + - `app.immich:///oauth-callback` (You **MUST** include this for iOS and Android mobile apps to work properly) Localhost @@ -96,16 +96,16 @@ When Auto Launch is enabled, the login page will automatically redirect the user ## Mobile Redirect URI -The redirect URI for the mobile app is `app.immich:/`, which is a [Custom Scheme](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app). If this custom scheme is an invalid redirect URI for your OAuth Provider, you can work around this by doing the following: +The redirect URI for the mobile app is `app.immich:///oauth-callback`, which is a [Custom Scheme](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app). If this custom scheme is an invalid redirect URI for your OAuth Provider, you can work around this by doing the following: -1. Configure an http(s) endpoint to forwards requests to `app.immich:/` +1. Configure an http(s) endpoint to forwards requests to `app.immich:///oauth-callback` 2. Whitelist the new endpoint as a valid redirect URI with your provider. 3. Specify the new endpoint as the `Mobile Redirect URI Override`, in the OAuth settings. With these steps in place, you should be able to use OAuth from the [Mobile App](/docs/features/mobile-app.mdx) without a custom scheme redirect URI. :::info -Immich has a route (`/api/oauth/mobile-redirect`) that is already configured to forward requests to `app.immich:/`, and can be used for step 1. +Immich has a route (`/api/oauth/mobile-redirect`) that is already configured to forward requests to `app.immich:///oauth-callback`, and can be used for step 1. ::: ## Example Configuration diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml index edb41510f0156..e5e3e2a396b5e 100644 --- a/mobile/android/app/src/main/AndroidManifest.xml +++ b/mobile/android/app/src/main/AndroidManifest.xml @@ -69,7 +69,7 @@ - + HEVC) - (31 --> VP9) - (35 --> AV1).", + "transcoding_disabled_description": "هیچ ویدیویی را تبدیل فرمت نکنید، زیرا ممکن است پخش در برخی از کلاینت‌ها را مختل کند", + "transcoding_hardware_acceleration": "شتاب دهنده سخت افزاری", + "transcoding_hardware_acceleration_description": "آزمایشی؛ بسیار سریع‌تر است، اما در همان بیت‌ریت کیفیت کمتری خواهد داشت", + "transcoding_hardware_decoding": "رمزگشایی سخت افزاری", "transcoding_hardware_decoding_setting_description": "", - "transcoding_hevc_codec": "", - "transcoding_max_b_frames": "", - "transcoding_max_b_frames_description": "", - "transcoding_max_bitrate": "", - "transcoding_max_bitrate_description": "", - "transcoding_max_keyframe_interval": "", - "transcoding_max_keyframe_interval_description": "", - "transcoding_optimal_description": "", - "transcoding_preferred_hardware_device": "", - "transcoding_preferred_hardware_device_description": "", - "transcoding_preset_preset": "", - "transcoding_preset_preset_description": "", - "transcoding_reference_frames": "", - "transcoding_reference_frames_description": "", - "transcoding_required_description": "", - "transcoding_settings": "", - "transcoding_settings_description": "", - "transcoding_target_resolution": "", - "transcoding_target_resolution_description": "", - "transcoding_temporal_aq": "", - "transcoding_temporal_aq_description": "", - "transcoding_threads": "", - "transcoding_threads_description": "", + "transcoding_hevc_codec": "کدک HEVC", + "transcoding_max_b_frames": "بیشترین B-frames", + "transcoding_max_b_frames_description": "مقادیر بالاتر کارایی فشرده سازی را بهبود می‌بخشند، اما کدگذاری را کند می‌کنند. ممکن است با شتاب دهی سخت‌افزاری در دستگاه‌های قدیمی سازگار نباشد. مقدار( 0 ) B-frames را غیرفعال می‌کند، در حالی که مقدار ( 1 ) این مقدار را به صورت خودکار تنظیم می‌کند.", + "transcoding_max_bitrate": "بیشترین بیت ریت", + "transcoding_max_bitrate_description": "تنظیم حداکثر بیت‌ریت می‌تواند اندازه فایل‌ها را در حدی قابل پیش‌بینی‌تر کند، هرچند که هزینه کمی برای کیفیت دارد. در وضوح 720p، مقادیر معمول 2600k برای VP9 یا HEVC و 4500k برای H.264 است. اگر به 0 تنظیم شود، غیرفعال می‌شود.", + "transcoding_max_keyframe_interval": "حداکثر فاصله کلید فریم", + "transcoding_max_keyframe_interval_description": "حداکثر فاصله فریم بین کلیدفریم‌ها را تنظیم می‌کند. مقادیر پایین‌تر کارایی فشرده‌سازی را کاهش می‌دهند، اما زمان جستجو را بهبود می‌بخشند و ممکن است کیفیت را در صحنه‌های با حرکت سریع بهبود دهند. مقدار 0 این مقدار را به‌طور خودکار تنظیم می‌کند.", + "transcoding_optimal_description": "ویدیوهایی که از رزولوشن هدف بالاتر هستند یا در قالب پذیرفته شده نیستند", + "transcoding_preferred_hardware_device": "دستگاه سخت‌افزاری ترجیحی", + "transcoding_preferred_hardware_device_description": "این گزینه فقط به VAAPI و QSV اعمال می‌شود. DRI node مورد استفاده برای تبدیل فرمت سخت‌افزاری را تنظیم می‌کند.", + "transcoding_preset_preset": "پیش‌تنظیم (preset-)", + "transcoding_preset_preset_description": "سرعت فشرده‌سازی. پیش‌تنظیم‌های کندتر فایل‌های کوچک‌تری تولید می‌کنند و کیفیت را هنگام هدف‌گذاری بر روی یک بیت‌ریت خاص افزایش می‌دهند. VP9 سرعت‌های بالاتر از 'faster' را نادیده می‌گیرد.", + "transcoding_reference_frames": "فریم‌های مرجع", + "transcoding_reference_frames_description": "تعداد فریم‌هایی که هنگام فشرده‌سازی یک فریم مشخص به آن‌ها ارجاع داده می‌شود. مقادیر بالاتر کارایی فشرده‌سازی را بهبود می‌بخشند، اما کدگذاری را کندتر می‌کنند. مقدار 0 این مقدار را به‌طور خودکار تنظیم می‌کند.", + "transcoding_required_description": "فقط ویدیوهایی که در فرمت پذیرفته‌شده نیستند", + "transcoding_settings": "تنظیمات تبدیل ویدیو", + "transcoding_settings_description": "مدیریت وضوح و اطلاعات کدگذاری فایل‌های ویدئویی", + "transcoding_target_resolution": "وضوح هدف", + "transcoding_target_resolution_description": "وضوح‌های بالاتر می‌توانند جزئیات بیشتری را حفظ کنند، اما زمان بیشتری برای کدگذاری نیاز دارند، اندازه فایل‌های بزرگ‌تری دارند و ممکن است باعث کاهش پاسخگویی برنامه شوند.", + "transcoding_temporal_aq": "AQ موقتی", + "transcoding_temporal_aq_description": "این مورد فقط برای NVENC اعمال می شود. افزایش کیفیت در صحنه های با جزئیات بالا و حرکت کم. ممکن است با دستگاه های قدیمی تر سازگار نباشد.", + "transcoding_threads": "رشته ها ( موضوعات )", + "transcoding_threads_description": "مقادیر بالاتر منجر به رمزگذاری سریع تر می شود، اما فضای کمتری برای پردازش سایر وظایف سرور در حین فعالیت باقی می گذارد. این مقدار نباید بیشتر از تعداد هسته های CPU باشد. اگر روی 0 تنظیم شود، بیشترین استفاده را خواهد داشت.", "transcoding_tone_mapping": "", - "transcoding_tone_mapping_description": "", + "transcoding_tone_mapping_description": "تلاش برای حفظ ظاهر ویدیوهای HDR هنگام تبدیل به SDR. هر الگوریتم تعادل های متفاوتی را برای رنگ، جزئیات و روشنایی ایجاد می کند. Hable جزئیات را حفظ می کند، Mobius رنگ را حفظ می کند و Reinhard روشنایی را حفظ می کند.", "transcoding_tone_mapping_npl": "", - "transcoding_tone_mapping_npl_description": "", - "transcoding_transcode_policy": "", - "transcoding_transcode_policy_description": "", - "transcoding_two_pass_encoding": "", - "transcoding_two_pass_encoding_setting_description": "", - "transcoding_video_codec": "", - "transcoding_video_codec_description": "", - "trash_enabled_description": "", - "trash_number_of_days": "", - "trash_number_of_days_description": "", - "trash_settings": "", - "trash_settings_description": "", - "untracked_files": "", - "untracked_files_description": "", - "user_delete_delay": "", - "user_delete_delay_settings": "", - "user_delete_delay_settings_description": "", - "user_delete_immediately": "", - "user_management": "", - "user_password_has_been_reset": "", - "user_password_reset_description": "", - "user_restore_description": "", - "user_settings": "", - "user_settings_description": "", - "user_successfully_removed": "", - "version_check_enabled_description": "", - "version_check_settings": "", - "version_check_settings_description": "", - "video_conversion_job": "", - "video_conversion_job_description": "" + "transcoding_tone_mapping_npl_description": "رنگ ها برای ظاهر طبیعی در یک نمایشگر با این روشنایی تنظیم خواهند شد. برخلاف انتظار، مقادیر پایین تر باعث افزایش روشنایی ویدیو و برعکس می شوند، زیرا آن را برای روشنایی نمایشگر جبران می کند. مقدار 0 این مقدار را به طور خودکار تنظیم می کند.", + "transcoding_transcode_policy": "سیاست رمزگذاری", + "transcoding_transcode_policy_description": "سیاست برای زمانی که ویدیویی باید مجددا تبدیل (رمزگذاری) شود. ویدیوهای HDR همیشه تبدیل (رمزگذاری) مجدد خواهند شد (مگر رمزگذاری مجدد غیرفعال باشد).", + "transcoding_two_pass_encoding": "تبدیل (رمزگذاری) دو مرحله ای", + "transcoding_two_pass_encoding_setting_description": "تبدیل (رمزگذاری) ویدیو در دو مرحله برای تولید ویدیوهای رمزگذاری شده بهتر. وقتی حداکثر نرخ بیت فعال باشد (برای کار با H.264 و HEVC لازم است)، این حالت از یک محدوده نرخ بیت بر اساس حداکثر نرخ بیت استفاده می کند و CRF را نادیده می گیرد. برای VP9، اگر حداکثر نرخ بیت غیرفعال باشد، می توان از CRF استفاده کرد.", + "transcoding_video_codec": "کدک ویدیویی", + "transcoding_video_codec_description": "VP9 کارایی بالا و سازگاری وب را دارد، اما تبدیل (رمزگذاری) مجدد آن زمان بیشتری می گیرد. HEVC عملکرد مشابهی دارد، اما سازگاری وب کمتری دارد. H.264 سازگاری گسترده و رمزگذاری سریع دارد، اما فایل های بزرگتری تولید می کند. AV1 کدک کارآمدترین است، اما از پشتیبانی در دستگاه های قدیمی تر برخوردار نیست.", + "trash_enabled_description": "فعال سازی ویژگی های سطل بازیافت (سطل زباله)", + "trash_number_of_days": "تعداد روزها", + "trash_number_of_days_description": "تعداد روزهایی که دارایی ها(عکسها و فیملها) در زباله دان(سطل بازیافت) قبل از حذف دائمی نگهداری میشوند", + "trash_settings": "تنظیمات سطل بازیافت (سطل زباله)", + "trash_settings_description": "مدیریت تنظیمات سطل بازیافت (سطل زباله)", + "untracked_files": "فایل های ردیابی نشده", + "untracked_files_description": "این فایل ها توسط برنامه ردیابی نمی شوند. می توانند نتیجه انتقال ناموفق، بارگذاری متوقف شده یا به دلیل یک باگ باقی مانده باشند", + "user_delete_delay": "{user}'s حساب کاربری و دارایی ها(عکس و فیلم) برای حذف دائمی در {delay, plural, one {# روز} other {# روز}} برنامه ریزی خواهند شد.", + "user_delete_delay_settings": "تأخیر در حذف", + "user_delete_delay_settings_description": "تعداد روزهایی که پس از حذف، حساب کاربری و دارایی های(عکس و فیلم) کاربر به طور دائمی حذف می شوند. کار حذف کاربر در نیمه شب اجرا می شود تا کاربرانی که آماده حذف هستند را بررسی کند. تغییرات در این تنظیم در اجرای بعدی ارزیابی خواهند شد.", + "user_delete_immediately": "{user}'s حساب کاربری و دارایی ها (عکس و فیلم) فوراً برای حذف دائمی در صف قرار خواهند گرفت.", + "user_delete_immediately_checkbox": "کاربر و دارایی ها (عکس و فیلم) را برای حذف فوری در صف قرار بده", + "user_management": "مدیریت کاربر", + "user_password_has_been_reset": "رمز عبور کاربر بازنشانی شد:", + "user_password_reset_description": "لطفاً رمز عبور موقت را به کاربر ارائه دهید و به او اطلاع دهید که باید در ورود بعدی رمز عبور خود را تغییر دهد.", + "user_restore_description": "{user}'s حساب کاربری بازیابی خواهد شد.", + "user_restore_scheduled_removal": "بازیابی کاربر - حذف برنامه ریزی شده در {date, date, long}", + "user_settings": "تنظیمات کاربر", + "user_settings_description": "مدیریت تنظیمات کاربر", + "user_successfully_removed": "کاربر {email} با موفقیت حذف شد.", + "version_check_enabled_description": "فعال‌سازی بررسی نسخه", + "version_check_implications": "ویژگی بررسی نسخه به ارتباط دوره ای با github.com متکی است", + "version_check_settings": "بررسی نسخه", + "version_check_settings_description": "فعال یا غیرفعال کردن اعلان نسخه جدید", + "video_conversion_job": "تبدیل (رمزگذاری) ویدیوها", + "video_conversion_job_description": "تبدیل (رمزگذاری)ویدیوها برای سازگاری بیشتر با مرورگرها و دستگاه‌ها" }, - "admin_email": "", - "admin_password": "", - "administration": "", - "advanced": "", + "admin_email": "ایمیل مدیر", + "admin_password": "رمز عبور مدیر", + "administration": "مدیریت", + "advanced": "پیشرفته", "album_added": "", "album_added_notification_setting_description": "", "album_cover_updated": "", diff --git a/web/src/lib/i18n/fi.json b/web/src/lib/i18n/fi.json index f87e2eed4ee65..da9a71379cfe5 100644 --- a/web/src/lib/i18n/fi.json +++ b/web/src/lib/i18n/fi.json @@ -129,6 +129,7 @@ "map_enable_description": "Ota käyttöön karttatoiminnot", "map_gps_settings": "Kartta & GPS- asetukset", "map_gps_settings_description": "Hallitse Kartan & GPS (Käänteinen Geokoodaus) Asetuksia", + "map_implications": "Kartta -ominaisuus käyttää ulkoista karttapalvelua", "map_light_style": "Vaalea teema", "map_manage_reverse_geocoding_settings": "Hallitse käänteisen geokoodauksen asetuksia", "map_reverse_geocoding": "Käänteinen Geokoodaus", @@ -333,8 +334,11 @@ "album_cover_updated": "Albumin kansikuva päivitetty", "album_delete_confirmation": "Haluatko varmasti poistaa albumin {album}?\nJos albumi on jaettu, muut eivät pääse siihen enää.", "album_info_updated": "Albumin tiedot päivitetty", + "album_leave": "Poistu albumista?", "album_name": "Albumin nimi", "album_options": "Albumin asetukset", + "album_remove_user": "Poista käyttäjä?", + "album_remove_user_confirmation": "Oletko varma että haluat poistaa {user}?", "album_share_no_users": "Näyttää että olet jakanut tämän albumin kaikkien kanssa, tai sinulla ei ole käyttäjiä joille jakaa.", "album_updated": "Albumi päivitetty", "album_updated_setting_description": "Saa sähköpostia kun jaetussa albumissa on uutta sisältöä", diff --git a/web/src/lib/i18n/fr.json b/web/src/lib/i18n/fr.json index 0aaa160729184..9963105cd88e8 100644 --- a/web/src/lib/i18n/fr.json +++ b/web/src/lib/i18n/fr.json @@ -129,12 +129,13 @@ "map_enable_description": "Activer la carte", "map_gps_settings": "Paramètres de la carte et GPS", "map_gps_settings_description": "Gérer les paramètres de la Carte & GPS", + "map_implications": "La carte repose sur un service de tuiles externe (tiles.immich.cloud)", "map_light_style": "Thème clair", "map_manage_reverse_geocoding_settings": "Gérer les paramètres de géocodage inversé", "map_reverse_geocoding": "Géocodage inversé", "map_reverse_geocoding_enable_description": "Activer le géocodage inversé", "map_reverse_geocoding_settings": "Paramètres de géocodage inversé", - "map_settings": "Paramètres de la carte", + "map_settings": "Carte", "map_settings_description": "Gérer les paramètres de la carte", "map_style_description": "URL vers un thème de carte au format style.json", "metadata_extraction_job": "Extraction des métadonnées", @@ -320,7 +321,8 @@ "user_settings": "Paramètres utilisateur", "user_settings_description": "Gérer les paramètres utilisateur", "user_successfully_removed": "L'utilisateur {email} a bien été supprimé.", - "version_check_enabled_description": "Activer la vérification périodique de nouvelle version sur GitHub", + "version_check_enabled_description": "Activer la vérification périodique de nouvelle version", + "version_check_implications": "Le contrôle de version repose sur une communication périodique avec github.com", "version_check_settings": "Vérification de la version", "version_check_settings_description": "Gérer la vérification de nouvelle version d'Immich", "video_conversion_job": "Transcodage des vidéos", @@ -336,7 +338,8 @@ "album_added": "Album ajouté", "album_added_notification_setting_description": "Recevoir une notification par courriel lorsque vous êtes ajouté(e) à un album partagé", "album_cover_updated": "Couverture de l'album mise à jour", - "album_delete_confirmation": "Êtes-vous sûr de vouloir supprimer l'album {album} ?\nSi cet album est partagé, les autres utilisateurs ne pourront plus y accéder.", + "album_delete_confirmation": "Êtes-vous sûr de vouloir supprimer l'album {album} ?", + "album_delete_confirmation_description": "Si cet album est partagé, d'autres utilisateurs ne pourront plus y accéder.", "album_info_updated": "Détails de l'album mis à jour", "album_leave": "Quitter l'album ?", "album_leave_confirmation": "Êtes-vous sûr de vouloir quitter l'album {album} ?", @@ -360,6 +363,7 @@ "allow_edits": "Autoriser les modifications", "allow_public_user_to_download": "Permettre aux utilisateurs non connectés de télécharger", "allow_public_user_to_upload": "Permettre aux utilisateurs non connectés de téléverser", + "anti_clockwise": "Sens anti-horaire", "api_key": "Clé API", "api_key_description": "Cette valeur ne sera affichée qu'une seule fois. Assurez-vous de la copier avant de fermer la fenêtre.", "api_key_empty": "Le nom de votre clé API ne doit pas être vide", @@ -441,6 +445,7 @@ "clear_all_recent_searches": "Supprimer les recherches récentes", "clear_message": "Effacer le message", "clear_value": "Effacer la valeur", + "clockwise": "Sens horaire", "close": "Fermer", "collapse": "Réduire", "collapse_all": "Tout réduire", @@ -517,6 +522,8 @@ "do_not_show_again": "Ne plus afficher ce message", "done": "Terminé", "download": "Télécharger", + "download_include_embedded_motion_videos": "Vidéos embarquées", + "download_include_embedded_motion_videos_description": "Inclure des vidéos intégrées dans les photos de mouvement comme un fichier séparé", "download_settings": "Télécharger", "download_settings_description": "Gérer les paramètres de téléchargement des médias", "downloading": "Téléchargement", @@ -550,6 +557,10 @@ "edit_user": "Modifier l'utilisateur", "edited": "Modifié", "editor": "Editeur", + "editor_close_without_save_prompt": "Les changements ne seront pas enregistrés", + "editor_close_without_save_title": "Fermer l'éditeur ?", + "editor_crop_tool_h2_aspect_ratios": "Rapports hauteur/largeur", + "editor_crop_tool_h2_rotation": "Rotation", "email": "Courriel", "empty": "", "empty_album": "Album vide", @@ -699,6 +710,7 @@ "expired": "Expiré", "expires_date": "Expire le {date}", "explore": "Explorer", + "explorer": "Explorateur", "export": "Exporter", "export_as_json": "Exporter en JSON", "extension": "Extension", @@ -720,6 +732,7 @@ "filter_people": "Filtrer les personnes", "find_them_fast": "Pour les retrouver rapidement par leur nom", "fix_incorrect_match": "Corriger une association incorrecte", + "folders": "Dossiers", "force_re-scan_library_files": "Forcer la réactualisation de tous les fichiers de la bibliothèque", "forward": "Avant", "general": "Général", @@ -912,6 +925,7 @@ "ok": "Ok", "oldest_first": "Anciens en premier", "onboarding": "Accueil", + "onboarding_privacy_description": "Les fonctions suivantes (optionnelles) dépendent de services externes et peuvent être désactivées à tout moment dans les paramètres d'administration.", "onboarding_theme_description": "Choisissez un thème de couleur pour votre instance. Vous pouvez le changer plus tard dans vos paramètres.", "onboarding_welcome_description": "Mettons votre instance en place avec quelques paramètres communs.", "onboarding_welcome_user": "Bienvenue {user}", @@ -985,6 +999,7 @@ "previous_memory": "Souvenir précédent", "previous_or_next_photo": "Photo précédente ou suivante", "primary": "Primaire", + "privacy": "Vie privée", "profile_image_of_user": "Image de profil de {user}", "profile_picture_set": "Photo de profil définie.", "public_album": "Album public", @@ -1023,6 +1038,8 @@ "purchase_settings_server_activated": "La clé du produit pour le Serveur est gérée par l'administrateur", "range": "", "rating": "Étoile d'évaluation", + "rating_clear": "Effacer l'évaluation", + "rating_count": "{count, plural, one {# étoile} other {# étoiles}}", "rating_description": "Afficher l'évaluation d'exif dans le panneau d'information", "raw": "", "reaction_options": "Options de réaction", @@ -1146,6 +1163,7 @@ "shared_by_user": "Partagé par {user}", "shared_by_you": "Partagé par vous", "shared_from_partner": "Photos de {partner}", + "shared_link_options": "Options de lien partagé", "shared_links": "Liens partagés", "shared_photos_and_videos_count": "{assetCount, plural, other {# photos et vidéos partagées.}}", "shared_with_partner": "Partagé avec {partner}", @@ -1221,7 +1239,7 @@ "to_login": "Se connecter", "to_trash": "Corbeille", "toggle_settings": "Inverser les paramètres", - "toggle_theme": "Changer le thème", + "toggle_theme": "Inverser le thème sombre", "toggle_visibility": "Modifier la visibilité", "total_usage": "Utilisation globale", "trash": "Corbeille", @@ -1243,6 +1261,7 @@ "unlink_oauth": "Déconnecter OAuth", "unlinked_oauth_account": "Compte OAuth non connecté", "unnamed_album": "Album sans nom", + "unnamed_album_delete_confirmation": "Êtes-vous sûr de vouloir supprimer cet album ?", "unnamed_share": "Partage sans nom", "unsaved_change": "Modification non enregistrée", "unselect_all": "Annuler la sélection", diff --git a/web/src/lib/i18n/he.json b/web/src/lib/i18n/he.json index d73b06ad7004b..1679ecbc4d866 100644 --- a/web/src/lib/i18n/he.json +++ b/web/src/lib/i18n/he.json @@ -129,12 +129,13 @@ "map_enable_description": "אפשר תכונות מפה", "map_gps_settings": "הגדרות מפה & GPS", "map_gps_settings_description": "נהל הגדרות מפה & GPS (קידוד גאוגרפי הפוך)", + "map_implications": "תכונת המפה מסתמכת על שירות אריח חיצוני (tiles.immich.cloud)", "map_light_style": "עיצוב בהיר", "map_manage_reverse_geocoding_settings": "נהל הגדרות קידוד גאוגרפי הפוך", "map_reverse_geocoding": "קידוד גיאוגרפי הפוך", "map_reverse_geocoding_enable_description": "אפשר קידוד גיאוגרפי הפוך", "map_reverse_geocoding_settings": "הגדרות קידוד גיאוגרפי הפוך", - "map_settings": "הגדרות מפה", + "map_settings": "מפה", "map_settings_description": "נהל הגדרות מפה", "map_style_description": "כתובת אתר לערכת נושא של מפה style.json", "metadata_extraction_job": "חלץ מטא-נתונים", @@ -320,7 +321,8 @@ "user_settings": "הגדרות משתמש", "user_settings_description": "נהל הגדרות משתמש", "user_successfully_removed": "המשתמש {email} הוסר בהצלחה.", - "version_check_enabled_description": "אפשר בקשות רשת תקופתיות ל-GitHub כדי לבדוק אם יש מהדורות חדשות", + "version_check_enabled_description": "אפשר בדיקת גרסה", + "version_check_implications": "תכונת בדיקת הגרסה מסתמכת על תקשורת תקופתית עם github.com", "version_check_settings": "בדיקת גרסה", "version_check_settings_description": "הפעל/השבת את ההתראה על גרסה חדשה", "video_conversion_job": "המרת קידוד סרטונים", @@ -360,6 +362,7 @@ "allow_edits": "אפשר עריכות", "allow_public_user_to_download": "אפשר למשתמש ציבורי להוריד", "allow_public_user_to_upload": "אפשר למשתמש ציבורי להעלות", + "anti_clockwise": "נגד כיוון השעון", "api_key": "מפתח API", "api_key_description": "הערך הזה יוצג רק פעם אחת. נא לוודא שהעתקת אותו לפני סגירת החלון.", "api_key_empty": "מפתח ה-API שלך לא אמור להיות ריק", @@ -441,6 +444,7 @@ "clear_all_recent_searches": "נקה את כל החיפושים האחרונים", "clear_message": "נקה הודעה", "clear_value": "נקה ערך", + "clockwise": "עם כיוון השעון", "close": "סגור", "collapse": "כווץ", "collapse_all": "כווץ הכל", @@ -517,6 +521,8 @@ "do_not_show_again": "אל תציג את ההודעה הזאת שוב", "done": "סיום", "download": "הורדה", + "download_include_embedded_motion_videos": "סרטונים מוטמעים", + "download_include_embedded_motion_videos_description": "כלול סרטונים מוטעמים בתמונות עם תנועה כקובץ נפרד", "download_settings": "הורדה", "download_settings_description": "נהל הגדרות הקשורות להורדת נכסים", "downloading": "מוריד", @@ -550,6 +556,10 @@ "edit_user": "ערוך משתמש", "edited": "נערך", "editor": "עורך", + "editor_close_without_save_prompt": "השינויים לא יישמרו", + "editor_close_without_save_title": "לסגור את העורך?", + "editor_crop_tool_h2_aspect_ratios": "יחסי רוחב גובה", + "editor_crop_tool_h2_rotation": "סיבוב", "email": "דוא\"ל", "empty": "", "empty_album": "אלבום ריק", @@ -912,6 +922,7 @@ "ok": "בסדר", "oldest_first": "הישן ביותר ראשון", "onboarding": "היכרות", + "onboarding_privacy_description": "התכונות (האופציונליות) הבאות מסתמכות על שירותים חיצוניים, וניתנות לביטול בכל עת בהגדרות הניהול.", "onboarding_theme_description": "בחר/י את צבע ערכת הנושא עבור ההתקנה שלך. את/ה יכול/ה לשנות את זה מאוחר יותר בהגדרות שלך.", "onboarding_welcome_description": "בואו נכין את ההתקנה שלכם עם כמה הגדרות נפוצות.", "onboarding_welcome_user": "ברוכ/ה הבא/ה, {user}", @@ -985,6 +996,7 @@ "previous_memory": "זיכרון קודם", "previous_or_next_photo": "התמונה הקודמת או הבאה", "primary": "ראשי", + "privacy": "פרטיות", "profile_image_of_user": "תמונת פרופיל של {user}", "profile_picture_set": "תמונת פרופיל נבחרה.", "public_album": "אלבום ציבורי", @@ -995,7 +1007,7 @@ "purchase_activated_title": "המפתח שלך הופעל בהצלחה", "purchase_button_activate": "הפעל", "purchase_button_buy": "קנה", - "purchase_button_buy_immich": "קנה Immich", + "purchase_button_buy_immich": "קנה את Immich", "purchase_button_never_show_again": "לעולם אל תראה שוב", "purchase_button_reminder": "הזכר לי בעוד 30 יום", "purchase_button_remove_key": "הסר מפתח", @@ -1146,6 +1158,7 @@ "shared_by_user": "משותף על ידי {user}", "shared_by_you": "משותף על ידך", "shared_from_partner": "תמונות מאת {partner}", + "shared_link_options": "אפשרויות קישור משותף", "shared_links": "קישורים משותפים", "shared_photos_and_videos_count": "{assetCount, plural, other {# תמונות וסרטונים משותפים.}}", "shared_with_partner": "משותף עם {partner}", diff --git a/web/src/lib/i18n/hr.json b/web/src/lib/i18n/hr.json index 6b07d5af6d9e0..8882d80e252c4 100644 --- a/web/src/lib/i18n/hr.json +++ b/web/src/lib/i18n/hr.json @@ -1,5 +1,5 @@ { - "about": "Oko", + "about": "O", "account": "Račun", "account_settings": "Postavke računa", "acknowledge": "Potvrdi", @@ -25,7 +25,7 @@ "add_to_shared_album": "Dodaj u dijeljeni album", "added_to_archive": "Dodano u arhivu", "added_to_favorites": "Dodano u omiljeno", - "added_to_favorites_count": "Dodano {count} u omiljeno", + "added_to_favorites_count": "Dodano {count, number} u omiljeno", "admin": { "add_exclusion_pattern_description": "", "authentication_settings": "Postavke autentikacije", @@ -33,6 +33,7 @@ "authentication_settings_disable_all": "Jeste li sigurni da želite onemogućenit sve načine prijave? Prijava će biti potpuno onemogućena.", "background_task_job": "Pozadinski zadaci", "check_all": "Provjeri sve", + "cleared_jobs": "Izbrisani poslovi za: {job}", "config_set_by_file": "Konfiguracija je trenutno postavljena konfiguracijskom datotekom", "confirm_delete_library": "Jeste li sigurni da želite izbrisati biblioteku {library}?", "confirm_delete_library_assets": "Jeste li sigurni da želite izbrisati ovu biblioteku? Time će se izbrisati sva {count} sadržana sredstva iz Immicha i ne može se poništiti. Datoteke će ostati na disku.", @@ -48,6 +49,7 @@ "face_detection": "Detekcija lica", "face_detection_description": "Prepoznajte lica u sredstvima pomoću strojnog učenja. Za videozapise u obzir se uzima samo minijaturni prikaz. \"Sve\" (ponovno) obrađuje svu imovinu. \"Nedostaje\" stavlja u red čekanja sredstva koja još nisu obrađena. Otkrivena lica bit će stavljena u red čekanja za prepoznavanje lica nakon dovršetka prepoznavanja lica, grupirajući ih u postojeće ili nove osobe.", "facial_recognition_job_description": "Grupirajte otkrivena lica u osobe. Ovaj se korak pokreće nakon dovršetka prepoznavanja lica. \"Sve\" (ponovno) grupira sva lica. \"Nedostajuća\" lica u redovima kojima nije dodijeljena osoba.", + "failed_job_command": "Naredba {command} nije uspjela za posao: {job}", "force_delete_user_warning": "UPOZORENJE: Ovo će odmah ukloniti korisnika i sve pripadajuće podatke. Ovo se ne može poništiti i datoteke se ne mogu vratiti.", "forcing_refresh_library_files": "Prisilno osvježavanje svih datoteka knjižnice", "image_format_description": "WebP proizvodi manje datoteke od JPEG-a, ali se sporije kodira.", @@ -67,30 +69,33 @@ "image_thumbnail_resolution_description": "Koristi se prilikom pregledavanja grupa fotografija (glavna vremenska traka, prikaz albuma itd.). Veće razlučivosti mogu sačuvati više detalja, ali trebaju dulje za kodiranje, imaju veće veličine datoteka i mogu smanjiti odaziv aplikacije.", "job_concurrency": "{job} istovremenost", "job_not_concurrency_safe": "Ovaj posao nije siguran za istovremenost.", - "job_settings": "", - "job_settings_description": "", - "job_status": "", + "job_settings": "Postavke posla", + "job_settings_description": "Upravljajte istovremenošću poslova", + "job_status": "Status posla", "jobs_delayed": "", "jobs_failed": "", - "library_created": "", - "library_cron_expression": "", - "library_cron_expression_presets": "", - "library_deleted": "", - "library_import_path_description": "", - "library_scanning": "", - "library_scanning_description": "", - "library_scanning_enable_description": "", - "library_settings": "", - "library_settings_description": "", - "library_tasks_description": "", - "library_watching_enable_description": "", - "library_watching_settings": "", - "library_watching_settings_description": "", - "logging_enable_description": "", - "logging_level_description": "", - "logging_settings": "", - "machine_learning_clip_model": "", - "machine_learning_duplicate_detection": "", + "library_created": "Stvorena biblioteka: {library}", + "library_cron_expression": "Cron izraz", + "library_cron_expression_description": "Postavite interval skeniranja koristeći cron format. Za više informacija pogledajte npr. Crontab Guru", + "library_cron_expression_presets": "Unaprijed postavljene cron izraze", + "library_deleted": "Biblioteka izbrisana", + "library_import_path_description": "Navedite mapu za uvoz. Ova će se mapa, uključujući podmape, skenirati u potrazi za slikama i videozapisima.", + "library_scanning": "Periodično Skeniranje", + "library_scanning_description": "Konfigurirajte periodično skeniranje biblioteke", + "library_scanning_enable_description": "Omogući periodično skeniranje biblioteke", + "library_settings": "Externa biblioteka", + "library_settings_description": "Upravljajte postavkama vanjske biblioteke", + "library_tasks_description": "Obavljati bibliotekne zadatke", + "library_watching_enable_description": "Pratite vanjske biblioteke za promjena datoteke", + "library_watching_settings": "Gledanje biblioteke (EKSPERIMENTALNO)", + "library_watching_settings_description": "Automatsko praćenje promijenjenih datoteke", + "logging_enable_description": "Omogući zapisivanje", + "logging_level_description": "Kada je omogućeno, koju razinu zapisavanje koristiti.", + "logging_settings": "Zapisavanje", + "machine_learning_clip_model": "CLIP model", + "machine_learning_clip_model_description": "Naziv CLIP modela navedenog ovdje. Imajte na umu da morate ponovno pokrenuti posao 'Pametno Pretraživanje' za sve slike nakon promjene modela.", + "machine_learning_duplicate_detection": "Detekcija Duplikata", + "machine_learning_duplicate_detection_enabled": "Omogući detekciju duplikata", "machine_learning_duplicate_detection_enabled_description": "", "machine_learning_duplicate_detection_setting_description": "", "machine_learning_enabled_description": "", @@ -111,53 +116,59 @@ "machine_learning_settings_description": "", "machine_learning_smart_search": "", "machine_learning_smart_search_description": "", - "machine_learning_smart_search_enabled_description": "", - "machine_learning_url_description": "", - "manage_concurrency": "", - "manage_log_settings": "", - "map_dark_style": "", - "map_enable_description": "", - "map_light_style": "", - "map_reverse_geocoding": "", - "map_reverse_geocoding_enable_description": "", - "map_reverse_geocoding_settings": "", - "map_settings": "", - "map_settings_description": "", - "map_style_description": "", - "metadata_extraction_job": "", - "metadata_extraction_job_description": "", - "migration_job": "", - "migration_job_description": "", + "machine_learning_smart_search_enabled": "Omogući pametno pretraživanje", + "machine_learning_smart_search_enabled_description": "Ako je onemogućeno, slike neće biti kodirane za pametno pretraživanje.", + "machine_learning_url_description": "URL poslužitelja strojnog učenja", + "manage_concurrency": "Upravljanje Istovremenošću", + "manage_log_settings": "Upravljanje postavkama zapisivanje", + "map_dark_style": "Tamni stil", + "map_enable_description": "Omogući značajke karte", + "map_gps_settings": "Postavke Karte i GPS-a", + "map_gps_settings_description": "Upravljajte Postavkama Karte i GPS-a (Obrnuto Geokodiranje)", + "map_implications": "Značajka karte se oslanja na vanjsku uslugu pločica (tiles.immich.cloud)", + "map_light_style": "Svijetli stil", + "map_manage_reverse_geocoding_settings": "Upravljajte postavkama Obrnutog Geokodiranja", + "map_reverse_geocoding": "Obrnuto Geokodiranje", + "map_reverse_geocoding_enable_description": "Omogući obrnuto geokodiranje", + "map_reverse_geocoding_settings": "Postavke Obrnuto Geokodiranje", + "map_settings": "Karta", + "map_settings_description": "Upravljanje postavkama karte", + "map_style_description": "URL na style.json temu karte", + "metadata_extraction_job": "Izdvoj metapodatke", + "metadata_extraction_job_description": "Izdvojite podatke o metapodacima iz svakog sredstva, kao što su GPS i rezolucija", + "migration_job": "Migracija", + "migration_job_description": "Premjestite minijature za sredstva i lica u najnoviju strukturu mapa", "no_paths_added": "", - "no_pattern_added": "", - "note_apply_storage_label_previous_assets": "", - "note_cannot_be_changed_later": "", - "note_unlimited_quota": "", - "notification_email_from_address": "", - "notification_email_from_address_description": "", - "notification_email_host_description": "", - "notification_email_ignore_certificate_errors": "", - "notification_email_ignore_certificate_errors_description": "", - "notification_email_password_description": "", - "notification_email_port_description": "", - "notification_email_sent_test_email_button": "", - "notification_email_setting_description": "", - "notification_email_test_email_failed": "", - "notification_email_test_email_sent": "", - "notification_email_username_description": "", - "notification_enable_email_notifications": "", - "notification_settings": "", - "notification_settings_description": "", - "oauth_auto_launch": "", - "oauth_auto_launch_description": "", - "oauth_auto_register": "", - "oauth_auto_register_description": "", - "oauth_button_text": "", - "oauth_client_id": "", - "oauth_client_secret": "", - "oauth_enable_description": "", - "oauth_issuer_url": "", - "oauth_mobile_redirect_uri": "", + "no_pattern_added": "Nije dodan uzorak", + "note_apply_storage_label_previous_assets": "Napomena: da biste primijenili Oznaku Pohrane na prethodno prenesena sredstva, pokrenite", + "note_cannot_be_changed_later": "NAPOMENA: Ovo se ne može promijeniti kasnije!", + "note_unlimited_quota": "Napomena: Unesite 0 za neograničenu kvotu", + "notification_email_from_address": "Od adrese", + "notification_email_from_address_description": "E-mail adresa pošiljatelja, na primjer: \"Immich Photo Server \"", + "notification_email_host_description": "Poslužitelja e-pošte (npr. smtp.immich.app)", + "notification_email_ignore_certificate_errors": "Ignoriraj pogreške certifikata", + "notification_email_ignore_certificate_errors_description": "Ignoriraj pogreške provjere valjanosti TLS certifikata (nije preporučeno)", + "notification_email_password_description": "Lozinka za korištenje pri autentifikaciji s poslužiteljem e-pošte", + "notification_email_port_description": "Port poslužitelja e-pošte (npr. 25, 465, ili 587)", + "notification_email_sent_test_email_button": "Pošaljite probni e-mail i spremi", + "notification_email_setting_description": "Postavke za slanje e-mail obavijeste", + "notification_email_test_email": "Pošalji probni e-mail", + "notification_email_test_email_failed": "Slanje testne e-pošte nije uspjelo, provjerite svoje postavke", + "notification_email_test_email_sent": "Testna e-poruka poslana je na {email}. Provjerite svoju pristiglu poštu.", + "notification_email_username_description": "Korisničko ime koje se koristi pri autentifikaciji s poslužiteljem e-pošte", + "notification_enable_email_notifications": "Omogući obavijesti putem e-pošte", + "notification_settings": "Postavke Obavijesti", + "notification_settings_description": "Upravljanje postavkama obavijesti, uključujući e-poštu", + "oauth_auto_launch": "Automatsko pokretanje", + "oauth_auto_launch_description": "Automatski pokrenite OAuth prijavu nakon navigacije na stranicu za prijavu", + "oauth_auto_register": "Automatska registracija", + "oauth_auto_register_description": "Automatski registrirajte nove korisnike nakon prijave s OAuth", + "oauth_button_text": "Tekst gumba", + "oauth_client_id": "ID Klijenta", + "oauth_client_secret": "Tajna Klijenta", + "oauth_enable_description": "Prijavite se putem OAutha", + "oauth_issuer_url": "URL Izdavatelja", + "oauth_mobile_redirect_uri": "Mobilnog Preusmjeravanja URI", "oauth_mobile_redirect_uri_override": "", "oauth_mobile_redirect_uri_override_description": "", "oauth_scope": "", @@ -839,27 +850,31 @@ "storage_label": "", "storage_usage": "", "submit": "", - "suggestions": "", - "sunrise_on_the_beach": "", + "suggestions": "Prijedlozi", + "sunrise_on_the_beach": "Sunrise on the beach", "swap_merge_direction": "", - "sync": "", + "sync": "Sink.", "template": "", - "theme": "", - "theme_selection": "", - "theme_selection_description": "", - "time_based_memories": "", - "timezone": "", - "to_archive": "", - "to_favorite": "", - "toggle_settings": "", - "toggle_theme": "", + "theme": "Tema", + "theme_selection": "Izbor teme", + "theme_selection_description": "Automatski postavite temu na svijetlu ili tamnu ovisno o postavkama sustava vašeg preglednika", + "they_will_be_merged_together": "Oni ću biti spojeni zajedno", + "time_based_memories": "Uspomene temeljene na vremenu", + "timezone": "Vremenska zona", + "to_archive": "Arhivaj", + "to_change_password": "Promjeni lozinku", + "to_favorite": "Omiljeni", + "to_login": "Prijava", + "to_trash": "Smeće", + "toggle_settings": "Uključi/isključi postavke", + "toggle_theme": "Promjeni temu", "toggle_visibility": "", - "total_usage": "", - "trash": "", - "trash_all": "", - "trash_no_results_message": "", - "trashed_items_will_be_permanently_deleted_after": "", - "type": "", + "total_usage": "Ukupna upotreba", + "trash": "Smeće", + "trash_all": "Stavi sve u smeće", + "trash_no_results_message": "Ovdje će se prikazati bačene fotografije i videozapisi.", + "trashed_items_will_be_permanently_deleted_after": "Stavke bačene u smeće trajno će se izbrisati nakon {days, plural, one {# day} other {# days}}.", + "type": "Vrsta", "unarchive": "", "unarchived": "", "unfavorite": "", diff --git a/web/src/lib/i18n/hu.json b/web/src/lib/i18n/hu.json index c754035c7a049..7869e956c7b01 100644 --- a/web/src/lib/i18n/hu.json +++ b/web/src/lib/i18n/hu.json @@ -129,12 +129,13 @@ "map_enable_description": "Térkép funkciók engedélyezése", "map_gps_settings": "Térkép és GPS beállítások", "map_gps_settings_description": "A térkép és a GPS (fordított geokódolás) beállításainak kezelése", + "map_implications": "A térkép szolgáltatás egy külső csempeszolgáltatót használ (tiles.immich.cloud)", "map_light_style": "Világos stílus", "map_manage_reverse_geocoding_settings": "A fordított geokódolás beállításainak kezelése", "map_reverse_geocoding": "Fordított Geokódolás", "map_reverse_geocoding_enable_description": "Fordított geokódolás engedélyezése", "map_reverse_geocoding_settings": "Fordított Geokódolási Beállítások", - "map_settings": "Térkép beállítások", + "map_settings": "Térkép", "map_settings_description": "Térkép beállítások kezelése", "map_style_description": "Egy style.json térképstílusra mutató URL", "metadata_extraction_job": "Metaadatok feldolgozása", @@ -227,7 +228,7 @@ "storage_template_migration_info": "A megváltozott sablon csak az újonnan feltöltött fájlokra lesz alkalmazva. A fájlok visszamenőleges megváltoztatásához futtatni kell a megfelelő munkát: {job}.", "storage_template_migration_job": "Tárhely Sablon Migrációja", "storage_template_more_details": "További információért erről a szolgáltatásról lásd Tárolási Sablont és az implikációkat", - "storage_template_onboarding_description": "Engedélyezve, ez a funkció automatikusan rendszerezi a fájlokat egy felhasználó által megadott sablon alapján. Stabilitási problémák miatt a funkció alapértelmezés szerint ki van kapcsolva. További információkért tekintse meg a dokumentációt.", + "storage_template_onboarding_description": "Ez a funkció, ha be van kapcsolva, automatikusan rendszerezi a fájlokat egy felhasználó által megadott sablon alapján. Stabilitási problémák miatt a funkció alapértelmezés szerint ki van kapcsolva. További információkért tekintse meg a dokumentációt.", "storage_template_path_length": "Út hozzávetőleges maximális hossza: {length, number}{limit, number}", "storage_template_settings": "Tárolási sablon", "storage_template_settings_description": "Kezelje a feltöltött képi vagyontárgyak mappaszerkezetét és fájlnevét", @@ -260,7 +261,7 @@ "transcoding_codecs_learn_more": "Hogy többet tudjon meg az itt felhasznált kifejezésekről, látogassa meg az FFmpeg dokumentációt a H.264 kodekhez, a HEVC kodekhez és a VP9 kodekhez.", "transcoding_constant_quality_mode": "Állandó minőségi mód", "transcoding_constant_quality_mode_description": "Az ICQ jobb, mint a CQP, viszont nem minden hardver támogatja. A rendszer az itt beállított módszert részesíti előnyben. A NVENC ignorálja a beállítást, mivel nem támogatja az ICQ-t.", - "transcoding_constant_rate_factor": "Állandó ráta tényező (árt)", + "transcoding_constant_rate_factor": "Állandó ráta tényező (-crf)", "transcoding_constant_rate_factor_description": "Videó minőségi szint. Jellemző értékek kodekenként: H.264: 23, HEVC: 28, VP9: 31, AV1: 35. Minél alacsonyabb, annál jobb minőséget eredményez, viszont nagyobb fájlmérettel is jár.", "transcoding_disabled_description": "Ne transzkódoljon videót. Nem lejátszható videókhoz vezethet néhány kliensen", "transcoding_hardware_acceleration": "Hardveres Gyorsítás", @@ -278,7 +279,7 @@ "transcoding_preferred_hardware_device": "Átkódoláshoz preferált hardver eszköz", "transcoding_preferred_hardware_device_description": "Csak VAAPI vagy QSV esetén. Beállítja a hardveres transzkódoláshoz használt DRI node-ot.", "transcoding_preset_preset": "Beállítás (-preset)", - "transcoding_preset_preset_description": "Tömörítési gyorsaság. Lassabb beállítások esetén kisebb fájlokat generál, valamint növeli a minőséget megcélzott bitráta esetén. A VP9 kódolás figyelmen kívül hagyja a `faster`-nél gyorsabb beállításokat.", + "transcoding_preset_preset_description": "Tömörítési gyorsaság. Lassabb beállítások esetén kisebb fájlokat generál, valamint növeli a minőséget megcélzott bitráta esetén. A VP9 kódolás figyelmen kívül hagyja a 'faster (gyorsabb)'-nál gyorsabb beállításokat.", "transcoding_reference_frames": "Referencia képkockák", "transcoding_reference_frames_description": "Ennyi képkockára hivatkozzon egy képkocka tömörítéséhez. Magasabb értékek növelik a tömörítési hatékonyságot, de lelassítják a kódolási folyamatot. 0 esetén a szoftver magának beállítja az értéket.", "transcoding_required_description": "Csak az el nem fogadott formátumú videókat", @@ -320,7 +321,8 @@ "user_settings": "Felhasználó Beállítások", "user_settings_description": "Felhasználó beállítások kezelése", "user_successfully_removed": "{email} felhasználó sikeresen törlésre került.", - "version_check_enabled_description": "Engedélyezze rendszeres kérések küldését a GitHub szervereinek új verzió elérhetőségének ellenőrzésére", + "version_check_enabled_description": "Új verziók elérhetőségének ellenőrzése", + "version_check_implications": "Az új verziók ellenőrzése szolgáltatás időszakos kommunikációt igényel a github.com oldallal", "version_check_settings": "Verzió Ellenőrzés", "version_check_settings_description": "Az új verzióról való értesítés be- és kikapcsolása", "video_conversion_job": "Videók Átkódolása", @@ -332,6 +334,7 @@ "advanced": "Haladó", "age_months": "Kor {months, plural, one {# month} other {# months}}", "age_year_months": "Kor 1 év, {months, plural, one {# month} other {# months}}", + "age_years": "{years, plural, other {# éves}}", "album_added": "Albumhoz hozzáadva", "album_added_notification_setting_description": "Küldjön emailes értesítőt, amikor hozzáadnak egy megosztott albumhoz", "album_cover_updated": "Album borító frissítve", @@ -358,7 +361,8 @@ "allow_dark_mode": "Sötét stílus engedélyezése", "allow_edits": "Szerkesztések engedélyezése", "allow_public_user_to_download": "Engedélyezze publikus felhasználónak, hogy letöltse", - "allow_public_user_to_upload": "Engedélyezze publikus felhasználónak, hogy feltöltsön", + "allow_public_user_to_upload": "Engedélyezze a feltöltést publikus felhasználónak", + "anti_clockwise": "Óramutató járásával ellentétes irány", "api_key": "API kulcs", "api_key_description": "Ez az érték csak egyszer jelenik meg. Az ablak bezárása előtt feltétlenül másolja át.", "api_key_empty": "A te API Kulcs neved nem kéne üres legyen", @@ -385,8 +389,18 @@ "asset_uploaded": "Feltöltve", "asset_uploading": "Feltöltés...", "assets": "elemek", + "assets_added_count": "{count, plural, other {# elem}} hozzáadva", + "assets_added_to_album_count": "{count, plural, other {# elem}} hozzáadva az albumhoz", + "assets_added_to_name_count": "{count, plural, other {# elem}} hozzáadva a(z) {hasName, select, true {{name}} other {új}} albumba", + "assets_count": "{count, plural, other {# elem}}", "assets_moved_to_trash": "{count, plural, one {# fájl} other {# fájl}} a lomtárba mozgatva", + "assets_moved_to_trash_count": "{count, plural, other {# elem}} szemétbe mozgatva", + "assets_permanently_deleted_count": "{count, plural, other {# elem}} örökre törölve", + "assets_removed_count": "{count, plural, other {# elem}} eltávolítva", "assets_restore_confirmation": "Biztosan visszaállítja a lomtárbeli elemeket? Ez a művelet nem visszavonható!", + "assets_restored_count": "{count, plural, other {# elem}} visszaállítva", + "assets_trashed_count": "{count, plural, other {# elem}} kidobva", + "assets_were_part_of_album_count": "{count, plural, other {# elem}} már az album része volt", "authorized_devices": "Engedélyezett készülékek", "back": "Vissza", "back_close_deselect": "Vissza, bezárás, vagy kijelölés törlése", @@ -394,7 +408,10 @@ "birthdate_saved": "Születésnap elmentve", "birthdate_set_description": "A születés napját a rendszer annak kijelzésére használja, hogy a fénykép készítésének idejében az illető hány éves volt.", "blurred_background": "Homályos háttér", + "build": "Építés", + "build_image": "Kép építése", "bulk_delete_duplicates_confirmation": "Biztosan kitöröl {count, plural, one {# duplikált fájlt} other {# duplikált fájlt}}? A művelet során minden hasonló fájlcsoportból a legnagyobb méretű fájlt megtartja, minden másik duplikált fájlt kitörli. Ez a művelet nem visszavonható!", + "bulk_keep_duplicates_confirmation": "Biztosan meg szeretne tartani {count, plural, other {# egyező elemet}}? Ez felold minden duplikátum csoportot elemek törlése nélkül.", "bulk_trash_duplicates_confirmation": "Biztosan kitöröl {count, plural, one {# duplikált fájlt} other {# duplikált fájlt}}? Ez a művelet megtartja minden fájlcsoportból a legnagyobb méretű elemet, és kitörli minden másik duplikáltat.", "buy": "Immich megvásárlása", "camera": "Fényképezőgép", @@ -427,6 +444,7 @@ "clear_all_recent_searches": "Legutóbbi keresések törlése", "clear_message": "Üzenet törlése", "clear_value": "Érték törlése", + "clockwise": "Óramutató járásával megegyező irány", "close": "Bezárás", "collapse": "Összecsuk", "collapse_all": "Mindet összecsuk", @@ -503,12 +521,15 @@ "do_not_show_again": "Ne mutassa többet ezt az üzenetet", "done": "Kész", "download": "Letöltés", + "download_include_embedded_motion_videos": "Beágyazott videók", + "download_include_embedded_motion_videos_description": "Mozgó képekbe beágyazott videók mutatása külön fájlként", "download_settings": "Letöltés", "download_settings_description": "Képi vagyontárgyak letöltésére vonatkozó beállítások", "downloading": "Letöltés", "downloading_asset_filename": "Fájl letöltése {filename}", "drop_files_to_upload": "Húzza a fájlokat bárhova a feltöltéshez", "duplicates": "Duplikátumok", + "duplicates_description": "Oldja fel a csoportokat a (ha léteznek) duplukátumok megjelölésével", "duration": "Időtartam", "durations": { "days": "{days, plural, one {nap} other {{days, number} nap}}", @@ -535,6 +556,10 @@ "edit_user": "Felhasználó módosítása", "edited": "Módosítva", "editor": "Szerkesztő", + "editor_close_without_save_prompt": "A változtatások nem lesznek mentve", + "editor_close_without_save_title": "Szerkesztő bezárása?", + "editor_crop_tool_h2_aspect_ratios": "Oldalarányok", + "editor_crop_tool_h2_rotation": "Forgatás", "email": "Email", "empty": "", "empty_album": "Üres Album", @@ -542,7 +567,7 @@ "empty_trash_confirmation": "Biztosan kiüríti a lomtárat? Ezzel minden lomtárbeli fájlt véglegesen letöröl az Immich szolgáltatásból.\nEz a művelet nem visszavonható!", "enable": "Engedélyezés", "enabled": "Engedélyezve", - "end_date": "", + "end_date": "Vég dátum", "error": "Hiba", "error_loading_image": "Hiba a kép betöltése közben", "error_title": "Hiba - valami félresikerült", @@ -550,7 +575,9 @@ "cannot_navigate_next_asset": "Nem lehet a következő elemhez navigálni", "cannot_navigate_previous_asset": "Nem lehet az előző elemhez navigálni", "cant_apply_changes": "Nem lehet alkalmazni a változtatásokat", + "cant_change_activity": "Nem lehet {enabled, select, true {engedélyezni} other {kikapcsolni}} tevékenységet", "cant_change_asset_favorite": "Nem lehet a kedvenc állapotot megváltoztatni ehhez az elemhez", + "cant_change_metadata_assets_count": "Nem lehet {count, plural, other {# elem}} metaadatát megváltoztatni", "cant_get_faces": "Arcok lekérdezése sikertelen", "cant_get_number_of_comments": "Hozzászólások számának lekérdezése sikertelen", "cant_search_people": "Emberek keresése sikertelen", @@ -564,6 +591,7 @@ "error_removing_assets_from_album": "Hiba történt az elemek albumból való eltávolítása során, további információért ellenőrizze a logokat", "error_selecting_all_assets": "Minden elem kijelölése közben hiba lépett fel", "exclusion_pattern_already_exists": "Ez a kizárási minta már létezik.", + "failed_job_command": "Parancs {command} hibával zárult a {job} munkában", "failed_to_create_album": "Album készítése sikertelen", "failed_to_create_shared_link": "Megosztott link készítése sikertelen", "failed_to_edit_shared_link": "Megosztott link szerkesztése sikertelen", @@ -572,81 +600,121 @@ "failed_to_load_assets": "Elemek betöltése sikertelen", "failed_to_load_people": "Emberek betöltése sikertelen", "failed_to_remove_product_key": "Termékkulcs eltávolítása sikertelen", + "failed_to_stack_assets": "Elemek csoportosítása sikertelen", + "failed_to_unstack_assets": "Elemek szétszedése sikertelen", "import_path_already_exists": "Ez az importálási útvonal már létezik.", "incorrect_email_or_password": "Helytelen e-mail vagy jelszó", "paths_validation_failed": "Sikertelen érvényesítés {paths, plural, one {# elérési útvonalon} other {# elérési útvonalon}}", "profile_picture_transparent_pixels": "Profilképek nem tartalmazhatnak átlátszó pixeleket. Közelítsen rá és/vagy mozgassa a képet.", "quota_higher_than_disk_size": "Az elérhető háttértárnál nagyobb kvótát állított be", + "repair_unable_to_check_items": "Nem sikerült {count, select, one {element} other {elemeket}} ellenőrizni", "unable_to_add_album_users": "Felhasználók hozzáadása albumhoz sikertelen", "unable_to_add_assets_to_shared_link": "Felhasználók hozzáadása megosztott linkhez sikertelen", "unable_to_add_comment": "Hozzászólás sikertelen", "unable_to_add_exclusion_pattern": "Kivétel minta hozzáadása sikertelen", "unable_to_add_import_path": "Importálási útvonal hozzáadása sikertelen", "unable_to_add_partners": "Partnerek hozzáadása sikertelen", - "unable_to_change_album_user_role": "", - "unable_to_change_date": "", - "unable_to_change_location": "", + "unable_to_add_remove_archive": "Elem {archived, select, true {eltávolítása archívumból} other {hozzáadása archívumba}} sikertelen", + "unable_to_add_remove_favorites": "Elem {favorite, select, true {eltávolítása kedvencekből} other {hozzáadása kedvencekhez}} sikertelen", + "unable_to_archive_unarchive": "Elem {archived, select, true {archiválása} other {kivétele archívumból}} sikertelen", + "unable_to_change_album_user_role": "Album tagjának szerepének megváltoztatása sikertelen", + "unable_to_change_date": "Dátum megváltoztatása sikertelen", + "unable_to_change_favorite": "Kedvenc állapot megváltoztatása sikertelen", + "unable_to_change_location": "Hely megváltoztatása sikertelen", + "unable_to_change_password": "Jelszó megváltoztatása sikertelen", + "unable_to_change_visibility": "{count, plural, other {# ember}} láthatóságának a megváltoztatása sikertelen", "unable_to_check_item": "", "unable_to_check_items": "", + "unable_to_complete_oauth_login": "OAuth bejelentkezés sikertelen", + "unable_to_connect": "Csatlakozás sikertelen", + "unable_to_connect_to_server": "Szerverhez való csatlakozás sikertelen", "unable_to_copy_to_clipboard": "Vágólapra másolás sikertelen. Ellenőrizze, hogy a kapcsolat https-en keresztül történik", - "unable_to_create_admin_account": "", - "unable_to_create_library": "", - "unable_to_create_user": "", - "unable_to_delete_album": "", - "unable_to_delete_asset": "", + "unable_to_create_admin_account": "Admin felhasználó létrehozása sikertelen", + "unable_to_create_api_key": "Új API kulcs létrehozása sikertelen", + "unable_to_create_library": "Könyvtár létrehozása sikertelen", + "unable_to_create_user": "Felhasználó létrehozása sikertelen", + "unable_to_delete_album": "Album törlése sikertelen", + "unable_to_delete_asset": "Elem törlése sikertelen", + "unable_to_delete_assets": "Hiba történt az elemek törlésekor", + "unable_to_delete_exclusion_pattern": "Kizárási minta törlése sikertelen", + "unable_to_delete_import_path": "Import útvonal törlése sikertelen", + "unable_to_delete_shared_link": "Megosztott link törlése sikertelen", "unable_to_delete_user": "Nem sikerült törölni a felhasználót", + "unable_to_download_files": "Fájlok letöltése sikertelen", + "unable_to_edit_exclusion_pattern": "Kizárási minta szerkesztése sikertelen", + "unable_to_edit_import_path": "Import útvonal szerkesztése sikertelen", "unable_to_empty_trash": "Nem sikerült a lomtár ürítése", "unable_to_enter_fullscreen": "Nem lehet belépni a teljes képernyőre", "unable_to_exit_fullscreen": "Nem lehet kilépni a teljes képernyőről", - "unable_to_hide_person": "", - "unable_to_load_album": "", - "unable_to_load_asset_activity": "", - "unable_to_load_items": "", - "unable_to_load_liked_status": "", - "unable_to_play_video": "", - "unable_to_refresh_user": "", - "unable_to_remove_album_users": "", + "unable_to_get_comments_number": "Hozzászólások számának lekérdezése sikertelen", + "unable_to_get_shared_link": "Megosztott link lekérdezése sikertelen", + "unable_to_hide_person": "Személy elrejtése sikertelen", + "unable_to_link_oauth_account": "OAuth felhasználó csatlakoztatása sikertelen", + "unable_to_load_album": "Album betöltése sikertelen", + "unable_to_load_asset_activity": "Elem aktivitásának betöltése sikertelen", + "unable_to_load_items": "Elemek betöltése sikertelen", + "unable_to_load_liked_status": "Tetszik állapot betöltése sikertelen", + "unable_to_log_out_all_devices": "Minden eszközből való kijelentkeztetés sikertelen", + "unable_to_log_out_device": "Sikertelen kijelentkezés", + "unable_to_login_with_oauth": "Sikertelen bejelentkezés OAuth-tal", + "unable_to_play_video": "Videó lejátszása sikertelen", + "unable_to_reassign_assets_existing_person": "Nem sikerült az elemeket áthelyezni {name, select, null {egy létező személyhez} other {hozzá: {name}}}", + "unable_to_reassign_assets_new_person": "Elemek áthelyezése új személyhez sikertelen", + "unable_to_refresh_user": "Felhasználó újratöltése sikertelen", + "unable_to_remove_album_users": "Felhasználó albumból való eltávolítása sikertelen", + "unable_to_remove_api_key": "API kulcs eltávolítása sikertelen", + "unable_to_remove_assets_from_shared_link": "Elemek eltávolítása megosztott linkből sikertelen", "unable_to_remove_comment": "", - "unable_to_remove_library": "", - "unable_to_remove_partner": "", - "unable_to_remove_reaction": "", + "unable_to_remove_library": "Könyvtár törlése sikertelen", + "unable_to_remove_offline_files": "Offline fájlok törlése sikertelen", + "unable_to_remove_partner": "Partner eltávolítása sikertelen", + "unable_to_remove_reaction": "Reakció eltávolítása sikertelen", "unable_to_remove_user": "", - "unable_to_repair_items": "", - "unable_to_reset_password": "", - "unable_to_resolve_duplicate": "", - "unable_to_restore_assets": "", + "unable_to_repair_items": "Elemek javítása sikertelen", + "unable_to_reset_password": "Jelszó visszaállítása sikertelen", + "unable_to_resolve_duplicate": "Duplikátum feloldása sikertelen", + "unable_to_restore_assets": "Elemek szemeteskosárból való visszaállítása sikertelen", "unable_to_restore_trash": "Nem sikerült a lomtár visszaállítása", - "unable_to_restore_user": "", - "unable_to_save_album": "", - "unable_to_save_name": "", - "unable_to_save_profile": "", - "unable_to_save_settings": "", - "unable_to_scan_libraries": "", - "unable_to_scan_library": "", - "unable_to_set_profile_picture": "", + "unable_to_restore_user": "Felhasználó visszaállítása sikertelen", + "unable_to_save_album": "Album mentése sikertelen", + "unable_to_save_api_key": "API kulcs mentése sikertelen", + "unable_to_save_date_of_birth": "Születési időpont mentése sikertelen", + "unable_to_save_name": "Név mentése sikertelen", + "unable_to_save_profile": "Profil mentése sikertelen", + "unable_to_save_settings": "Beállítások mentése sikertelen", + "unable_to_scan_libraries": "Könyvtárak ellenőrzése sikertelen", + "unable_to_scan_library": "Könyvtár ellenőrzése sikertelen", + "unable_to_set_feature_photo": "Kijelölt fénykép beállítása sikertelen", + "unable_to_set_profile_picture": "Profilkép beállítása sikertelen", "unable_to_submit_job": "Nem sikerült a profilt elmenteni", "unable_to_trash_asset": "Nem sikerült a fájl lomtárba mozgatása", "unable_to_unlink_account": "Nem sikerült a fiók lekapcsolása", + "unable_to_update_album_cover": "Albumborító beállítása sikertelen", + "unable_to_update_album_info": "Album információ frissítése sikertelen", "unable_to_update_library": "Nem sikerült a képtár módosítása", "unable_to_update_location": "Nem sikerült az elérés módosítása", "unable_to_update_settings": "Nem sikerült a beállítások módosítása", "unable_to_update_timeline_display_status": "Nem sikerült az idővonal kijelzési státuszának módosítása", - "unable_to_update_user": "Nem sikerült a felhasználó módosítása" + "unable_to_update_user": "Nem sikerült a felhasználó módosítása", + "unable_to_upload_file": "Fájlfeltöltés sikertelen" }, "every_day_at_onepm": "", "every_night_at_midnight": "", "every_night_at_twoam": "", "every_six_hours": "", + "exif": "Exif", "exit_slideshow": "Kilépés a diavetítésből", - "expand_all": "", + "expand_all": "Minden kinyitása", "expire_after": "Lejárati idő", "expired": "Lejárt", + "expires_date": "Lejár {date}", "explore": "Felfedezés", "export": "Exportálás", "export_as_json": "Exportálás JSON formátumban", "extension": "Kiterjesztés", "external": "Külső", "external_libraries": "Külső Képtárak", + "face_unassigned": "Nincs hozzárendelve", "failed_to_get_people": "Személyek lekérése sikertelen", "favorite": "Kedvenc", "favorite_or_unfavorite_photo": "Fotó kedvencnek jelölése vagy annak visszavonása", @@ -671,15 +739,33 @@ "go_to_search": "Ugrás a kereséshez", "go_to_share_page": "Ugrás a megosztás oldalhoz", "group_albums_by": "Albumok csoportosítása...", - "has_quota": "", + "group_no": "Nincs csoportosítás", + "group_owner": "Csoportosítás tulajdonosonként", + "group_year": "Csoportosítás évenként", + "has_quota": "Van kvótája", + "hi_user": "Helló {name} ({email})", + "hide_all_people": "Minden személy elrejtése", "hide_gallery": "Galéria elrejtése", + "hide_named_person": "Személy {name} elrejtése", "hide_password": "Jelszó elrejtése", "hide_person": "Személy elrejtése", + "hide_unnamed_people": "Megnevezetlen emberek elrejtése", "host": "", "hour": "Óra", "image": "Kép", + "image_alt_text_date": "{isVideo, select, true {Videó} other {Kép}} készítési dátuma {date}", + "image_alt_text_date_1_person": "{isVideo, select, true {Videó} other {Kép}} vele: {person1} készítve {date}", + "image_alt_text_date_2_people": "{isVideo, select, true {Videó} other {Kép}} velük: {person1} és {person2}, ekkor: {date}", + "image_alt_text_date_3_people": "{isVideo, select, true {Videó} other {Kép}} velük: {person1}, {person2} és {person3} ekkor: {date}", + "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Videó} other {Kép}} velük: {person1}, {person2} és {additionalCount, number} más ekkor: {date}", + "image_alt_text_date_place": "{isVideo, select, true {Videó} other {Kép}} itt: {country}, {city}, ekkor: {date}", + "image_alt_text_date_place_1_person": "{isVideo, select, true {Videó} other {Kép}} itt: {country}, {city}, vele: {person1}, ekkor: {date}", + "image_alt_text_date_place_2_people": "{isVideo, select, true {Videó} other {Kép}} itt: {country}, {city}, velük: {person1} és {person2}, ekkor: {date}", + "image_alt_text_date_place_3_people": "{isVideo, select, true {Videó} other {Kép}} itt: {country}, {city}, velük: {person1}, {person2} és {person3}, ekkor: {date}", + "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Videó} other {Kép}} itt: {country}, {city}, velük: {person1}, {person2} és {additionalCount, number} más, ekkor: {date}", "img": "", "immich_logo": "Immich Logó", + "immich_web_interface": "Immich web felület", "import_from_json": "Importálás JSON formátumból", "import_path": "Importálási útvonal", "in_albums": "{count, plural, one {# albumban} other {# albumban}}", @@ -691,12 +777,13 @@ "info": "Infó", "interval": { "day_at_onepm": "Minden nap 13 órakor", - "hours": "", + "hours": "{hours, plural, one {óránként} other {{hours, number} óránként}}", "night_at_midnight": "Minden éjjel éjfélkor", "night_at_twoam": "Minden éjjel 2 órakor" }, "invite_people": "Személyek Meghívása", "invite_to_album": "Meghívás az albumba", + "items_count": "{count, plural, other {# elem}}", "job_settings_description": "", "jobs": "Feladatok", "keep": "Megtartás", @@ -705,15 +792,18 @@ "language": "Nyelv", "language_setting_description": "Válassza ki preferált nyelvét", "last_seen": "Utoljára látva", + "latest_version": "Legfrissebb verzió", + "latitude": "Szélesség", "leave": "Elhagyás", "let_others_respond": "Engedd, hogy mások reagáljanak", "level": "Szint", "library": "Képtár", "library_options": "Képtár beállítások", "light": "Világos", + "like_deleted": "Tetszik törölve", "link_options": "Link beállítások", - "link_to_oauth": "", - "linked_oauth_account": "", + "link_to_oauth": "Csatlakoztatás OAuth-hoz", + "linked_oauth_account": "Csatlakoztatott OAuth felhasználó", "list": "Lista", "loading": "Betöltés", "loading_search_results_failed": "Keresési eredmények betöltése sikertelen", @@ -721,8 +811,12 @@ "log_out_all_devices": "Összes Eszköz Kijelentkeztetése", "logged_out_all_devices": "Az összes eszköz kijelentkeztetve", "logged_out_device": "Eszköz kijelentkeztetve", - "login_has_been_disabled": "", - "look": "", + "login": "Bejelentkezés", + "login_has_been_disabled": "Bejelentkezés le van tiltva.", + "logout_all_device_confirmation": "Biztos, hogy minden eszközből szeretne kijelentkezni?", + "logout_this_device_confirmation": "Biztos, hogy szeretne kijelentkezni ebből az eszközből?", + "longitude": "Hosszúság", + "look": "Kinézet", "loop_videos": "Videók ismétlése", "loop_videos_description": "Engedélyezi a videók folyamatosan ismételt lejátszását az elem megjelenítőben.", "make": "Gyártó", @@ -734,6 +828,7 @@ "manage_your_devices": "Engedélyezett készülékek kezelése", "manage_your_oauth_connection": "OAuth kapcsolat kezelése", "map": "Térkép", + "map_marker_for_images": "Térképjelölő a képekhez itt készült: {country}, {city}", "map_marker_with_image": "Térképjelölő képpel", "map_settings": "Térkép beállítások", "matches": "Megegyezések", @@ -741,13 +836,15 @@ "memories": "Emlékek", "memories_setting_description": "Emlékek tartalmának kezelése", "memory": "Emlék", + "memory_lane_title": "Emlékek {title}", "menu": "Menü", "merge": "Összevonás", "merge_people": "Személyek összevonása", "merge_people_limit": "Egyszerre legfeljebb 5 arcot vonhatsz össze", "merge_people_prompt": "Biztosan összevonod ezeket a személyeket? Ez a művelet nem visszavonható.", - "merge_people_successfully": "", - "minimize": "", + "merge_people_successfully": "Személyek sikeresen egyesítve", + "merged_people_count": "{count, plural, other {# személy}} egyesítve", + "minimize": "Lekicsinítés", "minute": "Perc", "missing": "Hiányzó", "model": "Modell", @@ -758,79 +855,110 @@ "name": "Név", "name_or_nickname": "Név vagy becenév", "never": "Soha", + "new_album": "Új album", "new_api_key": "Új API Kulcs", "new_password": "Új jelszó", "new_person": "Új személy", "new_user_created": "Új felhasználó létrehozva", + "new_version_available": "ÚJ VERZIÓ ELÉRHETŐ", "newest_first": "Legújabb először", "next": "Következő", "next_memory": "Következő emlék", "no": "Nem", "no_albums_message": "Hozzon létre új albumot a fotói és videói rendszerezéséhez", + "no_albums_with_name_yet": "Úgy tűnik, hogy nincs még ilyen névvel album.", + "no_albums_yet": "Úgy tűnik, hogy még nem lett album létrehozva.", "no_archived_assets_message": "Archiváljon fényképeket és videókat, hogy elrejtse azokat a Fényképek nézetből", "no_assets_message": "KATTINTSON AZ ELSŐ FÉNYKÉPE FELTÖLTÉSÉHEZ", - "no_exif_info_available": "", + "no_duplicates_found": "Duplikátumok nem találhatók.", + "no_exif_info_available": "Exif információ nem elérhető", "no_explore_results_message": "Töltsön fel több fényképet, hogy felfedezze a gyűjteményét.", "no_favorites_message": "Jelöljön meg kedvenceket, hogy gyorsan megtalálhassa legjobb fényképeit és videóit", "no_libraries_message": "Hozzon létre külső képtárat a fényképei és videói megtekintéséhez", "no_name": "Nincs Név", - "no_places": "", + "no_places": "Nincsenek helyek", "no_results": "Nincsenek eredmények", + "no_results_description": "Próbáljon egy szinonimát, vagy fogalmazzon általánosabban", "no_shared_albums_message": "Hozzon létre egy új albumot, hogy megoszthassa fényképeit és videóit másokkal", "not_in_any_album": "Nincs albumban", + "note_apply_storage_label_to_previously_uploaded assets": "Megjegyzés: hogy a Tárhelycímkézést végrehajtódjon a korábban feltöltött elemeken, futtassa a", + "note_unlimited_quota": "Megjegyzés: Írjon 0-t végtelen kvótához", "notes": "Jegyzetek", "notification_toggle_setting_description": "Emailes értesítések engedélyezése", "notifications": "Értesítések", "notifications_setting_description": "Értesítések kezelése", "oauth": "OAuth", "offline": "Offline", + "offline_paths": "Offline útvonalak", + "offline_paths_description": "Ezeket az eredményeket okozhatja a külső könyvtárhoz nem tartozó fájlok manuális törlése.", "ok": "Rendben", "oldest_first": "Legrégebbi először", + "onboarding": "Első lépések", + "onboarding_privacy_description": "Az alábbi (nem kötelező) szolgáltatások külső szolgáltatásokon alapulnak, és bármikor kikapcsolhatóak az adminisztrációs beállításokban.", + "onboarding_theme_description": "Válasszon egy színt az alkalmazásnak. Ezt bármikor megváltoztathatja a beállításokban.", + "onboarding_welcome_description": "Állítsunk be néhány gyakori beállítást.", + "onboarding_welcome_user": "Üdvözlöm, {user}", "online": "Online", "only_favorites": "Csak kedvencek", "only_refreshes_modified_files": "Csak a megváltoztatott fájlokat frissíti", - "open_the_search_filters": "", + "open_in_map_view": "Megnyitás térkép nézetben", + "open_in_openstreetmap": "Megnyitás OpenStreetMap-ben", + "open_the_search_filters": "Keresési szűrők megnyitása", "options": "Beállítások", + "or": "vagy", "organize_your_library": "Rendszerezze képtárát", + "original": "eredeti", "other": "Egyéb", "other_devices": "Egyéb eszközök", "other_variables": "Egyéb változók", "owned": "Tulajdonos", "owner": "Tulajdonos", + "partner": "Partner", + "partner_can_access": "{partner} hozzáférhet", + "partner_can_access_assets": "Minden fényképe és videója, kivéve amik archiválásra vagy törlésre kerültek", + "partner_can_access_location": "A fényképei készítési helye", "partner_sharing": "Társmegosztás", "partners": "Társak", "password": "Jelszó", - "password_does_not_match": "", - "password_required": "", - "password_reset_success": "", + "password_does_not_match": "Jelszavak nem egyeznek", + "password_required": "Jelszó szükséges", + "password_reset_success": "Jelszóvisszaállítás sikeres", "past_durations": { - "days": "", - "hours": "", - "years": "" + "days": "{days, plural, one {Tegnap} other {Elmúlt # nap}}", + "hours": "{hours, plural, one {Előző óra} other {Elmúlt # óra}}", + "years": "{years, plural, one {Tavaly} other {Elmúlt # év}}" }, "path": "Útvonal", "pattern": "Minta", "pause": "Szüneteltetés", "pause_memories": "Emlékek szüneteltetése", "paused": "Szüneteltetve", - "pending": "", + "pending": "Folyamatban lévő", "people": "Személyek", - "people_sidebar_description": "", + "people_edits_count": "{count, plural, other {# személy}} szerkesztve", + "people_sidebar_description": "Jelenítsen meg linket a Személyek fülhöz oldalt", "perform_library_tasks": "", - "permanent_deletion_warning": "", - "permanent_deletion_warning_setting_description": "", + "permanent_deletion_warning": "Figyelmeztetés végleges törlésről", + "permanent_deletion_warning_setting_description": "Figyelmeztessen fájlok végleges törlése előtt", "permanently_delete": "Végleges törlés", - "permanently_deleted_asset": "", + "permanently_delete_assets_count": "{count, plural, one {Elem} other {Elemek}} végleges törlése", + "permanently_delete_assets_prompt": "Biztos, hogy véglegesen törölni szeretné ezt {count, plural, one {az elemet?} other {a(z) # elemet?}} Ez el fogja távolítani az albumokból, amikben {count, plural, one {szerepel} other {szerepelnek}}.", + "permanently_deleted_asset": "Elem véglegesen törölve", + "permanently_deleted_assets_count": "{count, plural, other {# elem}} véglegesen törölve", + "person": "Személy", + "person_hidden": "{name}{hidden, select, true { (rejtett)} other {}}", + "photo_shared_all_users": "Mindenkivel megosztotta a fényképeit, vagy nincs senki, akivel meg tudná osztani.", "photos": "Képek", + "photos_and_videos": "Fényképek és videók", "photos_count": "{count, plural, one {{count, number} Fotó} other {{count, number} Fotó}}", "photos_from_previous_years": "Képek előző évekből", - "pick_a_location": "", + "pick_a_location": "Válasszon egy helyet", "place": "Hely", "places": "Helyek", "play": "Lejátszás", "play_memories": "Emlékek lejátszása", "play_motion_photo": "Mozgókép lejátszása", - "play_or_pause_video": "", + "play_or_pause_video": "Videó elindítása vagy megállítása", "point": "", "port": "Port", "preset": "Sablon", @@ -839,99 +967,193 @@ "previous_memory": "Előző emlék", "previous_or_next_photo": "Előző vagy következő fotó", "primary": "Elsődleges", - "profile_picture_set": "", + "privacy": "Magánszféra", + "profile_image_of_user": "{user} profilképe", + "profile_picture_set": "Profilkép beállítva.", + "public_album": "Publikus album", "public_share": "Nyilvános Megosztás", + "purchase_account_info": "Támogató", + "purchase_activated_subtitle": "Köszönjük, hogy támogatja az Immich-et és a nyílt forráskódú programokat", + "purchase_activated_time": "Aktiválva ekkor: {date, date}", + "purchase_activated_title": "Kulcs sikeresen aktiválva", + "purchase_button_activate": "Aktiválás", + "purchase_button_buy": "Vásárlás", + "purchase_button_buy_immich": "Vásárolja meg az Immich-et", + "purchase_button_never_show_again": "Soha többé ne mutassa", + "purchase_button_reminder": "Emlékeztessen 30 nap múlva", + "purchase_button_remove_key": "Kulcs eltávolítása", + "purchase_button_select": "Kiválasztás", + "purchase_failed_activation": "Aktiválás sikertelen! Ellenőrizze az e-mailjét a helyes termékkulcsért!", + "purchase_individual_description_1": "Magánszemélynek", + "purchase_individual_description_2": "Támogató állapot", + "purchase_individual_title": "Magánszemély", + "purchase_input_suggestion": "Van termékkulcsa? Adja meg a kulcsot alább", + "purchase_license_subtitle": "Vásárolja meg az Immich-et, hogy támogassa a szolgáltatás fejlesztését a jövőben is", + "purchase_lifetime_description": "Élethosszú vásárlás", + "purchase_option_title": "VÁSÁRLÁSI LEHETŐSÉGEK", + "purchase_panel_info_1": "Az Immich készítése sok időt és erőfeszítést igényel, és teljes munkaidőben foglalkoztatunk szoftvermérnököket hogy olyan jóvá tegyük, amennyire csak lehet. Küldetésünk, hogy a nyílt forráskódú szoftver és etikus üzleti gyakorlat fenntartható bevételi forrás legyen a fejlesztőinknek, és egy magánszférát tiszteletben tartó ökoszisztéma készítése, amely valódi alternatívát nyújt a felhasználókat kihasználó felhőszolgáltatásoknak.", + "purchase_panel_info_2": "Mivel elkötelezettek vagyunk, hogy nem zárunk fizetés mögé szolgáltatásokat, ez a vásárlás az Immich semmilyen új részét nem oldja fel. Olyan felhasználóktól, mint Öntől, függünk, hogy az Immich-et tudjuk fejleszteni.", + "purchase_panel_title": "Támogassa a projektet", + "purchase_per_server": "Szerverenként", + "purchase_per_user": "Felhasználónként", + "purchase_remove_product_key": "Termékkulcs eltávolítása", + "purchase_remove_product_key_prompt": "Biztosan el szeretné távolítani a termékkulcsot?", + "purchase_remove_server_product_key": "Szerver termékkulcs eltávolítása", + "purchase_remove_server_product_key_prompt": "Biztosan el szeretné távolítani a szerver termékkulcsot?", + "purchase_server_description_1": "Az egész szerverre", + "purchase_server_description_2": "Támogító állapot", + "purchase_server_title": "Szerver", + "purchase_settings_server_activated": "A szerver termékkulcsot az admin menedzseli", "range": "", + "rating": "Értékelés csillagokkal", + "rating_description": "Exif értékelés megjelenítése az infópanelben", "raw": "", - "reaction_options": "", - "read_changelog": "", + "reaction_options": "Reakció lehetőségek", + "read_changelog": "Változtatások olvasása", + "reassign": "Áthelyezés", + "reassigned_assets_to_existing_person": "{count, plural, other {# elem}} áthelyezve {name, select, null {egy létező személyhez} other {{name}}}", + "reassigned_assets_to_new_person": "{count, plural, other {# elem}} áthelyezve egy új személyhez", + "reassing_hint": "Kijelölt média hozzáadása létező emberhez", "recent": "Friss", "recent_searches": "Friss keresések", "refresh": "Frissítés", + "refresh_encoded_videos": "Elkódolt videók frissítése", + "refresh_metadata": "Metaadatok frissítése", + "refresh_thumbnails": "Előnézetek frissítése", "refreshed": "Frissítve", - "refreshes_every_file": "", + "refreshes_every_file": "Minden fájl frissítése", + "refreshing_encoded_video": "Elkódolt videók frissítése", + "refreshing_metadata": "Metaadatok frissítése", + "regenerating_thumbnails": "Előnézetek újragenerálása", "remove": "Eltávolítás", + "remove_assets_album_confirmation": "Biztosan szeretne eltávolítani {count, plural, one {# elemet} other {# elemet}} az albumból?", + "remove_assets_shared_link_confirmation": "Biztosan szeretne eltávolítani {count, plural, one {# elemet} other {# elemet}} ebből a megosztott linkből?", + "remove_assets_title": "Elemek eltávolítása?", + "remove_custom_date_range": "Szabadon megadott időintervallum eltávolítása", "remove_from_album": "Eltávolítás az albumból", "remove_from_favorites": "Eltávolítás a kedvencekből", "remove_from_shared_link": "Eltávolítás a megosztott linkből", "remove_offline_files": "Offline Fájlok Eltávolítása", + "remove_user": "Felhasználó eltávolítása", + "removed_api_key": "API Kulcs eltávolítva: {name}", + "removed_from_archive": "Archívumból eltávolítva", + "removed_from_favorites": "Kedvencekből eltávolítva", + "removed_from_favorites_count": "A kedvencekből el lett távolítva {count, plural, other {# elem}}", + "rename": "Átnevezés", "repair": "Javítás", - "repair_no_results_message": "", + "repair_no_results_message": "Nem megfigyelt és hiányzó fájlok itt jelennek meg", "replace_with_upload": "Csere feltöltéssel", - "require_password": "", + "repository": "Adattár", + "require_password": "Jelszó szükségessé tétele", + "require_user_to_change_password_on_first_login": "Felhasználó első bejelentkezéskor való jelszóváltoztatásának szükségessé tétele", "reset": "Visszaállítás", "reset_password": "Jelszó visszaállítása", - "reset_people_visibility": "", + "reset_people_visibility": "Emberek láthatóságának visszaállítása", "reset_settings_to_default": "", + "reset_to_default": "Visszaállítás alapállapotba", + "resolve_duplicates": "Duplikátumok feloldása", + "resolved_all_duplicates": "Minden duplikátum feloldása", "restore": "Visszaállít", + "restore_all": "Minden visszaállítása", "restore_user": "Felhasználó visszaállítása", - "retry_upload": "", - "review_duplicates": "", + "restored_asset": "Elem visszaállítása", + "resume": "Folytatás", + "retry_upload": "Feltöltés újrapróbálása", + "review_duplicates": "Megegyező elemek átnézése", "role": "Szerep", + "role_editor": "Szerkesztő", + "role_viewer": "Néző", "save": "Mentés", - "saved_profile": "", - "saved_settings": "", + "saved_api_key": "API Kulcs elmentve", + "saved_profile": "Profil elmentve", + "saved_settings": "Beállítások elmentve", "say_something": "Szólj hozzá", - "scan_all_libraries": "", - "scan_all_library_files": "", - "scan_new_library_files": "", - "scan_settings": "", + "scan_all_libraries": "Minden könyvtár átnézése", + "scan_all_library_files": "Minden könyvtárbeli elem újraellenőrzése", + "scan_new_library_files": "Ellenőrzés új könyvtárbeli elemekért", + "scan_settings": "Felfedezési beállítások", "search": "Keresés", "search_albums": "Albumok keresése", - "search_by_context": "", - "search_camera_make": "", - "search_camera_model": "", - "search_city": "", - "search_country": "", - "search_for_existing_person": "", - "search_people": "", - "search_places": "", - "search_state": "", - "search_timezone": "", - "search_type": "", + "search_by_context": "Keresés kontextus alapján", + "search_by_filename": "Keresés fájlnév vagy kiterjesztés alapján", + "search_by_filename_example": "például IMG_1234.JPG vagy PNG", + "search_camera_make": "Kameragyártó keresése...", + "search_camera_model": "Kameramodell keresése...", + "search_city": "Város keresése...", + "search_country": "Ország keresése...", + "search_for_existing_person": "Már meglévő személy keresése", + "search_no_people": "Nincs személy", + "search_no_people_named": "Nincs személy \"{name}\" néven", + "search_people": "Személyek keresése", + "search_places": "Helyek keresése", + "search_state": "Régió keresése...", + "search_timezone": "Időzóna keresése...", + "search_type": "Típus keresése", "search_your_photos": "Fotók keresése", "searching_locales": "", "second": "Másodperc", - "select_album_cover": "", + "see_all_people": "Minden személy megtekintése", + "select_album_cover": "Albumborító kiválasztása", "select_all": "Összes kijelölése", - "select_avatar_color": "", - "select_face": "", - "select_featured_photo": "", - "select_library_owner": "", - "select_new_face": "", + "select_all_duplicates": "Minden duplikátum kiválasztása", + "select_avatar_color": "Avatár színének választása", + "select_face": "Arc kiválasztása", + "select_featured_photo": "Kijelölt fénykép kiválasztása", + "select_from_computer": "Kiválasztás számítógépről", + "select_keep_all": "Minden megtartása", + "select_library_owner": "Könyvtártulajdonos kijelölése", + "select_new_face": "Új arc kiválasztása", "select_photos": "Fotók választása", + "select_trash_all": "Minden szemétbe helyezése", "selected": "Kijelölt", - "send_message": "", + "selected_count": "{count, plural, other {# kiválasztva}}", + "send_message": "Üzenet küldése", + "send_welcome_email": "Üdvözlő üzenet küldése", "server": "Szerver", - "server_stats": "", - "set": "", - "set_as_album_cover": "", - "set_as_profile_picture": "", - "set_date_of_birth": "", - "set_profile_picture": "", - "set_slideshow_to_fullscreen": "", + "server_offline": "Szerver Nem Elérhető", + "server_online": "Szerver Elérhető", + "server_stats": "Szerver Statisztikák", + "server_version": "Szerver Verzió", + "set": "Beállítás", + "set_as_album_cover": "Beállítás albumborítóként", + "set_as_profile_picture": "Beállítás profilképként", + "set_date_of_birth": "Születési dátum beállítása", + "set_profile_picture": "Profilkép beállítása", + "set_slideshow_to_fullscreen": "Diavetítés teljes képernyőre állítása", "settings": "Beállítások", - "settings_saved": "", + "settings_saved": "Beállítások mentve", "share": "Megosztás", "shared": "Megosztva", - "shared_by": "", - "shared_by_you": "", + "shared_by": "Megosztva általa:", + "shared_by_user": "Megosztva {user} által", + "shared_by_you": "Megosztva Ön által", + "shared_from_partner": "Fényképek {partner}-tól/től", + "shared_link_options": "Megosztott link beállítások", "shared_links": "Megosztott Linkek", + "shared_photos_and_videos_count": "{assetCount, plural, other {# megosztott kép és videó.}}", + "shared_with_partner": "Megosztva vele: {partner}", "sharing": "Megosztás", - "sharing_sidebar_description": "", - "show_album_options": "", - "show_file_location": "", - "show_gallery": "", - "show_hidden_people": "", - "show_in_timeline": "", - "show_in_timeline_setting_description": "", - "show_keyboard_shortcuts": "", + "sharing_enter_password": "Jelszó megadása szükséges az oldal megtekintéséhez.", + "sharing_sidebar_description": "Jelenítsen meg linket a Megosztás fülhöz oldalt", + "shift_to_permanent_delete": "nyomja meg a ⇧-t hogy véglegesen törölje az elemet", + "show_album_options": "Albummegjelenítési beállítások", + "show_albums": "Albumok megtekintése", + "show_all_people": "Minden személy megjelenítése", + "show_and_hide_people": "Személyek megjelenítése és elrejtése", + "show_file_location": "Fájl helyének megjelenítése", + "show_gallery": "Galéria megjelenítése", + "show_hidden_people": "Rejtett személyek megjelenítése", + "show_in_timeline": "Megjelenítés az idővonalon", + "show_in_timeline_setting_description": "Ettől a felhasználótól származó képek és videók megjelenítése az Ön idővonalán", + "show_keyboard_shortcuts": "Billentyűparancsok megjelenítése", "show_metadata": "Metaadatok mutatása", "show_or_hide_info": "Info mutatása vagy elrejtése", "show_password": "Jelszó mutatása", "show_person_options": "Személy opciók mutatása", - "show_progress_bar": "", + "show_progress_bar": "Haladás megjelenítése", "show_search_options": "Keresési opciók mutatása", + "show_supporter_badge": "Támogató jelvény", + "show_supporter_badge_description": "Támogató jelvény megjelenítése", "shuffle": "Keverés", "sign_out": "Kilépés", "sign_up": "Feliratkozás", @@ -940,61 +1162,99 @@ "slideshow": "Diavetítés", "slideshow_settings": "Diavetítés beállításai", "sort_albums_by": "Albumok rendezése...", + "sort_created": "Létrehozva", + "sort_items": "Elemek száma", + "sort_modified": "Módosítva", + "sort_oldest": "Legrégebbi fénykép", + "sort_recent": "Legújabb fénykép", + "sort_title": "Cím", + "source": "Forrás", "stack": "Fotók csoportosítása", - "stack_selected_photos": "", - "stacktrace": "", - "start_date": "", + "stack_duplicates": "Duplikátumok csoportosítása", + "stack_select_one_photo": "Fő fénykép kiválasztása", + "stack_selected_photos": "Kiválasztott fényképek csoportosítása", + "stacked_assets_count": "{count, plural, other {# elem}} csoportba helyezve", + "stacktrace": "Stacktrace", + "start": "Kezdet", + "start_date": "Kezdet", "state": "Állam", "status": "Állapot", "stop_motion_photo": "Mozgókép megállítása", "stop_photo_sharing": "Fotók megosztásának megszűntetése?", - "storage": "", - "storage_label": "", + "stop_photo_sharing_description": "{partner} mostantól nem fog tudni hozzáférni az Ön fényképeihez.", + "stop_sharing_photos_with_user": "Fényképek megosztásának abbahagyása ezzel a felhasználóval", + "storage": "Tárhely", + "storage_label": "Tárolási címke", "storage_usage": "{used}/{available} használatban", - "submit": "", + "submit": "Beadás", "suggestions": "Javaslatok", - "sunrise_on_the_beach": "", - "swap_merge_direction": "", + "sunrise_on_the_beach": "Napkelte a tengerparton", + "swap_merge_direction": "Egyesítés irányának megfordítása", "sync": "Szinkronizálás", "template": "Minta", "theme": "Téma", "theme_selection": "Témaválasztás", "theme_selection_description": "A böngésző beállításának megfelelően automatikusan használjon világos vagy sötét témát", - "time_based_memories": "", + "they_will_be_merged_together": "Egyesítve lesznek", + "time_based_memories": "Emlékek idő alapján", "timezone": "Időzóna", "to_archive": "Archívum", + "to_change_password": "Jelszó megváltoztatása", "to_favorite": "Kedvenc", + "to_login": "Bejelentkezés", + "to_trash": "Szemétbe helyezés", "toggle_settings": "Beállítások változtatása", "toggle_theme": "Témaváltás", "toggle_visibility": "Láthatóság változtatása", "total_usage": "Összesen használatban", "trash": "Lomtár", "trash_all": "Mindet lomtárba", + "trash_count": "{count, number} elem szemétbe helyezése", + "trash_delete_asset": "Elem szemétbe helyezése / törlése", "trash_no_results_message": "Itt lesznek láthatóak a lomtárba tett képek és videok.", - "type": "", + "trashed_items_will_be_permanently_deleted_after": "A szemeteskosárban lévő elemek véglegesen törlésre kerülnek {days, plural, other {# nap}} múlva.", + "type": "Típus", "unarchive": "Archívumból kivétel", "unarchived": "Archívumból kivett", + "unarchived_count": "{count, plural, other {# elem kivéve az archívumból}}", "unfavorite": "Nem Kedvenc", "unhide_person": "Nem rejtett személy", "unknown": "Ismeretlen", "unknown_album": "Ismeretlen Album", "unknown_year": "Ismeretlen év", "unlimited": "Korlátlan", - "unlink_oauth": "", - "unlinked_oauth_account": "", + "unlink_oauth": "OAuth leválasztása", + "unlinked_oauth_account": "Leválasztott OAuth felhasználó", "unnamed_album": "Névtelen Album", "unnamed_share": "Névtelen Megosztás", + "unsaved_change": "Mentés nélküli változtatás", "unselect_all": "Összes kiválasztás törlése", + "unselect_all_duplicates": "Duplikátumok kijelölésének megszüntetése", "unstack": "Csoport Megszűntetése", + "unstacked_assets_count": "{count, plural, other {# elemből}} álló csoport szétszedve", + "untracked_files": "Nem megfigyelt fájlok", + "untracked_files_decription": "Ezek a fájlok nincsenek az alkalmazás által megfigyelve. Létrehozódhattak sikertelen mozgatástól, félbeszakított feltöltéstől, vagy hátrahagyva hiba miatt", "up_next": "Következik", "updated_password": "Jelszó megváltoztatva", "upload": "Feltöltés", "upload_concurrency": "", - "url": "", - "usage": "", + "upload_errors": "Feltöltés befejezve {count, plural, other {# hibával}}, frissítse az oldalt az újonnan feltöltött elemek megtekintéséhez.", + "upload_progress": "Hátra van {remaining, number} - Feldolgozva {processed, number}/{total, number}", + "upload_skipped_duplicates": "{count, plural, other {# megegyező elem}} kihagyva", + "upload_status_duplicates": "Duplikátumok", + "upload_status_errors": "Hibák", + "upload_status_uploaded": "Feltöltve", + "upload_success": "Feltöltés sikeres, frissítse az oldalt az újonnan feltöltött elemek megtekintéséhez.", + "url": "URL", + "usage": "Felhasználás", + "use_custom_date_range": "Szabadon megadott időintervallum használata", "user": "Felhasználó", "user_id": "Felhasználó azonosítója", - "user_usage_detail": "", + "user_liked": "{type, select, photo {ez a fénykép} video {ez a videó} other {ez}} tetszik neki: {user}", + "user_purchase_settings": "Megvásárlás", + "user_purchase_settings_description": "Vásárlás kezelése", + "user_role_set": "{user} beállítása {role} szerepbe", + "user_usage_detail": "Felhasználó használati adatai", "username": "Felhasználónév", "users": "Felhasználók", "utilities": "Eszközök", @@ -1006,15 +1266,21 @@ "video_hover_setting_description": "Ha az egér a bélyegkép felett időzik, a bélyegkép videó lejátszása induljon el. A lejátszás az indítás ikon feletti időzéssel akkor is elindul, ha ez az opció ki van kapcsolva.", "videos": "Videók", "videos_count": "{count, plural, one {# Videó} other {# Videó}}", + "view": "Nézet", + "view_album": "Album megtekintése", "view_all": "Összes mutatása", - "view_all_users": "", - "view_links": "", - "view_next_asset": "", - "view_previous_asset": "", + "view_all_users": "Minden felhasználó megtekintése", + "view_links": "Linkek megtekintése", + "view_next_asset": "Következő elem megtekintése", + "view_previous_asset": "Előző elem megtekintése", + "view_stack": "Csoport megtekintése", "viewer": "", - "waiting": "", - "week": "", - "welcome_to_immich": "", + "visibility_changed": "Láthatóság megváltozott {count, plural, other {# személy}} számára", + "waiting": "Várakozás", + "warning": "Figyelmeztetés", + "week": "Hét", + "welcome": "Üdv", + "welcome_to_immich": "Üdvözöljük az Immich-ben", "year": "Év", "years_ago": "{years, plural, one {# évvel} other {# évvel}} ezelőtt", "yes": "Igen", diff --git a/web/src/lib/i18n/id.json b/web/src/lib/i18n/id.json index ac29e27ec38cf..1a525485ea05b 100644 --- a/web/src/lib/i18n/id.json +++ b/web/src/lib/i18n/id.json @@ -25,7 +25,7 @@ "add_to_shared_album": "Tambahkan ke album terbagi", "added_to_archive": "Ditambahkan ke arsip", "added_to_favorites": "Ditambahkan ke favorit", - "added_to_favorites_count": "Ditambahkan {count} ke favorit", + "added_to_favorites_count": "Ditambahkan {count, number} ke favorit", "admin": { "add_exclusion_pattern_description": "Tambahkan pola pengecualian. Glob menggunakan *, **, dan ? didukung. Untuk mengabaikan semua berkas dalam direktori apa pun bernama \"Raw\", gunakan \"**/Raw/**\". Untuk mengabaikan semua berkas berakhiran dengan \".tif\", gunakan \"**/*.tif\". Untuk mengabaikan jalur absolut, gunakan \"/jalur/untuk/diabaikan/**\".", "authentication_settings": "Pengaturan Autentikasi", @@ -127,12 +127,13 @@ "map_enable_description": "Aktifkan fitur peta", "map_gps_settings": "Pengaturan Peta & GPS", "map_gps_settings_description": "Kelola Pengaturan Peta & GPS (Pengodean Geografis Terbalik)", + "map_implications": "Fitur peta mengandalkan layanan tile eksternal", "map_light_style": "Gaya terang", "map_manage_reverse_geocoding_settings": "Kelola settingan Pengodean Geografis Terbalik", "map_reverse_geocoding": "Pengodean Geografis Terbalik", "map_reverse_geocoding_enable_description": "Aktifkan pengodean geografis terbalik", "map_reverse_geocoding_settings": "Pengaturan Pengodean Geografis Terbalik", - "map_settings": "Pengaturan Peta", + "map_settings": "Peta", "map_settings_description": "Kelola pengaturan peta", "map_style_description": "URL ke tema peta style.json", "metadata_extraction_job": "Ekstrak metadata", @@ -275,7 +276,7 @@ "transcoding_preferred_hardware_device": "Perangkat keras yang lebih disukai", "transcoding_preferred_hardware_device_description": "Hanya diterapkan pada VAAPI dan QSV. Menetapkan node dri yang digunakan untuk transkode perangkat keras.", "transcoding_preset_preset": "Prasetel (-preset)", - "transcoding_preset_preset_description": "Kecepatan pengompresan. Prasetel lebih lambat membuat berkas lebih kecil dan meningkatkan kualitas ketika menargetkan kecepatan bit tertentu. VP9 mengabaikan kecepatan di atas `faster`.", + "transcoding_preset_preset_description": "Kecepatan kompresi. Pra setel yang lebih lambat membuat berkas lebih kecil dan meningkatkan kualitas ketika menargetkan kecepatan bit tertentu. VP9 mengabaikan kecepatan di atas `faster`.", "transcoding_reference_frames": "Bingkai referensi", "transcoding_reference_frames_description": "Jumlah bingkai untuk direferensikan ketika mengompres bingkai tertentu. Nilai lebih tinggi meningkatkan efisiensi kompresi, tetapi membuat pengodean lambat. 0 menetapkan nilai ini secara otomatis.", "transcoding_required_description": "Hanya video dalam format yang tidak diterima", @@ -317,7 +318,8 @@ "user_settings": "Pengaturan Pengguna", "user_settings_description": "Kelola pengaturan pengguna", "user_successfully_removed": "Pengguna {email} berhasil dikeluarkan.", - "version_check_enabled_description": "Aktifkan permintaan berkala ke GitHub untuk memeriksa rilis baru", + "version_check_enabled_description": "Aktifkan pemeriksaan versi", + "version_check_implications": "Fitur pemeriksaan versi tergantung komunikasi berkala dengan github.com", "version_check_settings": "Pemeriksaan Versi", "version_check_settings_description": "Aktifkan/nonaktifkan notifikasi versi baru", "video_conversion_job": "Transkode video", @@ -333,7 +335,8 @@ "album_added": "Album ditambahkan", "album_added_notification_setting_description": "Terima notifikasi surel ketika Anda ditambahkan ke album terbagi", "album_cover_updated": "Kover album diperbarui", - "album_delete_confirmation": "Apakah Anda yakin ingin menghapus album {album}?\nJika album ini dibagikan, pengguna lain tidak akan dapat mengaksesnya lagi.", + "album_delete_confirmation": "Apakah Anda yakin ingin menghapus album {album}?", + "album_delete_confirmation_description": "Jika album ini dibagikan, pengguna lain tidak akan dapat mengaksesnya lagi.", "album_info_updated": "Info album diperbarui", "album_leave": "Tinggalkan album?", "album_leave_confirmation": "Apakah Anda yakin ingin keluar dari {album}?", @@ -357,6 +360,7 @@ "allow_edits": "Perbolehkan penyuntingan", "allow_public_user_to_download": "Perbolehkan pengguna publik untuk mengunduh", "allow_public_user_to_upload": "Perbolehkan pengguna publik untuk mengunggah", + "anti_clockwise": "Berlawanan arah jarum jam", "api_key": "Kunci API", "api_key_description": "Nilai ini hanya akan ditampilkan sekali. Pastikan untuk menyalin sebelum menutup jendela ini.", "api_key_empty": "Nama Kunci API Anda seharusnya jangan kosong", @@ -365,7 +369,7 @@ "appears_in": "Muncul dalam", "archive": "Arsip", "archive_or_unarchive_photo": "Arsipkan atau batalkan pengarsipan foto", - "archive_size": "Ukuran Arsip", + "archive_size": "Ukuran arsip", "archive_size_description": "Atur ukuran arsip untuk unduhan (dalam GiB)", "archived": "", "archived_count": "{count, plural, other {# terarsip}}", @@ -407,7 +411,7 @@ "bulk_delete_duplicates_confirmation": "Apakah Anda yakin ingin menghapus {count, plural, one {# aset duplikat} other {# aset duplikat}} secara bersamaan? Ini akan menjaga aset terbesar dari setiap kelompok dan menghapus semua duplikat lain secara permanen. Anda tidak dapat mengurungkan tindakan ini!", "bulk_keep_duplicates_confirmation": "Apakah Anda yakin ingin menyimpan {count, plural, one {# aset duplikat} other {# aset duplikat}}? Ini akan menyelesaikan semua kelompok duplikat tanpa menghapus apa pun.", "bulk_trash_duplicates_confirmation": "Apakah Anda yakin ingin membuang {count, plural, one {# aset duplikat} other {# aset duplikat}} secara bersamaan? Ini akan menyimpan aset terbesar dari setiap kelompok dan membuang semua duplikat lainnya.", - "buy": "Beli Lisensi", + "buy": "Beli Immich", "camera": "Kamera", "camera_brand": "Merek kamera", "camera_model": "Model kamera", @@ -435,8 +439,10 @@ "city": "Kota", "clear": "Hapus", "clear_all": "Hapus semua", + "clear_all_recent_searches": "Hapus semua pencarian terakhir", "clear_message": "Hapus pesan", "clear_value": "Hapus nilai", + "clockwise": "Searah jarum jam", "close": "Tutup", "collapse": "Tutup", "collapse_all": "Tutup Semua", @@ -513,6 +519,8 @@ "do_not_show_again": "Jangan tampilkan pesan ini lagi", "done": "Selesai", "download": "Unduh", + "download_include_embedded_motion_videos": "Video tersematkan", + "download_include_embedded_motion_videos_description": "Sertakan video yg tersematkan dalam foto gerak sebagai file terpisah", "download_settings": "Pengunduhan", "download_settings_description": "Kelola pengaturan berkaitan dengan pengunduhan aset", "downloading": "Mengunduh", @@ -538,7 +546,11 @@ "edit_title": "Sunting Judul", "edit_user": "Sunting pengguna", "edited": "Disunting", - "editor": "", + "editor": "Editor", + "editor_close_without_save_prompt": "Perubahan tidak akan di simpan", + "editor_close_without_save_title": "Tutup editor?", + "editor_crop_tool_h2_aspect_ratios": "Perbandingan aspek", + "editor_crop_tool_h2_rotation": "Rotasi", "email": "Surel", "empty_trash": "Kosongkan sampah", "empty_trash_confirmation": "Apakah Anda yakin ingin mengosongkan sampah? Ini akan menghapus semua aset dalam sampah secara permanen dari Immich.\nAnda tidak dapat mengurungkan tindakan ini!", @@ -564,6 +576,7 @@ "error_adding_users_to_album": "Terjadi kesalahan menambahkan pengguna ke album", "error_deleting_shared_user": "Terjadi eror menghapus pengguna terbagi", "error_downloading": "Terjadi eror mengunduh {filename}", + "error_hiding_buy_button": "Kesalahan menyembunyikan tombol beli", "error_removing_assets_from_album": "Terjadi eror menghapus aset dari album, lihat konsol untuk detail lebih lanjut", "error_selecting_all_assets": "Terjadi eror memilih semua aset", "exclusion_pattern_already_exists": "Pola pengecualian ini sudah ada.", @@ -574,6 +587,8 @@ "failed_to_get_people": "Gagal mendapatkan orang", "failed_to_load_asset": "Gagal membuka aset", "failed_to_load_assets": "Gagal membuka aset-aset", + "failed_to_load_people": "Gagal mengunggah orang", + "failed_to_remove_product_key": "Gagal menghapus kunci produk", "failed_to_stack_assets": "Gagal menumpuk aset", "failed_to_unstack_assets": "Gagal membatalkan penumpukan aset", "import_path_already_exists": "Jalur pengimporan ini sudah ada.", @@ -675,6 +690,7 @@ "expired": "Kedaluwarsa", "expires_date": "Kedaluwarsa pada {date}", "explore": "Jelajahi", + "explorer": "Jelajah", "export": "Ekspor", "export_as_json": "Ekspor sebagai JSON", "extension": "Ekstensi", @@ -693,6 +709,7 @@ "filter_people": "Saring orang", "find_them_fast": "Temukan dengan cepat berdasarkan nama dengan pencarian", "fix_incorrect_match": "Perbaiki pencocokan salah", + "folders": "Berkas", "force_re-scan_library_files": "Paksa Pindai Ulang Semua Berkas Pustaka", "forward": "Maju", "general": "Umum", @@ -716,7 +733,16 @@ "host": "Hos", "hour": "Jam", "image": "Gambar", - "image_alt_text_date": "pada {date}", + "image_alt_text_date": "{isVideo, select, true {Video} other {Image}} pada tanggal {date}", + "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} diambil oleh {person1} pada {date}", + "image_alt_text_date_2_people": "{isVideo, select, true {Video} other {Image}} diambil oleh {person1} dan {person2} pada {date}", + "image_alt_text_date_3_people": "{isVideo, select, true {Video} other {Image}} diambil oleh {person1}, {person2}, dan {person3} pada {date}", + "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Video} other {Image}} diambil oleh {person1}, {person2}, dan {additionalCount, number} lainnya pada {date}", + "image_alt_text_date_place": "{isVideo, select, true {Video} other {Image}} diambil di {city}, {country} pada {date}", + "image_alt_text_date_place_1_person": "{isVideo, select, true {Video} other {Image}} diambil di {city}, {country} oleh {person1} pada {date}", + "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} diambil di {city}, {country} oleh {person1} dan {person2} pada {date}", + "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} diambil di {city}, {country} oleh {person1}, {person2}, dan {person3} pada {date}", + "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} diambil di {city}, {country} oleh {person1}, {person2}, dan {additionalCount, number} lainnya pada {date}", "image_alt_text_people": "{count, plural, =1 {dengan {person1}} =2 {dengan {person1} dan {person2}} =3 {dengan {person1}, {person2}, dan {person3}} other {dengan {person1}, {person2}, dan {others, number} lainnya}}", "image_alt_text_place": "di {city}, {country}", "image_taken": "{isVideo, select, true {Video diambil} other {Gambar diambil}}", @@ -835,6 +861,7 @@ "name": "Nama", "name_or_nickname": "Nama atau nama panggilan", "never": "Tidak pernah", + "new_album": "Album baru", "new_api_key": "Kunci API Baru", "new_password": "Kata sandi baru", "new_person": "Orang baru", @@ -873,12 +900,14 @@ "ok": "Oke", "oldest_first": "Terlawas dahulu", "onboarding": "Memulai", + "onboarding_privacy_description": "Fitur berikut (opsional) bergantung pada layanan eksternal, dan dapat dinonaktifkan kapan saja di pengaturan administrasi.", "onboarding_theme_description": "Pilih tema warna untuk server Anda. Ini dapat diubah lagi dalam pengaturan Anda.", "onboarding_welcome_description": "Mari menyiapkan server Anda dengan beberapa pengaturan umum.", "onboarding_welcome_user": "Selamat datang, {user}", "online": "Daring", "only_favorites": "Hanya favorit", "only_refreshes_modified_files": "Hanya menyegarkan berkas yang diubah", + "open_in_map_view": "Buka dalam tampilan peta", "open_in_openstreetmap": "Buka di OpenStreetMap", "open_the_search_filters": "Buka saringan pencarian", "options": "Opsi", @@ -890,7 +919,7 @@ "other_variables": "Variabel lain", "owned": "Dimiliki", "owner": "Pemilik", - "partner": "Partner", + "partner": "Rekan", "partner_can_access": "{partner} dapat mengakses", "partner_can_access_assets": "Semua foto dan video Anda kecuali yang ada di Arsip dan Terhapus", "partner_can_access_location": "Lokasi di mana foto Anda diambil", @@ -943,10 +972,47 @@ "previous_memory": "Kenangan sebelumnya", "previous_or_next_photo": "Foto sebelumnya atau berikutnya", "primary": "Utama", + "privacy": "Privasi", "profile_image_of_user": "Foto profil dari {user}", "profile_picture_set": "Foto profil ditetapkan.", "public_album": "Album publik", "public_share": "Pembagian Publik", + "purchase_account_info": "Pendukung", + "purchase_activated_subtitle": "Terima kasih telah mendukung Immich dan perangkat lunak sumber terbuka", + "purchase_activated_time": "Di aktivasi pada {date, date}", + "purchase_activated_title": "Kunci kamu telah sukses di aktivasi", + "purchase_button_activate": "Aktifkan", + "purchase_button_buy": "Beli", + "purchase_button_buy_immich": "Beli Immich", + "purchase_button_never_show_again": "Jangan tampilkan lagi", + "purchase_button_reminder": "Ingatkan saya pada 30 hari lagi", + "purchase_button_remove_key": "Hapus kunci", + "purchase_button_select": "Pilih", + "purchase_failed_activation": "Gagal mengaktifkan! Silakan periksa email kamu untuk kunci produk yang benar!", + "purchase_individual_description_1": "Untuk perorangan", + "purchase_individual_description_2": "Status pendukung", + "purchase_individual_title": "Perorangan", + "purchase_input_suggestion": "Punya kunci produk? Masukkan kunci di bawah ini", + "purchase_license_subtitle": "Beli Immich untuk keberlangsungan pengembangan layanan", + "purchase_lifetime_description": "Pembayaran seumur hidup", + "purchase_option_title": "PILIHAN PEMBAYARAN", + "purchase_panel_info_1": "Membangun Immich membutuhkan banyak waktu dan upaya, dan kami memiliki insinyur penuh waktu yang bekerja untuk membuatnya sebaik mungkin. Misi kami adalah agar perangkat lunak sumber terbuka dan praktik bisnis yang beretika menjadi sumber pendapatan yang berkelanjutan bagi para pengembang dan menciptakan ekosistem yang menghargai privasi dengan alternatif nyata untuk layanan cloud yang eksploitatif.", + "purchase_panel_info_2": "Karena kami berkomitmen untuk tidak menambahkan paywall, pembelian ini tidak akan memberi kamu fitur tambahan apa pun di Immich. Kami mengandalkan pengguna seperti kamu untuk mendukung pengembangan Immich yang sedang berlangsung.", + "purchase_panel_title": "Dukung proyek ini", + "purchase_per_server": "Per server", + "purchase_per_user": "Per pengguna", + "purchase_remove_product_key": "Hapus Kunci Produk", + "purchase_remove_product_key_prompt": "Apakah kamu yakin ingin menghapus kunci produk?", + "purchase_remove_server_product_key": "Hapus kunci produk Server", + "purchase_remove_server_product_key_prompt": "Apakah kamu yakin ingin menghapus kunci produk Server?", + "purchase_server_description_1": "Untuk keseluruhan server", + "purchase_server_description_2": "Status pendukung", + "purchase_server_title": "Server", + "purchase_settings_server_activated": "Kunci produk server dikelola oleh admin", + "rating": "Peringkat bintang", + "rating_clear": "Hapus peringkat", + "rating_count": "{count, plural, one {# peringkat} other {# peringkat}}", + "rating_description": "Tampilkan peringkat exif pada panel info", "reaction_options": "Opsi reaksi", "read_changelog": "Baca Log Perubahan", "reassign": "Tetapkan ulang", @@ -989,6 +1055,7 @@ "reset_password": "Atur ulang kata sandi", "reset_people_visibility": "Atur ulang keterlihatan orang", "reset_to_default": "Atur ulang ke bawaan", + "resolve_duplicates": "Mengatasi duplikat", "resolved_all_duplicates": "Semua duplikat terselesaikan", "restore": "Pulihkan", "restore_all": "Pulihkan semua", @@ -1033,6 +1100,7 @@ "see_all_people": "Lihat semua orang", "select_album_cover": "Pilih kover album", "select_all": "Pilih semua", + "select_all_duplicates": "Pilih semua duplikat", "select_avatar_color": "Pilih warna avatar", "select_face": "Pilih wajah", "select_featured_photo": "Pilih foto terfitur", @@ -1065,6 +1133,7 @@ "shared_by_user": "Dibagikan oleh {user}", "shared_by_you": "Dibagikan oleh Anda", "shared_from_partner": "Foto dari {partner}", + "shared_link_options": "Pilihan tautan bersama", "shared_links": "Tautan terbagi", "shared_photos_and_videos_count": "{assetCount, plural, other {# foto & video terbagi.}}", "shared_with_partner": "Dibagikan dengan {partner}", @@ -1073,6 +1142,7 @@ "sharing_sidebar_description": "Tampilkan tautan ke Pembagian dalam bilah samping", "shift_to_permanent_delete": "tekan ⇧ untuk menghapus aset secara permanen", "show_album_options": "Tampilkan opsi album", + "show_albums": "Tampilkan album", "show_all_people": "Tampilkan semua orang", "show_and_hide_people": "Tampilkan & sembunyikan orang", "show_file_location": "Tampilkan lokasi berkas", @@ -1087,6 +1157,8 @@ "show_person_options": "Tampilkan opsi orang", "show_progress_bar": "Tampilkan Bilah Progres", "show_search_options": "Tampilkan opsi pencarian", + "show_supporter_badge": "Lencana suporter", + "show_supporter_badge_description": "Tampilkan lencana suporter", "shuffle": "Acak", "sign_out": "Keluar", "sign_up": "Daftar", @@ -1103,6 +1175,8 @@ "sort_title": "Judul", "source": "Sumber", "stack": "Tumpukan", + "stack_duplicates": "Stack duplikat", + "stack_select_one_photo": "Pilih satu foto utama untuk stack", "stack_selected_photos": "Tumpuk foto terpilih", "stacked_assets_count": "{count, plural, one {# aset} other {# aset}} ditumpuk", "stacktrace": "Jejak tumpukan", @@ -1135,12 +1209,12 @@ "to_login": "Log masuk", "to_trash": "Sampah", "toggle_settings": "Saklar pengaturan", - "toggle_theme": "Saklar tema", + "toggle_theme": "Beralih tema gelap", "toggle_visibility": "Saklar keterlihatan", "total_usage": "Jumlah penggunaan", "trash": "Sampah", "trash_all": "Buang Semua", - "trash_count": "Buang {count}", + "trash_count": "Sampah {count, number}", "trash_delete_asset": "Hapus Aset", "trash_no_results_message": "Foto dan video di sampah akan muncul di sini.", "trashed_items_will_be_permanently_deleted_after": "Item yang dibuang akan dihapus secara permanen setelah {days, plural, one {# hari} other {# hari}}.", @@ -1156,9 +1230,11 @@ "unlink_oauth": "Putuskan OAuth", "unlinked_oauth_account": "Akun OAuth terputus", "unnamed_album": "Album Tanpa Nama", + "unnamed_album_delete_confirmation": "Apakah kamu yakin akan menghapus album ini?", "unnamed_share": "Pembagian Tanpa Nama", "unsaved_change": "Perubahan belum disimpan", "unselect_all": "Batalkan semua pilihan", + "unselect_all_duplicates": "Batal pilih semua duplikat", "unstack": "Batalkan penumpukan", "unstacked_assets_count": "Penumpukan {count, plural, one {# aset} other {# aset}} dibatalkan", "untracked_files": "Berkas tidak dilacak", @@ -1168,7 +1244,7 @@ "upload": "Unggah", "upload_concurrency": "Konkurensi pengunggahan", "upload_errors": "Unggahan selesai dengan {count, plural, one {# eror} other {# eror}}, muat ulang laman untuk melihat aset terunggah baru.", - "upload_progress": "Tersisa {remaining} - Memproses {processed}/{total}", + "upload_progress": "Tersisa {remaining, number} - Di proses {processed, number}/{total, number}", "upload_skipped_duplicates": "Melewati {count, plural, one {# aset duplikat} other {# aset duplikat}}", "upload_status_duplicates": "Duplikat", "upload_status_errors": "Eror", @@ -1181,7 +1257,9 @@ "user_id": "ID Pengguna", "user_license_settings": "Lisensi", "user_license_settings_description": "Kelola lisensi Anda", - "user_liked": "{user} menyukai {type, select, photo {foto ini} video {video ini} asset {aset ini} other {ini}}", + "user_liked": "{user} menyukai {type, select, photo {foto ini} video {tayangan ini} asset {aset ini} other {ini}}", + "user_purchase_settings": "Pembelian", + "user_purchase_settings_description": "Atur pembelian kamu", "user_role_set": "Tetapkan {user} sebagai {role}", "user_usage_detail": "Detail penggunaan pengguna", "username": "Nama pengguna", diff --git a/web/src/lib/i18n/it.json b/web/src/lib/i18n/it.json index 86c0079e96eea..486f2dfaa620e 100644 --- a/web/src/lib/i18n/it.json +++ b/web/src/lib/i18n/it.json @@ -49,7 +49,7 @@ "external_library_created_at": "Libreria esterna (creata il {date})", "external_library_management": "Gestione Librerie Esterne", "face_detection": "Rilevamento Volti", - "face_detection_description": "Rileva i volti presenti negli assets utilizzando il machine learning. Per i video, viene presa in considerazione solo la miniatura. \"Tutto\" (ri-)processerà tutti gli assets. \"Mancanti\" selaziona solo gli assets che non sono ancora stati processati. I volti rilevati verranno selezionati per il riconoscimento facciale dopo che il rilevamento dei volti sarà stato completato, raggruppandoli in persone esistenti e/o nuove.", + "face_detection_description": "Rileva i volti presenti negli assets utilizzando il machine learning. Per i video, viene presa in considerazione solo la miniatura. \"Tutto\" (ri-)processerà tutti gli assets. \"Mancanti\" seleziona solo gli assets che non sono ancora stati processati. I volti rilevati verranno selezionati per il riconoscimento facciale dopo che il rilevamento dei volti sarà stato completato, raggruppandoli in persone esistenti e/o nuove.", "facial_recognition_job_description": "Raggruppa i volti rilevati in persone. Questo processo viene eseguito dopo che il rilevamento volti è stato completato. \"Tutti\" (ri-)unisce tutti i volti. \"Mancanti\" processa i volti che non hanno una persona assegnata.", "failed_job_command": "Il comando {command} è fallito per il processo: {job}", "force_delete_user_warning": "ATTENZIONE: Questo rimuoverà immediatamente l'utente e tutti i suoi assets. Non è possibile tornare indietro e i file non potranno essere recuperati.", @@ -68,7 +68,7 @@ "image_settings_description": "Gestisci qualità e risoluzione delle immagini generate", "image_thumbnail_format": "Formato miniatura", "image_thumbnail_resolution": "Risoluzione miniatura", - "image_thumbnail_resolution_description": "Utilizzato per vedere gruppi di foto (linea temporale,vista album, etc.). Risoluzioni piu' alte possono mantenere piu' dettaglio pero' l'encoding sara' piu' lungo, i file avranno dimensioni maggiori e potrebbero causare una riduzione nella responsivita' dell'applicazione.", + "image_thumbnail_resolution_description": "Utilizzato per vedere gruppi di foto (linea temporale, vista album, etc.). Risoluzioni più alte possono mantenere più dettaglio però l'encoding sarà più lungo, i file avranno dimensioni maggiori e potrebbero causare una riduzione nella responsività dell'applicazione.", "job_concurrency": "Concorrenza {job}", "job_not_concurrency_safe": "Questo processo non è eseguibile in maniera concorrente.", "job_settings": "Impostazioni dei processi", @@ -76,14 +76,14 @@ "job_status": "Stato Processi", "jobs_delayed": "{jobCount, plural, one {# posticipato} other {# posticipati}}", "jobs_failed": "{jobCount, plural, one {# fallito} other {# falliti}}", - "library_created": "Creata libreria {library}", + "library_created": "Creata libreria: {library}", "library_cron_expression": "Espressione cron", "library_cron_expression_description": "Imposta l'intervallo di rilevazione utilizzando il formato cron. Per più informazioni consulta es. Crontab Guru", "library_cron_expression_presets": "Espressioni cron preimpostate", "library_deleted": "Libreria eliminata", "library_import_path_description": "Specifica una cartella da importare. Questa cartella e le sue sottocartelle, verranno analizzate per cercare immagini e video.", "library_scanning": "Scansione periodica", - "library_scanning_description": "Conigura la scansione periodica della libreria", + "library_scanning_description": "Configura la scansione periodica della libreria", "library_scanning_enable_description": "Attiva la scansione periodica della libreria", "library_settings": "Libreria Esterna", "library_settings_description": "Gestisci le impostazioni della libreria esterna", @@ -95,7 +95,7 @@ "logging_level_description": "Quando attivato, che livello di log utilizzare.", "logging_settings": "Registro dei Log", "machine_learning_clip_model": "Modello CLIP", - "machine_learning_clip_model_description": "Il nome del modello CLIP mostrato qui. Bita cge devi rieseguire il processo 'Ricerca Intelligente' per tutte le immagini al cambio del modello.", + "machine_learning_clip_model_description": "Il nome del modello CLIP mostrato qui. Nota che devi rieseguire il processo 'Ricerca Intelligente' per tutte le immagini al cambio del modello.", "machine_learning_duplicate_detection": "Rilevamento Duplicati", "machine_learning_duplicate_detection_enabled": "Attiva rilevazione duplicati", "machine_learning_duplicate_detection_enabled_description": "Se disattivo, risorse perfettamente identiche saranno comunque deduplicate.", @@ -103,16 +103,16 @@ "machine_learning_enabled": "Attiva machine learning", "machine_learning_enabled_description": "Se disabilitato, tutte le funzioni di ML saranno disabilitate ignorando le importazioni sottostanti.", "machine_learning_facial_recognition": "Riconoscimento Facciale", - "machine_learning_facial_recognition_description": "Rileva, riconosci, e raggruppa faccie nelle immagini", + "machine_learning_facial_recognition_description": "Rileva, riconosci, e raggruppa facce nelle immagini", "machine_learning_facial_recognition_model": "Modello di riconoscimento facciale", - "machine_learning_facial_recognition_model_description": "I modelli sono mostrati in ordine decrescente in base alla dimensione. I modelli più grandi sono più lenti e utilizzano più memoria, peró producono risultati migliori. Nota che devi ri-eseguire il processo di rilevamento facciale per tutte le immagini quando cambi il modello.", + "machine_learning_facial_recognition_model_description": "I modelli sono mostrati in ordine decrescente in base alla dimensione. I modelli più grandi sono più lenti e utilizzano più memoria, però producono risultati migliori. Nota che devi ri-eseguire il processo di rilevamento facciale per tutte le immagini quando cambi il modello.", "machine_learning_facial_recognition_setting": "Attiva riconoscimento facciale", - "machine_learning_facial_recognition_setting_description": "Se disabilitato, le immagininon non saranno codificate per il riconoscimento facciale e non verranno mostrate nella sezione Persone della pagina Esplora.", + "machine_learning_facial_recognition_setting_description": "Se disabilitato, le immagini non saranno codificate per il riconoscimento facciale e non verranno mostrate nella sezione Persone della pagina Esplora.", "machine_learning_max_detection_distance": "Distanza massima di rilevazione", - "machine_learning_max_detection_distance_description": "Massima distanza fra due immagini per considerarle duplicate, variando da 0.001-0.1. Valori più alti rileveranno più duplicati, ma potrebbero causare risultati fasulli.", + "machine_learning_max_detection_distance_description": "Massima distanza fra due immagini per considerarle duplicate, variando da 0.001-0.1. Valori più alti rileveranno più duplicati, ma potrebbero causare falsi positivi.", "machine_learning_max_recognition_distance": "Distanza massima di riconoscimento", "machine_learning_max_recognition_distance_description": "La distanza massima tra due volti per essere considerati la stessa persona, che varia da 0 a 2. Abbassare questo valore può prevenire l'etichettatura di due persone come se fossero la stessa persona, mentre aumentarlo può prevenire l'etichettatura della stessa persona come se fossero due persone diverse. Nota che è più facile unire due persone che separare una persona in due, quindi è preferibile mantenere una soglia più bassa quando possibile.", - "machine_learning_min_detection_score": "Punteggio minimo di rilvazione", + "machine_learning_min_detection_score": "Punteggio minimo di rilevazione", "machine_learning_min_detection_score_description": "Punteggio di confidenza minimo per rilevare un volto, da 0 a 1. Valori più bassi rileveranno più volti, ma potrebbero generare risultati fasulli.", "machine_learning_min_recognized_faces": "Minimo volti rilevati", "machine_learning_min_recognized_faces_description": "Il numero minimo di volti riconosciuti per creare una persona. Aumentando questo valore si rende il riconoscimento facciale più preciso, ma aumenta la possibilità che un volto non venga assegnato a una persona.", @@ -129,7 +129,7 @@ "map_enable_description": "Abilita funzionalità della mappa", "map_gps_settings": "Impostazioni Mappe & GPS", "map_gps_settings_description": "Gestisci le impostazioni di Mappe & GPS (Geocoding Inverso)", - "map_implications": "La fnzione della mappa fa uso di un servizio tile esterno (tiles.immich.cloud)", + "map_implications": "La funzionalità mappa si basa su un servizio tile esterno (tiles.immich.cloud)", "map_light_style": "Tema chiaro", "map_manage_reverse_geocoding_settings": "Gestisci impostazioni Geocodifica inversa", "map_reverse_geocoding": "Geocodifica inversa", @@ -225,7 +225,7 @@ "storage_template_hash_verification_enabled_description": "Attiva verifica hash, non disabilitare questo se non sei certo delle implicazioni", "storage_template_migration": "Migrazione modello archiviazione", "storage_template_migration_description": "Applica il {template} attuale agli asset caricati in precedenza", - "storage_template_migration_info": "Le modifiche al modello di archiviazione verranno applicate solo agli asset nuovi. Per applicare le modifice retroattivamente esegui {job}.", + "storage_template_migration_info": "Le modifiche al modello di archiviazione verranno applicate solo agli asset nuovi. Per applicare le modifiche retroattivamente esegui {job}.", "storage_template_migration_job": "Processo Migrazione Modello di Archiviazione", "storage_template_more_details": "Per più informazioni riguardo a questa funzionalità, consulta il Modello Archiviazione e le sue conseguenze", "storage_template_onboarding_description": "Quando attivata, questa funzionalità organizzerà automaticamente i file utilizzando il modello di archiviazione definito dall'utente. Per ragioni di stabilità, questa funzionalità è disabilitata per impostazione predefinita. Per più informazioni, consulta la documentazione.", @@ -260,7 +260,7 @@ "transcoding_bitrate_description": "Video con bitrate superiore al massimo o in formato non accettato", "transcoding_codecs_learn_more": "Per saperne di più sulla terminologia utilizzata, fai riferimento alla documentazione di FFmpeg su codec H.264, codec HEVC e codec VP9.", "transcoding_constant_quality_mode": "Modalità qualità costante", - "transcoding_constant_quality_mode_description": "iCQ è migliore di CQP, peró alcuni dispositivi di accelerazione hardware non supportano questa modalità. Impostando questa opzione l'applicazione preferirà il modo specificato quando è in uso la codifica quality-based. Ignorato da NVENC perchè non supporta ICQ.", + "transcoding_constant_quality_mode_description": "iCQ è migliore di CQP, però alcuni dispositivi di accelerazione hardware non supportano questa modalità. Impostando questa opzione l'applicazione preferirà il modo specificato quando è in uso la codifica quality-based. Ignorato da NVENC perché non supporta ICQ.", "transcoding_constant_rate_factor": "Fattore di rateo costante (-crf)", "transcoding_constant_rate_factor_description": "Livello di qualità video. I valori tipici sono 23 per H.264, 28 per HEVC, 31 per VP9 e 35 per AV1. Un valore inferiore indica una qualità migliore, ma produce file di dimensioni maggiori.", "transcoding_disabled_description": "Non transcodificare alcun video, potrebbe rompere la riproduzione su alcuni client", @@ -274,12 +274,12 @@ "transcoding_max_bitrate": "Bitrate massimo", "transcoding_max_bitrate_description": "Impostare un bitrate massimo può rendere le dimensioni dei file più prevedibili a un costo minore per la qualità. A 720p, i valori tipici sono 2600k per VP9 o HEVC, o 4500k per H.264. Disabilitato se impostato su 0.", "transcoding_max_keyframe_interval": "Intervallo massimo dei keyframe", - "transcoding_max_keyframe_interval_description": "Imposta la distanza massima tra i keyframe. Valori più bassi peggiorano l'efficienza di compressione, peró migliorano i tempi di ricerca e possono migliorare la qualità nelle scene con movimenti rapidi. 0 imposta questo valore automaticamente.", + "transcoding_max_keyframe_interval_description": "Imposta la distanza massima tra i keyframe. Valori più bassi peggiorano l'efficienza di compressione, però migliorano i tempi di ricerca e possono migliorare la qualità nelle scene con movimenti rapidi. 0 imposta questo valore automaticamente.", "transcoding_optimal_description": "Video con risoluzione più alta rispetto alla risoluzione desiderata o in formato non accettato", "transcoding_preferred_hardware_device": "Dispositivo hardware preferito", "transcoding_preferred_hardware_device_description": "Si applica solo a VAAPI e QSV. Imposta il nodo DRI utilizzato per la transcodifica hardware.", "transcoding_preset_preset": "Preset (-preset)", - "transcoding_preset_preset_description": "Velocità di compressione. Presets più lenti producono file più piccoli e aumentano la qualità quando si punta a ottenere un certo bitrate. VP9 ignora velocità superiori a `faster`.", + "transcoding_preset_preset_description": "Velocità di compressione. Preset più lenti producono file più piccoli e aumentano la qualità quando viene impostato un certo bitrate. VP9 ignora velocità superiori a `faster`.", "transcoding_reference_frames": "Frame di riferimento", "transcoding_reference_frames_description": "Il numero di frame da prendere in considerazione nel comprimere un determinato frame. Valori più alti migliorano l'efficienza di compressione, ma rallentano la codifica. 0 imposta questo valore automaticamente.", "transcoding_required_description": "Solo video che non sono in un formato accettato", @@ -288,7 +288,7 @@ "transcoding_target_resolution": "Risoluzione desiderata", "transcoding_target_resolution_description": "Risoluzioni più elevate possono preservare più dettagli ma richiedono più tempo per la codifica, producono file di dimensioni maggiori e possono ridurre la reattività dell'applicazione.", "transcoding_temporal_aq": "AQ temporale", - "transcoding_temporal_aq_description": "Si applica solo a NVENC. Aumenta la qualita delle scene con molto dettaglio e poco movimento. Potrebbe non essere compatibile con dispositivi più vecchi.", + "transcoding_temporal_aq_description": "Si applica solo a NVENC. Aumenta la qualità delle scene con molto dettaglio e poco movimento. Potrebbe non essere compatibile con dispositivi più vecchi.", "transcoding_threads": "Thread", "transcoding_threads_description": "Valori più alti portano a una codifica più veloce, ma lasciano meno spazio al server per elaborare altre attività durante l'attività. Questo valore non dovrebbe essere superiore al numero di core CPU. Massimizza l'utilizzo se impostato su 0.", "transcoding_tone_mapping": "Mappatura della tonalità", @@ -310,14 +310,14 @@ "untracked_files_description": "Questi file non sono tracciati dall'applicazione. Potrebbero essere il risultato di spostamenti falliti, caricamenti interrotti o abbandonati a causa di un bug", "user_delete_delay": "L'account e gli asset dell'utente {user} verranno programmati per la cancellazione definitiva tra {delay, plural, one {# giorno} other {# giorni}}.", "user_delete_delay_settings": "Ritardo eliminazione", - "user_delete_delay_settings_description": "Numero di giorni dopo l'eliminazione per cancellare in modo definitivo l'account e gli asset di un utente. Il processo di cancellazione dell'utente viene eseguito a mezzanotte per verificare se esistono utenti pronti a essere eliminati. Le modifiche a questa impostazioni saranno prese in considerazione dalla possima esecuzione.", + "user_delete_delay_settings_description": "Numero di giorni dopo l'eliminazione per cancellare in modo definitivo l'account e gli asset di un utente. Il processo di cancellazione dell'utente viene eseguito a mezzanotte per verificare se esistono utenti pronti a essere eliminati. Le modifiche a questa impostazioni saranno prese in considerazione dalla prossima esecuzione.", "user_delete_immediately": "L'account e tutti gli asset dell'utente {user} verranno messi in coda per la cancellazione permanente immediata.", "user_delete_immediately_checkbox": "utente", "user_management": "Gestione Utenti", "user_password_has_been_reset": "La password dell'utente è stata reimpostata:", "user_password_reset_description": "Per favore inserisci una password temporanea per l'utente e informalo che dovrà cambiare la password al prossimo login.", "user_restore_description": "L'account di {user} verrà ripristinato.", - "user_restore_scheduled_removal": "Ripristina utente - rimozione progammata per il {date, date, long}", + "user_restore_scheduled_removal": "Ripristina utente - rimozione programmata per il {date, date, long}", "user_settings": "Impostazione Utente", "user_settings_description": "Gestisci impostazioni utente", "user_successfully_removed": "L'utente {email} è stato rimosso con successo.", @@ -362,6 +362,7 @@ "allow_edits": "Permetti modifiche", "allow_public_user_to_download": "Permetti di scaricare agli utenti pubblici", "allow_public_user_to_upload": "Permetti di caricare agli utenti pubblici", + "anti_clockwise": "Senso antiorario", "api_key": "Chiave API", "api_key_description": "Il campo verrà mostrato solo una volta. Abbi cura di copiarlo prima di chiudere la finestra.", "api_key_empty": "Il valore del nome dell'API Key non può essere vuoto", @@ -370,7 +371,7 @@ "appears_in": "Compare in", "archive": "Archivio", "archive_or_unarchive_photo": "Archivia o ripristina foto", - "archive_size": "Dimensioni Archivio", + "archive_size": "Dimensioni archivio", "archive_size_description": "Imposta le dimensioni dell'archivio per i download (in GiB)", "archived": "Archiviato", "archived_count": "{count, plural, other {Archiviati #}}", @@ -443,6 +444,7 @@ "clear_all_recent_searches": "Rimuovi tutte le ricerche recenti", "clear_message": "Pulisci messaggio", "clear_value": "Pulisci valore", + "clockwise": "Senso orario", "close": "Chiudi", "collapse": "Restringi", "collapse_all": "Comprimi tutto", @@ -455,7 +457,7 @@ "confirm_admin_password": "Conferma password amministratore", "confirm_delete_shared_link": "Sei sicuro di voler eliminare questo link condiviso?", "confirm_password": "Conferma password", - "contain": "Contieni", + "contain": "Adatta", "context": "Contesto", "continue": "Continua", "copied_image_to_clipboard": "Immagine copiata negli appunti.", @@ -468,7 +470,7 @@ "copy_password": "Copia password", "copy_to_clipboard": "Copia negli appunti", "country": "Nazione", - "cover": "Copri", + "cover": "Riempi", "covers": "Miniature", "create": "Crea", "create_album": "Crea album", @@ -519,8 +521,10 @@ "do_not_show_again": "Non mostrare questo messaggio di nuovo", "done": "Fatto", "download": "Scarica", + "download_include_embedded_motion_videos": "Video incorporati", + "download_include_embedded_motion_videos_description": "Includere i video incorporati nelle foto in movimento come file separato", "download_settings": "Scarica", - "download_settings_description": "Gestisci le impostazioni riguardandi il download degli asset", + "download_settings_description": "Gestisci le impostazioni relative al download degli asset", "downloading": "Scaricando", "downloading_asset_filename": "Scaricando l'asset {filename}", "drop_files_to_upload": "Rilascia i file ovunque per caricarli", @@ -552,6 +556,10 @@ "edit_user": "Modifica utente", "edited": "Modificato", "editor": "Editor", + "editor_close_without_save_prompt": "Le modifiche non verranno salvate", + "editor_close_without_save_title": "Vuoi chiudere l'editor?", + "editor_crop_tool_h2_aspect_ratios": "Proporzioni", + "editor_crop_tool_h2_rotation": "Rotazione", "email": "Email", "empty": "", "empty_album": "Album Vuoto", @@ -643,7 +651,7 @@ "unable_to_hide_person": "Impossibile nascondere persona", "unable_to_link_oauth_account": "Impossibile collegare l'account OAuth", "unable_to_load_album": "Impossibile caricare l'album", - "unable_to_load_asset_activity": "Impossiible caricare l'attività dell'asset", + "unable_to_load_asset_activity": "Impossibile caricare l'attività dell'asset", "unable_to_load_items": "Impossibile caricare gli elementi", "unable_to_load_liked_status": "Impossibile caricare lo stato dei preferiti", "unable_to_log_out_all_devices": "Impossibile eseguire il logout da tutti i dispositivi", @@ -663,7 +671,7 @@ "unable_to_remove_reaction": "Impossibile rimuovere reazione", "unable_to_remove_user": "", "unable_to_repair_items": "Impossibile riparare elementi", - "unable_to_reset_password": "Impossiible reimpostare la password", + "unable_to_reset_password": "Impossibile reimpostare la password", "unable_to_resolve_duplicate": "Impossibile risolvere duplicato", "unable_to_restore_assets": "Impossibile ripristinare gli asset", "unable_to_restore_trash": "Impossibile ripristinare cestino", @@ -671,23 +679,23 @@ "unable_to_save_album": "Impossibile salvare album", "unable_to_save_api_key": "Impossibile salvare chiave API", "unable_to_save_date_of_birth": "Impossible salvare la data di nascita", - "unable_to_save_name": "Impossibile salvare nome", - "unable_to_save_profile": "Impossibile salvare profilo", - "unable_to_save_settings": "Impossibile salvare impostazioni", - "unable_to_scan_libraries": "Impossibile analizzare librerie", - "unable_to_scan_library": "Impossibile analizzare libreria", + "unable_to_save_name": "Impossibile salvare il nome", + "unable_to_save_profile": "Impossibile salvare il profilo", + "unable_to_save_settings": "Impossibile salvare le impostazioni", + "unable_to_scan_libraries": "Impossibile analizzare le librerie", + "unable_to_scan_library": "Impossibile analizzare la libreria", "unable_to_set_feature_photo": "Impossibile impostare la foto in evidenza", - "unable_to_set_profile_picture": "Impossibile impostare foto profilo", - "unable_to_submit_job": "Impossibile confermare processo", - "unable_to_trash_asset": "Impossibile cestinare asset", - "unable_to_unlink_account": "Impossibile scollegare account", + "unable_to_set_profile_picture": "Impossibile impostare la foto profilo", + "unable_to_submit_job": "Impossibile eseguire l'attività", + "unable_to_trash_asset": "Impossibile cestinare l'asset", + "unable_to_unlink_account": "Impossibile scollegare l'account", "unable_to_update_album_cover": "Errore durante l'aggiornamento della copertina dell'album", - "unable_to_update_album_info": "Errore durante l'aggiornamento delle info dell'album", - "unable_to_update_library": "Impossibile aggiornare libreria", - "unable_to_update_location": "Impossibile aggiornare posizione", - "unable_to_update_settings": "Impossibile aggiornare impostazioni", - "unable_to_update_timeline_display_status": "Impossibile aggiornare lo stato visivo della linea temporale", - "unable_to_update_user": "Impossibile aggiornare utente", + "unable_to_update_album_info": "Impossibile aggiornare le informazioni sull'album", + "unable_to_update_library": "Impossibile aggiornare la libreria", + "unable_to_update_location": "Impossibile aggiornare la posizione", + "unable_to_update_settings": "Impossibile aggiornare le impostazioni", + "unable_to_update_timeline_display_status": "Impossibile aggiornare lo stato di visualizzazione della sequenza temporale", + "unable_to_update_user": "Impossibile aggiornare l'utente", "unable_to_upload_file": "Impossibile caricare il file" }, "every_day_at_onepm": "", @@ -695,7 +703,7 @@ "every_night_at_twoam": "", "every_six_hours": "", "exif": "Exif", - "exit_slideshow": "Esci dalla diapositiva", + "exit_slideshow": "Esci dalla presentazione", "expand_all": "Espandi tutto", "expire_after": "Scade dopo", "expired": "Scaduto", @@ -745,16 +753,16 @@ "host": "Host", "hour": "Ora", "image": "Immagine", - "image_alt_text_date": "{isVideo, select, true {Video} other {Immagine}} scattato il {date}", - "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} scattata con {person1} il giorno {date}", - "image_alt_text_date_2_people": "{isVideo, select, true {Video} other {Image}} scattata con {person1} e {person2} il giorno {date}", - "image_alt_text_date_3_people": "{isVideo, select, true {Video} other {Image}} scattata con {person1}, {person2}, e {person3} il giorno {date}", - "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Video} other {Image}} scattata con {person1}, {person2}, e altre {additionalCount, number} persone il giorno {date}", - "image_alt_text_date_place": "{isVideo, select, true {Video} other {Image}} scattata a {city}, {country} il giorno {date}", - "image_alt_text_date_place_1_person": "{isVideo, select, true {Video} other {Image}} scattata a {city}, {country} con {person1} il giorno {date}", - "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} scattata a {city}, {country} con {person1} e {person2} il giorno {date}", - "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} scattata a {city}, {country} con {person1}, {person2}, e {person3} il giorno {date}", - "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Immagine}} scattato a {city}, {country} con {person1}, {person2} e {additionalCount, number} altre persone il {date}", + "image_alt_text_date": "{isVideo, select, true {Video girato} other {Foto scattata}} il {date}", + "image_alt_text_date_1_person": "{isVideo, select, true {Video girato} other {Foto scattata}} con {person1} il giorno {date}", + "image_alt_text_date_2_people": "{isVideo, select, true {Video girato} other {Foto scattata}} con {person1} e {person2} il giorno {date}", + "image_alt_text_date_3_people": "{isVideo, select, true {Video girato} other {Foto scattata}} con {person1}, {person2}, e {person3} il giorno {date}", + "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Video girato} other {Foto scattata}} con {person1}, {person2}, e altre {additionalCount, number} persone il giorno {date}", + "image_alt_text_date_place": "{isVideo, select, true {Video girato} other {Foto scattata}} a {city}, {country} il giorno {date}", + "image_alt_text_date_place_1_person": "{isVideo, select, true {Video girato} other {Foto scattata}} a {city}, {country} con {person1} il giorno {date}", + "image_alt_text_date_place_2_people": "{isVideo, select, true {Video girato} other {Foto scattata}} a {city}, {country} con {person1} e {person2} il giorno {date}", + "image_alt_text_date_place_3_people": "{isVideo, select, true {Video girato} other {Foto scattata}} a {city}, {country} con {person1}, {person2}, e {person3} il giorno {date}", + "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video girato} other {Foto scattata}} a {city}, {country} con {person1}, {person2} e {additionalCount, number} altre persone il {date}", "image_alt_text_people": "{count, plural, =1 {con {person1}} =2 {con {person1} e {person2}} =3 {con {person1}, {person2} e {person3}} other {con {person1}, {person2} e {others, number} altri}}", "image_alt_text_place": "a {city}, {country}", "image_taken": "{isVideo, select, true {Video registrato} other {Immagine scattata}}", @@ -783,7 +791,7 @@ "jobs": "Processi", "keep": "Mantieni", "keep_all": "Tieni tutto", - "keyboard_shortcuts": "Comandi rapidi", + "keyboard_shortcuts": "Scorciatoie da tastiera", "language": "Lingua", "language_setting_description": "Seleziona la tua lingua predefinita", "last_seen": "Ultimo accesso", @@ -828,7 +836,7 @@ "loading": "Caricamento", "loading_search_results_failed": "Impossibile caricare i risultati della ricerca", "log_out": "Esci", - "log_out_all_devices": "Esci da tutti i dispositivi", + "log_out_all_devices": "Disconnetti tutti i dispositivi", "logged_out_all_devices": "Disconnesso da tutti i dispositivi", "logged_out_device": "Disconnesso dal dispositivo", "login": "Login", @@ -846,7 +854,7 @@ "manage_your_account": "Gestisci il tuo account", "manage_your_api_keys": "Gestisci le tue chiavi API", "manage_your_devices": "Gestisci i tuoi dispositivi collegati", - "manage_your_oauth_connection": "Gestisci la tua connesione OAuth", + "manage_your_oauth_connection": "Gestisci la tua connessione OAuth", "map": "Mappa", "map_marker_for_images": "Indicatore mappa per le immagini scattate in {city}, {country}", "map_marker_with_image": "Segnaposto con immagine", @@ -863,7 +871,7 @@ "merge_people_limit": "Puoi unire al massimo 5 volti alla volta", "merge_people_prompt": "Vuoi unire queste persone? Questa azione è irreversibile.", "merge_people_successfully": "Unione persone completata con successo", - "merged_people_count": "Uniti {count, plural, one {# persona} other {# persone}}", + "merged_people_count": "{count, plural, one {Unita # persona} other {Unite # persone}}", "minimize": "Minimizza", "minute": "Minuto", "missing": "Mancante", @@ -886,8 +894,8 @@ "next_memory": "Prossima memoria", "no": "No", "no_albums_message": "Crea un album per organizzare le tue foto ed i tuoi video", - "no_albums_with_name_yet": "Nessun album con questo nome, per ora.", - "no_albums_yet": "Nessun album presente, per ora.", + "no_albums_with_name_yet": "Sembra che tu non abbia ancora nessun album con questo nome.", + "no_albums_yet": "Sembra che tu non abbia ancora nessun album.", "no_archived_assets_message": "Archivia foto e video per nasconderli dalla galleria di foto", "no_assets_message": "CLICCA PER CARICARE LA TUA PRIMA FOTO", "no_duplicates_found": "Nessun duplicato trovato.", @@ -914,9 +922,9 @@ "ok": "Ok", "oldest_first": "Prima vecchi", "onboarding": "Inserimento", - "onboarding_privacy_description": "Le seguenti funzioni (opzionali) fanno uso di servizi esterni, e possono essere disabilitate in qualsiasi momento dalle impostazioni d'amministratore.", + "onboarding_privacy_description": "Le seguenti funzioni (opzionali) fanno uso di servizi esterni, e possono essere disabilitate in qualsiasi momento nelle impostazioni di amministrazione.", "onboarding_theme_description": "Scegli un tema colore per la tua istanza. Potrai cambiarlo nelle impostazioni.", - "onboarding_welcome_description": "Andiamo ad impostare la tua istanza con alcuni settaggi comuni.", + "onboarding_welcome_description": "Andiamo ad impostare la tua istanza con alcune impostazioni comuni.", "onboarding_welcome_user": "Benvenuto, {user}", "online": "Online", "only_favorites": "Solo preferiti", @@ -956,7 +964,7 @@ "pending": "In attesa", "people": "Persone", "people_edits_count": "{count, plural, one {Modificata # persona} other {Modificate # persone}}", - "people_sidebar_description": "Mosta un link alle persone nella barra laterale", + "people_sidebar_description": "Mostra un link alle persone nella barra laterale", "perform_library_tasks": "", "permanent_deletion_warning": "Avviso eliminazione permanente", "permanent_deletion_warning_setting_description": "Mostra un avviso all'eliminazione definitiva di un asset", @@ -1046,10 +1054,10 @@ "refreshing_metadata": "Ricaricando i metadati", "regenerating_thumbnails": "Rigenerando le anteprime", "remove": "Rimuovi", - "remove_assets_album_confirmation": "Sei sicuro di voler rimuovere {count, plural, one {# asset} other {# assets}} dall'album?", - "remove_assets_shared_link_confirmation": "Sei sicuro di voler rimuovere {count, plural, one {# asset} other {# assets}} da questo link condiviso?", + "remove_assets_album_confirmation": "Sei sicuro di voler rimuovere {count, plural, one {# asset} other {# asset}} dall'album?", + "remove_assets_shared_link_confirmation": "Sei sicuro di voler rimuovere {count, plural, one {# asset} other {# asset}} da questo link condiviso?", "remove_assets_title": "Rimuovere asset?", - "remove_custom_date_range": "Cancella intervallo data personalizzato", + "remove_custom_date_range": "Rimuovi intervallo data personalizzato", "remove_from_album": "Rimuovere dall'album", "remove_from_favorites": "Rimuovi dai preferiti", "remove_from_shared_link": "Rimuovi dal link condiviso", @@ -1058,7 +1066,7 @@ "removed_api_key": "Rimossa chiave API: {name}", "removed_from_archive": "Rimosso dall'archivio", "removed_from_favorites": "Rimosso dai preferiti", - "removed_from_favorites_count": "{count, plural, other {Rimossi #}} dai preferiti", + "removed_from_favorites_count": "{count, plural, one {Rimosso } other {Rimossi #}} dai preferiti", "rename": "Rinomina", "repair": "Ripara", "repair_no_results_message": "I file mancanti e non tracciati saranno mostrati qui", @@ -1089,7 +1097,7 @@ "saved_settings": "Impostazioni salvate", "say_something": "Dici qualcosa", "scan_all_libraries": "Analizza tutte le librerie", - "scan_all_library_files": "Ri-analizza Tutti i File della Libreria", + "scan_all_library_files": "Scansiona nuovamente tutti i file della libreria", "scan_new_library_files": "Analizza i File Nuovi della Libreria", "scan_settings": "Impostazioni Analisi", "scanning_for_album": "Sto cercando l'album...", @@ -1098,7 +1106,7 @@ "search_by_context": "Cerca con contesto", "search_by_filename": "Cerca per nome del file o estensione", "search_by_filename_example": "es. IMG_1234.JPG o PNG", - "search_camera_make": "Cerca manufattore fotocamera...", + "search_camera_make": "Cerca produttore fotocamera...", "search_camera_model": "Cerca modello fotocamera...", "search_city": "Cerca città...", "search_country": "Cerca paese...", @@ -1109,7 +1117,7 @@ "search_places": "Cerca luoghi", "search_state": "Cerca stato...", "search_timezone": "Cerca fuso orario...", - "search_type": "Certa tipo", + "search_type": "Cerca tipo", "search_your_photos": "Cerca le tue foto", "searching_locales": "Cerca localizzazioni...", "second": "Secondo", @@ -1127,7 +1135,7 @@ "select_photos": "Seleziona foto", "select_trash_all": "Seleziona cestina tutto", "selected": "Selezionato", - "selected_count": "{count, plural, other {# selezionati}}", + "selected_count": "{count, plural, one {# selezionato} other {# selezionati}}", "send_message": "Manda messaggio", "send_welcome_email": "Invia email di benvenuto", "server": "Server", @@ -1140,7 +1148,7 @@ "set_as_profile_picture": "Imposta come foto profilo", "set_date_of_birth": "Imposta data di nascita", "set_profile_picture": "Imposta foto profilo", - "set_slideshow_to_fullscreen": "Imposta diapositiva a schermo intero", + "set_slideshow_to_fullscreen": "Imposta presentazione a schermo intero", "settings": "Impostazioni", "settings_saved": "Impostazioni salvate", "share": "Condivisione", @@ -1173,9 +1181,9 @@ "show_person_options": "Mostra opzioni persona", "show_progress_bar": "Mostra Barra Avanzamento", "show_search_options": "Mostra impostazioni di ricerca", - "show_supporter_badge": "Insignia di Contributore", - "show_supporter_badge_description": "Mostra un'insignia di contributore", - "shuffle": "Mescola", + "show_supporter_badge": "Medaglia di Contributore", + "show_supporter_badge_description": "Mostra la medaglia di contributore", + "shuffle": "Casuale", "sign_out": "Esci", "sign_up": "Registrati", "size": "Dimensione", @@ -1194,16 +1202,16 @@ "stack_duplicates": "Raggruppa i duplicati", "stack_select_one_photo": "Seleziona una foto principale per il gruppo", "stack_selected_photos": "Impila foto selezionate", - "stacked_assets_count": "{count, plural, one {Raggruppato # asset} other {Raggruppati # assets}}", + "stacked_assets_count": "{count, plural, one {Raggruppato # asset} other {Raggruppati # asset}}", "stacktrace": "Traccia dell'errore", "start": "Inizio", "start_date": "Data di inizio", "state": "Provincia", "status": "Stato", "stop_motion_photo": "Ferma Foto in Movimento", - "stop_photo_sharing": "Stoppare la condivisione delle tue foto?", + "stop_photo_sharing": "Interrompere la condivisione delle tue foto?", "stop_photo_sharing_description": "{partner} non potrà più accedere alle tue foto.", - "stop_sharing_photos_with_user": "Non condividere più le tue foto con questo utente", + "stop_sharing_photos_with_user": "Interrompi la condivisione delle tue foto con questo utente", "storage": "Spazio di archiviazione", "storage_label": "Etichetta archiviazione", "storage_usage": "{used} di {available} utilizzati", @@ -1235,7 +1243,7 @@ "trash_no_results_message": "Le foto cestinate saranno mostrate qui.", "trashed_items_will_be_permanently_deleted_after": "Gli elementi cestinati saranno eliminati definitivamente dopo {days, plural, one {# giorno} other {# giorni}}.", "type": "Tipo", - "unarchive": "Rimuovi dagli archivi", + "unarchive": "Annulla l'archiviazione", "unarchived": "Rimosso dall'archivio", "unarchived_count": "{count, plural, other {Non archiviati #}}", "unfavorite": "Rimuovi preferito", @@ -1252,16 +1260,16 @@ "unselect_all": "Deseleziona tutto", "unselect_all_duplicates": "Deseleziona tutti i duplicati", "unstack": "Rimuovi dal gruppo", - "unstacked_assets_count": "{count, plural, one {Separato # asset} other {Separati # assets}}", + "unstacked_assets_count": "{count, plural, one {Separato # asset} other {Separati # asset}}", "untracked_files": "File non tracciati", "untracked_files_decription": "Questi file non vengono tracciati dall'applicazione. Sono il risultato di spostamenti falliti, caricamenti interrotti, oppure sono stati abbandonati a causa di un bug", "up_next": "Prossimo", "updated_password": "Password aggiornata", "upload": "Carica", "upload_concurrency": "Caricamenti contemporanei", - "upload_errors": "Caricamento completato con {count, plural, one {# errore} other {# errori}}, ricarica la pagina per vedere gli assets caricati.", + "upload_errors": "Caricamento completato con {count, plural, one {# errore} other {# errori}}, ricarica la pagina per vedere gli asset caricati.", "upload_progress": "Rimanenti {remaining, number} - Processati {processed, number}/{total, number}", - "upload_skipped_duplicates": "{count, plural, one {Ignorato # asset duplicato} other {Ignorati # assets duplicati}}", + "upload_skipped_duplicates": "{count, plural, one {Ignorato # asset duplicato} other {Ignorati # asset duplicati}}", "upload_status_duplicates": "Duplicati", "upload_status_errors": "Errori", "upload_status_uploaded": "Caricato", @@ -1285,7 +1293,7 @@ "variables": "Variabili", "version": "Versione", "version_announcement_closing": "Il tuo amico, Alex", - "version_announcement_message": "Heilà! È stata rilasciata una nuova versione dell'applicazione. Leggi le note di rilascio e assicurati che i tuoi file docker-compose.yml/.env siano aggiornati per evitare problemi e incongruenze, sopratutto se utilizzi WatchTower o altri strumenti per aggiornare l'applicazione in automatico.", + "version_announcement_message": "Ehilà! È stata rilasciata una nuova versione dell'applicazione. Leggi le note di rilascio e assicurati che i tuoi file docker-compose.yml/.env siano aggiornati per evitare problemi e incongruenze, soprattutto se utilizzi WatchTower o altri strumenti per aggiornare l'applicazione in automatico.", "video": "Video", "video_hover_setting": "Riproduci l'anteprima del video al passaggio del mouse", "video_hover_setting_description": "Riproduci miniatura video quando il mouse passa sopra l'elemento. Anche se disabilitato, la riproduzione può essere avviata passando con il mouse sopra l'icona riproduci.", @@ -1305,7 +1313,7 @@ "warning": "Attenzione", "week": "Settimana", "welcome": "Benvenuto", - "welcome_to_immich": "Benvenuto a immich", + "welcome_to_immich": "Benvenuto in immich", "year": "Anno", "years_ago": "{years, plural, one {# anno} other {# anni}} fa", "yes": "Si", diff --git a/web/src/lib/i18n/ko.json b/web/src/lib/i18n/ko.json index 9d94a918fb7c5..89c5ca068f8e6 100644 --- a/web/src/lib/i18n/ko.json +++ b/web/src/lib/i18n/ko.json @@ -129,12 +129,13 @@ "map_enable_description": "지도 기능 활성화", "map_gps_settings": "지도 및 GPS 설정", "map_gps_settings_description": "지도 및 GPS (역지오코딩) 설정 관리", + "map_implications": "지도 기능은 외부 타일 서비스(tiles.immich.clou를 사용합니다.", "map_light_style": "라이트 스타일", "map_manage_reverse_geocoding_settings": "역지오코딩 설정 관리", "map_reverse_geocoding": "역지오코딩", "map_reverse_geocoding_enable_description": "역지오코딩 활성화", "map_reverse_geocoding_settings": "역지오코딩 설정", - "map_settings": "지도 설정", + "map_settings": "지도", "map_settings_description": "지도 설정 관리", "map_style_description": "지도 테마 style.json URL", "metadata_extraction_job": "메타데이터 추출", @@ -320,7 +321,8 @@ "user_settings": "사용자 설정", "user_settings_description": "사용자 설정 관리", "user_successfully_removed": "{email}이(가) 성공적으로 제거되었습니다.", - "version_check_enabled_description": "최신 버전 확인을 위한 주기적인 GitHub 확인 활성화", + "version_check_enabled_description": "버전 확인 활성화", + "version_check_implications": "버전 확인 기능은 주기적으로 github.com에 요청을 보냅니다.", "version_check_settings": "버전 확인", "version_check_settings_description": "최신 버전 알림 설정 관리", "video_conversion_job": "동영상 트랜스코드", @@ -336,7 +338,8 @@ "album_added": "공유 앨범 초대", "album_added_notification_setting_description": "공유 앨범으로 초대를 받은 경우 이메일 알림 받기", "album_cover_updated": "앨범 커버를 변경했습니다.", - "album_delete_confirmation": "{album} 앨범을 삭제하시겠습니까?\n이 앨범을 공유한 경우 다른 사용자가 더 이상 앨범에 접근할 수 없습니다.", + "album_delete_confirmation": "{album} 앨범을 삭제하시겠습니까?", + "album_delete_confirmation_description": "이 앨범을 공유한 경우 다른 사용자가 더 이상 앨범에 접근할 수 없습니다.", "album_info_updated": "앨범 정보가 수정되었습니다.", "album_leave": "앨범에서 나가시겠습니까?", "album_leave_confirmation": "{album} 앨범에서 나가시겠습니까?", @@ -360,6 +363,7 @@ "allow_edits": "편집자로 설정", "allow_public_user_to_download": "모든 사용자의 다운로드 허용", "allow_public_user_to_upload": "모든 사용자의 업로드 허용", + "anti_clockwise": "반시계 방향", "api_key": "API 키", "api_key_description": "이 값은 한 번만 표시됩니다. 창을 닫기 전 반드시 복사하세요.", "api_key_empty": "키 이름은 비어 있을 수 없습니다.", @@ -441,6 +445,7 @@ "clear_all_recent_searches": "검색 기록 전체 삭제", "clear_message": "메시지 지우기", "clear_value": "값 지우기", + "clockwise": "시계 방향", "close": "닫기", "collapse": "접기", "collapse_all": "모두 접기", @@ -550,6 +555,10 @@ "edit_user": "사용자 수정", "edited": "펀집되었습니다.", "editor": "편집자", + "editor_close_without_save_prompt": "변경 사항이 반영되지 않습니다.", + "editor_close_without_save_title": "편집을 종료하시겠습니까?", + "editor_crop_tool_h2_aspect_ratios": "종횡비", + "editor_crop_tool_h2_rotation": "회전", "email": "이메일", "empty": "", "empty_album": "", @@ -720,6 +729,7 @@ "filter_people": "인물 필터", "find_them_fast": "이름으로 검색하여 빠르게 찾기", "fix_incorrect_match": "잘못된 분류 수정", + "folders": "폴더", "force_re-scan_library_files": "모든 파일 강제 다시 스캔", "forward": "앞으로", "general": "일반", @@ -895,6 +905,7 @@ "ok": "확인", "oldest_first": "오래된 순", "onboarding": "온보딩", + "onboarding_privacy_description": "이 선택적 기능은 외부 서비스를 사용하며, 관리자 설정에서 언제든 비활성화할 수 있습니다.", "onboarding_storage_template_description": "활성화한 경우, 사용자 정의 템플릿을 기반으로 파일을 자동 분류합니다. 안정성 문제로 인해 해당 기능은 기본적으로 비활성화 되어 있습니다. 자세한 내용은 [공식 문서]를 참조하세요.", "onboarding_theme_description": "색상 테마를 선택하세요. 나중에 설정에서 변경할 수 있습니다.", "onboarding_welcome_description": "몇 가지 일반적인 설정을 진행하겠습니다.", @@ -969,6 +980,7 @@ "previous_memory": "이전 추억", "previous_or_next_photo": "이전 또는 다음 이미지로", "primary": "주요", + "privacy": "프라이버시", "profile_image_of_user": "{user}님의 프로필 이미지", "profile_picture_set": "프로필 사진이 설정되었습니다.", "public_album": "공개 앨범", @@ -1007,6 +1019,8 @@ "purchase_settings_server_activated": "서버 제품 키는 관리자가 관리합니다.", "range": "", "rating": "등급", + "rating_clear": "등급 초기화", + "rating_count": "{count, plural, one {#점} other {#점}}", "rating_description": "상세 정보에 EXIF의 등급 정보 표시", "raw": "", "reaction_options": "반응 옵션", @@ -1130,6 +1144,7 @@ "shared_by_user": "{user}님이 공유함", "shared_by_you": "내가 공유함", "shared_from_partner": "{partner}님의 사진", + "shared_link_options": "공유 링크 옵션", "shared_links": "공유 링크", "shared_photos_and_videos_count": "사진 및 동영상 {assetCount, plural, other {#개를 공유했습니다.}}", "shared_with_partner": "{partner}님과 공유함", @@ -1205,7 +1220,7 @@ "to_login": "로그인", "to_trash": "삭제", "toggle_settings": "설정 변경", - "toggle_theme": "테마 변경", + "toggle_theme": "다크 모드 사용", "toggle_visibility": "숨김 여부 변경", "total_usage": "총 사용량", "trash": "휴지통", @@ -1227,6 +1242,7 @@ "unlink_oauth": "OAuth 연결 해제", "unlinked_oauth_account": "OAuth 계정 연결이 해제되었습니다.", "unnamed_album": "이름 없는 앨범", + "unnamed_album_delete_confirmation": "선텍한 앨범을 삭제하시겠습니까?", "unnamed_share": "이름 없는 공유", "unsaved_change": "저장되지 않은 변경 사항", "unselect_all": "모두 선택 해제", @@ -1283,7 +1299,7 @@ "warning": "경고", "week": "주", "welcome": "환영합니다", - "welcome_to_immich": "Immich에 오신 것을 환영합니다", + "welcome_to_immich": "환영합니다", "year": "년", "years_ago": "{years, plural, one {#년} other {#년}} 전", "yes": "네", diff --git a/web/src/lib/i18n/lt.json b/web/src/lib/i18n/lt.json index e656754c7d3ef..faf4dea2922d8 100644 --- a/web/src/lib/i18n/lt.json +++ b/web/src/lib/i18n/lt.json @@ -7,6 +7,7 @@ "actions": "Veiksmai", "active": "Vykdoma", "activity": "Veikla", + "activity_changed": "Veikla yra {enabled, select, true {enabled} other {disabled}}", "add": "Pridėti", "add_a_description": "Pridėti aprašymą", "add_a_location": "Pridėti vietovę", @@ -34,43 +35,51 @@ "config_set_by_file": "Konfigūracija dabar nustatyta konfigūracinio failo", "confirm_delete_library": "Ar tikrai norite ištrinti {library} biblioteką?", "confirm_email_below": "Patvirtinimui įveskite \"{email}\" žemiau", + "confirm_reprocess_all_faces": "Ar tikrai norite iš naujo apdoroti visus veidus? Tai taip pat ištrins įvardytus asmenis.", "confirm_user_password_reset": "Ar tikrai norite iš naujo nustatyti {user} slaptažodį?", "crontab_guru": "", "disable_login": "Išjungti prisijungimą", "disabled": "", - "duplicate_detection_job_description": "", + "duplicate_detection_job_description": "Vykdykite mašininį mokymąsi tam, kad aptiktumėte panašius vaizdus. Nuo šios funkcijos priklauso išmanioji paieška", "exclusion_pattern_description": "Išimčių šablonai leidžia nepaisyti failų ir aplankų skenuojant jūsų biblioteką. Tai yra naudinga, jei turite aplankų su failais, kurių nenorite importuoti, pavyzdžiui, RAW failai.", "external_library_created_at": "Išorinė biblioteka (sukurta {date})", "external_library_management": "Išorinių bibliotekų tvarkymas", "face_detection": "Veido atpažinimas", - "image_format_description": "", - "image_prefer_embedded_preview": "", + "failed_job_command": "Darbo {job} komanda {command} nepavyko", + "force_delete_user_warning": "ĮSPĖJIMAS: Šis veiksmas iš karto pašalins naudotoją ir visą jo informaciją. Šis žingsnis nesugrąžinamas ir failų nebus galima atkurti.", + "forcing_refresh_library_files": "Priverstinai atnaujinami visi failai bilbiotekoje", + "image_format_description": "WebP sukuria mažesnius failus nei JPEG, bet lėčiau juos apdoroja.", + "image_prefer_embedded_preview": "Pageidautinai rodyti įterptą peržiūrą", "image_prefer_embedded_preview_setting_description": "", - "image_prefer_wide_gamut": "", + "image_prefer_wide_gamut": "Teikti pirmenybę plačiai gamai", "image_prefer_wide_gamut_setting_description": "", "image_preview_format": "Peržiūros formatas", "image_preview_resolution": "Peržiūros rezoliucija", - "image_preview_resolution_description": "", + "image_preview_resolution_description": "Naudojama peržiūrint vieną nuotrauką ir mašininiam mokymui. Didesnė rezoliucija gali išsaugoti daugiau detalių, bet ilgiau užtrukti apdoroti ir sumažinti programos greitumą.", "image_quality": "Kokybė", "image_quality_description": "Vaizdo kokybė nuo 1 iki 100. Aukštesnė kokybė yra geresnė, tačiau sukuriami didesni failai. Ši parinktis turi įtakos peržiūros ir miniatiūrų vaizdams.", - "image_settings": "", - "image_settings_description": "", + "image_settings": "Nuotraukos nustatymai", + "image_settings_description": "Keisti sugeneruotų nuotraukų kokybę ir rezoliuciją", "image_thumbnail_format": "Miniatūros formatas", "image_thumbnail_resolution": "Miniatūros rezoliucija", - "image_thumbnail_resolution_description": "", - "job_settings": "", - "job_settings_description": "", + "image_thumbnail_resolution_description": "Naudojama žiūrint nuotraukų grupes (pagrindinis nuotraukų puslapis, albumų peržiūra ir t.t.). Aukštesnė rezoliucija gali išlaikyti daugiau detalių, bet užtrunka ilgiau apdoroti, gali turėti didesnius failų dydžius ir gali sumažinti programos greitumą.", + "job_concurrency": "{job} lygiagretumas", + "job_not_concurrency_safe": "Šis darbas nėra saugus apdoroti lygiagrečiai.", + "job_settings": "Darbo nustatymai", + "job_settings_description": "Keisti darbų lygiagretumą", "job_status": "Darbų būsenos", "library_created": "Sukurta biblioteka: {library}", "library_cron_expression": "Cron išraiška", + "library_cron_expression_description": "Nustatykite nuskaitymo intervalą naudodami „cron“ formatą. Daugiau informacijos rasite pvz. Crontab Guru", "library_cron_expression_presets": "", "library_deleted": "Biblioteka ištrinta", + "library_import_path_description": "Nurodykite aplanką, kurį norite importuoti. Šiame aplanke, įskaitant poaplankius, bus nuskaityti vaizdai ir vaizdo įrašai.", "library_scanning": "Periodinis skanavimas", "library_scanning_description": "Konfigūruoti periodinį bibliotekos skanavimą", "library_scanning_enable_description": "Įgalinti periodinį bibliotekos skanavimą", "library_settings": "Išorinė biblioteka", "library_settings_description": "Tvarkyti išorinės bibliotekos parametrus", - "library_tasks_description": "", + "library_tasks_description": "Atlikit bibliotekos užduotis", "library_watching_enable_description": "", "library_watching_settings": "", "library_watching_settings_description": "", @@ -83,7 +92,7 @@ "machine_learning_duplicate_detection_enabled_description": "", "machine_learning_duplicate_detection_setting_description": "", "machine_learning_enabled": "Įgalinti mašininį mokymąsi", - "machine_learning_enabled_description": "", + "machine_learning_enabled_description": "Jei išjungta, visos „ML“ funkcijos bus išjungtos, nepaisant toliau pateiktų nustatymų.", "machine_learning_facial_recognition": "Veido atpažinimas", "machine_learning_facial_recognition_description": "Aptikti, atpažinti ir sugrupuoti veidus nuotraukose", "machine_learning_facial_recognition_model": "Veido atpažinimo modelis", @@ -91,20 +100,20 @@ "machine_learning_facial_recognition_setting": "Įgalinti veido atpažinimą", "machine_learning_facial_recognition_setting_description": "", "machine_learning_max_detection_distance": "", - "machine_learning_max_detection_distance_description": "", - "machine_learning_max_recognition_distance": "", + "machine_learning_max_detection_distance_description": "Didžiausias atstumas tarp dviejų vaizdų, kad jie būtų laikomi dublikatais, svyruoja nuo 0,001 iki 0,1. Didesnės vertės aptiks daugiau dublikatų, tačiau gali būti klaidingai teigiami.", + "machine_learning_max_recognition_distance": "Maksimalus atpažinimo atstumas", "machine_learning_max_recognition_distance_description": "", "machine_learning_min_detection_score": "", "machine_learning_min_detection_score_description": "", "machine_learning_min_recognized_faces": "", - "machine_learning_min_recognized_faces_description": "", + "machine_learning_min_recognized_faces_description": "Mažiausias atpažintų veidų skaičius asmeniui, kurį reikia sukurti. Tai padidinus, veido atpažinimas tampa tikslesnis, bet padidėja tikimybė, kad veidas žmogui nepriskirtas.", "machine_learning_settings": "Mašininio mokymosi nustatymai", "machine_learning_settings_description": "Tvarkyti mašininio mokymosi funkcijas ir nustatymus", "machine_learning_smart_search": "Išmanioji paieška", "machine_learning_smart_search_description": "", "machine_learning_smart_search_enabled": "Įjungti išmaniąją paiešką", - "machine_learning_smart_search_enabled_description": "", - "machine_learning_url_description": "", + "machine_learning_smart_search_enabled_description": "Jei išjungta, vaizdai nebus užkoduoti išmaniajai paieškai.", + "machine_learning_url_description": "Mašininio mokymosi serverio URL", "manage_log_settings": "", "map_dark_style": "Tamsioji tema", "map_enable_description": "", @@ -190,20 +199,21 @@ "thumbnail_generation_job": "Generuoti miniatiūras", "thumbnail_generation_job_description": "", "transcode_policy_description": "", - "transcoding_acceleration_api": "", + "transcoding_acceleration_api": "Spartinimo API", "transcoding_acceleration_api_description": "", - "transcoding_acceleration_nvenc": "", + "transcoding_acceleration_nvenc": "NVENC (reikalinga NVIDIA GPU)", "transcoding_acceleration_qsv": "", "transcoding_acceleration_rkmpp": "", "transcoding_acceleration_vaapi": "VAAPI", "transcoding_accepted_audio_codecs": "", "transcoding_accepted_audio_codecs_description": "", + "transcoding_accepted_containers": "Priimami konteineriai", "transcoding_accepted_video_codecs": "", "transcoding_accepted_video_codecs_description": "", "transcoding_advanced_options_description": "Parinktys, kurių daugelis vartotojų keisti neturėtų", "transcoding_audio_codec": "Garso kodekas", - "transcoding_audio_codec_description": "", - "transcoding_bitrate_description": "", + "transcoding_audio_codec_description": "Opus yra aukščiausios kokybės variantas, tačiau turi mažesnį suderinamumą su senesniais įrenginiais ar programine įranga.", + "transcoding_bitrate_description": "Vaizdo įrašai viršija maksimalią leistiną bitų spartą arba nėra priimtino formato", "transcoding_constant_quality_mode": "Pastovios kokybės režimas", "transcoding_constant_quality_mode_description": "", "transcoding_constant_rate_factor": "", @@ -216,7 +226,7 @@ "transcoding_hevc_codec": "HEVC kodekas", "transcoding_max_b_frames": "", "transcoding_max_b_frames_description": "", - "transcoding_max_bitrate": "", + "transcoding_max_bitrate": "Maksimalus bitų srautas", "transcoding_max_bitrate_description": "", "transcoding_max_keyframe_interval": "", "transcoding_max_keyframe_interval_description": "", @@ -785,13 +795,27 @@ "public_share": "", "purchase_account_info": "Rėmėjas", "purchase_activated_subtitle": "Dėkojame, kad remiate Immich ir atviro kodo programinę įrangą", + "purchase_activated_time": "Suaktyvinta {date, date}", "purchase_activated_title": "Jūsų raktas sėkmingai aktyvuotas", "purchase_button_activate": "Aktyvuoti", "purchase_button_buy": "Pirkti", "purchase_button_buy_immich": "Pirkti Immich", + "purchase_button_never_show_again": "Niekada daugiau nerodyti", + "purchase_button_reminder": "Priminti man po 30 dienų", + "purchase_button_remove_key": "Pašalinti produkto rakta", "purchase_button_select": "Pasirinkti", + "purchase_failed_activation": "Nepavyko suaktyvinti! Patikrinkite el. paštą, ar turite teisingo produkto koda!", + "purchase_individual_description_1": "Asmeniui", "purchase_individual_description_2": "Rėmėjo statusas", "purchase_input_suggestion": "Turite produkto raktą? Įveskite jį žemiau", + "purchase_license_subtitle": "Įsigykite „Immich“, kad palaikytumėte tolesnį paslaugos vystymą", + "purchase_lifetime_description": "Pirkimas visam gyvenimui", + "purchase_option_title": "PIRKIMO PASIRINKIMAS", + "purchase_panel_info_1": "„Immich“ kūrimas užima daug laiko ir pastangų, o visą darbo dieną dirba inžinieriai, kad jis būtų kuo geresnis. Mūsų misija yra, kad atvirojo kodo programinė įranga ir etiška verslo praktika taptų tvariu programuotojų pajamų šaltiniu ir sukurtų privatumą gerbiančią ekosistemą su realiomis alternatyvomis išnaudojamoms debesijos paslaugoms.", + "purchase_panel_info_2": "Kadangi esame įsipareigoję nepridėti mokamų sienų, šis pirkinys nesuteiks jums jokių papildomų „Immich“ funkcijų. Mes tikime, kad tokie vartotojai kaip jūs palaikys nuolatinį „Immich“ vystymąsi.", + "purchase_panel_title": "Palaikykite projektą", + "purchase_per_server": "Vienam serveriui", + "purchase_per_user": "Vienam naudotojui", "purchase_remove_product_key": "Pašalinti produkto raktą", "purchase_remove_product_key_prompt": "Ar tikrai norite pašalinti produkto raktą?", "purchase_remove_server_product_key": "Pašalinti serverio produkto raktą", @@ -801,6 +825,7 @@ "purchase_server_title": "Serveris", "purchase_settings_server_activated": "Serverio produkto raktas yra tvarkomas administratoriaus", "range": "", + "rating": "Įvertinimas žvaigždutėmis", "raw": "", "reaction_options": "", "read_changelog": "", diff --git a/web/src/lib/i18n/mn.json b/web/src/lib/i18n/mn.json index 54a4710a0366e..1bd96a43fd323 100644 --- a/web/src/lib/i18n/mn.json +++ b/web/src/lib/i18n/mn.json @@ -1,32 +1,40 @@ { - "account": "", - "acknowledge": "", - "action": "", - "actions": "", - "active": "", - "activity": "", - "add": "", - "add_a_description": "", - "add_a_location": "", - "add_a_name": "", - "add_a_title": "", + "about": "Тухай", + "account": "Бүртгэл", + "account_settings": "Бүртгэлийн тохиргоо", + "acknowledge": "Ойлголоо", + "action": "Үйлдэл", + "actions": "Үйлдлүүд", + "active": "Идэвхтэй", + "activity": "Үйлдлийн бүртгэл", + "activity_changed": "Үйлдлийн бүртгэл {enabled, select, true {идэвхтэй} other {идэвхгүй}}", + "add": "Нэмэх", + "add_a_description": "Тайлбар оруулах", + "add_a_location": "Байршил нэмэх", + "add_a_name": "Нэр өгөх", + "add_a_title": "Гарчиг оруулах", "add_exclusion_pattern": "", "add_import_path": "", - "add_location": "", - "add_more_users": "", - "add_partner": "", + "add_location": "Байршил оруулах", + "add_more_users": "Өөр хэрэглэгчид нэмэх", + "add_partner": "Хамтрагч нэмэх", "add_path": "", - "add_photos": "", + "add_photos": "Зураг нэмэх", "add_to": "", - "add_to_album": "", - "add_to_shared_album": "", + "add_to_album": "Цомогт оруулах", + "add_to_shared_album": "Нээлттэй албумд оруулах", + "added_to_archive": "Архивд оруулах", + "added_to_favorites": "Дуртай зурганд нэмэх", + "added_to_favorites_count": "Дуртай зурагнуудад {count, number} нэмэгдлээ", "admin": { - "authentication_settings": "", + "authentication_settings": "Танин нэвтрэлт тохиргоо", "authentication_settings_description": "", + "check_all": "Бүгдийг сонгох", "crontab_guru": "", "disable_login": "", "disabled": "", "duplicate_detection_job_description": "", + "face_detection": "Нүүр илрүүлэх", "image_format_description": "", "image_prefer_embedded_preview": "", "image_prefer_embedded_preview_setting_description": "", @@ -35,15 +43,16 @@ "image_preview_format": "", "image_preview_resolution": "", "image_preview_resolution_description": "", - "image_quality": "", + "image_quality": "Чанар", "image_quality_description": "", "image_settings": "", "image_settings_description": "", "image_thumbnail_format": "", "image_thumbnail_resolution": "", "image_thumbnail_resolution_description": "", - "job_settings": "", + "job_settings": "Ажлын тохиргоо", "job_settings_description": "", + "job_status": "Ажлын төлөв", "library_cron_expression": "", "library_cron_expression_presets": "", "library_scanning": "", @@ -62,11 +71,13 @@ "machine_learning_duplicate_detection": "", "machine_learning_duplicate_detection_enabled_description": "", "machine_learning_duplicate_detection_setting_description": "", - "machine_learning_enabled_description": "", - "machine_learning_facial_recognition": "", - "machine_learning_facial_recognition_description": "", - "machine_learning_facial_recognition_model": "", - "machine_learning_facial_recognition_model_description": "", + "machine_learning_enabled": "Машин сургалт идэвхжүүлэх", + "machine_learning_enabled_description": "Идэвхгүй болгосон үед доорх тохиргооноос хамаарахгүйгээр бүх машин сургалтын боломж идэвхгүй болно.", + "machine_learning_facial_recognition": "Нүүр танилт", + "machine_learning_facial_recognition_description": "Зураг дээрх хүмүүсийн нүүрийг илрүүлж, таньж, бүлэглэнэ", + "machine_learning_facial_recognition_model": "Нүүр танилтын загвар", + "machine_learning_facial_recognition_model_description": "Загварууд хэмжээ нь буурах эрэмбээр жагссан. Том загварууд удаан, илүү их санах ой хэрэглэх боловч харьцангуй чанартай үр дүн үзүүлнэ. Загвар өөрчилсөн тохиолдолд нүүр илрүүлэлтийн ажлыг дахин эхлүүлэх шаардлагатайг санаарай.", + "machine_learning_facial_recognition_setting": "Нүүр танилт идэвхжүүлэх", "machine_learning_facial_recognition_setting_description": "", "machine_learning_max_detection_distance": "", "machine_learning_max_detection_distance_description": "", @@ -89,7 +100,7 @@ "map_reverse_geocoding": "", "map_reverse_geocoding_enable_description": "", "map_reverse_geocoding_settings": "", - "map_settings": "", + "map_settings": "Газрын зураг", "map_settings_description": "", "map_style_description": "", "metadata_extraction_job_description": "", @@ -210,14 +221,17 @@ "transcoding_two_pass_encoding_setting_description": "", "transcoding_video_codec": "", "transcoding_video_codec_description": "", - "trash_enabled_description": "", - "trash_number_of_days": "", - "trash_number_of_days_description": "", - "trash_settings": "", - "trash_settings_description": "", + "trash_enabled_description": "Хогийн сав идэвхжүүлэх", + "trash_number_of_days": "Хоногийн тоо", + "trash_number_of_days_description": "Хогийн саванд хэд хоног хадгалаад бүр мөсөн устгах вэ", + "trash_settings": "Хогийн савны тохиргоо", + "trash_settings_description": "Хогийн савны тохиргоог өөрчлөх", "user_delete_delay_settings": "", "user_delete_delay_settings_description": "", - "user_settings": "", + "user_management": "Хэрэглэгчийн удирдлага", + "user_password_has_been_reset": "Хэрэглэгчийн нууц үг шинээр тохируулагдлаа:", + "user_restore_description": "{user}-н бүртгэл сэргэнэ.", + "user_settings": "Хэрэглэгчийн тохиргоо", "user_settings_description": "", "version_check_enabled_description": "", "version_check_settings": "", @@ -226,57 +240,70 @@ }, "admin_email": "", "admin_password": "", - "administration": "", + "administration": "Админ", "advanced": "", - "album_added": "", + "album_added": "Цомог нэмэгдлээ", "album_added_notification_setting_description": "", "album_cover_updated": "", - "album_info_updated": "", - "album_name": "", - "album_options": "", + "album_info_updated": "Цомгийн мэлээлэл шинэчлэгдлээ", + "album_leave": "Цомгоос гарах уу?", + "album_leave_confirmation": "Та {album} цомгоос гарахдаа итгэлтэй байна уу?", + "album_name": "Цомгийн нэр", + "album_options": "Цомгийн тохиргоо", + "album_remove_user": "Хэрэглэгч хасах уу?", + "album_remove_user_confirmation": "{user} хэрэглэгчийг хасахдаа итгэлтэй байна уу?", "album_updated": "", "album_updated_setting_description": "", - "albums": "", - "all": "", - "all_people": "", - "allow_dark_mode": "", - "allow_edits": "", - "api_key": "", - "api_keys": "", - "app_settings": "", + "albums": "Цомгууд", + "all": "Бүгд", + "all_albums": "Бүх цомог", + "all_people": "Бүх хүн", + "all_videos": "Бүх бичлэг", + "allow_dark_mode": "Харанхуй горим зөвшөөрөх", + "allow_edits": "Засварлалт зөвшөөрөх", + "api_key": "API түлхүүр", + "api_key_description": "Энэ утга зөвхөн ганц л удаа харагдана. Цонхоо хаахаас өмнө хуулж аваарай.", + "api_key_empty": "Таны API түлхүүрийн нэр хоосон байж болохгүй", + "api_keys": "API түлхүүрүүд", + "app_settings": "Апп-н тохиргоо", "appears_in": "", - "archive": "", - "archive_or_unarchive_photo": "", + "archive": "Архив", + "archive_or_unarchive_photo": "Зургийг архивт хийх эсвэл гаргах", + "archive_size": "Архивын хэмжээ", + "archive_size_description": "Татах үеийн архивын хэмжээг тохируулах (GiB-р)", "archived": "", + "asset_added_to_album": "Цомогт нэмсэн", + "asset_adding_to_album": "Цомогт нэмж байна...", "asset_offline": "", "assets": "", "authorized_devices": "", "back": "", "backward": "", "blurred_background": "", - "camera": "", - "camera_brand": "", - "camera_model": "", + "buy": "Immich худалдаж авах", + "camera": "Камер", + "camera_brand": "Камерын үйлдвэр", + "camera_model": "Камерын загвар", "cancel": "Цуцлах", - "cancel_search": "", + "cancel_search": "Хайлт цуцлах", "cannot_merge_people": "", "cannot_update_the_description": "", "cant_apply_changes": "", "cant_get_faces": "", "cant_search_people": "", "cant_search_places": "", - "change_date": "", + "change_date": "Огноо өөрчлөх", "change_expiration_time": "", - "change_location": "", - "change_name": "", - "change_name_successfully": "", - "change_password": "", + "change_location": "Байршил өөрчлөх", + "change_name": "Нэр өөрчлөх", + "change_name_successfully": "Нэр амжилттай өөрчлөгдлөө", + "change_password": "Нууц үг өөрчлөх", "change_your_password": "", "changed_visibility_successfully": "", "check_logs": "", - "city": "", - "clear": "", - "clear_all": "", + "city": "Хот", + "clear": "Цэвэрлэх", + "clear_all": "Бүгдийг цэвэрлэх", "clear_message": "", "clear_value": "", "close": "", @@ -371,7 +398,7 @@ "email": "", "empty": "", "empty_album": "", - "empty_trash": "", + "empty_trash": "Хогийн сав хоослох", "enable": "", "enabled": "", "end_date": "", @@ -392,7 +419,7 @@ "unable_to_delete_album": "", "unable_to_delete_asset": "", "unable_to_delete_user": "", - "unable_to_empty_trash": "", + "unable_to_empty_trash": "Хогийн савыг хоослож чадсангүй", "unable_to_enter_fullscreen": "", "unable_to_exit_fullscreen": "", "unable_to_hide_person": "", @@ -412,7 +439,7 @@ "unable_to_reset_password": "", "unable_to_resolve_duplicate": "", "unable_to_restore_assets": "", - "unable_to_restore_trash": "", + "unable_to_restore_trash": "Хогийн савнаас гаргаж чадсангүй", "unable_to_restore_user": "", "unable_to_save_album": "", "unable_to_save_name": "", @@ -437,13 +464,13 @@ "expand_all": "", "expire_after": "", "expired": "", - "explore": "", + "explore": "Эрж олох", "extension": "", "external_libraries": "", "failed_to_get_people": "", "favorite": "", "favorite_or_unfavorite_photo": "", - "favorites": "", + "favorites": "Дуртай", "feature": "", "feature_photo_updated": "", "featurecollection": "", @@ -485,7 +512,7 @@ "night_at_midnight": "", "night_at_twoam": "" }, - "invite_people": "", + "invite_people": "Хүмүүс урих", "invite_to_album": "", "job_settings_description": "", "jobs": "", @@ -497,7 +524,7 @@ "leave": "", "let_others_respond": "", "level": "", - "library": "", + "library": "Зургийн сан", "library_options": "", "light": "", "link_options": "", @@ -551,9 +578,9 @@ "no": "", "no_albums_message": "", "no_archived_assets_message": "", - "no_assets_message": "", + "no_assets_message": "Энд дарж та эхний зургаа хуулж үзэх үү", "no_exif_info_available": "", - "no_explore_results_message": "", + "no_explore_results_message": "Зураг хуулж оруулсаны дараа ашиглах боломжтой болно.", "no_favorites_message": "", "no_libraries_message": "", "no_name": "", @@ -570,7 +597,7 @@ "ok": "", "oldest_first": "", "online": "", - "only_favorites": "", + "only_favorites": "Зөвхөн дуртай зурагнууд", "only_refreshes_modified_files": "", "open_the_search_filters": "", "options": "", @@ -597,7 +624,7 @@ "pause_memories": "", "paused": "", "pending": "", - "people": "", + "people": "Хүмүүс", "people_sidebar_description": "", "perform_library_tasks": "", "permanent_deletion_warning": "", @@ -608,7 +635,7 @@ "photos_from_previous_years": "", "pick_a_location": "", "place": "", - "places": "", + "places": "Байршилууд", "play": "", "play_memories": "", "play_motion_photo": "", @@ -634,9 +661,11 @@ "refreshes_every_file": "", "remove": "", "remove_from_album": "", - "remove_from_favorites": "", + "remove_from_favorites": "Дуртай зурагнуудаас хасах", "remove_from_shared_link": "", "remove_offline_files": "", + "removed_from_favorites": "Дуртай зурагнуудаас хасагдсан", + "removed_from_favorites_count": "Дуртай зурагнуудаас {count, plural, other {Removed #}} хасагдлаа", "repair": "", "repair_no_results_message": "", "replace_with_upload": "", @@ -667,11 +696,11 @@ "search_country": "", "search_for_existing_person": "", "search_people": "", - "search_places": "", + "search_places": "Байршил хайх", "search_state": "", "search_timezone": "", "search_type": "", - "search_your_photos": "", + "search_your_photos": "Зурагнуудаасаа хайлт хийх", "searching_locales": "", "second": "", "select_album_cover": "", @@ -685,6 +714,7 @@ "selected": "", "send_message": "", "server": "", + "server_online": "Сервер Онлайн", "server_stats": "", "set": "", "set_as_album_cover": "", @@ -699,7 +729,7 @@ "shared_by": "", "shared_by_you": "", "shared_links": "", - "sharing": "", + "sharing": "Хуваалцах", "sharing_sidebar_description": "", "show_album_options": "", "show_file_location": "", @@ -715,6 +745,7 @@ "show_progress_bar": "", "show_search_options": "", "shuffle": "", + "sign_out": "Гарах", "sign_up": "", "size": "", "skip_to_content": "", @@ -728,8 +759,9 @@ "state": "", "status": "", "stop_motion_photo": "", - "storage": "", + "storage": "Дискний багтаамж", "storage_label": "", + "storage_usage": "Нийт {available} боломжтойгоос {used} хэрэглэсэн", "submit": "", "suggestions": "", "sunrise_on_the_beach": "", @@ -745,7 +777,7 @@ "toggle_theme": "", "toggle_visibility": "", "total_usage": "", - "trash": "", + "trash": "Хогийн сав", "trash_all": "", "trash_no_results_message": "", "type": "", @@ -762,7 +794,7 @@ "unstack": "", "up_next": "", "updated_password": "", - "upload": "", + "upload": "Зураг хуулах", "upload_concurrency": "", "url": "", "usage": "", @@ -771,15 +803,15 @@ "user_usage_detail": "", "username": "", "users": "", - "utilities": "", + "utilities": "Багаж хэрэгсэл", "validate": "", "variables": "", "version": "", "video": "", "video_hover_setting_description": "", "videos": "", - "view_all": "", - "view_all_users": "", + "view_all": "Бүгдийг харах", + "view_all_users": "Бүх хэрэглэгчийг харах", "view_links": "", "view_next_asset": "", "view_previous_asset": "", diff --git a/web/src/lib/i18n/nb_NO.json b/web/src/lib/i18n/nb_NO.json index b3851a22477fc..df56d27a237bf 100644 --- a/web/src/lib/i18n/nb_NO.json +++ b/web/src/lib/i18n/nb_NO.json @@ -25,7 +25,7 @@ "add_to_shared_album": "Legg til delt album", "added_to_archive": "Lagt til i arkiv", "added_to_favorites": "Lagt til i favoritter", - "added_to_favorites_count": "Lagt til {count} i favoritter", + "added_to_favorites_count": "Lagt til {count, number} i favoritter", "admin": { "add_exclusion_pattern_description": "Legg til ekskluderingsmønstre. Globbing med *, ** og ? støttes. For å ignorere alle filer i en hvilken som helst mappe som heter \"Raw\", bruk \"**/Raw/**\". For å ignorere alle filer som slutter på \".tif\", bruk \"**/*.tif\". For å ignorere en absolutt filplassering, bruk \"/filbane/til/ignorer/**\".", "authentication_settings": "Autentiserings innstillinger", @@ -128,6 +128,7 @@ "map_dark_style": "Mørk stil", "map_enable_description": "Aktiver kartfunksjoner", "map_gps_settings": "Kart & GPS Innstillinger", + "map_gps_settings_description": "Administrer innstillinger for kart og GPS (Reversert geokoding)", "map_light_style": "Lys stil", "map_reverse_geocoding": "Omvendt geokoding", "map_reverse_geocoding_enable_description": "Aktiver omvendt geokoding", @@ -319,6 +320,7 @@ "user_settings_description": "Administrer brukerinnstillinger", "user_successfully_removed": "Brukeren {email} er nå fjernet.", "version_check_enabled_description": "Aktiver periodiske forespørsler til GitHub for å sjekke etter nye utgivelser", + "version_check_implications": "Versjonssjekkfunksjonen baserer seg på periodisk kommunikasjon med github.com", "version_check_settings": "Versjonssjekk", "version_check_settings_description": "Aktiver/deaktiver varsel om ny versjon", "video_conversion_job": "Transkod videoer", @@ -334,6 +336,7 @@ "album_added_notification_setting_description": "Motta en e-postvarsling når du legges til i et delt album", "album_cover_updated": "Albumomslag oppdatert", "album_delete_confirmation": "Er du sikker på at du vil slette albumet {album}?\nHvis dette albumet er delt, vil ikke andre brukere ha tilgang til det lenger.", + "album_delete_confirmation_description": "Hvis dette albumet deles, vil andre brukere miste tilgangen til dette.", "album_info_updated": "Albuminformasjon oppdatert", "album_leave": "Forlate album?", "album_leave_confirmation": "Er du sikker på at du vil forlate {album}?", @@ -357,6 +360,7 @@ "allow_edits": "Tillat redigering", "allow_public_user_to_download": "Tillat uautentiserte brukere å laste ned", "allow_public_user_to_upload": "Tillat uautentiserte brukere å laste opp", + "anti_clockwise": "Mot klokken", "api_key": "API Nøkkel", "api_key_description": "Denne verdien vil vises kun én gang. Pass på å kopiere den før du lukker vinduet.", "api_key_empty": "API Key-navnet bør ikke være tomt", diff --git a/web/src/lib/i18n/nl.json b/web/src/lib/i18n/nl.json index 36f9886b04d29..d448f1144fd1b 100644 --- a/web/src/lib/i18n/nl.json +++ b/web/src/lib/i18n/nl.json @@ -338,7 +338,8 @@ "album_added": "Album toegevoegd", "album_added_notification_setting_description": "Ontvang een e-mailmelding wanneer je aan een gedeeld album wordt toegevoegd", "album_cover_updated": "Album cover is bijgewerkt", - "album_delete_confirmation": "Weet je zeker dat je het album {album} wilt verwijderen?\nAls dit album gedeeld is, hebben andere gebruikers er geen toegang meer toe.", + "album_delete_confirmation": "Weet je zeker dat je het album {album} wilt verwijderen?", + "album_delete_confirmation_description": "Als dit album gedeeld is, hebben andere gebruikers er geen toegang meer toe.", "album_info_updated": "Albumgegevens bijgewerkt", "album_leave": "Album verlaten?", "album_leave_confirmation": "Weet je zeker dat je {album} wilt verlaten?", @@ -362,6 +363,7 @@ "allow_edits": "Bewerkingen toestaan", "allow_public_user_to_download": "Sta openbare gebruiker toe om te downloaden", "allow_public_user_to_upload": "Sta openbare gebruiker toe om te uploaden", + "anti_clockwise": "Linksom", "api_key": "API sleutel", "api_key_description": "Deze waarde wordt slechts één keer getoond. Zorg ervoor dat je deze kopieert voordat je het venster sluit.", "api_key_empty": "De naam van uw API sleutel mag niet leeg zijn", @@ -443,6 +445,7 @@ "clear_all_recent_searches": "Wis alle recente zoekopdrachten", "clear_message": "Bericht wissen", "clear_value": "Waarde wissen", + "clockwise": "Rechtsom", "close": "Sluiten", "collapse": "Inklappen", "collapse_all": "Alles inklappen", @@ -519,6 +522,8 @@ "do_not_show_again": "Laat dit bericht niet meer zien", "done": "Klaar", "download": "Downloaden", + "download_include_embedded_motion_videos": "Ingesloten video's", + "download_include_embedded_motion_videos_description": "Voeg video's toe die ingesloten zijn in bewegende foto's als een apart bestand", "download_settings": "Downloaden", "download_settings_description": "Beheer instellingen voor het downloaden van assets", "downloading": "Downloaden", @@ -552,6 +557,10 @@ "edit_user": "Gebruiker bewerken", "edited": "Bijgewerkt", "editor": "Bewerker", + "editor_close_without_save_prompt": "De wijzigingen worden niet opgeslagen", + "editor_close_without_save_title": "Editor sluiten?", + "editor_crop_tool_h2_aspect_ratios": "Beeldverhoudingen", + "editor_crop_tool_h2_rotation": "Rotatie", "email": "E-mailadres", "empty": "", "empty_album": "Leeg album", @@ -701,6 +710,7 @@ "expired": "Verlopen", "expires_date": "Verloopt {date}", "explore": "Verkennen", + "explorer": "Verkenner", "export": "Exporteren", "export_as_json": "Exporteren als JSON", "extension": "Extensie", @@ -722,6 +732,7 @@ "filter_people": "Filter op mensen", "find_them_fast": "Vind ze snel op naam door te zoeken", "fix_incorrect_match": "Onjuiste overeenkomst corrigeren", + "folders": "Mappen", "force_re-scan_library_files": "Forceer herscan van alle bibliotheekbestanden", "forward": "Vooruit", "general": "Algemeen", @@ -923,7 +934,7 @@ "only_favorites": "Alleen favorieten", "only_refreshes_modified_files": "Vernieuwt alleen gewijzigde bestanden", "open_in_map_view": "Openen in kaartweergave", - "open_in_openstreetmap": "Openen met OpenStreetMap", + "open_in_openstreetmap": "Openen in OpenStreetMap", "open_the_search_filters": "Open de zoekfilters", "options": "Opties", "or": "of", @@ -1028,6 +1039,8 @@ "purchase_settings_server_activated": "De productcode van de server wordt beheerd door de beheerder", "range": "", "rating": "Ster waardering", + "rating_clear": "Waardering verwijderen", + "rating_count": "{count, plural, one {# ster} other {# sterren}}", "rating_description": "De exif-waardering weergeven in het infopaneel", "raw": "", "reaction_options": "Reactie opties", @@ -1227,7 +1240,7 @@ "to_login": "Inloggen", "to_trash": "Prullenbak", "toggle_settings": "Zichtbaarheid instellingen wisselen", - "toggle_theme": "Thema wisselen", + "toggle_theme": "Donker thema toepassen", "toggle_visibility": "Zichtbaarheid wisselen", "total_usage": "Totaal gebruik", "trash": "Prullenbak", @@ -1249,6 +1262,7 @@ "unlink_oauth": "Ontkoppel OAuth", "unlinked_oauth_account": "OAuth account ontkoppeld", "unnamed_album": "Naamloos album", + "unnamed_album_delete_confirmation": "Weet je zeker dat je dit album wilt verwijderen?", "unnamed_share": "Naamloze deellink", "unsaved_change": "Niet-opgeslagen wijziging", "unselect_all": "Alles deselecteren", diff --git a/web/src/lib/i18n/pl.json b/web/src/lib/i18n/pl.json index 682f6fcb55ab1..267afd0141634 100644 --- a/web/src/lib/i18n/pl.json +++ b/web/src/lib/i18n/pl.json @@ -57,7 +57,7 @@ "image_format_description": "Użycie formatu WebP skutkuje utworzeniem plików o rozmiarze mniejszym niż w przypadku JPEG ale jego kodowanie trwa dłużej.", "image_prefer_embedded_preview": "Preferuj podgląd wbudowany", "image_prefer_embedded_preview_setting_description": "Jeśli to możliwe, używaj osadzonych podglądów w zdjęciach RAW jako danych wejściowych do przetwarzania obrazu. Może to zapewnić dokładniejsze kolory w przypadku niektórych obrazów, ale jakość podglądu zależy od aparatu, a obraz może zawierać więcej artefaktów kompresji.", - "image_prefer_wide_gamut": "Preferuj szeroką przestrzeń barw", + "image_prefer_wide_gamut": "Preferuj szeroką gamę kolorów", "image_prefer_wide_gamut_setting_description": "Do wyświetlania miniatur użyj wyświetlacza P3. Dzięki temu lepiej zachowuje się intensywność obrazów o dużej ilości kolorów, ale obrazy mogą wyglądać inaczej na starych urządzeniach ze starą wersją przeglądarki. Obrazy sRGB są zachowywane jako sRGB, aby uniknąć przesunięć kolorów.", "image_preview_format": "Format podglądu", "image_preview_resolution": "Rozdzielczość podglądu", @@ -129,6 +129,7 @@ "map_enable_description": "Włącz funkcję mapy", "map_gps_settings": "Mapa i ustawienia lokalizacji", "map_gps_settings_description": "Zarządzaj mapą oraz ustawieniami odwróconego geokodowania", + "map_implications": "Funkcja mapy opiera się na zewnętrznej usłudze kafelków (tiles.immich.cloud)", "map_light_style": "Styl jasny", "map_manage_reverse_geocoding_settings": "Zarządzaj Ustawieniem Odwrotne Geokodowanie", "map_reverse_geocoding": "Odwrotne Geokodowanie", @@ -320,7 +321,8 @@ "user_settings": "Ustawienia Użytkownika", "user_settings_description": "Zarządzaj ustawieniami użytkownika", "user_successfully_removed": "Użytkownik {email} został usunięty pomyślnie.", - "version_check_enabled_description": "Włącz cykliczne sprawdzanie nowych wersji na GitHubie", + "version_check_enabled_description": "Włącz sprawdzanie wersji", + "version_check_implications": "Funkcja sprawdzania wersji opiera się na okresowej komunikacji z github.com", "version_check_settings": "Sprawdzenie Wersji", "version_check_settings_description": "Włącz/wyłącz powiadomienie o nowej wersji", "video_conversion_job": "Transkodowanie wideo", @@ -336,7 +338,8 @@ "album_added": "Album udostępniony", "album_added_notification_setting_description": "Otrzymaj powiadomienie email, gdy zostanie Ci udostępniony album", "album_cover_updated": "Okładka albumu została zaktualizowana", - "album_delete_confirmation": "Na pewno chcesz usunąć album {album}?\nJeśli został udostępniony, inni użytkownicy nie będą w stanie go obejrzeć.", + "album_delete_confirmation": "Czy na pewno chcesz usunąć album {album}?", + "album_delete_confirmation_description": "Jeżeli album jest udostępniany, inny stracą do niego dostęp.", "album_info_updated": "Szczegóły albumu zostały zaktualizowane", "album_leave": "Opuścić album?", "album_leave_confirmation": "Na pewno chcesz opuścić {album}?", @@ -360,6 +363,7 @@ "allow_edits": "Pozwól edytować", "allow_public_user_to_download": "Zezwól użytkownikowi publicznemu na pobieranie", "allow_public_user_to_upload": "Zezwól użytkownikowi publicznemu na przesyłanie plików", + "anti_clockwise": "Przeciwnie do ruchu wskazówek zegara", "api_key": "Klucz API", "api_key_description": "Widzisz tę wartość po raz pierwszy i ostatni, więc lepiej ją skopiuj przed zamknięciem okna.", "api_key_empty": "Twój Klucz API nie powinien być pusty", @@ -368,8 +372,8 @@ "appears_in": "W albumach", "archive": "Archiwum", "archive_or_unarchive_photo": "Dodaj lub usuń zasób z archiwum", - "archive_size": "Maksymalny Rozmiar Archiwum", - "archive_size_description": "Podziel pobierane pliki na więcej niż jedno archiwum, jeżeli rozmiar archiwum przekroczy tą wartość w GiB", + "archive_size": "Rozmiar archiwum", + "archive_size_description": "Podziel pobierane pliki na więcej niż jedno archiwum, jeżeli rozmiar archiwum przekroczy tę wartość w GiB", "archived": "Zarchiwizowano", "archived_count": "{count, plural, other {Zarchiwizowano #}}", "are_these_the_same_person": "Czy to jedna i ta sama osoba?", @@ -405,7 +409,7 @@ "birthdate_saved": "Data urodzenia zapisana pomyślnie", "birthdate_set_description": "Data urodzenia jest używana do obliczenia wieku danej osoby podczas wykonania zdjęcia.", "blurred_background": "Rozmyte tło", - "build": "Build", + "build": "Kompilacja", "build_image": "Obraz Buildu", "bulk_delete_duplicates_confirmation": "Czy na pewno chcesz trwale usunąć {count, plural, one {# zduplikowany zasób} few {# zduplikowane zasoby} many {# zduplikowanych zasobów} other {# zduplikowanych zasobów}}? Zostanie zachowany największy zasób z każdej grupy, a wszystkie pozostałe duplikaty zostaną trwale usunięte. Nie można cofnąć tej operacji!", "bulk_keep_duplicates_confirmation": "Czy na pewno chcesz zachować {count, plural, one {# zduplikowany zasób} few {# zduplikowane zasoby} many {# zduplikowanych zasobów} other {# zduplikowanych zasobów}}? To spowoduje rozwiązanie wszystkich grup duplikatów bez usuwania czegokolwiek.", @@ -441,6 +445,7 @@ "clear_all_recent_searches": "Usuń ostatnio wyszukiwane", "clear_message": "Zamknij wiadomość", "clear_value": "Wyczyść wartość", + "clockwise": "Zgodnie z ruchem wskazówek zegara", "close": "Zamknij", "collapse": "Zwiń", "collapse_all": "Zwiń wszystko", @@ -517,6 +522,8 @@ "do_not_show_again": "Nie pokazuj więcej tej wiadomości", "done": "Gotowe", "download": "Pobierz", + "download_include_embedded_motion_videos": "Osadzone filmy", + "download_include_embedded_motion_videos_description": "Dołącz filmy osadzone w ruchomych zdjęciach jako oddzielny plik", "download_settings": "Pobieranie", "download_settings_description": "Zarządzaj pobieraniem zasobów", "downloading": "Pobieranie", @@ -550,6 +557,10 @@ "edit_user": "Edytuj użytkownika", "edited": "Edytowane", "editor": "Edytor", + "editor_close_without_save_prompt": "Zmiany nie zostaną zapisane", + "editor_close_without_save_title": "Zamknąć edytor?", + "editor_crop_tool_h2_aspect_ratios": "Proporcje obrazu", + "editor_crop_tool_h2_rotation": "Obrót", "email": "E-mail", "empty": "", "empty_album": "Pusty Album", @@ -692,7 +703,7 @@ "every_night_at_midnight": "", "every_night_at_twoam": "", "every_six_hours": "", - "exif": "Exif", + "exif": "Metadane EXIF", "exit_slideshow": "Zamknij Pokaz Slajdów", "expand_all": "Rozwiń wszystko", "expire_after": "Wygasa po", @@ -720,6 +731,7 @@ "filter_people": "Szukaj osoby", "find_them_fast": "Wyszukuj szybciej przypisując nazwę", "fix_incorrect_match": "Napraw nieprawidłowe dopasowanie", + "folders": "Foldery", "force_re-scan_library_files": "Wymuś ponowne przeskanowanie wszystkich plików biblioteki", "forward": "Do przodu", "general": "Ogólne", @@ -887,6 +899,7 @@ "ok": "Ok", "oldest_first": "Od najstarszych", "onboarding": "Wdrożenie", + "onboarding_privacy_description": "Śledzenie (opcjonalne) funkcja opiera się na zewnętrznych usługach i może zostać wyłączona w dowolnym momencie w ustawieniach administracyjnych.", "onboarding_theme_description": "Wybierz motyw kolorystyczny dla twojej instancji. Możesz go później zmienić w ustawieniach.", "onboarding_welcome_description": "Przejdźmy do konfiguracji twojej instancji, ustawiając kilka powszechnych opcji.", "onboarding_welcome_user": "Witaj, {user}", @@ -959,6 +972,7 @@ "previous_memory": "Poprzednie wspomnienie", "previous_or_next_photo": "Poprzednie lub następne zdjęcie", "primary": "Główny", + "privacy": "Prywatność", "profile_image_of_user": "Zdjęcie profilowe {user}", "profile_picture_set": "Zdjęcie profilowe ustawione.", "public_album": "Publiczny album", @@ -996,6 +1010,9 @@ "purchase_server_title": "Serwer", "purchase_settings_server_activated": "Klucz produktu serwera jest zarządzany przez administratora", "range": "", + "rating": "Ocena gwiazdkowa", + "rating_count": "{count, plural, one {# star} other {# stars}}", + "rating_description": "Wyświetl ocenę EXIF w panelu informacji", "raw": "", "reaction_options": "Opcje reakcji", "read_changelog": "Zobacz Zmiany", @@ -1118,6 +1135,7 @@ "shared_by_user": "Udostępnione przez {user}", "shared_by_you": "Udostępnione przez ciebie", "shared_from_partner": "Zdjęcia od {partner}", + "shared_link_options": "Opcje udostępniania linku", "shared_links": "Udostępnione linki", "shared_photos_and_videos_count": "{assetCount, plural, other {# udostępnione zdjęcia i filmy.}}", "shared_with_partner": "Dzielisz się z {partner}", @@ -1163,7 +1181,7 @@ "stack_select_one_photo": "Wybierz jedno główne zdjęcie do stosu", "stack_selected_photos": "Układaj wybrane zdjęcia", "stacked_assets_count": "Ułożone {count, plural, one {# zasób} other{# zasoby}}", - "stacktrace": "Stacktrace", + "stacktrace": "Ślad stosu", "start": "Start", "start_date": "Od dnia", "state": "Stan", @@ -1193,7 +1211,7 @@ "to_login": "Login", "to_trash": "Kosz", "toggle_settings": "Przełącz ustawienia", - "toggle_theme": "Przełącz motyw", + "toggle_theme": "Przełącz ciemny motyw", "toggle_visibility": "Zmień widoczność", "total_usage": "Całkowite wykorzystanie", "trash": "Kosz", @@ -1215,6 +1233,7 @@ "unlink_oauth": "Odłącz OAuth", "unlinked_oauth_account": "Odłączone konto OAuth", "unnamed_album": "Nienazwany album", + "unnamed_album_delete_confirmation": "Czy jesteś pewna/pewien, że chcesz usunąć te album?", "unnamed_share": "Nienazwany udział", "unsaved_change": "Niezapisana zmiana", "unselect_all": "Odznacz wszystko", diff --git a/web/src/lib/i18n/pt.json b/web/src/lib/i18n/pt.json index b146e2ee2fbda..943cde377dca9 100644 --- a/web/src/lib/i18n/pt.json +++ b/web/src/lib/i18n/pt.json @@ -129,12 +129,13 @@ "map_enable_description": "Ativar recursos do mapa", "map_gps_settings": "Mapas e Definições de GPS", "map_gps_settings_description": "Configurações de mapas e GPS (Geocoding inverso)", + "map_implications": "A funcionalidade do mapa necessita um servico externo (tiles.immich.cloud)", "map_light_style": "Tema Claro", "map_manage_reverse_geocoding_settings": "Gerir definições de Geocoding inverso", "map_reverse_geocoding": "Geocodificação reversa", "map_reverse_geocoding_enable_description": "Ativar geocodificação reversa", "map_reverse_geocoding_settings": "Configurações de geocodificação reversa", - "map_settings": "Configurações de mapas e GPS", + "map_settings": "Mapa", "map_settings_description": "Gerenciar configurações do mapa", "map_style_description": "URL para um tema de mapa style.json", "metadata_extraction_job": "Extrair metadados", @@ -217,6 +218,7 @@ "sidecar_job_description": "Descubra ou sincronize metadados secundários do sistema de arquivos", "slideshow_duration_description": "Tempo em segundos para exibir cada imagem", "smart_search_job_description": "Execute a aprendizagem automática em arquivos para oferecer suporte à pesquisa inteligente", + "storage_template_date_time_description": "O registro de data e hora da criação é usado para fornecer essas informações", "storage_template_date_time_sample": "Exemplo de tempo {date}", "storage_template_enable_description": "Habilitar mecanismo de modelo de armazenamento", "storage_template_hash_verification_enabled": "Verificação de hash ativada", @@ -315,10 +317,12 @@ "user_password_has_been_reset": "A senha do utilizador foi redefinida:", "user_password_reset_description": "Forneça a senha temporária ao utilizador e informe que ele precisará alterar a senha no próximo início de sessão.", "user_restore_description": "A conta de {user} será restaurada.", + "user_restore_scheduled_removal": "Restaurar usuário - planejar remoção em {date, date, long}", "user_settings": "Configurações do Utilizador", "user_settings_description": "Gerenciar configurações do utilizador", "user_successfully_removed": "O utilizador {email} foi removido com sucesso.", - "version_check_enabled_description": "Ativa verificações periódicas no GitHub para novas versões", + "version_check_enabled_description": "Ativa verificação de novas versões", + "version_check_implications": "A funcionalidade de verificação da versão necessita comunicação periodica com github.com", "version_check_settings": "Verificação de versão", "version_check_settings_description": "Ativar/desativar a notificação de nova versão", "video_conversion_job": "Transcodificar vídeos", @@ -334,7 +338,8 @@ "album_added": "Álbum adicionado", "album_added_notification_setting_description": "Receba uma notificação por e-mail quando você for adicionado a um álbum compartilhado", "album_cover_updated": "Capa do álbum atualizada", - "album_delete_confirmation": "De certeza que quer apagar o álbum {album}?\nSe o álbum for partilhado, os outros utilizadores não poderão acessá-lo novamente.", + "album_delete_confirmation": "Tem a certeza que quer apagar o álbum {album}? Se o álbum for partilhado, os outros utilizadores não poderão aceder-lhe novamente.", + "album_delete_confirmation_description": "Se este álbum for partilhado, os outros utilizadores deixam de poder aceder.", "album_info_updated": "Informações do álbum atualizadas", "album_leave": "Sair do álbum?", "album_leave_confirmation": "Tem a certeza que quer sair de {album}?", @@ -347,6 +352,7 @@ "album_updated_setting_description": "Receba uma notificação por e-mail quando um álbum compartilhado tiver novos arquivos", "album_user_left": "Saída {album}", "album_user_removed": "Utilizador {user} removido", + "album_with_link_access": "Permite acesso a fotos e pessoas deste album por qualquer pessoa com o link.", "albums": "Álbuns", "albums_count": "{count, plural, one {{count, number} Álbum} other {{count, number} Álbuns}}", "all": "Todos", @@ -355,23 +361,34 @@ "all_videos": "Todos os vídeos", "allow_dark_mode": "Permitir modo escuro", "allow_edits": "Permitir edições", + "allow_public_user_to_download": "Permit acesso de download ao user publico", + "allow_public_user_to_upload": "Permite acesso de upload ao user publico", + "anti_clockwise": "Sentido anti-horário", "api_key": "Chave de API", "api_key_description": "Este valor será apresentado apenas uma única vez. Por favor, certifique-se que o copiou antes de fechar a janela.", + "api_key_empty": "O nome da API Key não pode ser vazio", "api_keys": "Chaves de API", "app_settings": "Configurações do Aplicativo", "appears_in": "Aparece em", - "archive": "Arquivados", + "archive": "Arquivo", "archive_or_unarchive_photo": "Arquivar ou desarquivar foto", - "archive_size": "Tamanho do Arquivo", + "archive_size": "Tamanho do arquivo", "archive_size_description": "Configure o tamanho do arquivo para downloads (em GiB)", "archived": "Arquivado", + "archived_count": "{count, plural, other {Arquivado #}}", "are_these_the_same_person": "São a mesma pessoa?", + "are_you_sure_to_do_this": "Tem a certeza que quer fazer isto?", "asset_added_to_album": "Adicionado ao álbum", "asset_adding_to_album": "A adicionar ao álbum...", "asset_description_updated": "A descrição do arquivo foi atualizada", "asset_filename_is_offline": "O arquivo {filename} está offline", "asset_has_unassigned_faces": "O arquivo tem rostos sem atribuição", + "asset_hashing": "Hashing...", "asset_offline": "Ativo off-line", + "asset_offline_description": "Este arquivo está offline. Immich não consegue acessar o local do arquivo. Certifique-se de que o arquivo esteja disponível e, em seguida, escaneie a biblioteca novamente.", + "asset_skipped": "Ignorado", + "asset_uploaded": "Enviado", + "asset_uploading": "Em upload...", "assets": "Arquivos", "assets_added_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}}", "assets_added_to_album_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}} ao álbum", @@ -381,14 +398,19 @@ "assets_moved_to_trash_count": "{count, plural, one {# arquivo movido} other {# arquivos movidos}} para a lixeira", "assets_permanently_deleted_count": "{count, plural, one {# arquivo} other {# arquivos}} excluídos permanentemente", "assets_removed_count": "{count, plural, one {# arquivo excluído} other {# arquivos excluídos}}", + "assets_restore_confirmation": "Tem a certeza que quer recuperar todos os artigos apagados? Não é possivel voltar atrás nesta acção!", "assets_restored_count": "{count, plural, one {# arquivo restaurado} other {# arquivos restaurados}}", "assets_trashed_count": "{count, plural, one {# arquivo enviado} other {# arquivos enviados}} para a lixeira", + "assets_were_part_of_album_count": "{count, plural, one {Arquivo já era} other {Os arquivos já eram}} parte do álbum", "authorized_devices": "Dispositivos Autorizados", "back": "Voltar", + "back_close_deselect": "Voltar, fechar ou desmarcar", "backward": "Para trás", "birthdate_saved": "Data de nascimento guardada com sucesso", "birthdate_set_description": "A data de nascimento é usada para calcular a idade desta pessoa no momento em que uma fotografia foi tirada.", "blurred_background": "Fundo desfocado", + "build": "Construir", + "build_image": "Construir Imagem", "bulk_delete_duplicates_confirmation": "Tem a certeza de que deseja excluir {count, plural, one {# arquivo duplicado} other {# arquivos duplicados}}? Esta ação mantém o maior arquivo de cada grupo e exclui permanentemente todas as outras duplicidades. Você não pode desfazer esta ação!", "bulk_keep_duplicates_confirmation": "Tem certeza de que deseja manter {count, plural, one {# arquivo duplicado} other {# arquivos duplicados}}? Isso resolverá todos os grupos duplicados sem excluir nada.", "bulk_trash_duplicates_confirmation": "Tem a certeza de que deseja mover para a lixeira {count, plural, one {# arquivo duplicado} other {# arquivos duplicados}}? Isso manterá o maior arquivo de cada grupo e moverá para a lixeira todas as outras duplicidades.", @@ -399,6 +421,7 @@ "cancel": "Cancelar", "cancel_search": "Cancelar pesquisa", "cannot_merge_people": "Não é possível mesclar pessoas", + "cannot_undo_this_action": "Não pode voltar atrás nesta ação!", "cannot_update_the_description": "Não é possível atualizar a descrição", "cant_apply_changes": "Não é possível aplicar alterações", "cant_get_faces": "Não foi possível obter faces", @@ -410,6 +433,7 @@ "change_name": "Alterar nome", "change_name_successfully": "Nome alterado com sucesso", "change_password": "Mudar a senha", + "change_password_description": "Esta é a primeira vez que você está entrando no sistema ou uma solicitação foi feita para alterar sua senha. Insira a nova senha abaixo.", "change_your_password": "Alterar sua senha", "changed_visibility_successfully": "Visibilidade alterada com sucesso", "check_all": "Verificar tudo", @@ -421,12 +445,14 @@ "clear_all_recent_searches": "Limpar todas as pesquisas recentes", "clear_message": "Limpar mensagem", "clear_value": "Limpar valor", + "clockwise": "Sentido horário", "close": "Fechar", "collapse": "Colapsar", "collapse_all": "Colapsar tudo", "color_theme": "Tema de cores", "comment_deleted": "Comentário eliminado", "comment_options": "Opções de comentário", + "comments_and_likes": "Comentários e gostos", "comments_are_disabled": "Comentários estão desativados", "confirm": "Confirmar", "confirm_admin_password": "Confirmar senha de administrador", @@ -452,7 +478,9 @@ "create_library": "Criar biblioteca", "create_link": "Criar link", "create_link_to_share": "Criar link para partilhar", + "create_link_to_share_description": "Permiter a visualização desta imagem(s) a qualquer pessoa com este link", "create_new_person": "Criar nova pessoa", + "create_new_person_hint": "Associe os arquivos para uma nova pessoa", "create_new_user": "Criar novo utilizador", "create_user": "Criar utilizador", "created": "Criado", @@ -494,10 +522,13 @@ "do_not_show_again": "Não mostrar esta mensagem novamente", "done": "Feito", "download": "Transferir", + "download_include_embedded_motion_videos": "Vídeos incorporados", + "download_include_embedded_motion_videos_description": "Incluir vídeos incorporados em fotos em movimento como um arquivo separado", "download_settings": "Transferir", "download_settings_description": "Gerenciar configurações relacionadas a transferir ativos", "downloading": "Baixando", "downloading_asset_filename": "A transferir o arquivo {filename}", + "drop_files_to_upload": "Coloque os ficheiros em qualquer lugar para fazer o upload", "duplicates": "Duplicados", "duplicates_description": "Marque cada grupo indicando quais arquivos, se algum, são duplicados", "duration": "Duração", @@ -526,10 +557,15 @@ "edit_user": "Editar utilizador", "edited": "Editado", "editor": "Editar", + "editor_close_without_save_prompt": "As alterações não serão salvas", + "editor_close_without_save_title": "Fechar editor?", + "editor_crop_tool_h2_aspect_ratios": "Proporções de aspecto", + "editor_crop_tool_h2_rotation": "Rotação", "email": "E-mail", "empty": "", "empty_album": "", "empty_trash": "Esvaziar lixo", + "empty_trash_confirmation": "Tem certeza de que deseja esvaziar a lixeira? Isso removerá todos os arquivos da lixeira do Immich permanentemente.\nVocê não pode desfazer esta ação!", "enable": "Ativar", "enabled": "Ativado", "end_date": "Data final", @@ -537,7 +573,11 @@ "error_loading_image": "Erro ao carregar a página", "error_title": "Erro - Algo correu mal", "errors": { + "cannot_navigate_next_asset": "Não pode navegar para o proximo artigo", + "cannot_navigate_previous_asset": "Não pode navegar para o artigo anterior", "cant_apply_changes": "Não foi possível aplicar as alterações", + "cant_change_activity": "Não é possível {enabled, select, true {desativar} other {ativar}} atividade", + "cant_change_asset_favorite": "Não pode alterar o favorito deste artigo", "cant_change_metadata_assets_count": "Não foi possível alterar os metadados de {count, plural, one {# arquivo} other {# arquivos}}", "cant_get_faces": "Não foi possível obter os rostos", "cant_get_number_of_comments": "Não foi possível obter o número de comentários", @@ -545,34 +585,49 @@ "cant_search_places": "Não foi possível pesquisar locais", "cleared_jobs": "Trabalhos eliminados para: {job}", "error_adding_assets_to_album": "Erro ao adicionar arquivos ao álbum", + "error_adding_users_to_album": "Erro a adicionar utilizador ao album", + "error_deleting_shared_user": "Error a apagar o utilizador partilhado", "error_downloading": "Erro a transferir {filename}", "error_hiding_buy_button": "Erro ao esconder botão de compra", + "error_removing_assets_from_album": "Erro a eliminar artigos do album, verifique a consola para mais detalhes", "error_selecting_all_assets": "Erro ao selecionar todos os arquivos", "exclusion_pattern_already_exists": "Este padrão de exclusão já existe.", "failed_job_command": "Comando {command} falhou para o trabalho: {job}", "failed_to_create_album": "Falha ao criar álbum", + "failed_to_create_shared_link": "Falhou a criar um link partilhado", + "failed_to_edit_shared_link": "Falhou a editar o link partilhado", "failed_to_get_people": "Falha na obtenção de pessoas", "failed_to_load_asset": "Falha ao carregar arquivo", "failed_to_load_assets": "Falha ao carregar arquivos", "failed_to_load_people": "Falha ao carregar pessoas", "failed_to_remove_product_key": "Falha ao remover chave de produto", + "failed_to_stack_assets": "Falha ao empilhar os arquivos", + "failed_to_unstack_assets": "Falha ao desempilhar arquivos", "import_path_already_exists": "Este caminho de importação já existe.", + "incorrect_email_or_password": "Email ou password incorretos", "paths_validation_failed": "a validação de {paths, plural, one {# caminho falhou} other {# caminhos falharam}}", + "profile_picture_transparent_pixels": "Imagem de perfil não pode ter pixels transparentes. Por favor faça zoom in e/ou mova a imagem.", "quota_higher_than_disk_size": "Você definiu uma cota maior do que o tamanho do disco", "repair_unable_to_check_items": "Não foi possível verificar {count, select, one {um item} other {alguns itens}}", "unable_to_add_album_users": "Não foi possível adicionar utilizadores ao álbum", + "unable_to_add_assets_to_shared_link": "Não foi possivel adicionar os artigos ao link partilhado", "unable_to_add_comment": "Não foi possível adicionar o comentário", "unable_to_add_exclusion_pattern": "Não foi possível adicionar o padrão de exclusão", "unable_to_add_import_path": "Não foi possível adicionar o caminho de importação", "unable_to_add_partners": "Não foi possível adicionar parceiros", + "unable_to_add_remove_archive": "Não é possível {archived, select, true {remover o arquivo de} other {adicionar o arquivo}}", "unable_to_add_remove_favorites": "Não foi possível {favorite, select, true {adicionar arquivo aos} other {remover arquivo dos}} favoritos", + "unable_to_archive_unarchive": "Não é possível {archived, select, true {arquivar} other {desarquivar}}", "unable_to_change_album_user_role": "Não foi possível alterar a permissão do utilizador no álbum", "unable_to_change_date": "Não foi possível alterar a data", + "unable_to_change_favorite": "Não foi possivel mudar o favorito do artigo", "unable_to_change_location": "Não foi possível alterar a localização", "unable_to_change_password": "Não foi possível alterar a senha", + "unable_to_change_visibility": "Não é possível alterar a visibilidade de {count, plural, one {# pessoa} other {# pessoas}}", "unable_to_check_item": "", "unable_to_check_items": "", "unable_to_complete_oauth_login": "Não foi possível completar início de sessão com OAuth", + "unable_to_connect": "Não é possível conectar", "unable_to_connect_to_server": "Não foi possível ligar ao servidor", "unable_to_copy_to_clipboard": "Não é possível copiar para a área de transferência, certifique-se que está acessando a pagina através de https", "unable_to_create_admin_account": "Não foi possível criar conta de administrador", @@ -593,6 +648,7 @@ "unable_to_enter_fullscreen": "Não foi possível entrar em modo de tela cheia", "unable_to_exit_fullscreen": "Não foi possível sair do modo de tela cheia", "unable_to_get_comments_number": "Não foi possível obter número de comentários", + "unable_to_get_shared_link": "Falha ao obter link compartilhado", "unable_to_hide_person": "Não foi possível esconder a pessoa", "unable_to_link_oauth_account": "Não foi possível associar a conta OAuth", "unable_to_load_album": "Não foi possível carregar o álbum", @@ -603,9 +659,12 @@ "unable_to_log_out_device": "Não foi possível terminar a sessão no dispositivo", "unable_to_login_with_oauth": "Não foi possível iniciar sessão com OAuth", "unable_to_play_video": "Não foi possível reproduzir o vídeo", + "unable_to_reassign_assets_existing_person": "Não é possível reatribuir arquivos para {name, select, null {uma pessoa existente} other {{name}}}", + "unable_to_reassign_assets_new_person": "Não é possível reatribuir os arquivos a uma nova pessoa", "unable_to_refresh_user": "Não foi possível atualizar o utilizador", "unable_to_remove_album_users": "Não foi possível remover utilizador do álbum", "unable_to_remove_api_key": "Não foi possível a Chave de API", + "unable_to_remove_assets_from_shared_link": "Não é possível remover os arquivos do link compartilhado", "unable_to_remove_comment": "", "unable_to_remove_library": "Não foi possível remover a biblioteca", "unable_to_remove_offline_files": "Não foi possível remover arquivos offline", @@ -626,6 +685,7 @@ "unable_to_save_settings": "Não foi possível salvar as configurações", "unable_to_scan_libraries": "Não foi possível escanear as bibliotecas", "unable_to_scan_library": "Não foi possível escanear a biblioteca", + "unable_to_set_feature_photo": "Não é possível definir a foto do recurso", "unable_to_set_profile_picture": "Não foi possível definir a foto de perfil", "unable_to_submit_job": "Não foi possível enviar o trabalho", "unable_to_trash_asset": "Não foi possível enviar o ativo para a lixeira", @@ -650,6 +710,7 @@ "expired": "Expirou", "expires_date": "Expira em {date}", "explore": "Explorar", + "explorer": "Explorador", "export": "Exportar", "export_as_json": "Exportar como JSON", "extension": "Extensão", @@ -671,6 +732,7 @@ "filter_people": "Filtrar pessoas", "find_them_fast": "Encontre pelo nome em uma pesquisa", "fix_incorrect_match": "Corrigir correspondência incorreta", + "folders": "Pastas", "force_re-scan_library_files": "Força escanear novamente todos os arquivos da biblioteca", "forward": "Para frente", "general": "Geral", @@ -724,6 +786,7 @@ }, "invite_people": "Convidar Pessoas", "invite_to_album": "Convidar para o álbum", + "items_count": "{count, plural, one {item #} other {itens #}}", "job_settings_description": "", "jobs": "Trabalhos", "keep": "Manter", @@ -740,6 +803,7 @@ "library": "Biblioteca", "library_options": "Opções da biblioteca", "light": "Claro", + "like_deleted": "Curtida removida", "link_options": "Opções do Link", "link_to_oauth": "Link do OAuth", "linked_oauth_account": "Conta OAuth Vinculada", @@ -752,6 +816,8 @@ "logged_out_device": "Sessão terminada no dispositivo", "login": "Iniciar sessão", "login_has_been_disabled": "Login foi desativado.", + "logout_all_device_confirmation": "Tem certeza de que deseja desconectar todos os dispositivos?", + "logout_this_device_confirmation": "Tem certeza de que deseja sair deste dispositivo?", "longitude": "Longitude", "look": "Estilo", "loop_videos": "Repetir vídeos", @@ -773,12 +839,14 @@ "memories": "Memórias", "memories_setting_description": "Gerencie o que vê em suas memórias", "memory": "Memória", + "memory_lane_title": "Memórias {title}", "menu": "Menu", "merge": "Mesclar", "merge_people": "Mesclar pessoas", "merge_people_limit": "Só é possível mesclar até 5 faces de uma só vez", "merge_people_prompt": "Tem certeza que deseja mesclar estas pessoas? Esta ação é irreversível.", "merge_people_successfully": "Pessoas mescladas com sucesso", + "merged_people_count": "Mesclada {count, plural, one {1 pessoa} other {# pessoas}}", "minimize": "Minimizar", "minute": "Minuto", "missing": "Faltando", @@ -801,6 +869,8 @@ "next_memory": "Próxima memória", "no": "Não", "no_albums_message": "Crie um álbum para organizar suas fotos e vídeos", + "no_albums_with_name_yet": "Parece que você ainda não tem nenhum álbum com este nome.", + "no_albums_yet": "Parece que você ainda não tem nenhum álbum.", "no_archived_assets_message": "Arquive fotos e vídeos para os ocultar da sua visualização de fotos", "no_assets_message": "CLIQUE PARA CARREGAR SUA PRIMEIRA FOTO", "no_duplicates_found": "Nenhuma duplicidade foi encontrada.", @@ -811,6 +881,7 @@ "no_name": "Sem nome", "no_places": "Sem lugares", "no_results": "Sem resultados", + "no_results_description": "Tente um sinônimo ou uma palavra-chave mais comum", "no_shared_albums_message": "Crie um álbum para compartilhar fotos e vídeos com pessoas em sua rede", "not_in_any_album": "Fora de álbum", "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar o rótulo de armazenamento a arquivos carregados anteriormente, execute o", @@ -825,10 +896,15 @@ "offline_paths_description": "Estes resultados podem ser devidos a arquivos deletados manualmente e que não são parte de uma biblioteca externa.", "ok": "Ok", "oldest_first": "Mais antigo primeiro", + "onboarding": "Integração", + "onboarding_privacy_description": "Os seguintes recursos (opcionais) dependem de serviços externos e podem ser desabilitados a qualquer momento nas configurações de administração.", + "onboarding_theme_description": "Escolha um tema de cor para sua instância. Você pode alterar isso mais tarde em suas configurações.", + "onboarding_welcome_description": "Vamos configurar sua instância com algumas configurações comuns.", "onboarding_welcome_user": "Bem-vindo(a), {user}", "online": "Online", "only_favorites": "Somente favoritos", "only_refreshes_modified_files": "Somente atualize arquivos modificados", + "open_in_map_view": "Abrir na visualização do mapa", "open_in_openstreetmap": "Abrir no OpenStreetMap", "open_the_search_filters": "Abre os filtros de pesquisa", "options": "Opções", @@ -840,6 +916,7 @@ "other_variables": "Outras variáveis", "owned": "Seu", "owner": "Dono", + "partner": "Parceiro", "partner_can_access": "{partner} pode acessar", "partner_can_access_assets": "Todas as suas fotos e vídeos, exceto os Arquivados ou Excluídos", "partner_can_access_location": "A localização onde as fotos foram tiradas", @@ -850,9 +927,9 @@ "password_required": "A senha é obrigatório", "password_reset_success": "Senha resetada com sucesso", "past_durations": { - "days": "{days, plural, one {Último dia} other {Últimos {days, number} dias}}", - "hours": "{hours, plural, one {Última hora} other {Últimas {hours, number} horas}}", - "years": "{years, plural, one {Último ano} other {Últimos {years, number} anos}}" + "days": "{days, plural, one {Último dia} other {# últimos dias}}", + "hours": "Últimas {hours, plural, one {horas} other {# horas}}", + "years": "{years, plural, one {Último ano} other {Últimos # anos}}" }, "path": "Caminho", "pattern": "Padrão", @@ -868,10 +945,13 @@ "permanent_deletion_warning_setting_description": "Exibe um aviso ao excluir arquivos de forma permanente", "permanently_delete": "Deletar permanentemente", "permanently_delete_assets_count": "Excluir permanentemente {count, plural, one {arquivo} other {arquivos}}", + "permanently_delete_assets_prompt": "Tem certeza que deseja excluir permanentemente {count, plural, one {esse arquivo?} other {estes # arquivos?}} Essa ação também removerá {count, plural, one {isto do} other {isto dos}} álbum(s).", "permanently_deleted_asset": "Ativo deletado permanentemente", "permanently_deleted_assets": "{count, plural, one {# ativo deletado} other {# ativos deletados}} permanentemente", "permanently_deleted_assets_count": "{count, plural, one {# arquivo excluído} other {# arquivos excluídos}} permanentemente", "person": "Pessoa", + "person_hidden": "{name}{hidden, select, true { (oculto)} other {}}", + "photo_shared_all_users": "Parece que você compartilhou suas fotos com todos os usuários ou não tem nenhum usuário para compartilhar.", "photos": "Fotos", "photos_and_videos": "Fotos & Vídeos", "photos_count": "{count, plural, one {{count, number} Foto} other {{count, number} Fotos}}", @@ -891,41 +971,69 @@ "previous_memory": "Memória anterior", "previous_or_next_photo": "Foto anterior ou próxima", "primary": "Primário", + "privacy": "Privacidade", "profile_image_of_user": "Imagem de perfil de {user}", "profile_picture_set": "Foto de perfil definida.", "public_album": "Álbum público", "public_share": "Compartilhar Publicamente", + "purchase_account_info": "Apoiador", "purchase_activated_subtitle": "Agradecemos por apoiar o Immich e software de código aberto", + "purchase_activated_time": "Ativado em {date, date}", + "purchase_activated_title": "Sua chave foi ativada com sucesso", "purchase_button_activate": "Ativar", "purchase_button_buy": "Comprar", "purchase_button_buy_immich": "Comprar Immich", "purchase_button_never_show_again": "Nunca mostrar novamente", "purchase_button_reminder": "Relembrar-me daqui a 30 dias", - "purchase_individual_title": "Individual", + "purchase_button_remove_key": "Remover chave", + "purchase_button_select": "Selecionar", + "purchase_failed_activation": "Falha ao ativar! Verifique seu e-mail para obter a chave de produto correta!", + "purchase_individual_description_1": "Para uma pessoa", + "purchase_individual_description_2": "Status de apoiador", + "purchase_individual_title": "Particular", + "purchase_input_suggestion": "Tem uma chave de produto? Insira a chave abaixo", + "purchase_license_subtitle": "Compre Immich para apoiar o desenvolvimento contínuo do serviço", "purchase_lifetime_description": "Compra vitalícia", "purchase_option_title": "OPÇÕES DE COMPRA", "purchase_panel_info_1": "O desenvolvimento do Immich requer muito tempo e esforço, e temos engenheiros a tempo inteiro a trabalhar nele para melhorá-lo quanto possível. A nossa missão é para que o software de código aberto e práticas de negócio éticas se tornem numa fonte de rendimento sustentável para os desenvolvedores e criar um ecossistema que respeite a privacidade dos utilizadores e que ofereça alternativas reais a serviços cloud explorativos.", + "purchase_panel_info_2": "Como estamos comprometidos em não adicionar acesso pago, esta compra não lhe dará nenhum recurso adicional no Immich. Contamos com usuários como você para dar suporte ao desenvolvimento contínuo do Immich.", "purchase_panel_title": "Apoie o projeto", "purchase_per_server": "Por servidor", "purchase_per_user": "Por utilizador", "purchase_remove_product_key": "Remover chave de produto", + "purchase_remove_product_key_prompt": "Tem certeza de que deseja remover a chave do produto?", + "purchase_remove_server_product_key": "Remover chave do produto do servidor", + "purchase_remove_server_product_key_prompt": "Tem certeza de que deseja remover a chave do produto do servidor?", "purchase_server_description_1": "Para o servidor inteiro", + "purchase_server_description_2": "Status de apoiador", "purchase_server_title": "Servidor", "purchase_settings_server_activated": "A chave de produto para servidor é gerida pelo administrador", "range": "", + "rating": "Classificação por estrelas", + "rating_clear": "Limpar classificação", + "rating_count": "{contar, plural, um {# estrela} outro {# estrelas}}", + "rating_description": "Exibir a classificação exif no painel de informações", "raw": "", "reaction_options": "Opções de reação", "read_changelog": "Ler Novidades", + "reassign": "Reatribuir", + "reassigned_assets_to_existing_person": "Reatribuir {count, plural, one {# arquivo} other {# arquivos}} PARA {name, select, null {uma pessoa existente} other {{name}}}", + "reassigned_assets_to_new_person": "Reatribuir {count, plural, one {# arquivo} other {# arquivos}} a uma nova pessoa", + "reassing_hint": "Atribuir ativos selecionados a uma pessoa existente", "recent": "Recente", "recent_searches": "Pesquisas recentes", "refresh": "Atualizar", + "refresh_encoded_videos": "Atualizar vídeos codificados", "refresh_metadata": "Atualizar metadados", "refresh_thumbnails": "Atualizar miniaturas", "refreshed": "Atualizado", "refreshes_every_file": "Atualiza todos arquivos", + "refreshing_encoded_video": "Atualizando vídeo codificado", "refreshing_metadata": "A atualizar metadados", "regenerating_thumbnails": "A atualizar miniaturas", "remove": "Remover", + "remove_assets_album_confirmation": "Tem certeza que deseja remover {count, plural, one {# arquivo} other {# arquivos}} do álbum?", + "remove_assets_shared_link_confirmation": "Tem certeza que deseja remover {count, plural, one {# arquivo} other {# arquivos}} desse link compartilhado?", "remove_assets_title": "Remover arquivos?", "remove_custom_date_range": "Remover intervalo de datas personalizado", "remove_from_album": "Remover do álbum", @@ -934,7 +1042,9 @@ "remove_offline_files": "Remover arquivos offline", "remove_user": "Remover utilizador", "removed_api_key": "Removido a Chave de API: {name}", + "removed_from_archive": "Removido do arquivo", "removed_from_favorites": "Removido dos favoritos", + "removed_from_favorites_count": "{count, plural, other {Removido #}} dos favoritos", "rename": "Renomear", "repair": "Reparar", "repair_no_results_message": "Arquivos perdidos ou não rastreados aparecem aqui", @@ -947,15 +1057,18 @@ "reset_people_visibility": "Resetar pessoas ocultas", "reset_settings_to_default": "", "reset_to_default": "Repor predefinições", + "resolve_duplicates": "Resolver itens duplicados", "resolved_all_duplicates": "Todas duplicidades resolvidas", "restore": "Restaurar", "restore_all": "Restaurar tudo", "restore_user": "Restaurar utilizador", + "restored_asset": "Arquivo restaurado", "resume": "Continuar", "retry_upload": "Tentar carregar novamente", "review_duplicates": "Revisar duplicidade", "role": "Função", "role_editor": "Editor", + "role_viewer": "Visualizador", "save": "Guardar", "saved_api_key": "Chave de API salva", "saved_profile": "Perfil Salvo", @@ -976,6 +1089,8 @@ "search_city": "Pesquisar cidade...", "search_country": "Pesquisar país...", "search_for_existing_person": "Pesquisar por pessoas", + "search_no_people": "Nenhuma pessoa", + "search_no_people_named": "Nenhuma pessoa chamada \"{name}\"", "search_people": "Pesquisar pessoas", "search_places": "Pesquisar lugares", "search_state": "Pesquisar estado...", @@ -987,15 +1102,18 @@ "see_all_people": "Ver todas as pessoas", "select_album_cover": "Escolher capa do álbum", "select_all": "Selecionar todos", + "select_all_duplicates": "Selecionar todos os itens duplicados", "select_avatar_color": "Selecionar cor do avatar", "select_face": "Selecionar face", "select_featured_photo": "Selecionar foto principal", + "select_from_computer": "Selecionar do computador", "select_keep_all": "Marcar manter em todos", "select_library_owner": "Selecione o dono da biblioteca", "select_new_face": "Selecionar nova face", "select_photos": "Selecionar fotos", "select_trash_all": "Marcar lixo em todos", "selected": "Selecionados", + "selected_count": "{count, plural, other {# selecionado}}", "send_message": "Enviar mensagem", "send_welcome_email": "Enviar E-mail de boas vindas", "server": "Servidor", @@ -1017,12 +1135,16 @@ "shared_by_user": "Partilhado por {user}", "shared_by_you": "Compartilhado por você", "shared_from_partner": "Fotos de {partner}", + "shared_link_options": "Opções de link compartilhado", "shared_links": "Links compartilhados", - "shared_photos_and_videos_count": "{assetCount} fotos & vídeos compartilhados.", + "shared_photos_and_videos_count": "{assetCount, plural, other {# Fotos & videos compartilhados.}}", "shared_with_partner": "Compartilhado com {partner}", "sharing": "Compartilhar", + "sharing_enter_password": "Por favor, digite a senha para visualizar esta página.", "sharing_sidebar_description": "Exibe o link Compartilhar na barra lateral", + "shift_to_permanent_delete": "Pressione ⇧ para excluir o arquivo permanentemente", "show_album_options": "Exibir opções do álbum", + "show_albums": "Mostrar álbuns", "show_all_people": "Mostrar todas as pessoas", "show_and_hide_people": "Mostrar & ocultar pessoas", "show_file_location": "Exibir local do arquivo", @@ -1037,6 +1159,8 @@ "show_person_options": "Exibir opções da pessoa", "show_progress_bar": "Exibir barra de progresso", "show_search_options": "Exibir opções de pesquisa", + "show_supporter_badge": "Emblema de apoiador", + "show_supporter_badge_description": "Mostrar um emblema de apoiador", "shuffle": "Aleatório", "sign_out": "Sair", "sign_up": "Registrar", @@ -1046,13 +1170,17 @@ "slideshow_settings": "Opções de apresentação", "sort_albums_by": "Ordenar álbuns por...", "sort_created": "Data de criação", + "sort_items": "Número de itens", "sort_modified": "Data de modificação", "sort_oldest": "Foto mais antiga", "sort_recent": "Foto mais recente", "sort_title": "Título", "source": "Fonte", "stack": "Empilhar", + "stack_duplicates": "Empilhar duplicados", + "stack_select_one_photo": "Selecione uma foto principal para a pilha", "stack_selected_photos": "Empilhar fotos selecionadas", + "stacked_assets_count": "Empilhado {count, plural, one {# arquivo} other {# arquivos}}", "stacktrace": "Stacktrace", "start": "Início", "start_date": "Data inicial", @@ -1074,9 +1202,11 @@ "theme": "Tema", "theme_selection": "Selecionar tema", "theme_selection_description": "Defina automaticamente o tema como claro ou escuro com base na preferência do sistema do seu navegador", + "they_will_be_merged_together": "Eles serão mesclados", "time_based_memories": "Memórias baseada no tempo", "timezone": "Fuso horário", "to_archive": "Arquivar", + "to_change_password": "Alterar senha", "to_favorite": "Favorito", "to_login": "Iniciar sessão", "to_trash": "Lixo", @@ -1087,11 +1217,13 @@ "trash": "Lixeira", "trash_all": "Todos para o lixo", "trash_count": "Lixeira {count, number}", + "trash_delete_asset": "Excluir arquivo", "trash_no_results_message": "Fotos e vídeos enviados para o lixo aparecem aqui.", "trashed_items_will_be_permanently_deleted_after": "Os itens da lixeira são deletados permanentemente após {days, plural, one {# dia} other {# dias}}.", "type": "Tipo", "unarchive": "Desarquivar", "unarchived": "Restaurado do arquivo", + "unarchived_count": "{count, plural, other {Não arquivado #}}", "unfavorite": "Remover favorito", "unhide_person": "Exibir pessoa", "unknown": "Desconhecido", @@ -1101,25 +1233,34 @@ "unlink_oauth": "Desvincular OAuth", "unlinked_oauth_account": "Conta OAuth desvinculada", "unnamed_album": "Álbum sem nome", + "unnamed_album_delete_confirmation": "Tem a certeza que pretende remover este album?", "unnamed_share": "Compartilhamento sem nome", "unsaved_change": "Alteração não guardada", "unselect_all": "Limpar seleção", + "unselect_all_duplicates": "Remover seleção de todos os duplicados", "unstack": "Desempilhar", + "unstacked_assets_count": "Desempilhar {count, plural, one {# arquivo} other {# arquivos}}", "untracked_files": "Arquivos não monitorados", "untracked_files_decription": "Estes arquivos não são monitorados pela aplicação. Podem ser resultados de falhas em uma movimentação, carregamentos interrompidos, ou deixados para trás por causa de um problema", "up_next": "A seguir", "updated_password": "Senha atualizada", "upload": "Carregar", "upload_concurrency": "Carregar simultâneo", + "upload_errors": "Envio completo com {count, plural, one {# erro} other {# erros}}, atualize a página para ver novos arquivos enviados.", "upload_progress": "Restante(s) {remaining, number} - Processado(s) {processed, number}/{total, number}", + "upload_skipped_duplicates": "Ignorado {count, plural, one {# arquivo duplicado} other {# arquivos duplicados}}", "upload_status_duplicates": "Duplicados", "upload_status_errors": "Erros", + "upload_status_uploaded": "Enviado", + "upload_success": "Upload realizado com sucesso, atualize a página para ver os novos ativos de upload.", "url": "URL", "usage": "Uso", "use_custom_date_range": "Usar um intervalo de datas personalizado", "user": "Utilizador", "user_id": "ID do utilizador", + "user_liked": "{user} gostou {type, select, photo {dessa foto} video {deste video} asset {deste arquivo} other {disto}}", "user_purchase_settings": "Compra", + "user_purchase_settings_description": "Gerencie sua compra", "user_role_set": "Definir {user} como {role}", "user_usage_detail": "Detalhes de uso do utilizador", "username": "Nome do utilizador", @@ -1128,6 +1269,8 @@ "validate": "Validar", "variables": "Variáveis", "version": "Versão", + "version_announcement_closing": "Seu amigo, Alex", + "version_announcement_message": "Olá amigo, há uma nova versão do aplicativo. Reserve um tempo para visitar as histórico de mudanças e garantir que suas configurações docker-compose.yml e .env estejam atualizadas para evitar qualquer configuração incorreta, especialmente se você usar o WatchTower ou qualquer mecanismo que lide com a atualização do seu aplicativo automaticamente.", "video": "Vídeo", "video_hover_setting": "Reproduzir vídeo em miniatura quando passar por cima", "video_hover_setting_description": "Reproduzir vídeo em miniatura quando o mouse está sobre o item. Mesmo quando desativado, a reprodução ainda pode ser iniciada passando sobre o ícone.", @@ -1140,7 +1283,9 @@ "view_links": "Ver links", "view_next_asset": "Ver próximo ativo", "view_previous_asset": "Ver ativo anterior", + "view_stack": "Visualizar pilha", "viewer": "Visualizar", + "visibility_changed": "Visibilidade alterada para {count, plural, one {# pessoa} other {# pessoas}}", "waiting": "Aguardando", "warning": "Aviso", "week": "Semana", diff --git a/web/src/lib/i18n/pt_BR.json b/web/src/lib/i18n/pt_BR.json index ba0698d7c582d..6bf13dcee1e2a 100644 --- a/web/src/lib/i18n/pt_BR.json +++ b/web/src/lib/i18n/pt_BR.json @@ -2,12 +2,12 @@ "about": "Sobre", "account": "Conta", "account_settings": "Configurações da Conta", - "acknowledge": "Confirmar", + "acknowledge": "Entendi", "action": "Ação", "actions": "Ações", "active": "Em execução", "activity": "Atividade", - "activity_changed": "A atividade está {enabled, select, true {enabled} other {disabled}}", + "activity_changed": "A atividade está {enabled, select, true {ativada} other {desativada}}", "add": "Adicionar", "add_a_description": "Adicionar uma descrição", "add_a_location": "Adicionar uma localização", @@ -95,7 +95,7 @@ "logging_level_description": "Quando ativado, qual nível de log usar.", "logging_settings": "Registros", "machine_learning_clip_model": "Modelo CLIP", - "machine_learning_clip_model_description": "O nome de um modelo CLIP listado aqui. Lembre-se de reexecutar a tarefa de 'Pesquisa Inteligente' para todas as imagens ao alterar o modelo.", + "machine_learning_clip_model_description": "O nome de um modelo CLIP listado aqui. Lembre-se de executar novamente a tarefa de 'Pesquisa Inteligente' para todas as imagens após alterar o modelo.", "machine_learning_duplicate_detection": "Detecção de duplicidade", "machine_learning_duplicate_detection_enabled": "Habilitar detecção de duplicidade", "machine_learning_duplicate_detection_enabled_description": "Se desativado, arquivos exatamente idênticos ainda serão desduplicados.", @@ -129,12 +129,13 @@ "map_enable_description": "Ativar recursos do mapa", "map_gps_settings": "Mapa e Configurações de GPS", "map_gps_settings_description": "Gerenciar Mapa e Configurações de GPS (Geocodificação Reversa)", + "map_implications": "O mapa depende de um serviço externo para funcionar (tiles.immich.cloud)", "map_light_style": "Tema Claro", "map_manage_reverse_geocoding_settings": "Gerenciar configurações de Geocodificação reversa", "map_reverse_geocoding": "Geocodificação reversa", "map_reverse_geocoding_enable_description": "Ativar geocodificação reversa", "map_reverse_geocoding_settings": "Configurações de geocodificação reversa", - "map_settings": "Configurações de mapa e GPS", + "map_settings": "Mapa", "map_settings_description": "Gerenciar configurações do mapa", "map_style_description": "URL para um tema de mapa style.json", "metadata_extraction_job": "Extrair metadados", @@ -249,7 +250,7 @@ "transcoding_acceleration_vaapi": "VAAPI", "transcoding_accepted_audio_codecs": "Codecs de áudio aceitos", "transcoding_accepted_audio_codecs_description": "Selecione quais codecs de áudio não precisam ser transcodificados. Usado apenas para determinadas políticas de transcodificação.", - "transcoding_accepted_containers": "containers aceitos", + "transcoding_accepted_containers": "Containers aceitos", "transcoding_accepted_containers_description": "Selecione quais formatos de contêiner não precisam ser remixados para MP4. Usado apenas para determinadas políticas de transcodificação.", "transcoding_accepted_video_codecs": "Codecs de vídeo aceitos", "transcoding_accepted_video_codecs_description": "Selecione quais codecs de vídeo não precisam ser transcodificados. Usado apenas para determinadas políticas de transcodificação.", @@ -278,7 +279,7 @@ "transcoding_preferred_hardware_device": "Dispositivo de hardware preferido", "transcoding_preferred_hardware_device_description": "Aplica-se apenas a VAAPI e QSV. Define o nó dri usado para transcodificação de hardware.", "transcoding_preset_preset": "Predefinido (-preset)", - "transcoding_preset_preset_description": "Velocidade de compressão. Predefinições mais lentas produzem arquivos menores e aumentam a qualidade ao atingir uma determinada taxa de bits. VP9 ignora velocidades acima de `mais rápidas`.", + "transcoding_preset_preset_description": "Velocidade de compressão. As opções mais lentas produzem arquivos menores e aumentam a qualidade. VP9 ignora as velocidades acima de 'mais rápida'.", "transcoding_reference_frames": "Quadros de referência", "transcoding_reference_frames_description": "O número de quadros a serem referenciados ao compactar um determinado quadro. Valores mais altos melhoram a eficiência da compactação, mas retardam a codificação. 0 define esse valor automaticamente.", "transcoding_required_description": "Somente vídeos que não estejam em um formato aceito", @@ -310,7 +311,7 @@ "user_delete_delay": "A conta e os arquivos de {user} serão programados para exclusão permanente em {delay, plural, one {# dia} other {# dias}}.", "user_delete_delay_settings": "Excluir atraso", "user_delete_delay_settings_description": "Número de dias após a remoção para excluir permanentemente a conta e os arquivos de um usuário. A tarefa de exclusão de usuário é executada à meia-noite para verificar usuários que estão prontos para exclusão. As alterações nesta configuração serão avaliadas na próxima execução.", - "user_delete_immediately": "A conta e os arquivos de {user} serão postos na fila para exclusão permanente imediatamente.", + "user_delete_immediately": "A conta e os arquivos de {user} serão programados para exclusão permanente imediata.", "user_delete_immediately_checkbox": "Adicionar o usuário e seus ativos na fila para serem deletados imediatamente", "user_management": "Gerenciamento de usuários", "user_password_has_been_reset": "A senha do usuário foi redefinida:", @@ -320,7 +321,8 @@ "user_settings": "Configurações do Usuário", "user_settings_description": "Gerenciar configurações do usuário", "user_successfully_removed": "O usuário {email} foi removido com sucesso.", - "version_check_enabled_description": "Ativa verificações periódicas no GitHub para novas versões", + "version_check_enabled_description": "Ativa a verificação de versão", + "version_check_implications": "A verificação de versão depende de uma comunicação periódica com github.com", "version_check_settings": "Verificação de versão", "version_check_settings_description": "Ativar/desativar a notificação de nova versão", "video_conversion_job": "Transcodificar vídeos", @@ -344,11 +346,11 @@ "album_options": "Opções de álbum", "album_remove_user": "Remover usuário?", "album_remove_user_confirmation": "Tem certeza de que deseja remover {user}?", - "album_share_no_users": "Parece que você compartilhou este álbum com todos os usuários ou não tem nenhum usuário para compartilhar com ele.", + "album_share_no_users": "Parece que você já compartilhou este álbum com todos os usuários ou não há nenhum usuário para compartilhar.", "album_updated": "Álbum atualizado", "album_updated_setting_description": "Receba uma notificação por e-mail quando um álbum compartilhado tiver novos recursos", - "album_user_left": "Saída de {album}", - "album_user_removed": "Usuário {user} removido", + "album_user_left": "Saiu do álbum {album}", + "album_user_removed": "Usuário {user} foi removido", "album_with_link_access": "Permitir que qualquer pessoa com o link veja as fotos e as pessoas neste álbum.", "albums": "Álbuns", "albums_count": "{count, plural, one {{count, number} Álbum} other {{count, number} Álbuns}}", @@ -358,8 +360,9 @@ "all_videos": "Todos os vídeos", "allow_dark_mode": "Permitir modo escuro", "allow_edits": "Permitir edições", - "allow_public_user_to_download": "Permitir que usuários públicos façam download", - "allow_public_user_to_upload": "Permitir que usuários públicos enviem novos ativos", + "allow_public_user_to_download": "Permitir que usuários públicos baixem os arquivos", + "allow_public_user_to_upload": "Permitir que usuários públicos enviem novos arquivos", + "anti_clockwise": "Anti-horário", "api_key": "Chave de API", "api_key_description": "Este valor será mostrado apenas uma vez. Por favor, certifique-se de copiá-lo antes de fechar a janela.", "api_key_empty": "O nome da sua chave de API não deve estar vazio", @@ -368,8 +371,8 @@ "appears_in": "Aparece em", "archive": "Arquivados", "archive_or_unarchive_photo": "Arquivar ou desarquivar foto", - "archive_size": "Tamanho do Arquivo", - "archive_size_description": "Configure o tamanho do arquivo para downloads (em GiB)", + "archive_size": "Tamanho do arquivo", + "archive_size_description": "Configure o tamanho do arquivo para baixar (em GiB)", "archived": "Arquivado", "archived_count": "{count, plural, one {# Arquivado} other {# Arquivados}}", "are_these_the_same_person": "Essas pessoas são a mesma pessoa?", @@ -377,11 +380,11 @@ "asset_added_to_album": "Adicionado ao álbum", "asset_adding_to_album": "Adicionando ao álbum...", "asset_description_updated": "A descrição do ativo foi atualizada", - "asset_filename_is_offline": "O arquivo {filename} está offline", - "asset_has_unassigned_faces": "O arquivo tem rostos não atribuídos", + "asset_filename_is_offline": "O arquivo {filename} não está disponível", + "asset_has_unassigned_faces": "O arquivo tem rostos sem nomes", "asset_hashing": "Processando...", - "asset_offline": "Arquivo off-line", - "asset_offline_description": "Este arquivo está offline. O Immich não pode acessar sua localização de arquivo. Certifique-se de que o arquivo esteja disponível e depois escaneie novamente a biblioteca.", + "asset_offline": "Arquivo indisponível", + "asset_offline_description": "Este arquivo não está disponível. O Immich não pode acessar o local do arquivo. Certifique-se de que o arquivo esteja disponível e depois escaneie novamente a biblioteca.", "asset_skipped": "Ignorado", "asset_uploaded": "Carregado", "asset_uploading": "Carregando...", @@ -397,17 +400,17 @@ "assets_restore_confirmation": "Tem certeza de que deseja restaurar todos os seus arquivos na lixeira? Esta ação não pode ser desfeita!", "assets_restored_count": "{count, plural, one {# arquivo restaurado} other {# arquivos restaurados}}", "assets_trashed_count": "{count, plural, one {# arquivo movido para a lixeira} other {# arquivos movidos para a lixeira}}", - "assets_were_part_of_album_count": "{count, plural, one {O recurso estava} other {Os recursos estavam}} já fazendo parte do álbum", + "assets_were_part_of_album_count": "{count, plural, one {O arquivo já faz} other {Os arquivos já fazem}} parte do álbum", "authorized_devices": "Dispositivos Autorizados", "back": "Voltar", "back_close_deselect": "Voltar, fechar ou desmarcar", "backward": "Para trás", "birthdate_saved": "Data de nascimento salva com sucesso", - "birthdate_set_description": "A data de nascimento é usada para calcular a idade desta pessoa na época de uma foto.", + "birthdate_set_description": "A data de nascimento é usada para calcular a idade da pessoa no momento em que a foto foi tirada.", "blurred_background": "Fundo desfocado", "build": "Versão de compilação", "build_image": "Imagem de compilação", - "bulk_delete_duplicates_confirmation": "Tem a certeza de que deseja deletar {count, plural, one {# arquivo duplicado} other {em massa # arquivos duplicados}}? Esta ação mantém o maior arquivo de cada grupo e deleta permanentemente todos as outras duplicidades. Você não pode reverter esta ação!", + "bulk_delete_duplicates_confirmation": "Tem a certeza de que deseja deletar {count, plural, one {# arquivo duplicado} other {em massa # arquivos duplicados}}? Esta ação mantém o maior arquivo de cada grupo e deleta permanentemente todos as outras duplicidades. Você não pode desfazer esta ação!", "bulk_keep_duplicates_confirmation": "Tem certeza de que deseja manter {count, plural, one {# arquivo duplicado} other {# arquivos duplicados}}? Isso resolverá todos os grupos duplicados sem excluir nada.", "bulk_trash_duplicates_confirmation": "Tem a certeza de que deseja mover para a lixeira {count, plural, one {# arquivo duplicado} other {# arquivos duplicados}}? Isso manterá o maior arquivo de cada grupo e moverá para a lixeira todas as outras duplicidades.", "buy": "Comprar o Immich", @@ -441,6 +444,7 @@ "clear_all_recent_searches": "Limpar todas as buscas recentes", "clear_message": "Limpar mensagem", "clear_value": "Limpar valor", + "clockwise": "Horário", "close": "Fechar", "collapse": "Recolher", "collapse_all": "Colapsar tudo", @@ -517,6 +521,8 @@ "do_not_show_again": "Não mostrar esta mensagem novamente", "done": "Feito", "download": "Baixar", + "download_include_embedded_motion_videos": "Vídeos inclusos", + "download_include_embedded_motion_videos_description": "Baixar os vídeos inclusos de uma foto em movimento em um arquivo separado", "download_settings": "Baixar", "download_settings_description": "Gerenciar configurações relacionadas a transferência de arquivos", "downloading": "Baixando", @@ -550,6 +556,10 @@ "edit_user": "Editar usuário", "edited": "Editado", "editor": "Editar", + "editor_close_without_save_prompt": "As alterações não serão salvas", + "editor_close_without_save_title": "Fechar editor?", + "editor_crop_tool_h2_aspect_ratios": "Proporções", + "editor_crop_tool_h2_rotation": "Rotação", "email": "E-mail", "empty": "", "empty_album": "", @@ -562,16 +572,16 @@ "error_loading_image": "Erro ao carregar a página", "error_title": "Erro - Algo deu errado", "errors": { - "cannot_navigate_next_asset": "Não é possível navegar para o próximo arquivo", - "cannot_navigate_previous_asset": "Não é possível navegar para o arquivo anterior", - "cant_apply_changes": "Não é possível aplicar modificações", - "cant_change_activity": "Não é possível {enabled, select, true {disable} other {enable}} atividade", - "cant_change_asset_favorite": "Não é possível mudar favorito para o arquivo", - "cant_change_metadata_assets_count": "Não é possível alterar os metadados de {count, plural, one {# arquivo} other {# arquivos}}", + "cannot_navigate_next_asset": "Não foi possível navegar para o próximo arquivo", + "cannot_navigate_previous_asset": "Não foi possível navegar para o arquivo anterior", + "cant_apply_changes": "Não foi possível aplicar as alterações", + "cant_change_activity": "Não foi possível {enabled, select, true {desativar} other {habilitar}} a atividade", + "cant_change_asset_favorite": "Não foi possível mudar favorito para o arquivo", + "cant_change_metadata_assets_count": "Não foi possível alterar os metadados de {count, plural, one {# arquivo} other {# arquivos}}", "cant_get_faces": "Não foi possível obter os rostos", - "cant_get_number_of_comments": "Não é possível obter o número de comentários", - "cant_search_people": "Não é possível procurar pessoas", - "cant_search_places": "Não é possível procurar locais", + "cant_get_number_of_comments": "Não foi possível obter o número de comentários", + "cant_search_people": "Não foi possível procurar pessoas", + "cant_search_places": "Não foi possível procurar locais", "cleared_jobs": "Tarefas eliminadas para: {job}", "error_adding_assets_to_album": "Erro ao adicionar arquivos para o álbum", "error_adding_users_to_album": "Erro ao adicionar usuários para o álbum", @@ -605,11 +615,11 @@ "unable_to_add_import_path": "Não foi possível adicionar o caminho de importação", "unable_to_add_partners": "Não foi possível adicionar parceiros", "unable_to_add_remove_archive": "Não é possível {archived, select, true {remove asset from} other {add asset to}} arquivar", - "unable_to_add_remove_favorites": "Não é possível {favorite, select, true {add asset to} other {remove asset from}} favoritos", - "unable_to_archive_unarchive": "Não é possível {archived, select, true {archive} other {unarchive}}", + "unable_to_add_remove_favorites": "Não foi possível {favorite, select, true {adicionar o arquivo aos} other {remover o arquivo dos}} favoritos", + "unable_to_archive_unarchive": "Não foi possível {archived, select, true {arquivar} other {desarquivar}}", "unable_to_change_album_user_role": "Não foi possível alterar a permissão do usuário no álbum", "unable_to_change_date": "Não foi possível alterar a data", - "unable_to_change_favorite": "Não é possível alterar o favorito para o arquivo", + "unable_to_change_favorite": "Não foi possível alterar o favorito para o arquivo", "unable_to_change_location": "Não foi possível alterar a localização", "unable_to_change_password": "Não foi possível alterar a senha", "unable_to_change_visibility": "Não foi possível alterar a visibilidade de {count, plural, one {# pessoa} other {# pessoas}}", @@ -630,7 +640,7 @@ "unable_to_delete_import_path": "Não foi possível deletar o caminho de importação", "unable_to_delete_shared_link": "Não foi possível deletar o link compartilhado", "unable_to_delete_user": "Não foi possível deletar o usuário", - "unable_to_download_files": "Não foi possível fazer download dos arquivos", + "unable_to_download_files": "Não foi possível baixar os arquivos", "unable_to_edit_exclusion_pattern": "Não foi possível editar o padrão de exclusão", "unable_to_edit_import_path": "Não foi possível editar o caminho de importação", "unable_to_empty_trash": "Não foi possível esvaziar a lixeira", @@ -648,7 +658,7 @@ "unable_to_log_out_device": "Não foi possível sair do dispositivo", "unable_to_login_with_oauth": "Não foi possível fazer login com OAuth", "unable_to_play_video": "Não foi possível reproduzir o vídeo", - "unable_to_reassign_assets_existing_person": "Não foi possível reatribuir arquivos para {name, select, null {an existing person} other {{name}}}", + "unable_to_reassign_assets_existing_person": "Não foi possível reatribuir arquivos a {name, select, null {uma pessoa} other {{name}}}", "unable_to_reassign_assets_new_person": "Não foi possível reatribuir arquivos a uma nova pessoa", "unable_to_refresh_user": "Não foi possível atualizar o usuário", "unable_to_remove_album_users": "Não foi possível remover usuários do álbum", @@ -663,7 +673,7 @@ "unable_to_repair_items": "Não foi possível reparar os itens", "unable_to_reset_password": "Não foi possível resetar a senha", "unable_to_resolve_duplicate": "Não foi possível resolver a duplicidade", - "unable_to_restore_assets": "Não foi possível restaurar o(s) arquivo(s)", + "unable_to_restore_assets": "Não foi possível restaurar", "unable_to_restore_trash": "Não foi possível restaurar itens da lixeira", "unable_to_restore_user": "Não foi possível restaurar usuário", "unable_to_save_album": "Não foi possível salvar o álbum", @@ -699,6 +709,7 @@ "expired": "Expirou", "expires_date": "Expira em {date}", "explore": "Explorar", + "explorer": "Explorar", "export": "Exportar", "export_as_json": "Exportar como JSON", "extension": "Extensão", @@ -720,12 +731,13 @@ "filter_people": "Filtrar pessoas", "find_them_fast": "Encontre pelo nome em uma pesquisa", "fix_incorrect_match": "Corrigir correspondência incorreta", + "folders": "Pastas", "force_re-scan_library_files": "Força escanear novamente todos os arquivos da biblioteca", "forward": "Para frente", "general": "Geral", "get_help": "Obter Ajuda", "getting_started": "Primeiros passos", - "go_back": "Retornar", + "go_back": "Voltar", "go_to_search": "Ir para a pesquisa", "go_to_share_page": "Ir para a página de compartilhamento", "group_albums_by": "Agrupar álbuns por...", @@ -743,16 +755,16 @@ "host": "Host", "hour": "Hora", "image": "Imagem", - "image_alt_text_date": "{isVideo, select, true {Vídeo} other {Imagem}} {isVideo, select, true {tirado} other {tirada}} em {date}", - "image_alt_text_date_1_person": "{isVideo, select, true {Vídeo} other {Imagem}} {isVideo, select, true {tirado} other {tirada}} com {person1} em {date}", - "image_alt_text_date_2_people": "{isVideo, select, true {Vídeo} other {Imagem}} {isVideo, select, true {tirado} other {tirada}} com {person1} e {person2} em {date}", - "image_alt_text_date_3_people": "{isVideo, select, true {Vídeo} other {Imagem}} {isVideo, select, true {tirado} other {tirada}} com {person1}, {person2}, e {person3} em {date}", - "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Vídeo} other {Imagem}} {isVideo, select, true {tirado} other {tirada}} com {person1}, {person2}, e {additionalCount, number} outros em {date}", - "image_alt_text_date_place": "{isVideo, select, true {Vídeo} other {Imagem}} {isVideo, select, true {tirado} other {tirada}} em {city}, {country} em {date}", - "image_alt_text_date_place_1_person": "{isVideo, select, true {Vídeo} other {Imagem}} {isVideo, select, true {tirado} other {tirada}} em {city}, {country} com {person1} em {date}", - "image_alt_text_date_place_2_people": "{isVideo, select, true {Vídeo} other {Imagem}} {isVideo, select, true {tirado} other {tirada}} em {city}, {country} com {person1} e {person2} em {date}", - "image_alt_text_date_place_3_people": "{isVideo, select, true {Vídeo} other {Imagem}} {isVideo, select, true {tirado} other {tirada}} em {city}, {country} com {person1}, {person2}, e {person3} em {date}", - "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Vídeo} other {Imagem}} {isVideo, select, true {tirado} other {tirada}} em {city}, {country} com {person1}, {person2}, e {additionalCount, number} outros em {date}", + "image_alt_text_date": "{isVideo, select, true {Vídeo gravado} other {Foto tirada}} em {date}", + "image_alt_text_date_1_person": "{isVideo, select, true {Vídeo gravado} other {Foto tirada}} com {person1} em {date}", + "image_alt_text_date_2_people": "{isVideo, select, true {Vídeo gravado} other {Foto tirada}} com {person1} e {person2} em {date}", + "image_alt_text_date_3_people": "{isVideo, select, true {Vídeo gravado} other {Foto tirada}} com {person1}, {person2}, e {person3} em {date}", + "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Vídeo gravado} other {Foto tirada}} com {person1}, {person2}, e outras {additionalCount, number} em {date}", + "image_alt_text_date_place": "{isVideo, select, true {Vídeo gravado} other {Foto tirada}} em {city}, {country} em {date}", + "image_alt_text_date_place_1_person": "{isVideo, select, true {Vídeo gravado} other {Foto tirada}} em {city}, {country} com {person1} em {date}", + "image_alt_text_date_place_2_people": "{isVideo, select, true {Vídeo gravado} other {Foto tirada}} em {city}, {country} com {person1} e {person2} em {date}", + "image_alt_text_date_place_3_people": "{isVideo, select, true {Vídeo gravado} other {Foto tirada}} em {city}, {country} com {person1}, {person2}, e {person3} em {date}", + "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Vídeo gravado} other {Foto tirada}} em {city}, {country} com {person1}, {person2}, e {additionalCount, number} outros em {date}", "image_alt_text_people": "{count, plural, =1 {com {person1}} =2 {com {person1} e {person2}} =3 {com {person1}, {person2}, e {person3}} other {com {person1}, {person2} e outras {others, number} pessoas}}", "image_alt_text_place": "em {city}, {country}", "image_taken": "{isVideo, select, true {Gravado} other {Fotografado}}", @@ -912,6 +924,7 @@ "ok": "Ok", "oldest_first": "Mais antigo primeiro", "onboarding": "Integração", + "onboarding_privacy_description": "As seguintes funções opcionais dependem de serviços externos e podem ser desabilitadas a qualquer momento nas configurações de administração.", "onboarding_theme_description": "Escolha um tema de cores para sua instância. Você pode alterar isso posteriormente em suas configurações.", "onboarding_welcome_description": "Vamos configurar sua instância com algumas configurações comuns.", "onboarding_welcome_user": "Bem-vindo, {user}", @@ -985,6 +998,7 @@ "previous_memory": "Memória anterior", "previous_or_next_photo": "Foto anterior ou próxima", "primary": "Primário", + "privacy": "Privacidade", "profile_image_of_user": "Imagem do perfil de {user}", "profile_picture_set": "Foto de perfil definida.", "public_album": "Álbum público", @@ -1008,8 +1022,8 @@ "purchase_license_subtitle": "Compre o Immich para apoiar o desenvolvimento contínuo do serviço", "purchase_lifetime_description": "Compra vitalícia", "purchase_option_title": "OPÇÕES DE COMPRA", - "purchase_panel_info_1": "Construir o Immich leva muito tempo e esforço, e temos engenheiros dedicados trabalhando nele para torná-lo o melhor possível. Nossa missão é que programas de código aberto e as práticas empresariais éticas se tornem uma fonte de receita sustentável para os desenvolvedores e criar um ecossistema que respeite a privacidade, oferecendo alternativas reais aos serviços de nuvem exploratórios.", - "purchase_panel_info_2": "Como estamos comprometidos em não adicionar bloqueios de pagamento, esta compra não lhe concederá recursos adicionais no Immich. Contamos com usuários como você para apoiar o desenvolvimento contínuo do Immich.", + "purchase_panel_info_1": "Construir o Immich leva muito tempo e esforço. Temos engenheiros trabalhando em tempo integral para torná-lo o melhor possível. Nossa missão é fazer com que programas de código aberto e práticas empresariais éticas se tornem uma fonte de renda sustentável para os desenvolvedores e também criar um ecossistema que respeite a privacidade, oferecendo alternativas reais aos serviços de nuvem exploratórios.", + "purchase_panel_info_2": "Como estamos comprometidos em não adicionar funções bloqueadas por compras, esta compra não lhe concederá nenhum recurso adicional no Immich. Nós contamos com usuários como você para apoiar o desenvolvimento contínuo do Immich.", "purchase_panel_title": "Apoiar o projeto", "purchase_per_server": "Por servidor", "purchase_per_user": "Por usuário", @@ -1022,11 +1036,13 @@ "purchase_server_title": "Servidor", "purchase_settings_server_activated": "A chave do produto para servidor é gerenciada pelo administrador", "range": "", + "rating": "Estrelas", + "rating_description": "Exibir os metadados de classificação (estrelas) no painel de informações", "raw": "", "reaction_options": "Opções de reação", "read_changelog": "Ler Novidades", "reassign": "Reatribuir", - "reassigned_assets_to_existing_person": "{count, plural, one {# arquivo reatribuído} other {# arquivos reatribuídos}} a {name, select, null {an existing person} other {{name}}}", + "reassigned_assets_to_existing_person": "{count, plural, one {# arquivo reatribuído} other {# arquivos reatribuídos}} a {name, select, null {uma pessoa} other {{name}}}", "reassigned_assets_to_new_person": "{count, plural, one {# arquivo reatribuído} other {# arquivos reatribuídos}} a uma nova pessoa", "reassing_hint": "Atribuir arquivos selecionados a uma pessoa existente", "recent": "Recente", @@ -1126,8 +1142,8 @@ "send_message": "Enviar mensagem", "send_welcome_email": "Enviar E-mail de boas vindas", "server": "Servidor", - "server_offline": "Servidor Fora do Ar", - "server_online": "Servidor no Ar", + "server_offline": "Servidor Indisponível", + "server_online": "Servidor Disponível", "server_stats": "Status do servidor", "server_version": "Versão do servidor", "set": "Definir", @@ -1144,14 +1160,16 @@ "shared_by_user": "Compartilhado por {user}", "shared_by_you": "Compartilhado por você", "shared_from_partner": "Fotos de {partner}", + "shared_link_options": "Opções do link compartilhado", "shared_links": "Links compartilhados", - "shared_photos_and_videos_count": "{assetCount, plural, one {# foto e vídeo compartilhados.} other {# fotos e vídeos compartilhados.}}", + "shared_photos_and_videos_count": "{assetCount, plural, one {# arquivo compartilhado.} other {# arquivos compartilhados.}}", "shared_with_partner": "Compartilhado com {partner}", "sharing": "Compartilhar", "sharing_enter_password": "Digite a senha para visualizar esta página.", "sharing_sidebar_description": "Exibe o link Compartilhar na barra lateral", "shift_to_permanent_delete": "pressione ⇧ para excluir permanentemente o arquivo", "show_album_options": "Exibir opções do álbum", + "show_albums": "Exibir álbuns", "show_all_people": "Mostrar todas as pessoas", "show_and_hide_people": "Mostrar & ocultar pessoas", "show_file_location": "Exibir local do arquivo", @@ -1167,7 +1185,7 @@ "show_progress_bar": "Exibir barra de progresso", "show_search_options": "Exibir opções de pesquisa", "show_supporter_badge": "Insígnia de Contribuidor", - "show_supporter_badge_description": "Mostrar uma insígnia de contribuidor", + "show_supporter_badge_description": "Mostrar a insígnia de contribuidor", "shuffle": "Aleatório", "sign_out": "Sair", "sign_up": "Registrar", @@ -1184,6 +1202,8 @@ "sort_title": "Título", "source": "Fonte", "stack": "Empilhar", + "stack_duplicates": "Empilhar duplicados", + "stack_select_one_photo": "Selecione uma foto principal para a pilha", "stack_selected_photos": "Empilhar fotos selecionadas", "stacked_assets_count": "{count, plural, one {# arquivo empilhado} other {# arquivos empilhados}}", "stacktrace": "Stacktrace", @@ -1241,7 +1261,7 @@ "unnamed_share": "Compartilhamento sem nome", "unsaved_change": "Alteração não salva", "unselect_all": "Limpar seleção", - "unselect_all_duplicates": "Deselecionar todas as duplicatas", + "unselect_all_duplicates": "Desselecionar todas as duplicatas", "unstack": "Desempilhar", "unstacked_assets_count": "{count, plural, one {# arquivo não empilhado} other {# arquivos não empilhados}}", "untracked_files": "Arquivos não monitorados", @@ -1251,7 +1271,7 @@ "upload": "Carregar", "upload_concurrency": "Carregar simultâneo", "upload_errors": "Envio concluído com {count, plural, one {# erro} other {# erros}}, atualize a página para ver os novos arquivos carregados.", - "upload_progress": "{remaining, number} processando - {processed, number}/{total, number} já processados.", + "upload_progress": "{remaining, number} processando - {processed, number}/{total, number} já processados", "upload_skipped_duplicates": "{count, plural, one {# arquivo duplicado foi ignorado} other {# arquivos duplicados foram ignorados}}", "upload_status_duplicates": "Duplicados", "upload_status_errors": "Erros", @@ -1259,13 +1279,13 @@ "upload_success": "Carregado com sucesso, atualize a página para ver os novos arquivos.", "url": "URL", "usage": "Uso", - "use_custom_date_range": "Usar intervalo de datas personalizado invés", + "use_custom_date_range": "Usar intervalo de datas personalizado", "user": "Usuário", "user_id": "ID do usuário", "user_license_settings": "Licença", "user_license_settings_description": "Gerenciar sua licença", - "user_liked": "{user} curtiu {type, select, photo {this photo} video {this video} asset {this asset} other {it}}", - "user_purchase_settings": "Compra", + "user_liked": "{user} curtiu {type, select, photo {a foto} video {o vídeo} asset {o arquivo} other {isso}}", + "user_purchase_settings": "Comprar", "user_purchase_settings_description": "Gerenciar sua compra", "user_role_set": "Definir {user} como {role}", "user_usage_detail": "Detalhes de uso do usuário", @@ -1276,14 +1296,14 @@ "variables": "Variáveis", "version": "Versão", "version_announcement_closing": "De seu amigo, Alex", - "version_announcement_message": "Olá, amigo, há uma nova versão do aplicativo disponível. Por favor, visite com calma a página notas da versão e certifique-se de que a configuração do docker-compose.yml, e do .env estejam atualizadas para evitar configurações incorretas, especialmente se você usar o WatchTower ou qualquer mecanismo que lide com a atualização automática do aplicativo.", + "version_announcement_message": "Olá amigo! Uma nova versão do aplicativo está disponível. Para evitar configurações incorretas, por favor verifique com calma a página de notas da versão e certifique-se que os arquivos docker-compose.yml e .env estão configurados corretamente, principalmente se você usa o WatchTower ou qualquer outro mecanismo que faça atualizações automáticas.", "video": "Vídeo", "video_hover_setting": "Reproduzir miniatura do vídeo ao passar o mouse", "video_hover_setting_description": "Reproduzir a miniatura do vídeo ao passar o mouse sobre o item. Mesmo quando desativado, a reprodução pode ser iniciada ao passar o mouse sobre o ícone de reprodução.", "videos": "Vídeos", "videos_count": "{count, plural, one {# Vídeo} other {# Vídeos}}", "view": "Ver", - "view_album": "Exibir álbum", + "view_album": "Ver álbum", "view_all": "Ver tudo", "view_all_users": "Ver todos usuários", "view_links": "Ver links", @@ -1291,7 +1311,7 @@ "view_previous_asset": "Ver arquivo anterior", "view_stack": "Exibir Pilha", "viewer": "Visualizar", - "visibility_changed": "Visibilidade alterada para {count, plural, one {# pessoa} other {# pessoas}}", + "visibility_changed": "A visibilidade de {count, plural, one {# pessoa foi alterada} other {# pessoas foram alteradas}}", "waiting": "Aguardando", "warning": "Aviso", "week": "Semana", diff --git a/web/src/lib/i18n/ru.json b/web/src/lib/i18n/ru.json index 1a55ab009dd43..c56b3fce6fa58 100644 --- a/web/src/lib/i18n/ru.json +++ b/web/src/lib/i18n/ru.json @@ -129,6 +129,7 @@ "map_enable_description": "Включить функции карты", "map_gps_settings": "Настройки карты и GPS", "map_gps_settings_description": "Управление настройками карты и GPS (обратный геокодинг)", + "map_implications": "Функция отображения зависит от внешнего сервиса плиток (tiles.immich.cloud)", "map_light_style": "Светлый стиль", "map_manage_reverse_geocoding_settings": "Управление настройками Обратного геокодирования", "map_reverse_geocoding": "Обратное Геокодирование", @@ -320,7 +321,8 @@ "user_settings": "Пользовательские настройки", "user_settings_description": "Управление настройками пользователей", "user_successfully_removed": "Пользователь {email} был успешно удален.", - "version_check_enabled_description": "Включить периодические запросы к GitHub для проверки наличия новых версий", + "version_check_enabled_description": "Включить проверку наличия новых версий", + "version_check_implications": "Функция проверки версии зависит от периодического взаимодействия с github.com", "version_check_settings": "Проверка версии", "version_check_settings_description": "Включить/отключить уведомление о новой версии", "video_conversion_job": "Перекодирование видео", @@ -336,7 +338,8 @@ "album_added": "Альбом добавлен", "album_added_notification_setting_description": "Получать уведомление по электронной почте, когда вы добавлены к общему альбому", "album_cover_updated": "Обложка альбома обновлена", - "album_delete_confirmation": "Вы уверены, что хотите удалить альбом {album}?\nЕсли этот альбом общий, то другие пользователи не смогут получить к нему доступ.", + "album_delete_confirmation": "Вы уверены, что хотите удалить альбом {album}?", + "album_delete_confirmation_description": "Если альбом был общим, другие пользователи больше не смогут получить к нему доступ.", "album_info_updated": "Информация об альбоме обновлена", "album_leave": "Покинуть альбом?", "album_leave_confirmation": "Вы уверены, что хотите покинуть {album}?", @@ -519,6 +522,8 @@ "do_not_show_again": "Не показывать это сообщение в дальнейшем", "done": "Готово", "download": "Скачать", + "download_include_embedded_motion_videos": "Встроенные видео", + "download_include_embedded_motion_videos_description": "Включить видео, встроенные в живые фото, в виде отдельного файла", "download_settings": "Скачивание", "download_settings_description": "Управление настройками скачивания объектов", "downloading": "Загрузка", @@ -705,6 +710,7 @@ "expired": "Срок действия истек", "expires_date": "Срок действия до {date}", "explore": "Просмотр", + "explorer": "Проводник", "export": "Экспортировать", "export_as_json": "Экспорт в JSON", "extension": "Расширение", @@ -726,6 +732,7 @@ "filter_people": "Фильтр по людям", "find_them_fast": "Быстро найдите их по имени с помощью поиска", "fix_incorrect_match": "Исправить неправильное соответствие", + "folders": "Папки", "force_re-scan_library_files": "Принудительное повторное сканирование всех файлов библиотеки", "forward": "Переслать", "general": "Общие", @@ -918,6 +925,7 @@ "ok": "ОК", "oldest_first": "Сначала старые", "onboarding": "Начало работы", + "onboarding_privacy_description": "Следующие (необязательные) функции зависят от внешних сервисов и могут быть отключены в любое время в настройках администрирования.", "onboarding_theme_description": "Выберите цветовую тему. Вы можете изменить ее позже в настройках.", "onboarding_welcome_description": "Давайте настроим ваш экземпляр с некоторыми общими параметрами.", "onboarding_welcome_user": "Добро пожаловать, {user}", @@ -991,6 +999,7 @@ "previous_memory": "Предыдущее воспоминание", "previous_or_next_photo": "Предыдущая или следующая фотография", "primary": "Главное", + "privacy": "Конфиденциальность", "profile_image_of_user": "Изображение профиля {user}", "profile_picture_set": "Установлена картинка профиля.", "public_album": "Публичный альбом", @@ -1029,6 +1038,8 @@ "purchase_settings_server_activated": "Ключ продукта сервера управляется администратором", "range": "", "rating": "Рейтинг звёзд", + "rating_clear": "Очистить рейтинг", + "rating_count": "{count, plural, one {# звезда} other {# звезд}}", "rating_description": "Показывать рейтинг exif в панели информации", "raw": "", "reaction_options": "Опции реакций", @@ -1152,6 +1163,7 @@ "shared_by_user": "Владелец: {user}", "shared_by_you": "Вы поделились", "shared_from_partner": "Фото от {partner}", + "shared_link_options": "Параметры общих ссылок", "shared_links": "Общие ссылки", "shared_photos_and_videos_count": "{assetCount, plural, other {# поделился фото и видео.}}", "shared_with_partner": "Совместно с {partner}", @@ -1249,6 +1261,7 @@ "unlink_oauth": "Отключить OAuth", "unlinked_oauth_account": "Отключить аккаунт OAuth", "unnamed_album": "Альбом без названия", + "unnamed_album_delete_confirmation": "Вы уверены, что хотите удалить этот альбом?", "unnamed_share": "Общий доступ без названия", "unsaved_change": "Не сохраненное изменение", "unselect_all": "Снять всё", diff --git a/web/src/lib/i18n/sl.json b/web/src/lib/i18n/sl.json index bf8c55e5c4da7..ccd488174b8f8 100644 --- a/web/src/lib/i18n/sl.json +++ b/web/src/lib/i18n/sl.json @@ -7,6 +7,7 @@ "actions": "Dejanja", "active": "Aktivno", "activity": "Aktivnost", + "activity_changed": "Aktivnost {enabled, select, true {omogočena} other {onemogočena}}", "add": "Dodaj", "add_a_description": "Dodaj opis", "add_a_location": "Dodaj lokacijo", @@ -29,6 +30,7 @@ "add_exclusion_pattern_description": "Dodajte vzorec izključitev. Globiranje z uporabo *, ** in ? je podprto. Če želite prezreti vse datoteke v katerem koli imeniku z imenom \"Raw\", uporabite \"**/Raw/**\". Če želite prezreti vse datoteke, ki se končajo na \".tif\", uporabite \"**/*.tif\". Če želite prezreti absolutno pot, uporabite \"/pot/za/ignoriranje/**\".", "authentication_settings": "Nastavitve preverjanja pristnosti", "authentication_settings_description": "Upravljanje gesel, OAuth in drugih nastavitev preverjanja pristnosti", + "authentication_settings_disable_all": "Ali zares želite onemogočiti vse prijavne metode? Prijava bo popolnoma onemogočena.", "authentication_settings_reenable": "Ponovno omogoči z uporabo Server Command.", "background_task_job": "Opravila v ozadju", "check_all": "Označi vse", diff --git a/web/src/lib/i18n/sr_Cyrl.json b/web/src/lib/i18n/sr_Cyrl.json index 1c7b66df01b7e..d0c9b7d486017 100644 --- a/web/src/lib/i18n/sr_Cyrl.json +++ b/web/src/lib/i18n/sr_Cyrl.json @@ -129,6 +129,7 @@ "map_enable_description": "Омогућите карактеристике мапе", "map_gps_settings": "Мап & ГПС подешавања", "map_gps_settings_description": "Управљајте поставкама мапе и ГПС-а (обрнуто геокодирање)", + "map_implications": "Функција мапе се ослања на екстерну услугу плочица (tiles.immich.cloud)", "map_light_style": "Светли стил", "map_manage_reverse_geocoding_settings": "Управљајте подешавањима Обрнуто геокодирање", "map_reverse_geocoding": "Обрнуто геокодирање", @@ -320,7 +321,8 @@ "user_settings": "Подешавања корисника", "user_settings_description": "Управљајте корисничким подешавањима", "user_successfully_removed": "Корисник {email} је успешно уклоњен.", - "version_check_enabled_description": "Омогућите периодичне захтеве GitHub-u за проверу нових издања", + "version_check_enabled_description": "Омогућите проверу нових издања", + "version_check_implications": "Функција провере верзије се ослања на периодичну комуникацију са github.com", "version_check_settings": "Провера верзије", "version_check_settings_description": "Омогућите/oneмогућите обавештење о новој верзији", "video_conversion_job": "Транскодирање видео записа", @@ -336,7 +338,8 @@ "album_added": "Албум додан", "album_added_notification_setting_description": "Прими обавештење е-поштом кад будеш додан у дељен албум", "album_cover_updated": "Омот албума ажуриран", - "album_delete_confirmation": "Да ли стварно желите да избришете албум {album}?\nАко се овај албум дели, други корисници више неће моћи да му приступе.", + "album_delete_confirmation": "Да ли стварно желите да избришете албум {album}?", + "album_delete_confirmation_description": "Ако се овај албум дели, други корисници више неће моћи да му приступе.", "album_info_updated": "Информација албума ажурирана", "album_leave": "Напустити албум?", "album_leave_confirmation": "Да ли стварно желите да напустите {album}?", @@ -360,6 +363,7 @@ "allow_edits": "Дозволи уређење", "allow_public_user_to_download": "Дозволите јавном кориснику да преузме (download-uje)", "allow_public_user_to_upload": "Дозволи јавном кориснику да отпреми (уплоад-ује)", + "anti_clockwise": "У смеру супротном од казаљке на сату", "api_key": "АПИ кључ (key)", "api_key_description": "Ова вредност ће бити приказана само једном. Обавезно копирајте пре него што затворите прозор.", "api_key_empty": "Име вашег АПИ кључа не би требало да буде празно", @@ -441,6 +445,7 @@ "clear_all_recent_searches": "Обришите све недавне претраге", "clear_message": "Обриши поруку", "clear_value": "Јасна вредност", + "clockwise": "У смеру казаљке", "close": "Затвори", "collapse": "Скупи", "collapse_all": "Скупи све", @@ -517,6 +522,8 @@ "do_not_show_again": "Не прикажи поново ову поруку", "done": "Урађено", "download": "Преузми", + "download_include_embedded_motion_videos": "Уграђени видео снимци", + "download_include_embedded_motion_videos_description": "Укључите видео записе уграђене у фотографије у покрету као засебну датотеку", "download_settings": "Преузимање", "download_settings_description": "Управљајте подешавањима везаним за преузимање датотека", "downloading": "Преузимање у току", @@ -550,6 +557,10 @@ "edit_user": "Уреди корисника", "edited": "Уређено", "editor": "Urednik", + "editor_close_without_save_prompt": "Промене неће бити сачуване", + "editor_close_without_save_title": "Затворити уређивач?", + "editor_crop_tool_h2_aspect_ratios": "Пропорције (aspect ratios)", + "editor_crop_tool_h2_rotation": "Ротација", "email": "Е-пошта", "empty": "", "empty_album": "Isprazni album", @@ -699,6 +710,7 @@ "expired": "Истекло", "expires_date": "Истиче {date}", "explore": "Истражите", + "explorer": "Претраживач (Explorer)", "export": "Извези", "export_as_json": "Извези ЈСОН", "extension": "Екстензија (Extension)", @@ -720,6 +732,7 @@ "filter_people": "Филтрирање особа", "find_them_fast": "Брзо их пронађите по имену помоћу претраге", "fix_incorrect_match": "Исправите нетачно подударање", + "folders": "Фасцикле (Folders)", "force_re-scan_library_files": "Принудно поново скенирајте све датотеке библиотеке", "forward": "Напред", "general": "Генерално", @@ -749,6 +762,10 @@ "image_alt_text_date_3_people": "{isVideo, select, true {Video} other {Image}} снимили {person1}, {person2}, и {person3} {date}", "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Video} other {Image}} снимили {person1}, {person2}, и {additionalCount, number} осталих {date}", "image_alt_text_date_place": "{isVideo, select, true {Video} other {Image}} снимљено у {city}, {country} {date}", + "image_alt_text_date_place_1_person": "{isVideo, select, true {Video} other {Image}} снимљено у {city}, {country} са {person1} {date}", + "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} снимљено у {city}, {country} са {person1} и {person2} {date}", + "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} снимљено у {city}, {country} са {person1}, {person2}, и {person3} {date}", + "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} снимљено у {city}, {country} са {person1}, {person2}, и {additionalCount, number} других {date}", "image_alt_text_people": "{count, plural, =1 {са {person1}} =2 {са {person1} и {person2}} =3 {са {person1}, {person2}, и {person3}} other {са {person1}, {person2}, и {others, number} остали}}", "image_alt_text_place": "у {city}, {country}", "image_taken": "{isVideo, select, true {Видео запис снимљен} other {Фотографија усликана}}", @@ -908,6 +925,7 @@ "ok": "Ок", "oldest_first": "Најстарије прво", "onboarding": "Приступање (Онбоардинг)", + "onboarding_privacy_description": "Следеће (опционе) функције се ослањају на спољне услуге и могу се онемогућити у било ком тренутку у подешавањима администрације.", "onboarding_theme_description": "Изаберите тему боја за свој налог. Ово можете касније да промените у подешавањима.", "onboarding_welcome_description": "Хајде да подесимо вашу инстанцу са неким уобичајеним подешавањима.", "onboarding_welcome_user": "Добродошли, {user}", @@ -981,6 +999,7 @@ "previous_memory": "Prethodno сећање", "previous_or_next_photo": "Prethodna или следећа фотографија", "primary": "Примарна (Primary)", + "privacy": "Приватност", "profile_image_of_user": "Слика профила од корисника {user}", "profile_picture_set": "Профилна слика постављена.", "public_album": "Јавни албум", @@ -1019,6 +1038,8 @@ "purchase_settings_server_activated": "Кључем производа сервера управља администратор", "range": "", "rating": "Оцена звездица", + "rating_clear": "Обриши оцену", + "rating_count": "{count, plural, one {# звезда} other {# звезде}}", "rating_description": "Прикажите exif оцену у инфо панелу", "raw": "", "reaction_options": "Опције реакције", @@ -1142,6 +1163,7 @@ "shared_by_user": "Дели {user}", "shared_by_you": "Ви делите", "shared_from_partner": "Слике од {partner}", + "shared_link_options": "Опције дељене везе", "shared_links": "Дељене везе", "shared_photos_and_videos_count": "{assetCount, plural, other {# дељене фотографије и видео записе.}}", "shared_with_partner": "Дели се са {partner}", @@ -1217,7 +1239,7 @@ "to_login": "Пријава", "to_trash": "Смеће", "toggle_settings": "Намести подешавања", - "toggle_theme": "Намести теме", + "toggle_theme": "Намести тамну тему", "toggle_visibility": "Namesti vidljivost", "total_usage": "Укупна употреба", "trash": "Отпад", @@ -1239,6 +1261,7 @@ "unlink_oauth": "Прекини везу са Oauth-om", "unlinked_oauth_account": "Опозвана веза OAuth налога", "unnamed_album": "Неименовани албум", + "unnamed_album_delete_confirmation": "Да ли сте сигурни да желите да избришете овај албум?", "unnamed_share": "Неименовано делење", "unsaved_change": "Несачувана промена", "unselect_all": "Поништи све", diff --git a/web/src/lib/i18n/sr_Latn.json b/web/src/lib/i18n/sr_Latn.json index 5741354bdecbd..63b3ae1f131c2 100644 --- a/web/src/lib/i18n/sr_Latn.json +++ b/web/src/lib/i18n/sr_Latn.json @@ -129,6 +129,7 @@ "map_enable_description": "Omogućite karakteristike mape", "map_gps_settings": "Map & GPS podešavanja", "map_gps_settings_description": "Upravljajte postavkama mape i GPS-a (obrnuto geokodiranje)", + "map_implications": "Funkcija mape se oslanja na eksternu uslugu pločica (tiles.immich.cloud)", "map_light_style": "Svetli stil", "map_manage_reverse_geocoding_settings": "Upravljajte podešavanjima Obrnuto geokodiranje", "map_reverse_geocoding": "Obrnuto geokodiranje", @@ -320,7 +321,8 @@ "user_settings": "Podešavanja korisnika", "user_settings_description": "Upravljajte korisničkim podešavanjima", "user_successfully_removed": "Korisnik {email} je uspešno uklonjen.", - "version_check_enabled_description": "Omogućite periodične zahteve GitHub-u za proveru novih izdanja", + "version_check_enabled_description": "Omogućite proveru novih izdanja", + "version_check_implications": "Funkcija provere verzije se oslanja na periodičnu komunikaciju sa github.com", "version_check_settings": "Provera verzije", "version_check_settings_description": "Omogućite/onemogućite obaveštenje o novoj verziji", "video_conversion_job": "Transkodiranje video zapisa", @@ -336,7 +338,8 @@ "album_added": "Album dodan", "album_added_notification_setting_description": "Primi obaveštenje e-poštom kad budeš dodan u deljen album", "album_cover_updated": "Omot albuma ažuriran", - "album_delete_confirmation": "Da li stvarno želite da izbrišete album {album}?\nAko se ovaj album deli, drugi korisnici više neće moći da mu pristupe.", + "album_delete_confirmation": "Da li stvarno želite da izbrišete album {album}?", + "album_delete_confirmation_description": "Ako se ovaj album deli, drugi korisnici više neće moći da mu pristupe.", "album_info_updated": "Informacija albuma ažurirana", "album_leave": "Napustiti album?", "album_leave_confirmation": "Da li stvarno želite da napustite {album}?", @@ -360,6 +363,7 @@ "allow_edits": "Dozvoli uređenje", "allow_public_user_to_download": "Dozvolite javnom korisniku da preuzme (download-uje)", "allow_public_user_to_upload": "Dozvoli javnom korisniku da otpremi (upload-uje)", + "anti_clockwise": "U smeru suprotnom od kazaljke na satu", "api_key": "API ključ (key)", "api_key_description": "Ova vrednost će biti prikazana samo jednom. Obavezno kopirajte pre nego što zatvorite prozor.", "api_key_empty": "Ime vašeg API ključa ne bi trebalo da bude prazno", @@ -441,6 +445,7 @@ "clear_all_recent_searches": "Obrišite sve nedavne pretrage", "clear_message": "Obriši poruku", "clear_value": "Jasna vrednost", + "clockwise": "U smeru kazaljke", "close": "Zatvori", "collapse": "Skupi", "collapse_all": "Skupi sve", @@ -517,6 +522,8 @@ "do_not_show_again": "Ne prikaži ponovo ovu poruku", "done": "Urađeno", "download": "Preuzmi", + "download_include_embedded_motion_videos": "Ugrađeni video snimci", + "download_include_embedded_motion_videos_description": "Uključite video zapise ugrađene u fotografije u pokretu kao zasebnu datoteku", "download_settings": "Preuzimanje", "download_settings_description": "Upravljajte podešavanjima vezanim za preuzimanje datoteka", "downloading": "Preuzimanje u toku", @@ -550,6 +557,10 @@ "edit_user": "Uredi korisnika", "edited": "Uređeno", "editor": "Urednik", + "editor_close_without_save_prompt": "Promene neće biti sačuvane", + "editor_close_without_save_title": "Zatvoriti uređivač?", + "editor_crop_tool_h2_aspect_ratios": "Proporcije (aspect ratios)", + "editor_crop_tool_h2_rotation": "Rotacija", "email": "E-pošta", "empty": "", "empty_album": "Isprazni album", @@ -699,6 +710,7 @@ "expired": "Isteklo", "expires_date": "Ističe {date}", "explore": "Istražite", + "explorer": "Pretraživač (Explorer)", "export": "Izvezi", "export_as_json": "Izvezi JSON", "extension": "Ekstenzija (Extension)", @@ -720,6 +732,7 @@ "filter_people": "Filtriranje osoba", "find_them_fast": "Brzo ih pronađite po imenu pomoću pretrage", "fix_incorrect_match": "Ispravite netačno podudaranje", + "folders": "Fascikle (Folders)", "force_re-scan_library_files": "Prinudno ponovo skenirajte sve datoteke biblioteke", "forward": "Napred", "general": "Generalno", @@ -912,6 +925,7 @@ "ok": "Ok", "oldest_first": "Najstarije prvo", "onboarding": "Pristupanje (Onboarding)", + "onboarding_privacy_description": "Sledeće (opcione) funkcije se oslanjaju na spoljne usluge i mogu se onemogućiti u bilo kom trenutku u podešavanjima administracije.", "onboarding_theme_description": "Izaberite temu boja za svoj nalog. Ovo možete kasnije da promenite u podešavanjima.", "onboarding_welcome_description": "Hajde da podesimo vašu instancu sa nekim uobičajenim podešavanjima.", "onboarding_welcome_user": "Dobrodošli, {user}", @@ -985,6 +999,7 @@ "previous_memory": "Prethodno sećanje", "previous_or_next_photo": "Prethodna ili sledeća fotografija", "primary": "Primarna (Primary)", + "privacy": "Privatnost", "profile_image_of_user": "Slika profila od korisnika {user}", "profile_picture_set": "Profilna slika postavljena.", "public_album": "Javni album", @@ -1023,6 +1038,8 @@ "purchase_settings_server_activated": "Ključem proizvoda servera upravlja administrator", "range": "", "rating": "Ocena zvezdica", + "rating_clear": "Obriši ocenu", + "rating_count": "{count, plural, one {# zvezda} other {# zvezde}}", "rating_description": "Prikažite exif ocenu u info panelu", "raw": "", "reaction_options": "Opcije reakcije", @@ -1146,6 +1163,7 @@ "shared_by_user": "Deli {user}", "shared_by_you": "Vi delite", "shared_from_partner": "Slike od {partner}", + "shared_link_options": "Opcije deljene veze", "shared_links": "Deljene veze", "shared_photos_and_videos_count": "{assetCount, plural, other {# deljene fotografije i video zapise.}}", "shared_with_partner": "Deli se sa {partner}", @@ -1221,7 +1239,7 @@ "to_login": "Prijava", "to_trash": "Smeće", "toggle_settings": "Namesti podešavanja", - "toggle_theme": "Namesti teme", + "toggle_theme": "Namesti tamnu temu", "toggle_visibility": "Namesti vidljivost", "total_usage": "Ukupna upotreba", "trash": "Otpad", @@ -1243,6 +1261,7 @@ "unlink_oauth": "Prekini vezu sa Oauth-om", "unlinked_oauth_account": "Opozvana veza OAuth naloga", "unnamed_album": "Neimenovani album", + "unnamed_album_delete_confirmation": "Da li ste sigurni da želite da izbrišete ovaj album?", "unnamed_share": "Neimenovano delenje", "unsaved_change": "Nesačuvana promena", "unselect_all": "Poništi sve", diff --git a/web/src/lib/i18n/sv.json b/web/src/lib/i18n/sv.json index 3eec79b61506f..6bd9d9b72ee27 100644 --- a/web/src/lib/i18n/sv.json +++ b/web/src/lib/i18n/sv.json @@ -7,7 +7,7 @@ "actions": "Händelser", "active": "Aktiva", "activity": "Aktivitet", - "activity_changed": "Aktiviteten är {aktiverad, välj, sant {aktiverad} annat {inaktiverad}}", + "activity_changed": "Aktiviteten är {enabled, select, true {aktiverad} other {inaktiverad}}", "add": "Lägg till", "add_a_description": "Lägg till en beskrivning", "add_a_location": "Lägg till en plats", @@ -129,12 +129,13 @@ "map_enable_description": "Aktivera kartfunktioner", "map_gps_settings": "Karta & GPS Inställningar", "map_gps_settings_description": "Ändra kartor & GPS (Omvänd geokodning) inställningar", + "map_implications": "Kartfunktionen är beroende av en extern kartbitstjänst (tiles.immich.cloud)", "map_light_style": "Ljus stil", "map_manage_reverse_geocoding_settings": "Hantera inställningar för Omvänd geokodning", "map_reverse_geocoding": "Omvänd Geokodning", "map_reverse_geocoding_enable_description": "Aktivera omvänd geokodning", "map_reverse_geocoding_settings": "Inställningar för omvänd geokodning", - "map_settings": "Kartinställningar", + "map_settings": "Karta", "map_settings_description": "Hantera kartinställningar", "map_style_description": "URL till en style.json-karto tema", "metadata_extraction_job": "Extrahera metadata", @@ -157,7 +158,7 @@ "notification_email_setting_description": "Inställningar för att skicka epostnotiser", "notification_email_test_email": "Skicka test-epost", "notification_email_test_email_failed": "Misslyckades med att skicka test-epost, undersök dina värden", - "notification_email_test_email_sent": "Ett test-epostmeddelande has skickats till {epost}. Kolla din inkorg.", + "notification_email_test_email_sent": "Ett testmail har skickats till {email}. Kontrollera din inkorg.", "notification_email_username_description": "Användarnamn att använda vid autentisering med epost-servern", "notification_enable_email_notifications": "Aktivera epost-notiser", "notification_settings": "Notisinställningar", @@ -181,12 +182,12 @@ "oauth_settings_description": "Hantera OAuth-logininställningar", "oauth_settings_more_details": "För ytterligare detaljer om denna funktion, se dokumentationen.", "oauth_signing_algorithm": "Signeringsalgoritm", - "oauth_storage_label_claim": "", - "oauth_storage_label_claim_description": "", - "oauth_storage_quota_claim": "", - "oauth_storage_quota_claim_description": "", + "oauth_storage_label_claim": "Användaranknuten lagringsetikett", + "oauth_storage_label_claim_description": "Sätter automatiskt angiven användares lagringsetikett.", + "oauth_storage_quota_claim": "Användaranknuten lagringskvot", + "oauth_storage_quota_claim_description": "Sätter automatiskt angiven användares lagringskvot.", "oauth_storage_quota_default": "Standardlagringskvot (GiB)", - "oauth_storage_quota_default_description": "", + "oauth_storage_quota_default_description": "Kvot i GiB som används när ingen fordran angetts (Ange 0 för obegränsad kvot).", "offline_paths": "Offline-sökvägar", "offline_paths_description": "Dessa resultat kan bero på manuell borttagning av filer som inte är en del av ett externt bibliotek.", "password_enable_description": "Logga in med epost och lösenord", @@ -197,23 +198,36 @@ "refreshing_all_libraries": "Samtliga bibliotek uppdateras", "registration": "Administratörsregistrering", "registration_description": "Du utses till administratör eftersom du är systemets första användare. Du ansvarar för administration och kan skapa ytterligare användare.", - "removing_offline_files": "Tar Bort Offline-Filer", + "removing_offline_files": "Tar bort offline-filer", "repair_all": "Reparera alla", + "repair_matched_items": "Matchade {antal, plural, ett {# föremål} övriga {# föremål}}", + "repaired_items": "Reparerade {count, plural, one {# item} other {# items}}", + "require_password_change_on_login": "Kräv av användaren att byta lösenord vid första inloggning", "reset_settings_to_default": "Återställ inställningar till standard", + "reset_settings_to_recent_saved": "Återställ inställningar till de senaste sparade", + "scanning_library_for_changed_files": "Scannar bibliotek efter ändrade filer", "scanning_library_for_new_files": "Skannar biblioteket efter nya filer", + "send_welcome_email": "Skicka välkomstmail", "server_external_domain_settings": "Extern domän", "server_external_domain_settings_description": "Domän för publikt delade länkar, inklusive http(s)://", "server_settings": "Serverinställningar", "server_settings_description": "Hantera serverinställningar", "server_welcome_message": "Välkomstmeddelande", "server_welcome_message_description": "Ett meddelande som visas på inloggningssidan.", - "sidecar_job_description": "", + "sidecar_job": "Medföljande metadata", + "sidecar_job_description": "Upptäck eller synkronisera medföljande metadata från filsystemet", "slideshow_duration_description": "Antal sekunder att visa varje bild", - "smart_search_job_description": "", - "storage_template_enable_description": "", - "storage_template_hash_verification_enabled": "", + "smart_search_job_description": "Kör maskininlärning på objekt för att stödja smart sökning", + "storage_template_date_time_description": "Tidsstämpel för resursens skapande används för datum och tidsinformation", + "storage_template_date_time_sample": "Exempeltid {date}", + "storage_template_enable_description": "Aktivera mallmotor för lagring", + "storage_template_hash_verification_enabled": "Hash-verifiering aktiverat", "storage_template_hash_verification_enabled_description": "Aktiverar hash-verifiering, deaktiviera inte om du inte är säker på implikationerna", - "storage_template_migration_job": "", + "storage_template_migration_info": "Ändringar i mall gäller endast nya resurser. För att retoaktivt tillämpa mallen på tidigare uppladdade resurser kör {job}.", + "storage_template_migration_job": "Lagringsmall migreringsjobb", + "storage_template_more_details": "För mer information om den här funktionen se Lagringsmall och dess konsekvenser", + "storage_template_onboarding_description": "Vid aktivering organiserar denna funktion automatiskt filer baserat på en användardefinierad mall. På grunda av stabilitetsproblem är denna funktion avstängd som standard, för mer information se dokumentation.", + "storage_template_path_length": "Uppskattad längdbegränsning på sökväg: {length, number}/{limit, number}", "storage_template_settings": "Lagringsmall", "storage_template_settings_description": "", "system_settings": "Systeminställningar", @@ -221,22 +235,26 @@ "theme_custom_css_settings_description": "", "theme_settings": "Temainställningar", "theme_settings_description": "Hantera anpassningar av webbgränssnittet för Immich", - "thumbnail_generation_job_description": "", + "these_files_matched_by_checksum": "Dessa filer matchas av deras kontrollsummor", + "thumbnail_generation_job": "Generera Miniatyrer", + "thumbnail_generation_job_description": "Generera stora, små och suddiga miniatyrer för varje objekt, samt för varje person", "transcode_policy_description": "", - "transcoding_acceleration_api": "", - "transcoding_acceleration_api_description": "", + "transcoding_acceleration_api": "Accelerations-API", + "transcoding_acceleration_api_description": "API som kommer att interagera med din enhet för att accelerera omkodning. Inställning är 'best effort': vid fel kommer den att återgå till mjukvarubaserad omkodning. VP9 kan fungera eller inte, beroende på din hårdvara.", "transcoding_acceleration_nvenc": "NVENC (kräver NVIDIA GPU)", - "transcoding_acceleration_qsv": "", + "transcoding_acceleration_qsv": "Quick Sync (kräver 7 generationens Intel CPU eller senare)", "transcoding_acceleration_rkmpp": "RKMPP (bara med Rockchip SOCs)", "transcoding_acceleration_vaapi": "VAAPI", - "transcoding_accepted_audio_codecs": "", - "transcoding_accepted_audio_codecs_description": "", - "transcoding_accepted_video_codecs": "", - "transcoding_accepted_video_codecs_description": "", + "transcoding_accepted_audio_codecs": "Accepterade ljud-codecs", + "transcoding_accepted_audio_codecs_description": "Välj vilka ljud-codecs som inte behöver omkodas. Används endast för vissa omkodningspolicyer.", + "transcoding_accepted_containers": "Accepterade behållare", + "transcoding_accepted_video_codecs": "Accepterade video-codecs", + "transcoding_accepted_video_codecs_description": "Välj vilka video-codecs som inte behöver omkodas. Används endast för vissa omkodningspolicyer.", "transcoding_advanced_options_description": "Val som de flesta användare inte bör behöva ändra", - "transcoding_audio_codec": "", - "transcoding_audio_codec_description": "", - "transcoding_bitrate_description": "", + "transcoding_audio_codec": "Ljud-codec", + "transcoding_audio_codec_description": "Opus är bästa kvalitetsvalet, men är inte lika kompatibelt med äldre enheter eller mjukvara.", + "transcoding_bitrate_description": "Videor som är i högre än max bithastighet eller inte i ett accepterat format", + "transcoding_codecs_learn_more": "För att läsa mer om terminologin här se FFmpeg-dokumentationen för H.264 kodek, HEVC kodek och VP9 kodek.", "transcoding_constant_quality_mode": "", "transcoding_constant_quality_mode_description": "", "transcoding_constant_rate_factor": "", @@ -246,17 +264,17 @@ "transcoding_hardware_acceleration_description": "", "transcoding_hardware_decoding": "Hårdvaruavkodning", "transcoding_hardware_decoding_setting_description": "", - "transcoding_hevc_codec": "", + "transcoding_hevc_codec": "HEVC-codec", "transcoding_max_b_frames": "", - "transcoding_max_b_frames_description": "", - "transcoding_max_bitrate": "", + "transcoding_max_b_frames_description": "Högre värden förbättrar kompressionseffektiviteten, men saktar ner kodningen. Kan vara inkompatibel med hårdvaruacceleration på äldre enheter. 0 avaktiverar B-frames, medan -1 anger detta värde automatiskt.", + "transcoding_max_bitrate": "Max bithastighet", "transcoding_max_bitrate_description": "", "transcoding_max_keyframe_interval": "Max nyckelbildruteintervall", "transcoding_max_keyframe_interval_description": "", "transcoding_optimal_description": "", "transcoding_preferred_hardware_device": "", "transcoding_preferred_hardware_device_description": "", - "transcoding_preset_preset": "", + "transcoding_preset_preset": "Förinställning (-preset)", "transcoding_preset_preset_description": "", "transcoding_reference_frames": "", "transcoding_reference_frames_description": "", @@ -831,7 +849,8 @@ "total_usage": "Total användning", "trash": "Papperskorg", "trash_all": "", - "trash_no_results_message": "", + "trash_no_results_message": "Borttagna foton och videor kommer att visas här.", + "trashed_items_will_be_permanently_deleted_after": "Borttagna objekt kommer att tas bort permanent efter {days, plural, one {# dag} other {# dagar}}.", "type": "Typ", "unarchive": "Ångra arkivering", "unarchived": "", @@ -843,35 +862,40 @@ "unlimited": "Obegränsat", "unlink_oauth": "", "unlinked_oauth_account": "", + "unsaved_change": "Osparade ändringar", "unselect_all": "", "unstack": "Stapla Av", "up_next": "", - "updated_password": "", + "updated_password": "Lösenordet har uppdaterats", "upload": "Ladda upp", "upload_concurrency": "", "upload_status_duplicates": "Dubbletter", "upload_status_errors": "Fel", - "url": "", - "usage": "", + "url": "URL", + "usage": "Användning", "user": "Användare", - "user_id": "", + "user_id": "Användar-ID", + "user_purchase_settings": "Köp", + "user_purchase_settings_description": "Hantera dina köp", "user_usage_detail": "", - "username": "", + "username": "Användarnamn", "users": "Användare", "utilities": "Verktyg", "validate": "Validera", "variables": "Variabler", "version": "Version", + "version_announcement_closing": "Din vän, Alex", "video": "Video", "video_hover_setting_description": "", "videos": "Videor", "videos_count": "{count, plural, one {# Video} other {# Videor}}", "view": "Visa", + "view_album": "Visa Album", "view_all": "Visa alla", "view_all_users": "Visa alla användare", "view_links": "Visa länkar", - "view_next_asset": "", - "view_previous_asset": "", + "view_next_asset": "Visa nästa objekt", + "view_previous_asset": "Visa föregående objekt", "viewer": "", "waiting": "Väntar", "warning": "Varning", @@ -881,5 +905,6 @@ "year": "År", "years_ago": "{years, plural, one {# år} other {# år}} sedan", "yes": "Ja", + "you_dont_have_any_shared_links": "Du har inga delade länkar", "zoom_image": "Zooma bild" } diff --git a/web/src/lib/i18n/ta.json b/web/src/lib/i18n/ta.json index 543bfda2cded1..ec3f27124bdc2 100644 --- a/web/src/lib/i18n/ta.json +++ b/web/src/lib/i18n/ta.json @@ -1,4 +1,5 @@ { + "about": "விபரம்", "account": "கணக்கு", "account_settings": "கணக்கு அமைவுகள்", "acknowledge": "ஒப்புக்கொள்கிறேன்", @@ -6,6 +7,7 @@ "actions": "செயல்கள்", "active": "செயல்பாட்டில்", "activity": "செயல்பாடுகள்", + "activity_changed": "செயல்பாடு {இயக்கப்பட்டது, தேர்ந்தெடு, சரி {இயக்கப்பட்டது} மற்றது {முடக்கப்பட்டது}}", "add": "சேர்", "add_a_description": "விவரம் சேர்", "add_a_location": "இடத்தை சேர்க்கவும்", @@ -25,11 +27,11 @@ "added_to_favorites": "விருப்பங்களில் (பேவரிட்ஸ்) சேர்க்கப்பட்டது", "added_to_favorites_count": "விருப்பங்களில் (பேவரிட்ஸ்) {count} சேர்க்கப்பட்டது", "admin": { - "add_exclusion_pattern_description": "", + "add_exclusion_pattern_description": "விலக்கு வடிவங்களைச் சேர்க்கவும். *, **, மற்றும் ? ஆதரிக்கப்படுகிறது. \"Raw\" என்ற பெயரிடப்பட்ட எந்த கோப்பகத்திலும் உள்ள எல்லா கோப்புகளையும் புறக்கணிக்க, \"**/Raw/**\" ஐப் பயன்படுத்தவும். \".tif\" இல் முடியும் எல்லா கோப்புகளையும் புறக்கணிக்க, \"**/*.tif\" ஐப் பயன்படுத்தவும். ஒரு முழுமையான பாதையை புறக்கணிக்க, \"/path/to/ignore/**\" ஐப் பயன்படுத்தவும்.", "authentication_settings": "அடையாள உறுதிப்படுத்தல் அமைப்புகள் (செட்டிங்ஸ்)", "authentication_settings_description": "கடவுச்சொல், OAuth, மற்றும் பிற அடையாள அமைப்புகள்", "authentication_settings_disable_all": "எல்லா உள்நுழைவு முறைகளையும் நிச்சயமாக முடக்க விரும்புகிறீர்களா? உள்நுழைவு முற்றிலும் முடக்கப்படும்.", - "authentication_settings_reenable": "மீண்டும் இயக்க, சர்வர் கட்டளை பயன்படுத்தவும்", + "authentication_settings_reenable": "மீண்டும் இயக்க, சர்வர் கட்டளை பயன்படுத்தவும்.", "background_task_job": "பின்னணி பணிகள்", "check_all": "அனைத்தையும் தேர்ந்தெடு", "cleared_jobs": "முடித்த வேலைகள்: {job}", @@ -47,7 +49,7 @@ "face_detection": "முகம் கண்டறிதல்", "face_detection_description": "இயந்திர கற்றலைப் பயன்படுத்தி சொத்துக்களில் உள்ள முகங்களைக் கண்டறியவும். வீடியோக்களுக்கு, சிறுபடம் மட்டுமே கருதப்படுகிறது. \"அனைத்து\" (மறு-) அனைத்து சொத்துகளையும் செயலாக்குகிறது. இதுவரை செயலாக்கப்படாத புகைப்பட சொத்துக்களை \"காணவில்லை\" வரிசைப்படுத்துகிறது. முகம் கண்டறிதல் முடிந்ததும், கண்டறியப்பட்ட முகங்கள், ஏற்கனவே இருக்கும் அல்லது புதிய நபர்களாகக் குழுவாக்கப்பட்டு, முக அடையாளத்திற்காக வரிசையில் நிறுத்தப்படும்.", "facial_recognition_job_description": "நபர்களின் முகங்களைக் குழு கண்டறிந்தது. முகம் கண்டறிதல் முடிந்ததும் இந்தப் படி இயங்கும். அனைத்து முகங்களையும் \"அனைத்து\" (மறு-) கொத்துகள். \"காணவில்லை\" என்பது நபர் நியமிக்கப்படாத முகங்களை வரிசைப்படுத்துகிறது.", - "failed_job_command": "", + "failed_job_command": "பணிக்கான கட்டளை {command} தோல்வியடைந்தது: {job}", "force_delete_user_warning": "எச்சரிக்கை: இது பயனரையும் அனைத்து புகைப்பட சொத்துகளையும் உடனடியாக அகற்றும். இதை செயல்தவிர்க்க முடியாது மற்றும் புகைப்படங்களை மீட்டெடுக்க முடியாது.", "forcing_refresh_library_files": "அனைத்து லைப்ரரி புகைப்படங்களையும் கட்டாயப்படுத்தி புதுப்பிக்கவும்", "image_format_description": "WebP, JPEG ஐ விட சிறிய கோப்புகளை உருவாக்குகிறது, ஆனால் குறியாக்கம் செய்ய மெதுவாக உள்ளது.", diff --git a/web/src/lib/i18n/th.json b/web/src/lib/i18n/th.json index d7348f37e28ae..19496b423843f 100644 --- a/web/src/lib/i18n/th.json +++ b/web/src/lib/i18n/th.json @@ -223,7 +223,7 @@ "storage_template_migration": "การย้ายเทมเพลตที่เก็บข้อมูล", "storage_template_migration_description": "ใช้{template}ปัจจุบันกับสื่อที่อัพโหลดก่อนหน้านี้", "storage_template_migration_job": "", - "storage_template_settings": "", + "storage_template_settings": "เทมเพลตการจัดเก็บข้อมูล", "storage_template_settings_description": "", "system_settings": "การตั้งค่าระบบ", "theme_custom_css_settings": "CSS กําหนดเอง", diff --git a/web/src/lib/i18n/tr.json b/web/src/lib/i18n/tr.json index 2960af9ff5d23..7bf59d84dbad2 100644 --- a/web/src/lib/i18n/tr.json +++ b/web/src/lib/i18n/tr.json @@ -27,17 +27,17 @@ "added_to_favorites": "Favorilere eklendi", "added_to_favorites_count": "{count, number} fotoğraf favorilere eklendi", "admin": { - "add_exclusion_pattern_description": "Dışlama desenleri ekleyin. *, ** ve ? kullanılarak globbing desteklenir. Herhangi bir \"Raw\" adlı dizindeki tüm dosyaları yoksaymak için \"**/Raw/**\" kullanın. \".tif\" ile biten tüm dosyaları yoksaymak için \"**/*.tif\" kullanın. Mutlak yolu yoksaymak için \"/path/to/ignore/**\" kullanın.", - "authentication_settings": "Yetkilendirme ayarları", + "add_exclusion_pattern_description": "Dışlama desenleri ekleyin. *, ** ve ? kullanılarak Globbing (temsili yer doldurucu karakter) desteklenir. Farzedelim \"Raw\" adlı bir dizininiz var, içinde ki tüm dosyaları yoksaymak için \"**/Raw/**\" şeklinde yazabilirsiniz. \".tif\" ile biten tüm dosyaları yoksaymak için \"**/*.tif\" yazabilirsiniz. Mutlak yolu yoksaymak için \"/yoksayılacak/olan/yol/**\" şeklinde yazabilirsiniz.", + "authentication_settings": "Yetkilendirme Ayarları", "authentication_settings_description": "Şifre, OAuth, ve diğer yetkilendirme ayarlarını yönet", "authentication_settings_disable_all": "Tüm giriş yöntemlerini devre dışı bırakmak istediğinize emin misiniz? Giriş yapma fonksiyonu tamamen devre dışı bırakılacak.", "authentication_settings_reenable": "Yeniden aktif etmek için Sunucu Komutu'nu kullanın.", - "background_task_job": "Arka plan görevleri", - "check_all": "Hepsini kontrol et", + "background_task_job": "Arka Plan Görevleri", + "check_all": "Hepsini Kontrol Et", "cleared_jobs": "{job} için işler temizlendi", - "config_set_by_file": "Ayarlar şuan için config dosyası tarafından ayarlandı", + "config_set_by_file": "Ayarlar şuanda config dosyası tarafından ayarlanmıştır", "confirm_delete_library": "{library} kütüphanesini silmek istediğinize emin misiniz?", - "confirm_delete_library_assets": "Bu kütüphaneyi silmek istediğinize emin misiniz? Bu işlem {count, plural, one {# contained asset} other {all # contained assets}} tane varlığı Immich'den silecek ve bu işlem geri alınamaz. Silinen dosyalar diskten silinmeyecek.", + "confirm_delete_library_assets": "Bu kütüphaneyi silmek istediğinize emin misiniz? Bu işlem {count, plural, one {# tane varlığı} other {all # tane varlığı}} Immich'den silecek ve bu işlem geri alınamaz. Silinen dosyalar diskten silinmeyecek.", "confirm_email_below": "Onaylamak için aşağıya {email} yazın", "confirm_reprocess_all_faces": "Tüm yüzleri tekrardan işlemek istediğinize emin misiniz? Bu işlem isimlendirilmiş insanları da silecek.", "confirm_user_password_reset": "{user} adlı kullanıcının şifresini sıfırlamak istediğinize emin misiniz?", @@ -46,10 +46,10 @@ "duplicate_detection_job_description": "Benzer fotoğrafları bulmak için makine öğrenmesini çalıştır. Bu işlem Akıllı Arama'ya bağlıdır", "exclusion_pattern_description": "Kütüphaneyi tararken dosya ve klasörleri görmezden gelmek için dışlama desenlerini kullanabilirsiniz. RAW dosyaları gibi bazı dosya ve klasörleri içe aktarmak istemediğinizde bu seçeneği kullanabilirsiniz.", "external_library_created_at": "Dış kütüphane ({date} tarihinde oluşturuldu.)", - "external_library_management": "Dış kütüphane yönetimi", + "external_library_management": "Dış Kütüphane Yönetimi", "face_detection": "Yüz tarama", - "face_detection_description": "Makine öğrenmesini kullanarak medyalardaki yüzleri bulun. Videolar için sadece önizleme görüntüleri kullanılacak. \"All\" tüm medyaları tekrardan işler. \"Missing\" daha önce işlenmemiş medyaları işlenmeleri için sıraya koyar. Tespit edilen yüzler yüz tarama işlemi tamamlandıktan sonra Yüz Tanıma için sıraya koyulacak ve kişiler olarak gruplandırılacak.", - "facial_recognition_job_description": "Tespit edilen yüzleri gruplandır. Bu işlem, yüz tanıma işlemi tamamlandıktan sonra çalışır. \"All\" tüm yüzleri gruplandırır. \"Missing\" ise tespit edilen fakat kişi atanmamış olan yüzleri sıraya koyar.", + "face_detection_description": "Makine öğrenmesini kullanarak içeriklerinizde ki yüzleri bulun. Videolar için sadece önizleme görüntüleri kullanılacak. \"Hepsi\" seçeneği tüm medyaları tekrardan işler. \"İşlenmemiş\" daha önceden işlenmemiş içerikleri işlenmeleri için sıraya koyar. Tespit edilen yüzler yüz tarama işlemi tamamlandıktan sonra Yüz Tanıma için sıraya koyulacak ve kişiler olarak gruplandırılacak.", + "facial_recognition_job_description": "Tespit edilen yüzleri gruplandır. Bu işlem, yüz tanıma işlemi tamamlandıktan sonra çalışır. \"Hepsi\" tüm yüzleri gruplandırır. \"İşlenmemiş\" ise tespit edilen fakat kişi atanmamış olan yüzleri sıraya koyar.", "failed_job_command": "{job} işi için {command} komutu başarısız", "force_delete_user_warning": "UYARI: Bu işlem kullanıcıyı ve bütün verilerini silecek. Bu işlem geri alınamaz ve silinen veriler geri kurtarılamaz.", "forcing_refresh_library_files": "Tüm kütüphane dosyaları yenileniyor", @@ -128,6 +128,7 @@ "map_enable_description": "Harita ayarlarını etkinleştir", "map_gps_settings": "Harita & GPS Ayarları", "map_gps_settings_description": "Harita Yönetimi & GPS (Ters Jeokodlama) Ayarları", + "map_implications": "Harita özelliği, harici bir döşeme hizmetine (tiles.immich.cloud) bağlıdır", "map_light_style": "Açık mod", "map_manage_reverse_geocoding_settings": "Coğrafi Kodlama ayarlarını yönet", "map_reverse_geocoding": "Coğrafi Kodlama", @@ -257,7 +258,7 @@ "transcoding_bitrate_description": "Videolar maksimum bir oranından yürksek ya da kabul edilir bir formatta değil", "transcoding_codecs_learn_more": "Buradaki terminolojiyi öğrenmek için FFmpeg dokümantasyonlarına bakabilirsiniz: H.264, HEVC ve VP9.", "transcoding_constant_quality_mode": "Sabit kalite modu", - "transcoding_constant_quality_mode_description": "", + "transcoding_constant_quality_mode_description": "ICQ, CQP'den daha iyidir, ancak bazı donanım hızlandırma cihazları bu modu desteklemez. Bu seçeneğin ayarlanması, kalite tabanlı kodlama kullanırken belirtilen modu tercih eder. ICQ'yu desteklemediği için NVENC tarafından göz ardı edilir.", "transcoding_constant_rate_factor": "Sabit oran faktörü (-SOF)", "transcoding_constant_rate_factor_description": "Video kalite seviyesi. Tipik değerler H.264 için 23, HEVC için 28, VP9 için 31 ve AV1 için 35'tir. Daha düşük değerler daha iyi kalite sağlar, ancak daha büyük dosyalar üretir.", "transcoding_disabled_description": "Videoları dönüştürmeyin, bazı istemcilerde oynatma bozulabilir", @@ -272,16 +273,16 @@ "transcoding_max_bitrate_description": "Maksimum bit hızı ayarlamak, kaliteye küçük bir maliyetle dosya boyutlarını daha öngörülebilir hale getirebilir.", "transcoding_max_keyframe_interval": "", "transcoding_max_keyframe_interval_description": "", - "transcoding_optimal_description": "", + "transcoding_optimal_description": "Hedef çözünürlükten yüksek veya kabul edilen formatta olmayan videolar", "transcoding_preferred_hardware_device": "Tercih edilen donanım cihazı", "transcoding_preferred_hardware_device_description": "Sadece VAAPI ve QSV için uygulanır. Donanım kod çevrimi için DRI Node ayarlar.", "transcoding_preset_preset": "", "transcoding_preset_preset_description": "Sıkıştırma hızı. Daha yavaş olan ayarlar belirli bitrate ayarları için daha küçük ve daha kaliteli dosya üretir. VP9 ayarı 'daha hızlı' ayarının üstündeki ayarları görmezden gelir.", "transcoding_reference_frames": "Referans kareler", "transcoding_reference_frames_description": "", - "transcoding_required_description": "", - "transcoding_settings": "", - "transcoding_settings_description": "", + "transcoding_required_description": "Yalnızca kabul edilen formatta olmayan videolar", + "transcoding_settings": "Video Dönüştürme Ayarları", + "transcoding_settings_description": "Video dosyalarının çözünürlük ve kodlama bilgilerini yönetir", "transcoding_target_resolution": "Hedef çözünürlük", "transcoding_target_resolution_description": "Daha yüksek çözünürlükler daha fazla detayı koruyabilir fakat işlemesi daha uzun sürer, dosya boyutu daha yüksek olur ve uygulamanın akıcılığını etkileyebilir.", "transcoding_temporal_aq": "", @@ -289,9 +290,9 @@ "transcoding_threads": "İş Parçacıkları", "transcoding_threads_description": "", "transcoding_tone_mapping": "Ton-haritalama", - "transcoding_tone_mapping_description": "", + "transcoding_tone_mapping_description": "HDR videoların SDR'ye dönüştürülürken görünümünü korumayı amaçlar. Her algoritma renk, detay ve parlaklık için farklı dengeleme yapar. Hable detayları korur, Mobius renkleri korur ve Reinhard parlaklığı korur.", "transcoding_tone_mapping_npl": "", - "transcoding_tone_mapping_npl_description": "", + "transcoding_tone_mapping_npl_description": "Renkler, bu parlaklıkta bir ekran için normal görünecek şekilde ayarlanacaktır. Karşıt olarak, daha düşük değerler videonun parlaklığını artırır ve tersi de geçerlidir çünkü ekranın parlaklığını telafi eder. 0 bu değeri otomatik olarak ayarlar.", "transcoding_transcode_policy": "", "transcoding_transcode_policy_description": "", "transcoding_two_pass_encoding": "", diff --git a/web/src/lib/i18n/uk.json b/web/src/lib/i18n/uk.json index 0b8241d89edcc..00137c37e4409 100644 --- a/web/src/lib/i18n/uk.json +++ b/web/src/lib/i18n/uk.json @@ -1,7 +1,7 @@ { "about": "Про програму", "account": "Обліковий запис", - "account_settings": "Налаштування Облікового запису", + "account_settings": "Налаштування профілю", "acknowledge": "Прийняти", "action": "Дія", "actions": "Дії", @@ -47,7 +47,7 @@ "duplicate_detection_job_description": "Запустити машинне навчання на активах для виявлення схожих зображень. Залежить від інтелектуального пошуку", "exclusion_pattern_description": "Шаблони виключень дозволяють ігнорувати файли та папки під час сканування вашої бібліотеки. Це корисно, якщо у вас є папки, які містять файли, які ви не хочете імпортувати, наприклад, RAW-файли.", "external_library_created_at": "Зовнішня бібліотека (створена {date})", - "external_library_management": "Управління Зовнішньою Бібліотекою", + "external_library_management": "Керування зовнішніми бібліотеками", "face_detection": "Виявлення обличчя", "face_detection_description": "Виявлення обличчя на активах з використанням машинного навчання. Для відео розглядається лише ескіз. Опція \"Усі\" повторно обробляє всі активи. Опція \"Відсутні\" ставить в чергу активи, які ще не були оброблені. Виявлені обличчя будуть поставлені в чергу для визначення обличчя після завершення виявлення обличчя, групуючи їх в існуючих або нових людей.", "facial_recognition_job_description": "Групувати виявлені обличчя у людей. Цей крок виконується після завершення виявлення обличчя. Опція \"Усі\" перегруповує всі обличчя. Опція \"Відсутні\" ставить в чергу обличчя, які ще не мають призначеної особи.", @@ -129,12 +129,13 @@ "map_enable_description": "Увімкнути функції мапи", "map_gps_settings": "Налаштування карти та GPS", "map_gps_settings_description": "Керування налаштуваннями карти та GPS (зворотний геокодинг)", + "map_implications": "Функція карти використовує зовнішній сервіс плиток (tiles.immich.cloud)", "map_light_style": "Світлий стиль", "map_manage_reverse_geocoding_settings": "Керувати налаштуваннями зворотного геокодування", "map_reverse_geocoding": "Зворотне геокодування", "map_reverse_geocoding_enable_description": "Увімкнути зворотне геокодування", "map_reverse_geocoding_settings": "Налаштування зворотного геокодування", - "map_settings": "Налаштування Мапи", + "map_settings": "Мапа", "map_settings_description": "Управління налаштуваннями мапи", "map_style_description": "URL до теми мапи у форматі style.json", "metadata_extraction_job": "Витягнути метадані", @@ -209,7 +210,7 @@ "send_welcome_email": "Надіслати лист з вітанням", "server_external_domain_settings": "Зовнішній домен", "server_external_domain_settings_description": "Домен для публічних загальнодоступних посилань, включаючи http(s)://", - "server_settings": "Налаштування Серверу", + "server_settings": "Налаштування сервера", "server_settings_description": "Керування налаштуваннями сервера", "server_welcome_message": "Вітальне повідомлення", "server_welcome_message_description": "Повідомлення, яке відображається на сторінці входу.", @@ -278,11 +279,11 @@ "transcoding_preferred_hardware_device": "Переважний апаратний пристрій", "transcoding_preferred_hardware_device_description": "Застосовується тільки до VAAPI і QSV. Встановлює вузол DRI, який використовується для апаратного транскодування.", "transcoding_preset_preset": "Параметр (-preset)", - "transcoding_preset_preset_description": "Швидкість стиснення. Повільніше предустановки створюють менші файли і підвищують якість при встановленні певного бітрейту. VP9 ігнорує швидкості вище `faster`.", + "transcoding_preset_preset_description": "Швидкість стиснення. Повільніші пресети створюють менші файли і підвищують якість при певному бітрейті. VP9 ігнорує швидкості вище 'швидше'.", "transcoding_reference_frames": "Основні кадри", "transcoding_reference_frames_description": "Кількість кадрів, на які посилається при стисненні даного кадру. Вищі значення покращують ефективність стиснення, але збільшують час кодування. Значення 0 автоматично налаштовує це значення.", "transcoding_required_description": "Лише відео, що не у прийнятому форматі", - "transcoding_settings": "Налаштування Транскодування Відео", + "transcoding_settings": "Налаштування транскодування відео", "transcoding_settings_description": "Керування роздільною здатністю та кодуванням відеофайлів", "transcoding_target_resolution": "Роздільна здатність", "transcoding_target_resolution_description": "Вищі роздільні здатності можуть зберігати більше деталей, але займають більше часу на кодування, мають більші розміри файлів і можуть зменшити швидкість роботи додатку.", @@ -312,7 +313,7 @@ "user_delete_delay_settings_description": "Кількість днів після видалення для остаточного видалення акаунта користувача та його ресурсів. Задача видалення користувача запускається опівночі для перевірки користувачів, готових до видалення. Зміни цього налаштування будуть оцінені під час наступного виконання.", "user_delete_immediately": "Акаунт та ресурси користувача {user} будуть негайно поставлені в чергу на остаточне видалення.", "user_delete_immediately_checkbox": "Поставити користувача та ресурси в чергу для негайного видалення", - "user_management": "Управління користувачами", + "user_management": "Керування користувачами", "user_password_has_been_reset": "Пароль користувача було скинуто:", "user_password_reset_description": "Будь ласка, надайте користувачеві тимчасовий пароль і повідомте йому, що він повинен буде змінити пароль при наступному вході.", "user_restore_description": "Акаунт {user} буде відновлено.", @@ -320,7 +321,8 @@ "user_settings": "Налаштування користувача", "user_settings_description": "Керування налаштуваннями користувачів", "user_successfully_removed": "Користувача з електронною поштою {email} успішно видалено.", - "version_check_enabled_description": "Увімкнення періодичних запитів до GitHub для перевірки нових випусків", + "version_check_enabled_description": "Увімкнути перевірку версії", + "version_check_implications": "Функція перевірки версії залежить від періодичної комунікації з github.com", "version_check_settings": "Перевірка версії", "version_check_settings_description": "Увімкнути/вимкнути сповіщення про нову версію", "video_conversion_job": "Перекодувати відео", @@ -336,7 +338,8 @@ "album_added": "Альбом додано", "album_added_notification_setting_description": "Отримувати повідомлення по електронній пошті, коли вас додають до спільного альбому", "album_cover_updated": "Обкладинка альбому оновлена", - "album_delete_confirmation": "Ви впевнені, що хочете видалити альбом {album}?\nЯкщо цей альбом є спільним, інші користувачі більше не зможуть отримувати до нього доступ.", + "album_delete_confirmation": "Ви впевнені, що хочете видалити альбом {album}?", + "album_delete_confirmation_description": "Якщо альбом був спільним, інші користувачі не зможуть отримати доступ до нього.", "album_info_updated": "Інформація про альбом оновлена", "album_leave": "Залишити альбом?", "album_leave_confirmation": "Ви впевнені, що хочете залишити альбом {album}?", @@ -360,6 +363,7 @@ "allow_edits": "Дозволити редагування", "allow_public_user_to_download": "Дозволити публічному користувачеві завантажувати файли", "allow_public_user_to_upload": "Дозволити публічним користувачам завантажувати", + "anti_clockwise": "Проти годинникової стрілки", "api_key": "Ключ API", "api_key_description": "Це значення буде показане лише один раз. Будь ласка, обов'язково скопіюйте його перед закриттям вікна.", "api_key_empty": "Назва вашого ключа API не може бути порожньою", @@ -440,6 +444,7 @@ "clear_all_recent_searches": "Очистити всі останні пошукові запити", "clear_message": "Очистити повідомлення", "clear_value": "Очистити значення", + "clockwise": "По годинниковій стрілці", "close": "Закрити", "collapse": "Згорнути", "collapse_all": "Згорнути все", @@ -516,6 +521,8 @@ "do_not_show_again": "Не показувати це повідомлення знову", "done": "Готово", "download": "Скачати", + "download_include_embedded_motion_videos": "Вбудовані відео", + "download_include_embedded_motion_videos_description": "Включати відео, вбудовані в рухомі фотографії, як окремий файл", "download_settings": "Скачати", "download_settings_description": "Керування налаштуваннями, пов'язаними з завантаженням ресурсів", "downloading": "Скачування", @@ -548,7 +555,11 @@ "edit_title": "Редагувати заголовок", "edit_user": "Редагувати користувача", "edited": "Відредаговано", - "editor": "", + "editor": "Редактор", + "editor_close_without_save_prompt": "Зміни не будуть збережені", + "editor_close_without_save_title": "Закрити редактор?", + "editor_crop_tool_h2_aspect_ratios": "Пропорції зображення", + "editor_crop_tool_h2_rotation": "Орієнтація", "email": "Електронна пошта", "empty": "", "empty_album": "", @@ -698,6 +709,7 @@ "expired": "Закінчився термін дії", "expires_date": "Термін дії закінчується {date}", "explore": "Дослідити", + "explorer": "Провідник", "export": "Експортувати", "export_as_json": "Експорт в JSON", "extension": "Розширення", @@ -719,6 +731,7 @@ "filter_people": "Фільтр по людях", "find_them_fast": "Швидко знаходьте їх за назвою за допомогою пошуку", "fix_incorrect_match": "Виправити неправильний збіг", + "folders": "Папки", "force_re-scan_library_files": "Примусово пересканувати всі файли бібліотеки", "forward": "Переслати", "general": "Загальні", @@ -911,6 +924,7 @@ "ok": "ОК", "oldest_first": "Спочатку найстарші", "onboarding": "Введення", + "onboarding_privacy_description": "Наступні (необов'язкові) функції залежать від зовнішніх сервісів і можуть бути вимкнені в будь-який час у налаштуваннях адміністрації.", "onboarding_theme_description": "Виберіть колірну тему для свого екземпляра. Ви можете змінити її пізніше в налаштуваннях.", "onboarding_welcome_description": "Давайте налаштуємо ваш екземпляр за допомогою деяких загальних параметрів.", "onboarding_welcome_user": "Ласкаво просимо, {user}", @@ -927,7 +941,7 @@ "other": "Інше", "other_devices": "Інші пристрої", "other_variables": "Інші змінні", - "owned": "У власності", + "owned": "Власні", "owner": "Власник", "partner": "Партнер", "partner_can_access": "{partner} має доступ", @@ -983,6 +997,7 @@ "previous_memory": "Попередній спогад", "previous_or_next_photo": "Попередня або наступна фотографія", "primary": "Головне", + "privacy": "Конфіденційність", "profile_image_of_user": "Зображення профілю {user}", "profile_picture_set": "Зображення профілю встановлено.", "public_album": "Публічний альбом", @@ -1021,6 +1036,9 @@ "purchase_settings_server_activated": "Ключ продукту сервера керується адміністратором", "range": "", "rating": "Зоряний рейтинг", + "rating_clear": "Очистити рейтинг", + "rating_count": "{count, plural, one {# зірка} few {# зірки} many {# зірок} other {# зірок}}", + "rating_description": "Показувати рейтинг EXIF в інформаційній панелі", "raw": "", "reaction_options": "Опції реакції", "read_changelog": "Прочитати зміни в оновленні", @@ -1125,8 +1143,8 @@ "send_message": "Надіслати повідомлення", "send_welcome_email": "Надішліть вітальний лист", "server": "Сервер", - "server_offline": "Сервер відключено", - "server_online": "Сервер підключено", + "server_offline": "Сервер офлайн", + "server_online": "Сервер онлайн", "server_stats": "Статистика сервера", "server_version": "Версія сервера", "set": "Встановіть", @@ -1143,6 +1161,7 @@ "shared_by_user": "Спільний доступ з {user}", "shared_by_you": "Ви поділились", "shared_from_partner": "Фото від {partner}", + "shared_link_options": "Опції спільних посилань", "shared_links": "Спільні посилання", "shared_photos_and_videos_count": "{assetCount, plural, other {# спільні фотографії та відео.}}", "shared_with_partner": "Спільно з {partner}", @@ -1151,6 +1170,7 @@ "sharing_sidebar_description": "Відображати посилання на загальний доступ у бічній панелі", "shift_to_permanent_delete": "натисніть ⇧ щоб видалити об'єкт назавжди", "show_album_options": "Показати параметри альбому", + "show_albums": "Показувати альбоми", "show_all_people": "Показати всіх людей", "show_and_hide_people": "Показати та приховати людей", "show_file_location": "Показати розташування файлу", @@ -1179,10 +1199,12 @@ "sort_items": "Кількість елементів", "sort_modified": "Дата зміни", "sort_oldest": "Старі фото", - "sort_recent": "Нещодавні фото", + "sort_recent": "Нещодавні", "sort_title": "Заголовок", "source": "Джерело", "stack": "Стек", + "stack_duplicates": "Групувати дублікати", + "stack_select_one_photo": "Вибрати одне основне фото для групи", "stack_selected_photos": "Сгрупувати обрані фотографії", "stacked_assets_count": "Згруповано {count, plural, one {# ресурс} few {# ресурси} many {# ресурсів} other {# ресурсів}}", "stacktrace": "Стек викликів", @@ -1194,7 +1216,7 @@ "stop_photo_sharing": "Припинити надання ваших знімків?", "stop_photo_sharing_description": "{partner} більше не матиме доступу до ваших фотографій.", "stop_sharing_photos_with_user": "Припинити ділитися своїми фотографіями з цим користувачем", - "storage": "Місце для зберігання", + "storage": "Сховище", "storage_label": "Мітка для зберігання", "storage_usage": "{used} з {available} доступних", "submit": "Підтвердити", @@ -1237,6 +1259,7 @@ "unlink_oauth": "Від'єднайте OAuth", "unlinked_oauth_account": "Відключити акаунт OAuth", "unnamed_album": "Альбом без назви", + "unnamed_album_delete_confirmation": "Ви впевнені, що бажаєте видалити цей альбом?", "unnamed_share": "Спільний доступ без назви", "unsaved_change": "Незбережена зміна", "unselect_all": "Зняти все", diff --git a/web/src/lib/i18n/vi.json b/web/src/lib/i18n/vi.json index 58fb4a85f35b3..ff6fb87193c00 100644 --- a/web/src/lib/i18n/vi.json +++ b/web/src/lib/i18n/vi.json @@ -129,6 +129,7 @@ "map_enable_description": "Bật tính năng bản đồ", "map_gps_settings": "Bản đồ & GPS", "map_gps_settings_description": "Quản lý cài đặt Bản đồ & GPS (Mã hóa địa lý ngược)", + "map_implications": "Tính năng bản đồ phụ thuộc vào dịch vụ thẻ bản đồ bên ngoài (tiles.immich.cloud)", "map_light_style": "Giao diện sáng", "map_manage_reverse_geocoding_settings": "Quản lý cài đặt Mã hóa địa lý ngược", "map_reverse_geocoding": "Mã hoá địa lý ngược (Reverse Geocoding)", @@ -222,7 +223,7 @@ "storage_template_enable_description": "Bật công cụ mẫu lưu trữ", "storage_template_hash_verification_enabled": "Bật xác minh băm", "storage_template_hash_verification_enabled_description": "Bật xác minh băm, không tắt tính năng này trừ khi bạn chắc chắn về các rủi ro có thể xảy ra", - "storage_template_migration": "Dịch chuyển mẫu lưu trữ", + "storage_template_migration": "Di chuyển mẫu lưu trữ", "storage_template_migration_description": "Áp dụng {template} hiện tại cho các ảnh đã được tải lên trước đây", "storage_template_migration_info": "Các thay đổi mẫu chỉ áp dụng cho các ảnh mới. Để áp dụng lại mẫu cho các ảnh đã được tải lên trước đây, hãy chạy {job}.", "storage_template_migration_job": "Tác vụ di chuyển mẫu lưu trữ", @@ -320,7 +321,8 @@ "user_settings": "Người dùng", "user_settings_description": "Quản lý cài đặt người dùng", "user_successfully_removed": "Người dùng {email} đã được xóa thành công.", - "version_check_enabled_description": "Bật gửi yêu cầu định kỳ đến GitHub để kiểm tra các bản phát hành mới", + "version_check_enabled_description": "Bật kiểm tra phiên bản", + "version_check_implications": "Tính năng kiểm tra phiên bản yêu cầu kết nối thường xuyên đến github.com", "version_check_settings": "Kiểm tra phiên bản", "version_check_settings_description": "Bật/tắt thông báo phiên bản mới", "video_conversion_job": "Chuyển mã video", @@ -336,7 +338,8 @@ "album_added": "Đã thêm album", "album_added_notification_setting_description": "Nhận thông báo qua email khi bạn được thêm vào một album chia sẻ", "album_cover_updated": "Đã cập nhật ảnh bìa album", - "album_delete_confirmation": "Bạn có chắc chắn muốn xóa album {album} không?\nNếu album này đang được chia sẻ, các người dùng khác sẽ không còn truy cập được nữa.", + "album_delete_confirmation": "Bạn có chắc chắn muốn xóa album {album} không?", + "album_delete_confirmation_description": "Nếu album này được chia sẻ, các người dùng khác sẽ không còn truy cập được nữa.", "album_info_updated": "Đã cập nhật thông tin album", "album_leave": "Rời album?", "album_leave_confirmation": "Bạn có chắc chắn muốn rời khỏi {album} không?", @@ -360,6 +363,7 @@ "allow_edits": "Cho phép chỉnh sửa", "allow_public_user_to_download": "Cho phép người dùng công khai tải xuống", "allow_public_user_to_upload": "Cho phép người dùng công khai tải lên", + "anti_clockwise": "Xoay trái", "api_key": "Khóa API", "api_key_description": "Giá trị này chỉ được hiển thị một lần. Vui lòng sao chép nó trước khi đóng cửa sổ.", "api_key_empty": "Tên khóa API của bạn không được để trống", @@ -369,7 +373,7 @@ "archive": "Lưu trữ", "archive_or_unarchive_photo": "Lưu trữ hoặc huỷ lưu trữ ảnh", "archive_size": "Kích thước gói nén", - "archive_size_description": "Cấu hình kích thước cho các tập tin nén tải về (đơn vị GiB)", + "archive_size_description": "Cấu hình kích thước nén cho các tập tin tải xuống (đơn vị GiB)", "archived": "", "archived_count": "{count, plural, other {Đã lưu trữ # mục}}", "are_these_the_same_person": "Đây có phải cùng một người không?", @@ -440,10 +444,11 @@ "clear_all_recent_searches": "Xóa tất cả tìm kiếm gần đây", "clear_message": "Xóa tin nhắn", "clear_value": "Xóa giá trị", + "clockwise": "Xoay phải", "close": "Đóng", "collapse": "Thu gọn", "collapse_all": "Thu gọn tất cả", - "color_theme": "Giao diện màu", + "color_theme": "Chủ đề màu sắc", "comment_deleted": "Bình luận đã bị xóa", "comment_options": "Tùy chọn bình luận", "comments_and_likes": "Bình luận & lượt thích", @@ -480,7 +485,7 @@ "created": "Đã tạo", "current_device": "Thiết bị hiện tại", "custom_locale": "Ngôn ngữ và khu vực tùy chỉnh", - "custom_locale_description": "Định dạng ngày tháng và số dựa trên ngôn ngữ và khu vực", + "custom_locale_description": "Định dạng ngày và số dựa trên ngôn ngữ và khu vực", "dark": "Tối", "date_after": "Ngày sau", "date_and_time": "Ngày và giờ", @@ -490,7 +495,7 @@ "day": "Ngày", "deduplicate_all": "Xóa tất cả mục trùng lặp", "default_locale": "Ngôn ngữ và khu vực mặc định", - "default_locale_description": "Định dạng ngày tháng và số dựa trên ngôn ngữ của trình duyệt của bạn", + "default_locale_description": "Định dạng ngày và số dựa trên ngôn ngữ trình duyệt của bạn", "delete": "Xóa", "delete_album": "Xóa album", "delete_api_key_prompt": "Bạn có chắc chắn muốn xóa khóa API này không?", @@ -506,7 +511,7 @@ "direction": "Hướng", "disabled": "Tắt", "disallow_edits": "Không cho phép chỉnh sửa", - "discover": "Khám phá", + "discover": "Tìm", "dismiss_all_errors": "Bỏ qua tất cả lỗi", "dismiss_error": "Bỏ qua lỗi", "display_options": "Tùy chọn hiển thị", @@ -516,6 +521,8 @@ "do_not_show_again": "Không hiển thị thông báo này nữa", "done": "Xong", "download": "Tải xuống", + "download_include_embedded_motion_videos": "Các video nhúng", + "download_include_embedded_motion_videos_description": "Gồm các video được nhúng trong ảnh chuyển động thành một tập tin riêng", "download_settings": "Tải xuống", "download_settings_description": "Quản lý cài đặt liên quan đến việc tải ảnh xuống", "downloading": "Đang tải xuống", @@ -548,7 +555,11 @@ "edit_title": "Chỉnh sửa tiêu đề", "edit_user": "Chỉnh sửa người dùng", "edited": "Đã chỉnh sửa", - "editor": "", + "editor": "Trình chỉnh sửa", + "editor_close_without_save_prompt": "Những thay đổi sẽ không được lưu", + "editor_close_without_save_title": "Đóng trình chỉnh sửa?", + "editor_crop_tool_h2_aspect_ratios": "Tỷ lệ khung hình", + "editor_crop_tool_h2_rotation": "Xoay", "email": "Email", "empty": "", "empty_album": "", @@ -698,6 +709,7 @@ "expired": "Hết hạn", "expires_date": "Hết hạn vào {date}", "explore": "Khám phá", + "explorer": "Khám phá", "export": "Xuất", "export_as_json": "Xuất dưới dạng JSON", "extension": "Phần mở rộng", @@ -719,6 +731,7 @@ "filter_people": "Lọc người", "find_them_fast": "Tìm nhanh bằng tên với tìm kiếm", "fix_incorrect_match": "Sửa lỗi trùng khớp không chính xác", + "folders": "Thư mục", "force_re-scan_library_files": "Yêu cầu quét lại tất cả các tập tin thư viện", "forward": "Tiến về phía trước", "general": "Chung", @@ -883,6 +896,7 @@ "ok": "Đồng ý", "oldest_first": "Cũ nhất trước", "onboarding": "Hướng dẫn sử dụng", + "onboarding_privacy_description": "Các tính năng (tùy chọn) sau đây phụ thuộc vào các dịch vụ bên ngoài và có thể bị tắt bất kỳ lúc nào trong cài đặt quản trị.", "onboarding_theme_description": "Chọn chủ đề màu sắc cho tài khoản riêng của bạn. Bạn có thể thay đổi điều này sau trong cài đặt của bạn.", "onboarding_welcome_description": "Hãy thiết lập tài khoản riêng của bạn với một số cài đặt cơ bản.", "onboarding_welcome_user": "Chào mừng, {user}", @@ -945,7 +959,7 @@ "places": "Địa điểm", "play": "Phát", "play_memories": "Phát kỷ niệm", - "play_motion_photo": "Phát ảnh động", + "play_motion_photo": "Phát ảnh chuyển động", "play_or_pause_video": "Phát hoặc tạm dừng video", "point": "", "port": "Cổng", @@ -955,6 +969,7 @@ "previous_memory": "Kỷ niệm trước", "previous_or_next_photo": "Ảnh trước hoặc sau", "primary": "Chính", + "privacy": "Bảo mật", "profile_image_of_user": "Ảnh đại diệncủa {user}", "profile_picture_set": "Ảnh đại diện đã được đặt.", "public_album": "Album công khai", @@ -968,7 +983,7 @@ "purchase_button_buy_immich": "Mua Immich", "purchase_button_never_show_again": "Không hiển thị lại", "purchase_button_reminder": "Nhắc tôi trong 30 ngày", - "purchase_button_remove_key": "Xoá khóa", + "purchase_button_remove_key": "Xóa khóa", "purchase_button_select": "Chọn", "purchase_failed_activation": "Kích hoạt thất bại! Vui lòng kiểm tra email của bạn để biết khóa sản phẩm chính xác!", "purchase_individual_description_1": "Dành cho cá nhân", @@ -983,9 +998,9 @@ "purchase_panel_title": "Hỗ trợ dự án", "purchase_per_server": "Mỗi máy chủ", "purchase_per_user": "Mỗi người dùng", - "purchase_remove_product_key": "Xoá khóa sản phẩm", + "purchase_remove_product_key": "Xóa khóa sản phẩm", "purchase_remove_product_key_prompt": "Bạn có chắc chắn muốn xoá khóa sản phẩm?", - "purchase_remove_server_product_key": "Xoá khóa sản phẩm máy chủ", + "purchase_remove_server_product_key": "Xóa khóa sản phẩm máy chủ", "purchase_remove_server_product_key_prompt": "Bạn có chắc chắn muốn xoá khóa sản phẩm máy chủ?", "purchase_server_description_1": "Dành cho toàn bộ máy chủ", "purchase_server_description_2": "Trạng thái người hỗ trợ", @@ -993,6 +1008,8 @@ "purchase_settings_server_activated": "Khóa sản phẩm máy chủ được quản lý bởi quản trị viên", "range": "", "rating": "Xếp hạng sao", + "rating_clear": "Xóa đánh giá", + "rating_count": "{count, plural, one {# sao} other {# sao}}", "rating_description": "Hiển thị xếp hạng ảnh trong bảng thông tin", "raw": "", "reaction_options": "Tùy chọn phản ứng", @@ -1012,16 +1029,16 @@ "refreshing_encoded_video": "Đang làm mới video đã mã hóa", "refreshing_metadata": "Đang làm mới metadata", "regenerating_thumbnails": "Đang tạo lại hình thu nhỏ", - "remove": "Xoá", + "remove": "Xóa", "remove_assets_album_confirmation": "Bạn có chắc chắn muốn xoá {count, plural, one {# mục} other {# mục}} khỏi album?", "remove_assets_shared_link_confirmation": "Bạn có chắc chắn muốn xoá {count, plural, one {# mục} other {# mục}} khỏi liên kết chia sẻ này?", - "remove_assets_title": "Xoá mục?", + "remove_assets_title": "Xóa mục?", "remove_custom_date_range": "Bỏ chọn khoảng ngày tùy chỉnh", - "remove_from_album": "Xoá khỏi album", - "remove_from_favorites": "Xoá khỏi Mục yêu thích", - "remove_from_shared_link": "Xoá khỏi liên kết chia sẻ", + "remove_from_album": "Xóa khỏi album", + "remove_from_favorites": "Xóa khỏi Mục yêu thích", + "remove_from_shared_link": "Xóa khỏi liên kết chia sẻ", "remove_offline_files": "Loại bỏ tập tin ngoại tuyến", - "remove_user": "Xoá người dùng", + "remove_user": "Xóa người dùng", "removed_api_key": "Khóa API đã xóa: {name}", "removed_from_archive": "Đã xoá khỏi Kho lưu trữ", "removed_from_favorites": "Đã xoá khỏi Mục yêu thích", @@ -1163,7 +1180,7 @@ "stack_selected_photos": "Nhóm các ảnh đã chọn", "stacked_assets_count": "Đã nhóm {count, plural, one {# mục} other {# mục}}", "stacktrace": "Thông tin chi tiết lỗi", - "start": "Bắt đầu", + "start": "Chạy", "start_date": "Ngày bắt đầu", "state": "Tỉnh", "status": "Trạng thái", @@ -1180,9 +1197,9 @@ "swap_merge_direction": "Đổi hướng hợp nhất", "sync": "Đồng bộ", "template": "Mẫu", - "theme": "Giao diện", - "theme_selection": "Giao diện tổng thể", - "theme_selection_description": "Tự động đặt giao diện sáng hoặc tối dựa trên tùy chọn hệ thống của trình duyệt của bạn", + "theme": "Chủ đề", + "theme_selection": "Chủ đề tổng thể", + "theme_selection_description": "Tự động đặt chủ đề sáng hoặc tối dựa trên tùy chọn hệ thống của trình duyệt của bạn", "they_will_be_merged_together": "Chúng sẽ được hợp nhất với nhau", "time_based_memories": "Kỷ niệm dựa trên thời gian", "timezone": "Múi giờ", @@ -1190,14 +1207,14 @@ "to_change_password": "Đổi mật khẩu", "to_favorite": "Yêu thích", "to_login": "Đăng nhập", - "to_trash": "Xoá", + "to_trash": "Xóa", "toggle_settings": "Chuyển đổi cài đặt", - "toggle_theme": "Chuyển đổi giao diện", + "toggle_theme": "Chuyển đổi chủ đề tối", "toggle_visibility": "", "total_usage": "Tổng dung lượng đã sử dụng", "trash": "Thùng rác", - "trash_all": "Xoá hết", - "trash_count": "Xoá {count, number} mục", + "trash_all": "Xóa hết", + "trash_count": "Xóa {count, number} mục", "trash_delete_asset": "Chuyển vào thùng rác/Xóa vĩnh viễn", "trash_no_results_message": "Ảnh và video đã bị xoá sẽ hiển thị ở đây.", "trashed_items_will_be_permanently_deleted_after": "Các mục đã xóa sẽ bị xóa vĩnh viễn sau {days, plural, one {# ngày} other {# ngày}}.", @@ -1214,6 +1231,7 @@ "unlink_oauth": "Huỷ liên kết OAuth", "unlinked_oauth_account": "Đã huỷ liên kết tài khoản OAuth", "unnamed_album": "Album chưa đặt tên", + "unnamed_album_delete_confirmation": "Bạn có chắc chắn muốn xóa album này không?", "unnamed_share": "Chia sẻ chưa đặt tên", "unsaved_change": "Thay đổi chưa lưu", "unselect_all": "Bỏ chọn tất cả", diff --git a/web/src/lib/i18n/zh_Hant.json b/web/src/lib/i18n/zh_Hant.json index f0787bd5b316d..0ef3dca88a77a 100644 --- a/web/src/lib/i18n/zh_Hant.json +++ b/web/src/lib/i18n/zh_Hant.json @@ -24,8 +24,8 @@ "add_to_album": "加入相簿", "add_to_shared_album": "加入共享相簿", "added_to_archive": "已加入封存", - "added_to_favorites": "新增至收藏", - "added_to_favorites_count": "已新增 {count, number} 個項目至收藏", + "added_to_favorites": "已加入收藏", + "added_to_favorites_count": "已把 {count, number} 個項目加入收藏", "admin": { "add_exclusion_pattern_description": "新增排除規則。支援使用「*」、「 **」、「?」來匹配字串。如果要排除所有名稱為「Raw」的檔案或目錄,請使用「**/Raw/**」。如果要排除所有「.tif」結尾的檔案,請使用「**/*.tif」。如果要排除某個絕對路徑,請使用「/path/to/ignore/**」。", "authentication_settings": "驗證設定", @@ -44,7 +44,7 @@ "crontab_guru": "", "disable_login": "停用登入", "disabled": "已禁用", - "duplicate_detection_job_description": "運行機器學習以檢測相似圖像。此功能仰賴智慧搜尋", + "duplicate_detection_job_description": "對檔案執行機器學習來偵測相似圖片。(此功能仰賴智慧搜尋)", "exclusion_pattern_description": "排除規則讓您在掃描資料庫時忽略特定文件和文件夾。用於當您有不想導入的文件(例如 RAW 文件)或文件夾。", "external_library_created_at": "外部圖庫(於 {date} 建立)", "external_library_management": "外部圖庫管理", @@ -61,14 +61,14 @@ "image_prefer_wide_gamut_setting_description": "使用 Display P3 來製作縮圖。這可以更好地保留廣色域圖片的鮮豔度,但在舊版瀏覽器或舊設備上,圖片可能會顯示不同。sRGB 圖片會維持 sRGB 以避免顏色變化。", "image_preview_format": "預覽格式", "image_preview_resolution": "預覽解析度", - "image_preview_resolution_description": "檢視單張照片和機器學習時用。高解析度可以保留更多細節,但會增加編碼時間、增加檔案大小、降低應用軟體的流暢度。", + "image_preview_resolution_description": "觀賞單張照片及機器學習時用。較高的解析度可以保留更多細節,但編碼時間較長,檔案也較大,且可能降低應用程式的響應速度。", "image_quality": "品質", "image_quality_description": "圖片品質從1到100,數值越高代表品質越好但檔案也越大,此選項影響預覽和縮圖圖片。", "image_settings": "圖片設定", - "image_settings_description": "管理生成圖片的品質和解析度", + "image_settings_description": "管理產生圖片的品質和解析度", "image_thumbnail_format": "縮圖格式", "image_thumbnail_resolution": "縮圖解析度", - "image_thumbnail_resolution_description": "檢視多張照片時用(時間軸、相冊等⋯)。高解析度可以保留更多細節,但會增加編碼時間、增加檔案大小、降低應用軟體的流暢度。", + "image_thumbnail_resolution_description": "觀賞多張照片時(時間軸、相簿等)用。較高的解析度可以保留更多細節,但編碼時間較長,檔案也較大,且可能降低應用程式的響應速度。", "job_concurrency": "{job}並行", "job_not_concurrency_safe": "這個任務並行並不安全。", "job_settings": "任務設定", @@ -77,9 +77,9 @@ "jobs_delayed": "{jobCount, plural, other {# 項任務延遲}}", "jobs_failed": "{jobCount, plural, other {# 項}}任務失敗", "library_created": "已建立圖庫:{library}", - "library_cron_expression": "Cron 表達式", - "library_cron_expression_description": "以 cron 格式設定掃描時段。詳細資訊請參考 Crontab Guru", - "library_cron_expression_presets": "現成的 Cron 表達式", + "library_cron_expression": "Cron 運算式", + "library_cron_expression_description": "以 Cron 格式設定掃描時段。詳細資訊請參閱 Crontab Guru", + "library_cron_expression_presets": "現成的 Cron 運算式", "library_deleted": "圖庫已刪除", "library_import_path_description": "選取要載入的資料夾。以掃描資料夾(含子資料夾)內的影像和影片。", "library_scanning": "定期掃描", @@ -96,8 +96,8 @@ "logging_settings": "記錄檔", "machine_learning_clip_model": "CLIP 模型", "machine_learning_clip_model_description": "CLIP 模型 名稱列表。更換模型後須對所有影像重新執行「智慧搜尋」。", - "machine_learning_duplicate_detection": "重複檢測", - "machine_learning_duplicate_detection_enabled": "啟用重複檢測", + "machine_learning_duplicate_detection": "重複項目偵測", + "machine_learning_duplicate_detection_enabled": "啟用重複項目偵測", "machine_learning_duplicate_detection_enabled_description": "即使停用,完全一樣的素材仍會被忽略。", "machine_learning_duplicate_detection_setting_description": "用 CLIP 向量比對潛在重複", "machine_learning_enabled": "啟用機器學習", @@ -125,16 +125,17 @@ "machine_learning_url_description": "機器學習伺服器的網址", "manage_concurrency": "管理並行", "manage_log_settings": "管理日誌設定", - "map_dark_style": "深色模式", + "map_dark_style": "深色樣式", "map_enable_description": "啟用地圖功能", "map_gps_settings": "地圖與 GPS 設定", "map_gps_settings_description": "管理地圖和 GPS(逆向地理編碼)設定", - "map_light_style": "淺色模式", + "map_implications": "地圖功能依賴外部平貼服務(tiles.immich.cloud)", + "map_light_style": "淺色樣式", "map_manage_reverse_geocoding_settings": "管理逆向地理編碼設定", "map_reverse_geocoding": "逆向地理編碼", "map_reverse_geocoding_enable_description": "啟用逆向地理編碼", "map_reverse_geocoding_settings": "逆向地理編碼設定", - "map_settings": "地圖設定", + "map_settings": "地圖", "map_settings_description": "管理地圖設定", "map_style_description": "地圖主題(style.json)的網址", "metadata_extraction_job": "擷取元資料", @@ -143,16 +144,16 @@ "migration_job_description": "將照片和人臉的縮圖遷移到最新的文件夾結構", "no_paths_added": "未添加路徑", "no_pattern_added": "未添加pattern", - "note_apply_storage_label_previous_assets": "注意:若要將存儲標籤應用於先前上傳的圖片,請運行", - "note_cannot_be_changed_later": "註:這將無法更改!", + "note_apply_storage_label_previous_assets": "註:要將存標記用於先前上傳的檔案,請執行", + "note_cannot_be_changed_later": "註:之後就無法更改嘍!", "note_unlimited_quota": "註:輸入 0 表示不限制配額", - "notification_email_from_address": "發出電郵", - "notification_email_from_address_description": "寄出人電郵,例如:\"Immich 相片伺服器 \"", - "notification_email_host_description": "電郵伺服器主機位址 (e.g. smtp.immich.app)", + "notification_email_from_address": "寄件地址", + "notification_email_from_address_description": "寄件者電子郵件地址(例:Immich Photo Server )", + "notification_email_host_description": "電子郵件伺服器主機(例:smtp.immich.app)", "notification_email_ignore_certificate_errors": "忽略憑證錯誤", "notification_email_ignore_certificate_errors_description": "忽略 TLS 憑證驗證錯誤(不建議)", "notification_email_password_description": "以電子郵件伺服器驗證身份時的密碼", - "notification_email_port_description": "電郵伺服器端口(例如 25、465 或 587)", + "notification_email_port_description": "電子郵件伺服器埠口(如: 25、465 或 587)", "notification_email_sent_test_email_button": "傳送測試電子郵件並儲存", "notification_email_setting_description": "發送電子郵件通知的設置", "notification_email_test_email": "傳送測試電子郵件", @@ -160,15 +161,15 @@ "notification_email_test_email_sent": "測試電子郵件已發送至 {email}。請檢查您的收件箱。", "notification_email_username_description": "以電子郵件伺服器驗證身份時的使用者名稱", "notification_enable_email_notifications": "啟用電子郵件通知", - "notification_settings": "通知設定", + "notification_settings": "通知", "notification_settings_description": "管理通知設置,包括電子郵件通知", "oauth_auto_launch": "自動啟動", "oauth_auto_launch_description": "導覽至登入頁面後自動進行 OAuth 登入流程", "oauth_auto_register": "自動註冊", "oauth_auto_register_description": "使用 OAuth 登錄後自動註冊新用戶", "oauth_button_text": "按鈕文字", - "oauth_client_id": "用戶端識別碼", - "oauth_client_secret": "用戶端密碼", + "oauth_client_id": "客戶端 ID", + "oauth_client_secret": "客戶端密鑰", "oauth_enable_description": "用 OAuth 登入", "oauth_issuer_url": "簽發者網址", "oauth_mobile_redirect_uri": "移動端重定向 URI", @@ -195,28 +196,28 @@ "paths_validated_successfully": "所有路徑驗證成功", "quota_size_gib": "配額(GiB)", "refreshing_all_libraries": "正在重新整理所有圖庫", - "registration": "管理員註冊", + "registration": "管理者註冊", "registration_description": "由於您是本系統的首位使用者,因此將您指派爲負責管理本系統的管理者,其他使用者須由您協助建立帳號。", "removing_offline_files": "移除離線檔案中", "repair_all": "全部糾正", "repair_matched_items": "有 {count, plural, other {# 個項目相符}}", "repaired_items": "已糾正 {count, plural, other {# 個項目}}", "require_password_change_on_login": "要求使用者在首次登入時更改密碼", - "reset_settings_to_default": "重置設置為默認值", - "reset_settings_to_recent_saved": "重置設置為最近保存的設置", + "reset_settings_to_default": "將設定重設回預設", + "reset_settings_to_recent_saved": "已設回最後儲存的設定", "scanning_library_for_changed_files": "正在掃描資料庫以檢查文件變更", "scanning_library_for_new_files": "正在掃描資料庫以檢查新文件", "send_welcome_email": "傳送歡迎電子郵件", "server_external_domain_settings": "外部網域", "server_external_domain_settings_description": "公開分享鏈結的網域(包含「http(s)://」)", - "server_settings": "伺服器設定", + "server_settings": "伺服器", "server_settings_description": "管理伺服器設定", "server_welcome_message": "歡迎訊息", "server_welcome_message_description": "在登入頁面顯示的訊息。", "sidecar_job": "側接元資料", "sidecar_job_description": "從檔案系統探索或同步側接(Sidecar)元資料", "slideshow_duration_description": "每張圖片放映的秒數", - "smart_search_job_description": "對檔案運行機器學習以用於智能搜尋", + "smart_search_job_description": "對檔案執行機器學習,以利智慧搜尋", "storage_template_date_time_description": "檔案的創建時戳會用於判斷時間資訊", "storage_template_date_time_sample": "時間樣式 {date}", "storage_template_enable_description": "啟用存儲模板引擎", @@ -230,16 +231,16 @@ "storage_template_onboarding_description": "啟用此功能後,將根據用戶自定義的模板自動組織文件。由於穩定性問題,此功能已默認關閉。欲了解更多信息,請參閱 文檔。", "storage_template_path_length": "大致路徑長度限制:{length, number}/{limit, number}", "storage_template_settings": "存儲模板", - "storage_template_settings_description": "管理上傳檔案的文件夾結構和文件名", + "storage_template_settings_description": "管理上傳檔案的資料夾結構和檔名", "storage_template_user_label": "{label} 是用戶的存儲標籤", "system_settings": "系統設定", "theme_custom_css_settings": "自訂 CSS", - "theme_custom_css_settings_description": "層疊樣式表(CSS)允許自定義 Immich 的設計。", - "theme_settings": "主題設定", - "theme_settings_description": "管理 Immich 網頁界面的自定義設置", + "theme_custom_css_settings_description": "可以用層疊樣式表(CSS)來自訂 Immich 的設計。", + "theme_settings": "主題", + "theme_settings_description": "自訂 Immich 的網頁界面", "these_files_matched_by_checksum": "這些檔案的核對和(Checksum)是相符的", - "thumbnail_generation_job": "生成縮圖", - "thumbnail_generation_job_description": "為每個資產生成大、小和模糊的縮圖,並為每個人生成縮圖", + "thumbnail_generation_job": "產生縮圖", + "thumbnail_generation_job_description": "爲每個檔案產生大、小及模糊縮圖,也爲每位人物產生縮圖", "transcode_policy_description": "", "transcoding_acceleration_api": "加速 API", "transcoding_acceleration_api_description": "該 API 將用您的設備加速轉碼。設置是“盡力而為”:如果失敗,它將退回到軟件轉碼。VP9 轉碼是否可行取決於您的硬件。", @@ -262,7 +263,7 @@ "transcoding_constant_quality_mode_description": "ICQ 比 CQP 更好,但某些硬件加速設備不支持此模式。設置此選項時,會在使用基於質量的編碼時偏好指定的模式。由於 NVENC 不支持 ICQ,此選項對其無效。", "transcoding_constant_rate_factor": "恆定速率因子(-crf)", "transcoding_constant_rate_factor_description": "視頻質量級別。典型值為 H.264 的 23、HEVC 的 28、VP9 的 31 和 AV1 的 35。數值越低,質量越高,但會產生較大的文件。", - "transcoding_disabled_description": "不要轉碼任何視頻,可能會導致某些客戶端無法播放", + "transcoding_disabled_description": "不轉碼影片,可能會讓某些客戶端無法正常播放", "transcoding_hardware_acceleration": "硬體加速", "transcoding_hardware_acceleration_description": "實驗性功能;速度更快,但在相同比特率下質量較低", "transcoding_hardware_decoding": "硬體解碼", @@ -274,7 +275,7 @@ "transcoding_max_bitrate_description": "設置最大比特率可以使文件大小更具可預測性,但會稍微降低質量。在 720p 分辨率下,典型值為 VP9 或 HEVC 的 2600k,或 H.264 的 4500k。設置為 0 則禁用此功能。", "transcoding_max_keyframe_interval": "最大關鍵幀間隔", "transcoding_max_keyframe_interval_description": "設置關鍵幀之間的最大幀距。較低的值會降低壓縮效率,但可以改善尋找時間,並可能改善快速運動場景中的質量。0 會自動設置此值。", - "transcoding_optimal_description": "分辨率高於目標或格式不被接受的視頻", + "transcoding_optimal_description": "高於目標解析度或格式不被支援的影片", "transcoding_preferred_hardware_device": "首選硬件設備", "transcoding_preferred_hardware_device_description": "僅適用於 VAAPI 和 QSV。設置用於硬件轉碼的 DRI 節點。", "transcoding_preset_preset": "預設值(-preset)", @@ -282,10 +283,10 @@ "transcoding_reference_frames": "參考幀數", "transcoding_reference_frames_description": "壓縮給定幀時參考的幀數。較高的值可以提高壓縮效率,但會降低編碼速度。0 會自動設置此值。", "transcoding_required_description": "僅限於格式不被接受的視頻", - "transcoding_settings": "影片轉碼設定", + "transcoding_settings": "影片轉碼", "transcoding_settings_description": "管理影片的解析度和編碼資訊", "transcoding_target_resolution": "目標解析度", - "transcoding_target_resolution_description": "較高的解析度可以保留更多細節,但編碼所需時間更長,文件大小也會增加,並可能降低應用程序的響應速度。", + "transcoding_target_resolution_description": "較高的解析度可以保留更多細節,但編碼時間較長,檔案也較大,且可能降低應用程式的響應速度。", "transcoding_temporal_aq": "時間自適應量化(Temporal AQ)", "transcoding_temporal_aq_description": "僅適用於 NVENC。提高高細節、低運動場景的質量。可能與舊設備不兼容。", "transcoding_threads": "線程數量", @@ -300,11 +301,11 @@ "transcoding_two_pass_encoding_setting_description": "使用雙通道編碼以產生更高質量的編碼視頻。當啟用最大比特率時(對 H.264 和 HEVC 有效),此模式使用基於最大比特率的比特率範圍,並忽略 CRF。對於 VP9,如果禁用最大比特率,可以使用 CRF。", "transcoding_video_codec": "視頻編解碼器", "transcoding_video_codec_description": "VP9 具有高效能和網頁兼容性,但轉碼時間較長。HEVC 性能相似,但網頁兼容性較低。H.264 兼容性廣泛且轉碼速度快,但生成的文件較大。AV1 是最有效的編解碼器,但在舊設備上支持度不足。", - "trash_enabled_description": "啟用垃圾箱功能", + "trash_enabled_description": "啟用垃圾桶功能", "trash_number_of_days": "日數", - "trash_number_of_days_description": "永久刪除之前,檔案於垃圾箱中保留的日數", - "trash_settings": "垃圾箱設置", - "trash_settings_description": "管理垃圾箱設置", + "trash_number_of_days_description": "永久刪除之前,將檔案保留在垃圾桶中的日數", + "trash_settings": "垃圾桶", + "trash_settings_description": "管理垃圾桶設定", "untracked_files": "未被追蹤的檔案", "untracked_files_description": "這些檔案不會被追蹤。它們可能是移動失誤、上傳中斷或遇到漏洞而遺留的產物", "user_delete_delay": "{user} 的帳戶和資產將安排在 {delay, plural, one {# 天} other {# 天}} 後進行永久刪除。", @@ -315,28 +316,30 @@ "user_management": "使用者管理", "user_password_has_been_reset": "使用者密碼已重設:", "user_password_reset_description": "請提供使用者臨時密碼,並告知下次登入時需要更改密碼。", - "user_restore_description": "{user} 的帳戶將被恢復。", - "user_restore_scheduled_removal": "恢復用戶 - 預定於 {date, date, long} 移除", - "user_settings": "使用者設定", + "user_restore_description": "{user} 的帳號將被還原。", + "user_restore_scheduled_removal": "還原使用者 - 預定於 {date, date, long} 移除", + "user_settings": "使用者", "user_settings_description": "管理使用者設定", "user_successfully_removed": "已成功移除 {email}(使用者)。", - "version_check_enabled_description": "啟用定期向 GitHub 發送請求以檢查新版本", + "version_check_enabled_description": "啟用版本檢查", + "version_check_implications": "版本檢查功能會定期與 github.com 通訊", "version_check_settings": "版本檢查", - "version_check_settings_description": "啟用/禁用新版本通知", - "video_conversion_job": "轉碼視頻", - "video_conversion_job_description": "轉碼視頻以提高瀏覽器和設備的兼容性" + "version_check_settings_description": "啟用 / 停用新版本通知", + "video_conversion_job": "轉碼影片", + "video_conversion_job_description": "對影片轉碼,相容更多瀏覽器和裝置" }, - "admin_email": "管理員電子郵件", + "admin_email": "管理者電子郵件", "admin_password": "管理者密碼", "administration": "管理", "advanced": "進階", - "age_months": "年齡 {months, plural, one {# 個月} other {# 個月}}", - "age_year_months": "年齡 1 年,{months, plural, one {# 個月} other {# 個月}}", - "age_years": "{years, plural, other {年齡 #}}", - "album_added": "已新增相簿", + "age_months": "{months, plural, other {# 個月大}}", + "age_year_months": "1 歲,{months, plural, other {# 個月}}", + "age_years": "{years, plural, other {# 歲}}", + "album_added": "加入相簿時", "album_added_notification_setting_description": "當我被加入共享相簿時,用電子郵件通知我", "album_cover_updated": "已更新相簿封面", - "album_delete_confirmation": "確定要刪除「{album}」(相簿)嗎?\n如果已分享此相簿,其他使用者就無法再存取。", + "album_delete_confirmation": "確定要刪除「{album}」(相簿)嗎?", + "album_delete_confirmation_description": "如果已分享此相簿,其他使用者就無法再存取這本相簿了。", "album_info_updated": "已更新相簿資訊", "album_leave": "離開相簿?", "album_leave_confirmation": "您確定要離開 {album} 嗎?", @@ -345,7 +348,7 @@ "album_remove_user": "移除使用者?", "album_remove_user_confirmation": "確定要移除 {user} 嗎?", "album_share_no_users": "看來您與所有使用者共享了這本相簿,或沒有其他使用者可供分享。", - "album_updated": "已更新相簿", + "album_updated": "更新相簿時", "album_updated_setting_description": "當共享相簿有新檔案時,用電子郵件通知我", "album_user_left": "已離開 {album}", "album_user_removed": "已移除 {user}", @@ -356,15 +359,16 @@ "all_albums": "所有相簿", "all_people": "所有人", "all_videos": "所有視頻", - "allow_dark_mode": "允許黑暗模式", + "allow_dark_mode": "允許深色模式", "allow_edits": "允許編輯", "allow_public_user_to_download": "開放給使用者下載", "allow_public_user_to_upload": "開放讓使用者上傳", + "anti_clockwise": "逆時針", "api_key": "API 金鑰", "api_key_description": "此值僅顯示一次。請確保在關閉窗口之前複製它。", "api_key_empty": "您的 API 金鑰名稱不能爲空", "api_keys": "API 金鑰", - "app_settings": "應用設置", + "app_settings": "應用程式設定", "appears_in": "出現在", "archive": "封存", "archive_or_unarchive_photo": "封存或取消封存照片", @@ -382,48 +386,48 @@ "asset_hashing": "Hashing中...", "asset_offline": "檔案離線", "asset_offline_description": "此檔案己離線。Immich 無法訪問其文件位置。請確保資產可用,然後重新掃描資料庫。", - "asset_skipped": "跳過", + "asset_skipped": "已略過", "asset_uploaded": "已上傳", - "asset_uploading": "上傳中...", + "asset_uploading": "上傳中…", "assets": "檔案", "assets_added_count": "已添加 {count, plural, one {# 個資產} other {# 個資產}}", "assets_added_to_album_count": "已將 {count, plural, other {# 個檔案}}加入相簿", "assets_added_to_name_count": "已將 {count, plural, other {# 個檔案}}加入{hasName, select, true {{name}} other {新相簿}}", "assets_count": "{count, plural, one {# 個檔案} other {# 個檔案}}", - "assets_moved_to_trash_count": "已將 {count, plural, one {# 個檔案} other {# 個檔案}} 移到垃圾箱", + "assets_moved_to_trash_count": "已將 {count, plural, other {# 個檔案}}丟進垃圾桶", "assets_permanently_deleted_count": "已永久刪除 {count, plural, one {# 個檔案} other {# 個檔案}}", "assets_removed_count": "已移除 {count, plural, one {# 個檔案} other {# 個檔案}}", - "assets_restore_confirmation": "您確定要恢復所有垃圾箱中的檔案嗎?此操作無法撤銷!", - "assets_restored_count": "已恢復 {count, plural, one {# 個檔案} other {# 個檔案}}", - "assets_trashed_count": "{count, plural, one {# 個檔案} other {# 個檔案}} 已放入垃圾箱", + "assets_restore_confirmation": "確定要還原所有丟掉的檔案嗎?此步驟無法取消喔!", + "assets_restored_count": "已還原 {count, plural, other {# 個檔案}}", + "assets_trashed_count": "已丟掉 {count, plural, other {# 個檔案}}", "assets_were_part_of_album_count": "{count, plural, one {檔案已} other {檔案已}} 是相冊的一部分", "authorized_devices": "授權裝置", "back": "后退", - "back_close_deselect": "返回、關閉或取消選擇", + "back_close_deselect": "返回、關閉及取消選取", "backward": "倒轉", - "birthdate_saved": "已成功保存出生日期", - "birthdate_set_description": "出生日期會用於計算此人在照片拍攝時的年齡。", + "birthdate_saved": "出生日期儲存成功", + "birthdate_set_description": "出生日期會用來計算此人拍照時的歲數。", "blurred_background": "模糊背景", "build": "建置編號", "build_image": "建置映像", "bulk_delete_duplicates_confirmation": "您確定要批量刪除 {count, plural, one {# 個重複檔案} other {# 個重複檔案}} 嗎?這將保留每組中的最大檔案,並永久刪除所有其他重複項。此操作無法撤銷!", "bulk_keep_duplicates_confirmation": "您確定要保留 {count, plural, one {# 個重複檔案} other {# 個重複檔案}} 嗎?這將解決所有重複組而不刪除任何內容。", - "bulk_trash_duplicates_confirmation": "您確定要批量將 {count, plural, one {# 個重複檔案} other {# 個重複檔案}} 移到垃圾箱嗎?這將保留每組中最大的檔案,並將所有其他重複項放入垃圾箱。", - "buy": "購買 Immich", + "bulk_trash_duplicates_confirmation": "確定要一次丟掉 {count, plural, other {# 個重複的檔案}}嗎?這樣每組重複的檔案中,最大的會留下來,其它的會被丟進垃圾桶。", + "buy": "購置 Immich", "camera": "相機", "camera_brand": "相機品牌", "camera_model": "相機型號", "cancel": "取消", "cancel_search": "取消搜尋", "cannot_merge_people": "無法合併人物", - "cannot_undo_this_action": "您無法撤銷此操作!", + "cannot_undo_this_action": "此步驟無法取消喔!", "cannot_update_the_description": "無法更新描述", "cant_apply_changes": "", "cant_get_faces": "", "cant_search_people": "", "cant_search_places": "", "change_date": "更改日期", - "change_expiration_time": "更改有效期限", + "change_expiration_time": "更改失效期限", "change_location": "更改位置", "change_name": "改名", "change_name_successfully": "改名成功", @@ -440,6 +444,7 @@ "clear_all_recent_searches": "清除所有最近的搜尋", "clear_message": "清除訊息", "clear_value": "清除值", + "clockwise": "順時針", "close": "關閉", "collapse": "折疊", "collapse_all": "全部折疊", @@ -448,9 +453,9 @@ "comment_options": "評論選項", "comments_and_likes": "評論與讚好", "comments_are_disabled": "評論已禁用", - "confirm": "确定", + "confirm": "確認", "confirm_admin_password": "確認管理者密碼", - "confirm_delete_shared_link": "您確定要刪除這個共享鏈接嗎?", + "confirm_delete_shared_link": "確定要刪除這條分享鏈結嗎?", "confirm_password": "確認密碼", "contain": "包含", "context": "情境", @@ -458,7 +463,7 @@ "copied_image_to_clipboard": "圖片已複製到剪貼簿。", "copied_to_clipboard": "已複製到剪貼簿!", "copy_error": "複製錯誤", - "copy_file_path": "複製文件路徑", + "copy_file_path": "複製檔案路徑", "copy_image": "複製圖片", "copy_link": "複製鏈結", "copy_link_to_clipboard": "將鏈結複製到剪貼簿", @@ -467,9 +472,9 @@ "country": "國家", "cover": "封面", "covers": "封面", - "create": "创建", + "create": "建立", "create_album": "建立相簿", - "create_library": "創建圖庫", + "create_library": "建立圖庫", "create_link": "建立鏈結", "create_link_to_share": "建立分享鏈結", "create_link_to_share_description": "允許任何擁有鏈接的人查看所選的照片", @@ -479,18 +484,18 @@ "create_user": "建立使用者", "created": "建立於", "current_device": "此裝置", - "custom_locale": "自定義區域設定", - "custom_locale_description": "根據語言和地區格式化日期和數字", + "custom_locale": "自訂區域", + "custom_locale_description": "依語言和區域設定日期和數字格式", "dark": "深色", "date_after": "日期之後", - "date_and_time": "日期和时间", + "date_and_time": "日期與時間", "date_before": "日期之前", - "date_of_birth_saved": "出生日期已成功保存", + "date_of_birth_saved": "出生日期儲存成功", "date_range": "日期範圍", "day": "日", "deduplicate_all": "刪除所有重複項目", - "default_locale": "默認區域設定", - "default_locale_description": "根據您的瀏覽器區域設定格式化日期和數字", + "default_locale": "預設區域", + "default_locale_description": "依瀏覽器區域設定日期和數字格式", "delete": "删除", "delete_album": "刪除相簿", "delete_api_key_prompt": "您確定要刪除這個 API Key嗎?", @@ -512,10 +517,12 @@ "display_options": "顯示選項", "display_order": "顯示順序", "display_original_photos": "顯示原始照片", - "display_original_photos_setting_description": "當網頁兼容原始照片時,偏好查看照片時顯示原始檔案而非縮略圖。這可能會導致照片顯示速度變慢。", + "display_original_photos_setting_description": "在網頁與原始檔案相容的情況下,查看檔案時優先顯示原始檔案而非縮圖。這可能會讓照片顯示速度變慢。", "do_not_show_again": "不再顯示此訊息", "done": "完成", "download": "下載", + "download_include_embedded_motion_videos": "嵌入影片", + "download_include_embedded_motion_videos_description": "把嵌入動態照片的影片作爲單獨的檔案包含在內", "download_settings": "下載", "download_settings_description": "管理與檔案下載相關的設定", "downloading": "下載中", @@ -548,12 +555,16 @@ "edit_title": "編輯標題", "edit_user": "編輯使用者", "edited": "己編輯", - "editor": "", + "editor": "編輯器", + "editor_close_without_save_prompt": "編輯過的內容不會儲存起來", + "editor_close_without_save_title": "要關閉編輯器嗎?", + "editor_crop_tool_h2_aspect_ratios": "長寬比", + "editor_crop_tool_h2_rotation": "旋轉", "email": "電子郵件", "empty": "", "empty_album": "", - "empty_trash": "清空回收站", - "empty_trash_confirmation": "您確定要清空垃圾桶嗎?這將永久刪除 Immich 中所有垃圾桶中的檔案。\n您不能撤銷這個操作!", + "empty_trash": "清空垃圾桶", + "empty_trash_confirmation": "確定要清空垃圾桶嗎?這會永久刪除 Immich 垃圾桶中所有的檔案。\n此步驟無法取消喔!", "enable": "啟用", "enabled": "己啟用", "end_date": "結束日期", @@ -576,7 +587,7 @@ "error_adding_users_to_album": "將使用者加入相簿時出錯", "error_deleting_shared_user": "刪除共享使用者時出錯", "error_downloading": "下載 {filename} 時出錯", - "error_hiding_buy_button": "隱藏購買按鈕時出錯", + "error_hiding_buy_button": "隱藏購置按鈕時出錯", "error_removing_assets_from_album": "從相簿中移除檔案時出錯了,請到控制臺瞭解詳情", "error_selecting_all_assets": "選擇所有檔案時出錯", "exclusion_pattern_already_exists": "此排除模式已存在。", @@ -590,7 +601,7 @@ "failed_to_load_people": "無法載入人物", "failed_to_remove_product_key": "無法移除產品密鑰", "failed_to_stack_assets": "無法堆疊檔案", - "failed_to_unstack_assets": "無法解除堆疊資產", + "failed_to_unstack_assets": "無法解除堆疊檔案", "import_path_already_exists": "此匯入路徑已存在。", "incorrect_email_or_password": "電子郵件或密碼有誤", "paths_validation_failed": "{paths, plural, one {# 個路徑} other {# 個路徑}} 驗證失敗", @@ -604,7 +615,7 @@ "unable_to_add_import_path": "無法添加匯入路徑", "unable_to_add_partners": "無法添加夥伴", "unable_to_add_remove_archive": "無法{archived, select, true {從封存中移除檔案} other {將檔案加入封存}}", - "unable_to_add_remove_favorites": "無法 {favorite, select, true {將檔案添加至} other {從中移除檔案}} 收藏夾", + "unable_to_add_remove_favorites": "無法將檔案{favorite, select, true {加入收藏} other {從收藏中移除}}", "unable_to_archive_unarchive": "無法{archived, select, true {封存} other {取消封存}}", "unable_to_change_album_user_role": "無法更改相簿使用者的角色", "unable_to_change_date": "無法更改日期", @@ -618,7 +629,7 @@ "unable_to_connect": "無法連接", "unable_to_connect_to_server": "無法連接到伺服器", "unable_to_copy_to_clipboard": "無法複製到剪貼板,請確保您以 https 存取該頁面", - "unable_to_create_admin_account": "無法建立管理員帳戶", + "unable_to_create_admin_account": "無法建立管理者帳號", "unable_to_create_api_key": "無法建立新的 API 金鑰", "unable_to_create_library": "無法建立資料庫", "unable_to_create_user": "無法建立使用者", @@ -662,9 +673,9 @@ "unable_to_repair_items": "無法糾正項目", "unable_to_reset_password": "無法重設密碼", "unable_to_resolve_duplicate": "無法解決重複項", - "unable_to_restore_assets": "無法恢復檔案", - "unable_to_restore_trash": "無法恢復垃圾桶內容", - "unable_to_restore_user": "無法恢復使用者", + "unable_to_restore_assets": "無法還原檔案", + "unable_to_restore_trash": "無法還原垃圾桶中的項目", + "unable_to_restore_user": "無法還原使用者", "unable_to_save_album": "無法儲存相簿", "unable_to_save_api_key": "無法儲存 API 金鑰", "unable_to_save_date_of_birth": "無法儲存出生日期", @@ -676,7 +687,7 @@ "unable_to_set_feature_photo": "無法設置特色照片", "unable_to_set_profile_picture": "無法設置個人頭像", "unable_to_submit_job": "無法提交作業", - "unable_to_trash_asset": "無法將檔案移至垃圾桶", + "unable_to_trash_asset": "無法將檔案丟進垃圾桶", "unable_to_unlink_account": "無法對帳號取消連接", "unable_to_update_album_cover": "無法更新相簿封面", "unable_to_update_album_info": "無法更新相簿資訊", @@ -694,10 +705,11 @@ "exif": "Exif", "exit_slideshow": "退出幻燈片", "expand_all": "展開全部", - "expire_after": "有效時間", + "expire_after": "失效時間", "expired": "已過期", - "expires_date": "有效期限:{date}", + "expires_date": "失效期限:{date}", "explore": "探索", + "explorer": "探測器", "export": "匯出", "export_as_json": "匯出 JSON", "extension": "副檔名", @@ -718,6 +730,7 @@ "filter_people": "篩選人物", "find_them_fast": "搜尋名稱,快速找人", "fix_incorrect_match": "修復不相符的", + "folders": "資料夾", "force_re-scan_library_files": "強制重新掃描所有資料庫檔案", "forward": "順序", "general": "一般", @@ -746,7 +759,7 @@ "image_alt_text_date_2_people": "{isVideo, select, true {影片} other {圖片}} 與 {person1} 和 {person2} 一同於 {date} 拍攝", "image_alt_text_date_3_people": "{isVideo, select, true {影片} other {圖片}} 與 {person1}、{person2} 和 {person3} 一同於 {date} 拍攝", "image_alt_text_date_4_or_more_people": "{isVideo, select, true {影片} other {圖片}} 與 {person1}、{person2} 和其他 {additionalCount, number} 人於 {date} 拍攝", - "image_alt_text_date_place": "{isVideo, select, true {影片} other {圖片}} 於 {city}、{country},{date} 拍攝", + "image_alt_text_date_place": "{date}在 {country} - {city} 拍攝的{isVideo, select, true {影片} other {圖片}}", "image_alt_text_date_place_1_person": "{isVideo, select, true {影片} other {圖片}} 於 {city}、{country},與 {person1} 一同在 {date} 拍攝", "image_alt_text_date_place_2_people": "{isVideo, select, true {影片} other {圖片}} 在 {city}、{country},與 {person1} 和 {person2} 一同於 {date} 拍攝", "image_alt_text_date_place_3_people": "{isVideo, select, true {影片} other {圖片}} 在 {city}、{country},與 {person1}、{person2} 和 {person3} 一同於 {date} 拍攝", @@ -765,7 +778,7 @@ "info": "資訊", "interval": { "day_at_onepm": "每天下午 1 點", - "hours": "每 {hours, plural, one {小時} other {{hours, number} 小時}}", + "hours": "每 {hours, plural, other {{hours, number} 小時}}", "night_at_midnight": "每晚午夜", "night_at_twoam": "每晚凌晨 2 點" }, @@ -802,7 +815,7 @@ "login": "登入", "login_has_been_disabled": "已停用登入功能。", "logout_all_device_confirmation": "您確定要登出所有裝置嗎?", - "logout_this_device_confirmation": "您確定要登出這個裝置嗎?", + "logout_this_device_confirmation": "要登出這臺裝置嗎?", "longitude": "經度", "look": "樣貌", "loop_videos": "重播影片", @@ -811,7 +824,7 @@ "manage_shared_links": "管理分享鏈結", "manage_sharing_with_partners": "管理與夥伴的分享", "manage_the_app_settings": "管理應用程式設定", - "manage_your_account": "管理您的帳戶", + "manage_your_account": "管理您的帳號", "manage_your_api_keys": "管理您的 API 金鑰", "manage_your_devices": "管理已登入的裝置", "manage_your_oauth_connection": "管理您的 OAuth 連接", @@ -822,7 +835,7 @@ "matches": "相符", "media_type": "媒體類型", "memories": "回憶", - "memories_setting_description": "管理您在回憶中顯示的內容", + "memories_setting_description": "管理您的回憶中顯示的內容", "memory": "回憶", "memory_lane_title": "回憶長廊{title}", "menu": "選單", @@ -838,11 +851,11 @@ "model": "型號", "month": "月", "more": "更多", - "moved_to_trash": "已移至垃圾桶", + "moved_to_trash": "已丟進垃圾桶", "my_albums": "我的相簿", "name": "名稱", "name_or_nickname": "名稱或暱稱", - "never": "永遠", + "never": "永不失效", "new_album": "新相簿", "new_api_key": "新的 API 金鑰", "new_password": "新密碼", @@ -861,7 +874,7 @@ "no_duplicates_found": "沒發現重複項目。", "no_exif_info_available": "沒有可用的 Exif 資訊", "no_explore_results_message": "上傳更多照片以利探索。", - "no_favorites_message": "將最喜愛的項目添加至收藏夾,以便快速找到您的最佳照片和影片", + "no_favorites_message": "加入收藏,加速尋找影像", "no_libraries_message": "建立外部圖庫來查看您的照片和影片", "no_name": "無名", "no_places": "沒有地點", @@ -869,7 +882,7 @@ "no_results_description": "試試同義詞或更通用的關鍵字吧", "no_shared_albums_message": "建立相簿分享照片和影片", "not_in_any_album": "不在任何相簿中", - "note_apply_storage_label_to_previously_uploaded assets": "注意:要將存儲標籤應用於先前上傳的檔案,請運行", + "note_apply_storage_label_to_previously_uploaded assets": "註:要將存標記用於先前上傳的檔案,請執行", "note_unlimited_quota": "註:輸入 0 表示不限制配額", "notes": "提示", "notification_toggle_setting_description": "啟用電子郵件通知", @@ -882,6 +895,7 @@ "ok": "確定", "oldest_first": "由舊至新", "onboarding": "入門指南", + "onboarding_privacy_description": "以下(可選)功能依賴外部服務,可隨時在管理設定中停用。", "onboarding_theme_description": "選擇顏色主題。您可以稍後在設定中更改此選項。", "onboarding_welcome_description": "讓我們為您的伺服器架構一些常見的設置。", "onboarding_welcome_user": "歡迎,{user}", @@ -890,7 +904,7 @@ "only_refreshes_modified_files": "只重新整理修改過的檔案", "open_in_map_view": "開啟地圖檢視", "open_in_openstreetmap": "用 OpenStreetMap 開啟", - "open_the_search_filters": "打開搜尋過濾器", + "open_the_search_filters": "開啟搜尋篩選器", "options": "選項", "or": "或", "organize_your_library": "整理您的圖庫", @@ -943,7 +957,7 @@ "places": "地點", "play": "播放", "play_memories": "播放回憶", - "play_motion_photo": "播放動態相片", + "play_motion_photo": "播放動態照片", "play_or_pause_video": "播放或暫停影片", "point": "", "port": "埠口", @@ -951,46 +965,49 @@ "preview": "預覽", "previous": "上一張", "previous_memory": "上一張回憶", - "previous_or_next_photo": "上一張或下一張照片", + "previous_or_next_photo": "上、下一張照片", "primary": "首要", + "privacy": "隱私", "profile_image_of_user": "{user} 的個人資料圖片", "profile_picture_set": "已設定個人資料圖片。", "public_album": "公開相簿", "public_share": "公開分享", "purchase_account_info": "擁護者", - "purchase_activated_subtitle": "感謝您對 Immich 及開源軟體的支持", + "purchase_activated_subtitle": "感謝您對 Immich 及開源軟體的支援", "purchase_activated_time": "於 {date, date} 啟用", "purchase_activated_title": "金鑰成功啟用了", "purchase_button_activate": "啟用", - "purchase_button_buy": "購買", - "purchase_button_buy_immich": "購買 Immich", + "purchase_button_buy": "購置", + "purchase_button_buy_immich": "購置 Immich", "purchase_button_never_show_again": "不再顯示", - "purchase_button_reminder": "30天後提醒我", + "purchase_button_reminder": "過 30 天再提醒我", "purchase_button_remove_key": "移除金鑰", - "purchase_button_select": "選擇", + "purchase_button_select": "選這個", "purchase_failed_activation": "啟用失敗!請檢查您的電子郵件以取得正確的產品金鑰!", "purchase_individual_description_1": "針對個人", - "purchase_individual_description_2": "支持者狀態", + "purchase_individual_description_2": "擁護者狀態", "purchase_individual_title": "個人", "purchase_input_suggestion": "有產品金鑰嗎?請在下面輸入金鑰", - "purchase_license_subtitle": "購買 Immich 以支持軟件發展", - "purchase_lifetime_description": "終身購買", - "purchase_option_title": "購買選項", + "purchase_license_subtitle": "購置 Immich 來支援軟體開發", + "purchase_lifetime_description": "終身購置", + "purchase_option_title": "購置選項", "purchase_panel_info_1": "開發 Immich 可不是件容易的事,花了我們不少功夫。好在有一群全職工程師在背後默默努力,爲的就是把它做到最好。我們的目標很簡單:讓開源軟體和正當的商業模式能成爲開發者的長期飯碗,同時打造出重視隱私的生態系統,讓大家有個不被剝削的雲端服務新選擇。", - "purchase_panel_info_2": "由於我們於不設付費牆,這筆購買不會為你提供 Immich 任何額外功能。我們依賴像你這樣的用戶來支持 Immich 持續開發。", - "purchase_panel_title": "支持這個項目", - "purchase_per_server": "每台伺服器", + "purchase_panel_info_2": "我們承諾不設付費牆,所以購置 Immich 並不會讓您獲得額外的功能。我們是依賴使用者們的支援來開發 Immich 的。", + "purchase_panel_title": "支援這項專案", + "purchase_per_server": "每臺伺服器", "purchase_per_user": "每位使用者", - "purchase_remove_product_key": "移除產品密鑰", - "purchase_remove_product_key_prompt": "您確定要移除產品密鑰嗎?", - "purchase_remove_server_product_key": "移除伺服器產品密鑰", - "purchase_remove_server_product_key_prompt": "您確定要移除伺服器產品密鑰嗎?", - "purchase_server_description_1": "適用於整個伺服器", - "purchase_server_description_2": "支持者狀態", + "purchase_remove_product_key": "移除產品金鑰", + "purchase_remove_product_key_prompt": "確定要移除產品金鑰嗎?", + "purchase_remove_server_product_key": "移除伺服器產品金鑰", + "purchase_remove_server_product_key_prompt": "確定要移除伺服器產品金鑰嗎?", + "purchase_server_description_1": "給整臺伺服器", + "purchase_server_description_2": "擁護者狀態", "purchase_server_title": "伺服器", "purchase_settings_server_activated": "伺服器產品金鑰是由管理者管理的", "range": "", "rating": "評星", + "rating_clear": "清除評等", + "rating_count": "{count, plural, other {# 星}}", "rating_description": "在資訊面板中顯示 Exif 評等", "raw": "", "reaction_options": "反應選項", @@ -1023,7 +1040,7 @@ "removed_api_key": "已移除 API 金鑰:{name}", "removed_from_archive": "從封存中移除", "removed_from_favorites": "已從收藏中移除", - "removed_from_favorites_count": "已從收藏中移除 {count, plural, one {#} other {#}}", + "removed_from_favorites_count": "已移除收藏的 {count, plural, other {# 個項目}}", "rename": "改名", "repair": "糾正", "repair_no_results_message": "未被追蹤及遺失的檔案會顯示在這裏", @@ -1033,22 +1050,22 @@ "require_user_to_change_password_on_first_login": "要求使用者在首次登入時更改密碼", "reset": "重設", "reset_password": "重設密碼", - "reset_people_visibility": "重置人物可見性", + "reset_people_visibility": "重設人物可見性", "reset_settings_to_default": "", - "reset_to_default": "設爲預設", + "reset_to_default": "重設回預設", "resolve_duplicates": "解決重複項", "resolved_all_duplicates": "已解決所有重複項目", - "restore": "恢复", - "restore_all": "恢復全部", - "restore_user": "恢復使用者", - "restored_asset": "已恢復檔案", + "restore": "還原", + "restore_all": "全部還原", + "restore_user": "還原使用者", + "restored_asset": "已還原檔案", "resume": "繼續", - "retry_upload": "重試上傳", + "retry_upload": "重新上傳", "review_duplicates": "查核重複項目", "role": "角色", "role_editor": "編輯者", "role_viewer": "檢視者", - "save": "保存", + "save": "儲存", "saved_api_key": "已儲存的 API 密鑰", "saved_profile": "已儲存個人資料", "saved_settings": "已儲存設定", @@ -1069,14 +1086,14 @@ "search_country": "搜尋國家…", "search_for_existing_person": "搜尋現有的人物", "search_no_people": "沒有人找到", - "search_no_people_named": "沒有名為 \"{name}\" 的人", + "search_no_people_named": "沒有名爲「{name}」的人物", "search_people": "搜尋人物", "search_places": "搜尋地點", "search_state": "搜尋地區…", - "search_timezone": "搜尋時區...", + "search_timezone": "搜尋時區…", "search_type": "搜尋類型", "search_your_photos": "搜尋照片", - "searching_locales": "搜尋地區...", + "searching_locales": "搜尋區域…", "second": "秒", "see_all_people": "查看所有人物", "select_album_cover": "選擇相簿封面", @@ -1089,7 +1106,7 @@ "select_keep_all": "全部保留", "select_library_owner": "選擇圖庫擁有者", "select_new_face": "選擇新臉孔", - "select_photos": "選相片", + "select_photos": "選照片", "select_trash_all": "全部刪除", "selected": "已選擇", "selected_count": "{count, plural, other {選了 # 項}}", @@ -1100,10 +1117,10 @@ "server_online": "伺服器在線", "server_stats": "伺服器統計", "server_version": "目前版本", - "set": "設置", + "set": "設定", "set_as_album_cover": "設爲相簿封面", "set_as_profile_picture": "設為個人資料圖片", - "set_date_of_birth": "設置出生日期", + "set_date_of_birth": "設定出生日期", "set_profile_picture": "設置個人資料圖片", "set_slideshow_to_fullscreen": "以全螢幕放映幻燈片", "settings": "設定", @@ -1114,6 +1131,7 @@ "shared_by_user": "由 {user} 分享", "shared_by_you": "由你分享", "shared_from_partner": "來自 {partner} 的照片", + "shared_link_options": "分享鏈結選項", "shared_links": "分享鏈結", "shared_photos_and_videos_count": "{assetCount, plural, other {已分享 # 張照片及影片。}}", "shared_with_partner": "與 {partner} 共享", @@ -1137,8 +1155,8 @@ "show_person_options": "顯示人物選項", "show_progress_bar": "顯示進度條", "show_search_options": "顯示搜尋選項", - "show_supporter_badge": "支持者徽章", - "show_supporter_badge_description": "顯示支持者徽章", + "show_supporter_badge": "擁護者徽章", + "show_supporter_badge_description": "顯示擁護者徽章", "shuffle": "隨機排序", "sign_out": "登出", "sign_up": "註冊", @@ -1157,14 +1175,14 @@ "stack": "堆叠", "stack_duplicates": "堆疊重複項目", "stack_select_one_photo": "爲堆疊選一張主要照片", - "stack_selected_photos": "堆疊選定的照片", + "stack_selected_photos": "堆疊所選的照片", "stacked_assets_count": "已堆疊 {count, plural, one {# 個檔案} other {# 個檔案}}", "stacktrace": "堆疊追蹤", "start": "開始", "start_date": "開始日期", "state": "地區", "status": "狀態", - "stop_motion_photo": "停格照片", + "stop_motion_photo": "停止動態照片", "stop_photo_sharing": "要停止分享您的照片嗎?", "stop_photo_sharing_description": "{partner} 將無法再訪問你的照片。", "stop_sharing_photos_with_user": "停止與此用戶共享你的照片", @@ -1179,7 +1197,7 @@ "template": "模板", "theme": "主題", "theme_selection": "主題選項", - "theme_selection_description": "根據你的瀏覽器系統偏好自動設置主題為淺色或深色", + "theme_selection_description": "依瀏覽器系統偏好自動設定深、淺色主題", "they_will_be_merged_together": "它們將會被合併在一起", "time_based_memories": "依時間回憶", "timezone": "時區", @@ -1189,13 +1207,13 @@ "to_login": "登入", "to_trash": "垃圾桶", "toggle_settings": "切換設定", - "toggle_theme": "切換主題", + "toggle_theme": "切換深色主題", "toggle_visibility": "", "total_usage": "總用量", "trash": "垃圾桶", - "trash_all": "全丟進垃圾桶", - "trash_count": "刪除 {count, number} 檔案", - "trash_delete_asset": "刪除檔案/放入垃圾桶", + "trash_all": "全部丟掉", + "trash_count": "丟掉 {count, number} 個檔案", + "trash_delete_asset": "將檔案丟進垃圾桶 / 刪除", "trash_no_results_message": "垃圾桶中的照片和影片將顯示在這裡。", "trashed_items_will_be_permanently_deleted_after": "垃圾桶中的項目會在 {days, plural, other {# 天}}後永久刪除。", "type": "類型", @@ -1211,12 +1229,13 @@ "unlink_oauth": "取消連接 OAuth", "unlinked_oauth_account": "已解除連接 OAuth 帳號", "unnamed_album": "未命名相簿", + "unnamed_album_delete_confirmation": "確定要刪除這本相簿嗎?", "unnamed_share": "未命名分享", - "unsaved_change": "未儲存的更改", + "unsaved_change": "更改未儲存", "unselect_all": "取消全選", - "unselect_all_duplicates": "取消選擇所有重複項", + "unselect_all_duplicates": "取消選取所有的重複項目", "unstack": "取消堆叠", - "unstacked_assets_count": "已取消堆疊 {count, plural, one {# 個檔案} other {# 個檔案}}", + "unstacked_assets_count": "已解除堆疊 {count, plural, other {# 個檔案}}", "untracked_files": "未被追蹤的檔案", "untracked_files_decription": "這些檔案不會被追蹤。它們可能是移動失誤、上傳中斷或遇到漏洞而遺留的產物", "up_next": "下一個", @@ -1225,10 +1244,10 @@ "upload_concurrency": "上傳並行", "upload_errors": "上傳完成,但有 {count, plural, other {# 處出錯}},要查看新上傳的檔案請重新整理頁面。", "upload_progress": "剩餘 {remaining, number} - 已處理 {processed, number}/{total, number}", - "upload_skipped_duplicates": "跳過 {count, plural, one {# 個重複檔案} other {# 個重複檔案}}", + "upload_skipped_duplicates": "已略過 {count, plural, other {# 個重複的檔案}}", "upload_status_duplicates": "重複項目", "upload_status_errors": "錯誤", - "upload_status_uploaded": "己上載", + "upload_status_uploaded": "已上傳", "upload_success": "上傳成功,要查看新上傳的檔案請重新整理頁面。", "url": "網址", "usage": "用量", @@ -1236,7 +1255,7 @@ "user": "使用者", "user_id": "使用者 ID", "user_liked": "{user} 喜歡了 {type, select, photo {這張照片} video {這段影片} asset {這個檔案} other {它}}", - "user_purchase_settings": "購買", + "user_purchase_settings": "購置", "user_purchase_settings_description": "管理你的購買", "user_role_set": "設 {user} 爲{role}", "user_usage_detail": "使用者用量詳情", @@ -1249,7 +1268,7 @@ "version_announcement_closing": "敬祝順心,Alex", "version_announcement_message": "嗨~本應用程式可以更新了,爲防止配置出錯,請花點時間閱讀發行說明,並確保 docker-compose.yml.env 設置是最新的,特別是使用 WatchTower 等自動更新工具時。", "video": "影片", - "video_hover_setting": "在鼠標懸停時播放影片縮圖", + "video_hover_setting": "游標停留時播放影片縮圖", "video_hover_setting_description": "當滑鼠停在項目上時播放影片縮圖。即使停用,將滑鼠停在播放圖示上也可以播放。", "videos": "影片", "videos_count": "{count, plural, other {# 部影片}}", @@ -1262,7 +1281,7 @@ "view_previous_asset": "查看上一項", "view_stack": "查看堆疊", "viewer": "", - "visibility_changed": "{count, plural, one {# 人} other {# 人}} 的可見性已更改", + "visibility_changed": "已更改 {count, plural, other {# 位人物}}的可見性", "waiting": "待處理", "warning": "警告", "week": "周", diff --git a/web/src/lib/i18n/zh_SIMPLIFIED.json b/web/src/lib/i18n/zh_SIMPLIFIED.json index fd3fd5815cbbe..b5379c27e2efd 100644 --- a/web/src/lib/i18n/zh_SIMPLIFIED.json +++ b/web/src/lib/i18n/zh_SIMPLIFIED.json @@ -129,12 +129,13 @@ "map_enable_description": "启用地图功能", "map_gps_settings": "地图与GPS设置", "map_gps_settings_description": "管理地图与GPS(反向地理编码)设置", + "map_implications": "地图功能依赖于外部图块服务(tiles.immich.cloud)", "map_light_style": "浅色模式", "map_manage_reverse_geocoding_settings": "管理反向地理编码设置", "map_reverse_geocoding": "反向地理编码", "map_reverse_geocoding_enable_description": "启用反向地理编码", "map_reverse_geocoding_settings": "反向地理编码设置", - "map_settings": "地图设置", + "map_settings": "地图", "map_settings_description": "管理地图设置", "map_style_description": "地图主题 style.json 的 URL", "metadata_extraction_job": "提取元数据", @@ -320,7 +321,8 @@ "user_settings": "用户设置", "user_settings_description": "管理用户设置", "user_successfully_removed": "用户{email}已被成功删除。", - "version_check_enabled_description": "启用对GitHub的定期请求以检查新版本", + "version_check_enabled_description": "启用版本检测", + "version_check_implications": "版本检查功能依赖于与 github.com 的定期通信", "version_check_settings": "版本检查", "version_check_settings_description": "启用或禁用新版本通知", "video_conversion_job": "视频转码", @@ -336,7 +338,8 @@ "album_added": "相册已添加", "album_added_notification_setting_description": "当您被添加到共享相册时,接收电子邮件通知", "album_cover_updated": "相册封面已更新", - "album_delete_confirmation": "是否确定要删除相册{album}?\n如果这是共享相册,其他用户将无法再访问它。", + "album_delete_confirmation": "是否确定要删除相册{album}?", + "album_delete_confirmation_description": "如果该相册是共享的,其他用户将无法再访问它。", "album_info_updated": "相册信息已更新", "album_leave": "退出相册?", "album_leave_confirmation": "确定要退出相册{album}?", @@ -360,6 +363,7 @@ "allow_edits": "允许编辑", "allow_public_user_to_download": "开放下载给所有人", "allow_public_user_to_upload": "允许所有用户上传", + "anti_clockwise": "逆时针", "api_key": "API Key", "api_key_description": "该应用密钥只会展示一次。请确保在关闭窗口前复制下来。", "api_key_empty": "API Key的名称不可以为空", @@ -441,6 +445,7 @@ "clear_all_recent_searches": "清除所有最近搜索", "clear_message": "清空消息", "clear_value": "清空值", + "clockwise": "顺时针", "close": "关闭", "collapse": "折叠", "collapse_all": "全部折叠", @@ -517,6 +522,8 @@ "do_not_show_again": "不再显示该信息", "done": "完成", "download": "下载", + "download_include_embedded_motion_videos": "内嵌视频", + "download_include_embedded_motion_videos_description": "将动态照片中的内嵌视频作为单独文件纳入", "download_settings": "下载", "download_settings_description": "管理项目下载相关设置", "downloading": "下载中", @@ -550,6 +557,10 @@ "edit_user": "编辑用户", "edited": "已编辑", "editor": "编辑器", + "editor_close_without_save_prompt": "此更改不会被保存", + "editor_close_without_save_title": "关闭编辑器?", + "editor_crop_tool_h2_aspect_ratios": "长宽比", + "editor_crop_tool_h2_rotation": "旋转", "email": "邮箱", "empty": "空", "empty_album": "清空相册", @@ -699,6 +710,7 @@ "expired": "已过期", "expires_date": "{date}过期", "explore": "探索", + "explorer": "浏览器", "export": "导出", "export_as_json": "导出为JSON", "extension": "扩展", @@ -720,6 +732,7 @@ "filter_people": "过滤人物", "find_them_fast": "按名称快速搜索", "fix_incorrect_match": "修复不正确的匹配", + "folders": "文件夹", "force_re-scan_library_files": "强制重新扫描所有图库文件", "forward": "向前", "general": "通用", @@ -872,7 +885,7 @@ "my_albums": "我的相册", "name": "名称", "name_or_nickname": "名称或昵称", - "never": "从不", + "never": "永不过期", "new_album": "新相册", "new_api_key": "新API Key", "new_password": "新密码", @@ -912,6 +925,7 @@ "ok": "确定", "oldest_first": "最旧优先", "onboarding": "盛大开启", + "onboarding_privacy_description": "以下(可选)功能依赖外部服务,可随时在管理设置中禁用。", "onboarding_theme_description": "选择服务的颜色主题。稍后可以在设置中进行修改。", "onboarding_welcome_description": "我们在启用服务前先做一些通用设置。", "onboarding_welcome_user": "欢迎,{user}", @@ -985,6 +999,7 @@ "previous_memory": "上一个", "previous_or_next_photo": "上一张或下一张照片", "primary": "首要", + "privacy": "隐私", "profile_image_of_user": "{user}的个人资料图片", "profile_picture_set": "个人资料图片已设置。", "public_album": "公开相册", @@ -1023,6 +1038,8 @@ "purchase_settings_server_activated": "服务器产品密钥正在由管理员管理", "range": "范围", "rating": "星级", + "rating_clear": "删除星级", + "rating_count": "{count, plural, one {#星} other {#星}}", "rating_description": "在信息面板中展示EXIF星级", "raw": "Raw", "reaction_options": "反应选项", @@ -1146,6 +1163,7 @@ "shared_by_user": "由{user}共享", "shared_by_you": "你的共享", "shared_from_partner": "来自{partner}的照片", + "shared_link_options": "共享链接选项", "shared_links": "共享链接", "shared_photos_and_videos_count": "{assetCount, plural, other {#项已共享照片&视频。}}", "shared_with_partner": "与{partner}共享", @@ -1221,7 +1239,7 @@ "to_login": "登录", "to_trash": "放入回收站", "toggle_settings": "切换设置", - "toggle_theme": "切换主题", + "toggle_theme": "切换深色主题", "toggle_visibility": "切换可见性", "total_usage": "总用量", "trash": "回收站", @@ -1243,6 +1261,7 @@ "unlink_oauth": "解绑OAuth", "unlinked_oauth_account": "解绑OAuth账户", "unnamed_album": "未命名相册", + "unnamed_album_delete_confirmation": "您确定要删除该相册吗?", "unnamed_share": "未命名共享", "unsaved_change": "未保存的修改", "unselect_all": "取消全选", From 5811025ebdbbf2be7089f73d7325e58aea4afbc5 Mon Sep 17 00:00:00 2001 From: aviv926 <51673860+aviv926@users.noreply.github.com> Date: Wed, 28 Aug 2024 19:43:51 +0300 Subject: [PATCH 046/160] docs: Documentation updates (#11516) * Documentation updates * PR feedback * PR feedback * Originally implemented using #11880 * add to FAQ * Remove mTLS --------- Co-authored-by: Jason Rasmussen --- docs/docs/FAQ.mdx | 5 +++++ docs/docs/administration/img/admin-jobs.png | Bin 1108716 -> 0 bytes docs/docs/administration/img/admin-jobs.webp | Bin 0 -> 81174 bytes docs/docs/administration/jobs-workers.md | 2 +- docs/docs/administration/system-settings.md | 18 +++++++++++++----- docs/docs/features/shared-albums.md | 4 ++-- docs/docs/guides/remote-access.md | 8 ++++++-- docs/docs/guides/remote-machine-learning.md | 4 ++++ docs/src/components/community-projects.tsx | 13 ++++++------- 9 files changed, 37 insertions(+), 17 deletions(-) delete mode 100644 docs/docs/administration/img/admin-jobs.png create mode 100644 docs/docs/administration/img/admin-jobs.webp diff --git a/docs/docs/FAQ.mdx b/docs/docs/FAQ.mdx index 501a67d5f2a58..b1a24e1788a2f 100644 --- a/docs/docs/FAQ.mdx +++ b/docs/docs/FAQ.mdx @@ -67,6 +67,11 @@ No, Immich does not modify the original files. All edited metadata is saved in companion `.xmp` sidecar files and the database. However, Immich will delete original files that have been trashed when the trash is emptied in the Immich UI. +### Why do my file names appear as a random string in the file manager? + +When Storage Template is off (default) Immich saves the file names in a random string (also known as random UUIDs) to prevent duplicate file names. To retrieve the original file names, you must enable the Storage Template and then run the STORAGE TEMPLATE MIGRATION job. +It is recommended to read about [Storage Template](https://immich.app/docs/administration/storage-template) before activation. + ### Can I add my existing photo library? Yes, with an [External Library](/docs/features/libraries.md). diff --git a/docs/docs/administration/img/admin-jobs.png b/docs/docs/administration/img/admin-jobs.png deleted file mode 100644 index 096bce4354f0f0b85ab1998bc5e0d161d689e199..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1108716 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

gqPI84}Rv1U!q!g0xV2eYd$&`1)ok6;q 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 vBeT_`1lmP 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≷+Ht%k3(B!e?8se z2Wqad=Di{Zc|$MH0Hw-iyLiIaDmysIK8~oP{Qj zl3Jq60d6XPEQ)6W^I(=UJJ>lejreBCjhL|@tlL8yWa9EZS z$q5?K262Z0)yRQp4D+t=(q_0GVp z1?C1rK$;uL-~AqLblw(K%Gjeo>`(;Ix(~OFfr$4|UwfD-X5d3rjkb%G zypgX+Qmg}iqFDBxMZ;)`Z0gf9)0dunXx@n{^o+GSuFi(Ck@rb$7FHUx5e`sT1It`p>*k`iJuOU*%heX~M)5o99F4`K8Cf z+uC0S)}Md`X(Hb>1Ml^Un(cli?zN=%`o_+U^}17YX2)M1L5!5!1Cij7i6WG2z2-(^ zvZs?)+Z&Zk31V(gkqCiXEI4&cTLE7}?`t?glhQT09D}7~Vc`pWqyD#{b(F zxF_#A{XID%!__9Op+>FZyp?y}(x7$@oQms?6Rcgg3C9Gb3v|tY;rc8^=Cq_7{a1#6 zV*TKzC5Pywh_y~zk^&hTqna(rt@Y?r^>w4PUPAZj0R4Dm8)+XX#!>oV;$Up7Kw=!X z8S4LxTD<=degqW+th*&p>sEt)93_>$1?^=-B{FZ!~-Z zS^MVc8K#*7FXX@fIe$+;mA0>=o$f@q>N2R@OWwhj(vw=+X=pW{4>WHvlsB~_c=W{Z zsCj}?F#LLYcd}Sn*J97>1mIux9qIm1vy@N&w#%zc@0Oz4Po2O@4-8qI3Bh8*P4a&U zU2>+@q+gPj1Qa4-ye5Ch=`42YSexTAb_8szFl7hJ?P;V(9Xn7+*tYCH%v0`Qywjrh zhyElaY=7$C$ku_zEchXg=Im3Hi5zJAWd!*9)Vm6vIvYbE#aO=r7rcEa*%q!gK5QFd z3RR!V*8T9hLRI1!tkY&}=Wl~&U1e10QZ)F!_eUk~5~&=G!A#J1-h2(T<`=GYehIs> zjFBxN1Jb0|U!$pqI>@uc{?8ld#YPB`b;4D>i^@ZWR-s!PK8{C~LhPx8bWa#3EDr+}+cPa_*C^0`|Pf1);+UyvXv(d1!4S0SgP|TGb1Gd>8#o zS(~SA)|Z*Wz!N0OA>)db;C*~KQ!blTbu~m_JE7ybp3(C|upJz zlXHnB$Gys8nZKsn*0!m{=4oPRD*jY3kKKK7xdkS`V`u@yZCTDZ~#F+hiWE18#Z2eGk=6+R!QT5HYaTTK4 zzE}~O85{AJP@iKw34eKt)#Q4^s;VS4ME>~Bw=Kz)+`zI%9cBoBt_nj|gJ}Qyy!HO^ z`ysX!_Grw<;~~}z9*bmNf+TX_Ek-$jteZ+hrv(jkqD1Q?OvyZ@l~>@DHR%<^j@ZD`!u?B&(?*(|`D%?t*oZAWwwvKC?Il^5>+*pSQv&kE zx}EHJJq8DM}p!Pycq3dJhf;Q4wvGB<-pSD5BvRKTqpnVRRD{kNW? zw$ZSf zNSJs>_uB9@Rn;%ac^h0EJdqMyW3jeEwfm!r&btE`%b^Vj%5;hMaV?FuNIRa*4-0T| zP};>Ahd_JzI&Up-g!>gKgtMg}_a7rCjuzNYE$kun&Hg8Bovim!#e@Ghghpv7;i9>OpC6<#IhUpu}CHZFhLCU$$#n zTX`k&P8dz_m#K%r&TLODP|SP+m%OtNcKIQ z8Femip@tZi;G)nvIUR!6FTt9MrB!GorMR4_9a<+K$j|BD*&%XJ@pG)Xj$IYvv8s8- zfMzBE<@>R3xMtV7oen9^7gF{5g#b36tl9FecZkK8r&c=W1CxR#?Vri2sy;DnU>q`U zgbS86&J*N3xL`3cMXsN(u3Yg*>x;n@-pt$MrV0xYFOhj`o}ECoNY~i;eSoNV>@ltA zD&ebxP)`GCH{ARcroef~f->NyG}W}VJ{BRElhYMR?+Ko-DlY}bc8YcL$abS$}0^PwX;g17kbtLW_XEVK^4F9F;UpTaSZCBN0 ze|m@V?6s2-;C@>Iz$dEduoYM&2i-o{T%?LC<@zk+H7HOuq=?r3)aI8Hq!C`YFS2X3 zUyFkl!Y|26(pTe$1>EDDOrcGm;B@geOW&>TdU(>{xkN(k!pW>gR2f_;dqrj|)}MEv zi*lY@@TGmJy3ek)PJ(6H&r}~qLmTwTf!Z)M#Z`}5v5lc0X^5Npuj(1k2z3=B0n*S! zyNB7}lr+BR=vr+x(|lrU#(z((9S~jNX)nSqOKDB!0=i6u^sRG|;|M}Fs-4gY5XfL@ zgA<7LZtzrNjoi31KP&KGH!1-Ld8Y#LZH#!XkwRlNQ=@(I+A5S){iT$A`DnX7nwvGd!ln=;4lwFe0yjg6e;`AnEk%~2yIm>6Leb@!23s_;y|m<6cvmPIAHaV!5i zMoQBotFqG&D3N@XZa3S~5ZC%!0j&pingOpt*3#DNTW$}7ZnPcUjE&ZFr)(h^e2*PG zjO(7&yBmp^#qSb@L*QQIcx&|BgN@H|O1as0%DD&nXBDLh=x7p8qC@4Z0&N-9jD`0^ zEcP(3v*~i2A%kbvrDjuXDq#&~)2vf#_#p5fS(b`~wC3evnsVT!x-6pw#BrJu-^FjB zv&buHl1KD)7xfs+K!Ulym0UG!4fW(_`&1oY6nq#Z{;KS^lBPm=ptTVA&!+9;C5Qym ze~g?c^lw~3UK!-$Ave&^$6@;ML~nnben?TN)t7h)iw)_t!d~>YR%JsAUGjoBYN=zl z$vyy=Z))%#=Ty)*1u}GTNCg^|hp`ka;7XkMZHeCaRSwd;Fq$py<(I&@DDnto8uEdJ z1gsQ!LiA`r0X&3%JZcs5egaz1%P4W_mCuE|vD~Up;m@xVKhckrG6Qd~cThWLGJ zk|x0!m*=YBPXM))@r=rMEoozl=;7^1jq+9DWB%FhAy#Lze+&F~(f4c7bKjYu2NLDd zcQZM1;JG-eFK#B{UAHkRIr;bk4t2U0X#`SWcWyqDC_Lx z2W8{e8EqW1FFZ7nGcvlEq>KMl;sro`vIa_p?@LP_Ej?tGqJ=Xwc{n#_xC&bMWmtiiit$e?L^b;f zOHmH;oNp#wzvAS|&L3cDbbnny9)q~~mv2GFtj_90o#Bn$8eC=~rFc{8aSq5(5Y#QO zxLa8!1gxbEDew#H$83?e{z&Yb)BickP|@$f%=I~<-Cl5~gR#I4Y?N5nO^wHPJZMP@ z`dbDt%rtyNSKzfGuTZtt3*7`ARZOA{QTZ!^SR(++QNW@R!@L4_AWGw<=cOLtef;(o z75oBC%3V~<(Lrk$7tYU33s*dq+z%|;q1&hi|6BmqK>KzRg?9(QHP1vu1H7wR{+``9 zdGq59QED1NoQbD(hq}gw5`{XHN6W4pFT4ZnLzX zBO&x=_`10aG| zqk>)K#Q4ck%PO6-dFmRK%R-s#ej!b?@b@2P_*Ac5+L*(k?;djkB?{_#PVBW(Jt|)U z9JoySi7j?*g;0em4B>1GWhRdG8>i_2Ymskc(ANob@brqv>nQ5nU1&RtNm<&3&Ol;m z9;E_d^&l3+q2NAxUAC)5oVm&tMTP`T3a}?X}y zwa`4CZgQy`DwL6#gnWMhl1o@2+2RB?Ao29`eEF(Tc3mUo@dRM*LPgP^MP)L|UGQ`s zA33J~gTk}5v8W%*;%}1)wb63MP=}Cwd_p62&_#QfjFsk*)Bs)vnckA-R|PS5Jjmne;-C{rf@*bqv+|gFL^ms)t;`7TS0(90HzEG zj?7frE%CQS0^r`Qx*@nli3wR0X%9SPtm7;h?MXrfV#zWg9Gb{QTV@pD z1fYG~jWY+SUQhOduyjETH-LNJ0J0P6VQ>jEB<*ghDI8D;&^S%I+Cx>h&v_oOh0sJS zozU^+kyjP6l~C%RRMq6q)HONfYav0}JU1F{c66~1Sc>NhNxf@QE6o;Gy2@0vB0{#Z z#f~R*fbez`i5G|hQ2gCownD1_UMS^J2F89LOtIe&nVlZL)MV|RfK^Sr47|^4uw&n4 z!ogN6%jJ;{mHTEoJOGFYJOa8m>+=gADEI+qPRlJC0;>WAk9v?&H$O-?UMp8<7O7WB zi-?d{ZL@z#=(V1L09A9TR);%r>TB<<%{_UDeXwmrjYH2siEf=ezap%1yP;lZ!@y2iW!GL2{>WpD zvz1t!%9gW$t&lL8hArE|kc{*w6!gtTwOAS0z_U`n#MY^ubjJeQL-FLV_N7}C+|~>2 zv`2_^AkNFzDKI(6h^5APsz&TI1fh(vt&)zWfko#`SoO$^y2v~^F)QYG&Fs5-3Tb@8 zaixkUa(+5vXj1oFAHah$6(+kdM~mrc@~-KM6Y6a9zMC7yt`=Z7#dl&dqc7EVoRmwq zbE=5aVVy?i2SgHa;O(9Qr?i!_1l{5l3r#b5-*`t|Fjrm~FCwoHuK_2hSae(&JT=E* z#nk7d{&gV?(t*~0^2%fq>BDb}-yh26=q-@yLX-GxR+j*g+@sJQRb0AwI)EegYVqJ} z-e4%UDB2vSFzG}YW56bPer|J>I?Nn#JsO!HqQFQdNxqGwdOjAx*3^+HxrthhK&efK z^8(`w>{w2-P+xR*tQ|h6u8BCh4f6-RV}xT^g4N{NDnZVN27l3$Uvu}GC@BE}h&0>T zq3a}}bsPPq!jp-a4}w?htr(XSq)9IC;FHw;HN%UQ581Xkc*_{lu4E? z)d#&2>Z1Rz$7(K3F*CW)fOD?SZp3_IL~E1_ED#XxlsVU}(Oj4OtNbBqp_gG~GLtgy zYLG!S*5fXXaQ75kHYcMC91`{hpY}O&QAbODj^4nh^rdBaDTq!n(?*>)F^LM2HP_c+jxeA(#I58?Swd9 z_5@echb8d8*>{-NjF+n(dA?I|0`8-L{fK0h6}A0pS$0tamVjo_U5QYUs7HhVkaKyi zI=l#L%LS6Jh*{H)A=cu1^9~hgyK(AskxF9D+fl${UJY+8l>uY&&SRL@E%mgm~i* z(o#b-kEC*y;vCzD2j8~Qi4{&9u@?}fl?ogDh@1A2O5*853qz2CAmS5kC82#t2qM*{ z*cX{3`mj=FTo1NV$~htw>WqG@L?{6gvE zVF+{ZW62kJz1VBwaSVJt2{-AisdOp)(R_;@HH_KJMk~{if=2{9>vTG^pvwvHdTgt* zWxPpNIKHQ4%z9}naynG2;zz9GiP#|%S^W)}sWSFF`CfwuP4t4uOt+FQXj-9EY$}kW z5rhL@Kb<6O&PN1vn#jQTy~3IBF$LH~v&~s1gLn?*6J|4vB|Lq~nON8NR465xrE_i! z8wPj33}u%3d=(6^SagQS-`$}W=Q@!3G%Ps@Z~%<-^BPRl&Sd-Q8TChpFFD3a1MEaF z_IXIj$5-lF#!QMLal)~jshElEp9+Y&l8SK!&I(*md|;n7K4RdjvEGd={YUaqtJGj!_E`qWG3gz}Rj zjod8CQ6m%3iest|*{(7J^+nKu$Z7C&2R_CCC8s`)6vFkYzFspXkASFe#A zRVsYJsA?>GJ_-5MY^p#GJQ_FsIyk$ zCWYddLjEl}$gU#rOKQa8U7~qmDHMHB+|&g%<^Yd><9lsrMoJ1nnX}Vr6aIb_uF@Hp zB0KaXf-)UZFyVsrhF_J9dKVaHQw}NXE8WFghwudFV#x(asLL=QabuM~8cWlXW2qpi z?L^P3a1xC&5IhNc;AxKQL8Xs0DLBW^zwpJu12QZ}=!&mEkn5Rme<-ZUhWX@#lA-&B z=RO+#tw6xy#-&1mSp~4++INX)CSJxF)FM$im0^1v*4pv!YosI0ke4auR>$bM56>bu8*yEZ~=9u*pjlHsL(xljzNCLva zq&TNKMUNVcwE9P^LmnO7yxGFpIfqoJVPyZvud*_qI3du&?ld0=6>$f!lou65D-3i1 zzXB$YmAF$hI#^?Lq76B;q66yaI~ubcCE~Z-r0^e&%J?uI}*RFY@Y$nGH&ct)}SmIY4Cx2X6#olMc^F;NK zNOK_tNd}O#s<2TyuML6eWmV|gs z+u8spkT)cR5EI3w?6Q68*Z>iBZ%;zH`!>~Kd5I%jJPZq(#~7-?OkAV{4m$n?+?G#2Gw~}o z|Kb%yf3DQzPiY|t3mznMBVyq@QT?DkdK3NGhHb~_3r#{tjCu&bG zFF4LpDNARHG0W{*M7JrDXXDcV%aVIe)zk$>$%+EwOcx6o1ATLypHfd&?MxiMH4Z~4 z#E)^Vo&lZZ5ZO~4HyW(SpA@I%t3n*5@$l~y=$^*|78r0I40E_cuy;wc7OFkk(^qv4 z-YIZB)qM{A;PWi^Ohr&#WaB8tuyZ=y@seY8`?5;nQY!DAGOg3p*>fOJNnCqEq4oJT zG^06pLr}3|w-a-s>{ z^Pb)Nxv%Tq@A|&htnti$=J*{ZaeHdYz`N|JMg8ECp15G`oVb>2i)V?NuzMnJK=b&Z;it`|Cq--gXjw^5=6#9D9)9-b3^R3??R8 zHKXb@cbB5@K|h7t>_Q+grX1R`VSGzBrW0fjjZ?sz`^+>}y&nah{<@ShEQy4O4U0wH z&pRvJ`>qcgp+}NYTqnSeW|Q-eXivXb-^jc{qr=PZ{IO~S$78&nT*#TTyT8IskhrQ)w ztt}Gy$mMyJ9mV88HsfYb3ktM#R0DaBG+YjImGlhH!j-o#szy>sdpk_w|h zIZkbgxf)KzY!`^8EVpB?SyjS_+M=9e6CR9ryrZ|#~` zbmhR{Wr;0!Elol}UE1ji`f_}teXr4v%w%?zUKUoNM^Ve{xCtptnRWlG%hr@-L#})j zFDqE{db~^P=P0GTpox2xUa^MYYUJN6DRaRGWr(mQlN6(l1G&_{>A6{O>~VO#Gw}>H z{bDUv!;Sd3ak%xADh>4~X=;&;!_UozUa0wcce7^oVFdt-AJ6XVp77ixLBt2*9x+}-yD-zm+I%1dRBY%r?^4Z9xx*ST+qcNtp z`eXR3Kqs$Cwh7k8dNUED0SO}h zT9!z01$za@hRA#IuPm3#^%!Q@#ycb)AaAs}wCe@R$y&gRO~mP%$9RP$m&~Zf`rA7E z=AZa}w2c6(KHT>J4FRVUUh>^xP+d@q`TJ94)TST!RbSn}e-BF`5HE+qJ(2~*9F(Mf zB@>en*9%si?wcjz;>7?I|B_0xGL%sNZY(PTZ&p|UmMXWJMnVbloTm@q6PIVkA9;fP z1y2Yc(`Wyq18rN>Mc#v&8)q8f1{oRw4nj;B zvTDLa7?0s=GAnKHLai9I`)>nnhaUq~JN2-~$WYrJh8xK1VK~c_>HIuwn7{AbBAsY9 ze?&S*xuXgX{G?608;ZFxW<}>6wDhaNl0}3PW#R^Z2A>S$hIb8-gpjJ*0~nH+_I`1| z{l2G$Qhx6`Y-0T`e_lwA0Ik8MuG*;o^Bo-=1g?Ky$f*klRwi9?3rBUDGc1&)WO|7H zi+`LUnl$IWKKaFZWlYrTQBRR$8Wa&KPuN;XU2LOgvzr?jV!}^UpmzyOXiBT8^%-0{ zE@+lq88K@{goPc1fV0H7xm635_mE;VU!g8(@Hrt>WzN~V3s{w|v0Os3j*EnKA3S1yzw{^GOu?H38Go5YmG z_slsrdWo0_EHxSpSH$22)wO))WtxXo!{o#WBgn^gi>@E$hEWC+{ap!5G0JglCjsa9 zK&G3bIv}N#@B@HZmA^Ft=Tb#!oSO)<1xAQ!CLm|^PO(Z{-%K_MWhgyJHL{ic8|>R2 z;-DQ-4aj+ce!g&wYzh)J=pI0`Buc^2|JAeI4f9dKP`Qv!$8Y^*MzSBo1xA!0HX`9I z(Wj-7AE89ag-s%xUUxTX=}@D9*q~#)-hRWf4zQ^c29G zrXOo9K!zIP+ZV*NnwD_u1NA_f{(dF&cwyK{PZuVw+X77y)WcJHpB_hHY8sdw(zLGr z2eKQ=B;x8L#&h=k8P6QPJs5FWTyC@PVEDgJfXAzeDn(8W3D8;i8KoqwVr0?_nU#({ zrizC{N2lujQ=KqiX1W7^Wb9W3GzHPT(O%GyK^UXmah1FH8?6W zbCz{U#dwF#YT!kq3?Z}NykWrxpurePt=zvd$j##suy_)4G73r27_zwM^97vlp(Y>8 zThdIUuB2r|-nv-~~WV zXr1Mn)r|nx$^r|zxs|M4N-yYGtmn%Z1nD0mX4D%H+suLWyKE3onH?-_h)MaxV4SJ; z8nN8(B~EZ@%4o-VtT+GNY z&onrA?F@!piU9&ime_?}2`%8~cWD%^ z*03o#^>C8PGE84Aip2LZD8{!0Dj#XBFM3YoEhDM5RK`I?<@Le>ip z<#`lQj2q<}e}U4Mq#bn)KvepM4+clh?=9=>0Cu?VX!ea8ZERnO0}Li(;!}nQy%d8K zy+G1qDA;Ch01gT`P<$sbNe!znrLpX+U?B@>fjl{BmVspZyiUgHH_5Iwy3411<~Fr9$Mpg z(!r@8V^~F-*$Ld3773tMQD)X47;xN}Bsj;czR*H6Ax9i+QkkjqfNkx-x)KMzjBS)m zP!E`ffcl5bN|Y4zLr}M1i1!asr<4V-hOBPh)1BDEczMpd1Q6Y0*+g+?A9fGvILNMM z1H*$)xZ+w-*le~+vDqlmtlv?utv^ZX`wxX%2Dyt5*uUv)W(3aWml!e5G#4(TTw9}; z46(nv9TuC>fXK2oyVNa=pax%|SEE%(#H#)f3zM77*ASn}&t}i0GaE#;>Gu&ySXw+J zGPY&w$@)kVJ+<)dzp{SNITf>wqPHr3IHq&j=&IdZtVQX+QYa)OG^s?yf} zJSiU%dw9ThjS3YnxPgm)NRFJ6^PJ2JoDz)*GDFib z@Q)5ui7T*?sihBupjXnZDK{_`reS>kJcD9Jx6b6+YJDc)CH^o?A3tBHhbT3LXHiBA z!3SAa?6ii--aVG1s=_#8QNkiRTulU|BkFUpbyI7Wln0`@!$MMDOLf3^;M*jRb>O!$ zoxKb~GC1+g=RpMEtxyWPn1xUp1b`Ju%tVDuVqab}{D=id)|cQH6XVworUScUW!Fo6cykx_LiQp;sHEmuQ zMeHY~b$U{&4qiLs#;K2=OUgowg=SV{B_kchKf$`*a{5M@tEz?1*~}j?n*bf28s0W{ zQ=}!1B0t={fu1}N;Rc3pQ3_+5tiGJ2<^LnN{+5z@*HzU~6}?N)5^VU(ST$5bDE|SZ zY(k1kCx=;pwU8+#1jrXGZ3%T2+f#G*{%n8f_)xSz!}_9eikCD>*;p^BBe(hVJJ~yi z%+s)y5Zgu63huAy)BYz`Fr_Z5JRa}feGZ^FMRnZIM(u_aJRR|K#yKUhVa-Rb^#n6y zVFxU+6z4Nx+?BZS!yByD^)UWozB;GQ(XsWv9dzYR1GLiKbf$Nig7=cj?t=Ra3S;{M zK!c93G5_#L3@AjRh0GIG?7@0&SPY@S?85PeStJ+Gk#Wn6E34JZ9x(rmwGoKwZ8TLubqj9z@j}26Fegcx z|B!Pg88?SjLMw{lJa|uDm5$W!9hNfSn@rx8&EkgRIKG>^5T6?G08*liNJ-9?4ZVu^ zoT_jGQ=nUH*-%N@OVue*&7ezP+KZq>@6!}pJ{-Pj7Hv|UE{7TdmC>0n@F zT5!bt=P7f(8XCZO31U=J19&C1R9r&af0w*P*G@^D$hxEyVz#!l!5j&H1>aR4~CR zQj3YR@0f=pks%-HH;a7f#cRszkctv8?yZqfdlDZ^JXD}&4sh039m*+HDQKJg3QCyS z9G(wQOaCD30rf)~gsqY}p^js>V`eyFI>$D~vf>^UGJITC4y#vCWW-N6^0E57->AH-!LFlJ?xdjE<$XI z5<3y^ebI%F8E_(8z_Uvmd@>KblXvW~E$0s#0R%!UDcucan&396Rg{^l6~b#%d- z!`O~EF0YEqOp!6iZqFR22i^DC3=|U!R>Nouc~e5!e)+8o^2G?!5Zo#|Mgx2s@&L;f zY0tfurfag~;97Anzr?Yfn}kKy2e=r7X;G0&-(W9hiCZUa;m(!h5`}Gy zlZwJY+6}NawH^}`7Kg^v_Q|1&sQ|?7_0Px~QmM*{$%jTD6(n8G>MN}{{A749? zSI{CDNqU^)ldo{zF_@FQp>@8t`AXj_c%_76!%VN*Zf&dYfx+lK5?}>>O~_3#J$39n zF(P?I#B-u@FDvvu^T{tN3ndwKsRC7=xLnuIDo#h^_Vvh_HiCv~bJD=m6~{qFF&^pg zz~m9W`M)DeKxf&u*kr*15SP%wAfVb99e$) zEX6H%cekv(N6o0Go@j!hIoLnp(xEcIuLremu*WH~TQ4!Pp}nA5G;v743o@&P#R6fz zS98lCdqf+d%vqfmz2iUz?j@Q@c;+qy3gFoglYl~6^ZQeyW^m2huljP@BXJ@&&b`B= zmV;*p6yR{|V~3UZr!}LwINmv~h6RdN)F`~Wh1heJUOoa+-p1&8f^RhEJ~x_Zp9L%V z=9>n6ZltQ4`}#pK_)yyAE_qQ3oqfD6NqIu4B={?~2SJjpWlz5sr(_Q`78I}K-^+K< z(=z|U50r={R$!%_yb#6~oHF3ywA(m!Zc}U{ehoYCPtK3rqr%ZR{60pqcaBgY*Lt6n zAt0bVIz?wzh>Aazow$#Vr_$(a)F_I4W0fNoI8~A;@txC&(ds-xbqX0$xK+a?&O<@EvBEW>QO%9A>oSlxP8QgVujvXLWRWEfr z9M9T@RsJ>ND@M2X4%l9Vr+);T8zl1&{d6hbq}dHas^v6sS^`Q5EGYZV)_5ND4ax!q3P|=CCO_N%?ZpI#sSxibsrbG78VdA|e2E zB>&O;seUc0V!jKYk14W4YFy271sreUOpdIF>y&PWW&If5aIz2*h?&U{Z=dJ@p;YI4n#c0RGhCG!7ZnUe9{C z8tTAfV~tgqITzLqnobC|cq{X$&9&ruaW|R{3_6k`DJ!30^FZEAU#MTshz;f7IsV20 zF`AEr2h)E>PLf#wn>^1R$7~G70qK~*thh`Q7P88bsBdN4s7SQ~f6x?Exnj@|B?@uL z_47iIcmJ6R%XAwdjg0vcHiFFGV46{In1TW^;me|{p#C%AlY)~OAzMZE01yODSj;#R1 zg90!%0_D>NnBI{>&WUXQ;NhQ#ho--ue|%5;-+3%T+FEx{iYED3KQM7CeTc+O3t*t0 z(4b$SqubeR^~|^f%LX0vvw7sycFiBcF5Fde`GlLiE&+M<$MS^|Ot83Qs_e2nlGu#Y zQS(T_il)ej8ah~w$5!%7;egd=1#Cc~9+O%)S?U4xQc#|v$F?Q*_1HvvXoVKcB!4@m z^f}IzqZua}TWknLB8Xy)$c?~VnhTPTG)(uvPfc zEnWbM3Ud4qlG8AhC%HO1}~y4)M7(WX$bcjEo!O2k;Ajq1LwkS z!a0|vZN5wFB4WZTuu=7@DlZTso(EglPqQv)$OVZ7%TQO%1^+k*i4xIG~#EeCa}_pUx&^ypx+5Md5jStmyFm>o!u83j%30J zikW_$O7@%LBvflrih+`6$tqIh)>0{SA9i17Hk3A4MrL%AQ7x++r?mmq2w)Y(H{u`> zZjJKsh1@GPA?H`WQP_|O!Cojf_l_;0yc@|Ec25Fy1>}Y*s=Bf+ymSW}mXSo>2d8tCpVP%V= zf7l~G&p3Y6>+a+*e@adhI3}dfdnV6!byf$d!yUvsoN3p3DWuQTC?-Ef7ay<+v&3NY zUDu>`KuFY%p3bl~?V|PYwF{PH4|9%qG!Jbb^xz9M>omk8ObHgjP_kx66Qnh=)_#=x zSOwy{DpjLc8R;ZBr-3`!BN@P`Eu5;y_rDcBwbp#FJ$wU;m=)G(YmDCeFp%gXcnufU zy2`{ivO2&AeSq-hm5zL%WLk1M$*V_>&Z0Va%D{Pryk<+FV>6bLI1v|AB@O20-<-kX z-l5D+BwNY3H^{!kXiWO)aBp=DM3Y(lf}q9SGX9O=<*7B&94KuzS>igyE!0I}+m_^+ z;H23NQ*Z1E;$5HObM7F95lSP96w9o%V@k4UtVD?{Z>8?a0(NZl&=#Cv2O6}yjdiyL z&sKy5mJL=Cyrwl?CoW);D<}BO#F|UeR@JDPlXouX5~5jv@dBELpz6L+yZmmT)>sb@ zo|R_frx1@fw0Vlz)CY^Q+Pp*>;MoQj zD@41F=GWM>ze#=TjX@x@}%!~}MM~9)wU(~OxDW$ZSMAV*yA6D(5 zU646^$bYG9+sY3_T@>%skmPfG!(>dpdt+wNsl6s_Cz7nyUW)j+ihQJL>k+>CmCn62 z`V7oHWi|}rT#KYg+f8&0nL#A_t%eqlVjnfr({wK#IYAI@fy8;*+IngK=TA4l7^@KW zEo|tCXLHr7<>9eV<8Lg$bzVV&#t_?@yYZon?j`@))w{v2s3jq^W6iDBWa{bp@N_F| zxVtGV(b{VqApUxO->_lqe`6HGBg@g~wM|c~zrwC;4~er4Wg3JtmJ5-BjFjy!g!Lr) zmIug>=H?U0M*3%2KN6R;YEd5+VHZr{d)xdc#uT(NBlP47mQ6QgmlfzRtayk+Ngt%> zJ0ug9z}DK*^Ni+;TznetowJKQH@$2HddnYsRz0=c9?`T;09WJ%T9&X(l;HKd8edzS z2Mt`@o10e6srpC@{YbZSpiN_Rj?6A(I%Vkp0f`Ctsl>&Ty@1!q-v*qal38@K?6zuJF#@infYCw;H;-KUi3c0f{pfjNE|>JZ#(nUgg|K;DC8x`E64Nvo1tOsUDy!cq z$;rY;iQ)(Bv=sIcIrifbg^ZKatGshzHl5MYV>j+Ur@Gl)^aCr#3)#_bBUDJdScsU( zJo`)5;2Da196gmM92P=dy#_A_wkgX9#;bot$S^txF#b{@)M!XWY0#->dua3h<}@1^ zB6i`J)TO@R^gDc2Df2+V4T}pe9jk!fR=9l+@yDjH4s}QJKyUi-BU zyj|Lm|N$t0;%zFMXs;Y|ogZhN@Cqa_Hm-O>A#Q8_X8 zk0YWu6I=s+S$?HY7`(&JM%iP=xvy%F$ry+TUGk3^CwDPeL^>D9XJ9k`!{i(RqB0J|~Ll zegK%e)U(}0%|e~md1jjZv`e=xof~uId(+ki%LF$FD48OoWwL|MS z!~#PFEPR$;Wul1AZ$AVpAU6Lry2bmbyf(TS)NY4Kor3pr>B? zw`yr}5vomw7V|gAVWa~2x58#jg+TL^=%PZ*Vh?U}$L8X41JW%Pyol{R_-HeJ*6Bom zde9^Y>Na^|MLQqU)pK@VG^4m#uS2LeDYR$!=;dG(SI&)7$ag|3nkE#MlMr^Eue%O% z&nJfawbe&D_T-D*ETj?iZzX zS-Q6H1+ut2xD%_YgQzf6>@Btjjdh)=cSfWM6F)5|UFO<68^7lFl>3^m>a=BRl9}Q#oZ-;=+tp z7za<7^#91eKs0KaQl{S(qzN;OXd#lR)maC4Jc$T9Zr*rwjK?5hOv?*a3(6+$iR^#9 z!KGxa#^^WS%%y_Gh**9qOQtjjsrts)7bl&zw5lYC9ij3M#}%!d{3=wfxcs+P;~6%V zK{5!^UZ7~3*bu%9XP`X94t>(*zi@0I!4!`TMBbQHN&KCGY@0rNV54!eY1p7IPLTOj zof!(e6(+3lBMr;nG7->7`WH}FmpOYL z%{HAMF!VhCE5tp3`aM{JB2yPpWef1}0atPhhk2l9qe98f?z{|0l4AUSAmP6~Q-<`} z_2tjw#mc>+8g3!{9~!R!kCEz;cMdtMKJW{ARn&_DdlEx*j!23jhQ?=$Jl~d^J~iX# zuynt6$ETJt%32h&kI*sF+p7D zbkmrGwU#E#{O*wFBzv%~i8@UQcsTgK4_JyB$&Vj4KA6mX)LJAc_JvXYj$FQcay;>5 z#(yfM{mr-@`I!ARyO1N)<#YxNv^<-sp^%P2nLI+F=|!sQfn@DFy4~Q`$`Mw1b;U!W zxt^cKkjR@Dz&M?Oa`P92h5Vl$2K@fKM`Xk=W71SPF7LOaszwi-g8Am5qwF7GsW9G3 z5CJ}u*54qXn~0AXc%icgB^mhd8_56ZI!f9^fn;HyP|W%G6?ns7a=kK`Z4O#5KXs2; z|H56K;U%XPaa7OZ`lGBhg-Q5llHNc53LU}MeI3iJ5yl^ccn;&mT*yE-`qrEa>R`sg z3PGseRUwf$`l;zeVeb)yl%@O8WDq8l;l=m(tNT|2pozw?0W=*s&*Z&MNqN*LV)w3q3Qgz|-(vJvhYX}sd=uCOMJ~tN8Uw;jgZ@$mbixVUHy!^< zjYX6eBYTBWY29AW%65(z-RE2w)Pxm12GGl|+}09_MNDrG+Z z3ZeaOqYR>fh)p1h3A^`sxV7tu0Z0hLL1rT5)&5VrRGAmG9L~5twJZP=_olk(BfNG9 z1EQ-E;q$?W2;U)WL6EzEIqg}SvWMzLa<02R`iqm%<%5HhD|QJV1=k-7SO>*${0mOH z8AR?qlmGU56DFif#?psCeM*Y#2@@|7Jv6wBB?wfY3u2c0pNG;~QH`_)3m^0EZG~%0 zvXy%&nVo(OoQDc7!o^wH?Eeg&aYPgrr3l;xrx&SVr(L*$2#IJh{RX6LJJy|iZK|?f zq}ipb{EL141FS9WrSjYX-=nn|zo&j^;z{{!zzW21J5XdVDR zy+1HZGXj)sT7K0YA;WMq@2RQme~2wHeQfzr_?x>>`Qc?kA7>Cq$G9#?mQ=#5r(zH8 zR8rW~kdW}_QTRL0Bm94dD~#-WUfp6X$8Rj%z!f|${xaQ?@_^cs2@sIKkO5}2Hy|Bj>HNr9CN-PkYjKjnUN~ZnKW1S)ni*;?1@4v12ZeGTvbOp26+SoKe zu5IKO*stX9$)COt2+XhC`QL%W0*SYRdtRn}3ot|B2*(;5J$UB(_8DKKBLpOT>aVle z<+TCBlI{KXcp3%QyOjLhe}3y28Kn(Nf(n%j`lED|g4j0QsidH0P|yGmxMy!h`;Z-? z{3v^Dc1`S6#GDI&ItH+%7$t6;2&$zUL*+JRwWp$8D>Mw#jrpS5oI4NNZ(Se8^<1;@R*jqG3@qmA5cUV z)&?Frw=?KNLkVCJPuFMPg|20o5NBKuJ$sv$mu0SP;8AHSV!*`-;nwg#9K~$Ki_6Dcf7RVIn9Bnv~B+8e<&wVZFH>oT7e`aHL3pC`he9f3AkSTB< zevytoT45Obfl_qe^j?2-`+y_qGX#e#L>xeBz%h6gYH)f9#r31T&TY=N% z6a4)=g`R>VNnG=bPX``#7zm}1)&B&J9yJ#5^+SqLbmg#P z5cU0E#i5E=DHE}!P~5dJBmL<2;ks2lu&3gf!Lcz=;gO4O_FS(`c7~^_bdP8~DsLhg z?`WNCopUa35Y|cY%J*Rik@AjR=?pjmqFpBx!~bScE<@wFM>wpj=3hDp|PX$43r6!gFii!*%q~fn2NQ$pg zJ(a~ttTkc6C&O;kJg;mFqM)E=ya#X(FpIuL1c16Y&Sm7Td~?p)@J|>U@c>dB;iCI} zIiz^Y{iVksUx3qcpv`=HywXu4VuJ87?u-Xi-k(Ve!Js;R^};A6YW0|fC|c}`5ycjP zP4EBgei9Q?b4oQ0v*e9o4Ew(63!#H!*{mOeDip+MnBj7!TJNokp3je8X}u@XwtYZ{ znKrHJhW4h*c>s~#C{0=R09{vNqo6=hl_IjiyxjMH z9gXy0OFL9uO>4SwMp+c*Gpo5{WXQq$AHn1yqdPTJZjOW@Rby`uW+53SOo;Ar=9hx7 z45tFn$?e3y2afXh-YITF#^>*o43dd-tJ&;2i(b1muA_fqbAx`J(d=V3L}SPF0bV_7 za~*)C%AX}cj5-a6pxlM1M<)?af&4rSxc$|dp_S3_gyn zv`oELecS8oDojWx;`7JtjM7-($!ERBIrxdhl10u%!=tJEKNIwPNwQ3Y8`GaBK4zT% z-+_nxn%TTU=EYM-K0^AWpfk)Wvt;`EUo@+aBM{9>a1ldq5ivd6xXAo)SCK}NZ>+^o zq@ylhzXd|-<#=ku#OaSc+7=ZSV@Xes%8;%!T31rC#`tCAk@W=jD7|~G zn$qisIDwH(A(NoxP`UAIohr;0(&2240WoIo2@(-Ko=rv>05J~Sw2Miw*er9m*!qim zMJj4J%(r)GoT8oBaq5>c$H~|#(r?uK!}>83h~X~Z z8Gq)L9@aerF%9|gD82`Wp7Yny2a)6~Ce!3&3+yO`6xWiUP7WCi3DmpK8eUV5T;(-9 z2xZnaJv*36gwhiKVO9xofXxuR$ZEX5Y6?U&T|V(Gx#BFDrEzgP9HoVw9EX`BGS%wO zu!kJNpyF{za32*IxdDb3tWRHK6wzmL9n_wm&Hm8UDjY}`rOwLuCeJ2fAoul*UkcN#cZ~+HhhewX zEC!-pd#b?p^C(N%rP8jsjspV9bG>jF)rQ5hSw7s_b_0}(pRu^OxJ=D4A7#~2W{%MQ zamN7$tPzSeLt_cHCFckih8F>Yl&(xw@U z7JX4I{Yo64wi)+SY?1Z`VWklD+F>f>&uyiK3(OuJo{T3$XC0L^$83k?P)VhDntPFJ zMyx9(9 z-YN&b%cH(mt-S3<{JQ$>PEUKj;^JoZ&%#+{zHz}Pyi!43qDqJRziohIbQFA4FxyOF zQ2^)Wn*VW#0!9rO$Z*|hYthU9jUZ6yy;GM!YNm#&1H)?n#1$y?#81ukk_G<_qG3m- zUIxb}Cy@up{8##b5NSMJ zk8NxH!_aNC#qXP&7v+69Ip$)GxrKL*y+tM8(piGJFYv^a%I0OcCMy$nY2#({eP^=h zpXG=PX}R=^00tdyDW?Fh(dh5^TBEM3G@qn81mclnLFLO6H#(%RmPgD$5vKU^n zpc|y#sF>)MLlQGmWI!07eqoUMci=D^%s>Ip9+Cd%uEX z{$a{F`zMshC|aGE%r58b?5tilQv1=aU*vt^Y7;ZDY5eoQKc-~;e)d+P3EUe+&6c?E z#z>SnmYy`KPC1tCDE`;tMj%L~vQm{K_2sR?T$(l_e8^*Vg{^qSX0qo?$;3YOf8`_4 zQqXs&c9+~&Em3FjYBxtkJgiFHL6(-Pll4E_Y#JJ?I&l6q$bYs3(*MVx>hD>{e>IH% z)69T}Q~%V?--!CZwo$;3A}>V#ESmo{>BT}k)xHyRVV@9Ex}b}W;p06GRB;uvEE3uh z4I?dVOTe8!vP}uA%tscqPe1tAolgCNB~LtkIM2RLi(U-xHg~?j?9{EZRj~D*%F&PY zm@lVUEQY>9D22xUSjIS)U(T%V3y#~;(gIa}odVSTK(8HBjL$1w7|%`zX33=7fN0~Y zCe^MuDI+r}QG?d?&2GDU=dkOi9Vv~~UekWt&9;BWsRpRjSav&l>jQ7Y@UytiD6l8Q z>?pf+*K#FITSCf`X35FO=y(<@@%;8L2F9z zN1rQ(1Ng!;R0DVa&_jKVrUiCgia+&-#ABm}AUQ>~v~-%UyDk{l^N$Z4z~ z8?d4Z9UQLdkTec(Go5OWFi???d~Y2ZjPIMJDHUe3h|ck!><2H!VZ3u`ZP zbNO$z>zsTmRSj~jD^<;O9~I4i%T@z{Ay#wsSN_Kg@BQy8jhf6%oo@3hJhvpmjn)9I z($Zr{f>2uf!ornKd9H&$|MLqLTcI)w(fS|mNc1YBsE`?Q9Ob4+e#PgLo_awUn?!$$ zp&r|ItHtap)0cVNFb*WacDvE;qvx$ibFB&YcJ#92@r}@H%qWb|kK%Mz3|X)BHa?r; zZ%34vkFDwDxeZ3=v|DFyHQ~dTVOT3ilk0J z`Z3dM_zBMFJw+DV)sOhiDW|CJ`>YufWdQx_+VrH~|Fk%XZ}~YtC!d5?f7Mo&mRaY| zXI(8G>*bt@EFytryF)9_RgHB5M}sCX%caFvuO%+1!)6IHH#hwKSwcR}Z`+$^->Qz? z7Cp~j+BNETCsZ~f&C7s2#3dh0e68NGmu-gM#>@P%a>=H}K1;9lSHMZOH}Fypv1b_H z_E9NU@6@X=a^KgIOl_bg;j!1p)epA+sVX=I)=Py}A?N9rjnXpUU$Hsk#AZ~6X`k6H z<*Wohf4V`v1F`LAWfBxLXJ|jaJ+v%k!68}a(2(QiWSmtF+OzO2<*fbryzGW;(=&7Z z>@E8-<8vLK6(NTrMPlk79PEjK5?`3ly=+}oV(t5fc4^(ZS-~~6P?WIpIXU z5)v}OGZz*`Y5-@<hnsMJ1=k{!)JZHrx&);4v?lr$&YbBah zXgDs^WoU@3kVX@S%6ZI2qH*&@F)8w9EE@G5C1zo&y(~xZ=Ju+Vv+t?u^cr0}6?Zme znzgOOdkzNSsus`v20$=PMoQzl*-7>~tVR`AMcKXyACq7?hPc>=&80tkLYGMCQLye4 zj6};5tG*6ZLJl{Vlx(}12(lyX#H}1`GK5S?6cJSp(2<6nDYKYQf?)o9d>3^j&&g4S zA2a1XlQZ8F3F|s`{ifCPPED0s38^qR5X1f0_k&z8k-m*I%b*@gZ#aWg)m3czB=G>G;w@61>&H&=!}vNCApI_qp)sh_ zEQqNz_c|bp#?5*XF43?1J(VfI%}NmLR?D}t#CAXVV_NVooTEf$H@u4ELK;~%deeff z9`B*c(>wu(jWb3xD(zVKO8iSgWpZSJOQCooCI#p1SoHZ^LlWf*Ux8ibSqyF#=W;Tx zolKUNxCEdrd)q6k~gT=y@(@lN1Gch!k6lBWMaylJ=O*C)0tcMtAFOFvN#r zz)m&*g2CY=`H6mqz?pji^T{S0^4(!_CM|N&ILpXk-Ky;Obh@^guy40uiWm#^@c+Dw z%l6cX&}w>ZLY~aL)VNQbpU6x#md*mMHe@{c|1tH>VV3p4_V7&Gwr$(CZQHhO+qR}{ zPTRJoZDZQ~oq6xQzvuh+Jg4@qB(;-NvTCh_XCnk5^eO%3;|f<&k=_ifE2uteBF`;0 zjsItRUz_@AGm4aWD}btCnHfS#1bm$BG5DXo)Up&)t`Wzj;bi*g{sM&bMfZV6B5OyQ zk$4%L_5XN|`EIKa#TusM5@L9PnBaz?plf)Zgd9CA!`$;Pjv_z{RqIx4S2gQz1r9wc zN?EcDnJv_n$t>?NYK;Ex3ADhqj(Z>(BCzU+sRG--_B$A}RHrhT{)=7tp>Z>gJbv$) zpOGGAg&9R%+z{a+5r;xDVE`8aJqKt!cVwfMBj_bPtjc3>RrQWU3>LFlfK>EbDs3uN zX5F<SZLy;qVQh z#Wov2s8>uZY7Ia?X`eyp=O^4kx}K{4&xe@LGB(PybwOfWvJBfH?|)vKChaEV*B?+6 zS?~lvj&3uY!T#5sfS9$9R~=!kzSQGk#HIPyDNLo>4l3?Z5Oy(ps0Wk)+yChYZD{=o zU4V_OI$)j$kRoGDJN@4iZrCp&^fwLw#@OaNF7M#tY|P35RHx}(KpHl0Dnd(rB_l+O zr^&TDUfeM`{f$;?CSk)^%&n_BeYEMz>wnyL&vNhBZK^HhvH(pj6N6H;))}zdDZbLa zgoD?C4BVh+Y{PC5uGzjXQs6XEz|QY?yGjGAwC+VT9)VE+OnPg4xEdi>F~Ke1jUp5! zGZo0@8O{Kw8+iXjq-jHLBA8Nfr75obH#`1w`F%P;irUCS@dpx?49s~ft>W;B3>}d_;5`wZLBOc7J&WLJPU;52qBs`P(Z_!1?&I% zkkOECmKa2Im06oS2vZ;W)_hi_2$u(i#XNw0FoOPn{Zy7{YA9M&JL~_MnfEET){%7c!CXLHx6~ed)jV-(T zWE3|UTY*XP*$z->kD6@S`t!pto0*N576NG1^$Av66QdQ%E{=FpVM)oe!9$1auzAru z+3s}po~%2Hn0I!Oeu;ih&`*4V>&6DKQ2p~qdSyAaa@nf=MTBSvq$XrqW2%-qdbAQg zJXUP*ukRLbcmr(4zxZpgC~`Ft-vZSO!axDvAKjl8Q=m#y9&a}88WxfaPr}KRE2mD? z;>lw%cgg~rP>9*oFyi6cLsWy4cSL^~Aind^!LNzG`2i2Bvex90CR)lyAIMe1tA`Oi z%n{bM5E0Sk<(S26|6fNwGh$;>YG}!@Kz(Lhv@5RF3>(MYthY!}LR{oQi&SbDrbv)H z&KLrGvb1~7Go640PaY>RG^*8$ebuANtx=iW`UCEhG<(8XriRR;OqV{LnbUe9egLHl zc_x6420tc(F1hB|K0q{IUjnT1c;^KgB*21Y$a(kMs-=qAx@4Inz{tjeOBA(9(p5LHuyC{I>03#AZ9*Ppop{dVMYqoYI+kaM)Uzkx zv>(Rx&HWe!(5mP1KSW2E+gWrPP^e{~+miY-ujj3M_zs*$9qtZC*WD za^%4UiZronas^4)s{C@C!!4I)WXV`qSOi4VC*sB)c*q=b2c}K$wss1iKV0h>D!LV6 z=1+;Tr`@b5Gm2&U032uau)of?j>9+y>FvS>Z2<$~I})VTqDS44GDq1y_)@#Zo%mRJc7@N0#HUoG$_eN#toa z9*rZ<_i;KHnclKv%hJ_d-`i7MzPoG{osX5}E~us^$r4@@B=`Zj0@J36H`m_|Ew%T< zE8MXA^Bsi7&Kwk5;&z-FJKZ*t8{%7WCy&1wRDZ`qc|CrNt z-*UR5ufxMyJ09LLVw}Yj5CzrTAB5w-|A40Baao@LgQQt);#rKhn93L+fA&Zz0+zKa zS=|1I>xk#Fa-~bJRjC>`;X!PxPWls<=XIX>Pp6xG$K#>D|3~JHjhmRie_#GoMlvUF zpj3IR-rszSNW)mMN2j1i(7HL=C#*V`l71G2`6~*X;`ozL8}05WY^?8FySH4|*HCkA zqA{=vjCW%#lTn?`pjjkm;?;)QfIEb=6ab%`Iz+RPm+1oxG}j*Sk)ASgd*1>#mo ztZLk7Ly@E{Hu=Pt&1a0=#3meQL0l5@EI%Lp5d!mS7$yA=aL$O506F zibZibNvLmE@#?`<)}nbA1lwcyFx?sk>pEJAsAQBG;<8SwZp1cPK15$(sJ8|IG`#Yl z3zA}O%-=-<*SD`8qZzLYv941MAvfF0YkFt!~2E_da1oaCtYUAy|foeXO&Lm#Ai z(dNl4VF@xr9XKWeCF_nhZm$?fkQ(n|nawhs_pk($5;qsSv8^6IO!$#q^%*YOF>CXT zIk?F%mk-!GAhFbnz9T%(kaSi{)rf7ytdu&2))kP={Rs~VorT@~yYw}IfdzSdD{SSA z`W%Fe+{kJ_#{HmPF!85`>r8A!Apw-6X$+qhEjq+q?esK4fhk(bxoI)V%Dav=F1a}0 zsU}S&&WF@kzT;dzMwUEcLwS&R_{R+c;|n*};(Ag6>jPgoHbEo}ucBTZs`dz_CHr8| zY{?=qG4UZYMoLngbdvW~Co8)DpB~8u7HabJ=Zls%f!?j{ZD7Yo0NO>b@$geRov!Bt z`x~hF41XiZuf%3O*=6?9tHDI0=ly+ z8^5jM1WSPBw%q(o9`V2L4&?biT_We>1=qHhm8;p2$7L`$iHk#J$k>gKYm6FvmQ~et z-PTPlUL=}qgtBl+W7Jag)rzY}Hp7w0Hn?kXE%hC@>Q7Stz|aZl#s_g9x^&p7jy?LV z!E`ougy2`JTNg0pU0z*VCLmU>fCPvn?MG8m%H$dxZ*!>IWa3nTFO5YiT??gXFJETC z!Dl~=!fQyrqiu^)bwHY=G+OFuQ>8Lx#;j~DRfMfN)%)R317vrW0?K@$2^xs6JW)6@ zfl6cSBh~bE*It1D`zOU|5@XQojTY;fob@>B$zTJ=;V9H4rN(u-m0URkU^5KV&DGW7 zzx_f#G1pJGoM&JEsU)n}y1Tb&DlD!vmk+!5-1y#P^ZS1R&Sge)uNGYtLedvMNlIpK zi#P;yb@%F?jpsU3;%gshCgWG{RVr2HZ;$^0BCp#I{sCz$&(W(9Ep3$L&N3=1J{o|T zKZu1d<}Bz{X~$o=T<(`i39-;K7EofZ2vjOWhJPleC=>}ypcGoYCI>+!h6Q^S+3tld zn_VHaSFElX79YRCWJ9<(s8OQPghNHCTT85&Q(_#fGw!qGxFlwf$;vHS{zTOdgsDrT zP%%ufjm_?-nw{KLnOwxiGmsokjffey)`6+ao?L_DeEHR1+ML)3+;D%LvE1yZS` zl9!{hG0d+Ag!_{Um{~LPuam5oHH|soafULpzg4un?<5Rxw zr5DY=`)CAPj^`|eR65=mxiF}|;LXh{V5xT(vtdDVdapOBMbSKJmY$i;IEZDT5+oAL zO;8S{9T*9ElcdX_RA~Xb{A=`{VALX~pmG^Yh~g6Q0GA~_t7+8sCxo2$L?bXn;!q)q zYjQzx72SqK_t1}<`FX{J2r!<|APbkq$$PSUwP7c9b#zVPJ7$>P!ku%jB}1LjVhR*a z+LR*PXd(K-Wy+@JKEzLz6E_o4T(VgdxZyY$5yq8qJ`3YSe1K;4EdRF;T{anZ#x3*B zcmFrLu^4=GCT8Pi>tB7(qg3`?pNBZyE-!CybsGIAfb^Q*Kwz|nf#WV0;L7lyl#IHr zYbelK7$O5LuxY9`072?}YhyzfSt7%iSoW%+)lD6WIyN9CL*e#(a-=8R~KcbT&k-{A-A2(vi4;H0n7>r&~4**mtcu0 zk9$>4(;?dCaGiiwg&ye6m@< z-2OOZ5)V#yno?!Kf)ikp(5kL-LIUVSEAbH?P3~%%T@#nq(jw2EmB2k-4<C~d($G}sAIKKa7!{~mb2Emz8Hk7})1#jL-$4Opsmn3^d0GC7S7MWwP^%0m~ zlGERozVPHCLe%92k*exIJ7r26V22!lNd}l z{+|7C{;s$3nq%p{x2Imt2Q1jgrI2w`Yo#6)8k|+JOWa;}Sy|CotH8};%i>`yO0G06 zoz}++&G$!g{*^xKO#tE%?|x$Vw*}XtbhcFO`sJ0Z&q4NX*HddYUFT`hq8x9JMg?>W z4OxH?LumV<9Fg*O+6u<-ME!D8Ar|5em3(VBRwV||lo52EqM&GX>mYTcARuvPo7bYD zDL+aO9v-t=e#{aaOdUbOYQOw_+yDJ_2&DieVz{!Rz4MIj$8a& z-Uh=(R{nQL!4cQh&!C|yIG}(R9ho{8c4*v~%Suw+<`T8iZGN)+azs|(L=B?7jis$R zwAJ79Fu&5;rK;)GoS9Y1mg0z%Cs^1h>+8#fz$uC(_#hSbBBVr!p)O|aJA(tVKT;2Nl;kY}A_&`xh{A z$Nf*VcV~{2&RCKfWBEOoI~KM~ZL$PXBlTIQ=*I6&ni90CVmsw_>qAJCYu4ygr27e9 zRJXN@qDMCPo1Xvl{tj4nMT!G}a|%%MYFDwE!P?&a5^3&LUGBXpAJXJ+4=LPNrKFt;x_HWI0CiOOSArHCvmhEdub`GG@cs4sR9{6iYVt%$2pKTcGPJAoolgC53Z3*oL|FIY0Z z&&WxFINEq6yJ1^50ZtTED?v!lJl3=7Y8lv*Iign0_i*nW+0l?!2p=FUPro(KpF+PZ zJUW}6#kTM9^9Y^%xcnhhtFGF8yl~R#Ool9R?QzJs%gwIocjuvccA(bIL3D_%lhNTN z_E$Zw)dB3<1IuV+t>eS-^vC4!bUt+dk8p**1~)WmP}ZxssPU6#o@0dJV{DLN2)z9P z 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%0Da2>*7s4QWQ-L+-j5 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

;8H*W6bo0St=p5*o=%eya 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 z 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�lQLS# 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 z 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

?G3uf3lw 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�`gl)5KmX9Jr*&CLj;vkG}J;o5`z$~3)4 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%0A5v9OY&#@ifrOLM{D0K(89VHwFc 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 z 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 z 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

jVs){ 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

E(Z*JZ+X;Ly@G>5Y;(g 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%0DGpZoyC;lZ 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%0DTFCUjF4yDh&94eA#AIGK!GNI} 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

` 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

-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�C@R6~ZR+@L+$nj}7C?Yer3z4wY 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 literal 0 HcmV?d00001 diff --git a/docs/docs/administration/jobs-workers.md b/docs/docs/administration/jobs-workers.md index ff74ea4673f7c..fb5ca7c059165 100644 --- a/docs/docs/administration/jobs-workers.md +++ b/docs/docs/administration/jobs-workers.md @@ -52,4 +52,4 @@ Additionally, some jobs run on a schedule, which is every night at midnight. Thi Storage Migration job can be run after changing the [Storage Template](/docs/administration/storage-template.mdx), in order to apply the change to the existing library. ::: - + diff --git a/docs/docs/administration/system-settings.md b/docs/docs/administration/system-settings.md index f92bab4938921..9f35ed1010e2f 100644 --- a/docs/docs/administration/system-settings.md +++ b/docs/docs/administration/system-settings.md @@ -104,7 +104,7 @@ You can choose to disable a certain type of machine learning, for example smart ### Smart Search -The smart search settings are designed to allow the search tool to be used using [CLIP](https://openai.com/research/clip) models that [can be changed](/docs/FAQ#can-i-use-a-custom-clip-model), different models will necessarily give better results but may consume more processing power, when changing a model it is mandatory to re-run the +The [smart search](/docs/features/smart-search) settings are designed to allow the search tool to be used using [CLIP](https://openai.com/research/clip) models that [can be changed](/docs/FAQ#can-i-use-a-custom-clip-model), different models will necessarily give better results but may consume more processing power, when changing a model it is mandatory to re-run the Smart Search job on all images to fully apply the change. :::info Internet connection @@ -113,15 +113,23 @@ After downloading, there is no need for Immich to connect to the network Unless version checking has been enabled in the settings. ::: +### Duplicate Detection + +Use CLIP embeddings to find likely duplicates. The maximum detection distance can be configured in order to improve / reduce the level of accuracy. + +- **Maximum detection distance -** Maximum distance between two images to consider them duplicates, ranging from 0.001-0.1. Higher values will detect more duplicates, but may result in false positives. + ### Facial Recognition Under these settings, you can change the facial recognition settings Editable settings: -- **Facial Recognition Model -** Models are listed in descending order of size. Larger models are slower and use more memory, but produce better results. Note that you must re-run the Face Detection job for all images upon changing a model. -- **Min Detection Score -** Minimum confidence score for a face to be detected from 0-1. Lower values will detect more faces but may result in false positives. -- **Max Recognition Distance -** Maximum distance between two faces to be considered the same person, ranging from 0-2. Lowering this can prevent labeling two people as the same person, while raising it can prevent labeling the same person as two different people. Note that it is easier to merge two people than to split one person in two, so err on the side of a lower threshold when possible. -- **Min Recognized Faces -** The minimum number of recognized faces for a person to be created (AKA: Core face). Increasing this makes Facial Recognition more precise at the cost of increasing the chance that a face is not assigned to a person. +- **Facial Recognition Model** +- **Min Detection Score** +- **Max Recognition Distance** +- **Min Recognized Faces** + +You can learn more about these options on the [Facial Recognition page](/docs/features/facial-recognition#how-face-detection-works) :::info When changing the values in Min Detection Score, Max Recognition Distance, and Min Recognized Faces. diff --git a/docs/docs/features/shared-albums.md b/docs/docs/features/shared-albums.md index 2684acfd9c5be..dcf884bc9bbe5 100644 --- a/docs/docs/features/shared-albums.md +++ b/docs/docs/features/shared-albums.md @@ -16,7 +16,7 @@ When sharing shared albums, whats shared is: - Download all assets as zip file (Web only). :::info Archive size limited. - If the size of the album exceeds 4GB, the archive files will be divided into 4GB each. + If the size of the album exceeds 4GB, the archive files will by default be divided into 4GB each. This can be changed on the user settings page. ::: - Add a description to the album (Web only). - Slideshow view (Web only). @@ -152,7 +152,7 @@ Some of the features are not available on mobile, to understand what the full fe ## Sharing Between Users -#### Add or remove users from the album. +#### Add or remove users from the album :::info remove user(s) When a user is removed from the album, the photos he uploaded will still appear in the album. diff --git a/docs/docs/guides/remote-access.md b/docs/docs/guides/remote-access.md index cd8bf66f14cc9..326ac6c93d634 100644 --- a/docs/docs/guides/remote-access.md +++ b/docs/docs/guides/remote-access.md @@ -11,13 +11,13 @@ Never forward port 2283 directly to the internet without additional configuratio You may use a VPN service to open an encrypted connection to your Immich instance. OpenVPN and Wireguard are two popular VPN solutions. Here is a guide on setting up VPN access to your server - [Pihole documentation](https://docs.pi-hole.net/guides/vpn/wireguard/overview/) -### Pros: +### Pros - Simple to set up and very secure. - Single point of potential failure, i.e., the VPN software itself. Even if there is a zero-day vulnerability on Immich, you will not be at risk. - Both Wireguard and OpenVPN are independently security-audited, so the risk of serious zero-day exploits are minimal. -### Cons: +### Cons - If you don't have a static IP address, you would need to set up a [Dynamic DNS](https://www.cloudflare.com/learning/dns/glossary/dynamic-dns/). [DuckDNS](https://www.duckdns.org/) is a free DDNS provider. - VPN software needs to be installed and active on both server-side and client-side. @@ -27,6 +27,10 @@ You may use a VPN service to open an encrypted connection to your Immich instanc If you are unable to open a port on your router for Wireguard or OpenVPN to your server, [Tailscale](https://tailscale.com/) is a good option. Tailscale mediates a peer-to-peer wireguard tunnel between your server and remote device, even if one or both of them are behind a [NAT firewall](https://en.wikipedia.org/wiki/Network_address_translation). +:::tip Video toturial +You can learn how to set up Tailscale together with Immich with the [tutorial video](https://www.youtube.com/watch?v=Vt4PDUXB_fg) they created. +::: + ### Pros - Minimal configuration needed on server and client sides. diff --git a/docs/docs/guides/remote-machine-learning.md b/docs/docs/guides/remote-machine-learning.md index e4186e1697db3..4dbb72a408f16 100644 --- a/docs/docs/guides/remote-machine-learning.md +++ b/docs/docs/guides/remote-machine-learning.md @@ -11,6 +11,10 @@ To alleviate [performance issues on low-memory systems](/docs/FAQ.mdx#why-is-imm Smart Search and Face Detection will use this feature, but Facial Recognition is handled in the server. ::: +:::danger +When using remote machine learning, the thumbnails are sent to the remote machine learning container. Use this option carefully when running this on a public computer or a paid processing cloud. +::: + ```yaml name: immich_remote_ml diff --git a/docs/src/components/community-projects.tsx b/docs/src/components/community-projects.tsx index 0f30bac60f66e..23a55ca9ce7d9 100644 --- a/docs/src/components/community-projects.tsx +++ b/docs/src/components/community-projects.tsx @@ -28,11 +28,6 @@ const projects: CommunityProjectProps[] = [ description: 'A simple way to remove orphaned offline assets from the Immich database', url: 'https://github.com/Thoroslives/immich_remove_offline_files', }, - { - title: 'Create albums from folders', - description: 'A Python script to create albums based on the folder structure of an external library.', - url: 'https://github.com/Salvoxia/immich-folder-album-creator', - }, { title: 'Immich-Tools', description: 'Provides scripts for handling problems on the repair page.', @@ -58,6 +53,11 @@ const projects: CommunityProjectProps[] = [ description: 'Unofficial Immich Android TV app.', url: 'https://github.com/giejay/Immich-Android-TV', }, + { + title: 'Create albums from folders', + description: 'A Python script to create albums based on the folder structure of an external library.', + url: 'https://github.com/Salvoxia/immich-folder-album-creator', + }, { title: 'Powershell Module PSImmich', description: 'Powershell Module for the Immich API', @@ -75,8 +75,7 @@ const projects: CommunityProjectProps[] = [ }, { title: 'Immich Power Tools', - description: - 'An unofficial immich client providing tools to speed up your workflows in Immich to organize your people and albums.', + description: 'Power tools for organizing your immich library.', url: 'https://github.com/varun-raj/immich-power-tools', }, ]; From 363c558db7164d427517f4419f4c933f96a29426 Mon Sep 17 00:00:00 2001 From: Jonathan Jogenfors Date: Wed, 28 Aug 2024 19:05:48 +0200 Subject: [PATCH 047/160] fix(server): don't crash when refreshing large libraries (#7934) * add job to check for offline files * fix lint * only check for offline when using checkForOffline * improve tests * remove old test * wip * remove trie * refactor batches * also check offline status * fix spelling * don't do offline scan * rename scan to check * fix job statuses * fix lint * cleanup * add test * open-api * fix test * fix spinner * reset text * don't double batch * fix comments from mert * remove tries * fix tests * fix e2e * fix test * fix test * add tests * fix lint * fix e2e * interweave scans * fix errors * fix messages * fix test * add mock * fix sql * fix e2e * use library batch size * save -> update * add file extensions * update specs * test for import paths * check import paths when testing offline * fix lint * normalize import path * remove console logs * decrease batch size to 1000 * add test for import path * add test for already-online assets * fix merge * fix lint * add library job back * add offline job to correct queue * library spec compiles now * move one test to new e2e * fix comments * fix comments * fix lint * refactor path validation * fix loop bug * remove logging * expect responses * fix asset mock * take the straightforward approach * use generator correctly * fix vitest on file edit * bump vitest to 1.6.0 * test for offline check * add e2e tests for offlining assets depending on import path * cleanup e2e test after finish * cleanup library service * paginate the walk generator * fix tests * fix typo * refactoring handleOfflineCheck * better testing of handleOfflineCheck * fix lint * handle large library deletions * dont check if library is deleted * fix mock * add a 100k page size to library * fix loading animation * better log messages * Better logging for offline asset removal * fix sql and tests * fix number format * Remove submodule * fix format * chore: cleanup * chore: fix tests --------- Co-authored-by: Alex Co-authored-by: Jason Rasmussen --- e2e/src/api/specs/library.e2e-spec.ts | 39 ++- server/package-lock.json | 27 -- server/package.json | 1 - server/src/dtos/library.dto.ts | 10 +- server/src/interfaces/asset.interface.ts | 1 + server/src/interfaces/job.interface.ts | 7 + server/src/interfaces/library.interface.ts | 1 - server/src/interfaces/storage.interface.ts | 6 +- server/src/queries/library.repository.sql | 11 - server/src/repositories/asset.repository.ts | 11 +- server/src/repositories/job.repository.ts | 1 + server/src/repositories/library.repository.ts | 24 -- server/src/repositories/storage.repository.ts | 17 +- server/src/services/library.service.spec.ts | 165 +++++++--- server/src/services/library.service.ts | 299 +++++++++--------- server/src/services/microservices.service.ts | 3 +- .../repositories/library.repository.mock.ts | 1 - .../admin/library-management/+page.svelte | 22 +- 18 files changed, 364 insertions(+), 282 deletions(-) diff --git a/e2e/src/api/specs/library.e2e-spec.ts b/e2e/src/api/specs/library.e2e-spec.ts index 59968f3b7942b..013e1364ca783 100644 --- a/e2e/src/api/specs/library.e2e-spec.ts +++ b/e2e/src/api/specs/library.e2e-spec.ts @@ -364,7 +364,7 @@ describe('/libraries', () => { utils.removeImageFile(`${testAssetDir}/temp/directoryA/assetB.png`); }); - it('should offline missing files', async () => { + it('should offline a file missing from disk', async () => { utils.createImageFile(`${testAssetDir}/temp/directoryA/assetB.png`); const library = await utils.createLibrary(admin.accessToken, { ownerId: admin.userId, @@ -391,6 +391,43 @@ describe('/libraries', () => { ); }); + it('should offline a file outside of import paths', async () => { + utils.createImageFile(`${testAssetDir}/temp/directoryA/assetB.png`); + utils.createImageFile(`${testAssetDir}/temp/directoryB/assetC.png`); + const library = await utils.createLibrary(admin.accessToken, { + ownerId: admin.userId, + importPaths: [`${testAssetDirInternal}/temp`], + }); + + await scan(admin.accessToken, library.id); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + + await request(app) + .put(`/libraries/${library.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ importPaths: [`${testAssetDirInternal}/temp/directoryA`] }); + + await scan(admin.accessToken, library.id); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + + const { assets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id }); + + expect(assets.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + isOffline: false, + originalFileName: 'assetB.png', + }), + expect.objectContaining({ + isOffline: true, + originalFileName: 'assetC.png', + }), + ]), + ); + + utils.removeImageFile(`${testAssetDir}/temp/directoryB/assetC.png`); + }); + it('should not try to delete offline files', async () => { utils.createImageFile(`${testAssetDir}/temp/offline1/assetA.png`); diff --git a/server/package-lock.json b/server/package-lock.json index 972d1164633ba..1ec4fe0fb0006 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -45,7 +45,6 @@ "js-yaml": "^4.1.0", "lodash": "^4.17.21", "luxon": "^3.4.2", - "mnemonist": "^0.39.8", "nest-commander": "^3.11.1", "nestjs-cls": "^4.3.0", "nestjs-otel": "^6.0.0", @@ -10434,14 +10433,6 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "dev": true }, - "node_modules/mnemonist": { - "version": "0.39.8", - "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.8.tgz", - "integrity": "sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ==", - "dependencies": { - "obliterator": "^2.0.1" - } - }, "node_modules/mock-fs": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.2.0.tgz", @@ -10955,11 +10946,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/obliterator": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", - "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==" - }, "node_modules/obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", @@ -22483,14 +22469,6 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "dev": true }, - "mnemonist": { - "version": "0.39.8", - "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.8.tgz", - "integrity": "sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ==", - "requires": { - "obliterator": "^2.0.1" - } - }, "mock-fs": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.2.0.tgz", @@ -22855,11 +22833,6 @@ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" }, - "obliterator": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", - "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==" - }, "obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", diff --git a/server/package.json b/server/package.json index f58ad98b0868a..9b429222787a7 100644 --- a/server/package.json +++ b/server/package.json @@ -71,7 +71,6 @@ "js-yaml": "^4.1.0", "lodash": "^4.17.21", "luxon": "^3.4.2", - "mnemonist": "^0.39.8", "nest-commander": "^3.11.1", "nestjs-cls": "^4.3.0", "nestjs-otel": "^6.0.0", diff --git a/server/src/dtos/library.dto.ts b/server/src/dtos/library.dto.ts index b9578a2c3766b..c2c3ac9d27546 100644 --- a/server/src/dtos/library.dto.ts +++ b/server/src/dtos/library.dto.ts @@ -48,12 +48,16 @@ export class UpdateLibraryDto { exclusionPatterns?: string[]; } -export class CrawlOptionsDto { - pathsToCrawl!: string[]; - includeHidden? = false; +export interface CrawlOptionsDto { + pathsToCrawl: string[]; + includeHidden?: boolean; exclusionPatterns?: string[]; } +export interface WalkOptionsDto extends CrawlOptionsDto { + take: number; +} + export class ValidateLibraryDto { @Optional() @IsString({ each: true }) diff --git a/server/src/interfaces/asset.interface.ts b/server/src/interfaces/asset.interface.ts index 666c6d3f7ed71..9f9218a3e3534 100644 --- a/server/src/interfaces/asset.interface.ts +++ b/server/src/interfaces/asset.interface.ts @@ -36,6 +36,7 @@ export enum WithoutProperty { export enum WithProperty { SIDECAR = 'sidecar', + IS_ONLINE = 'isOnline', IS_OFFLINE = 'isOffline', } diff --git a/server/src/interfaces/job.interface.ts b/server/src/interfaces/job.interface.ts index 7776d2bd370b5..fab959936f0a9 100644 --- a/server/src/interfaces/job.interface.ts +++ b/server/src/interfaces/job.interface.ts @@ -76,6 +76,7 @@ export enum JobName { LIBRARY_SCAN = 'library-refresh', LIBRARY_SCAN_ASSET = 'library-refresh-asset', LIBRARY_REMOVE_OFFLINE = 'library-remove-offline', + LIBRARY_CHECK_OFFLINE = 'library-check-offline', LIBRARY_DELETE = 'library-delete', LIBRARY_QUEUE_SCAN_ALL = 'library-queue-all-refresh', LIBRARY_QUEUE_CLEANUP = 'library-queue-cleanup', @@ -110,6 +111,7 @@ export enum JobName { } export const JOBS_ASSET_PAGINATION_SIZE = 1000; +export const JOBS_LIBRARY_PAGINATION_SIZE = 100_000; export interface IBaseJob { force?: boolean; @@ -129,6 +131,10 @@ export interface ILibraryFileJob extends IEntityJob { assetPath: string; } +export interface ILibraryOfflineJob extends IEntityJob { + importPaths: string[]; +} + export interface ILibraryRefreshJob extends IEntityJob { refreshModifiedFiles: boolean; refreshAllFiles: boolean; @@ -264,6 +270,7 @@ export type JobItem = | { name: JobName.LIBRARY_REMOVE_OFFLINE; data: IEntityJob } | { name: JobName.LIBRARY_DELETE; data: IEntityJob } | { name: JobName.LIBRARY_QUEUE_SCAN_ALL; data: IBaseJob } + | { name: JobName.LIBRARY_CHECK_OFFLINE; data: IEntityJob } | { name: JobName.LIBRARY_QUEUE_CLEANUP; data: IBaseJob } // Notification diff --git a/server/src/interfaces/library.interface.ts b/server/src/interfaces/library.interface.ts index 6468977df4b21..d8f1a1303116e 100644 --- a/server/src/interfaces/library.interface.ts +++ b/server/src/interfaces/library.interface.ts @@ -12,5 +12,4 @@ export interface ILibraryRepository { softDelete(id: string): Promise; update(library: Partial): Promise; getStatistics(id: string): Promise; - getAssetIds(id: string, withDeleted?: boolean): Promise; } diff --git a/server/src/interfaces/storage.interface.ts b/server/src/interfaces/storage.interface.ts index f27edaccc91bd..fec3d66dd5c03 100644 --- a/server/src/interfaces/storage.interface.ts +++ b/server/src/interfaces/storage.interface.ts @@ -2,7 +2,7 @@ import { WatchOptions } from 'chokidar'; import { Stats } from 'node:fs'; import { FileReadOptions } from 'node:fs/promises'; import { Readable } from 'node:stream'; -import { CrawlOptionsDto } from 'src/dtos/library.dto'; +import { CrawlOptionsDto, WalkOptionsDto } from 'src/dtos/library.dto'; export interface ImmichReadStream { stream: Readable; @@ -45,8 +45,8 @@ export interface IStorageRepository { checkDiskUsage(folder: string): Promise; readdir(folder: string): Promise; stat(filepath: string): Promise; - crawl(crawlOptions: CrawlOptionsDto): Promise; - walk(crawlOptions: CrawlOptionsDto): AsyncGenerator; + crawl(options: CrawlOptionsDto): Promise; + walk(options: WalkOptionsDto): AsyncGenerator; copyFile(source: string, target: string): Promise; rename(source: string, target: string): Promise; watch(paths: string[], options: WatchOptions, events: Partial): () => Promise; diff --git a/server/src/queries/library.repository.sql b/server/src/queries/library.repository.sql index bc20bf4bd3af9..5dd32ce365d9e 100644 --- a/server/src/queries/library.repository.sql +++ b/server/src/queries/library.repository.sql @@ -145,14 +145,3 @@ WHERE AND ("libraries"."deletedAt" IS NULL) GROUP BY "libraries"."id" - --- LibraryRepository.getAssetIds -SELECT - "assets"."id" AS "assets_id" -FROM - "libraries" "library" - INNER JOIN "assets" "assets" ON "assets"."libraryId" = "library"."id" - AND ("assets"."deletedAt" IS NULL) -WHERE - ("library"."id" = $1) - AND ("library"."deletedAt" IS NULL) diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index b95db5f3a8e62..1a2a0474a10d7 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -383,7 +383,7 @@ export class AssetRepository implements IAssetRepository { @GenerateSql( ...Object.values(WithProperty) - .filter((property) => property !== WithProperty.IS_OFFLINE) + .filter((property) => property !== WithProperty.IS_OFFLINE && property !== WithProperty.IS_ONLINE) .map((property) => ({ name: property, params: [DummyValue.PAGINATION, property], @@ -539,7 +539,14 @@ export class AssetRepository implements IAssetRepository { if (!libraryId) { throw new Error('Library id is required when finding offline assets'); } - where = [{ isOffline: true, libraryId: libraryId }]; + where = [{ isOffline: true, libraryId }]; + break; + } + case WithProperty.IS_ONLINE: { + if (!libraryId) { + throw new Error('Library id is required when finding online assets'); + } + where = [{ isOffline: false, libraryId }]; break; } diff --git a/server/src/repositories/job.repository.ts b/server/src/repositories/job.repository.ts index 88834afc00273..f64e5175e5127 100644 --- a/server/src/repositories/job.repository.ts +++ b/server/src/repositories/job.repository.ts @@ -79,6 +79,7 @@ export const JOBS_TO_QUEUE: Record = { [JobName.LIBRARY_SCAN_ASSET]: QueueName.LIBRARY, [JobName.LIBRARY_SCAN]: QueueName.LIBRARY, [JobName.LIBRARY_DELETE]: QueueName.LIBRARY, + [JobName.LIBRARY_CHECK_OFFLINE]: QueueName.LIBRARY, [JobName.LIBRARY_REMOVE_OFFLINE]: QueueName.LIBRARY, [JobName.LIBRARY_QUEUE_SCAN_ALL]: QueueName.LIBRARY, [JobName.LIBRARY_QUEUE_CLEANUP]: QueueName.LIBRARY, diff --git a/server/src/repositories/library.repository.ts b/server/src/repositories/library.repository.ts index 963b0aaf73dfc..36fb4b921751b 100644 --- a/server/src/repositories/library.repository.ts +++ b/server/src/repositories/library.repository.ts @@ -94,30 +94,6 @@ export class LibraryRepository implements ILibraryRepository { }; } - @GenerateSql({ params: [DummyValue.UUID] }) - async getAssetIds(libraryId: string, withDeleted = false): Promise { - const builder = this.repository - .createQueryBuilder('library') - .innerJoinAndSelect('library.assets', 'assets') - .where('library.id = :id', { id: libraryId }) - .select('assets.id'); - - if (withDeleted) { - builder.withDeleted(); - } - - // Return all asset paths for a given library - const rawResults = await builder.getRawMany(); - - const results: string[] = []; - - for (const rawPath of rawResults) { - results.push(rawPath.assets_id); - } - - return results; - } - private async save(library: Partial) { const { id } = await this.repository.save(library); return this.repository.findOneByOrFail({ id }); diff --git a/server/src/repositories/storage.repository.ts b/server/src/repositories/storage.repository.ts index b310f2e1100aa..c699047ce1575 100644 --- a/server/src/repositories/storage.repository.ts +++ b/server/src/repositories/storage.repository.ts @@ -5,7 +5,7 @@ import { escapePath, glob, globStream } from 'fast-glob'; import { constants, createReadStream, existsSync, mkdirSync } from 'node:fs'; import fs from 'node:fs/promises'; import path from 'node:path'; -import { CrawlOptionsDto } from 'src/dtos/library.dto'; +import { CrawlOptionsDto, WalkOptionsDto } from 'src/dtos/library.dto'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { DiskUsage, @@ -157,8 +157,8 @@ export class StorageRepository implements IStorageRepository { }); } - async *walk(crawlOptions: CrawlOptionsDto): AsyncGenerator { - const { pathsToCrawl, exclusionPatterns, includeHidden } = crawlOptions; + async *walk(walkOptions: WalkOptionsDto): AsyncGenerator { + const { pathsToCrawl, exclusionPatterns, includeHidden } = walkOptions; if (pathsToCrawl.length === 0) { async function* emptyGenerator() {} return emptyGenerator(); @@ -172,8 +172,17 @@ export class StorageRepository implements IStorageRepository { ignore: exclusionPatterns, }); + let batch: string[] = []; for await (const value of stream) { - yield value as string; + batch.push(value.toString()); + if (batch.length === walkOptions.take) { + yield batch; + batch = []; + } + } + + if (batch.length > 0) { + yield batch; } } diff --git a/server/src/services/library.service.spec.ts b/server/src/services/library.service.spec.ts index 8a74ec918996c..9e260e98efa15 100644 --- a/server/src/services/library.service.spec.ts +++ b/server/src/services/library.service.spec.ts @@ -8,7 +8,15 @@ import { AssetType } from 'src/enum'; import { IAssetRepository } from 'src/interfaces/asset.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IDatabaseRepository } from 'src/interfaces/database.interface'; -import { IJobRepository, ILibraryFileJob, ILibraryRefreshJob, JobName, JobStatus } from 'src/interfaces/job.interface'; +import { + IJobRepository, + ILibraryFileJob, + ILibraryOfflineJob, + ILibraryRefreshJob, + JobName, + JOBS_LIBRARY_PAGINATION_SIZE, + JobStatus, +} from 'src/interfaces/job.interface'; import { ILibraryRepository } from 'src/interfaces/library.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; @@ -154,17 +162,19 @@ describe(LibraryService.name, () => { }); describe('handleQueueAssetRefresh', () => { - it('should queue new assets', async () => { + it('should queue refresh of a new asset', async () => { const mockLibraryJob: ILibraryRefreshJob = { id: libraryStub.externalLibrary1.id, refreshModifiedFiles: false, refreshAllFiles: false, }; + assetMock.getWith.mockResolvedValue({ items: [], hasNextPage: false }); + libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); // eslint-disable-next-line @typescript-eslint/require-await storageMock.walk.mockImplementation(async function* generator() { - yield '/data/user1/photo.jpg'; + yield ['/data/user1/photo.jpg']; }); assetMock.getExternalLibraryAssetPaths.mockResolvedValue({ items: [], hasNextPage: false }); @@ -183,6 +193,44 @@ describe(LibraryService.name, () => { ]); }); + it('should queue offline check of existing online assets', async () => { + const mockLibraryJob: ILibraryRefreshJob = { + id: libraryStub.externalLibrary1.id, + refreshModifiedFiles: false, + refreshAllFiles: false, + }; + + assetMock.getWith.mockResolvedValue({ items: [], hasNextPage: false }); + libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); + storageMock.walk.mockImplementation(async function* generator() {}); + assetMock.getWith.mockResolvedValue({ items: [assetStub.external], hasNextPage: false }); + + await sut.handleQueueAssetRefresh(mockLibraryJob); + + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.LIBRARY_CHECK_OFFLINE, + data: { + id: assetStub.external.id, + importPaths: libraryStub.externalLibrary1.importPaths, + exclusionPatterns: [], + }, + }, + ]); + }); + + it("should fail when library can't be found", async () => { + const mockLibraryJob: ILibraryRefreshJob = { + id: libraryStub.externalLibrary1.id, + refreshModifiedFiles: false, + refreshAllFiles: false, + }; + + libraryMock.get.mockResolvedValue(null); + + await expect(sut.handleQueueAssetRefresh(mockLibraryJob)).resolves.toBe(JobStatus.SKIPPED); + }); + it('should force queue new assets', async () => { const mockLibraryJob: ILibraryRefreshJob = { id: libraryStub.externalLibrary1.id, @@ -190,10 +238,11 @@ describe(LibraryService.name, () => { refreshAllFiles: true, }; + assetMock.getWith.mockResolvedValue({ items: [], hasNextPage: false }); libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); // eslint-disable-next-line @typescript-eslint/require-await storageMock.walk.mockImplementation(async function* generator() { - yield '/data/user1/photo.jpg'; + yield ['/data/user1/photo.jpg']; }); assetMock.getExternalLibraryAssetPaths.mockResolvedValue({ items: [], hasNextPage: false }); @@ -225,6 +274,8 @@ describe(LibraryService.name, () => { storageMock.checkFileExists.mockResolvedValue(true); + assetMock.getWith.mockResolvedValue({ items: [], hasNextPage: false }); + const mockLibraryJob: ILibraryRefreshJob = { id: libraryStub.externalLibraryWithImportPaths1.id, refreshModifiedFiles: false, @@ -239,51 +290,78 @@ describe(LibraryService.name, () => { expect(storageMock.walk).toHaveBeenCalledWith({ pathsToCrawl: [libraryStub.externalLibraryWithImportPaths1.importPaths[1]], exclusionPatterns: [], + includeHidden: false, + take: JOBS_LIBRARY_PAGINATION_SIZE, }); }); + }); - it('should set missing assets offline', async () => { - const mockLibraryJob: ILibraryRefreshJob = { - id: libraryStub.externalLibrary1.id, - refreshModifiedFiles: false, - refreshAllFiles: false, + describe('handleOfflineCheck', () => { + it('should skip missing assets', async () => { + const mockAssetJob: ILibraryOfflineJob = { + id: assetStub.external.id, + importPaths: ['/'], }; - libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); - assetMock.getExternalLibraryAssetPaths.mockResolvedValue({ - items: [assetStub.external], - hasNextPage: false, - }); + assetMock.getById.mockResolvedValue(null); - await sut.handleQueueAssetRefresh(mockLibraryJob); + await expect(sut.handleOfflineCheck(mockAssetJob)).resolves.toBe(JobStatus.SKIPPED); - expect(assetMock.updateAll).toHaveBeenCalledWith([assetStub.image.id], { isOffline: true }); - expect(assetMock.updateAll).not.toHaveBeenCalledWith(expect.anything(), { isOffline: false }); - expect(jobMock.queueAll).not.toHaveBeenCalled(); + expect(assetMock.update).not.toHaveBeenCalled(); }); - it('should set crawled assets that were previously offline back online', async () => { - const mockLibraryJob: ILibraryRefreshJob = { - id: libraryStub.externalLibrary1.id, - refreshModifiedFiles: false, - refreshAllFiles: false, + it('should do nothing with already-offline assets', async () => { + const mockAssetJob: ILibraryOfflineJob = { + id: assetStub.external.id, + importPaths: ['/'], }; - libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); - // eslint-disable-next-line @typescript-eslint/require-await - storageMock.walk.mockImplementation(async function* generator() { - yield assetStub.externalOffline.originalPath; - }); - assetMock.getExternalLibraryAssetPaths.mockResolvedValue({ - items: [assetStub.externalOffline], - hasNextPage: false, - }); + assetMock.getById.mockResolvedValue(assetStub.offline); - await sut.handleQueueAssetRefresh(mockLibraryJob); + await expect(sut.handleOfflineCheck(mockAssetJob)).resolves.toBe(JobStatus.SUCCESS); - expect(assetMock.updateAll).toHaveBeenCalledWith([assetStub.externalOffline.id], { isOffline: false }); - expect(assetMock.updateAll).not.toHaveBeenCalledWith(expect.anything(), { isOffline: true }); - expect(jobMock.queueAll).not.toHaveBeenCalled(); + expect(assetMock.update).not.toHaveBeenCalled(); + }); + + it('should offline assets no longer on disk or matching exclusion pattern', async () => { + const mockAssetJob: ILibraryOfflineJob = { + id: assetStub.external.id, + importPaths: ['/'], + }; + + assetMock.getById.mockResolvedValue(assetStub.external); + + await expect(sut.handleOfflineCheck(mockAssetJob)).resolves.toBe(JobStatus.SUCCESS); + + expect(assetMock.update).toHaveBeenCalledWith({ id: assetStub.external.id, isOffline: true }); + }); + + it('should set assets outside of import paths as offline', async () => { + const mockAssetJob: ILibraryOfflineJob = { + id: assetStub.external.id, + importPaths: ['/data/user2'], + }; + + assetMock.getById.mockResolvedValue(assetStub.external); + storageMock.checkFileExists.mockResolvedValue(true); + + await expect(sut.handleOfflineCheck(mockAssetJob)).resolves.toBe(JobStatus.SUCCESS); + + expect(assetMock.update).toHaveBeenCalledWith({ id: assetStub.external.id, isOffline: true }); + }); + + it('should do nothing with online assets', async () => { + const mockAssetJob: ILibraryOfflineJob = { + id: assetStub.external.id, + importPaths: ['/'], + }; + + assetMock.getById.mockResolvedValue(assetStub.external); + storageMock.checkFileExists.mockResolvedValue(true); + + await expect(sut.handleOfflineCheck(mockAssetJob)).resolves.toBe(JobStatus.SUCCESS); + + expect(assetMock.update).not.toHaveBeenCalled(); }); }); @@ -1115,18 +1193,9 @@ describe(LibraryService.name, () => { }); describe('handleDeleteLibrary', () => { - it('should not delete a nonexistent library', async () => { - libraryMock.get.mockResolvedValue(null); - - libraryMock.getAssetIds.mockResolvedValue([]); - libraryMock.delete.mockImplementation(async () => {}); - - await expect(sut.handleDeleteLibrary({ id: libraryStub.externalLibrary1.id })).resolves.toBe(JobStatus.FAILED); - }); - it('should delete an empty library', async () => { libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); - libraryMock.getAssetIds.mockResolvedValue([]); + assetMock.getAll.mockResolvedValue({ items: [], hasNextPage: false }); libraryMock.delete.mockImplementation(async () => {}); await expect(sut.handleDeleteLibrary({ id: libraryStub.externalLibrary1.id })).resolves.toBe(JobStatus.SUCCESS); @@ -1134,7 +1203,7 @@ describe(LibraryService.name, () => { it('should delete a library with assets', async () => { libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); - libraryMock.getAssetIds.mockResolvedValue([assetStub.image1.id]); + assetMock.getAll.mockResolvedValue({ items: [assetStub.image1], hasNextPage: false }); libraryMock.delete.mockImplementation(async () => {}); assetMock.getById.mockResolvedValue(assetStub.image1); @@ -1273,7 +1342,7 @@ describe(LibraryService.name, () => { assetMock.getWith.mockResolvedValue({ items: [assetStub.image1], hasNextPage: false }); assetMock.getById.mockResolvedValue(assetStub.image1); - await expect(sut.handleOfflineRemoval({ id: libraryStub.externalLibrary1.id })).resolves.toBe(JobStatus.SUCCESS); + await expect(sut.handleRemoveOffline({ id: libraryStub.externalLibrary1.id })).resolves.toBe(JobStatus.SUCCESS); expect(jobMock.queueAll).toHaveBeenCalledWith([ { name: JobName.ASSET_DELETION, data: { id: assetStub.image1.id, deleteOnDisk: false } }, diff --git a/server/src/services/library.service.ts b/server/src/services/library.service.ts index 4b82c9811d8d3..9e31107027c56 100644 --- a/server/src/services/library.service.ts +++ b/server/src/services/library.service.ts @@ -1,5 +1,4 @@ import { BadRequestException, Inject, Injectable } from '@nestjs/common'; -import { Trie } from 'mnemonist'; import { R_OK } from 'node:constants'; import { Stats } from 'node:fs'; import path, { basename, parse } from 'node:path'; @@ -18,7 +17,6 @@ import { ValidateLibraryResponseDto, mapLibrary, } from 'src/dtos/library.dto'; -import { LibraryEntity } from 'src/entities/library.entity'; import { AssetType } from 'src/enum'; import { IAssetRepository, WithProperty } from 'src/interfaces/asset.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; @@ -29,8 +27,9 @@ import { IEntityJob, IJobRepository, ILibraryFileJob, + ILibraryOfflineJob, ILibraryRefreshJob, - JOBS_ASSET_PAGINATION_SIZE, + JOBS_LIBRARY_PAGINATION_SIZE, JobName, JobStatus, } from 'src/interfaces/job.interface'; @@ -43,8 +42,6 @@ import { handlePromiseError } from 'src/utils/misc'; import { usePagination } from 'src/utils/pagination'; import { validateCronExpression } from 'src/validation'; -const LIBRARY_SCAN_BATCH_SIZE = 5000; - @Injectable() export class LibraryService { private configCore: SystemConfigCore; @@ -254,26 +251,17 @@ export class LibraryService { } private async scanAssets(libraryId: string, assetPaths: string[], ownerId: string, force = false) { - this.logger.verbose(`Queuing refresh of ${assetPaths.length} asset(s)`); - - // We perform this in batches to save on memory when performing large refreshes (greater than 1M assets) - const batchSize = 5000; - for (let i = 0; i < assetPaths.length; i += batchSize) { - const batch = assetPaths.slice(i, i + batchSize); - await this.jobRepository.queueAll( - batch.map((assetPath) => ({ - name: JobName.LIBRARY_SCAN_ASSET, - data: { - id: libraryId, - assetPath: assetPath, - ownerId, - force, - }, - })), - ); - } - - this.logger.debug('Asset refresh queue completed'); + await this.jobRepository.queueAll( + assetPaths.map((assetPath) => ({ + name: JobName.LIBRARY_SCAN_ASSET, + data: { + id: libraryId, + assetPath, + ownerId, + force, + }, + })), + ); } private async validateImportPath(importPath: string): Promise { @@ -348,27 +336,32 @@ export class LibraryService { } async handleDeleteLibrary(job: IEntityJob): Promise { - const library = await this.repository.get(job.id, true); - if (!library) { - return JobStatus.FAILED; - } + const libraryId = job.id; - // TODO use pagination - const assetIds = await this.repository.getAssetIds(job.id, true); - this.logger.debug(`Will delete ${assetIds.length} asset(s) in library ${job.id}`); - await this.jobRepository.queueAll( - assetIds.map((assetId) => ({ - name: JobName.ASSET_DELETION, - data: { - id: assetId, - deleteOnDisk: false, - }, - })), + const assetPagination = usePagination(JOBS_LIBRARY_PAGINATION_SIZE, (pagination) => + this.assetRepository.getAll(pagination, { libraryId: libraryId, withDeleted: true }), ); - if (assetIds.length === 0) { - this.logger.log(`Deleting library ${job.id}`); - await this.repository.delete(job.id); + let assetsFound = false; + + this.logger.debug(`Will delete all assets in library ${libraryId}`); + for await (const assets of assetPagination) { + assetsFound = true; + this.logger.debug(`Queueing deletion of ${assets.length} asset(s) in library ${libraryId}`); + await this.jobRepository.queueAll( + assets.map((asset) => ({ + name: JobName.ASSET_DELETION, + data: { + id: asset.id, + deleteOnDisk: false, + }, + })), + ); + } + + if (!assetsFound) { + this.logger.log(`Deleting library ${libraryId}`); + await this.repository.delete(libraryId); } return JobStatus.SUCCESS; } @@ -453,6 +446,7 @@ export class LibraryService { sidecarPath = `${assetPath}.xmp`; } + // TODO: device asset id is deprecated, remove it const deviceAssetId = `${basename(assetPath)}`.replaceAll(/\s+/g, ''); let assetId; @@ -494,7 +488,7 @@ export class LibraryService { return JobStatus.SKIPPED; } - this.logger.debug(`Queuing metadata extraction for: ${assetPath}`); + this.logger.debug(`Queueing metadata extraction for: ${assetPath}`); await this.jobRepository.queue({ name: JobName.METADATA_EXTRACTION, data: { id: assetId, source: 'upload' } }); @@ -519,17 +513,15 @@ export class LibraryService { } async queueRemoveOffline(id: string) { - this.logger.verbose(`Removing offline files from library: ${id}`); + this.logger.verbose(`Queueing offline file removal from library ${id}`); await this.jobRepository.queue({ name: JobName.LIBRARY_REMOVE_OFFLINE, data: { id } }); } async handleQueueAllScan(job: IBaseJob): Promise { this.logger.debug(`Refreshing all external libraries: force=${job.force}`); - // Queue cleanup await this.jobRepository.queue({ name: JobName.LIBRARY_QUEUE_CLEANUP, data: {} }); - // Queue all library refresh const libraries = await this.repository.getAll(true); await this.jobRepository.queueAll( libraries.map((library) => ({ @@ -544,22 +536,71 @@ export class LibraryService { return JobStatus.SUCCESS; } - async handleOfflineRemoval(job: IEntityJob): Promise { - const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => + async handleOfflineCheck(job: ILibraryOfflineJob): Promise { + const asset = await this.assetRepository.getById(job.id); + + if (!asset) { + // Asset is no longer in the database, skip + return JobStatus.SKIPPED; + } + + if (asset.isOffline) { + this.logger.verbose(`Asset is already offline: ${asset.originalPath}`); + return JobStatus.SUCCESS; + } + + const isInPath = job.importPaths.find((path) => asset.originalPath.startsWith(path)); + if (!isInPath) { + this.logger.debug(`Asset is no longer in an import path, marking offline: ${asset.originalPath}`); + await this.assetRepository.update({ id: asset.id, isOffline: true }); + return JobStatus.SUCCESS; + } + + const fileExists = await this.storageRepository.checkFileExists(asset.originalPath, R_OK); + if (!fileExists) { + this.logger.debug( + `Asset is no longer found on disk or is covered by exclusion pattern, marking offline: ${asset.originalPath}`, + ); + await this.assetRepository.update({ id: asset.id, isOffline: true }); + return JobStatus.SUCCESS; + } + + this.logger.verbose( + `Asset is found on disk, not covered by an exclusion pattern, and is in an import path, keeping online: ${asset.originalPath}`, + ); + + return JobStatus.SUCCESS; + } + + async handleRemoveOffline(job: IEntityJob): Promise { + this.logger.debug(`Removing offline assets for library ${job.id}`); + + const assetPagination = usePagination(JOBS_LIBRARY_PAGINATION_SIZE, (pagination) => this.assetRepository.getWith(pagination, WithProperty.IS_OFFLINE, job.id), ); + let offlineAssets = 0; for await (const assets of assetPagination) { - this.logger.debug(`Removing ${assets.length} offline assets`); - await this.jobRepository.queueAll( - assets.map((asset) => ({ - name: JobName.ASSET_DELETION, - data: { - id: asset.id, - deleteOnDisk: false, - }, - })), - ); + offlineAssets += assets.length; + if (assets.length > 0) { + this.logger.debug(`Discovered ${offlineAssets} offline assets in library ${job.id}`); + await this.jobRepository.queueAll( + assets.map((asset) => ({ + name: JobName.ASSET_DELETION, + data: { + id: asset.id, + deleteOnDisk: false, + }, + })), + ); + this.logger.verbose(`Queued deletion of ${assets.length} offline assets in library ${job.id}`); + } + } + + if (offlineAssets) { + this.logger.debug(`Finished queueing deletion of ${offlineAssets} offline assets for library ${job.id}`); + } else { + this.logger.debug(`Found no offline assets to delete from library ${job.id}`); } return JobStatus.SUCCESS; @@ -568,73 +609,67 @@ export class LibraryService { async handleQueueAssetRefresh(job: ILibraryRefreshJob): Promise { const library = await this.repository.get(job.id); if (!library) { - this.logger.warn('Library not found'); - return JobStatus.FAILED; + return JobStatus.SKIPPED; } - this.logger.log(`Refreshing library: ${job.id}`); + this.logger.log(`Refreshing library ${library.id}`); - const crawledAssetPaths = await this.getPathTrie(library); - this.logger.debug(`Found ${crawledAssetPaths.size} asset(s) when crawling import paths ${library.importPaths}`); + const validImportPaths: string[] = []; - const assetIdsToMarkOffline = []; - const assetIdsToMarkOnline = []; - const pagination = usePagination(LIBRARY_SCAN_BATCH_SIZE, (pagination) => - this.assetRepository.getExternalLibraryAssetPaths(pagination, library.id), + for (const importPath of library.importPaths) { + const validation = await this.validateImportPath(importPath); + if (validation.isValid) { + validImportPaths.push(path.normalize(importPath)); + } else { + this.logger.warn(`Skipping invalid import path: ${importPath}. Reason: ${validation.message}`); + } + } + + if (validImportPaths.length === 0) { + this.logger.warn(`No valid import paths found for library ${library.id}`); + } + + const assetsOnDisk = this.storageRepository.walk({ + pathsToCrawl: validImportPaths, + includeHidden: false, + exclusionPatterns: library.exclusionPatterns, + take: JOBS_LIBRARY_PAGINATION_SIZE, + }); + + let crawledAssets = 0; + + for await (const assetBatch of assetsOnDisk) { + crawledAssets += assetBatch.length; + this.logger.debug(`Discovered ${crawledAssets} asset(s) on disk for library ${library.id}...`); + await this.scanAssets(job.id, assetBatch, library.ownerId, job.refreshAllFiles ?? false); + this.logger.verbose(`Queued scan of ${assetBatch.length} crawled asset(s) in library ${library.id}...`); + } + + if (crawledAssets) { + this.logger.debug(`Finished queueing scan of ${crawledAssets} assets on disk for library ${library.id}`); + } else { + this.logger.debug(`No non-excluded assets found in any import path for library ${library.id}`); + } + + const onlineAssets = usePagination(JOBS_LIBRARY_PAGINATION_SIZE, (pagination) => + this.assetRepository.getWith(pagination, WithProperty.IS_ONLINE, job.id), ); - this.logger.verbose(`Crawled asset paths paginated`); - - const shouldScanAll = job.refreshAllFiles || job.refreshModifiedFiles; - for await (const page of pagination) { - for (const asset of page) { - const isOffline = !crawledAssetPaths.has(asset.originalPath); - if (isOffline && !asset.isOffline) { - assetIdsToMarkOffline.push(asset.id); - this.logger.verbose(`Added to mark-offline list: ${asset.originalPath}`); - } - - if (!isOffline && asset.isOffline) { - assetIdsToMarkOnline.push(asset.id); - this.logger.verbose(`Added to mark-online list: ${asset.originalPath}`); - } - - if (!shouldScanAll) { - crawledAssetPaths.delete(asset.originalPath); - } - } + let onlineAssetCount = 0; + for await (const assets of onlineAssets) { + onlineAssetCount += assets.length; + this.logger.debug(`Discovered ${onlineAssetCount} asset(s) in library ${library.id}...`); + await this.jobRepository.queueAll( + assets.map((asset) => ({ + name: JobName.LIBRARY_CHECK_OFFLINE, + data: { id: asset.id, importPaths: validImportPaths, exclusionPatterns: library.exclusionPatterns }, + })), + ); + this.logger.debug(`Queued online check of ${assets.length} asset(s) in library ${library.id}...`); } - this.logger.verbose(`Crawled assets have been checked for online/offline status`); - - if (assetIdsToMarkOffline.length > 0) { - this.logger.debug(`Found ${assetIdsToMarkOffline.length} offline asset(s) previously marked as online`); - await this.assetRepository.updateAll(assetIdsToMarkOffline, { isOffline: true }); - } - - if (assetIdsToMarkOnline.length > 0) { - this.logger.debug(`Found ${assetIdsToMarkOnline.length} online asset(s) previously marked as offline`); - await this.assetRepository.updateAll(assetIdsToMarkOnline, { isOffline: false }); - } - - if (crawledAssetPaths.size > 0) { - if (!shouldScanAll) { - this.logger.debug(`Will import ${crawledAssetPaths.size} new asset(s)`); - } - - let batch = []; - for (const assetPath of crawledAssetPaths) { - batch.push(assetPath); - - if (batch.length >= LIBRARY_SCAN_BATCH_SIZE) { - await this.scanAssets(job.id, batch, library.ownerId, job.refreshAllFiles ?? false); - batch = []; - } - } - - if (batch.length > 0) { - await this.scanAssets(job.id, batch, library.ownerId, job.refreshAllFiles ?? false); - } + if (onlineAssetCount) { + this.logger.log(`Finished queueing online check of ${onlineAssetCount} assets for library ${library.id}`); } await this.repository.update({ id: job.id, refreshedAt: new Date() }); @@ -642,34 +677,6 @@ export class LibraryService { return JobStatus.SUCCESS; } - private async getPathTrie(library: LibraryEntity): Promise> { - const pathValidation = await Promise.all( - library.importPaths.map(async (importPath) => await this.validateImportPath(importPath)), - ); - - const validImportPaths = pathValidation - .map((validation) => { - if (!validation.isValid) { - this.logger.error(`Skipping invalid import path: ${validation.importPath}. Reason: ${validation.message}`); - } - return validation; - }) - .filter((validation) => validation.isValid) - .map((validation) => validation.importPath); - - const generator = this.storageRepository.walk({ - pathsToCrawl: validImportPaths, - exclusionPatterns: library.exclusionPatterns, - }); - - const trie = new Trie(); - for await (const filePath of generator) { - trie.add(filePath); - } - - return trie; - } - private async findOrFail(id: string) { const library = await this.repository.get(id); if (!library) { diff --git a/server/src/services/microservices.service.ts b/server/src/services/microservices.service.ts index 5b28e6a00a189..025400cc9bde3 100644 --- a/server/src/services/microservices.service.ts +++ b/server/src/services/microservices.service.ts @@ -85,7 +85,8 @@ export class MicroservicesService { [JobName.LIBRARY_SCAN_ASSET]: (data) => this.libraryService.handleAssetRefresh(data), [JobName.LIBRARY_SCAN]: (data) => this.libraryService.handleQueueAssetRefresh(data), [JobName.LIBRARY_DELETE]: (data) => this.libraryService.handleDeleteLibrary(data), - [JobName.LIBRARY_REMOVE_OFFLINE]: (data) => this.libraryService.handleOfflineRemoval(data), + [JobName.LIBRARY_CHECK_OFFLINE]: (data) => this.libraryService.handleOfflineCheck(data), + [JobName.LIBRARY_REMOVE_OFFLINE]: (data) => this.libraryService.handleRemoveOffline(data), [JobName.LIBRARY_QUEUE_SCAN_ALL]: (data) => this.libraryService.handleQueueAllScan(data), [JobName.LIBRARY_QUEUE_CLEANUP]: () => this.libraryService.handleQueueCleanup(), [JobName.SEND_EMAIL]: (data) => this.notificationService.handleSendEmail(data), diff --git a/server/test/repositories/library.repository.mock.ts b/server/test/repositories/library.repository.mock.ts index e5b8e5c7636d6..83e97c7ffaa9b 100644 --- a/server/test/repositories/library.repository.mock.ts +++ b/server/test/repositories/library.repository.mock.ts @@ -9,7 +9,6 @@ export const newLibraryRepositoryMock = (): Mocked => { softDelete: vitest.fn(), update: vitest.fn(), getStatistics: vitest.fn(), - getAssetIds: vitest.fn(), getAllDeleted: vitest.fn(), getAll: vitest.fn(), }; diff --git a/web/src/routes/admin/library-management/+page.svelte b/web/src/routes/admin/library-management/+page.svelte index 64b104624b6a9..74db5628ba3a6 100644 --- a/web/src/routes/admin/library-management/+page.svelte +++ b/web/src/routes/admin/library-management/+page.svelte @@ -329,17 +329,21 @@ {:else}{owner[index].name}{/if} - - {#if totalCount[index] == undefined} - + + {#if totalCount[index] == undefined} - - {:else} - + {:else} {totalCount[index].toLocaleString($locale)} - - {diskUsage[index]} {diskUsageUnit[index]} - {/if} + {/if} + + + {#if diskUsage[index] == undefined} + + {:else} + {diskUsage[index]} + {diskUsageUnit[index]} + {/if} + Date: Wed, 28 Aug 2024 21:59:09 +0200 Subject: [PATCH 048/160] feat(server): sort images in duplicate groups by date (#12094) * feat(server): sort images in duplicate groups by date * Update server/src/dtos/duplicate.dto.ts Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> --------- Co-authored-by: Alex Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> --- server/src/dtos/duplicate.dto.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/src/dtos/duplicate.dto.ts b/server/src/dtos/duplicate.dto.ts index 73863fa95da51..09976b3213595 100644 --- a/server/src/dtos/duplicate.dto.ts +++ b/server/src/dtos/duplicate.dto.ts @@ -1,5 +1,5 @@ import { IsNotEmpty } from 'class-validator'; -import { groupBy } from 'lodash'; +import { groupBy, sortBy } from 'lodash'; import { AssetResponseDto } from 'src/dtos/asset-response.dto'; import { ValidateUUID } from 'src/validation'; @@ -19,7 +19,8 @@ export function mapDuplicateResponse(assets: AssetResponseDto[]): DuplicateRespo const grouped = groupBy(assets, (a) => a.duplicateId); - for (const [duplicateId, assets] of Object.entries(grouped)) { + for (const [duplicateId, unsortedAssets] of Object.entries(grouped)) { + const assets = sortBy(unsortedAssets, (asset) => asset.localDateTime); result.push({ duplicateId, assets }); } From f0c86846e09bbb15fb2dd2128cfd0d01cdda11b8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 17:59:57 -0400 Subject: [PATCH 049/160] fix(deps): update machine-learning (major) (#11928) --- machine-learning/poetry.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/machine-learning/poetry.lock b/machine-learning/poetry.lock index 31949aee84ccb..d52e22dafb082 100644 --- a/machine-learning/poetry.lock +++ b/machine-learning/poetry.lock @@ -1111,13 +1111,13 @@ test = ["objgraph", "psutil"] [[package]] name = "gunicorn" -version = "22.0.0" +version = "23.0.0" description = "WSGI HTTP Server for UNIX" optional = false python-versions = ">=3.7" files = [ - {file = "gunicorn-22.0.0-py3-none-any.whl", hash = "sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9"}, - {file = "gunicorn-22.0.0.tar.gz", hash = "sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63"}, + {file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"}, + {file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"}, ] [package.dependencies] @@ -2512,13 +2512,13 @@ testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] [[package]] name = "pytest-cov" -version = "4.1.0" +version = "5.0.0" description = "Pytest plugin for measuring coverage." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, - {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, ] [package.dependencies] @@ -2526,7 +2526,7 @@ coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-mock" From c6c7c54fa5d75f9345c81271459cfb9cc627e6bf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 18:00:47 -0400 Subject: [PATCH 050/160] chore(deps): update machine-learning (#12062) --- machine-learning/Dockerfile | 4 +- machine-learning/export/Dockerfile | 2 +- machine-learning/poetry.lock | 96 +++++++++++++++--------------- 3 files changed, 51 insertions(+), 51 deletions(-) diff --git a/machine-learning/Dockerfile b/machine-learning/Dockerfile index c06b4900e699d..8fc72b308f08a 100644 --- a/machine-learning/Dockerfile +++ b/machine-learning/Dockerfile @@ -1,6 +1,6 @@ ARG DEVICE=cpu -FROM python:3.11-bookworm@sha256:add76c758e402c3acf53b8251da50d8ae67989a81ca96ff4331e296773df853d AS builder-cpu +FROM python:3.11-bookworm@sha256:f7543d9969bdc112dd9819ca642e14433fdacfe857f170f6b803392fc7e451ad AS builder-cpu FROM builder-cpu AS builder-openvino @@ -34,7 +34,7 @@ RUN python3 -m venv /opt/venv COPY poetry.lock pyproject.toml ./ RUN poetry install --sync --no-interaction --no-ansi --no-root --with ${DEVICE} --without dev -FROM python:3.11-slim-bookworm@sha256:1c0c54195c7c7b46e61a2f3b906e9b55a8165f20388a0eeb4af4c6f8579988ac AS prod-cpu +FROM python:3.11-slim-bookworm@sha256:ad5dadd957a398226996bc4846e522c39f2a77340b531b28aaab85b2d361210b AS prod-cpu FROM prod-cpu AS prod-openvino diff --git a/machine-learning/export/Dockerfile b/machine-learning/export/Dockerfile index 94082ae9573d8..d458d92d15014 100644 --- a/machine-learning/export/Dockerfile +++ b/machine-learning/export/Dockerfile @@ -1,4 +1,4 @@ -FROM mambaorg/micromamba:bookworm-slim@sha256:e37ec9f3f7dea01ef9958d3d924d46077911f7e29c4faed40cd6b37a9ac239fc AS builder +FROM mambaorg/micromamba:bookworm-slim@sha256:475730daef12ff9c0733e70092aeeefdf4c373a584c952dac3f7bdb739601990 AS builder ENV TRANSFORMERS_CACHE=/cache \ PYTHONDONTWRITEBYTECODE=1 \ diff --git a/machine-learning/poetry.lock b/machine-learning/poetry.lock index d52e22dafb082..7385d1269d3f8 100644 --- a/machine-learning/poetry.lock +++ b/machine-learning/poetry.lock @@ -2373,54 +2373,54 @@ files = [ [[package]] name = "pydantic" -version = "1.10.17" +version = "1.10.18" description = "Data validation and settings management using python type hints" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-1.10.17-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fa51175313cc30097660b10eec8ca55ed08bfa07acbfe02f7a42f6c242e9a4b"}, - {file = "pydantic-1.10.17-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7e8988bb16988890c985bd2093df9dd731bfb9d5e0860db054c23034fab8f7a"}, - {file = "pydantic-1.10.17-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:371dcf1831f87c9e217e2b6a0c66842879a14873114ebb9d0861ab22e3b5bb1e"}, - {file = "pydantic-1.10.17-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4866a1579c0c3ca2c40575398a24d805d4db6cb353ee74df75ddeee3c657f9a7"}, - {file = "pydantic-1.10.17-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:543da3c6914795b37785703ffc74ba4d660418620cc273490d42c53949eeeca6"}, - {file = "pydantic-1.10.17-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7623b59876f49e61c2e283551cc3647616d2fbdc0b4d36d3d638aae8547ea681"}, - {file = "pydantic-1.10.17-cp310-cp310-win_amd64.whl", hash = "sha256:409b2b36d7d7d19cd8310b97a4ce6b1755ef8bd45b9a2ec5ec2b124db0a0d8f3"}, - {file = "pydantic-1.10.17-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fa43f362b46741df8f201bf3e7dff3569fa92069bcc7b4a740dea3602e27ab7a"}, - {file = "pydantic-1.10.17-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2a72d2a5ff86a3075ed81ca031eac86923d44bc5d42e719d585a8eb547bf0c9b"}, - {file = "pydantic-1.10.17-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4ad32aed3bf5eea5ca5decc3d1bbc3d0ec5d4fbcd72a03cdad849458decbc63"}, - {file = "pydantic-1.10.17-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeb4e741782e236ee7dc1fb11ad94dc56aabaf02d21df0e79e0c21fe07c95741"}, - {file = "pydantic-1.10.17-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d2f89a719411cb234105735a520b7c077158a81e0fe1cb05a79c01fc5eb59d3c"}, - {file = "pydantic-1.10.17-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db3b48d9283d80a314f7a682f7acae8422386de659fffaba454b77a083c3937d"}, - {file = "pydantic-1.10.17-cp311-cp311-win_amd64.whl", hash = "sha256:9c803a5113cfab7bbb912f75faa4fc1e4acff43e452c82560349fff64f852e1b"}, - {file = "pydantic-1.10.17-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:820ae12a390c9cbb26bb44913c87fa2ff431a029a785642c1ff11fed0a095fcb"}, - {file = "pydantic-1.10.17-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c1e51d1af306641b7d1574d6d3307eaa10a4991542ca324f0feb134fee259815"}, - {file = "pydantic-1.10.17-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e53fb834aae96e7b0dadd6e92c66e7dd9cdf08965340ed04c16813102a47fab"}, - {file = "pydantic-1.10.17-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e2495309b1266e81d259a570dd199916ff34f7f51f1b549a0d37a6d9b17b4dc"}, - {file = "pydantic-1.10.17-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:098ad8de840c92ea586bf8efd9e2e90c6339d33ab5c1cfbb85be66e4ecf8213f"}, - {file = "pydantic-1.10.17-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:525bbef620dac93c430d5d6bdbc91bdb5521698d434adf4434a7ef6ffd5c4b7f"}, - {file = "pydantic-1.10.17-cp312-cp312-win_amd64.whl", hash = "sha256:6654028d1144df451e1da69a670083c27117d493f16cf83da81e1e50edce72ad"}, - {file = "pydantic-1.10.17-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c87cedb4680d1614f1d59d13fea353faf3afd41ba5c906a266f3f2e8c245d655"}, - {file = "pydantic-1.10.17-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11289fa895bcbc8f18704efa1d8020bb9a86314da435348f59745473eb042e6b"}, - {file = "pydantic-1.10.17-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94833612d6fd18b57c359a127cbfd932d9150c1b72fea7c86ab58c2a77edd7c7"}, - {file = "pydantic-1.10.17-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d4ecb515fa7cb0e46e163ecd9d52f9147ba57bc3633dca0e586cdb7a232db9e3"}, - {file = "pydantic-1.10.17-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7017971ffa7fd7808146880aa41b266e06c1e6e12261768a28b8b41ba55c8076"}, - {file = "pydantic-1.10.17-cp37-cp37m-win_amd64.whl", hash = "sha256:e840e6b2026920fc3f250ea8ebfdedf6ea7a25b77bf04c6576178e681942ae0f"}, - {file = "pydantic-1.10.17-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bfbb18b616abc4df70591b8c1ff1b3eabd234ddcddb86b7cac82657ab9017e33"}, - {file = "pydantic-1.10.17-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebb249096d873593e014535ab07145498957091aa6ae92759a32d40cb9998e2e"}, - {file = "pydantic-1.10.17-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8c209af63ccd7b22fba94b9024e8b7fd07feffee0001efae50dd99316b27768"}, - {file = "pydantic-1.10.17-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4b40c9e13a0b61583e5599e7950490c700297b4a375b55b2b592774332798b7"}, - {file = "pydantic-1.10.17-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c31d281c7485223caf6474fc2b7cf21456289dbaa31401844069b77160cab9c7"}, - {file = "pydantic-1.10.17-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ae5184e99a060a5c80010a2d53c99aee76a3b0ad683d493e5f0620b5d86eeb75"}, - {file = "pydantic-1.10.17-cp38-cp38-win_amd64.whl", hash = "sha256:ad1e33dc6b9787a6f0f3fd132859aa75626528b49cc1f9e429cdacb2608ad5f0"}, - {file = "pydantic-1.10.17-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e17c0ee7192e54a10943f245dc79e36d9fe282418ea05b886e1c666063a7b54"}, - {file = "pydantic-1.10.17-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cafb9c938f61d1b182dfc7d44a7021326547b7b9cf695db5b68ec7b590214773"}, - {file = "pydantic-1.10.17-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95ef534e3c22e5abbdbdd6f66b6ea9dac3ca3e34c5c632894f8625d13d084cbe"}, - {file = "pydantic-1.10.17-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d96b8799ae3d782df7ec9615cb59fc32c32e1ed6afa1b231b0595f6516e8ab"}, - {file = "pydantic-1.10.17-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ab2f976336808fd5d539fdc26eb51f9aafc1f4b638e212ef6b6f05e753c8011d"}, - {file = "pydantic-1.10.17-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8ad363330557beac73159acfbeed220d5f1bfcd6b930302a987a375e02f74fd"}, - {file = "pydantic-1.10.17-cp39-cp39-win_amd64.whl", hash = "sha256:48db882e48575ce4b39659558b2f9f37c25b8d348e37a2b4e32971dd5a7d6227"}, - {file = "pydantic-1.10.17-py3-none-any.whl", hash = "sha256:e41b5b973e5c64f674b3b4720286ded184dcc26a691dd55f34391c62c6934688"}, - {file = "pydantic-1.10.17.tar.gz", hash = "sha256:f434160fb14b353caf634149baaf847206406471ba70e64657c1e8330277a991"}, + {file = "pydantic-1.10.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e405ffcc1254d76bb0e760db101ee8916b620893e6edfbfee563b3c6f7a67c02"}, + {file = "pydantic-1.10.18-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e306e280ebebc65040034bff1a0a81fd86b2f4f05daac0131f29541cafd80b80"}, + {file = "pydantic-1.10.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11d9d9b87b50338b1b7de4ebf34fd29fdb0d219dc07ade29effc74d3d2609c62"}, + {file = "pydantic-1.10.18-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b661ce52c7b5e5f600c0c3c5839e71918346af2ef20062705ae76b5c16914cab"}, + {file = "pydantic-1.10.18-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c20f682defc9ef81cd7eaa485879ab29a86a0ba58acf669a78ed868e72bb89e0"}, + {file = "pydantic-1.10.18-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c5ae6b7c8483b1e0bf59e5f1843e4fd8fd405e11df7de217ee65b98eb5462861"}, + {file = "pydantic-1.10.18-cp310-cp310-win_amd64.whl", hash = "sha256:74fe19dda960b193b0eb82c1f4d2c8e5e26918d9cda858cbf3f41dd28549cb70"}, + {file = "pydantic-1.10.18-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:72fa46abace0a7743cc697dbb830a41ee84c9db8456e8d77a46d79b537efd7ec"}, + {file = "pydantic-1.10.18-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef0fe7ad7cbdb5f372463d42e6ed4ca9c443a52ce544472d8842a0576d830da5"}, + {file = "pydantic-1.10.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a00e63104346145389b8e8f500bc6a241e729feaf0559b88b8aa513dd2065481"}, + {file = "pydantic-1.10.18-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae6fa2008e1443c46b7b3a5eb03800121868d5ab6bc7cda20b5df3e133cde8b3"}, + {file = "pydantic-1.10.18-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9f463abafdc92635da4b38807f5b9972276be7c8c5121989768549fceb8d2588"}, + {file = "pydantic-1.10.18-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3445426da503c7e40baccefb2b2989a0c5ce6b163679dd75f55493b460f05a8f"}, + {file = "pydantic-1.10.18-cp311-cp311-win_amd64.whl", hash = "sha256:467a14ee2183bc9c902579bb2f04c3d3dac00eff52e252850509a562255b2a33"}, + {file = "pydantic-1.10.18-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:efbc8a7f9cb5fe26122acba1852d8dcd1e125e723727c59dcd244da7bdaa54f2"}, + {file = "pydantic-1.10.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:24a4a159d0f7a8e26bf6463b0d3d60871d6a52eac5bb6a07a7df85c806f4c048"}, + {file = "pydantic-1.10.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b74be007703547dc52e3c37344d130a7bfacca7df112a9e5ceeb840a9ce195c7"}, + {file = "pydantic-1.10.18-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcb20d4cb355195c75000a49bb4a31d75e4295200df620f454bbc6bdf60ca890"}, + {file = "pydantic-1.10.18-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:46f379b8cb8a3585e3f61bf9ae7d606c70d133943f339d38b76e041ec234953f"}, + {file = "pydantic-1.10.18-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cbfbca662ed3729204090c4d09ee4beeecc1a7ecba5a159a94b5a4eb24e3759a"}, + {file = "pydantic-1.10.18-cp312-cp312-win_amd64.whl", hash = "sha256:c6d0a9f9eccaf7f438671a64acf654ef0d045466e63f9f68a579e2383b63f357"}, + {file = "pydantic-1.10.18-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3d5492dbf953d7d849751917e3b2433fb26010d977aa7a0765c37425a4026ff1"}, + {file = "pydantic-1.10.18-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe734914977eed33033b70bfc097e1baaffb589517863955430bf2e0846ac30f"}, + {file = "pydantic-1.10.18-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15fdbe568beaca9aacfccd5ceadfb5f1a235087a127e8af5e48df9d8a45ae85c"}, + {file = "pydantic-1.10.18-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c3e742f62198c9eb9201781fbebe64533a3bbf6a76a91b8d438d62b813079dbc"}, + {file = "pydantic-1.10.18-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:19a3bd00b9dafc2cd7250d94d5b578edf7a0bd7daf102617153ff9a8fa37871c"}, + {file = "pydantic-1.10.18-cp37-cp37m-win_amd64.whl", hash = "sha256:2ce3fcf75b2bae99aa31bd4968de0474ebe8c8258a0110903478bd83dfee4e3b"}, + {file = "pydantic-1.10.18-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:335a32d72c51a313b33fa3a9b0fe283503272ef6467910338e123f90925f0f03"}, + {file = "pydantic-1.10.18-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:34a3613c7edb8c6fa578e58e9abe3c0f5e7430e0fc34a65a415a1683b9c32d9a"}, + {file = "pydantic-1.10.18-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9ee4e6ca1d9616797fa2e9c0bfb8815912c7d67aca96f77428e316741082a1b"}, + {file = "pydantic-1.10.18-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23e8ec1ce4e57b4f441fc91e3c12adba023fedd06868445a5b5f1d48f0ab3682"}, + {file = "pydantic-1.10.18-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:44ae8a3e35a54d2e8fa88ed65e1b08967a9ef8c320819a969bfa09ce5528fafe"}, + {file = "pydantic-1.10.18-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5389eb3b48a72da28c6e061a247ab224381435256eb541e175798483368fdd3"}, + {file = "pydantic-1.10.18-cp38-cp38-win_amd64.whl", hash = "sha256:069b9c9fc645474d5ea3653788b544a9e0ccd3dca3ad8c900c4c6eac844b4620"}, + {file = "pydantic-1.10.18-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:80b982d42515632eb51f60fa1d217dfe0729f008e81a82d1544cc392e0a50ddf"}, + {file = "pydantic-1.10.18-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:aad8771ec8dbf9139b01b56f66386537c6fe4e76c8f7a47c10261b69ad25c2c9"}, + {file = "pydantic-1.10.18-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941a2eb0a1509bd7f31e355912eb33b698eb0051730b2eaf9e70e2e1589cae1d"}, + {file = "pydantic-1.10.18-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65f7361a09b07915a98efd17fdec23103307a54db2000bb92095457ca758d485"}, + {file = "pydantic-1.10.18-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6951f3f47cb5ca4da536ab161ac0163cab31417d20c54c6de5ddcab8bc813c3f"}, + {file = "pydantic-1.10.18-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7a4c5eec138a9b52c67f664c7d51d4c7234c5ad65dd8aacd919fb47445a62c86"}, + {file = "pydantic-1.10.18-cp39-cp39-win_amd64.whl", hash = "sha256:49e26c51ca854286bffc22b69787a8d4063a62bf7d83dc21d44d2ff426108518"}, + {file = "pydantic-1.10.18-py3-none-any.whl", hash = "sha256:06a189b81ffc52746ec9c8c007f16e5167c8b0a696e1a726369327e3db7b2a82"}, + {file = "pydantic-1.10.18.tar.gz", hash = "sha256:baebdff1907d1d96a139c25136a9bb7d17e118f133a76a2ef3b845e831e3403a"}, ] [package.dependencies] @@ -2494,17 +2494,17 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments [[package]] name = "pytest-asyncio" -version = "0.23.8" +version = "0.24.0" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "pytest_asyncio-0.23.8-py3-none-any.whl", hash = "sha256:50265d892689a5faefb84df80819d1ecef566eb3549cf915dfb33569359d1ce2"}, - {file = "pytest_asyncio-0.23.8.tar.gz", hash = "sha256:759b10b33a6dc61cce40a8bd5205e302978bbbcc00e279a8b61d9a6a3c82e4d3"}, + {file = "pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b"}, + {file = "pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276"}, ] [package.dependencies] -pytest = ">=7.0.0,<9" +pytest = ">=8.2,<9" [package.extras] docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] From bab5ad7ebd34cb792054c090068e571e8c92ef7e Mon Sep 17 00:00:00 2001 From: Jonathan Jogenfors Date: Thu, 29 Aug 2024 01:51:25 +0200 Subject: [PATCH 051/160] fix(server): ensure new exclusion patterns work (#12102) * add test for bug * find excluded paths when checking offline * fix filename * fix unit tests * bump picomatch * fix e2e paths * improve e2e * add unit tests * cleanup e2e * set correct asset count * fix e2e test * fix lint --- e2e/src/api/specs/library.e2e-spec.ts | 67 ++++++++++++++++----- server/package-lock.json | 3 +- server/package.json | 2 +- server/src/interfaces/job.interface.ts | 1 + server/src/services/library.service.spec.ts | 21 ++++++- server/src/services/library.service.ts | 11 +++- 6 files changed, 85 insertions(+), 20 deletions(-) diff --git a/e2e/src/api/specs/library.e2e-spec.ts b/e2e/src/api/specs/library.e2e-spec.ts index 013e1364ca783..ec42cbe4fa9db 100644 --- a/e2e/src/api/specs/library.e2e-spec.ts +++ b/e2e/src/api/specs/library.e2e-spec.ts @@ -353,7 +353,7 @@ describe('/libraries', () => { expect(assets.count).toBe(2); - utils.createImageFile(`${testAssetDir}/temp/directoryA/assetB.png`); + utils.createImageFile(`${testAssetDir}/temp/directoryA/assetC.png`); await scan(admin.accessToken, library.id); await utils.waitForWebsocketEvent({ event: 'assetUpload', total: 3 }); @@ -361,11 +361,11 @@ describe('/libraries', () => { const { assets: newAssets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id }); expect(newAssets.count).toBe(3); - utils.removeImageFile(`${testAssetDir}/temp/directoryA/assetB.png`); + utils.removeImageFile(`${testAssetDir}/temp/directoryA/assetC.png`); }); it('should offline a file missing from disk', async () => { - utils.createImageFile(`${testAssetDir}/temp/directoryA/assetB.png`); + utils.createImageFile(`${testAssetDir}/temp/directoryA/assetC.png`); const library = await utils.createLibrary(admin.accessToken, { ownerId: admin.userId, importPaths: [`${testAssetDirInternal}/temp`], @@ -374,26 +374,28 @@ describe('/libraries', () => { await scan(admin.accessToken, library.id); await utils.waitForQueueFinish(admin.accessToken, 'library'); - utils.removeImageFile(`${testAssetDir}/temp/directoryA/assetB.png`); + const { assets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id }); + expect(assets.count).toBe(3); + + utils.removeImageFile(`${testAssetDir}/temp/directoryA/assetC.png`); await scan(admin.accessToken, library.id); await utils.waitForQueueFinish(admin.accessToken, 'library'); - const { assets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id }); + const { assets: newAssets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id }); + expect(newAssets.count).toBe(3); - expect(assets.items).toEqual( + expect(newAssets.items).toEqual( expect.arrayContaining([ expect.objectContaining({ isOffline: true, - originalFileName: 'assetB.png', + originalFileName: 'assetC.png', }), ]), ); }); it('should offline a file outside of import paths', async () => { - utils.createImageFile(`${testAssetDir}/temp/directoryA/assetB.png`); - utils.createImageFile(`${testAssetDir}/temp/directoryB/assetC.png`); const library = await utils.createLibrary(admin.accessToken, { ownerId: admin.userId, importPaths: [`${testAssetDirInternal}/temp`], @@ -416,16 +418,49 @@ describe('/libraries', () => { expect.arrayContaining([ expect.objectContaining({ isOffline: false, - originalFileName: 'assetB.png', + originalFileName: 'assetA.png', }), expect.objectContaining({ isOffline: true, - originalFileName: 'assetC.png', + originalFileName: 'assetB.png', }), ]), ); + }); - utils.removeImageFile(`${testAssetDir}/temp/directoryB/assetC.png`); + it('should offline a file covered by an exclusion pattern', async () => { + const library = await utils.createLibrary(admin.accessToken, { + ownerId: admin.userId, + importPaths: [`${testAssetDirInternal}/temp`], + }); + + await scan(admin.accessToken, library.id); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + + await request(app) + .put(`/libraries/${library.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ exclusionPatterns: ['**/directoryB/**'] }); + + await scan(admin.accessToken, library.id); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + + const { assets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id }); + + expect(assets.count).toBe(2); + + expect(assets.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + isOffline: false, + originalFileName: 'assetA.png', + }), + expect.objectContaining({ + isOffline: true, + originalFileName: 'assetB.png', + }), + ]), + ); }); it('should not try to delete offline files', async () => { @@ -471,6 +506,8 @@ describe('/libraries', () => { await utils.waitForWebsocketEvent({ event: 'assetDelete', total: 1 }); expect(existsSync(`${testAssetDir}/temp/offline1/assetA.png`)).toBe(true); + + utils.removeImageFile(`${testAssetDir}/temp/offline1/assetA.png`); }); it('should scan new files', async () => { @@ -482,14 +519,14 @@ describe('/libraries', () => { await scan(admin.accessToken, library.id); await utils.waitForQueueFinish(admin.accessToken, 'library'); - utils.createImageFile(`${testAssetDir}/temp/directoryA/assetC.png`); + utils.createImageFile(`${testAssetDir}/temp/directoryC/assetC.png`); await scan(admin.accessToken, library.id); await utils.waitForQueueFinish(admin.accessToken, 'library'); - utils.removeImageFile(`${testAssetDir}/temp/directoryA/assetC.png`); const { assets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id }); + expect(assets.count).toBe(3); expect(assets.items).toEqual( expect.arrayContaining([ expect.objectContaining({ @@ -497,6 +534,8 @@ describe('/libraries', () => { }), ]), ); + + utils.removeImageFile(`${testAssetDir}/temp/directoryC/assetC.png`); }); describe('with refreshModifiedFiles=true', () => { diff --git a/server/package-lock.json b/server/package-lock.json index 1ec4fe0fb0006..e90256e29b1f5 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -51,7 +51,7 @@ "nodemailer": "^6.9.13", "openid-client": "^5.4.3", "pg": "^8.11.3", - "picomatch": "^4.0.0", + "picomatch": "^4.0.2", "react": "^18.3.1", "react-email": "^3.0.0", "reflect-metadata": "^0.2.0", @@ -11403,6 +11403,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "license": "MIT", "engines": { "node": ">=12" }, diff --git a/server/package.json b/server/package.json index 9b429222787a7..42552f20b74eb 100644 --- a/server/package.json +++ b/server/package.json @@ -77,7 +77,7 @@ "nodemailer": "^6.9.13", "openid-client": "^5.4.3", "pg": "^8.11.3", - "picomatch": "^4.0.0", + "picomatch": "^4.0.2", "react": "^18.3.1", "react-email": "^3.0.0", "reflect-metadata": "^0.2.0", diff --git a/server/src/interfaces/job.interface.ts b/server/src/interfaces/job.interface.ts index fab959936f0a9..b2ac5ec6f12d9 100644 --- a/server/src/interfaces/job.interface.ts +++ b/server/src/interfaces/job.interface.ts @@ -133,6 +133,7 @@ export interface ILibraryFileJob extends IEntityJob { export interface ILibraryOfflineJob extends IEntityJob { importPaths: string[]; + exclusionPatterns: string[]; } export interface ILibraryRefreshJob extends IEntityJob { diff --git a/server/src/services/library.service.spec.ts b/server/src/services/library.service.spec.ts index 9e260e98efa15..2d4e1d5776bfe 100644 --- a/server/src/services/library.service.spec.ts +++ b/server/src/services/library.service.spec.ts @@ -301,6 +301,7 @@ describe(LibraryService.name, () => { const mockAssetJob: ILibraryOfflineJob = { id: assetStub.external.id, importPaths: ['/'], + exclusionPatterns: [], }; assetMock.getById.mockResolvedValue(null); @@ -314,6 +315,7 @@ describe(LibraryService.name, () => { const mockAssetJob: ILibraryOfflineJob = { id: assetStub.external.id, importPaths: ['/'], + exclusionPatterns: [], }; assetMock.getById.mockResolvedValue(assetStub.offline); @@ -323,10 +325,25 @@ describe(LibraryService.name, () => { expect(assetMock.update).not.toHaveBeenCalled(); }); - it('should offline assets no longer on disk or matching exclusion pattern', async () => { + it('should offline assets no longer on disk', async () => { const mockAssetJob: ILibraryOfflineJob = { id: assetStub.external.id, importPaths: ['/'], + exclusionPatterns: [], + }; + + assetMock.getById.mockResolvedValue(assetStub.external); + + await expect(sut.handleOfflineCheck(mockAssetJob)).resolves.toBe(JobStatus.SUCCESS); + + expect(assetMock.update).toHaveBeenCalledWith({ id: assetStub.external.id, isOffline: true }); + }); + + it('should offline assets matching an exclusion pattern', async () => { + const mockAssetJob: ILibraryOfflineJob = { + id: assetStub.external.id, + importPaths: ['/'], + exclusionPatterns: ['**/user1/**'], }; assetMock.getById.mockResolvedValue(assetStub.external); @@ -340,6 +357,7 @@ describe(LibraryService.name, () => { const mockAssetJob: ILibraryOfflineJob = { id: assetStub.external.id, importPaths: ['/data/user2'], + exclusionPatterns: [], }; assetMock.getById.mockResolvedValue(assetStub.external); @@ -354,6 +372,7 @@ describe(LibraryService.name, () => { const mockAssetJob: ILibraryOfflineJob = { id: assetStub.external.id, importPaths: ['/'], + exclusionPatterns: [], }; assetMock.getById.mockResolvedValue(assetStub.external); diff --git a/server/src/services/library.service.ts b/server/src/services/library.service.ts index 9e31107027c56..c7f82eddea5d8 100644 --- a/server/src/services/library.service.ts +++ b/server/src/services/library.service.ts @@ -556,11 +556,16 @@ export class LibraryService { return JobStatus.SUCCESS; } + const isExcluded = job.exclusionPatterns.some((pattern) => picomatch.isMatch(asset.originalPath, pattern)); + if (isExcluded) { + this.logger.debug(`Asset is covered by an exclusion pattern, marking offline: ${asset.originalPath}`); + await this.assetRepository.update({ id: asset.id, isOffline: true }); + return JobStatus.SUCCESS; + } + const fileExists = await this.storageRepository.checkFileExists(asset.originalPath, R_OK); if (!fileExists) { - this.logger.debug( - `Asset is no longer found on disk or is covered by exclusion pattern, marking offline: ${asset.originalPath}`, - ); + this.logger.debug(`Asset is no longer found on disk, marking offline: ${asset.originalPath}`); await this.assetRepository.update({ id: asset.id, isOffline: true }); return JobStatus.SUCCESS; } From 9f5a3f1e84b6cb6f0144eec1b0a3b248784a6da1 Mon Sep 17 00:00:00 2001 From: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Date: Thu, 29 Aug 2024 14:41:39 +0200 Subject: [PATCH 052/160] chore(web): enforce valid translation keys using typescript (#12106) --- web/src/app.d.ts | 28 +++++++++++++++++ .../i18n/__test__/format-message.spec.ts | 24 +++++++------- .../i18n/__test__/format-tag-b.svelte | 3 +- .../i18n/format-bold-message.svelte | 3 +- .../lib/components/i18n/format-message.svelte | 4 +-- web/src/lib/i18n.spec.ts | 31 ------------------- web/src/routes/+page.ts | 4 +-- 7 files changed, 48 insertions(+), 49 deletions(-) diff --git a/web/src/app.d.ts b/web/src/app.d.ts index 4fcb901892306..b13a0c97d59de 100644 --- a/web/src/app.d.ts +++ b/web/src/app.d.ts @@ -27,3 +27,31 @@ interface Element { // Make optional, because it's unavailable on iPhones. requestFullscreen?(options?: FullscreenOptions): Promise; } + +import type en from '$lib/i18n/en.json'; +import 'svelte-i18n'; + +type NestedKeys = K extends keyof T & string + ? `${K}` | (T[K] extends object ? `${K}.${NestedKeys}` : never) + : never; + +declare module 'svelte-i18n' { + import type { InterpolationValues } from '$lib/components/i18n/format-message.svelte'; + import type { Readable } from 'svelte/store'; + + type Translations = NestedKeys; + + interface MessageObject { + id: Translations; + locale?: string; + format?: string; + default?: string; + values?: InterpolationValues; + } + + type MessageFormatter = (id: Translations | MessageObject, options?: Omit) => string; + + const format: Readable; + const t: Readable; + const _: Readable; +} diff --git a/web/src/lib/components/i18n/__test__/format-message.spec.ts b/web/src/lib/components/i18n/__test__/format-message.spec.ts index 589d9024e7ada..52eb77c80bd6d 100644 --- a/web/src/lib/components/i18n/__test__/format-message.spec.ts +++ b/web/src/lib/components/i18n/__test__/format-message.spec.ts @@ -2,7 +2,7 @@ import FormatTagB from '$lib/components/i18n/__test__/format-tag-b.svelte'; import FormatMessage from '$lib/components/i18n/format-message.svelte'; import '@testing-library/jest-dom'; import { render, screen } from '@testing-library/svelte'; -import { init, locale, register, waitLocale } from 'svelte-i18n'; +import { init, locale, register, waitLocale, type Translations } from 'svelte-i18n'; import { describe } from 'vitest'; describe('FormatMessage component', () => { @@ -25,7 +25,7 @@ describe('FormatMessage component', () => { it('formats a plain text message', () => { render(FormatMessage, { - key: 'hello', + key: 'hello' as Translations, values: { name: 'test' }, }); expect(screen.getByText('Hello test')).toBeInTheDocument(); @@ -33,20 +33,20 @@ describe('FormatMessage component', () => { it('throws an error when locale is empty', async () => { await locale.set(undefined); - expect(() => render(FormatMessage, { key: '' })).toThrowError(); + expect(() => render(FormatMessage, { key: '' as Translations })).toThrowError(); await locale.set('en'); }); it('shows raw message when value is empty', () => { render(FormatMessage, { - key: 'hello', + key: 'hello' as Translations, }); expect(screen.getByText('Hello {name}')).toBeInTheDocument(); }); it('shows message when slot is empty', () => { render(FormatMessage, { - key: 'html', + key: 'html' as Translations, values: { name: 'test' }, }); expect(screen.getByText('Hello test')).toBeInTheDocument(); @@ -54,7 +54,7 @@ describe('FormatMessage component', () => { it('renders a message with html', () => { const { container } = render(FormatTagB, { - key: 'html', + key: 'html' as Translations, values: { name: 'test' }, }); expect(container.innerHTML).toBe('Hello test'); @@ -62,7 +62,7 @@ describe('FormatMessage component', () => { it('renders a message with html and plural', () => { const { container } = render(FormatTagB, { - key: 'plural', + key: 'plural' as Translations, values: { count: 1 }, }); expect(container.innerHTML).toBe('You have 1 item'); @@ -70,19 +70,19 @@ describe('FormatMessage component', () => { it('protects agains XSS injection', () => { render(FormatMessage, { - key: 'xss', + key: 'xss' as Translations, }); expect(screen.getByText('')).toBeInTheDocument(); }); it('displays the message key when not found', () => { - render(FormatMessage, { key: 'invalid.key' }); + render(FormatMessage, { key: 'invalid.key' as Translations }); expect(screen.getByText('invalid.key')).toBeInTheDocument(); }); it('supports html tags inside plurals', () => { const { container } = render(FormatTagB, { - key: 'plural_with_html', + key: 'plural_with_html' as Translations, values: { count: 10 }, }); expect(container.innerHTML).toBe('You have 10 items'); @@ -90,7 +90,7 @@ describe('FormatMessage component', () => { it('supports html tags inside select', () => { const { container } = render(FormatTagB, { - key: 'select_with_html', + key: 'select_with_html' as Translations, values: { status: true }, }); expect(container.innerHTML).toBe('Item is disabled'); @@ -98,7 +98,7 @@ describe('FormatMessage component', () => { it('supports html tags inside selectordinal', () => { const { container } = render(FormatTagB, { - key: 'ordinal_with_html', + key: 'ordinal_with_html' as Translations, values: { count: 4 }, }); expect(container.innerHTML).toBe('4th item'); 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 f06a54a1e0063..122358c6b7a58 100644 --- a/web/src/lib/components/i18n/__test__/format-tag-b.svelte +++ b/web/src/lib/components/i18n/__test__/format-tag-b.svelte @@ -1,8 +1,9 @@ diff --git a/web/src/lib/components/i18n/format-bold-message.svelte b/web/src/lib/components/i18n/format-bold-message.svelte index 6a449e880819d..052b220edc615 100644 --- a/web/src/lib/components/i18n/format-bold-message.svelte +++ b/web/src/lib/components/i18n/format-bold-message.svelte @@ -1,8 +1,9 @@ diff --git a/web/src/lib/components/i18n/format-message.svelte b/web/src/lib/components/i18n/format-message.svelte index d6ff09ed1c3a2..48c59478c6154 100644 --- a/web/src/lib/components/i18n/format-message.svelte +++ b/web/src/lib/components/i18n/format-message.svelte @@ -11,14 +11,14 @@ type PluralElement, type SelectElement, } from '@formatjs/icu-messageformat-parser'; - import { locale as i18nLocale, json } from 'svelte-i18n'; + import { locale as i18nLocale, json, type Translations } from 'svelte-i18n'; type MessagePart = { message: string; tag?: string; }; - export let key: string; + export let key: Translations; export let values: InterpolationValues = {}; const getLocale = (locale?: string | null) => { diff --git a/web/src/lib/i18n.spec.ts b/web/src/lib/i18n.spec.ts index c9261dcec5a08..13d926e6473f3 100644 --- a/web/src/lib/i18n.spec.ts +++ b/web/src/lib/i18n.spec.ts @@ -1,39 +1,8 @@ import { langs } from '$lib/constants'; -import messages from '$lib/i18n/en.json'; import { getClosestAvailableLocale } from '$lib/utils/i18n'; -import { exec as execCallback } from 'node:child_process'; import { readFileSync, readdirSync } from 'node:fs'; -import { promisify } from 'node:util'; - -type Messages = { [key: string]: string | Messages }; - -const exec = promisify(execCallback); - -function setEmptyMessages(messages: Messages) { - const copy = { ...messages }; - - for (const key in copy) { - const message = copy[key]; - if (typeof message === 'string') { - copy[key] = ''; - } else if (typeof message === 'object') { - setEmptyMessages(message); - } - } - - return copy; -} describe('i18n', () => { - test('no missing messages', async () => { - const { stdout } = await exec('npx svelte-i18n extract -c svelte.config.js "src/**/*"'); - const extractedMessages: Messages = JSON.parse(stdout); - const existingMessages = setEmptyMessages(messages); - - // Only translations directly using the store seem to get extracted - expect({ ...extractedMessages, ...existingMessages }).toEqual(existingMessages); - }); - describe('loaders', () => { const languageFiles = readdirSync('src/lib/i18n').sort(); for (const filename of languageFiles) { diff --git a/web/src/routes/+page.ts b/web/src/routes/+page.ts index bcc854cc3cd4d..0f3a7377d2644 100644 --- a/web/src/routes/+page.ts +++ b/web/src/routes/+page.ts @@ -12,7 +12,6 @@ export const ssr = false; export const csr = true; export const load = (async ({ fetch }) => { - let $t = (arg: string) => arg; try { await init(fetch); const authenticated = await loadUser(); @@ -26,7 +25,6 @@ export const load = (async ({ fetch }) => { redirect(302, AppRoute.AUTH_LOGIN); } - $t = await getFormatter(); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (redirectError: any) { if (redirectError?.status === 302) { @@ -34,6 +32,8 @@ export const load = (async ({ fetch }) => { } } + const $t = await getFormatter(); + return { meta: { title: $t('welcome') + ' 🎉', From f3e176e192fe63daba827f61e4bd739c004f00b7 Mon Sep 17 00:00:00 2001 From: Richard Kojedzinszky Date: Thu, 29 Aug 2024 17:11:49 +0200 Subject: [PATCH 053/160] feat(ml): support dynamic scaling (#12065) feat(ml): make http keep-alive configurable Closes #12064 --- docs/docs/install/environment-variables.md | 33 ++++++++++++---------- machine-learning/start.sh | 2 ++ 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/docs/docs/install/environment-variables.md b/docs/docs/install/environment-variables.md index 78cd16cf1b7c2..9a4b0b9360b78 100644 --- a/docs/docs/install/environment-variables.md +++ b/docs/docs/install/environment-variables.md @@ -159,26 +159,29 @@ Redis (Sentinel) URL example JSON before encoding: ## Machine Learning -| Variable | Description | Default | Containers | -| :----------------------------------------------- | :-------------------------------------------------------------------------------------------------- | :-----------------------------------: | :--------------- | -| `MACHINE_LEARNING_MODEL_TTL` | Inactivity time (s) before a model is unloaded (disabled if \<= 0) | `300` | machine learning | -| `MACHINE_LEARNING_MODEL_TTL_POLL_S` | Interval (s) between checks for the model TTL (disabled if \<= 0) | `10` | machine learning | -| `MACHINE_LEARNING_CACHE_FOLDER` | Directory where models are downloaded | `/cache` | machine learning | -| `MACHINE_LEARNING_REQUEST_THREADS`\*1 | Thread count of the request thread pool (disabled if \<= 0) | number of CPU cores | machine learning | -| `MACHINE_LEARNING_MODEL_INTER_OP_THREADS` | Number of parallel model operations | `1` | machine learning | -| `MACHINE_LEARNING_MODEL_INTRA_OP_THREADS` | Number of threads for each model operation | `2` | machine learning | -| `MACHINE_LEARNING_WORKERS`\*2 | Number of worker processes to spawn | `1` | machine learning | -| `MACHINE_LEARNING_WORKER_TIMEOUT` | Maximum time (s) of unresponsiveness before a worker is killed | `120` (`300` if using OpenVINO image) | machine learning | -| `MACHINE_LEARNING_PRELOAD__CLIP` | Name of a CLIP model to be preloaded and kept in cache | | machine learning | -| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION` | Name of a facial recognition model to be preloaded and kept in cache | | machine learning | -| `MACHINE_LEARNING_ANN` | Enable ARM-NN hardware acceleration if supported | `True` | machine learning | -| `MACHINE_LEARNING_ANN_FP16_TURBO` | Execute operations in FP16 precision: increasing speed, reducing precision (applies only to ARM-NN) | `False` | machine learning | -| `MACHINE_LEARNING_ANN_TUNING_LEVEL` | ARM-NN GPU tuning level (1: rapid, 2: normal, 3: exhaustive) | `2` | machine learning | +| Variable | Description | Default | Containers | +| :-------------------------------------------------------- | :-------------------------------------------------------------------------------------------------- | :-----------------------------------: | :--------------- | +| `MACHINE_LEARNING_MODEL_TTL` | Inactivity time (s) before a model is unloaded (disabled if \<= 0) | `300` | machine learning | +| `MACHINE_LEARNING_MODEL_TTL_POLL_S` | Interval (s) between checks for the model TTL (disabled if \<= 0) | `10` | machine learning | +| `MACHINE_LEARNING_CACHE_FOLDER` | Directory where models are downloaded | `/cache` | machine learning | +| `MACHINE_LEARNING_REQUEST_THREADS`\*1 | Thread count of the request thread pool (disabled if \<= 0) | number of CPU cores | machine learning | +| `MACHINE_LEARNING_MODEL_INTER_OP_THREADS` | Number of parallel model operations | `1` | machine learning | +| `MACHINE_LEARNING_MODEL_INTRA_OP_THREADS` | Number of threads for each model operation | `2` | machine learning | +| `MACHINE_LEARNING_WORKERS`\*2 | Number of worker processes to spawn | `1` | machine learning | +| `MACHINE_LEARNING_HTTP_KEEPALIVE_TIMEOUT_S`\*3 | HTTP Keep-alive time in seconds | `2` | machine learning | +| `MACHINE_LEARNING_WORKER_TIMEOUT` | Maximum time (s) of unresponsiveness before a worker is killed | `120` (`300` if using OpenVINO image) | machine learning | +| `MACHINE_LEARNING_PRELOAD__CLIP` | Name of a CLIP model to be preloaded and kept in cache | | machine learning | +| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION` | Name of a facial recognition model to be preloaded and kept in cache | | machine learning | +| `MACHINE_LEARNING_ANN` | Enable ARM-NN hardware acceleration if supported | `True` | machine learning | +| `MACHINE_LEARNING_ANN_FP16_TURBO` | Execute operations in FP16 precision: increasing speed, reducing precision (applies only to ARM-NN) | `False` | machine learning | +| `MACHINE_LEARNING_ANN_TUNING_LEVEL` | ARM-NN GPU tuning level (1: rapid, 2: normal, 3: exhaustive) | `2` | machine learning | \*1: It is recommended to begin with this parameter when changing the concurrency levels of the machine learning service and then tune the other ones. \*2: Since each process duplicates models in memory, changing this is not recommended unless you have abundant memory to go around. +\*3: For scenarios like HPA in K8S. https://github.com/immich-app/immich/discussions/12064 + :::info Other machine learning parameters can be tuned from the admin UI. diff --git a/machine-learning/start.sh b/machine-learning/start.sh index 6b8e55a23657d..c3fda523df832 100755 --- a/machine-learning/start.sh +++ b/machine-learning/start.sh @@ -13,6 +13,7 @@ fi : "${IMMICH_HOST:=[::]}" : "${IMMICH_PORT:=3003}" : "${MACHINE_LEARNING_WORKERS:=1}" +: "${MACHINE_LEARNING_HTTP_KEEPALIVE_TIMEOUT_S:=2}" gunicorn app.main:app \ -k app.config.CustomUvicornWorker \ @@ -20,4 +21,5 @@ gunicorn app.main:app \ -w "$MACHINE_LEARNING_WORKERS" \ -t "$MACHINE_LEARNING_WORKER_TIMEOUT" \ --log-config-json log_conf.json \ + --keep-alive "$MACHINE_LEARNING_HTTP_KEEPALIVE_TIMEOUT_S" \ --graceful-timeout 0 From c008feca6306556e0a704b27b3a056f6c82e4197 Mon Sep 17 00:00:00 2001 From: kaziu687 Date: Thu, 29 Aug 2024 17:40:17 +0200 Subject: [PATCH 054/160] feat(web): navigate assets with gestures (next/prev) (#11888) Co-authored-by: Alex Tran --- web/package-lock.json | 7 +++++++ web/package.json | 1 + .../asset-viewer/asset-viewer.svelte | 19 ++++++++++++++++++- .../asset-viewer/photo-viewer.svelte | 17 +++++++++++++++++ .../asset-viewer/video-native-viewer.svelte | 15 +++++++++++++++ .../asset-viewer/video-wrapper-viewer.svelte | 12 +++++++++++- 6 files changed, 69 insertions(+), 2 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 5670cf2cc99d2..d5a27478935d6 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -24,6 +24,7 @@ "lodash-es": "^4.17.21", "luxon": "^3.4.4", "socket.io-client": "^4.7.4", + "svelte-gestures": "^5.0.4", "svelte-i18n": "^4.0.0", "svelte-local-storage-store": "^0.6.4", "svelte-maplibre": "^0.9.0", @@ -7791,6 +7792,12 @@ } } }, + "node_modules/svelte-gestures": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/svelte-gestures/-/svelte-gestures-5.0.4.tgz", + "integrity": "sha512-a6cnR46AfFZ8zZyvA38A1wBLBFI7rYuAWQnmv3yYgSdbaJK/U7JG34rSkjMCePRvf4BETJSDfMNngLs5zEAfbw==", + "license": "MIT" + }, "node_modules/svelte-hmr": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", diff --git a/web/package.json b/web/package.json index 7163b04788c73..d87b6e6c08caa 100644 --- a/web/package.json +++ b/web/package.json @@ -80,6 +80,7 @@ "lodash-es": "^4.17.21", "luxon": "^3.4.4", "socket.io-client": "^4.7.4", + "svelte-gestures": "^5.0.4", "svelte-i18n": "^4.0.0", "svelte-local-storage-store": "^0.6.4", "svelte-maplibre": "^0.9.0", diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte index 4e98546069dd7..69d35b9aa4593 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -462,6 +462,8 @@ bind:copyImage asset={previewStackedAsset} {preloadAssets} + onPreviousAsset={() => navigateAsset('previous')} + onNextAsset={() => navigateAsset('next')} on:close={closeViewer} haveFadeTransition={false} {sharedLink} @@ -472,6 +474,8 @@ checksum={previewStackedAsset.checksum} projectionType={previewStackedAsset.exifInfo?.projectionType} loopVideo={true} + onPreviousAsset={() => navigateAsset('previous')} + onNextAsset={() => navigateAsset('next')} on:close={closeViewer} on:onVideoEnded={() => navigateAsset()} on:onVideoStarted={handleVideoStarted} @@ -487,6 +491,8 @@ checksum={asset.checksum} projectionType={asset.exifInfo?.projectionType} loopVideo={$slideshowState !== SlideshowState.PlaySlideshow} + onPreviousAsset={() => navigateAsset('previous')} + onNextAsset={() => navigateAsset('next')} on:close={closeViewer} on:onVideoEnded={() => (shouldPlayMotionPhoto = false)} /> @@ -497,7 +503,16 @@ {:else if isShowEditor && selectedEditType === 'crop'} {:else} - + navigateAsset('previous')} + onNextAsset={() => navigateAsset('next')} + on:close={closeViewer} + {sharedLink} + /> {/if} {:else} navigateAsset('previous')} + onNextAsset={() => navigateAsset('next')} on:close={closeViewer} on:onVideoEnded={() => navigateAsset()} on:onVideoStarted={handleVideoStarted} diff --git a/web/src/lib/components/asset-viewer/photo-viewer.svelte b/web/src/lib/components/asset-viewer/photo-viewer.svelte index 7589ce130aab5..6f6af652b98fe 100644 --- a/web/src/lib/components/asset-viewer/photo-viewer.svelte +++ b/web/src/lib/components/asset-viewer/photo-viewer.svelte @@ -15,6 +15,7 @@ import { canCopyImagesToClipboard, copyImageToClipboard } from 'copy-image-clipboard'; import { onDestroy, onMount } from 'svelte'; import { t } from 'svelte-i18n'; + import { type SwipeCustomEvent, swipe } from 'svelte-gestures'; import { fade } from 'svelte/transition'; import LoadingSpinner from '../shared-components/loading-spinner.svelte'; import { NotificationType, notificationController } from '../shared-components/notification/notification'; @@ -24,6 +25,8 @@ export let element: HTMLDivElement | undefined = undefined; export let haveFadeTransition = true; export let sharedLink: SharedLinkResponseDto | undefined = undefined; + export let onPreviousAsset: (() => void) | null = null; + export let onNextAsset: (() => void) | null = null; export let copyImage: (() => Promise) | null = null; export let zoomToggle: (() => void) | null = null; @@ -110,6 +113,18 @@ handlePromiseError(copyImage()); }; + const onSwipe = (event: SwipeCustomEvent) => { + if ($photoZoomState.currentZoom > 1) { + return; + } + if (onNextAsset && event.detail.direction === 'left') { + onNextAsset(); + } + if (onPreviousAsset && event.detail.direction === 'right') { + onPreviousAsset(); + } + }; + onMount(() => { const onload = () => { imageLoaded = true; @@ -166,6 +181,8 @@ {$getAltText(asset)} @@ -59,6 +72,8 @@ playsinline controls class="h-full object-contain" + use:swipe + on:swipe={onSwipe} on:canplay={(e) => handleCanPlay(e.currentTarget)} on:ended={() => dispatch('onVideoEnded')} on:volumechange={(e) => { diff --git a/web/src/lib/components/asset-viewer/video-wrapper-viewer.svelte b/web/src/lib/components/asset-viewer/video-wrapper-viewer.svelte index 129b6c8be796b..5f03784c42258 100644 --- a/web/src/lib/components/asset-viewer/video-wrapper-viewer.svelte +++ b/web/src/lib/components/asset-viewer/video-wrapper-viewer.svelte @@ -8,10 +8,20 @@ export let projectionType: string | null | undefined; export let checksum: string; export let loopVideo: boolean; + export let onPreviousAsset: () => void; + export let onNextAsset: () => void; {#if projectionType === ProjectionType.EQUIRECTANGULAR} {:else} - + {/if} From 682adaa334568eefae1fccb6b83d5e274e2e7b97 Mon Sep 17 00:00:00 2001 From: src Date: Thu, 29 Aug 2024 15:57:42 +0000 Subject: [PATCH 055/160] fix(mobile): allow create empty non-shared albums, add proper button colors (#12103) * Add proper colors to create album button Allow creation of empty albums with names, or non-empty albums without names * Add proper colors to create album button Allow creation of empty albums with names, or non-empty albums without names * Small changes * Revert change * Simplify logic * lint --------- Co-authored-by: Alex --- mobile/lib/pages/common/create_album.page.dart | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mobile/lib/pages/common/create_album.page.dart b/mobile/lib/pages/common/create_album.page.dart index 51282d8dd6ad4..1fd860520d5c7 100644 --- a/mobile/lib/pages/common/create_album.page.dart +++ b/mobile/lib/pages/common/create_album.page.dart @@ -52,6 +52,7 @@ class CreateAlbumPage extends HookConsumerWidget { if (albumTitleController.text.isEmpty) { albumTitleController.text = 'create_album_page_untitled'.tr(); + isAlbumTitleEmpty.value = false; ref .watch(albumTitleProvider.notifier) .setAlbumTitle('create_album_page_untitled'.tr()); @@ -191,6 +192,7 @@ class CreateAlbumPage extends HookConsumerWidget { } createNonSharedAlbum() async { + onBackgroundTapped(); var newAlbum = await ref.watch(albumProvider.notifier).createAlbum( ref.watch(albumTitleProvider), selectedAssets.value, @@ -238,15 +240,16 @@ class CreateAlbumPage extends HookConsumerWidget { ), if (!isSharedAlbum) TextButton( - onPressed: albumTitleController.text.isNotEmpty && - selectedAssets.value.isNotEmpty + onPressed: albumTitleController.text.isNotEmpty ? createNonSharedAlbum : null, child: Text( 'create_shared_album_page_create'.tr(), style: TextStyle( fontWeight: FontWeight.bold, - color: context.primaryColor, + color: albumTitleController.text.isNotEmpty + ? context.primaryColor + : context.themeData.disabledColor, ), ), ), From d08a20bd5708670f21fc7a65bc29c65f17111446 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Thu, 29 Aug 2024 12:14:03 -0400 Subject: [PATCH 056/160] feat: tags (#11980) * feat: tags * fix: folder tree icons * navigate to tag from detail panel * delete tag * Tag position and add tag button * Tag asset in detail panel * refactor form * feat: navigate to tag page from clicking on a tag * feat: delete tags from the tag page * refactor: moving tag section in detail panel and add + tag button * feat: tag asset action in detail panel * refactor add tag form * fdisable add tag button when there is no selection * feat: tag bulk endpoint * feat: tag colors * chore: clean up * chore: unit tests * feat: write tags to sidecar * Remove tag and auto focus on tag creation form opened * chore: regenerate migration * chore: linting * add color picker to tag edit form * fix: force render tags timeline on navigating back from asset viewer * feat: read tags from keywords * chore: clean up --------- Co-authored-by: Alex Tran --- e2e/src/api/specs/tag.e2e-spec.ts | 559 ++++++++++++++++++ e2e/src/utils.ts | 1 + mobile/openapi/README.md | 13 +- mobile/openapi/lib/api.dart | 8 +- mobile/openapi/lib/api/tags_api.dart | 208 ++++--- mobile/openapi/lib/api/timeline_api.dart | 26 +- mobile/openapi/lib/api_client.dart | 16 +- mobile/openapi/lib/api_helper.dart | 3 - mobile/openapi/lib/model/permission.dart | 3 + .../lib/model/tag_bulk_assets_dto.dart | 110 ++++ .../model/tag_bulk_assets_response_dto.dart | 98 +++ ...pdate_tag_dto.dart => tag_create_dto.dart} | 71 ++- .../openapi/lib/model/tag_response_dto.dart | 55 +- mobile/openapi/lib/model/tag_type_enum.dart | 88 --- ...reate_tag_dto.dart => tag_update_dto.dart} | 61 +- mobile/openapi/lib/model/tag_upsert_dto.dart | 100 ++++ open-api/immich-openapi-specs.json | 285 ++++++--- open-api/typescript-sdk/src/fetch-client.ts | 103 ++-- server/src/controllers/tag.controller.ts | 54 +- server/src/dtos/asset-response.dto.ts | 2 +- server/src/dtos/tag.dto.ts | 62 +- server/src/dtos/time-bucket.dto.ts | 3 + server/src/entities/tag.entity.ts | 63 +- server/src/enum.ts | 1 + server/src/interfaces/access.interface.ts | 4 + server/src/interfaces/asset.interface.ts | 1 + server/src/interfaces/event.interface.ts | 4 + server/src/interfaces/job.interface.ts | 1 + server/src/interfaces/tag.interface.ts | 22 +- .../1724790460210-NestedTagTable.ts | 57 ++ server/src/queries/access.repository.sql | 11 + server/src/queries/asset.repository.sql | 8 +- server/src/queries/tag.repository.sql | 30 + server/src/repositories/access.repository.ts | 27 + server/src/repositories/asset.repository.ts | 9 + server/src/repositories/tag.repository.ts | 193 +++--- server/src/services/metadata.service.spec.ts | 72 +++ server/src/services/metadata.service.ts | 113 ++-- server/src/services/tag.service.spec.ts | 233 +++++--- server/src/services/tag.service.ts | 159 +++-- server/src/services/timeline.service.ts | 4 + server/src/utils/access.ts | 19 +- server/src/utils/request.ts | 2 +- server/src/utils/tag.ts | 30 + server/test/fixtures/tag.stub.ts | 55 +- .../repositories/access.repository.mock.ts | 5 + .../test/repositories/tag.repository.mock.ts | 17 +- .../asset-viewer/detail-panel-tags.svelte | 80 +++ .../asset-viewer/detail-panel.svelte | 11 +- .../assets/thumbnail/thumbnail.svelte | 2 +- .../components/forms/tag-asset-form.svelte | 82 +++ .../layouts/user-page-layout.svelte | 8 +- .../photos-page/actions/tag-action.svelte | 47 ++ .../photos-page/asset-date-group.svelte | 8 +- .../components/photos-page/asset-grid.svelte | 16 +- .../shared-components/combobox.svelte | 5 +- .../settings/setting-input-field.svelte | 94 ++- .../side-bar/side-bar.svelte | 3 + .../shared-components/tree/tree-items.svelte | 16 +- .../shared-components/tree/tree.svelte | 16 +- web/src/lib/constants.ts | 1 + web/src/lib/i18n/en.json | 15 + web/src/lib/utils/asset-store-task-manager.ts | 26 +- web/src/lib/utils/asset-utils.ts | 51 ++ .../[[assetId=id]]/+page.svelte | 11 +- .../(user)/photos/[[assetId=id]]/+page.svelte | 2 + .../[[assetId=id]]/+page.svelte | 251 ++++++++ .../[[photos=photos]]/[[assetId=id]]/+page.ts | 32 + 68 files changed, 3032 insertions(+), 814 deletions(-) create mode 100644 e2e/src/api/specs/tag.e2e-spec.ts create mode 100644 mobile/openapi/lib/model/tag_bulk_assets_dto.dart create mode 100644 mobile/openapi/lib/model/tag_bulk_assets_response_dto.dart rename mobile/openapi/lib/model/{update_tag_dto.dart => tag_create_dto.dart} (56%) delete mode 100644 mobile/openapi/lib/model/tag_type_enum.dart rename mobile/openapi/lib/model/{create_tag_dto.dart => tag_update_dto.dart} (57%) create mode 100644 mobile/openapi/lib/model/tag_upsert_dto.dart create mode 100644 server/src/migrations/1724790460210-NestedTagTable.ts create mode 100644 server/src/queries/tag.repository.sql create mode 100644 server/src/utils/tag.ts create mode 100644 web/src/lib/components/asset-viewer/detail-panel-tags.svelte create mode 100644 web/src/lib/components/forms/tag-asset-form.svelte create mode 100644 web/src/lib/components/photos-page/actions/tag-action.svelte create mode 100644 web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte create mode 100644 web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.ts diff --git a/e2e/src/api/specs/tag.e2e-spec.ts b/e2e/src/api/specs/tag.e2e-spec.ts new file mode 100644 index 0000000000000..0a26ccef0eced --- /dev/null +++ b/e2e/src/api/specs/tag.e2e-spec.ts @@ -0,0 +1,559 @@ +import { + AssetMediaResponseDto, + LoginResponseDto, + Permission, + TagCreateDto, + createTag, + getAllTags, + tagAssets, + upsertTags, +} from '@immich/sdk'; +import { createUserDto, uuidDto } from 'src/fixtures'; +import { errorDto } from 'src/responses'; +import { app, asBearerAuth, utils } from 'src/utils'; +import request from 'supertest'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; + +const create = (accessToken: string, dto: TagCreateDto) => + createTag({ tagCreateDto: dto }, { headers: asBearerAuth(accessToken) }); + +const upsert = (accessToken: string, tags: string[]) => + upsertTags({ tagUpsertDto: { tags } }, { headers: asBearerAuth(accessToken) }); + +describe('/tags', () => { + let admin: LoginResponseDto; + let user: LoginResponseDto; + let userAsset: AssetMediaResponseDto; + + beforeAll(async () => { + await utils.resetDatabase(); + + admin = await utils.adminSetup(); + user = await utils.userSetup(admin.accessToken, createUserDto.user1); + userAsset = await utils.createAsset(user.accessToken); + }); + + beforeEach(async () => { + // tagging assets eventually triggers metadata extraction which can impact other tests + await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction'); + await utils.resetDatabase(['tags']); + }); + + describe('POST /tags', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).post('/tags').send({ name: 'TagA' }); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should require authorization (api key)', async () => { + const { secret } = await utils.createApiKey(user.accessToken, [Permission.AssetRead]); + const { status, body } = await request(app).post('/tags').set('x-api-key', secret).send({ name: 'TagA' }); + expect(status).toBe(403); + expect(body).toEqual(errorDto.missingPermission('tag.create')); + }); + + it('should work with tag.create', async () => { + const { secret } = await utils.createApiKey(user.accessToken, [Permission.TagCreate]); + const { status, body } = await request(app).post('/tags').set('x-api-key', secret).send({ name: 'TagA' }); + expect(body).toEqual({ + id: expect.any(String), + name: 'TagA', + value: 'TagA', + createdAt: expect.any(String), + updatedAt: expect.any(String), + }); + expect(status).toBe(201); + }); + + it('should create a tag', async () => { + const { status, body } = await request(app) + .post('/tags') + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ name: 'TagA' }); + expect(body).toEqual({ + id: expect.any(String), + name: 'TagA', + value: 'TagA', + createdAt: expect.any(String), + updatedAt: expect.any(String), + }); + expect(status).toBe(201); + }); + + it('should create a nested tag', async () => { + const parent = await create(admin.accessToken, { name: 'TagA' }); + + const { status, body } = await request(app) + .post('/tags') + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ name: 'TagB', parentId: parent.id }); + expect(body).toEqual({ + id: expect.any(String), + name: 'TagB', + value: 'TagA/TagB', + createdAt: expect.any(String), + updatedAt: expect.any(String), + }); + expect(status).toBe(201); + }); + }); + + describe('GET /tags', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).get('/tags'); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should require authorization (api key)', async () => { + const { secret } = await utils.createApiKey(user.accessToken, [Permission.AssetRead]); + const { status, body } = await request(app).get('/tags').set('x-api-key', secret); + expect(status).toBe(403); + expect(body).toEqual(errorDto.missingPermission('tag.read')); + }); + + it('should start off empty', async () => { + const { status, body } = await request(app).get('/tags').set('Authorization', `Bearer ${admin.accessToken}`); + expect(body).toEqual([]); + expect(status).toEqual(200); + }); + + it('should return a list of tags', async () => { + const [tagA, tagB, tagC] = await Promise.all([ + create(admin.accessToken, { name: 'TagA' }), + create(admin.accessToken, { name: 'TagB' }), + create(admin.accessToken, { name: 'TagC' }), + ]); + const { status, body } = await request(app).get('/tags').set('Authorization', `Bearer ${admin.accessToken}`); + expect(body).toHaveLength(3); + expect(body).toEqual([tagA, tagB, tagC]); + expect(status).toEqual(200); + }); + + it('should return a nested tags', async () => { + await upsert(admin.accessToken, ['TagA/TagB/TagC', 'TagD']); + const { status, body } = await request(app).get('/tags').set('Authorization', `Bearer ${admin.accessToken}`); + expect(body).toHaveLength(4); + expect(body).toEqual([ + expect.objectContaining({ name: 'TagA', value: 'TagA' }), + expect.objectContaining({ name: 'TagB', value: 'TagA/TagB' }), + expect.objectContaining({ name: 'TagC', value: 'TagA/TagB/TagC' }), + expect.objectContaining({ name: 'TagD', value: 'TagD' }), + ]); + expect(status).toEqual(200); + }); + }); + + describe('PUT /tags', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).put(`/tags`).send({ name: 'TagA/TagB' }); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should require authorization (api key)', async () => { + const { secret } = await utils.createApiKey(user.accessToken, [Permission.AssetRead]); + const { status, body } = await request(app).put('/tags').set('x-api-key', secret).send({ name: 'TagA' }); + expect(status).toBe(403); + expect(body).toEqual(errorDto.missingPermission('tag.create')); + }); + + it('should upsert tags', async () => { + const { status, body } = await request(app) + .put(`/tags`) + .send({ tags: ['TagA/TagB/TagC/TagD'] }) + .set('Authorization', `Bearer ${user.accessToken}`); + expect(status).toBe(200); + expect(body).toEqual([expect.objectContaining({ name: 'TagD', value: 'TagA/TagB/TagC/TagD' })]); + }); + }); + + describe('PUT /tags/assets', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).put(`/tags/assets`).send({ tagIds: [], assetIds: [] }); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should require authorization (api key)', async () => { + const { secret } = await utils.createApiKey(user.accessToken, [Permission.AssetRead]); + const { status, body } = await request(app) + .put('/tags/assets') + .set('x-api-key', secret) + .send({ assetIds: [], tagIds: [] }); + expect(status).toBe(403); + expect(body).toEqual(errorDto.missingPermission('tag.asset')); + }); + + it('should skip assets that are not owned by the user', async () => { + const [tagA, tagB, tagC, assetA, assetB] = await Promise.all([ + create(user.accessToken, { name: 'TagA' }), + create(user.accessToken, { name: 'TagB' }), + create(user.accessToken, { name: 'TagC' }), + utils.createAsset(user.accessToken), + utils.createAsset(admin.accessToken), + ]); + const { status, body } = await request(app) + .put(`/tags/assets`) + .send({ tagIds: [tagA.id, tagB.id, tagC.id], assetIds: [assetA.id, assetB.id] }) + .set('Authorization', `Bearer ${user.accessToken}`); + expect(status).toBe(200); + expect(body).toEqual({ count: 3 }); + }); + + it('should skip tags that are not owned by the user', async () => { + const [tagA, tagB, tagC, assetA, assetB] = await Promise.all([ + create(user.accessToken, { name: 'TagA' }), + create(user.accessToken, { name: 'TagB' }), + create(admin.accessToken, { name: 'TagC' }), + utils.createAsset(user.accessToken), + utils.createAsset(user.accessToken), + ]); + const { status, body } = await request(app) + .put(`/tags/assets`) + .send({ tagIds: [tagA.id, tagB.id, tagC.id], assetIds: [assetA.id, assetB.id] }) + .set('Authorization', `Bearer ${user.accessToken}`); + expect(status).toBe(200); + expect(body).toEqual({ count: 4 }); + }); + + it('should bulk tag assets', async () => { + const [tagA, tagB, tagC, assetA, assetB] = await Promise.all([ + create(user.accessToken, { name: 'TagA' }), + create(user.accessToken, { name: 'TagB' }), + create(user.accessToken, { name: 'TagC' }), + utils.createAsset(user.accessToken), + utils.createAsset(user.accessToken), + ]); + const { status, body } = await request(app) + .put(`/tags/assets`) + .send({ tagIds: [tagA.id, tagB.id, tagC.id], assetIds: [assetA.id, assetB.id] }) + .set('Authorization', `Bearer ${user.accessToken}`); + expect(status).toBe(200); + expect(body).toEqual({ count: 6 }); + }); + }); + + describe('GET /tags/:id', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).get(`/tags/${uuidDto.notFound}`); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should require authorization', async () => { + const tag = await create(user.accessToken, { name: 'TagA' }); + const { status, body } = await request(app) + .get(`/tags/${tag.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.noPermission); + }); + + it('should require authorization (api key)', async () => { + const { secret } = await utils.createApiKey(user.accessToken, [Permission.AssetRead]); + const { status, body } = await request(app) + .get(`/tags/${uuidDto.notFound}`) + .set('x-api-key', secret) + .send({ assetIds: [], tagIds: [] }); + expect(status).toBe(403); + expect(body).toEqual(errorDto.missingPermission('tag.read')); + }); + + it('should require a valid uuid', async () => { + const { status, body } = await request(app) + .get(`/tags/${uuidDto.invalid}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['id must be a UUID'])); + }); + + it('should get tag details', async () => { + const tag = await create(user.accessToken, { name: 'TagA' }); + const { status, body } = await request(app) + .get(`/tags/${tag.id}`) + .set('Authorization', `Bearer ${user.accessToken}`); + expect(status).toBe(200); + expect(body).toEqual({ + id: expect.any(String), + name: 'TagA', + value: 'TagA', + createdAt: expect.any(String), + updatedAt: expect.any(String), + }); + }); + + it('should get nested tag details', async () => { + const tagA = await create(user.accessToken, { name: 'TagA' }); + const tagB = await create(user.accessToken, { name: 'TagB', parentId: tagA.id }); + const tagC = await create(user.accessToken, { name: 'TagC', parentId: tagB.id }); + const tagD = await create(user.accessToken, { name: 'TagD', parentId: tagC.id }); + + const { status, body } = await request(app) + .get(`/tags/${tagD.id}`) + .set('Authorization', `Bearer ${user.accessToken}`); + expect(status).toBe(200); + expect(body).toEqual({ + id: expect.any(String), + name: 'TagD', + value: 'TagA/TagB/TagC/TagD', + createdAt: expect.any(String), + updatedAt: expect.any(String), + }); + }); + }); + + describe('PUT /tags/:id', () => { + it('should require authentication', async () => { + const tag = await create(user.accessToken, { name: 'TagA' }); + const { status, body } = await request(app).put(`/tags/${tag.id}`).send({ color: '#000000' }); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should require authorization', async () => { + const tag = await create(admin.accessToken, { name: 'tagA' }); + const { status, body } = await request(app) + .put(`/tags/${tag.id}`) + .send({ color: '#000000' }) + .set('Authorization', `Bearer ${user.accessToken}`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.noPermission); + }); + + it('should require authorization (api key)', async () => { + const tag = await create(user.accessToken, { name: 'TagA' }); + const { secret } = await utils.createApiKey(user.accessToken, [Permission.AssetRead]); + const { status, body } = await request(app) + .put(`/tags/${tag.id}`) + .set('x-api-key', secret) + .send({ color: '#000000' }); + expect(status).toBe(403); + expect(body).toEqual(errorDto.missingPermission('tag.update')); + }); + + it('should update a tag', async () => { + const tag = await create(user.accessToken, { name: 'tagA' }); + const { status, body } = await request(app) + .put(`/tags/${tag.id}`) + .send({ color: '#000000' }) + .set('Authorization', `Bearer ${user.accessToken}`); + expect(status).toBe(200); + expect(body).toEqual(expect.objectContaining({ color: `#000000` })); + }); + + it('should update a tag color without a # prefix', async () => { + const tag = await create(user.accessToken, { name: 'tagA' }); + const { status, body } = await request(app) + .put(`/tags/${tag.id}`) + .send({ color: '000000' }) + .set('Authorization', `Bearer ${user.accessToken}`); + expect(status).toBe(200); + expect(body).toEqual(expect.objectContaining({ color: `#000000` })); + }); + }); + + describe('DELETE /tags/:id', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).delete(`/tags/${uuidDto.notFound}`); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should require authorization', async () => { + const tag = await create(user.accessToken, { name: 'TagA' }); + const { status, body } = await request(app) + .delete(`/tags/${tag.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.noPermission); + }); + + it('should require authorization (api key)', async () => { + const tag = await create(user.accessToken, { name: 'TagA' }); + const { secret } = await utils.createApiKey(user.accessToken, [Permission.AssetRead]); + const { status, body } = await request(app).delete(`/tags/${tag.id}`).set('x-api-key', secret); + expect(status).toBe(403); + expect(body).toEqual(errorDto.missingPermission('tag.delete')); + }); + + it('should require a valid uuid', async () => { + const { status, body } = await request(app) + .delete(`/tags/${uuidDto.invalid}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['id must be a UUID'])); + }); + + it('should delete a tag', async () => { + const tag = await create(user.accessToken, { name: 'TagA' }); + const { status } = await request(app) + .delete(`/tags/${tag.id}`) + .set('Authorization', `Bearer ${user.accessToken}`); + expect(status).toBe(204); + }); + + it('should delete a nested tag (root)', async () => { + const tagA = await create(user.accessToken, { name: 'TagA' }); + await create(user.accessToken, { name: 'TagB', parentId: tagA.id }); + const { status } = await request(app) + .delete(`/tags/${tagA.id}`) + .set('Authorization', `Bearer ${user.accessToken}`); + expect(status).toBe(204); + const tags = await getAllTags({ headers: asBearerAuth(user.accessToken) }); + expect(tags.length).toBe(0); + }); + + it('should delete a nested tag (leaf)', async () => { + const tagA = await create(user.accessToken, { name: 'TagA' }); + const tagB = await create(user.accessToken, { name: 'TagB', parentId: tagA.id }); + const { status } = await request(app) + .delete(`/tags/${tagB.id}`) + .set('Authorization', `Bearer ${user.accessToken}`); + expect(status).toBe(204); + const tags = await getAllTags({ headers: asBearerAuth(user.accessToken) }); + expect(tags.length).toBe(1); + expect(tags[0]).toEqual(tagA); + }); + }); + + describe('PUT /tags/:id/assets', () => { + it('should require authentication', async () => { + const tagA = await create(user.accessToken, { name: 'TagA' }); + const { status, body } = await request(app) + .put(`/tags/${tagA.id}/assets`) + .send({ ids: [userAsset.id] }); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should require authorization', async () => { + const tag = await create(user.accessToken, { name: 'TagA' }); + const { status, body } = await request(app) + .put(`/tags/${tag.id}/assets`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ ids: [userAsset.id] }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.noPermission); + }); + + it('should require authorization (api key)', async () => { + const tag = await create(user.accessToken, { name: 'TagA' }); + const { secret } = await utils.createApiKey(user.accessToken, [Permission.AssetRead]); + const { status, body } = await request(app) + .put(`/tags/${tag.id}/assets`) + .set('x-api-key', secret) + .send({ ids: [userAsset.id] }); + expect(status).toBe(403); + expect(body).toEqual(errorDto.missingPermission('tag.asset')); + }); + + it('should be able to tag own asset', async () => { + const tagA = await create(user.accessToken, { name: 'TagA' }); + const { status, body } = await request(app) + .put(`/tags/${tagA.id}/assets`) + .set('Authorization', `Bearer ${user.accessToken}`) + .send({ ids: [userAsset.id] }); + + expect(status).toBe(200); + expect(body).toEqual([expect.objectContaining({ id: userAsset.id, success: true })]); + }); + + it("should not be able to add assets to another user's tag", async () => { + const tagA = await create(admin.accessToken, { name: 'TagA' }); + const { status, body } = await request(app) + .put(`/tags/${tagA.id}/assets`) + .set('Authorization', `Bearer ${user.accessToken}`) + .send({ ids: [userAsset.id] }); + + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest('Not found or no tag.asset access')); + }); + + it('should add duplicate assets only once', async () => { + const tagA = await create(user.accessToken, { name: 'TagA' }); + const { status, body } = await request(app) + .put(`/tags/${tagA.id}/assets`) + .set('Authorization', `Bearer ${user.accessToken}`) + .send({ ids: [userAsset.id, userAsset.id] }); + + expect(status).toBe(200); + expect(body).toEqual([ + expect.objectContaining({ id: userAsset.id, success: true }), + expect.objectContaining({ id: userAsset.id, success: false, error: 'duplicate' }), + ]); + }); + }); + + describe('DELETE /tags/:id/assets', () => { + it('should require authentication', async () => { + const tagA = await create(admin.accessToken, { name: 'TagA' }); + const { status, body } = await request(app) + .delete(`/tags/${tagA}/assets`) + .send({ ids: [userAsset.id] }); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should require authorization', async () => { + const tagA = await create(user.accessToken, { name: 'TagA' }); + await tagAssets( + { id: tagA.id, bulkIdsDto: { ids: [userAsset.id] } }, + { headers: asBearerAuth(user.accessToken) }, + ); + const { status, body } = await request(app) + .delete(`/tags/${tagA.id}/assets`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ ids: [userAsset.id] }); + + expect(status).toBe(400); + expect(body).toEqual(errorDto.noPermission); + }); + + it('should require authorization (api key)', async () => { + const tag = await create(user.accessToken, { name: 'TagA' }); + const { secret } = await utils.createApiKey(user.accessToken, [Permission.AssetRead]); + const { status, body } = await request(app) + .delete(`/tags/${tag.id}/assets`) + .set('x-api-key', secret) + .send({ ids: [userAsset.id] }); + expect(status).toBe(403); + expect(body).toEqual(errorDto.missingPermission('tag.asset')); + }); + + it('should be able to remove own asset from own tag', async () => { + const tagA = await create(user.accessToken, { name: 'TagA' }); + await tagAssets( + { id: tagA.id, bulkIdsDto: { ids: [userAsset.id] } }, + { headers: asBearerAuth(user.accessToken) }, + ); + const { status, body } = await request(app) + .delete(`/tags/${tagA.id}/assets`) + .set('Authorization', `Bearer ${user.accessToken}`) + .send({ ids: [userAsset.id] }); + + expect(status).toBe(200); + expect(body).toEqual([expect.objectContaining({ id: userAsset.id, success: true })]); + }); + + it('should remove duplicate assets only once', async () => { + const tagA = await create(user.accessToken, { name: 'TagA' }); + await tagAssets( + { id: tagA.id, bulkIdsDto: { ids: [userAsset.id] } }, + { headers: asBearerAuth(user.accessToken) }, + ); + const { status, body } = await request(app) + .delete(`/tags/${tagA.id}/assets`) + .set('Authorization', `Bearer ${user.accessToken}`) + .send({ ids: [userAsset.id, userAsset.id] }); + + expect(status).toBe(200); + expect(body).toEqual([ + expect.objectContaining({ id: userAsset.id, success: true }), + expect.objectContaining({ id: userAsset.id, success: false, error: 'not_found' }), + ]); + }); + }); +}); diff --git a/e2e/src/utils.ts b/e2e/src/utils.ts index 30e2497b514d1..a53a3ddd25f0d 100644 --- a/e2e/src/utils.ts +++ b/e2e/src/utils.ts @@ -148,6 +148,7 @@ export const utils = { 'sessions', 'users', 'system_metadata', + 'tags', ]; const sql: string[] = []; diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 1da4463a1225b..1f8958dd95d4d 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -210,14 +210,15 @@ Class | Method | HTTP request | Description *SystemMetadataApi* | [**getAdminOnboarding**](doc//SystemMetadataApi.md#getadminonboarding) | **GET** /system-metadata/admin-onboarding | *SystemMetadataApi* | [**getReverseGeocodingState**](doc//SystemMetadataApi.md#getreversegeocodingstate) | **GET** /system-metadata/reverse-geocoding-state | *SystemMetadataApi* | [**updateAdminOnboarding**](doc//SystemMetadataApi.md#updateadminonboarding) | **POST** /system-metadata/admin-onboarding | +*TagsApi* | [**bulkTagAssets**](doc//TagsApi.md#bulktagassets) | **PUT** /tags/assets | *TagsApi* | [**createTag**](doc//TagsApi.md#createtag) | **POST** /tags | *TagsApi* | [**deleteTag**](doc//TagsApi.md#deletetag) | **DELETE** /tags/{id} | *TagsApi* | [**getAllTags**](doc//TagsApi.md#getalltags) | **GET** /tags | -*TagsApi* | [**getTagAssets**](doc//TagsApi.md#gettagassets) | **GET** /tags/{id}/assets | *TagsApi* | [**getTagById**](doc//TagsApi.md#gettagbyid) | **GET** /tags/{id} | *TagsApi* | [**tagAssets**](doc//TagsApi.md#tagassets) | **PUT** /tags/{id}/assets | *TagsApi* | [**untagAssets**](doc//TagsApi.md#untagassets) | **DELETE** /tags/{id}/assets | -*TagsApi* | [**updateTag**](doc//TagsApi.md#updatetag) | **PATCH** /tags/{id} | +*TagsApi* | [**updateTag**](doc//TagsApi.md#updatetag) | **PUT** /tags/{id} | +*TagsApi* | [**upsertTags**](doc//TagsApi.md#upserttags) | **PUT** /tags | *TimelineApi* | [**getTimeBucket**](doc//TimelineApi.md#gettimebucket) | **GET** /timeline/bucket | *TimelineApi* | [**getTimeBuckets**](doc//TimelineApi.md#gettimebuckets) | **GET** /timeline/buckets | *TrashApi* | [**emptyTrash**](doc//TrashApi.md#emptytrash) | **POST** /trash/empty | @@ -305,7 +306,6 @@ Class | Method | HTTP request | Description - [CreateAlbumDto](doc//CreateAlbumDto.md) - [CreateLibraryDto](doc//CreateLibraryDto.md) - [CreateProfileImageResponseDto](doc//CreateProfileImageResponseDto.md) - - [CreateTagDto](doc//CreateTagDto.md) - [DownloadArchiveInfo](doc//DownloadArchiveInfo.md) - [DownloadInfoDto](doc//DownloadInfoDto.md) - [DownloadResponse](doc//DownloadResponse.md) @@ -429,8 +429,12 @@ Class | Method | HTTP request | Description - [SystemConfigThemeDto](doc//SystemConfigThemeDto.md) - [SystemConfigTrashDto](doc//SystemConfigTrashDto.md) - [SystemConfigUserDto](doc//SystemConfigUserDto.md) + - [TagBulkAssetsDto](doc//TagBulkAssetsDto.md) + - [TagBulkAssetsResponseDto](doc//TagBulkAssetsResponseDto.md) + - [TagCreateDto](doc//TagCreateDto.md) - [TagResponseDto](doc//TagResponseDto.md) - - [TagTypeEnum](doc//TagTypeEnum.md) + - [TagUpdateDto](doc//TagUpdateDto.md) + - [TagUpsertDto](doc//TagUpsertDto.md) - [TimeBucketResponseDto](doc//TimeBucketResponseDto.md) - [TimeBucketSize](doc//TimeBucketSize.md) - [ToneMapping](doc//ToneMapping.md) @@ -441,7 +445,6 @@ Class | Method | HTTP request | Description - [UpdateAssetDto](doc//UpdateAssetDto.md) - [UpdateLibraryDto](doc//UpdateLibraryDto.md) - [UpdatePartnerDto](doc//UpdatePartnerDto.md) - - [UpdateTagDto](doc//UpdateTagDto.md) - [UsageByUserDto](doc//UsageByUserDto.md) - [UserAdminCreateDto](doc//UserAdminCreateDto.md) - [UserAdminDeleteDto](doc//UserAdminDeleteDto.md) diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index 05a43c8af7031..532d7e22cddf4 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -120,7 +120,6 @@ part 'model/colorspace.dart'; part 'model/create_album_dto.dart'; part 'model/create_library_dto.dart'; part 'model/create_profile_image_response_dto.dart'; -part 'model/create_tag_dto.dart'; part 'model/download_archive_info.dart'; part 'model/download_info_dto.dart'; part 'model/download_response.dart'; @@ -244,8 +243,12 @@ part 'model/system_config_template_storage_option_dto.dart'; part 'model/system_config_theme_dto.dart'; part 'model/system_config_trash_dto.dart'; part 'model/system_config_user_dto.dart'; +part 'model/tag_bulk_assets_dto.dart'; +part 'model/tag_bulk_assets_response_dto.dart'; +part 'model/tag_create_dto.dart'; part 'model/tag_response_dto.dart'; -part 'model/tag_type_enum.dart'; +part 'model/tag_update_dto.dart'; +part 'model/tag_upsert_dto.dart'; part 'model/time_bucket_response_dto.dart'; part 'model/time_bucket_size.dart'; part 'model/tone_mapping.dart'; @@ -256,7 +259,6 @@ part 'model/update_album_user_dto.dart'; part 'model/update_asset_dto.dart'; part 'model/update_library_dto.dart'; part 'model/update_partner_dto.dart'; -part 'model/update_tag_dto.dart'; part 'model/usage_by_user_dto.dart'; part 'model/user_admin_create_dto.dart'; part 'model/user_admin_delete_dto.dart'; diff --git a/mobile/openapi/lib/api/tags_api.dart b/mobile/openapi/lib/api/tags_api.dart index e5d1e9c650311..87c9001a3c63e 100644 --- a/mobile/openapi/lib/api/tags_api.dart +++ b/mobile/openapi/lib/api/tags_api.dart @@ -16,16 +16,63 @@ class TagsApi { final ApiClient apiClient; + /// Performs an HTTP 'PUT /tags/assets' operation and returns the [Response]. + /// Parameters: + /// + /// * [TagBulkAssetsDto] tagBulkAssetsDto (required): + Future bulkTagAssetsWithHttpInfo(TagBulkAssetsDto tagBulkAssetsDto,) async { + // ignore: prefer_const_declarations + final path = r'/tags/assets'; + + // ignore: prefer_final_locals + Object? postBody = tagBulkAssetsDto; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = ['application/json']; + + + return apiClient.invokeAPI( + path, + 'PUT', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [TagBulkAssetsDto] tagBulkAssetsDto (required): + Future bulkTagAssets(TagBulkAssetsDto tagBulkAssetsDto,) async { + final response = await bulkTagAssetsWithHttpInfo(tagBulkAssetsDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'TagBulkAssetsResponseDto',) as TagBulkAssetsResponseDto; + + } + return null; + } + /// Performs an HTTP 'POST /tags' operation and returns the [Response]. /// Parameters: /// - /// * [CreateTagDto] createTagDto (required): - Future createTagWithHttpInfo(CreateTagDto createTagDto,) async { + /// * [TagCreateDto] tagCreateDto (required): + Future createTagWithHttpInfo(TagCreateDto tagCreateDto,) async { // ignore: prefer_const_declarations final path = r'/tags'; // ignore: prefer_final_locals - Object? postBody = createTagDto; + Object? postBody = tagCreateDto; final queryParams = []; final headerParams = {}; @@ -47,9 +94,9 @@ class TagsApi { /// Parameters: /// - /// * [CreateTagDto] createTagDto (required): - Future createTag(CreateTagDto createTagDto,) async { - final response = await createTagWithHttpInfo(createTagDto,); + /// * [TagCreateDto] tagCreateDto (required): + Future createTag(TagCreateDto tagCreateDto,) async { + final response = await createTagWithHttpInfo(tagCreateDto,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -147,57 +194,6 @@ class TagsApi { return null; } - /// Performs an HTTP 'GET /tags/{id}/assets' operation and returns the [Response]. - /// Parameters: - /// - /// * [String] id (required): - Future getTagAssetsWithHttpInfo(String id,) async { - // ignore: prefer_const_declarations - final path = r'/tags/{id}/assets' - .replaceAll('{id}', id); - - // ignore: prefer_final_locals - Object? postBody; - - final queryParams = []; - final headerParams = {}; - final formParams = {}; - - const contentTypes = []; - - - return apiClient.invokeAPI( - path, - 'GET', - queryParams, - postBody, - headerParams, - formParams, - contentTypes.isEmpty ? null : contentTypes.first, - ); - } - - /// Parameters: - /// - /// * [String] id (required): - Future?> getTagAssets(String id,) async { - final response = await getTagAssetsWithHttpInfo(id,); - if (response.statusCode >= HttpStatus.badRequest) { - throw ApiException(response.statusCode, await _decodeBodyBytes(response)); - } - // When a remote server returns no body with a status of 204, we shall not decode it. - // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" - // FormatException when trying to decode an empty string. - if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { - final responseBody = await _decodeBodyBytes(response); - return (await apiClient.deserializeAsync(responseBody, 'List') as List) - .cast() - .toList(growable: false); - - } - return null; - } - /// Performs an HTTP 'GET /tags/{id}' operation and returns the [Response]. /// Parameters: /// @@ -251,14 +247,14 @@ class TagsApi { /// /// * [String] id (required): /// - /// * [AssetIdsDto] assetIdsDto (required): - Future tagAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto,) async { + /// * [BulkIdsDto] bulkIdsDto (required): + Future tagAssetsWithHttpInfo(String id, BulkIdsDto bulkIdsDto,) async { // ignore: prefer_const_declarations final path = r'/tags/{id}/assets' .replaceAll('{id}', id); // ignore: prefer_final_locals - Object? postBody = assetIdsDto; + Object? postBody = bulkIdsDto; final queryParams = []; final headerParams = {}; @@ -282,9 +278,9 @@ class TagsApi { /// /// * [String] id (required): /// - /// * [AssetIdsDto] assetIdsDto (required): - Future?> tagAssets(String id, AssetIdsDto assetIdsDto,) async { - final response = await tagAssetsWithHttpInfo(id, assetIdsDto,); + /// * [BulkIdsDto] bulkIdsDto (required): + Future?> tagAssets(String id, BulkIdsDto bulkIdsDto,) async { + final response = await tagAssetsWithHttpInfo(id, bulkIdsDto,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -293,8 +289,8 @@ class TagsApi { // FormatException when trying to decode an empty string. if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { final responseBody = await _decodeBodyBytes(response); - return (await apiClient.deserializeAsync(responseBody, 'List') as List) - .cast() + return (await apiClient.deserializeAsync(responseBody, 'List') as List) + .cast() .toList(growable: false); } @@ -306,14 +302,14 @@ class TagsApi { /// /// * [String] id (required): /// - /// * [AssetIdsDto] assetIdsDto (required): - Future untagAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto,) async { + /// * [BulkIdsDto] bulkIdsDto (required): + Future untagAssetsWithHttpInfo(String id, BulkIdsDto bulkIdsDto,) async { // ignore: prefer_const_declarations final path = r'/tags/{id}/assets' .replaceAll('{id}', id); // ignore: prefer_final_locals - Object? postBody = assetIdsDto; + Object? postBody = bulkIdsDto; final queryParams = []; final headerParams = {}; @@ -337,9 +333,9 @@ class TagsApi { /// /// * [String] id (required): /// - /// * [AssetIdsDto] assetIdsDto (required): - Future?> untagAssets(String id, AssetIdsDto assetIdsDto,) async { - final response = await untagAssetsWithHttpInfo(id, assetIdsDto,); + /// * [BulkIdsDto] bulkIdsDto (required): + Future?> untagAssets(String id, BulkIdsDto bulkIdsDto,) async { + final response = await untagAssetsWithHttpInfo(id, bulkIdsDto,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -348,27 +344,27 @@ class TagsApi { // FormatException when trying to decode an empty string. if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { final responseBody = await _decodeBodyBytes(response); - return (await apiClient.deserializeAsync(responseBody, 'List') as List) - .cast() + return (await apiClient.deserializeAsync(responseBody, 'List') as List) + .cast() .toList(growable: false); } return null; } - /// Performs an HTTP 'PATCH /tags/{id}' operation and returns the [Response]. + /// Performs an HTTP 'PUT /tags/{id}' operation and returns the [Response]. /// Parameters: /// /// * [String] id (required): /// - /// * [UpdateTagDto] updateTagDto (required): - Future updateTagWithHttpInfo(String id, UpdateTagDto updateTagDto,) async { + /// * [TagUpdateDto] tagUpdateDto (required): + Future updateTagWithHttpInfo(String id, TagUpdateDto tagUpdateDto,) async { // ignore: prefer_const_declarations final path = r'/tags/{id}' .replaceAll('{id}', id); // ignore: prefer_final_locals - Object? postBody = updateTagDto; + Object? postBody = tagUpdateDto; final queryParams = []; final headerParams = {}; @@ -379,7 +375,7 @@ class TagsApi { return apiClient.invokeAPI( path, - 'PATCH', + 'PUT', queryParams, postBody, headerParams, @@ -392,9 +388,9 @@ class TagsApi { /// /// * [String] id (required): /// - /// * [UpdateTagDto] updateTagDto (required): - Future updateTag(String id, UpdateTagDto updateTagDto,) async { - final response = await updateTagWithHttpInfo(id, updateTagDto,); + /// * [TagUpdateDto] tagUpdateDto (required): + Future updateTag(String id, TagUpdateDto tagUpdateDto,) async { + final response = await updateTagWithHttpInfo(id, tagUpdateDto,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -407,4 +403,54 @@ class TagsApi { } return null; } + + /// Performs an HTTP 'PUT /tags' operation and returns the [Response]. + /// Parameters: + /// + /// * [TagUpsertDto] tagUpsertDto (required): + Future upsertTagsWithHttpInfo(TagUpsertDto tagUpsertDto,) async { + // ignore: prefer_const_declarations + final path = r'/tags'; + + // ignore: prefer_final_locals + Object? postBody = tagUpsertDto; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = ['application/json']; + + + return apiClient.invokeAPI( + path, + 'PUT', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [TagUpsertDto] tagUpsertDto (required): + Future?> upsertTags(TagUpsertDto tagUpsertDto,) async { + final response = await upsertTagsWithHttpInfo(tagUpsertDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + final responseBody = await _decodeBodyBytes(response); + return (await apiClient.deserializeAsync(responseBody, 'List') as List) + .cast() + .toList(growable: false); + + } + return null; + } } diff --git a/mobile/openapi/lib/api/timeline_api.dart b/mobile/openapi/lib/api/timeline_api.dart index 4acb98bdf2c49..8c94e09bf5c3f 100644 --- a/mobile/openapi/lib/api/timeline_api.dart +++ b/mobile/openapi/lib/api/timeline_api.dart @@ -37,12 +37,14 @@ class TimelineApi { /// /// * [String] personId: /// + /// * [String] tagId: + /// /// * [String] userId: /// /// * [bool] withPartners: /// /// * [bool] withStacked: - Future getTimeBucketWithHttpInfo(TimeBucketSize size, String timeBucket, { String? albumId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? userId, bool? withPartners, bool? withStacked, }) async { + Future getTimeBucketWithHttpInfo(TimeBucketSize size, String timeBucket, { String? albumId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, bool? withPartners, bool? withStacked, }) async { // ignore: prefer_const_declarations final path = r'/timeline/bucket'; @@ -75,6 +77,9 @@ class TimelineApi { queryParams.addAll(_queryParams('', 'personId', personId)); } queryParams.addAll(_queryParams('', 'size', size)); + if (tagId != null) { + queryParams.addAll(_queryParams('', 'tagId', tagId)); + } queryParams.addAll(_queryParams('', 'timeBucket', timeBucket)); if (userId != null) { queryParams.addAll(_queryParams('', 'userId', userId)); @@ -120,13 +125,15 @@ class TimelineApi { /// /// * [String] personId: /// + /// * [String] tagId: + /// /// * [String] userId: /// /// * [bool] withPartners: /// /// * [bool] withStacked: - Future?> getTimeBucket(TimeBucketSize size, String timeBucket, { String? albumId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? userId, bool? withPartners, bool? withStacked, }) async { - final response = await getTimeBucketWithHttpInfo(size, timeBucket, albumId: albumId, isArchived: isArchived, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, userId: userId, withPartners: withPartners, withStacked: withStacked, ); + Future?> getTimeBucket(TimeBucketSize size, String timeBucket, { String? albumId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, bool? withPartners, bool? withStacked, }) async { + final response = await getTimeBucketWithHttpInfo(size, timeBucket, albumId: albumId, isArchived: isArchived, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, tagId: tagId, userId: userId, withPartners: withPartners, withStacked: withStacked, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -162,12 +169,14 @@ class TimelineApi { /// /// * [String] personId: /// + /// * [String] tagId: + /// /// * [String] userId: /// /// * [bool] withPartners: /// /// * [bool] withStacked: - Future getTimeBucketsWithHttpInfo(TimeBucketSize size, { String? albumId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? userId, bool? withPartners, bool? withStacked, }) async { + Future getTimeBucketsWithHttpInfo(TimeBucketSize size, { String? albumId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, bool? withPartners, bool? withStacked, }) async { // ignore: prefer_const_declarations final path = r'/timeline/buckets'; @@ -200,6 +209,9 @@ class TimelineApi { queryParams.addAll(_queryParams('', 'personId', personId)); } queryParams.addAll(_queryParams('', 'size', size)); + if (tagId != null) { + queryParams.addAll(_queryParams('', 'tagId', tagId)); + } if (userId != null) { queryParams.addAll(_queryParams('', 'userId', userId)); } @@ -242,13 +254,15 @@ class TimelineApi { /// /// * [String] personId: /// + /// * [String] tagId: + /// /// * [String] userId: /// /// * [bool] withPartners: /// /// * [bool] withStacked: - Future?> getTimeBuckets(TimeBucketSize size, { String? albumId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? userId, bool? withPartners, bool? withStacked, }) async { - final response = await getTimeBucketsWithHttpInfo(size, albumId: albumId, isArchived: isArchived, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, userId: userId, withPartners: withPartners, withStacked: withStacked, ); + Future?> getTimeBuckets(TimeBucketSize size, { String? albumId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, bool? withPartners, bool? withStacked, }) async { + final response = await getTimeBucketsWithHttpInfo(size, albumId: albumId, isArchived: isArchived, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, tagId: tagId, userId: userId, withPartners: withPartners, withStacked: withStacked, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index c9ed2a508d78b..54873a59557f2 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -295,8 +295,6 @@ class ApiClient { return CreateLibraryDto.fromJson(value); case 'CreateProfileImageResponseDto': return CreateProfileImageResponseDto.fromJson(value); - case 'CreateTagDto': - return CreateTagDto.fromJson(value); case 'DownloadArchiveInfo': return DownloadArchiveInfo.fromJson(value); case 'DownloadInfoDto': @@ -543,10 +541,18 @@ class ApiClient { return SystemConfigTrashDto.fromJson(value); case 'SystemConfigUserDto': return SystemConfigUserDto.fromJson(value); + case 'TagBulkAssetsDto': + return TagBulkAssetsDto.fromJson(value); + case 'TagBulkAssetsResponseDto': + return TagBulkAssetsResponseDto.fromJson(value); + case 'TagCreateDto': + return TagCreateDto.fromJson(value); case 'TagResponseDto': return TagResponseDto.fromJson(value); - case 'TagTypeEnum': - return TagTypeEnumTypeTransformer().decode(value); + case 'TagUpdateDto': + return TagUpdateDto.fromJson(value); + case 'TagUpsertDto': + return TagUpsertDto.fromJson(value); case 'TimeBucketResponseDto': return TimeBucketResponseDto.fromJson(value); case 'TimeBucketSize': @@ -567,8 +573,6 @@ class ApiClient { return UpdateLibraryDto.fromJson(value); case 'UpdatePartnerDto': return UpdatePartnerDto.fromJson(value); - case 'UpdateTagDto': - return UpdateTagDto.fromJson(value); case 'UsageByUserDto': return UsageByUserDto.fromJson(value); case 'UserAdminCreateDto': diff --git a/mobile/openapi/lib/api_helper.dart b/mobile/openapi/lib/api_helper.dart index 7f46e145b15eb..a486551cc5987 100644 --- a/mobile/openapi/lib/api_helper.dart +++ b/mobile/openapi/lib/api_helper.dart @@ -127,9 +127,6 @@ String parameterToString(dynamic value) { if (value is SharedLinkType) { return SharedLinkTypeTypeTransformer().encode(value).toString(); } - if (value is TagTypeEnum) { - return TagTypeEnumTypeTransformer().encode(value).toString(); - } if (value is TimeBucketSize) { return TimeBucketSizeTypeTransformer().encode(value).toString(); } diff --git a/mobile/openapi/lib/model/permission.dart b/mobile/openapi/lib/model/permission.dart index 3f89c9826d645..1244a434b6ee7 100644 --- a/mobile/openapi/lib/model/permission.dart +++ b/mobile/openapi/lib/model/permission.dart @@ -96,6 +96,7 @@ class Permission { static const tagPeriodRead = Permission._(r'tag.read'); static const tagPeriodUpdate = Permission._(r'tag.update'); static const tagPeriodDelete = Permission._(r'tag.delete'); + static const tagPeriodAsset = Permission._(r'tag.asset'); static const adminPeriodUserPeriodCreate = Permission._(r'admin.user.create'); static const adminPeriodUserPeriodRead = Permission._(r'admin.user.read'); static const adminPeriodUserPeriodUpdate = Permission._(r'admin.user.update'); @@ -176,6 +177,7 @@ class Permission { tagPeriodRead, tagPeriodUpdate, tagPeriodDelete, + tagPeriodAsset, adminPeriodUserPeriodCreate, adminPeriodUserPeriodRead, adminPeriodUserPeriodUpdate, @@ -291,6 +293,7 @@ class PermissionTypeTransformer { case r'tag.read': return Permission.tagPeriodRead; case r'tag.update': return Permission.tagPeriodUpdate; case r'tag.delete': return Permission.tagPeriodDelete; + case r'tag.asset': return Permission.tagPeriodAsset; case r'admin.user.create': return Permission.adminPeriodUserPeriodCreate; case r'admin.user.read': return Permission.adminPeriodUserPeriodRead; case r'admin.user.update': return Permission.adminPeriodUserPeriodUpdate; diff --git a/mobile/openapi/lib/model/tag_bulk_assets_dto.dart b/mobile/openapi/lib/model/tag_bulk_assets_dto.dart new file mode 100644 index 0000000000000..c11cb66ce081f --- /dev/null +++ b/mobile/openapi/lib/model/tag_bulk_assets_dto.dart @@ -0,0 +1,110 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class TagBulkAssetsDto { + /// Returns a new [TagBulkAssetsDto] instance. + TagBulkAssetsDto({ + this.assetIds = const [], + this.tagIds = const [], + }); + + List assetIds; + + List tagIds; + + @override + bool operator ==(Object other) => identical(this, other) || other is TagBulkAssetsDto && + _deepEquality.equals(other.assetIds, assetIds) && + _deepEquality.equals(other.tagIds, tagIds); + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (assetIds.hashCode) + + (tagIds.hashCode); + + @override + String toString() => 'TagBulkAssetsDto[assetIds=$assetIds, tagIds=$tagIds]'; + + Map toJson() { + final json = {}; + json[r'assetIds'] = this.assetIds; + json[r'tagIds'] = this.tagIds; + return json; + } + + /// Returns a new [TagBulkAssetsDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static TagBulkAssetsDto? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + return TagBulkAssetsDto( + assetIds: json[r'assetIds'] is Iterable + ? (json[r'assetIds'] as Iterable).cast().toList(growable: false) + : const [], + tagIds: json[r'tagIds'] is Iterable + ? (json[r'tagIds'] as Iterable).cast().toList(growable: false) + : const [], + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = TagBulkAssetsDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = TagBulkAssetsDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of TagBulkAssetsDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = TagBulkAssetsDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'assetIds', + 'tagIds', + }; +} + diff --git a/mobile/openapi/lib/model/tag_bulk_assets_response_dto.dart b/mobile/openapi/lib/model/tag_bulk_assets_response_dto.dart new file mode 100644 index 0000000000000..d4dcb91d8c45d --- /dev/null +++ b/mobile/openapi/lib/model/tag_bulk_assets_response_dto.dart @@ -0,0 +1,98 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class TagBulkAssetsResponseDto { + /// Returns a new [TagBulkAssetsResponseDto] instance. + TagBulkAssetsResponseDto({ + required this.count, + }); + + int count; + + @override + bool operator ==(Object other) => identical(this, other) || other is TagBulkAssetsResponseDto && + other.count == count; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (count.hashCode); + + @override + String toString() => 'TagBulkAssetsResponseDto[count=$count]'; + + Map toJson() { + final json = {}; + json[r'count'] = this.count; + return json; + } + + /// Returns a new [TagBulkAssetsResponseDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static TagBulkAssetsResponseDto? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + return TagBulkAssetsResponseDto( + count: mapValueOfType(json, r'count')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = TagBulkAssetsResponseDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = TagBulkAssetsResponseDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of TagBulkAssetsResponseDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = TagBulkAssetsResponseDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'count', + }; +} + diff --git a/mobile/openapi/lib/model/update_tag_dto.dart b/mobile/openapi/lib/model/tag_create_dto.dart similarity index 56% rename from mobile/openapi/lib/model/update_tag_dto.dart rename to mobile/openapi/lib/model/tag_create_dto.dart index dfa9b8cfc078c..dd7e537a0a021 100644 --- a/mobile/openapi/lib/model/update_tag_dto.dart +++ b/mobile/openapi/lib/model/tag_create_dto.dart @@ -10,10 +10,12 @@ part of openapi.api; -class UpdateTagDto { - /// Returns a new [UpdateTagDto] instance. - UpdateTagDto({ - this.name, +class TagCreateDto { + /// Returns a new [TagCreateDto] instance. + TagCreateDto({ + this.color, + required this.name, + this.parentId, }); /// @@ -22,49 +24,65 @@ class UpdateTagDto { /// source code must fall back to having a nullable type. /// Consider adding a "default:" property in the specification file to hide this note. /// - String? name; + String? color; + + String name; + + String? parentId; @override - bool operator ==(Object other) => identical(this, other) || other is UpdateTagDto && - other.name == name; + bool operator ==(Object other) => identical(this, other) || other is TagCreateDto && + other.color == color && + other.name == name && + other.parentId == parentId; @override int get hashCode => // ignore: unnecessary_parenthesis - (name == null ? 0 : name!.hashCode); + (color == null ? 0 : color!.hashCode) + + (name.hashCode) + + (parentId == null ? 0 : parentId!.hashCode); @override - String toString() => 'UpdateTagDto[name=$name]'; + String toString() => 'TagCreateDto[color=$color, name=$name, parentId=$parentId]'; Map toJson() { final json = {}; - if (this.name != null) { - json[r'name'] = this.name; + if (this.color != null) { + json[r'color'] = this.color; } else { - // json[r'name'] = null; + // json[r'color'] = null; + } + json[r'name'] = this.name; + if (this.parentId != null) { + json[r'parentId'] = this.parentId; + } else { + // json[r'parentId'] = null; } return json; } - /// Returns a new [UpdateTagDto] instance and imports its values from + /// Returns a new [TagCreateDto] instance and imports its values from /// [value] if it's a [Map], null otherwise. // ignore: prefer_constructors_over_static_methods - static UpdateTagDto? fromJson(dynamic value) { + static TagCreateDto? fromJson(dynamic value) { if (value is Map) { final json = value.cast(); - return UpdateTagDto( - name: mapValueOfType(json, r'name'), + return TagCreateDto( + color: mapValueOfType(json, r'color'), + name: mapValueOfType(json, r'name')!, + parentId: mapValueOfType(json, r'parentId'), ); } return null; } - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; if (json is List && json.isNotEmpty) { for (final row in json) { - final value = UpdateTagDto.fromJson(row); + final value = TagCreateDto.fromJson(row); if (value != null) { result.add(value); } @@ -73,12 +91,12 @@ class UpdateTagDto { return result.toList(growable: growable); } - static Map mapFromJson(dynamic json) { - final map = {}; + static Map mapFromJson(dynamic json) { + final map = {}; if (json is Map && json.isNotEmpty) { json = json.cast(); // ignore: parameter_assignments for (final entry in json.entries) { - final value = UpdateTagDto.fromJson(entry.value); + final value = TagCreateDto.fromJson(entry.value); if (value != null) { map[entry.key] = value; } @@ -87,14 +105,14 @@ class UpdateTagDto { return map; } - // maps a json object with a list of UpdateTagDto-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = >{}; + // maps a json object with a list of TagCreateDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; if (json is Map && json.isNotEmpty) { // ignore: parameter_assignments json = json.cast(); for (final entry in json.entries) { - map[entry.key] = UpdateTagDto.listFromJson(entry.value, growable: growable,); + map[entry.key] = TagCreateDto.listFromJson(entry.value, growable: growable,); } } return map; @@ -102,6 +120,7 @@ class UpdateTagDto { /// The list of required keys that must be present in a JSON. static const requiredKeys = { + 'name', }; } diff --git a/mobile/openapi/lib/model/tag_response_dto.dart b/mobile/openapi/lib/model/tag_response_dto.dart index d371bd1c0473d..4f0a62a8b9669 100644 --- a/mobile/openapi/lib/model/tag_response_dto.dart +++ b/mobile/openapi/lib/model/tag_response_dto.dart @@ -13,44 +13,66 @@ part of openapi.api; class TagResponseDto { /// Returns a new [TagResponseDto] instance. TagResponseDto({ + this.color, + required this.createdAt, required this.id, required this.name, - required this.type, - required this.userId, + required this.updatedAt, + required this.value, }); + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? color; + + DateTime createdAt; + String id; String name; - TagTypeEnum type; + DateTime updatedAt; - String userId; + String value; @override bool operator ==(Object other) => identical(this, other) || other is TagResponseDto && + other.color == color && + other.createdAt == createdAt && other.id == id && other.name == name && - other.type == type && - other.userId == userId; + other.updatedAt == updatedAt && + other.value == value; @override int get hashCode => // ignore: unnecessary_parenthesis + (color == null ? 0 : color!.hashCode) + + (createdAt.hashCode) + (id.hashCode) + (name.hashCode) + - (type.hashCode) + - (userId.hashCode); + (updatedAt.hashCode) + + (value.hashCode); @override - String toString() => 'TagResponseDto[id=$id, name=$name, type=$type, userId=$userId]'; + String toString() => 'TagResponseDto[color=$color, createdAt=$createdAt, id=$id, name=$name, updatedAt=$updatedAt, value=$value]'; Map toJson() { final json = {}; + if (this.color != null) { + json[r'color'] = this.color; + } else { + // json[r'color'] = null; + } + json[r'createdAt'] = this.createdAt.toUtc().toIso8601String(); json[r'id'] = this.id; json[r'name'] = this.name; - json[r'type'] = this.type; - json[r'userId'] = this.userId; + json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String(); + json[r'value'] = this.value; return json; } @@ -62,10 +84,12 @@ class TagResponseDto { final json = value.cast(); return TagResponseDto( + color: mapValueOfType(json, r'color'), + createdAt: mapDateTime(json, r'createdAt', r'')!, id: mapValueOfType(json, r'id')!, name: mapValueOfType(json, r'name')!, - type: TagTypeEnum.fromJson(json[r'type'])!, - userId: mapValueOfType(json, r'userId')!, + updatedAt: mapDateTime(json, r'updatedAt', r'')!, + value: mapValueOfType(json, r'value')!, ); } return null; @@ -113,10 +137,11 @@ class TagResponseDto { /// The list of required keys that must be present in a JSON. static const requiredKeys = { + 'createdAt', 'id', 'name', - 'type', - 'userId', + 'updatedAt', + 'value', }; } diff --git a/mobile/openapi/lib/model/tag_type_enum.dart b/mobile/openapi/lib/model/tag_type_enum.dart deleted file mode 100644 index 3f2e723796b81..0000000000000 --- a/mobile/openapi/lib/model/tag_type_enum.dart +++ /dev/null @@ -1,88 +0,0 @@ -// -// AUTO-GENERATED FILE, DO NOT MODIFY! -// -// @dart=2.18 - -// ignore_for_file: unused_element, unused_import -// ignore_for_file: always_put_required_named_parameters_first -// ignore_for_file: constant_identifier_names -// ignore_for_file: lines_longer_than_80_chars - -part of openapi.api; - - -class TagTypeEnum { - /// Instantiate a new enum with the provided [value]. - const TagTypeEnum._(this.value); - - /// The underlying value of this enum member. - final String value; - - @override - String toString() => value; - - String toJson() => value; - - static const OBJECT = TagTypeEnum._(r'OBJECT'); - static const FACE = TagTypeEnum._(r'FACE'); - static const CUSTOM = TagTypeEnum._(r'CUSTOM'); - - /// List of all possible values in this [enum][TagTypeEnum]. - static const values = [ - OBJECT, - FACE, - CUSTOM, - ]; - - static TagTypeEnum? fromJson(dynamic value) => TagTypeEnumTypeTransformer().decode(value); - - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; - if (json is List && json.isNotEmpty) { - for (final row in json) { - final value = TagTypeEnum.fromJson(row); - if (value != null) { - result.add(value); - } - } - } - return result.toList(growable: growable); - } -} - -/// Transformation class that can [encode] an instance of [TagTypeEnum] to String, -/// and [decode] dynamic data back to [TagTypeEnum]. -class TagTypeEnumTypeTransformer { - factory TagTypeEnumTypeTransformer() => _instance ??= const TagTypeEnumTypeTransformer._(); - - const TagTypeEnumTypeTransformer._(); - - String encode(TagTypeEnum data) => data.value; - - /// Decodes a [dynamic value][data] to a TagTypeEnum. - /// - /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, - /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] - /// cannot be decoded successfully, then an [UnimplementedError] is thrown. - /// - /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, - /// and users are still using an old app with the old code. - TagTypeEnum? decode(dynamic data, {bool allowNull = true}) { - if (data != null) { - switch (data) { - case r'OBJECT': return TagTypeEnum.OBJECT; - case r'FACE': return TagTypeEnum.FACE; - case r'CUSTOM': return TagTypeEnum.CUSTOM; - default: - if (!allowNull) { - throw ArgumentError('Unknown enum value to decode: $data'); - } - } - } - return null; - } - - /// Singleton [TagTypeEnumTypeTransformer] instance. - static TagTypeEnumTypeTransformer? _instance; -} - diff --git a/mobile/openapi/lib/model/create_tag_dto.dart b/mobile/openapi/lib/model/tag_update_dto.dart similarity index 57% rename from mobile/openapi/lib/model/create_tag_dto.dart rename to mobile/openapi/lib/model/tag_update_dto.dart index 31b194993d22d..661f65896e56f 100644 --- a/mobile/openapi/lib/model/create_tag_dto.dart +++ b/mobile/openapi/lib/model/tag_update_dto.dart @@ -10,58 +10,55 @@ part of openapi.api; -class CreateTagDto { - /// Returns a new [CreateTagDto] instance. - CreateTagDto({ - required this.name, - required this.type, +class TagUpdateDto { + /// Returns a new [TagUpdateDto] instance. + TagUpdateDto({ + this.color, }); - String name; - - TagTypeEnum type; + String? color; @override - bool operator ==(Object other) => identical(this, other) || other is CreateTagDto && - other.name == name && - other.type == type; + bool operator ==(Object other) => identical(this, other) || other is TagUpdateDto && + other.color == color; @override int get hashCode => // ignore: unnecessary_parenthesis - (name.hashCode) + - (type.hashCode); + (color == null ? 0 : color!.hashCode); @override - String toString() => 'CreateTagDto[name=$name, type=$type]'; + String toString() => 'TagUpdateDto[color=$color]'; Map toJson() { final json = {}; - json[r'name'] = this.name; - json[r'type'] = this.type; + if (this.color != null) { + json[r'color'] = this.color; + } else { + // json[r'color'] = null; + } return json; } - /// Returns a new [CreateTagDto] instance and imports its values from + /// Returns a new [TagUpdateDto] instance and imports its values from /// [value] if it's a [Map], null otherwise. // ignore: prefer_constructors_over_static_methods - static CreateTagDto? fromJson(dynamic value) { + static TagUpdateDto? fromJson(dynamic value) { if (value is Map) { final json = value.cast(); - return CreateTagDto( - name: mapValueOfType(json, r'name')!, - type: TagTypeEnum.fromJson(json[r'type'])!, + return TagUpdateDto( + color: mapValueOfType(json, r'color'), ); } return null; } - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; if (json is List && json.isNotEmpty) { for (final row in json) { - final value = CreateTagDto.fromJson(row); + final value = TagUpdateDto.fromJson(row); if (value != null) { result.add(value); } @@ -70,12 +67,12 @@ class CreateTagDto { return result.toList(growable: growable); } - static Map mapFromJson(dynamic json) { - final map = {}; + static Map mapFromJson(dynamic json) { + final map = {}; if (json is Map && json.isNotEmpty) { json = json.cast(); // ignore: parameter_assignments for (final entry in json.entries) { - final value = CreateTagDto.fromJson(entry.value); + final value = TagUpdateDto.fromJson(entry.value); if (value != null) { map[entry.key] = value; } @@ -84,14 +81,14 @@ class CreateTagDto { return map; } - // maps a json object with a list of CreateTagDto-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = >{}; + // maps a json object with a list of TagUpdateDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; if (json is Map && json.isNotEmpty) { // ignore: parameter_assignments json = json.cast(); for (final entry in json.entries) { - map[entry.key] = CreateTagDto.listFromJson(entry.value, growable: growable,); + map[entry.key] = TagUpdateDto.listFromJson(entry.value, growable: growable,); } } return map; @@ -99,8 +96,6 @@ class CreateTagDto { /// The list of required keys that must be present in a JSON. static const requiredKeys = { - 'name', - 'type', }; } diff --git a/mobile/openapi/lib/model/tag_upsert_dto.dart b/mobile/openapi/lib/model/tag_upsert_dto.dart new file mode 100644 index 0000000000000..941d25b6aee6c --- /dev/null +++ b/mobile/openapi/lib/model/tag_upsert_dto.dart @@ -0,0 +1,100 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class TagUpsertDto { + /// Returns a new [TagUpsertDto] instance. + TagUpsertDto({ + this.tags = const [], + }); + + List tags; + + @override + bool operator ==(Object other) => identical(this, other) || other is TagUpsertDto && + _deepEquality.equals(other.tags, tags); + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (tags.hashCode); + + @override + String toString() => 'TagUpsertDto[tags=$tags]'; + + Map toJson() { + final json = {}; + json[r'tags'] = this.tags; + return json; + } + + /// Returns a new [TagUpsertDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static TagUpsertDto? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + return TagUpsertDto( + tags: json[r'tags'] is Iterable + ? (json[r'tags'] as Iterable).cast().toList(growable: false) + : const [], + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = TagUpsertDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = TagUpsertDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of TagUpsertDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = TagUpsertDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'tags', + }; +} + diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 2137bf7b11ff1..4d80353177d36 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -6169,7 +6169,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateTagDto" + "$ref": "#/components/schemas/TagCreateDto" } } }, @@ -6201,6 +6201,91 @@ "tags": [ "Tags" ] + }, + "put": { + "operationId": "upsertTags", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TagUpsertDto" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/TagResponseDto" + }, + "type": "array" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Tags" + ] + } + }, + "/tags/assets": { + "put": { + "operationId": "bulkTagAssets", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TagBulkAssetsDto" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TagBulkAssetsResponseDto" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Tags" + ] } }, "/tags/{id}": { @@ -6218,7 +6303,7 @@ } ], "responses": { - "200": { + "204": { "description": "" } }, @@ -6277,7 +6362,7 @@ "Tags" ] }, - "patch": { + "put": { "operationId": "updateTag", "parameters": [ { @@ -6294,7 +6379,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateTagDto" + "$ref": "#/components/schemas/TagUpdateDto" } } }, @@ -6346,7 +6431,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AssetIdsDto" + "$ref": "#/components/schemas/BulkIdsDto" } } }, @@ -6358,50 +6443,7 @@ "application/json": { "schema": { "items": { - "$ref": "#/components/schemas/AssetIdsResponseDto" - }, - "type": "array" - } - } - }, - "description": "" - } - }, - "security": [ - { - "bearer": [] - }, - { - "cookie": [] - }, - { - "api_key": [] - } - ], - "tags": [ - "Tags" - ] - }, - "get": { - "operationId": "getTagAssets", - "parameters": [ - { - "name": "id", - "required": true, - "in": "path", - "schema": { - "format": "uuid", - "type": "string" - } - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "items": { - "$ref": "#/components/schemas/AssetResponseDto" + "$ref": "#/components/schemas/BulkIdResponseDto" }, "type": "array" } @@ -6442,7 +6484,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AssetIdsDto" + "$ref": "#/components/schemas/BulkIdsDto" } } }, @@ -6454,7 +6496,7 @@ "application/json": { "schema": { "items": { - "$ref": "#/components/schemas/AssetIdsResponseDto" + "$ref": "#/components/schemas/BulkIdResponseDto" }, "type": "array" } @@ -6549,6 +6591,15 @@ "$ref": "#/components/schemas/TimeBucketSize" } }, + { + "name": "tagId", + "required": false, + "in": "query", + "schema": { + "format": "uuid", + "type": "string" + } + }, { "name": "timeBucket", "required": true, @@ -6684,6 +6735,15 @@ "$ref": "#/components/schemas/TimeBucketSize" } }, + { + "name": "tagId", + "required": false, + "in": "query", + "schema": { + "format": "uuid", + "type": "string" + } + }, { "name": "userId", "required": false, @@ -8685,21 +8745,6 @@ ], "type": "object" }, - "CreateTagDto": { - "properties": { - "name": { - "type": "string" - }, - "type": { - "$ref": "#/components/schemas/TagTypeEnum" - } - }, - "required": [ - "name", - "type" - ], - "type": "object" - }, "DownloadArchiveInfo": { "properties": { "assetIds": { @@ -10053,6 +10098,7 @@ "tag.read", "tag.update", "tag.delete", + "tag.asset", "admin.user.create", "admin.user.read", "admin.user.update", @@ -11848,36 +11894,113 @@ ], "type": "object" }, + "TagBulkAssetsDto": { + "properties": { + "assetIds": { + "items": { + "format": "uuid", + "type": "string" + }, + "type": "array" + }, + "tagIds": { + "items": { + "format": "uuid", + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "assetIds", + "tagIds" + ], + "type": "object" + }, + "TagBulkAssetsResponseDto": { + "properties": { + "count": { + "type": "integer" + } + }, + "required": [ + "count" + ], + "type": "object" + }, + "TagCreateDto": { + "properties": { + "color": { + "type": "string" + }, + "name": { + "type": "string" + }, + "parentId": { + "format": "uuid", + "nullable": true, + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, "TagResponseDto": { "properties": { + "color": { + "type": "string" + }, + "createdAt": { + "format": "date-time", + "type": "string" + }, "id": { "type": "string" }, "name": { "type": "string" }, - "type": { - "$ref": "#/components/schemas/TagTypeEnum" + "updatedAt": { + "format": "date-time", + "type": "string" }, - "userId": { + "value": { "type": "string" } }, "required": [ + "createdAt", "id", "name", - "type", - "userId" + "updatedAt", + "value" ], "type": "object" }, - "TagTypeEnum": { - "enum": [ - "OBJECT", - "FACE", - "CUSTOM" + "TagUpdateDto": { + "properties": { + "color": { + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "TagUpsertDto": { + "properties": { + "tags": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "tags" ], - "type": "string" + "type": "object" }, "TimeBucketResponseDto": { "properties": { @@ -12021,14 +12144,6 @@ ], "type": "object" }, - "UpdateTagDto": { - "properties": { - "name": { - "type": "string" - } - }, - "type": "object" - }, "UsageByUserDto": { "properties": { "photos": { diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index bf0c63c2b8c9a..3fdcf33757932 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -198,10 +198,12 @@ export type AssetStackResponseDto = { primaryAssetId: string; }; export type TagResponseDto = { + color?: string; + createdAt: string; id: string; name: string; - "type": TagTypeEnum; - userId: string; + updatedAt: string; + value: string; }; export type AssetResponseDto = { /** base64 encoded sha1 hash */ @@ -1171,12 +1173,23 @@ export type ReverseGeocodingStateResponseDto = { lastImportFileName: string | null; lastUpdate: string | null; }; -export type CreateTagDto = { +export type TagCreateDto = { + color?: string; name: string; - "type": TagTypeEnum; + parentId?: string | null; }; -export type UpdateTagDto = { - name?: string; +export type TagUpsertDto = { + tags: string[]; +}; +export type TagBulkAssetsDto = { + assetIds: string[]; + tagIds: string[]; +}; +export type TagBulkAssetsResponseDto = { + count: number; +}; +export type TagUpdateDto = { + color?: string | null; }; export type TimeBucketResponseDto = { count: number; @@ -2835,8 +2848,8 @@ export function getAllTags(opts?: Oazapfts.RequestOpts) { ...opts })); } -export function createTag({ createTagDto }: { - createTagDto: CreateTagDto; +export function createTag({ tagCreateDto }: { + tagCreateDto: TagCreateDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 201; @@ -2844,7 +2857,31 @@ export function createTag({ createTagDto }: { }>("/tags", oazapfts.json({ ...opts, method: "POST", - body: createTagDto + body: tagCreateDto + }))); +} +export function upsertTags({ tagUpsertDto }: { + tagUpsertDto: TagUpsertDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: TagResponseDto[]; + }>("/tags", oazapfts.json({ + ...opts, + method: "PUT", + body: tagUpsertDto + }))); +} +export function bulkTagAssets({ tagBulkAssetsDto }: { + tagBulkAssetsDto: TagBulkAssetsDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: TagBulkAssetsResponseDto; + }>("/tags/assets", oazapfts.json({ + ...opts, + method: "PUT", + body: tagBulkAssetsDto }))); } export function deleteTag({ id }: { @@ -2865,56 +2902,46 @@ export function getTagById({ id }: { ...opts })); } -export function updateTag({ id, updateTagDto }: { +export function updateTag({ id, tagUpdateDto }: { id: string; - updateTagDto: UpdateTagDto; + tagUpdateDto: TagUpdateDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: TagResponseDto; }>(`/tags/${encodeURIComponent(id)}`, oazapfts.json({ ...opts, - method: "PATCH", - body: updateTagDto + method: "PUT", + body: tagUpdateDto }))); } -export function untagAssets({ id, assetIdsDto }: { +export function untagAssets({ id, bulkIdsDto }: { id: string; - assetIdsDto: AssetIdsDto; + bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; - data: AssetIdsResponseDto[]; + data: BulkIdResponseDto[]; }>(`/tags/${encodeURIComponent(id)}/assets`, oazapfts.json({ ...opts, method: "DELETE", - body: assetIdsDto + body: bulkIdsDto }))); } -export function getTagAssets({ id }: { +export function tagAssets({ id, bulkIdsDto }: { id: string; + bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; - data: AssetResponseDto[]; - }>(`/tags/${encodeURIComponent(id)}/assets`, { - ...opts - })); -} -export function tagAssets({ id, assetIdsDto }: { - id: string; - assetIdsDto: AssetIdsDto; -}, opts?: Oazapfts.RequestOpts) { - return oazapfts.ok(oazapfts.fetchJson<{ - status: 200; - data: AssetIdsResponseDto[]; + data: BulkIdResponseDto[]; }>(`/tags/${encodeURIComponent(id)}/assets`, oazapfts.json({ ...opts, method: "PUT", - body: assetIdsDto + body: bulkIdsDto }))); } -export function getTimeBucket({ albumId, isArchived, isFavorite, isTrashed, key, order, personId, size, timeBucket, userId, withPartners, withStacked }: { +export function getTimeBucket({ albumId, isArchived, isFavorite, isTrashed, key, order, personId, size, tagId, timeBucket, userId, withPartners, withStacked }: { albumId?: string; isArchived?: boolean; isFavorite?: boolean; @@ -2923,6 +2950,7 @@ export function getTimeBucket({ albumId, isArchived, isFavorite, isTrashed, key, order?: AssetOrder; personId?: string; size: TimeBucketSize; + tagId?: string; timeBucket: string; userId?: string; withPartners?: boolean; @@ -2940,6 +2968,7 @@ export function getTimeBucket({ albumId, isArchived, isFavorite, isTrashed, key, order, personId, size, + tagId, timeBucket, userId, withPartners, @@ -2948,7 +2977,7 @@ export function getTimeBucket({ albumId, isArchived, isFavorite, isTrashed, key, ...opts })); } -export function getTimeBuckets({ albumId, isArchived, isFavorite, isTrashed, key, order, personId, size, userId, withPartners, withStacked }: { +export function getTimeBuckets({ albumId, isArchived, isFavorite, isTrashed, key, order, personId, size, tagId, userId, withPartners, withStacked }: { albumId?: string; isArchived?: boolean; isFavorite?: boolean; @@ -2957,6 +2986,7 @@ export function getTimeBuckets({ albumId, isArchived, isFavorite, isTrashed, key order?: AssetOrder; personId?: string; size: TimeBucketSize; + tagId?: string; userId?: string; withPartners?: boolean; withStacked?: boolean; @@ -2973,6 +3003,7 @@ export function getTimeBuckets({ albumId, isArchived, isFavorite, isTrashed, key order, personId, size, + tagId, userId, withPartners, withStacked @@ -3162,11 +3193,6 @@ export enum AlbumUserRole { Editor = "editor", Viewer = "viewer" } -export enum TagTypeEnum { - Object = "OBJECT", - Face = "FACE", - Custom = "CUSTOM" -} export enum AssetTypeEnum { Image = "IMAGE", Video = "VIDEO", @@ -3257,6 +3283,7 @@ export enum Permission { TagRead = "tag.read", TagUpdate = "tag.update", TagDelete = "tag.delete", + TagAsset = "tag.asset", AdminUserCreate = "admin.user.create", AdminUserRead = "admin.user.read", AdminUserUpdate = "admin.user.update", diff --git a/server/src/controllers/tag.controller.ts b/server/src/controllers/tag.controller.ts index 8b646400cc960..cf6b8ac695c2c 100644 --- a/server/src/controllers/tag.controller.ts +++ b/server/src/controllers/tag.controller.ts @@ -1,10 +1,15 @@ -import { Body, Controller, Delete, Get, Param, Patch, Post, Put } from '@nestjs/common'; +import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto'; -import { AssetResponseDto } from 'src/dtos/asset-response.dto'; -import { AssetIdsDto } from 'src/dtos/asset.dto'; +import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; -import { CreateTagDto, TagResponseDto, UpdateTagDto } from 'src/dtos/tag.dto'; +import { + TagBulkAssetsDto, + TagBulkAssetsResponseDto, + TagCreateDto, + TagResponseDto, + TagUpdateDto, + TagUpsertDto, +} from 'src/dtos/tag.dto'; import { Permission } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { TagService } from 'src/services/tag.service'; @@ -17,7 +22,7 @@ export class TagController { @Post() @Authenticated({ permission: Permission.TAG_CREATE }) - createTag(@Auth() auth: AuthDto, @Body() dto: CreateTagDto): Promise { + createTag(@Auth() auth: AuthDto, @Body() dto: TagCreateDto): Promise { return this.service.create(auth, dto); } @@ -27,47 +32,54 @@ export class TagController { return this.service.getAll(auth); } + @Put() + @Authenticated({ permission: Permission.TAG_CREATE }) + upsertTags(@Auth() auth: AuthDto, @Body() dto: TagUpsertDto): Promise { + return this.service.upsert(auth, dto); + } + + @Put('assets') + @Authenticated({ permission: Permission.TAG_ASSET }) + bulkTagAssets(@Auth() auth: AuthDto, @Body() dto: TagBulkAssetsDto): Promise { + return this.service.bulkTagAssets(auth, dto); + } + @Get(':id') @Authenticated({ permission: Permission.TAG_READ }) getTagById(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { - return this.service.getById(auth, id); + return this.service.get(auth, id); } - @Patch(':id') + @Put(':id') @Authenticated({ permission: Permission.TAG_UPDATE }) - updateTag(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @Body() dto: UpdateTagDto): Promise { + updateTag(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @Body() dto: TagUpdateDto): Promise { return this.service.update(auth, id, dto); } @Delete(':id') + @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.TAG_DELETE }) deleteTag(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.remove(auth, id); } - @Get(':id/assets') - @Authenticated() - getTagAssets(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { - return this.service.getAssets(auth, id); - } - @Put(':id/assets') - @Authenticated() + @Authenticated({ permission: Permission.TAG_ASSET }) tagAssets( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, - @Body() dto: AssetIdsDto, - ): Promise { + @Body() dto: BulkIdsDto, + ): Promise { return this.service.addAssets(auth, id, dto); } @Delete(':id/assets') - @Authenticated() + @Authenticated({ permission: Permission.TAG_ASSET }) untagAssets( @Auth() auth: AuthDto, - @Body() dto: AssetIdsDto, + @Body() dto: BulkIdsDto, @Param() { id }: UUIDParamDto, - ): Promise { + ): Promise { return this.service.removeAssets(auth, id, dto); } } diff --git a/server/src/dtos/asset-response.dto.ts b/server/src/dtos/asset-response.dto.ts index caeae2971a228..463ab119a694d 100644 --- a/server/src/dtos/asset-response.dto.ts +++ b/server/src/dtos/asset-response.dto.ts @@ -140,7 +140,7 @@ export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): As exifInfo: entity.exifInfo ? mapExif(entity.exifInfo) : undefined, smartInfo: entity.smartInfo ? mapSmartInfo(entity.smartInfo) : undefined, livePhotoVideoId: entity.livePhotoVideoId, - tags: entity.tags?.map(mapTag), + tags: entity.tags?.map((tag) => mapTag(tag)), people: peopleWithFaces(entity.faces), unassignedFaces: entity.faces?.filter((face) => !face.person).map((a) => mapFacesWithoutPerson(a)), checksum: entity.checksum.toString('base64'), diff --git a/server/src/dtos/tag.dto.ts b/server/src/dtos/tag.dto.ts index 1094d70df375a..40c5b176ffc3a 100644 --- a/server/src/dtos/tag.dto.ts +++ b/server/src/dtos/tag.dto.ts @@ -1,38 +1,64 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; -import { TagEntity, TagType } from 'src/entities/tag.entity'; -import { Optional } from 'src/validation'; +import { Transform } from 'class-transformer'; +import { IsHexColor, IsNotEmpty, IsString } from 'class-validator'; +import { TagEntity } from 'src/entities/tag.entity'; +import { Optional, ValidateUUID } from 'src/validation'; -export class CreateTagDto { +export class TagCreateDto { @IsString() @IsNotEmpty() name!: string; - @IsEnum(TagType) - @IsNotEmpty() - @ApiProperty({ enumName: 'TagTypeEnum', enum: TagType }) - type!: TagType; + @ValidateUUID({ optional: true, nullable: true }) + parentId?: string | null; + + @IsHexColor() + @Optional({ nullable: true, emptyToNull: true }) + color?: string; } -export class UpdateTagDto { - @IsString() - @Optional() - name?: string; +export class TagUpdateDto { + @Optional({ nullable: true, emptyToNull: true }) + @IsHexColor() + @Transform(({ value }) => (typeof value === 'string' && value[0] !== '#' ? `#${value}` : value)) + color?: string | null; +} + +export class TagUpsertDto { + @IsString({ each: true }) + @IsNotEmpty({ each: true }) + tags!: string[]; +} + +export class TagBulkAssetsDto { + @ValidateUUID({ each: true }) + tagIds!: string[]; + + @ValidateUUID({ each: true }) + assetIds!: string[]; +} + +export class TagBulkAssetsResponseDto { + @ApiProperty({ type: 'integer' }) + count!: number; } export class TagResponseDto { id!: string; - @ApiProperty({ enumName: 'TagTypeEnum', enum: TagType }) - type!: string; name!: string; - userId!: string; + value!: string; + createdAt!: Date; + updatedAt!: Date; + color?: string; } export function mapTag(entity: TagEntity): TagResponseDto { return { id: entity.id, - type: entity.type, - name: entity.name, - userId: entity.userId, + name: entity.value.split('/').at(-1) as string, + value: entity.value, + createdAt: entity.createdAt, + updatedAt: entity.updatedAt, + color: entity.color ?? undefined, }; } diff --git a/server/src/dtos/time-bucket.dto.ts b/server/src/dtos/time-bucket.dto.ts index 8803f24fc467d..dd7a01df356ef 100644 --- a/server/src/dtos/time-bucket.dto.ts +++ b/server/src/dtos/time-bucket.dto.ts @@ -19,6 +19,9 @@ export class TimeBucketDto { @ValidateUUID({ optional: true }) personId?: string; + @ValidateUUID({ optional: true }) + tagId?: string; + @ValidateBoolean({ optional: true }) isArchived?: boolean; diff --git a/server/src/entities/tag.entity.ts b/server/src/entities/tag.entity.ts index 93edcb0555656..940b446aeafcc 100644 --- a/server/src/entities/tag.entity.ts +++ b/server/src/entities/tag.entity.ts @@ -1,45 +1,48 @@ import { AssetEntity } from 'src/entities/asset.entity'; import { UserEntity } from 'src/entities/user.entity'; -import { Column, Entity, ManyToMany, ManyToOne, PrimaryGeneratedColumn, Unique } from 'typeorm'; +import { + Column, + CreateDateColumn, + Entity, + ManyToMany, + ManyToOne, + PrimaryGeneratedColumn, + Tree, + TreeChildren, + TreeParent, + UpdateDateColumn, +} from 'typeorm'; @Entity('tags') -@Unique('UQ_tag_name_userId', ['name', 'userId']) +@Tree('closure-table') export class TagEntity { @PrimaryGeneratedColumn('uuid') id!: string; - @Column() - type!: TagType; + @Column({ unique: true }) + value!: string; - @Column() - name!: string; + @CreateDateColumn({ type: 'timestamptz' }) + createdAt!: Date; - @ManyToOne(() => UserEntity, (user) => user.tags) - user!: UserEntity; + @UpdateDateColumn({ type: 'timestamptz' }) + updatedAt!: Date; + + @Column({ type: 'varchar', nullable: true, default: null }) + color!: string | null; + + @TreeParent({ onDelete: 'CASCADE' }) + parent?: TagEntity; + + @TreeChildren() + children?: TagEntity[]; + + @ManyToOne(() => UserEntity, (user) => user.tags, { onUpdate: 'CASCADE', onDelete: 'CASCADE' }) + user?: UserEntity; @Column() userId!: string; - @Column({ type: 'uuid', comment: 'The new renamed tagId', nullable: true }) - renameTagId!: string | null; - - @ManyToMany(() => AssetEntity, (asset) => asset.tags) - assets!: AssetEntity[]; -} - -export enum TagType { - /** - * Tag that is detected by the ML model for object detection will use this type - */ - OBJECT = 'OBJECT', - - /** - * Face that is detected by the ML model for facial detection (TBD/NOT YET IMPLEMENTED) will use this type - */ - FACE = 'FACE', - - /** - * Tag that is created by the user will use this type - */ - CUSTOM = 'CUSTOM', + @ManyToMany(() => AssetEntity, (asset) => asset.tags, { onUpdate: 'CASCADE', onDelete: 'CASCADE' }) + assets?: AssetEntity[]; } diff --git a/server/src/enum.ts b/server/src/enum.ts index 25ccbf961ed4b..9cd5c189e8431 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -130,6 +130,7 @@ export enum Permission { TAG_READ = 'tag.read', TAG_UPDATE = 'tag.update', TAG_DELETE = 'tag.delete', + TAG_ASSET = 'tag.asset', ADMIN_USER_CREATE = 'admin.user.create', ADMIN_USER_READ = 'admin.user.read', diff --git a/server/src/interfaces/access.interface.ts b/server/src/interfaces/access.interface.ts index 2dcf9d6b942a7..d8d7b4e807ab9 100644 --- a/server/src/interfaces/access.interface.ts +++ b/server/src/interfaces/access.interface.ts @@ -46,4 +46,8 @@ export interface IAccessRepository { stack: { checkOwnerAccess(userId: string, stackIds: Set): Promise>; }; + + tag: { + checkOwnerAccess(userId: string, tagIds: Set): Promise>; + }; } diff --git a/server/src/interfaces/asset.interface.ts b/server/src/interfaces/asset.interface.ts index 9f9218a3e3534..e323d98640a9e 100644 --- a/server/src/interfaces/asset.interface.ts +++ b/server/src/interfaces/asset.interface.ts @@ -51,6 +51,7 @@ export interface AssetBuilderOptions { isTrashed?: boolean; isDuplicate?: boolean; albumId?: string; + tagId?: string; personId?: string; userIds?: string[]; withStacked?: boolean; diff --git a/server/src/interfaces/event.interface.ts b/server/src/interfaces/event.interface.ts index 609f42cc32016..bb2b0d9ab4bc9 100644 --- a/server/src/interfaces/event.interface.ts +++ b/server/src/interfaces/event.interface.ts @@ -17,6 +17,10 @@ type EmitEventMap = { 'album.update': [{ id: string; updatedBy: string }]; 'album.invite': [{ id: string; userId: string }]; + // tag events + 'asset.tag': [{ assetId: string }]; + 'asset.untag': [{ assetId: string }]; + // user events 'user.signup': [{ notify: boolean; id: string; tempPassword?: string }]; }; diff --git a/server/src/interfaces/job.interface.ts b/server/src/interfaces/job.interface.ts index b2ac5ec6f12d9..bc780398eaf05 100644 --- a/server/src/interfaces/job.interface.ts +++ b/server/src/interfaces/job.interface.ts @@ -155,6 +155,7 @@ export interface ISidecarWriteJob extends IEntityJob { latitude?: number; longitude?: number; rating?: number; + tags?: true; } export interface IDeferrableJob extends IEntityJob { diff --git a/server/src/interfaces/tag.interface.ts b/server/src/interfaces/tag.interface.ts index 8071461dfca07..f9f3784f065d3 100644 --- a/server/src/interfaces/tag.interface.ts +++ b/server/src/interfaces/tag.interface.ts @@ -1,17 +1,19 @@ -import { AssetEntity } from 'src/entities/asset.entity'; import { TagEntity } from 'src/entities/tag.entity'; +import { IBulkAsset } from 'src/utils/asset.util'; export const ITagRepository = 'ITagRepository'; -export interface ITagRepository { - getById(userId: string, tagId: string): Promise; +export type AssetTagItem = { assetId: string; tagId: string }; + +export interface ITagRepository extends IBulkAsset { getAll(userId: string): Promise; + getByValue(userId: string, value: string): Promise; + create(tag: Partial): Promise; - update(tag: Partial): Promise; - remove(tag: TagEntity): Promise; - hasName(userId: string, name: string): Promise; - hasAsset(userId: string, tagId: string, assetId: string): Promise; - getAssets(userId: string, tagId: string): Promise; - addAssets(userId: string, tagId: string, assetIds: string[]): Promise; - removeAssets(userId: string, tagId: string, assetIds: string[]): Promise; + get(id: string): Promise; + update(tag: { id: string } & Partial): Promise; + delete(id: string): Promise; + + upsertAssetTags({ assetId, tagIds }: { assetId: string; tagIds: string[] }): Promise; + upsertAssetIds(items: AssetTagItem[]): Promise; } diff --git a/server/src/migrations/1724790460210-NestedTagTable.ts b/server/src/migrations/1724790460210-NestedTagTable.ts new file mode 100644 index 0000000000000..dfda9a6d7a38e --- /dev/null +++ b/server/src/migrations/1724790460210-NestedTagTable.ts @@ -0,0 +1,57 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class NestedTagTable1724790460210 implements MigrationInterface { + name = 'NestedTagTable1724790460210' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query('TRUNCATE TABLE "tags" CASCADE'); + await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_92e67dc508c705dd66c94615576"`); + await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_tag_name_userId"`); + await queryRunner.query(`CREATE TABLE "tags_closure" ("id_ancestor" uuid NOT NULL, "id_descendant" uuid NOT NULL, CONSTRAINT "PK_eab38eb12a3ec6df8376c95477c" PRIMARY KEY ("id_ancestor", "id_descendant"))`); + await queryRunner.query(`CREATE INDEX "IDX_15fbcbc67663c6bfc07b354c22" ON "tags_closure" ("id_ancestor") `); + await queryRunner.query(`CREATE INDEX "IDX_b1a2a7ed45c29179b5ad51548a" ON "tags_closure" ("id_descendant") `); + await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "renameTagId"`); + await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "type"`); + await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "name"`); + await queryRunner.query(`ALTER TABLE "tags" ADD "value" character varying NOT NULL`); + await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451" UNIQUE ("value")`); + await queryRunner.query(`ALTER TABLE "tags" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); + await queryRunner.query(`ALTER TABLE "tags" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); + await queryRunner.query(`ALTER TABLE "tags" ADD "color" character varying`); + await queryRunner.query(`ALTER TABLE "tags" ADD "parentId" uuid`); + await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_9f9590cc11561f1f48ff034ef99" FOREIGN KEY ("parentId") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_92e67dc508c705dd66c94615576" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); + await queryRunner.query(`ALTER TABLE "tags_closure" ADD CONSTRAINT "FK_15fbcbc67663c6bfc07b354c22c" FOREIGN KEY ("id_ancestor") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "tags_closure" ADD CONSTRAINT "FK_b1a2a7ed45c29179b5ad51548a1" FOREIGN KEY ("id_descendant") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42"`); + await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9"`); + await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); + await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42" FOREIGN KEY ("tagsId") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE CASCADE`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42"`); + await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9"`); + await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); + await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42" FOREIGN KEY ("tagsId") REFERENCES "tags"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "tags_closure" DROP CONSTRAINT "FK_b1a2a7ed45c29179b5ad51548a1"`); + await queryRunner.query(`ALTER TABLE "tags_closure" DROP CONSTRAINT "FK_15fbcbc67663c6bfc07b354c22c"`); + await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_92e67dc508c705dd66c94615576"`); + await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_9f9590cc11561f1f48ff034ef99"`); + await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "parentId"`); + await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "color"`); + await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "updatedAt"`); + await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451"`); + await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "value"`); + await queryRunner.query(`ALTER TABLE "tags" ADD "name" character varying NOT NULL`); + await queryRunner.query(`ALTER TABLE "tags" ADD "type" character varying NOT NULL`); + await queryRunner.query(`ALTER TABLE "tags" ADD "renameTagId" uuid`); + await queryRunner.query(`DROP INDEX "public"."IDX_b1a2a7ed45c29179b5ad51548a"`); + await queryRunner.query(`DROP INDEX "public"."IDX_15fbcbc67663c6bfc07b354c22"`); + await queryRunner.query(`DROP TABLE "tags_closure"`); + await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_tag_name_userId" UNIQUE ("name", "userId")`); + await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_92e67dc508c705dd66c94615576" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); + } + +} diff --git a/server/src/queries/access.repository.sql b/server/src/queries/access.repository.sql index 48a93f546b090..ad57eac0ad90d 100644 --- a/server/src/queries/access.repository.sql +++ b/server/src/queries/access.repository.sql @@ -259,6 +259,17 @@ WHERE AND ("StackEntity"."ownerId" = $2) ) +-- AccessRepository.tag.checkOwnerAccess +SELECT + "TagEntity"."id" AS "TagEntity_id" +FROM + "tags" "TagEntity" +WHERE + ( + ("TagEntity"."id" IN ($1)) + AND ("TagEntity"."userId" = $2) + ) + -- AccessRepository.timeline.checkPartnerAccess SELECT "partner"."sharedById" AS "partner_sharedById", diff --git a/server/src/queries/asset.repository.sql b/server/src/queries/asset.repository.sql index b08130b183eb0..ba52f7d1481c1 100644 --- a/server/src/queries/asset.repository.sql +++ b/server/src/queries/asset.repository.sql @@ -184,10 +184,12 @@ SELECT "AssetEntity__AssetEntity_smartInfo"."tags" AS "AssetEntity__AssetEntity_smartInfo_tags", "AssetEntity__AssetEntity_smartInfo"."objects" AS "AssetEntity__AssetEntity_smartInfo_objects", "AssetEntity__AssetEntity_tags"."id" AS "AssetEntity__AssetEntity_tags_id", - "AssetEntity__AssetEntity_tags"."type" AS "AssetEntity__AssetEntity_tags_type", - "AssetEntity__AssetEntity_tags"."name" AS "AssetEntity__AssetEntity_tags_name", + "AssetEntity__AssetEntity_tags"."value" AS "AssetEntity__AssetEntity_tags_value", + "AssetEntity__AssetEntity_tags"."createdAt" AS "AssetEntity__AssetEntity_tags_createdAt", + "AssetEntity__AssetEntity_tags"."updatedAt" AS "AssetEntity__AssetEntity_tags_updatedAt", + "AssetEntity__AssetEntity_tags"."color" AS "AssetEntity__AssetEntity_tags_color", "AssetEntity__AssetEntity_tags"."userId" AS "AssetEntity__AssetEntity_tags_userId", - "AssetEntity__AssetEntity_tags"."renameTagId" AS "AssetEntity__AssetEntity_tags_renameTagId", + "AssetEntity__AssetEntity_tags"."parentId" AS "AssetEntity__AssetEntity_tags_parentId", "AssetEntity__AssetEntity_faces"."id" AS "AssetEntity__AssetEntity_faces_id", "AssetEntity__AssetEntity_faces"."assetId" AS "AssetEntity__AssetEntity_faces_assetId", "AssetEntity__AssetEntity_faces"."personId" AS "AssetEntity__AssetEntity_faces_personId", diff --git a/server/src/queries/tag.repository.sql b/server/src/queries/tag.repository.sql new file mode 100644 index 0000000000000..ba1aac82b356c --- /dev/null +++ b/server/src/queries/tag.repository.sql @@ -0,0 +1,30 @@ +-- NOTE: This file is auto generated by ./sql-generator + +-- TagRepository.getAssetIds +SELECT + "tag_asset"."assetsId" AS "assetId" +FROM + "tag_asset" "tag_asset" +WHERE + "tag_asset"."tagsId" = $1 + AND "tag_asset"."assetsId" IN ($2) + +-- TagRepository.addAssetIds +INSERT INTO + "tag_asset" ("assetsId", "tagsId") +VALUES + ($1, $2) + +-- TagRepository.removeAssetIds +DELETE FROM "tag_asset" +WHERE + ( + "tagsId" = $1 + AND "assetsId" IN ($2) + ) + +-- TagRepository.upsertAssetIds +INSERT INTO + "tag_asset" ("assetsId", "tagsId") +VALUES + ($1, $2) diff --git a/server/src/repositories/access.repository.ts b/server/src/repositories/access.repository.ts index 6dd6d47a468e2..f6921ffe27423 100644 --- a/server/src/repositories/access.repository.ts +++ b/server/src/repositories/access.repository.ts @@ -12,6 +12,7 @@ import { PersonEntity } from 'src/entities/person.entity'; import { SessionEntity } from 'src/entities/session.entity'; import { SharedLinkEntity } from 'src/entities/shared-link.entity'; import { StackEntity } from 'src/entities/stack.entity'; +import { TagEntity } from 'src/entities/tag.entity'; import { AlbumUserRole } from 'src/enum'; import { IAccessRepository } from 'src/interfaces/access.interface'; import { Instrumentation } from 'src/utils/instrumentation'; @@ -25,6 +26,7 @@ type IMemoryAccess = IAccessRepository['memory']; type IPersonAccess = IAccessRepository['person']; type IPartnerAccess = IAccessRepository['partner']; type IStackAccess = IAccessRepository['stack']; +type ITagAccess = IAccessRepository['tag']; type ITimelineAccess = IAccessRepository['timeline']; @Instrumentation() @@ -444,6 +446,28 @@ class PartnerAccess implements IPartnerAccess { } } +class TagAccess implements ITagAccess { + constructor(private tagRepository: Repository) {} + + @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) + @ChunkedSet({ paramIndex: 1 }) + async checkOwnerAccess(userId: string, tagIds: Set): Promise> { + if (tagIds.size === 0) { + return new Set(); + } + + return this.tagRepository + .find({ + select: { id: true }, + where: { + id: In([...tagIds]), + userId, + }, + }) + .then((tags) => new Set(tags.map((tag) => tag.id))); + } +} + export class AccessRepository implements IAccessRepository { activity: IActivityAccess; album: IAlbumAccess; @@ -453,6 +477,7 @@ export class AccessRepository implements IAccessRepository { person: IPersonAccess; partner: IPartnerAccess; stack: IStackAccess; + tag: ITagAccess; timeline: ITimelineAccess; constructor( @@ -467,6 +492,7 @@ export class AccessRepository implements IAccessRepository { @InjectRepository(SharedLinkEntity) sharedLinkRepository: Repository, @InjectRepository(SessionEntity) sessionRepository: Repository, @InjectRepository(StackEntity) stackRepository: Repository, + @InjectRepository(TagEntity) tagRepository: Repository, ) { this.activity = new ActivityAccess(activityRepository, albumRepository); this.album = new AlbumAccess(albumRepository, sharedLinkRepository); @@ -476,6 +502,7 @@ export class AccessRepository implements IAccessRepository { this.person = new PersonAccess(assetFaceRepository, personRepository); this.partner = new PartnerAccess(partnerRepository); this.stack = new StackAccess(stackRepository); + this.tag = new TagAccess(tagRepository); this.timeline = new TimelineAccess(partnerRepository); } } diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index 1a2a0474a10d7..dd526dd664b5a 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -723,6 +723,15 @@ export class AssetRepository implements IAssetRepository { builder.andWhere('asset.type = :assetType', { assetType: options.assetType }); } + if (options.tagId) { + builder.innerJoin( + 'asset.tags', + 'asset_tags', + 'asset_tags.id IN (SELECT id_descendant FROM tags_closure WHERE id_ancestor = :tagId)', + { tagId: options.tagId }, + ); + } + let stackJoined = false; if (options.exifInfo !== false) { diff --git a/server/src/repositories/tag.repository.ts b/server/src/repositories/tag.repository.ts index 788b9763578e2..7699d5897aab7 100644 --- a/server/src/repositories/tag.repository.ts +++ b/server/src/repositories/tag.repository.ts @@ -1,33 +1,36 @@ import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { AssetEntity } from 'src/entities/asset.entity'; +import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; +import { Chunked, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators'; import { TagEntity } from 'src/entities/tag.entity'; -import { ITagRepository } from 'src/interfaces/tag.interface'; +import { AssetTagItem, ITagRepository } from 'src/interfaces/tag.interface'; import { Instrumentation } from 'src/utils/instrumentation'; -import { Repository } from 'typeorm'; +import { DataSource, In, Repository } from 'typeorm'; @Instrumentation() @Injectable() export class TagRepository implements ITagRepository { constructor( - @InjectRepository(AssetEntity) private assetRepository: Repository, + @InjectDataSource() private dataSource: DataSource, @InjectRepository(TagEntity) private repository: Repository, ) {} - getById(userId: string, id: string): Promise { - return this.repository.findOne({ - where: { - id, - userId, - }, - relations: { - user: true, - }, - }); + get(id: string): Promise { + return this.repository.findOne({ where: { id } }); } - getAll(userId: string): Promise { - return this.repository.find({ where: { userId } }); + getByValue(userId: string, value: string): Promise { + return this.repository.findOne({ where: { userId, value } }); + } + + async getAll(userId: string): Promise { + const tags = await this.repository.find({ + where: { userId }, + order: { + value: 'ASC', + }, + }); + + return tags; } create(tag: Partial): Promise { @@ -38,89 +41,99 @@ export class TagRepository implements ITagRepository { return this.save(tag); } - async remove(tag: TagEntity): Promise { - await this.repository.remove(tag); + async delete(id: string): Promise { + await this.repository.delete(id); } - async getAssets(userId: string, tagId: string): Promise { - return this.assetRepository.find({ - where: { - tags: { - userId, - id: tagId, - }, - }, - relations: { - exifInfo: true, - tags: true, - faces: { - person: true, - }, - }, - order: { - createdAt: 'ASC', - }, - }); - } - - async addAssets(userId: string, id: string, assetIds: string[]): Promise { - for (const assetId of assetIds) { - const asset = await this.assetRepository.findOneOrFail({ - where: { - ownerId: userId, - id: assetId, - }, - relations: { - tags: true, - }, - }); - asset.tags.push({ id } as TagEntity); - await this.assetRepository.save(asset); + @GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] }) + @ChunkedSet({ paramIndex: 1 }) + async getAssetIds(tagId: string, assetIds: string[]): Promise> { + if (assetIds.length === 0) { + return new Set(); } + + const results = await this.dataSource + .createQueryBuilder() + .select('tag_asset.assetsId', 'assetId') + .from('tag_asset', 'tag_asset') + .where('"tag_asset"."tagsId" = :tagId', { tagId }) + .andWhere('"tag_asset"."assetsId" IN (:...assetIds)', { assetIds }) + .getRawMany<{ assetId: string }>(); + + return new Set(results.map(({ assetId }) => assetId)); } - async removeAssets(userId: string, id: string, assetIds: string[]): Promise { - for (const assetId of assetIds) { - const asset = await this.assetRepository.findOneOrFail({ - where: { - ownerId: userId, - id: assetId, - }, - relations: { - tags: true, - }, - }); - asset.tags = asset.tags.filter((tag) => tag.id !== id); - await this.assetRepository.save(asset); + @GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] }) + async addAssetIds(tagId: string, assetIds: string[]): Promise { + if (assetIds.length === 0) { + return; } + + await this.dataSource.manager + .createQueryBuilder() + .insert() + .into('tag_asset', ['tagsId', 'assetsId']) + .values(assetIds.map((assetId) => ({ tagsId: tagId, assetsId: assetId }))) + .execute(); } - hasAsset(userId: string, tagId: string, assetId: string): Promise { - return this.repository.exists({ - where: { - id: tagId, - userId, - assets: { - id: assetId, - }, - }, - relations: { - assets: true, - }, + @GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] }) + @Chunked({ paramIndex: 1 }) + async removeAssetIds(tagId: string, assetIds: string[]): Promise { + if (assetIds.length === 0) { + return; + } + + await this.dataSource + .createQueryBuilder() + .delete() + .from('tag_asset') + .where({ + tagsId: tagId, + assetsId: In(assetIds), + }) + .execute(); + } + + @GenerateSql({ params: [[{ assetId: DummyValue.UUID, tagId: DummyValue.UUID }]] }) + @Chunked() + async upsertAssetIds(items: AssetTagItem[]): Promise { + if (items.length === 0) { + return []; + } + + const { identifiers } = await this.dataSource + .createQueryBuilder() + .insert() + .into('tag_asset', ['assetsId', 'tagsId']) + .values(items.map(({ assetId, tagId }) => ({ assetsId: assetId, tagsId: tagId }))) + .execute(); + + return (identifiers as Array<{ assetsId: string; tagsId: string }>).map(({ assetsId, tagsId }) => ({ + assetId: assetsId, + tagId: tagsId, + })); + } + + async upsertAssetTags({ assetId, tagIds }: { assetId: string; tagIds: string[] }) { + await this.dataSource.transaction(async (manager) => { + await manager.createQueryBuilder().delete().from('tag_asset').where({ assetsId: assetId }).execute(); + + if (tagIds.length === 0) { + return; + } + + await manager + .createQueryBuilder() + .insert() + .into('tag_asset', ['tagsId', 'assetsId']) + .values(tagIds.map((tagId) => ({ tagsId: tagId, assetsId: assetId }))) + .execute(); }); } - hasName(userId: string, name: string): Promise { - return this.repository.exists({ - where: { - name, - userId, - }, - }); - } - - private async save(tag: Partial): Promise { - const { id } = await this.repository.save(tag); - return this.repository.findOneOrFail({ where: { id }, relations: { user: true } }); + private async save(partial: Partial): Promise { + const { id } = await this.repository.save(partial); + return this.repository.findOneOrFail({ where: { id } }); } } diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index 6585b8c2ee0cc..cb89de184a559 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -18,11 +18,13 @@ import { IMoveRepository } from 'src/interfaces/move.interface'; import { IPersonRepository } from 'src/interfaces/person.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; +import { ITagRepository } from 'src/interfaces/tag.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; import { MetadataService, Orientation } from 'src/services/metadata.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { fileStub } from 'test/fixtures/file.stub'; import { probeStub } from 'test/fixtures/media.stub'; +import { tagStub } from 'test/fixtures/tag.stub'; import { newAlbumRepositoryMock } from 'test/repositories/album.repository.mock'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; @@ -37,6 +39,7 @@ import { newMoveRepositoryMock } from 'test/repositories/move.repository.mock'; import { newPersonRepositoryMock } from 'test/repositories/person.repository.mock'; import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { newSystemMetadataRepositoryMock } from 'test/repositories/system-metadata.repository.mock'; +import { newTagRepositoryMock } from 'test/repositories/tag.repository.mock'; import { newUserRepositoryMock } from 'test/repositories/user.repository.mock'; import { Mocked } from 'vitest'; @@ -56,6 +59,7 @@ describe(MetadataService.name, () => { let databaseMock: Mocked; let userMock: Mocked; let loggerMock: Mocked; + let tagMock: Mocked; let sut: MetadataService; beforeEach(() => { @@ -74,6 +78,7 @@ describe(MetadataService.name, () => { databaseMock = newDatabaseRepositoryMock(); userMock = newUserRepositoryMock(); loggerMock = newLoggerRepositoryMock(); + tagMock = newTagRepositoryMock(); sut = new MetadataService( albumMock, @@ -89,6 +94,7 @@ describe(MetadataService.name, () => { personMock, storageMock, systemMock, + tagMock, userMock, loggerMock, ); @@ -356,6 +362,72 @@ describe(MetadataService.name, () => { expect(assetMock.upsertExif).toHaveBeenCalledWith(expect.objectContaining({ latitude: null, longitude: null })); }); + it('should extract tags from TagsList', async () => { + assetMock.getByIds.mockResolvedValue([assetStub.image]); + metadataMock.readTags.mockResolvedValue({ TagsList: ['Parent'] }); + tagMock.getByValue.mockResolvedValue(null); + tagMock.create.mockResolvedValue(tagStub.parent); + + await sut.handleMetadataExtraction({ id: assetStub.image.id }); + + expect(tagMock.create).toHaveBeenCalledWith({ userId: 'user-id', value: 'Parent', parent: undefined }); + }); + + it('should extract hierarchy from TagsList', async () => { + assetMock.getByIds.mockResolvedValue([assetStub.image]); + metadataMock.readTags.mockResolvedValue({ TagsList: ['Parent/Child'] }); + tagMock.getByValue.mockResolvedValue(null); + tagMock.create.mockResolvedValueOnce(tagStub.parent); + tagMock.create.mockResolvedValueOnce(tagStub.child); + + await sut.handleMetadataExtraction({ id: assetStub.image.id }); + + expect(tagMock.create).toHaveBeenNthCalledWith(1, { userId: 'user-id', value: 'Parent', parent: undefined }); + expect(tagMock.create).toHaveBeenNthCalledWith(2, { + userId: 'user-id', + value: 'Parent/Child', + parent: tagStub.parent, + }); + }); + + it('should extract tags from Keywords as a string', async () => { + assetMock.getByIds.mockResolvedValue([assetStub.image]); + metadataMock.readTags.mockResolvedValue({ Keywords: 'Parent' }); + tagMock.getByValue.mockResolvedValue(null); + tagMock.create.mockResolvedValue(tagStub.parent); + + await sut.handleMetadataExtraction({ id: assetStub.image.id }); + + expect(tagMock.create).toHaveBeenCalledWith({ userId: 'user-id', value: 'Parent', parent: undefined }); + }); + + it('should extract tags from Keywords as a list', async () => { + assetMock.getByIds.mockResolvedValue([assetStub.image]); + metadataMock.readTags.mockResolvedValue({ Keywords: ['Parent'] }); + tagMock.getByValue.mockResolvedValue(null); + tagMock.create.mockResolvedValue(tagStub.parent); + + await sut.handleMetadataExtraction({ id: assetStub.image.id }); + + expect(tagMock.create).toHaveBeenCalledWith({ userId: 'user-id', value: 'Parent', parent: undefined }); + }); + + it('should extract hierarchal tags from Keywords', async () => { + assetMock.getByIds.mockResolvedValue([assetStub.image]); + metadataMock.readTags.mockResolvedValue({ Keywords: 'Parent/Child' }); + tagMock.getByValue.mockResolvedValue(null); + tagMock.create.mockResolvedValue(tagStub.parent); + + await sut.handleMetadataExtraction({ id: assetStub.image.id }); + + expect(tagMock.create).toHaveBeenNthCalledWith(1, { userId: 'user-id', value: 'Parent', parent: undefined }); + expect(tagMock.create).toHaveBeenNthCalledWith(2, { + userId: 'user-id', + value: 'Parent/Child', + parent: tagStub.parent, + }); + }); + it('should not apply motion photos if asset is video', async () => { assetMock.getByIds.mockResolvedValue([{ ...assetStub.livePhotoMotionAsset, isVisible: true }]); mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer); diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index 3c938a4e59701..875414d84df7a 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -22,8 +22,8 @@ import { IEntityJob, IJobRepository, ISidecarWriteJob, - JOBS_ASSET_PAGINATION_SIZE, JobName, + JOBS_ASSET_PAGINATION_SIZE, JobStatus, QueueName, } from 'src/interfaces/job.interface'; @@ -35,8 +35,10 @@ import { IMoveRepository } from 'src/interfaces/move.interface'; import { IPersonRepository } from 'src/interfaces/person.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; +import { ITagRepository } from 'src/interfaces/tag.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; import { usePagination } from 'src/utils/pagination'; +import { upsertTags } from 'src/utils/tag'; /** look for a date from these tags (in order) */ const EXIF_DATE_TAGS: Array = [ @@ -105,6 +107,7 @@ export class MetadataService { @Inject(IPersonRepository) personRepository: IPersonRepository, @Inject(IStorageRepository) private storageRepository: IStorageRepository, @Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository, + @Inject(ITagRepository) private tagRepository: ITagRepository, @Inject(IUserRepository) private userRepository: IUserRepository, @Inject(ILoggerRepository) private logger: ILoggerRepository, ) { @@ -217,24 +220,27 @@ export class MetadataService { return JobStatus.FAILED; } - const { exifData, tags } = await this.exifData(asset); + const { exifData, exifTags } = await this.exifData(asset); if (asset.type === AssetType.VIDEO) { await this.applyVideoMetadata(asset, exifData); } - await this.applyMotionPhotos(asset, tags); + await this.applyMotionPhotos(asset, exifTags); await this.applyReverseGeocoding(asset, exifData); + await this.applyTagList(asset, exifTags); + await this.assetRepository.upsertExif(exifData); const dateTimeOriginal = exifData.dateTimeOriginal; let localDateTime = dateTimeOriginal ?? undefined; - const timeZoneOffset = tzOffset(firstDateTime(tags as Tags)) ?? 0; + const timeZoneOffset = tzOffset(firstDateTime(exifTags as Tags)) ?? 0; if (dateTimeOriginal && timeZoneOffset) { localDateTime = new Date(dateTimeOriginal.getTime() + timeZoneOffset * 60_000); } + await this.assetRepository.update({ id: asset.id, duration: asset.duration, @@ -278,22 +284,35 @@ export class MetadataService { return this.processSidecar(id, false); } + @OnEmit({ event: 'asset.tag' }) + async handleTagAsset({ assetId }: ArgOf<'asset.tag'>) { + await this.jobRepository.queue({ name: JobName.SIDECAR_WRITE, data: { id: assetId, tags: true } }); + } + + @OnEmit({ event: 'asset.untag' }) + async handleUntagAsset({ assetId }: ArgOf<'asset.untag'>) { + await this.jobRepository.queue({ name: JobName.SIDECAR_WRITE, data: { id: assetId, tags: true } }); + } + async handleSidecarWrite(job: ISidecarWriteJob): Promise { - const { id, description, dateTimeOriginal, latitude, longitude, rating } = job; - const [asset] = await this.assetRepository.getByIds([id]); + const { id, description, dateTimeOriginal, latitude, longitude, rating, tags } = job; + const [asset] = await this.assetRepository.getByIds([id], { tags: true }); if (!asset) { return JobStatus.FAILED; } + const tagsList = (asset.tags || []).map((tag) => tag.value); + const sidecarPath = asset.sidecarPath || `${asset.originalPath}.xmp`; - const exif = _.omitBy( - { + const exif = _.omitBy( + { Description: description, ImageDescription: description, DateTimeOriginal: dateTimeOriginal, GPSLatitude: latitude, GPSLongitude: longitude, Rating: rating, + TagsList: tags ? tagsList : undefined, }, _.isUndefined, ); @@ -332,6 +351,28 @@ export class MetadataService { } } + private async applyTagList(asset: AssetEntity, exifTags: ImmichTags) { + const tags: string[] = []; + + if (exifTags.TagsList) { + tags.push(...exifTags.TagsList); + } + + if (exifTags.Keywords) { + let keywords = exifTags.Keywords; + if (typeof keywords === 'string') { + keywords = [keywords]; + } + tags.push(...keywords); + } + + if (tags.length > 0) { + const results = await upsertTags(this.tagRepository, { userId: asset.ownerId, tags }); + const tagIds = results.map((tag) => tag.id); + await this.tagRepository.upsertAssetTags({ assetId: asset.id, tagIds }); + } + } + private async applyMotionPhotos(asset: AssetEntity, tags: ImmichTags) { if (asset.type !== AssetType.IMAGE) { return; @@ -466,7 +507,7 @@ export class MetadataService { private async exifData( asset: AssetEntity, - ): Promise<{ exifData: ExifEntityWithoutGeocodeAndTypeOrm; tags: ImmichTags }> { + ): Promise<{ exifData: ExifEntityWithoutGeocodeAndTypeOrm; exifTags: ImmichTags }> { const stats = await this.storageRepository.stat(asset.originalPath); const mediaTags = await this.repository.readTags(asset.originalPath); const sidecarTags = asset.sidecarPath ? await this.repository.readTags(asset.sidecarPath) : null; @@ -479,38 +520,38 @@ export class MetadataService { } } - const tags = { ...mediaTags, ...sidecarTags }; + const exifTags = { ...mediaTags, ...sidecarTags }; - this.logger.verbose('Exif Tags', tags); + this.logger.verbose('Exif Tags', exifTags); const exifData = { // altitude: tags.GPSAltitude ?? null, assetId: asset.id, - bitsPerSample: this.getBitsPerSample(tags), - colorspace: tags.ColorSpace ?? null, - dateTimeOriginal: this.getDateTimeOriginal(tags) ?? asset.fileCreatedAt, - description: String(tags.ImageDescription || tags.Description || '').trim(), - exifImageHeight: validate(tags.ImageHeight), - exifImageWidth: validate(tags.ImageWidth), - exposureTime: tags.ExposureTime ?? null, + bitsPerSample: this.getBitsPerSample(exifTags), + colorspace: exifTags.ColorSpace ?? null, + dateTimeOriginal: this.getDateTimeOriginal(exifTags) ?? asset.fileCreatedAt, + description: String(exifTags.ImageDescription || exifTags.Description || '').trim(), + exifImageHeight: validate(exifTags.ImageHeight), + exifImageWidth: validate(exifTags.ImageWidth), + exposureTime: exifTags.ExposureTime ?? null, fileSizeInByte: stats.size, - fNumber: validate(tags.FNumber), - focalLength: validate(tags.FocalLength), - fps: validate(Number.parseFloat(tags.VideoFrameRate!)), - iso: validate(tags.ISO), - latitude: validate(tags.GPSLatitude), - lensModel: tags.LensModel ?? null, - livePhotoCID: (tags.ContentIdentifier || tags.MediaGroupUUID) ?? null, - autoStackId: this.getAutoStackId(tags), - longitude: validate(tags.GPSLongitude), - make: tags.Make ?? null, - model: tags.Model ?? null, - modifyDate: exifDate(tags.ModifyDate) ?? asset.fileModifiedAt, - orientation: validate(tags.Orientation)?.toString() ?? null, - profileDescription: tags.ProfileDescription || null, - projectionType: tags.ProjectionType ? String(tags.ProjectionType).toUpperCase() : null, - timeZone: tags.tz ?? null, - rating: tags.Rating ?? null, + fNumber: validate(exifTags.FNumber), + focalLength: validate(exifTags.FocalLength), + fps: validate(Number.parseFloat(exifTags.VideoFrameRate!)), + iso: validate(exifTags.ISO), + latitude: validate(exifTags.GPSLatitude), + lensModel: exifTags.LensModel ?? null, + livePhotoCID: (exifTags.ContentIdentifier || exifTags.MediaGroupUUID) ?? null, + autoStackId: this.getAutoStackId(exifTags), + longitude: validate(exifTags.GPSLongitude), + make: exifTags.Make ?? null, + model: exifTags.Model ?? null, + modifyDate: exifDate(exifTags.ModifyDate) ?? asset.fileModifiedAt, + orientation: validate(exifTags.Orientation)?.toString() ?? null, + profileDescription: exifTags.ProfileDescription || null, + projectionType: exifTags.ProjectionType ? String(exifTags.ProjectionType).toUpperCase() : null, + timeZone: exifTags.tz ?? null, + rating: exifTags.Rating ?? null, }; if (exifData.latitude === 0 && exifData.longitude === 0) { @@ -519,7 +560,7 @@ export class MetadataService { exifData.longitude = null; } - return { exifData, tags }; + return { exifData, exifTags }; } private getAutoStackId(tags: ImmichTags | null): string | null { diff --git a/server/src/services/tag.service.spec.ts b/server/src/services/tag.service.spec.ts index 4323c061e1f1d..ffa7895cb4c8f 100644 --- a/server/src/services/tag.service.spec.ts +++ b/server/src/services/tag.service.spec.ts @@ -1,21 +1,28 @@ import { BadRequestException } from '@nestjs/common'; -import { AssetIdErrorReason } from 'src/dtos/asset-ids.response.dto'; -import { TagType } from 'src/entities/tag.entity'; +import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto'; +import { IEventRepository } from 'src/interfaces/event.interface'; import { ITagRepository } from 'src/interfaces/tag.interface'; import { TagService } from 'src/services/tag.service'; -import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { tagResponseStub, tagStub } from 'test/fixtures/tag.stub'; +import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock'; +import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; import { newTagRepositoryMock } from 'test/repositories/tag.repository.mock'; import { Mocked } from 'vitest'; describe(TagService.name, () => { let sut: TagService; + let accessMock: IAccessRepositoryMock; + let eventMock: Mocked; let tagMock: Mocked; beforeEach(() => { + accessMock = newAccessRepositoryMock(); + eventMock = newEventRepositoryMock(); tagMock = newTagRepositoryMock(); - sut = new TagService(tagMock); + sut = new TagService(accessMock, eventMock, tagMock); + + accessMock.tag.checkOwnerAccess.mockResolvedValue(new Set(['tag-1'])); }); it('should work', () => { @@ -30,148 +37,216 @@ describe(TagService.name, () => { }); }); - describe('getById', () => { + describe('get', () => { it('should throw an error for an invalid id', async () => { - tagMock.getById.mockResolvedValue(null); - await expect(sut.getById(authStub.admin, 'tag-1')).rejects.toBeInstanceOf(BadRequestException); - expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.user.id, 'tag-1'); + tagMock.get.mockResolvedValue(null); + await expect(sut.get(authStub.admin, 'tag-1')).rejects.toBeInstanceOf(BadRequestException); + expect(tagMock.get).toHaveBeenCalledWith('tag-1'); }); it('should return a tag for a user', async () => { - tagMock.getById.mockResolvedValue(tagStub.tag1); - await expect(sut.getById(authStub.admin, 'tag-1')).resolves.toEqual(tagResponseStub.tag1); - expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.user.id, 'tag-1'); + tagMock.get.mockResolvedValue(tagStub.tag1); + await expect(sut.get(authStub.admin, 'tag-1')).resolves.toEqual(tagResponseStub.tag1); + expect(tagMock.get).toHaveBeenCalledWith('tag-1'); + }); + }); + + describe('create', () => { + it('should throw an error for no parent tag access', async () => { + accessMock.tag.checkOwnerAccess.mockResolvedValue(new Set()); + await expect(sut.create(authStub.admin, { name: 'tag', parentId: 'tag-parent' })).rejects.toBeInstanceOf( + BadRequestException, + ); + expect(tagMock.create).not.toHaveBeenCalled(); + }); + + it('should create a tag with a parent', async () => { + accessMock.tag.checkOwnerAccess.mockResolvedValue(new Set(['tag-parent'])); + tagMock.create.mockResolvedValue(tagStub.tag1); + tagMock.get.mockResolvedValueOnce(tagStub.parent); + tagMock.get.mockResolvedValueOnce(tagStub.child); + await expect(sut.create(authStub.admin, { name: 'tagA', parentId: 'tag-parent' })).resolves.toBeDefined(); + expect(tagMock.create).toHaveBeenCalledWith(expect.objectContaining({ value: 'Parent/tagA' })); + }); + + it('should handle invalid parent ids', async () => { + accessMock.tag.checkOwnerAccess.mockResolvedValue(new Set(['tag-parent'])); + await expect(sut.create(authStub.admin, { name: 'tagA', parentId: 'tag-parent' })).rejects.toBeInstanceOf( + BadRequestException, + ); + expect(tagMock.create).not.toHaveBeenCalled(); }); }); describe('create', () => { it('should throw an error for a duplicate tag', async () => { - tagMock.hasName.mockResolvedValue(true); - await expect(sut.create(authStub.admin, { name: 'tag-1', type: TagType.CUSTOM })).rejects.toBeInstanceOf( - BadRequestException, - ); - expect(tagMock.hasName).toHaveBeenCalledWith(authStub.admin.user.id, 'tag-1'); + tagMock.getByValue.mockResolvedValue(tagStub.tag1); + await expect(sut.create(authStub.admin, { name: 'tag-1' })).rejects.toBeInstanceOf(BadRequestException); + expect(tagMock.getByValue).toHaveBeenCalledWith(authStub.admin.user.id, 'tag-1'); expect(tagMock.create).not.toHaveBeenCalled(); }); it('should create a new tag', async () => { tagMock.create.mockResolvedValue(tagStub.tag1); - await expect(sut.create(authStub.admin, { name: 'tag-1', type: TagType.CUSTOM })).resolves.toEqual( - tagResponseStub.tag1, - ); + await expect(sut.create(authStub.admin, { name: 'tag-1' })).resolves.toEqual(tagResponseStub.tag1); expect(tagMock.create).toHaveBeenCalledWith({ userId: authStub.admin.user.id, - name: 'tag-1', - type: TagType.CUSTOM, + value: 'tag-1', }); }); }); describe('update', () => { - it('should throw an error for an invalid id', async () => { - tagMock.getById.mockResolvedValue(null); - await expect(sut.update(authStub.admin, 'tag-1', { name: 'tag-2' })).rejects.toBeInstanceOf(BadRequestException); - expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.user.id, 'tag-1'); - expect(tagMock.remove).not.toHaveBeenCalled(); + it('should throw an error for no update permission', async () => { + accessMock.tag.checkOwnerAccess.mockResolvedValue(new Set()); + await expect(sut.update(authStub.admin, 'tag-1', { color: '#000000' })).rejects.toBeInstanceOf( + BadRequestException, + ); + expect(tagMock.update).not.toHaveBeenCalled(); }); it('should update a tag', async () => { - tagMock.getById.mockResolvedValue(tagStub.tag1); - tagMock.update.mockResolvedValue(tagStub.tag1); - await expect(sut.update(authStub.admin, 'tag-1', { name: 'tag-2' })).resolves.toEqual(tagResponseStub.tag1); - expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.user.id, 'tag-1'); - expect(tagMock.update).toHaveBeenCalledWith({ id: 'tag-1', name: 'tag-2' }); + accessMock.tag.checkOwnerAccess.mockResolvedValue(new Set(['tag-1'])); + tagMock.update.mockResolvedValue(tagStub.color1); + await expect(sut.update(authStub.admin, 'tag-1', { color: '#000000' })).resolves.toEqual(tagResponseStub.color1); + expect(tagMock.update).toHaveBeenCalledWith({ id: 'tag-1', color: '#000000' }); + }); + }); + + describe('upsert', () => { + it('should upsert a new tag', async () => { + tagMock.create.mockResolvedValue(tagStub.parent); + await expect(sut.upsert(authStub.admin, { tags: ['Parent'] })).resolves.toBeDefined(); + expect(tagMock.create).toHaveBeenCalledWith({ + value: 'Parent', + userId: 'admin_id', + parentId: undefined, + }); + }); + + it('should upsert a nested tag', async () => { + tagMock.getByValue.mockResolvedValueOnce(null); + tagMock.create.mockResolvedValueOnce(tagStub.parent); + tagMock.create.mockResolvedValueOnce(tagStub.child); + await expect(sut.upsert(authStub.admin, { tags: ['Parent/Child'] })).resolves.toBeDefined(); + expect(tagMock.create).toHaveBeenNthCalledWith(1, { + value: 'Parent', + userId: 'admin_id', + parentId: undefined, + }); + expect(tagMock.create).toHaveBeenNthCalledWith(2, { + value: 'Parent/Child', + userId: 'admin_id', + parent: expect.objectContaining({ id: 'tag-parent' }), + }); }); }); describe('remove', () => { it('should throw an error for an invalid id', async () => { - tagMock.getById.mockResolvedValue(null); + accessMock.tag.checkOwnerAccess.mockResolvedValue(new Set()); await expect(sut.remove(authStub.admin, 'tag-1')).rejects.toBeInstanceOf(BadRequestException); - expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.user.id, 'tag-1'); - expect(tagMock.remove).not.toHaveBeenCalled(); + expect(tagMock.delete).not.toHaveBeenCalled(); }); it('should remove a tag', async () => { - tagMock.getById.mockResolvedValue(tagStub.tag1); + tagMock.get.mockResolvedValue(tagStub.tag1); await sut.remove(authStub.admin, 'tag-1'); - expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.user.id, 'tag-1'); - expect(tagMock.remove).toHaveBeenCalledWith(tagStub.tag1); + expect(tagMock.delete).toHaveBeenCalledWith('tag-1'); }); }); - describe('getAssets', () => { - it('should throw an error for an invalid id', async () => { - tagMock.getById.mockResolvedValue(null); - await expect(sut.remove(authStub.admin, 'tag-1')).rejects.toBeInstanceOf(BadRequestException); - expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.user.id, 'tag-1'); - expect(tagMock.remove).not.toHaveBeenCalled(); + describe('bulkTagAssets', () => { + it('should handle invalid requests', async () => { + accessMock.tag.checkOwnerAccess.mockResolvedValue(new Set()); + tagMock.upsertAssetIds.mockResolvedValue([]); + await expect(sut.bulkTagAssets(authStub.admin, { tagIds: ['tag-1'], assetIds: ['asset-1'] })).resolves.toEqual({ + count: 0, + }); + expect(tagMock.upsertAssetIds).toHaveBeenCalledWith([]); }); - it('should get the assets for a tag', async () => { - tagMock.getById.mockResolvedValue(tagStub.tag1); - tagMock.getAssets.mockResolvedValue([assetStub.image]); - await sut.getAssets(authStub.admin, 'tag-1'); - expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.user.id, 'tag-1'); - expect(tagMock.getAssets).toHaveBeenCalledWith(authStub.admin.user.id, 'tag-1'); + it('should upsert records', async () => { + accessMock.tag.checkOwnerAccess.mockResolvedValue(new Set(['tag-1', 'tag-2'])); + accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2', 'asset-3'])); + tagMock.upsertAssetIds.mockResolvedValue([ + { tagId: 'tag-1', assetId: 'asset-1' }, + { tagId: 'tag-1', assetId: 'asset-2' }, + { tagId: 'tag-1', assetId: 'asset-3' }, + { tagId: 'tag-2', assetId: 'asset-1' }, + { tagId: 'tag-2', assetId: 'asset-2' }, + { tagId: 'tag-2', assetId: 'asset-3' }, + ]); + await expect( + sut.bulkTagAssets(authStub.admin, { tagIds: ['tag-1', 'tag-2'], assetIds: ['asset-1', 'asset-2', 'asset-3'] }), + ).resolves.toEqual({ + count: 6, + }); + expect(tagMock.upsertAssetIds).toHaveBeenCalledWith([ + { tagId: 'tag-1', assetId: 'asset-1' }, + { tagId: 'tag-1', assetId: 'asset-2' }, + { tagId: 'tag-1', assetId: 'asset-3' }, + { tagId: 'tag-2', assetId: 'asset-1' }, + { tagId: 'tag-2', assetId: 'asset-2' }, + { tagId: 'tag-2', assetId: 'asset-3' }, + ]); }); }); describe('addAssets', () => { - it('should throw an error for an invalid id', async () => { - tagMock.getById.mockResolvedValue(null); - await expect(sut.addAssets(authStub.admin, 'tag-1', { assetIds: ['asset-1'] })).rejects.toBeInstanceOf( - BadRequestException, - ); - expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.user.id, 'tag-1'); - expect(tagMock.addAssets).not.toHaveBeenCalled(); + it('should handle invalid ids', async () => { + tagMock.get.mockResolvedValue(null); + tagMock.getAssetIds.mockResolvedValue(new Set([])); + await expect(sut.addAssets(authStub.admin, 'tag-1', { ids: ['asset-1'] })).resolves.toEqual([ + { id: 'asset-1', success: false, error: 'no_permission' }, + ]); + expect(tagMock.getAssetIds).toHaveBeenCalledWith('tag-1', ['asset-1']); + expect(tagMock.addAssetIds).not.toHaveBeenCalled(); }); - it('should reject duplicate asset ids and accept new ones', async () => { - tagMock.getById.mockResolvedValue(tagStub.tag1); - tagMock.hasAsset.mockImplementation((userId, tagId, assetId) => Promise.resolve(assetId === 'asset-1')); + it('should accept accept ids that are new and reject the rest', async () => { + tagMock.get.mockResolvedValue(tagStub.tag1); + tagMock.getAssetIds.mockResolvedValue(new Set(['asset-1'])); + accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-2'])); await expect( sut.addAssets(authStub.admin, 'tag-1', { - assetIds: ['asset-1', 'asset-2'], + ids: ['asset-1', 'asset-2'], }), ).resolves.toEqual([ - { assetId: 'asset-1', success: false, error: AssetIdErrorReason.DUPLICATE }, - { assetId: 'asset-2', success: true }, + { id: 'asset-1', success: false, error: BulkIdErrorReason.DUPLICATE }, + { id: 'asset-2', success: true }, ]); - expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.user.id, 'tag-1'); - expect(tagMock.hasAsset).toHaveBeenCalledTimes(2); - expect(tagMock.addAssets).toHaveBeenCalledWith(authStub.admin.user.id, 'tag-1', ['asset-2']); + expect(tagMock.getAssetIds).toHaveBeenCalledWith('tag-1', ['asset-1', 'asset-2']); + expect(tagMock.addAssetIds).toHaveBeenCalledWith('tag-1', ['asset-2']); }); }); describe('removeAssets', () => { it('should throw an error for an invalid id', async () => { - tagMock.getById.mockResolvedValue(null); - await expect(sut.removeAssets(authStub.admin, 'tag-1', { assetIds: ['asset-1'] })).rejects.toBeInstanceOf( - BadRequestException, - ); - expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.user.id, 'tag-1'); - expect(tagMock.removeAssets).not.toHaveBeenCalled(); + tagMock.get.mockResolvedValue(null); + tagMock.getAssetIds.mockResolvedValue(new Set()); + await expect(sut.removeAssets(authStub.admin, 'tag-1', { ids: ['asset-1'] })).resolves.toEqual([ + { id: 'asset-1', success: false, error: 'not_found' }, + ]); }); it('should accept accept ids that are tagged and reject the rest', async () => { - tagMock.getById.mockResolvedValue(tagStub.tag1); - tagMock.hasAsset.mockImplementation((userId, tagId, assetId) => Promise.resolve(assetId === 'asset-1')); + tagMock.get.mockResolvedValue(tagStub.tag1); + tagMock.getAssetIds.mockResolvedValue(new Set(['asset-1'])); await expect( sut.removeAssets(authStub.admin, 'tag-1', { - assetIds: ['asset-1', 'asset-2'], + ids: ['asset-1', 'asset-2'], }), ).resolves.toEqual([ - { assetId: 'asset-1', success: true }, - { assetId: 'asset-2', success: false, error: AssetIdErrorReason.NOT_FOUND }, + { id: 'asset-1', success: true }, + { id: 'asset-2', success: false, error: BulkIdErrorReason.NOT_FOUND }, ]); - expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.user.id, 'tag-1'); - expect(tagMock.hasAsset).toHaveBeenCalledTimes(2); - expect(tagMock.removeAssets).toHaveBeenCalledWith(authStub.admin.user.id, 'tag-1', ['asset-1']); + expect(tagMock.getAssetIds).toHaveBeenCalledWith('tag-1', ['asset-1', 'asset-2']); + expect(tagMock.removeAssetIds).toHaveBeenCalledWith('tag-1', ['asset-1']); }); }); }); diff --git a/server/src/services/tag.service.ts b/server/src/services/tag.service.ts index c04f9b14c4b33..97b0ef1be6843 100644 --- a/server/src/services/tag.service.ts +++ b/server/src/services/tag.service.ts @@ -1,102 +1,145 @@ import { BadRequestException, Inject, Injectable } from '@nestjs/common'; -import { AssetIdErrorReason, AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto'; -import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; -import { AssetIdsDto } from 'src/dtos/asset.dto'; +import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; -import { CreateTagDto, TagResponseDto, UpdateTagDto, mapTag } from 'src/dtos/tag.dto'; -import { ITagRepository } from 'src/interfaces/tag.interface'; +import { + TagBulkAssetsDto, + TagBulkAssetsResponseDto, + TagCreateDto, + TagResponseDto, + TagUpdateDto, + TagUpsertDto, + mapTag, +} from 'src/dtos/tag.dto'; +import { TagEntity } from 'src/entities/tag.entity'; +import { Permission } from 'src/enum'; +import { IAccessRepository } from 'src/interfaces/access.interface'; +import { IEventRepository } from 'src/interfaces/event.interface'; +import { AssetTagItem, ITagRepository } from 'src/interfaces/tag.interface'; +import { checkAccess, requireAccess } from 'src/utils/access'; +import { addAssets, removeAssets } from 'src/utils/asset.util'; +import { upsertTags } from 'src/utils/tag'; @Injectable() export class TagService { - constructor(@Inject(ITagRepository) private repository: ITagRepository) {} + constructor( + @Inject(IAccessRepository) private access: IAccessRepository, + @Inject(IEventRepository) private eventRepository: IEventRepository, + @Inject(ITagRepository) private repository: ITagRepository, + ) {} - getAll(auth: AuthDto) { - return this.repository.getAll(auth.user.id).then((tags) => tags.map((tag) => mapTag(tag))); + async getAll(auth: AuthDto) { + const tags = await this.repository.getAll(auth.user.id); + return tags.map((tag) => mapTag(tag)); } - async getById(auth: AuthDto, id: string): Promise { - const tag = await this.findOrFail(auth, id); + async get(auth: AuthDto, id: string): Promise { + await requireAccess(this.access, { auth, permission: Permission.TAG_READ, ids: [id] }); + const tag = await this.findOrFail(id); return mapTag(tag); } - async create(auth: AuthDto, dto: CreateTagDto) { - const duplicate = await this.repository.hasName(auth.user.id, dto.name); + async create(auth: AuthDto, dto: TagCreateDto) { + let parent: TagEntity | undefined; + if (dto.parentId) { + await requireAccess(this.access, { auth, permission: Permission.TAG_READ, ids: [dto.parentId] }); + parent = (await this.repository.get(dto.parentId)) || undefined; + if (!parent) { + throw new BadRequestException('Tag not found'); + } + } + + const userId = auth.user.id; + const value = parent ? `${parent.value}/${dto.name}` : dto.name; + const duplicate = await this.repository.getByValue(userId, value); if (duplicate) { throw new BadRequestException(`A tag with that name already exists`); } - const tag = await this.repository.create({ - userId: auth.user.id, - name: dto.name, - type: dto.type, - }); + const tag = await this.repository.create({ userId, value, parent }); return mapTag(tag); } - async update(auth: AuthDto, id: string, dto: UpdateTagDto): Promise { - await this.findOrFail(auth, id); - const tag = await this.repository.update({ id, name: dto.name }); + async update(auth: AuthDto, id: string, dto: TagUpdateDto): Promise { + await requireAccess(this.access, { auth, permission: Permission.TAG_UPDATE, ids: [id] }); + + const { color } = dto; + const tag = await this.repository.update({ id, color }); return mapTag(tag); } + async upsert(auth: AuthDto, dto: TagUpsertDto) { + const tags = await upsertTags(this.repository, { userId: auth.user.id, tags: dto.tags }); + return tags.map((tag) => mapTag(tag)); + } + async remove(auth: AuthDto, id: string): Promise { - const tag = await this.findOrFail(auth, id); - await this.repository.remove(tag); + await requireAccess(this.access, { auth, permission: Permission.TAG_DELETE, ids: [id] }); + + // TODO sync tag changes for affected assets + + await this.repository.delete(id); } - async getAssets(auth: AuthDto, id: string): Promise { - await this.findOrFail(auth, id); - const assets = await this.repository.getAssets(auth.user.id, id); - return assets.map((asset) => mapAsset(asset)); - } + async bulkTagAssets(auth: AuthDto, dto: TagBulkAssetsDto): Promise { + const [tagIds, assetIds] = await Promise.all([ + checkAccess(this.access, { auth, permission: Permission.TAG_ASSET, ids: dto.tagIds }), + checkAccess(this.access, { auth, permission: Permission.ASSET_UPDATE, ids: dto.assetIds }), + ]); - async addAssets(auth: AuthDto, id: string, dto: AssetIdsDto): Promise { - await this.findOrFail(auth, id); - - const results: AssetIdsResponseDto[] = []; - for (const assetId of dto.assetIds) { - const hasAsset = await this.repository.hasAsset(auth.user.id, id, assetId); - if (hasAsset) { - results.push({ assetId, success: false, error: AssetIdErrorReason.DUPLICATE }); - } else { - results.push({ assetId, success: true }); + const items: AssetTagItem[] = []; + for (const tagId of tagIds) { + for (const assetId of assetIds) { + items.push({ tagId, assetId }); } } - await this.repository.addAssets( - auth.user.id, - id, - results.filter((result) => result.success).map((result) => result.assetId), + const results = await this.repository.upsertAssetIds(items); + for (const assetId of new Set(results.map((item) => item.assetId))) { + await this.eventRepository.emit('asset.tag', { assetId }); + } + + return { count: results.length }; + } + + async addAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise { + await requireAccess(this.access, { auth, permission: Permission.TAG_ASSET, ids: [id] }); + + const results = await addAssets( + auth, + { access: this.access, bulk: this.repository }, + { parentId: id, assetIds: dto.ids }, ); + for (const { id: assetId, success } of results) { + if (success) { + await this.eventRepository.emit('asset.tag', { assetId }); + } + } + return results; } - async removeAssets(auth: AuthDto, id: string, dto: AssetIdsDto): Promise { - await this.findOrFail(auth, id); + async removeAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise { + await requireAccess(this.access, { auth, permission: Permission.TAG_ASSET, ids: [id] }); - const results: AssetIdsResponseDto[] = []; - for (const assetId of dto.assetIds) { - const hasAsset = await this.repository.hasAsset(auth.user.id, id, assetId); - if (hasAsset) { - results.push({ assetId, success: true }); - } else { - results.push({ assetId, success: false, error: AssetIdErrorReason.NOT_FOUND }); + const results = await removeAssets( + auth, + { access: this.access, bulk: this.repository }, + { parentId: id, assetIds: dto.ids, canAlwaysRemove: Permission.TAG_DELETE }, + ); + + for (const { id: assetId, success } of results) { + if (success) { + await this.eventRepository.emit('asset.untag', { assetId }); } } - await this.repository.removeAssets( - auth.user.id, - id, - results.filter((result) => result.success).map((result) => result.assetId), - ); - return results; } - private async findOrFail(auth: AuthDto, id: string) { - const tag = await this.repository.getById(auth.user.id, id); + private async findOrFail(id: string) { + const tag = await this.repository.get(id); if (!tag) { throw new BadRequestException('Tag not found'); } diff --git a/server/src/services/timeline.service.ts b/server/src/services/timeline.service.ts index 052565fca99f5..bc08505b944eb 100644 --- a/server/src/services/timeline.service.ts +++ b/server/src/services/timeline.service.ts @@ -68,6 +68,10 @@ export class TimelineService { } } + if (dto.tagId) { + await requireAccess(this.access, { auth, permission: Permission.TAG_READ, ids: [dto.tagId] }); + } + if (dto.withPartners) { const requestedArchived = dto.isArchived === true || dto.isArchived === undefined; const requestedFavorite = dto.isFavorite === true || dto.isFavorite === false; diff --git a/server/src/utils/access.ts b/server/src/utils/access.ts index 45badeec73e8a..d3219a1a6c4b6 100644 --- a/server/src/utils/access.ts +++ b/server/src/utils/access.ts @@ -41,7 +41,10 @@ export const requireAccess = async (access: IAccessRepository, request: AccessRe } }; -export const checkAccess = async (access: IAccessRepository, { ids, auth, permission }: AccessRequest) => { +export const checkAccess = async ( + access: IAccessRepository, + { ids, auth, permission }: AccessRequest, +): Promise> => { const idSet = Array.isArray(ids) ? new Set(ids) : ids; if (idSet.size === 0) { return new Set(); @@ -52,7 +55,10 @@ export const checkAccess = async (access: IAccessRepository, { ids, auth, permis : checkOtherAccess(access, { auth, permission, ids: idSet }); }; -const checkSharedLinkAccess = async (access: IAccessRepository, request: SharedLinkAccessRequest) => { +const checkSharedLinkAccess = async ( + access: IAccessRepository, + request: SharedLinkAccessRequest, +): Promise> => { const { sharedLink, permission, ids } = request; const sharedLinkId = sharedLink.id; @@ -96,7 +102,7 @@ const checkSharedLinkAccess = async (access: IAccessRepository, request: SharedL } }; -const checkOtherAccess = async (access: IAccessRepository, request: OtherAccessRequest) => { +const checkOtherAccess = async (access: IAccessRepository, request: OtherAccessRequest): Promise> => { const { auth, permission, ids } = request; switch (permission) { @@ -211,6 +217,13 @@ const checkOtherAccess = async (access: IAccessRepository, request: OtherAccessR return await access.authDevice.checkOwnerAccess(auth.user.id, ids); } + case Permission.TAG_ASSET: + case Permission.TAG_READ: + case Permission.TAG_UPDATE: + case Permission.TAG_DELETE: { + return await access.tag.checkOwnerAccess(auth.user.id, ids); + } + case Permission.TIMELINE_READ: { const isOwner = ids.has(auth.user.id) ? new Set([auth.user.id]) : new Set(); const isPartner = await access.timeline.checkPartnerAccess(auth.user.id, setDifference(ids, isOwner)); diff --git a/server/src/utils/request.ts b/server/src/utils/request.ts index f6edb2f8b34a6..19d3cac661248 100644 --- a/server/src/utils/request.ts +++ b/server/src/utils/request.ts @@ -2,4 +2,4 @@ export const fromChecksum = (checksum: string): Buffer => { return Buffer.from(checksum, checksum.length === 28 ? 'base64' : 'hex'); }; -export const fromMaybeArray = (param: string | string[] | undefined) => (Array.isArray(param) ? param[0] : param); +export const fromMaybeArray = (param: T | T[]) => (Array.isArray(param) ? param[0] : param); diff --git a/server/src/utils/tag.ts b/server/src/utils/tag.ts new file mode 100644 index 0000000000000..12c46d24400d5 --- /dev/null +++ b/server/src/utils/tag.ts @@ -0,0 +1,30 @@ +import { TagEntity } from 'src/entities/tag.entity'; +import { ITagRepository } from 'src/interfaces/tag.interface'; + +type UpsertRequest = { userId: string; tags: string[] }; +export const upsertTags = async (repository: ITagRepository, { userId, tags }: UpsertRequest) => { + tags = [...new Set(tags)]; + + const results: TagEntity[] = []; + + for (const tag of tags) { + const parts = tag.split('/'); + let parent: TagEntity | undefined; + + for (const part of parts) { + const value = parent ? `${parent.value}/${part}` : part; + let tag = await repository.getByValue(userId, value); + if (!tag) { + tag = await repository.create({ userId, value, parent }); + } + + parent = tag; + } + + if (parent) { + results.push(parent); + } + } + + return results; +}; diff --git a/server/test/fixtures/tag.stub.ts b/server/test/fixtures/tag.stub.ts index 537c65db47e96..b245bfe9e5641 100644 --- a/server/test/fixtures/tag.stub.ts +++ b/server/test/fixtures/tag.stub.ts @@ -1,24 +1,65 @@ import { TagResponseDto } from 'src/dtos/tag.dto'; -import { TagEntity, TagType } from 'src/entities/tag.entity'; +import { TagEntity } from 'src/entities/tag.entity'; import { userStub } from 'test/fixtures/user.stub'; +const parent = Object.freeze({ + id: 'tag-parent', + createdAt: new Date('2021-01-01T00:00:00Z'), + updatedAt: new Date('2021-01-01T00:00:00Z'), + value: 'Parent', + color: null, + userId: userStub.admin.id, + user: userStub.admin, +}); + +const child = Object.freeze({ + id: 'tag-child', + createdAt: new Date('2021-01-01T00:00:00Z'), + updatedAt: new Date('2021-01-01T00:00:00Z'), + value: 'Parent/Child', + color: null, + parent, + userId: userStub.admin.id, + user: userStub.admin, +}); + export const tagStub = { tag1: Object.freeze({ id: 'tag-1', - name: 'Tag1', - type: TagType.CUSTOM, + createdAt: new Date('2021-01-01T00:00:00Z'), + updatedAt: new Date('2021-01-01T00:00:00Z'), + value: 'Tag1', + color: null, + userId: userStub.admin.id, + user: userStub.admin, + }), + parent, + child, + color1: Object.freeze({ + id: 'tag-1', + createdAt: new Date('2021-01-01T00:00:00Z'), + updatedAt: new Date('2021-01-01T00:00:00Z'), + value: 'Tag1', + color: '#000000', userId: userStub.admin.id, user: userStub.admin, - renameTagId: null, - assets: [], }), }; export const tagResponseStub = { tag1: Object.freeze({ id: 'tag-1', + createdAt: new Date('2021-01-01T00:00:00Z'), + updatedAt: new Date('2021-01-01T00:00:00Z'), name: 'Tag1', - type: 'CUSTOM', - userId: 'admin_id', + value: 'Tag1', + }), + color1: Object.freeze({ + id: 'tag-1', + createdAt: new Date('2021-01-01T00:00:00Z'), + updatedAt: new Date('2021-01-01T00:00:00Z'), + color: '#000000', + name: 'Tag1', + value: 'Tag1', }), }; diff --git a/server/test/repositories/access.repository.mock.ts b/server/test/repositories/access.repository.mock.ts index c9db8cd76a7b6..9e9bf5406bd6d 100644 --- a/server/test/repositories/access.repository.mock.ts +++ b/server/test/repositories/access.repository.mock.ts @@ -11,6 +11,7 @@ export interface IAccessRepositoryMock { partner: Mocked; stack: Mocked; timeline: Mocked; + tag: Mocked; } export const newAccessRepositoryMock = (): IAccessRepositoryMock => { @@ -58,5 +59,9 @@ export const newAccessRepositoryMock = (): IAccessRepositoryMock => { timeline: { checkPartnerAccess: vitest.fn().mockResolvedValue(new Set()), }, + + tag: { + checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()), + }, }; }; diff --git a/server/test/repositories/tag.repository.mock.ts b/server/test/repositories/tag.repository.mock.ts index a5123e0f36eaf..35b3de1576084 100644 --- a/server/test/repositories/tag.repository.mock.ts +++ b/server/test/repositories/tag.repository.mock.ts @@ -4,14 +4,17 @@ import { Mocked, vitest } from 'vitest'; export const newTagRepositoryMock = (): Mocked => { return { getAll: vitest.fn(), - getById: vitest.fn(), + getByValue: vitest.fn(), + upsertAssetTags: vitest.fn(), + + get: vitest.fn(), create: vitest.fn(), update: vitest.fn(), - remove: vitest.fn(), - hasAsset: vitest.fn(), - hasName: vitest.fn(), - getAssets: vitest.fn(), - addAssets: vitest.fn(), - removeAssets: vitest.fn(), + delete: vitest.fn(), + + getAssetIds: vitest.fn(), + addAssetIds: vitest.fn(), + removeAssetIds: vitest.fn(), + upsertAssetIds: vitest.fn(), }; }; diff --git a/web/src/lib/components/asset-viewer/detail-panel-tags.svelte b/web/src/lib/components/asset-viewer/detail-panel-tags.svelte new file mode 100644 index 0000000000000..434682f73ec27 --- /dev/null +++ b/web/src/lib/components/asset-viewer/detail-panel-tags.svelte @@ -0,0 +1,80 @@ + + +{#if isOwner && !isSharedLink()} +

+
+

{$t('tags').toUpperCase()}

+
+
+ {#each tags as tag (tag.id)} +
+ +

+ {tag.value} +

+
+ + +
+ {/each} + +
+
+{/if} + +{#if isOpen} + handleTag(tagsIds)} onCancel={handleCancel} /> +{/if} diff --git a/web/src/lib/components/asset-viewer/detail-panel.svelte b/web/src/lib/components/asset-viewer/detail-panel.svelte index 88417f248f1f8..0a105430cc879 100644 --- a/web/src/lib/components/asset-viewer/detail-panel.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel.svelte @@ -43,6 +43,7 @@ import DetailPanelRating from '$lib/components/asset-viewer/detail-panel-star-rating.svelte'; import { t } from 'svelte-i18n'; import { goto } from '$app/navigation'; + import DetailPanelTags from '$lib/components/asset-viewer/detail-panel-tags.svelte'; export let asset: AssetResponseDto; export let albums: AlbumResponseDto[] = []; @@ -157,7 +158,7 @@ {#if (!isSharedLink() && unassignedFaces.length > 0) || people.length > 0} -
+

{$t('people').toUpperCase()}

@@ -472,11 +473,11 @@ {/if} {#if albums.length > 0} -
+

{$t('appears_in').toUpperCase()}

{#each albums as album} -
+
{album.albumName} {/if} +
+ +
+ {#if showEditFaces} (intersecting = false)); + assetStore.taskManager.separatedThumbnail(componentId, dateGroup, asset, () => (intersecting = false)); } else { intersecting = false; } diff --git a/web/src/lib/components/forms/tag-asset-form.svelte b/web/src/lib/components/forms/tag-asset-form.svelte new file mode 100644 index 0000000000000..306d25d3f1a2b --- /dev/null +++ b/web/src/lib/components/forms/tag-asset-form.svelte @@ -0,0 +1,82 @@ + + + + +
+ handleSelect(option)} + label={$t('tag')} + options={allTags.map((tag) => ({ id: tag.id, label: tag.value, value: tag.id }))} + placeholder={$t('search_tags')} + /> +
+ + +
+ {#each selectedIds as tagId (tagId)} + {@const tag = tagMap[tagId]} + {#if tag} +
+ +

+ {tag.value} +

+
+ + +
+ {/if} + {/each} +
+ + + + + +
diff --git a/web/src/lib/components/layouts/user-page-layout.svelte b/web/src/lib/components/layouts/user-page-layout.svelte index 8222007d57a4b..6511a9debaa52 100644 --- a/web/src/lib/components/layouts/user-page-layout.svelte +++ b/web/src/lib/components/layouts/user-page-layout.svelte @@ -35,12 +35,16 @@
- {#if title} + {#if title || $$slots.title || $$slots.buttons}
-
{title}
+ + {#if title} +
{title}
+ {/if} +
{#if description}

{description}

{/if} diff --git a/web/src/lib/components/photos-page/actions/tag-action.svelte b/web/src/lib/components/photos-page/actions/tag-action.svelte new file mode 100644 index 0000000000000..77e91d7235aa4 --- /dev/null +++ b/web/src/lib/components/photos-page/actions/tag-action.svelte @@ -0,0 +1,47 @@ + + +{#if menuItem} + +{/if} + +{#if !menuItem} + {#if loading} + + {:else} + + {/if} +{/if} + +{#if isOpen} + handleTag(tagIds)} onCancel={handleCancel} /> +{/if} diff --git a/web/src/lib/components/photos-page/asset-date-group.svelte b/web/src/lib/components/photos-page/asset-date-group.svelte index 5ca29967fe065..5cbc2e7dcaaca 100644 --- a/web/src/lib/components/photos-page/asset-date-group.svelte +++ b/web/src/lib/components/photos-page/asset-date-group.svelte @@ -109,7 +109,7 @@ ); }, onSeparate: () => { - $assetStore.taskManager.seperatedDateGroup(componentId, dateGroup, () => + $assetStore.taskManager.separatedDateGroup(componentId, dateGroup, () => assetStore.updateBucketDateGroup(bucket, dateGroup, { intersecting: false }), ); }, @@ -186,9 +186,9 @@
onAssetInGrid?.(asset), - top: `-${TITLE_HEIGHT}px`, - bottom: `-${viewport.height - TITLE_HEIGHT - 1}px`, - right: `-${viewport.width - 1}px`, + top: `${-TITLE_HEIGHT}px`, + bottom: `${-(viewport.height - TITLE_HEIGHT - 1)}px`, + right: `${-(viewport.width - 1)}px`, root: assetGridElement, }} data-asset-id={asset.id} diff --git a/web/src/lib/components/photos-page/asset-grid.svelte b/web/src/lib/components/photos-page/asset-grid.svelte index db030ed14caf6..40dc79c4f25a8 100644 --- a/web/src/lib/components/photos-page/asset-grid.svelte +++ b/web/src/lib/components/photos-page/asset-grid.svelte @@ -498,21 +498,21 @@ } }; - function intersectedHandler(bucket: AssetBucket) { + function handleIntersect(bucket: AssetBucket) { updateLastIntersectedBucketDate(); - const intersectedTask = () => { + const task = () => { $assetStore.updateBucket(bucket.bucketDate, { intersecting: true }); void $assetStore.loadBucket(bucket.bucketDate); }; - $assetStore.taskManager.intersectedBucket(componentId, bucket, intersectedTask); + $assetStore.taskManager.intersectedBucket(componentId, bucket, task); } - function seperatedHandler(bucket: AssetBucket) { - const seperatedTask = () => { + function handleSeparate(bucket: AssetBucket) { + const task = () => { $assetStore.updateBucket(bucket.bucketDate, { intersecting: false }); bucket.cancel(); }; - $assetStore.taskManager.seperatedBucket(componentId, bucket, seperatedTask); + $assetStore.taskManager.separatedBucket(componentId, bucket, task); } const handlePrevious = async () => { @@ -809,8 +809,8 @@
intersectedHandler(bucket), - onSeparate: () => seperatedHandler(bucket), + onIntersect: () => handleIntersect(bucket), + onSeparate: () => handleSeparate(bucket), top: BUCKET_INTERSECTION_ROOT_TOP, bottom: BUCKET_INTERSECTION_ROOT_BOTTOM, root: element, diff --git a/web/src/lib/components/shared-components/combobox.svelte b/web/src/lib/components/shared-components/combobox.svelte index 64ec16fda6541..d3e022a75933c 100644 --- a/web/src/lib/components/shared-components/combobox.svelte +++ b/web/src/lib/components/shared-components/combobox.svelte @@ -1,5 +1,6 @@ @@ -13,6 +14,7 @@ import { fly } from 'svelte/transition'; import PasswordField from '../password-field.svelte'; import { t } from 'svelte-i18n'; + import { onMount, tick } from 'svelte'; export let inputType: SettingInputFieldType; export let value: string | number; @@ -25,8 +27,11 @@ export let required = false; export let disabled = false; export let isEdited = false; + export let autofocus = false; export let passwordAutocomplete: string = 'current-password'; + let input: HTMLInputElement; + const handleChange: FormEventHandler = (e) => { value = e.currentTarget.value; @@ -41,6 +46,14 @@ value = newValue; } }; + + onMount(() => { + if (autofocus) { + tick() + .then(() => input?.focus()) + .catch((_) => {}); + } + });
@@ -69,22 +82,46 @@ {/if} {#if inputType !== SettingInputFieldType.PASSWORD} - +
+ {#if inputType === SettingInputFieldType.COLOR} + + {/if} + + +
{:else} {/if}
+ + diff --git a/web/src/lib/components/shared-components/side-bar/side-bar.svelte b/web/src/lib/components/shared-components/side-bar/side-bar.svelte index 1985160b27ae2..dd777d12596a5 100644 --- a/web/src/lib/components/shared-components/side-bar/side-bar.svelte +++ b/web/src/lib/components/shared-components/side-bar/side-bar.svelte @@ -21,6 +21,7 @@ mdiToolbox, mdiToolboxOutline, mdiFolderOutline, + mdiTagMultipleOutline, } from '@mdi/js'; import SideBarSection from './side-bar-section.svelte'; import SideBarLink from './side-bar-link.svelte'; @@ -105,6 +106,8 @@ + + import Tree from '$lib/components/shared-components/tree/tree.svelte'; - import type { RecursiveObject } from '$lib/utils/tree-utils'; + import { normalizeTreePath, type RecursiveObject } from '$lib/utils/tree-utils'; export let items: RecursiveObject; export let parent = ''; export let active = ''; + export let icons: { default: string; active: string }; export let getLink: (path: string) => string; + export let getColor: (path: string) => string | undefined = () => undefined;
    - {#each Object.entries(items) as [path, tree], index (index)} -
  • - -
  • + {#each Object.entries(items) as [path, tree]} + {@const value = normalizeTreePath(`${parent}/${path}`)} + {@const key = value + getColor(value)} + {#key key} +
  • + +
  • + {/key} {/each}
diff --git a/web/src/lib/components/shared-components/tree/tree.svelte b/web/src/lib/components/shared-components/tree/tree.svelte index 7975825c5ea88..99928f5bbd750 100644 --- a/web/src/lib/components/shared-components/tree/tree.svelte +++ b/web/src/lib/components/shared-components/tree/tree.svelte @@ -2,18 +2,21 @@ import Icon from '$lib/components/elements/icon.svelte'; import TreeItems from '$lib/components/shared-components/tree/tree-items.svelte'; import { normalizeTreePath, type RecursiveObject } from '$lib/utils/tree-utils'; - import { mdiChevronDown, mdiChevronRight, mdiFolder, mdiFolderOutline } from '@mdi/js'; + import { mdiChevronDown, mdiChevronRight } from '@mdi/js'; export let tree: RecursiveObject; export let parent: string; export let value: string; export let active = ''; + export let icons: { default: string; active: string }; export let getLink: (path: string) => string; + export let getColor: (path: string) => string | undefined; $: path = normalizeTreePath(`${parent}/${value}`); $: isActive = active.startsWith(path); $: isOpen = isActive; $: isTarget = active === path; + $: color = getColor(path); -
@@ -35,5 +43,5 @@
{#if isOpen} - + {/if} diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts index 34d64098487fe..ce5cefd8153d2 100644 --- a/web/src/lib/constants.ts +++ b/web/src/lib/constants.ts @@ -47,6 +47,7 @@ export enum AppRoute { DUPLICATES = '/utilities/duplicates', FOLDERS = '/folders', + TAGS = '/tags', } export enum ProjectionType { diff --git a/web/src/lib/i18n/en.json b/web/src/lib/i18n/en.json index 8b1ec452d78b8..684cb0e319ec3 100644 --- a/web/src/lib/i18n/en.json +++ b/web/src/lib/i18n/en.json @@ -440,6 +440,7 @@ "close": "Close", "collapse": "Collapse", "collapse_all": "Collapse all", + "color": "Color", "color_theme": "Color theme", "comment_deleted": "Comment deleted", "comment_options": "Comment options", @@ -473,6 +474,8 @@ "create_new_person": "Create new person", "create_new_person_hint": "Assign selected assets to a new person", "create_new_user": "Create new user", + "create_tag": "Create tag", + "create_tag_description": "Create a new tag. For nested tags, please enter the full path of the tag including forward slashes.", "create_user": "Create user", "created": "Created", "current_device": "Current device", @@ -496,6 +499,8 @@ "delete_library": "Delete library", "delete_link": "Delete link", "delete_shared_link": "Delete shared link", + "delete_tag": "Delete tag", + "delete_tag_confirmation_prompt": "Are you sure you want to delete {tagName} tag?", "delete_user": "Delete user", "deleted_shared_link": "Deleted shared link", "description": "Description", @@ -537,6 +542,7 @@ "edit_location": "Edit location", "edit_name": "Edit name", "edit_people": "Edit people", + "edit_tag": "Edit tag", "edit_title": "Edit Title", "edit_user": "Edit user", "edited": "Edited", @@ -1007,6 +1013,7 @@ "removed_from_archive": "Removed from archive", "removed_from_favorites": "Removed from favorites", "removed_from_favorites_count": "{count, plural, other {Removed #}} from favorites", + "removed_tagged_assets": "Removed tag from {count, plural, one {# asset} other {# assets}}", "rename": "Rename", "repair": "Repair", "repair_no_results_message": "Untracked and missing files will show up here", @@ -1055,6 +1062,7 @@ "search_people": "Search people", "search_places": "Search places", "search_state": "Search state...", + "search_tags": "Search tags...", "search_timezone": "Search timezone...", "search_type": "Search type", "search_your_photos": "Search your photos", @@ -1158,6 +1166,12 @@ "sunrise_on_the_beach": "Sunrise on the beach", "swap_merge_direction": "Swap merge direction", "sync": "Sync", + "tag": "Tag", + "tag_assets": "Tag assets", + "tag_created": "Created tag: {tag}", + "tag_updated": "Updated tag: {tag}", + "tagged_assets": "Tagged {count, plural, one {# asset} other {# assets}}", + "tags": "Tags", "template": "Template", "theme": "Theme", "theme_selection": "Theme selection", @@ -1169,6 +1183,7 @@ "to_change_password": "Change password", "to_favorite": "Favorite", "to_login": "Login", + "to_root": "To root", "to_trash": "Trash", "toggle_settings": "Toggle settings", "toggle_theme": "Toggle dark theme", diff --git a/web/src/lib/utils/asset-store-task-manager.ts b/web/src/lib/utils/asset-store-task-manager.ts index 6ece1327c4751..6ca4f057bd419 100644 --- a/web/src/lib/utils/asset-store-task-manager.ts +++ b/web/src/lib/utils/asset-store-task-manager.ts @@ -256,9 +256,9 @@ export class AssetGridTaskManager { bucketTask.scheduleIntersected(componentId, task); } - seperatedBucket(componentId: string, bucket: AssetBucket, seperated: Task) { + separatedBucket(componentId: string, bucket: AssetBucket, separated: Task) { const bucketTask = this.getOrCreateBucketTask(bucket); - bucketTask.scheduleSeparated(componentId, seperated); + bucketTask.scheduleSeparated(componentId, separated); } intersectedDateGroup(componentId: string, dateGroup: DateGroup, intersected: Task) { @@ -266,9 +266,9 @@ export class AssetGridTaskManager { bucketTask.intersectedDateGroup(componentId, dateGroup, intersected); } - seperatedDateGroup(componentId: string, dateGroup: DateGroup, seperated: Task) { + separatedDateGroup(componentId: string, dateGroup: DateGroup, separated: Task) { const bucketTask = this.getOrCreateBucketTask(dateGroup.bucket); - bucketTask.separatedDateGroup(componentId, dateGroup, seperated); + bucketTask.separatedDateGroup(componentId, dateGroup, separated); } intersectedThumbnail(componentId: string, dateGroup: DateGroup, asset: AssetResponseDto, intersected: Task) { @@ -277,16 +277,16 @@ export class AssetGridTaskManager { dateGroupTask.intersectedThumbnail(componentId, asset, intersected); } - seperatedThumbnail(componentId: string, dateGroup: DateGroup, asset: AssetResponseDto, seperated: Task) { + separatedThumbnail(componentId: string, dateGroup: DateGroup, asset: AssetResponseDto, separated: Task) { const bucketTask = this.getOrCreateBucketTask(dateGroup.bucket); const dateGroupTask = bucketTask.getOrCreateDateGroupTask(dateGroup); - dateGroupTask.separatedThumbnail(componentId, asset, seperated); + dateGroupTask.separatedThumbnail(componentId, asset, separated); } } class IntersectionTask { internalTaskManager: InternalTaskManager; - seperatedKey; + separatedKey; intersectedKey; priority; @@ -295,7 +295,7 @@ class IntersectionTask { constructor(internalTaskManager: InternalTaskManager, keyPrefix: string, key: string, priority: number) { this.internalTaskManager = internalTaskManager; - this.seperatedKey = keyPrefix + ':s:' + key; + this.separatedKey = keyPrefix + ':s:' + key; this.intersectedKey = keyPrefix + ':i:' + key; this.priority = priority; } @@ -325,14 +325,14 @@ class IntersectionTask { this.separated = execTask; const cleanup = () => { this.separated = undefined; - this.internalTaskManager.deleteFromComponentTasks(componentId, this.seperatedKey); + this.internalTaskManager.deleteFromComponentTasks(componentId, this.separatedKey); }; return { task: execTask, cleanup }; } removePendingSeparated() { if (this.separated) { - this.internalTaskManager.removeSeparateTask(this.seperatedKey); + this.internalTaskManager.removeSeparateTask(this.separatedKey); } } removePendingIntersected() { @@ -368,7 +368,7 @@ class IntersectionTask { task, cleanup, componentId: componentId, - taskId: this.seperatedKey, + taskId: this.separatedKey, }); } } @@ -448,9 +448,9 @@ class DateGroupTask extends IntersectionTask { thumbnailTask.scheduleIntersected(componentId, intersected); } - separatedThumbnail(componentId: string, asset: AssetResponseDto, seperated: Task) { + separatedThumbnail(componentId: string, asset: AssetResponseDto, separated: Task) { const thumbnailTask = this.getOrCreateThumbnailTask(asset); - thumbnailTask.scheduleSeparated(componentId, seperated); + thumbnailTask.scheduleSeparated(componentId, separated); } } class ThumbnailTask extends IntersectionTask { diff --git a/web/src/lib/utils/asset-utils.ts b/web/src/lib/utils/asset-utils.ts index 576b14b20179e..ce7944b9c98f2 100644 --- a/web/src/lib/utils/asset-utils.ts +++ b/web/src/lib/utils/asset-utils.ts @@ -10,6 +10,7 @@ import { preferences } from '$lib/stores/user.store'; import { downloadRequest, getKey, withError } from '$lib/utils'; import { createAlbum } from '$lib/utils/album-utils'; import { getByteUnitString } from '$lib/utils/byte-units'; +import { getFormatter } from '$lib/utils/i18n'; import { addAssetsToAlbum as addAssets, createStack, @@ -18,6 +19,8 @@ import { getBaseUrl, getDownloadInfo, getStack, + tagAssets as tagAllAssets, + untagAssets, updateAsset, updateAssets, type AlbumResponseDto, @@ -61,6 +64,54 @@ export const addAssetsToAlbum = async (albumId: string, assetIds: string[], show } }; +export const tagAssets = async ({ + assetIds, + tagIds, + showNotification = true, +}: { + assetIds: string[]; + tagIds: string[]; + showNotification?: boolean; +}) => { + for (const tagId of tagIds) { + await tagAllAssets({ id: tagId, bulkIdsDto: { ids: assetIds } }); + } + + if (showNotification) { + const $t = await getFormatter(); + notificationController.show({ + message: $t('tagged_assets', { values: { count: assetIds.length } }), + type: NotificationType.Info, + }); + } + + return assetIds; +}; + +export const removeTag = async ({ + assetIds, + tagIds, + showNotification = true, +}: { + assetIds: string[]; + tagIds: string[]; + showNotification?: boolean; +}) => { + for (const tagId of tagIds) { + await untagAssets({ id: tagId, bulkIdsDto: { ids: assetIds } }); + } + + if (showNotification) { + const $t = await getFormatter(); + notificationController.show({ + message: $t('removed_tagged_assets', { values: { count: assetIds.length } }), + type: NotificationType.Info, + }); + } + + return assetIds; +}; + export const addAssetsToNewAlbum = async (albumName: string, assetIds: string[]) => { const album = await createAlbum(albumName, assetIds); if (!album) { diff --git a/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte index b5301843427ed..a8b8602c02d00 100644 --- a/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -13,7 +13,7 @@ import { foldersStore } from '$lib/stores/folders.store'; import { buildTree, normalizeTreePath } from '$lib/utils/tree-utils'; import { type AssetResponseDto } from '@immich/sdk'; - import { mdiArrowUpLeft, mdiChevronRight, mdiFolder, mdiFolderHome } from '@mdi/js'; + import { mdiArrowUpLeft, mdiChevronRight, mdiFolder, mdiFolderHome, mdiFolderOutline } from '@mdi/js'; import { onMount } from 'svelte'; import { t } from 'svelte-i18n'; import type { PageData } from './$types'; @@ -60,7 +60,12 @@
{$t('explorer').toUpperCase()}
- +
@@ -73,7 +78,7 @@
- + {#each pathSegments as segment, index} diff --git a/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte index 9bcbdbeea08d0..e15c20cbbe8d7 100644 --- a/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte @@ -25,6 +25,7 @@ import { preferences, user } from '$lib/stores/user.store'; import { t } from 'svelte-i18n'; import { onDestroy } from 'svelte'; + import TagAction from '$lib/components/photos-page/actions/tag-action.svelte'; let { isViewing: showAssetViewer } = assetViewingStore; const assetStore = new AssetStore({ isArchived: false, withStacked: true, withPartners: true }); @@ -80,6 +81,7 @@ assetStore.removeAssets(assetIds)} /> + assetStore.removeAssets(assetIds)} />
diff --git a/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte new file mode 100644 index 0000000000000..7335bf83c1e11 --- /dev/null +++ b/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -0,0 +1,251 @@ + + + + +
+
{$t('explorer').toUpperCase()}
+
+ +
+
+
+ +
+ +
+ + +
+
+ + +
+ + +
+
+ + {#if pathSegments.length > 0 && tag} + +
+ + +
+
+ {/if} +
+ +
+ + + + {#each pathSegments as segment, index} + +

+ {#if index < pathSegments.length - 1} + + {/if} +

+ {/each} +
+ +
+ {#key $page.url.href} + {#if tag} + + + + {:else} + + {/if} + {/key} +
+
+ +{#if isNewOpen} + +
+

+ {$t('create_tag_description')} +

+
+ +
+
+ +
+
+ + + + +
+{/if} + +{#if isEditOpen} + +
+
+ +
+
+ + + + +
+{/if} diff --git a/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.ts b/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.ts new file mode 100644 index 0000000000000..23846e57c43d3 --- /dev/null +++ b/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.ts @@ -0,0 +1,32 @@ +import { QueryParameter } from '$lib/constants'; +import { authenticate } from '$lib/utils/auth'; +import { getFormatter } from '$lib/utils/i18n'; +import { getAssetInfoFromParam } from '$lib/utils/navigation'; +import { buildTree, normalizeTreePath } from '$lib/utils/tree-utils'; +import { getAllTags } from '@immich/sdk'; +import type { PageLoad } from './$types'; + +export const load = (async ({ params, url }) => { + await authenticate(); + const asset = await getAssetInfoFromParam(params); + const $t = await getFormatter(); + + const path = url.searchParams.get(QueryParameter.PATH); + const tags = await getAllTags(); + const tree = buildTree(tags.map((tag) => tag.value)); + let currentTree = tree; + const parts = normalizeTreePath(path || '').split('/'); + for (const part of parts) { + currentTree = currentTree?.[part]; + } + + return { + tags, + asset, + path, + children: Object.keys(currentTree || {}), + meta: { + title: $t('tags'), + }, + }; +}) satisfies PageLoad; From 74f18a45235845b7dd16b4c4088d084ec3ea20b7 Mon Sep 17 00:00:00 2001 From: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Date: Thu, 29 Aug 2024 20:10:09 +0200 Subject: [PATCH 057/160] fix(server): skip smtp validation if unchanged (#12111) * fix(server): skip smtp validation if unchanged * update comparison + convert config to plain object --- server/src/services/notification.service.spec.ts | 10 ++++++++++ server/src/services/notification.service.ts | 4 ++-- server/src/services/system-config.service.ts | 3 ++- server/src/utils/object.ts | 15 +++++++++++++++ 4 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 server/src/utils/object.ts diff --git a/server/src/services/notification.service.spec.ts b/server/src/services/notification.service.spec.ts index bcce902e91dcd..5bcead0ff31ae 100644 --- a/server/src/services/notification.service.spec.ts +++ b/server/src/services/notification.service.spec.ts @@ -1,4 +1,6 @@ +import { plainToInstance } from 'class-transformer'; import { defaults, SystemConfig } from 'src/config'; +import { SystemConfigDto } from 'src/dtos/system-config.dto'; import { AlbumUserEntity } from 'src/entities/album-user.entity'; import { AssetFileEntity } from 'src/entities/asset-files.entity'; import { AssetFileType, UserMetadataKey } from 'src/enum'; @@ -112,6 +114,14 @@ describe(NotificationService.name, () => { expect(notificationMock.verifySmtp).not.toHaveBeenCalled(); }); + it('skips smtp validation with DTO when there are no changes', async () => { + const oldConfig = { ...configs.smtpEnabled }; + const newConfig = plainToInstance(SystemConfigDto, configs.smtpEnabled); + + await expect(sut.onConfigValidate({ oldConfig, newConfig })).resolves.not.toThrow(); + expect(notificationMock.verifySmtp).not.toHaveBeenCalled(); + }); + it('skips smtp validation when smtp is disabled', async () => { const oldConfig = { ...configs.smtpEnabled }; const newConfig = { ...configs.smtpDisabled }; diff --git a/server/src/services/notification.service.ts b/server/src/services/notification.service.ts index ace8240b39c57..274c91661ca2b 100644 --- a/server/src/services/notification.service.ts +++ b/server/src/services/notification.service.ts @@ -1,5 +1,4 @@ import { HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common'; -import { isEqual } from 'lodash'; import { DEFAULT_EXTERNAL_DOMAIN } from 'src/constants'; import { SystemConfigCore } from 'src/cores/system-config.core'; import { OnEmit } from 'src/decorators'; @@ -23,6 +22,7 @@ import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interf import { IUserRepository } from 'src/interfaces/user.interface'; import { getAssetFiles } from 'src/utils/asset.util'; import { getFilenameExtension } from 'src/utils/file'; +import { isEqualObject } from 'src/utils/object'; import { getPreferences } from 'src/utils/preferences'; @Injectable() @@ -47,7 +47,7 @@ export class NotificationService { try { if ( newConfig.notifications.smtp.enabled && - !isEqual(oldConfig.notifications.smtp, newConfig.notifications.smtp) + !isEqualObject(oldConfig.notifications.smtp, newConfig.notifications.smtp) ) { await this.notificationRepository.verifySmtp(newConfig.notifications.smtp.transport); } diff --git a/server/src/services/system-config.service.ts b/server/src/services/system-config.service.ts index 26a91f1d09e85..5ec9ab7a5db05 100644 --- a/server/src/services/system-config.service.ts +++ b/server/src/services/system-config.service.ts @@ -18,6 +18,7 @@ import { SystemConfigDto, SystemConfigTemplateStorageOptionDto, mapConfig } from import { ArgOf, ClientEvent, IEventRepository, ServerEvent } from 'src/interfaces/event.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; +import { toPlainObject } from 'src/utils/object'; @Injectable() export class SystemConfigService { @@ -63,7 +64,7 @@ export class SystemConfigService { const oldConfig = await this.core.getConfig({ withCache: false }); try { - await this.eventRepository.emit('config.validate', { newConfig: dto, oldConfig }); + await this.eventRepository.emit('config.validate', { newConfig: toPlainObject(dto), oldConfig }); } catch (error) { this.logger.warn(`Unable to save system config due to a validation error: ${error}`); throw new BadRequestException(error instanceof Error ? error.message : error); diff --git a/server/src/utils/object.ts b/server/src/utils/object.ts new file mode 100644 index 0000000000000..25ae42cba8b2a --- /dev/null +++ b/server/src/utils/object.ts @@ -0,0 +1,15 @@ +import { isEqual, isPlainObject } from 'lodash'; + +/** + * Deeply clones and converts a class instance to a plain object. + */ +export function toPlainObject(obj: T): T { + return isPlainObject(obj) ? obj : structuredClone(obj); +} + +/** + * Performs a deep comparison between objects, converting them to plain objects first if needed. + */ +export function isEqualObject(value: object, other: object): boolean { + return isEqual(toPlainObject(value), toPlainObject(other)); +} From 9bfaa525db29d0f591750582415894ebfeced0f6 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 29 Aug 2024 13:46:47 -0500 Subject: [PATCH 058/160] fix(mobile): long waiting time for login request when server is unreachable (#12100) * fix(mobile): long waiting time for login request when server is unreachable * lint * increase timeout duration --- mobile/lib/providers/authentication.provider.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mobile/lib/providers/authentication.provider.dart b/mobile/lib/providers/authentication.provider.dart index 5d3ae5bc22677..b56e71b11b3f6 100644 --- a/mobile/lib/providers/authentication.provider.dart +++ b/mobile/lib/providers/authentication.provider.dart @@ -170,8 +170,10 @@ class AuthenticationNotifier extends StateNotifier { UserPreferencesResponseDto? userPreferences; try { final responses = await Future.wait([ - _apiService.usersApi.getMyUser(), - _apiService.usersApi.getMyPreferences(), + _apiService.usersApi.getMyUser().timeout(const Duration(seconds: 7)), + _apiService.usersApi + .getMyPreferences() + .timeout(const Duration(seconds: 7)), ]); userResponse = responses[0] as UserAdminResponseDto; userPreferences = responses[1] as UserPreferencesResponseDto; From ebecb60f39819e8b56cfe29dd8b118b065ef487d Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 29 Aug 2024 14:29:04 -0500 Subject: [PATCH 059/160] feat: user's features preferences (#12099) * feat: metadata in UserPreference * feat: web metadata settings * feat: web metadata settings * fix: typo * patch openapi * fix: missing translation key * new organization of preference strucutre * feature settings on web * localization * added and used feature settings * add default value to response dto * patch openapi * format en.json file * implement helper method * use tags preference logic * Fix logic bug and add tests * fix preference can be null in detail panel --- mobile/lib/utils/openapi_patching.dart | 30 +++- mobile/openapi/README.md | 14 +- mobile/openapi/lib/api.dart | 14 +- mobile/openapi/lib/api_client.dart | 28 ++- .../openapi/lib/model/folders_response.dart | 106 ++++++++++++ mobile/openapi/lib/model/folders_update.dart | 124 ++++++++++++++ ...y_response.dart => memories_response.dart} | 38 ++-- ...ating_update.dart => memories_update.dart} | 36 ++-- mobile/openapi/lib/model/people_response.dart | 106 ++++++++++++ mobile/openapi/lib/model/people_update.dart | 124 ++++++++++++++ ...ng_response.dart => ratings_response.dart} | 36 ++-- ...memory_update.dart => ratings_update.dart} | 36 ++-- mobile/openapi/lib/model/tags_response.dart | 106 ++++++++++++ mobile/openapi/lib/model/tags_update.dart | 124 ++++++++++++++ .../model/user_preferences_response_dto.dart | 44 +++-- .../model/user_preferences_update_dto.dart | 73 ++++++-- .../modules/utils/openapi_patching_test.dart | 49 ++++++ open-api/immich-openapi-specs.json | 162 +++++++++++++++--- open-api/typescript-sdk/src/fetch-client.ts | 46 ++++- server/src/dtos/user-preferences.dto.ts | 85 +++++++-- server/src/entities/user-metadata.entity.ts | 28 ++- .../detail-panel-star-rating.svelte | 2 +- .../asset-viewer/detail-panel.svelte | 10 +- .../settings/setting-accordion.svelte | 17 +- .../side-bar/side-bar.svelte | 38 ++-- .../user-settings-page/app-settings.svelte | 43 ----- .../feature-settings.svelte | 124 ++++++++++++++ .../memories-settings.svelte | 46 ----- .../user-settings-list.svelte | 7 +- web/src/lib/i18n/en.json | 9 +- web/src/lib/stores/preferences.store.ts | 5 - .../(user)/photos/[[assetId=id]]/+page.svelte | 4 +- 32 files changed, 1418 insertions(+), 296 deletions(-) create mode 100644 mobile/openapi/lib/model/folders_response.dart create mode 100644 mobile/openapi/lib/model/folders_update.dart rename mobile/openapi/lib/model/{memory_response.dart => memories_response.dart} (62%) rename mobile/openapi/lib/model/{rating_update.dart => memories_update.dart} (68%) create mode 100644 mobile/openapi/lib/model/people_response.dart create mode 100644 mobile/openapi/lib/model/people_update.dart rename mobile/openapi/lib/model/{rating_response.dart => ratings_response.dart} (63%) rename mobile/openapi/lib/model/{memory_update.dart => ratings_update.dart} (69%) create mode 100644 mobile/openapi/lib/model/tags_response.dart create mode 100644 mobile/openapi/lib/model/tags_update.dart create mode 100644 mobile/test/modules/utils/openapi_patching_test.dart create mode 100644 web/src/lib/components/user-settings-page/feature-settings.svelte delete mode 100644 web/src/lib/components/user-settings-page/memories-settings.svelte diff --git a/mobile/lib/utils/openapi_patching.dart b/mobile/lib/utils/openapi_patching.dart index 7a2f7396eb3ab..349b2322afac1 100644 --- a/mobile/lib/utils/openapi_patching.dart +++ b/mobile/lib/utils/openapi_patching.dart @@ -4,14 +4,30 @@ dynamic upgradeDto(dynamic value, String targetType) { switch (targetType) { case 'UserPreferencesResponseDto': if (value is Map) { - if (value['rating'] == null) { - value['rating'] = RatingResponse().toJson(); - } - - if (value['download']['includeEmbeddedVideos'] == null) { - value['download']['includeEmbeddedVideos'] = false; - } + addDefault(value, 'download.includeEmbeddedVideos', false); + addDefault(value, 'folders', FoldersResponse().toJson()); + addDefault(value, 'memories', MemoriesResponse().toJson()); + addDefault(value, 'ratings', RatingsResponse().toJson()); + addDefault(value, 'people', PeopleResponse().toJson()); + addDefault(value, 'tags', TagsResponse().toJson()); } break; } } + +addDefault(dynamic value, String keys, dynamic defaultValue) { + // Loop through the keys and assign the default value if the key is not present + List keyList = keys.split('.'); + dynamic current = value; + + for (int i = 0; i < keyList.length - 1; i++) { + if (current[keyList[i]] == null) { + current[keyList[i]] = {}; + } + current = current[keyList[i]]; + } + + if (current[keyList.last] == null) { + current[keyList.last] = defaultValue; + } +} diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 1f8958dd95d4d..b831f60b9a2a0 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -324,6 +324,8 @@ Class | Method | HTTP request | Description - [FileReportDto](doc//FileReportDto.md) - [FileReportFixDto](doc//FileReportFixDto.md) - [FileReportItemDto](doc//FileReportItemDto.md) + - [FoldersResponse](doc//FoldersResponse.md) + - [FoldersUpdate](doc//FoldersUpdate.md) - [ImageFormat](doc//ImageFormat.md) - [JobCommand](doc//JobCommand.md) - [JobCommandDto](doc//JobCommandDto.md) @@ -342,12 +344,12 @@ Class | Method | HTTP request | Description - [MapMarkerResponseDto](doc//MapMarkerResponseDto.md) - [MapReverseGeocodeResponseDto](doc//MapReverseGeocodeResponseDto.md) - [MapTheme](doc//MapTheme.md) + - [MemoriesResponse](doc//MemoriesResponse.md) + - [MemoriesUpdate](doc//MemoriesUpdate.md) - [MemoryCreateDto](doc//MemoryCreateDto.md) - [MemoryLaneResponseDto](doc//MemoryLaneResponseDto.md) - - [MemoryResponse](doc//MemoryResponse.md) - [MemoryResponseDto](doc//MemoryResponseDto.md) - [MemoryType](doc//MemoryType.md) - - [MemoryUpdate](doc//MemoryUpdate.md) - [MemoryUpdateDto](doc//MemoryUpdateDto.md) - [MergePersonDto](doc//MergePersonDto.md) - [MetadataSearchDto](doc//MetadataSearchDto.md) @@ -359,7 +361,9 @@ Class | Method | HTTP request | Description - [PartnerResponseDto](doc//PartnerResponseDto.md) - [PathEntityType](doc//PathEntityType.md) - [PathType](doc//PathType.md) + - [PeopleResponse](doc//PeopleResponse.md) - [PeopleResponseDto](doc//PeopleResponseDto.md) + - [PeopleUpdate](doc//PeopleUpdate.md) - [PeopleUpdateDto](doc//PeopleUpdateDto.md) - [PeopleUpdateItem](doc//PeopleUpdateItem.md) - [Permission](doc//Permission.md) @@ -372,8 +376,8 @@ Class | Method | HTTP request | Description - [PurchaseResponse](doc//PurchaseResponse.md) - [PurchaseUpdate](doc//PurchaseUpdate.md) - [QueueStatusDto](doc//QueueStatusDto.md) - - [RatingResponse](doc//RatingResponse.md) - - [RatingUpdate](doc//RatingUpdate.md) + - [RatingsResponse](doc//RatingsResponse.md) + - [RatingsUpdate](doc//RatingsUpdate.md) - [ReactionLevel](doc//ReactionLevel.md) - [ReactionType](doc//ReactionType.md) - [ReverseGeocodingStateResponseDto](doc//ReverseGeocodingStateResponseDto.md) @@ -435,6 +439,8 @@ Class | Method | HTTP request | Description - [TagResponseDto](doc//TagResponseDto.md) - [TagUpdateDto](doc//TagUpdateDto.md) - [TagUpsertDto](doc//TagUpsertDto.md) + - [TagsResponse](doc//TagsResponse.md) + - [TagsUpdate](doc//TagsUpdate.md) - [TimeBucketResponseDto](doc//TimeBucketResponseDto.md) - [TimeBucketSize](doc//TimeBucketSize.md) - [ToneMapping](doc//ToneMapping.md) diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index 532d7e22cddf4..d6ce89624cee6 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -138,6 +138,8 @@ part 'model/file_checksum_response_dto.dart'; part 'model/file_report_dto.dart'; part 'model/file_report_fix_dto.dart'; part 'model/file_report_item_dto.dart'; +part 'model/folders_response.dart'; +part 'model/folders_update.dart'; part 'model/image_format.dart'; part 'model/job_command.dart'; part 'model/job_command_dto.dart'; @@ -156,12 +158,12 @@ part 'model/logout_response_dto.dart'; part 'model/map_marker_response_dto.dart'; part 'model/map_reverse_geocode_response_dto.dart'; part 'model/map_theme.dart'; +part 'model/memories_response.dart'; +part 'model/memories_update.dart'; part 'model/memory_create_dto.dart'; part 'model/memory_lane_response_dto.dart'; -part 'model/memory_response.dart'; part 'model/memory_response_dto.dart'; part 'model/memory_type.dart'; -part 'model/memory_update.dart'; part 'model/memory_update_dto.dart'; part 'model/merge_person_dto.dart'; part 'model/metadata_search_dto.dart'; @@ -173,7 +175,9 @@ part 'model/partner_direction.dart'; part 'model/partner_response_dto.dart'; part 'model/path_entity_type.dart'; part 'model/path_type.dart'; +part 'model/people_response.dart'; part 'model/people_response_dto.dart'; +part 'model/people_update.dart'; part 'model/people_update_dto.dart'; part 'model/people_update_item.dart'; part 'model/permission.dart'; @@ -186,8 +190,8 @@ part 'model/places_response_dto.dart'; part 'model/purchase_response.dart'; part 'model/purchase_update.dart'; part 'model/queue_status_dto.dart'; -part 'model/rating_response.dart'; -part 'model/rating_update.dart'; +part 'model/ratings_response.dart'; +part 'model/ratings_update.dart'; part 'model/reaction_level.dart'; part 'model/reaction_type.dart'; part 'model/reverse_geocoding_state_response_dto.dart'; @@ -249,6 +253,8 @@ part 'model/tag_create_dto.dart'; part 'model/tag_response_dto.dart'; part 'model/tag_update_dto.dart'; part 'model/tag_upsert_dto.dart'; +part 'model/tags_response.dart'; +part 'model/tags_update.dart'; part 'model/time_bucket_response_dto.dart'; part 'model/time_bucket_size.dart'; part 'model/tone_mapping.dart'; diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index 54873a59557f2..47375f0b504f1 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -331,6 +331,10 @@ class ApiClient { return FileReportFixDto.fromJson(value); case 'FileReportItemDto': return FileReportItemDto.fromJson(value); + case 'FoldersResponse': + return FoldersResponse.fromJson(value); + case 'FoldersUpdate': + return FoldersUpdate.fromJson(value); case 'ImageFormat': return ImageFormatTypeTransformer().decode(value); case 'JobCommand': @@ -367,18 +371,18 @@ class ApiClient { return MapReverseGeocodeResponseDto.fromJson(value); case 'MapTheme': return MapThemeTypeTransformer().decode(value); + case 'MemoriesResponse': + return MemoriesResponse.fromJson(value); + case 'MemoriesUpdate': + return MemoriesUpdate.fromJson(value); case 'MemoryCreateDto': return MemoryCreateDto.fromJson(value); case 'MemoryLaneResponseDto': return MemoryLaneResponseDto.fromJson(value); - case 'MemoryResponse': - return MemoryResponse.fromJson(value); case 'MemoryResponseDto': return MemoryResponseDto.fromJson(value); case 'MemoryType': return MemoryTypeTypeTransformer().decode(value); - case 'MemoryUpdate': - return MemoryUpdate.fromJson(value); case 'MemoryUpdateDto': return MemoryUpdateDto.fromJson(value); case 'MergePersonDto': @@ -401,8 +405,12 @@ class ApiClient { return PathEntityTypeTypeTransformer().decode(value); case 'PathType': return PathTypeTypeTransformer().decode(value); + case 'PeopleResponse': + return PeopleResponse.fromJson(value); case 'PeopleResponseDto': return PeopleResponseDto.fromJson(value); + case 'PeopleUpdate': + return PeopleUpdate.fromJson(value); case 'PeopleUpdateDto': return PeopleUpdateDto.fromJson(value); case 'PeopleUpdateItem': @@ -427,10 +435,10 @@ class ApiClient { return PurchaseUpdate.fromJson(value); case 'QueueStatusDto': return QueueStatusDto.fromJson(value); - case 'RatingResponse': - return RatingResponse.fromJson(value); - case 'RatingUpdate': - return RatingUpdate.fromJson(value); + case 'RatingsResponse': + return RatingsResponse.fromJson(value); + case 'RatingsUpdate': + return RatingsUpdate.fromJson(value); case 'ReactionLevel': return ReactionLevelTypeTransformer().decode(value); case 'ReactionType': @@ -553,6 +561,10 @@ class ApiClient { return TagUpdateDto.fromJson(value); case 'TagUpsertDto': return TagUpsertDto.fromJson(value); + case 'TagsResponse': + return TagsResponse.fromJson(value); + case 'TagsUpdate': + return TagsUpdate.fromJson(value); case 'TimeBucketResponseDto': return TimeBucketResponseDto.fromJson(value); case 'TimeBucketSize': diff --git a/mobile/openapi/lib/model/folders_response.dart b/mobile/openapi/lib/model/folders_response.dart new file mode 100644 index 0000000000000..5bfc4c793deed --- /dev/null +++ b/mobile/openapi/lib/model/folders_response.dart @@ -0,0 +1,106 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class FoldersResponse { + /// Returns a new [FoldersResponse] instance. + FoldersResponse({ + this.enabled = false, + this.sidebarWeb = false, + }); + + bool enabled; + + bool sidebarWeb; + + @override + bool operator ==(Object other) => identical(this, other) || other is FoldersResponse && + other.enabled == enabled && + other.sidebarWeb == sidebarWeb; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (enabled.hashCode) + + (sidebarWeb.hashCode); + + @override + String toString() => 'FoldersResponse[enabled=$enabled, sidebarWeb=$sidebarWeb]'; + + Map toJson() { + final json = {}; + json[r'enabled'] = this.enabled; + json[r'sidebarWeb'] = this.sidebarWeb; + return json; + } + + /// Returns a new [FoldersResponse] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static FoldersResponse? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + return FoldersResponse( + enabled: mapValueOfType(json, r'enabled')!, + sidebarWeb: mapValueOfType(json, r'sidebarWeb')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = FoldersResponse.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = FoldersResponse.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of FoldersResponse-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = FoldersResponse.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'enabled', + 'sidebarWeb', + }; +} + diff --git a/mobile/openapi/lib/model/folders_update.dart b/mobile/openapi/lib/model/folders_update.dart new file mode 100644 index 0000000000000..088c98a4d8fd2 --- /dev/null +++ b/mobile/openapi/lib/model/folders_update.dart @@ -0,0 +1,124 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class FoldersUpdate { + /// Returns a new [FoldersUpdate] instance. + FoldersUpdate({ + this.enabled, + this.sidebarWeb, + }); + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? enabled; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? sidebarWeb; + + @override + bool operator ==(Object other) => identical(this, other) || other is FoldersUpdate && + other.enabled == enabled && + other.sidebarWeb == sidebarWeb; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (enabled == null ? 0 : enabled!.hashCode) + + (sidebarWeb == null ? 0 : sidebarWeb!.hashCode); + + @override + String toString() => 'FoldersUpdate[enabled=$enabled, sidebarWeb=$sidebarWeb]'; + + Map toJson() { + final json = {}; + if (this.enabled != null) { + json[r'enabled'] = this.enabled; + } else { + // json[r'enabled'] = null; + } + if (this.sidebarWeb != null) { + json[r'sidebarWeb'] = this.sidebarWeb; + } else { + // json[r'sidebarWeb'] = null; + } + return json; + } + + /// Returns a new [FoldersUpdate] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static FoldersUpdate? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + return FoldersUpdate( + enabled: mapValueOfType(json, r'enabled'), + sidebarWeb: mapValueOfType(json, r'sidebarWeb'), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = FoldersUpdate.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = FoldersUpdate.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of FoldersUpdate-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = FoldersUpdate.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + }; +} + diff --git a/mobile/openapi/lib/model/memory_response.dart b/mobile/openapi/lib/model/memories_response.dart similarity index 62% rename from mobile/openapi/lib/model/memory_response.dart rename to mobile/openapi/lib/model/memories_response.dart index fb34bc1518876..e215a66a03f67 100644 --- a/mobile/openapi/lib/model/memory_response.dart +++ b/mobile/openapi/lib/model/memories_response.dart @@ -10,16 +10,16 @@ part of openapi.api; -class MemoryResponse { - /// Returns a new [MemoryResponse] instance. - MemoryResponse({ - required this.enabled, +class MemoriesResponse { + /// Returns a new [MemoriesResponse] instance. + MemoriesResponse({ + this.enabled = true, }); bool enabled; @override - bool operator ==(Object other) => identical(this, other) || other is MemoryResponse && + bool operator ==(Object other) => identical(this, other) || other is MemoriesResponse && other.enabled == enabled; @override @@ -28,7 +28,7 @@ class MemoryResponse { (enabled.hashCode); @override - String toString() => 'MemoryResponse[enabled=$enabled]'; + String toString() => 'MemoriesResponse[enabled=$enabled]'; Map toJson() { final json = {}; @@ -36,25 +36,25 @@ class MemoryResponse { return json; } - /// Returns a new [MemoryResponse] instance and imports its values from + /// Returns a new [MemoriesResponse] instance and imports its values from /// [value] if it's a [Map], null otherwise. // ignore: prefer_constructors_over_static_methods - static MemoryResponse? fromJson(dynamic value) { + static MemoriesResponse? fromJson(dynamic value) { if (value is Map) { final json = value.cast(); - return MemoryResponse( + return MemoriesResponse( enabled: mapValueOfType(json, r'enabled')!, ); } return null; } - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; if (json is List && json.isNotEmpty) { for (final row in json) { - final value = MemoryResponse.fromJson(row); + final value = MemoriesResponse.fromJson(row); if (value != null) { result.add(value); } @@ -63,12 +63,12 @@ class MemoryResponse { return result.toList(growable: growable); } - static Map mapFromJson(dynamic json) { - final map = {}; + static Map mapFromJson(dynamic json) { + final map = {}; if (json is Map && json.isNotEmpty) { json = json.cast(); // ignore: parameter_assignments for (final entry in json.entries) { - final value = MemoryResponse.fromJson(entry.value); + final value = MemoriesResponse.fromJson(entry.value); if (value != null) { map[entry.key] = value; } @@ -77,14 +77,14 @@ class MemoryResponse { return map; } - // maps a json object with a list of MemoryResponse-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = >{}; + // maps a json object with a list of MemoriesResponse-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; if (json is Map && json.isNotEmpty) { // ignore: parameter_assignments json = json.cast(); for (final entry in json.entries) { - map[entry.key] = MemoryResponse.listFromJson(entry.value, growable: growable,); + map[entry.key] = MemoriesResponse.listFromJson(entry.value, growable: growable,); } } return map; diff --git a/mobile/openapi/lib/model/rating_update.dart b/mobile/openapi/lib/model/memories_update.dart similarity index 68% rename from mobile/openapi/lib/model/rating_update.dart rename to mobile/openapi/lib/model/memories_update.dart index bb8f7eadc2f55..d30949136197e 100644 --- a/mobile/openapi/lib/model/rating_update.dart +++ b/mobile/openapi/lib/model/memories_update.dart @@ -10,9 +10,9 @@ part of openapi.api; -class RatingUpdate { - /// Returns a new [RatingUpdate] instance. - RatingUpdate({ +class MemoriesUpdate { + /// Returns a new [MemoriesUpdate] instance. + MemoriesUpdate({ this.enabled, }); @@ -25,7 +25,7 @@ class RatingUpdate { bool? enabled; @override - bool operator ==(Object other) => identical(this, other) || other is RatingUpdate && + bool operator ==(Object other) => identical(this, other) || other is MemoriesUpdate && other.enabled == enabled; @override @@ -34,7 +34,7 @@ class RatingUpdate { (enabled == null ? 0 : enabled!.hashCode); @override - String toString() => 'RatingUpdate[enabled=$enabled]'; + String toString() => 'MemoriesUpdate[enabled=$enabled]'; Map toJson() { final json = {}; @@ -46,25 +46,25 @@ class RatingUpdate { return json; } - /// Returns a new [RatingUpdate] instance and imports its values from + /// Returns a new [MemoriesUpdate] instance and imports its values from /// [value] if it's a [Map], null otherwise. // ignore: prefer_constructors_over_static_methods - static RatingUpdate? fromJson(dynamic value) { + static MemoriesUpdate? fromJson(dynamic value) { if (value is Map) { final json = value.cast(); - return RatingUpdate( + return MemoriesUpdate( enabled: mapValueOfType(json, r'enabled'), ); } return null; } - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; if (json is List && json.isNotEmpty) { for (final row in json) { - final value = RatingUpdate.fromJson(row); + final value = MemoriesUpdate.fromJson(row); if (value != null) { result.add(value); } @@ -73,12 +73,12 @@ class RatingUpdate { return result.toList(growable: growable); } - static Map mapFromJson(dynamic json) { - final map = {}; + static Map mapFromJson(dynamic json) { + final map = {}; if (json is Map && json.isNotEmpty) { json = json.cast(); // ignore: parameter_assignments for (final entry in json.entries) { - final value = RatingUpdate.fromJson(entry.value); + final value = MemoriesUpdate.fromJson(entry.value); if (value != null) { map[entry.key] = value; } @@ -87,14 +87,14 @@ class RatingUpdate { return map; } - // maps a json object with a list of RatingUpdate-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = >{}; + // maps a json object with a list of MemoriesUpdate-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; if (json is Map && json.isNotEmpty) { // ignore: parameter_assignments json = json.cast(); for (final entry in json.entries) { - map[entry.key] = RatingUpdate.listFromJson(entry.value, growable: growable,); + map[entry.key] = MemoriesUpdate.listFromJson(entry.value, growable: growable,); } } return map; diff --git a/mobile/openapi/lib/model/people_response.dart b/mobile/openapi/lib/model/people_response.dart new file mode 100644 index 0000000000000..e12f86eeab5ba --- /dev/null +++ b/mobile/openapi/lib/model/people_response.dart @@ -0,0 +1,106 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class PeopleResponse { + /// Returns a new [PeopleResponse] instance. + PeopleResponse({ + this.enabled = true, + this.sidebarWeb = false, + }); + + bool enabled; + + bool sidebarWeb; + + @override + bool operator ==(Object other) => identical(this, other) || other is PeopleResponse && + other.enabled == enabled && + other.sidebarWeb == sidebarWeb; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (enabled.hashCode) + + (sidebarWeb.hashCode); + + @override + String toString() => 'PeopleResponse[enabled=$enabled, sidebarWeb=$sidebarWeb]'; + + Map toJson() { + final json = {}; + json[r'enabled'] = this.enabled; + json[r'sidebarWeb'] = this.sidebarWeb; + return json; + } + + /// Returns a new [PeopleResponse] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static PeopleResponse? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + return PeopleResponse( + enabled: mapValueOfType(json, r'enabled')!, + sidebarWeb: mapValueOfType(json, r'sidebarWeb')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = PeopleResponse.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = PeopleResponse.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of PeopleResponse-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = PeopleResponse.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'enabled', + 'sidebarWeb', + }; +} + diff --git a/mobile/openapi/lib/model/people_update.dart b/mobile/openapi/lib/model/people_update.dart new file mode 100644 index 0000000000000..7803e6297036a --- /dev/null +++ b/mobile/openapi/lib/model/people_update.dart @@ -0,0 +1,124 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class PeopleUpdate { + /// Returns a new [PeopleUpdate] instance. + PeopleUpdate({ + this.enabled, + this.sidebarWeb, + }); + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? enabled; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? sidebarWeb; + + @override + bool operator ==(Object other) => identical(this, other) || other is PeopleUpdate && + other.enabled == enabled && + other.sidebarWeb == sidebarWeb; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (enabled == null ? 0 : enabled!.hashCode) + + (sidebarWeb == null ? 0 : sidebarWeb!.hashCode); + + @override + String toString() => 'PeopleUpdate[enabled=$enabled, sidebarWeb=$sidebarWeb]'; + + Map toJson() { + final json = {}; + if (this.enabled != null) { + json[r'enabled'] = this.enabled; + } else { + // json[r'enabled'] = null; + } + if (this.sidebarWeb != null) { + json[r'sidebarWeb'] = this.sidebarWeb; + } else { + // json[r'sidebarWeb'] = null; + } + return json; + } + + /// Returns a new [PeopleUpdate] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static PeopleUpdate? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + return PeopleUpdate( + enabled: mapValueOfType(json, r'enabled'), + sidebarWeb: mapValueOfType(json, r'sidebarWeb'), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = PeopleUpdate.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = PeopleUpdate.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of PeopleUpdate-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = PeopleUpdate.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + }; +} + diff --git a/mobile/openapi/lib/model/rating_response.dart b/mobile/openapi/lib/model/ratings_response.dart similarity index 63% rename from mobile/openapi/lib/model/rating_response.dart rename to mobile/openapi/lib/model/ratings_response.dart index 31505550eff9c..c8791aa91a5ee 100644 --- a/mobile/openapi/lib/model/rating_response.dart +++ b/mobile/openapi/lib/model/ratings_response.dart @@ -10,16 +10,16 @@ part of openapi.api; -class RatingResponse { - /// Returns a new [RatingResponse] instance. - RatingResponse({ +class RatingsResponse { + /// Returns a new [RatingsResponse] instance. + RatingsResponse({ this.enabled = false, }); bool enabled; @override - bool operator ==(Object other) => identical(this, other) || other is RatingResponse && + bool operator ==(Object other) => identical(this, other) || other is RatingsResponse && other.enabled == enabled; @override @@ -28,7 +28,7 @@ class RatingResponse { (enabled.hashCode); @override - String toString() => 'RatingResponse[enabled=$enabled]'; + String toString() => 'RatingsResponse[enabled=$enabled]'; Map toJson() { final json = {}; @@ -36,25 +36,25 @@ class RatingResponse { return json; } - /// Returns a new [RatingResponse] instance and imports its values from + /// Returns a new [RatingsResponse] instance and imports its values from /// [value] if it's a [Map], null otherwise. // ignore: prefer_constructors_over_static_methods - static RatingResponse? fromJson(dynamic value) { + static RatingsResponse? fromJson(dynamic value) { if (value is Map) { final json = value.cast(); - return RatingResponse( + return RatingsResponse( enabled: mapValueOfType(json, r'enabled')!, ); } return null; } - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; if (json is List && json.isNotEmpty) { for (final row in json) { - final value = RatingResponse.fromJson(row); + final value = RatingsResponse.fromJson(row); if (value != null) { result.add(value); } @@ -63,12 +63,12 @@ class RatingResponse { return result.toList(growable: growable); } - static Map mapFromJson(dynamic json) { - final map = {}; + static Map mapFromJson(dynamic json) { + final map = {}; if (json is Map && json.isNotEmpty) { json = json.cast(); // ignore: parameter_assignments for (final entry in json.entries) { - final value = RatingResponse.fromJson(entry.value); + final value = RatingsResponse.fromJson(entry.value); if (value != null) { map[entry.key] = value; } @@ -77,14 +77,14 @@ class RatingResponse { return map; } - // maps a json object with a list of RatingResponse-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = >{}; + // maps a json object with a list of RatingsResponse-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; if (json is Map && json.isNotEmpty) { // ignore: parameter_assignments json = json.cast(); for (final entry in json.entries) { - map[entry.key] = RatingResponse.listFromJson(entry.value, growable: growable,); + map[entry.key] = RatingsResponse.listFromJson(entry.value, growable: growable,); } } return map; diff --git a/mobile/openapi/lib/model/memory_update.dart b/mobile/openapi/lib/model/ratings_update.dart similarity index 69% rename from mobile/openapi/lib/model/memory_update.dart rename to mobile/openapi/lib/model/ratings_update.dart index f2529186c0432..bde51bad1b360 100644 --- a/mobile/openapi/lib/model/memory_update.dart +++ b/mobile/openapi/lib/model/ratings_update.dart @@ -10,9 +10,9 @@ part of openapi.api; -class MemoryUpdate { - /// Returns a new [MemoryUpdate] instance. - MemoryUpdate({ +class RatingsUpdate { + /// Returns a new [RatingsUpdate] instance. + RatingsUpdate({ this.enabled, }); @@ -25,7 +25,7 @@ class MemoryUpdate { bool? enabled; @override - bool operator ==(Object other) => identical(this, other) || other is MemoryUpdate && + bool operator ==(Object other) => identical(this, other) || other is RatingsUpdate && other.enabled == enabled; @override @@ -34,7 +34,7 @@ class MemoryUpdate { (enabled == null ? 0 : enabled!.hashCode); @override - String toString() => 'MemoryUpdate[enabled=$enabled]'; + String toString() => 'RatingsUpdate[enabled=$enabled]'; Map toJson() { final json = {}; @@ -46,25 +46,25 @@ class MemoryUpdate { return json; } - /// Returns a new [MemoryUpdate] instance and imports its values from + /// Returns a new [RatingsUpdate] instance and imports its values from /// [value] if it's a [Map], null otherwise. // ignore: prefer_constructors_over_static_methods - static MemoryUpdate? fromJson(dynamic value) { + static RatingsUpdate? fromJson(dynamic value) { if (value is Map) { final json = value.cast(); - return MemoryUpdate( + return RatingsUpdate( enabled: mapValueOfType(json, r'enabled'), ); } return null; } - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; if (json is List && json.isNotEmpty) { for (final row in json) { - final value = MemoryUpdate.fromJson(row); + final value = RatingsUpdate.fromJson(row); if (value != null) { result.add(value); } @@ -73,12 +73,12 @@ class MemoryUpdate { return result.toList(growable: growable); } - static Map mapFromJson(dynamic json) { - final map = {}; + static Map mapFromJson(dynamic json) { + final map = {}; if (json is Map && json.isNotEmpty) { json = json.cast(); // ignore: parameter_assignments for (final entry in json.entries) { - final value = MemoryUpdate.fromJson(entry.value); + final value = RatingsUpdate.fromJson(entry.value); if (value != null) { map[entry.key] = value; } @@ -87,14 +87,14 @@ class MemoryUpdate { return map; } - // maps a json object with a list of MemoryUpdate-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = >{}; + // maps a json object with a list of RatingsUpdate-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; if (json is Map && json.isNotEmpty) { // ignore: parameter_assignments json = json.cast(); for (final entry in json.entries) { - map[entry.key] = MemoryUpdate.listFromJson(entry.value, growable: growable,); + map[entry.key] = RatingsUpdate.listFromJson(entry.value, growable: growable,); } } return map; diff --git a/mobile/openapi/lib/model/tags_response.dart b/mobile/openapi/lib/model/tags_response.dart new file mode 100644 index 0000000000000..3a5ea3b20b3ec --- /dev/null +++ b/mobile/openapi/lib/model/tags_response.dart @@ -0,0 +1,106 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class TagsResponse { + /// Returns a new [TagsResponse] instance. + TagsResponse({ + this.enabled = true, + this.sidebarWeb = true, + }); + + bool enabled; + + bool sidebarWeb; + + @override + bool operator ==(Object other) => identical(this, other) || other is TagsResponse && + other.enabled == enabled && + other.sidebarWeb == sidebarWeb; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (enabled.hashCode) + + (sidebarWeb.hashCode); + + @override + String toString() => 'TagsResponse[enabled=$enabled, sidebarWeb=$sidebarWeb]'; + + Map toJson() { + final json = {}; + json[r'enabled'] = this.enabled; + json[r'sidebarWeb'] = this.sidebarWeb; + return json; + } + + /// Returns a new [TagsResponse] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static TagsResponse? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + return TagsResponse( + enabled: mapValueOfType(json, r'enabled')!, + sidebarWeb: mapValueOfType(json, r'sidebarWeb')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = TagsResponse.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = TagsResponse.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of TagsResponse-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = TagsResponse.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'enabled', + 'sidebarWeb', + }; +} + diff --git a/mobile/openapi/lib/model/tags_update.dart b/mobile/openapi/lib/model/tags_update.dart new file mode 100644 index 0000000000000..8355b00a00d49 --- /dev/null +++ b/mobile/openapi/lib/model/tags_update.dart @@ -0,0 +1,124 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class TagsUpdate { + /// Returns a new [TagsUpdate] instance. + TagsUpdate({ + this.enabled, + this.sidebarWeb, + }); + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? enabled; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? sidebarWeb; + + @override + bool operator ==(Object other) => identical(this, other) || other is TagsUpdate && + other.enabled == enabled && + other.sidebarWeb == sidebarWeb; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (enabled == null ? 0 : enabled!.hashCode) + + (sidebarWeb == null ? 0 : sidebarWeb!.hashCode); + + @override + String toString() => 'TagsUpdate[enabled=$enabled, sidebarWeb=$sidebarWeb]'; + + Map toJson() { + final json = {}; + if (this.enabled != null) { + json[r'enabled'] = this.enabled; + } else { + // json[r'enabled'] = null; + } + if (this.sidebarWeb != null) { + json[r'sidebarWeb'] = this.sidebarWeb; + } else { + // json[r'sidebarWeb'] = null; + } + return json; + } + + /// Returns a new [TagsUpdate] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static TagsUpdate? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + return TagsUpdate( + enabled: mapValueOfType(json, r'enabled'), + sidebarWeb: mapValueOfType(json, r'sidebarWeb'), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = TagsUpdate.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = TagsUpdate.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of TagsUpdate-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = TagsUpdate.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + }; +} + diff --git a/mobile/openapi/lib/model/user_preferences_response_dto.dart b/mobile/openapi/lib/model/user_preferences_response_dto.dart index 6401a36f9fda2..d3927df8d7ee3 100644 --- a/mobile/openapi/lib/model/user_preferences_response_dto.dart +++ b/mobile/openapi/lib/model/user_preferences_response_dto.dart @@ -16,9 +16,12 @@ class UserPreferencesResponseDto { required this.avatar, required this.download, required this.emailNotifications, + required this.folders, required this.memories, + required this.people, required this.purchase, - required this.rating, + required this.ratings, + required this.tags, }); AvatarResponse avatar; @@ -27,20 +30,29 @@ class UserPreferencesResponseDto { EmailNotificationsResponse emailNotifications; - MemoryResponse memories; + FoldersResponse folders; + + MemoriesResponse memories; + + PeopleResponse people; PurchaseResponse purchase; - RatingResponse rating; + RatingsResponse ratings; + + TagsResponse tags; @override bool operator ==(Object other) => identical(this, other) || other is UserPreferencesResponseDto && other.avatar == avatar && other.download == download && other.emailNotifications == emailNotifications && + other.folders == folders && other.memories == memories && + other.people == people && other.purchase == purchase && - other.rating == rating; + other.ratings == ratings && + other.tags == tags; @override int get hashCode => @@ -48,21 +60,27 @@ class UserPreferencesResponseDto { (avatar.hashCode) + (download.hashCode) + (emailNotifications.hashCode) + + (folders.hashCode) + (memories.hashCode) + + (people.hashCode) + (purchase.hashCode) + - (rating.hashCode); + (ratings.hashCode) + + (tags.hashCode); @override - String toString() => 'UserPreferencesResponseDto[avatar=$avatar, download=$download, emailNotifications=$emailNotifications, memories=$memories, purchase=$purchase, rating=$rating]'; + String toString() => 'UserPreferencesResponseDto[avatar=$avatar, download=$download, emailNotifications=$emailNotifications, folders=$folders, memories=$memories, people=$people, purchase=$purchase, ratings=$ratings, tags=$tags]'; Map toJson() { final json = {}; json[r'avatar'] = this.avatar; json[r'download'] = this.download; json[r'emailNotifications'] = this.emailNotifications; + json[r'folders'] = this.folders; json[r'memories'] = this.memories; + json[r'people'] = this.people; json[r'purchase'] = this.purchase; - json[r'rating'] = this.rating; + json[r'ratings'] = this.ratings; + json[r'tags'] = this.tags; return json; } @@ -77,9 +95,12 @@ class UserPreferencesResponseDto { avatar: AvatarResponse.fromJson(json[r'avatar'])!, download: DownloadResponse.fromJson(json[r'download'])!, emailNotifications: EmailNotificationsResponse.fromJson(json[r'emailNotifications'])!, - memories: MemoryResponse.fromJson(json[r'memories'])!, + folders: FoldersResponse.fromJson(json[r'folders'])!, + memories: MemoriesResponse.fromJson(json[r'memories'])!, + people: PeopleResponse.fromJson(json[r'people'])!, purchase: PurchaseResponse.fromJson(json[r'purchase'])!, - rating: RatingResponse.fromJson(json[r'rating'])!, + ratings: RatingsResponse.fromJson(json[r'ratings'])!, + tags: TagsResponse.fromJson(json[r'tags'])!, ); } return null; @@ -130,9 +151,12 @@ class UserPreferencesResponseDto { 'avatar', 'download', 'emailNotifications', + 'folders', 'memories', + 'people', 'purchase', - 'rating', + 'ratings', + 'tags', }; } diff --git a/mobile/openapi/lib/model/user_preferences_update_dto.dart b/mobile/openapi/lib/model/user_preferences_update_dto.dart index cf55aebf97df7..2841c2f572c11 100644 --- a/mobile/openapi/lib/model/user_preferences_update_dto.dart +++ b/mobile/openapi/lib/model/user_preferences_update_dto.dart @@ -16,9 +16,12 @@ class UserPreferencesUpdateDto { this.avatar, this.download, this.emailNotifications, + this.folders, this.memories, + this.people, this.purchase, - this.rating, + this.ratings, + this.tags, }); /// @@ -51,7 +54,23 @@ class UserPreferencesUpdateDto { /// source code must fall back to having a nullable type. /// Consider adding a "default:" property in the specification file to hide this note. /// - MemoryUpdate? memories; + FoldersUpdate? folders; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + MemoriesUpdate? memories; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + PeopleUpdate? people; /// /// Please note: This property should have been non-nullable! Since the specification file @@ -67,16 +86,27 @@ class UserPreferencesUpdateDto { /// source code must fall back to having a nullable type. /// Consider adding a "default:" property in the specification file to hide this note. /// - RatingUpdate? rating; + RatingsUpdate? ratings; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + TagsUpdate? tags; @override bool operator ==(Object other) => identical(this, other) || other is UserPreferencesUpdateDto && other.avatar == avatar && other.download == download && other.emailNotifications == emailNotifications && + other.folders == folders && other.memories == memories && + other.people == people && other.purchase == purchase && - other.rating == rating; + other.ratings == ratings && + other.tags == tags; @override int get hashCode => @@ -84,12 +114,15 @@ class UserPreferencesUpdateDto { (avatar == null ? 0 : avatar!.hashCode) + (download == null ? 0 : download!.hashCode) + (emailNotifications == null ? 0 : emailNotifications!.hashCode) + + (folders == null ? 0 : folders!.hashCode) + (memories == null ? 0 : memories!.hashCode) + + (people == null ? 0 : people!.hashCode) + (purchase == null ? 0 : purchase!.hashCode) + - (rating == null ? 0 : rating!.hashCode); + (ratings == null ? 0 : ratings!.hashCode) + + (tags == null ? 0 : tags!.hashCode); @override - String toString() => 'UserPreferencesUpdateDto[avatar=$avatar, download=$download, emailNotifications=$emailNotifications, memories=$memories, purchase=$purchase, rating=$rating]'; + String toString() => 'UserPreferencesUpdateDto[avatar=$avatar, download=$download, emailNotifications=$emailNotifications, folders=$folders, memories=$memories, people=$people, purchase=$purchase, ratings=$ratings, tags=$tags]'; Map toJson() { final json = {}; @@ -108,20 +141,35 @@ class UserPreferencesUpdateDto { } else { // json[r'emailNotifications'] = null; } + if (this.folders != null) { + json[r'folders'] = this.folders; + } else { + // json[r'folders'] = null; + } if (this.memories != null) { json[r'memories'] = this.memories; } else { // json[r'memories'] = null; } + if (this.people != null) { + json[r'people'] = this.people; + } else { + // json[r'people'] = null; + } if (this.purchase != null) { json[r'purchase'] = this.purchase; } else { // json[r'purchase'] = null; } - if (this.rating != null) { - json[r'rating'] = this.rating; + if (this.ratings != null) { + json[r'ratings'] = this.ratings; } else { - // json[r'rating'] = null; + // json[r'ratings'] = null; + } + if (this.tags != null) { + json[r'tags'] = this.tags; + } else { + // json[r'tags'] = null; } return json; } @@ -137,9 +185,12 @@ class UserPreferencesUpdateDto { avatar: AvatarUpdate.fromJson(json[r'avatar']), download: DownloadUpdate.fromJson(json[r'download']), emailNotifications: EmailNotificationsUpdate.fromJson(json[r'emailNotifications']), - memories: MemoryUpdate.fromJson(json[r'memories']), + folders: FoldersUpdate.fromJson(json[r'folders']), + memories: MemoriesUpdate.fromJson(json[r'memories']), + people: PeopleUpdate.fromJson(json[r'people']), purchase: PurchaseUpdate.fromJson(json[r'purchase']), - rating: RatingUpdate.fromJson(json[r'rating']), + ratings: RatingsUpdate.fromJson(json[r'ratings']), + tags: TagsUpdate.fromJson(json[r'tags']), ); } return null; diff --git a/mobile/test/modules/utils/openapi_patching_test.dart b/mobile/test/modules/utils/openapi_patching_test.dart new file mode 100644 index 0000000000000..b956c4bfb9d80 --- /dev/null +++ b/mobile/test/modules/utils/openapi_patching_test.dart @@ -0,0 +1,49 @@ +import 'dart:convert'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:openapi/api.dart'; +import 'package:immich_mobile/utils/openapi_patching.dart'; + +void main() { + group('Test OpenApi Patching', () { + test('upgradeDto', () { + dynamic value; + String targetType; + + targetType = 'UserPreferencesResponseDto'; + value = jsonDecode(""" +{ + "download": { + "archiveSize": 4294967296, + "includeEmbeddedVideos": false + } +} +"""); + + upgradeDto(value, targetType); + expect(value['tags'], TagsResponse().toJson()); + expect(value['download']['includeEmbeddedVideos'], false); + }); + + test('addDefault', () { + dynamic value = jsonDecode(""" +{ + "download": { + "archiveSize": 4294967296, + "includeEmbeddedVideos": false + } +} +"""); + String keys = 'download.unknownKey'; + dynamic defaultValue = 69420; + + addDefault(value, keys, defaultValue); + expect(value['download']['unknownKey'], 69420); + + keys = 'alpha.beta'; + defaultValue = 'gamma'; + addDefault(value, keys, defaultValue); + expect(value['alpha']['beta'], 'gamma'); + }); + }); +} diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 4d80353177d36..1ca112bf268a5 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -9164,6 +9164,34 @@ ], "type": "object" }, + "FoldersResponse": { + "properties": { + "enabled": { + "default": false, + "type": "boolean" + }, + "sidebarWeb": { + "default": false, + "type": "boolean" + } + }, + "required": [ + "enabled", + "sidebarWeb" + ], + "type": "object" + }, + "FoldersUpdate": { + "properties": { + "enabled": { + "type": "boolean" + }, + "sidebarWeb": { + "type": "boolean" + } + }, + "type": "object" + }, "ImageFormat": { "enum": [ "jpeg", @@ -9534,6 +9562,26 @@ ], "type": "string" }, + "MemoriesResponse": { + "properties": { + "enabled": { + "default": true, + "type": "boolean" + } + }, + "required": [ + "enabled" + ], + "type": "object" + }, + "MemoriesUpdate": { + "properties": { + "enabled": { + "type": "boolean" + } + }, + "type": "object" + }, "MemoryCreateDto": { "properties": { "assetIds": { @@ -9586,17 +9634,6 @@ ], "type": "object" }, - "MemoryResponse": { - "properties": { - "enabled": { - "type": "boolean" - } - }, - "required": [ - "enabled" - ], - "type": "object" - }, "MemoryResponseDto": { "properties": { "assets": { @@ -9660,14 +9697,6 @@ ], "type": "string" }, - "MemoryUpdate": { - "properties": { - "enabled": { - "type": "boolean" - } - }, - "type": "object" - }, "MemoryUpdateDto": { "properties": { "isSaved": { @@ -9953,6 +9982,23 @@ ], "type": "string" }, + "PeopleResponse": { + "properties": { + "enabled": { + "default": true, + "type": "boolean" + }, + "sidebarWeb": { + "default": false, + "type": "boolean" + } + }, + "required": [ + "enabled", + "sidebarWeb" + ], + "type": "object" + }, "PeopleResponseDto": { "properties": { "hasNextPage": { @@ -9979,6 +10025,17 @@ ], "type": "object" }, + "PeopleUpdate": { + "properties": { + "enabled": { + "type": "boolean" + }, + "sidebarWeb": { + "type": "boolean" + } + }, + "type": "object" + }, "PeopleUpdateDto": { "properties": { "people": { @@ -10300,7 +10357,7 @@ ], "type": "object" }, - "RatingResponse": { + "RatingsResponse": { "properties": { "enabled": { "default": false, @@ -10312,7 +10369,7 @@ ], "type": "object" }, - "RatingUpdate": { + "RatingsUpdate": { "properties": { "enabled": { "type": "boolean" @@ -12002,6 +12059,34 @@ ], "type": "object" }, + "TagsResponse": { + "properties": { + "enabled": { + "default": true, + "type": "boolean" + }, + "sidebarWeb": { + "default": true, + "type": "boolean" + } + }, + "required": [ + "enabled", + "sidebarWeb" + ], + "type": "object" + }, + "TagsUpdate": { + "properties": { + "enabled": { + "type": "boolean" + }, + "sidebarWeb": { + "type": "boolean" + } + }, + "type": "object" + }, "TimeBucketResponseDto": { "properties": { "count": { @@ -12379,23 +12464,35 @@ "emailNotifications": { "$ref": "#/components/schemas/EmailNotificationsResponse" }, + "folders": { + "$ref": "#/components/schemas/FoldersResponse" + }, "memories": { - "$ref": "#/components/schemas/MemoryResponse" + "$ref": "#/components/schemas/MemoriesResponse" + }, + "people": { + "$ref": "#/components/schemas/PeopleResponse" }, "purchase": { "$ref": "#/components/schemas/PurchaseResponse" }, - "rating": { - "$ref": "#/components/schemas/RatingResponse" + "ratings": { + "$ref": "#/components/schemas/RatingsResponse" + }, + "tags": { + "$ref": "#/components/schemas/TagsResponse" } }, "required": [ "avatar", "download", "emailNotifications", + "folders", "memories", + "people", "purchase", - "rating" + "ratings", + "tags" ], "type": "object" }, @@ -12410,14 +12507,23 @@ "emailNotifications": { "$ref": "#/components/schemas/EmailNotificationsUpdate" }, + "folders": { + "$ref": "#/components/schemas/FoldersUpdate" + }, "memories": { - "$ref": "#/components/schemas/MemoryUpdate" + "$ref": "#/components/schemas/MemoriesUpdate" + }, + "people": { + "$ref": "#/components/schemas/PeopleUpdate" }, "purchase": { "$ref": "#/components/schemas/PurchaseUpdate" }, - "rating": { - "$ref": "#/components/schemas/RatingUpdate" + "ratings": { + "$ref": "#/components/schemas/RatingsUpdate" + }, + "tags": { + "$ref": "#/components/schemas/TagsUpdate" } }, "type": "object" diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 3fdcf33757932..bad370ecfe858 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -93,23 +93,38 @@ export type EmailNotificationsResponse = { albumUpdate: boolean; enabled: boolean; }; -export type MemoryResponse = { +export type FoldersResponse = { enabled: boolean; + sidebarWeb: boolean; +}; +export type MemoriesResponse = { + enabled: boolean; +}; +export type PeopleResponse = { + enabled: boolean; + sidebarWeb: boolean; }; export type PurchaseResponse = { hideBuyButtonUntil: string; showSupportBadge: boolean; }; -export type RatingResponse = { +export type RatingsResponse = { enabled: boolean; }; +export type TagsResponse = { + enabled: boolean; + sidebarWeb: boolean; +}; export type UserPreferencesResponseDto = { avatar: AvatarResponse; download: DownloadResponse; emailNotifications: EmailNotificationsResponse; - memories: MemoryResponse; + folders: FoldersResponse; + memories: MemoriesResponse; + people: PeopleResponse; purchase: PurchaseResponse; - rating: RatingResponse; + ratings: RatingsResponse; + tags: TagsResponse; }; export type AvatarUpdate = { color?: UserAvatarColor; @@ -123,23 +138,38 @@ export type EmailNotificationsUpdate = { albumUpdate?: boolean; enabled?: boolean; }; -export type MemoryUpdate = { +export type FoldersUpdate = { enabled?: boolean; + sidebarWeb?: boolean; +}; +export type MemoriesUpdate = { + enabled?: boolean; +}; +export type PeopleUpdate = { + enabled?: boolean; + sidebarWeb?: boolean; }; export type PurchaseUpdate = { hideBuyButtonUntil?: string; showSupportBadge?: boolean; }; -export type RatingUpdate = { +export type RatingsUpdate = { enabled?: boolean; }; +export type TagsUpdate = { + enabled?: boolean; + sidebarWeb?: boolean; +}; export type UserPreferencesUpdateDto = { avatar?: AvatarUpdate; download?: DownloadUpdate; emailNotifications?: EmailNotificationsUpdate; - memories?: MemoryUpdate; + folders?: FoldersUpdate; + memories?: MemoriesUpdate; + people?: PeopleUpdate; purchase?: PurchaseUpdate; - rating?: RatingUpdate; + ratings?: RatingsUpdate; + tags?: TagsUpdate; }; export type AlbumUserResponseDto = { role: AlbumUserRole; diff --git a/server/src/dtos/user-preferences.dto.ts b/server/src/dtos/user-preferences.dto.ts index 7ccf6cd78bbb3..8de7021eaf3c5 100644 --- a/server/src/dtos/user-preferences.dto.ts +++ b/server/src/dtos/user-preferences.dto.ts @@ -12,16 +12,40 @@ class AvatarUpdate { color?: UserAvatarColor; } -class MemoryUpdate { +class MemoriesUpdate { @ValidateBoolean({ optional: true }) enabled?: boolean; } -class RatingUpdate { +class RatingsUpdate { @ValidateBoolean({ optional: true }) enabled?: boolean; } +class FoldersUpdate { + @ValidateBoolean({ optional: true }) + enabled?: boolean; + + @ValidateBoolean({ optional: true }) + sidebarWeb?: boolean; +} + +class PeopleUpdate { + @ValidateBoolean({ optional: true }) + enabled?: boolean; + + @ValidateBoolean({ optional: true }) + sidebarWeb?: boolean; +} + +class TagsUpdate { + @ValidateBoolean({ optional: true }) + enabled?: boolean; + + @ValidateBoolean({ optional: true }) + sidebarWeb?: boolean; +} + class EmailNotificationsUpdate { @ValidateBoolean({ optional: true }) enabled?: boolean; @@ -56,19 +80,34 @@ class PurchaseUpdate { export class UserPreferencesUpdateDto { @Optional() @ValidateNested() - @Type(() => RatingUpdate) - rating?: RatingUpdate; + @Type(() => FoldersUpdate) + folders?: FoldersUpdate; + + @Optional() + @ValidateNested() + @Type(() => MemoriesUpdate) + memories?: MemoriesUpdate; + + @Optional() + @ValidateNested() + @Type(() => PeopleUpdate) + people?: PeopleUpdate; + + @Optional() + @ValidateNested() + @Type(() => RatingsUpdate) + ratings?: RatingsUpdate; + + @Optional() + @ValidateNested() + @Type(() => TagsUpdate) + tags?: TagsUpdate; @Optional() @ValidateNested() @Type(() => AvatarUpdate) avatar?: AvatarUpdate; - @Optional() - @ValidateNested() - @Type(() => MemoryUpdate) - memories?: MemoryUpdate; - @Optional() @ValidateNested() @Type(() => EmailNotificationsUpdate) @@ -90,12 +129,27 @@ class AvatarResponse { color!: UserAvatarColor; } -class RatingResponse { +class RatingsResponse { enabled: boolean = false; } -class MemoryResponse { - enabled!: boolean; +class MemoriesResponse { + enabled: boolean = true; +} + +class FoldersResponse { + enabled: boolean = false; + sidebarWeb: boolean = false; +} + +class PeopleResponse { + enabled: boolean = true; + sidebarWeb: boolean = false; +} + +class TagsResponse { + enabled: boolean = true; + sidebarWeb: boolean = true; } class EmailNotificationsResponse { @@ -117,8 +171,11 @@ class PurchaseResponse { } export class UserPreferencesResponseDto implements UserPreferences { - rating!: RatingResponse; - memories!: MemoryResponse; + folders!: FoldersResponse; + memories!: MemoriesResponse; + people!: PeopleResponse; + ratings!: RatingsResponse; + tags!: TagsResponse; avatar!: AvatarResponse; emailNotifications!: EmailNotificationsResponse; download!: DownloadResponse; diff --git a/server/src/entities/user-metadata.entity.ts b/server/src/entities/user-metadata.entity.ts index eadcdeec57eb0..c342cb71f8ae2 100644 --- a/server/src/entities/user-metadata.entity.ts +++ b/server/src/entities/user-metadata.entity.ts @@ -19,12 +19,24 @@ export class UserMetadataEntity } export interface UserPreferences { - rating: { + folders: { enabled: boolean; + sidebarWeb: boolean; }; memories: { enabled: boolean; }; + people: { + enabled: boolean; + sidebarWeb: boolean; + }; + ratings: { + enabled: boolean; + }; + tags: { + enabled: boolean; + sidebarWeb: boolean; + }; avatar: { color: UserAvatarColor; }; @@ -50,12 +62,24 @@ export const getDefaultPreferences = (user: { email: string }): UserPreferences ); return { - rating: { + folders: { enabled: false, + sidebarWeb: false, }, memories: { enabled: true, }, + people: { + enabled: true, + sidebarWeb: false, + }, + ratings: { + enabled: false, + }, + tags: { + enabled: false, + sidebarWeb: false, + }, avatar: { color: values[randomIndex], }, diff --git a/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte b/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte index 8b18d14f03d52..b73fe7171619e 100644 --- a/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte @@ -20,7 +20,7 @@ }; -{#if !isSharedLink() && $preferences?.rating?.enabled} +{#if !isSharedLink() && $preferences?.ratings.enabled}
handlePromiseError(handleChangeRating(rating))} />
diff --git a/web/src/lib/components/asset-viewer/detail-panel.svelte b/web/src/lib/components/asset-viewer/detail-panel.svelte index 0a105430cc879..5ffc5120b6cbc 100644 --- a/web/src/lib/components/asset-viewer/detail-panel.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel.svelte @@ -6,7 +6,7 @@ import { boundingBoxesArray } from '$lib/stores/people.store'; import { locale } from '$lib/stores/preferences.store'; import { featureFlags } from '$lib/stores/server-config.store'; - import { user } from '$lib/stores/user.store'; + import { preferences, user } from '$lib/stores/user.store'; import { getAssetThumbnailUrl, getPeopleThumbnailUrl, handlePromiseError, isSharedLink } from '$lib/utils'; import { delay, isFlipped } from '$lib/utils/asset-utils'; import { @@ -502,9 +502,11 @@
{/if} -
- -
+{#if $preferences?.tags?.enabled} +
+ +
+{/if} {#if showEditFaces} { - accordionElement.scrollIntoView({ - behavior: 'smooth', - block: 'start', - }); - }, 200); + if (autoScrollTo) { + setTimeout(() => { + accordionElement.scrollIntoView({ + behavior: 'smooth', + block: 'start', + }); + }, 200); + } } else { $accordionState.delete(key); $accordionState = $accordionState; @@ -72,7 +75,7 @@ {#if isOpen} -
    +
    {/if} diff --git a/web/src/lib/components/shared-components/side-bar/side-bar.svelte b/web/src/lib/components/shared-components/side-bar/side-bar.svelte index dd777d12596a5..fab7c6ed6dce9 100644 --- a/web/src/lib/components/shared-components/side-bar/side-bar.svelte +++ b/web/src/lib/components/shared-components/side-bar/side-bar.svelte @@ -1,5 +1,4 @@
    @@ -189,29 +169,6 @@ bind:checked={$showDeleteModal} />
- -
- -
-
- -
-
- handleRatingChange(enabled)} - /> -
diff --git a/web/src/lib/components/user-settings-page/feature-settings.svelte b/web/src/lib/components/user-settings-page/feature-settings.svelte new file mode 100644 index 0000000000000..dc11dab15e8d0 --- /dev/null +++ b/web/src/lib/components/user-settings-page/feature-settings.svelte @@ -0,0 +1,124 @@ + + +
+
+
+
+ +
+ +
+ + {#if foldersEnabled} +
+ +
+ {/if} +
+ + +
+ +
+
+ + +
+ +
+ + {#if peopleEnabled} +
+ +
+ {/if} +
+ + +
+ +
+
+ + +
+ +
+ {#if tagsEnabled} +
+ +
+ {/if} +
+ +
+ +
+
+
+
+
diff --git a/web/src/lib/components/user-settings-page/memories-settings.svelte b/web/src/lib/components/user-settings-page/memories-settings.svelte deleted file mode 100644 index e8a58bf01651b..0000000000000 --- a/web/src/lib/components/user-settings-page/memories-settings.svelte +++ /dev/null @@ -1,46 +0,0 @@ - - -
-
-
-
-
- -
-
- -
-
-
-
-
diff --git a/web/src/lib/components/user-settings-page/user-settings-list.svelte b/web/src/lib/components/user-settings-page/user-settings-list.svelte index df32126a2d47a..596efaedef86d 100644 --- a/web/src/lib/components/user-settings-page/user-settings-list.svelte +++ b/web/src/lib/components/user-settings-page/user-settings-list.svelte @@ -10,7 +10,6 @@ import AppSettings from './app-settings.svelte'; import ChangePasswordSettings from './change-password-settings.svelte'; import DeviceList from './device-list.svelte'; - import MemoriesSettings from './memories-settings.svelte'; import OAuthSettings from './oauth-settings.svelte'; import PartnerSettings from './partner-settings.svelte'; import UserAPIKeyList from './user-api-key-list.svelte'; @@ -19,6 +18,7 @@ import { t } from 'svelte-i18n'; import DownloadSettings from '$lib/components/user-settings-page/download-settings.svelte'; import UserPurchaseSettings from '$lib/components/user-settings-page/user-purchase-settings.svelte'; + import FeatureSettings from '$lib/components/user-settings-page/feature-settings.svelte'; export let keys: ApiKeyResponseDto[] = []; export let sessions: SessionResponseDto[] = []; @@ -53,8 +53,8 @@ - - + + @@ -84,6 +84,7 @@ key="user-purchase-settings" title={$t('user_purchase_settings')} subtitle={$t('user_purchase_settings_description')} + autoScrollTo={true} > diff --git a/web/src/lib/i18n/en.json b/web/src/lib/i18n/en.json index 684cb0e319ec3..dcefccf2ef0d9 100644 --- a/web/src/lib/i18n/en.json +++ b/web/src/lib/i18n/en.json @@ -701,6 +701,8 @@ "favorite_or_unfavorite_photo": "Favorite or unfavorite photo", "favorites": "Favorites", "feature_photo_updated": "Feature photo updated", + "features": "Features", + "features_setting_description": "Manage the app features", "file_name": "File name", "file_name_or_extension": "File name or extension", "filename": "Filename", @@ -709,6 +711,7 @@ "find_them_fast": "Find them fast by name with search", "fix_incorrect_match": "Fix incorrect match", "folders": "Folders", + "folders_feature_description": "Browsing the folder view for the photos and videos on the file system", "force_re-scan_library_files": "Force Re-scan All Library Files", "forward": "Forward", "general": "General", @@ -912,6 +915,7 @@ "pending": "Pending", "people": "People", "people_edits_count": "Edited {count, plural, one {# person} other {# people}}", + "people_feature_description": "Browsing photos and videos grouped by people", "people_sidebar_description": "Display a link to People in the sidebar", "permanent_deletion_warning": "Permanent deletion warning", "permanent_deletion_warning_setting_description": "Show a warning when permanently deleting assets", @@ -981,7 +985,7 @@ "rating": "Star rating", "rating_clear": "Clear rating", "rating_count": "{count, plural, one {# star} other {# stars}}", - "rating_description": "Display the exif rating in the info panel", + "rating_description": "Display the EXIF rating in the info panel", "reaction_options": "Reaction options", "read_changelog": "Read Changelog", "reassign": "Reassign", @@ -1130,6 +1134,8 @@ "show_supporter_badge": "Supporter badge", "show_supporter_badge_description": "Show a supporter badge", "shuffle": "Shuffle", + "sidebar": "Sidebar", + "sidebar_display_description": "Display a link to the view in the sidebar", "sign_out": "Sign Out", "sign_up": "Sign up", "size": "Size", @@ -1169,6 +1175,7 @@ "tag": "Tag", "tag_assets": "Tag assets", "tag_created": "Created tag: {tag}", + "tag_feature_description": "Browsing photos and videos grouped by logical tag topics", "tag_updated": "Updated tag: {tag}", "tagged_assets": "Tagged {count, plural, one {# asset} other {# assets}}", "tags": "Tags", diff --git a/web/src/lib/stores/preferences.store.ts b/web/src/lib/stores/preferences.store.ts index 11473f80612a6..de80702b95406 100644 --- a/web/src/lib/stores/preferences.store.ts +++ b/web/src/lib/stores/preferences.store.ts @@ -96,11 +96,6 @@ export interface SidebarSettings { sharing: boolean; } -export const sidebarSettings = persisted('sidebar-settings-1', { - people: false, - sharing: true, -}); - export enum SortOrder { Asc = 'asc', Desc = 'desc', diff --git a/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte index e15c20cbbe8d7..70e74f84f17b3 100644 --- a/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte @@ -81,7 +81,9 @@ assetStore.removeAssets(assetIds)} /> - + {#if $preferences.tags.enabled} + + {/if} assetStore.removeAssets(assetIds)} />
From 6fe011e2d791c9e4c37c6ff4b86f71068bdac577 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 29 Aug 2024 16:14:52 -0500 Subject: [PATCH 060/160] feat(web): jump to timeline (#12117) * feat(web): jump to timeline * Update web/src/lib/components/memory-page/memory-viewer.svelte Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> * wording and open in new tab * Use correct wording and icon * fix: hide on archived and trashed assets --------- Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Co-authored-by: Jason Rasmussen --- .../asset-viewer/asset-viewer-nav-bar.svelte | 10 ++++++++++ .../components/memory-page/memory-viewer.svelte | 14 ++++++++++++++ web/src/lib/i18n/en.json | 1 + 3 files changed, 25 insertions(+) diff --git a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte index a57a7faef8a51..0f75f9bb830f4 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte @@ -1,4 +1,5 @@ +
+

+ + + {message} + + +

+
here", "tag_updated": "Updated tag: {tag}", "tagged_assets": "Tagged {count, plural, one {# asset} other {# assets}}", "tags": "Tags", From b9e5e40ced02b74784dc45c0101e1f21cf0f7a7c Mon Sep 17 00:00:00 2001 From: Pierre Couy Date: Fri, 30 Aug 2024 18:26:31 +0200 Subject: [PATCH 074/160] docs(guide): nginx caching proxy (#12140) * docs:Add link to nginx caching proxy guide Following comments on https://github.com/immich-app/immich/pull/11350 * docs:Fix typo * docs:Fix typo * docs:Switch to GitHub link --- docs/src/components/community-guides.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/src/components/community-guides.tsx b/docs/src/components/community-guides.tsx index 1c1ad7cabdaa8..6982853fade77 100644 --- a/docs/src/components/community-guides.tsx +++ b/docs/src/components/community-guides.tsx @@ -43,6 +43,11 @@ const guides: CommunityGuidesProps[] = [ description: 'Access your local Immich installation over the internet using your own domain', url: 'https://github.com/ppr88/immich-guides/blob/main/open-immich-custom-domain.md', }, + { + title: 'Nginx caching map server', + description: 'Increase privacy by using nginx as a caching proxy in front of a map tile server', + url: 'https://github.com/pcouy/pcouy.github.io/blob/main/_posts/2024-08-30-proxying-a-map-tile-server-for-increased-privacy.md', + }, ]; function CommunityGuide({ title, description, url }: CommunityGuidesProps): JSX.Element { From 9b1a985d29fe6866b06a7c06e2ccf0746953d2d5 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Fri, 30 Aug 2024 12:44:24 -0400 Subject: [PATCH 075/160] fix(server): tag upsert (#12141) --- e2e/src/api/specs/tag.e2e-spec.ts | 58 ++++++++++++++++--- .../openapi/lib/model/tag_response_dto.dart | 19 +++++- open-api/immich-openapi-specs.json | 3 + open-api/typescript-sdk/src/fetch-client.ts | 1 + server/src/dtos/tag.dto.ts | 2 + server/src/entities/tag.entity.ts | 7 ++- server/src/interfaces/tag.interface.ts | 1 + .../1725023079109-FixTagUniqueness.ts | 16 +++++ server/src/queries/asset.repository.sql | 2 +- server/src/repositories/tag.repository.ts | 42 ++++++++++++++ server/src/services/metadata.service.spec.ts | 31 +++++----- server/src/services/tag.service.spec.ts | 14 ++--- server/src/utils/tag.ts | 7 +-- .../test/repositories/tag.repository.mock.ts | 1 + 14 files changed, 163 insertions(+), 41 deletions(-) create mode 100644 server/src/migrations/1725023079109-FixTagUniqueness.ts diff --git a/e2e/src/api/specs/tag.e2e-spec.ts b/e2e/src/api/specs/tag.e2e-spec.ts index 0a26ccef0eced..a4cbc99ed3bc7 100644 --- a/e2e/src/api/specs/tag.e2e-spec.ts +++ b/e2e/src/api/specs/tag.e2e-spec.ts @@ -3,6 +3,7 @@ import { LoginResponseDto, Permission, TagCreateDto, + TagResponseDto, createTag, getAllTags, tagAssets, @@ -81,15 +82,31 @@ describe('/tags', () => { expect(status).toBe(201); }); + it('should allow multiple users to create tags with the same value', async () => { + await create(admin.accessToken, { name: 'TagA' }); + const { status, body } = await request(app) + .post('/tags') + .set('Authorization', `Bearer ${user.accessToken}`) + .send({ name: 'TagA' }); + expect(body).toEqual({ + id: expect.any(String), + name: 'TagA', + value: 'TagA', + createdAt: expect.any(String), + updatedAt: expect.any(String), + }); + expect(status).toBe(201); + }); + it('should create a nested tag', async () => { const parent = await create(admin.accessToken, { name: 'TagA' }); - const { status, body } = await request(app) .post('/tags') .set('Authorization', `Bearer ${admin.accessToken}`) .send({ name: 'TagB', parentId: parent.id }); expect(body).toEqual({ id: expect.any(String), + parentId: parent.id, name: 'TagB', value: 'TagA/TagB', createdAt: expect.any(String), @@ -134,14 +151,20 @@ describe('/tags', () => { it('should return a nested tags', async () => { await upsert(admin.accessToken, ['TagA/TagB/TagC', 'TagD']); const { status, body } = await request(app).get('/tags').set('Authorization', `Bearer ${admin.accessToken}`); + expect(body).toHaveLength(4); - expect(body).toEqual([ - expect.objectContaining({ name: 'TagA', value: 'TagA' }), - expect.objectContaining({ name: 'TagB', value: 'TagA/TagB' }), - expect.objectContaining({ name: 'TagC', value: 'TagA/TagB/TagC' }), - expect.objectContaining({ name: 'TagD', value: 'TagD' }), - ]); expect(status).toEqual(200); + + const tags = body as TagResponseDto[]; + const tagA = tags.find((tag) => tag.value === 'TagA') as TagResponseDto; + const tagB = tags.find((tag) => tag.value === 'TagA/TagB') as TagResponseDto; + const tagC = tags.find((tag) => tag.value === 'TagA/TagB/TagC') as TagResponseDto; + const tagD = tags.find((tag) => tag.value === 'TagD') as TagResponseDto; + + expect(tagA).toEqual(expect.objectContaining({ name: 'TagA', value: 'TagA' })); + expect(tagB).toEqual(expect.objectContaining({ name: 'TagB', value: 'TagA/TagB', parentId: tagA.id })); + expect(tagC).toEqual(expect.objectContaining({ name: 'TagC', value: 'TagA/TagB/TagC', parentId: tagB.id })); + expect(tagD).toEqual(expect.objectContaining({ name: 'TagD', value: 'TagD' })); }); }); @@ -167,6 +190,26 @@ describe('/tags', () => { expect(status).toBe(200); expect(body).toEqual([expect.objectContaining({ name: 'TagD', value: 'TagA/TagB/TagC/TagD' })]); }); + + it('should upsert tags in parallel without conflicts', async () => { + const [[tag1], [tag2], [tag3], [tag4]] = await Promise.all([ + upsert(admin.accessToken, ['TagA/TagB/TagC/TagD']), + upsert(admin.accessToken, ['TagA/TagB/TagC/TagD']), + upsert(admin.accessToken, ['TagA/TagB/TagC/TagD']), + upsert(admin.accessToken, ['TagA/TagB/TagC/TagD']), + ]); + + const { id, parentId, createdAt } = tag1; + for (const tag of [tag1, tag2, tag3, tag4]) { + expect(tag).toMatchObject({ + id, + parentId, + createdAt, + name: 'TagD', + value: 'TagA/TagB/TagC/TagD', + }); + } + }); }); describe('PUT /tags/assets', () => { @@ -296,6 +339,7 @@ describe('/tags', () => { expect(status).toBe(200); expect(body).toEqual({ id: expect.any(String), + parentId: tagC.id, name: 'TagD', value: 'TagA/TagB/TagC/TagD', createdAt: expect.any(String), diff --git a/mobile/openapi/lib/model/tag_response_dto.dart b/mobile/openapi/lib/model/tag_response_dto.dart index 4f0a62a8b9669..1d1a88c3cff29 100644 --- a/mobile/openapi/lib/model/tag_response_dto.dart +++ b/mobile/openapi/lib/model/tag_response_dto.dart @@ -17,6 +17,7 @@ class TagResponseDto { required this.createdAt, required this.id, required this.name, + this.parentId, required this.updatedAt, required this.value, }); @@ -35,6 +36,14 @@ class TagResponseDto { String name; + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? parentId; + DateTime updatedAt; String value; @@ -45,6 +54,7 @@ class TagResponseDto { other.createdAt == createdAt && other.id == id && other.name == name && + other.parentId == parentId && other.updatedAt == updatedAt && other.value == value; @@ -55,11 +65,12 @@ class TagResponseDto { (createdAt.hashCode) + (id.hashCode) + (name.hashCode) + + (parentId == null ? 0 : parentId!.hashCode) + (updatedAt.hashCode) + (value.hashCode); @override - String toString() => 'TagResponseDto[color=$color, createdAt=$createdAt, id=$id, name=$name, updatedAt=$updatedAt, value=$value]'; + String toString() => 'TagResponseDto[color=$color, createdAt=$createdAt, id=$id, name=$name, parentId=$parentId, updatedAt=$updatedAt, value=$value]'; Map toJson() { final json = {}; @@ -71,6 +82,11 @@ class TagResponseDto { json[r'createdAt'] = this.createdAt.toUtc().toIso8601String(); json[r'id'] = this.id; json[r'name'] = this.name; + if (this.parentId != null) { + json[r'parentId'] = this.parentId; + } else { + // json[r'parentId'] = null; + } json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String(); json[r'value'] = this.value; return json; @@ -88,6 +104,7 @@ class TagResponseDto { createdAt: mapDateTime(json, r'createdAt', r'')!, id: mapValueOfType(json, r'id')!, name: mapValueOfType(json, r'name')!, + parentId: mapValueOfType(json, r'parentId'), updatedAt: mapDateTime(json, r'updatedAt', r'')!, value: mapValueOfType(json, r'value')!, ); diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 50bd57b527e2d..97a31ead266c5 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -12024,6 +12024,9 @@ "name": { "type": "string" }, + "parentId": { + "type": "string" + }, "updatedAt": { "format": "date-time", "type": "string" diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 0bd67c231e02e..2c336f98be7b1 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -232,6 +232,7 @@ export type TagResponseDto = { createdAt: string; id: string; name: string; + parentId?: string; updatedAt: string; value: string; }; diff --git a/server/src/dtos/tag.dto.ts b/server/src/dtos/tag.dto.ts index 40c5b176ffc3a..cff11962d744a 100644 --- a/server/src/dtos/tag.dto.ts +++ b/server/src/dtos/tag.dto.ts @@ -45,6 +45,7 @@ export class TagBulkAssetsResponseDto { export class TagResponseDto { id!: string; + parentId?: string; name!: string; value!: string; createdAt!: Date; @@ -55,6 +56,7 @@ export class TagResponseDto { export function mapTag(entity: TagEntity): TagResponseDto { return { id: entity.id, + parentId: entity.parentId ?? undefined, name: entity.value.split('/').at(-1) as string, value: entity.value, createdAt: entity.createdAt, diff --git a/server/src/entities/tag.entity.ts b/server/src/entities/tag.entity.ts index 940b446aeafcc..ebcc6853c9bbd 100644 --- a/server/src/entities/tag.entity.ts +++ b/server/src/entities/tag.entity.ts @@ -10,16 +10,18 @@ import { Tree, TreeChildren, TreeParent, + Unique, UpdateDateColumn, } from 'typeorm'; @Entity('tags') +@Unique(['userId', 'value']) @Tree('closure-table') export class TagEntity { @PrimaryGeneratedColumn('uuid') id!: string; - @Column({ unique: true }) + @Column() value!: string; @CreateDateColumn({ type: 'timestamptz' }) @@ -31,6 +33,9 @@ export class TagEntity { @Column({ type: 'varchar', nullable: true, default: null }) color!: string | null; + @Column({ nullable: true }) + parentId?: string; + @TreeParent({ onDelete: 'CASCADE' }) parent?: TagEntity; diff --git a/server/src/interfaces/tag.interface.ts b/server/src/interfaces/tag.interface.ts index f9f3784f065d3..aca9c223d552b 100644 --- a/server/src/interfaces/tag.interface.ts +++ b/server/src/interfaces/tag.interface.ts @@ -8,6 +8,7 @@ export type AssetTagItem = { assetId: string; tagId: string }; export interface ITagRepository extends IBulkAsset { getAll(userId: string): Promise; getByValue(userId: string, value: string): Promise; + upsertValue(request: { userId: string; value: string; parent?: TagEntity }): Promise; create(tag: Partial): Promise; get(id: string): Promise; diff --git a/server/src/migrations/1725023079109-FixTagUniqueness.ts b/server/src/migrations/1725023079109-FixTagUniqueness.ts new file mode 100644 index 0000000000000..859712621c1a9 --- /dev/null +++ b/server/src/migrations/1725023079109-FixTagUniqueness.ts @@ -0,0 +1,16 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class FixTagUniqueness1725023079109 implements MigrationInterface { + name = 'FixTagUniqueness1725023079109' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451"`); + await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_79d6f16e52bb2c7130375246793" UNIQUE ("userId", "value")`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_79d6f16e52bb2c7130375246793"`); + await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451" UNIQUE ("value")`); + } + +} diff --git a/server/src/queries/asset.repository.sql b/server/src/queries/asset.repository.sql index ba52f7d1481c1..3852439936d83 100644 --- a/server/src/queries/asset.repository.sql +++ b/server/src/queries/asset.repository.sql @@ -188,8 +188,8 @@ SELECT "AssetEntity__AssetEntity_tags"."createdAt" AS "AssetEntity__AssetEntity_tags_createdAt", "AssetEntity__AssetEntity_tags"."updatedAt" AS "AssetEntity__AssetEntity_tags_updatedAt", "AssetEntity__AssetEntity_tags"."color" AS "AssetEntity__AssetEntity_tags_color", - "AssetEntity__AssetEntity_tags"."userId" AS "AssetEntity__AssetEntity_tags_userId", "AssetEntity__AssetEntity_tags"."parentId" AS "AssetEntity__AssetEntity_tags_parentId", + "AssetEntity__AssetEntity_tags"."userId" AS "AssetEntity__AssetEntity_tags_userId", "AssetEntity__AssetEntity_faces"."id" AS "AssetEntity__AssetEntity_faces_id", "AssetEntity__AssetEntity_faces"."assetId" AS "AssetEntity__AssetEntity_faces_assetId", "AssetEntity__AssetEntity_faces"."personId" AS "AssetEntity__AssetEntity_faces_personId", diff --git a/server/src/repositories/tag.repository.ts b/server/src/repositories/tag.repository.ts index 7699d5897aab7..9389aeb13b4e3 100644 --- a/server/src/repositories/tag.repository.ts +++ b/server/src/repositories/tag.repository.ts @@ -22,6 +22,48 @@ export class TagRepository implements ITagRepository { return this.repository.findOne({ where: { userId, value } }); } + async upsertValue({ + userId, + value, + parent, + }: { + userId: string; + value: string; + parent?: TagEntity; + }): Promise { + return this.dataSource.transaction(async (manager) => { + // upsert tag + const { identifiers } = await manager.upsert( + TagEntity, + { userId, value, parentId: parent?.id }, + { conflictPaths: { userId: true, value: true } }, + ); + const id = identifiers[0]?.id; + if (!id) { + throw new Error('Failed to upsert tag'); + } + + // update closure table + await manager.query( + `INSERT INTO tags_closure (id_ancestor, id_descendant) + VALUES ($1, $1) + ON CONFLICT DO NOTHING;`, + [id], + ); + + if (parent) { + await manager.query( + `INSERT INTO tags_closure (id_ancestor, id_descendant) + SELECT id_ancestor, '${id}' as id_descendant FROM tags_closure WHERE id_descendant = $1 + ON CONFLICT DO NOTHING`, + [parent.id], + ); + } + + return manager.findOneOrFail(TagEntity, { where: { id } }); + }); + } + async getAll(userId: string): Promise { const tags = await this.repository.find({ where: { userId }, diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index cb89de184a559..8f449622790cc 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -365,25 +365,23 @@ describe(MetadataService.name, () => { it('should extract tags from TagsList', async () => { assetMock.getByIds.mockResolvedValue([assetStub.image]); metadataMock.readTags.mockResolvedValue({ TagsList: ['Parent'] }); - tagMock.getByValue.mockResolvedValue(null); - tagMock.create.mockResolvedValue(tagStub.parent); + tagMock.upsertValue.mockResolvedValue(tagStub.parent); await sut.handleMetadataExtraction({ id: assetStub.image.id }); - expect(tagMock.create).toHaveBeenCalledWith({ userId: 'user-id', value: 'Parent', parent: undefined }); + expect(tagMock.upsertValue).toHaveBeenCalledWith({ userId: 'user-id', value: 'Parent', parent: undefined }); }); it('should extract hierarchy from TagsList', async () => { assetMock.getByIds.mockResolvedValue([assetStub.image]); metadataMock.readTags.mockResolvedValue({ TagsList: ['Parent/Child'] }); - tagMock.getByValue.mockResolvedValue(null); - tagMock.create.mockResolvedValueOnce(tagStub.parent); - tagMock.create.mockResolvedValueOnce(tagStub.child); + tagMock.upsertValue.mockResolvedValueOnce(tagStub.parent); + tagMock.upsertValue.mockResolvedValueOnce(tagStub.child); await sut.handleMetadataExtraction({ id: assetStub.image.id }); - expect(tagMock.create).toHaveBeenNthCalledWith(1, { userId: 'user-id', value: 'Parent', parent: undefined }); - expect(tagMock.create).toHaveBeenNthCalledWith(2, { + expect(tagMock.upsertValue).toHaveBeenNthCalledWith(1, { userId: 'user-id', value: 'Parent', parent: undefined }); + expect(tagMock.upsertValue).toHaveBeenNthCalledWith(2, { userId: 'user-id', value: 'Parent/Child', parent: tagStub.parent, @@ -393,35 +391,32 @@ describe(MetadataService.name, () => { it('should extract tags from Keywords as a string', async () => { assetMock.getByIds.mockResolvedValue([assetStub.image]); metadataMock.readTags.mockResolvedValue({ Keywords: 'Parent' }); - tagMock.getByValue.mockResolvedValue(null); - tagMock.create.mockResolvedValue(tagStub.parent); + tagMock.upsertValue.mockResolvedValue(tagStub.parent); await sut.handleMetadataExtraction({ id: assetStub.image.id }); - expect(tagMock.create).toHaveBeenCalledWith({ userId: 'user-id', value: 'Parent', parent: undefined }); + expect(tagMock.upsertValue).toHaveBeenCalledWith({ userId: 'user-id', value: 'Parent', parent: undefined }); }); it('should extract tags from Keywords as a list', async () => { assetMock.getByIds.mockResolvedValue([assetStub.image]); metadataMock.readTags.mockResolvedValue({ Keywords: ['Parent'] }); - tagMock.getByValue.mockResolvedValue(null); - tagMock.create.mockResolvedValue(tagStub.parent); + tagMock.upsertValue.mockResolvedValue(tagStub.parent); await sut.handleMetadataExtraction({ id: assetStub.image.id }); - expect(tagMock.create).toHaveBeenCalledWith({ userId: 'user-id', value: 'Parent', parent: undefined }); + expect(tagMock.upsertValue).toHaveBeenCalledWith({ userId: 'user-id', value: 'Parent', parent: undefined }); }); it('should extract hierarchal tags from Keywords', async () => { assetMock.getByIds.mockResolvedValue([assetStub.image]); metadataMock.readTags.mockResolvedValue({ Keywords: 'Parent/Child' }); - tagMock.getByValue.mockResolvedValue(null); - tagMock.create.mockResolvedValue(tagStub.parent); + tagMock.upsertValue.mockResolvedValue(tagStub.parent); await sut.handleMetadataExtraction({ id: assetStub.image.id }); - expect(tagMock.create).toHaveBeenNthCalledWith(1, { userId: 'user-id', value: 'Parent', parent: undefined }); - expect(tagMock.create).toHaveBeenNthCalledWith(2, { + expect(tagMock.upsertValue).toHaveBeenNthCalledWith(1, { userId: 'user-id', value: 'Parent', parent: undefined }); + expect(tagMock.upsertValue).toHaveBeenNthCalledWith(2, { userId: 'user-id', value: 'Parent/Child', parent: tagStub.parent, diff --git a/server/src/services/tag.service.spec.ts b/server/src/services/tag.service.spec.ts index ffa7895cb4c8f..de270777b06c5 100644 --- a/server/src/services/tag.service.spec.ts +++ b/server/src/services/tag.service.spec.ts @@ -115,9 +115,9 @@ describe(TagService.name, () => { describe('upsert', () => { it('should upsert a new tag', async () => { - tagMock.create.mockResolvedValue(tagStub.parent); + tagMock.upsertValue.mockResolvedValue(tagStub.parent); await expect(sut.upsert(authStub.admin, { tags: ['Parent'] })).resolves.toBeDefined(); - expect(tagMock.create).toHaveBeenCalledWith({ + expect(tagMock.upsertValue).toHaveBeenCalledWith({ value: 'Parent', userId: 'admin_id', parentId: undefined, @@ -126,15 +126,15 @@ describe(TagService.name, () => { it('should upsert a nested tag', async () => { tagMock.getByValue.mockResolvedValueOnce(null); - tagMock.create.mockResolvedValueOnce(tagStub.parent); - tagMock.create.mockResolvedValueOnce(tagStub.child); + tagMock.upsertValue.mockResolvedValueOnce(tagStub.parent); + tagMock.upsertValue.mockResolvedValueOnce(tagStub.child); await expect(sut.upsert(authStub.admin, { tags: ['Parent/Child'] })).resolves.toBeDefined(); - expect(tagMock.create).toHaveBeenNthCalledWith(1, { + expect(tagMock.upsertValue).toHaveBeenNthCalledWith(1, { value: 'Parent', userId: 'admin_id', - parentId: undefined, + parent: undefined, }); - expect(tagMock.create).toHaveBeenNthCalledWith(2, { + expect(tagMock.upsertValue).toHaveBeenNthCalledWith(2, { value: 'Parent/Child', userId: 'admin_id', parent: expect.objectContaining({ id: 'tag-parent' }), diff --git a/server/src/utils/tag.ts b/server/src/utils/tag.ts index 12c46d24400d5..6d6c70f1d73ac 100644 --- a/server/src/utils/tag.ts +++ b/server/src/utils/tag.ts @@ -13,12 +13,7 @@ export const upsertTags = async (repository: ITagRepository, { userId, tags }: U for (const part of parts) { const value = parent ? `${parent.value}/${part}` : part; - let tag = await repository.getByValue(userId, value); - if (!tag) { - tag = await repository.create({ userId, value, parent }); - } - - parent = tag; + parent = await repository.upsertValue({ userId, value, parent }); } if (parent) { diff --git a/server/test/repositories/tag.repository.mock.ts b/server/test/repositories/tag.repository.mock.ts index 35b3de1576084..a3fc0e77e0312 100644 --- a/server/test/repositories/tag.repository.mock.ts +++ b/server/test/repositories/tag.repository.mock.ts @@ -5,6 +5,7 @@ export const newTagRepositoryMock = (): Mocked => { return { getAll: vitest.fn(), getByValue: vitest.fn(), + upsertValue: vitest.fn(), upsertAssetTags: vitest.fn(), get: vitest.fn(), From 860ba78650693a371d2a3f7b8b4cd8502ca59dea Mon Sep 17 00:00:00 2001 From: Zack Pollard Date: Fri, 30 Aug 2024 18:07:02 +0100 Subject: [PATCH 076/160] ci: fix release script (#12146) --- .github/workflows/prepare-release.yml | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 6668976bcf0fe..fc03b24d085b7 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -29,17 +29,6 @@ jobs: ref: ${{ steps.push-tag.outputs.commit_long_sha }} steps: - - name: Checkout - uses: actions/checkout@v4 - with: - token: ${{ secrets.ORG_RELEASE_TOKEN }} - - - name: Install Poetry - run: pipx install poetry - - - name: Bump version - run: misc/release/pump-version.sh -s "${{ inputs.serverBump }}" -m "${{ inputs.mobileBump }}" - - name: Generate a token id: generate-token uses: actions/create-github-app-token@v1 @@ -47,6 +36,17 @@ jobs: app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} + - name: Checkout + uses: actions/checkout@v4 + with: + token: ${{ steps.generate-token.outputs.token }} + + - name: Install Poetry + run: pipx install poetry + + - name: Bump version + run: misc/release/pump-version.sh -s "${{ inputs.serverBump }}" -m "${{ inputs.mobileBump }}" + - name: Commit and tag id: push-tag uses: EndBug/add-and-commit@v9 @@ -55,7 +55,6 @@ jobs: message: 'chore: version ${{ env.IMMICH_VERSION }}' tag: ${{ env.IMMICH_VERSION }} push: true - github-token: ${{ steps.generate-token.outputs.token }} build_mobile: uses: ./.github/workflows/build-mobile.yml From cc88cbb456e6f3e1c77680cebde4806ed44a8915 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 17:16:21 +0000 Subject: [PATCH 077/160] chore: version v1.113.0 --- cli/package-lock.json | 6 +++--- cli/package.json | 2 +- docs/static/archived-versions.json | 4 ++++ e2e/package-lock.json | 8 ++++---- e2e/package.json | 2 +- machine-learning/pyproject.toml | 2 +- mobile/android/fastlane/Fastfile | 4 ++-- mobile/ios/fastlane/Fastfile | 2 +- mobile/openapi/README.md | 2 +- mobile/pubspec.yaml | 2 +- open-api/immich-openapi-specs.json | 2 +- open-api/typescript-sdk/package-lock.json | 4 ++-- open-api/typescript-sdk/package.json | 2 +- open-api/typescript-sdk/src/fetch-client.ts | 2 +- server/package-lock.json | 4 ++-- server/package.json | 2 +- web/package-lock.json | 6 +++--- web/package.json | 2 +- 18 files changed, 31 insertions(+), 27 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index fa38bd275e7fc..a8a636b3b6527 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/cli", - "version": "2.2.15", + "version": "2.2.16", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/cli", - "version": "2.2.15", + "version": "2.2.16", "license": "GNU Affero General Public License version 3", "dependencies": { "fast-glob": "^3.3.2", @@ -52,7 +52,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.112.1", + "version": "1.113.0", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/cli/package.json b/cli/package.json index d739cc3895c61..316f8fb37a43d 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@immich/cli", - "version": "2.2.15", + "version": "2.2.16", "description": "Command Line Interface (CLI) for Immich", "type": "module", "exports": "./dist/index.js", diff --git a/docs/static/archived-versions.json b/docs/static/archived-versions.json index c2bce22893622..1dc2cd3a1f656 100644 --- a/docs/static/archived-versions.json +++ b/docs/static/archived-versions.json @@ -1,4 +1,8 @@ [ + { + "label": "v1.113.0", + "url": "https://v1.113.0.archive.immich.app" + }, { "label": "v1.112.1", "url": "https://v1.112.1.archive.immich.app" diff --git a/e2e/package-lock.json b/e2e/package-lock.json index cd591270db147..3336c9774000a 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-e2e", - "version": "1.112.1", + "version": "1.113.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-e2e", - "version": "1.112.1", + "version": "1.113.0", "license": "GNU Affero General Public License version 3", "devDependencies": { "@eslint/eslintrc": "^3.1.0", @@ -45,7 +45,7 @@ }, "../cli": { "name": "@immich/cli", - "version": "2.2.15", + "version": "2.2.16", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { @@ -92,7 +92,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.112.1", + "version": "1.113.0", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/e2e/package.json b/e2e/package.json index add072df84441..063b01cff1258 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,6 +1,6 @@ { "name": "immich-e2e", - "version": "1.112.1", + "version": "1.113.0", "description": "", "main": "index.js", "type": "module", diff --git a/machine-learning/pyproject.toml b/machine-learning/pyproject.toml index 05ac4618cdef2..08e7d01bf490f 100644 --- a/machine-learning/pyproject.toml +++ b/machine-learning/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "machine-learning" -version = "1.112.1" +version = "1.113.0" description = "" authors = ["Hau Tran "] readme = "README.md" diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index 3905d6d555783..c0284945ff934 100644 --- a/mobile/android/fastlane/Fastfile +++ b/mobile/android/fastlane/Fastfile @@ -35,8 +35,8 @@ platform :android do task: 'bundle', build_type: 'Release', properties: { - "android.injected.version.code" => 154, - "android.injected.version.name" => "1.112.1", + "android.injected.version.code" => 155, + "android.injected.version.name" => "1.113.0", } ) upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab') diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index c7d078ceeafb4..a9000ba86d032 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -19,7 +19,7 @@ platform :ios do desc "iOS Release" lane :release do increment_version_number( - version_number: "1.112.1" + version_number: "1.113.0" ) increment_build_number( build_number: latest_testflight_build_number + 1, diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index b831f60b9a2a0..66707f917594e 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -3,7 +3,7 @@ Immich API This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: -- API version: 1.112.1 +- API version: 1.113.0 - Generator version: 7.5.0 - Build package: org.openapitools.codegen.languages.DartClientCodegen diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 51a31a24e3aa6..eeaae505a3914 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -2,7 +2,7 @@ name: immich_mobile description: Immich - selfhosted backup media file on mobile phone publish_to: 'none' -version: 1.112.1+154 +version: 1.113.0+155 environment: sdk: '>=3.3.0 <4.0.0' diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 97a31ead266c5..0b0e40b478df4 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -7394,7 +7394,7 @@ "info": { "title": "Immich", "description": "Immich API", - "version": "1.112.1", + "version": "1.113.0", "contact": {} }, "tags": [], diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index afa002a5a3b9f..312858d0a3082 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/sdk", - "version": "1.112.1", + "version": "1.113.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/sdk", - "version": "1.112.1", + "version": "1.113.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index d7d6ba6cc5e30..c86a58ffb96b2 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@immich/sdk", - "version": "1.112.1", + "version": "1.113.0", "description": "Auto-generated TypeScript SDK for the Immich API", "type": "module", "main": "./build/index.js", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 2c336f98be7b1..e7e4e6adbef34 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1,6 +1,6 @@ /** * Immich - * 1.112.1 + * 1.113.0 * DO NOT MODIFY - This file has been generated using oazapfts. * See https://www.npmjs.com/package/oazapfts */ diff --git a/server/package-lock.json b/server/package-lock.json index e90256e29b1f5..4fb6a04deb180 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich", - "version": "1.112.1", + "version": "1.113.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "immich", - "version": "1.112.1", + "version": "1.113.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@nestjs/bullmq": "^10.0.1", diff --git a/server/package.json b/server/package.json index 42552f20b74eb..e3fa9a6081995 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.112.1", + "version": "1.113.0", "description": "", "author": "", "private": true, diff --git a/web/package-lock.json b/web/package-lock.json index d5a27478935d6..fded54b2dc130 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-web", - "version": "1.112.1", + "version": "1.113.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-web", - "version": "1.112.1", + "version": "1.113.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@formatjs/icu-messageformat-parser": "^2.7.8", @@ -74,7 +74,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.112.1", + "version": "1.113.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/web/package.json b/web/package.json index d87b6e6c08caa..383bde7ac88e5 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "immich-web", - "version": "1.112.1", + "version": "1.113.0", "license": "GNU Affero General Public License version 3", "scripts": { "dev": "vite dev --host 0.0.0.0 --port 3000", From 51a11d0cb6d1557531e55fefbfaa988ff721a5ca Mon Sep 17 00:00:00 2001 From: Bastian Machek <16717398+bmachek@users.noreply.github.com> Date: Fri, 30 Aug 2024 20:01:50 +0200 Subject: [PATCH 078/160] docs(project): lightroom project (#12149) * Update community-projects.tsx Added my community project: lrc-immich-plugin * Update community-projects.tsx typo --- docs/src/components/community-projects.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/src/components/community-projects.tsx b/docs/src/components/community-projects.tsx index 23a55ca9ce7d9..d8273c67c2179 100644 --- a/docs/src/components/community-projects.tsx +++ b/docs/src/components/community-projects.tsx @@ -38,6 +38,11 @@ const projects: CommunityProjectProps[] = [ description: 'Lightroom plugin to publish photos from Lightroom collections to Immich albums.', url: 'https://github.com/midzelis/mi.Immich.Publisher', }, + { + title: 'Lightroom Immich Plugin: lrc-immich-plugin', + description: 'Another Lightroom plugin to publish or export photos from Lightroom to Immich.', + url: 'https://github.com/bmachek/lrc-immich-plugin', + }, { title: 'Immich Duplicate Finder', description: 'Webapp that uses machine learning to identify near-duplicate images.', From 40854f358c86e2503269b6b172fddd4e3b18e026 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 14:03:44 -0400 Subject: [PATCH 079/160] chore(deps): update dependency svelte to v4.2.19 [security] (#12147) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- web/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index fded54b2dc130..d62a186189d18 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -7719,9 +7719,9 @@ } }, "node_modules/svelte": { - "version": "4.2.18", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.18.tgz", - "integrity": "sha512-d0FdzYIiAePqRJEb90WlJDkjUEx42xhivxN8muUBmfZnP+tzUgz12DJ2hRJi8sIHCME7jeK1PTMgKPSfTd8JrA==", + "version": "4.2.19", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.19.tgz", + "integrity": "sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==", "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.1", From 5e6ac87eafd4d93c205c834075d23729be4a5dd7 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Fri, 30 Aug 2024 14:38:53 -0400 Subject: [PATCH 080/160] chore: object shorthand linting rule (#12152) chore: object shorthand --- cli/eslint.config.mjs | 1 + e2e/eslint.config.mjs | 1 + server/eslint.config.mjs | 1 + server/src/dtos/user-profile.dto.ts | 4 ++-- server/src/repositories/map.repository.ts | 2 +- server/src/services/album.service.ts | 2 +- server/src/services/auth.service.spec.ts | 6 +++--- server/src/services/auth.service.ts | 2 +- server/src/services/library.service.ts | 4 ++-- server/src/services/storage-template.service.spec.ts | 2 +- server/src/services/storage-template.service.ts | 2 +- server/test/fixtures/asset.stub.ts | 2 +- web/eslint.config.mjs | 1 + .../lib/components/faces-page/merge-face-selector.svelte | 2 +- .../components/photos-page/actions/remove-from-album.svelte | 2 +- .../photos-page/actions/remove-from-shared-link.svelte | 2 +- .../lib/components/photos-page/measure-date-group.svelte | 6 ++---- .../sharedlinks-page/covers/__tests__/share-cover.spec.ts | 2 +- web/src/lib/utils/asset-store-task-manager.ts | 4 ++-- web/src/lib/utils/asset-utils.ts | 4 ++-- web/src/lib/utils/person.ts | 2 +- web/src/lib/utils/timeline-util.ts | 2 +- .../[[photos=photos]]/[[assetId=id]]/+page.svelte | 2 +- 23 files changed, 30 insertions(+), 28 deletions(-) diff --git a/cli/eslint.config.mjs b/cli/eslint.config.mjs index 3f724506a3c8e..9115a1feb79e5 100644 --- a/cli/eslint.config.mjs +++ b/cli/eslint.config.mjs @@ -55,6 +55,7 @@ export default [ 'unicorn/import-style': 'off', curly: 2, 'prettier/prettier': 0, + 'object-shorthand': ['error', 'always'], }, }, ]; diff --git a/e2e/eslint.config.mjs b/e2e/eslint.config.mjs index 9a1bb9959851a..fd1e8a0af693d 100644 --- a/e2e/eslint.config.mjs +++ b/e2e/eslint.config.mjs @@ -59,6 +59,7 @@ export default [ 'unicorn/prefer-top-level-await': 'off', 'unicorn/prefer-event-target': 'off', 'unicorn/no-thenable': 'off', + 'object-shorthand': ['error', 'always'], }, }, ]; diff --git a/server/eslint.config.mjs b/server/eslint.config.mjs index 638b7b2959e58..d29b6f72385d5 100644 --- a/server/eslint.config.mjs +++ b/server/eslint.config.mjs @@ -63,6 +63,7 @@ export default [ '@typescript-eslint/require-await': 'error', curly: 2, 'prettier/prettier': 0, + 'object-shorthand': ['error', 'always'], 'no-restricted-imports': [ 'error', diff --git a/server/src/dtos/user-profile.dto.ts b/server/src/dtos/user-profile.dto.ts index b14662c844026..9659fa39650a3 100644 --- a/server/src/dtos/user-profile.dto.ts +++ b/server/src/dtos/user-profile.dto.ts @@ -13,7 +13,7 @@ export class CreateProfileImageResponseDto { export function mapCreateProfileImageResponse(userId: string, profileImagePath: string): CreateProfileImageResponseDto { return { - userId: userId, - profileImagePath: profileImagePath, + userId, + profileImagePath, }; } diff --git a/server/src/repositories/map.repository.ts b/server/src/repositories/map.repository.ts index 555f1042bbc0b..da4e30d47cbf8 100644 --- a/server/src/repositories/map.repository.ts +++ b/server/src/repositories/map.repository.ts @@ -317,7 +317,7 @@ export class MapRepository implements IMapRepository { } const input = createReadStream(filePath); - const lineReader = readLine.createInterface({ input: input }); + const lineReader = readLine.createInterface({ input }); const adminMap = new Map(); for await (const line of lineReader) { diff --git a/server/src/services/album.service.ts b/server/src/services/album.service.ts index 1cd5237b7ae39..b59364af9fb6e 100644 --- a/server/src/services/album.service.ts +++ b/server/src/services/album.service.ts @@ -239,7 +239,7 @@ export class AlbumService { throw new BadRequestException('User not found'); } - await this.albumUserRepository.create({ userId: userId, albumId: id, role }); + await this.albumUserRepository.create({ userId, albumId: id, role }); await this.eventRepository.emit('album.invite', { id, userId }); } diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index d73896edb1be0..f2fa0c520a30f 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -46,7 +46,7 @@ const fixtures = { }; const oauthUserWithDefaultQuota = { - email: email, + email, name: ' ', oauthId: sub, quotaSizeInBytes: 1_073_741_824, @@ -561,7 +561,7 @@ describe('AuthService', () => { ); expect(userMock.create).toHaveBeenCalledWith({ - email: email, + email, name: ' ', oauthId: sub, quotaSizeInBytes: null, @@ -581,7 +581,7 @@ describe('AuthService', () => { ); expect(userMock.create).toHaveBeenCalledWith({ - email: email, + email, name: ' ', oauthId: sub, quotaSizeInBytes: 5_368_709_120, diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index 10cf93b6a4e6d..2b25decc07035 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -421,7 +421,7 @@ export class AuthService { await this.sessionRepository.update({ id: session.id, updatedAt: new Date() }); } - return { user: session.user, session: session }; + return { user: session.user, session }; } throw new UnauthorizedException('Invalid user token'); diff --git a/server/src/services/library.service.ts b/server/src/services/library.service.ts index c7f82eddea5d8..bcd0a842c7065 100644 --- a/server/src/services/library.service.ts +++ b/server/src/services/library.service.ts @@ -339,7 +339,7 @@ export class LibraryService { const libraryId = job.id; const assetPagination = usePagination(JOBS_LIBRARY_PAGINATION_SIZE, (pagination) => - this.assetRepository.getAll(pagination, { libraryId: libraryId, withDeleted: true }), + this.assetRepository.getAll(pagination, { libraryId, withDeleted: true }), ); let assetsFound = false; @@ -465,7 +465,7 @@ export class LibraryService { libraryId: job.id, checksum: pathHash, originalPath: assetPath, - deviceAssetId: deviceAssetId, + deviceAssetId, deviceId: 'Library Import', fileCreatedAt: stats.mtime, fileModifiedAt: stats.mtime, diff --git a/server/src/services/storage-template.service.spec.ts b/server/src/services/storage-template.service.spec.ts index 92d11eaa125f7..093cc5b2ff1d6 100644 --- a/server/src/services/storage-template.service.spec.ts +++ b/server/src/services/storage-template.service.spec.ts @@ -309,7 +309,7 @@ describe(StorageTemplateService.name, () => { entityId: assetStub.image.id, pathType: AssetPathType.ORIGINAL, oldPath: assetStub.image.originalPath, - newPath: newPath, + newPath, }); expect(storageMock.rename).toHaveBeenCalledWith(assetStub.image.originalPath, newPath); expect(storageMock.copyFile).toHaveBeenCalledWith(assetStub.image.originalPath, newPath); diff --git a/server/src/services/storage-template.service.ts b/server/src/services/storage-template.service.ts index 829863e228e73..9836ad40ace47 100644 --- a/server/src/services/storage-template.service.ts +++ b/server/src/services/storage-template.service.ts @@ -227,7 +227,7 @@ export class StorageTemplateService { const storagePath = this.render(this.template.compiled, { asset, filename: sanitized, - extension: extension, + extension, albumName, }); const fullPath = path.normalize(path.join(rootPath, storagePath)); diff --git a/server/test/fixtures/asset.stub.ts b/server/test/fixtures/asset.stub.ts index b8c7e06d8218d..5ee42224ba862 100644 --- a/server/test/fixtures/asset.stub.ts +++ b/server/test/fixtures/asset.stub.ts @@ -31,7 +31,7 @@ const files: AssetFileEntity[] = [previewFile, thumbnailFile]; export const stackStub = (stackId: string, assets: AssetEntity[]): StackEntity => { return { id: stackId, - assets: assets, + assets, owner: assets[0].owner, ownerId: assets[0].ownerId, primaryAsset: assets[0], diff --git a/web/eslint.config.mjs b/web/eslint.config.mjs index f4aec0e728011..f1ba46355f73a 100644 --- a/web/eslint.config.mjs +++ b/web/eslint.config.mjs @@ -87,6 +87,7 @@ export default [ '@typescript-eslint/no-floating-promises': 'error', '@typescript-eslint/no-misused-promises': 'error', '@typescript-eslint/require-await': 'error', + 'object-shorthand': ['error', 'always'], }, }, { diff --git a/web/src/lib/components/faces-page/merge-face-selector.svelte b/web/src/lib/components/faces-page/merge-face-selector.svelte index ea1445a938320..71358361ce257 100644 --- a/web/src/lib/components/faces-page/merge-face-selector.svelte +++ b/web/src/lib/components/faces-page/merge-face-selector.svelte @@ -81,7 +81,7 @@ const mergedPerson = await getPerson({ id: person.id }); const count = results.filter(({ success }) => success).length; notificationController.show({ - message: $t('merged_people_count', { values: { count: count } }), + message: $t('merged_people_count', { values: { count } }), type: NotificationType.Info, }); dispatch('merge', mergedPerson); diff --git a/web/src/lib/components/photos-page/actions/remove-from-album.svelte b/web/src/lib/components/photos-page/actions/remove-from-album.svelte index d76ea7b275560..2384f95d2e0a1 100644 --- a/web/src/lib/components/photos-page/actions/remove-from-album.svelte +++ b/web/src/lib/components/photos-page/actions/remove-from-album.svelte @@ -40,7 +40,7 @@ const count = results.filter(({ success }) => success).length; notificationController.show({ type: NotificationType.Info, - message: $t('assets_removed_count', { values: { count: count } }), + message: $t('assets_removed_count', { values: { count } }), }); clearSelect(); diff --git a/web/src/lib/components/photos-page/actions/remove-from-shared-link.svelte b/web/src/lib/components/photos-page/actions/remove-from-shared-link.svelte index 0c785830d0937..e838f0813d461 100644 --- a/web/src/lib/components/photos-page/actions/remove-from-shared-link.svelte +++ b/web/src/lib/components/photos-page/actions/remove-from-shared-link.svelte @@ -45,7 +45,7 @@ notificationController.show({ type: NotificationType.Info, - message: $t('assets_removed_count', { values: { count: count } }), + message: $t('assets_removed_count', { values: { count } }), }); clearSelect(); diff --git a/web/src/lib/components/photos-page/measure-date-group.svelte b/web/src/lib/components/photos-page/measure-date-group.svelte index 98e423ae94e00..f458fe40dd84b 100644 --- a/web/src/lib/components/photos-page/measure-date-group.svelte +++ b/web/src/lib/components/photos-page/measure-date-group.svelte @@ -39,7 +39,7 @@ if (!heightPending) { const height = element.getBoundingClientRect().height; if (height !== 0) { - $assetStore.updateBucket(bucket.bucketDate, { height: height, measured: true }); + $assetStore.updateBucket(bucket.bucketDate, { height, measured: true }); } onMeasured(); @@ -65,9 +65,7 @@
{#each bucket.dateGroups as dateGroup}
-
$assetStore.updateBucketDateGroup(bucket, dateGroup, { height: height })} - > +
$assetStore.updateBucketDateGroup(bucket, dateGroup, { height })}>
{ it.skip('renders fallback image when asset is not resized', () => { const link = sharedLinkFactory.build({ assets: [assetFactory.build()] }); render(ShareCover, { - link: link, + link, preload: false, }); diff --git a/web/src/lib/utils/asset-store-task-manager.ts b/web/src/lib/utils/asset-store-task-manager.ts index 6ca4f057bd419..e476738456d3b 100644 --- a/web/src/lib/utils/asset-store-task-manager.ts +++ b/web/src/lib/utils/asset-store-task-manager.ts @@ -350,7 +350,7 @@ class IntersectionTask { this.internalTaskManager.queueScrollSensitiveTask({ task, cleanup, - componentId: componentId, + componentId, priority: this.priority, taskId: this.intersectedKey, }); @@ -367,7 +367,7 @@ class IntersectionTask { this.internalTaskManager.queueSeparateTask({ task, cleanup, - componentId: componentId, + componentId, taskId: this.separatedKey, }); } diff --git a/web/src/lib/utils/asset-utils.ts b/web/src/lib/utils/asset-utils.ts index ce7944b9c98f2..e309db5ff6a1e 100644 --- a/web/src/lib/utils/asset-utils.ts +++ b/web/src/lib/utils/asset-utils.ts @@ -52,7 +52,7 @@ export const addAssetsToAlbum = async (albumId: string, assetIds: string[], show timeout: 5000, message: count > 0 - ? $t('assets_added_to_album_count', { values: { count: count } }) + ? $t('assets_added_to_album_count', { values: { count } }) : $t('assets_were_part_of_album_count', { values: { count: assetIds.length } }), button: { text: $t('view_album'), @@ -264,7 +264,7 @@ export const downloadFile = async (asset: AssetResponseDto) => { downloadBlob(data, filename); } catch (error) { - handleError(error, $t('errors.error_downloading', { values: { filename: filename } })); + handleError(error, $t('errors.error_downloading', { values: { filename } })); downloadManager.clear(downloadKey); } finally { setTimeout(() => downloadManager.clear(downloadKey), 5000); diff --git a/web/src/lib/utils/person.ts b/web/src/lib/utils/person.ts index 79f9284d8aa71..0b30556516cb2 100644 --- a/web/src/lib/utils/person.ts +++ b/web/src/lib/utils/person.ts @@ -28,5 +28,5 @@ export const searchNameLocal = ( }; export const getPersonNameWithHiddenValue = derived(t, ($t) => { - return (name: string, isHidden: boolean) => $t('person_hidden', { values: { name: name, hidden: isHidden } }); + return (name: string, isHidden: boolean) => $t('person_hidden', { values: { name, hidden: isHidden } }); }); diff --git a/web/src/lib/utils/timeline-util.ts b/web/src/lib/utils/timeline-util.ts index 3a8f66ee08b67..541ebea7f5444 100644 --- a/web/src/lib/utils/timeline-util.ts +++ b/web/src/lib/utils/timeline-util.ts @@ -107,7 +107,7 @@ export function splitBucketIntoDateGroups(bucket: AssetBucket, locale: string | heightActual: false, intersecting: false, geometry: emptyGeometry(), - bucket: bucket, + bucket, }; }); } diff --git a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte index 6762e3a1ccfd2..0fa325c6f573e 100644 --- a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -291,7 +291,7 @@ const count = results.filter(({ success }) => success).length; notificationController.show({ type: NotificationType.Info, - message: $t('assets_added_count', { values: { count: count } }), + message: $t('assets_added_count', { values: { count } }), }); await refreshAlbum(); From fcbc1ba399d24b73f788f7be4fd511cd1a249014 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 30 Aug 2024 14:00:31 -0500 Subject: [PATCH 081/160] fix(web): memory view in timeline href (#12158) --- web/src/lib/components/memory-page/memory-viewer.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/lib/components/memory-page/memory-viewer.svelte b/web/src/lib/components/memory-page/memory-viewer.svelte index 48a6cd1ceccc0..0088eb7a43cf1 100644 --- a/web/src/lib/components/memory-page/memory-viewer.svelte +++ b/web/src/lib/components/memory-page/memory-viewer.svelte @@ -309,7 +309,7 @@ class:opacity-100={!galleryInView} > Date: Fri, 30 Aug 2024 23:31:42 +0200 Subject: [PATCH 082/160] fix(web): unable to scroll timeline after using gesture (#12163) --- .../lib/components/asset-viewer/photo-viewer.svelte | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/web/src/lib/components/asset-viewer/photo-viewer.svelte b/web/src/lib/components/asset-viewer/photo-viewer.svelte index 6f6af652b98fe..0a3da9ade3639 100644 --- a/web/src/lib/components/asset-viewer/photo-viewer.svelte +++ b/web/src/lib/components/asset-viewer/photo-viewer.svelte @@ -169,7 +169,13 @@
{:else if !imageError} -
+
{#if $slideshowState !== SlideshowState.None && $slideshowLook === SlideshowLook.BlurredBackground} {$getAltText(asset)}{value} + {value} {#if isOpen} From d18bc7007a5f1b63cd80202bd3b96af5096b5bb1 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Fri, 30 Aug 2024 17:33:42 -0400 Subject: [PATCH 084/160] fix: keyword parsing (#12164) --- server/src/services/metadata.service.spec.ts | 11 +++++++++++ server/src/services/metadata.service.ts | 7 +++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index 8f449622790cc..3e3e5e0db1fc8 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -408,6 +408,17 @@ describe(MetadataService.name, () => { expect(tagMock.upsertValue).toHaveBeenCalledWith({ userId: 'user-id', value: 'Parent', parent: undefined }); }); + it('should extract tags from Keywords as a list with a number', async () => { + assetMock.getByIds.mockResolvedValue([assetStub.image]); + metadataMock.readTags.mockResolvedValue({ Keywords: ['Parent', 2024] as any[] }); + tagMock.upsertValue.mockResolvedValue(tagStub.parent); + + await sut.handleMetadataExtraction({ id: assetStub.image.id }); + + expect(tagMock.upsertValue).toHaveBeenCalledWith({ userId: 'user-id', value: 'Parent', parent: undefined }); + expect(tagMock.upsertValue).toHaveBeenCalledWith({ userId: 'user-id', value: '2024', parent: undefined }); + }); + it('should extract hierarchal tags from Keywords', async () => { assetMock.getByIds.mockResolvedValue([assetStub.image]); metadataMock.readTags.mockResolvedValue({ Keywords: 'Parent/Child' }); diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index 875414d84df7a..a0a8f9ebef975 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -352,22 +352,21 @@ export class MetadataService { } private async applyTagList(asset: AssetEntity, exifTags: ImmichTags) { - const tags: string[] = []; - + const tags: unknown[] = []; if (exifTags.TagsList) { tags.push(...exifTags.TagsList); } if (exifTags.Keywords) { let keywords = exifTags.Keywords; - if (typeof keywords === 'string') { + if (!Array.isArray(keywords)) { keywords = [keywords]; } tags.push(...keywords); } if (tags.length > 0) { - const results = await upsertTags(this.tagRepository, { userId: asset.ownerId, tags }); + const results = await upsertTags(this.tagRepository, { userId: asset.ownerId, tags: tags.map(String) }); const tagIds = results.map((tag) => tag.id); await this.tagRepository.upsertAssetTags({ assetId: asset.id, tagIds }); } From 40327ad987d3811e80b8563bcb460e055a99ac2a Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 30 Aug 2024 16:35:06 -0500 Subject: [PATCH 085/160] chore(mobile): post release tasks (#12157) * sent to reviewer * sent to reviewer * update to app store * update to app store --- .../android/app/src/main/AndroidManifest.xml | 4 +- mobile/android/fastlane/Fastfile | 2 +- mobile/ios/Runner.xcodeproj/project.pbxproj | 40 +++++++++---------- mobile/ios/Runner/Base.lproj/Main.storyboard | 13 +++--- mobile/ios/Runner/Info.plist | 4 +- mobile/pubspec.yaml | 2 +- 6 files changed, 34 insertions(+), 31 deletions(-) diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml index 041a4dbb72cf5..39827b9391ce7 100644 --- a/mobile/android/app/src/main/AndroidManifest.xml +++ b/mobile/android/app/src/main/AndroidManifest.xml @@ -69,7 +69,7 @@ - + @@ -14,13 +16,14 @@ - + - + + diff --git a/mobile/ios/Runner/Info.plist b/mobile/ios/Runner/Info.plist index c7a5991212991..1c3ac477f8227 100644 --- a/mobile/ios/Runner/Info.plist +++ b/mobile/ios/Runner/Info.plist @@ -58,11 +58,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.112.1 + 1.113.0 CFBundleSignature ???? CFBundleVersion - 169 + 171 FLTEnableImpeller ITSAppUsesNonExemptEncryption diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index eeaae505a3914..7b31e4f231b9d 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -2,7 +2,7 @@ name: immich_mobile description: Immich - selfhosted backup media file on mobile phone publish_to: 'none' -version: 1.113.0+155 +version: 1.113.0+156 environment: sdk: '>=3.3.0 <4.0.0' From 67468ea3672f482cb374a274de1896e62e48ddff Mon Sep 17 00:00:00 2001 From: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Date: Sat, 31 Aug 2024 19:24:38 +0200 Subject: [PATCH 086/160] fix(web): avoid deleting empty album unexpectedly (#12175) --- e2e/src/web/specs/album.e2e-spec.ts | 25 +++++++++++++++++++ .../[[assetId=id]]/+page.svelte | 4 +-- 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 e2e/src/web/specs/album.e2e-spec.ts diff --git a/e2e/src/web/specs/album.e2e-spec.ts b/e2e/src/web/specs/album.e2e-spec.ts new file mode 100644 index 0000000000000..953c7d00ae1cd --- /dev/null +++ b/e2e/src/web/specs/album.e2e-spec.ts @@ -0,0 +1,25 @@ +import { LoginResponseDto } from '@immich/sdk'; +import { test } from '@playwright/test'; +import { utils } from 'src/utils'; + +test.describe('Album', () => { + let admin: LoginResponseDto; + + test.beforeAll(async () => { + utils.initSdk(); + await utils.resetDatabase(); + admin = await utils.adminSetup(); + }); + + test(`doesn't delete album after canceling add assets`, async ({ context, page }) => { + await utils.setAuthCookies(context, admin.accessToken); + + await page.goto('/albums'); + await page.getByRole('button', { name: 'Create album' }).click(); + await page.getByRole('button', { name: 'Select photos' }).click(); + await page.getByRole('button', { name: 'Close' }).click(); + + await page.reload(); + await page.getByRole('button', { name: 'Select photos' }).waitFor(); + }); +}); diff --git a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte index 0fa325c6f573e..46812ff289bc7 100644 --- a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -419,8 +419,8 @@ } }; - onNavigate(async () => { - if (album.assetCount === 0 && !album.albumName) { + onNavigate(async ({ to }) => { + if (!isAlbumsRoute(to?.route.id) && album.assetCount === 0 && !album.albumName) { await deleteAlbum(album); } }); From 6bfe54788f65e28c9d64b1e638a6d7eee7ffd41e Mon Sep 17 00:00:00 2001 From: Marco Malavolti Date: Sat, 31 Aug 2024 19:33:17 +0200 Subject: [PATCH 087/160] docs: update google oauth examples (#12162) * Small update on oauth.md for Google Authn * Replace "demo" with "example" to be consistent with other example --- docs/docs/administration/oauth.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/docs/administration/oauth.md b/docs/docs/administration/oauth.md index 96dca68e4fa9d..2aba658933b24 100644 --- a/docs/docs/administration/oauth.md +++ b/docs/docs/administration/oauth.md @@ -154,21 +154,21 @@ Configuration of Authorised redirect URIs (Google Console) Configuration of OAuth in Immich System Settings -| Setting | Value | -| ---------------------------- | ------------------------------------------------------------------------------------------------------ | -| Issuer URL | [https://accounts.google.com](https://accounts.google.com) | -| Client ID | 7\***\*\*\*\*\*\*\***\*\*\***\*\*\*\*\*\*\***vuls.apps.googleusercontent.com | -| Client Secret | G\***\*\*\*\*\*\*\***\*\*\***\*\*\*\*\*\*\***OO | -| Scope | openid email profile | -| Signing Algorithm | RS256 | -| Storage Label Claim | preferred_username | -| Storage Quota Claim | immich_quota | -| Default Storage Quota (GiB) | 0 (0 for unlimited quota) | -| Button Text | Sign in with Google (optional) | -| Auto Register | Enabled (optional) | -| Auto Launch | Enabled | -| Mobile Redirect URI Override | Enabled (required) | -| Mobile Redirect URI | [https://demo.immich.app/api/oauth/mobile-redirect](https://demo.immich.app/api/oauth/mobile-redirect) | +| Setting | Value | +| ---------------------------- | ---------------------------------------------------------------------------------------------------- | +| Issuer URL | `https://accounts.google.com` | +| Client ID | 7\***\*\*\*\*\*\*\***\*\*\***\*\*\*\*\*\*\***vuls.apps.googleusercontent.com | +| Client Secret | G\***\*\*\*\*\*\*\***\*\*\***\*\*\*\*\*\*\***OO | +| Scope | openid email profile | +| Signing Algorithm | RS256 | +| Storage Label Claim | preferred_username | +| Storage Quota Claim | immich_quota | +| Default Storage Quota (GiB) | 0 (0 for unlimited quota) | +| Button Text | Sign in with Google (optional) | +| Auto Register | Enabled (optional) | +| Auto Launch | Enabled | +| Mobile Redirect URI Override | Enabled (required) | +| Mobile Redirect URI | `https://example.immich.app/api/oauth/mobile-redirect` | From 28bc7f318e6418960456327e76564970b7728b96 Mon Sep 17 00:00:00 2001 From: Qhilm <3350433+Qhilm@users.noreply.github.com> Date: Sat, 31 Aug 2024 21:52:20 +0200 Subject: [PATCH 088/160] docs: typo - accesible => accessible (#12178) [typo] accesible => accessible --- docs/docs/guides/remote-access.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/guides/remote-access.md b/docs/docs/guides/remote-access.md index 326ac6c93d634..1ea068c3a0a79 100644 --- a/docs/docs/guides/remote-access.md +++ b/docs/docs/guides/remote-access.md @@ -48,7 +48,7 @@ A reverse proxy is a service that sits between web servers and clients. A revers If you're hosting your own reverse proxy, [Nginx](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) is a great option. An example configuration for Nginx is provided [here](/docs/administration/reverse-proxy.md). -You'll also need your own certificate to authenticate https connections. If you're making Immich publicly accesible, [Let's Encrypt](https://letsencrypt.org/) can provide a free certificate for your domain and is the recommended option. Alternatively, a [self-signed certificate](https://en.wikipedia.org/wiki/Self-signed_certificate) allows you to encrypt your connection to Immich, but it raises a security warning on the client's browser. +You'll also need your own certificate to authenticate https connections. If you're making Immich publicly accessible, [Let's Encrypt](https://letsencrypt.org/) can provide a free certificate for your domain and is the recommended option. Alternatively, a [self-signed certificate](https://en.wikipedia.org/wiki/Self-signed_certificate) allows you to encrypt your connection to Immich, but it raises a security warning on the client's browser. A remote reverse proxy like [Cloudflare](https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/) increases security by hiding the server IP address, which makes targeted attacks like [DDoS](https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/) harder. From 39141d3f1cd3413ad8459bd85db41640eae84ab0 Mon Sep 17 00:00:00 2001 From: Jonathan Jogenfors Date: Mon, 2 Sep 2024 01:06:35 +0200 Subject: [PATCH 089/160] fix(server): remove offline assets from trash (#12199) * use port not taken by immich-dev for e2e * remove offline files from trash --- e2e/src/api/specs/library.e2e-spec.ts | 58 +++++++++++++++++++-- server/src/interfaces/asset.interface.ts | 7 ++- server/src/repositories/asset.repository.ts | 8 ++- server/src/services/library.service.ts | 2 +- 4 files changed, 67 insertions(+), 8 deletions(-) diff --git a/e2e/src/api/specs/library.e2e-spec.ts b/e2e/src/api/specs/library.e2e-spec.ts index ec42cbe4fa9db..2f6274d1fca4d 100644 --- a/e2e/src/api/specs/library.e2e-spec.ts +++ b/e2e/src/api/specs/library.e2e-spec.ts @@ -635,10 +635,11 @@ describe('/libraries', () => { it('should remove offline files', async () => { const library = await utils.createLibrary(admin.accessToken, { ownerId: admin.userId, - importPaths: [`${testAssetDirInternal}/temp/offline2`], + importPaths: [`${testAssetDirInternal}/temp/offline`], }); - utils.createImageFile(`${testAssetDir}/temp/offline2/assetA.png`); + utils.createImageFile(`${testAssetDir}/temp/offline/online.png`); + utils.createImageFile(`${testAssetDir}/temp/offline/offline.png`); await scan(admin.accessToken, library.id); await utils.waitForQueueFinish(admin.accessToken, 'library'); @@ -646,9 +647,9 @@ describe('/libraries', () => { const { assets: initialAssets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id, }); - expect(initialAssets.count).toBe(1); + expect(initialAssets.count).toBe(2); - utils.removeImageFile(`${testAssetDir}/temp/offline2/assetA.png`); + utils.removeImageFile(`${testAssetDir}/temp/offline/offline.png`); await scan(admin.accessToken, library.id); await utils.waitForQueueFinish(admin.accessToken, 'library'); @@ -669,7 +670,54 @@ describe('/libraries', () => { const { assets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id }); - expect(assets.count).toBe(0); + expect(assets.count).toBe(1); + + utils.removeImageFile(`${testAssetDir}/temp/offline/online.png`); + }); + + it('should remove offline files from trash', async () => { + const library = await utils.createLibrary(admin.accessToken, { + ownerId: admin.userId, + importPaths: [`${testAssetDirInternal}/temp/offline`], + }); + + utils.createImageFile(`${testAssetDir}/temp/offline/online.png`); + utils.createImageFile(`${testAssetDir}/temp/offline/offline.png`); + + await scan(admin.accessToken, library.id); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + + const { assets: initialAssets } = await utils.metadataSearch(admin.accessToken, { + libraryId: library.id, + }); + + expect(initialAssets.count).toBe(2); + utils.removeImageFile(`${testAssetDir}/temp/offline/offline.png`); + + await scan(admin.accessToken, library.id); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + + const { assets: offlineAssets } = await utils.metadataSearch(admin.accessToken, { + libraryId: library.id, + isOffline: true, + }); + expect(offlineAssets.count).toBe(1); + + const { status } = await request(app) + .post(`/libraries/${library.id}/removeOffline`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send(); + expect(status).toBe(204); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + await utils.waitForQueueFinish(admin.accessToken, 'backgroundTask'); + + const { assets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id }); + + expect(assets.count).toBe(1); + expect(assets.items[0].isOffline).toBe(false); + expect(assets.items[0].originalPath).toEqual(`${testAssetDirInternal}/temp/offline/online.png`); + + utils.removeImageFile(`${testAssetDir}/temp/offline/online.png`); }); it('should not remove online files', async () => { diff --git a/server/src/interfaces/asset.interface.ts b/server/src/interfaces/asset.interface.ts index e323d98640a9e..9f7213de82b80 100644 --- a/server/src/interfaces/asset.interface.ts +++ b/server/src/interfaces/asset.interface.ts @@ -169,7 +169,12 @@ export interface IAssetRepository { order?: FindOptionsOrder, ): Promise; getWithout(pagination: PaginationOptions, property: WithoutProperty): Paginated; - getWith(pagination: PaginationOptions, property: WithProperty, libraryId?: string): Paginated; + getWith( + pagination: PaginationOptions, + property: WithProperty, + libraryId?: string, + withDeleted?: boolean, + ): Paginated; getRandom(userId: string, count: number): Promise; getFirstAssetForAlbumId(albumId: string): Promise; getLastUpdatedAssetForAlbumId(albumId: string): Promise; diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index dd526dd664b5a..77622b1618cdc 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -527,7 +527,12 @@ export class AssetRepository implements IAssetRepository { }); } - getWith(pagination: PaginationOptions, property: WithProperty, libraryId?: string): Paginated { + getWith( + pagination: PaginationOptions, + property: WithProperty, + libraryId?: string, + withDeleted = false, + ): Paginated { let where: FindOptionsWhere | FindOptionsWhere[] = {}; switch (property) { @@ -557,6 +562,7 @@ export class AssetRepository implements IAssetRepository { return paginate(this.repository, pagination, { where, + withDeleted, order: { // Ensures correct order when paginating createdAt: 'ASC', diff --git a/server/src/services/library.service.ts b/server/src/services/library.service.ts index bcd0a842c7065..2aa0df402a373 100644 --- a/server/src/services/library.service.ts +++ b/server/src/services/library.service.ts @@ -581,7 +581,7 @@ export class LibraryService { this.logger.debug(`Removing offline assets for library ${job.id}`); const assetPagination = usePagination(JOBS_LIBRARY_PAGINATION_SIZE, (pagination) => - this.assetRepository.getWith(pagination, WithProperty.IS_OFFLINE, job.id), + this.assetRepository.getWith(pagination, WithProperty.IS_OFFLINE, job.id, true), ); let offlineAssets = 0; From 438344fc8f2a8cbcb4c5d066306cc20f16ad6eb2 Mon Sep 17 00:00:00 2001 From: PyKen Date: Mon, 2 Sep 2024 22:31:02 +0900 Subject: [PATCH 090/160] fix(server): get assetFiles when retrieving assets WithoutProperty.THUMBNAIL (#12225) --- ...25258039306-UpsertMissingAssetJobStatus.ts | 21 +++++++++++++++++++ server/src/repositories/asset.repository.ts | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 server/src/migrations/1725258039306-UpsertMissingAssetJobStatus.ts diff --git a/server/src/migrations/1725258039306-UpsertMissingAssetJobStatus.ts b/server/src/migrations/1725258039306-UpsertMissingAssetJobStatus.ts new file mode 100644 index 0000000000000..8eb47db438c29 --- /dev/null +++ b/server/src/migrations/1725258039306-UpsertMissingAssetJobStatus.ts @@ -0,0 +1,21 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpsertMissingAssetJobStatus1725258039306 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `INSERT INTO "asset_job_status" ("assetId", "facesRecognizedAt", "metadataExtractedAt", "duplicatesDetectedAt", "previewAt", "thumbnailAt") SELECT "assetId", NULL, NULL, NULL, NULL, NULL FROM "asset_files" f WHERE "f"."path" IS NOT NULL ON CONFLICT DO NOTHING`, + ); + + await queryRunner.query( + `UPDATE "asset_job_status" SET "previewAt" = NOW() FROM "asset_files" f WHERE "previewAt" IS NULL AND "asset_job_status"."assetId" = "f"."assetId" AND "f"."type" = 'preview' AND "f"."path" IS NOT NULL`, + ); + + await queryRunner.query( + `UPDATE "asset_job_status" SET "thumbnailAt" = NOW() FROM "asset_files" f WHERE "thumbnailAt" IS NULL AND "asset_job_status"."assetId" = "f"."assetId" AND "f"."type" = 'thumbnail' AND "f"."path" IS NOT NULL`, + ); + } + + public async down(): Promise { + // do nothing + } +} diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index 77622b1618cdc..3763cccd53c5d 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -395,7 +395,7 @@ export class AssetRepository implements IAssetRepository { switch (property) { case WithoutProperty.THUMBNAIL: { - relations = { jobStatus: true }; + relations = { jobStatus: true, files: true }; where = [ { jobStatus: { previewAt: IsNull() }, isVisible: true }, { jobStatus: { thumbnailAt: IsNull() }, isVisible: true }, From b80cc0d90f18cc7a43848215ba143bfd56f1d5a5 Mon Sep 17 00:00:00 2001 From: Niklas Fischer Date: Mon, 2 Sep 2024 18:33:32 +0200 Subject: [PATCH 091/160] fix(web): German translation for explorer (#12180) fix German translation for explorer --- web/src/lib/i18n/de.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/lib/i18n/de.json b/web/src/lib/i18n/de.json index e06ff8069391a..95b9850634fb6 100644 --- a/web/src/lib/i18n/de.json +++ b/web/src/lib/i18n/de.json @@ -716,7 +716,7 @@ "expired": "Verfallen", "expires_date": "Läuft am {date} ab", "explore": "Erkunden", - "explorer": "Entdeccker", + "explorer": "Explorer", "export": "Exportieren", "export_as_json": "Als JSON exportieren", "extension": "Erweiterung", From bd6c5e1b1c991c3a1ba46ee0401ec94aa7920ebc Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 2 Sep 2024 14:39:16 -0500 Subject: [PATCH 092/160] feat(web): tag button in album/shared album (#12172) --- .../[[photos=photos]]/[[assetId=id]]/+page.svelte | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte index 46812ff289bc7..2c3f058e14daf 100644 --- a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -38,7 +38,7 @@ import { assetViewingStore } from '$lib/stores/asset-viewing.store'; import { AssetStore } from '$lib/stores/assets.store'; import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store'; - import { user } from '$lib/stores/user.store'; + import { preferences, user } from '$lib/stores/user.store'; import { handlePromiseError } from '$lib/utils'; import { downloadAlbum } from '$lib/utils/asset-utils'; import { openFileUploadDialog } from '$lib/utils/file-uploader'; @@ -85,6 +85,7 @@ import { t } from 'svelte-i18n'; import { onDestroy } from 'svelte'; import { confirmAlbumDelete } from '$lib/utils/album-utils'; + import TagAction from '$lib/components/photos-page/actions/tag-action.svelte'; export let data: PageData; @@ -458,6 +459,11 @@ {/if} assetStore.triggerUpdate()} /> {/if} + + {#if $preferences.tags.enabled && isAllUserOwned} + + {/if} + {#if isOwned || isAllUserOwned} {/if} From 862d6d9abe68d255f3f44ced12dbdf5fb01d5da6 Mon Sep 17 00:00:00 2001 From: Vietbao Tran <46217210+TapuCosmo@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:39:55 -0700 Subject: [PATCH 093/160] feat(web): load original panorama image when zoomed in to 75% or above (#12222) * feat(web): load original panorama image when zoomed in to 75% or above * add checks that original 360 image is web compatible and better error handling * fix web compatability check typing * fix asset type --- .../asset-viewer/panorama-viewer.svelte | 15 +++++++++++++-- .../photo-sphere-viewer-adapter.svelte | 16 ++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/web/src/lib/components/asset-viewer/panorama-viewer.svelte b/web/src/lib/components/asset-viewer/panorama-viewer.svelte index 71ed4b899779a..dee9a5f8ec542 100644 --- a/web/src/lib/components/asset-viewer/panorama-viewer.svelte +++ b/web/src/lib/components/asset-viewer/panorama-viewer.svelte @@ -1,11 +1,12 @@
@@ -34,7 +38,14 @@ {#await Promise.all([loadAssetData(), import('./photo-sphere-viewer-adapter.svelte'), ...photoSphereConfigs])} {:then [data, module, adapter, plugins, navbar]} - + {:catch} {$t('errors.failed_to_load_asset')} {/await} diff --git a/web/src/lib/components/asset-viewer/photo-sphere-viewer-adapter.svelte b/web/src/lib/components/asset-viewer/photo-sphere-viewer-adapter.svelte index 0c0e707693b7c..30a2018febc80 100644 --- a/web/src/lib/components/asset-viewer/photo-sphere-viewer-adapter.svelte +++ b/web/src/lib/components/asset-viewer/photo-sphere-viewer-adapter.svelte @@ -1,6 +1,7 @@ + + diff --git a/web/src/lib/i18n/en.json b/web/src/lib/i18n/en.json index d0d330480ab2c..25f3b6ea2faab 100644 --- a/web/src/lib/i18n/en.json +++ b/web/src/lib/i18n/en.json @@ -1191,7 +1191,7 @@ "to_change_password": "Change password", "to_favorite": "Favorite", "to_login": "Login", - "to_root": "To root", + "to_parent": "Go to parent", "to_trash": "Trash", "toggle_settings": "Toggle settings", "toggle_theme": "Toggle dark theme", diff --git a/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte index a8b8602c02d00..1ffa64d3a3213 100644 --- a/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -1,8 +1,6 @@ @@ -14,7 +14,6 @@ - From ee6550c02cc9154485154772bd26a14859b3eb21 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 4 Sep 2024 09:20:45 -0400 Subject: [PATCH 124/160] feat(web): add Malay language (#12311) feat(web): add ms.json --- web/src/lib/constants.ts | 1 + web/src/lib/i18n/ms.json | 1 + 2 files changed, 2 insertions(+) create mode 100644 web/src/lib/i18n/ms.json diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts index ce5cefd8153d2..05011680dcc41 100644 --- a/web/src/lib/constants.ts +++ b/web/src/lib/constants.ts @@ -284,6 +284,7 @@ export const langs = [ { name: 'Lithuanian', code: 'lt', loader: () => import('$lib/i18n/lt.json') }, { name: 'Latvian', code: 'lv', loader: () => import('$lib/i18n/lv.json') }, { name: 'Mongolian', code: 'mn', loader: () => import('$lib/i18n/mn.json') }, + { name: 'Malay', code: 'ms', loader: () => import('$lib/i18n/ms.json') }, { name: 'Norwegian Bokmål', code: 'nb-NO', weblateCode: 'nb_NO', loader: () => import('$lib/i18n/nb_NO.json') }, { name: 'Dutch', code: 'nl', loader: () => import('$lib/i18n/nl.json') }, { name: 'Polish', code: 'pl', loader: () => import('$lib/i18n/pl.json') }, diff --git a/web/src/lib/i18n/ms.json b/web/src/lib/i18n/ms.json new file mode 100644 index 0000000000000..0967ef424bce6 --- /dev/null +++ b/web/src/lib/i18n/ms.json @@ -0,0 +1 @@ +{} From cbb0a7f8d40f87535c877257e598853c29f66d32 Mon Sep 17 00:00:00 2001 From: Carsten Otto Date: Wed, 4 Sep 2024 16:27:04 +0200 Subject: [PATCH 125/160] fix(server): parse time zone with explicit zero offset (#12307) * fix(server): fix test: use data as returned by exiftool-vendored * fix(server): retain +00:00 timezone if set explicitly --- server/src/services/metadata.service.spec.ts | 42 ++++++++++++++++---- server/src/services/metadata.service.ts | 27 +++++++++++-- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index 2fc95df00e629..834fd16afc01c 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -1,4 +1,4 @@ -import { BinaryField } from 'exiftool-vendored'; +import { BinaryField, ExifDateTime } from 'exiftool-vendored'; import { randomBytes } from 'node:crypto'; import { Stats } from 'node:fs'; import { constants } from 'node:fs/promises'; @@ -746,6 +746,8 @@ describe(MetadataService.name, () => { }); it('should save all metadata', async () => { + const dateForTest = new Date('1970-01-01T00:00:00.000-11:30'); + const tags: ImmichTags = { BitsPerSample: 1, ComponentBitDepth: 1, @@ -753,7 +755,7 @@ describe(MetadataService.name, () => { BitDepth: 1, ColorBitDepth: 1, ColorSpace: '1', - DateTimeOriginal: new Date('1970-01-01').toISOString(), + DateTimeOriginal: ExifDateTime.fromISO(dateForTest.toISOString()), ExposureTime: '100ms', FocalLength: 20, ImageDescription: 'test description', @@ -762,11 +764,11 @@ describe(MetadataService.name, () => { MediaGroupUUID: 'livePhoto', Make: 'test-factory', Model: "'mockel'", - ModifyDate: new Date('1970-01-01').toISOString(), + ModifyDate: ExifDateTime.fromISO(dateForTest.toISOString()), Orientation: 0, ProfileDescription: 'extensive description', ProjectionType: 'equirectangular', - tz: '+02:00', + tz: 'UTC-11:30', Rating: 3, }; assetMock.getByIds.mockResolvedValue([assetStub.image]); @@ -779,7 +781,7 @@ describe(MetadataService.name, () => { bitsPerSample: expect.any(Number), autoStackId: null, colorspace: tags.ColorSpace, - dateTimeOriginal: new Date('1970-01-01'), + dateTimeOriginal: dateForTest, description: tags.ImageDescription, exifImageHeight: null, exifImageWidth: null, @@ -805,11 +807,37 @@ describe(MetadataService.name, () => { expect(assetMock.update).toHaveBeenCalledWith({ id: assetStub.image.id, duration: null, - fileCreatedAt: new Date('1970-01-01'), - localDateTime: new Date('1970-01-01'), + fileCreatedAt: dateForTest, + localDateTime: dateForTest, }); }); + it('should extract +00:00 timezone from raw value', async () => { + // exiftool-vendored returns "no timezone" information even though "+00:00" might be set explicitly + // https://github.com/photostructure/exiftool-vendored.js/issues/203 + + // this only tests our assumptions of exiftool-vendored, demonstrating the issue + const someDate = '2024-09-01T00:00:00.000'; + expect(ExifDateTime.fromISO(someDate + 'Z')?.zone).toBe('UTC'); + expect(ExifDateTime.fromISO(someDate + '+00:00')?.zone).toBe('UTC'); // this is the issue, should be UTC+0 + expect(ExifDateTime.fromISO(someDate + '+04:00')?.zone).toBe('UTC+4'); + + const tags: ImmichTags = { + DateTimeOriginal: ExifDateTime.fromISO(someDate + '+00:00'), + tz: undefined, + }; + assetMock.getByIds.mockResolvedValue([assetStub.image]); + metadataMock.readTags.mockResolvedValue(tags); + + await sut.handleMetadataExtraction({ id: assetStub.image.id }); + expect(assetMock.getByIds).toHaveBeenCalledWith([assetStub.image.id]); + expect(assetMock.upsertExif).toHaveBeenCalledWith( + expect.objectContaining({ + timeZone: 'UTC+0', + }), + ); + }); + it('should extract duration', async () => { assetMock.getByIds.mockResolvedValue([{ ...assetStub.video }]); mediaMock.probe.mockResolvedValue({ diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index 29aebc4a36abb..7eab4702ad630 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -531,12 +531,16 @@ export class MetadataService { this.logger.verbose('Exif Tags', exifTags); + const dateTimeOriginalWithRawValue = this.getDateTimeOriginalWithRawValue(exifTags); + const dateTimeOriginal = dateTimeOriginalWithRawValue.exifDate ?? asset.fileCreatedAt; + const timeZone = this.getTimeZone(exifTags, dateTimeOriginalWithRawValue.rawValue); + const exifData = { // altitude: tags.GPSAltitude ?? null, assetId: asset.id, bitsPerSample: this.getBitsPerSample(exifTags), colorspace: exifTags.ColorSpace ?? null, - dateTimeOriginal: this.getDateTimeOriginal(exifTags) ?? asset.fileCreatedAt, + dateTimeOriginal, description: String(exifTags.ImageDescription || exifTags.Description || '').trim(), exifImageHeight: validate(exifTags.ImageHeight), exifImageWidth: validate(exifTags.ImageWidth), @@ -557,7 +561,7 @@ export class MetadataService { orientation: validate(exifTags.Orientation)?.toString() ?? null, profileDescription: exifTags.ProfileDescription || null, projectionType: exifTags.ProjectionType ? String(exifTags.ProjectionType).toUpperCase() : null, - timeZone: exifTags.tz ?? null, + timeZone, rating: exifTags.Rating ?? null, }; @@ -578,10 +582,25 @@ export class MetadataService { } private getDateTimeOriginal(tags: ImmichTags | Tags | null) { + return this.getDateTimeOriginalWithRawValue(tags).exifDate; + } + + private getDateTimeOriginalWithRawValue(tags: ImmichTags | Tags | null): { exifDate: Date | null; rawValue: string } { if (!tags) { - return null; + return { exifDate: null, rawValue: '' }; } - return exifDate(firstDateTime(tags as Tags, EXIF_DATE_TAGS)); + const first = firstDateTime(tags as Tags, EXIF_DATE_TAGS); + return { exifDate: exifDate(first), rawValue: first?.rawValue ?? '' }; + } + + private getTimeZone(exifTags: ImmichTags, rawValue: string) { + const timeZone = exifTags.tz ?? null; + if (timeZone == null && rawValue.endsWith('+00:00')) { + // exiftool-vendored returns "no timezone" information even though "+00:00" might be set explicitly + // https://github.com/photostructure/exiftool-vendored.js/issues/203 + return 'UTC+0'; + } + return timeZone; } private getBitsPerSample(tags: ImmichTags): number | null { From 4bf82fb4c4880d4cf43b2935cdb713bb02867697 Mon Sep 17 00:00:00 2001 From: Carsten Otto Date: Wed, 4 Sep 2024 16:47:40 +0200 Subject: [PATCH 126/160] fix(web): retain selected time zone offset also for +00:00 (#12310) Co-authored-by: Alex --- .../shared-components/change-date.svelte | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/web/src/lib/components/shared-components/change-date.svelte b/web/src/lib/components/shared-components/change-date.svelte index 962a97ecf7e0c..916b9349f9f7c 100644 --- a/web/src/lib/components/shared-components/change-date.svelte +++ b/web/src/lib/components/shared-components/change-date.svelte @@ -10,18 +10,25 @@ type ZoneOption = { /** - * Timezone name + * Timezone name with offset * * e.g. Asia/Jerusalem (+03:00) */ label: string; /** - * Timezone offset + * Timezone name * - * e.g. UTC+01:00 + * e.g. Asia/Jerusalem */ value: string; + + /** + * Timezone offset in minutes + * + * e.g. 300 + */ + offsetMinutes: number; }; const timezones: ZoneOption[] = Intl.supportedValuesOf('timeZone') @@ -37,21 +44,23 @@ const offset = zone.toFormat('ZZ'); return { label: `${zone.zoneName} (${offset})`, - value: 'UTC' + offset, + value: zone.zoneName, + offsetMinutes: zone.offset, }; }); - const initialOption = timezones.find((item) => item.value === 'UTC' + initialDate.toFormat('ZZ')); + const initialOption = timezones.find((item) => item.offsetMinutes === initialDate.offset); let selectedOption = initialOption && { label: initialOption?.label || '', + offsetMinutes: initialOption?.offsetMinutes || 0, value: initialOption?.value || '', }; let selectedDate = initialDate.toFormat("yyyy-MM-dd'T'HH:mm"); - // Keep local time if not it's really confusing - $: date = DateTime.fromISO(selectedDate).setZone(selectedOption?.value, { keepLocalTime: true }); + // when changing the time zone, assume the configured date/time is meant for that time zone (instead of updating it) + $: date = DateTime.fromISO(selectedDate, { zone: selectedOption?.value, setZone: true }); const dispatch = createEventDispatcher<{ cancel: void; From d685bc1f340762156619f8f403ac3f721b467c8f Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 4 Sep 2024 10:39:31 -0500 Subject: [PATCH 127/160] chore(mobile): handle sync album on duplicated (#12173) * chore(mobile): handle sync album on duplicated * remove check for duplicate in manual sync * linting --- mobile/lib/services/asset.service.dart | 13 ------------- mobile/lib/services/backup.service.dart | 2 +- mobile/pubspec.lock | 4 ++-- 3 files changed, 3 insertions(+), 16 deletions(-) diff --git a/mobile/lib/services/asset.service.dart b/mobile/lib/services/asset.service.dart index 17508cba5153e..c4f258e259129 100644 --- a/mobile/lib/services/asset.service.dart +++ b/mobile/lib/services/asset.service.dart @@ -8,7 +8,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/exif_info.entity.dart'; -import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/models/backup/backup_candidate.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; @@ -309,18 +308,6 @@ class AssetService { useTimeFilter: false, ); - final duplicates = await _apiService.assetsApi.checkExistingAssets( - CheckExistingAssetsDto( - deviceAssetIds: candidates.map((c) => c.asset.id).toList(), - deviceId: Store.get(StoreKey.deviceId), - ), - ); - - if (duplicates != null) { - candidates - .removeWhere((c) => !duplicates.existingIds.contains(c.asset.id)); - } - await refreshRemoteAssets(); final remoteAssets = await _db.assets .where() diff --git a/mobile/lib/services/backup.service.dart b/mobile/lib/services/backup.service.dart index 12edd14d609ca..858499443ee1d 100644 --- a/mobile/lib/services/backup.service.dart +++ b/mobile/lib/services/backup.service.dart @@ -484,7 +484,7 @@ class BackupService { ), ); - if (shouldSyncAlbums && !isDuplicate) { + if (shouldSyncAlbums) { await _albumService.syncUploadAlbums( candidate.albumNames, [responseBody['id'] as String], diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 14b487ce4dd48..c9493f6490b72 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -1737,10 +1737,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.4" + version: "14.2.5" wakelock_plus: dependency: "direct main" description: From 1783dfd393168dd98ece34e0a6fbaec6d37a62e5 Mon Sep 17 00:00:00 2001 From: Zack Pollard Date: Wed, 4 Sep 2024 17:02:37 +0100 Subject: [PATCH 128/160] fix(web): handle RTL languages in the map component (#12308) --- web/package-lock.json | 115 +++++++++++++++++- web/package.json | 3 +- .../shared-components/map/map.svelte | 3 + 3 files changed, 119 insertions(+), 2 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 97b1a303a573e..4ddc6d9baa966 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.7.8", "@immich/sdk": "file:../open-api/typescript-sdk", + "@mapbox/mapbox-gl-rtl-text": "^0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.7.1", "@photo-sphere-viewer/equirectangular-video-adapter": "^5.7.2", @@ -27,7 +28,7 @@ "svelte-gestures": "^5.0.4", "svelte-i18n": "^4.0.0", "svelte-local-storage-store": "^0.6.4", - "svelte-maplibre": "^0.9.0", + "svelte-maplibre": "^0.9.13", "thumbhash": "^0.1.1" }, "devDependencies": { @@ -1446,6 +1447,13 @@ "geojson-rewind": "geojson-rewind" } }, + "node_modules/@mapbox/geojson-types": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-types/-/geojson-types-1.0.2.tgz", + "integrity": "sha512-e9EBqHHv3EORHrSfbR9DqecPNn+AmuAoQxV6aL8Xu30bJMJR1o8PZLZzpk1Wq7/NfCbuhmakHTPYRhoqLsXRnw==", + "license": "ISC", + "peer": true + }, "node_modules/@mapbox/jsonlint-lines-primitives": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", @@ -1454,6 +1462,25 @@ "node": ">= 0.6" } }, + "node_modules/@mapbox/mapbox-gl-rtl-text": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-rtl-text/-/mapbox-gl-rtl-text-0.2.3.tgz", + "integrity": "sha512-RaCYfnxULUUUxNwcUimV9C/o2295ktTyLEUzD/+VWkqXqvaVfFcZ5slytGzb2Sd/Jj4MlbxD0DCZbfa6CzcmMw==", + "license": "BSD-2-Clause", + "peerDependencies": { + "mapbox-gl": ">=0.32.1 <2.0.0" + } + }, + "node_modules/@mapbox/mapbox-gl-supported": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.5.0.tgz", + "integrity": "sha512-/PT1P6DNf7vjEEiPkVIRJkvibbqWtqnyGaBz3nfRdcxclNSnSdaLU5tfAgcD7I8Yt5i+L19s406YLl1koLnLbg==", + "license": "BSD-3-Clause", + "peer": true, + "peerDependencies": { + "mapbox-gl": ">=0.32.1 <2.0.0" + } + }, "node_modules/@mapbox/point-geometry": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", @@ -3307,6 +3334,13 @@ "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", "dev": true }, + "node_modules/csscolorparser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", + "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==", + "license": "MIT", + "peer": true + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -4563,6 +4597,13 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/grid-index": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/grid-index/-/grid-index-1.1.0.tgz", + "integrity": "sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==", + "license": "ISC", + "peer": true + }, "node_modules/handlebars": { "version": "4.7.8", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", @@ -5388,6 +5429,78 @@ "node": ">=10" } }, + "node_modules/mapbox-gl": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.13.3.tgz", + "integrity": "sha512-p8lJFEiqmEQlyv+DQxFAOG/XPWN0Wp7j/Psq93Zywz7qt9CcUKFYDBOoOEKzqe6gudHVJY8/Bhqw6VDpX2lSBg==", + "license": "SEE LICENSE IN LICENSE.txt", + "peer": true, + "dependencies": { + "@mapbox/geojson-rewind": "^0.5.2", + "@mapbox/geojson-types": "^1.0.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/mapbox-gl-supported": "^1.5.0", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^1.1.1", + "@mapbox/unitbezier": "^0.0.0", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "csscolorparser": "~1.0.3", + "earcut": "^2.2.2", + "geojson-vt": "^3.2.1", + "gl-matrix": "^3.2.1", + "grid-index": "^1.1.0", + "murmurhash-js": "^1.0.0", + "pbf": "^3.2.1", + "potpack": "^1.0.1", + "quickselect": "^2.0.0", + "rw": "^1.3.3", + "supercluster": "^7.1.0", + "tinyqueue": "^2.0.3", + "vt-pbf": "^3.1.1" + }, + "engines": { + "node": ">=6.4.0" + } + }, + "node_modules/mapbox-gl/node_modules/@mapbox/tiny-sdf": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-1.2.5.tgz", + "integrity": "sha512-cD8A/zJlm6fdJOk6DqPUV8mcpyJkRz2x2R+/fYcWDYG3oWbG7/L7Yl/WqQ1VZCjnL9OTIMAn6c+BC5Eru4sQEw==", + "license": "BSD-2-Clause", + "peer": true + }, + "node_modules/mapbox-gl/node_modules/@mapbox/unitbezier": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz", + "integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==", + "license": "BSD-2-Clause", + "peer": true + }, + "node_modules/mapbox-gl/node_modules/kdbush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz", + "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==", + "license": "ISC", + "peer": true + }, + "node_modules/mapbox-gl/node_modules/potpack": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", + "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==", + "license": "ISC", + "peer": true + }, + "node_modules/mapbox-gl/node_modules/supercluster": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.5.tgz", + "integrity": "sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==", + "license": "ISC", + "peer": true, + "dependencies": { + "kdbush": "^3.0.0" + } + }, "node_modules/maplibre-gl": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.0.1.tgz", diff --git a/web/package.json b/web/package.json index 1996f4eaefe1c..1ba350022d98c 100644 --- a/web/package.json +++ b/web/package.json @@ -67,6 +67,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.7.8", "@immich/sdk": "file:../open-api/typescript-sdk", + "@mapbox/mapbox-gl-rtl-text": "^0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.7.1", "@photo-sphere-viewer/equirectangular-video-adapter": "^5.7.2", @@ -83,7 +84,7 @@ "svelte-gestures": "^5.0.4", "svelte-i18n": "^4.0.0", "svelte-local-storage-store": "^0.6.4", - "svelte-maplibre": "^0.9.0", + "svelte-maplibre": "^0.9.13", "thumbhash": "^0.1.1" }, "volta": { diff --git a/web/src/lib/components/shared-components/map/map.svelte b/web/src/lib/components/shared-components/map/map.svelte index 45d2879b37fff..7d0dbbee6fa40 100644 --- a/web/src/lib/components/shared-components/map/map.svelte +++ b/web/src/lib/components/shared-components/map/map.svelte @@ -7,6 +7,7 @@ import { mdiCog, mdiMap, mdiMapMarker } from '@mdi/js'; import type { Feature, GeoJsonProperties, Geometry, Point } from 'geojson'; import type { GeoJSONSource, LngLatLike, StyleSpecification } from 'maplibre-gl'; + import mapboxRtlUrl from '@mapbox/mapbox-gl-rtl-text/mapbox-gl-rtl-text.min.js?url'; import maplibregl from 'maplibre-gl'; import { createEventDispatcher } from 'svelte'; import { @@ -51,6 +52,8 @@ let map: maplibregl.Map; let marker: maplibregl.Marker | null = null; + void maplibregl.setRTLTextPlugin(mapboxRtlUrl, true); + $: style = (() => getMapStyle({ theme: ($mapSettings.allowDarkMode ? $colorTheme.value : Theme.LIGHT) as unknown as MapTheme, From 12b65e3c24bc78d7cef6d33a713723f150c07528 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 4 Sep 2024 13:32:43 -0400 Subject: [PATCH 129/160] fix(server): auto-reconnect to database (#12320) --- server/src/app.module.ts | 17 +++++-- server/src/interfaces/database.interface.ts | 1 + server/src/middleware/error.interceptor.ts | 8 ++-- .../src/middleware/global-exception.filter.ts | 47 +++++++++++++++++++ .../src/middleware/http-exception.filter.ts | 39 --------------- .../src/repositories/database.repository.ts | 13 +++++ server/src/repositories/logger.repository.ts | 2 +- server/src/services/database.service.ts | 25 ++++++++++ .../src/utils/{logger-colors.ts => logger.ts} | 23 +++++++++ .../repositories/database.repository.mock.ts | 1 + 10 files changed, 130 insertions(+), 46 deletions(-) create mode 100644 server/src/middleware/global-exception.filter.ts delete mode 100644 server/src/middleware/http-exception.filter.ts rename server/src/utils/{logger-colors.ts => logger.ts} (55%) diff --git a/server/src/app.module.ts b/server/src/app.module.ts index c6cd68a96ff77..9446010127450 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -18,10 +18,11 @@ import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { AuthGuard } from 'src/middleware/auth.guard'; import { ErrorInterceptor } from 'src/middleware/error.interceptor'; import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor'; -import { HttpExceptionFilter } from 'src/middleware/http-exception.filter'; +import { GlobalExceptionFilter } from 'src/middleware/global-exception.filter'; import { LoggingInterceptor } from 'src/middleware/logging.interceptor'; import { repositories } from 'src/repositories'; import { services } from 'src/services'; +import { DatabaseService } from 'src/services/database.service'; import { setupEventHandlers } from 'src/utils/events'; import { otelConfig } from 'src/utils/instrumentation'; @@ -29,7 +30,7 @@ const common = [...services, ...repositories]; const middleware = [ FileUploadInterceptor, - { provide: APP_FILTER, useClass: HttpExceptionFilter }, + { provide: APP_FILTER, useClass: GlobalExceptionFilter }, { provide: APP_PIPE, useValue: new ValidationPipe({ transform: true, whitelist: true }) }, { provide: APP_INTERCEPTOR, useClass: LoggingInterceptor }, { provide: APP_INTERCEPTOR, useClass: ErrorInterceptor }, @@ -43,7 +44,17 @@ const imports = [ ConfigModule.forRoot(immichAppConfig), EventEmitterModule.forRoot(), OpenTelemetryModule.forRoot(otelConfig), - TypeOrmModule.forRoot(databaseConfig), + TypeOrmModule.forRootAsync({ + inject: [ModuleRef], + useFactory: (moduleRef: ModuleRef) => { + return { + ...databaseConfig, + poolErrorHandler: (error) => { + moduleRef.get(DatabaseService, { strict: false }).handleConnectionError(error); + }, + }; + }, + }), TypeOrmModule.forFeature(entities), ]; diff --git a/server/src/interfaces/database.interface.ts b/server/src/interfaces/database.interface.ts index 98bb0c02889c2..373f1091429d7 100644 --- a/server/src/interfaces/database.interface.ts +++ b/server/src/interfaces/database.interface.ts @@ -40,6 +40,7 @@ export interface VectorUpdateResult { export const IDatabaseRepository = 'IDatabaseRepository'; export interface IDatabaseRepository { + reconnect(): Promise; getExtensionVersion(extension: DatabaseExtension): Promise; getExtensionVersionRange(extension: VectorExtension): string; getPostgresVersion(): Promise; diff --git a/server/src/middleware/error.interceptor.ts b/server/src/middleware/error.interceptor.ts index a0c333e4b24cb..5d93b40dc22d0 100644 --- a/server/src/middleware/error.interceptor.ts +++ b/server/src/middleware/error.interceptor.ts @@ -9,6 +9,7 @@ import { } from '@nestjs/common'; import { Observable, catchError, throwError } from 'rxjs'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; +import { logGlobalError } from 'src/utils/logger'; import { routeToErrorMessage } from 'src/utils/misc'; @Injectable() @@ -25,9 +26,10 @@ export class ErrorInterceptor implements NestInterceptor { return error; } - const errorMessage = routeToErrorMessage(context.getHandler().name); - this.logger.error(errorMessage, error, error?.errors, error?.stack); - return new InternalServerErrorException(errorMessage); + logGlobalError(this.logger, error); + + const message = routeToErrorMessage(context.getHandler().name); + return new InternalServerErrorException(message); }), ), ); diff --git a/server/src/middleware/global-exception.filter.ts b/server/src/middleware/global-exception.filter.ts new file mode 100644 index 0000000000000..6200363e86e9e --- /dev/null +++ b/server/src/middleware/global-exception.filter.ts @@ -0,0 +1,47 @@ +import { ArgumentsHost, Catch, ExceptionFilter, HttpException, Inject } from '@nestjs/common'; +import { Response } from 'express'; +import { ClsService } from 'nestjs-cls'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; +import { logGlobalError } from 'src/utils/logger'; + +@Catch() +export class GlobalExceptionFilter implements ExceptionFilter { + constructor( + @Inject(ILoggerRepository) private logger: ILoggerRepository, + private cls: ClsService, + ) { + this.logger.setContext(GlobalExceptionFilter.name); + } + + catch(error: Error, host: ArgumentsHost) { + const ctx = host.switchToHttp(); + const response = ctx.getResponse(); + const { status, body } = this.fromError(error); + if (!response.headersSent) { + response.status(status).json({ ...body, statusCode: status, correlationId: this.cls.getId() }); + } + } + + private fromError(error: Error) { + logGlobalError(this.logger, error); + + if (error instanceof HttpException) { + const status = error.getStatus(); + let body = error.getResponse(); + + // unclear what circumstances would return a string + if (typeof body === 'string') { + body = { message: body }; + } + + return { status, body }; + } + + return { + status: 500, + body: { + message: 'Internal server error', + }, + }; + } +} diff --git a/server/src/middleware/http-exception.filter.ts b/server/src/middleware/http-exception.filter.ts deleted file mode 100644 index 3306b50ca67e0..0000000000000 --- a/server/src/middleware/http-exception.filter.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { ArgumentsHost, Catch, ExceptionFilter, HttpException, Inject } from '@nestjs/common'; -import { Response } from 'express'; -import { ClsService } from 'nestjs-cls'; -import { ILoggerRepository } from 'src/interfaces/logger.interface'; - -@Catch(HttpException) -export class HttpExceptionFilter implements ExceptionFilter { - constructor( - @Inject(ILoggerRepository) private logger: ILoggerRepository, - private cls: ClsService, - ) { - this.logger.setContext(HttpExceptionFilter.name); - } - - catch(exception: HttpException, host: ArgumentsHost) { - const ctx = host.switchToHttp(); - const response = ctx.getResponse(); - const status = exception.getStatus(); - - this.logger.debug(`HttpException(${status}) ${JSON.stringify(exception.getResponse())}`); - - let responseBody = exception.getResponse(); - // unclear what circumstances would return a string - if (typeof responseBody === 'string') { - responseBody = { - error: 'Unknown', - message: responseBody, - statusCode: status, - }; - } - - if (!response.headersSent) { - response.status(status).json({ - ...responseBody, - correlationId: this.cls.getId(), - }); - } - } -} diff --git a/server/src/repositories/database.repository.ts b/server/src/repositories/database.repository.ts index 9ee7f8e6fccea..0453421a39d1b 100644 --- a/server/src/repositories/database.repository.ts +++ b/server/src/repositories/database.repository.ts @@ -31,6 +31,19 @@ export class DatabaseRepository implements IDatabaseRepository { this.logger.setContext(DatabaseRepository.name); } + async reconnect() { + try { + if (this.dataSource.isInitialized) { + await this.dataSource.destroy(); + } + const { isInitialized } = await this.dataSource.initialize(); + return isInitialized; + } catch (error) { + this.logger.error(`Database connection failed: ${error}`); + return false; + } + } + async getExtensionVersion(extension: DatabaseExtension): Promise { const [res]: ExtensionVersion[] = await this.dataSource.query( `SELECT default_version as "availableVersion", installed_version as "installedVersion" diff --git a/server/src/repositories/logger.repository.ts b/server/src/repositories/logger.repository.ts index 1527965b496cd..1e0c7b74d973e 100644 --- a/server/src/repositories/logger.repository.ts +++ b/server/src/repositories/logger.repository.ts @@ -3,7 +3,7 @@ import { isLogLevelEnabled } from '@nestjs/common/services/utils/is-log-level-en import { ClsService } from 'nestjs-cls'; import { LogLevel } from 'src/config'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; -import { LogColor } from 'src/utils/logger-colors'; +import { LogColor } from 'src/utils/logger'; const LOG_LEVELS = [LogLevel.VERBOSE, LogLevel.DEBUG, LogLevel.LOG, LogLevel.WARN, LogLevel.ERROR, LogLevel.FATAL]; diff --git a/server/src/services/database.service.ts b/server/src/services/database.service.ts index d2a2813a0550c..a5280ff28be23 100644 --- a/server/src/services/database.service.ts +++ b/server/src/services/database.service.ts @@ -1,4 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; +import { Duration } from 'luxon'; import semver from 'semver'; import { getVectorExtension } from 'src/database.config'; import { OnEmit } from 'src/decorators'; @@ -59,8 +60,12 @@ const messages = { If ${name} ${installedVersion} is compatible with Immich, please ensure the Postgres instance has this available.`, }; +const RETRY_DURATION = Duration.fromObject({ seconds: 5 }); + @Injectable() export class DatabaseService { + private reconnection?: NodeJS.Timeout; + constructor( @Inject(IDatabaseRepository) private databaseRepository: IDatabaseRepository, @Inject(ILoggerRepository) private logger: ILoggerRepository, @@ -117,6 +122,26 @@ export class DatabaseService { }); } + handleConnectionError(error: Error) { + if (this.reconnection) { + return; + } + + this.logger.error(`Database disconnected: ${error}`); + this.reconnection = setInterval(() => void this.reconnect(), RETRY_DURATION.toMillis()); + } + + private async reconnect() { + const isConnected = await this.databaseRepository.reconnect(); + if (isConnected) { + this.logger.log('Database reconnected'); + clearInterval(this.reconnection); + delete this.reconnection; + } else { + this.logger.warn(`Database connection failed, retrying in ${RETRY_DURATION.toHuman()}`); + } + } + private async createExtension(extension: DatabaseExtension) { try { await this.databaseRepository.createExtension(extension); diff --git a/server/src/utils/logger-colors.ts b/server/src/utils/logger.ts similarity index 55% rename from server/src/utils/logger-colors.ts rename to server/src/utils/logger.ts index 36104ee520c82..d4eb02ead21ab 100644 --- a/server/src/utils/logger-colors.ts +++ b/server/src/utils/logger.ts @@ -1,3 +1,7 @@ +import { HttpException } from '@nestjs/common'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; +import { TypeORMError } from 'typeorm'; + type ColorTextFn = (text: string) => string; const isColorAllowed = () => !process.env.NO_COLOR; @@ -15,3 +19,22 @@ export const LogColor = { export const LogStyle = { bold: colorIfAllowed((text: string) => `\u001B[1m${text}\u001B[0m`), }; + +export const logGlobalError = (logger: ILoggerRepository, error: Error) => { + if (error instanceof HttpException) { + const status = error.getStatus(); + const response = error.getResponse(); + logger.debug(`HttpException(${status}): ${JSON.stringify(response)}`); + return; + } + + if (error instanceof TypeORMError) { + logger.error(`Database error: ${error}`); + return; + } + + if (error instanceof Error) { + logger.error(`Unknown error: ${error}`); + return; + } +}; diff --git a/server/test/repositories/database.repository.mock.ts b/server/test/repositories/database.repository.mock.ts index e8b0817dfe0b7..0e1d4ab3e71dd 100644 --- a/server/test/repositories/database.repository.mock.ts +++ b/server/test/repositories/database.repository.mock.ts @@ -3,6 +3,7 @@ import { Mocked, vitest } from 'vitest'; export const newDatabaseRepositoryMock = (): Mocked => { return { + reconnect: vitest.fn(), getExtensionVersion: vitest.fn(), getExtensionVersionRange: vitest.fn(), getPostgresVersion: vitest.fn().mockResolvedValue('14.10 (Debian 14.10-1.pgdg120+1)'), From f8211a128e4f92cd79bdf425f73494b9841e9dd9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 14:36:12 -0400 Subject: [PATCH 130/160] fix(deps): update machine-learning (#12257) --- machine-learning/Dockerfile | 4 +- machine-learning/export/Dockerfile | 2 +- machine-learning/poetry.lock | 123 +++++++++++++++-------------- 3 files changed, 65 insertions(+), 64 deletions(-) diff --git a/machine-learning/Dockerfile b/machine-learning/Dockerfile index 8fc72b308f08a..f680aac826af3 100644 --- a/machine-learning/Dockerfile +++ b/machine-learning/Dockerfile @@ -1,6 +1,6 @@ ARG DEVICE=cpu -FROM python:3.11-bookworm@sha256:f7543d9969bdc112dd9819ca642e14433fdacfe857f170f6b803392fc7e451ad AS builder-cpu +FROM python:3.11-bookworm@sha256:20c1819af5af3acba0b2b66074a2615e398ceee6842adf03cd7ad5f8d0ee3daf AS builder-cpu FROM builder-cpu AS builder-openvino @@ -34,7 +34,7 @@ RUN python3 -m venv /opt/venv COPY poetry.lock pyproject.toml ./ RUN poetry install --sync --no-interaction --no-ansi --no-root --with ${DEVICE} --without dev -FROM python:3.11-slim-bookworm@sha256:ad5dadd957a398226996bc4846e522c39f2a77340b531b28aaab85b2d361210b AS prod-cpu +FROM python:3.11-slim-bookworm@sha256:ed4e985674f478c90ce879e9aa224fbb772c84e39b4aed5155b9e2280f131039 AS prod-cpu FROM prod-cpu AS prod-openvino diff --git a/machine-learning/export/Dockerfile b/machine-learning/export/Dockerfile index d458d92d15014..eaa35d14be0dd 100644 --- a/machine-learning/export/Dockerfile +++ b/machine-learning/export/Dockerfile @@ -1,4 +1,4 @@ -FROM mambaorg/micromamba:bookworm-slim@sha256:475730daef12ff9c0733e70092aeeefdf4c373a584c952dac3f7bdb739601990 AS builder +FROM mambaorg/micromamba:bookworm-slim@sha256:29174348bd09352e5f1b1f6756cf1d00021487b8340fae040e91e4f98e954ce5 AS builder ENV TRANSFORMERS_CACHE=/cache \ PYTHONDONTWRITEBYTECODE=1 \ diff --git a/machine-learning/poetry.lock b/machine-learning/poetry.lock index 7385d1269d3f8..bd09bd8469e67 100644 --- a/machine-learning/poetry.lock +++ b/machine-learning/poetry.lock @@ -680,13 +680,13 @@ test = ["pytest (>=6)"] [[package]] name = "fastapi-slim" -version = "0.112.1" +version = "0.112.2" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi_slim-0.112.1-py3-none-any.whl", hash = "sha256:cc227cf9402d0ba54a24f80eb205c33bcb25d3ea18d53fdac3fd76ea5af8e76d"}, - {file = "fastapi_slim-0.112.1.tar.gz", hash = "sha256:876ebd24e72273986709db2d469b75dc18f04c3ab9140ffd78b29d7785d26687"}, + {file = "fastapi_slim-0.112.2-py3-none-any.whl", hash = "sha256:c023f74768f187af142c2fe5ff9e4ca3c4c1940bbde7df008cb283532422a23f"}, + {file = "fastapi_slim-0.112.2.tar.gz", hash = "sha256:75b8eb0c6ee05a20270da7a527ac7ad53b83414602f42b68f7027484dab3aedb"}, ] [package.dependencies] @@ -695,8 +695,8 @@ starlette = ">=0.37.2,<0.39.0" typing-extensions = ">=4.8.0" [package.extras] -all = ["email_validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] -standard = ["email_validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"] +all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"] [[package]] name = "filelock" @@ -1212,13 +1212,13 @@ test = ["Cython (>=0.29.24,<0.30.0)"] [[package]] name = "httpx" -version = "0.27.0" +version = "0.27.2" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, - {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, + {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, + {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, ] [package.dependencies] @@ -1233,6 +1233,7 @@ brotli = ["brotli", "brotlicffi"] cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] +zstd = ["zstandard (>=0.18.0)"] [[package]] name = "huggingface-hub" @@ -1530,13 +1531,13 @@ test = ["pytest (>=7.4)", "pytest-cov (>=4.1)"] [[package]] name = "locust" -version = "2.31.3" +version = "2.31.5" description = "Developer-friendly load testing framework" optional = false python-versions = ">=3.9" files = [ - {file = "locust-2.31.3-py3-none-any.whl", hash = "sha256:03122e007519b371a5a553d578af502826755de83551d79ea8a412ea1c660115"}, - {file = "locust-2.31.3.tar.gz", hash = "sha256:25f4603f24afa11ef1ee1f26b1c86a232eb9a1140be30b2a4642c12d7a7af8ae"}, + {file = "locust-2.31.5-py3-none-any.whl", hash = "sha256:2904ff6307d54d3202c9ebd776f9170214f6dfbe4059504dad9e3ffaca03f600"}, + {file = "locust-2.31.5.tar.gz", hash = "sha256:14b2fa6f95bf248668e6dc92d100a44f06c5dcb1c26f88a5442bcaaee18faceb"}, ] [package.dependencies] @@ -1794,38 +1795,38 @@ files = [ [[package]] name = "mypy" -version = "1.11.1" +version = "1.11.2" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c"}, - {file = "mypy-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411"}, - {file = "mypy-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03"}, - {file = "mypy-1.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4"}, - {file = "mypy-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58"}, - {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"}, - {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"}, - {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"}, - {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"}, - {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"}, - {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"}, - {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"}, - {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"}, - {file = "mypy-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:749fd3213916f1751fff995fccf20c6195cae941dc968f3aaadf9bb4e430e5a2"}, - {file = "mypy-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b639dce63a0b19085213ec5fdd8cffd1d81988f47a2dec7100e93564f3e8fb3b"}, - {file = "mypy-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c956b49c5d865394d62941b109728c5c596a415e9c5b2be663dd26a1ff07bc0"}, - {file = "mypy-1.11.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45df906e8b6804ef4b666af29a87ad9f5921aad091c79cc38e12198e220beabd"}, - {file = "mypy-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:d44be7551689d9d47b7abc27c71257adfdb53f03880841a5db15ddb22dc63edb"}, - {file = "mypy-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe"}, - {file = "mypy-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c"}, - {file = "mypy-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69"}, - {file = "mypy-1.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74"}, - {file = "mypy-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b"}, - {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"}, - {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"}, + {file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"}, + {file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"}, + {file = "mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383"}, + {file = "mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8"}, + {file = "mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"}, + {file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"}, + {file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"}, + {file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"}, + {file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"}, + {file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"}, + {file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"}, + {file = "mypy-1.11.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:37c7fa6121c1cdfcaac97ce3d3b5588e847aa79b580c1e922bb5d5d2902df19b"}, + {file = "mypy-1.11.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a8a53bc3ffbd161b5b2a4fff2f0f1e23a33b0168f1c0778ec70e1a3d66deb86"}, + {file = "mypy-1.11.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ff93107f01968ed834f4256bc1fc4475e2fecf6c661260066a985b52741ddce"}, + {file = "mypy-1.11.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:edb91dded4df17eae4537668b23f0ff6baf3707683734b6a818d5b9d0c0c31a1"}, + {file = "mypy-1.11.2-cp38-cp38-win_amd64.whl", hash = "sha256:ee23de8530d99b6db0573c4ef4bd8f39a2a6f9b60655bf7a1357e585a3486f2b"}, + {file = "mypy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6"}, + {file = "mypy-1.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70"}, + {file = "mypy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d"}, + {file = "mypy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d"}, + {file = "mypy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24"}, + {file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"}, + {file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"}, ] [package.dependencies] @@ -2815,13 +2816,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" -version = "13.7.1" +version = "13.8.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, - {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, + {file = "rich-13.8.0-py3-none-any.whl", hash = "sha256:2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc"}, + {file = "rich-13.8.0.tar.gz", hash = "sha256:a5ac1f1cd448ade0d59cc3356f7db7a7ccda2c8cbae9c7a90c28ff463d3e91f4"}, ] [package.dependencies] @@ -2833,29 +2834,29 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruff" -version = "0.6.2" +version = "0.6.3" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.6.2-py3-none-linux_armv6l.whl", hash = "sha256:5c8cbc6252deb3ea840ad6a20b0f8583caab0c5ef4f9cca21adc5a92b8f79f3c"}, - {file = "ruff-0.6.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:17002fe241e76544448a8e1e6118abecbe8cd10cf68fde635dad480dba594570"}, - {file = "ruff-0.6.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3dbeac76ed13456f8158b8f4fe087bf87882e645c8e8b606dd17b0b66c2c1158"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:094600ee88cda325988d3f54e3588c46de5c18dae09d683ace278b11f9d4d534"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:316d418fe258c036ba05fbf7dfc1f7d3d4096db63431546163b472285668132b"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d72b8b3abf8a2d51b7b9944a41307d2f442558ccb3859bbd87e6ae9be1694a5d"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2aed7e243be68487aa8982e91c6e260982d00da3f38955873aecd5a9204b1d66"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d371f7fc9cec83497fe7cf5eaf5b76e22a8efce463de5f775a1826197feb9df8"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8f310d63af08f583363dfb844ba8f9417b558199c58a5999215082036d795a1"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7db6880c53c56addb8638fe444818183385ec85eeada1d48fc5abe045301b2f1"}, - {file = "ruff-0.6.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1175d39faadd9a50718f478d23bfc1d4da5743f1ab56af81a2b6caf0a2394f23"}, - {file = "ruff-0.6.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5b939f9c86d51635fe486585389f54582f0d65b8238e08c327c1534844b3bb9a"}, - {file = "ruff-0.6.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d0d62ca91219f906caf9b187dea50d17353f15ec9bb15aae4a606cd697b49b4c"}, - {file = "ruff-0.6.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7438a7288f9d67ed3c8ce4d059e67f7ed65e9fe3aa2ab6f5b4b3610e57e3cb56"}, - {file = "ruff-0.6.2-py3-none-win32.whl", hash = "sha256:279d5f7d86696df5f9549b56b9b6a7f6c72961b619022b5b7999b15db392a4da"}, - {file = "ruff-0.6.2-py3-none-win_amd64.whl", hash = "sha256:d9f3469c7dd43cd22eb1c3fc16926fb8258d50cb1b216658a07be95dd117b0f2"}, - {file = "ruff-0.6.2-py3-none-win_arm64.whl", hash = "sha256:f28fcd2cd0e02bdf739297516d5643a945cc7caf09bd9bcb4d932540a5ea4fa9"}, - {file = "ruff-0.6.2.tar.gz", hash = "sha256:239ee6beb9e91feb8e0ec384204a763f36cb53fb895a1a364618c6abb076b3be"}, + {file = "ruff-0.6.3-py3-none-linux_armv6l.whl", hash = "sha256:97f58fda4e309382ad30ede7f30e2791d70dd29ea17f41970119f55bdb7a45c3"}, + {file = "ruff-0.6.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3b061e49b5cf3a297b4d1c27ac5587954ccb4ff601160d3d6b2f70b1622194dc"}, + {file = "ruff-0.6.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:34e2824a13bb8c668c71c1760a6ac7d795ccbd8d38ff4a0d8471fdb15de910b1"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bddfbb8d63c460f4b4128b6a506e7052bad4d6f3ff607ebbb41b0aa19c2770d1"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ced3eeb44df75353e08ab3b6a9e113b5f3f996bea48d4f7c027bc528ba87b672"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47021dff5445d549be954eb275156dfd7c37222acc1e8014311badcb9b4ec8c1"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7d7bd20dc07cebd68cc8bc7b3f5ada6d637f42d947c85264f94b0d1cd9d87384"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:500f166d03fc6d0e61c8e40a3ff853fa8a43d938f5d14c183c612df1b0d6c58a"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42844ff678f9b976366b262fa2d1d1a3fe76f6e145bd92c84e27d172e3c34500"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70452a10eb2d66549de8e75f89ae82462159855e983ddff91bc0bce6511d0470"}, + {file = "ruff-0.6.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:65a533235ed55f767d1fc62193a21cbf9e3329cf26d427b800fdeacfb77d296f"}, + {file = "ruff-0.6.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2e2c23cef30dc3cbe9cc5d04f2899e7f5e478c40d2e0a633513ad081f7361b5"}, + {file = "ruff-0.6.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d8a136aa7d228975a6aee3dd8bea9b28e2b43e9444aa678fb62aeb1956ff2351"}, + {file = "ruff-0.6.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f92fe93bc72e262b7b3f2bba9879897e2d58a989b4714ba6a5a7273e842ad2f8"}, + {file = "ruff-0.6.3-py3-none-win32.whl", hash = "sha256:7a62d3b5b0d7f9143d94893f8ba43aa5a5c51a0ffc4a401aa97a81ed76930521"}, + {file = "ruff-0.6.3-py3-none-win_amd64.whl", hash = "sha256:746af39356fee2b89aada06c7376e1aa274a23493d7016059c3a72e3b296befb"}, + {file = "ruff-0.6.3-py3-none-win_arm64.whl", hash = "sha256:14a9528a8b70ccc7a847637c29e56fd1f9183a9db743bbc5b8e0c4ad60592a82"}, + {file = "ruff-0.6.3.tar.gz", hash = "sha256:183b99e9edd1ef63be34a3b51fee0a9f4ab95add123dbf89a71f7b1f0c991983"}, ] [[package]] From 0a8bd7dc661e4a7033aaedf13f923161c6bfc7e2 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 4 Sep 2024 14:07:32 -0500 Subject: [PATCH 131/160] fix(web): correct color for active tree item (#12318) * fix(web): correct color for active tree item * remove white space --- web/src/lib/components/shared-components/tree/tree.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/lib/components/shared-components/tree/tree.svelte b/web/src/lib/components/shared-components/tree/tree.svelte index 5bc7a715ac793..5c4b367a5482f 100644 --- a/web/src/lib/components/shared-components/tree/tree.svelte +++ b/web/src/lib/components/shared-components/tree/tree.svelte @@ -13,7 +13,7 @@ export let getColor: (path: string) => string | undefined; $: path = normalizeTreePath(`${parent}/${value}`); - $: isActive = active.startsWith(path); + $: isActive = active === path || active.startsWith(`${path}/`); $: isOpen = isActive; $: isTarget = active === path; $: color = getColor(path); From 720412645f3c5344d9c8e2ef4ef538e4563e0b0c Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 4 Sep 2024 18:21:21 -0400 Subject: [PATCH 132/160] feat(web): sort albums in modal (#12331) --- .../components/album-page/albums-list.svelte | 71 +++---------------- .../album-selection-modal.svelte | 18 ++--- web/src/lib/utils/album-utils.ts | 62 ++++++++++++++++ 3 files changed, 81 insertions(+), 70 deletions(-) diff --git a/web/src/lib/components/album-page/albums-list.svelte b/web/src/lib/components/album-page/albums-list.svelte index 4355aca94d58b..5e3499bd10b3f 100644 --- a/web/src/lib/components/album-page/albums-list.svelte +++ b/web/src/lib/components/album-page/albums-list.svelte @@ -1,6 +1,6 @@ + +
+
+ +
+ +
+ + onReset({ ...options, configKeys: ['metadata'] })} + onSave={() => onSave({ metadata: config.metadata })} + showResetToDefault={!isEqual(savedConfig.metadata.faces.import, defaultConfig.metadata.faces.import)} + {disabled} + /> + +
+
diff --git a/web/src/lib/i18n/en.json b/web/src/lib/i18n/en.json index 25f3b6ea2faab..113998dc890d1 100644 --- a/web/src/lib/i18n/en.json +++ b/web/src/lib/i18n/en.json @@ -137,7 +137,11 @@ "map_settings_description": "Manage map settings", "map_style_description": "URL to a style.json map theme", "metadata_extraction_job": "Extract metadata", - "metadata_extraction_job_description": "Extract metadata information from each asset, such as GPS and resolution", + "metadata_extraction_job_description": "Extract metadata information from each asset, such as GPS, faces and resolution", + "metadata_faces_import_setting": "Enable face import", + "metadata_faces_import_setting_description": "Import faces from image EXIF data and sidecar files", + "metadata_settings": "Metadata Settings", + "metadata_settings_description": "Manage metadata settings", "migration_job": "Migration", "migration_job_description": "Migrate thumbnails for assets and faces to the latest folder structure", "no_paths_added": "No paths added", diff --git a/web/src/lib/stores/server-config.store.ts b/web/src/lib/stores/server-config.store.ts index 1d3c4bc00eb26..14d1e4e66e895 100644 --- a/web/src/lib/stores/server-config.store.ts +++ b/web/src/lib/stores/server-config.store.ts @@ -8,6 +8,7 @@ export const featureFlags = writable({ smartSearch: true, duplicateDetection: false, facialRecognition: true, + importFaces: false, sidecar: true, map: true, reverseGeocoding: true, diff --git a/web/src/routes/admin/system-settings/+page.svelte b/web/src/routes/admin/system-settings/+page.svelte index 0555bab256f68..d03865cb39075 100644 --- a/web/src/routes/admin/system-settings/+page.svelte +++ b/web/src/routes/admin/system-settings/+page.svelte @@ -4,6 +4,7 @@ import FFmpegSettings from '$lib/components/admin-page/settings/ffmpeg/ffmpeg-settings.svelte'; import ImageSettings from '$lib/components/admin-page/settings/image/image-settings.svelte'; import JobSettings from '$lib/components/admin-page/settings/job-settings/job-settings.svelte'; + import MetadataSettings from '$lib/components/admin-page/settings/metadata-settings/metadata-settings.svelte'; import LibrarySettings from '$lib/components/admin-page/settings/library-settings/library-settings.svelte'; import LoggingSettings from '$lib/components/admin-page/settings/logging-settings/logging-settings.svelte'; import MachineLearningSettings from '$lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte'; @@ -86,6 +87,12 @@ subtitle: $t('admin.job_settings_description'), key: 'job', }, + { + component: MetadataSettings, + title: $t('admin.metadata_settings'), + subtitle: $t('admin.metadata_settings_description'), + key: 'metadata', + }, { component: LibrarySettings, title: $t('admin.library_settings'), From 0d6bef2c05a10b16ebbd4b2b4d1b238f34fec988 Mon Sep 17 00:00:00 2001 From: Zack Pollard Date: Wed, 4 Sep 2024 23:28:30 +0100 Subject: [PATCH 134/160] ci: job naming improvements and success job for matrix (#12316) Co-authored-by: bo0tzz --- .github/workflows/cli.yml | 2 +- .github/workflows/docker.yml | 13 +++++++++++++ .github/workflows/docs-build.yml | 1 + .github/workflows/docs-deploy.yml | 2 ++ .github/workflows/docs-destroy.yml | 1 + .github/workflows/test.yml | 12 ++++++------ 6 files changed, 24 insertions(+), 7 deletions(-) diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 1ec17b381dbfd..5292075cce902 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -22,7 +22,7 @@ permissions: jobs: publish: - name: Publish + name: CLI Publish runs-on: ubuntu-latest defaults: run: diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 7784b32f362f1..6be26c9bbe62f 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -234,3 +234,16 @@ jobs: BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.metadata.outputs.tags }} BUILD_SOURCE_REF=${{ github.ref_name }} BUILD_SOURCE_COMMIT=${{ github.sha }} + + success-check: + name: Docker Build & Push Success + needs: [build_and_push_ml, build_and_push_server] + runs-on: ubuntu-latest + if: always() + steps: + - name: Any jobs failed? + if: ${{ contains(needs.*.result, 'failure') }} + run: exit 1 + - name: All jobs passed or skipped + if: ${{ !(contains(needs.*.result, 'failure')) }} + run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}" diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml index 682e3c45f008a..387d8e042496b 100644 --- a/.github/workflows/docs-build.yml +++ b/.github/workflows/docs-build.yml @@ -30,6 +30,7 @@ jobs: run: echo "should_force=${{ github.event_name == 'release' }}" >> "$GITHUB_OUTPUT" build: + name: Docs Build needs: pre-job if: ${{ needs.pre-job.outputs.should_run == 'true' }} runs-on: ubuntu-latest diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index a863cf8ed2f9c..ab197fa459d94 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -7,6 +7,7 @@ on: jobs: checks: + name: Docs Deploy Checks runs-on: ubuntu-latest outputs: parameters: ${{ steps.parameters.outputs.result }} @@ -91,6 +92,7 @@ jobs: return parameters; deploy: + name: Docs Deploy runs-on: ubuntu-latest needs: checks if: ${{ fromJson(needs.checks.outputs.artifact).found && fromJson(needs.checks.outputs.parameters).shouldDeploy }} diff --git a/.github/workflows/docs-destroy.yml b/.github/workflows/docs-destroy.yml index 861a6319fe95d..80700569245d1 100644 --- a/.github/workflows/docs-destroy.yml +++ b/.github/workflows/docs-destroy.yml @@ -5,6 +5,7 @@ on: jobs: deploy: + name: Docs Destroy runs-on: ubuntu-latest steps: - name: Checkout code diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ac6236d2eb6c2..24e3e086235f0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -48,7 +48,7 @@ jobs: run: echo "should_force=${{ github.event_name == 'workflow_dispatch' }}" >> "$GITHUB_OUTPUT" server-unit-tests: - name: Server + name: Test & Lint Server needs: pre-job if: ${{ needs.pre-job.outputs.should_run_server == 'true' }} runs-on: ubuntu-latest @@ -85,7 +85,7 @@ jobs: if: ${{ !cancelled() }} cli-unit-tests: - name: CLI + name: Unit Test CLI needs: pre-job if: ${{ needs.pre-job.outputs.should_run_cli == 'true' }} runs-on: ubuntu-latest @@ -126,7 +126,7 @@ jobs: if: ${{ !cancelled() }} cli-unit-tests-win: - name: CLI (Windows) + name: Unit Test CLI (Windows) needs: pre-job if: ${{ needs.pre-job.outputs.should_run_cli == 'true' }} runs-on: windows-latest @@ -160,7 +160,7 @@ jobs: if: ${{ !cancelled() }} web-unit-tests: - name: Web + name: Test & Lint Web needs: pre-job if: ${{ needs.pre-job.outputs.should_run_web == 'true' }} runs-on: ubuntu-latest @@ -327,7 +327,7 @@ jobs: if: ${{ !cancelled() }} mobile-unit-tests: - name: Mobile + name: Unit Test Mobile needs: pre-job if: ${{ needs.pre-job.outputs.should_run_mobile == 'true' }} runs-on: ubuntu-latest @@ -343,7 +343,7 @@ jobs: run: flutter test -j 1 ml-unit-tests: - name: Machine Learning + name: Unit Test ML needs: pre-job if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }} runs-on: ubuntu-latest From f4ec8425775c37c25e11c89b3dfd5b11de46b42d Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 4 Sep 2024 23:38:55 -0400 Subject: [PATCH 135/160] refactor(web): upload panel (#12326) Co-authored-by: Alex --- web/src/lib/components/elements/icon.svelte | 3 +- .../upload-asset-preview.svelte | 146 +++++++++--------- .../shared-components/upload-panel.svelte | 37 +++-- web/src/lib/models/upload-asset.ts | 2 +- web/src/lib/stores/upload.ts | 89 +++++++---- web/src/lib/utils/file-uploader.ts | 47 ++++-- 6 files changed, 184 insertions(+), 140 deletions(-) diff --git a/web/src/lib/components/elements/icon.svelte b/web/src/lib/components/elements/icon.svelte index bb22276286f11..5965928718276 100644 --- a/web/src/lib/components/elements/icon.svelte +++ b/web/src/lib/components/elements/icon.svelte @@ -16,13 +16,14 @@ export let ariaLabelledby: string | undefined = undefined; export let strokeWidth: number = 0; export let strokeColor: string = 'currentColor'; + export let spin = false; + import Icon from '$lib/components/elements/icon.svelte'; + import { AppRoute } from '$lib/constants'; import type { UploadAsset } from '$lib/models/upload-asset'; import { UploadState } from '$lib/models/upload-asset'; import { locale } from '$lib/stores/preferences.store'; - import { getByteUnitString } from '$lib/utils/byte-units'; - import { fade } from 'svelte/transition'; - import ImmichLogo from './immich-logo.svelte'; - import { getFilenameExtension } from '$lib/utils/asset-utils'; import { uploadAssetsStore } from '$lib/stores/upload'; - import Icon from '$lib/components/elements/icon.svelte'; + import { getByteUnitString } from '$lib/utils/byte-units'; import { fileUploadHandler } from '$lib/utils/file-uploader'; - import { mdiRefresh, mdiCancel } from '@mdi/js'; + import { + mdiAlertCircle, + mdiCheckCircle, + mdiCircleOutline, + mdiClose, + mdiLoading, + mdiOpenInNew, + mdiRestart, + } from '@mdi/js'; import { t } from 'svelte-i18n'; + import { fade } from 'svelte/transition'; export let uploadAsset: UploadAsset; + const handleDismiss = (uploadAsset: UploadAsset) => { + uploadAssetsStore.removeItem(uploadAsset.id); + }; + const handleRetry = async (uploadAsset: UploadAsset) => { - uploadAssetsStore.removeUploadAsset(uploadAsset.id); + uploadAssetsStore.removeItem(uploadAsset.id); await fileUploadHandler([uploadAsset.file], uploadAsset.albumId); }; @@ -23,86 +34,69 @@
-
-
-
- -
-
-

- .{getFilenameExtension(uploadAsset.file.name)} -

-
+
+
+ {#if uploadAsset.state === UploadState.PENDING} + + {:else if uploadAsset.state === UploadState.STARTED} + + {:else if uploadAsset.state === UploadState.ERROR} + + {:else if uploadAsset.state === UploadState.DUPLICATED} + + {:else if uploadAsset.state === UploadState.DONE} + + {/if}
-
- + + {uploadAsset.file.name} -
- {#if uploadAsset.state === UploadState.STARTED} -
-

- {#if uploadAsset.message} - {uploadAsset.message} - {:else} - {uploadAsset.progress}% - {getByteUnitString(uploadAsset.speed || 0, $locale)}/s - {uploadAsset.eta}s - {/if} -

- {:else if uploadAsset.state === UploadState.PENDING} -
-

{$t('pending')}

- {:else if uploadAsset.state === UploadState.ERROR} -
-

{$t('error')}

- {:else if uploadAsset.state === UploadState.DUPLICATED} -
-

- {$t('asset_skipped')} - {#if uploadAsset.message} - ({uploadAsset.message}) - {/if} -

- {:else if uploadAsset.state === UploadState.DONE} -
-

- {$t('asset_uploaded')} - {#if uploadAsset.message} - ({uploadAsset.message}) - {/if} -

- {/if} -
-
- {#if uploadAsset.state === UploadState.ERROR} -
- - +
+ {:else if uploadAsset.state === UploadState.ERROR} +
+ +
{/if}
+ {#if uploadAsset.state === UploadState.STARTED} +
+
+

+ {#if uploadAsset.message} + {uploadAsset.message} + {:else} + {uploadAsset.progress}% - {getByteUnitString(uploadAsset.speed || 0, $locale)}/s - {uploadAsset.eta}s + {/if} +

+
+ {/if} + {#if uploadAsset.state === UploadState.ERROR}
-

+

{uploadAsset.error}

diff --git a/web/src/lib/components/shared-components/upload-panel.svelte b/web/src/lib/components/shared-components/upload-panel.svelte index ee213d796925f..d5360532862b1 100644 --- a/web/src/lib/components/shared-components/upload-panel.svelte +++ b/web/src/lib/components/shared-components/upload-panel.svelte @@ -15,8 +15,7 @@ let showOptions = false; let concurrency = uploadExecutionQueue.concurrency; - let { isUploading, hasError, remainingUploads, errorCounter, duplicateCounter, successCounter, totalUploadCounter } = - uploadAssetsStore; + let { stats, isDismissible, isUploading, remainingUploads } = uploadAssetsStore; const autoHide = () => { if (!$isUploading && showDetail) { @@ -33,29 +32,29 @@ } -{#if $hasError || $isUploading} +{#if $isUploading}
{ - if ($errorCounter > 0) { + if ($stats.errors > 0) { notificationController.show({ - message: $t('upload_errors', { values: { count: $errorCounter } }), + message: $t('upload_errors', { values: { count: $stats.errors } }), type: NotificationType.Warning, }); - } else if ($successCounter > 0) { + } else if ($stats.success > 0) { notificationController.show({ message: $t('upload_success'), type: NotificationType.Info, }); } - if ($duplicateCounter > 0) { + if ($stats.duplicates > 0) { notificationController.show({ - message: $t('upload_skipped_duplicates', { values: { count: $duplicateCounter } }), + message: $t('upload_skipped_duplicates', { values: { count: $stats.duplicates } }), type: NotificationType.Warning, }); } - uploadAssetsStore.resetStore(); + uploadAssetsStore.reset(); }} class="fixed bottom-6 right-6 z-[10000]" > @@ -70,20 +69,20 @@ {$t('upload_progress', { values: { remaining: $remainingUploads, - processed: $successCounter + $errorCounter, - total: $totalUploadCounter, + processed: $stats.total - $remainingUploads, + total: $stats.total, }, })}

{$t('upload_status_uploaded')} - {$successCounter.toLocaleString($locale)} + {$stats.success.toLocaleString($locale)} - {$t('upload_status_errors')} - {$errorCounter.toLocaleString($locale)} + {$stats.errors.toLocaleString($locale)} - {$t('upload_status_duplicates')} - {$duplicateCounter.toLocaleString($locale)} + {$stats.duplicates.toLocaleString($locale)}

@@ -103,7 +102,7 @@ on:click={() => (showDetail = false)} />
- {#if $hasError} + {#if $isDismissible}
{#if showOptions} -
+
@@ -133,7 +132,7 @@ />
{/if} -
+
{#each $uploadAssetsStore as uploadAsset (uploadAsset.id)} {/each} @@ -149,14 +148,14 @@ > {$remainingUploads.toLocaleString($locale)} - {#if $hasError} + {#if $stats.errors > 0} {/if}
{/if} - +

{ expect(img.alt).toBe('123'); expect(img.getAttribute('src')).toBe('wee'); expect(img.getAttribute('loading')).toBe('eager'); - expect(img.className).toBe('z-0 rounded-xl object-cover aspect-square asdf'); + expect(img.className).toBe('size-full rounded-xl object-cover aspect-square asdf'); }); }); diff --git a/web/src/lib/components/sharedlinks-page/covers/__tests__/no-cover.spec.ts b/web/src/lib/components/sharedlinks-page/covers/__tests__/no-cover.spec.ts index bdf0b8878c406..bb87c02135d90 100644 --- a/web/src/lib/components/sharedlinks-page/covers/__tests__/no-cover.spec.ts +++ b/web/src/lib/components/sharedlinks-page/covers/__tests__/no-cover.spec.ts @@ -10,7 +10,7 @@ describe('NoCover component', () => { }); const img = component.getByTestId('album-image') as HTMLImageElement; expect(img.alt).toBe('123'); - expect(img.className).toBe('z-0 rounded-xl object-cover aspect-square asdf'); + expect(img.className).toBe('size-full rounded-xl object-cover aspect-square asdf'); expect(img.getAttribute('loading')).toBe('eager'); expect(img.src).toStrictEqual(expect.any(String)); }); diff --git a/web/src/lib/components/sharedlinks-page/covers/__tests__/share-cover.spec.ts b/web/src/lib/components/sharedlinks-page/covers/__tests__/share-cover.spec.ts index 83ca07b40e0e5..76de04ea3127a 100644 --- a/web/src/lib/components/sharedlinks-page/covers/__tests__/share-cover.spec.ts +++ b/web/src/lib/components/sharedlinks-page/covers/__tests__/share-cover.spec.ts @@ -17,7 +17,7 @@ describe('ShareCover component', () => { const img = component.getByTestId('album-image') as HTMLImageElement; expect(img.alt).toBe('123'); expect(img.getAttribute('loading')).toBe('lazy'); - expect(img.className).toBe('z-0 rounded-xl object-cover aspect-square text'); + expect(img.className).toBe('size-full rounded-xl object-cover aspect-square text'); }); it('renders an image when the shared link is an individual share', () => { @@ -30,7 +30,7 @@ describe('ShareCover component', () => { const img = component.getByTestId('album-image') as HTMLImageElement; expect(img.alt).toBe('individual_share'); expect(img.getAttribute('loading')).toBe('lazy'); - expect(img.className).toBe('z-0 rounded-xl object-cover aspect-square text'); + expect(img.className).toBe('size-full rounded-xl object-cover aspect-square text'); expect(img.getAttribute('src')).toBe('/asdf'); expect(getAssetThumbnailUrl).toHaveBeenCalledWith('someId'); }); @@ -44,7 +44,7 @@ describe('ShareCover component', () => { const img = component.getByTestId('album-image') as HTMLImageElement; expect(img.alt).toBe('unnamed_share'); expect(img.getAttribute('loading')).toBe('lazy'); - expect(img.className).toBe('z-0 rounded-xl object-cover aspect-square text'); + expect(img.className).toBe('size-full rounded-xl object-cover aspect-square text'); }); it.skip('renders fallback image when asset is not resized', () => { diff --git a/web/src/lib/components/sharedlinks-page/covers/asset-cover.svelte b/web/src/lib/components/sharedlinks-page/covers/asset-cover.svelte index d8b0a1b0d7bce..bf5031e39d037 100644 --- a/web/src/lib/components/sharedlinks-page/covers/asset-cover.svelte +++ b/web/src/lib/components/sharedlinks-page/covers/asset-cover.svelte @@ -16,7 +16,7 @@ (isBroken = true)} - class="z-0 rounded-xl object-cover aspect-square {className}" + class="size-full rounded-xl object-cover aspect-square {className}" data-testid="album-image" draggable="false" loading={preload ? 'eager' : 'lazy'} diff --git a/web/src/lib/components/sharedlinks-page/covers/no-cover.svelte b/web/src/lib/components/sharedlinks-page/covers/no-cover.svelte index 45d7d4b315d21..087204d6a5df0 100644 --- a/web/src/lib/components/sharedlinks-page/covers/no-cover.svelte +++ b/web/src/lib/components/sharedlinks-page/covers/no-cover.svelte @@ -7,7 +7,7 @@ Date: Fri, 6 Sep 2024 15:16:59 +0200 Subject: [PATCH 154/160] feat: optimize copy image to clipboard (#12366) * feat: optimize copy image to clipboard * pr feedback * fix: urlToBlob Co-authored-by: Jason Rasmussen * fix: imgToBlob Co-authored-by: Jason Rasmussen * chore: finish rename * fix: dimensions --------- Co-authored-by: Jason Rasmussen --- web/package-lock.json | 6 --- web/package.json | 1 - .../asset-viewer/asset-viewer-nav-bar.svelte | 4 +- .../asset-viewer/photo-viewer.svelte | 14 +++---- web/src/lib/utils/asset-utils.spec.ts | 8 +++- web/src/lib/utils/asset-utils.ts | 38 +++++++++++++++++++ 6 files changed, 52 insertions(+), 19 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 4ddc6d9baa966..7bcd5c2b01d47 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -17,7 +17,6 @@ "@photo-sphere-viewer/equirectangular-video-adapter": "^5.7.2", "@photo-sphere-viewer/video-plugin": "^5.7.2", "@zoom-image/svelte": "^0.2.6", - "copy-image-clipboard": "^2.1.2", "dom-to-image": "^2.6.0", "handlebars": "^4.7.8", "intl-messageformat": "^10.5.14", @@ -3284,11 +3283,6 @@ "node": ">= 0.6" } }, - "node_modules/copy-image-clipboard": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/copy-image-clipboard/-/copy-image-clipboard-2.1.2.tgz", - "integrity": "sha512-3VCXVl2IpFfOyD8drv9DozcNlwmqBqxOlsgkEGyVAzadjlPk1go8YNZyy8QmTnwHPxSFpeCR9OdsStEdVK7qDA==" - }, "node_modules/core-js-compat": { "version": "3.37.1", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", diff --git a/web/package.json b/web/package.json index 1ba350022d98c..c84bbf0db43d3 100644 --- a/web/package.json +++ b/web/package.json @@ -73,7 +73,6 @@ "@photo-sphere-viewer/equirectangular-video-adapter": "^5.7.2", "@photo-sphere-viewer/video-plugin": "^5.7.2", "@zoom-image/svelte": "^0.2.6", - "copy-image-clipboard": "^2.1.2", "dom-to-image": "^2.6.0", "handlebars": "^4.7.8", "intl-messageformat": "^10.5.14", diff --git a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte index 0f75f9bb830f4..db216641d5c2f 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte @@ -41,7 +41,7 @@ mdiPresentationPlay, mdiUpload, } from '@mdi/js'; - import { canCopyImagesToClipboard } from 'copy-image-clipboard'; + import { canCopyImageToClipboard } from '$lib/utils/asset-utils'; import { t } from 'svelte-i18n'; export let asset: AssetResponseDto; @@ -101,7 +101,7 @@ on:click={onZoomImage} /> {/if} - {#if canCopyImagesToClipboard() && asset.type === AssetTypeEnum.Image} + {#if canCopyImageToClipboard() && asset.type === AssetTypeEnum.Image} {/if} diff --git a/web/src/lib/components/asset-viewer/photo-viewer.svelte b/web/src/lib/components/asset-viewer/photo-viewer.svelte index 40a36fa0e09a5..4157c558d24cd 100644 --- a/web/src/lib/components/asset-viewer/photo-viewer.svelte +++ b/web/src/lib/components/asset-viewer/photo-viewer.svelte @@ -8,17 +8,17 @@ import { SlideshowLook, SlideshowState, slideshowLookCssMapping, slideshowStore } from '$lib/stores/slideshow.store'; import { photoZoomState } from '$lib/stores/zoom-image.store'; import { getAssetOriginalUrl, getAssetThumbnailUrl, handlePromiseError } from '$lib/utils'; - import { isWebCompatibleImage } from '$lib/utils/asset-utils'; + import { isWebCompatibleImage, canCopyImageToClipboard, copyImageToClipboard } from '$lib/utils/asset-utils'; import { getBoundingBox } from '$lib/utils/people-utils'; import { getAltText } from '$lib/utils/thumbnail-util'; import { AssetMediaSize, AssetTypeEnum, type AssetResponseDto, type SharedLinkResponseDto } from '@immich/sdk'; - import { canCopyImagesToClipboard, copyImageToClipboard } from 'copy-image-clipboard'; import { onDestroy, onMount } from 'svelte'; import { t } from 'svelte-i18n'; import { type SwipeCustomEvent, swipe } from 'svelte-gestures'; import { fade } from 'svelte/transition'; import LoadingSpinner from '../shared-components/loading-spinner.svelte'; import { NotificationType, notificationController } from '../shared-components/notification/notification'; + import { handleError } from '$lib/utils/handle-error'; export let asset: AssetResponseDto; export let preloadAssets: AssetResponseDto[] | undefined = undefined; @@ -81,23 +81,19 @@ }; copyImage = async () => { - if (!canCopyImagesToClipboard()) { + if (!canCopyImageToClipboard()) { return; } try { - await copyImageToClipboard(assetFileUrl); + await copyImageToClipboard($photoViewer ?? assetFileUrl); notificationController.show({ type: NotificationType.Info, message: $t('copied_image_to_clipboard'), timeout: 3000, }); } catch (error) { - console.error('Error [photo-viewer]:', error); - notificationController.show({ - type: NotificationType.Error, - message: 'Copying image to clipboard failed.', - }); + handleError(error, $t('copy_error')); } }; diff --git a/web/src/lib/utils/asset-utils.spec.ts b/web/src/lib/utils/asset-utils.spec.ts index 8970a6a65219c..b3a668192d57f 100644 --- a/web/src/lib/utils/asset-utils.spec.ts +++ b/web/src/lib/utils/asset-utils.spec.ts @@ -1,5 +1,5 @@ import type { AssetResponseDto } from '@immich/sdk'; -import { getAssetFilename, getFilenameExtension } from './asset-utils'; +import { canCopyImageToClipboard, getAssetFilename, getFilenameExtension } from './asset-utils'; describe('get file extension from filename', () => { it('returns the extension without including the dot', () => { @@ -56,3 +56,9 @@ describe('get asset filename', () => { } }); }); + +describe('copy image to clipboard', () => { + it('should not allow copy image to clipboard', () => { + expect(canCopyImageToClipboard()).toEqual(false); + }); +}); diff --git a/web/src/lib/utils/asset-utils.ts b/web/src/lib/utils/asset-utils.ts index e309db5ff6a1e..84a896452f7f1 100644 --- a/web/src/lib/utils/asset-utils.ts +++ b/web/src/lib/utils/asset-utils.ts @@ -527,3 +527,41 @@ export const archiveAssets = async (assets: AssetResponseDto[], archive: boolean export const delay = async (ms: number) => { return new Promise((resolve) => setTimeout(resolve, ms)); }; + +export const canCopyImageToClipboard = (): boolean => { + return !!(navigator.clipboard && window.ClipboardItem); +}; + +const imgToBlob = async (imageElement: HTMLImageElement) => { + const canvas = document.createElement('canvas'); + const context = canvas.getContext('2d'); + + canvas.width = imageElement.naturalWidth; + canvas.height = imageElement.naturalHeight; + + if (context) { + context.drawImage(imageElement, 0, 0); + + return await new Promise((resolve) => { + canvas.toBlob((blob) => { + if (blob) { + resolve(blob); + } else { + throw new Error('Canvas conversion to Blob failed'); + } + }); + }); + } + + throw new Error('Canvas context is null'); +}; + +const urlToBlob = async (imageSource: string) => { + const response = await fetch(imageSource); + return await response.blob(); +}; + +export const copyImageToClipboard = async (source: HTMLImageElement | string) => { + const blob = source instanceof HTMLImageElement ? await imgToBlob(source) : await urlToBlob(source); + await navigator.clipboard.write([new ClipboardItem({ [blob.type]: blob })]); +}; From 529b7fe748e5e5830b6b28b5130f220826ea0837 Mon Sep 17 00:00:00 2001 From: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Date: Fri, 6 Sep 2024 15:18:45 +0200 Subject: [PATCH 155/160] fix(web): show focus outline for asset thumbnails again (#12382) * fix(web): show focus outline for asset thumbnails again * fix e2e test --- e2e/src/web/specs/shared-link.e2e-spec.ts | 2 +- web/src/lib/components/assets/thumbnail/thumbnail.svelte | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/e2e/src/web/specs/shared-link.e2e-spec.ts b/e2e/src/web/specs/shared-link.e2e-spec.ts index 8679bb3236b49..2a02e429a5900 100644 --- a/e2e/src/web/specs/shared-link.e2e-spec.ts +++ b/e2e/src/web/specs/shared-link.e2e-spec.ts @@ -44,7 +44,7 @@ test.describe('Shared Links', () => { test('download from a shared link', async ({ page }) => { await page.goto(`/share/${sharedLink.key}`); await page.getByRole('heading', { name: 'Test Album' }).waitFor(); - await page.locator('.group').first().hover(); + await page.locator(`[data-asset-id="${asset.id}"]`).hover(); await page.waitForSelector('#asset-group-by-date svg'); await page.getByRole('checkbox').click(); await page.getByRole('button', { name: 'Download' }).click(); diff --git a/web/src/lib/components/assets/thumbnail/thumbnail.svelte b/web/src/lib/components/assets/thumbnail/thumbnail.svelte index 69f777f530658..af22887185a36 100644 --- a/web/src/lib/components/assets/thumbnail/thumbnail.svelte +++ b/web/src/lib/components/assets/thumbnail/thumbnail.svelte @@ -175,7 +175,7 @@ data-int={intersecting} style:width="{width}px" style:height="{height}px" - class="group focus-visible:outline-none flex overflow-hidden {disabled + class="focus-visible:outline-none flex overflow-hidden {disabled ? 'bg-gray-300' : 'bg-immich-primary/20 dark:bg-immich-dark-primary/20'}" > @@ -193,6 +193,7 @@

Date: Fri, 6 Sep 2024 06:26:58 -0700 Subject: [PATCH 156/160] feat(web): add download shortcut on the timeline & asset viewer (#12339) feat(web): implement download shortcut --- .../lib/components/photos-page/actions/download-action.svelte | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/src/lib/components/photos-page/actions/download-action.svelte b/web/src/lib/components/photos-page/actions/download-action.svelte index 073d20901ca4e..7716fbe36d53d 100644 --- a/web/src/lib/components/photos-page/actions/download-action.svelte +++ b/web/src/lib/components/photos-page/actions/download-action.svelte @@ -2,6 +2,7 @@ import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte'; import { downloadArchive, downloadFile } from '$lib/utils/asset-utils'; import MenuOption from '../../shared-components/context-menu/menu-option.svelte'; + import { shortcut } from '$lib/actions/shortcut'; import { getAssetControlContext } from '../asset-select-control-bar.svelte'; import { mdiCloudDownloadOutline, mdiFileDownloadOutline, mdiFolderDownloadOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; @@ -26,6 +27,8 @@ $: menuItemIcon = getAssets().size === 1 ? mdiFileDownloadOutline : mdiFolderDownloadOutline; + + {#if menuItem} {:else} From 5d8052202e4deb4b9f1f8f459b5ee0830ab20c00 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 6 Sep 2024 08:30:26 -0500 Subject: [PATCH 157/160] chore(mobile): Translations update (#12392) chore(mobile): translation update --- mobile/assets/i18n/ar-JO.json | 4 ++ mobile/assets/i18n/cs-CZ.json | 6 ++- mobile/assets/i18n/da-DK.json | 4 ++ mobile/assets/i18n/de-DE.json | 4 ++ mobile/assets/i18n/el-GR.json | 4 ++ mobile/assets/i18n/en-US.json | 13 +++-- mobile/assets/i18n/es-ES.json | 34 ++++++------ mobile/assets/i18n/es-MX.json | 4 ++ mobile/assets/i18n/es-PE.json | 4 ++ mobile/assets/i18n/es-US.json | 4 ++ mobile/assets/i18n/fi-FI.json | 4 ++ mobile/assets/i18n/fr-CA.json | 4 ++ mobile/assets/i18n/fr-FR.json | 4 ++ mobile/assets/i18n/he-IL.json | 22 ++++---- mobile/assets/i18n/hi-IN.json | 4 ++ mobile/assets/i18n/hu-HU.json | 4 ++ mobile/assets/i18n/it-IT.json | 4 ++ mobile/assets/i18n/ja-JP.json | 4 ++ mobile/assets/i18n/ko-KR.json | 94 +++++++++++++++++---------------- mobile/assets/i18n/lt-LT.json | 4 ++ mobile/assets/i18n/lv-LV.json | 4 ++ mobile/assets/i18n/mn.json | 4 ++ mobile/assets/i18n/nb-NO.json | 4 ++ mobile/assets/i18n/nl-NL.json | 4 ++ mobile/assets/i18n/pl-PL.json | 4 ++ mobile/assets/i18n/pt-PT.json | 4 ++ mobile/assets/i18n/ro-RO.json | 4 ++ mobile/assets/i18n/ru-RU.json | 4 ++ mobile/assets/i18n/sk-SK.json | 4 ++ mobile/assets/i18n/sl-SI.json | 4 ++ mobile/assets/i18n/sr-Cyrl.json | 4 ++ mobile/assets/i18n/sr-Latn.json | 4 ++ mobile/assets/i18n/sv-FI.json | 4 ++ mobile/assets/i18n/sv-SE.json | 4 ++ mobile/assets/i18n/th-TH.json | 4 ++ mobile/assets/i18n/uk-UA.json | 4 ++ mobile/assets/i18n/vi-VN.json | 6 ++- mobile/assets/i18n/zh-CN.json | 4 ++ mobile/assets/i18n/zh-Hans.json | 4 ++ mobile/assets/i18n/zh-TW.json | 4 ++ 40 files changed, 235 insertions(+), 76 deletions(-) diff --git a/mobile/assets/i18n/ar-JO.json b/mobile/assets/i18n/ar-JO.json index 8b9f8c42c4041..fdc54da2b777f 100644 --- a/mobile/assets/i18n/ar-JO.json +++ b/mobile/assets/i18n/ar-JO.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "حذف الرابط المشترك", "description_input_hint_text": "اضف وصفا...", "description_input_submit_error": "خطأ تحديث الوصف ، تحقق من السجل لمزيد من التفاصيل", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "التاريخ و الوقت", "edit_date_time_dialog_timezone": "وحدة زمنية", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/cs-CZ.json b/mobile/assets/i18n/cs-CZ.json index 9872aa13247ee..4a81de75960ab 100644 --- a/mobile/assets/i18n/cs-CZ.json +++ b/mobile/assets/i18n/cs-CZ.json @@ -63,7 +63,7 @@ "assets_trashed_from_server": "{} položek vyhozeno do koše na Immich serveru", "asset_viewer_settings_title": "Prohlížeč", "backup_album_selection_page_albums_device": "Alba v zařízení ({})", - "backup_album_selection_page_albums_tap": "Klepnutím na položku ji zahrnete, dvojím klepnutím ji vyloučíte", + "backup_album_selection_page_albums_tap": "Klepnutím na položku ji zahrnete, opětovným klepnutím ji vyloučíte", "backup_album_selection_page_assets_scatter": "Položky mohou být roztroušeny ve více albech. To umožňuje zahrnout nebo vyloučit alba během procesu zálohování.", "backup_album_selection_page_select_albums": "Vybraná alba", "backup_album_selection_page_selection_info": "Informace o výběru", @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Odstranit sdílený odkaz", "description_input_hint_text": "Přidat popis...", "description_input_submit_error": "Chyba aktualizace popisu, další podrobnosti najdete v logu", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Datum a čas", "edit_date_time_dialog_timezone": "Časové pásmo", "edit_image_title": "Upravit", diff --git a/mobile/assets/i18n/da-DK.json b/mobile/assets/i18n/da-DK.json index b9688a39c9f5d..20c3c43b09410 100644 --- a/mobile/assets/i18n/da-DK.json +++ b/mobile/assets/i18n/da-DK.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Slet delt link", "description_input_hint_text": "Tilføj en beskrivelse...", "description_input_submit_error": "Fejl med at opdatere beskrivelsen. Tjek loggen for flere detaljer", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Dato og klokkeslæt", "edit_date_time_dialog_timezone": "Tidszone", "edit_image_title": "Rediger", diff --git a/mobile/assets/i18n/de-DE.json b/mobile/assets/i18n/de-DE.json index 1da83fb551dae..bb2ed31f8a456 100644 --- a/mobile/assets/i18n/de-DE.json +++ b/mobile/assets/i18n/de-DE.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Geteilten Link löschen", "description_input_hint_text": "Beschreibung hinzufügen...", "description_input_submit_error": "Beschreibung konnte nicht geändert werden, bitte im Log für mehr Details nachsehen.", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Datum und Uhrzeit", "edit_date_time_dialog_timezone": "Zeitzone", "edit_image_title": "Bearbeiten", diff --git a/mobile/assets/i18n/el-GR.json b/mobile/assets/i18n/el-GR.json index 29efeb03d1353..88426a6076ba9 100644 --- a/mobile/assets/i18n/el-GR.json +++ b/mobile/assets/i18n/el-GR.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Διαγραφή Κοινοποιημένου Συνδέσμου", "description_input_hint_text": "Προσθήκη περιγραφής...", "description_input_submit_error": "Σφάλμα κατά την ενημέρωση της περιγραφής, ελέγξτε το αρχείο καταγραφής για περισσότερες λεπτομέρειες", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Ημερομηνία και Ώρα", "edit_date_time_dialog_timezone": "Ζώνη ώρας", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/en-US.json b/mobile/assets/i18n/en-US.json index 9dbe49589f75c..324c9069fdf46 100644 --- a/mobile/assets/i18n/en-US.json +++ b/mobile/assets/i18n/en-US.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Delete Shared Link", "description_input_hint_text": "Add description...", "description_input_submit_error": "Error updating description, check the log for more details", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Date and Time", "edit_date_time_dialog_timezone": "Timezone", "edit_image_title": "Edit", @@ -252,10 +256,9 @@ "home_page_share_err_local": "Can not share local assets via link, skipping", "home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping", "image_saved_successfully": "Image saved", - "download_error": "Download Error", - "download_started": "Download started", - "download_sucess": "Download success", - "download_sucess_android": "The media has been downloaded to DCIM/Immich", + "image_viewer_page_state_provider_download_error": "Download Error", + "image_viewer_page_state_provider_download_started": "Download Started", + "image_viewer_page_state_provider_download_success": "Download Success", "image_viewer_page_state_provider_share_error": "Share Error", "invalid_date": "Invalid date", "invalid_date_format": "Invalid date format", @@ -586,4 +589,4 @@ "viewer_remove_from_stack": "Remove from Stack", "viewer_stack_use_as_main_asset": "Use as Main Asset", "viewer_unstack": "Un-Stack" -} +} \ No newline at end of file diff --git a/mobile/assets/i18n/es-ES.json b/mobile/assets/i18n/es-ES.json index 82e77aa4762c5..1943116b4ff5f 100644 --- a/mobile/assets/i18n/es-ES.json +++ b/mobile/assets/i18n/es-ES.json @@ -173,8 +173,8 @@ "control_bottom_app_bar_delete": "Eliminar", "control_bottom_app_bar_delete_from_immich": "Borrar de Immich", "control_bottom_app_bar_delete_from_local": "Borrar del dispositivo", - "control_bottom_app_bar_download": "Download", - "control_bottom_app_bar_edit": "Edit", + "control_bottom_app_bar_download": "Descargar", + "control_bottom_app_bar_edit": "Editar", "control_bottom_app_bar_edit_location": "Editar ubicación", "control_bottom_app_bar_edit_time": "Editar fecha y hora", "control_bottom_app_bar_favorite": "Favorito", @@ -190,7 +190,7 @@ "create_shared_album_page_share": "Compartir", "create_shared_album_page_share_add_assets": "AGREGAR ELEMENTOS", "create_shared_album_page_share_select_photos": "Seleccionar Fotos", - "crop": "Crop", + "crop": "Recortar", "curated_location_page_title": "Lugares", "curated_object_page_title": "Objetos", "daily_title_text_date": "E dd, MMM", @@ -210,9 +210,13 @@ "delete_shared_link_dialog_title": "Eliminar enlace compartido", "description_input_hint_text": "Agregar descripción...", "description_input_submit_error": "Error al actualizar la descripción, verifica el registro para obtener más detalles", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Fecha y Hora", "edit_date_time_dialog_timezone": "Zona horaria", - "edit_image_title": "Edit", + "edit_image_title": "Editar", "edit_location_dialog_title": "Ubicación", "error_saving_image": "Error: {}", "exif_bottom_sheet_description": "Agregar Descripción...", @@ -251,13 +255,13 @@ "home_page_first_time_notice": "Si esta es la primera vez que usas la app, por favor, asegúrate de elegir un álbum de respaldo para que la línea de tiempo pueda cargar fotos y videos en los álbumes.", "home_page_share_err_local": "No se pueden compartir elementos locales a través de un enlace, omitiendo", "home_page_upload_err_limit": "Solo se pueden subir 30 elementos simultáneamente, omitiendo", - "image_saved_successfully": "Image saved", + "image_saved_successfully": "Imágenes guardas", "image_viewer_page_state_provider_download_error": "Error de descarga", "image_viewer_page_state_provider_download_started": "Descarga Iniciada", "image_viewer_page_state_provider_download_success": "Descarga exitosa", "image_viewer_page_state_provider_share_error": "Error al compartir", - "invalid_date": "Invalid date", - "invalid_date_format": "Invalid date format", + "invalid_date": "Fecha incorrecta", + "invalid_date_format": "Formato de fecha incorrecto", "library_page_albums": "Álbumes", "library_page_archive": "Archivo", "library_page_device_albums": "Álbumes en el dispositivo", @@ -380,27 +384,27 @@ "profile_drawer_sign_out": "Cerrar Sesión", "profile_drawer_trash": "Papelera", "recently_added_page_title": "Recién Agregadas", - "save_to_gallery": "Save to gallery", + "save_to_gallery": "Guardado en la galería", "scaffold_body_error_occurred": "Ha ocurrido un error", "search_bar_hint": "Busca tus fotos", "search_filter_apply": "Aplicar filtros", - "search_filter_camera": "Camera", + "search_filter_camera": "Cámara", "search_filter_camera_make": "Marca", "search_filter_camera_model": "Modelo", "search_filter_camera_title": "Select camera type", - "search_filter_date": "Date", - "search_filter_date_interval": "{start} to {end}", - "search_filter_date_title": "Select a date range", + "search_filter_date": "Fecha", + "search_filter_date_interval": "{start} al {end}", + "search_filter_date_title": "Selecciona un intervalo de fechas", "search_filter_display_option_archive": "Archivado", "search_filter_display_option_favorite": "Favorito", "search_filter_display_option_not_in_album": "No en álbum", "search_filter_display_options": "Display Options", "search_filter_display_options_title": "Display options", - "search_filter_location": "Location", + "search_filter_location": "Ubicación", "search_filter_location_city": "Ciudad", "search_filter_location_country": "País", "search_filter_location_state": "Estado", - "search_filter_location_title": "Select location", + "search_filter_location_title": "Seleccionar una ubicación", "search_filter_media_type": "Media Type", "search_filter_media_type_all": "Todos", "search_filter_media_type_image": "Imagen", @@ -535,7 +539,7 @@ "sharing_silver_appbar_create_shared_album": "Crear un álbum compartido", "sharing_silver_appbar_shared_links": "Enlaces compartidos", "sharing_silver_appbar_share_partner": "Compartir con el compañero", - "sync": "Sync", + "sync": "Sincronizar", "sync_albums": "Sync albums", "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", diff --git a/mobile/assets/i18n/es-MX.json b/mobile/assets/i18n/es-MX.json index 80eeae8d39df4..8361e9a285107 100644 --- a/mobile/assets/i18n/es-MX.json +++ b/mobile/assets/i18n/es-MX.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Eliminar enlace compartido", "description_input_hint_text": "Agregar descripción...", "description_input_submit_error": "Error al actualizar la descripción, verifica el registro para obtener más detalles", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Date and Time", "edit_date_time_dialog_timezone": "Timezone", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/es-PE.json b/mobile/assets/i18n/es-PE.json index 4971435f9ee3b..cee06c9512cd2 100644 --- a/mobile/assets/i18n/es-PE.json +++ b/mobile/assets/i18n/es-PE.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Eliminar enlace compartido", "description_input_hint_text": "Agregar descripción...", "description_input_submit_error": "Error al actualizar la descripción, verifica el registro para obtener más detalles", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Date and Time", "edit_date_time_dialog_timezone": "Timezone", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/es-US.json b/mobile/assets/i18n/es-US.json index cff40b28ba009..ea0b328a808d4 100644 --- a/mobile/assets/i18n/es-US.json +++ b/mobile/assets/i18n/es-US.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Eliminar enlace compartido", "description_input_hint_text": "Agregar descripción...", "description_input_submit_error": "Error al actualizar la descripción, verifica el registro para obtener más detalles", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Date and Time", "edit_date_time_dialog_timezone": "Timezone", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/fi-FI.json b/mobile/assets/i18n/fi-FI.json index 410a7e4719f40..cb687ecef5f24 100644 --- a/mobile/assets/i18n/fi-FI.json +++ b/mobile/assets/i18n/fi-FI.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Poista jaettu linkki", "description_input_hint_text": "Lisää kuvaus...", "description_input_submit_error": "Virhe kuvauksen päivittämisessä, tarkista lisätiedot lokista", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Päivämäärä ja aika", "edit_date_time_dialog_timezone": "Aikavyöhyke", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/fr-CA.json b/mobile/assets/i18n/fr-CA.json index 97a23c4cc70d1..8d742c3a5943e 100644 --- a/mobile/assets/i18n/fr-CA.json +++ b/mobile/assets/i18n/fr-CA.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Supprimer le lien partagé", "description_input_hint_text": "Ajouter une description...", "description_input_submit_error": "Erreur de mise à jour de la description, vérifier le journal pour plus de détails", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Date and Time", "edit_date_time_dialog_timezone": "Timezone", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/fr-FR.json b/mobile/assets/i18n/fr-FR.json index fe280aa4d27f6..9ff5c6f28031e 100644 --- a/mobile/assets/i18n/fr-FR.json +++ b/mobile/assets/i18n/fr-FR.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Supprimer le lien partagé", "description_input_hint_text": "Ajouter une description…", "description_input_submit_error": "Erreur de mise à jour de la description, vérifier le journal pour plus de détails", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Date et heure", "edit_date_time_dialog_timezone": "Fuseau horaire", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/he-IL.json b/mobile/assets/i18n/he-IL.json index b57b6c01d6b93..7ddbb392a0cf6 100644 --- a/mobile/assets/i18n/he-IL.json +++ b/mobile/assets/i18n/he-IL.json @@ -190,7 +190,7 @@ "create_shared_album_page_share": "שתף", "create_shared_album_page_share_add_assets": "הוסף נכסים", "create_shared_album_page_share_select_photos": "בחירת תמונות", - "crop": "Crop", + "crop": "חתוך", "curated_location_page_title": "מקומות", "curated_object_page_title": "דברים", "daily_title_text_date": "E, MMM dd", @@ -210,11 +210,15 @@ "delete_shared_link_dialog_title": "מחק קישור משותף", "description_input_hint_text": "הוסף תיאור...", "description_input_submit_error": "שגיאה בעדכון תיאור, בדוק את היומן לפרטים נוספים", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "תאריך וזמן", "edit_date_time_dialog_timezone": "אזור זמן", - "edit_image_title": "Edit", + "edit_image_title": "ערוך", "edit_location_dialog_title": "מיקום", - "error_saving_image": "Error: {}", + "error_saving_image": "שגיאה: {}", "exif_bottom_sheet_description": "הוסף תיאור...", "exif_bottom_sheet_details": "פרטים", "exif_bottom_sheet_location": "מיקום", @@ -251,7 +255,7 @@ "home_page_first_time_notice": "אם זאת הפעם הראשונה שאת/ה משתמש/ת ביישום, נא להקפיד לבחור אלבומ(ים) לגיבוי כך שציר הזמן יוכל לאכלס תמונות וסרטונים באלבומ(ים)", "home_page_share_err_local": "לא ניתן לשתף נכסים מקומיים על ידי קישור, מדלג", "home_page_upload_err_limit": "ניתן להעלות רק מקסימום של 30 נכסים בכל פעם, מדלג", - "image_saved_successfully": "Image saved", + "image_saved_successfully": "תמונה נשמרה", "image_viewer_page_state_provider_download_error": "שגיאת הורדה", "image_viewer_page_state_provider_download_started": "ההורדה החלה", "image_viewer_page_state_provider_download_success": "הצלחת הורדה", @@ -380,7 +384,7 @@ "profile_drawer_sign_out": "יציאה", "profile_drawer_trash": "אשפה", "recently_added_page_title": "נוסף לאחרונה", - "save_to_gallery": "Save to gallery", + "save_to_gallery": "שמור לגלריה", "scaffold_body_error_occurred": "אירעה שגיאה", "search_bar_hint": "חפש/י בתמונות שלך", "search_filter_apply": "החל סינון", @@ -535,10 +539,10 @@ "sharing_silver_appbar_create_shared_album": "אלבום משותף חדש", "sharing_silver_appbar_shared_links": "קישורים משותפים", "sharing_silver_appbar_share_partner": "שיתוף עם שותף", - "sync": "Sync", - "sync_albums": "Sync albums", - "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", - "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", + "sync": "סנכרן", + "sync_albums": "סנכרן אלבומים", + "sync_albums_manual_subtitle": "סנכרן את כל הסרטונים והתמונות שהועלו לאלבומי הגיבוי שנבחרו", + "sync_upload_album_setting_subtitle": "צור והעלה תמונות וסרטונים שלך לאלבומים שנבחרו ביישום", "tab_controller_nav_library": "ספרייה", "tab_controller_nav_photos": "תמונות", "tab_controller_nav_search": "חיפוש", diff --git a/mobile/assets/i18n/hi-IN.json b/mobile/assets/i18n/hi-IN.json index 7d415cc2f8607..534cae0622d9d 100644 --- a/mobile/assets/i18n/hi-IN.json +++ b/mobile/assets/i18n/hi-IN.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "साझा किए गए लिंक को हटाएं", "description_input_hint_text": "Add description...", "description_input_submit_error": "Error updating description, check the log for more details", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Date and Time", "edit_date_time_dialog_timezone": "Timezone", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/hu-HU.json b/mobile/assets/i18n/hu-HU.json index 72012b1ca3cfd..8f14b9673a578 100644 --- a/mobile/assets/i18n/hu-HU.json +++ b/mobile/assets/i18n/hu-HU.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Megosztott Link Törlése", "description_input_hint_text": "Leírás hozzáadása...", "description_input_submit_error": "Nem sikerült frissíteni a leírást. További információért kérjük, nézd meg az eseménynaplót", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Dátum és Idő", "edit_date_time_dialog_timezone": "Időzóna", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/it-IT.json b/mobile/assets/i18n/it-IT.json index febac12c05927..d7585c753c99b 100644 --- a/mobile/assets/i18n/it-IT.json +++ b/mobile/assets/i18n/it-IT.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Elimina link condiviso", "description_input_hint_text": "Aggiungi descrizione...", "description_input_submit_error": "Errore modificare descrizione, controlli I log per maggiori dettagli", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Data e ora", "edit_date_time_dialog_timezone": "Fuso orario", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/ja-JP.json b/mobile/assets/i18n/ja-JP.json index ccd78380a732b..21b8bea9e35e9 100644 --- a/mobile/assets/i18n/ja-JP.json +++ b/mobile/assets/i18n/ja-JP.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "共有リンクを消す", "description_input_hint_text": "説明を追加", "description_input_submit_error": "説明の編集に失敗しました。詳細はログを確認してください。", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "日付と時間", "edit_date_time_dialog_timezone": "タイムゾーン", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/ko-KR.json b/mobile/assets/i18n/ko-KR.json index bf381fee22dac..e6da75c2f6f28 100644 --- a/mobile/assets/i18n/ko-KR.json +++ b/mobile/assets/i18n/ko-KR.json @@ -31,7 +31,7 @@ "album_viewer_appbar_share_err_delete": "앨범을 삭제하지 못했습니다.", "album_viewer_appbar_share_err_leave": "앨범에서 나가지 못했습니다.", "album_viewer_appbar_share_err_remove": "앨범에서 항목을 제거하지 못했습니다.", - "album_viewer_appbar_share_err_title": "앨범 이름을 변경하지 못했습니다.", + "album_viewer_appbar_share_err_title": "앨범명을 변경하지 못했습니다.", "album_viewer_appbar_share_leave": "앨범 나가기", "album_viewer_appbar_share_remove": "앨범에서 제거", "album_viewer_appbar_share_to": "공유 대상", @@ -55,12 +55,12 @@ "asset_list_settings_subtitle": "사진 배열 레이아웃 설정", "asset_list_settings_title": "사진 배열", "asset_restored_successfully": "항목이 성공적으로 복원되었습니다.", - "assets_deleted_permanently": "{} 미디어가 영구 삭제됨", - "assets_deleted_permanently_from_server": "Immich 서버에서 {} 미디어가 영구 삭제되었습니다.", - "assets_removed_permanently_from_device": "장치에서 {} 미디어가 영구적으로 제거되었습니다.", - "assets_restored_successfully": "{} 미디어가 성공적으로 복원되었습니다.", - "assets_trashed": "{} 미디어가 휴지통에 버려졌습니다.", - "assets_trashed_from_server": "Immich 서버에서 {} 미디어를 삭제했습니다.", + "assets_deleted_permanently": "{}개 항목이 영구적으로 삭제됨", + "assets_deleted_permanently_from_server": "{}개 항목이 Immich 서버에서 영구적으로 삭제됨", + "assets_removed_permanently_from_device": "{}개 항목이 기기에서 영구적으로 삭제됨", + "assets_restored_successfully": "항목 {}개를 복원했습니다.", + "assets_trashed": "휴지통으로 {}개 항목이 이동되었습니다.", + "assets_trashed_from_server": "휴지통으로 Immich 서버의 {}개 항목이 이동되었습니다.", "asset_viewer_settings_title": "보기 옵션", "backup_album_selection_page_albums_device": "기기의 앨범 ({})", "backup_album_selection_page_albums_tap": "한 번 눌러 선택, 두 번 눌러 제외하세요.", @@ -69,55 +69,55 @@ "backup_album_selection_page_selection_info": "선택한 앨범", "backup_album_selection_page_total_assets": "전체 항목", "backup_all": "모두", - "backup_background_service_backup_failed_message": "백업하지 못했습니다. 다시 시도하는 중...", + "backup_background_service_backup_failed_message": "항목을 백업하지 못했습니다. 다시 시도하는 중...", "backup_background_service_connection_failed_message": "서버에 연결하지 못했습니다. 다시 시도하는 중...", "backup_background_service_current_upload_notification": "{} 업로드 중", "backup_background_service_default_notification": "백업할 항목을 확인하는 중...", "backup_background_service_error_title": "백업 오류", "backup_background_service_in_progress_notification": "선택한 항목을 백업하는 중...", "backup_background_service_upload_failure_notification": "{} 업로드 실패", - "backup_controller_page_albums": "백업 대상 앨범", + "backup_controller_page_albums": "백업할 앨범", "backup_controller_page_background_app_refresh_disabled_content": "백그라운드 백업을 사용하려면 설정 > 일반 > 백그라운드 앱 새로 고침에서 백그라운드 앱 새로 고침을 활성화하세요.", "backup_controller_page_background_app_refresh_disabled_title": "백그라운드 새로 고침 비활성화됨", "backup_controller_page_background_app_refresh_enable_button_text": "설정으로 이동", "backup_controller_page_background_battery_info_link": "설정 방법", - "backup_controller_page_background_battery_info_message": "최상의 백그라운드 백업 환경을 위해, Immich의 백그라운드 활동을 제한하는 배터리 최적화를 비활성화하세요.\n\n설정 방법은 기기마다 다르므로, 제조 업체에서 관련 정보를 찾아보세요.", + "backup_controller_page_background_battery_info_message": "최상의 백그라운드 백업 환경을 위해 Immich 백그라운드 활동을 제한하는 배터리 최적화 기능을 비활성화하세요.\n\n기기마다 설정 방법에 차이가 있어 제조 업체에서 관련 정보를 찾아보세요.", "backup_controller_page_background_battery_info_ok": "확인", "backup_controller_page_background_battery_info_title": "배터리 최적화", "backup_controller_page_background_charging": "충전 중에만", "backup_controller_page_background_configure_error": "백그라운드 서비스 구성 실패", "backup_controller_page_background_delay": "새 콘텐츠 백업 간격: {}", - "backup_controller_page_background_description": "백그라운드 서비스를 활성화하면 앱을 열지 않고도 새 콘텐츠를 자동으로 백업할 수 있습니다.", - "backup_controller_page_background_is_off": "자동 백그라운드 백업이 비활성화되었습니다.", - "backup_controller_page_background_is_on": "자동 백그라운드 백업이 활성화되었습니다.", + "backup_controller_page_background_description": "백그라운드 서비스를 활성화하여 앱을 실행하지 않고 새 항목을 자동으로 백업하세요.", + "backup_controller_page_background_is_off": "백그라운드 백업이 비활성화되었습니다.", + "backup_controller_page_background_is_on": "백그라운드 백업이 활성화되었습니다.", "backup_controller_page_background_turn_off": "백그라운드 서비스 비활성화", "backup_controller_page_background_turn_on": "백그라운드 서비스 활성화", "backup_controller_page_background_wifi": "Wi-Fi에서만", "backup_controller_page_backup": "백업", - "backup_controller_page_backup_selected": "선택: ", + "backup_controller_page_backup_selected": "선택됨:", "backup_controller_page_backup_sub": "백업된 사진 및 동영상", "backup_controller_page_cancel": "취소", "backup_controller_page_created": "생성일: {}", - "backup_controller_page_desc_backup": "앱을 열 때 새 항목을 서버에 자동으로 업로드하려면 포그라운드 백업을 활성화하세요.", - "backup_controller_page_excluded": "제외: ", + "backup_controller_page_desc_backup": "포그라운드 백업을 활성화하여 앱을 시작할 때 새 항목을 서버에 자동으로 업로드하세요.", + "backup_controller_page_excluded": "제외됨:", "backup_controller_page_failed": "실패 ({})", "backup_controller_page_filename": "파일명: {} [{}]", "backup_controller_page_id": "ID: {}", "backup_controller_page_info": "백업 정보", "backup_controller_page_none_selected": "선택한 항목이 없습니다.", "backup_controller_page_remainder": "남은 항목", - "backup_controller_page_remainder_sub": "백업할 사진 및 동영상", + "backup_controller_page_remainder_sub": "백업 대기 중인 사진 및 동영상", "backup_controller_page_select": "선택", "backup_controller_page_server_storage": "저장 공간", "backup_controller_page_start_backup": "백업 시작", - "backup_controller_page_status_off": "자동 백업이 비활성화되었습니다.", - "backup_controller_page_status_on": "자동 백업이 활성화되었습니다.", + "backup_controller_page_status_off": "포그라운드 백업이 비활성화되었습니다.", + "backup_controller_page_status_on": "포그라운드 백업이 활성화되었습니다.", "backup_controller_page_storage_format": "{} 사용 중, 전체 {}", - "backup_controller_page_to_backup": "백업 대상 앨범 목록", + "backup_controller_page_to_backup": "백업할 앨범 목록", "backup_controller_page_total": "전체", - "backup_controller_page_total_sub": "선택한 앨범의 모든 사진 및 동영상", - "backup_controller_page_turn_off": "백업 비활성화", - "backup_controller_page_turn_on": "백업 활성화", + "backup_controller_page_total_sub": "선택한 앨범의 고유한 사진 및 동영상", + "backup_controller_page_turn_off": "비활성화", + "backup_controller_page_turn_on": "활성화", "backup_controller_page_uploading_file_info": "파일 정보 업로드 중", "backup_err_only_album": "유일한 앨범은 제거할 수 없습니다.", "backup_info_card_assets": "항목", @@ -154,7 +154,7 @@ "client_cert_enter_password": "비밀번호 입력", "client_cert_import": "가져오기", "client_cert_import_success_msg": "클라이언트 인증서를 가져왔습니다.", - "client_cert_invalid_msg": "유효하지 않은 인증서이거나 비밀번호가 일치하지 않습니다.", + "client_cert_invalid_msg": "유효하지 않은 인증서 또는 패스프레이즈가 일치하지 않습니다.", "client_cert_remove": "제거", "client_cert_remove_msg": "클라이언트 인증서가 제거되었습니다.", "client_cert_subtitle": "인증서 가져오기/제거는 로그인 전에만 가능합니다. PKCS12 (.p12, .pfx) 형식을 지원합니다.", @@ -210,11 +210,15 @@ "delete_shared_link_dialog_title": "공유 링크 삭제", "description_input_hint_text": "설명 추가...", "description_input_submit_error": "설명을 변경하는 중 문제가 발생했습니다. 자세한 내용은 로그를 참조하세요.", + "download_error": "다운로드 중 문제가 발생했습니다.", + "download_started": "다운로드가 시작되었습니다.", + "download_sucess": "다운로드가 완료되었습니다.", + "download_sucess_android": "미디어가 DCIM/Immich에 저장되었습니다.", "edit_date_time_dialog_date_time": "날짜 및 시간", "edit_date_time_dialog_timezone": "시간대", "edit_image_title": "편집", "edit_location_dialog_title": "위치", - "error_saving_image": "오류입니다: {}", + "error_saving_image": "오류: {}", "exif_bottom_sheet_description": "설명 추가...", "exif_bottom_sheet_details": "상세 정보", "exif_bottom_sheet_location": "위치", @@ -251,7 +255,7 @@ "home_page_first_time_notice": "앱을 처음 사용하는 경우 타임라인에 앨범의 사진과 동영상을 채울 수 있도록 백업할 앨범을 선택하세요.", "home_page_share_err_local": "기기의 항목은 링크로 공유할 수 없습니다. 건너뜁니다.", "home_page_upload_err_limit": "한 번에 최대 30개의 항목만 업로드할 수 있습니다.", - "image_saved_successfully": "이미지 저장", + "image_saved_successfully": "이미지가 저장되었습니다.", "image_viewer_page_state_provider_download_error": "다운로드 오류", "image_viewer_page_state_provider_download_started": "다운로드가 시작되었습니다.", "image_viewer_page_state_provider_download_success": "다운로드 완료", @@ -282,14 +286,14 @@ "login_form_back_button_text": "뒤로", "login_form_button_text": "로그인", "login_form_email_hint": "youremail@email.com", - "login_form_endpoint_hint": "https://your-server-ip:port/api", + "login_form_endpoint_hint": "http://your-server-ip:port/api", "login_form_endpoint_url": "서버 엔드포인트 URL", "login_form_err_http": "http:// 또는 https://로 시작해야 합니다.", "login_form_err_invalid_email": "유효하지 않은 이메일", "login_form_err_invalid_url": "잘못된 URL입니다.", - "login_form_err_leading_whitespace": "앞에 공백 문자가 있습니다.", - "login_form_err_trailing_whitespace": "뒤에 공백 문자가 있습니다.", - "login_form_failed_get_oauth_server_config": "OAuth 로그인 중 문제 발생, 서버 URL을 확인해주세요.", + "login_form_err_leading_whitespace": "시작 부분에 공백이 있습니다.", + "login_form_err_trailing_whitespace": "끝 부분에 공백이 있습니다.", + "login_form_failed_get_oauth_server_config": "OAuth 로그인 중 문제 발생, 서버 URL을 확인하세요.", "login_form_failed_get_oauth_server_disable": "이 서버는 OAuth 기능을 지원하지 않습니다.", "login_form_failed_login": "로그인 오류. 서버 URL, 이메일 및 비밀번호를 확인하세요.", "login_form_handshake_exception": "서버와 통신 중 인증서 예외가 발생했습니다. 자체 서명된 인증서를 사용 중이라면, 설정에서 자체 서명된 인증서 허용을 활성화하세요.", @@ -343,7 +347,7 @@ "notification_permission_dialog_cancel": "취소", "notification_permission_dialog_content": "알림을 활성화하려면 설정에서 알림 권한을 허용하세요.", "notification_permission_dialog_settings": "설정", - "notification_permission_list_tile_content": "알림을 활성화하기 위해 권한을 부여하세요.", + "notification_permission_list_tile_content": "알림을 활성화하려면 권한을 부여하세요.", "notification_permission_list_tile_enable_button": "알림 활성화", "notification_permission_list_tile_title": "알림 권한", "partner_list_user_photos": "{user}님의 사진", @@ -371,7 +375,7 @@ "profile_drawer_app_logs": "로그", "profile_drawer_client_out_of_date_major": "모바일 앱이 최신 버전이 아닙니다. 최신 버전으로 업데이트하세요.", "profile_drawer_client_out_of_date_minor": "모바일 앱이 최신 버전이 아닙니다. 최신 버전으로 업데이트하세요.", - "profile_drawer_client_server_up_to_date": "모바일 앱과 서버가 최신 버전입니다.", + "profile_drawer_client_server_up_to_date": "클라이언트와 서버가 최신입니다.", "profile_drawer_documentation": "문서", "profile_drawer_github": "Github", "profile_drawer_server_out_of_date_major": "서버 버전이 최신이 아닙니다. 최신 버전으로 업데이트하세요.", @@ -389,7 +393,7 @@ "search_filter_camera_model": "모델명", "search_filter_camera_title": "카메라 종류 선택", "search_filter_date": "날짜", - "search_filter_date_interval": "{start}에서 {end} 까지", + "search_filter_date_interval": "{start} - {end}", "search_filter_date_title": "날짜 범위 선택", "search_filter_display_option_archive": "보관함", "search_filter_display_option_favorite": "즐겨찾기", @@ -411,8 +415,8 @@ "search_page_categories": "분류", "search_page_favorites": "즐겨찾기", "search_page_motion_photos": "모션 포토", - "search_page_no_objects": "사물 정보가 없습니다.", - "search_page_no_places": "장소 정보가 없습니다.", + "search_page_no_objects": "사용 가능한 사물 정보 없음", + "search_page_no_places": "사용 가능한 위치 정보 없음", "search_page_people": "인물", "search_page_person_add_name_dialog_cancel": "취소", "search_page_person_add_name_dialog_hint": "이름", @@ -435,7 +439,7 @@ "search_suggestion_list_smart_search_hint_2": "m:your-search-term", "select_additional_user_for_sharing_page_suggestions": "추천", "select_user_for_sharing_page_err_album": "앨범을 생성하지 못했습니다.", - "select_user_for_sharing_page_share_suggestions": "추천", + "select_user_for_sharing_page_share_suggestions": "제안", "server_info_box_app_version": "앱 버전", "server_info_box_latest_release": "최신 버전", "server_info_box_server_url": "서버 URL", @@ -454,12 +458,12 @@ "setting_notifications_notify_minutes": "{}분 후", "setting_notifications_notify_never": "알리지 않음", "setting_notifications_notify_seconds": "{}초", - "setting_notifications_single_progress_subtitle": "각 항목의 세부 업로드 정보 표시", - "setting_notifications_single_progress_title": "백그라운드 작업의 세부 진행률 표시", + "setting_notifications_single_progress_subtitle": "개별 항목의 상세 업로드 정보 표시", + "setting_notifications_single_progress_title": "백그라운드 백업 상세 진행률 표시", "setting_notifications_subtitle": "알림 기본 설정 조정", "setting_notifications_title": "알림", "setting_notifications_total_progress_subtitle": "전체 업로드 진행률 (완료/전체)", - "setting_notifications_total_progress_title": "백그라운드 작업의 전체 진행률 표시", + "setting_notifications_total_progress_title": "백그라운드 백업 전체 진행률 표시", "setting_pages_app_bar_settings": "설정", "settings_require_restart": "설정을 적용하려면 Immich를 다시 시작하세요.", "setting_video_viewer_looping_subtitle": "상세 보기에서 동영상을 자동으로 반복합니다.", @@ -467,7 +471,7 @@ "setting_video_viewer_title": "동영상", "share_add": "추가", "share_add_photos": "사진 추가", - "share_add_title": "앨범 제목 입력", + "share_add_title": "앨범명 추가", "share_assets_selected": "{}개 항목 선택됨", "share_create_album": "앨범 생성", "shared_album_activities_input_disable": "댓글이 비활성화되었습니다", @@ -528,7 +532,7 @@ "shared_link_manage_links": "공유 링크 관리", "shared_link_public_album": "공개 앨범", "share_done": "완료", - "share_invite": "앨범에 초대", + "share_invite": "앨범으로 초대", "sharing_page_album": "공유 앨범", "sharing_page_description": "공유 앨범을 만들어 주변 사람들과 사진 및 동영상을 공유하세요.", "sharing_page_empty_list": "공유 앨범 없음", @@ -537,8 +541,8 @@ "sharing_silver_appbar_share_partner": "파트너와 공유", "sync": "동기화", "sync_albums": "앨범 동기화", - "sync_albums_manual_subtitle": "업로드한 모든 동영상과 사진을 선택한 백업 앨범에 동기화합니다.", - "sync_upload_album_setting_subtitle": "Immich에서 선택한 앨범에 사진 및 동영상을 만들고 업로드하세요.", + "sync_albums_manual_subtitle": "업로드한 모든 동영상과 사진을 선택한 백업 앨범에 동기화", + "sync_upload_album_setting_subtitle": "선택한 앨범을 Immich에 생성하고 사진 및 동영상을 업로드하세요.", "tab_controller_nav_library": "라이브러리", "tab_controller_nav_photos": "사진", "tab_controller_nav_search": "검색", @@ -546,7 +550,7 @@ "theme_setting_asset_list_storage_indicator_title": "항목에 스토리지 동기화 여부 표시", "theme_setting_asset_list_tiles_per_row_title": "한 줄에 표시할 항목 수 ({})", "theme_setting_colorful_interface_subtitle": "배경에 대표 색상을 적용합니다.", - "theme_setting_colorful_interface_title": "컬러풀 인터페이스", + "theme_setting_colorful_interface_title": "미려한 인터페이스", "theme_setting_dark_mode_switch": "다크 모드", "theme_setting_image_viewer_quality_subtitle": "상세 보기 이미지 품질 조정", "theme_setting_image_viewer_quality_title": "이미지 보기 품질", @@ -559,7 +563,7 @@ "theme_setting_three_stage_loading_subtitle": "이 기능은 앱의 로드 성능을 향상시킬 수 있지만 더 많은 데이터를 사용합니다.", "theme_setting_three_stage_loading_title": "3단계 로드 활성화", "translated_text_options": "옵션", - "trash_emptied": "휴지통 비우기", + "trash_emptied": "휴지통을 비움", "trash_page_delete": "삭제", "trash_page_delete_all": "모두 삭제", "trash_page_empty_trash_btn": "휴지통 비우기", diff --git a/mobile/assets/i18n/lt-LT.json b/mobile/assets/i18n/lt-LT.json index 3d1fb4e4d6f3e..324c9069fdf46 100644 --- a/mobile/assets/i18n/lt-LT.json +++ b/mobile/assets/i18n/lt-LT.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Delete Shared Link", "description_input_hint_text": "Add description...", "description_input_submit_error": "Error updating description, check the log for more details", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Date and Time", "edit_date_time_dialog_timezone": "Timezone", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/lv-LV.json b/mobile/assets/i18n/lv-LV.json index 70af1ac37eae5..c9f86535fc5a3 100644 --- a/mobile/assets/i18n/lv-LV.json +++ b/mobile/assets/i18n/lv-LV.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Dzēst Kopīgošanas saiti", "description_input_hint_text": "Pievienot aprakstu...", "description_input_submit_error": "Atjauninot aprakstu, radās kļūda; papildinformāciju skatiet žurnālā", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Datums un Laiks", "edit_date_time_dialog_timezone": "Laika zona", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/mn.json b/mobile/assets/i18n/mn.json index 5ebabbee8c0c2..cf951cea0b50b 100644 --- a/mobile/assets/i18n/mn.json +++ b/mobile/assets/i18n/mn.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Delete Shared Link", "description_input_hint_text": "Add description...", "description_input_submit_error": "Error updating description, check the log for more details", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Date and Time", "edit_date_time_dialog_timezone": "Timezone", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/nb-NO.json b/mobile/assets/i18n/nb-NO.json index a5f151db597bb..7141faef72687 100644 --- a/mobile/assets/i18n/nb-NO.json +++ b/mobile/assets/i18n/nb-NO.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Slett delt link", "description_input_hint_text": "Legg til beskrivelse ...", "description_input_submit_error": "Feil ved oppdatering av beskrivelse, sjekk loggen for flere detaljer", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Dato og tid", "edit_date_time_dialog_timezone": "Tidssone", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/nl-NL.json b/mobile/assets/i18n/nl-NL.json index f34480f9858a1..a6a151d5069f0 100644 --- a/mobile/assets/i18n/nl-NL.json +++ b/mobile/assets/i18n/nl-NL.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Verwijder gedeelde link", "description_input_hint_text": "Beschrijving toevoegen...", "description_input_submit_error": "Beschrijving bijwerken mislukt, controleer het logboek voor meer details", + "download_error": "Fout bij downloaden", + "download_started": "Download gestart", + "download_sucess": "Succesvol gedownload", + "download_sucess_android": "Het bestand is gedownload naar DCIM/Immich", "edit_date_time_dialog_date_time": "Datum en tijd", "edit_date_time_dialog_timezone": "Tijdzone", "edit_image_title": "Bewerken", diff --git a/mobile/assets/i18n/pl-PL.json b/mobile/assets/i18n/pl-PL.json index 8a737d31d5312..ec9009e28ff65 100644 --- a/mobile/assets/i18n/pl-PL.json +++ b/mobile/assets/i18n/pl-PL.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Usuń udostępniony link", "description_input_hint_text": "Dodaj opis...", "description_input_submit_error": "Błąd aktualizacji opisu, sprawdź dziennik, aby uzyskać więcej szczegółów", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Data i godzina", "edit_date_time_dialog_timezone": "Strefa czasowa", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/pt-PT.json b/mobile/assets/i18n/pt-PT.json index 769289b59c903..991fdfaf361f4 100644 --- a/mobile/assets/i18n/pt-PT.json +++ b/mobile/assets/i18n/pt-PT.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Excluir link compartilhado", "description_input_hint_text": "Adicionar descrição...", "description_input_submit_error": "Erro ao atualizar a descrição, verifique o registo para obter mais detalhes", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Data e Hora", "edit_date_time_dialog_timezone": "Fuso horário", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/ro-RO.json b/mobile/assets/i18n/ro-RO.json index 8ac8601714a1e..4cb043d1962d2 100644 --- a/mobile/assets/i18n/ro-RO.json +++ b/mobile/assets/i18n/ro-RO.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Șterge link distribuire", "description_input_hint_text": "Adaugă descriere...", "description_input_submit_error": "Eroare actualizare descriere, verifică log-urile pentru mai multe detalii", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Dată și Oră", "edit_date_time_dialog_timezone": "Fus orar", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/ru-RU.json b/mobile/assets/i18n/ru-RU.json index 8679b46df39df..1c5741a963ef2 100644 --- a/mobile/assets/i18n/ru-RU.json +++ b/mobile/assets/i18n/ru-RU.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Удалить общую ссылку", "description_input_hint_text": "Добавить описание...", "description_input_submit_error": "Не удалось обновить описание, проверьте логи, чтобы узнать причину", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Дата и время", "edit_date_time_dialog_timezone": "Часовой пояс", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/sk-SK.json b/mobile/assets/i18n/sk-SK.json index 2c48a7f6c5e8a..200db9e32038b 100644 --- a/mobile/assets/i18n/sk-SK.json +++ b/mobile/assets/i18n/sk-SK.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Odstrániť zdieľaný odkaz", "description_input_hint_text": "Pridať popis...", "description_input_submit_error": "Chyba pri aktualizovaní popisu, zobrazte log pre viac detailov", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Dátum a čas", "edit_date_time_dialog_timezone": "Časové pásmo", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/sl-SI.json b/mobile/assets/i18n/sl-SI.json index c7018f7b6fa53..7871d65de9b90 100644 --- a/mobile/assets/i18n/sl-SI.json +++ b/mobile/assets/i18n/sl-SI.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Izbriši povezavo skupne rabe", "description_input_hint_text": "Dodaj opis ...", "description_input_submit_error": "Napaka pri posodabljanju opisa, preverite dnevnik za več podrobnosti", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Datum in ura", "edit_date_time_dialog_timezone": "Časovni pas", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/sr-Cyrl.json b/mobile/assets/i18n/sr-Cyrl.json index 3d1fb4e4d6f3e..324c9069fdf46 100644 --- a/mobile/assets/i18n/sr-Cyrl.json +++ b/mobile/assets/i18n/sr-Cyrl.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Delete Shared Link", "description_input_hint_text": "Add description...", "description_input_submit_error": "Error updating description, check the log for more details", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Date and Time", "edit_date_time_dialog_timezone": "Timezone", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/sr-Latn.json b/mobile/assets/i18n/sr-Latn.json index 31a0d0f48e352..744ebe72ce63f 100644 --- a/mobile/assets/i18n/sr-Latn.json +++ b/mobile/assets/i18n/sr-Latn.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Delete Shared Link", "description_input_hint_text": "Add description...", "description_input_submit_error": "Error updating description, check the log for more details", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Date and Time", "edit_date_time_dialog_timezone": "Timezone", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/sv-FI.json b/mobile/assets/i18n/sv-FI.json index 3d1fb4e4d6f3e..324c9069fdf46 100644 --- a/mobile/assets/i18n/sv-FI.json +++ b/mobile/assets/i18n/sv-FI.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Delete Shared Link", "description_input_hint_text": "Add description...", "description_input_submit_error": "Error updating description, check the log for more details", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Date and Time", "edit_date_time_dialog_timezone": "Timezone", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/sv-SE.json b/mobile/assets/i18n/sv-SE.json index d51bdd54ed146..0d6c7a310855d 100644 --- a/mobile/assets/i18n/sv-SE.json +++ b/mobile/assets/i18n/sv-SE.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Ta Bort Delad Länk", "description_input_hint_text": "Lägg till beskrivning...", "description_input_submit_error": "Fel vid uppdatering av beskrivning, se loggen för fler detaljer", + "download_error": "Fel vid nedladdning", + "download_started": "Nedladdning påbörjad", + "download_sucess": "Nedladdning lyckades", + "download_sucess_android": "Media har laddats ner till DCIM/Immich", "edit_date_time_dialog_date_time": "Datum och Tid", "edit_date_time_dialog_timezone": "Tidszon", "edit_image_title": "Redigera", diff --git a/mobile/assets/i18n/th-TH.json b/mobile/assets/i18n/th-TH.json index 9a45ff463a280..c93b0a37cfa12 100644 --- a/mobile/assets/i18n/th-TH.json +++ b/mobile/assets/i18n/th-TH.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "ลบลิงก์ที่แชร์", "description_input_hint_text": "เพื่มรายละเอียด...", "description_input_submit_error": "อัพเดตรายละเอียดผิดพลาด ตรวจสอบ log เพื่อรายละเอียดเพิ่มเติม", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "วันและเวลา", "edit_date_time_dialog_timezone": "เขดเวลา", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/uk-UA.json b/mobile/assets/i18n/uk-UA.json index 816ef5277f9d9..f3b2b0ba5f4b4 100644 --- a/mobile/assets/i18n/uk-UA.json +++ b/mobile/assets/i18n/uk-UA.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Видалити спільне посилання", "description_input_hint_text": "Додати опис...", "description_input_submit_error": "Помилка оновлення опису, перевірте логи для подробиць", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Дата і час", "edit_date_time_dialog_timezone": "Часовий пояс", "edit_image_title": "Edit", diff --git a/mobile/assets/i18n/vi-VN.json b/mobile/assets/i18n/vi-VN.json index 31eff88dd7eaf..6cd2a080e4713 100644 --- a/mobile/assets/i18n/vi-VN.json +++ b/mobile/assets/i18n/vi-VN.json @@ -94,7 +94,7 @@ "backup_controller_page_background_turn_on": "Bật dịch vụ nền", "backup_controller_page_background_wifi": "Chỉ khi dùng Wi-Fi", "backup_controller_page_backup": "Sao lưu", - "backup_controller_page_backup_selected": "Đã chọn:", + "backup_controller_page_backup_selected": "Đã chọn: ", "backup_controller_page_backup_sub": "Ảnh và video đã sao lưu", "backup_controller_page_cancel": "Từ chối", "backup_controller_page_created": "Tạo vào: {}", @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Xoá liên kết đã chia sẻ", "description_input_hint_text": "Thêm mô tả...", "description_input_submit_error": "Cập nhật mô tả không thành công, vui lòng kiểm tra nhật ký để biết thêm chi tiết", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Ngày và Giờ", "edit_date_time_dialog_timezone": "Múi giờ", "edit_image_title": "Sửa", diff --git a/mobile/assets/i18n/zh-CN.json b/mobile/assets/i18n/zh-CN.json index e421fff575f53..d4e7f0406e3aa 100644 --- a/mobile/assets/i18n/zh-CN.json +++ b/mobile/assets/i18n/zh-CN.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "删除共享链接", "description_input_hint_text": "添加描述...", "description_input_submit_error": "更新描述时出错,请检查日志以获取更多详细信息", + "download_error": "下载出错", + "download_started": "开始下载", + "download_sucess": "下载成功", + "download_sucess_android": "媒体已下载至 DCIM/Immich", "edit_date_time_dialog_date_time": "日期和时间", "edit_date_time_dialog_timezone": "时区", "edit_image_title": "编辑", diff --git a/mobile/assets/i18n/zh-Hans.json b/mobile/assets/i18n/zh-Hans.json index d98299a046634..f5ec6ab2a1bc8 100644 --- a/mobile/assets/i18n/zh-Hans.json +++ b/mobile/assets/i18n/zh-Hans.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "删除共享链接", "description_input_hint_text": "添加描述...", "description_input_submit_error": "更新描述时出错,请检查日志以获取更多详细信息", + "download_error": "下载出错", + "download_started": "开始下载", + "download_sucess": "下载成功", + "download_sucess_android": "媒体已下载至 DCIM/Immich", "edit_date_time_dialog_date_time": "日期和时间", "edit_date_time_dialog_timezone": "时区", "edit_image_title": "编辑", diff --git a/mobile/assets/i18n/zh-TW.json b/mobile/assets/i18n/zh-TW.json index 3d1fb4e4d6f3e..324c9069fdf46 100644 --- a/mobile/assets/i18n/zh-TW.json +++ b/mobile/assets/i18n/zh-TW.json @@ -210,6 +210,10 @@ "delete_shared_link_dialog_title": "Delete Shared Link", "description_input_hint_text": "Add description...", "description_input_submit_error": "Error updating description, check the log for more details", + "download_error": "Download Error", + "download_started": "Download started", + "download_sucess": "Download success", + "download_sucess_android": "The media has been downloaded to DCIM/Immich", "edit_date_time_dialog_date_time": "Date and Time", "edit_date_time_dialog_timezone": "Timezone", "edit_image_title": "Edit", From 068904f7461118d4e4d6f8733e7e4186b6c38c84 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 13:49:08 +0000 Subject: [PATCH 158/160] chore: version v1.114.0 --- cli/package-lock.json | 6 +++--- cli/package.json | 2 +- docs/static/archived-versions.json | 4 ++++ e2e/package-lock.json | 8 ++++---- e2e/package.json | 2 +- machine-learning/pyproject.toml | 2 +- mobile/android/fastlane/Fastfile | 4 ++-- mobile/ios/fastlane/Fastfile | 2 +- mobile/openapi/README.md | 2 +- mobile/pubspec.yaml | 2 +- open-api/immich-openapi-specs.json | 2 +- open-api/typescript-sdk/package-lock.json | 4 ++-- open-api/typescript-sdk/package.json | 2 +- open-api/typescript-sdk/src/fetch-client.ts | 2 +- server/package-lock.json | 4 ++-- server/package.json | 2 +- web/package-lock.json | 6 +++--- web/package.json | 2 +- 18 files changed, 31 insertions(+), 27 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 3778fac8c0f13..f443c141b9e06 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/cli", - "version": "2.2.17", + "version": "2.2.18", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/cli", - "version": "2.2.17", + "version": "2.2.18", "license": "GNU Affero General Public License version 3", "dependencies": { "fast-glob": "^3.3.2", @@ -52,7 +52,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.113.1", + "version": "1.114.0", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/cli/package.json b/cli/package.json index efb52c8afac4e..0d560c8456585 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@immich/cli", - "version": "2.2.17", + "version": "2.2.18", "description": "Command Line Interface (CLI) for Immich", "type": "module", "exports": "./dist/index.js", diff --git a/docs/static/archived-versions.json b/docs/static/archived-versions.json index c6c7832b62d46..c16413f4c5f49 100644 --- a/docs/static/archived-versions.json +++ b/docs/static/archived-versions.json @@ -1,4 +1,8 @@ [ + { + "label": "v1.114.0", + "url": "https://v1.114.0.archive.immich.app" + }, { "label": "v1.113.1", "url": "https://v1.113.1.archive.immich.app" diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 7c54a3f227bc2..97e396c09f1b0 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-e2e", - "version": "1.113.1", + "version": "1.114.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-e2e", - "version": "1.113.1", + "version": "1.114.0", "license": "GNU Affero General Public License version 3", "devDependencies": { "@eslint/eslintrc": "^3.1.0", @@ -45,7 +45,7 @@ }, "../cli": { "name": "@immich/cli", - "version": "2.2.17", + "version": "2.2.18", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { @@ -92,7 +92,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.113.1", + "version": "1.114.0", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/e2e/package.json b/e2e/package.json index 3da113da35247..3577bc4510a9e 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,6 +1,6 @@ { "name": "immich-e2e", - "version": "1.113.1", + "version": "1.114.0", "description": "", "main": "index.js", "type": "module", diff --git a/machine-learning/pyproject.toml b/machine-learning/pyproject.toml index ac2ff0e34e72b..a69fb33a8d50e 100644 --- a/machine-learning/pyproject.toml +++ b/machine-learning/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "machine-learning" -version = "1.113.1" +version = "1.114.0" description = "" authors = ["Hau Tran "] readme = "README.md" diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index 0338c6ff343af..c127032b19ee2 100644 --- a/mobile/android/fastlane/Fastfile +++ b/mobile/android/fastlane/Fastfile @@ -35,8 +35,8 @@ platform :android do task: 'bundle', build_type: 'Release', properties: { - "android.injected.version.code" => 157, - "android.injected.version.name" => "1.113.1", + "android.injected.version.code" => 158, + "android.injected.version.name" => "1.114.0", } ) upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab') diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index 68b577c9c9bdb..c1740771d98c4 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -19,7 +19,7 @@ platform :ios do desc "iOS Release" lane :release do increment_version_number( - version_number: "1.113.1" + version_number: "1.114.0" ) increment_build_number( build_number: latest_testflight_build_number + 1, diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index b67a3e33839e1..bb845157979b6 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -3,7 +3,7 @@ Immich API This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: -- API version: 1.113.1 +- API version: 1.114.0 - Generator version: 7.8.0 - Build package: org.openapitools.codegen.languages.DartClientCodegen diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 728b90c3f330a..3db5457c8c1c1 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -2,7 +2,7 @@ name: immich_mobile description: Immich - selfhosted backup media file on mobile phone publish_to: 'none' -version: 1.113.1+157 +version: 1.114.0+158 environment: sdk: '>=3.3.0 <4.0.0' diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index bbfabfe1d7bf6..2325f24ee59d4 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -7394,7 +7394,7 @@ "info": { "title": "Immich", "description": "Immich API", - "version": "1.113.1", + "version": "1.114.0", "contact": {} }, "tags": [], diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index 39140326519d5..6d5b78ee9a588 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/sdk", - "version": "1.113.1", + "version": "1.114.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/sdk", - "version": "1.113.1", + "version": "1.114.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index a62e032ef6199..afa5f4585810b 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@immich/sdk", - "version": "1.113.1", + "version": "1.114.0", "description": "Auto-generated TypeScript SDK for the Immich API", "type": "module", "main": "./build/index.js", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 9e74ae88a00d7..43777552c59bf 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1,6 +1,6 @@ /** * Immich - * 1.113.1 + * 1.114.0 * DO NOT MODIFY - This file has been generated using oazapfts. * See https://www.npmjs.com/package/oazapfts */ diff --git a/server/package-lock.json b/server/package-lock.json index ca6a54c82c20e..51f038dfa0301 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich", - "version": "1.113.1", + "version": "1.114.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "immich", - "version": "1.113.1", + "version": "1.114.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@nestjs/bullmq": "^10.0.1", diff --git a/server/package.json b/server/package.json index 58d7208adf91f..48e873a8f84c6 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.113.1", + "version": "1.114.0", "description": "", "author": "", "private": true, diff --git a/web/package-lock.json b/web/package-lock.json index 7bcd5c2b01d47..0fe66f8832e7b 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-web", - "version": "1.113.1", + "version": "1.114.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-web", - "version": "1.113.1", + "version": "1.114.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@formatjs/icu-messageformat-parser": "^2.7.8", @@ -74,7 +74,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.113.1", + "version": "1.114.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/web/package.json b/web/package.json index c84bbf0db43d3..4dddc36e41c2e 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "immich-web", - "version": "1.113.1", + "version": "1.114.0", "license": "GNU Affero General Public License version 3", "scripts": { "dev": "vite dev --host 0.0.0.0 --port 3000", From 8e677ed844592741b8849db09dfcf139ce025b97 Mon Sep 17 00:00:00 2001 From: Zack Pollard Date: Fri, 6 Sep 2024 19:01:01 +0100 Subject: [PATCH 159/160] ci: tag ml and server images even when they aren't built (#12390) --- .github/workflows/docker.yml | 70 ++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 6be26c9bbe62f..8a2ba9f841434 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -40,6 +40,57 @@ jobs: id: should_force run: echo "should_force=${{ github.event_name == 'workflow_dispatch' || github.event_name == 'release' }}" >> "$GITHUB_OUTPUT" + retag_ml: + name: Re-Tag ML + needs: pre-job + if: ${{ needs.pre-job.outputs.should_run_ml == 'false' }} + runs-on: ubuntu-latest + strategy: + matrix: + suffix: ["", "-cuda", "-openvino", "-armnn"] + steps: + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + # Skip when PR from a fork + if: ${{ !github.event.pull_request.head.repo.fork }} + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Re-tag image + run: | + REGISTRY_NAME="ghcr.io" + REPOSITORY=${{ github.repository_owner }}/immich-machine-learning + TAG_OLD=main${{ matrix.suffix }} + TAG_NEW=${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }} + docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_NEW $REGISTRY_NAME/$REPOSITORY:$TAG_OLD + + retag_server: + name: Re-Tag Server + needs: pre-job + if: ${{ needs.pre-job.outputs.should_run_server == 'false' }} + runs-on: ubuntu-latest + strategy: + matrix: + suffix: [""] + steps: + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + # Skip when PR from a fork + if: ${{ !github.event.pull_request.head.repo.fork }} + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Re-tag image + run: | + REGISTRY_NAME="ghcr.io" + REPOSITORY=${{ github.repository_owner }}/immich-server + TAG_OLD=main${{ matrix.suffix }} + TAG_NEW=${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }} + docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_NEW $REGISTRY_NAME/$REPOSITORY:$TAG_OLD + + build_and_push_ml: name: Build and Push ML needs: pre-job @@ -235,9 +286,22 @@ jobs: BUILD_SOURCE_REF=${{ github.ref_name }} BUILD_SOURCE_COMMIT=${{ github.sha }} - success-check: - name: Docker Build & Push Success - needs: [build_and_push_ml, build_and_push_server] + success-check-server: + name: Docker Build & Push Server Success + needs: [build_and_push_server, retag_server] + runs-on: ubuntu-latest + if: always() + steps: + - name: Any jobs failed? + if: ${{ contains(needs.*.result, 'failure') }} + run: exit 1 + - name: All jobs passed or skipped + if: ${{ !(contains(needs.*.result, 'failure')) }} + run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}" + + success-check-ml: + name: Docker Build & Push ML Success + needs: [build_and_push_ml, retag_ml] runs-on: ubuntu-latest if: always() steps: From 7bcef37ba70837008c7b2e613de8386bc029ae41 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Fri, 6 Sep 2024 15:13:17 -0400 Subject: [PATCH 160/160] chore: auto-label translations (#12404) --- .github/labeler.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/labeler.yml b/.github/labeler.yml index a0eec41346a68..2a9abc7840381 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -33,3 +33,8 @@ documentation: - changed-files: - any-glob-to-any-file: - machine-learning/app/** + +changelog:translation: + - changed-files: + - any-glob-to-any-file: + - web/src/lib/i18n/*.json