Compare commits

..

32 Commits

Author SHA1 Message Date
github-actions
58ae77ec92 chore: version v1.134.0 2025-05-27 16:47:49 +00:00
Mert
4794a1a092 fix(server): handle startup reindexing after failed model change (#18688)
drop constraint
2025-05-27 11:36:30 -05:00
Daimolean
6abcfaef99 docs: update link (#18689) 2025-05-27 16:22:57 +00:00
Alex
f6903696cb fix: revert accidental docker-compose dev change (#18686) 2025-05-27 17:15:45 +01:00
renovate[bot]
724a081bb5 fix(deps): update typescript-projects (#18681)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-27 18:00:45 +02:00
Daimolean
4e332db2fb fix(web): update after delete (#18684) 2025-05-27 15:42:08 +00:00
Zack Pollard
0712183a18 fix: replace edit user button with view button for user details screen (#18683) 2025-05-27 15:38:16 +00:00
Alex
d004c03990 fix: z-index search bar (#18685) 2025-05-27 15:36:03 +00:00
Alex
fff651f8a5 fix(web): handle nullable assets duration (#18679)
* fix(web): handle nullable assets duration

* Update web/src/lib/components/assets/thumbnail/thumbnail.svelte

Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com>

* fix: format

---------

Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com>
2025-05-27 10:24:17 -05:00
Mert
e2720e85bb fix(server): handle period in database name (#18590) 2025-05-27 16:05:13 +01:00
Nicholas
5fdc8c9481 feat: add vscode extensions as recommendations (#18641)
* add vscode extensions as recommendations

* forgot to add DCM because it's not available for VSCodium afaict

* update docs

* fix formatting
2025-05-27 10:02:36 -05:00
renovate[bot]
a3404cf420 fix(deps): update typescript-projects (#18671)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2025-05-27 17:00:29 +02:00
Daniel Dietzler
5268dc4ee2 feat: version check endpoint (#18572) 2025-05-27 09:33:23 -05:00
renovate[bot]
ef060e97b6 chore(deps): update github-actions (#18660)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-27 15:32:47 +01:00
Brandon Wees
a9851df8d1 fix(web): move support & feedback button to user modal (#18651)
* move support & feedback button to user modal

* cleanup styling of link

* update sign out button to use immich/ui

* revise sign out button to match design from @alextran1502

* more margin on support/feedback
2025-05-27 09:26:40 -05:00
Indrek Haav
099a1e4210 feat(mobile): add Estonian (#18666) 2025-05-27 14:25:44 +00:00
Daimolean
79d760ccd7 fix(server): reverse isTrash field (#18665) 2025-05-27 16:22:09 +02:00
bo0tzz
369d3dfa38 fix: use single bulkTagAssets call instead of loop (#18672) 2025-05-27 10:35:22 +00:00
renovate[bot]
93e53f6d74 chore(deps): update node.js to v22.16.0 (#18662)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-27 12:13:30 +02:00
renovate[bot]
d8f0a69dc8 chore(deps): update node (#18661)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-27 12:12:37 +02:00
renovate[bot]
09d9fa9755 chore(deps): pin dependencies (#18659)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-27 12:10:30 +02:00
Alex
118dc8cf5a fix: meta+click on thumbnail (#18648) 2025-05-26 14:58:46 -05:00
bo0tzz
9557395991 chore: remove checkbox requirement from dupe search question (#18647)
* Update bug_report.yaml

* Update feature-request.yaml
2025-05-26 10:47:58 -05:00
Alex
a5d63d6953 fix(web): modal anchor (#18621)
fix: modal anchor
2025-05-25 20:38:46 +00:00
arnonm
5ee4a43e74 fix: use correct flutter version in devcontainer (#18285)
Fixed issue 18284

Co-authored-by: Arnon Meshoulam <arnonm@gmail.com>
2025-05-25 19:43:14 +00:00
Arno
c3aeb6c497 chore: refactor slide-show-settings modal (#18570)
* chore: refactor slide-show-settings modal

* fix: dropdown getting clipped in modals

* Revert "fix: dropdown getting clipped in modals"

This reverts commit 0120932a49.

* fix: changed to show method
2025-05-25 14:38:13 -05:00
Xuan Binh
d22fb2d5db fix(web): enhance face tagging confirmation and fix #18605 (#18610)
* Fix: enhance face tagging confirmation and fix double label in checkboxes

* fix code formatting

---------

Co-authored-by: dvbthien <dvbthien@gmail.com>
2025-05-25 14:34:12 -05:00
Lukas
c4df96bd72 fix(web): center memory lane buttons (#18613)
* fix(web): center memory lane buttons

* format
2025-05-25 19:33:25 +00:00
toamz
40e7b58ba4 fix(mobile): pinch to zoom + move acceleration (#18569)
Fix pinch to zoom + move acceleration
2025-05-25 14:32:04 -05:00
Alex
4743a085f1 fix: more z-index issue (#18598)
* fix: search suggestion

* fix: play icon lay on top of the search bar
2025-05-25 14:31:24 -05:00
Alex
911c877e72 feat: clean up memory with locked assets (#18532) 2025-05-24 07:31:25 -05:00
Alex
806000e671 chore: post release tasks (#18549) 2025-05-24 00:44:25 +05:30
76 changed files with 1331 additions and 3275 deletions

View File

@@ -4,7 +4,7 @@ FROM ${BASEIMAGE}
# Flutter SDK
# https://flutter.dev/docs/development/tools/sdk/releases?tab=linux
ENV FLUTTER_CHANNEL="stable"
ENV FLUTTER_VERSION="3.29.1"
ENV FLUTTER_VERSION="3.29.3"
ENV FLUTTER_HOME=/flutter
ENV PATH=${PATH}:${FLUTTER_HOME}/bin

2
.github/.nvmrc vendored
View File

@@ -1 +1 @@
22.15.1
22.16.0

View File

@@ -14,7 +14,6 @@ body:
label: I have searched the existing feature requests, both open and closed, to make sure this is not a duplicate request.
options:
- label: 'Yes'
required: true
- type: textarea
id: feature

View File

@@ -6,7 +6,6 @@ body:
label: I have searched the existing issues, both open and closed, to make sure this is not a duplicate report.
options:
- label: 'Yes'
required: true
- type: markdown
attributes:

View File

@@ -50,7 +50,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -63,7 +63,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -76,6 +76,6 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
with:
category: '/language:${{matrix.language}}'

View File

@@ -14,7 +14,7 @@ jobs:
pull-requests: write
steps:
- name: Require PR to have a changelog label
uses: mheap/github-action-required-labels@388fd6af37b34cdfe5a23b37060e763217e58b03 # v5.5.0
uses: mheap/github-action-required-labels@fb29a14a076b0f74099f6198f77750e8fc236016 # v5.5.0
with:
mode: exactly
count: 1

View File

@@ -118,7 +118,7 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
with:
sarif_file: results.sarif
category: zizmor

View File

@@ -648,7 +648,7 @@ jobs:
contents: read
services:
postgres:
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:14bec5d02e8704081eafd566029204a4eb6bb75f3056cfb34e81c5ab1657a490
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:1076bb152a3000df23911fdec83f14ea83f0dd0c42bc7d4e14b854e9bda1b0c9
env:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres

1
.gitignore vendored
View File

@@ -3,6 +3,7 @@
.DS_Store
.vscode/*
!.vscode/launch.json
!.vscode/extensions.json
.idea
docker/upload

10
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,10 @@
{
"recommendations": [
"esbenp.prettier-vscode",
"svelte.svelte-vscode",
"dbaeumer.vscode-eslint",
"dart-code.flutter",
"dart-code.dart-code",
"dcmdev.dcm-vscode-extension"
]
}

View File

@@ -1 +1 @@
22.15.1
22.16.0

View File

@@ -1,4 +1,4 @@
FROM node:22.15.0-alpine3.20@sha256:686b8892b69879ef5bfd6047589666933508f9a5451c67320df3070ba0e9807b AS core
FROM node:22.16.0-alpine3.20@sha256:2289fb1fba0f4633b08ec47b94a89c7e20b829fc5679f9b7b298eaa2f1ed8b7e AS core
WORKDIR /usr/src/open-api/typescript-sdk
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./

1107
cli/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@immich/cli",
"version": "2.2.67",
"version": "2.2.68",
"description": "Command Line Interface (CLI) for Immich",
"type": "module",
"exports": "./dist/index.js",
@@ -21,7 +21,7 @@
"@types/lodash-es": "^4.17.12",
"@types/micromatch": "^4.0.9",
"@types/mock-fs": "^4.13.1",
"@types/node": "^22.15.18",
"@types/node": "^22.15.21",
"@vitest/coverage-v8": "^3.0.0",
"byte-size": "^9.0.0",
"cli-progress": "^3.12.0",
@@ -69,6 +69,6 @@
"micromatch": "^4.0.8"
},
"volta": {
"node": "22.15.1"
"node": "22.16.0"
}
}

View File

@@ -16,7 +16,7 @@ name: immich-dev
services:
immich-server:
container_name: immich_server
command: ['/usr/src/app/bin/immich-dev']
command: [ '/usr/src/app/bin/immich-dev' ]
image: immich-server-dev:latest
# extends:
# file: hwaccel.transcoding.yml
@@ -48,7 +48,7 @@ services:
IMMICH_THIRD_PARTY_SOURCE_URL: https://github.com/immich-app/immich/
IMMICH_THIRD_PARTY_BUG_FEATURE_URL: https://github.com/immich-app/immich/issues
IMMICH_THIRD_PARTY_DOCUMENTATION_URL: https://immich.app/docs
IMMICH_THIRD_PARTY_SUPPORT_URL: https://immich.app/docs/third-party
IMMICH_THIRD_PARTY_SUPPORT_URL: https://immich.app/docs/community-guides
ulimits:
nofile:
soft: 1048576
@@ -70,7 +70,7 @@ services:
# user: 0:0
build:
context: ../web
command: ['/usr/src/app/bin/immich-web']
command: [ '/usr/src/app/bin/immich-web' ]
env_file:
- .env
ports:
@@ -122,7 +122,7 @@ services:
database:
container_name: immich_postgres
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvectors0.2.0
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvectors0.2.0@sha256:fa4f6e0971f454cd95fec5a9aaed2ed93d8f46725cc6bc61e0698e97dba96da1
env_file:
- .env
environment:
@@ -134,7 +134,6 @@ services:
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
ports:
- 5432:5432
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
# immich-prometheus:
# container_name: immich_prometheus

View File

@@ -63,7 +63,7 @@ services:
database:
container_name: immich_postgres
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvectors0.2.0
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvectors0.2.0@sha256:fa4f6e0971f454cd95fec5a9aaed2ed93d8f46725cc6bc61e0698e97dba96da1
env_file:
- .env
environment:

View File

@@ -56,7 +56,7 @@ services:
database:
container_name: immich_postgres
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvectors0.2.0
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvectors0.2.0@sha256:fa4f6e0971f454cd95fec5a9aaed2ed93d8f46725cc6bc61e0698e97dba96da1
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}

View File

@@ -1 +1 @@
22.15.1
22.16.0

View File

@@ -115,32 +115,72 @@ Note: Activating the license is not required.
### VSCode
Install `Flutter`, `DCM`, `Prettier`, `ESLint` and `Svelte` extensions.
Install `Flutter`, `DCM`, `Prettier`, `ESLint` and `Svelte` extensions. These extensions are listed in the `extensions.json` file under `.vscode/` and should appear as workspace recommendations.
in User `settings.json` (`cmd + shift + p` and search for `Open User Settings JSON`) add the following:
Here are the settings we use, they should be active as workspace settings (`settings.json`):
```json title="settings.json"
{
"editor.formatOnSave": true,
"[javascript][typescript][css]": {
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.tabSize": 2,
"editor.formatOnSave": true
},
"[svelte]": {
"editor.defaultFormatter": "svelte.svelte-vscode",
"editor.formatOnSave": true,
"editor.tabSize": 2
},
"svelte.enable-ts-plugin": true,
"eslint.validate": ["javascript", "svelte"],
"[dart]": {
"editor.defaultFormatter": "Dart-Code.dart-code",
"editor.formatOnSave": true,
"editor.selectionHighlight": false,
"editor.suggest.snippetsPreventQuickSuggestions": false,
"editor.suggestSelection": "first",
"editor.tabCompletion": "onlySnippets",
"editor.wordBasedSuggestions": "off",
"editor.defaultFormatter": "Dart-Code.dart-code"
}
"editor.wordBasedSuggestions": "off"
},
"[javascript]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.removeUnusedImports": "explicit"
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.tabSize": 2
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.tabSize": 2
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.tabSize": 2
},
"[svelte]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.removeUnusedImports": "explicit"
},
"editor.defaultFormatter": "svelte.svelte-vscode",
"editor.formatOnSave": true,
"editor.tabSize": 2
},
"[typescript]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.removeUnusedImports": "explicit"
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.tabSize": 2
},
"cSpell.words": ["immich"],
"editor.formatOnSave": true,
"eslint.validate": ["javascript", "svelte"],
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": {
"*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart",
"*.ts": "${capture}.spec.ts,${capture}.mock.ts"
},
"svelte.enable-ts-plugin": true,
"typescript.preferences.importModuleSpecifier": "non-relative"
}
```

View File

@@ -57,6 +57,6 @@
"node": ">=20"
},
"volta": {
"node": "22.15.1"
"node": "22.16.0"
}
}

View File

@@ -1,4 +1,8 @@
[
{
"label": "v1.134.0",
"url": "https://v1.134.0.archive.immich.app"
},
{
"label": "v1.133.1",
"url": "https://v1.133.1.archive.immich.app"

View File

@@ -1 +1 @@
22.15.1
22.16.0

View File

@@ -37,7 +37,7 @@ services:
image: redis:6.2-alpine@sha256:3211c33a618c457e5d241922c975dbc4f446d0bdb2dc75694f5573ef8e2d01fa
database:
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:e6d1209c1c13791c6f9fbf726c41865e3320dfe2445a6b4ffb03e25f904b3b37
command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf
environment:
POSTGRES_PASSWORD: postgres

1096
e2e/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "immich-e2e",
"version": "1.133.1",
"version": "1.134.0",
"description": "",
"main": "index.js",
"type": "module",
@@ -25,7 +25,7 @@
"@immich/sdk": "file:../open-api/typescript-sdk",
"@playwright/test": "^1.44.1",
"@types/luxon": "^3.4.2",
"@types/node": "^22.15.18",
"@types/node": "^22.15.21",
"@types/oidc-provider": "^8.5.1",
"@types/pg": "^8.15.1",
"@types/pngjs": "^6.0.4",
@@ -52,6 +52,6 @@
"vitest": "^3.0.0"
},
"volta": {
"node": "22.15.1"
"node": "22.16.0"
}
}

View File

@@ -1,4 +1,10 @@
import { AssetMediaResponseDto, AssetVisibility, LoginResponseDto, SharedLinkType } from '@immich/sdk';
import {
AssetMediaResponseDto,
AssetVisibility,
LoginResponseDto,
SharedLinkType,
TimeBucketAssetResponseDto,
} from '@immich/sdk';
import { DateTime } from 'luxon';
import { createUserDto } from 'src/fixtures';
import { errorDto } from 'src/responses';
@@ -19,7 +25,8 @@ describe('/timeline', () => {
let user: LoginResponseDto;
let timeBucketUser: LoginResponseDto;
let userAssets: AssetMediaResponseDto[];
let user1Assets: AssetMediaResponseDto[];
let user2Assets: AssetMediaResponseDto[];
beforeAll(async () => {
await utils.resetDatabase();
@@ -29,7 +36,7 @@ describe('/timeline', () => {
utils.userSetup(admin.accessToken, createUserDto.create('time-bucket')),
]);
userAssets = await Promise.all([
user1Assets = await Promise.all([
utils.createAsset(user.accessToken),
utils.createAsset(user.accessToken),
utils.createAsset(user.accessToken, {
@@ -42,12 +49,15 @@ describe('/timeline', () => {
utils.createAsset(user.accessToken),
]);
await Promise.all([
user2Assets = await Promise.all([
utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-01-01').toISOString() }),
utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-02-10').toISOString() }),
utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-02-11').toISOString() }),
utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-02-11').toISOString() }),
utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-02-12').toISOString() }),
]);
await utils.deleteAssets(timeBucketUser.accessToken, [user2Assets[4].id]);
});
describe('GET /timeline/buckets', () => {
@@ -74,7 +84,7 @@ describe('/timeline', () => {
it('should not allow access for unrelated shared links', async () => {
const sharedLink = await utils.createSharedLink(user.accessToken, {
type: SharedLinkType.Individual,
assetIds: userAssets.map(({ id }) => id),
assetIds: user1Assets.map(({ id }) => id),
});
const { status, body } = await request(app).get('/timeline/buckets').query({ key: sharedLink.key });
@@ -202,5 +212,17 @@ describe('/timeline', () => {
thumbhash: [],
});
});
it('should return time bucket in trash', async () => {
const { status, body } = await request(app)
.get('/timeline/bucket')
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
.query({ timeBucket: '1970-02-01T00:00:00.000Z', isTrashed: true });
expect(status).toBe(200);
const timeBucket: TimeBucketAssetResponseDto = body;
expect(timeBucket.isTrashed).toEqual([true]);
});
});
});

View File

@@ -301,10 +301,9 @@
"transcoding_encoding_options": "Encoding Options",
"transcoding_encoding_options_description": "Set codecs, resolution, quality and other options for the encoded videos",
"transcoding_hardware_acceleration": "Hardware Acceleration",
"transcoding_hardware_acceleration_description": "Experimental; much faster, but will have lower quality at the same bitrate",
"transcoding_hardware_acceleration_description": "Experimental: faster transcoding but may reduce quality at same bitrate",
"transcoding_hardware_decoding": "Hardware decoding",
"transcoding_hardware_decoding_setting_description": "Enables end-to-end acceleration instead of only accelerating encoding. May not work on all videos.",
"transcoding_hevc_codec": "HEVC codec",
"transcoding_max_b_frames": "Maximum B-frames",
"transcoding_max_b_frames_description": "Higher values improve compression efficiency, but slow down encoding. May not be compatible with hardware acceleration on older devices. 0 disables B-frames, while -1 sets this value automatically.",
"transcoding_max_bitrate": "Maximum bitrate",
@@ -662,6 +661,8 @@
"confirm_keep_this_delete_others": "All other assets in the stack will be deleted except for this asset. Are you sure you want to continue?",
"confirm_new_pin_code": "Confirm new PIN code",
"confirm_password": "Confirm password",
"confirm_tag_face": "Do you want to tag this face as {name}?",
"confirm_tag_face_unnamed": "Do you want to tag this face?",
"connected_to": "Connected to",
"contain": "Contain",
"context": "Context",
@@ -840,6 +841,7 @@
"error_delete_face": "Error deleting face from asset",
"error_loading_image": "Error loading image",
"error_saving_image": "Error: {error}",
"error_tag_face_bounding_box": "Error tagging face - cannot get bounding box coordinates",
"error_title": "Error - Something went wrong",
"errors": {
"cannot_navigate_next_asset": "Cannot navigate to the next asset",
@@ -978,7 +980,6 @@
"exif_bottom_sheet_location": "LOCATION",
"exif_bottom_sheet_people": "PEOPLE",
"exif_bottom_sheet_person_add_person": "Add name",
"exif_bottom_sheet_person_age": "Age {age}",
"exif_bottom_sheet_person_age_months": "Age {months} months",
"exif_bottom_sheet_person_age_year_months": "Age 1 year, {months} months",
"exif_bottom_sheet_person_age_years": "Age {years}",
@@ -1942,6 +1943,7 @@
"view_previous_asset": "View previous asset",
"view_qr_code": "View QR code",
"view_stack": "View Stack",
"view_user": "View User",
"viewer_remove_from_stack": "Remove from Stack",
"viewer_stack_use_as_main_asset": "Use as Main Asset",
"viewer_unstack": "Un-Stack",

View File

@@ -35,8 +35,8 @@ platform :android do
task: 'bundle',
build_type: 'Release',
properties: {
"android.injected.version.code" => 199,
"android.injected.version.name" => "1.133.1",
"android.injected.version.code" => 200,
"android.injected.version.name" => "1.134.0",
}
)
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')

View File

@@ -543,7 +543,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 205;
CURRENT_PROJECT_VERSION = 207;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_BITCODE = NO;
@@ -687,7 +687,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 205;
CURRENT_PROJECT_VERSION = 207;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_BITCODE = NO;
@@ -717,7 +717,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 205;
CURRENT_PROJECT_VERSION = 207;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_BITCODE = NO;
@@ -750,7 +750,7 @@
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 205;
CURRENT_PROJECT_VERSION = 207;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
@@ -794,7 +794,7 @@
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 205;
CURRENT_PROJECT_VERSION = 207;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
@@ -835,7 +835,7 @@
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 205;
CURRENT_PROJECT_VERSION = 207;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_USER_SCRIPT_SANDBOXING = YES;

View File

@@ -78,7 +78,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.132.3</string>
<string>1.133.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
@@ -93,7 +93,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>205</string>
<string>207</string>
<key>FLTEnableImpeller</key>
<true />
<key>ITSAppUsesNonExemptEncryption</key>

View File

@@ -22,7 +22,7 @@ platform :ios do
path: "./Runner.xcodeproj",
)
increment_version_number(
version_number: "1.133.1"
version_number: "1.134.0"
)
increment_build_number(
build_number: latest_testflight_build_number + 1,

View File

@@ -13,6 +13,7 @@ const Map<String, Locale> locales = {
'Czech (cs)': Locale('cs'),
'Danish (da)': Locale('da'),
'Dutch (nl)': Locale('nl'),
'Estonian (et)': Locale('et'),
'Finnish (fi)': Locale('fi'),
'French (fr)': Locale('fr'),
'Galician (gl)': Locale('gl'),

View File

@@ -169,7 +169,7 @@ class PhotoViewCoreState extends State<PhotoViewCore>
scale: newScale,
position: widget.enablePanAlways
? delta
: clampPosition(position: delta * details.scale),
: clampPosition(position: delta, scale: details.scale),
rotation:
widget.enableRotation ? _rotationBefore! + details.rotation : null,
rotationFocusPoint: widget.enableRotation ? details.focalPoint : null,

View File

@@ -3,7 +3,7 @@ Immich API
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
- API version: 1.133.1
- API version: 1.134.0
- Generator version: 7.8.0
- Build package: org.openapitools.codegen.languages.DartClientCodegen
@@ -192,6 +192,7 @@ Class | Method | HTTP request | Description
*ServerApi* | [**getStorage**](doc//ServerApi.md#getstorage) | **GET** /server/storage |
*ServerApi* | [**getSupportedMediaTypes**](doc//ServerApi.md#getsupportedmediatypes) | **GET** /server/media-types |
*ServerApi* | [**getTheme**](doc//ServerApi.md#gettheme) | **GET** /server/theme |
*ServerApi* | [**getVersionCheck**](doc//ServerApi.md#getversioncheck) | **GET** /server/version-check |
*ServerApi* | [**getVersionHistory**](doc//ServerApi.md#getversionhistory) | **GET** /server/version-history |
*ServerApi* | [**pingServer**](doc//ServerApi.md#pingserver) | **GET** /server/ping |
*ServerApi* | [**setServerLicense**](doc//ServerApi.md#setserverlicense) | **PUT** /server/license |
@@ -226,6 +227,7 @@ Class | Method | HTTP request | Description
*SystemConfigApi* | [**updateConfig**](doc//SystemConfigApi.md#updateconfig) | **PUT** /system-config |
*SystemMetadataApi* | [**getAdminOnboarding**](doc//SystemMetadataApi.md#getadminonboarding) | **GET** /system-metadata/admin-onboarding |
*SystemMetadataApi* | [**getReverseGeocodingState**](doc//SystemMetadataApi.md#getreversegeocodingstate) | **GET** /system-metadata/reverse-geocoding-state |
*SystemMetadataApi* | [**getVersionCheckState**](doc//SystemMetadataApi.md#getversioncheckstate) | **GET** /system-metadata/version-check-state |
*SystemMetadataApi* | [**updateAdminOnboarding**](doc//SystemMetadataApi.md#updateadminonboarding) | **POST** /system-metadata/admin-onboarding |
*TagsApi* | [**bulkTagAssets**](doc//TagsApi.md#bulktagassets) | **PUT** /tags/assets |
*TagsApi* | [**createTag**](doc//TagsApi.md#createtag) | **POST** /tags |
@@ -525,6 +527,7 @@ Class | Method | HTTP request | Description
- [ValidateLibraryDto](doc//ValidateLibraryDto.md)
- [ValidateLibraryImportPathResponseDto](doc//ValidateLibraryImportPathResponseDto.md)
- [ValidateLibraryResponseDto](doc//ValidateLibraryResponseDto.md)
- [VersionCheckStateResponseDto](doc//VersionCheckStateResponseDto.md)
- [VideoCodec](doc//VideoCodec.md)
- [VideoContainer](doc//VideoContainer.md)

View File

@@ -320,6 +320,7 @@ part 'model/validate_access_token_response_dto.dart';
part 'model/validate_library_dto.dart';
part 'model/validate_library_import_path_response_dto.dart';
part 'model/validate_library_response_dto.dart';
part 'model/version_check_state_response_dto.dart';
part 'model/video_codec.dart';
part 'model/video_container.dart';

View File

@@ -418,6 +418,47 @@ class ServerApi {
return null;
}
/// Performs an HTTP 'GET /server/version-check' operation and returns the [Response].
Future<Response> getVersionCheckWithHttpInfo() async {
// ignore: prefer_const_declarations
final apiPath = r'/server/version-check';
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>[];
return apiClient.invokeAPI(
apiPath,
'GET',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
Future<VersionCheckStateResponseDto?> getVersionCheck() async {
final response = await getVersionCheckWithHttpInfo();
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'VersionCheckStateResponseDto',) as VersionCheckStateResponseDto;
}
return null;
}
/// Performs an HTTP 'GET /server/version-history' operation and returns the [Response].
Future<Response> getVersionHistoryWithHttpInfo() async {
// ignore: prefer_const_declarations

View File

@@ -98,6 +98,47 @@ class SystemMetadataApi {
return null;
}
/// Performs an HTTP 'GET /system-metadata/version-check-state' operation and returns the [Response].
Future<Response> getVersionCheckStateWithHttpInfo() async {
// ignore: prefer_const_declarations
final apiPath = r'/system-metadata/version-check-state';
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>[];
return apiClient.invokeAPI(
apiPath,
'GET',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
Future<VersionCheckStateResponseDto?> getVersionCheckState() async {
final response = await getVersionCheckStateWithHttpInfo();
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'VersionCheckStateResponseDto',) as VersionCheckStateResponseDto;
}
return null;
}
/// Performs an HTTP 'POST /system-metadata/admin-onboarding' operation and returns the [Response].
/// Parameters:
///

View File

@@ -696,6 +696,8 @@ class ApiClient {
return ValidateLibraryImportPathResponseDto.fromJson(value);
case 'ValidateLibraryResponseDto':
return ValidateLibraryResponseDto.fromJson(value);
case 'VersionCheckStateResponseDto':
return VersionCheckStateResponseDto.fromJson(value);
case 'VideoCodec':
return VideoCodecTypeTransformer().decode(value);
case 'VideoContainer':

View File

@@ -0,0 +1,115 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class VersionCheckStateResponseDto {
/// Returns a new [VersionCheckStateResponseDto] instance.
VersionCheckStateResponseDto({
required this.checkedAt,
required this.releaseVersion,
});
String? checkedAt;
String? releaseVersion;
@override
bool operator ==(Object other) => identical(this, other) || other is VersionCheckStateResponseDto &&
other.checkedAt == checkedAt &&
other.releaseVersion == releaseVersion;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(checkedAt == null ? 0 : checkedAt!.hashCode) +
(releaseVersion == null ? 0 : releaseVersion!.hashCode);
@override
String toString() => 'VersionCheckStateResponseDto[checkedAt=$checkedAt, releaseVersion=$releaseVersion]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
if (this.checkedAt != null) {
json[r'checkedAt'] = this.checkedAt;
} else {
// json[r'checkedAt'] = null;
}
if (this.releaseVersion != null) {
json[r'releaseVersion'] = this.releaseVersion;
} else {
// json[r'releaseVersion'] = null;
}
return json;
}
/// Returns a new [VersionCheckStateResponseDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static VersionCheckStateResponseDto? fromJson(dynamic value) {
upgradeDto(value, "VersionCheckStateResponseDto");
if (value is Map) {
final json = value.cast<String, dynamic>();
return VersionCheckStateResponseDto(
checkedAt: mapValueOfType<String>(json, r'checkedAt'),
releaseVersion: mapValueOfType<String>(json, r'releaseVersion'),
);
}
return null;
}
static List<VersionCheckStateResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <VersionCheckStateResponseDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = VersionCheckStateResponseDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, VersionCheckStateResponseDto> mapFromJson(dynamic json) {
final map = <String, VersionCheckStateResponseDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = VersionCheckStateResponseDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of VersionCheckStateResponseDto-objects as value to a dart map
static Map<String, List<VersionCheckStateResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<VersionCheckStateResponseDto>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = VersionCheckStateResponseDto.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'checkedAt',
'releaseVersion',
};
}

View File

@@ -2,7 +2,7 @@ name: immich_mobile
description: Immich - selfhosted backup media file on mobile phone
publish_to: 'none'
version: 1.133.1+199
version: 1.134.0+200
environment:
sdk: '>=3.3.0 <4.0.0'

View File

@@ -5563,6 +5563,38 @@
]
}
},
"/server/version-check": {
"get": {
"operationId": "getVersionCheck",
"parameters": [],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/VersionCheckStateResponseDto"
}
}
},
"description": ""
}
},
"security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
}
],
"tags": [
"Server"
]
}
},
"/server/version-history": {
"get": {
"operationId": "getVersionHistory",
@@ -6846,6 +6878,38 @@
]
}
},
"/system-metadata/version-check-state": {
"get": {
"operationId": "getVersionCheckState",
"parameters": [],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/VersionCheckStateResponseDto"
}
}
},
"description": ""
}
},
"security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
}
],
"tags": [
"System Metadata"
]
}
},
"/tags": {
"get": {
"operationId": "getAllTags",
@@ -8132,7 +8196,7 @@
"info": {
"title": "Immich",
"description": "Immich API",
"version": "1.133.1",
"version": "1.134.0",
"contact": {}
},
"tags": [],
@@ -14939,6 +15003,23 @@
},
"type": "object"
},
"VersionCheckStateResponseDto": {
"properties": {
"checkedAt": {
"nullable": true,
"type": "string"
},
"releaseVersion": {
"nullable": true,
"type": "string"
}
},
"required": [
"checkedAt",
"releaseVersion"
],
"type": "object"
},
"VideoCodec": {
"enum": [
"h264",

View File

@@ -1 +1 @@
22.15.1
22.16.0

View File

@@ -1,18 +1,18 @@
{
"name": "@immich/sdk",
"version": "1.133.1",
"version": "1.134.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@immich/sdk",
"version": "1.133.1",
"version": "1.134.0",
"license": "GNU Affero General Public License version 3",
"dependencies": {
"@oazapfts/runtime": "^1.0.2"
},
"devDependencies": {
"@types/node": "^22.15.18",
"@types/node": "^22.15.21",
"typescript": "^5.3.3"
}
},
@@ -23,9 +23,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "22.15.19",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.19.tgz",
"integrity": "sha512-3vMNr4TzNQyjHcRZadojpRaD9Ofr6LsonZAoQ+HMUa/9ORTPoxVIw0e0mpqWpdjj8xybyCM+oKOUH2vwFu/oEw==",
"version": "22.15.21",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz",
"integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@immich/sdk",
"version": "1.133.1",
"version": "1.134.0",
"description": "Auto-generated TypeScript SDK for the Immich API",
"type": "module",
"main": "./build/index.js",
@@ -19,7 +19,7 @@
"@oazapfts/runtime": "^1.0.2"
},
"devDependencies": {
"@types/node": "^22.15.18",
"@types/node": "^22.15.21",
"typescript": "^5.3.3"
},
"repository": {
@@ -28,6 +28,6 @@
"directory": "open-api/typescript-sdk"
},
"volta": {
"node": "22.15.1"
"node": "22.16.0"
}
}

View File

@@ -1,6 +1,6 @@
/**
* Immich
* 1.133.1
* 1.134.0
* DO NOT MODIFY - This file has been generated using oazapfts.
* See https://www.npmjs.com/package/oazapfts
*/
@@ -1076,6 +1076,10 @@ export type ServerVersionResponseDto = {
minor: number;
patch: number;
};
export type VersionCheckStateResponseDto = {
checkedAt: string | null;
releaseVersion: string | null;
};
export type ServerVersionHistoryResponseDto = {
createdAt: string;
id: string;
@@ -2947,6 +2951,14 @@ export function getServerVersion(opts?: Oazapfts.RequestOpts) {
...opts
}));
}
export function getVersionCheck(opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
data: VersionCheckStateResponseDto;
}>("/server/version-check", {
...opts
}));
}
export function getVersionHistory(opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
@@ -3284,6 +3296,14 @@ export function getReverseGeocodingState(opts?: Oazapfts.RequestOpts) {
...opts
}));
}
export function getVersionCheckState(opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
data: VersionCheckStateResponseDto;
}>("/system-metadata/version-check-state", {
...opts
}));
}
export function getAllTags(opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;

View File

@@ -1 +1 @@
22.15.1
22.16.0

View File

@@ -26,7 +26,7 @@ COPY --from=dev /usr/src/app/node_modules/@img ./node_modules/@img
COPY --from=dev /usr/src/app/node_modules/exiftool-vendored.pl ./node_modules/exiftool-vendored.pl
# web build
FROM node:22.15.0-alpine3.20@sha256:686b8892b69879ef5bfd6047589666933508f9a5451c67320df3070ba0e9807b AS web
FROM node:22.16.0-alpine3.20@sha256:2289fb1fba0f4633b08ec47b94a89c7e20b829fc5679f9b7b298eaa2f1ed8b7e AS web
WORKDIR /usr/src/open-api/typescript-sdk
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./

529
server/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "immich",
"version": "1.133.1",
"version": "1.134.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "immich",
"version": "1.133.1",
"version": "1.134.0",
"hasInstallScript": true,
"license": "GNU Affero General Public License version 3",
"dependencies": {
@@ -64,7 +64,7 @@
"sanitize-filename": "^1.6.3",
"sanitize-html": "^2.14.0",
"semver": "^7.6.2",
"sharp": "^0.34.0",
"sharp": "^0.34.2",
"sirv": "^3.0.0",
"tailwindcss-preset-email": "^1.3.2",
"thumbhash": "^0.1.1",
@@ -92,7 +92,7 @@
"@types/lodash": "^4.14.197",
"@types/mock-fs": "^4.13.1",
"@types/multer": "^1.4.7",
"@types/node": "^22.15.18",
"@types/node": "^22.15.21",
"@types/nodemailer": "^6.4.14",
"@types/picomatch": "^4.0.0",
"@types/pngjs": "^6.0.5",
@@ -661,12 +661,12 @@
}
},
"node_modules/@babel/parser": {
"version": "7.27.2",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz",
"integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==",
"version": "7.27.3",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.3.tgz",
"integrity": "sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw==",
"license": "MIT",
"dependencies": {
"@babel/types": "^7.27.1"
"@babel/types": "^7.27.3"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -717,9 +717,9 @@
}
},
"node_modules/@babel/types": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz",
"integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==",
"version": "7.27.3",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz",
"integrity": "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==",
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
@@ -966,9 +966,9 @@
}
},
"node_modules/@eslint/core": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
"integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz",
"integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -1016,13 +1016,16 @@
}
},
"node_modules/@eslint/js": {
"version": "9.26.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.26.0.tgz",
"integrity": "sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==",
"version": "9.27.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz",
"integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://eslint.org/donate"
}
},
"node_modules/@eslint/object-schema": {
@@ -1036,13 +1039,13 @@
}
},
"node_modules/@eslint/plugin-kit": {
"version": "0.2.8",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz",
"integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==",
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz",
"integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^0.13.0",
"@eslint/core": "^0.14.0",
"levn": "^0.4.1"
},
"engines": {
@@ -1185,9 +1188,9 @@
}
},
"node_modules/@img/sharp-darwin-arm64": {
"version": "0.34.1",
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.1.tgz",
"integrity": "sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==",
"version": "0.34.2",
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz",
"integrity": "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==",
"cpu": [
"arm64"
],
@@ -1207,9 +1210,9 @@
}
},
"node_modules/@img/sharp-darwin-x64": {
"version": "0.34.1",
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.1.tgz",
"integrity": "sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==",
"version": "0.34.2",
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz",
"integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==",
"cpu": [
"x64"
],
@@ -1373,9 +1376,9 @@
}
},
"node_modules/@img/sharp-linux-arm": {
"version": "0.34.1",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.1.tgz",
"integrity": "sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==",
"version": "0.34.2",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz",
"integrity": "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==",
"cpu": [
"arm"
],
@@ -1395,9 +1398,9 @@
}
},
"node_modules/@img/sharp-linux-arm64": {
"version": "0.34.1",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.1.tgz",
"integrity": "sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==",
"version": "0.34.2",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz",
"integrity": "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==",
"cpu": [
"arm64"
],
@@ -1417,9 +1420,9 @@
}
},
"node_modules/@img/sharp-linux-s390x": {
"version": "0.34.1",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.1.tgz",
"integrity": "sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==",
"version": "0.34.2",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz",
"integrity": "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==",
"cpu": [
"s390x"
],
@@ -1439,9 +1442,9 @@
}
},
"node_modules/@img/sharp-linux-x64": {
"version": "0.34.1",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.1.tgz",
"integrity": "sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==",
"version": "0.34.2",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz",
"integrity": "sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==",
"cpu": [
"x64"
],
@@ -1461,9 +1464,9 @@
}
},
"node_modules/@img/sharp-linuxmusl-arm64": {
"version": "0.34.1",
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.1.tgz",
"integrity": "sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==",
"version": "0.34.2",
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz",
"integrity": "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==",
"cpu": [
"arm64"
],
@@ -1483,9 +1486,9 @@
}
},
"node_modules/@img/sharp-linuxmusl-x64": {
"version": "0.34.1",
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.1.tgz",
"integrity": "sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==",
"version": "0.34.2",
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz",
"integrity": "sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==",
"cpu": [
"x64"
],
@@ -1505,16 +1508,16 @@
}
},
"node_modules/@img/sharp-wasm32": {
"version": "0.34.1",
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.1.tgz",
"integrity": "sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==",
"version": "0.34.2",
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz",
"integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==",
"cpu": [
"wasm32"
],
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
"optional": true,
"dependencies": {
"@emnapi/runtime": "^1.4.0"
"@emnapi/runtime": "^1.4.3"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
@@ -1523,10 +1526,29 @@
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-win32-arm64": {
"version": "0.34.2",
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz",
"integrity": "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-win32-ia32": {
"version": "0.34.1",
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.1.tgz",
"integrity": "sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==",
"version": "0.34.2",
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz",
"integrity": "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==",
"cpu": [
"ia32"
],
@@ -1543,9 +1565,9 @@
}
},
"node_modules/@img/sharp-win32-x64": {
"version": "0.34.1",
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.1.tgz",
"integrity": "sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==",
"version": "0.34.2",
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz",
"integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==",
"cpu": [
"x64"
],
@@ -2118,28 +2140,6 @@
"integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==",
"license": "MIT"
},
"node_modules/@modelcontextprotocol/sdk": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.1.tgz",
"integrity": "sha512-9LfmxKTb1v+vUS1/emSk1f5ePmTLkb9Le9AxOB5T0XM59EUumwcS45z05h7aiZx3GI0Bl7mjb3FMEglYj+acuQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"content-type": "^1.0.5",
"cors": "^2.8.5",
"cross-spawn": "^7.0.3",
"eventsource": "^3.0.2",
"express": "^5.0.1",
"express-rate-limit": "^7.5.0",
"pkce-challenge": "^5.0.0",
"raw-body": "^3.0.0",
"zod": "^3.23.8",
"zod-to-json-schema": "^3.24.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz",
@@ -4867,9 +4867,9 @@
"license": "MIT"
},
"node_modules/@swc/core": {
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.11.24.tgz",
"integrity": "sha512-MaQEIpfcEMzx3VWWopbofKJvaraqmL6HbLlw2bFZ7qYqYw3rkhM0cQVEgyzbHtTWwCwPMFZSC2DUbhlZgrMfLg==",
"version": "1.11.29",
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.11.29.tgz",
"integrity": "sha512-g4mThMIpWbNhV8G2rWp5a5/Igv8/2UFRJx2yImrLGMgrDDYZIopqZ/z0jZxDgqNA1QDx93rpwNF7jGsxVWcMlA==",
"dev": true,
"hasInstallScript": true,
"license": "Apache-2.0",
@@ -4885,16 +4885,16 @@
"url": "https://opencollective.com/swc"
},
"optionalDependencies": {
"@swc/core-darwin-arm64": "1.11.24",
"@swc/core-darwin-x64": "1.11.24",
"@swc/core-linux-arm-gnueabihf": "1.11.24",
"@swc/core-linux-arm64-gnu": "1.11.24",
"@swc/core-linux-arm64-musl": "1.11.24",
"@swc/core-linux-x64-gnu": "1.11.24",
"@swc/core-linux-x64-musl": "1.11.24",
"@swc/core-win32-arm64-msvc": "1.11.24",
"@swc/core-win32-ia32-msvc": "1.11.24",
"@swc/core-win32-x64-msvc": "1.11.24"
"@swc/core-darwin-arm64": "1.11.29",
"@swc/core-darwin-x64": "1.11.29",
"@swc/core-linux-arm-gnueabihf": "1.11.29",
"@swc/core-linux-arm64-gnu": "1.11.29",
"@swc/core-linux-arm64-musl": "1.11.29",
"@swc/core-linux-x64-gnu": "1.11.29",
"@swc/core-linux-x64-musl": "1.11.29",
"@swc/core-win32-arm64-msvc": "1.11.29",
"@swc/core-win32-ia32-msvc": "1.11.29",
"@swc/core-win32-x64-msvc": "1.11.29"
},
"peerDependencies": {
"@swc/helpers": ">=0.5.17"
@@ -4906,9 +4906,9 @@
}
},
"node_modules/@swc/core-darwin-arm64": {
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.11.24.tgz",
"integrity": "sha512-dhtVj0PC1APOF4fl5qT2neGjRLgHAAYfiVP8poJelhzhB/318bO+QCFWAiimcDoyMgpCXOhTp757gnoJJrheWA==",
"version": "1.11.29",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.11.29.tgz",
"integrity": "sha512-whsCX7URzbuS5aET58c75Dloby3Gtj/ITk2vc4WW6pSDQKSPDuONsIcZ7B2ng8oz0K6ttbi4p3H/PNPQLJ4maQ==",
"cpu": [
"arm64"
],
@@ -4923,9 +4923,9 @@
}
},
"node_modules/@swc/core-darwin-x64": {
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.11.24.tgz",
"integrity": "sha512-H/3cPs8uxcj2Fe3SoLlofN5JG6Ny5bl8DuZ6Yc2wr7gQFBmyBkbZEz+sPVgsID7IXuz7vTP95kMm1VL74SO5AQ==",
"version": "1.11.29",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.11.29.tgz",
"integrity": "sha512-S3eTo/KYFk+76cWJRgX30hylN5XkSmjYtCBnM4jPLYn7L6zWYEPajsFLmruQEiTEDUg0gBEWLMNyUeghtswouw==",
"cpu": [
"x64"
],
@@ -4940,9 +4940,9 @@
}
},
"node_modules/@swc/core-linux-arm-gnueabihf": {
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.11.24.tgz",
"integrity": "sha512-PHJgWEpCsLo/NGj+A2lXZ2mgGjsr96ULNW3+T3Bj2KTc8XtMUkE8tmY2Da20ItZOvPNC/69KroU7edyo1Flfbw==",
"version": "1.11.29",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.11.29.tgz",
"integrity": "sha512-o9gdshbzkUMG6azldHdmKklcfrcMx+a23d/2qHQHPDLUPAN+Trd+sDQUYArK5Fcm7TlpG4sczz95ghN0DMkM7g==",
"cpu": [
"arm"
],
@@ -4957,9 +4957,9 @@
}
},
"node_modules/@swc/core-linux-arm64-gnu": {
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.11.24.tgz",
"integrity": "sha512-C2FJb08+n5SD4CYWCTZx1uR88BN41ZieoHvI8A55hfVf2woT8+6ZiBzt74qW2g+ntZ535Jts5VwXAKdu41HpBg==",
"version": "1.11.29",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.11.29.tgz",
"integrity": "sha512-sLoaciOgUKQF1KX9T6hPGzvhOQaJn+3DHy4LOHeXhQqvBgr+7QcZ+hl4uixPKTzxk6hy6Hb0QOvQEdBAAR1gXw==",
"cpu": [
"arm64"
],
@@ -4974,9 +4974,9 @@
}
},
"node_modules/@swc/core-linux-arm64-musl": {
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.11.24.tgz",
"integrity": "sha512-ypXLIdszRo0re7PNNaXN0+2lD454G8l9LPK/rbfRXnhLWDBPURxzKlLlU/YGd2zP98wPcVooMmegRSNOKfvErw==",
"version": "1.11.29",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.11.29.tgz",
"integrity": "sha512-PwjB10BC0N+Ce7RU/L23eYch6lXFHz7r3NFavIcwDNa/AAqywfxyxh13OeRy+P0cg7NDpWEETWspXeI4Ek8otw==",
"cpu": [
"arm64"
],
@@ -4991,9 +4991,9 @@
}
},
"node_modules/@swc/core-linux-x64-gnu": {
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.11.24.tgz",
"integrity": "sha512-IM7d+STVZD48zxcgo69L0yYptfhaaE9cMZ+9OoMxirNafhKKXwoZuufol1+alEFKc+Wbwp+aUPe/DeWC/Lh3dg==",
"version": "1.11.29",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.11.29.tgz",
"integrity": "sha512-i62vBVoPaVe9A3mc6gJG07n0/e7FVeAvdD9uzZTtGLiuIfVfIBta8EMquzvf+POLycSk79Z6lRhGPZPJPYiQaA==",
"cpu": [
"x64"
],
@@ -5008,9 +5008,9 @@
}
},
"node_modules/@swc/core-linux-x64-musl": {
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.11.24.tgz",
"integrity": "sha512-DZByJaMVzSfjQKKQn3cqSeqwy6lpMaQDQQ4HPlch9FWtDx/dLcpdIhxssqZXcR2rhaQVIaRQsCqwV6orSDGAGw==",
"version": "1.11.29",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.11.29.tgz",
"integrity": "sha512-YER0XU1xqFdK0hKkfSVX1YIyCvMDI7K07GIpefPvcfyNGs38AXKhb2byySDjbVxkdl4dycaxxhRyhQ2gKSlsFQ==",
"cpu": [
"x64"
],
@@ -5025,9 +5025,9 @@
}
},
"node_modules/@swc/core-win32-arm64-msvc": {
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.11.24.tgz",
"integrity": "sha512-Q64Ytn23y9aVDKN5iryFi8mRgyHw3/kyjTjT4qFCa8AEb5sGUuSj//AUZ6c0J7hQKMHlg9do5Etvoe61V98/JQ==",
"version": "1.11.29",
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.11.29.tgz",
"integrity": "sha512-po+WHw+k9g6FAg5IJ+sMwtA/fIUL3zPQ4m/uJgONBATCVnDDkyW6dBA49uHNVtSEvjvhuD8DVWdFP847YTcITw==",
"cpu": [
"arm64"
],
@@ -5042,9 +5042,9 @@
}
},
"node_modules/@swc/core-win32-ia32-msvc": {
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.11.24.tgz",
"integrity": "sha512-9pKLIisE/Hh2vJhGIPvSoTK4uBSPxNVyXHmOrtdDot4E1FUUI74Vi8tFdlwNbaj8/vusVnb8xPXsxF1uB0VgiQ==",
"version": "1.11.29",
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.11.29.tgz",
"integrity": "sha512-h+NjOrbqdRBYr5ItmStmQt6x3tnhqgwbj9YxdGPepbTDamFv7vFnhZR0YfB3jz3UKJ8H3uGJ65Zw1VsC+xpFkg==",
"cpu": [
"ia32"
],
@@ -5059,9 +5059,9 @@
}
},
"node_modules/@swc/core-win32-x64-msvc": {
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.11.24.tgz",
"integrity": "sha512-sybnXtOsdB+XvzVFlBVGgRHLqp3yRpHK7CrmpuDKszhj/QhmsaZzY/GHSeALlMtLup13M0gqbcQvsTNlAHTg3w==",
"version": "1.11.29",
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.11.29.tgz",
"integrity": "sha512-Q8cs2BDV9wqDvqobkXOYdC+pLUSEpX/KvI0Dgfun1F+LzuLotRFuDhrvkU9ETJA6OnD2+Fn/ieHgloiKA/Mn/g==",
"cpu": [
"x64"
],
@@ -5104,23 +5104,23 @@
}
},
"node_modules/@testcontainers/postgresql": {
"version": "10.26.0",
"resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-10.26.0.tgz",
"integrity": "sha512-xc5ahJ7HcYayMuyl5lmAdx/9CddBpbdmaSWhPtxRWel3t3EoMGR/7tk8uynTq+mZeYHZFXaudy/x3etsWwM2mQ==",
"version": "10.27.0",
"resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-10.27.0.tgz",
"integrity": "sha512-xJMhH68GVl1wE+FifIgKElZqArBeagat4qFh0etN/bvR3FC0mdHWKkzzAToNficSNXxutN5UarYlSSt0YLMMbQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"testcontainers": "^10.26.0"
"testcontainers": "^10.27.0"
}
},
"node_modules/@testcontainers/redis": {
"version": "10.26.0",
"resolved": "https://registry.npmjs.org/@testcontainers/redis/-/redis-10.26.0.tgz",
"integrity": "sha512-eDX421zhoePqbVpNMhyxw7IAK65VdIp0rrxdREfZm6epkVSv8lRlWtLoet7Hxm3Jak+sfLH5TvVDML7cLxfcAg==",
"version": "10.27.0",
"resolved": "https://registry.npmjs.org/@testcontainers/redis/-/redis-10.27.0.tgz",
"integrity": "sha512-jxiGM9kcUFU+2EnoeLQX8WniCVyxPoKhQml+SqnLbTxZ7S59Ih7Ccur/U/CDRhJ/9cTAkdZzURzuuMqY2u8UjA==",
"dev": true,
"license": "MIT",
"dependencies": {
"testcontainers": "^10.26.0"
"testcontainers": "^10.27.0"
}
},
"node_modules/@tokenizer/inflate": {
@@ -5244,13 +5244,14 @@
}
},
"node_modules/@types/compression": {
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.5.tgz",
"integrity": "sha512-AAQvK5pxMpaT+nDvhHrsBhLSYG5yQdtkaJE1WYieSNY2mVFKAgmU4ks65rkZD5oqnGCFLyQpUr1CqI4DmUMyDg==",
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.8.0.tgz",
"integrity": "sha512-g4vmPIwbTii9dX1HVioHbOolubEaf4re4vDxuzpKrzz9uI7uarBExi9begX0cXyIB85jXZ5X2A/v8rsHZxSAPw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/express": "*"
"@types/express": "*",
"@types/node": "*"
}
},
"node_modules/@types/connect": {
@@ -5341,9 +5342,9 @@
"license": "MIT"
},
"node_modules/@types/express": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
"integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
"version": "4.17.22",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.22.tgz",
"integrity": "sha512-eZUmSnhRX9YRSkplpz0N+k6NljUUn5l3EWZIKZvYzhvMphEuNiyyy1viH/ejgt66JWgALwC/gtSUAeQKtSwW/w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5415,9 +5416,9 @@
"license": "MIT"
},
"node_modules/@types/lodash": {
"version": "4.17.16",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz",
"integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==",
"version": "4.17.17",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.17.tgz",
"integrity": "sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ==",
"dev": true,
"license": "MIT"
},
@@ -5480,9 +5481,9 @@
}
},
"node_modules/@types/node": {
"version": "22.15.19",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.19.tgz",
"integrity": "sha512-3vMNr4TzNQyjHcRZadojpRaD9Ofr6LsonZAoQ+HMUa/9ORTPoxVIw0e0mpqWpdjj8xybyCM+oKOUH2vwFu/oEw==",
"version": "22.15.21",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz",
"integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
@@ -5576,9 +5577,9 @@
"license": "MIT"
},
"node_modules/@types/react": {
"version": "19.1.4",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.4.tgz",
"integrity": "sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g==",
"version": "19.1.5",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.5.tgz",
"integrity": "sha512-piErsCVVbpMMT2r7wbawdZsq4xMvIAhQuac2gedQHysu1TZYEigE6pnFfgZT+/jQnrRuF5r+SHzuehFjfRjr4g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5951,9 +5952,9 @@
}
},
"node_modules/@vitest/coverage-v8": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.3.tgz",
"integrity": "sha512-cj76U5gXCl3g88KSnf80kof6+6w+K4BjOflCl7t6yRJPDuCrHtVu0SgNYOUARJOL5TI8RScDbm5x4s1/P9bvpw==",
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.4.tgz",
"integrity": "sha512-G4p6OtioySL+hPV7Y6JHlhpsODbJzt1ndwHAFkyk6vVjpK03PFsKnauZIzcd0PrK4zAbc5lc+jeZ+eNGiMA+iw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5974,8 +5975,8 @@
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"@vitest/browser": "3.1.3",
"vitest": "3.1.3"
"@vitest/browser": "3.1.4",
"vitest": "3.1.4"
},
"peerDependenciesMeta": {
"@vitest/browser": {
@@ -5984,14 +5985,14 @@
}
},
"node_modules/@vitest/expect": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.3.tgz",
"integrity": "sha512-7FTQQuuLKmN1Ig/h+h/GO+44Q1IlglPlR2es4ab7Yvfx+Uk5xsv+Ykk+MEt/M2Yn/xGmzaLKxGw2lgy2bwuYqg==",
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.4.tgz",
"integrity": "sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/spy": "3.1.3",
"@vitest/utils": "3.1.3",
"@vitest/spy": "3.1.4",
"@vitest/utils": "3.1.4",
"chai": "^5.2.0",
"tinyrainbow": "^2.0.0"
},
@@ -6000,13 +6001,13 @@
}
},
"node_modules/@vitest/mocker": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.3.tgz",
"integrity": "sha512-PJbLjonJK82uCWHjzgBJZuR7zmAOrSvKk1QBxrennDIgtH4uK0TB1PvYmc0XBCigxxtiAVPfWtAdy4lpz8SQGQ==",
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.4.tgz",
"integrity": "sha512-8IJ3CvwtSw/EFXqWFL8aCMu+YyYXG2WUSrQbViOZkWTKTVicVwZ/YiEZDSqD00kX+v/+W+OnxhNWoeVKorHygA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/spy": "3.1.3",
"@vitest/spy": "3.1.4",
"estree-walker": "^3.0.3",
"magic-string": "^0.30.17"
},
@@ -6037,9 +6038,9 @@
}
},
"node_modules/@vitest/pretty-format": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.3.tgz",
"integrity": "sha512-i6FDiBeJUGLDKADw2Gb01UtUNb12yyXAqC/mmRWuYl+m/U9GS7s8us5ONmGkGpUUo7/iAYzI2ePVfOZTYvUifA==",
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.4.tgz",
"integrity": "sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6050,13 +6051,13 @@
}
},
"node_modules/@vitest/runner": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.3.tgz",
"integrity": "sha512-Tae+ogtlNfFei5DggOsSUvkIaSuVywujMj6HzR97AHK6XK8i3BuVyIifWAm/sE3a15lF5RH9yQIrbXYuo0IFyA==",
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.4.tgz",
"integrity": "sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/utils": "3.1.3",
"@vitest/utils": "3.1.4",
"pathe": "^2.0.3"
},
"funding": {
@@ -6064,13 +6065,13 @@
}
},
"node_modules/@vitest/snapshot": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.3.tgz",
"integrity": "sha512-XVa5OPNTYUsyqG9skuUkFzAeFnEzDp8hQu7kZ0N25B1+6KjGm4hWLtURyBbsIAOekfWQ7Wuz/N/XXzgYO3deWQ==",
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.4.tgz",
"integrity": "sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "3.1.3",
"@vitest/pretty-format": "3.1.4",
"magic-string": "^0.30.17",
"pathe": "^2.0.3"
},
@@ -6079,9 +6080,9 @@
}
},
"node_modules/@vitest/spy": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.3.tgz",
"integrity": "sha512-x6w+ctOEmEXdWaa6TO4ilb7l9DxPR5bwEb6hILKuxfU1NqWT2mpJD9NJN7t3OTfxmVlOMrvtoFJGdgyzZ605lQ==",
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.4.tgz",
"integrity": "sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6092,13 +6093,13 @@
}
},
"node_modules/@vitest/utils": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.3.tgz",
"integrity": "sha512-2Ltrpht4OmHO9+c/nmHtF09HWiyWdworqnHIwjfvDyWjuwKbdkcS9AnhsDn+8E2RM4x++foD1/tNuLPVvWG1Rg==",
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.4.tgz",
"integrity": "sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "3.1.3",
"@vitest/pretty-format": "3.1.4",
"loupe": "^3.1.3",
"tinyrainbow": "^2.0.0"
},
@@ -7104,9 +7105,9 @@
}
},
"node_modules/bullmq": {
"version": "5.52.2",
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.52.2.tgz",
"integrity": "sha512-fK/dKIv8ymyys4K+zeNEPA+yuYWzRPmBWUmwIMz8DvYekadl8VG19yUx94Na0n0cLAi+spdn3a/+ufkYK7CBUg==",
"version": "5.53.0",
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.53.0.tgz",
"integrity": "sha512-AbzcwR+9GdgrenolOC9kApF+TkUKZpUCMiFbXgRYw9ivWhOfLCqKeajIptM7NdwhY7cpXgv+QpbweUuQZUxkyA==",
"license": "MIT",
"dependencies": {
"cron-parser": "^4.9.0",
@@ -8393,9 +8394,9 @@
"license": "MIT"
},
"node_modules/detect-libc": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
"integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
"integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
"license": "Apache-2.0",
"engines": {
"node": ">=8"
@@ -8971,9 +8972,9 @@
}
},
"node_modules/eslint": {
"version": "9.26.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.26.0.tgz",
"integrity": "sha512-Hx0MOjPh6uK9oq9nVsATZKE/Wlbai7KFjfCuw9UHaguDW3x+HF0O5nIi3ud39TWgrTjTO5nHxmL3R1eANinWHQ==",
"version": "9.27.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz",
"integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8981,14 +8982,13 @@
"@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.20.0",
"@eslint/config-helpers": "^0.2.1",
"@eslint/core": "^0.13.0",
"@eslint/core": "^0.14.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.26.0",
"@eslint/plugin-kit": "^0.2.8",
"@eslint/js": "9.27.0",
"@eslint/plugin-kit": "^0.3.1",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
"@modelcontextprotocol/sdk": "^1.8.0",
"@types/estree": "^1.0.6",
"@types/json-schema": "^7.0.15",
"ajv": "^6.12.4",
@@ -9012,8 +9012,7 @@
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
"optionator": "^0.9.3",
"zod": "^3.24.2"
"optionator": "^0.9.3"
},
"bin": {
"eslint": "bin/eslint.js"
@@ -9288,29 +9287,6 @@
"node": ">=0.8.x"
}
},
"node_modules/eventsource": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
"integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
"dev": true,
"license": "MIT",
"dependencies": {
"eventsource-parser": "^3.0.1"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/eventsource-parser": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.1.tgz",
"integrity": "sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/exiftool-vendored": {
"version": "28.8.0",
"resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-28.8.0.tgz",
@@ -9397,22 +9373,6 @@
"url": "https://opencollective.com/express"
}
},
"node_modules/express-rate-limit": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz",
"integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/express-rate-limit"
},
"peerDependencies": {
"express": "^4.11 || 5 || ^5.0.0-beta.1"
}
},
"node_modules/express/node_modules/cookie": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
@@ -13503,16 +13463,6 @@
"node": ">= 6"
}
},
"node_modules/pkce-challenge": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz",
"integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/pluralize": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
@@ -15012,15 +14962,15 @@
"license": "MIT"
},
"node_modules/sharp": {
"version": "0.34.1",
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.1.tgz",
"integrity": "sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==",
"version": "0.34.2",
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.2.tgz",
"integrity": "sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"color": "^4.2.3",
"detect-libc": "^2.0.3",
"semver": "^7.7.1"
"detect-libc": "^2.0.4",
"semver": "^7.7.2"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
@@ -15029,8 +14979,8 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-darwin-arm64": "0.34.1",
"@img/sharp-darwin-x64": "0.34.1",
"@img/sharp-darwin-arm64": "0.34.2",
"@img/sharp-darwin-x64": "0.34.2",
"@img/sharp-libvips-darwin-arm64": "1.1.0",
"@img/sharp-libvips-darwin-x64": "1.1.0",
"@img/sharp-libvips-linux-arm": "1.1.0",
@@ -15040,15 +14990,16 @@
"@img/sharp-libvips-linux-x64": "1.1.0",
"@img/sharp-libvips-linuxmusl-arm64": "1.1.0",
"@img/sharp-libvips-linuxmusl-x64": "1.1.0",
"@img/sharp-linux-arm": "0.34.1",
"@img/sharp-linux-arm64": "0.34.1",
"@img/sharp-linux-s390x": "0.34.1",
"@img/sharp-linux-x64": "0.34.1",
"@img/sharp-linuxmusl-arm64": "0.34.1",
"@img/sharp-linuxmusl-x64": "0.34.1",
"@img/sharp-wasm32": "0.34.1",
"@img/sharp-win32-ia32": "0.34.1",
"@img/sharp-win32-x64": "0.34.1"
"@img/sharp-linux-arm": "0.34.2",
"@img/sharp-linux-arm64": "0.34.2",
"@img/sharp-linux-s390x": "0.34.2",
"@img/sharp-linux-x64": "0.34.2",
"@img/sharp-linuxmusl-arm64": "0.34.2",
"@img/sharp-linuxmusl-x64": "0.34.2",
"@img/sharp-wasm32": "0.34.2",
"@img/sharp-win32-arm64": "0.34.2",
"@img/sharp-win32-ia32": "0.34.2",
"@img/sharp-win32-x64": "0.34.2"
}
},
"node_modules/shebang-command": {
@@ -15489,9 +15440,9 @@
"license": "BSD-3-Clause"
},
"node_modules/sql-formatter": {
"version": "15.6.1",
"resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.6.1.tgz",
"integrity": "sha512-uoKbRLVbjzwa8ouY4lI9YM387zRxDv9Gg5kZBzu2iNls2wVBlDLshhudCstczddRvj7J+xOpHTTWX6Z0lRgYGA==",
"version": "15.6.2",
"resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.6.2.tgz",
"integrity": "sha512-ZjqOfJGuB97UeHzTJoTbadlM0h9ynehtSTHNUbGfXR4HZ4rCIoD2oIW91W+A5oE76k8hl0Uz5GD8Sx3Pt9Xa3w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -16477,9 +16428,9 @@
}
},
"node_modules/testcontainers": {
"version": "10.26.0",
"resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.26.0.tgz",
"integrity": "sha512-4Iv3KB23pZcnxnXg4eIlOpXuvx4aHexxLY6URiciRQrc3Dao09NsVoYGxD8sV9heKUZ107mecalDITx4NZHo7A==",
"version": "10.27.0",
"resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.27.0.tgz",
"integrity": "sha512-Y1A2OerjRKUDZ00tAbn1hHHHVeEH+jRgg7a41AF6mLUZPlBIkL8stSQkEzziFp1IK3Id9hPKu7Iq7rIpavkYaw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -16497,7 +16448,7 @@
"ssh-remote-port-forward": "^1.0.4",
"tar-fs": "^3.0.7",
"tmp": "^0.2.3",
"undici": "^5.28.5"
"undici": "^5.29.0"
}
},
"node_modules/testcontainers/node_modules/tmp": {
@@ -17519,9 +17470,9 @@
}
},
"node_modules/vite-node": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.3.tgz",
"integrity": "sha512-uHV4plJ2IxCl4u1up1FQRrqclylKAogbtBfOTwcuJ28xFi+89PZ57BRh+naIRvH70HPwxy5QHYzg1OrEaC7AbA==",
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.4.tgz",
"integrity": "sha512-6enNwYnpyDo4hEgytbmc6mYWHXDHYEn0D1/rw4Q+tnHUGtKTJsn8T1YkX6Q18wI5LCrS8CTYlBaiCqxOy2kvUA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -17649,19 +17600,19 @@
}
},
"node_modules/vitest": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.3.tgz",
"integrity": "sha512-188iM4hAHQ0km23TN/adso1q5hhwKqUpv+Sd6p5sOuh6FhQnRNW3IsiIpvxqahtBabsJ2SLZgmGSpcYK4wQYJw==",
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.4.tgz",
"integrity": "sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/expect": "3.1.3",
"@vitest/mocker": "3.1.3",
"@vitest/pretty-format": "^3.1.3",
"@vitest/runner": "3.1.3",
"@vitest/snapshot": "3.1.3",
"@vitest/spy": "3.1.3",
"@vitest/utils": "3.1.3",
"@vitest/expect": "3.1.4",
"@vitest/mocker": "3.1.4",
"@vitest/pretty-format": "^3.1.4",
"@vitest/runner": "3.1.4",
"@vitest/snapshot": "3.1.4",
"@vitest/spy": "3.1.4",
"@vitest/utils": "3.1.4",
"chai": "^5.2.0",
"debug": "^4.4.0",
"expect-type": "^1.2.1",
@@ -17674,7 +17625,7 @@
"tinypool": "^1.0.2",
"tinyrainbow": "^2.0.0",
"vite": "^5.0.0 || ^6.0.0",
"vite-node": "3.1.3",
"vite-node": "3.1.4",
"why-is-node-running": "^2.3.0"
},
"bin": {
@@ -17690,8 +17641,8 @@
"@edge-runtime/vm": "*",
"@types/debug": "^4.1.12",
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
"@vitest/browser": "3.1.3",
"@vitest/ui": "3.1.3",
"@vitest/browser": "3.1.4",
"@vitest/ui": "3.1.4",
"happy-dom": "*",
"jsdom": "*"
},
@@ -18267,26 +18218,6 @@
"engines": {
"node": ">= 14"
}
},
"node_modules/zod": {
"version": "3.24.4",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.4.tgz",
"integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
},
"node_modules/zod-to-json-schema": {
"version": "3.24.5",
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz",
"integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==",
"dev": true,
"license": "ISC",
"peerDependencies": {
"zod": "^3.24.1"
}
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "immich",
"version": "1.133.1",
"version": "1.134.0",
"description": "",
"author": "",
"private": true,
@@ -90,7 +90,7 @@
"sanitize-filename": "^1.6.3",
"sanitize-html": "^2.14.0",
"semver": "^7.6.2",
"sharp": "^0.34.0",
"sharp": "^0.34.2",
"sirv": "^3.0.0",
"tailwindcss-preset-email": "^1.3.2",
"thumbhash": "^0.1.1",
@@ -118,7 +118,7 @@
"@types/lodash": "^4.14.197",
"@types/mock-fs": "^4.13.1",
"@types/multer": "^1.4.7",
"@types/node": "^22.15.18",
"@types/node": "^22.15.21",
"@types/nodemailer": "^6.4.14",
"@types/picomatch": "^4.0.0",
"@types/pngjs": "^6.0.5",
@@ -155,9 +155,9 @@
"vitest": "^3.0.0"
},
"volta": {
"node": "22.15.1"
"node": "22.16.0"
},
"overrides": {
"sharp": "^0.34.0"
"sharp": "^0.34.2"
}
}

