diff --git a/.devcontainer/mobile/container-compose-overrides.yml b/.devcontainer/mobile/container-compose-overrides.yml index c0655e50e7..d6cd95018f 100644 --- a/.devcontainer/mobile/container-compose-overrides.yml +++ b/.devcontainer/mobile/container-compose-overrides.yml @@ -12,7 +12,6 @@ services: - server_node_modules:/workspaces/immich/server/node_modules - web_node_modules:/workspaces/immich/web/node_modules - ${UPLOAD_LOCATION}/photos:/data - - ${UPLOAD_LOCATION}/photos/upload:/data/upload - /etc/localtime:/etc/localtime:ro database: diff --git a/.devcontainer/server/container-compose-overrides.yml b/.devcontainer/server/container-compose-overrides.yml index 0fa8f2ff48..3be5cd8f3f 100644 --- a/.devcontainer/server/container-compose-overrides.yml +++ b/.devcontainer/server/container-compose-overrides.yml @@ -8,8 +8,7 @@ services: - IMMICH_SERVER_URL=http://127.0.0.1:2283/ volumes: !override - ..:/workspaces/immich - - ${UPLOAD_LOCATION:-upload1-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data - - ${UPLOAD_LOCATION:-upload2-devcontainer-volume}${UPLOAD_LOCATION:+/photos/upload}:/data/upload + - ${UPLOAD_LOCATION:-upload-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data - /etc/localtime:/etc/localtime:ro - pnpm-store:/usr/src/app/.pnpm-store - server-node_modules:/usr/src/app/server/node_modules @@ -24,9 +23,6 @@ services: - coverage:/usr/src/app/web/coverage immich-web: env_file: !reset [] - init: - env_file: !reset [] - command: sh -c 'find /data -maxdepth 1 ! -path "/data/postgres" -type d -exec chown ${UID:-0}:${GID:-0} {} + 2>/dev/null || true; for path in /usr/src/app/.pnpm-store /usr/src/app/server/node_modules /usr/src/app/server/dist /usr/src/app/.github/node_modules /usr/src/app/cli/node_modules /usr/src/app/docs/node_modules /usr/src/app/e2e/node_modules /usr/src/app/open-api/typescript-sdk/node_modules /usr/src/app/web/.svelte-kit /usr/src/app/web/coverage /usr/src/app/node_modules /usr/src/app/web/node_modules; do [ -e "$$path" ] && chown -R ${UID:-0}:${GID:-0} "$$path" || true; done' immich-machine-learning: env_file: !reset [] database: @@ -42,7 +38,5 @@ services: redis: env_file: !reset [] volumes: - # Node modules for each service to avoid conflicts and ensure consistent dependencies - upload1-devcontainer-volume: - upload2-devcontainer-volume: + upload-devcontainer-volume: postgres-devcontainer-volume: diff --git a/.github/workflows/fix-format.yml b/.github/workflows/fix-format.yml index 14043772ee..bec34c2713 100644 --- a/.github/workflows/fix-format.yml +++ b/.github/workflows/fix-format.yml @@ -28,6 +28,9 @@ jobs: token: ${{ steps.generate-token.outputs.token }} persist-credentials: true + - name: Setup pnpm + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: diff --git a/Makefile b/Makefile index 8c5865448d..34fb408c41 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,13 @@ -dev: prepare-volumes +dev: @trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --remove-orphans dev-down: docker compose -f ./docker/docker-compose.dev.yml down --remove-orphans -dev-update: prepare-volumes +dev-update: @trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans -dev-scale: prepare-volumes +dev-scale: @trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans dev-docs: @@ -23,7 +23,7 @@ e2e-update: e2e-down: docker compose -f ./e2e/docker-compose.yml down --remove-orphans -prod: +prod: @trap 'make prod-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.prod.yml up --build -V --remove-orphans prod-down: @@ -33,16 +33,16 @@ prod-scale: @trap 'make prod-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.prod.yml up --build -V --scale immich-server=3 --scale immich-microservices=3 --remove-orphans .PHONY: open-api -open-api: prepare-volumes +open-api: cd ./open-api && bash ./bin/generate-open-api.sh -open-api-dart: prepare-volumes +open-api-dart: cd ./open-api && bash ./bin/generate-open-api.sh dart -open-api-typescript: prepare-volumes +open-api-typescript: cd ./open-api && bash ./bin/generate-open-api.sh typescript -sql: prepare-volumes +sql: pnpm --filter immich run sync:sql attach-server: @@ -68,32 +68,6 @@ VOLUME_DIRS = \ # Include .env file if it exists -include docker/.env -# Helper function to chown, on error suggest remediation and exit -define safe_chown - CURRENT_OWNER=$$(stat -c '%u:%g' "$(1)" 2>/dev/null || echo "none"); \ - DESIRED_OWNER="$(or $(UID),0):$(or $(GID),0)"; \ - if [ "$$CURRENT_OWNER" != "$$DESIRED_OWNER" ] && ! chown -v $(2) $$DESIRED_OWNER "$(1)" 2>/dev/null; then \ - echo "Permission denied when changing owner of volumes and upload location. Try running 'sudo make prepare-volumes' first."; \ - exit 1; \ - fi; -endef -# create empty directories and chown -prepare-volumes: - @$(foreach dir,$(VOLUME_DIRS),mkdir -p $(dir);) - @$(foreach dir,$(VOLUME_DIRS),$(call safe_chown,$(dir),-R)) -ifneq ($(UPLOAD_LOCATION),) -ifeq ($(filter /%,$(UPLOAD_LOCATION)),) - @mkdir -p "docker/$(UPLOAD_LOCATION)/photos/upload" - @$(call safe_chown,docker/$(UPLOAD_LOCATION),) - @$(call safe_chown,docker/$(UPLOAD_LOCATION)/photos,-R) -else - @mkdir -p "$(UPLOAD_LOCATION)/photos/upload" - @$(call safe_chown,$(UPLOAD_LOCATION),) - @$(call safe_chown,$(UPLOAD_LOCATION)/photos,-R) -endif -endif - - MODULES = e2e server web cli sdk docs .github # directory to package name mapping function diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 7864a1edd1..f97cf0ca0d 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -21,16 +21,14 @@ services: # extends: # file: hwaccel.transcoding.yml # service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding - user: '${UID:-0}:${GID:-0}' build: context: ../ - dockerfile: server/Dockerfile + dockerfile: server/Dockerfile.dev target: dev restart: unless-stopped volumes: - ..:/usr/src/app - ${UPLOAD_LOCATION}/photos:/data - - ${UPLOAD_LOCATION}/photos/upload:/data/upload - /etc/localtime:/etc/localtime:ro - pnpm-store:/usr/src/app/.pnpm-store - server-node_modules:/usr/src/app/server/node_modules @@ -72,20 +70,15 @@ services: condition: service_started database: condition: service_started - init: - condition: service_completed_successfully healthcheck: disable: false immich-web: container_name: immich_web image: immich-web-dev:latest - # Needed for rootless docker setup, see https://github.com/moby/moby/issues/45919 - # user: 0:0 - user: '${UID:-0}:${GID:-0}' build: context: ../ - dockerfile: server/Dockerfile + dockerfile: server/Dockerfile.dev target: dev command: ['immich-web'] env_file: @@ -114,8 +107,6 @@ services: depends_on: immich-server: condition: service_started - init: - condition: service_completed_successfully immich-machine-learning: container_name: immich_machine_learning @@ -183,25 +174,6 @@ services: # volumes: # - grafana-data:/var/lib/grafana - init: - container_name: init - image: busybox@sha256:ab33eacc8251e3807b85bb6dba570e4698c3998eca6f0fc2ccb60575a563ea74 - env_file: - - .env - user: 0:0 - command: sh -c 'find /data -maxdepth 1 -type d -exec chown ${UID:-0}:${GID:-0} {} + 2>/dev/null || true; for path in /usr/src/app/.pnpm-store /usr/src/app/server/node_modules /usr/src/app/server/dist /usr/src/app/.github/node_modules /usr/src/app/cli/node_modules /usr/src/app/docs/node_modules /usr/src/app/e2e/node_modules /usr/src/app/open-api/typescript-sdk/node_modules /usr/src/app/web/.svelte-kit /usr/src/app/web/coverage /usr/src/app/node_modules /usr/src/app/web/node_modules; do [ -e "$$path" ] && chown -R ${UID:-0}:${GID:-0} "$$path" || true; done' - volumes: - - pnpm-store:/usr/src/app/.pnpm-store - - server-node_modules:/usr/src/app/server/node_modules - - web-node_modules:/usr/src/app/web/node_modules - - github-node_modules:/usr/src/app/.github/node_modules - - cli-node_modules:/usr/src/app/cli/node_modules - - docs-node_modules:/usr/src/app/docs/node_modules - - e2e-node_modules:/usr/src/app/e2e/node_modules - - sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules - - app-node_modules:/usr/src/app/node_modules - - sveltekit:/usr/src/app/web/.svelte-kit - - coverage:/usr/src/app/web/coverage volumes: model-cache: prometheus-data: diff --git a/docs/src/components/community-guides.tsx b/docs/src/components/community-guides.tsx index 49ba7a8a08..08c8e096d9 100644 --- a/docs/src/components/community-guides.tsx +++ b/docs/src/components/community-guides.tsx @@ -28,6 +28,12 @@ const guides: CommunityGuidesProps[] = [ description: `synchronize folders in imported library with albums having the folders name.`, url: 'https://github.com/immich-app/immich/discussions/3382', }, + { + title: 'Immich Podman Quadlets Handbook', + description: + 'A rewrite of the original Immich Docker Compose file using Podman Quadlets, with a set of extra guides in the repository’s wiki.', + url: 'https://github.com/linux-universe/immich-podman-quadlets/blob/main/README.md', + }, { title: 'Podman/Quadlets Install', description: 'Documentation for simple podman setup using quadlets.', diff --git a/docs/src/components/community-projects.tsx b/docs/src/components/community-projects.tsx index 930cff66c1..efce831df0 100644 --- a/docs/src/components/community-projects.tsx +++ b/docs/src/components/community-projects.tsx @@ -110,6 +110,16 @@ const projects: CommunityProjectProps[] = [ description: 'A tiny, zero-login web app for collecting photos/videos from anyone into your Immich server.', url: 'https://github.com/Nasogaa/immich-drop', }, + { + title: 'Immich Birthday Sync', + description: 'Bulk-upload and -download birthdays, with CardDAV sync support', + url: 'https://github.com/sid3windr/immich-birthday', + }, + { + title: 'Immich Stack', + description: 'Auto-stack photos with identical filenames and differing extensions (i.e. JPG+RAW)', + url: 'https://github.com/sid3windr/immich-stack', + }, ]; function CommunityProject({ title, description, url }: CommunityProjectProps): JSX.Element { diff --git a/docs/src/pages/errors.md b/docs/src/pages/errors.md index 5f73162a61..fed72f21c7 100644 --- a/docs/src/pages/errors.md +++ b/docs/src/pages/errors.md @@ -2,7 +2,17 @@ ## TypeORM Upgrade -In order to update to Immich to `v1.137.0` (or above), the application must be started at least once on a version in the range between `1.132.0` and `1.136.0`. Doing so will complete database schema upgrades that are required for `v1.137.0` (and above). After Immich has successfully updated to a version in this range, you can now attempt to update to v1.137.0 (or above). We recommend users upgrade to `1.132.0` since it does not have any other breaking changes. +If you encountered "Migrations failed: Error: Invalid upgrade path" then perform an intermediate upgrade to `v1.132.3` first. + +:::tip +We recommend users upgrade to `v1.132.3` since it does not have any breaking changes or bugs on this upgrade path. +::: + +In order to update to Immich `v1.137.0` or above, the application must be started at least once on a version in the range between `1.132.0` and `1.136.0`. Doing so will complete database schema upgrades that are required for `v1.137.0` (and above). After Immich has successfully updated to a version in this range, you can now attempt to update to `v1.137.0` (or above). + +:::caution +Avoid `v1.136.0` if upgrading from `v1.131.0` (or earlier) due to a bug blocking this upgrade in some installations. +::: ## Inconsistent Media Location diff --git a/i18n/en.json b/i18n/en.json index 82c5c147aa..fef3243ec0 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -387,8 +387,6 @@ "admin_password": "Admin Password", "administration": "Administration", "advanced": "Advanced", - "advanced_settings_beta_timeline_subtitle": "Try the new app experience", - "advanced_settings_beta_timeline_title": "Beta Timeline", "advanced_settings_enable_alternate_media_filter_subtitle": "Use this option to filter media during sync based on alternate criteria. Only try this if you have issues with the app detecting all albums.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Use alternate device album sync filter", "advanced_settings_log_level_title": "Log level: {level}", @@ -425,6 +423,7 @@ "album_remove_user_confirmation": "Are you sure you want to remove {user}?", "album_search_not_found": "No albums found matching your search", "album_share_no_users": "Looks like you have shared this album with all users or you don't have any user to share with.", + "album_summary": "Album summary", "album_updated": "Album updated", "album_updated_setting_description": "Receive an email notification when a shared album has new assets", "album_user_left": "Left {album}", @@ -496,6 +495,8 @@ "asset_restored_successfully": "Asset restored successfully", "asset_skipped": "Skipped", "asset_skipped_in_trash": "In trash", + "asset_trashed": "Asset trashed", + "asset_troubleshoot": "Asset Troubleshoot", "asset_uploaded": "Uploaded", "asset_uploading": "Uploading…", "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", @@ -529,6 +530,7 @@ "autoplay_slideshow": "Autoplay slideshow", "back": "Back", "back_close_deselect": "Back, close, or deselect", + "background_backup_running_error": "Background backup is currently running, cannot start manual backup", "background_location_permission": "Background location permission", "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", "backup": "Backup", @@ -740,6 +742,7 @@ "create_user": "Create user", "created": "Created", "created_at": "Created", + "creating_linked_albums": "Creating linked albums...", "crop": "Crop", "curated_object_page_title": "Things", "current_device": "Current device", @@ -889,7 +892,9 @@ "error": "Error", "error_change_sort_album": "Failed to change album sort order", "error_delete_face": "Error deleting face from asset", + "error_getting_places": "Error getting places", "error_loading_image": "Error loading image", + "error_loading_partners": "Error loading partners: {error}", "error_saving_image": "Error: {error}", "error_tag_face_bounding_box": "Error tagging face - cannot get bounding box coordinates", "error_title": "Error - Something went wrong", @@ -1054,6 +1059,7 @@ "favorites_page_no_favorites": "No favorite assets found", "feature_photo_updated": "Feature photo updated", "features": "Features", + "features_in_development": "Features in Development", "features_setting_description": "Manage the app features", "file_name": "File name", "file_name_or_extension": "File name or extension", @@ -1218,6 +1224,7 @@ "local": "Local", "local_asset_cast_failed": "Unable to cast an asset that is not uploaded to the server", "local_assets": "Local Assets", + "local_media_summary": "Local Media Summary", "local_network": "Local network", "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", "location_permission": "Location permission", @@ -1229,6 +1236,7 @@ "location_picker_longitude_hint": "Enter your longitude here", "lock": "Lock", "locked_folder": "Locked Folder", + "log_detail_title": "Log Detail", "log_out": "Log out", "log_out_all_devices": "Log Out All Devices", "logged_in_as": "Logged in as {user}", @@ -1259,6 +1267,7 @@ "login_password_changed_success": "Password updated successfully", "logout_all_device_confirmation": "Are you sure you want to log out all devices?", "logout_this_device_confirmation": "Are you sure you want to log out this device?", + "logs": "Logs", "longitude": "Longitude", "look": "Look", "loop_videos": "Loop videos", @@ -1301,6 +1310,7 @@ "mark_as_read": "Mark as read", "marked_all_as_read": "Marked all as read", "matches": "Matches", + "matching_assets": "Matching Assets", "media_type": "Media type", "memories": "Memories", "memories_all_caught_up": "All caught up", @@ -1351,6 +1361,7 @@ "new_person": "New person", "new_pin_code": "New PIN code", "new_pin_code_subtitle": "This is your first time accessing the locked folder. Create a PIN code to securely access this page", + "new_timeline": "New Timeline", "new_user_created": "New user created", "new_version_available": "NEW VERSION AVAILABLE", "newest_first": "Newest first", @@ -1364,20 +1375,25 @@ "no_assets_message": "CLICK TO UPLOAD YOUR FIRST PHOTO", "no_assets_to_show": "No assets to show", "no_cast_devices_found": "No cast devices found", + "no_checksum_local": "No checksum available - cannot fetch local assets", + "no_checksum_remote": "No checksum available - cannot fetch remote asset", "no_duplicates_found": "No duplicates were found.", "no_exif_info_available": "No exif info available", "no_explore_results_message": "Upload more photos to explore your collection.", "no_favorites_message": "Add favorites to quickly find your best pictures and videos", "no_libraries_message": "Create an external library to view your photos and videos", + "no_local_assets_found": "No local assets found with this checksum", "no_locked_photos_message": "Photos and videos in the locked folder are hidden and won't show up as you browse or search your library.", "no_name": "No Name", "no_notifications": "No notifications", "no_people_found": "No matching people found", "no_places": "No places", + "no_remote_assets_found": "No remote assets found with this checksum", "no_results": "No results", "no_results_description": "Try a synonym or more general keyword", "no_shared_albums_message": "Create an album to share photos and videos with people in your network", "no_uploads_in_progress": "No uploads in progress", + "not_available": "N/A", "not_in_any_album": "Not in any album", "not_selected": "Not selected", "note_apply_storage_label_to_previously_uploaded assets": "Note: To apply the Storage Label to previously uploaded assets, run the", @@ -1588,6 +1604,7 @@ "regenerating_thumbnails": "Regenerating thumbnails", "remote": "Remote", "remote_assets": "Remote Assets", + "remote_media_summary": "Remote Media Summary", "remove": "Remove", "remove_assets_album_confirmation": "Are you sure you want to remove {count, plural, one {# asset} other {# assets}} from the album?", "remove_assets_shared_link_confirmation": "Are you sure you want to remove {count, plural, one {# asset} other {# assets}} from this shared link?", @@ -1863,6 +1880,7 @@ "show_slideshow_transition": "Show slideshow transition", "show_supporter_badge": "Supporter badge", "show_supporter_badge_description": "Show a supporter badge", + "show_text_search_menu": "Show text search menu", "shuffle": "Shuffle", "sidebar": "Sidebar", "sidebar_display_description": "Display a link to the view in the sidebar", @@ -2095,5 +2113,6 @@ "yes": "Yes", "you_dont_have_any_shared_links": "You don't have any shared links", "your_wifi_name": "Your Wi-Fi name", - "zoom_image": "Zoom Image" + "zoom_image": "Zoom Image", + "zoom_to_bounds": "Zoom to bounds" } diff --git a/mise.toml b/mise.toml index 47acb66b21..ff9fa3895d 100644 --- a/mise.toml +++ b/mise.toml @@ -300,7 +300,7 @@ run = "tsc --noEmit" depends = "web:svelte-kit-sync" env._.path = "web/node_modules/.bin" dir = "web" -run = "svelte-check --no-tsconfig --fail-on-warnings --compiler-warnings 'reactive_declaration_non_reactive_property:ignore' --ignore src/lib/components/photos-page/asset-grid.svelte" +run = "svelte-check --no-tsconfig --fail-on-warnings --compiler-warnings 'reactive_declaration_non_reactive_property:ignore' --ignore src/lib/components/timeline/Timeline.svelte" [tasks."web:checklist"] run = [ diff --git a/mobile/lib/pages/backup/backup_controller.page.dart b/mobile/lib/pages/backup/backup_controller.page.dart index 093ff952ae..4f55d00ea0 100644 --- a/mobile/lib/pages/backup/backup_controller.page.dart +++ b/mobile/lib/pages/backup/backup_controller.page.dart @@ -205,9 +205,9 @@ class BackupControllerPage extends HookConsumerWidget { } buildBackgroundBackupInfo() { - return const ListTile( - leading: Icon(Icons.info_outline_rounded), - title: Text("Background backup is currently running, cannot start manual backup"), + return ListTile( + leading: const Icon(Icons.info_outline_rounded), + title: Text('background_backup_running_error'.tr()), ); } diff --git a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart index e734dc300c..8cfdd2db95 100644 --- a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; @@ -249,7 +250,7 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState - Text('Error: $error', style: TextStyle(color: context.colorScheme.error)), + error: (error, stackTrace) => Text( + 'error_saving_image'.tr(args: [error.toString()]), + style: TextStyle(color: context.colorScheme.error), + ), loading: () => const SizedBox(height: 16, width: 16, child: CircularProgressIndicator.adaptive()), ), ], @@ -83,7 +86,7 @@ class DriftBackupAssetDetailPage extends ConsumerWidget { ); }, error: (Object error, StackTrace stackTrace) { - return Center(child: Text('Error: $error')); + return Center(child: Text('error_saving_image'.tr(args: [error.toString()]))); }, loading: () { return const SizedBox(height: 48, width: 48, child: Center(child: CircularProgressIndicator.adaptive())); diff --git a/mobile/lib/pages/common/app_log.page.dart b/mobile/lib/pages/common/app_log.page.dart index fe0c0ea442..37aec2f13c 100644 --- a/mobile/lib/pages/common/app_log.page.dart +++ b/mobile/lib/pages/common/app_log.page.dart @@ -1,4 +1,5 @@ import 'package:auto_route/auto_route.dart'; +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'; @@ -8,7 +9,6 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/immich_logger.service.dart'; -import 'package:intl/intl.dart'; @RoutePage() class AppLogPage extends HookConsumerWidget { @@ -49,7 +49,7 @@ class AppLogPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: const Text("Logs", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0)), + title: Text('logs'.tr(), style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0)), scrolledUnderElevation: 1, elevation: 2, actions: [ diff --git a/mobile/lib/pages/common/app_log_detail.page.dart b/mobile/lib/pages/common/app_log_detail.page.dart index c9773f36e1..de9604b7ad 100644 --- a/mobile/lib/pages/common/app_log_detail.page.dart +++ b/mobile/lib/pages/common/app_log_detail.page.dart @@ -1,4 +1,5 @@ import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -36,7 +37,7 @@ class AppLogDetailPage extends HookConsumerWidget { context.scaffoldMessenger.showSnackBar( SnackBar( content: Text( - "Copied to clipboard", + "copied_to_clipboard".tr(), style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), @@ -97,7 +98,7 @@ class AppLogDetailPage extends HookConsumerWidget { } return Scaffold( - appBar: AppBar(title: const Text("Log Detail")), + appBar: AppBar(title: Text("log_detail_title".tr())), body: SafeArea( child: ListView( children: [ diff --git a/mobile/lib/pages/library/partner/drift_partner.page.dart b/mobile/lib/pages/library/partner/drift_partner.page.dart index 834e55ffd4..d81cc44c76 100644 --- a/mobile/lib/pages/library/partner/drift_partner.page.dart +++ b/mobile/lib/pages/library/partner/drift_partner.page.dart @@ -134,7 +134,7 @@ class _SharedToPartnerList extends ConsumerWidget { ); }, loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stack) => Center(child: Text("Error loading partners: $error")), + error: (error, stack) => Center(child: Text('error_loading_partners'.tr(args: [error.toString()]))), ); } } diff --git a/mobile/lib/pages/library/places/places_collection.page.dart b/mobile/lib/pages/library/places/places_collection.page.dart index 73c38a109c..f376709316 100644 --- a/mobile/lib/pages/library/places/places_collection.page.dart +++ b/mobile/lib/pages/library/places/places_collection.page.dart @@ -85,7 +85,7 @@ class PlacesCollectionPage extends HookConsumerWidget { }, ); }, - error: (error, stask) => const Text('Error getting places'), + error: (error, stask) => Text('error_getting_places'.tr()), loading: () => const Center(child: CircularProgressIndicator()), ), ], diff --git a/mobile/lib/pages/search/search.page.dart b/mobile/lib/pages/search/search.page.dart index 97205e000c..acfa2fd59f 100644 --- a/mobile/lib/pages/search/search.page.dart +++ b/mobile/lib/pages/search/search.page.dart @@ -435,7 +435,7 @@ class SearchPage extends HookConsumerWidget { } }, icon: const Icon(Icons.more_vert_rounded), - tooltip: 'Show text search menu', + tooltip: 'show_text_search_menu'.tr(), ); }, menuChildren: [ diff --git a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart index d3f0e3c1bc..491c38e7a8 100644 --- a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart +++ b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:auto_route/auto_route.dart'; import 'package:drift/drift.dart' hide Column; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; @@ -135,7 +136,7 @@ class FeatInDevPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('Features in Development'), centerTitle: true), + appBar: AppBar(title: Text('features_in_development'.tr()), centerTitle: true), body: Column( children: [ Flexible( diff --git a/mobile/lib/presentation/pages/dev/media_stat.page.dart b/mobile/lib/presentation/pages/dev/media_stat.page.dart index b48d8fc307..4c18a09200 100644 --- a/mobile/lib/presentation/pages/dev/media_stat.page.dart +++ b/mobile/lib/presentation/pages/dev/media_stat.page.dart @@ -1,5 +1,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:collection/collection.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; @@ -55,7 +56,7 @@ class LocalMediaSummaryPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('Local Media Summary')), + appBar: AppBar(title: Text('local_media_summary'.tr())), body: Consumer( builder: (ctx, ref, __) { final db = ref.watch(driftProvider); @@ -78,7 +79,7 @@ class LocalMediaSummaryPage extends StatelessWidget { const Divider(), Padding( padding: const EdgeInsets.only(left: 15), - child: Text("Album summary", style: ctx.textTheme.titleMedium), + child: Text("album_summary".tr(), style: ctx.textTheme.titleMedium), ), ], ), @@ -135,7 +136,7 @@ class RemoteMediaSummaryPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('Remote Media Summary')), + appBar: AppBar(title: Text('remote_media_summary'.tr())), body: Consumer( builder: (ctx, ref, __) { final db = ref.watch(driftProvider); @@ -158,7 +159,7 @@ class RemoteMediaSummaryPage extends StatelessWidget { const Divider(), Padding( padding: const EdgeInsets.only(left: 15), - child: Text("Album summary", style: ctx.textTheme.titleMedium), + child: Text("album_summary".tr(), style: ctx.textTheme.titleMedium), ), ], ), diff --git a/mobile/lib/presentation/pages/drift_asset_troubleshoot.page.dart b/mobile/lib/presentation/pages/drift_asset_troubleshoot.page.dart index 1cd6bee67d..7a899f4e72 100644 --- a/mobile/lib/presentation/pages/drift_asset_troubleshoot.page.dart +++ b/mobile/lib/presentation/pages/drift_asset_troubleshoot.page.dart @@ -1,4 +1,5 @@ import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; @@ -14,7 +15,7 @@ class AssetTroubleshootPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( - appBar: AppBar(title: const Text("Asset Troubleshoot")), + appBar: AppBar(title: Text('asset_troubleshoot'.tr())), body: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(16.0), @@ -37,20 +38,23 @@ class _AssetDetailsView extends ConsumerWidget { children: [ _AssetPropertiesSection(asset: asset), const SizedBox(height: 16), - Text('Matching Assets', style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold)), + Text( + 'matching_assets'.tr(), + style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold), + ), if (asset.checksum != null) ...[ _LocalAssetsSection(asset: asset), const SizedBox(height: 16), _RemoteAssetSection(asset: asset), ] else ...[ - const _PropertySectionCard( + _PropertySectionCard( title: 'Local Assets', - properties: [_PropertyItem(label: 'Status', value: 'No checksum available - cannot fetch local assets')], + properties: [_PropertyItem(label: 'Status', value: 'no_checksum_local'.tr())], ), const SizedBox(height: 16), - const _PropertySectionCard( + _PropertySectionCard( title: 'Remote Assets', - properties: [_PropertyItem(label: 'Status', value: 'No checksum available - cannot fetch remote asset')], + properties: [_PropertyItem(label: 'Status', value: 'no_checksum_remote'.tr())], ), ], ], @@ -222,9 +226,9 @@ class _LocalAssetsSection extends ConsumerWidget { } if (localAssets.isEmpty) { - return const _PropertySectionCard( + return _PropertySectionCard( title: 'Local Assets', - properties: [_PropertyItem(label: 'Status', value: 'No local assets found with this checksum')], + properties: [_PropertyItem(label: 'Status', value: 'no_local_assets_found'.tr())], ); } @@ -281,9 +285,9 @@ class _RemoteAssetSection extends ConsumerWidget { final remoteAsset = snapshot.data; if (remoteAsset == null) { - return const _PropertySectionCard( + return _PropertySectionCard( title: 'Remote Assets', - properties: [_PropertyItem(label: 'Status', value: 'No remote asset found with this checksum')], + properties: [_PropertyItem(label: 'Status', value: 'no_remote_assets_found'.tr())], ); } @@ -336,7 +340,10 @@ class _PropertyItem extends StatelessWidget { child: Text('$label:', style: const TextStyle(fontWeight: FontWeight.w500)), ), Expanded( - child: Text(value ?? 'N/A', style: TextStyle(color: Theme.of(context).colorScheme.secondary)), + child: Text( + value ?? 'not_available'.tr(), + style: TextStyle(color: Theme.of(context).colorScheme.secondary), + ), ), ], ), diff --git a/mobile/lib/presentation/pages/drift_library.page.dart b/mobile/lib/presentation/pages/drift_library.page.dart index af7014bbdc..d1d663e4f4 100644 --- a/mobile/lib/presentation/pages/drift_library.page.dart +++ b/mobile/lib/presentation/pages/drift_library.page.dart @@ -1,4 +1,5 @@ import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; @@ -302,7 +303,9 @@ class _LocalAlbumsCollectionCard extends ConsumerWidget { }).toList(); }, error: (error, _) { - return [Center(child: Text('Error: $error'))]; + return [ + Center(child: Text('error_saving_image'.tr(args: [error.toString()]))), + ]; }, loading: () { return [const Center(child: CircularProgressIndicator())]; diff --git a/mobile/lib/presentation/pages/drift_local_album.page.dart b/mobile/lib/presentation/pages/drift_local_album.page.dart index 536d9d82e2..6b133f7a6f 100644 --- a/mobile/lib/presentation/pages/drift_local_album.page.dart +++ b/mobile/lib/presentation/pages/drift_local_album.page.dart @@ -1,4 +1,5 @@ import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; @@ -46,9 +47,9 @@ class _AlbumList extends ConsumerWidget { ), data: (albums) { if (albums.isEmpty) { - return const SliverToBoxAdapter( + return SliverToBoxAdapter( child: Center( - child: Padding(padding: EdgeInsets.all(20.0), child: Text('No albums found')), + child: Padding(padding: const EdgeInsets.all(20.0), child: Text('no_albums_yet'.tr())), ), ); } diff --git a/mobile/lib/presentation/pages/search/drift_search.page.dart b/mobile/lib/presentation/pages/search/drift_search.page.dart index d44b215b03..162dbb308e 100644 --- a/mobile/lib/presentation/pages/search/drift_search.page.dart +++ b/mobile/lib/presentation/pages/search/drift_search.page.dart @@ -440,7 +440,7 @@ class DriftSearchPage extends HookConsumerWidget { } }, icon: const Icon(Icons.more_vert_rounded), - tooltip: 'Show text search menu', + tooltip: 'show_text_search_menu'.tr(), ); }, menuChildren: [ diff --git a/mobile/lib/presentation/widgets/action_buttons/like_activity_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/like_activity_action_button.widget.dart index d143f600ce..33794eae11 100644 --- a/mobile/lib/presentation/widgets/action_buttons/like_activity_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/like_activity_action_button.widget.dart @@ -1,4 +1,5 @@ import 'package:collection/collection.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; @@ -58,7 +59,7 @@ class LikeActivityActionButton extends ConsumerWidget { label: "like".t(context: context), menuItem: menuItem, ), - error: (error, stack) => Text("Error: $error"), + error: (error, stack) => Text('error_saving_image'.tr(args: [error.toString()])), ); } } diff --git a/mobile/lib/presentation/widgets/album/album_selector.widget.dart b/mobile/lib/presentation/widgets/album/album_selector.widget.dart index bd36db9eeb..f79b4bd7b1 100644 --- a/mobile/lib/presentation/widgets/album/album_selector.widget.dart +++ b/mobile/lib/presentation/widgets/album/album_selector.widget.dart @@ -504,9 +504,9 @@ class _AlbumList extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { if (albums.isEmpty) { - return const SliverToBoxAdapter( + return SliverToBoxAdapter( child: Center( - child: Padding(padding: EdgeInsets.all(20.0), child: Text('No albums found')), + child: Padding(padding: const EdgeInsets.all(20.0), child: Text('album_search_not_found'.tr())), ), ); } @@ -599,9 +599,9 @@ class _AlbumGrid extends StatelessWidget { @override Widget build(BuildContext context) { if (albums.isEmpty) { - return const SliverToBoxAdapter( + return SliverToBoxAdapter( child: Center( - child: Padding(padding: EdgeInsets.all(20.0), child: Text('No albums found')), + child: Padding(padding: const EdgeInsets.all(20.0), child: Text('album_search_not_found'.tr())), ), ); } diff --git a/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart b/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart index 10c794b8be..5974f3e0a7 100644 --- a/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart @@ -46,7 +46,7 @@ class Scrubber extends ConsumerStatefulWidget { } List<_Segment> _buildSegments({required List layoutSegments, required double timelineHeight}) { - const double offsetThreshold = 20.0; + const double offsetThreshold = 40.0; final segments = <_Segment>[]; if (layoutSegments.isEmpty || layoutSegments.first.bucket is! TimeBucket) { @@ -390,7 +390,7 @@ class _SegmentWidget extends StatelessWidget { Widget build(BuildContext context) { return IgnorePointer( child: Container( - margin: const EdgeInsets.only(right: 12.0), + margin: const EdgeInsets.only(right: 36.0), child: Material( color: context.colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(16.0)), diff --git a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart index c7125e2200..00f7bc494d 100644 --- a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart +++ b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart @@ -98,7 +98,12 @@ class BottomGalleryBar extends ConsumerWidget { if (isDeleted) { // Can only trash assets stored in server. Local assets are always permanently removed for now if (context.mounted && asset.isRemote && isStackPrimaryAsset) { - ImmichToast.show(durationInSecond: 1, context: context, msg: 'Asset trashed', gravity: ToastGravity.BOTTOM); + ImmichToast.show( + durationInSecond: 1, + context: context, + msg: 'asset_trashed'.tr(), + gravity: ToastGravity.BOTTOM, + ); } removeAssetFromStack(); } diff --git a/mobile/lib/widgets/asset_viewer/cast_dialog.dart b/mobile/lib/widgets/asset_viewer/cast_dialog.dart index 4db1d9bb69..f7c80cca3d 100644 --- a/mobile/lib/widgets/asset_viewer/cast_dialog.dart +++ b/mobile/lib/widgets/asset_viewer/cast_dialog.dart @@ -29,7 +29,7 @@ class CastDialog extends ConsumerWidget { future: ref.read(castProvider.notifier).getDevices(), builder: (context, snapshot) { if (snapshot.hasError) { - return Text('Error: ${snapshot.error.toString()}'); + return Text('error_saving_image'.tr(args: [snapshot.error.toString()])); } else if (!snapshot.hasData) { return const SizedBox(height: 48, child: Center(child: CircularProgressIndicator())); } diff --git a/mobile/lib/widgets/common/immich_sliver_app_bar.dart b/mobile/lib/widgets/common/immich_sliver_app_bar.dart index 378a31f33e..09c0518a23 100644 --- a/mobile/lib/widgets/common/immich_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/immich_sliver_app_bar.dart @@ -97,29 +97,11 @@ class _ImmichLogoWithText extends StatelessWidget { children: [ Builder( builder: (context) { - return Badge( - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), - backgroundColor: context.primaryColor, - alignment: Alignment.centerRight, - offset: const Offset(16, -8), - label: Text( - 'β', - style: TextStyle( - fontSize: 11, - color: context.colorScheme.onPrimary, - fontWeight: FontWeight.bold, - fontFamily: 'OverpassMono', - height: 1.2, - ), - ), - child: Padding( - padding: const EdgeInsets.only(top: 3.0), - child: SvgPicture.asset( - context.isDarkTheme - ? 'assets/immich-logo-inline-dark.svg' - : 'assets/immich-logo-inline-light.svg', - height: 40, - ), + return Padding( + padding: const EdgeInsets.only(top: 3.0), + child: SvgPicture.asset( + context.isDarkTheme ? 'assets/immich-logo-inline-dark.svg' : 'assets/immich-logo-inline-light.svg', + height: 40, ), ); }, diff --git a/mobile/lib/widgets/map/map_asset_grid.dart b/mobile/lib/widgets/map/map_asset_grid.dart index 488b8480f2..b6c1e708a7 100644 --- a/mobile/lib/widgets/map/map_asset_grid.dart +++ b/mobile/lib/widgets/map/map_asset_grid.dart @@ -275,7 +275,7 @@ class _MapSheetDragRegion extends StatelessWidget { child: IconButton( icon: Icon(Icons.map_outlined, color: context.textTheme.displayLarge?.color), iconSize: 24, - tooltip: 'Zoom to bounds', + tooltip: 'zoom_to_bounds'.tr(), onPressed: () => onZoomToAsset?.call(value!), ), ), diff --git a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart index 8786fe2c5f..9a3b4f5c96 100644 --- a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart +++ b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart @@ -28,10 +28,10 @@ class BetaTimelineListTile extends ConsumerWidget { context: context, builder: (context) { return AlertDialog( - title: value ? const Text("Enable Beta Timeline") : const Text("Disable Beta Timeline"), + title: value ? const Text("Enable New Timeline") : const Text("Disable New Timeline"), content: value - ? const Text("Are you sure you want to enable the beta timeline?") - : const Text("Are you sure you want to disable the beta timeline?"), + ? const Text("Are you sure you want to enable the new timeline?") + : const Text("Are you sure you want to disable the new timeline?"), actions: [ TextButton( onPressed: () { @@ -58,8 +58,7 @@ class BetaTimelineListTile extends ConsumerWidget { return Padding( padding: const EdgeInsets.only(left: 4.0), child: ListTile( - title: Text("advanced_settings_beta_timeline_title".t(context: context)), - subtitle: Text("advanced_settings_beta_timeline_subtitle".t(context: context)), + title: Text("new_timeline".t(context: context)), trailing: Switch.adaptive( value: betaTimelineValue, onChanged: onSwitchChanged, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 89b6581d81..bd707f29f6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -109,10 +109,10 @@ importers: version: 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) vite: specifier: ^7.0.0 - version: 7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + version: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite-tsconfig-paths: specifier: ^5.0.0 - version: 5.1.4(typescript@5.9.2)(vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) vitest: specifier: ^3.0.0 version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) @@ -667,10 +667,10 @@ importers: version: 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) unplugin-swc: specifier: ^1.4.5 - version: 1.5.5(@swc/core@1.13.3(@swc/helpers@0.5.17))(rollup@4.46.3) + version: 1.5.5(@swc/core@1.13.3(@swc/helpers@0.5.17))(rollup@4.50.1) vite-tsconfig-paths: specifier: ^5.0.0 - version: 5.1.4(typescript@5.9.2)(vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) vitest: specifier: ^3.0.0 version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) @@ -794,25 +794,25 @@ importers: version: 3.1.2 '@sveltejs/adapter-static': specifier: ^3.0.8 - version: 3.0.9(@sveltejs/kit@2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))) + version: 3.0.9(@sveltejs/kit@2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))) '@sveltejs/enhanced-img': specifier: ^0.8.0 - version: 0.8.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.46.3)(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 0.8.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.50.1)(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@sveltejs/kit': specifier: ^2.27.1 - version: 2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@sveltejs/vite-plugin-svelte': specifier: 6.1.2 - version: 6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@tailwindcss/vite': specifier: ^4.1.7 - version: 4.1.12(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 4.1.12(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@testing-library/jest-dom': specifier: ^6.4.2 version: 6.7.0 '@testing-library/svelte': specifier: ^5.2.8 - version: 5.2.8(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 5.2.8(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@testing-library/user-event': specifier: ^14.5.2 version: 14.6.1(@testing-library/dom@10.4.0) @@ -878,7 +878,7 @@ importers: version: 3.4.0(prettier@3.6.2)(svelte@5.35.5) rollup-plugin-visualizer: specifier: ^6.0.0 - version: 6.0.3(rollup@4.46.3) + version: 6.0.3(rollup@4.50.1) svelte: specifier: 5.35.5 version: 5.35.5 @@ -899,7 +899,7 @@ importers: version: 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) vite: specifier: ^7.1.2 - version: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + version: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vitest: specifier: ^3.0.0 version: 3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) @@ -1561,8 +1561,8 @@ packages: resolution: {integrity: sha512-vDVrlmRAY8z9Ul/HxT+8ceAru95LQgkSKiXkSYZvqtbkPSfhZJgpRp45Cldbh1GJ1kxzQkI70AqyrTI58KpaWQ==} engines: {node: '>=6.9.0'} - '@babel/runtime@7.28.3': - resolution: {integrity: sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==} + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} '@babel/runtime@7.28.4': @@ -3542,103 +3542,108 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.46.3': - resolution: {integrity: sha512-UmTdvXnLlqQNOCJnyksjPs1G4GqXNGW1LrzCe8+8QoaLhhDeTXYBgJ3k6x61WIhlHX2U+VzEJ55TtIjR/HTySA==} + '@rollup/rollup-android-arm-eabi@4.50.1': + resolution: {integrity: sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.46.3': - resolution: {integrity: sha512-8NoxqLpXm7VyeI0ocidh335D6OKT0UJ6fHdnIxf3+6oOerZZc+O7r+UhvROji6OspyPm+rrIdb1gTXtVIqn+Sg==} + '@rollup/rollup-android-arm64@4.50.1': + resolution: {integrity: sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.46.3': - resolution: {integrity: sha512-csnNavqZVs1+7/hUKtgjMECsNG2cdB8F7XBHP6FfQjqhjF8rzMzb3SLyy/1BG7YSfQ+bG75Ph7DyedbUqwq1rA==} + '@rollup/rollup-darwin-arm64@4.50.1': + resolution: {integrity: sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.46.3': - resolution: {integrity: sha512-r2MXNjbuYabSIX5yQqnT8SGSQ26XQc8fmp6UhlYJd95PZJkQD1u82fWP7HqvGUf33IsOC6qsiV+vcuD4SDP6iw==} + '@rollup/rollup-darwin-x64@4.50.1': + resolution: {integrity: sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.46.3': - resolution: {integrity: sha512-uluObTmgPJDuJh9xqxyr7MV61Imq+0IvVsAlWyvxAaBSNzCcmZlhfYcRhCdMaCsy46ccZa7vtDDripgs9Jkqsw==} + '@rollup/rollup-freebsd-arm64@4.50.1': + resolution: {integrity: sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.46.3': - resolution: {integrity: sha512-AVJXEq9RVHQnejdbFvh1eWEoobohUYN3nqJIPI4mNTMpsyYN01VvcAClxflyk2HIxvLpRcRggpX1m9hkXkpC/A==} + '@rollup/rollup-freebsd-x64@4.50.1': + resolution: {integrity: sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.46.3': - resolution: {integrity: sha512-byyflM+huiwHlKi7VHLAYTKr67X199+V+mt1iRgJenAI594vcmGGddWlu6eHujmcdl6TqSNnvqaXJqZdnEWRGA==} + '@rollup/rollup-linux-arm-gnueabihf@4.50.1': + resolution: {integrity: sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.46.3': - resolution: {integrity: sha512-aLm3NMIjr4Y9LklrH5cu7yybBqoVCdr4Nvnm8WB7PKCn34fMCGypVNpGK0JQWdPAzR/FnoEoFtlRqZbBBLhVoQ==} + '@rollup/rollup-linux-arm-musleabihf@4.50.1': + resolution: {integrity: sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.46.3': - resolution: {integrity: sha512-VtilE6eznJRDIoFOzaagQodUksTEfLIsvXymS+UdJiSXrPW7Ai+WG4uapAc3F7Hgs791TwdGh4xyOzbuzIZrnw==} + '@rollup/rollup-linux-arm64-gnu@4.50.1': + resolution: {integrity: sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.46.3': - resolution: {integrity: sha512-dG3JuS6+cRAL0GQ925Vppafi0qwZnkHdPeuZIxIPXqkCLP02l7ka+OCyBoDEv8S+nKHxfjvjW4OZ7hTdHkx8/w==} + '@rollup/rollup-linux-arm64-musl@4.50.1': + resolution: {integrity: sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.46.3': - resolution: {integrity: sha512-iU8DxnxEKJptf8Vcx4XvAUdpkZfaz0KWfRrnIRrOndL0SvzEte+MTM7nDH4A2Now4FvTZ01yFAgj6TX/mZl8hQ==} + '@rollup/rollup-linux-loongarch64-gnu@4.50.1': + resolution: {integrity: sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.46.3': - resolution: {integrity: sha512-VrQZp9tkk0yozJoQvQcqlWiqaPnLM6uY1qPYXvukKePb0fqaiQtOdMJSxNFUZFsGw5oA5vvVokjHrx8a9Qsz2A==} + '@rollup/rollup-linux-ppc64-gnu@4.50.1': + resolution: {integrity: sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.46.3': - resolution: {integrity: sha512-uf2eucWSUb+M7b0poZ/08LsbcRgaDYL8NCGjUeFMwCWFwOuFcZ8D9ayPl25P3pl+D2FH45EbHdfyUesQ2Lt9wA==} + '@rollup/rollup-linux-riscv64-gnu@4.50.1': + resolution: {integrity: sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.46.3': - resolution: {integrity: sha512-7tnUcDvN8DHm/9ra+/nF7lLzYHDeODKKKrh6JmZejbh1FnCNZS8zMkZY5J4sEipy2OW1d1Ncc4gNHUd0DLqkSg==} + '@rollup/rollup-linux-riscv64-musl@4.50.1': + resolution: {integrity: sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.46.3': - resolution: {integrity: sha512-MUpAOallJim8CsJK+4Lc9tQzlfPbHxWDrGXZm2z6biaadNpvh3a5ewcdat478W+tXDoUiHwErX/dOql7ETcLqg==} + '@rollup/rollup-linux-s390x-gnu@4.50.1': + resolution: {integrity: sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.46.3': - resolution: {integrity: sha512-F42IgZI4JicE2vM2PWCe0N5mR5vR0gIdORPqhGQ32/u1S1v3kLtbZ0C/mi9FFk7C5T0PgdeyWEPajPjaUpyoKg==} + '@rollup/rollup-linux-x64-gnu@4.50.1': + resolution: {integrity: sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.46.3': - resolution: {integrity: sha512-oLc+JrwwvbimJUInzx56Q3ujL3Kkhxehg7O1gWAYzm8hImCd5ld1F2Gry5YDjR21MNb5WCKhC9hXgU7rRlyegQ==} + '@rollup/rollup-linux-x64-musl@4.50.1': + resolution: {integrity: sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.46.3': - resolution: {integrity: sha512-lOrQ+BVRstruD1fkWg9yjmumhowR0oLAAzavB7yFSaGltY8klttmZtCLvOXCmGE9mLIn8IBV/IFrQOWz5xbFPg==} + '@rollup/rollup-openharmony-arm64@4.50.1': + resolution: {integrity: sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.50.1': + resolution: {integrity: sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.46.3': - resolution: {integrity: sha512-vvrVKPRS4GduGR7VMH8EylCBqsDcw6U+/0nPDuIjXQRbHJc6xOBj+frx8ksfZAh6+Fptw5wHrN7etlMmQnPQVg==} + '@rollup/rollup-win32-ia32-msvc@4.50.1': + resolution: {integrity: sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.46.3': - resolution: {integrity: sha512-fi3cPxCnu3ZeM3EwKZPgXbWoGzm2XHgB/WShKI81uj8wG0+laobmqy5wbgEwzstlbLu4MyO8C19FyhhWseYKNQ==} + '@rollup/rollup-win32-x64-msvc@4.50.1': + resolution: {integrity: sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==} cpu: [x64] os: [win32] @@ -8370,8 +8375,8 @@ packages: peerDependencies: webpack: ^4.0.0 || ^5.0.0 - nwsapi@2.2.21: - resolution: {integrity: sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==} + nwsapi@2.2.22: + resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==} nwsapi@2.2.22: resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==} @@ -9759,8 +9764,8 @@ packages: rollup: optional: true - rollup@4.46.3: - resolution: {integrity: sha512-RZn2XTjXb8t5g13f5YclGoilU/kwT696DIkY3sywjdZidNSi3+vseaQov7D7BZXVJCPv3pDWUN69C78GGbXsKw==} + rollup@4.50.1: + resolution: {integrity: sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -10525,8 +10530,8 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyglobby@0.2.14: - resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} tinypool@1.1.1: @@ -10963,8 +10968,8 @@ packages: vite: optional: true - vite@7.1.2: - resolution: {integrity: sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==} + vite@7.1.5: + resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -12243,7 +12248,7 @@ snapshots: dependencies: core-js-pure: 3.43.0 - '@babel/runtime@7.28.3': {} + '@babel/runtime@7.28.4': {} '@babel/runtime@7.28.4': {} @@ -14803,72 +14808,75 @@ snapshots: react: 18.3.1 react-redux: 7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@rollup/pluginutils@5.2.0(rollup@4.46.3)': + '@rollup/pluginutils@5.2.0(rollup@4.50.1)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.46.3 + rollup: 4.50.1 - '@rollup/rollup-android-arm-eabi@4.46.3': + '@rollup/rollup-android-arm-eabi@4.50.1': optional: true - '@rollup/rollup-android-arm64@4.46.3': + '@rollup/rollup-android-arm64@4.50.1': optional: true - '@rollup/rollup-darwin-arm64@4.46.3': + '@rollup/rollup-darwin-arm64@4.50.1': optional: true - '@rollup/rollup-darwin-x64@4.46.3': + '@rollup/rollup-darwin-x64@4.50.1': optional: true - '@rollup/rollup-freebsd-arm64@4.46.3': + '@rollup/rollup-freebsd-arm64@4.50.1': optional: true - '@rollup/rollup-freebsd-x64@4.46.3': + '@rollup/rollup-freebsd-x64@4.50.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.46.3': + '@rollup/rollup-linux-arm-gnueabihf@4.50.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.46.3': + '@rollup/rollup-linux-arm-musleabihf@4.50.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.46.3': + '@rollup/rollup-linux-arm64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.46.3': + '@rollup/rollup-linux-arm64-musl@4.50.1': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.46.3': + '@rollup/rollup-linux-loongarch64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.46.3': + '@rollup/rollup-linux-ppc64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.46.3': + '@rollup/rollup-linux-riscv64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.46.3': + '@rollup/rollup-linux-riscv64-musl@4.50.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.46.3': + '@rollup/rollup-linux-s390x-gnu@4.50.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.46.3': + '@rollup/rollup-linux-x64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-x64-musl@4.46.3': + '@rollup/rollup-linux-x64-musl@4.50.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.46.3': + '@rollup/rollup-openharmony-arm64@4.50.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.46.3': + '@rollup/rollup-win32-arm64-msvc@4.50.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.46.3': + '@rollup/rollup-win32-ia32-msvc@4.50.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.50.1': optional: true '@scarf/scarf@1.4.0': {} @@ -14925,29 +14933,29 @@ snapshots: dependencies: acorn: 8.15.0 - '@sveltejs/adapter-static@3.0.9(@sveltejs/kit@2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))': + '@sveltejs/adapter-static@3.0.9(@sveltejs/kit@2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))': dependencies: - '@sveltejs/kit': 2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/kit': 2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) - '@sveltejs/enhanced-img@0.8.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.46.3)(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/enhanced-img@0.8.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.50.1)(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte': 6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) magic-string: 0.30.17 sharp: 0.34.3 svelte: 5.35.5 svelte-parse-markup: 0.1.5(svelte@5.35.5) - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vite-imagetools: 8.0.0(rollup@4.46.3) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite-imagetools: 8.0.0(rollup@4.50.1) zimmerframe: 1.1.2 transitivePeerDependencies: - rollup - supports-color - '@sveltejs/kit@2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/kit@2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@standard-schema/spec': 1.0.0 '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) - '@sveltejs/vite-plugin-svelte': 6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte': 6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@types/cookie': 0.6.0 acorn: 8.15.0 cookie: 0.6.0 @@ -14960,27 +14968,27 @@ snapshots: set-cookie-parser: 2.7.1 sirv: 3.0.1 svelte: 5.35.5 - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - '@sveltejs/vite-plugin-svelte-inspector@5.0.0(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/vite-plugin-svelte-inspector@5.0.0(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte': 6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) debug: 4.4.1 svelte: 5.35.5 - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 5.0.0(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte-inspector': 5.0.0(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) debug: 4.4.1 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.17 svelte: 5.35.5 - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vitefu: 1.1.1(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vitefu: 1.1.1(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) transitivePeerDependencies: - supports-color @@ -15202,17 +15210,17 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.1.12 '@tailwindcss/oxide-win32-x64-msvc': 4.1.12 - '@tailwindcss/vite@4.1.12(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@tailwindcss/vite@4.1.12(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@tailwindcss/node': 4.1.12 '@tailwindcss/oxide': 4.1.12 tailwindcss: 4.1.12 - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) '@testing-library/dom@10.4.0': dependencies: '@babel/code-frame': 7.27.1 - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@types/aria-query': 5.0.4 aria-query: 5.3.0 chalk: 4.1.2 @@ -15229,12 +15237,12 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/svelte@5.2.8(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@testing-library/svelte@5.2.8(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@testing-library/dom': 10.4.0 svelte: 5.35.5 optionalDependencies: - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.0)': @@ -15897,21 +15905,21 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@vitest/mocker@3.2.4(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - '@vitest/mocker@3.2.4(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@vitest/mocker@3.2.4(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) '@vitest/pretty-format@3.2.4': dependencies: @@ -19337,7 +19345,7 @@ snapshots: http-proxy-agent: 5.0.0 https-proxy-agent: 5.0.1 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.21 + nwsapi: 2.2.22 parse5: 7.3.0 saxes: 6.0.0 symbol-tree: 3.2.4 @@ -20633,7 +20641,7 @@ snapshots: proc-log: 5.0.0 semver: 7.7.2 tar: 7.4.3 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 which: 5.0.0 transitivePeerDependencies: - supports-color @@ -20691,7 +20699,7 @@ snapshots: schema-utils: 3.3.0 webpack: 5.100.2 - nwsapi@2.2.21: + nwsapi@2.2.22: optional: true nwsapi@2.2.22: @@ -22254,39 +22262,40 @@ snapshots: robust-predicates@3.0.2: {} - rollup-plugin-visualizer@6.0.3(rollup@4.46.3): + rollup-plugin-visualizer@6.0.3(rollup@4.50.1): dependencies: open: 8.4.2 picomatch: 4.0.3 source-map: 0.7.4 yargs: 17.7.2 optionalDependencies: - rollup: 4.46.3 + rollup: 4.50.1 - rollup@4.46.3: + rollup@4.50.1: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.46.3 - '@rollup/rollup-android-arm64': 4.46.3 - '@rollup/rollup-darwin-arm64': 4.46.3 - '@rollup/rollup-darwin-x64': 4.46.3 - '@rollup/rollup-freebsd-arm64': 4.46.3 - '@rollup/rollup-freebsd-x64': 4.46.3 - '@rollup/rollup-linux-arm-gnueabihf': 4.46.3 - '@rollup/rollup-linux-arm-musleabihf': 4.46.3 - '@rollup/rollup-linux-arm64-gnu': 4.46.3 - '@rollup/rollup-linux-arm64-musl': 4.46.3 - '@rollup/rollup-linux-loongarch64-gnu': 4.46.3 - '@rollup/rollup-linux-ppc64-gnu': 4.46.3 - '@rollup/rollup-linux-riscv64-gnu': 4.46.3 - '@rollup/rollup-linux-riscv64-musl': 4.46.3 - '@rollup/rollup-linux-s390x-gnu': 4.46.3 - '@rollup/rollup-linux-x64-gnu': 4.46.3 - '@rollup/rollup-linux-x64-musl': 4.46.3 - '@rollup/rollup-win32-arm64-msvc': 4.46.3 - '@rollup/rollup-win32-ia32-msvc': 4.46.3 - '@rollup/rollup-win32-x64-msvc': 4.46.3 + '@rollup/rollup-android-arm-eabi': 4.50.1 + '@rollup/rollup-android-arm64': 4.50.1 + '@rollup/rollup-darwin-arm64': 4.50.1 + '@rollup/rollup-darwin-x64': 4.50.1 + '@rollup/rollup-freebsd-arm64': 4.50.1 + '@rollup/rollup-freebsd-x64': 4.50.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.50.1 + '@rollup/rollup-linux-arm-musleabihf': 4.50.1 + '@rollup/rollup-linux-arm64-gnu': 4.50.1 + '@rollup/rollup-linux-arm64-musl': 4.50.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.50.1 + '@rollup/rollup-linux-ppc64-gnu': 4.50.1 + '@rollup/rollup-linux-riscv64-gnu': 4.50.1 + '@rollup/rollup-linux-riscv64-musl': 4.50.1 + '@rollup/rollup-linux-s390x-gnu': 4.50.1 + '@rollup/rollup-linux-x64-gnu': 4.50.1 + '@rollup/rollup-linux-x64-musl': 4.50.1 + '@rollup/rollup-openharmony-arm64': 4.50.1 + '@rollup/rollup-win32-arm64-msvc': 4.50.1 + '@rollup/rollup-win32-ia32-msvc': 4.50.1 + '@rollup/rollup-win32-x64-msvc': 4.50.1 fsevents: 2.3.3 router@2.2.0: @@ -23307,7 +23316,7 @@ snapshots: tinyexec@0.3.2: {} - tinyglobby@0.2.14: + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 @@ -23597,9 +23606,9 @@ snapshots: unpipe@1.0.0: {} - unplugin-swc@1.5.5(@swc/core@1.13.3(@swc/helpers@0.5.17))(rollup@4.46.3): + unplugin-swc@1.5.5(@swc/core@1.13.3(@swc/helpers@0.5.17))(rollup@4.50.1): dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.46.3) + '@rollup/pluginutils': 5.2.0(rollup@4.50.1) '@swc/core': 1.13.3(@swc/helpers@0.5.17) load-tsconfig: 0.2.5 unplugin: 2.3.5 @@ -23739,9 +23748,9 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - vite-imagetools@8.0.0(rollup@4.46.3): + vite-imagetools@8.0.0(rollup@4.50.1): dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.46.3) + '@rollup/pluginutils': 5.2.0(rollup@4.50.1) imagetools-core: 8.0.0 sharp: 0.34.3 transitivePeerDependencies: @@ -23754,7 +23763,7 @@ snapshots: debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -23775,7 +23784,7 @@ snapshots: debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -23790,25 +23799,25 @@ snapshots: - tsx - yaml - vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): + vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): dependencies: debug: 4.4.1 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.2) optionalDependencies: - vite: 7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - supports-color - typescript - vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): dependencies: esbuild: 0.25.9 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.46.3 - tinyglobby: 0.2.14 + rollup: 4.50.1 + tinyglobby: 0.2.15 optionalDependencies: '@types/node': 22.18.1 fsevents: 2.3.3 @@ -23817,14 +23826,14 @@ snapshots: terser: 5.43.1 yaml: 2.8.1 - vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): dependencies: esbuild: 0.25.9 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.46.3 - tinyglobby: 0.2.14 + rollup: 4.50.1 + tinyglobby: 0.2.15 optionalDependencies: '@types/node': 24.3.0 fsevents: 2.3.3 @@ -23833,9 +23842,9 @@ snapshots: terser: 5.43.1 yaml: 2.8.1 - vitefu@1.1.1(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): + vitefu@1.1.1(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): optionalDependencies: - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vitest-fetch-mock@0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): dependencies: @@ -23845,7 +23854,7 @@ snapshots: dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -23860,10 +23869,10 @@ snapshots: std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite-node: 3.2.4(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: @@ -23889,7 +23898,7 @@ snapshots: dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -23904,10 +23913,10 @@ snapshots: std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite-node: 3.2.4(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: @@ -23933,7 +23942,7 @@ snapshots: dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -23948,10 +23957,10 @@ snapshots: std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite-node: 3.2.4(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: diff --git a/server/Dockerfile b/server/Dockerfile index 6bdf57d4dc..e95eca7c12 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,83 +1,4 @@ -# dev build -FROM ghcr.io/immich-app/base-server-dev:202509091104@sha256:4f9275330f1e49e7ce9840758ea91839052fe6ed40972d5bb97a9af857fa956a AS dev - -ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \ - CI=1 \ - COREPACK_HOME=/tmp - -RUN npm install --global corepack@latest && \ - corepack enable pnpm && \ - echo "store-dir=/buildcache/pnpm-store" >> /usr/local/etc/npmrc && \ - echo "devdir=/buildcache/node-gyp" >> /usr/local/etc/npmrc - -COPY ./package* ./pnpm* .pnpmfile.cjs /tmp/create-dep-cache/ -COPY ./web/package* ./web/pnpm* /tmp/create-dep-cache/web/ -COPY ./server/package* ./server/pnpm* /tmp/create-dep-cache/server/ -COPY ./open-api/typescript-sdk/package* ./open-api/typescript-sdk/pnpm* /tmp/create-dep-cache/open-api/typescript-sdk/ -WORKDIR /tmp/create-dep-cache -RUN pnpm fetch && rm -rf /tmp/create-dep-cache && chmod -R o+rw /buildcache -WORKDIR /usr/src/app - -ENV PATH="${PATH}:/usr/src/app/server/bin:/usr/src/app/web/bin" \ - IMMICH_ENV=development \ - NVIDIA_DRIVER_CAPABILITIES=all \ - NVIDIA_VISIBLE_DEVICES=all -ENTRYPOINT ["tini", "--", "/bin/bash", "-c"] - -FROM dev AS dev-container-server - -RUN apt-get update --allow-releaseinfo-change && \ - apt-get install sudo inetutils-ping openjdk-11-jre-headless \ - vim nano \ - -y --no-install-recommends --fix-missing - -RUN usermod -aG sudo node && \ - echo "node ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers && \ - mkdir -p /workspaces/immich - -RUN chown node:node -R /workspaces -COPY --chown=node:node --chmod=755 ../.devcontainer/server/*.sh /immich-devcontainer/ - -WORKDIR /workspaces/immich - -FROM dev-container-server AS dev-container-mobile -USER root -# Enable multiarch for arm64 if necessary -RUN if [ "$(dpkg --print-architecture)" = "arm64" ]; then \ - dpkg --add-architecture amd64 && \ - apt-get update && \ - apt-get install -y --no-install-recommends \ - qemu-user-static \ - libc6:amd64 \ - libstdc++6:amd64 \ - libgcc1:amd64; \ - fi - -# Flutter SDK -# https://flutter.dev/docs/development/tools/sdk/releases?tab=linux -ENV FLUTTER_CHANNEL="stable" -ENV FLUTTER_VERSION="3.32.8" -ENV FLUTTER_HOME=/flutter -ENV PATH=${PATH}:${FLUTTER_HOME}/bin - -# Flutter SDK -RUN mkdir -p ${FLUTTER_HOME} \ - && curl -C - --output flutter.tar.xz https://storage.googleapis.com/flutter_infra_release/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_${FLUTTER_VERSION}-${FLUTTER_CHANNEL}.tar.xz \ - && tar -xf flutter.tar.xz --strip-components=1 -C ${FLUTTER_HOME} \ - && rm flutter.tar.xz \ - && chown -R node ${FLUTTER_HOME} - - -RUN apt-get update \ - && wget -qO- https://dcm.dev/pgp-key.public | gpg --dearmor -o /usr/share/keyrings/dcm.gpg \ - && echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | tee /etc/apt/sources.list.d/dart_stable.list \ - && apt-get update \ - && apt-get install dcm -y - -RUN dart --disable-analytics - -# production-builder-base image -FROM ghcr.io/immich-app/base-server-dev:202509091104@sha256:4f9275330f1e49e7ce9840758ea91839052fe6ed40972d5bb97a9af857fa956a AS prod-builder-base +FROM ghcr.io/immich-app/base-server-dev:202509091104@sha256:4f9275330f1e49e7ce9840758ea91839052fe6ed40972d5bb97a9af857fa956a AS builder ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \ CI=1 \ COREPACK_HOME=/tmp @@ -85,8 +6,7 @@ ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \ RUN npm install --global corepack@latest && \ corepack enable pnpm -# server production build -FROM prod-builder-base AS server-prod +FROM builder AS server WORKDIR /usr/src/app COPY ./package* ./pnpm* .pnpmfile.cjs ./ @@ -94,8 +14,7 @@ COPY ./server ./server/ RUN SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile build && \ SHARP_FORCE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile --prod --no-optional deploy /output/server-pruned -# web production build -FROM prod-builder-base AS web-prod +FROM builder AS web WORKDIR /usr/src/app COPY ./package* ./pnpm* .pnpmfile.cjs ./ @@ -105,7 +24,7 @@ COPY ./open-api ./open-api/ RUN SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter @immich/sdk --filter immich-web --frozen-lockfile --force install && \ pnpm --filter @immich/sdk --filter immich-web build -FROM prod-builder-base AS cli-prod +FROM builder AS cli COPY ./package* ./pnpm* .pnpmfile.cjs ./ COPY ./cli ./cli/ @@ -114,7 +33,6 @@ RUN pnpm --filter @immich/sdk --filter @immich/cli --frozen-lockfile install && pnpm --filter @immich/sdk --filter @immich/cli build && \ pnpm --filter @immich/cli --prod --no-optional deploy /output/cli-pruned -# prod base image FROM ghcr.io/immich-app/base-server-prod:202509091104@sha256:d1ccbac24c84f2f8277cf85281edfca62d85d7daed6a62b8efd3a81bcd3c5e0e WORKDIR /usr/src/app @@ -122,9 +40,9 @@ ENV NODE_ENV=production \ NVIDIA_DRIVER_CAPABILITIES=all \ NVIDIA_VISIBLE_DEVICES=all -COPY --from=server-prod /output/server-pruned ./server -COPY --from=web-prod /usr/src/app/web/build /build/www -COPY --from=cli-prod /output/cli-pruned ./cli +COPY --from=server /output/server-pruned ./server +COPY --from=web /usr/src/app/web/build /build/www +COPY --from=cli /output/cli-pruned ./cli RUN ln -s ../../cli/bin/immich server/bin/immich COPY LICENSE /licenses/LICENSE.txt COPY LICENSE /LICENSE diff --git a/server/Dockerfile.dev b/server/Dockerfile.dev new file mode 100644 index 0000000000..1db03bc844 --- /dev/null +++ b/server/Dockerfile.dev @@ -0,0 +1,77 @@ +# dev build +FROM ghcr.io/immich-app/base-server-dev:202509091104@sha256:4f9275330f1e49e7ce9840758ea91839052fe6ed40972d5bb97a9af857fa956a AS dev + +ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \ + CI=1 \ + COREPACK_HOME=/tmp + +RUN npm install --global corepack@latest && \ + corepack enable pnpm && \ + echo "store-dir=/buildcache/pnpm-store" >> /usr/local/etc/npmrc && \ + echo "devdir=/buildcache/node-gyp" >> /usr/local/etc/npmrc + +COPY ./package* ./pnpm* .pnpmfile.cjs /tmp/create-dep-cache/ +COPY ./web/package* ./web/pnpm* /tmp/create-dep-cache/web/ +COPY ./server/package* ./server/pnpm* /tmp/create-dep-cache/server/ +COPY ./open-api/typescript-sdk/package* ./open-api/typescript-sdk/pnpm* /tmp/create-dep-cache/open-api/typescript-sdk/ +WORKDIR /tmp/create-dep-cache +RUN pnpm fetch && rm -rf /tmp/create-dep-cache && chmod -R o+rw /buildcache +WORKDIR /usr/src/app + +ENV PATH="${PATH}:/usr/src/app/server/bin:/usr/src/app/web/bin" \ + IMMICH_ENV=development \ + NVIDIA_DRIVER_CAPABILITIES=all \ + NVIDIA_VISIBLE_DEVICES=all +ENTRYPOINT ["tini", "--", "/bin/bash", "-c"] + +FROM dev AS dev-container-server + +RUN apt-get update --allow-releaseinfo-change && \ + apt-get install sudo inetutils-ping openjdk-11-jre-headless \ + vim nano \ + -y --no-install-recommends --fix-missing + +RUN usermod -aG sudo node && \ + echo "node ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers && \ + mkdir -p /workspaces/immich + +RUN chown node:node -R /workspaces +COPY --chown=node:node --chmod=755 ../.devcontainer/server/*.sh /immich-devcontainer/ + +WORKDIR /workspaces/immich + +FROM dev-container-server AS dev-container-mobile +USER root +# Enable multiarch for arm64 if necessary +RUN if [ "$(dpkg --print-architecture)" = "arm64" ]; then \ + dpkg --add-architecture amd64 && \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + qemu-user-static \ + libc6:amd64 \ + libstdc++6:amd64 \ + libgcc1:amd64; \ + fi + +# Flutter SDK +# https://flutter.dev/docs/development/tools/sdk/releases?tab=linux +ENV FLUTTER_CHANNEL="stable" +ENV FLUTTER_VERSION="3.32.8" +ENV FLUTTER_HOME=/flutter +ENV PATH=${PATH}:${FLUTTER_HOME}/bin + +# Flutter SDK +RUN mkdir -p ${FLUTTER_HOME} \ + && curl -C - --output flutter.tar.xz https://storage.googleapis.com/flutter_infra_release/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_${FLUTTER_VERSION}-${FLUTTER_CHANNEL}.tar.xz \ + && tar -xf flutter.tar.xz --strip-components=1 -C ${FLUTTER_HOME} \ + && rm flutter.tar.xz \ + && chown -R node ${FLUTTER_HOME} + + +RUN apt-get update \ + && wget -qO- https://dcm.dev/pgp-key.public | gpg --dearmor -o /usr/share/keyrings/dcm.gpg \ + && echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | tee /etc/apt/sources.list.d/dart_stable.list \ + && apt-get update \ + && apt-get install dcm -y + +RUN dart --disable-analytics diff --git a/server/src/repositories/oauth.repository.ts b/server/src/repositories/oauth.repository.ts index 9a436e4b9a..58b1144647 100644 --- a/server/src/repositories/oauth.repository.ts +++ b/server/src/repositories/oauth.repository.ts @@ -29,6 +29,7 @@ export class OAuthRepository { ); const client = await this.getClient(config); state ??= randomState(); + let codeVerifier: string | null; if (codeChallenge) { codeVerifier = null; @@ -36,13 +37,20 @@ export class OAuthRepository { codeVerifier = randomPKCECodeVerifier(); codeChallenge = await calculatePKCECodeChallenge(codeVerifier); } - const url = buildAuthorizationUrl(client, { + + const params: Record = { redirect_uri: redirectUrl, scope: config.scope, state, - code_challenge: client.serverMetadata().supportsPKCE() ? codeChallenge : '', - code_challenge_method: client.serverMetadata().supportsPKCE() ? 'S256' : '', - }).toString(); + }; + + if (client.serverMetadata().supportsPKCE()) { + params.code_challenge = codeChallenge; + params.code_challenge_method = 'S256'; + } + + const url = buildAuthorizationUrl(client, params).toString(); + return { url, state, codeVerifier }; } diff --git a/server/src/services/asset.service.spec.ts b/server/src/services/asset.service.spec.ts index 7b29b8ab96..93861149c3 100755 --- a/server/src/services/asset.service.spec.ts +++ b/server/src/services/asset.service.spec.ts @@ -420,7 +420,7 @@ describe(AssetService.name, () => { ids: ['asset-1'], latitude: 0, longitude: 0, - visibility: undefined, + visibility: AssetVisibility.Archive, isFavorite: false, duplicateId: undefined, rating: undefined, diff --git a/server/src/services/asset.service.ts b/server/src/services/asset.service.ts index 725e3cff15..6cb0219745 100644 --- a/server/src/services/asset.service.ts +++ b/server/src/services/asset.service.ts @@ -115,59 +115,68 @@ export class AssetService extends BaseService { } async updateAll(auth: AuthDto, dto: AssetBulkUpdateDto): Promise { - const { ids, description, dateTimeOriginal, dateTimeRelative, timeZone, latitude, longitude, ...options } = dto; + const { + ids, + isFavorite, + visibility, + dateTimeOriginal, + latitude, + longitude, + rating, + description, + duplicateId, + dateTimeRelative, + timeZone, + } = dto; await this.requireAccess({ auth, permission: Permission.AssetUpdate, ids }); - const staticValuesChanged = - description !== undefined || dateTimeOriginal !== undefined || latitude !== undefined || longitude !== undefined; + const assetDto = { isFavorite, visibility, duplicateId }; + const exifDto = { latitude, longitude, rating, description, dateTimeOriginal }; - if (staticValuesChanged) { - await this.assetRepository.updateAllExif(ids, { description, dateTimeOriginal, latitude, longitude }); + const isExifChanged = Object.values(exifDto).some((v) => v !== undefined); + if (isExifChanged) { + await this.assetRepository.updateAllExif(ids, exifDto); } const assets = (dateTimeRelative !== undefined && dateTimeRelative !== 0) || timeZone !== undefined ? await this.assetRepository.updateDateTimeOriginal(ids, dateTimeRelative, timeZone) - : null; + : undefined; - const dateTimesWithTimezone = - assets?.map((asset) => { - const isoString = asset.dateTimeOriginal?.toISOString(); - let dateTime = isoString ? DateTime.fromISO(isoString) : null; + const dateTimesWithTimezone = assets + ? assets.map((asset) => { + const isoString = asset.dateTimeOriginal?.toISOString(); + let dateTime = isoString ? DateTime.fromISO(isoString) : null; - if (dateTime && asset.timeZone) { - dateTime = dateTime.setZone(asset.timeZone); - } + if (dateTime && asset.timeZone) { + dateTime = dateTime.setZone(asset.timeZone); + } - return { - assetId: asset.assetId, - dateTimeOriginal: dateTime?.toISO() ?? null, - }; - }) ?? null; + return { + assetId: asset.assetId, + dateTimeOriginal: dateTime?.toISO() ?? null, + }; + }) + : ids.map((id) => ({ assetId: id, dateTimeOriginal })); - if (staticValuesChanged || dateTimesWithTimezone) { - const entries: JobItem[] = (dateTimesWithTimezone ?? ids).map((entry: any) => ({ - name: JobName.SidecarWrite, - data: { - id: entry.assetId ?? entry, - description, - dateTimeOriginal: entry.dateTimeOriginal ?? dateTimeOriginal, - latitude, - longitude, - }, - })); - await this.jobRepository.queueAll(entries); + if (dateTimesWithTimezone.length > 0) { + await this.jobRepository.queueAll( + dateTimesWithTimezone.map(({ assetId: id, dateTimeOriginal }) => ({ + name: JobName.SidecarWrite, + data: { + ...exifDto, + id, + dateTimeOriginal: dateTimeOriginal ?? undefined, + }, + })), + ); } - if ( - options.visibility !== undefined || - options.isFavorite !== undefined || - options.duplicateId !== undefined || - options.rating !== undefined - ) { - await this.assetRepository.updateAll(ids, options); + const isAssetChanged = Object.values(assetDto).some((v) => v !== undefined); + if (isAssetChanged) { + await this.assetRepository.updateAll(ids, assetDto); - if (options.visibility === AssetVisibility.Locked) { + if (visibility === AssetVisibility.Locked) { await this.albumRepository.removeAssetsFromAll(ids); } } diff --git a/web/bin/immich-web b/web/bin/immich-web index 29e7c46edd..23377eddd6 100755 --- a/web/bin/immich-web +++ b/web/bin/immich-web @@ -1,8 +1,9 @@ #!/usr/bin/env sh -echo "Build dependencies for Immich Web" cd /usr/src/app || exit +pnpm --filter @immich/sdk build + COUNT=0 UPSTREAM="${IMMICH_SERVER_URL:-http://immich-server:2283/}" UPSTREAM="${UPSTREAM%/}" @@ -14,5 +15,4 @@ until wget --spider --quiet "${UPSTREAM}/api/server/config" > /dev/null 2>&1; do sleep 1 done echo "Connected to $UPSTREAM, starting Immich Web..." -pnpm --filter @immich/sdk build pnpm --filter immich-web exec vite dev --host 0.0.0.0 --port 3000 diff --git a/web/package.json b/web/package.json index 68de904f91..1e903d2bcd 100644 --- a/web/package.json +++ b/web/package.json @@ -9,7 +9,7 @@ "build:stats": "BUILD_STATS=true vite build", "package": "svelte-kit package", "preview": "vite preview", - "check:svelte": "svelte-check --no-tsconfig --fail-on-warnings --compiler-warnings 'reactive_declaration_non_reactive_property:ignore' --ignore src/lib/components/photos-page/asset-grid.svelte", + "check:svelte": "svelte-check --no-tsconfig --fail-on-warnings --compiler-warnings 'reactive_declaration_non_reactive_property:ignore' --ignore src/lib/components/timeline/Timeline.svelte", "check:typescript": "tsc --noEmit", "check:watch": "npm run check:svelte -- --watch", "check:code": "npm run format && npm run lint:p && npm run check:svelte && npm run check:typescript", diff --git a/web/src/lib/components/admin-page/jobs/job-tile.svelte b/web/src/lib/components/admin-page/jobs/job-tile.svelte index d2e0ca3ac4..dcd76fa238 100644 --- a/web/src/lib/components/admin-page/jobs/job-tile.svelte +++ b/web/src/lib/components/admin-page/jobs/job-tile.svelte @@ -67,7 +67,7 @@
{#if jobCounts.failed > 0} @@ -137,7 +137,7 @@ onClick={() => onCommand({ command: JobCommand.Start, force: false })} > - {$t('disabled').toUpperCase()} + {$t('disabled')} {/if} @@ -145,7 +145,7 @@ {#if waitingCount > 0} onCommand({ command: JobCommand.Empty, force: false })}> - {$t('clear').toUpperCase()} + {$t('clear')} {/if} {#if queueStatus.isPaused} @@ -153,12 +153,12 @@ onCommand({ command: JobCommand.Resume, force: false })}> - {$t('resume').toUpperCase()} + {$t('resume')} {:else} onCommand({ command: JobCommand.Pause, force: false })}> - {$t('pause').toUpperCase()} + {$t('pause')} {/if} {/if} @@ -167,25 +167,25 @@ {#if allText} onCommand({ command: JobCommand.Start, force: true })}> - {allText} + {allText} {/if} {#if refreshText} onCommand({ command: JobCommand.Start, force: undefined })}> - {refreshText} + {refreshText} {/if} onCommand({ command: JobCommand.Start, force: false })}> - {missingText} + {missingText} {/if} {#if !disabled && !multipleButtons && isIdle} onCommand({ command: JobCommand.Start, force: false })}> - {missingText} + {missingText} {/if}
diff --git a/web/src/lib/components/admin-page/jobs/jobs-panel.svelte b/web/src/lib/components/admin-page/jobs/jobs-panel.svelte index 463bcb3d20..93e015e251 100644 --- a/web/src/lib/components/admin-page/jobs/jobs-panel.svelte +++ b/web/src/lib/components/admin-page/jobs/jobs-panel.svelte @@ -177,9 +177,9 @@ {disabled} {subtitle} {description} - allText={allText?.toUpperCase()} - refreshText={refreshText?.toUpperCase()} - missingText={missingText.toUpperCase()} + {allText} + {refreshText} + {missingText} {jobCounts} {queueStatus} onCommand={(command) => (handleCommandOverride || handleCommand)(jobName, command)} diff --git a/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte b/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte index b9ceec7445..25c5a0dc56 100644 --- a/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte +++ b/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte @@ -36,19 +36,19 @@
-

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

+

{$t('total_usage')}

-

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

+

{$t('photos')}

@@ -60,7 +60,7 @@
-

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

+

{$t('videos')}

@@ -72,7 +72,7 @@
-

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

+

{$t('storage')}

@@ -87,7 +87,7 @@
-

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

+

{$t('user_usage_detail')}

- {title} + {title}
diff --git a/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte b/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte index ef371910c5..07a0e3952c 100644 --- a/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte +++ b/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte @@ -183,7 +183,7 @@ {$t('template')}
-

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

+

{$t('preview')}

diff --git a/web/src/lib/components/admin-page/settings/storage-template/supported-datetime-panel.svelte b/web/src/lib/components/admin-page/settings/storage-template/supported-datetime-panel.svelte index 8d8d6f0fa7..af21cdc3c9 100644 --- a/web/src/lib/components/admin-page/settings/storage-template/supported-datetime-panel.svelte +++ b/web/src/lib/components/admin-page/settings/storage-template/supported-datetime-panel.svelte @@ -16,7 +16,7 @@

-

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

+

{$t('date_and_time')}

@@ -27,7 +27,7 @@
-

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

+

{$t('year')}

    {#each options.yearOptions as yearFormat, index (index)}
  • {'{{'}{yearFormat}{'}}'} - {getLuxonExample(yearFormat)}
  • @@ -36,7 +36,7 @@
-

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

+

{$t('month')}

    {#each options.monthOptions as monthFormat, index (index)}
  • {'{{'}{monthFormat}{'}}'} - {getLuxonExample(monthFormat)}
  • @@ -45,7 +45,7 @@
-

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

+

{$t('week')}

    {#each options.weekOptions as weekFormat, index (index)}
  • {'{{'}{weekFormat}{'}}'} - {getLuxonExample(weekFormat)}
  • @@ -54,7 +54,7 @@
-

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

+

{$t('day')}

    {#each options.dayOptions as dayFormat, index (index)}
  • {'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}
  • @@ -63,7 +63,7 @@
-

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

+

{$t('hour')}

    {#each options.hourOptions as dayFormat, index (index)}
  • {'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}
  • @@ -72,7 +72,7 @@
-

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

+

{$t('minute')}

    {#each options.minuteOptions as dayFormat, index (index)}
  • {'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}
  • @@ -81,7 +81,7 @@
-

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

+

{$t('second')}