View File

@@ -1,5 +1,6 @@
import { ServerController } from 'src/controllers/server.controller';
import { ServerService } from 'src/services/server.service';
import { SystemMetadataService } from 'src/services/system-metadata.service';
import { VersionService } from 'src/services/version.service';
import request from 'supertest';
import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils';
@@ -7,11 +8,13 @@ import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'
describe(ServerController.name, () => {
let ctx: ControllerContext;
const serverService = mockBaseService(ServerService);
const systemMetadataService = mockBaseService(SystemMetadataService);
const versionService = mockBaseService(VersionService);
beforeAll(async () => {
ctx = await controllerSetup(ServerController, [
{ provide: ServerService, useValue: serverService },
{ provide: SystemMetadataService, useValue: systemMetadataService },
{ provide: VersionService, useValue: versionService },
]);
return () => ctx.close();

View File

@@ -13,8 +13,10 @@ import {
ServerVersionHistoryResponseDto,
ServerVersionResponseDto,
} from 'src/dtos/server.dto';
import { VersionCheckStateResponseDto } from 'src/dtos/system-metadata.dto';
import { Authenticated } from 'src/middleware/auth.guard';
import { ServerService } from 'src/services/server.service';
import { SystemMetadataService } from 'src/services/system-metadata.service';
import { VersionService } from 'src/services/version.service';
@ApiTags('Server')
@@ -22,6 +24,7 @@ import { VersionService } from 'src/services/version.service';
export class ServerController {
constructor(
private service: ServerService,
private systemMetadataService: SystemMetadataService,
private versionService: VersionService,
) {}
@@ -96,4 +99,10 @@ export class ServerController {
getServerLicense(): Promise<LicenseResponseDto> {
return this.service.getLicense();
}
@Get('version-check')
@Authenticated()
getVersionCheck(): Promise<VersionCheckStateResponseDto> {
return this.systemMetadataService.getVersionCheckState();
}
}

View File

@@ -1,6 +1,10 @@
import { Body, Controller, Get, HttpCode, HttpStatus, Post } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { AdminOnboardingUpdateDto, ReverseGeocodingStateResponseDto } from 'src/dtos/system-metadata.dto';
import {
AdminOnboardingUpdateDto,
ReverseGeocodingStateResponseDto,
VersionCheckStateResponseDto,
} from 'src/dtos/system-metadata.dto';
import { Permission } from 'src/enum';
import { Authenticated } from 'src/middleware/auth.guard';
import { SystemMetadataService } from 'src/services/system-metadata.service';
@@ -28,4 +32,10 @@ export class SystemMetadataController {
getReverseGeocodingState(): Promise<ReverseGeocodingStateResponseDto> {
return this.service.getReverseGeocodingState();
}
@Get('version-check-state')
@Authenticated({ permission: Permission.SYSTEM_METADATA_READ, admin: true })
getVersionCheckState(): Promise<VersionCheckStateResponseDto> {
return this.service.getVersionCheckState();
}
}

View File

@@ -13,3 +13,8 @@ export class ReverseGeocodingStateResponseDto {
lastUpdate!: string | null;
lastImportFileName!: string | null;
}
export class VersionCheckStateResponseDto {
checkedAt!: string | null;
releaseVersion!: string | null;
}

View File

@@ -264,7 +264,7 @@ with
"assets"."visibility",
"assets"."isFavorite",
assets.type = 'IMAGE' as "isImage",
assets."deletedAt" is null as "isTrashed",
assets."deletedAt" is not null as "isTrashed",
"assets"."livePhotoVideoId",
"assets"."localDateTime",
"assets"."ownerId",

View File

@@ -15,6 +15,7 @@ select
inner join "memories_assets_assets" on "assets"."id" = "memories_assets_assets"."assetsId"
where
"memories_assets_assets"."memoriesId" = "memories"."id"
and "assets"."visibility" = 'timeline'
and "assets"."deletedAt" is null
order by
"assets"."fileCreatedAt" asc
@@ -43,6 +44,7 @@ select
inner join "memories_assets_assets" on "assets"."id" = "memories_assets_assets"."assetsId"
where
"memories_assets_assets"."memoriesId" = "memories"."id"
and "assets"."visibility" = 'timeline'
and "assets"."deletedAt" is null
order by
"assets"."fileCreatedAt" asc
@@ -79,6 +81,7 @@ select
inner join "memories_assets_assets" on "assets"."id" = "memories_assets_assets"."assetsId"
where
"memories_assets_assets"."memoriesId" = "memories"."id"
and "assets"."visibility" = 'timeline'
and "assets"."deletedAt" is null
order by
"assets"."fileCreatedAt" asc
@@ -111,6 +114,7 @@ select
inner join "memories_assets_assets" on "assets"."id" = "memories_assets_assets"."assetsId"
where
"memories_assets_assets"."memoriesId" = "memories"."id"
and "assets"."visibility" = 'timeline'
and "assets"."deletedAt" is null
order by
"assets"."fileCreatedAt" asc

View File

@@ -595,7 +595,7 @@ export class AssetRepository {
'assets.visibility',
'assets.isFavorite',
sql`assets.type = 'IMAGE'`.as('isImage'),
sql`assets."deletedAt" is null`.as('isTrashed'),
sql`assets."deletedAt" is not null`.as('isTrashed'),
'assets.livePhotoVideoId',
'assets.localDateTime',
'assets.ownerId',

View File

@@ -118,7 +118,7 @@ export class DatabaseRepository {
this.logger.log(`Creating ${EXTENSION_NAMES[extension]} extension`);
await sql`CREATE EXTENSION IF NOT EXISTS ${sql.raw(extension)} CASCADE`.execute(this.db);
if (extension === DatabaseExtension.VECTORCHORD) {
const dbName = sql.table(await this.getDatabaseName());
const dbName = sql.id(await this.getDatabaseName());
await sql`ALTER DATABASE ${dbName} SET vchordrq.prewarm_dim = '512,640,768,1024,1152,1536'`.execute(this.db);
await sql`SET vchordrq.prewarm_dim = '512,640,768,1024,1152,1536'`.execute(this.db);
await sql`ALTER DATABASE ${dbName} SET vchordrq.probes = 1`.execute(this.db);
@@ -247,7 +247,10 @@ export class DatabaseRepository {
return;
}
const dimSize = await this.getDimensionSize(table);
await sql`DROP INDEX IF EXISTS ${sql.raw(indexName)}`.execute(this.db);
await this.db.schema.dropIndex(indexName).ifExists().execute();
if (table === 'smart_search') {
await this.db.schema.alterTable(table).dropConstraint('dim_size_constraint').ifExists().execute();
}
await this.db.transaction().execute(async (tx) => {
if (!rows.some((row) => row.columnName === 'embedding')) {
this.logger.warn(`Column 'embedding' does not exist in table '${table}', truncating and adding column.`);

View File

@@ -1,18 +1,26 @@
import { Injectable } from '@nestjs/common';
import { Insertable, Kysely, Updateable } from 'kysely';
import { Insertable, Kysely, sql, Updateable } from 'kysely';
import { jsonArrayFrom } from 'kysely/helpers/postgres';
import { DateTime } from 'luxon';
import { InjectKysely } from 'nestjs-kysely';
import { DB, Memories } from 'src/db';
import { Chunked, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
import { MemorySearchDto } from 'src/dtos/memory.dto';
import { AssetVisibility } from 'src/enum';
import { IBulkAsset } from 'src/types';
@Injectable()
export class MemoryRepository implements IBulkAsset {
constructor(@InjectKysely() private db: Kysely<DB>) {}
cleanup() {
async cleanup() {
await this.db
.deleteFrom('memories_assets_assets')
.using('assets')
.whereRef('memories_assets_assets.assetsId', '=', 'assets.id')
.where('assets.visibility', '!=', AssetVisibility.TIMELINE)
.execute();
return this.db
.deleteFrom('memories')
.where('createdAt', '<', DateTime.now().minus({ days: 30 }).toJSDate())
@@ -36,6 +44,7 @@ export class MemoryRepository implements IBulkAsset {
.innerJoin('memories_assets_assets', 'assets.id', 'memories_assets_assets.assetsId')
.whereRef('memories_assets_assets.memoriesId', '=', 'memories.id')
.orderBy('assets.fileCreatedAt', 'asc')
.where('assets.visibility', '=', sql.lit(AssetVisibility.TIMELINE))
.where('assets.deletedAt', 'is', null),
).as('assets'),
)
@@ -138,6 +147,7 @@ export class MemoryRepository implements IBulkAsset {
.innerJoin('memories_assets_assets', 'assets.id', 'memories_assets_assets.assetsId')
.whereRef('memories_assets_assets.memoriesId', '=', 'memories.id')
.orderBy('assets.fileCreatedAt', 'asc')
.where('assets.visibility', '=', sql.lit(AssetVisibility.TIMELINE))
.where('assets.deletedAt', 'is', null),
).as('assets'),
)

View File

@@ -3,6 +3,7 @@ import {
AdminOnboardingResponseDto,
AdminOnboardingUpdateDto,
ReverseGeocodingStateResponseDto,
VersionCheckStateResponseDto,
} from 'src/dtos/system-metadata.dto';
import { SystemMetadataKey } from 'src/enum';
import { BaseService } from 'src/services/base.service';
@@ -24,4 +25,9 @@ export class SystemMetadataService extends BaseService {
const value = await this.systemMetadataRepository.get(SystemMetadataKey.REVERSE_GEOCODING_STATE);
return { lastUpdate: null, lastImportFileName: null, ...value };
}
async getVersionCheckState(): Promise<VersionCheckStateResponseDto> {
const value = await this.systemMetadataRepository.get(SystemMetadataKey.VERSION_CHECK_STATE);
return { checkedAt: null, releaseVersion: null, ...value };
}
}

View File

@@ -1 +1 @@
22.15.1
22.16.0

View File

@@ -1,4 +1,4 @@
FROM node:22.15.0-alpine3.20@sha256:686b8892b69879ef5bfd6047589666933508f9a5451c67320df3070ba0e9807b
FROM node:22.16.0-alpine3.20@sha256:2289fb1fba0f4633b08ec47b94a89c7e20b829fc5679f9b7b298eaa2f1ed8b7e
RUN apk add --no-cache tini
USER node

1018
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "immich-web",
"version": "1.133.1",
"version": "1.134.0",
"license": "GNU Affero General Public License version 3",
"type": "module",
"scripts": {
@@ -28,7 +28,7 @@
"dependencies": {
"@formatjs/icu-messageformat-parser": "^2.9.8",
"@immich/sdk": "file:../open-api/typescript-sdk",
"@immich/ui": "^0.22.2",
"@immich/ui": "^0.22.4",
"@mapbox/mapbox-gl-rtl-text": "0.2.3",
"@mdi/js": "^7.4.47",
"@photo-sphere-viewer/core": "^5.11.5",
@@ -80,7 +80,7 @@
"dotenv": "^16.4.7",
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.0",
"eslint-p": "^0.22.0",
"eslint-p": "^0.23.0",
"eslint-plugin-svelte": "^3.0.0",
"eslint-plugin-unicorn": "^57.0.0",
"factory.ts": "^1.4.1",
@@ -101,6 +101,6 @@
"vitest": "^3.0.0"
},
"volta": {
"node": "22.15.1"
"node": "22.16.0"
}
}

View File

@@ -58,6 +58,7 @@
const deleteAsset = async () => {
try {
preAction({ type: AssetAction.DELETE, asset: toTimelineAsset(asset) });
await deleteAssets({ assetBulkDeleteDto: { ids: [asset.id], force: true } });
onAction({ type: AssetAction.DELETE, asset: toTimelineAsset(asset) });

View File

@@ -279,12 +279,17 @@
const data = getFaceCroppedCoordinates();
if (!data) {
notificationController.show({
message: 'Error tagging face - cannot get bounding box coordinates',
message: $t('error_tag_face_bounding_box'),
});
return;
}
const isConfirmed = await modalManager.showDialog({ prompt: `Do you want to tag this face as ${person.name}?` });
const isConfirmed = await modalManager.showDialog({
prompt: person.name
? $t('confirm_tag_face', { values: { name: person.name } })
: $t('confirm_tag_face_unnamed'),
});
if (!isConfirmed) {
return;
}

View File

@@ -3,6 +3,7 @@
import ProgressBar from '$lib/components/shared-components/progress-bar/progress-bar.svelte';
import SlideshowSettings from '$lib/components/slideshow-settings.svelte';
import { ProgressBarStatus } from '$lib/constants';
import { modalManager } from '$lib/managers/modal-manager.svelte';
import { SlideshowNavigation, slideshowStore } from '$lib/stores/slideshow.store';
import { IconButton } from '@immich/ui';
import { mdiChevronLeft, mdiChevronRight, mdiClose, mdiCog, mdiFullscreen, mdiPause, mdiPlay } from '@mdi/js';
@@ -31,7 +32,6 @@
let progressBarStatus: ProgressBarStatus | undefined = $state();
let progressBar = $state<ReturnType<typeof ProgressBar>>();
let showSettings = $state(false);
let showControls = $state(true);
let timer: NodeJS.Timeout;
let isOverControls = $state(false);
@@ -99,11 +99,11 @@
onNext();
};
const onSettingToggled = async () => {
showSettings = !showSettings;
if (document.fullscreenElement && showSettings) {
const onShowSettings = async () => {
if (document.fullscreenElement) {
await document.exitFullscreen();
}
await modalManager.show(SlideshowSettings, {});
};
</script>
@@ -168,7 +168,7 @@
shape="round"
color="secondary"
icon={mdiCog}
onclick={onSettingToggled}
onclick={onShowSettings}
aria-label={$t('slideshow_settings')}
class="text-white"
/>
@@ -185,9 +185,6 @@
{/if}
</div>
{/if}
{#if showSettings}
<SlideshowSettings onClose={() => (showSettings = false)} />
{/if}
<ProgressBar
autoplay

View File

@@ -104,8 +104,10 @@
const handleClick = (e: MouseEvent) => {
if (e.ctrlKey || e.metaKey) {
window.open(currentUrlReplaceAssetId(asset.id), '_blank');
return;
}
e.stopPropagation();
e.preventDefault();
callClickHandlers();
@@ -249,19 +251,6 @@
>
<!-- icon overlay -->
<div>
{#if !usingMobileDevice && mouseOver && !disableLinkMouseOver}
<!-- lazy show the url on mouse over-->
<a
class="absolute w-full top-0 bottom-0"
style:cursor="unset"
href={currentUrlReplaceAssetId(asset.id)}
onclick={(evt) => evt.preventDefault()}
tabindex={-1}
aria-label="Thumbnail URL"
>
</a>
{/if}
<!-- Gradient overlay on hover -->
{#if !usingMobileDevice && !disabled}
<div
@@ -320,6 +309,20 @@
</div>
{/if}
</div>
<!-- lazy show the url on mouse over-->
{#if !usingMobileDevice && mouseOver && !disableLinkMouseOver}
<a
class="absolute w-full top-0 bottom-0"
style:cursor="unset"
href={currentUrlReplaceAssetId(asset.id)}
onclick={(evt) => evt.preventDefault()}
tabindex={-1}
aria-label="Thumbnail URL"
>
</a>
{/if}
<ImageThumbnail
class={imageClass}
{brokenAssetClass}
@@ -336,7 +339,7 @@
url={getAssetPlaybackUrl({ id: asset.id, cacheKey: asset.thumbhash })}
enablePlayback={mouseOver && $playVideoThumbnailOnHover}
curve={selected}
durationInSeconds={timeToSeconds(asset.duration!)}
durationInSeconds={asset.duration ? timeToSeconds(asset.duration) : 0}
playbackOnIconHover={!$playVideoThumbnailOnHover}
/>
</div>

View File

@@ -55,35 +55,6 @@
};
</script>
<div class="absolute end-0 top-0 z-1 flex place-items-center gap-1 text-xs font-medium text-white">
{#if showTime}
<span class="pt-2">
{#if remainingSeconds < 60}
{Duration.fromObject({ seconds: remainingSeconds }).toFormat('m:ss')}
{:else if remainingSeconds < 3600}
{Duration.fromObject({ seconds: remainingSeconds }).toFormat('mm:ss')}
{:else}
{Duration.fromObject({ seconds: remainingSeconds }).toFormat('h:mm:ss')}
{/if}
</span>
{/if}
<!-- svelte-ignore a11y_no_static_element_interactions -->
<span class="pe-2 pt-2" onmouseenter={onMouseEnter} onmouseleave={onMouseLeave}>
{#if enablePlayback}
{#if loading}
<LoadingSpinner />
{:else if error}
<Icon path={mdiAlertCircleOutline} size="24" class="text-red-600" />
{:else}
<Icon path={pauseIcon} size="24" />
{/if}
{:else}
<Icon path={playIcon} size="24" />
{/if}
</span>
</div>
{#if enablePlayback}
<video
bind:this={player}
@@ -114,3 +85,32 @@
}}
></video>
{/if}
<div class="absolute end-0 top-0 flex place-items-center gap-1 text-xs font-medium text-white">
{#if showTime}
<span class="pt-2">
{#if remainingSeconds < 60}
{Duration.fromObject({ seconds: remainingSeconds }).toFormat('m:ss')}
{:else if remainingSeconds < 3600}
{Duration.fromObject({ seconds: remainingSeconds }).toFormat('mm:ss')}
{:else}
{Duration.fromObject({ seconds: remainingSeconds }).toFormat('h:mm:ss')}
{/if}
</span>
{/if}
<!-- svelte-ignore a11y_no_static_element_interactions -->
<span class="pe-2 pt-2" onmouseenter={onMouseEnter} onmouseleave={onMouseLeave}>
{#if enablePlayback}
{#if loading}
<LoadingSpinner />
{:else if error}
<Icon path={mdiAlertCircleOutline} size="24" class="text-red-600" />
{:else}
<Icon path={pauseIcon} size="24" />
{/if}
{:else}
<Icon path={playIcon} size="24" />
{/if}
</span>
</div>

View File

@@ -47,7 +47,10 @@
{#if canScrollLeft || canScrollRight}
<div class="sticky start-0 z-1">
{#if canScrollLeft}
<div class="absolute start-4 top-24" transition:fade={{ duration: 200 }}>
<div
class="absolute start-4 max-md:top-[75px] top-[108px] -translate-y-1/2"
transition:fade={{ duration: 200 }}
>
<button
type="button"
class="rounded-full border border-gray-500 bg-gray-100 p-2 text-gray-500 opacity-50 hover:opacity-100"
@@ -60,7 +63,10 @@
</div>
{/if}
{#if canScrollRight}
<div class="absolute end-4 top-24 z-1" transition:fade={{ duration: 200 }}>
<div
class="absolute end-4 max-md:top-[75px] top-[108px] -translate-y-1/2 z-1"
transition:fade={{ duration: 200 }}
>
<button
type="button"
class="rounded-full border border-gray-500 bg-gray-100 p-2 text-gray-500 opacity-50 hover:opacity-100"
@@ -77,7 +83,7 @@
<div class="inline-block" use:resizeObserver={({ width }) => (innerWidth = width)}>
{#each memoryStore.memories as memory (memory.id)}
<a
class="memory-card relative me-2 md:me-4 last:me-0 inline-block aspect-3/4 md:aspect-4/3 max-md:h-[150px] xl:aspect-video h-[215px] rounded-xl"
class="memory-card relative me-2 md:me-4 last:me-0 inline-block aspect-3/4 md:aspect-4/3 max-md:h-[150px] xl:aspect-video h-[216px] rounded-xl"
href="{AppRoute.MEMORY}?{QueryParameter.ID}={memory.assets[0].id}"
>
<img

View File

@@ -6,9 +6,13 @@
import { AppRoute } from '$lib/constants';
import { modalManager } from '$lib/managers/modal-manager.svelte';
import AvatarEditModal from '$lib/modals/AvatarEditModal.svelte';
import HelpAndFeedbackModal from '$lib/modals/HelpAndFeedbackModal.svelte';
import { user } from '$lib/stores/user.store';
import { userInteraction } from '$lib/stores/user.svelte';
import { getAboutInfo, type ServerAboutResponseDto } from '@immich/sdk';
import { Button } from '@immich/ui';
import { mdiCog, mdiLogout, mdiPencil, mdiWrench } from '@mdi/js';
import { onMount } from 'svelte';
import { t } from 'svelte-i18n';
import { fade } from 'svelte/transition';
import UserAvatar from '../user-avatar.svelte';
@@ -19,6 +23,12 @@
}
let { onLogout, onClose = () => {} }: Props = $props();
let info: ServerAboutResponseDto | undefined = $state();
onMount(async () => {
info = userInteraction.aboutInfo ?? (await getAboutInfo());
});
</script>
<div
@@ -29,7 +39,7 @@
use:focusTrap
>
<div
class="mx-4 mt-4 flex flex-col items-center justify-center gap-4 rounded-3xl bg-white p-4 dark:bg-immich-dark-primary/10"
class="mx-4 mt-4 flex flex-col items-center justify-center gap-4 rounded-t-3xl bg-white p-4 dark:bg-immich-dark-primary/10"
>
<div class="relative">
<UserAvatar user={$user} size="xl" />
@@ -91,13 +101,25 @@
</div>
<div class="mb-4 flex flex-col">
<Button
class="m-1 mx-4 rounded-none rounded-b-3xl bg-white p-3 dark:bg-immich-dark-primary/10"
onclick={onLogout}
leadingIcon={mdiLogout}
variant="ghost"
color="secondary">{$t('sign_out')}</Button
>
<button
type="button"
class="flex w-full place-content-center place-items-center gap-2 py-3 font-medium text-gray-500 hover:bg-immich-primary/10 dark:text-gray-300"
onclick={onLogout}
>
<Icon path={mdiLogout} size={24} />
{$t('sign_out')}</button
class="text-center mt-4 underline text-xs text-immich-primary dark:text-immich-dark-primary"
onclick={async () => {
onClose();
if (info) {
await modalManager.show(HelpAndFeedbackModal, { info });
}
}}
>
{$t('support_and_feedback')}
</button>
</div>
</div>

View File

@@ -12,18 +12,13 @@
import SearchBar from '$lib/components/shared-components/search-bar/search-bar.svelte';
import { AppRoute } from '$lib/constants';
import { authManager } from '$lib/managers/auth-manager.svelte';
import { modalManager } from '$lib/managers/modal-manager.svelte';
import HelpAndFeedbackModal from '$lib/modals/HelpAndFeedbackModal.svelte';
import { mobileDevice } from '$lib/stores/mobile-device.svelte';
import { notificationManager } from '$lib/stores/notification-manager.svelte';
import { featureFlags } from '$lib/stores/server-config.store';
import { sidebarStore } from '$lib/stores/sidebar.svelte';
import { user } from '$lib/stores/user.store';
import { userInteraction } from '$lib/stores/user.svelte';
import { getAboutInfo, type ServerAboutResponseDto } from '@immich/sdk';
import { Button, IconButton } from '@immich/ui';
import { mdiBellBadge, mdiBellOutline, mdiHelpCircleOutline, mdiMagnify, mdiMenu, mdiTrayArrowUp } from '@mdi/js';
import { onMount } from 'svelte';
import { mdiBellBadge, mdiBellOutline, mdiMagnify, mdiMenu, mdiTrayArrowUp } from '@mdi/js';
import { t } from 'svelte-i18n';
import ThemeButton from '../theme-button.svelte';
import UserAvatar from '../user-avatar.svelte';
@@ -42,17 +37,11 @@
let shouldShowNotificationPanel = $state(false);
let innerWidth: number = $state(0);
const hasUnreadNotifications = $derived(notificationManager.notifications.length > 0);
let info: ServerAboutResponseDto | undefined = $state();
onMount(async () => {
info = userInteraction.aboutInfo ?? (await getAboutInfo());
});
</script>
<svelte:window bind:innerWidth />
<nav id="dashboard-navbar" class="max-md:h-(--navbar-height-md) h-(--navbar-height) w-dvw text-sm overflow-hidden">
<nav id="dashboard-navbar" class="max-md:h-(--navbar-height-md) h-(--navbar-height) w-dvw text-sm">
<SkipLink text={$t('skip_to_content')} />
<div
class="grid h-full grid-cols-[--spacing(32)_auto] items-center py-2 sidebar:grid-cols-[--spacing(64)_auto] {noBorder
@@ -130,18 +119,6 @@
<ThemeButton padding="2" />
<div>
<IconButton
shape="round"
color="secondary"
variant="ghost"
size="medium"
icon={mdiHelpCircleOutline}
onclick={() => info && modalManager.show(HelpAndFeedbackModal, { info })}
aria-label={$t('support_and_feedback')}
/>
</div>
<div
use:clickOutside={{
onOutclick: () => (shouldShowNotificationPanel = false),

View File

@@ -214,7 +214,7 @@
]}
/>
<div class="w-full relative" use:focusOutside={{ onFocusOut }} tabindex="-1">
<div class="w-full relative z-auto" use:focusOutside={{ onFocusOut }} tabindex="-1">
<form
draggable="false"
autocomplete="off"

View File

@@ -1,10 +1,10 @@
<script lang="ts">
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
import Icon from '$lib/components/elements/icon.svelte';
import { searchStore } from '$lib/stores/search.svelte';
import { mdiMagnify, mdiClose } from '@mdi/js';
import { fly } from 'svelte/transition';
import { mdiClose, mdiMagnify } from '@mdi/js';
import { t } from 'svelte-i18n';
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
import { fly } from 'svelte/transition';
interface Props {
id: string;
@@ -95,7 +95,7 @@
{#if isOpen && isSearchSuggestions}
<div
transition:fly={{ y: 25, duration: 150 }}
class="absolute w-full rounded-b-3xl border-2 border-t-0 border-gray-200 bg-white pb-5 shadow-2xl transition-all dark:border-gray-700 dark:bg-immich-dark-gray dark:text-gray-300"
class="absolute w-full rounded-b-3xl border-2 border-t-0 border-gray-200 bg-white pb-5 shadow-2xl transition-all dark:border-gray-700 dark:bg-immich-dark-gray dark:text-gray-300 z-1"
>
<div class="flex items-center justify-between px-5 pt-5 text-xs">
<p class="py-2" aria-hidden={true}>{$t('recent_searches').toUpperCase()}</p>

View File

@@ -60,9 +60,7 @@
{disabled}
onCheckedChange={() => handleCheckboxChange(option.value)}
/>
<Label label={option.text} for="{option.value}-checkbox">
{option.text}
</Label>
<Label label={option.text} for="{option.value}-checkbox" />
</div>
{/each}
</div>

View File

@@ -21,6 +21,7 @@ import { navigate } from '$lib/utils/navigation';
import {
addAssetsToAlbum as addAssets,
AssetVisibility,
bulkTagAssets,
createStack,
deleteAssets,
deleteStacks,
@@ -28,7 +29,6 @@ import {
getBaseUrl,
getDownloadInfo,
getStack,
tagAssets as tagAllAssets,
untagAssets,
updateAsset,
updateAssets,
@@ -83,9 +83,7 @@ export const tagAssets = async ({
tagIds: string[];
showNotification?: boolean;
}) => {
for (const tagId of tagIds) {
await tagAllAssets({ id: tagId, bulkIdsDto: { ids: assetIds } });
}
await bulkTagAssets({ tagBulkAssetsDto: { tagIds, assetIds } });
if (showNotification) {
const $t = await getFormatter();

View File

@@ -10,7 +10,6 @@
import { modalManager } from '$lib/managers/modal-manager.svelte';
import UserCreateModal from '$lib/modals/UserCreateModal.svelte';
import UserDeleteConfirmModal from '$lib/modals/UserDeleteConfirmModal.svelte';
import UserEditModal from '$lib/modals/UserEditModal.svelte';
import UserRestoreConfirmModal from '$lib/modals/UserRestoreConfirmModal.svelte';
import { locale } from '$lib/stores/preferences.store';
import { serverConfig } from '$lib/stores/server-config.store';
@@ -18,8 +17,8 @@
import { websocketEvents } from '$lib/stores/websocket';
import { getByteUnitString } from '$lib/utils/byte-units';
import { UserStatus, searchUsersAdmin, type UserAdminResponseDto } from '@immich/sdk';
import { Button, HStack, IconButton, Link, Text } from '@immich/ui';
import { mdiDeleteRestore, mdiInfinity, mdiPencilOutline, mdiPlusBoxOutline, mdiTrashCanOutline } from '@mdi/js';
import { Button, HStack, IconButton, Text } from '@immich/ui';
import { mdiDeleteRestore, mdiEyeOutline, mdiInfinity, mdiPlusBoxOutline, mdiTrashCanOutline } from '@mdi/js';
import { DateTime } from 'luxon';
import { onMount } from 'svelte';
import { t } from 'svelte-i18n';
@@ -63,13 +62,6 @@
await refresh();
};
const handleEdit = async (dto: UserAdminResponseDto) => {
const result = await modalManager.show(UserEditModal, { user: dto });
if (result) {
await refresh();
}
};
const handleDelete = async (user: UserAdminResponseDto) => {
const result = await modalManager.show(UserDeleteConfirmModal, { user });
if (result) {
@@ -116,9 +108,9 @@
? 'bg-red-300 dark:bg-red-900'
: 'even:bg-subtle/20 odd:bg-subtle/80'}"
>
<td class="w-8/12 sm:w-5/12 lg:w-6/12 xl:w-4/12 2xl:w-5/12 text-ellipsis break-all px-2 text-sm"
><Link href="{AppRoute.ADMIN_USERS}/{immichUser.id}">{immichUser.email}</Link></td
>
<td class="w-8/12 sm:w-5/12 lg:w-6/12 xl:w-4/12 2xl:w-5/12 text-ellipsis break-all px-2 text-sm">
{immichUser.email}
</td>
<td class="hidden sm:block w-3/12 text-ellipsis break-all px-2 text-sm">{immichUser.name}</td>
<td class="hidden xl:block w-3/12 2xl:w-2/12 text-ellipsis break-all px-2 text-sm">
<div class="container mx-auto flex flex-wrap justify-center">
@@ -136,10 +128,10 @@
<IconButton
shape="round"
size="medium"
icon={mdiPencilOutline}
title={$t('edit_user')}
onclick={() => handleEdit(immichUser)}
aria-label={$t('edit_user')}
icon={mdiEyeOutline}
title={$t('view_user')}
href={`${AppRoute.ADMIN_USERS}/${immichUser.id}`}
aria-label={$t('view_user')}
/>
{#if immichUser.id !== $user.id}
<IconButton