From 2ad7efb669e3093ad97e947af6fe6af21edfed3a Mon Sep 17 00:00:00 2001 From: Tink bot Date: Thu, 14 May 2026 07:25:31 +0000 Subject: [PATCH 001/480] fix(kanban): prevent task taps from leaking through the sticky add-task footer on touch devices The sticky bucket footer had no z-index, so the absolutely positioned `.handle` overlays on each task (z-index: 1, used to capture taps on touch devices) stacked above the Add Task button. Tapping the button where a task scrolled behind it would open that task instead of opening the new-task input. --- frontend/src/components/project/views/ProjectKanban.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/components/project/views/ProjectKanban.vue b/frontend/src/components/project/views/ProjectKanban.vue index 325b3d7c3..1bdbf6ee0 100644 --- a/frontend/src/components/project/views/ProjectKanban.vue +++ b/frontend/src/components/project/views/ProjectKanban.vue @@ -1094,6 +1094,7 @@ $filter-container-height: '1rem - #{$switch-view-height}'; .bucket-footer { position: sticky; inset-block-end: 0; + z-index: 2; block-size: min-content; padding: .5rem; background-color: var(--grey-100); From 7caaa9a16a0629242ebb9119cbefe4153a61e015 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 00:33:16 +0000 Subject: [PATCH 002/480] chore(deps): update dev-dependencies --- desktop/package.json | 2 +- desktop/pnpm-lock.yaml | 10 +- frontend/package.json | 10 +- frontend/pnpm-lock.yaml | 404 +++++++++++++++++++++------------------- 4 files changed, 220 insertions(+), 206 deletions(-) diff --git a/desktop/package.json b/desktop/package.json index e60798ba7..1bc0909fa 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -61,7 +61,7 @@ } }, "devDependencies": { - "electron": "40.9.3", + "electron": "40.10.0", "electron-builder": "26.8.1", "unzipper": "0.12.3" }, diff --git a/desktop/pnpm-lock.yaml b/desktop/pnpm-lock.yaml index 488330fa4..aae2ca03a 100644 --- a/desktop/pnpm-lock.yaml +++ b/desktop/pnpm-lock.yaml @@ -19,8 +19,8 @@ importers: version: 5.2.1 devDependencies: electron: - specifier: 40.9.3 - version: 40.9.3 + specifier: 40.10.0 + version: 40.10.0 electron-builder: specifier: 26.8.1 version: 26.8.1(electron-builder-squirrel-windows@24.13.3) @@ -560,8 +560,8 @@ packages: electron-publish@26.8.1: resolution: {integrity: sha512-q+jrSTIh/Cv4eGZa7oVR+grEJo/FoLMYBAnSL5GCtqwUpr1T+VgKB/dn1pnzxIxqD8S/jP1yilT9VrwCqINR4w==} - electron@40.9.3: - resolution: {integrity: sha512-rDcJOT6BBE689Ada+4jD3rVr05pMv9MZOgT0x/rIMVDF9c4ttx4RTb6lVARTyxZC7uqpirttCtcli1eg1DX5qg==} + electron@40.10.0: + resolution: {integrity: sha512-e7XVcAfyWoFQGS7ZhgxeNn0AijHaqgRCa6uA6TYOrvBWv8smI6JILvMR/8DYBIn07oqvxDLRC90tu/xa2cJCow==} engines: {node: '>= 12.20.55'} hasBin: true @@ -2364,7 +2364,7 @@ snapshots: transitivePeerDependencies: - supports-color - electron@40.9.3: + electron@40.10.0: dependencies: '@electron/get': 2.0.3 '@types/node': 24.10.9 diff --git a/frontend/package.json b/frontend/package.json index 482dde9c9..ba4aa302c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -113,11 +113,11 @@ "@tsconfig/node24": "24.0.4", "@types/codemirror": "5.60.17", "@types/is-touch-device": "1.0.3", - "@types/node": "24.12.3", + "@types/node": "24.12.4", "@types/sortablejs": "1.15.9", "@types/ws": "8.18.1", - "@typescript-eslint/eslint-plugin": "8.59.2", - "@typescript-eslint/parser": "8.59.2", + "@typescript-eslint/eslint-plugin": "8.59.3", + "@typescript-eslint/parser": "8.59.3", "@vitejs/plugin-vue": "6.0.6", "@vue/eslint-config-typescript": "14.7.0", "@vue/test-utils": "2.4.10", @@ -153,9 +153,9 @@ "vite-plugin-pwa": "1.3.0", "vite-plugin-vue-devtools": "8.1.2", "vite-svg-loader": "5.1.1", - "vitest": "4.1.5", + "vitest": "4.1.6", "vue-tsc": "3.2.8", - "wait-on": "9.0.5", + "wait-on": "9.0.10", "workbox-cli": "7.4.1", "ws": "8.20.0" }, diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 8ec59b699..4825cfab0 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -174,10 +174,10 @@ importers: version: 10.4.0 '@histoire/plugin-screenshot': specifier: 1.0.0-beta.1 - version: 1.0.0-beta.1(histoire@1.0.0-beta.1(@types/node@24.12.3)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(yaml@2.8.3))(typescript@5.9.3) + version: 1.0.0-beta.1(histoire@1.0.0-beta.1(@types/node@24.12.4)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(yaml@2.8.3))(typescript@5.9.3) '@histoire/plugin-vue': specifier: 1.0.0-beta.1 - version: 1.0.0-beta.1(histoire@1.0.0-beta.1(@types/node@24.12.3)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(yaml@2.8.3))(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(vue@3.5.27(typescript@5.9.3)) + version: 1.0.0-beta.1(histoire@1.0.0-beta.1(@types/node@24.12.4)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(yaml@2.8.3))(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(vue@3.5.27(typescript@5.9.3)) '@playwright/test': specifier: 1.58.2 version: 1.58.2 @@ -186,7 +186,7 @@ importers: version: 3.6.1 '@tailwindcss/vite': specifier: 4.3.0 - version: 4.3.0(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) + version: 4.3.0(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) '@tsconfig/node24': specifier: 24.0.4 version: 24.0.4 @@ -197,8 +197,8 @@ importers: specifier: 1.0.3 version: 1.0.3 '@types/node': - specifier: 24.12.3 - version: 24.12.3 + specifier: 24.12.4 + version: 24.12.4 '@types/sortablejs': specifier: 1.15.9 version: 1.15.9 @@ -206,17 +206,17 @@ importers: specifier: 8.18.1 version: 8.18.1 '@typescript-eslint/eslint-plugin': - specifier: 8.59.2 - version: 8.59.2(@typescript-eslint/parser@8.59.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + specifier: 8.59.3 + version: 8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': - specifier: 8.59.2 - version: 8.59.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + specifier: 8.59.3 + version: 8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) '@vitejs/plugin-vue': specifier: 6.0.6 - version: 6.0.6(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(vue@3.5.27(typescript@5.9.3)) + version: 6.0.6(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(vue@3.5.27(typescript@5.9.3)) '@vue/eslint-config-typescript': specifier: 14.7.0 - version: 14.7.0(eslint-plugin-vue@10.9.1(@typescript-eslint/parser@8.59.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + version: 14.7.0(eslint-plugin-vue@10.9.1(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) '@vue/test-utils': specifier: 2.4.10 version: 2.4.10(@vue/compiler-dom@3.5.27)(@vue/server-renderer@3.5.27(vue@3.5.27(typescript@5.9.3)))(vue@3.5.27(typescript@5.9.3)) @@ -249,13 +249,13 @@ importers: version: 1.5.0(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-vue: specifier: 10.9.1 - version: 10.9.1(@typescript-eslint/parser@8.59.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))) + version: 10.9.1(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))) happy-dom: specifier: 20.9.0 version: 20.9.0 histoire: specifier: 1.0.0-beta.1 - version: 1.0.0-beta.1(@types/node@24.12.3)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(yaml@2.8.3) + version: 1.0.0-beta.1(@types/node@24.12.4)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(yaml@2.8.3) otplib: specifier: 12.0.1 version: 12.0.1 @@ -306,25 +306,25 @@ importers: version: 3.0.0 vite: specifier: 7.3.3 - version: 7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) + version: 7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) vite-plugin-pwa: specifier: 1.3.0 - version: 1.3.0(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(workbox-build@7.4.1)(workbox-window@7.4.1) + version: 1.3.0(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(workbox-build@7.4.1)(workbox-window@7.4.1) vite-plugin-vue-devtools: specifier: 8.1.2 - version: 8.1.2(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(vue@3.5.27(typescript@5.9.3)) + version: 8.1.2(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(vue@3.5.27(typescript@5.9.3)) vite-svg-loader: specifier: 5.1.1 version: 5.1.1(vue@3.5.27(typescript@5.9.3)) vitest: - specifier: 4.1.5 - version: 4.1.5(@types/node@24.12.3)(happy-dom@20.9.0)(jsdom@27.4.0)(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) + specifier: 4.1.6 + version: 4.1.6(@types/node@24.12.4)(happy-dom@20.9.0)(jsdom@27.4.0)(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) vue-tsc: specifier: 3.2.8 version: 3.2.8(typescript@5.9.3) wait-on: - specifier: 9.0.5 - version: 9.0.5 + specifier: 9.0.10 + version: 9.0.10 workbox-cli: specifier: 7.4.1 version: 7.4.1 @@ -2812,8 +2812,8 @@ packages: '@types/minimist@1.2.5': resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} - '@types/node@24.12.3': - resolution: {integrity: sha512-8oljBDGun9cIsZRJR6fkihn0TSXJI0UDOOhncYaERq6M0JMDoPLxyscwruJcb4GKS6dvK/d8xebYBg27h/duaQ==} + '@types/node@24.12.4': + resolution: {integrity: sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -2853,11 +2853,11 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/eslint-plugin@8.59.2': - resolution: {integrity: sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==} + '@typescript-eslint/eslint-plugin@8.59.3': + resolution: {integrity: sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.59.2 + '@typescript-eslint/parser': ^8.59.3 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' @@ -2868,8 +2868,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.59.2': - resolution: {integrity: sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==} + '@typescript-eslint/parser@8.59.3': + resolution: {integrity: sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -2887,8 +2887,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.59.2': - resolution: {integrity: sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==} + '@typescript-eslint/project-service@8.59.3': + resolution: {integrity: sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -2901,8 +2901,8 @@ packages: resolution: {integrity: sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.59.2': - resolution: {integrity: sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==} + '@typescript-eslint/scope-manager@8.59.3': + resolution: {integrity: sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.56.0': @@ -2917,14 +2917,14 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/tsconfig-utils@8.59.1': - resolution: {integrity: sha512-/0nEyPbX7gRsk0Uwfe4ALwwgxuA66d/l2mhRDNlAvaj4U3juhUtJNq0DsY8M2AYwwb9rEq2hrC3IcIcEt++iJA==} + '@typescript-eslint/tsconfig-utils@8.59.2': + resolution: {integrity: sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/tsconfig-utils@8.59.2': - resolution: {integrity: sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==} + '@typescript-eslint/tsconfig-utils@8.59.3': + resolution: {integrity: sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -2936,8 +2936,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.59.2': - resolution: {integrity: sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==} + '@typescript-eslint/type-utils@8.59.3': + resolution: {integrity: sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -2951,14 +2951,14 @@ packages: resolution: {integrity: sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.59.1': - resolution: {integrity: sha512-ZDCjgccSdYPw5Bxh+my4Z0lJU96ZDN7jbBzvmEn0FZx3RtU1C7VWl6NbDx94bwY3V5YsgwRzJPOgeY2Q/nLG8A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.59.2': resolution: {integrity: sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.59.3': + resolution: {integrity: sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.56.0': resolution: {integrity: sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2971,8 +2971,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/typescript-estree@8.59.2': - resolution: {integrity: sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==} + '@typescript-eslint/typescript-estree@8.59.3': + resolution: {integrity: sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -2991,8 +2991,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.59.2': - resolution: {integrity: sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==} + '@typescript-eslint/utils@8.59.3': + resolution: {integrity: sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -3006,8 +3006,8 @@ packages: resolution: {integrity: sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.59.2': - resolution: {integrity: sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==} + '@typescript-eslint/visitor-keys@8.59.3': + resolution: {integrity: sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': @@ -3021,11 +3021,11 @@ packages: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 vue: ^3.2.25 - '@vitest/expect@4.1.5': - resolution: {integrity: sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==} + '@vitest/expect@4.1.6': + resolution: {integrity: sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg==} - '@vitest/mocker@4.1.5': - resolution: {integrity: sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==} + '@vitest/mocker@4.1.6': + resolution: {integrity: sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -3035,20 +3035,20 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.5': - resolution: {integrity: sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==} + '@vitest/pretty-format@4.1.6': + resolution: {integrity: sha512-h5SxD/IzNhZYnrSZRsUZQIC+vD0GY8cUvq0iwsmkFKixRCKLLWqCXa/FIQ4S1R+sI+PGoojkHsdNrbZiM9Qpgw==} - '@vitest/runner@4.1.5': - resolution: {integrity: sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==} + '@vitest/runner@4.1.6': + resolution: {integrity: sha512-nOPCmn2+yD0ZNmKdsXGv/UxMMWbMuKeD6GyYncNwdkYDxpQvrPSKYj2rWuDjC2Y4b6w6hjip5dBKFzEUuZe3vA==} - '@vitest/snapshot@4.1.5': - resolution: {integrity: sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==} + '@vitest/snapshot@4.1.6': + resolution: {integrity: sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw==} - '@vitest/spy@4.1.5': - resolution: {integrity: sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==} + '@vitest/spy@4.1.6': + resolution: {integrity: sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg==} - '@vitest/utils@4.1.5': - resolution: {integrity: sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==} + '@vitest/utils@4.1.6': + resolution: {integrity: sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ==} '@volar/language-core@2.4.28': resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==} @@ -3305,6 +3305,9 @@ packages: axios@1.15.2: resolution: {integrity: sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==} + axios@1.16.1: + resolution: {integrity: sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==} + b4a@1.6.7: resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==} @@ -4725,8 +4728,8 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true - joi@18.1.2: - resolution: {integrity: sha512-rF5MAmps5esSlhCA+N1b6IYHDw9j/btzGaqfgie522jS02Ju/HXBxamlXVlKEHAxoMKQL77HWI8jlqWsFuekZA==} + joi@18.2.1: + resolution: {integrity: sha512-2/OKlogiESf2Nh3TFCrRjrr9z1DRHeW0I+KReF67+4J0Ns+8hBtHRmoWAZ2OFU6I5+TWLEe6sVlSdXPjHm5UbQ==} engines: {node: '>= 20'} js-beautify@1.15.1: @@ -6747,20 +6750,20 @@ packages: yaml: optional: true - vitest@4.1.5: - resolution: {integrity: sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==} + vitest@4.1.6: + resolution: {integrity: sha512-6lvjbS3p9b4CrdCmguzbh2/4uoXhGE2q71R4OX5sqF9R1bo9Xd6fGrMAfvp5wnCzlBnFVdCOp6onuTQVbo8iUQ==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.5 - '@vitest/browser-preview': 4.1.5 - '@vitest/browser-webdriverio': 4.1.5 - '@vitest/coverage-istanbul': 4.1.5 - '@vitest/coverage-v8': 4.1.5 - '@vitest/ui': 4.1.5 + '@vitest/browser-playwright': 4.1.6 + '@vitest/browser-preview': 4.1.6 + '@vitest/browser-webdriverio': 4.1.6 + '@vitest/coverage-istanbul': 4.1.6 + '@vitest/coverage-v8': 4.1.6 + '@vitest/ui': 4.1.6 happy-dom: '*' jsdom: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -6869,8 +6872,8 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} - wait-on@9.0.5: - resolution: {integrity: sha512-qgnbHDfDTRIp73ANEJNRW/7kn8CrDUcvZz18xotJQku/P4saTGkbIzvnMZebPmVvVNUiRq1qWAPyqCH+W4H8KA==} + wait-on@9.0.10: + resolution: {integrity: sha512-rCoJEhvMr0X6alHmwc9abbrA5ZrLZFKpFQVKPNFwl2h7DapXOGdmimIHDtLOWhT4PjhZhxFEtZoQgEXbkDWdZw==} engines: {node: '>=20.0.0'} hasBin: true @@ -8597,17 +8600,17 @@ snapshots: dependencies: '@hapi/hoek': 11.0.7 - '@histoire/app@1.0.0-beta.1(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))': + '@histoire/app@1.0.0-beta.1(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))': dependencies: - '@histoire/controls': 1.0.0-beta.1(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) - '@histoire/shared': 1.0.0-beta.1(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) + '@histoire/controls': 1.0.0-beta.1(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) + '@histoire/shared': 1.0.0-beta.1(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) '@histoire/vendors': 1.0.0-beta.1 fuse.js: 7.1.0 shiki: 3.2.1 transitivePeerDependencies: - vite - '@histoire/controls@1.0.0-beta.1(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))': + '@histoire/controls@1.0.0-beta.1(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))': dependencies: '@codemirror/commands': 6.8.1 '@codemirror/lang-json': 6.0.1 @@ -8616,17 +8619,17 @@ snapshots: '@codemirror/state': 6.5.2 '@codemirror/theme-one-dark': 6.1.2 '@codemirror/view': 6.36.5 - '@histoire/shared': 1.0.0-beta.1(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) + '@histoire/shared': 1.0.0-beta.1(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) '@histoire/vendors': 1.0.0-beta.1 transitivePeerDependencies: - vite - '@histoire/plugin-screenshot@1.0.0-beta.1(histoire@1.0.0-beta.1(@types/node@24.12.3)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(yaml@2.8.3))(typescript@5.9.3)': + '@histoire/plugin-screenshot@1.0.0-beta.1(histoire@1.0.0-beta.1(@types/node@24.12.4)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(yaml@2.8.3))(typescript@5.9.3)': dependencies: capture-website: 4.2.0(typescript@5.9.3) defu: 6.1.7 fs-extra: 11.2.0 - histoire: 1.0.0-beta.1(@types/node@24.12.3)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(yaml@2.8.3) + histoire: 1.0.0-beta.1(@types/node@24.12.4)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(yaml@2.8.3) pathe: 1.1.2 transitivePeerDependencies: - bare-buffer @@ -8635,21 +8638,21 @@ snapshots: - typescript - utf-8-validate - '@histoire/plugin-vue@1.0.0-beta.1(histoire@1.0.0-beta.1(@types/node@24.12.3)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(yaml@2.8.3))(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(vue@3.5.27(typescript@5.9.3))': + '@histoire/plugin-vue@1.0.0-beta.1(histoire@1.0.0-beta.1(@types/node@24.12.4)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(yaml@2.8.3))(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(vue@3.5.27(typescript@5.9.3))': dependencies: - '@histoire/controls': 1.0.0-beta.1(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) - '@histoire/shared': 1.0.0-beta.1(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) + '@histoire/controls': 1.0.0-beta.1(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) + '@histoire/shared': 1.0.0-beta.1(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) '@histoire/vendors': 1.0.0-beta.1 change-case: 5.4.4 globby: 14.1.0 - histoire: 1.0.0-beta.1(@types/node@24.12.3)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(yaml@2.8.3) + histoire: 1.0.0-beta.1(@types/node@24.12.4)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(yaml@2.8.3) launch-editor: 2.10.0 pathe: 1.1.2 vue: 3.5.27(typescript@5.9.3) transitivePeerDependencies: - vite - '@histoire/shared@1.0.0-beta.1(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))': + '@histoire/shared@1.0.0-beta.1(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))': dependencies: '@histoire/vendors': 1.0.0-beta.1 '@types/fs-extra': 11.0.4 @@ -8657,7 +8660,7 @@ snapshots: chokidar: 4.0.3 pathe: 1.1.2 picocolors: 1.1.1 - vite: 7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) + vite: 7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) '@histoire/vendors@1.0.0-beta.1': {} @@ -9275,12 +9278,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.0 '@tailwindcss/oxide-win32-x64-msvc': 4.3.0 - '@tailwindcss/vite@4.3.0(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))': + '@tailwindcss/vite@4.3.0(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))': dependencies: '@tailwindcss/node': 4.3.0 '@tailwindcss/oxide': 4.3.0 tailwindcss: 4.3.0 - vite: 7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) + vite: 7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) '@tiptap/core@3.17.0(@tiptap/pm@3.17.0)': dependencies: @@ -9510,7 +9513,7 @@ snapshots: '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 - '@types/node': 24.12.3 + '@types/node': 24.12.4 '@types/hast@3.0.4': dependencies: @@ -9522,7 +9525,7 @@ snapshots: '@types/jsonfile@6.1.4': dependencies: - '@types/node': 24.12.3 + '@types/node': 24.12.4 '@types/linkify-it@5.0.0': {} @@ -9539,7 +9542,7 @@ snapshots: '@types/minimist@1.2.5': {} - '@types/node@24.12.3': + '@types/node@24.12.4': dependencies: undici-types: 7.16.0 @@ -9563,11 +9566,11 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 24.12.3 + '@types/node': 24.12.4 '@types/yauzl@2.10.3': dependencies: - '@types/node': 24.12.3 + '@types/node': 24.12.4 optional: true '@typescript-eslint/eslint-plugin@8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': @@ -9586,14 +9589,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.59.2(@typescript-eslint/parser@8.59.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.59.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.59.2 - '@typescript-eslint/type-utils': 8.59.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.59.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.59.2 + '@typescript-eslint/parser': 8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/type-utils': 8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.59.3 eslint: 9.39.4(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 @@ -9614,12 +9617,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.59.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.59.2 - '@typescript-eslint/types': 8.59.2 - '@typescript-eslint/typescript-estree': 8.59.2(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.59.2 + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.59.3 debug: 4.4.3 eslint: 9.39.4(jiti@2.6.1) typescript: 5.9.3 @@ -9628,8 +9631,8 @@ snapshots: '@typescript-eslint/project-service@8.56.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.59.1(typescript@5.9.3) - '@typescript-eslint/types': 8.59.1 + '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.9.3) + '@typescript-eslint/types': 8.59.2 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: @@ -9637,17 +9640,17 @@ snapshots: '@typescript-eslint/project-service@8.58.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.59.1(typescript@5.9.3) - '@typescript-eslint/types': 8.59.1 + '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.9.3) + '@typescript-eslint/types': 8.59.2 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.59.2(typescript@5.9.3)': + '@typescript-eslint/project-service@8.59.3(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.9.3) - '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@5.9.3) + '@typescript-eslint/types': 8.59.3 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: @@ -9663,10 +9666,10 @@ snapshots: '@typescript-eslint/types': 8.58.0 '@typescript-eslint/visitor-keys': 8.58.0 - '@typescript-eslint/scope-manager@8.59.2': + '@typescript-eslint/scope-manager@8.59.3': dependencies: - '@typescript-eslint/types': 8.59.2 - '@typescript-eslint/visitor-keys': 8.59.2 + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/visitor-keys': 8.59.3 '@typescript-eslint/tsconfig-utils@8.56.0(typescript@5.9.3)': dependencies: @@ -9676,11 +9679,11 @@ snapshots: dependencies: typescript: 5.9.3 - '@typescript-eslint/tsconfig-utils@8.59.1(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.59.2(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/tsconfig-utils@8.59.2(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.59.3(typescript@5.9.3)': dependencies: typescript: 5.9.3 @@ -9696,11 +9699,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.59.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.59.2 - '@typescript-eslint/typescript-estree': 8.59.2(typescript@5.9.3) - '@typescript-eslint/utils': 8.59.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.3 eslint: 9.39.4(jiti@2.6.1) ts-api-utils: 2.5.0(typescript@5.9.3) @@ -9712,10 +9715,10 @@ snapshots: '@typescript-eslint/types@8.58.0': {} - '@typescript-eslint/types@8.59.1': {} - '@typescript-eslint/types@8.59.2': {} + '@typescript-eslint/types@8.59.3': {} + '@typescript-eslint/typescript-estree@8.56.0(typescript@5.9.3)': dependencies: '@typescript-eslint/project-service': 8.56.0(typescript@5.9.3) @@ -9746,12 +9749,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.59.2(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.59.3(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.59.2(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.9.3) - '@typescript-eslint/types': 8.59.2 - '@typescript-eslint/visitor-keys': 8.59.2 + '@typescript-eslint/project-service': 8.59.3(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@5.9.3) + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/visitor-keys': 8.59.3 debug: 4.4.3 minimatch: 10.2.4 semver: 7.7.3 @@ -9783,12 +9786,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.59.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.59.2 - '@typescript-eslint/types': 8.59.2 - '@typescript-eslint/typescript-estree': 8.59.2(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.9.3) eslint: 9.39.4(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: @@ -9804,57 +9807,57 @@ snapshots: '@typescript-eslint/types': 8.58.0 eslint-visitor-keys: 5.0.0 - '@typescript-eslint/visitor-keys@8.59.2': + '@typescript-eslint/visitor-keys@8.59.3': dependencies: - '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/types': 8.59.3 eslint-visitor-keys: 5.0.0 '@ungap/structured-clone@1.3.0': {} - '@vitejs/plugin-vue@6.0.6(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(vue@3.5.27(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.6(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(vue@3.5.27(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.13 - vite: 7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) + vite: 7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) vue: 3.5.27(typescript@5.9.3) - '@vitest/expect@4.1.5': + '@vitest/expect@4.1.6': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.2 - '@vitest/spy': 4.1.5 - '@vitest/utils': 4.1.5 + '@vitest/spy': 4.1.6 + '@vitest/utils': 4.1.6 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.5(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))': + '@vitest/mocker@4.1.6(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))': dependencies: - '@vitest/spy': 4.1.5 + '@vitest/spy': 4.1.6 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) + vite: 7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) - '@vitest/pretty-format@4.1.5': + '@vitest/pretty-format@4.1.6': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.5': + '@vitest/runner@4.1.6': dependencies: - '@vitest/utils': 4.1.5 + '@vitest/utils': 4.1.6 pathe: 2.0.3 - '@vitest/snapshot@4.1.5': + '@vitest/snapshot@4.1.6': dependencies: - '@vitest/pretty-format': 4.1.5 - '@vitest/utils': 4.1.5 + '@vitest/pretty-format': 4.1.6 + '@vitest/utils': 4.1.6 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.5': {} + '@vitest/spy@4.1.6': {} - '@vitest/utils@4.1.5': + '@vitest/utils@4.1.6': dependencies: - '@vitest/pretty-format': 4.1.5 + '@vitest/pretty-format': 4.1.6 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -9965,11 +9968,11 @@ snapshots: '@vue/devtools-shared@8.1.2': {} - '@vue/eslint-config-typescript@14.7.0(eslint-plugin-vue@10.9.1(@typescript-eslint/parser@8.59.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@vue/eslint-config-typescript@14.7.0(eslint-plugin-vue@10.9.1(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@typescript-eslint/utils': 8.58.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.4(jiti@2.6.1) - eslint-plugin-vue: 10.9.1(@typescript-eslint/parser@8.59.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))) + eslint-plugin-vue: 10.9.1(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))) fast-glob: 3.3.3 typescript-eslint: 8.56.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) vue-eslint-parser: 10.4.0(eslint@9.39.4(jiti@2.6.1)) @@ -10169,6 +10172,16 @@ snapshots: transitivePeerDependencies: - debug + axios@1.16.1: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.5 + https-proxy-agent: 5.0.1 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + - supports-color + b4a@1.6.7: {} babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.26.0): @@ -10933,7 +10946,7 @@ snapshots: module-replacements: 2.11.0 semver: 7.7.3 - eslint-plugin-vue@10.9.1(@typescript-eslint/parser@8.59.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))): + eslint-plugin-vue@10.9.1(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) eslint: 9.39.4(jiti@2.6.1) @@ -10944,7 +10957,7 @@ snapshots: vue-eslint-parser: 10.4.0(eslint@9.39.4(jiti@2.6.1)) xml-name-validator: 4.0.0 optionalDependencies: - '@typescript-eslint/parser': 8.59.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) eslint-scope@8.4.0: dependencies: @@ -11349,7 +11362,7 @@ snapshots: happy-dom@20.9.0: dependencies: - '@types/node': 24.12.3 + '@types/node': 24.12.4 '@types/whatwg-mimetype': 3.0.2 '@types/ws': 8.18.1 entities: 7.0.1 @@ -11409,12 +11422,12 @@ snapshots: highlight.js@11.11.1: {} - histoire@1.0.0-beta.1(@types/node@24.12.3)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(yaml@2.8.3): + histoire@1.0.0-beta.1(@types/node@24.12.4)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(yaml@2.8.3): dependencies: '@akryum/tinypool': 0.3.1 - '@histoire/app': 1.0.0-beta.1(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) - '@histoire/controls': 1.0.0-beta.1(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) - '@histoire/shared': 1.0.0-beta.1(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) + '@histoire/app': 1.0.0-beta.1(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) + '@histoire/controls': 1.0.0-beta.1(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) + '@histoire/shared': 1.0.0-beta.1(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) '@histoire/vendors': 1.0.0-beta.1 '@types/markdown-it': 14.1.2 birpc: 0.2.19 @@ -11439,8 +11452,8 @@ snapshots: sade: 1.8.1 shiki: 3.2.1 sirv: 3.0.2 - vite: 7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) - vite-node: 3.2.4(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) + vite: 7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) + vite-node: 3.2.4(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) transitivePeerDependencies: - '@exodus/crypto' - '@types/node' @@ -11760,7 +11773,7 @@ snapshots: jiti@2.6.1: {} - joi@18.1.2: + joi@18.2.1: dependencies: '@hapi/address': 5.1.1 '@hapi/formula': 3.0.2 @@ -13891,23 +13904,23 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - vite-dev-rpc@1.1.0(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)): + vite-dev-rpc@1.1.0(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)): dependencies: birpc: 2.6.1 - vite: 7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) - vite-hot-client: 2.1.0(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) + vite: 7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) + vite-hot-client: 2.1.0(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) - vite-hot-client@2.1.0(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)): + vite-hot-client@2.1.0(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)): dependencies: - vite: 7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) + vite: 7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) - vite-node@3.2.4(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3): + vite-node@3.2.4(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) + vite: 7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) transitivePeerDependencies: - '@types/node' - jiti @@ -13922,7 +13935,7 @@ snapshots: - tsx - yaml - vite-plugin-inspect@11.3.3(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)): + vite-plugin-inspect@11.3.3(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)): dependencies: ansis: 4.1.0 debug: 4.4.3 @@ -13932,37 +13945,37 @@ snapshots: perfect-debounce: 2.0.0 sirv: 3.0.2 unplugin-utils: 0.3.0 - vite: 7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) - vite-dev-rpc: 1.1.0(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) + vite: 7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) + vite-dev-rpc: 1.1.0(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) transitivePeerDependencies: - supports-color - vite-plugin-pwa@1.3.0(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(workbox-build@7.4.1)(workbox-window@7.4.1): + vite-plugin-pwa@1.3.0(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(workbox-build@7.4.1)(workbox-window@7.4.1): dependencies: debug: 4.4.3 pretty-bytes: 6.1.1 tinyglobby: 0.2.15 - vite: 7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) + vite: 7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) workbox-build: 7.4.1 workbox-window: 7.4.1 transitivePeerDependencies: - supports-color - vite-plugin-vue-devtools@8.1.2(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(vue@3.5.27(typescript@5.9.3)): + vite-plugin-vue-devtools@8.1.2(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(vue@3.5.27(typescript@5.9.3)): dependencies: '@vue/devtools-core': 8.1.2(vue@3.5.27(typescript@5.9.3)) '@vue/devtools-kit': 8.1.2 '@vue/devtools-shared': 8.1.2 sirv: 3.0.2 - vite: 7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) - vite-plugin-inspect: 11.3.3(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) - vite-plugin-vue-inspector: 6.0.0(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) + vite: 7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) + vite-plugin-inspect: 11.3.3(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) + vite-plugin-vue-inspector: 6.0.0(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) transitivePeerDependencies: - '@nuxt/kit' - supports-color - vue - vite-plugin-vue-inspector@6.0.0(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)): + vite-plugin-vue-inspector@6.0.0(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)): dependencies: '@babel/core': 7.26.0 '@babel/plugin-proposal-decorators': 7.25.9(@babel/core@7.26.0) @@ -13973,7 +13986,7 @@ snapshots: '@vue/compiler-dom': 3.5.27 kolorist: 1.8.0 magic-string: 0.30.21 - vite: 7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) + vite: 7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) transitivePeerDependencies: - supports-color @@ -13985,7 +13998,7 @@ snapshots: transitivePeerDependencies: - supports-color - vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3): + vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3): dependencies: esbuild: 0.27.5 fdir: 6.5.0(picomatch@4.0.4) @@ -13994,7 +14007,7 @@ snapshots: rollup: 4.60.3 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 24.12.3 + '@types/node': 24.12.4 fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.32.0 @@ -14003,15 +14016,15 @@ snapshots: terser: 5.31.6 yaml: 2.8.3 - vitest@4.1.5(@types/node@24.12.3)(happy-dom@20.9.0)(jsdom@27.4.0)(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)): + vitest@4.1.6(@types/node@24.12.4)(happy-dom@20.9.0)(jsdom@27.4.0)(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)): dependencies: - '@vitest/expect': 4.1.5 - '@vitest/mocker': 4.1.5(vite@7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) - '@vitest/pretty-format': 4.1.5 - '@vitest/runner': 4.1.5 - '@vitest/snapshot': 4.1.5 - '@vitest/spy': 4.1.5 - '@vitest/utils': 4.1.5 + '@vitest/expect': 4.1.6 + '@vitest/mocker': 4.1.6(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) + '@vitest/pretty-format': 4.1.6 + '@vitest/runner': 4.1.6 + '@vitest/snapshot': 4.1.6 + '@vitest/spy': 4.1.6 + '@vitest/utils': 4.1.6 es-module-lexer: 2.0.0 expect-type: 1.3.0 magic-string: 0.30.21 @@ -14023,10 +14036,10 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.1.0 - vite: 7.3.3(@types/node@24.12.3)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) + vite: 7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.12.3 + '@types/node': 24.12.4 happy-dom: 20.9.0 jsdom: 27.4.0 transitivePeerDependencies: @@ -14108,15 +14121,16 @@ snapshots: dependencies: xml-name-validator: 5.0.0 - wait-on@9.0.5: + wait-on@9.0.10: dependencies: - axios: 1.15.2 - joi: 18.1.2 + axios: 1.16.1 + joi: 18.2.1 lodash: 4.18.1 minimist: 1.2.8 rxjs: 7.8.2 transitivePeerDependencies: - debug + - supports-color wcwidth@1.0.1: dependencies: From 2b38c2a19693bf514fd5ffcb4babb004cd366161 Mon Sep 17 00:00:00 2001 From: Brett Randall Date: Sat, 9 May 2026 08:09:03 +1000 Subject: [PATCH 003/480] chore: add mise.toml to pin tool versions Consolidates tool versions already declared across the project into a single mise.toml so that `mise install` / `mise exec` activates the correct runtime in one step. Without an explicit project-level pin, mise falls back to the global user config, silently using the wrong version even when .nvmrc is present (legacy files rank below all mise config files). Versions mirror existing project pins: - node 24.13.0 (frontend/.nvmrc) - pnpm 10.28.1 (frontend/package.json#packageManager) - go 1.25.7 (go.mod) --- mise.toml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 mise.toml diff --git a/mise.toml b/mise.toml new file mode 100644 index 000000000..2148b7af6 --- /dev/null +++ b/mise.toml @@ -0,0 +1,4 @@ +[tools] +node = "24.13.0" # keep in sync with frontend/.nvmrc +pnpm = "10.28.1" # keep in sync with frontend/package.json#packageManager +go = "1.25.7" # keep in sync with go.mod From bc7e41c2b02f09e0f2f68116b0a4a07a96f4fc3c Mon Sep 17 00:00:00 2001 From: Brett Randall Date: Sat, 9 May 2026 10:56:30 +1000 Subject: [PATCH 004/480] chore(deps): group node and pnpm updates across mise and version files Add packageRules to keep mise.toml in sync with the files it mirrors when Renovate raises version-bump PRs: - node: groups mise.toml and frontend/.nvmrc (nvm manager) into one PR - pnpm: groups mise.toml and frontend/package.json#packageManager (npm manager) into one PR Without these rules Renovate would open separate PRs for each file, allowing them to drift out of sync. --- renovate.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/renovate.json b/renovate.json index ad9041050..0752b1bf1 100644 --- a/renovate.json +++ b/renovate.json @@ -75,6 +75,26 @@ "extends": [ "schedule:weekly" ] + }, + { + "groupName": "node", + "matchPackageNames": [ + "node" + ], + "matchManagers": [ + "mise", + "nvm" + ] + }, + { + "groupName": "pnpm", + "matchPackageNames": [ + "pnpm" + ], + "matchManagers": [ + "mise", + "npm" + ] } ] } From c371ca719622515238d9ab5f763f1e66700f594e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 10:42:28 +0000 Subject: [PATCH 005/480] chore(deps): update dev-dependencies --- frontend/package.json | 12 +- frontend/pnpm-lock.yaml | 739 +++++++++++++++++++++------------------- 2 files changed, 395 insertions(+), 356 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index ba4aa302c..fc79cda7d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -118,7 +118,7 @@ "@types/ws": "8.18.1", "@typescript-eslint/eslint-plugin": "8.59.3", "@typescript-eslint/parser": "8.59.3", - "@vitejs/plugin-vue": "6.0.6", + "@vitejs/plugin-vue": "6.0.7", "@vue/eslint-config-typescript": "14.7.0", "@vue/test-utils": "2.4.10", "@vue/tsconfig": "0.9.1", @@ -137,11 +137,11 @@ "postcss": "8.5.14", "postcss-easing-gradients": "3.0.1", "postcss-html": "1.8.1", - "postcss-preset-env": "11.2.1", - "rollup": "4.60.3", + "postcss-preset-env": "11.3.0", + "rollup": "4.60.4", "rollup-plugin-visualizer": "6.0.11", "sass-embedded": "1.99.0", - "stylelint": "17.11.0", + "stylelint": "17.11.1", "stylelint-config-property-sort-order-smacss": "10.0.0", "stylelint-config-recommended-vue": "1.6.1", "stylelint-config-standard-scss": "17.0.0", @@ -154,10 +154,10 @@ "vite-plugin-vue-devtools": "8.1.2", "vite-svg-loader": "5.1.1", "vitest": "4.1.6", - "vue-tsc": "3.2.8", + "vue-tsc": "3.2.9", "wait-on": "9.0.10", "workbox-cli": "7.4.1", - "ws": "8.20.0" + "ws": "8.20.1" }, "pnpm": { "onlyBuiltDependencies": [ diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 4825cfab0..f681726b6 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -6,7 +6,7 @@ settings: overrides: minimatch: ^10.2.3 - rollup: 4.60.3 + rollup: 4.60.4 basic-ftp: '>=5.2.2' serialize-javascript: ^7.0.5 flatted: ^3.4.1 @@ -32,7 +32,7 @@ importers: version: 3.1.3(@fortawesome/fontawesome-svg-core@7.1.0)(vue@3.5.27(typescript@5.9.3)) '@intlify/unplugin-vue-i18n': specifier: 11.0.3 - version: 11.0.3(@vue/compiler-dom@3.5.27)(eslint@9.39.4(jiti@2.6.1))(rollup@4.60.3)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.27(typescript@5.9.3)))(vue@3.5.27(typescript@5.9.3)) + version: 11.0.3(@vue/compiler-dom@3.5.27)(eslint@9.39.4(jiti@2.6.1))(rollup@4.60.4)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.27(typescript@5.9.3)))(vue@3.5.27(typescript@5.9.3)) '@kyvg/vue3-notification': specifier: 3.4.2 version: 3.4.2(vue@3.5.27(typescript@5.9.3)) @@ -212,8 +212,8 @@ importers: specifier: 8.59.3 version: 8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) '@vitejs/plugin-vue': - specifier: 6.0.6 - version: 6.0.6(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(vue@3.5.27(typescript@5.9.3)) + specifier: 6.0.7 + version: 6.0.7(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(vue@3.5.27(typescript@5.9.3)) '@vue/eslint-config-typescript': specifier: 14.7.0 version: 14.7.0(eslint-plugin-vue@10.9.1(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) @@ -269,32 +269,32 @@ importers: specifier: 1.8.1 version: 1.8.1 postcss-preset-env: - specifier: 11.2.1 - version: 11.2.1(postcss@8.5.14) + specifier: 11.3.0 + version: 11.3.0(postcss@8.5.14) rollup: - specifier: 4.60.3 - version: 4.60.3 + specifier: 4.60.4 + version: 4.60.4 rollup-plugin-visualizer: specifier: 6.0.11 - version: 6.0.11(rollup@4.60.3) + version: 6.0.11(rollup@4.60.4) sass-embedded: specifier: 1.99.0 version: 1.99.0 stylelint: - specifier: 17.11.0 - version: 17.11.0(typescript@5.9.3) + specifier: 17.11.1 + version: 17.11.1(typescript@5.9.3) stylelint-config-property-sort-order-smacss: specifier: 10.0.0 - version: 10.0.0(stylelint@17.11.0(typescript@5.9.3)) + version: 10.0.0(stylelint@17.11.1(typescript@5.9.3)) stylelint-config-recommended-vue: specifier: 1.6.1 - version: 1.6.1(postcss-html@1.8.1)(stylelint@17.11.0(typescript@5.9.3)) + version: 1.6.1(postcss-html@1.8.1)(stylelint@17.11.1(typescript@5.9.3)) stylelint-config-standard-scss: specifier: 17.0.0 - version: 17.0.0(postcss@8.5.14)(stylelint@17.11.0(typescript@5.9.3)) + version: 17.0.0(postcss@8.5.14)(stylelint@17.11.1(typescript@5.9.3)) stylelint-use-logical: specifier: 2.1.3 - version: 2.1.3(stylelint@17.11.0(typescript@5.9.3)) + version: 2.1.3(stylelint@17.11.1(typescript@5.9.3)) tailwindcss: specifier: 4.3.0 version: 4.3.0 @@ -320,8 +320,8 @@ importers: specifier: 4.1.6 version: 4.1.6(@types/node@24.12.4)(happy-dom@20.9.0)(jsdom@27.4.0)(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) vue-tsc: - specifier: 3.2.8 - version: 3.2.8(typescript@5.9.3) + specifier: 3.2.9 + version: 3.2.9(typescript@5.9.3) wait-on: specifier: 9.0.10 version: 9.0.10 @@ -329,8 +329,8 @@ importers: specifier: 7.4.1 version: 7.4.1 ws: - specifier: 8.20.0 - version: 8.20.0 + specifier: 8.20.1 + version: 8.20.1 packages: @@ -947,6 +947,13 @@ packages: '@csstools/css-parser-algorithms': ^4.0.0 '@csstools/css-tokenizer': ^4.0.0 + '@csstools/css-calc@3.2.1': + resolution: {integrity: sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + '@csstools/css-color-parser@3.1.0': resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} engines: {node: '>=18'} @@ -954,8 +961,8 @@ packages: '@csstools/css-parser-algorithms': ^3.0.5 '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-color-parser@4.1.0': - resolution: {integrity: sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==} + '@csstools/css-color-parser@4.1.1': + resolution: {integrity: sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 @@ -996,8 +1003,8 @@ packages: '@csstools/css-parser-algorithms': ^4.0.0 '@csstools/css-tokenizer': ^4.0.0 - '@csstools/postcss-alpha-function@2.0.4': - resolution: {integrity: sha512-fti7+GybzvfMrv5TSU6x8rWtXWOth5nLefT5w5AKJ3F3T0bZoxlRqajF0ZUgTtnytfMd4dQ8n5UiaNmsjFA65A==} + '@csstools/postcss-alpha-function@2.0.5': + resolution: {integrity: sha512-i2lNJ6b4GdMoybHlpUM07TIk8KQRXTTe7Qf8LfctQhjDRTIgaodWTQqzWU4fpWO/nxBWNkSloDM22Lw/30NBcg==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 @@ -1008,44 +1015,50 @@ packages: peerDependencies: postcss: ^8.4 - '@csstools/postcss-color-function-display-p3-linear@2.0.3': - resolution: {integrity: sha512-u8QNV2TKOxG6cqK4ZrJkpctnxdrwdNTMrkyokmCi+iuLpJegOraA0cqC7HoxF2tHhxjuXc+BxwY/Qd62SwvanQ==} + '@csstools/postcss-color-function-display-p3-linear@2.0.4': + resolution: {integrity: sha512-xrGqSFj9pu6XbJYD4NNCxYK9WFbf0KMfXFaisnJezkIRDZCwefUB2azkU4Zr0dFmLtIb9LlshrSZ0be1/QVthQ==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 - '@csstools/postcss-color-function@5.0.3': - resolution: {integrity: sha512-BiBukIeQ7rPjx9A//9+qgJugBjX6FY9eWiojbnfIJCPulWrl8J07rCgQbFkloTXena+a6Aw5xa25weU+3MA75A==} + '@csstools/postcss-color-function@5.0.4': + resolution: {integrity: sha512-PhUu86ppxKcNHHqrJ43ZL1mYa2uHKGRoY0KPbZA9k8iOaanL3I+1zYqbgVumxj1UgNTDw5BE3BUQ1Dono6bD6g==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 - '@csstools/postcss-color-mix-function@4.0.3': - resolution: {integrity: sha512-M8ju3iqHRXtW1/5HYuOmi9WFR5rGGFgqkPh+kXkv/eG56oYK/WYtTeIwJgdcro7lRwjlo4Ut8xqbV3Iovkwfrw==} + '@csstools/postcss-color-mix-function@4.0.4': + resolution: {integrity: sha512-zYS78MHBuih9f9qtPFcSvVXMKg9q/lNPeFJUjyw7+/W1VHRjubvs5MlzuC363UUeahAhrOvYdo2ZZhmlxZbj6w==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 - '@csstools/postcss-color-mix-variadic-function-arguments@2.0.3': - resolution: {integrity: sha512-tL46UyFjIjz7mDywoPOe/JgOpvMic0rsTUfdMBB1OHrUcCtE8MQpBILzYl/cAOtinJGu+ZQLuDhqTgTBOoeg3g==} + '@csstools/postcss-color-mix-variadic-function-arguments@2.0.4': + resolution: {integrity: sha512-qlrABMEFPUqbCxX0aOsHcxQZo/8XgMqnEtqqtVUbdizcuTUtJyLdHike7hkoemwDspMSEotdIfRlUY4jhZaD+A==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 - '@csstools/postcss-content-alt-text@3.0.0': - resolution: {integrity: sha512-OHa+4aCcrJtHpPWB3zptScHwpS1TUbeLR4uO0ntIz0Su/zw9SoWkVu+tDMSySSAsNtNSI3kut4fTliFwIsrHxA==} + '@csstools/postcss-container-rule-prelude-list@1.0.1': + resolution: {integrity: sha512-c5qlevVGKHU+zDbVoUGSZl1Mw7Vl1gVRKv6cdIYnaoyM+9Ou23Ian0H5Gr2ZF+lsDWovPK03hOSAbkw6HS8aTg==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 - '@csstools/postcss-contrast-color-function@3.0.3': - resolution: {integrity: sha512-YcohXq+/hfYeobKirg3oXGivDaaTfOPv568bE3jYQCn9ILpFz+RgyJR/kF7ZWh5560TTlTjeCqF4ZmVsj2zwnw==} + '@csstools/postcss-content-alt-text@3.0.1': + resolution: {integrity: sha512-mK5lCgzgV/ZC+LgnFy4rAQVMcXR6HsnX3D1+4Q5gshSQsst5TtcvHbxTdzKy1XTv09sNZHJX8CO4CEQF9zA4ug==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 - '@csstools/postcss-exponential-functions@3.0.2': - resolution: {integrity: sha512-WDrfdFJXF4M67+wniEGr/5XVzsmn1rt2lL1YAlTfE7x7XDlRstTc5e+HuFoGv6jkiMWTwPsiADJaLwsnGC3UjQ==} + '@csstools/postcss-contrast-color-function@3.0.4': + resolution: {integrity: sha512-EiTZzUICztGqEuYg8AVCUWH9vH2jDzO6RryxMja+PWluZHP6n3/iG6i1leTt5LiDQjDUQlCRbQtMNj7V7S+b4Q==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-exponential-functions@3.0.3': + resolution: {integrity: sha512-mB/NoeHLBHh0LZiVSrFdRDA/NxSfmg4tSN9117IJH9bdC2BzSTVgc82h3Gu/sdBXay6kDH2sA7fbkTigMiEi2A==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 @@ -1062,26 +1075,32 @@ packages: peerDependencies: postcss: ^8.4 - '@csstools/postcss-gamut-mapping@3.0.3': - resolution: {integrity: sha512-3v5ZvcVuynhFh5qCJX2LIJ9Iry8/SvxfOEj6vDngNxbH/3OKTZBFLgK+DgLuIbsP1DLA9LLH3Rn7jmRxXgEDLA==} + '@csstools/postcss-gamut-mapping@3.0.4': + resolution: {integrity: sha512-2dWGsxtxypKU9Ra862F2335W8xegRwl9ohQ6hk808PiQlEahSaFtt5fqsGmKDaSiaFUx+2X8GZxVo970Ajr2vQ==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 - '@csstools/postcss-gradients-interpolation-method@6.0.3': - resolution: {integrity: sha512-wrRIaRv1dkq30a8nvYWtSAf41bwCl+sVzLBKGnqeOwk81aSktKN3NattJpkiPyoOtEoFqChisl3WH3Csj/rOsw==} + '@csstools/postcss-gradients-interpolation-method@6.0.4': + resolution: {integrity: sha512-sC/7dqVTtQTniLjPp/NagzeUn4sGinnMTicNBLDzirKq/GNXuJaApBOnvBmgNXjV6XPizfMhNRYCk5stn3q2nQ==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 - '@csstools/postcss-hwb-function@5.0.3': - resolution: {integrity: sha512-bHz0uc/PBg2wJEAlGinUf494nMyuXsVKH/fExc2xGkvL6WHOKlxzx/lkn+2AVCQACtWBLVRCBDgDnkYr4RSC9w==} + '@csstools/postcss-hwb-function@5.0.4': + resolution: {integrity: sha512-cl0KPaaeYyAXNHO3pqK8adbpbAGmIU1cT1thyaEkmP8yvbJvmyztkpdGADGqziUUoh4dZQ0IhHxOxnKQ296T+A==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 - '@csstools/postcss-ic-unit@5.0.0': - resolution: {integrity: sha512-/ws5d6c4uKqfM9zIL3ugcGI+3fvZEOOkJHNzAyTAGJIdZ+aSL9BVPNlHGV4QzmL0vqBSCOdU3+rhcMEj3+KzYw==} + '@csstools/postcss-ic-unit@5.0.1': + resolution: {integrity: sha512-jmsVLXPdMBTlaJAhiEijhIR3qL0j75MrlRfhJEs91DF1Wlt2kpJTDsbpXQpYFzn1nPFHZC/WEf+Mw0I/HXkHzQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-image-function@1.0.0': + resolution: {integrity: sha512-iuQztV6Cfeuc7NczazfickrzEhALOpxUS0yWgGkmRY1zZ0CKjBBFc/7WWSN9qupfpNAzHY7cPNcJCqUhtr+YMw==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 @@ -1098,8 +1117,8 @@ packages: peerDependencies: postcss: ^8.4 - '@csstools/postcss-light-dark-function@3.0.0': - resolution: {integrity: sha512-s++V5/hYazeRUCYIn2lsBVzUsxdeC46gtwpgW6lu5U/GlPOS5UTDT14kkEyPgXmFbCvaWLREqV7YTMJq1K3G6w==} + '@csstools/postcss-light-dark-function@3.0.1': + resolution: {integrity: sha512-tD2MMJmZ6XXCHgDythLHcXQDNi5z7KEEWPe7JeB3vPcw+YMuMabpW5ugRqndhIrui+vduhc0Md7f7yGPCmOErg==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 @@ -1134,8 +1153,8 @@ packages: peerDependencies: postcss: ^8.4 - '@csstools/postcss-media-minmax@3.0.2': - resolution: {integrity: sha512-+ABxs2ZhJDhy+B9PJg7pgkGq6/d3XPXsWl7+6yZfAk4b2ba6aQ1h2AiTn04XwS6rpMpZEF3tONli/ubfu4y8AQ==} + '@csstools/postcss-media-minmax@3.0.3': + resolution: {integrity: sha512-ch1tNS+1QayiHTGsyc53zv3AzrSd0zigjbkfLxoeuzzJyn32+P3V7em3u5vLVnqLMzBbEZK//GI13EVTIPRdDA==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 @@ -1164,8 +1183,8 @@ packages: peerDependencies: postcss: ^8.4 - '@csstools/postcss-oklab-function@5.0.3': - resolution: {integrity: sha512-vTMgJFMwMt9gnPvhKaDnMR7E/h9Nb+rPUv825SY5VUo4PWj+w0OH/N2NqgvjYeubaA3BVckbKDlvADATRpD4Hw==} + '@csstools/postcss-oklab-function@5.0.4': + resolution: {integrity: sha512-vIgrKe5ffW99it5SUIXOBczGLSiTaHBhU6afVr9KPwoZ4uq9H0E3Ehvi+xsUjmvnAyMTxOUSszNo04kEhbvYjQ==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 @@ -1176,8 +1195,8 @@ packages: peerDependencies: postcss: ^8.4 - '@csstools/postcss-progressive-custom-properties@5.0.0': - resolution: {integrity: sha512-NsJoZ89rxmDrUsITf8QIk5w+lQZQ8Xw5K6cLFG+cfiffsLYHb3zcbOOrHLetGl1WIhjWWQ4Cr8MMrg46Q+oACg==} + '@csstools/postcss-progressive-custom-properties@5.1.0': + resolution: {integrity: sha512-lt/4yHy2GdKcGVpK4OGhBdSIq+z2PXynSusSRggn/T4y7uFurYAhdHqo/aYM+xI37vNb8rJlEKchqKKvVCXROQ==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 @@ -1188,14 +1207,14 @@ packages: peerDependencies: postcss: ^8.4 - '@csstools/postcss-random-function@3.0.2': - resolution: {integrity: sha512-iQ3vfX1LIqRXX7P1/ol45EpJ5CTWdQCAfdpTlHlsRPU4jMQeepmeNjQ0F60bj8RWTS1RkJ318fzzq4mUlyZ7hA==} + '@csstools/postcss-random-function@3.0.3': + resolution: {integrity: sha512-0EScyKxscGonwpi30Hj9DEAr0X8D2eDhOqqayQXE91gIqGli9UT+deLYqoogZLOy5GT+ncqltMqztc/q+0UkhA==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 - '@csstools/postcss-relative-color-syntax@4.0.3': - resolution: {integrity: sha512-SZSImz4KufmLi0dRwYivWXlza+7HF84SRApY8R48SyWgn+f0gDvmCn7D2Ie4CED7qU0JJK+YfCUC1HVlaQ10dg==} + '@csstools/postcss-relative-color-syntax@4.0.4': + resolution: {integrity: sha512-reFFKD9eS602We8621e5cAroKD7hH4104duLNBBhzwawGN7dhbnL1+c/DRHqwyq6eGK35HaKMMiifEZhAztlOA==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 @@ -1206,14 +1225,14 @@ packages: peerDependencies: postcss: ^8.4 - '@csstools/postcss-sign-functions@2.0.2': - resolution: {integrity: sha512-vOxkkMCMVnyaj7CW03uKR2R/zhJaCrptsXlm31HgI/dqC1lSIGnmu5W7N68x23XwcSgc8fE/fg0jKj4x1XFH4w==} + '@csstools/postcss-sign-functions@2.0.3': + resolution: {integrity: sha512-2BCPwlpeQweTC/8S8oQFYhYD5kxYkiroLf3AUJV2kVoKkSZ+4WM4rSwySXlKrqXL8HfCryAwVrJg7B0jr/RnOw==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 - '@csstools/postcss-stepped-value-functions@5.0.2': - resolution: {integrity: sha512-4PtqkRoBcMSxZG00gcDv+nq7cxVUua+Yd7TmG16qzJjdolyICHkx1RfhNL5mKSnWOLxUnk/IdxAoWN+KU7E/ng==} + '@csstools/postcss-stepped-value-functions@5.0.3': + resolution: {integrity: sha512-nXMFQBz5Pi2LLG02iqm2k+scrqwtqJT9ta/gN8S79oBZ23M0E7O3wDJ20//3z5Q6HU5e+K0n+SmmxN6iWtbm6w==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 @@ -1236,8 +1255,8 @@ packages: peerDependencies: postcss: ^8.4 - '@csstools/postcss-trigonometric-functions@5.0.2': - resolution: {integrity: sha512-hRansZmQk1HH11WGUNlWy8H/DCB9Wy6zDbRcyBfF2UUP+V2fubK+qwmq0q6LIDje5gRzxlKyWhgFYxPy1ohivA==} + '@csstools/postcss-trigonometric-functions@5.0.3': + resolution: {integrity: sha512-p9LTvLj+DFpl5RHbG/X9QGwg7BoMOBsRBZqsUAKKVvCw7MRCsk1P1llTUR/MW5nyZ4IsjFGDtDwTTj1reJjxvg==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 @@ -2175,8 +2194,8 @@ packages: '@remusao/trie@1.5.0': resolution: {integrity: sha512-UX+3utJKgwCsg6sUozjxd38gNMVRXrY4TNX9VvCdSrlZBS1nZjRPi98ON3QjRAdf6KCguJFyQARRsulTeqQiPg==} - '@rolldown/pluginutils@1.0.0-rc.13': - resolution: {integrity: sha512-3ngTAv6F/Py35BsYbeeLeecvhMKdsKm4AoOETVhAA+Qc8nrA2I0kF7oa93mE9qnIurngOSpMnQ0x2nQY2FPviA==} + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} '@rollup/plugin-babel@6.1.0': resolution: {integrity: sha512-dFZNuFD2YRcoomP4oYf+DvQNSUA9ih+A3vUqopQx5EdtPGo3WBnQcI/S8pwpz91UsGfL0HsMSOlaMld8HrbubA==} @@ -2184,7 +2203,7 @@ packages: peerDependencies: '@babel/core': ^7.0.0 '@types/babel__core': ^7.1.9 - rollup: 4.60.3 + rollup: 4.60.4 peerDependenciesMeta: '@types/babel__core': optional: true @@ -2195,7 +2214,7 @@ packages: resolution: {integrity: sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==} engines: {node: '>=14.0.0'} peerDependencies: - rollup: 4.60.3 + rollup: 4.60.4 peerDependenciesMeta: rollup: optional: true @@ -2204,7 +2223,7 @@ packages: resolution: {integrity: sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==} engines: {node: '>=14.0.0'} peerDependencies: - rollup: 4.60.3 + rollup: 4.60.4 peerDependenciesMeta: rollup: optional: true @@ -2213,7 +2232,7 @@ packages: resolution: {integrity: sha512-FnCxhTBx6bMOYQrar6C8h3scPt8/JwIzw3+AJ2K++6guogH5fYaIFia+zZuhqv0eo1RN7W1Pz630SyvLbDjhtQ==} engines: {node: '>=20.0.0'} peerDependencies: - rollup: 4.60.3 + rollup: 4.60.4 peerDependenciesMeta: rollup: optional: true @@ -2222,133 +2241,133 @@ packages: resolution: {integrity: sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==} engines: {node: '>=14.0.0'} peerDependencies: - rollup: 4.60.3 + rollup: 4.60.4 peerDependenciesMeta: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.60.3': - resolution: {integrity: sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==} + '@rollup/rollup-android-arm-eabi@4.60.4': + resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.60.3': - resolution: {integrity: sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==} + '@rollup/rollup-android-arm64@4.60.4': + resolution: {integrity: sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.60.3': - resolution: {integrity: sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==} + '@rollup/rollup-darwin-arm64@4.60.4': + resolution: {integrity: sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.60.3': - resolution: {integrity: sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==} + '@rollup/rollup-darwin-x64@4.60.4': + resolution: {integrity: sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.60.3': - resolution: {integrity: sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==} + '@rollup/rollup-freebsd-arm64@4.60.4': + resolution: {integrity: sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.60.3': - resolution: {integrity: sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==} + '@rollup/rollup-freebsd-x64@4.60.4': + resolution: {integrity: sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.60.3': - resolution: {integrity: sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==} + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.60.3': - resolution: {integrity: sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==} + '@rollup/rollup-linux-arm-musleabihf@4.60.4': + resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.60.3': - resolution: {integrity: sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==} + '@rollup/rollup-linux-arm64-gnu@4.60.4': + resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.60.3': - resolution: {integrity: sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==} + '@rollup/rollup-linux-arm64-musl@4.60.4': + resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.60.3': - resolution: {integrity: sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==} + '@rollup/rollup-linux-loong64-gnu@4.60.4': + resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-loong64-musl@4.60.3': - resolution: {integrity: sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==} + '@rollup/rollup-linux-loong64-musl@4.60.4': + resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.60.3': - resolution: {integrity: sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==} + '@rollup/rollup-linux-ppc64-gnu@4.60.4': + resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-ppc64-musl@4.60.3': - resolution: {integrity: sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==} + '@rollup/rollup-linux-ppc64-musl@4.60.4': + resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.60.3': - resolution: {integrity: sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==} + '@rollup/rollup-linux-riscv64-gnu@4.60.4': + resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.60.3': - resolution: {integrity: sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==} + '@rollup/rollup-linux-riscv64-musl@4.60.4': + resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.60.3': - resolution: {integrity: sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==} + '@rollup/rollup-linux-s390x-gnu@4.60.4': + resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.60.3': - resolution: {integrity: sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==} + '@rollup/rollup-linux-x64-gnu@4.60.4': + resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.60.3': - resolution: {integrity: sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==} + '@rollup/rollup-linux-x64-musl@4.60.4': + resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==} cpu: [x64] os: [linux] - '@rollup/rollup-openbsd-x64@4.60.3': - resolution: {integrity: sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==} + '@rollup/rollup-openbsd-x64@4.60.4': + resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.60.3': - resolution: {integrity: sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==} + '@rollup/rollup-openharmony-arm64@4.60.4': + resolution: {integrity: sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.60.3': - resolution: {integrity: sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==} + '@rollup/rollup-win32-arm64-msvc@4.60.4': + resolution: {integrity: sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.60.3': - resolution: {integrity: sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==} + '@rollup/rollup-win32-ia32-msvc@4.60.4': + resolution: {integrity: sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.60.3': - resolution: {integrity: sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==} + '@rollup/rollup-win32-x64-gnu@4.60.4': + resolution: {integrity: sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.60.3': - resolution: {integrity: sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==} + '@rollup/rollup-win32-x64-msvc@4.60.4': + resolution: {integrity: sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==} cpu: [x64] os: [win32] @@ -3014,8 +3033,8 @@ packages: resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} deprecated: Potential CWE-502 - Update to 1.3.1 or higher - '@vitejs/plugin-vue@6.0.6': - resolution: {integrity: sha512-u9HHgfrq3AjXlysn0eINFnWQOJQLO9WN6VprZ8FXl7A2bYisv3Hui9Ij+7QZ41F/WYWarHjwBbXtD7dKg3uxbg==} + '@vitejs/plugin-vue@6.0.7': + resolution: {integrity: sha512-km+p+XdSz9Sxm5rqUbqcSfZYaAniKxWBj1KURl+Jr7UaPvvX7BmaWMdP69I5rrFDeQGyxAG7NXdc57vz+snhWg==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -3121,8 +3140,8 @@ packages: typescript: optional: true - '@vue/language-core@3.2.8': - resolution: {integrity: sha512-9OiSPQFiAAWNVnXb0d2dcTmcKnFQamhuNES6ayyISrb/mwPWVgoGdAqSfCWqKhQpa3D5gDTcYD+w7ObiheZ81g==} + '@vue/language-core@3.2.9': + resolution: {integrity: sha512-ie0ojt/0fU/GfIogh+zgHbaYRPlt9S+cLOxcWwF7nTSFh897BVgnFKL2byT4kpp1mlqYWZ2psGwSniyE2xsxYw==} '@vue/reactivity@3.5.27': resolution: {integrity: sha512-vvorxn2KXfJ0nBEnj4GYshSgsyMNFnIQah/wczXlsNXt+ijhugmW+PpJ2cNPe4V6jpnBcs0MhCODKllWG+nvoQ==} @@ -3214,8 +3233,8 @@ packages: ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} - alien-signals@3.1.2: - resolution: {integrity: sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==} + alien-signals@3.2.1: + resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==} ansi-align@3.0.1: resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} @@ -3660,8 +3679,8 @@ packages: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} - cssdb@8.8.0: - resolution: {integrity: sha512-QbLeyz2Bgso1iRlh7IpWk6OKa3lLNGXsujVjDMPl9rOZpxKeiG69icLpbLCFxeURwmcdIfZqQyhlooKJYM4f8Q==} + cssdb@8.9.0: + resolution: {integrity: sha512-J8jOU/hLjaXcO1LldOLraJSQpfLXRKof0I7mtbRyOy2AAXgqst0x9rlgi2qXeD6d0ou3ZLqcPAMqYVbpCbrxEw==} cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} @@ -5373,8 +5392,8 @@ packages: peerDependencies: postcss: ^8.4.6 - postcss-color-functional-notation@8.0.3: - resolution: {integrity: sha512-MyaFK+3PusD7F2+qlMDP6+zfSgHWP17AtmvHQs44W3+Qbb39VptVDVRJ4Lf7gHSVffW5ekEy/XrsZ0S0t34hrA==} + postcss-color-functional-notation@8.0.4: + resolution: {integrity: sha512-Zn3yPgBFakVXthmA2n1NUMY7gdhuFUB/DrUJ0Eug/d0rl9wahMQZykp4NVTJLGzQrDUwZ2rzjiTeW5udxFNG8A==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 @@ -5415,8 +5434,8 @@ packages: peerDependencies: postcss: ^8.4 - postcss-double-position-gradients@7.0.0: - resolution: {integrity: sha512-Msr/dxj8Os7KLJE5Hdhvprwm3K5Zrh1KTY0eFN3ngPKNkej/Usy4BM9JQmqE6CLAkDpHoQVsi4snbL72CPt6qg==} + postcss-double-position-gradients@7.0.1: + resolution: {integrity: sha512-M69I4EolEGwiYa0KmxKWg4zZp2DxhlNM0Bz12OvHCj930GXDVCvFhdWNGsRscz6BIijN6tFryzSFsy8kMLyD5Q==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 @@ -5458,8 +5477,8 @@ packages: peerDependencies: postcss: ^8.4 - postcss-lab-function@8.0.3: - resolution: {integrity: sha512-rUa27RLVXjMn1aDkHEt5dRsK80+bAACPr8w5Ow0BkIlfH6gEk0Mh1I0REkYhtp4UhKFw1HLEk3AzvKBi6BGOqw==} + postcss-lab-function@8.0.4: + resolution: {integrity: sha512-dqcJSzVasdELD9xqJ1wfP95uzP57J6zFd80c7S3AWK127H9zwqR9Kbk5ZgyIfN2DiMStI7Vq8E7ablXNeTvpew==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 @@ -5502,8 +5521,8 @@ packages: peerDependencies: postcss: ^8.4 - postcss-preset-env@11.2.1: - resolution: {integrity: sha512-dqL7WR5wg9yP/+6pTHIsIeIpK6XVghJDE4/r4ZSdr5ExrbLiN5x78gly0Xs0MLGbHy2oT3WWNfbxowmnw9BurQ==} + postcss-preset-env@11.3.0: + resolution: {integrity: sha512-PpijTuY+NT35vvk7us0pw9lJVrsZZWukjONZsza2Kq1Gag8nrUXRkgdKdxyyhZPJ6R43L3/nLpspUK99TmU9xg==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4 @@ -5809,15 +5828,15 @@ packages: hasBin: true peerDependencies: rolldown: 1.x || ^1.0.0-beta - rollup: 4.60.3 + rollup: 4.60.4 peerDependenciesMeta: rolldown: optional: true rollup: optional: true - rollup@4.60.3: - resolution: {integrity: sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==} + rollup@4.60.4: + resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -6302,8 +6321,8 @@ packages: peerDependencies: stylelint: '>= 11 < 18' - stylelint@17.11.0: - resolution: {integrity: sha512-/3czzmbF9XdGWvReDF3Ex4R23Ajolo7j8RB2bFNEqk6Ht356nlpVV+G5bG2Qt8AW1ofJzXztBRDnAtd7cgowWA==} + stylelint@17.11.1: + resolution: {integrity: sha512-+smN/HqVTggUx3iuAzOi9fPh8SrH+cJWlZrYVldXoJ06orWBhZ4Ue/QEp64oei6pVrAh4w3tG+Y12Vw7MbCFRQ==} engines: {node: '>=20.19.0'} hasBin: true @@ -6842,8 +6861,8 @@ packages: peerDependencies: vue: ^3.5.0 - vue-tsc@3.2.8: - resolution: {integrity: sha512-27vTLJ6Q2370obOd0PFYoYoKnmXJ521uUIedrs3Zhhhg/8YG10VOCMmwt+JQslatpAMTDbnWiitLnoD5VlIvog==} + vue-tsc@3.2.9: + resolution: {integrity: sha512-qm8/nbo+9eZc1SCndm9wT+gq23pM+wRIdHY0wjm83B3lIginHTwcdrLUyTrKjDWXbMVNjKegNrnymhpdqnCL3A==} hasBin: true peerDependencies: typescript: '>=5.0.0' @@ -7032,8 +7051,8 @@ packages: resolution: {integrity: sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg==} engines: {node: ^20.17.0 || >=22.9.0} - ws@8.20.0: - resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} + ws@8.20.1: + resolution: {integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -7919,6 +7938,11 @@ snapshots: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 + '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': dependencies: '@csstools/color-helpers': 5.1.0 @@ -7926,10 +7950,10 @@ snapshots: '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 - '@csstools/css-color-parser@4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-color-parser@4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/color-helpers': 6.0.2 - '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 @@ -7954,12 +7978,12 @@ snapshots: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-alpha-function@2.0.4(postcss@8.5.14)': + '@csstools/postcss-alpha-function@2.0.5(postcss@8.5.14)': dependencies: - '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.14) + '@csstools/postcss-progressive-custom-properties': 5.1.0(postcss@8.5.14) '@csstools/utilities': 3.0.0(postcss@8.5.14) postcss: 8.5.14 @@ -7969,62 +7993,68 @@ snapshots: postcss: 8.5.14 postcss-selector-parser: 7.1.1 - '@csstools/postcss-color-function-display-p3-linear@2.0.3(postcss@8.5.14)': + '@csstools/postcss-color-function-display-p3-linear@2.0.4(postcss@8.5.14)': dependencies: - '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.14) + '@csstools/postcss-progressive-custom-properties': 5.1.0(postcss@8.5.14) '@csstools/utilities': 3.0.0(postcss@8.5.14) postcss: 8.5.14 - '@csstools/postcss-color-function@5.0.3(postcss@8.5.14)': + '@csstools/postcss-color-function@5.0.4(postcss@8.5.14)': dependencies: - '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.14) + '@csstools/postcss-progressive-custom-properties': 5.1.0(postcss@8.5.14) '@csstools/utilities': 3.0.0(postcss@8.5.14) postcss: 8.5.14 - '@csstools/postcss-color-mix-function@4.0.3(postcss@8.5.14)': + '@csstools/postcss-color-mix-function@4.0.4(postcss@8.5.14)': dependencies: - '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.14) + '@csstools/postcss-progressive-custom-properties': 5.1.0(postcss@8.5.14) '@csstools/utilities': 3.0.0(postcss@8.5.14) postcss: 8.5.14 - '@csstools/postcss-color-mix-variadic-function-arguments@2.0.3(postcss@8.5.14)': + '@csstools/postcss-color-mix-variadic-function-arguments@2.0.4(postcss@8.5.14)': dependencies: - '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.14) + '@csstools/postcss-progressive-custom-properties': 5.1.0(postcss@8.5.14) '@csstools/utilities': 3.0.0(postcss@8.5.14) postcss: 8.5.14 - '@csstools/postcss-content-alt-text@3.0.0(postcss@8.5.14)': + '@csstools/postcss-container-rule-prelude-list@1.0.1(postcss@8.5.14)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.14) - '@csstools/utilities': 3.0.0(postcss@8.5.14) postcss: 8.5.14 - '@csstools/postcss-contrast-color-function@3.0.3(postcss@8.5.14)': + '@csstools/postcss-content-alt-text@3.0.1(postcss@8.5.14)': dependencies: - '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.14) + '@csstools/postcss-progressive-custom-properties': 5.1.0(postcss@8.5.14) '@csstools/utilities': 3.0.0(postcss@8.5.14) postcss: 8.5.14 - '@csstools/postcss-exponential-functions@3.0.2(postcss@8.5.14)': + '@csstools/postcss-contrast-color-function@3.0.4(postcss@8.5.14)': dependencies: - '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/postcss-progressive-custom-properties': 5.1.0(postcss@8.5.14) + '@csstools/utilities': 3.0.0(postcss@8.5.14) + postcss: 8.5.14 + + '@csstools/postcss-exponential-functions@3.0.3(postcss@8.5.14)': + dependencies: + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 postcss: 8.5.14 @@ -8040,38 +8070,46 @@ snapshots: '@csstools/utilities': 3.0.0(postcss@8.5.14) postcss: 8.5.14 - '@csstools/postcss-gamut-mapping@3.0.3(postcss@8.5.14)': + '@csstools/postcss-gamut-mapping@3.0.4(postcss@8.5.14)': dependencies: - '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 postcss: 8.5.14 - '@csstools/postcss-gradients-interpolation-method@6.0.3(postcss@8.5.14)': + '@csstools/postcss-gradients-interpolation-method@6.0.4(postcss@8.5.14)': dependencies: - '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.14) + '@csstools/postcss-progressive-custom-properties': 5.1.0(postcss@8.5.14) '@csstools/utilities': 3.0.0(postcss@8.5.14) postcss: 8.5.14 - '@csstools/postcss-hwb-function@5.0.3(postcss@8.5.14)': + '@csstools/postcss-hwb-function@5.0.4(postcss@8.5.14)': dependencies: - '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.14) + '@csstools/postcss-progressive-custom-properties': 5.1.0(postcss@8.5.14) '@csstools/utilities': 3.0.0(postcss@8.5.14) postcss: 8.5.14 - '@csstools/postcss-ic-unit@5.0.0(postcss@8.5.14)': + '@csstools/postcss-ic-unit@5.0.1(postcss@8.5.14)': dependencies: - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.14) + '@csstools/postcss-progressive-custom-properties': 5.1.0(postcss@8.5.14) '@csstools/utilities': 3.0.0(postcss@8.5.14) postcss: 8.5.14 postcss-value-parser: 4.2.0 + '@csstools/postcss-image-function@1.0.0(postcss@8.5.14)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/postcss-progressive-custom-properties': 5.1.0(postcss@8.5.14) + '@csstools/utilities': 3.0.0(postcss@8.5.14) + postcss: 8.5.14 + '@csstools/postcss-initial@3.0.0(postcss@8.5.14)': dependencies: postcss: 8.5.14 @@ -8082,11 +8120,11 @@ snapshots: postcss: 8.5.14 postcss-selector-parser: 7.1.1 - '@csstools/postcss-light-dark-function@3.0.0(postcss@8.5.14)': + '@csstools/postcss-light-dark-function@3.0.1(postcss@8.5.14)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.14) + '@csstools/postcss-progressive-custom-properties': 5.1.0(postcss@8.5.14) '@csstools/utilities': 3.0.0(postcss@8.5.14) postcss: 8.5.14 @@ -8113,9 +8151,9 @@ snapshots: '@csstools/utilities': 3.0.0(postcss@8.5.14) postcss: 8.5.14 - '@csstools/postcss-media-minmax@3.0.2(postcss@8.5.14)': + '@csstools/postcss-media-minmax@3.0.3(postcss@8.5.14)': dependencies: - '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) @@ -8145,12 +8183,12 @@ snapshots: postcss: 8.5.14 postcss-value-parser: 4.2.0 - '@csstools/postcss-oklab-function@5.0.3(postcss@8.5.14)': + '@csstools/postcss-oklab-function@5.0.4(postcss@8.5.14)': dependencies: - '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.14) + '@csstools/postcss-progressive-custom-properties': 5.1.0(postcss@8.5.14) '@csstools/utilities': 3.0.0(postcss@8.5.14) postcss: 8.5.14 @@ -8158,7 +8196,7 @@ snapshots: dependencies: postcss: 8.5.14 - '@csstools/postcss-progressive-custom-properties@5.0.0(postcss@8.5.14)': + '@csstools/postcss-progressive-custom-properties@5.1.0(postcss@8.5.14)': dependencies: postcss: 8.5.14 postcss-value-parser: 4.2.0 @@ -8169,19 +8207,19 @@ snapshots: '@csstools/css-tokenizer': 4.0.0 postcss: 8.5.14 - '@csstools/postcss-random-function@3.0.2(postcss@8.5.14)': + '@csstools/postcss-random-function@3.0.3(postcss@8.5.14)': dependencies: - '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 postcss: 8.5.14 - '@csstools/postcss-relative-color-syntax@4.0.3(postcss@8.5.14)': + '@csstools/postcss-relative-color-syntax@4.0.4(postcss@8.5.14)': dependencies: - '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.14) + '@csstools/postcss-progressive-custom-properties': 5.1.0(postcss@8.5.14) '@csstools/utilities': 3.0.0(postcss@8.5.14) postcss: 8.5.14 @@ -8190,16 +8228,16 @@ snapshots: postcss: 8.5.14 postcss-selector-parser: 7.1.1 - '@csstools/postcss-sign-functions@2.0.2(postcss@8.5.14)': + '@csstools/postcss-sign-functions@2.0.3(postcss@8.5.14)': dependencies: - '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 postcss: 8.5.14 - '@csstools/postcss-stepped-value-functions@5.0.2(postcss@8.5.14)': + '@csstools/postcss-stepped-value-functions@5.0.3(postcss@8.5.14)': dependencies: - '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 postcss: 8.5.14 @@ -8221,9 +8259,9 @@ snapshots: postcss: 8.5.14 postcss-value-parser: 4.2.0 - '@csstools/postcss-trigonometric-functions@5.0.2(postcss@8.5.14)': + '@csstools/postcss-trigonometric-functions@5.0.3(postcss@8.5.14)': dependencies: - '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 postcss: 8.5.14 @@ -8703,13 +8741,13 @@ snapshots: '@intlify/shared@11.2.8': {} - '@intlify/unplugin-vue-i18n@11.0.3(@vue/compiler-dom@3.5.27)(eslint@9.39.4(jiti@2.6.1))(rollup@4.60.3)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.27(typescript@5.9.3)))(vue@3.5.27(typescript@5.9.3))': + '@intlify/unplugin-vue-i18n@11.0.3(@vue/compiler-dom@3.5.27)(eslint@9.39.4(jiti@2.6.1))(rollup@4.60.4)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.27(typescript@5.9.3)))(vue@3.5.27(typescript@5.9.3))': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) '@intlify/bundle-utils': 11.0.3(vue-i18n@11.2.8(vue@3.5.27(typescript@5.9.3))) '@intlify/shared': 11.2.8 '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.2.8)(@vue/compiler-dom@3.5.27)(vue-i18n@11.2.8(vue@3.5.27(typescript@5.9.3)))(vue@3.5.27(typescript@5.9.3)) - '@rollup/pluginutils': 5.1.3(rollup@4.60.3) + '@rollup/pluginutils': 5.1.3(rollup@4.60.4) '@typescript-eslint/scope-manager': 8.58.0 '@typescript-eslint/typescript-estree': 8.58.0(typescript@5.9.3) debug: 4.4.3 @@ -8954,124 +8992,124 @@ snapshots: '@remusao/trie@1.5.0': {} - '@rolldown/pluginutils@1.0.0-rc.13': {} + '@rolldown/pluginutils@1.0.1': {} - '@rollup/plugin-babel@6.1.0(@babel/core@7.26.0)(rollup@4.60.3)': + '@rollup/plugin-babel@6.1.0(@babel/core@7.26.0)(rollup@4.60.4)': dependencies: '@babel/core': 7.26.0 '@babel/helper-module-imports': 7.25.9 - '@rollup/pluginutils': 5.1.3(rollup@4.60.3) + '@rollup/pluginutils': 5.1.3(rollup@4.60.4) optionalDependencies: - rollup: 4.60.3 + rollup: 4.60.4 transitivePeerDependencies: - supports-color - '@rollup/plugin-node-resolve@16.0.3(rollup@4.60.3)': + '@rollup/plugin-node-resolve@16.0.3(rollup@4.60.4)': dependencies: - '@rollup/pluginutils': 5.1.3(rollup@4.60.3) + '@rollup/pluginutils': 5.1.3(rollup@4.60.4) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.8 optionalDependencies: - rollup: 4.60.3 + rollup: 4.60.4 - '@rollup/plugin-replace@6.0.3(rollup@4.60.3)': + '@rollup/plugin-replace@6.0.3(rollup@4.60.4)': dependencies: - '@rollup/pluginutils': 5.1.3(rollup@4.60.3) + '@rollup/pluginutils': 5.1.3(rollup@4.60.4) magic-string: 0.30.21 optionalDependencies: - rollup: 4.60.3 + rollup: 4.60.4 - '@rollup/plugin-terser@1.0.0(rollup@4.60.3)': + '@rollup/plugin-terser@1.0.0(rollup@4.60.4)': dependencies: serialize-javascript: 7.0.5 smob: 1.5.0 terser: 5.31.6 optionalDependencies: - rollup: 4.60.3 + rollup: 4.60.4 - '@rollup/pluginutils@5.1.3(rollup@4.60.3)': + '@rollup/pluginutils@5.1.3(rollup@4.60.4)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.4 optionalDependencies: - rollup: 4.60.3 + rollup: 4.60.4 - '@rollup/rollup-android-arm-eabi@4.60.3': + '@rollup/rollup-android-arm-eabi@4.60.4': optional: true - '@rollup/rollup-android-arm64@4.60.3': + '@rollup/rollup-android-arm64@4.60.4': optional: true - '@rollup/rollup-darwin-arm64@4.60.3': + '@rollup/rollup-darwin-arm64@4.60.4': optional: true - '@rollup/rollup-darwin-x64@4.60.3': + '@rollup/rollup-darwin-x64@4.60.4': optional: true - '@rollup/rollup-freebsd-arm64@4.60.3': + '@rollup/rollup-freebsd-arm64@4.60.4': optional: true - '@rollup/rollup-freebsd-x64@4.60.3': + '@rollup/rollup-freebsd-x64@4.60.4': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.60.3': + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.60.3': + '@rollup/rollup-linux-arm-musleabihf@4.60.4': optional: true - '@rollup/rollup-linux-arm64-gnu@4.60.3': + '@rollup/rollup-linux-arm64-gnu@4.60.4': optional: true - '@rollup/rollup-linux-arm64-musl@4.60.3': + '@rollup/rollup-linux-arm64-musl@4.60.4': optional: true - '@rollup/rollup-linux-loong64-gnu@4.60.3': + '@rollup/rollup-linux-loong64-gnu@4.60.4': optional: true - '@rollup/rollup-linux-loong64-musl@4.60.3': + '@rollup/rollup-linux-loong64-musl@4.60.4': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.60.3': + '@rollup/rollup-linux-ppc64-gnu@4.60.4': optional: true - '@rollup/rollup-linux-ppc64-musl@4.60.3': + '@rollup/rollup-linux-ppc64-musl@4.60.4': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.60.3': + '@rollup/rollup-linux-riscv64-gnu@4.60.4': optional: true - '@rollup/rollup-linux-riscv64-musl@4.60.3': + '@rollup/rollup-linux-riscv64-musl@4.60.4': optional: true - '@rollup/rollup-linux-s390x-gnu@4.60.3': + '@rollup/rollup-linux-s390x-gnu@4.60.4': optional: true - '@rollup/rollup-linux-x64-gnu@4.60.3': + '@rollup/rollup-linux-x64-gnu@4.60.4': optional: true - '@rollup/rollup-linux-x64-musl@4.60.3': + '@rollup/rollup-linux-x64-musl@4.60.4': optional: true - '@rollup/rollup-openbsd-x64@4.60.3': + '@rollup/rollup-openbsd-x64@4.60.4': optional: true - '@rollup/rollup-openharmony-arm64@4.60.3': + '@rollup/rollup-openharmony-arm64@4.60.4': optional: true - '@rollup/rollup-win32-arm64-msvc@4.60.3': + '@rollup/rollup-win32-arm64-msvc@4.60.4': optional: true - '@rollup/rollup-win32-ia32-msvc@4.60.3': + '@rollup/rollup-win32-ia32-msvc@4.60.4': optional: true - '@rollup/rollup-win32-x64-gnu@4.60.3': + '@rollup/rollup-win32-x64-gnu@4.60.4': optional: true - '@rollup/rollup-win32-x64-msvc@4.60.3': + '@rollup/rollup-win32-x64-msvc@4.60.4': optional: true '@sentry-internal/browser-utils@10.36.0': @@ -9814,9 +9852,9 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vitejs/plugin-vue@6.0.6(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(vue@3.5.27(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.7(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(vue@3.5.27(typescript@5.9.3))': dependencies: - '@rolldown/pluginutils': 1.0.0-rc.13 + '@rolldown/pluginutils': 1.0.1 vite: 7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3) vue: 3.5.27(typescript@5.9.3) @@ -9981,12 +10019,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@vue/language-core@3.2.8': + '@vue/language-core@3.2.9': dependencies: '@volar/language-core': 2.4.28 '@vue/compiler-dom': 3.5.27 '@vue/shared': 3.5.27 - alien-signals: 3.1.2 + alien-signals: 3.2.1 muggle-string: 0.4.1 path-browserify: 1.0.1 picomatch: 4.0.4 @@ -10082,7 +10120,7 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - alien-signals@3.1.2: {} + alien-signals@3.2.1: {} ansi-align@3.0.1: dependencies: @@ -10539,7 +10577,7 @@ snapshots: css-what@6.1.0: {} - cssdb@8.8.0: {} + cssdb@8.9.0: {} cssesc@3.0.0: {} @@ -11367,7 +11405,7 @@ snapshots: '@types/ws': 8.18.1 entities: 7.0.1 whatwg-mimetype: 3.0.0 - ws: 8.20.0 + ws: 8.20.1 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -11828,7 +11866,7 @@ snapshots: webidl-conversions: 8.0.1 whatwg-mimetype: 4.0.0 whatwg-url: 15.1.0 - ws: 8.20.0 + ws: 8.20.1 xml-name-validator: 5.0.0 transitivePeerDependencies: - '@exodus/crypto' @@ -12386,12 +12424,12 @@ snapshots: postcss: 8.5.14 postcss-value-parser: 4.2.0 - postcss-color-functional-notation@8.0.3(postcss@8.5.14): + postcss-color-functional-notation@8.0.4(postcss@8.5.14): dependencies: - '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.14) + '@csstools/postcss-progressive-custom-properties': 5.1.0(postcss@8.5.14) '@csstools/utilities': 3.0.0(postcss@8.5.14) postcss: 8.5.14 @@ -12437,9 +12475,9 @@ snapshots: postcss: 8.5.14 postcss-selector-parser: 7.1.1 - postcss-double-position-gradients@7.0.0(postcss@8.5.14): + postcss-double-position-gradients@7.0.1(postcss@8.5.14): dependencies: - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.14) + '@csstools/postcss-progressive-custom-properties': 5.1.0(postcss@8.5.14) '@csstools/utilities': 3.0.0(postcss@8.5.14) postcss: 8.5.14 postcss-value-parser: 4.2.0 @@ -12482,12 +12520,12 @@ snapshots: postcss: 8.5.14 postcss-value-parser: 4.2.0 - postcss-lab-function@8.0.3(postcss@8.5.14): + postcss-lab-function@8.0.4(postcss@8.5.14): dependencies: - '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.14) + '@csstools/postcss-progressive-custom-properties': 5.1.0(postcss@8.5.14) '@csstools/utilities': 3.0.0(postcss@8.5.14) postcss: 8.5.14 @@ -12523,73 +12561,75 @@ snapshots: postcss: 8.5.14 postcss-value-parser: 4.2.0 - postcss-preset-env@11.2.1(postcss@8.5.14): + postcss-preset-env@11.3.0(postcss@8.5.14): dependencies: - '@csstools/postcss-alpha-function': 2.0.4(postcss@8.5.14) + '@csstools/postcss-alpha-function': 2.0.5(postcss@8.5.14) '@csstools/postcss-cascade-layers': 6.0.0(postcss@8.5.14) - '@csstools/postcss-color-function': 5.0.3(postcss@8.5.14) - '@csstools/postcss-color-function-display-p3-linear': 2.0.3(postcss@8.5.14) - '@csstools/postcss-color-mix-function': 4.0.3(postcss@8.5.14) - '@csstools/postcss-color-mix-variadic-function-arguments': 2.0.3(postcss@8.5.14) - '@csstools/postcss-content-alt-text': 3.0.0(postcss@8.5.14) - '@csstools/postcss-contrast-color-function': 3.0.3(postcss@8.5.14) - '@csstools/postcss-exponential-functions': 3.0.2(postcss@8.5.14) + '@csstools/postcss-color-function': 5.0.4(postcss@8.5.14) + '@csstools/postcss-color-function-display-p3-linear': 2.0.4(postcss@8.5.14) + '@csstools/postcss-color-mix-function': 4.0.4(postcss@8.5.14) + '@csstools/postcss-color-mix-variadic-function-arguments': 2.0.4(postcss@8.5.14) + '@csstools/postcss-container-rule-prelude-list': 1.0.1(postcss@8.5.14) + '@csstools/postcss-content-alt-text': 3.0.1(postcss@8.5.14) + '@csstools/postcss-contrast-color-function': 3.0.4(postcss@8.5.14) + '@csstools/postcss-exponential-functions': 3.0.3(postcss@8.5.14) '@csstools/postcss-font-format-keywords': 5.0.0(postcss@8.5.14) '@csstools/postcss-font-width-property': 1.0.0(postcss@8.5.14) - '@csstools/postcss-gamut-mapping': 3.0.3(postcss@8.5.14) - '@csstools/postcss-gradients-interpolation-method': 6.0.3(postcss@8.5.14) - '@csstools/postcss-hwb-function': 5.0.3(postcss@8.5.14) - '@csstools/postcss-ic-unit': 5.0.0(postcss@8.5.14) + '@csstools/postcss-gamut-mapping': 3.0.4(postcss@8.5.14) + '@csstools/postcss-gradients-interpolation-method': 6.0.4(postcss@8.5.14) + '@csstools/postcss-hwb-function': 5.0.4(postcss@8.5.14) + '@csstools/postcss-ic-unit': 5.0.1(postcss@8.5.14) + '@csstools/postcss-image-function': 1.0.0(postcss@8.5.14) '@csstools/postcss-initial': 3.0.0(postcss@8.5.14) '@csstools/postcss-is-pseudo-class': 6.0.0(postcss@8.5.14) - '@csstools/postcss-light-dark-function': 3.0.0(postcss@8.5.14) + '@csstools/postcss-light-dark-function': 3.0.1(postcss@8.5.14) '@csstools/postcss-logical-float-and-clear': 4.0.0(postcss@8.5.14) '@csstools/postcss-logical-overflow': 3.0.0(postcss@8.5.14) '@csstools/postcss-logical-overscroll-behavior': 3.0.0(postcss@8.5.14) '@csstools/postcss-logical-resize': 4.0.0(postcss@8.5.14) '@csstools/postcss-logical-viewport-units': 4.0.0(postcss@8.5.14) - '@csstools/postcss-media-minmax': 3.0.2(postcss@8.5.14) + '@csstools/postcss-media-minmax': 3.0.3(postcss@8.5.14) '@csstools/postcss-media-queries-aspect-ratio-number-values': 4.0.0(postcss@8.5.14) '@csstools/postcss-mixins': 1.0.0(postcss@8.5.14) '@csstools/postcss-nested-calc': 5.0.0(postcss@8.5.14) '@csstools/postcss-normalize-display-values': 5.0.1(postcss@8.5.14) - '@csstools/postcss-oklab-function': 5.0.3(postcss@8.5.14) + '@csstools/postcss-oklab-function': 5.0.4(postcss@8.5.14) '@csstools/postcss-position-area-property': 2.0.0(postcss@8.5.14) - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.14) + '@csstools/postcss-progressive-custom-properties': 5.1.0(postcss@8.5.14) '@csstools/postcss-property-rule-prelude-list': 2.0.0(postcss@8.5.14) - '@csstools/postcss-random-function': 3.0.2(postcss@8.5.14) - '@csstools/postcss-relative-color-syntax': 4.0.3(postcss@8.5.14) + '@csstools/postcss-random-function': 3.0.3(postcss@8.5.14) + '@csstools/postcss-relative-color-syntax': 4.0.4(postcss@8.5.14) '@csstools/postcss-scope-pseudo-class': 5.0.0(postcss@8.5.14) - '@csstools/postcss-sign-functions': 2.0.2(postcss@8.5.14) - '@csstools/postcss-stepped-value-functions': 5.0.2(postcss@8.5.14) + '@csstools/postcss-sign-functions': 2.0.3(postcss@8.5.14) + '@csstools/postcss-stepped-value-functions': 5.0.3(postcss@8.5.14) '@csstools/postcss-syntax-descriptor-syntax-production': 2.0.0(postcss@8.5.14) '@csstools/postcss-system-ui-font-family': 2.0.0(postcss@8.5.14) '@csstools/postcss-text-decoration-shorthand': 5.0.3(postcss@8.5.14) - '@csstools/postcss-trigonometric-functions': 5.0.2(postcss@8.5.14) + '@csstools/postcss-trigonometric-functions': 5.0.3(postcss@8.5.14) '@csstools/postcss-unset-value': 5.0.0(postcss@8.5.14) autoprefixer: 10.5.0(postcss@8.5.14) browserslist: 4.28.2 css-blank-pseudo: 8.0.1(postcss@8.5.14) css-has-pseudo: 8.0.0(postcss@8.5.14) css-prefers-color-scheme: 11.0.0(postcss@8.5.14) - cssdb: 8.8.0 + cssdb: 8.9.0 postcss: 8.5.14 postcss-attribute-case-insensitive: 8.0.0(postcss@8.5.14) postcss-clamp: 4.1.0(postcss@8.5.14) - postcss-color-functional-notation: 8.0.3(postcss@8.5.14) + postcss-color-functional-notation: 8.0.4(postcss@8.5.14) postcss-color-hex-alpha: 11.0.0(postcss@8.5.14) postcss-color-rebeccapurple: 11.0.0(postcss@8.5.14) postcss-custom-media: 12.0.1(postcss@8.5.14) postcss-custom-properties: 15.0.1(postcss@8.5.14) postcss-custom-selectors: 9.0.1(postcss@8.5.14) postcss-dir-pseudo-class: 10.0.0(postcss@8.5.14) - postcss-double-position-gradients: 7.0.0(postcss@8.5.14) + postcss-double-position-gradients: 7.0.1(postcss@8.5.14) postcss-focus-visible: 11.0.0(postcss@8.5.14) postcss-focus-within: 10.0.0(postcss@8.5.14) postcss-font-variant: 5.0.0(postcss@8.5.14) postcss-gap-properties: 7.0.0(postcss@8.5.14) postcss-image-set-function: 8.0.0(postcss@8.5.14) - postcss-lab-function: 8.0.3(postcss@8.5.14) + postcss-lab-function: 8.0.4(postcss@8.5.14) postcss-logical: 9.0.0(postcss@8.5.14) postcss-nesting: 14.0.0(postcss@8.5.14) postcss-opacity-percentage: 3.0.0(postcss@8.5.14) @@ -12804,7 +12844,7 @@ snapshots: debug: 4.4.3 devtools-protocol: 0.0.1367902 typed-query-selector: 2.12.0 - ws: 8.20.0 + ws: 8.20.1 transitivePeerDependencies: - bare-buffer - bufferutil @@ -12959,44 +12999,44 @@ snapshots: rfdc@1.4.1: {} - rollup-plugin-visualizer@6.0.11(rollup@4.60.3): + rollup-plugin-visualizer@6.0.11(rollup@4.60.4): dependencies: open: 8.4.2 picomatch: 4.0.4 source-map: 0.7.4 yargs: 17.7.2 optionalDependencies: - rollup: 4.60.3 + rollup: 4.60.4 - rollup@4.60.3: + rollup@4.60.4: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.60.3 - '@rollup/rollup-android-arm64': 4.60.3 - '@rollup/rollup-darwin-arm64': 4.60.3 - '@rollup/rollup-darwin-x64': 4.60.3 - '@rollup/rollup-freebsd-arm64': 4.60.3 - '@rollup/rollup-freebsd-x64': 4.60.3 - '@rollup/rollup-linux-arm-gnueabihf': 4.60.3 - '@rollup/rollup-linux-arm-musleabihf': 4.60.3 - '@rollup/rollup-linux-arm64-gnu': 4.60.3 - '@rollup/rollup-linux-arm64-musl': 4.60.3 - '@rollup/rollup-linux-loong64-gnu': 4.60.3 - '@rollup/rollup-linux-loong64-musl': 4.60.3 - '@rollup/rollup-linux-ppc64-gnu': 4.60.3 - '@rollup/rollup-linux-ppc64-musl': 4.60.3 - '@rollup/rollup-linux-riscv64-gnu': 4.60.3 - '@rollup/rollup-linux-riscv64-musl': 4.60.3 - '@rollup/rollup-linux-s390x-gnu': 4.60.3 - '@rollup/rollup-linux-x64-gnu': 4.60.3 - '@rollup/rollup-linux-x64-musl': 4.60.3 - '@rollup/rollup-openbsd-x64': 4.60.3 - '@rollup/rollup-openharmony-arm64': 4.60.3 - '@rollup/rollup-win32-arm64-msvc': 4.60.3 - '@rollup/rollup-win32-ia32-msvc': 4.60.3 - '@rollup/rollup-win32-x64-gnu': 4.60.3 - '@rollup/rollup-win32-x64-msvc': 4.60.3 + '@rollup/rollup-android-arm-eabi': 4.60.4 + '@rollup/rollup-android-arm64': 4.60.4 + '@rollup/rollup-darwin-arm64': 4.60.4 + '@rollup/rollup-darwin-x64': 4.60.4 + '@rollup/rollup-freebsd-arm64': 4.60.4 + '@rollup/rollup-freebsd-x64': 4.60.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.4 + '@rollup/rollup-linux-arm-musleabihf': 4.60.4 + '@rollup/rollup-linux-arm64-gnu': 4.60.4 + '@rollup/rollup-linux-arm64-musl': 4.60.4 + '@rollup/rollup-linux-loong64-gnu': 4.60.4 + '@rollup/rollup-linux-loong64-musl': 4.60.4 + '@rollup/rollup-linux-ppc64-gnu': 4.60.4 + '@rollup/rollup-linux-ppc64-musl': 4.60.4 + '@rollup/rollup-linux-riscv64-gnu': 4.60.4 + '@rollup/rollup-linux-riscv64-musl': 4.60.4 + '@rollup/rollup-linux-s390x-gnu': 4.60.4 + '@rollup/rollup-linux-x64-gnu': 4.60.4 + '@rollup/rollup-linux-x64-musl': 4.60.4 + '@rollup/rollup-openbsd-x64': 4.60.4 + '@rollup/rollup-openharmony-arm64': 4.60.4 + '@rollup/rollup-win32-arm64-msvc': 4.60.4 + '@rollup/rollup-win32-ia32-msvc': 4.60.4 + '@rollup/rollup-win32-x64-gnu': 4.60.4 + '@rollup/rollup-win32-x64-msvc': 4.60.4 fsevents: 2.3.3 rope-sequence@1.3.4: {} @@ -13429,58 +13469,58 @@ snapshots: style-mod@4.1.2: {} - stylelint-config-html@1.1.0(postcss-html@1.8.1)(stylelint@17.11.0(typescript@5.9.3)): + stylelint-config-html@1.1.0(postcss-html@1.8.1)(stylelint@17.11.1(typescript@5.9.3)): dependencies: postcss-html: 1.8.1 - stylelint: 17.11.0(typescript@5.9.3) + stylelint: 17.11.1(typescript@5.9.3) - stylelint-config-property-sort-order-smacss@10.0.0(stylelint@17.11.0(typescript@5.9.3)): + stylelint-config-property-sort-order-smacss@10.0.0(stylelint@17.11.1(typescript@5.9.3)): dependencies: css-property-sort-order-smacss: 2.2.0 - stylelint: 17.11.0(typescript@5.9.3) - stylelint-order: 6.0.4(stylelint@17.11.0(typescript@5.9.3)) + stylelint: 17.11.1(typescript@5.9.3) + stylelint-order: 6.0.4(stylelint@17.11.1(typescript@5.9.3)) - stylelint-config-recommended-scss@17.0.0(postcss@8.5.14)(stylelint@17.11.0(typescript@5.9.3)): + stylelint-config-recommended-scss@17.0.0(postcss@8.5.14)(stylelint@17.11.1(typescript@5.9.3)): dependencies: postcss-scss: 4.0.9(postcss@8.5.14) - stylelint: 17.11.0(typescript@5.9.3) - stylelint-config-recommended: 18.0.0(stylelint@17.11.0(typescript@5.9.3)) - stylelint-scss: 7.0.0(stylelint@17.11.0(typescript@5.9.3)) + stylelint: 17.11.1(typescript@5.9.3) + stylelint-config-recommended: 18.0.0(stylelint@17.11.1(typescript@5.9.3)) + stylelint-scss: 7.0.0(stylelint@17.11.1(typescript@5.9.3)) optionalDependencies: postcss: 8.5.14 - stylelint-config-recommended-vue@1.6.1(postcss-html@1.8.1)(stylelint@17.11.0(typescript@5.9.3)): + stylelint-config-recommended-vue@1.6.1(postcss-html@1.8.1)(stylelint@17.11.1(typescript@5.9.3)): dependencies: postcss-html: 1.8.1 semver: 7.7.3 - stylelint: 17.11.0(typescript@5.9.3) - stylelint-config-html: 1.1.0(postcss-html@1.8.1)(stylelint@17.11.0(typescript@5.9.3)) - stylelint-config-recommended: 18.0.0(stylelint@17.11.0(typescript@5.9.3)) + stylelint: 17.11.1(typescript@5.9.3) + stylelint-config-html: 1.1.0(postcss-html@1.8.1)(stylelint@17.11.1(typescript@5.9.3)) + stylelint-config-recommended: 18.0.0(stylelint@17.11.1(typescript@5.9.3)) - stylelint-config-recommended@18.0.0(stylelint@17.11.0(typescript@5.9.3)): + stylelint-config-recommended@18.0.0(stylelint@17.11.1(typescript@5.9.3)): dependencies: - stylelint: 17.11.0(typescript@5.9.3) + stylelint: 17.11.1(typescript@5.9.3) - stylelint-config-standard-scss@17.0.0(postcss@8.5.14)(stylelint@17.11.0(typescript@5.9.3)): + stylelint-config-standard-scss@17.0.0(postcss@8.5.14)(stylelint@17.11.1(typescript@5.9.3)): dependencies: - stylelint: 17.11.0(typescript@5.9.3) - stylelint-config-recommended-scss: 17.0.0(postcss@8.5.14)(stylelint@17.11.0(typescript@5.9.3)) - stylelint-config-standard: 40.0.0(stylelint@17.11.0(typescript@5.9.3)) + stylelint: 17.11.1(typescript@5.9.3) + stylelint-config-recommended-scss: 17.0.0(postcss@8.5.14)(stylelint@17.11.1(typescript@5.9.3)) + stylelint-config-standard: 40.0.0(stylelint@17.11.1(typescript@5.9.3)) optionalDependencies: postcss: 8.5.14 - stylelint-config-standard@40.0.0(stylelint@17.11.0(typescript@5.9.3)): + stylelint-config-standard@40.0.0(stylelint@17.11.1(typescript@5.9.3)): dependencies: - stylelint: 17.11.0(typescript@5.9.3) - stylelint-config-recommended: 18.0.0(stylelint@17.11.0(typescript@5.9.3)) + stylelint: 17.11.1(typescript@5.9.3) + stylelint-config-recommended: 18.0.0(stylelint@17.11.1(typescript@5.9.3)) - stylelint-order@6.0.4(stylelint@17.11.0(typescript@5.9.3)): + stylelint-order@6.0.4(stylelint@17.11.1(typescript@5.9.3)): dependencies: postcss: 8.5.14 postcss-sorting: 8.0.2(postcss@8.5.14) - stylelint: 17.11.0(typescript@5.9.3) + stylelint: 17.11.1(typescript@5.9.3) - stylelint-scss@7.0.0(stylelint@17.11.0(typescript@5.9.3)): + stylelint-scss@7.0.0(stylelint@17.11.1(typescript@5.9.3)): dependencies: css-tree: 3.2.1 is-plain-object: 5.0.0 @@ -13490,13 +13530,13 @@ snapshots: postcss-resolve-nested-selector: 0.1.6 postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 - stylelint: 17.11.0(typescript@5.9.3) + stylelint: 17.11.1(typescript@5.9.3) - stylelint-use-logical@2.1.3(stylelint@17.11.0(typescript@5.9.3)): + stylelint-use-logical@2.1.3(stylelint@17.11.1(typescript@5.9.3)): dependencies: - stylelint: 17.11.0(typescript@5.9.3) + stylelint: 17.11.1(typescript@5.9.3) - stylelint@17.11.0(typescript@5.9.3): + stylelint@17.11.1(typescript@5.9.3): dependencies: '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) @@ -13519,7 +13559,6 @@ snapshots: html-tags: 5.1.0 ignore: 7.0.5 import-meta-resolve: 4.2.0 - is-plain-object: 5.0.0 mathml-tag-names: 4.0.0 meow: 14.1.0 micromatch: 4.0.8 @@ -14004,7 +14043,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 postcss: 8.5.14 - rollup: 4.60.3 + rollup: 4.60.4 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 24.12.4 @@ -14093,10 +14132,10 @@ snapshots: '@vue/devtools-api': 6.6.4 vue: 3.5.27(typescript@5.9.3) - vue-tsc@3.2.8(typescript@5.9.3): + vue-tsc@3.2.9(typescript@5.9.3): dependencies: '@volar/typescript': 2.4.28 - '@vue/language-core': 3.2.8 + '@vue/language-core': 3.2.9 typescript: 5.9.3 vue@3.5.27(typescript@5.9.3): @@ -14245,10 +14284,10 @@ snapshots: '@babel/core': 7.26.0 '@babel/preset-env': 7.26.0(@babel/core@7.26.0) '@babel/runtime': 7.25.4 - '@rollup/plugin-babel': 6.1.0(@babel/core@7.26.0)(rollup@4.60.3) - '@rollup/plugin-node-resolve': 16.0.3(rollup@4.60.3) - '@rollup/plugin-replace': 6.0.3(rollup@4.60.3) - '@rollup/plugin-terser': 1.0.0(rollup@4.60.3) + '@rollup/plugin-babel': 6.1.0(@babel/core@7.26.0)(rollup@4.60.4) + '@rollup/plugin-node-resolve': 16.0.3(rollup@4.60.4) + '@rollup/plugin-replace': 6.0.3(rollup@4.60.4) + '@rollup/plugin-terser': 1.0.0(rollup@4.60.4) '@trickfilm400/rollup-plugin-off-main-thread': 3.0.0-pre1 ajv: 8.18.0 common-tags: 1.8.2 @@ -14257,7 +14296,7 @@ snapshots: fs-extra: 9.1.0 glob: 11.1.0 pretty-bytes: 5.6.0 - rollup: 4.60.3 + rollup: 4.60.4 source-map: 0.8.0-beta.0 stringify-object: 3.3.0 strip-comments: 2.0.1 @@ -14386,7 +14425,7 @@ snapshots: dependencies: signal-exit: 4.1.0 - ws@8.20.0: {} + ws@8.20.1: {} wsl-utils@0.1.0: dependencies: From 70393f38d21d6f2004cbf468c02e6fcf35c13ad2 Mon Sep 17 00:00:00 2001 From: kolaente Date: Fri, 15 May 2026 17:25:09 +0200 Subject: [PATCH 006/480] feat: add Atom feed for user notifications with API token auth (#2758) --- go.mod | 1 + go.sum | 2 + pkg/db/fixtures/api_tokens.yml | 10 ++ pkg/i18n/lang/en.json | 5 + pkg/mail/testing.go | 14 +++ pkg/models/api_routes.go | 6 + pkg/models/api_tokens.go | 9 ++ pkg/models/api_tokens_expiry_notification.go | 15 ++- pkg/models/api_tokens_test.go | 30 +++++ pkg/models/notifications.go | 78 +++++++++---- pkg/models/notifications_test.go | 84 ++++++++++++++ pkg/notifications/notification.go | 36 ++++++ pkg/notifications/notification_test.go | 100 ++++++++++++++++ pkg/routes/feeds/auth.go | 86 ++++++++++++++ pkg/routes/feeds/auth_test.go | 113 +++++++++++++++++++ pkg/routes/feeds/handler.go | 95 ++++++++++++++++ pkg/routes/feeds/handler_test.go | 73 ++++++++++++ pkg/routes/feeds/main_test.go | 41 +++++++ pkg/routes/routes.go | 12 +- 19 files changed, 787 insertions(+), 23 deletions(-) create mode 100644 pkg/routes/feeds/auth.go create mode 100644 pkg/routes/feeds/auth_test.go create mode 100644 pkg/routes/feeds/handler.go create mode 100644 pkg/routes/feeds/handler_test.go create mode 100644 pkg/routes/feeds/main_test.go diff --git a/go.mod b/go.mod index f759db0c5..a0fd3335b 100644 --- a/go.mod +++ b/go.mod @@ -144,6 +144,7 @@ require ( github.com/goccy/go-yaml v1.18.0 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/gorilla/css v1.0.1 // indirect + github.com/gorilla/feeds v1.2.0 // indirect github.com/huandu/go-clone v1.7.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect diff --git a/go.sum b/go.sum index fe3bdbab8..a917d62a4 100644 --- a/go.sum +++ b/go.sum @@ -245,6 +245,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= +github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc= +github.com/gorilla/feeds v1.2.0/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= diff --git a/pkg/db/fixtures/api_tokens.yml b/pkg/db/fixtures/api_tokens.yml index 2ff417bff..46a97e7f5 100644 --- a/pkg/db/fixtures/api_tokens.yml +++ b/pkg/db/fixtures/api_tokens.yml @@ -68,3 +68,13 @@ owner_id: 15 created: 2024-01-01 00:00:00 # token in plaintext is tk_nocaldav_token_test_000000005678efab +- id: 8 + title: 'feeds access token for user 13' + token_salt: fEdRTk9sR2 + token_hash: c1231ac23940702dcbdf20ae4c125a904780788b091f6d6c56f94f3620a634ec00aac5288659e04174a69a60b20ea86cdfa5 + token_last_eight: feed0013 + permissions: '{"feeds":["access"]}' + expires_at: 2099-01-01 00:00:00 + owner_id: 13 + created: 2024-01-01 00:00:00 + # token in plaintext is tk_feeds_access_token_user_0013_feed0013 diff --git a/pkg/i18n/lang/en.json b/pkg/i18n/lang/en.json index e8b05efb4..b9040d628 100644 --- a/pkg/i18n/lang/en.json +++ b/pkg/i18n/lang/en.json @@ -173,5 +173,10 @@ "since_hours": "one hour|%[1]d hours", "since_minutes": "one minute|%[1]d minutes", "list_last_separator": "and" + }, + "feeds": { + "notifications": { + "title": "Vikunja notifications for %[1]s" + } } } diff --git a/pkg/mail/testing.go b/pkg/mail/testing.go index 02a047586..b7e3f4bc1 100644 --- a/pkg/mail/testing.go +++ b/pkg/mail/testing.go @@ -46,3 +46,17 @@ func AssertSent(t *testing.T, opts *Opts) { assert.True(t, found, "Failed to assert mail '%v' has been sent.", opts) } + +// LastSent returns the most recently captured mail when running under Fake(), +// or nil if no mail has been sent. Intended for tests. +func LastSent() *Opts { + if len(sentMails) == 0 { + return nil + } + return sentMails[len(sentMails)-1] +} + +// ResetSent clears the captured mail buffer. Intended for tests. +func ResetSent() { + sentMails = nil +} diff --git a/pkg/models/api_routes.go b/pkg/models/api_routes.go index 0f8fbcbeb..607274092 100644 --- a/pkg/models/api_routes.go +++ b/pkg/models/api_routes.go @@ -35,6 +35,12 @@ func init() { Method: "ANY", }, } + apiTokenRoutes["feeds"] = APITokenRoute{ + "access": &RouteDetail{ + Path: "/feeds/*", + Method: "GET", + }, + } } type APITokenRoute map[string]*RouteDetail diff --git a/pkg/models/api_tokens.go b/pkg/models/api_tokens.go index 3664f1b3a..8bbe48550 100644 --- a/pkg/models/api_tokens.go +++ b/pkg/models/api_tokens.go @@ -207,6 +207,15 @@ func (t *APIToken) HasCaldavAccess() bool { return slices.Contains(perms, "access") } +// HasFeedsAccess checks whether the token has the feeds access permission. +func (t *APIToken) HasFeedsAccess() bool { + perms, has := t.APIPermissions["feeds"] + if !has { + return false + } + return slices.Contains(perms, "access") +} + // GetTokenFromTokenString returns the full token object from the original token string. func GetTokenFromTokenString(s *xorm.Session, token string) (apiToken *APIToken, err error) { lastEight := token[len(token)-8:] diff --git a/pkg/models/api_tokens_expiry_notification.go b/pkg/models/api_tokens_expiry_notification.go index 8a84d90eb..f99ef435f 100644 --- a/pkg/models/api_tokens_expiry_notification.go +++ b/pkg/models/api_tokens_expiry_notification.go @@ -23,15 +23,23 @@ import ( "code.vikunja.io/api/pkg/user" ) +func init() { + notifications.Register(func() notifications.Notification { return &APITokenExpiringWeekNotification{} }) + notifications.Register(func() notifications.Notification { return &APITokenExpiringDayNotification{} }) +} + // APITokenExpiringWeekNotification is sent 7 days before an API token expires. type APITokenExpiringWeekNotification struct { User *user.User `json:"user"` Token *APIToken `json:"api_token"` } +func (n *APITokenExpiringWeekNotification) ToTitle(lang string) string { + return i18n.T(lang, "notifications.api_token.expiring.week.subject", n.Token.Title) +} + func (n *APITokenExpiringWeekNotification) ToMail(lang string) *notifications.Mail { return notifications.NewMail(). - Subject(i18n.T(lang, "notifications.api_token.expiring.week.subject", n.Token.Title)). Greeting(i18n.T(lang, "notifications.greeting", n.User.GetName())). Line(i18n.T(lang, "notifications.api_token.expiring.week.message", notifications.EscapeMarkdown(n.Token.Title), n.Token.ExpiresAt.Format("2006-01-02"))). Action(i18n.T(lang, "notifications.api_token.expiring.action"), config.ServicePublicURL.GetString()+"user/settings/api-tokens"). @@ -56,9 +64,12 @@ type APITokenExpiringDayNotification struct { Token *APIToken `json:"api_token"` } +func (n *APITokenExpiringDayNotification) ToTitle(lang string) string { + return i18n.T(lang, "notifications.api_token.expiring.day.subject", n.Token.Title) +} + func (n *APITokenExpiringDayNotification) ToMail(lang string) *notifications.Mail { return notifications.NewMail(). - Subject(i18n.T(lang, "notifications.api_token.expiring.day.subject", n.Token.Title)). Greeting(i18n.T(lang, "notifications.greeting", n.User.GetName())). Line(i18n.T(lang, "notifications.api_token.expiring.day.message", notifications.EscapeMarkdown(n.Token.Title), n.Token.ExpiresAt.Format("2006-01-02"))). Action(i18n.T(lang, "notifications.api_token.expiring.action"), config.ServicePublicURL.GetString()+"user/settings/api-tokens"). diff --git a/pkg/models/api_tokens_test.go b/pkg/models/api_tokens_test.go index 6677c09fa..261de26bd 100644 --- a/pkg/models/api_tokens_test.go +++ b/pkg/models/api_tokens_test.go @@ -125,6 +125,36 @@ func TestAPIToken_HasCaldavAccess(t *testing.T) { }) } +func TestAPIToken_HasFeedsAccess(t *testing.T) { + t.Run("has feeds access", func(t *testing.T) { + token := &APIToken{ + APIPermissions: APIPermissions{"feeds": {"access"}}, + } + assert.True(t, token.HasFeedsAccess()) + }) + t.Run("no feeds group", func(t *testing.T) { + token := &APIToken{ + APIPermissions: APIPermissions{"tasks": {"read_all"}}, + } + assert.False(t, token.HasFeedsAccess()) + }) + t.Run("feeds group but wrong permission", func(t *testing.T) { + token := &APIToken{ + APIPermissions: APIPermissions{"feeds": {"read_all"}}, + } + assert.False(t, token.HasFeedsAccess()) + }) + t.Run("feeds access among other permissions", func(t *testing.T) { + token := &APIToken{ + APIPermissions: APIPermissions{ + "tasks": {"read_all", "update"}, + "feeds": {"access"}, + }, + } + assert.True(t, token.HasFeedsAccess()) + }) +} + func TestAPIToken_GetTokenFromTokenString(t *testing.T) { t.Run("valid token", func(t *testing.T) { s := db.NewSession() diff --git a/pkg/models/notifications.go b/pkg/models/notifications.go index 3b1e4680a..4f27d04f4 100644 --- a/pkg/models/notifications.go +++ b/pkg/models/notifications.go @@ -32,6 +32,16 @@ import ( "code.vikunja.io/api/pkg/utils" ) +func init() { + notifications.Register(func() notifications.Notification { return &ReminderDueNotification{} }) + notifications.Register(func() notifications.Notification { return &TaskCommentNotification{} }) + notifications.Register(func() notifications.Notification { return &TaskAssignedNotification{} }) + notifications.Register(func() notifications.Notification { return &TaskDeletedNotification{} }) + notifications.Register(func() notifications.Notification { return &ProjectCreatedNotification{} }) + notifications.Register(func() notifications.Notification { return &TeamMemberAddedNotification{} }) + notifications.Register(func() notifications.Notification { return &UserMentionedInTaskNotification{} }) +} + // getDoerAvatarDataURI returns the avatar data URI for a user, for use in email headers. func getDoerAvatarDataURI(doer *user.User) string { provider := avatar.GetProvider(doer) @@ -55,12 +65,16 @@ type ReminderDueNotification struct { TaskReminder *TaskReminder `json:"reminder"` } +// ToTitle returns the translated one-line title for ReminderDueNotification +func (n *ReminderDueNotification) ToTitle(lang string) string { + return i18n.T(lang, "notifications.task.reminder.subject", n.Task.Title, n.Project.Title) +} + // ToMail returns the mail notification for ReminderDueNotification func (n *ReminderDueNotification) ToMail(lang string) *notifications.Mail { return notifications.NewMail(). IncludeLinkToSettings(lang). To(n.User.Email). - Subject(i18n.T(lang, "notifications.task.reminder.subject", n.Task.Title, n.Project.Title)). Greeting(i18n.T(lang, "notifications.greeting", n.User.GetName())). Line(i18n.T(lang, "notifications.task.reminder.message", notifications.EscapeMarkdown(n.Task.Title), notifications.EscapeMarkdown(n.Project.Title))). Action(i18n.T(lang, "notifications.common.actions.open_task"), config.ServicePublicURL.GetString()+"tasks/"+strconv.FormatInt(n.Task.ID, 10)). @@ -98,6 +112,14 @@ func (n *TaskCommentNotification) SubjectID() int64 { return n.Comment.ID } +// ToTitle returns the translated one-line title for TaskCommentNotification +func (n *TaskCommentNotification) ToTitle(lang string) string { + if n.Mentioned { + return i18n.T(lang, "notifications.task.comment.mentioned_subject", n.Doer.GetName(), n.Task.Title, n.Task.GetFullIdentifier()) + } + return i18n.T(lang, "notifications.task.comment.subject", n.Task.Title, n.Task.GetFullIdentifier()) +} + // ToMail returns the mail notification for TaskCommentNotification func (n *TaskCommentNotification) ToMail(lang string) *notifications.Mail { s := db.NewSession() @@ -106,14 +128,12 @@ func (n *TaskCommentNotification) ToMail(lang string) *notifications.Mail { mail := notifications.NewMail(). Conversational(). - From(n.Doer.GetNameAndFromEmail()). - Subject(i18n.T(lang, "notifications.task.comment.subject", n.Task.Title, n.Task.GetFullIdentifier())) + From(n.Doer.GetNameAndFromEmail()) // Add header line action := i18n.T(lang, "notifications.common.actions.left_comment", n.Doer.GetName()) if n.Mentioned { action = i18n.T(lang, "notifications.common.actions.mentioned_you_comment", n.Doer.GetName()) - mail.Subject(i18n.T(lang, "notifications.task.comment.mentioned_subject", n.Doer.GetName(), n.Task.Title, n.Task.GetFullIdentifier())) } headerLine := notifications.CreateConversationalHeader( @@ -158,13 +178,23 @@ type TaskAssignedNotification struct { Project *Project `json:"project"` } +// ToTitle returns the translated one-line title for TaskAssignedNotification +func (n *TaskAssignedNotification) ToTitle(lang string) string { + if n.Target.ID == n.Assignee.ID { + return i18n.T(lang, "notifications.task.assigned.subject_to_assignee", n.Task.Title, n.Task.GetFullIdentifier()) + } + if n.Doer.ID == n.Assignee.ID { + return i18n.T(lang, "notifications.task.assigned.subject_to_others_self", n.Task.Title, n.Task.GetFullIdentifier(), n.Doer.GetName()) + } + return i18n.T(lang, "notifications.task.assigned.subject_to_others", n.Task.Title, n.Task.GetFullIdentifier(), n.Assignee.GetName()) +} + // ToMail returns the mail notification for TaskAssignedNotification func (n *TaskAssignedNotification) ToMail(lang string) *notifications.Mail { if n.Target.ID == n.Assignee.ID { // Notification to the assignee return notifications.NewMail(). From(n.Doer.GetNameAndFromEmail()). - Subject(i18n.T(lang, "notifications.task.assigned.subject_to_assignee", n.Task.Title, n.Task.GetFullIdentifier())). Greeting(i18n.T(lang, "notifications.greeting", n.Target.GetName())). Line(i18n.T(lang, "notifications.task.assigned.message_to_assignee", notifications.EscapeMarkdown(n.Doer.GetName()), notifications.EscapeMarkdown(n.Task.Title))). Action(i18n.T(lang, "notifications.common.actions.open_task"), n.Task.GetFrontendURL()). @@ -175,7 +205,6 @@ func (n *TaskAssignedNotification) ToMail(lang string) *notifications.Mail { if n.Doer.ID == n.Assignee.ID { return notifications.NewMail(). From(n.Doer.GetNameAndFromEmail()). - Subject(i18n.T(lang, "notifications.task.assigned.subject_to_others_self", n.Task.Title, n.Task.GetFullIdentifier(), n.Doer.GetName())). Greeting(i18n.T(lang, "notifications.greeting", n.Target.GetName())). Line(i18n.T(lang, "notifications.task.assigned.message_to_others_self", notifications.EscapeMarkdown(n.Doer.GetName()))). Action(i18n.T(lang, "notifications.common.actions.open_task"), n.Task.GetFrontendURL()). @@ -185,7 +214,6 @@ func (n *TaskAssignedNotification) ToMail(lang string) *notifications.Mail { // Notification to others about assignment return notifications.NewMail(). From(n.Doer.GetNameAndFromEmail()). - Subject(i18n.T(lang, "notifications.task.assigned.subject_to_others", n.Task.Title, n.Task.GetFullIdentifier(), n.Assignee.GetName())). Greeting(i18n.T(lang, "notifications.greeting", n.Target.GetName())). Line(i18n.T(lang, "notifications.task.assigned.message_to_others", notifications.EscapeMarkdown(n.Doer.GetName()), notifications.EscapeMarkdown(n.Assignee.GetName()))). Action(i18n.T(lang, "notifications.common.actions.open_task"), n.Task.GetFrontendURL()). @@ -213,10 +241,14 @@ type TaskDeletedNotification struct { Task *Task `json:"task"` } +// ToTitle returns the translated one-line title for TaskDeletedNotification +func (n *TaskDeletedNotification) ToTitle(lang string) string { + return i18n.T(lang, "notifications.task.deleted.subject", n.Task.Title, n.Task.GetFullIdentifier()) +} + // ToMail returns the mail notification for TaskDeletedNotification func (n *TaskDeletedNotification) ToMail(lang string) *notifications.Mail { return notifications.NewMail(). - Subject(i18n.T(lang, "notifications.task.deleted.subject", n.Task.Title, n.Task.GetFullIdentifier())). Line(i18n.T(lang, "notifications.task.deleted.message", notifications.EscapeMarkdown(n.Doer.GetName()), notifications.EscapeMarkdown(n.Task.Title), notifications.EscapeMarkdown(n.Task.GetFullIdentifier()))) } @@ -241,10 +273,14 @@ type ProjectCreatedNotification struct { Project *Project `json:"project"` } +// ToTitle returns the translated one-line title for ProjectCreatedNotification +func (n *ProjectCreatedNotification) ToTitle(lang string) string { + return i18n.T(lang, "notifications.project.created", n.Doer.GetName(), n.Project.Title) +} + // ToMail returns the mail notification for ProjectCreatedNotification func (n *ProjectCreatedNotification) ToMail(lang string) *notifications.Mail { return notifications.NewMail(). - Subject(i18n.T(lang, "notifications.project.created", n.Doer.GetName(), n.Project.Title)). Line(i18n.T(lang, "notifications.project.created", notifications.EscapeMarkdown(n.Doer.GetName()), notifications.EscapeMarkdown(n.Project.Title))). Action(i18n.T(lang, "notifications.common.actions.open_project"), config.ServicePublicURL.GetString()+"projects/") } @@ -266,10 +302,14 @@ type TeamMemberAddedNotification struct { Team *Team `json:"team"` } +// ToTitle returns the translated one-line title for TeamMemberAddedNotification +func (n *TeamMemberAddedNotification) ToTitle(lang string) string { + return i18n.T(lang, "notifications.team.member_added.subject", n.Doer.GetName(), n.Team.Name) +} + // ToMail returns the mail notification for TeamMemberAddedNotification func (n *TeamMemberAddedNotification) ToMail(lang string) *notifications.Mail { return notifications.NewMail(). - Subject(i18n.T(lang, "notifications.team.member_added.subject", n.Doer.GetName(), n.Team.Name)). From(n.Doer.GetNameAndFromEmail()). Greeting(i18n.T(lang, "notifications.greeting", n.Member.GetName())). Line(i18n.T(lang, "notifications.team.member_added.message", notifications.EscapeMarkdown(n.Doer.GetName()), notifications.EscapeMarkdown(n.Team.Name))). @@ -385,23 +425,23 @@ func (n *UserMentionedInTaskNotification) SubjectID() int64 { return n.Task.ID } +// ToTitle returns the translated one-line title for UserMentionedInTaskNotification +func (n *UserMentionedInTaskNotification) ToTitle(lang string) string { + if n.IsNew { + return i18n.T(lang, "notifications.task.mentioned.subject_new", n.Doer.GetName(), n.Task.Title, n.Task.GetFullIdentifier()) + } + return i18n.T(lang, "notifications.task.mentioned.subject", n.Doer.GetName(), n.Task.Title, n.Task.GetFullIdentifier()) +} + // ToMail returns the mail notification for UserMentionedInTaskNotification func (n *UserMentionedInTaskNotification) ToMail(lang string) *notifications.Mail { s := db.NewSession() defer s.Close() formattedDescription := formatMentionsForEmail(s, n.Task.Description) - var subject string - if n.IsNew { - subject = i18n.T(lang, "notifications.task.mentioned.subject_new", n.Doer.GetName(), n.Task.Title, n.Task.GetFullIdentifier()) - } else { - subject = i18n.T(lang, "notifications.task.mentioned.subject", n.Doer.GetName(), n.Task.Title, n.Task.GetFullIdentifier()) - } - mail := notifications.NewMail(). Conversational(). - From(n.Doer.GetNameAndFromEmail()). - Subject(subject) + From(n.Doer.GetNameAndFromEmail()) // Add header line action := i18n.T(lang, "notifications.common.actions.mentioned_you", n.Doer.GetName()) diff --git a/pkg/models/notifications_test.go b/pkg/models/notifications_test.go index 9e777d896..1aac48239 100644 --- a/pkg/models/notifications_test.go +++ b/pkg/models/notifications_test.go @@ -142,6 +142,90 @@ func TestUndoneTasksOverdueNotification_TitleIsMarkdownEscaped(t *testing.T) { "malicious title must render as literal text") } +func TestTaskCommentNotification_ToTitle(t *testing.T) { + doer := &user.User{ID: 1, Name: "alice", Username: "alice"} + task := &Task{ID: 42, Title: "Take out trash", Index: 7} + + t.Run("regular comment", func(t *testing.T) { + n := &TaskCommentNotification{Doer: doer, Task: task, Mentioned: false} + title := n.ToTitle("en") + assert.Contains(t, title, "Take out trash") + assert.NotContains(t, title, "alice", "regular comment title should not mention the doer") + }) + + t.Run("mention switches title", func(t *testing.T) { + n := &TaskCommentNotification{Doer: doer, Task: task, Mentioned: true} + title := n.ToTitle("en") + assert.Contains(t, title, "alice", "mentioned title should mention the doer") + assert.Contains(t, title, "Take out trash") + }) + + t.Run("regular and mentioned produce different titles", func(t *testing.T) { + regular := (&TaskCommentNotification{Doer: doer, Task: task, Mentioned: false}).ToTitle("en") + mentioned := (&TaskCommentNotification{Doer: doer, Task: task, Mentioned: true}).ToTitle("en") + assert.NotEqual(t, regular, mentioned) + }) +} + +func TestTaskAssignedNotification_ToTitle(t *testing.T) { + doer := &user.User{ID: 1, Name: "alice", Username: "alice"} + assignee := &user.User{ID: 2, Name: "bob", Username: "bob"} + third := &user.User{ID: 3, Name: "carol", Username: "carol"} + task := &Task{ID: 42, Title: "Take out trash", Index: 7} + + t.Run("to assignee themself", func(t *testing.T) { + n := &TaskAssignedNotification{Doer: doer, Task: task, Assignee: assignee, Target: assignee} + title := n.ToTitle("en") + assert.Contains(t, title, "Take out trash") + }) + + t.Run("doer assigned to themself", func(t *testing.T) { + n := &TaskAssignedNotification{Doer: doer, Task: task, Assignee: doer, Target: third} + title := n.ToTitle("en") + assert.Contains(t, title, "alice") + }) + + t.Run("doer assigned someone else, target is third party", func(t *testing.T) { + n := &TaskAssignedNotification{Doer: doer, Task: task, Assignee: assignee, Target: third} + title := n.ToTitle("en") + assert.Contains(t, title, "bob") + }) + + t.Run("three branches produce three distinct titles", func(t *testing.T) { + a := (&TaskAssignedNotification{Doer: doer, Task: task, Assignee: assignee, Target: assignee}).ToTitle("en") + b := (&TaskAssignedNotification{Doer: doer, Task: task, Assignee: doer, Target: third}).ToTitle("en") + c := (&TaskAssignedNotification{Doer: doer, Task: task, Assignee: assignee, Target: third}).ToTitle("en") + assert.NotEqual(t, a, b) + assert.NotEqual(t, a, c) + assert.NotEqual(t, b, c) + }) +} + +func TestUserMentionedInTaskNotification_ToTitle(t *testing.T) { + doer := &user.User{ID: 1, Name: "alice", Username: "alice"} + task := &Task{ID: 42, Title: "Take out trash", Index: 7} + + t.Run("existing task", func(t *testing.T) { + n := &UserMentionedInTaskNotification{Doer: doer, Task: task, IsNew: false} + title := n.ToTitle("en") + assert.Contains(t, title, "alice") + assert.Contains(t, title, "Take out trash") + }) + + t.Run("new task", func(t *testing.T) { + n := &UserMentionedInTaskNotification{Doer: doer, Task: task, IsNew: true} + title := n.ToTitle("en") + assert.Contains(t, title, "alice") + assert.Contains(t, title, "Take out trash") + }) + + t.Run("new task and existing task produce different titles", func(t *testing.T) { + existing := (&UserMentionedInTaskNotification{Doer: doer, Task: task, IsNew: false}).ToTitle("en") + newOne := (&UserMentionedInTaskNotification{Doer: doer, Task: task, IsNew: true}).ToTitle("en") + assert.NotEqual(t, existing, newOne) + }) +} + func TestReminderDueNotification_TitleIsMarkdownEscaped(t *testing.T) { originalPublicURL := config.ServicePublicURL.GetString() t.Cleanup(func() { config.ServicePublicURL.Set(originalPublicURL) }) diff --git a/pkg/notifications/notification.go b/pkg/notifications/notification.go index d50735793..cdb795dfb 100644 --- a/pkg/notifications/notification.go +++ b/pkg/notifications/notification.go @@ -45,6 +45,36 @@ type ThreadID interface { ThreadID() string } +// Titler is an optional capability for notifications that can render a +// one-line, translated title. Used as the mail subject when ToMail does not +// set one explicitly, and as the item title in the notifications feed. +type Titler interface { + ToTitle(lang string) string +} + +var registry = map[string]func() Notification{} + +// Register makes a notification type discoverable by name. It should be +// called from init() in the package that defines the type. Only notifications +// that persist to the database need to register, since only persisted +// notifications are re-hydrated from JSON (e.g. by the feed handler). +// The name is derived from the notification's own Name() method, so it stays +// in one place. +func Register(factory func() Notification) { + registry[factory().Name()] = factory +} + +// Lookup returns a fresh, empty instance of the notification type registered +// under the given name. The second return value is false if no type is +// registered with that name. +func Lookup(name string) (Notification, bool) { + f, ok := registry[name] + if !ok { + return nil, false + } + return f(), true +} + // Notifiable is an entity which can be notified. Usually a user. type Notifiable interface { // RouteForMail should return the email address this notifiable has. @@ -91,6 +121,12 @@ func notifyMail(notifiable Notifiable, notification Notification) error { return nil } + if mail.subject == "" { + if t, is := notification.(Titler); is { + mail.subject = t.ToTitle(notifiable.Lang()) + } + } + to, err := notifiable.RouteForMail() if err != nil { return err diff --git a/pkg/notifications/notification_test.go b/pkg/notifications/notification_test.go index f15eb350b..54b0be9fe 100644 --- a/pkg/notifications/notification_test.go +++ b/pkg/notifications/notification_test.go @@ -20,7 +20,9 @@ import ( "testing" "code.vikunja.io/api/pkg/db" + "code.vikunja.io/api/pkg/mail" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "xorm.io/xorm" "xorm.io/xorm/schemas" @@ -74,6 +76,68 @@ func (t *testNotifiable) Lang() string { return t.Language } +// titlerNoSubjectNotification implements Titler and intentionally omits a +// Subject from ToMail so the fallback path is exercised. +type titlerNoSubjectNotification struct { + title string +} + +func (n *titlerNoSubjectNotification) ToMail(_ string) *Mail { + return NewMail().Line("body") +} +func (n *titlerNoSubjectNotification) ToDB() interface{} { return nil } +func (n *titlerNoSubjectNotification) Name() string { return "test.titler.no.subject" } +func (n *titlerNoSubjectNotification) ToTitle(_ string) string { return n.title } + +// titlerWithExplicitSubjectNotification implements Titler but also sets an +// explicit subject in ToMail; that explicit subject must win. +type titlerWithExplicitSubjectNotification struct { + title string + subject string +} + +func (n *titlerWithExplicitSubjectNotification) ToMail(_ string) *Mail { + return NewMail().Subject(n.subject).Line("body") +} +func (n *titlerWithExplicitSubjectNotification) ToDB() interface{} { return nil } +func (n *titlerWithExplicitSubjectNotification) Name() string { return "test.titler.with.subject" } +func (n *titlerWithExplicitSubjectNotification) ToTitle(_ string) string { return n.title } + +// noTitlerNotification is the control: no Titler, no Subject, fallback must +// leave subject empty without panicking. +type noTitlerNotification struct{} + +func (n *noTitlerNotification) ToMail(_ string) *Mail { return NewMail().Line("body") } +func (n *noTitlerNotification) ToDB() interface{} { return nil } +func (n *noTitlerNotification) Name() string { return "test.no.titler" } + +// titlerRegisteredNotification is used to exercise Register/Lookup. +type titlerRegisteredNotification struct { + Title string `json:"title"` +} + +func (n *titlerRegisteredNotification) ToMail(_ string) *Mail { return NewMail().Line("body") } +func (n *titlerRegisteredNotification) ToDB() interface{} { return n } +func (n *titlerRegisteredNotification) Name() string { return "test.registry.titler" } +func (n *titlerRegisteredNotification) ToTitle(_ string) string { return n.Title } + +func TestRegistry(t *testing.T) { + Register(func() Notification { return &titlerRegisteredNotification{} }) + + t.Run("known name returns fresh instance", func(t *testing.T) { + n, ok := Lookup("test.registry.titler") + require.True(t, ok) + require.NotNil(t, n) + _, ok = n.(*titlerRegisteredNotification) + assert.True(t, ok) + }) + + t.Run("unknown name returns false", func(t *testing.T) { + _, ok := Lookup("does.not.exist") + assert.False(t, ok) + }) +} + func TestNotify(t *testing.T) { t.Run("normal", func(t *testing.T) { @@ -111,6 +175,42 @@ func TestNotify(t *testing.T) { db.AssertExists(t, "notifications", vals, true) }) + t.Run("subject fallback uses ToTitle when ToMail omits Subject", func(t *testing.T) { + mail.ResetSent() + tnf := &testNotifiable{ShouldSendNotification: true, Language: "en"} + + err := notifyMail(tnf, &titlerNoSubjectNotification{title: "From ToTitle"}) + require.NoError(t, err) + + sent := mail.LastSent() + require.NotNil(t, sent) + assert.Equal(t, "From ToTitle", sent.Subject) + }) + + t.Run("explicit Subject in ToMail wins over ToTitle", func(t *testing.T) { + mail.ResetSent() + tnf := &testNotifiable{ShouldSendNotification: true, Language: "en"} + + err := notifyMail(tnf, &titlerWithExplicitSubjectNotification{title: "From ToTitle", subject: "Explicit"}) + require.NoError(t, err) + + sent := mail.LastSent() + require.NotNil(t, sent) + assert.Equal(t, "Explicit", sent.Subject) + }) + + t.Run("no fallback when notification does not implement Titler", func(t *testing.T) { + mail.ResetSent() + tnf := &testNotifiable{ShouldSendNotification: true, Language: "en"} + + err := notifyMail(tnf, &noTitlerNotification{}) + require.NoError(t, err) + + sent := mail.LastSent() + require.NotNil(t, sent) + assert.Empty(t, sent.Subject) + }) + t.Run("disabled notifiable", func(t *testing.T) { s := db.NewSession() diff --git a/pkg/routes/feeds/auth.go b/pkg/routes/feeds/auth.go new file mode 100644 index 000000000..419fd2ccd --- /dev/null +++ b/pkg/routes/feeds/auth.go @@ -0,0 +1,86 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-present Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package feeds + +import ( + "strings" + + "code.vikunja.io/api/pkg/db" + "code.vikunja.io/api/pkg/log" + "code.vikunja.io/api/pkg/models" + "code.vikunja.io/api/pkg/user" + "xorm.io/xorm" + + "github.com/labstack/echo/v5" +) + +func checkAPIToken(s *xorm.Session, username, token string) (*user.User, error) { + apiToken, u, err := models.ValidateTokenAndGetOwner(s, token) + if err != nil { + return nil, err + } + if apiToken == nil || u == nil { + return nil, nil + } + + if !apiToken.HasFeedsAccess() { + log.Debugf("[feeds auth] API token %d does not have feeds access permission", apiToken.ID) + return nil, nil + } + + if u.Username != username { + log.Debugf("[feeds auth] API token %d owner %s does not match provided username %s", apiToken.ID, u.Username, username) + return nil, nil + } + + return u, nil +} + +// BasicAuth authenticates feed requests. Only API tokens are accepted — +// password and LDAP credentials are rejected outright because feed URLs are +// commonly exported, shared, or cached by feed readers. +func BasicAuth(c *echo.Context, username, password string) (bool, error) { + if !strings.HasPrefix(password, models.APITokenPrefix) { + return false, nil + } + // GetTokenFromTokenString slices password[len-8:] without a length check, + // so a stray "tk_" or other short prefix-only string would panic before + // the credentials could be rejected. Real tokens are far longer than + // prefix+8, so anything shorter is invalid by construction. + if len(password) < len(models.APITokenPrefix)+8 { + return false, nil + } + + s := db.NewSession() + defer s.Close() + + u, err := checkAPIToken(s, username, password) + if err != nil { + log.Errorf("Error during API token auth for feeds: %v", err) + return false, nil + } + if u == nil { + return false, nil + } + if u.IsBot() { + log.Warningf("Feed auth rejected for bot user %d", u.ID) + return false, nil + } + + c.Set("userBasicAuth", u) + return true, nil +} diff --git a/pkg/routes/feeds/auth_test.go b/pkg/routes/feeds/auth_test.go new file mode 100644 index 000000000..5a8359fef --- /dev/null +++ b/pkg/routes/feeds/auth_test.go @@ -0,0 +1,113 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-present Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package feeds + +import ( + "net/http" + "net/http/httptest" + "testing" + + "code.vikunja.io/api/pkg/db" + "code.vikunja.io/api/pkg/user" + + "github.com/labstack/echo/v5" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + feedsTokenUser13Valid = "tk_feeds_access_token_user_0013_feed0013" // owner_id 13, feeds scope + caldavOnlyToken = "tk_caldav_api_token_test_00000000aabbccdd" // owner_id 15, caldav scope only + expiredTasksToken = "tk_a5e6f92ddbad68f49ee2c63e52174db0235008c8" // expired + tasksScopedTokenOwner1 = "tk_2eef46f40ebab3304919ab2e7e39993f75f29d2e" // owner_id 1, no feeds scope +) + +func newContext() *echo.Context { + e := echo.New() + req := httptest.NewRequest(http.MethodGet, "/feeds/notifications.atom", nil) + rec := httptest.NewRecorder() + return e.NewContext(req, rec) +} + +func TestBasicAuth(t *testing.T) { + db.LoadAndAssertFixtures(t) + + t.Run("rejects non-token password without touching db", func(t *testing.T) { + c := newContext() + ok, err := BasicAuth(c, "user1", "plaintextpassword") + require.NoError(t, err) + assert.False(t, ok) + assert.Nil(t, c.Get("userBasicAuth")) + }) + + t.Run("rejects unknown token", func(t *testing.T) { + c := newContext() + ok, err := BasicAuth(c, "user1", "tk_nonexistent_token_value_aaaaaaaaaaaaaaaa") + require.NoError(t, err) + assert.False(t, ok) + }) + + t.Run("rejects token shorter than prefix+8 without panicking", func(t *testing.T) { + // Real tokens are far longer; anything shorter is invalid by + // construction and would otherwise panic inside GetTokenFromTokenString. + for _, short := range []string{"tk_", "tk_abc", "tk_1234567"} { + c := newContext() + ok, err := BasicAuth(c, "user1", short) + require.NoError(t, err) + assert.False(t, ok, "short token %q must be rejected", short) + } + }) + + t.Run("rejects token whose owner username does not match", func(t *testing.T) { + c := newContext() + // feedsTokenUser13Valid belongs to user 13; supply a different username. + ok, err := BasicAuth(c, "wrongname", feedsTokenUser13Valid) + require.NoError(t, err) + assert.False(t, ok) + }) + + t.Run("rejects token without feeds scope", func(t *testing.T) { + c := newContext() + ok, err := BasicAuth(c, "user1", tasksScopedTokenOwner1) + require.NoError(t, err) + assert.False(t, ok) + }) + + t.Run("rejects token with caldav scope but no feeds scope", func(t *testing.T) { + c := newContext() + ok, err := BasicAuth(c, "user15", caldavOnlyToken) + require.NoError(t, err) + assert.False(t, ok) + }) + + t.Run("rejects expired token", func(t *testing.T) { + c := newContext() + ok, err := BasicAuth(c, "user1", expiredTasksToken) + require.NoError(t, err) + assert.False(t, ok) + }) + + t.Run("accepts valid token with feeds scope", func(t *testing.T) { + c := newContext() + ok, err := BasicAuth(c, "user13", feedsTokenUser13Valid) + require.NoError(t, err) + assert.True(t, ok) + u, is := c.Get("userBasicAuth").(*user.User) + require.True(t, is) + assert.Equal(t, int64(13), u.ID) + }) +} diff --git a/pkg/routes/feeds/handler.go b/pkg/routes/feeds/handler.go new file mode 100644 index 000000000..9d0794c76 --- /dev/null +++ b/pkg/routes/feeds/handler.go @@ -0,0 +1,95 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-present Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package feeds + +import ( + "encoding/json" + "net/http" + "strconv" + "time" + + "code.vikunja.io/api/pkg/config" + "code.vikunja.io/api/pkg/db" + "code.vikunja.io/api/pkg/i18n" + "code.vikunja.io/api/pkg/notifications" + "code.vikunja.io/api/pkg/user" + + "github.com/gorilla/feeds" + "github.com/labstack/echo/v5" +) + +const feedItemLimit = 50 + +// NotificationsAtomFeed serves the authenticated user's notifications as an +// Atom feed. Notifications are not marked as read by being fetched here. +func NotificationsAtomFeed(c *echo.Context) error { + u, ok := c.Get("userBasicAuth").(*user.User) + if !ok { + return echo.NewHTTPError(http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized)) + } + + s := db.NewSession() + defer s.Close() + + rows, _, _, err := notifications.GetNotificationsForUser(s, u.ID, feedItemLimit, 0) + if err != nil { + return err + } + + publicURL := config.ServicePublicURL.GetString() + feed := &feeds.Feed{ + Title: i18n.T(u.Language, "feeds.notifications.title", u.GetName()), + Link: &feeds.Link{Href: publicURL + "feeds/notifications.atom"}, + Author: &feeds.Author{Name: u.GetName()}, + Updated: time.Now(), + } + + for _, row := range rows { + typed, ok := notifications.Lookup(row.Name) + if !ok { + continue + } + + raw, err := json.Marshal(row.Notification) + if err != nil { + continue + } + if err := json.Unmarshal(raw, typed); err != nil { + continue + } + + titler, ok := typed.(notifications.Titler) + if !ok { + continue + } + + feed.Items = append(feed.Items, &feeds.Item{ + Id: "vikunja-notification-" + strconv.FormatInt(row.ID, 10), + Title: titler.ToTitle(u.Language), + Created: row.Created, + Link: &feeds.Link{Href: publicURL}, + }) + } + + atom, err := feed.ToAtom() + if err != nil { + return err + } + + c.Response().Header().Set(echo.HeaderContentType, "application/atom+xml; charset=utf-8") + return c.String(http.StatusOK, atom) +} diff --git a/pkg/routes/feeds/handler_test.go b/pkg/routes/feeds/handler_test.go new file mode 100644 index 000000000..dfc984d68 --- /dev/null +++ b/pkg/routes/feeds/handler_test.go @@ -0,0 +1,73 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-present Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package feeds + +import ( + "encoding/xml" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "code.vikunja.io/api/pkg/db" + "code.vikunja.io/api/pkg/user" + + "github.com/labstack/echo/v5" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNotificationsAtomFeed(t *testing.T) { + db.LoadAndAssertFixtures(t) + + t.Run("returns valid atom XML for authenticated user with no notifications", func(t *testing.T) { + e := echo.New() + req := httptest.NewRequest(http.MethodGet, "/feeds/notifications.atom", nil) + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + + c.Set("userBasicAuth", &user.User{ID: 1, Name: "User 1", Username: "user1", Language: "en"}) + + err := NotificationsAtomFeed(c) + require.NoError(t, err) + + assert.Equal(t, http.StatusOK, rec.Code) + assert.True(t, strings.HasPrefix(rec.Header().Get(echo.HeaderContentType), "application/atom+xml"), + "unexpected content type: %s", rec.Header().Get(echo.HeaderContentType)) + + // Must be parseable as XML. + var doc struct { + XMLName xml.Name `xml:"feed"` + Title string `xml:"title"` + } + require.NoError(t, xml.Unmarshal(rec.Body.Bytes(), &doc)) + assert.Contains(t, doc.Title, "User 1", "feed title should include the user's name") + }) + + t.Run("returns 401 when context has no authenticated user", func(t *testing.T) { + e := echo.New() + req := httptest.NewRequest(http.MethodGet, "/feeds/notifications.atom", nil) + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + + err := NotificationsAtomFeed(c) + require.Error(t, err) + httpErr, ok := err.(*echo.HTTPError) + require.True(t, ok, "expected echo.HTTPError, got %T", err) + assert.Equal(t, http.StatusUnauthorized, httpErr.Code) + }) +} diff --git a/pkg/routes/feeds/main_test.go b/pkg/routes/feeds/main_test.go new file mode 100644 index 000000000..6f58af476 --- /dev/null +++ b/pkg/routes/feeds/main_test.go @@ -0,0 +1,41 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-present Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package feeds + +import ( + "os" + "testing" + + "code.vikunja.io/api/pkg/config" + "code.vikunja.io/api/pkg/events" + "code.vikunja.io/api/pkg/files" + "code.vikunja.io/api/pkg/i18n" + "code.vikunja.io/api/pkg/log" + "code.vikunja.io/api/pkg/models" + "code.vikunja.io/api/pkg/user" +) + +func TestMain(m *testing.M) { + log.InitLogger() + config.InitDefaultConfig() + i18n.Init() + files.InitTests() + user.InitTests() + models.SetupTests() + events.Fake() + os.Exit(m.Run()) +} diff --git a/pkg/routes/routes.go b/pkg/routes/routes.go index e37927e5f..c4ce0a002 100644 --- a/pkg/routes/routes.go +++ b/pkg/routes/routes.go @@ -81,6 +81,7 @@ import ( apiv1 "code.vikunja.io/api/pkg/routes/api/v1" adminapi "code.vikunja.io/api/pkg/routes/api/v1/admin" "code.vikunja.io/api/pkg/routes/caldav" + "code.vikunja.io/api/pkg/routes/feeds" "code.vikunja.io/api/pkg/version" "code.vikunja.io/api/pkg/web/handler" ws "code.vikunja.io/api/pkg/websocket" @@ -259,6 +260,11 @@ func RegisterRoutes(e *echo.Echo) { registerCalDavRoutes(c) } + // Feeds routes (Atom feed for user notifications) + f := e.Group("/feeds") + f.Use(middleware.BasicAuth(feeds.BasicAuth)) + f.GET("/notifications.atom", feeds.NotificationsAtomFeed) + // healthcheck e.GET("/health", HealthcheckHandler) @@ -282,8 +288,10 @@ func RegisterRoutes(e *echo.Echo) { // Since it is not possible to register this middleware just for the api group, // we just disable it when for caldav requests. // Caldav requires OPTIONS requests to be answered in a specific manner, - // not doing this would break the caldav implementation - return strings.HasPrefix(context.Path(), "/dav") + // not doing this would break the caldav implementation. + // Feed readers are server-side and don't need CORS either. + p := context.Path() + return strings.HasPrefix(p, "/dav") || strings.HasPrefix(p, "/feeds") }, })) } From fc373ae963202cadf2e059030907b978c43091e1 Mon Sep 17 00:00:00 2001 From: Tink bot Date: Fri, 15 May 2026 14:11:33 +0000 Subject: [PATCH 007/480] test(trello): serve testimage from local server instead of vikunja.io Mirrors the Todoist migration test setup so TestConvertTrelloToVikunja no longer depends on https://vikunja.io/testimage.jpg being reachable. --- pkg/modules/migration/trello/trello_test.go | 22 ++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/pkg/modules/migration/trello/trello_test.go b/pkg/modules/migration/trello/trello_test.go index 563b4afc8..970f9c3cb 100644 --- a/pkg/modules/migration/trello/trello_test.go +++ b/pkg/modules/migration/trello/trello_test.go @@ -18,10 +18,13 @@ package trello import ( "bytes" + "net/http" + "net/http/httptest" "os" "testing" "time" + "code.vikunja.io/api/pkg/config" "code.vikunja.io/api/pkg/files" "code.vikunja.io/api/pkg/models" @@ -228,7 +231,7 @@ func getTestBoard(t *testing.T) ([]*trello.Board, time.Time) { }, }, } - trelloData[0].Prefs.BackgroundImage = "https://vikunja.io/testimage.jpg" // Using an image which we are hosting, so it'll still be up + trelloData[0].Prefs.BackgroundImage = "https://vikunja.io/testimage.jpg" // Overridden in TestConvertTrelloToVikunja to point at a local test server. return trelloData, time1 } @@ -239,6 +242,23 @@ func TestConvertTrelloToVikunja(t *testing.T) { exampleFile, err := os.ReadFile("../testimage.jpg") require.NoError(t, err) + // Serve the attachment from a local test server so the test does not depend + // on an external host being reachable. The SSRF-safe client used by + // migration.DownloadFile rejects non-routable IPs by default, so allow them + // for the duration of this test. + prevAllowNonRoutable := config.OutgoingRequestsAllowNonRoutableIPs.GetBool() + config.OutgoingRequestsAllowNonRoutableIPs.Set("true") + t.Cleanup(func() { + config.OutgoingRequestsAllowNonRoutableIPs.Set(prevAllowNonRoutable) + }) + attachmentServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write(exampleFile) + })) + t.Cleanup(attachmentServer.Close) + + trelloData[0].Prefs.BackgroundImage = attachmentServer.URL + "/testimage.jpg" + trelloData[0].Lists[0].Cards[0].Attachments[0].URL = attachmentServer.URL + "/testimage.jpg" + expectedHierarchyOrg := map[string][]*models.ProjectWithTasksAndBuckets{ "orgid": { { From 6b143078960c9b27079d3a09c66ea51a803a4bbe Mon Sep 17 00:00:00 2001 From: Tink bot Date: Fri, 15 May 2026 15:01:19 +0000 Subject: [PATCH 008/480] test(trello): drop redundant BackgroundImage assignment in getTestBoard --- pkg/modules/migration/trello/trello_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/modules/migration/trello/trello_test.go b/pkg/modules/migration/trello/trello_test.go index 970f9c3cb..7f64724b9 100644 --- a/pkg/modules/migration/trello/trello_test.go +++ b/pkg/modules/migration/trello/trello_test.go @@ -231,7 +231,6 @@ func getTestBoard(t *testing.T) ([]*trello.Board, time.Time) { }, }, } - trelloData[0].Prefs.BackgroundImage = "https://vikunja.io/testimage.jpg" // Overridden in TestConvertTrelloToVikunja to point at a local test server. return trelloData, time1 } From b9e3bb95fae565cfb5a8ce3319fb10a1b8f622b6 Mon Sep 17 00:00:00 2001 From: kolaente Date: Fri, 15 May 2026 19:28:29 +0200 Subject: [PATCH 009/480] feat(frontend): add Atom feed settings page and notifications discovery (#2760) --- frontend/src/components/misc/Icon.ts | 2 + .../notifications/Notifications.vue | 26 ++++++- frontend/src/i18n/lang/en.json | 10 ++- frontend/src/router/index.ts | 5 ++ frontend/src/views/user/Settings.vue | 4 + frontend/src/views/user/settings/AtomFeed.vue | 75 +++++++++++++++++++ 6 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 frontend/src/views/user/settings/AtomFeed.vue diff --git a/frontend/src/components/misc/Icon.ts b/frontend/src/components/misc/Icon.ts index 558daeff5..4eda41616 100644 --- a/frontend/src/components/misc/Icon.ts +++ b/frontend/src/components/misc/Icon.ts @@ -58,6 +58,7 @@ import { faPlay, faPlus, faPowerOff, + faRss, faSearch, faShareAlt, faSignOutAlt, @@ -168,6 +169,7 @@ library.add(faPercent) library.add(faPlay) library.add(faPlus) library.add(faPowerOff) +library.add(faRss) library.add(faSave) library.add(faSearch) library.add(faShareAlt) diff --git a/frontend/src/components/notifications/Notifications.vue b/frontend/src/components/notifications/Notifications.vue index c8e702142..cec662781 100644 --- a/frontend/src/components/notifications/Notifications.vue +++ b/frontend/src/components/notifications/Notifications.vue @@ -24,7 +24,18 @@ ref="popup" class="notifications-list" > - {{ $t('notification.title') }} +
+ {{ $t('notification.title') }} + + {{ $t('notification.subscribeFeed') }} + + +
import('@/views/user/settings/DataExport.vue'), }, + { + path: '/user/settings/feeds', + name: 'user.settings.feeds', + component: () => import('@/views/user/settings/AtomFeed.vue'), + }, { path: '/user/settings/deletion', name: 'user.settings.deletion', diff --git a/frontend/src/views/user/Settings.vue b/frontend/src/views/user/Settings.vue index aa1b92350..4879cf617 100644 --- a/frontend/src/views/user/Settings.vue +++ b/frontend/src/views/user/Settings.vue @@ -67,6 +67,10 @@ const navigationItems = computed(() => { routeName: 'user.settings.caldav', condition: caldavEnabled.value, }, + { + title: t('user.settings.feeds.title'), + routeName: 'user.settings.feeds', + }, { title: t('user.settings.apiTokens.title'), routeName: 'user.settings.apiTokens', diff --git a/frontend/src/views/user/settings/AtomFeed.vue b/frontend/src/views/user/settings/AtomFeed.vue new file mode 100644 index 000000000..0a1e29f29 --- /dev/null +++ b/frontend/src/views/user/settings/AtomFeed.vue @@ -0,0 +1,75 @@ + + + From d4e186a02406396eec48a569b823c9c372422032 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 09:25:44 +0000 Subject: [PATCH 010/480] chore(deps): update dependency caniuse-lite to v1.0.30001793 --- frontend/package.json | 2 +- frontend/pnpm-lock.yaml | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index fc79cda7d..0f6ea8f8a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -125,7 +125,7 @@ "@vueuse/shared": "14.3.0", "autoprefixer": "10.5.0", "browserslist": "4.28.2", - "caniuse-lite": "1.0.30001792", + "caniuse-lite": "1.0.30001793", "csstype": "3.2.3", "esbuild": "0.28.0", "eslint": "9.39.4", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index f681726b6..f7c45e291 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -233,8 +233,8 @@ importers: specifier: 4.28.2 version: 4.28.2 caniuse-lite: - specifier: 1.0.30001792 - version: 1.0.30001792 + specifier: 1.0.30001793 + version: 1.0.30001793 csstype: specifier: 3.2.3 version: 3.2.3 @@ -3482,8 +3482,8 @@ packages: resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} engines: {node: '>=16'} - caniuse-lite@1.0.30001792: - resolution: {integrity: sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==} + caniuse-lite@1.0.30001793: + resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==} capture-website@4.2.0: resolution: {integrity: sha512-EmkSn36CXTC8tUsS6aNmvvsdpfVTYYkuRp7U5bV9gcJwcDbqqA5c0Op/iskYPKtDdOkuVp61mjn/LLywX0h7cw==} @@ -10192,7 +10192,7 @@ snapshots: autoprefixer@10.5.0(postcss@8.5.14): dependencies: browserslist: 4.28.2 - caniuse-lite: 1.0.30001792 + caniuse-lite: 1.0.30001793 fraction.js: 5.3.4 picocolors: 1.1.1 postcss: 8.5.14 @@ -10321,7 +10321,7 @@ snapshots: browserslist@4.28.2: dependencies: baseline-browser-mapping: 2.10.12 - caniuse-lite: 1.0.30001792 + caniuse-lite: 1.0.30001793 electron-to-chromium: 1.5.329 node-releases: 2.0.36 update-browserslist-db: 1.2.3(browserslist@4.28.2) @@ -10380,7 +10380,7 @@ snapshots: camelcase@8.0.0: {} - caniuse-lite@1.0.30001792: {} + caniuse-lite@1.0.30001793: {} capture-website@4.2.0(typescript@5.9.3): dependencies: From 4a16df8af1a832caf3f35e99771e66c5a0bf0c80 Mon Sep 17 00:00:00 2001 From: bradmartin333 Date: Fri, 15 May 2026 17:59:18 -0600 Subject: [PATCH 011/480] fix(frontend): ensure text color inherits in filter autocomplete component --- frontend/src/components/input/filter/FilterCommandsList.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/components/input/filter/FilterCommandsList.vue b/frontend/src/components/input/filter/FilterCommandsList.vue index fe1348bd4..34c92887b 100644 --- a/frontend/src/components/input/filter/FilterCommandsList.vue +++ b/frontend/src/components/input/filter/FilterCommandsList.vue @@ -135,6 +135,7 @@ defineExpose({ inline-size: 100%; text-align: start; background: transparent; + color: inherit; border-radius: $radius; border: 0; padding: 0.375rem 0.5rem; From dbccbd64ef76a1508a63a5a3fd1e4edadafeb251 Mon Sep 17 00:00:00 2001 From: kolaente Date: Mon, 18 May 2026 13:23:43 +0200 Subject: [PATCH 012/480] fix(relations): correctly position quick add magic hint (#2766) --- .../src/components/tasks/partials/RelatedTasks.vue | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/tasks/partials/RelatedTasks.vue b/frontend/src/components/tasks/partials/RelatedTasks.vue index 3f9eef36b..8e81a2083 100644 --- a/frontend/src/components/tasks/partials/RelatedTasks.vue +++ b/frontend/src/components/tasks/partials/RelatedTasks.vue @@ -77,7 +77,7 @@ - +
component is too much resulting in a From 52f3dd6806299276c128a93155c5e03b37c07035 Mon Sep 17 00:00:00 2001 From: Tink bot Date: Mon, 18 May 2026 17:39:13 +0000 Subject: [PATCH 013/480] fix(ci): commit newly added Crowdin translation files The Crowdin sync workflow used `git diff --quiet` and `git commit -am`, both of which only consider tracked files. New language files downloaded by Crowdin (e.g. el-GR, th-TH) were therefore left untracked and silently dropped on each run. Switch the change check to `git status --porcelain` scoped to the translation directories and stage them explicitly before committing so new locales are included. --- .github/workflows/crowdin.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/crowdin.yml b/.github/workflows/crowdin.yml index b364de00d..93d5bbf71 100644 --- a/.github/workflows/crowdin.yml +++ b/.github/workflows/crowdin.yml @@ -41,7 +41,7 @@ jobs: - name: Check for changes id: check_changes run: | - if git diff --quiet; then + if [ -z "$(git status --porcelain pkg/i18n/lang frontend/src/i18n/lang)" ]; then echo "changes_exist=0" >> "$GITHUB_OUTPUT" else echo "changes_exist=1" >> "$GITHUB_OUTPUT" @@ -51,7 +51,8 @@ jobs: run: | git config --local user.email "bot@vikunja.io" git config --local user.name "Frederick [Bot]" - git commit -am "chore(i18n): update translations via Crowdin" + git add pkg/i18n/lang frontend/src/i18n/lang + git commit -m "chore(i18n): update translations via Crowdin" - name: Push changes if: steps.check_changes.outputs.changes_exist != '0' uses: ad-m/github-push-action@master From 941f6bb1bef23da0ce65f053c28c1ba3ca054599 Mon Sep 17 00:00:00 2001 From: Tink bot Date: Mon, 18 May 2026 15:22:27 +0000 Subject: [PATCH 014/480] fix(tooltip): show tooltips in top layer when inside modal dialog Tooltips on relative dates (and other content) were invisible when a task was opened in the modal. The modal uses opened via showModal(), which places it in the browser's top layer. floating-vue teleports tooltips to by default, so they were rendered *below* the dialog backdrop and hidden behind it. Wrap the v-tooltip directive to detect the nearest ancestor of the target and use it as the tooltip's container, keeping the tooltip in the same top-layer context as the modal it belongs to. Tooltips outside any dialog still teleport to as before. --- frontend/src/directives/tooltip.ts | 49 ++++++++++++++++++++++++++++++ frontend/src/main.ts | 4 +-- 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 frontend/src/directives/tooltip.ts diff --git a/frontend/src/directives/tooltip.ts b/frontend/src/directives/tooltip.ts new file mode 100644 index 000000000..4c76d6ed6 --- /dev/null +++ b/frontend/src/directives/tooltip.ts @@ -0,0 +1,49 @@ +import type {Directive, DirectiveBinding} from 'vue' +import {vTooltip} from 'floating-vue' + +// When a tooltip target lives inside a opened via showModal(), the +// dialog is in the browser's top layer. floating-vue teleports tooltips to +// by default, so they render *below* the dialog's ::backdrop and are +// not visible. Teleporting them into the dialog keeps them in the top layer. +function buildBinding(el: Element, binding: DirectiveBinding): DirectiveBinding { + const dialog = el.closest('dialog') + if (!dialog) { + return binding + } + + const value = binding.value + let normalized: Record + if (typeof value === 'string') { + normalized = {content: value} + } else if (value && typeof value === 'object') { + normalized = {...value as Record} + } else { + return binding + } + + if (normalized.container === undefined) { + normalized.container = dialog + } + + return {...binding, value: normalized} +} + +// Bind via `mounted` rather than `beforeMount` so the element is already +// attached to the DOM — otherwise `el.closest('dialog')` cannot find the +// dialog ancestor. +const tooltip: Directive = { + mounted(el, binding) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ;(vTooltip as any).beforeMount(el, buildBinding(el, binding)) + }, + updated(el, binding) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ;(vTooltip as any).updated(el, buildBinding(el, binding)) + }, + beforeUnmount(el) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ;(vTooltip as any).beforeUnmount(el) + }, +} + +export default tooltip diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 1a81de2a2..6f64e8d8c 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -38,7 +38,7 @@ if (window.API_URL.endsWith('/')) { // directives import focus from '@/directives/focus' -import {vTooltip} from 'floating-vue' +import tooltip from '@/directives/tooltip' import 'floating-vue/dist/style.css' import shortcut from '@/directives/shortcut' import testid from '@/directives/testid' @@ -66,7 +66,7 @@ setLanguage(browserLanguage).then(() => { app.use(Notifications) app.directive('focus', focus) - app.directive('tooltip', vTooltip) + app.directive('tooltip', tooltip) app.directive('shortcut', shortcut) app.directive('cy', testid) From f349b6360e25829f28a28db92bbd682e8c9077e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 17:38:02 +0000 Subject: [PATCH 015/480] chore(deps): bump brace-expansion from 5.0.5 to 5.0.6 in /frontend Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 5.0.5 to 5.0.6. - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.5...v5.0.6) --- updated-dependencies: - dependency-name: brace-expansion dependency-version: 5.0.6 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- frontend/pnpm-lock.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index f7c45e291..e6ef1c0bf 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -3417,8 +3417,8 @@ packages: resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} engines: {node: '>=18'} - brace-expansion@5.0.5: - resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} engines: {node: 18 || 20 || >=22} braces@3.0.3: @@ -10310,7 +10310,7 @@ snapshots: widest-line: 5.0.0 wrap-ansi: 9.0.2 - brace-expansion@5.0.5: + brace-expansion@5.0.6: dependencies: balanced-match: 4.0.3 @@ -12145,7 +12145,7 @@ snapshots: minimatch@10.2.4: dependencies: - brace-expansion: 5.0.5 + brace-expansion: 5.0.6 minimist-options@4.1.0: dependencies: From ad457488fdb3a134f3efabdd3edc05007002e2a5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 12:08:10 +0000 Subject: [PATCH 016/480] chore(deps): update dependency vue-tsc to v3.3.0 --- frontend/package.json | 2 +- frontend/pnpm-lock.yaml | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 0f6ea8f8a..8929a1482 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -154,7 +154,7 @@ "vite-plugin-vue-devtools": "8.1.2", "vite-svg-loader": "5.1.1", "vitest": "4.1.6", - "vue-tsc": "3.2.9", + "vue-tsc": "3.3.0", "wait-on": "9.0.10", "workbox-cli": "7.4.1", "ws": "8.20.1" diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index e6ef1c0bf..2ddc24715 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -320,8 +320,8 @@ importers: specifier: 4.1.6 version: 4.1.6(@types/node@24.12.4)(happy-dom@20.9.0)(jsdom@27.4.0)(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3)) vue-tsc: - specifier: 3.2.9 - version: 3.2.9(typescript@5.9.3) + specifier: 3.3.0 + version: 3.3.0(typescript@5.9.3) wait-on: specifier: 9.0.10 version: 9.0.10 @@ -3140,8 +3140,8 @@ packages: typescript: optional: true - '@vue/language-core@3.2.9': - resolution: {integrity: sha512-ie0ojt/0fU/GfIogh+zgHbaYRPlt9S+cLOxcWwF7nTSFh897BVgnFKL2byT4kpp1mlqYWZ2psGwSniyE2xsxYw==} + '@vue/language-core@3.3.0': + resolution: {integrity: sha512-EyUxq1b8Yoxk6hQ6X33BIRnfFLb9Rbm9w/8G8y6uMxlQu7CW7yy9JS/z54xSpIvBvVWX6Lt5v1aBGwmrqD4aJw==} '@vue/reactivity@3.5.27': resolution: {integrity: sha512-vvorxn2KXfJ0nBEnj4GYshSgsyMNFnIQah/wczXlsNXt+ijhugmW+PpJ2cNPe4V6jpnBcs0MhCODKllWG+nvoQ==} @@ -6861,8 +6861,8 @@ packages: peerDependencies: vue: ^3.5.0 - vue-tsc@3.2.9: - resolution: {integrity: sha512-qm8/nbo+9eZc1SCndm9wT+gq23pM+wRIdHY0wjm83B3lIginHTwcdrLUyTrKjDWXbMVNjKegNrnymhpdqnCL3A==} + vue-tsc@3.3.0: + resolution: {integrity: sha512-kY8RcoTOENASi0P1GLPvJgA2+hoGF+t8We1UGgmnAb1r/GjTUMSE3zz+WGfjPORZNnBHdAt67sVPhBLXWunkeg==} hasBin: true peerDependencies: typescript: '>=5.0.0' @@ -10019,7 +10019,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@vue/language-core@3.2.9': + '@vue/language-core@3.3.0': dependencies: '@volar/language-core': 2.4.28 '@vue/compiler-dom': 3.5.27 @@ -14132,10 +14132,10 @@ snapshots: '@vue/devtools-api': 6.6.4 vue: 3.5.27(typescript@5.9.3) - vue-tsc@3.2.9(typescript@5.9.3): + vue-tsc@3.3.0(typescript@5.9.3): dependencies: '@volar/typescript': 2.4.28 - '@vue/language-core': 3.2.9 + '@vue/language-core': 3.3.0 typescript: 5.9.3 vue@3.5.27(typescript@5.9.3): From faeeebe66169dde08628f595fe79591174613302 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 18:39:17 +0000 Subject: [PATCH 017/480] chore(deps): update dev-dependencies to v8.59.4 --- frontend/package.json | 4 +- frontend/pnpm-lock.yaml | 150 ++++++++++++++++++++-------------------- 2 files changed, 77 insertions(+), 77 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 8929a1482..5bc5aaf52 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -116,8 +116,8 @@ "@types/node": "24.12.4", "@types/sortablejs": "1.15.9", "@types/ws": "8.18.1", - "@typescript-eslint/eslint-plugin": "8.59.3", - "@typescript-eslint/parser": "8.59.3", + "@typescript-eslint/eslint-plugin": "8.59.4", + "@typescript-eslint/parser": "8.59.4", "@vitejs/plugin-vue": "6.0.7", "@vue/eslint-config-typescript": "14.7.0", "@vue/test-utils": "2.4.10", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 2ddc24715..24bf97e1d 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -206,17 +206,17 @@ importers: specifier: 8.18.1 version: 8.18.1 '@typescript-eslint/eslint-plugin': - specifier: 8.59.3 - version: 8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + specifier: 8.59.4 + version: 8.59.4(@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': - specifier: 8.59.3 - version: 8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + specifier: 8.59.4 + version: 8.59.4(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) '@vitejs/plugin-vue': specifier: 6.0.7 version: 6.0.7(vite@7.3.3(@types/node@24.12.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.31.6)(yaml@2.8.3))(vue@3.5.27(typescript@5.9.3)) '@vue/eslint-config-typescript': specifier: 14.7.0 - version: 14.7.0(eslint-plugin-vue@10.9.1(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + version: 14.7.0(eslint-plugin-vue@10.9.1(@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) '@vue/test-utils': specifier: 2.4.10 version: 2.4.10(@vue/compiler-dom@3.5.27)(@vue/server-renderer@3.5.27(vue@3.5.27(typescript@5.9.3)))(vue@3.5.27(typescript@5.9.3)) @@ -249,7 +249,7 @@ importers: version: 1.5.0(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-vue: specifier: 10.9.1 - version: 10.9.1(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))) + version: 10.9.1(@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))) happy-dom: specifier: 20.9.0 version: 20.9.0 @@ -2872,11 +2872,11 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/eslint-plugin@8.59.3': - resolution: {integrity: sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw==} + '@typescript-eslint/eslint-plugin@8.59.4': + resolution: {integrity: sha512-PegsU+XfyJJNjd4+u/k6f9yTyp0lEXXiPopUNobZcIAUJFGICFLN+sP0Rb3JehVmiij1Ph0dFGYqODoRo/2+6A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.59.3 + '@typescript-eslint/parser': ^8.59.4 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' @@ -2887,8 +2887,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.59.3': - resolution: {integrity: sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==} + '@typescript-eslint/parser@8.59.4': + resolution: {integrity: sha512-zORHqO/tuhxY1zWuTvMUqddRxpiFJ72xVfcNoWpqdLjs6lfPbuQBJuW4pk+49/uBMy7Ssr4bzgjiKmmDB1UbZQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -2906,8 +2906,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.59.3': - resolution: {integrity: sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==} + '@typescript-eslint/project-service@8.59.4': + resolution: {integrity: sha512-Ly00Vu4oAacfDeHp2Zg85ioNG6l8HG+tN1D7J+xTHSxu9y0awYKJ2zH1rFBn8ZSfuGK+7FxK3Cgl3uAz0aZZLg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -2920,8 +2920,8 @@ packages: resolution: {integrity: sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.59.3': - resolution: {integrity: sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==} + '@typescript-eslint/scope-manager@8.59.4': + resolution: {integrity: sha512-mUeR/3H1WrTAddJrwut8OoPjfauaztMQmRwV5fQTUyNVJCLiUXXe4lGEyYIL2oFDpP7UtgbGJXCt72wT0z2S3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.56.0': @@ -2936,14 +2936,14 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/tsconfig-utils@8.59.2': - resolution: {integrity: sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==} + '@typescript-eslint/tsconfig-utils@8.59.3': + resolution: {integrity: sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/tsconfig-utils@8.59.3': - resolution: {integrity: sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==} + '@typescript-eslint/tsconfig-utils@8.59.4': + resolution: {integrity: sha512-DLCpnKgD4alVxTBSKulK+gU1KCqOgUXfDRDXh2mZgzokQKa/70ax93I2uVO3m/LLvIAtWZIFoiifudmIqAxpMA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -2955,8 +2955,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.59.3': - resolution: {integrity: sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ==} + '@typescript-eslint/type-utils@8.59.4': + resolution: {integrity: sha512-uonTuPAAKr9XaBGqJ3LjYTh72zy5DyGesljO9gtmk/eFW0W1fRHjnwVYKB35Lm8d5Q5CluEW3gPHjTvZTmgrfA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -2970,14 +2970,14 @@ packages: resolution: {integrity: sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.59.2': - resolution: {integrity: sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.59.3': resolution: {integrity: sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.59.4': + resolution: {integrity: sha512-F1o7WJcCq+bc8dwcO/YsSEOudAH8RDtaOhM6wcAQhcUsFhnWQl81JKy48q1hoxAU0qrzM89+31GYh1515Zde3Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.56.0': resolution: {integrity: sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2990,8 +2990,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/typescript-estree@8.59.3': - resolution: {integrity: sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==} + '@typescript-eslint/typescript-estree@8.59.4': + resolution: {integrity: sha512-F+RuOmcDXo4+TPdfd/TCLS3m2nw8gE9XXyZLrA3JBfaA5tz9TtdkyD3YJFmPxulyc2cKbEok/CvFE3MgSLWnag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -3010,8 +3010,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.59.3': - resolution: {integrity: sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==} + '@typescript-eslint/utils@8.59.4': + resolution: {integrity: sha512-cYXeNAUsG4lJo5dbc1FcKm+JwIWrj1/UpTORsC6tGMjEZ81DYcvIr9/ueikhMa/Y/gDQYGp+YX9/xQrXje5BJw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -3025,8 +3025,8 @@ packages: resolution: {integrity: sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.59.3': - resolution: {integrity: sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==} + '@typescript-eslint/visitor-keys@8.59.4': + resolution: {integrity: sha512-U3gxVaDVnuZKhSspW/MzMxE1kq7zOdc072FcSNoqA1I9p8HyKbBFfEHoWckBAMgNMph4MamwS5iTVzFmrnt8TQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': @@ -9627,14 +9627,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.59.3 - '@typescript-eslint/type-utils': 8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.59.3 + '@typescript-eslint/parser': 8.59.4(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.59.4 + '@typescript-eslint/type-utils': 8.59.4(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.4(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.59.4 eslint: 9.39.4(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 @@ -9655,12 +9655,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.59.3 - '@typescript-eslint/types': 8.59.3 - '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.59.3 + '@typescript-eslint/scope-manager': 8.59.4 + '@typescript-eslint/types': 8.59.4 + '@typescript-eslint/typescript-estree': 8.59.4(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.59.4 debug: 4.4.3 eslint: 9.39.4(jiti@2.6.1) typescript: 5.9.3 @@ -9669,8 +9669,8 @@ snapshots: '@typescript-eslint/project-service@8.56.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.9.3) - '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@5.9.3) + '@typescript-eslint/types': 8.59.3 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: @@ -9678,17 +9678,17 @@ snapshots: '@typescript-eslint/project-service@8.58.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.9.3) - '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@5.9.3) + '@typescript-eslint/types': 8.59.3 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.59.3(typescript@5.9.3)': + '@typescript-eslint/project-service@8.59.4(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@5.9.3) - '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/tsconfig-utils': 8.59.4(typescript@5.9.3) + '@typescript-eslint/types': 8.59.4 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: @@ -9704,10 +9704,10 @@ snapshots: '@typescript-eslint/types': 8.58.0 '@typescript-eslint/visitor-keys': 8.58.0 - '@typescript-eslint/scope-manager@8.59.3': + '@typescript-eslint/scope-manager@8.59.4': dependencies: - '@typescript-eslint/types': 8.59.3 - '@typescript-eslint/visitor-keys': 8.59.3 + '@typescript-eslint/types': 8.59.4 + '@typescript-eslint/visitor-keys': 8.59.4 '@typescript-eslint/tsconfig-utils@8.56.0(typescript@5.9.3)': dependencies: @@ -9717,11 +9717,11 @@ snapshots: dependencies: typescript: 5.9.3 - '@typescript-eslint/tsconfig-utils@8.59.2(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.59.3(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/tsconfig-utils@8.59.3(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.59.4(typescript@5.9.3)': dependencies: typescript: 5.9.3 @@ -9737,11 +9737,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.59.4(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.59.3 - '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.9.3) - '@typescript-eslint/utils': 8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/types': 8.59.4 + '@typescript-eslint/typescript-estree': 8.59.4(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.4(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.3 eslint: 9.39.4(jiti@2.6.1) ts-api-utils: 2.5.0(typescript@5.9.3) @@ -9753,10 +9753,10 @@ snapshots: '@typescript-eslint/types@8.58.0': {} - '@typescript-eslint/types@8.59.2': {} - '@typescript-eslint/types@8.59.3': {} + '@typescript-eslint/types@8.59.4': {} + '@typescript-eslint/typescript-estree@8.56.0(typescript@5.9.3)': dependencies: '@typescript-eslint/project-service': 8.56.0(typescript@5.9.3) @@ -9787,12 +9787,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.59.3(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.59.4(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.59.3(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@5.9.3) - '@typescript-eslint/types': 8.59.3 - '@typescript-eslint/visitor-keys': 8.59.3 + '@typescript-eslint/project-service': 8.59.4(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.59.4(typescript@5.9.3) + '@typescript-eslint/types': 8.59.4 + '@typescript-eslint/visitor-keys': 8.59.4 debug: 4.4.3 minimatch: 10.2.4 semver: 7.7.3 @@ -9824,12 +9824,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.59.4(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.59.3 - '@typescript-eslint/types': 8.59.3 - '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.59.4 + '@typescript-eslint/types': 8.59.4 + '@typescript-eslint/typescript-estree': 8.59.4(typescript@5.9.3) eslint: 9.39.4(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: @@ -9845,9 +9845,9 @@ snapshots: '@typescript-eslint/types': 8.58.0 eslint-visitor-keys: 5.0.0 - '@typescript-eslint/visitor-keys@8.59.3': + '@typescript-eslint/visitor-keys@8.59.4': dependencies: - '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/types': 8.59.4 eslint-visitor-keys: 5.0.0 '@ungap/structured-clone@1.3.0': {} @@ -10006,11 +10006,11 @@ snapshots: '@vue/devtools-shared@8.1.2': {} - '@vue/eslint-config-typescript@14.7.0(eslint-plugin-vue@10.9.1(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@vue/eslint-config-typescript@14.7.0(eslint-plugin-vue@10.9.1(@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@typescript-eslint/utils': 8.58.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.4(jiti@2.6.1) - eslint-plugin-vue: 10.9.1(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))) + eslint-plugin-vue: 10.9.1(@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))) fast-glob: 3.3.3 typescript-eslint: 8.56.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) vue-eslint-parser: 10.4.0(eslint@9.39.4(jiti@2.6.1)) @@ -10984,7 +10984,7 @@ snapshots: module-replacements: 2.11.0 semver: 7.7.3 - eslint-plugin-vue@10.9.1(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))): + eslint-plugin-vue@10.9.1(@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.6.1))): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) eslint: 9.39.4(jiti@2.6.1) @@ -10995,7 +10995,7 @@ snapshots: vue-eslint-parser: 10.4.0(eslint@9.39.4(jiti@2.6.1)) xml-name-validator: 4.0.0 optionalDependencies: - '@typescript-eslint/parser': 8.59.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.59.4(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) eslint-scope@8.4.0: dependencies: From fee2d2ea58b9452ee8323f215de8d17366687d06 Mon Sep 17 00:00:00 2001 From: Tink bot Date: Mon, 18 May 2026 18:15:20 +0000 Subject: [PATCH 018/480] fix(notifications): skip logo attachment for conversational mails The conversational mail template does not reference cid:logo.png, but RenderMail still attached the embedded logo to every outgoing mail. That left an orphan inline part that some clients render as a stray attachment. Only embed logo.png when the formal template is in use. --- pkg/notifications/mail_render.go | 7 +++++-- pkg/notifications/mail_test.go | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pkg/notifications/mail_render.go b/pkg/notifications/mail_render.go index 67b254d35..292927c80 100644 --- a/pkg/notifications/mail_render.go +++ b/pkg/notifications/mail_render.go @@ -407,9 +407,12 @@ func RenderMail(m *Mail, lang string) (mailOpts *mail.Opts, err error) { HTMLMessage: htmlContent.String(), Boundary: boundary, ThreadID: m.threadID, - EmbedFS: map[string]*embed.FS{ + } + + if !m.conversational { + mailOpts.EmbedFS = map[string]*embed.FS{ "logo.png": &logo, - }, + } } return mailOpts, nil diff --git a/pkg/notifications/mail_test.go b/pkg/notifications/mail_test.go index 125a13cd0..fca5c6447 100644 --- a/pkg/notifications/mail_test.go +++ b/pkg/notifications/mail_test.go @@ -533,6 +533,7 @@ func TestConversationalMail(t *testing.T) { // Should NOT have logo (completely removed) assert.NotContains(t, mailopts.HTMLMessage, "logo.png") assert.NotContains(t, mailopts.HTMLMessage, "Vikunja") + assert.NotContains(t, mailopts.EmbedFS, "logo.png") // Should have inline action link with arrow assert.Contains(t, mailopts.HTMLMessage, "View Task →") @@ -571,6 +572,7 @@ func TestConversationalMail(t *testing.T) { // Should HAVE logo in formal emails assert.Contains(t, mailopts.HTMLMessage, "logo.png") assert.Contains(t, mailopts.HTMLMessage, "Vikunja") + assert.Contains(t, mailopts.EmbedFS, "logo.png") // Should have formal button styling assert.Contains(t, mailopts.HTMLMessage, "background-color: #1973ff") From a79517a79a262831d9ab0751100c2b1e52f5320c Mon Sep 17 00:00:00 2001 From: Tink bot Date: Mon, 18 May 2026 18:59:35 +0000 Subject: [PATCH 019/480] fix(frontend): prevent avatar layout shift while loading The .avatar img in User.vue relied solely on the width/height HTML attributes for sizing. Those are presentational hints with zero CSS specificity, so Bulma's global reset (img { height: auto; max-width: 100% }) overrode them. While avatarSrc was still resolving (initial src=""), the browser had no intrinsic dimensions to compute the auto height from and fell back to the broken-image box (~96px in Chrome), then snapped to the real size once the blob URL loaded. Set inline-size/block-size explicitly via a CSS custom property bound to the avatarSize prop so the rendered size is locked regardless of load state or the Bulma reset. --- frontend/src/components/misc/User.vue | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/src/components/misc/User.vue b/frontend/src/components/misc/User.vue index b7348b1a5..bc7fe137a 100644 --- a/frontend/src/components/misc/User.vue +++ b/frontend/src/components/misc/User.vue @@ -2,6 +2,7 @@
[props.user, props.avatarSize], loadAvatar, { immediate: true }) } .avatar { + inline-size: var(--avatar-size); + block-size: var(--avatar-size); border-radius: 100%; vertical-align: middle; } From c761ab9761eb5f20b2c56c50297548f83e726e11 Mon Sep 17 00:00:00 2001 From: "Frederick [Bot]" Date: Tue, 19 May 2026 02:26:35 +0000 Subject: [PATCH 020/480] chore(i18n): update translations via Crowdin --- frontend/src/i18n/lang/de-DE.json | 10 +- frontend/src/i18n/lang/de-swiss.json | 10 +- frontend/src/i18n/lang/el-GR.json | 88 +++++++++++++ frontend/src/i18n/lang/fa-IR.json | 1 + frontend/src/i18n/lang/th-TH.json | 1 + pkg/i18n/lang/de-DE.json | 5 + pkg/i18n/lang/de-swiss.json | 5 + pkg/i18n/lang/el-GR.json | 182 +++++++++++++++++++++++++++ pkg/i18n/lang/fa-IR.json | 1 + pkg/i18n/lang/th-TH.json | 1 + 10 files changed, 302 insertions(+), 2 deletions(-) create mode 100644 frontend/src/i18n/lang/el-GR.json create mode 100644 frontend/src/i18n/lang/fa-IR.json create mode 100644 frontend/src/i18n/lang/th-TH.json create mode 100644 pkg/i18n/lang/el-GR.json create mode 100644 pkg/i18n/lang/fa-IR.json create mode 100644 pkg/i18n/lang/th-TH.json diff --git a/frontend/src/i18n/lang/de-DE.json b/frontend/src/i18n/lang/de-DE.json index 4d88eaa68..02958a0ec 100644 --- a/frontend/src/i18n/lang/de-DE.json +++ b/frontend/src/i18n/lang/de-DE.json @@ -219,6 +219,13 @@ "usernameIs": "Dein Anmeldename für CalDAV lautet: {0}", "apiTokenHint": "Du kannst auch ein API-Token mit CalDAV-Berechtigung verwenden. Erstelle eins unter {link}." }, + "feeds": { + "title": "Atom-Feed", + "howTo": "Du kannst deine Vikunja-Benachrichtigungen von jedem Atom-kompatiblen Feed-Reader abonnieren. Benutze die folgende URL:", + "usernameIs": "Dein Anmeldename für das Feed lautet: {0}", + "apiTokenHint": "Authentifiziere dich mit einem API-Token mit der {scope} Berechtigung. Erstellen eins unter {link}.", + "tokenTitle": "Atom-Feed" + }, "avatar": { "title": "Avatar", "initials": "Initialen", @@ -1323,7 +1330,8 @@ "none": "Du hast keine Benachrichtigungen. Einen schönen Tag noch!", "explainer": "Benachrichtigungen werden hier angezeigt, wenn Aktionen für Projekte oder Aufgaben, die du abonniert hast, ausgeführt werden.", "markAllRead": "Alle Benachrichtigungen als gelesen markieren", - "markAllReadSuccess": "Alle Benachrichtigungen erfolgreich als gelesen markiert." + "markAllReadSuccess": "Alle Benachrichtigungen erfolgreich als gelesen markiert.", + "subscribeFeed": "Benachrichtigungen über Atom-Feed abonnieren" }, "quickActions": { "notLoggedIn": "Bitte melde dich zuerst im Hauptfenster von Vikunja an.", diff --git a/frontend/src/i18n/lang/de-swiss.json b/frontend/src/i18n/lang/de-swiss.json index c20af1643..2f53ebd43 100644 --- a/frontend/src/i18n/lang/de-swiss.json +++ b/frontend/src/i18n/lang/de-swiss.json @@ -219,6 +219,13 @@ "usernameIs": "Dein Anmeldename für CalDAV lautet: {0}", "apiTokenHint": "Du kannst auch ein API-Token mit CalDAV-Berechtigung verwenden. Erstelle eins unter {link}." }, + "feeds": { + "title": "Atom-Feed", + "howTo": "Du kannst deine Vikunja-Benachrichtigungen von jedem Atom-kompatiblen Feed-Reader abonnieren. Benutze die folgende URL:", + "usernameIs": "Dein Anmeldename für das Feed lautet: {0}", + "apiTokenHint": "Authentifiziere dich mit einem API-Token mit der {scope} Berechtigung. Erstellen eins unter {link}.", + "tokenTitle": "Atom-Feed" + }, "avatar": { "title": "Herr Der Elemente", "initials": "Initialä", @@ -1323,7 +1330,8 @@ "none": "Du hesch kei neui Benachrichtunge. Heb e schös Tägli!", "explainer": "Benachrichtigungen werden hier angezeigt, wenn Aktionen für Projekte oder Aufgaben, die du abonniert hast, ausgeführt werden.", "markAllRead": "Alle Benachrichtigungen als gelesen markieren", - "markAllReadSuccess": "Alle Benachrichtigungen erfolgreich als gelesen markiert." + "markAllReadSuccess": "Alle Benachrichtigungen erfolgreich als gelesen markiert.", + "subscribeFeed": "Benachrichtigungen über Atom-Feed abonnieren" }, "quickActions": { "notLoggedIn": "Bitte melde dich zuerst im Hauptfenster von Vikunja an.", diff --git a/frontend/src/i18n/lang/el-GR.json b/frontend/src/i18n/lang/el-GR.json new file mode 100644 index 000000000..5083fb8d1 --- /dev/null +++ b/frontend/src/i18n/lang/el-GR.json @@ -0,0 +1,88 @@ +{ + "home": { + "welcomeNight": "Καληνύχτα {username}!", + "welcomeNightOwl": "Γεια σου νυχτερινή κουκουβάγια {username}", + "welcomeNightBurning": "Κάνουμε υπερωρίες {username};", + "welcomeDayKeepGoing": "Συνέχισε, {username}" + }, + "user": { + "auth": { + "openIdTotpRequired": "Ο λογαριασμός σας απαιτεί ταυτοποίηση δύο παραγόντων. Εισαγάγετε τον κωδικό σας μίας χρήσης και κάνετε είσοδο εκ νέου.", + "desktopWaitingForAuth": "Αναμονή για ταυτοποίηση…", + "desktopOAuthError": "Η ταυτοποίηση απέτυχε: {error}" + }, + "settings": { + "bots": { + "description": "Οι χρήστες bot είναι μόνο API-χρήστες που εσείς κατέχετε. Μπορούν να προστεθούν σε έργα, ανατεθειμένες εργασίες και ταυτοποιούνται με τεκμήρια API. Δεν μπορούν να συνδεθούν διαδραστικά." + }, + "totp": { + "title": "Ταυτοποίηση Δύο Παραγόντων", + "confirmNotice": "Μετά την ενεργοποίηση της ταυτοποίησης δύο παραγόντων, θα αποσυνδεθείτε από όλες τις συνεδρίες και θα πρέπει να κάνετε είσοδο εκ νέου.", + "setupSuccess": "Έχετε παραμετροποιήσει με επιτυχία τον έλεγχο ταυτοποίησης δύο παραγόντων!", + "disable": "Απενεργοποίηση ταυτοποίησης δύο παραγόντων", + "confirmSuccess": "Έχετε ενεργοποιήσει με επιτυχία τον έλεγχο ταυτοποίησης δύο παραγόντων!", + "disableSuccess": "Ο έλεγχος ταυτοποίησης δύο παραγόντων απενεργοποιήθηκε με επιτυχία." + }, + "caldav": { + "tokensHowTo": "Για ταυτοποίηση CalDAV μπορείτε να χρησιμοποιήσετε είτε τον κανονικό κωδικό πρόσβασής σας είτε ένα τεκμήριο CalDAV." + }, + "feeds": { + "apiTokenHint": "Ταυτοποιηθείτε με τεκμήριο API που έχει την άδεια {scope}. Δημιουργήστε ένα στο {link}." + } + } + }, + "project": { + "webhooks": { + "basicauthuser": "Χρήστης για Βασική Ταυτοποίηση", + "basicauthpassword": "Κωδικός πρόσβασης για Βασική Ταυτοποίηση", + "basicauthlink": "Χρήση της Βασικής Ταυτοποίησης;" + }, + "views": { + "deleteText": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε την προβολή; Δε θα είναι πλέον δυνατό να τη χρησιμοποιήσετε για να δείτε εργασίες σε αυτό το έργο. Αυτή η ενέργεια δε θα διαγράψει καμία εργασία. Αυτή η ενέργεια είναι μη αναιρέσιμη!" + } + }, + "filters": { + "fromView": "Η τρέχουσα προβολή έχει επίσης ένα σύνολο φίλτρων:", + "fromViewBoth": "Θα χρησιμοποιηθεί σε συνδυασμό με αυτό που εισαγάγετε εδώ.", + "create": { + "description": "Ένα αποθηκευμένο φίλτρο είναι ένα εικονικό έργο το οποίο δημιουργείται από ένα σύνολο φίλτρων κάθε φορά που το προσπελαύνει κανείς." + }, + "query": { + "help": { + "canUseDatemath": "Μπορείτε να χρησιμοποιήσετε μαθηματικά στις ημερομηνίες για να ορίσετε σχετικές ημερομηνίες. Κάντε κλικ στην τιμή της ημερομηνίας σε ένα ερώτημα για να μάθετε περισσότερα.", + "fields": { + "done": "Αν η εργασία έχει ολοκληρωθεί ή όχι", + "reminders": "Οι υπενθυμίσεις της εργασίας ως πεδίο ημερομηνίας, θα επιστρέψουν όλες τις εργασίες με τουλάχιστον μία υπενθύμιση που ταιριάζει με το ερώτημα", + "created": "Η ώρα και ημερομηνία που δημιουργήθηκε η εργασία", + "updated": "Η ώρα και η ημερομηνία τελευταίας τροποποίησης μιας εργασίας" + }, + "operators": { + "intro": "Οι διαθέσιμοι τελεστές για το φιλτράρισμα περιλαμβάνουν:", + "in": "Ταιριάζει με οποιαδήποτε τιμή από μια λίστα τιμών χωρισμένες με κόμμα", + "notIn": "Ταιριάζει με οποιαδήποτε τιμή που δεν υπάρχει σε μια λίστα τιμών χωρισμένες με κόμμα" + }, + "logicalOperators": { + "intro": "Για να συνδυάσετε πολλαπλές συνθήκες, μπορείτε να χρησιμοποιήσετε τους ακόλουθους λογικούς τελεστές:", + "and": "Τελεστής AND, ταιριάζει όλες τις συνθήκες που είναι αληθείς", + "or": "Τελεστής OR, ταιριάζει αν κάποια από τις συνθήκες είναι αληθής", + "parentheses": "Παρενθέσεις για ομαδοποίηση συνθηκών" + }, + "examples": { + "intro": "Παρακάτω υπάρχουν ορισμένα παραδείγματα ερωτημάτων φιλτραρίσματος:", + "priorityEqual": "Ταιριάζει με εργασίες με προτεραιτότητα επιπέδου 4", + "undoneHighPriority": "Ταιριάζει με μη ολοκληρωμένες εργασίες με προτεραιότητα επιπέδου 3 ή μεγαλύτερο", + "assigneesIn": "Ταιριάζει με εργασίες που έχουν ανατεθεί είτε στο χρήστη \"user1\" ή στον \"user2\"" + } + } + } + }, + "sorting": { + "description": "Επιλέξτε πως ταξινομούνται οι εργασίες σε αυτή τη λίστα. Κατά τη χειροκίνητη ταξινόμηση, μπορείτε να σύρετε και να αφήσετε εργασίες για να τις αλλάξετε τη σειρά." + }, + "migrate": { + "description": "Κάντε κλικ στο λογότυπο μιας από τις παρακάτω υπηρεσίες τρίτων για να ξεκινήσετε.", + "descriptionDo": "Το Vikunja θα εισαγάγει όλες τις λίστες, εργασίες, σημειώσεις, υπενθυμίσεις και αρχεία που έχετε πρόσβαση.", + "authorize": "Για να επιτρέψετε στο Vikunja να προσπελάσει το {name} Λογαριασμό σας, κάντε κλικ στο παρακάτω πλήκτρο.", + "alreadyMigrated1": "Φαίνεται ότι έχετε ήδη εισαγάγει τα στοιχεία σας από το {name} στο {date}." + } +} \ No newline at end of file diff --git a/frontend/src/i18n/lang/fa-IR.json b/frontend/src/i18n/lang/fa-IR.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/frontend/src/i18n/lang/fa-IR.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/frontend/src/i18n/lang/th-TH.json b/frontend/src/i18n/lang/th-TH.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/frontend/src/i18n/lang/th-TH.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/pkg/i18n/lang/de-DE.json b/pkg/i18n/lang/de-DE.json index 64762d377..c446214de 100644 --- a/pkg/i18n/lang/de-DE.json +++ b/pkg/i18n/lang/de-DE.json @@ -173,5 +173,10 @@ "since_hours": "einer Stunde|%[1]d Stunden", "since_minutes": "einer Minute|%[1]d Minuten", "list_last_separator": "und" + }, + "feeds": { + "notifications": { + "title": "Vikunja Benachrichtigungen für %[1]s" + } } } \ No newline at end of file diff --git a/pkg/i18n/lang/de-swiss.json b/pkg/i18n/lang/de-swiss.json index 64762d377..c446214de 100644 --- a/pkg/i18n/lang/de-swiss.json +++ b/pkg/i18n/lang/de-swiss.json @@ -173,5 +173,10 @@ "since_hours": "einer Stunde|%[1]d Stunden", "since_minutes": "einer Minute|%[1]d Minuten", "list_last_separator": "und" + }, + "feeds": { + "notifications": { + "title": "Vikunja Benachrichtigungen für %[1]s" + } } } \ No newline at end of file diff --git a/pkg/i18n/lang/el-GR.json b/pkg/i18n/lang/el-GR.json new file mode 100644 index 000000000..d14c2664d --- /dev/null +++ b/pkg/i18n/lang/el-GR.json @@ -0,0 +1,182 @@ +{ + "notifications": { + "greeting": "Γεια σου %[1]s,", + "email_confirm": { + "subject": "%[1]s, παρακαλώ επιβεβαίωσε τη διεύθυνση email σου στο Vikunja", + "subject_new": "%[1]s + Vikunja = <3", + "welcome": "Καλωσορίσατε στο Vikunja!", + "confirm": "Για να επιβεβαιώσεις τη διεύθυνση email σου, κάνε κλικ στον παρακάτω σύνδεσμο:" + }, + "password": { + "changed": { + "subject": "Ο κωδικός σου πρόσβασης στο Vikunja άλλαξε", + "success": "Ο κωδικός πρόσβασης του λογαριασμού σου άλλαξε με επιτυχία.", + "warning": "Αν δεν ήσουν εσύ, αυτό θα μπορούσε να σημαίνει ότι κάποιος παραβίασε το λογαριασμό σου. Σε αυτή την περίπτωση επικοινώνησε με το διαχειριστή του διακομιστή σου." + }, + "reset": { + "subject": "Επανάφερε τον κωδικό σου στο Vikunja", + "instructions": "Για να επαναφέρεις τον κωδικό σου, πάτησε στον παρακάτω σύνδεσμο:", + "valid_duration": "Ο σύνδεσμος θα ισχύει για 24 ώρες." + } + }, + "totp": { + "invalid": { + "subject": "Κάποιος μόλις προσπάθησε να εισέλθει στο λογαριασμό σου στο Vikunja, αλλά απέτυχε", + "message": "Κάποιος μόλις προσπάθησε να συνδεθεί στο λογαριασμό σου με το σωστό όνομα χρήστη και κωδικό πρόσβασης, αλλά με λάθος κωδικό TOTP.", + "warning": "**Εάν δεν ήσουν εσύ, κάποιος άλλος γνωρίζει τον κωδικό σου πρόσβασης. Θα πρέπει να ορίσεις ένα νέο αμέσως!**" + }, + "account_locked": { + "subject": "Έχουμε απενεργοποιήσει το λογαριασμό σου στο Vikunja", + "message": "Κάποιος προσπάθησε να συνδεθεί με τα διαπιστευτήριά σας αλλά απέτυχε να δώσει έγκυρο κωδικό πρόσβασης TOTP.", + "disabled": "Μετά από 10 αποτυχημένες προσπάθειες, έχουμε απενεργοποιήσει το λογαριασμό σας και επαναφέραμε τον κωδικό σας πρόσβασης. Για να ορίσετε ένα νέο, ακολουθήστε τις οδηγίες στο email επαναφοράς που μόλις σας στείλαμε.", + "reset_instructions": "Εάν δε λάβατε ένα email με οδηγίες επαναφοράς, μπορείτε πάντα να ζητήσετε ένα νέο στο [%[1]s](%[2]s)." + } + }, + "login": { + "failed": { + "subject": "Κάποιος μόλις προσπάθησε να εισέλθει στο λογαριασμό σας στο Vikunja, αλλά απέτυχε να δώσει σωστό κωδικό πρόσβασης", + "message": "Κάποιος μόλις προσπάθησε να συνδεθεί στο λογαριασμό σας με λάθος κωδικό πρόσβασης τρεις φορές συνεχόμενα.", + "warning": "Αν δεν ήσασταν εσείς, θα μπορούσε να είναι κάποιος άλλος που προσπαθεί να εισέλθει κακόβουλα στο λογαριασμό σας.", + "enhance_security": "Για να ενισχύσετε την ασφάλεια του λογαριασμού σας μπορείτε να ορίσετε έναν ισχυρότερο κωδικό πρόσβασης ή να ενεργοποιήσετε τον έλεγχο ταυτοποίησης TOTP στις ρυθμίσεις:" + } + }, + "account": { + "deletion": { + "confirm": { + "subject": "Παρακαλώ επιβεβαιώστε τη διαγραφή του λογαριασμού σας στο Vikunja", + "request": "Ζητήσατε τη διαγραφή του λογαριασμού σας. Για να επιβεβαιώσετε την ενέργεια, κάντε κλικ στον παρακάτω σύνδεσμο:", + "valid_duration": "Ο σύνδεσμος θα ισχύει για 24 ώρες.", + "schedule_info": "Μόλις επιβεβαιώσετε τη διαγραφή, θα προγραμματίσουμε τη διαγραφή του λογαριασμού σας σε τρεις ημέρες και θα σας στείλουμε ένα άλλο email μέχρι τότε.", + "consequences": "Αν προχωρήσετε με τη διαγραφή του λογαριασμού σας, θα καταργήσουμε όλα τα έργα και τις εργασίες που δημιουργήσατε. Η κυριότητα όλων όσων μοιραστήκατε με άλλο χρήστη ή ομάδα θα μεταφερθεί σε αυτούς.", + "changed_mind": "Αν δεν αιτηθήκατε τη διαγραφή ή αλλάξατε γνώμη, μπορείτε απλά να αγνοήσετε αυτό το email." + }, + "scheduled": { + "subject_days": "Ο λογαριασμός σας στο Vikunja θα διαγραφεί σε %[1]ημέρες", + "subject_tomorrow": "Ο λογαριασμός σας στο Vikunja θα διαγραφεί αύριο", + "request_reminder": "Αιτηθήκατε πρόσφατα τη διαγραφή του λογαριασμού σας στο Vikunja.", + "deletion_time_days": "Θα διαγράψουμε τον λογαριασμό σας σε %[1]s ημέρες.", + "deletion_time_tomorrow": "Θα διαγράψουμε τον λογαριασμό σας αύριο.", + "changed_mind": "Αν αλλάξατε γνώμη, απλά κάντε κλικ στον παρακάτω σύνδεσμο για να ακυρώσετε τη διαγραφή και ακολουθήστε τις οδηγίες:" + }, + "completed": { + "subject": "Ο λογαριασμός σας στο Vikunja έχει διαγραφεί", + "confirmation": "Όπως ζητήθηκε, έχουμε διαγράψει το λογαριασμό σας στο Vikunja.", + "permanent": "Η διαγραφή είναι μόνιμη. Αν δε δημιουργήσατε αντίγραφο ασφαλείας και χρειάζεστε τα δεδομένα σας αυτή τη στιγμή, επικοινωνήστε με το διαχειριστή σας." + } + } + }, + "task": { + "reminder": { + "subject": "Υπενθύμιση για \"%[1]s\" (%[2]s)", + "message": "Αυτή είναι μια φιλική υπενθύμιση για την εργασία \"%[1]s\" (%[2]s)." + }, + "comment": { + "subject": "Σχ: %[1]s (%[2]s)", + "mentioned_subject": "Ο/Η %[1]s σας ανέφερε σε ένα σχόλιο στο \"%[2]s\" (%[3]s)" + }, + "assigned": { + "subject_to_assignee": "Σας έχει ανατεθεί το \"%[1]s\" (%[2]s)", + "message_to_assignee": "Ο/Η %[1]s σας έχει αναθέσει το \"%[2]s\".", + "subject_to_others": "Το \"%[1]s\" (%[2]s) έχει ανατεθεί στον/στην %[3]s", + "message_to_others": "Ο/Η %[1]s έχει αναθέσει την εργασία στον/στην %[2]s.", + "subject_to_others_self": "Το \"%[1]s\" (%[2]s) έχει ανατεθεί από τον/την %[3]s στον εαυτό τους", + "message_to_others_self": "Ο/Η %[1]s έχει αναθέσει την εργασία στον εαυτό τους." + }, + "deleted": { + "subject": "Το \"%[1]s\" (%[2]s) έχει διαγραφεί", + "message": "Ο/Η %[1]s έχει διαγράψει την εργασία \"%[2]s\" (%[3]s)" + }, + "mentioned": { + "subject_new": "Ο/Η %[1]s σας ανέφερε σε μια νέα εργασία \"%[2]s\" (%[3]s)", + "subject": "Ο/Η %[1]s σας ανέφερε σε μια εργασία \"%[2]s\" (%[3]s)" + }, + "overdue": { + "subject": "Η εργασία \"%[1]s\" (%[2]s) είναι ληξιπρόθεσμη", + "message": "Αυτή είναι μια φιλική υπενθύμιση για την εργασία \"%[1]s\" (%[2]s) που είναι ληξιπρόθεσμη %[3]s και δεν έχει ακόμη παραδοθεί.", + "multiple_subject": "Οι εκπρόθεσμες εργασίες σας", + "multiple_message": "Έχετε τις παρακάτω εκπρόθεσμες εργασίες:", + "overdue_since": "από %[1]s", + "overdue_now": "τώρα", + "overdue": "εκπρόθεσμη %[1]s" + } + }, + "project": { + "created": "Ο/Η %[1]s δημιούργησε το έργο \"%[2]s\"" + }, + "team": { + "member_added": { + "subject": "Ο/Η %[1]s σας πρόσθεσε στην ομάδα \"%[2]s\" στο Vikunja", + "message": "Ο/Η %[1]s σας πρόσθεσε στην ομάδα %[2]s στο Vikunja." + } + }, + "data_export": { + "ready": { + "subject": "Η εξαγωγή δεδομένων σας από το Vikunja είναι έτοιμη", + "message": "Η εξαγωγή δεδομένων σας από το Vikunja είναι έτοιμη για λήψη. Κάντε κλικ στο πλήκτρο παρακάτω για τα κατεβάσετε:", + "availability": "Η λήψη θα είναι διαθέσιμη για τις επόμενες 7 ημέρες." + } + }, + "migration": { + "done": { + "subject": "Η μετάβαση από το %[1]s στο Vikunja ολοκληρώθηκε", + "imported": "Το Vikunja έχει εισαγάγει όλες τις λίστες / έργα, εργασίες, σημειώσεις, υπενθυμίσεις και αρχεία από το %[1]s που έχετε πρόσβαση.", + "have_fun": "Καλή διασκέδαση με τα νέα (παλιά) έργα σας!" + }, + "failed": { + "subject": "Η μετάβαση από το %[1]s στο Vikunja απέτυχε", + "message": "Φαίνεται ότι η μετάβαση από το %[1]s δεν πήγε όπως θέλαμε αυτή τη φορά.", + "retry": "Μην ανησυχείτε, όμως! Απλά δώστε μια ακόμη ευκαιρία ξεκινώντας με τον ίδιο τρόπο όπως και πριν. Μερικές φορές, αυτές οι αναποδιές συμβαίνουν λόγω προβλημάτων από την πλευρά του %[1]s και δοκιμάζοντας ξανά πολλές φορές λύνει το πρόβλημα.", + "error": "Εντοπίσαμε ένα μικρό σφάλμα στην πορεία: `%[2]s`.", + "report": "Παρακαλώ αφήστε μια σημείωση σχετικά με αυτό [στο φόρουμ](https://community.vikunja.io/) ή σε οποιοδήποτε από τα συνηθισμένα μέρη, έτσι ώστε να μπορούμε να ρίξουμε μια ματιά στο γιατί απέτυχε.", + "working_on_it": "Έχουμε το μήνυμα σφάλματος στο ραντάρ μας και είμαστε έτοιμοι να το τακτοποιήσουμε σύντομα." + } + }, + "api_token": { + "expiring": { + "week": { + "subject": "Το API τεκμήριό σας \"%[1]s\" λήγει σύντομα", + "message": "Το τεκμήριό σας API \"%[1]s\" θα λήξει στις %[2]s. Αν εξακολουθείτε να το χρειάζεστε, παρακαλώ δημιουργήστε ένα νέο προτού λήξει." + }, + "day": { + "subject": "Το API τεκμήριό σας \"%[1]s\" λήγει αύριο", + "message": "Το τεκμήριό σας API \"%[1]s\" θα λήξει στις %[2]s. Αν εξακολουθείτε να το χρειάζεστε, παρακαλώ δημιουργήστε ένα νέο προτού λήξει." + }, + "action": "Διαχείριση Τεκμηρίων API" + } + }, + "common": { + "have_nice_day": "Να έχεις μια όμορφη μέρα!", + "copy_url": "Αν το παραπάνω κουμπί δε λειτουργεί, αντιγράψτε το παρακάτω url και επικολλήστε το στη γραμμή διευθύνσεων του προγράμματός σας πλοήγησης:", + "actions": { + "open_task": "Άνοιγμα Εργασίας στο Vikunja", + "open_vikunja": "Άνοιγμα του Vikunja", + "open_project": "Άνοιγμα Έργου", + "open_team": "Άνοιγμα Ομάδας", + "download": "Λήψη", + "reset_password": "Επαναφορά του κωδικού σας πρόσβασης", + "go_to_settings": "Μετάβαση στις ρυθμίσεις", + "confirm_email": "Επιβεβαιώστε τη διεύθυνση email σας", + "abort_deletion": "Ματαίωση της διαγραφής", + "confirm_account_deletion": "Επιβεβαίωση της διαγραφής του λογαριασμού μου", + "change_notification_settings_link": "Μπορείτε να αλλάξετε τις ρυθμίσεις σας ειδοποίησης [here](%[1]s).", + "left_comment": "Ο/Η %[1]s άφησε ένα σχόλιο", + "mentioned_you_comment": "Ο/Η %[1]s σας ανέφερε σε ένα σχόλιο", + "mentioned_you": "Ο/Η %[1]s σας ανέφερε", + "mentioned_you_new_task": "Ο/Η %[1]s σας ανέφερε σε μια νέα εργασία" + } + } + }, + "time": { + "since_years": "ένα έτος|%[1]d έτη", + "since_weeks": "μία εβδομάδα|%[1]d εβδομάδες", + "since_days": "μία ημέρα|%[1]d ημέρες", + "since_hours": "μία ώρα|%[1]d ώρες", + "since_minutes": "ένα λεπτό|%[1]d λεπτά", + "list_last_separator": "και" + }, + "feeds": { + "notifications": { + "title": "Ειδοποιήσεις του Vikunja για %[1]s" + } + } +} \ No newline at end of file diff --git a/pkg/i18n/lang/fa-IR.json b/pkg/i18n/lang/fa-IR.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/pkg/i18n/lang/fa-IR.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/pkg/i18n/lang/th-TH.json b/pkg/i18n/lang/th-TH.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/pkg/i18n/lang/th-TH.json @@ -0,0 +1 @@ +{} \ No newline at end of file From 21ce33f8fd1ccfb66b66fc88462f6daf7e1db878 Mon Sep 17 00:00:00 2001 From: kolaente Date: Tue, 19 May 2026 10:35:43 +0200 Subject: [PATCH 021/480] feat(projects): always store identifiers as uppercase (#2775) --- pkg/db/fixtures/projects.yml | 64 ++++++++-------- pkg/migration/20260519120000.go | 108 +++++++++++++++++++++++++++ pkg/models/project.go | 5 ++ pkg/models/task_collection_test.go | 60 +++++++-------- pkg/webtests/task_collection_test.go | 30 ++++---- 5 files changed, 190 insertions(+), 77 deletions(-) create mode 100644 pkg/migration/20260519120000.go diff --git a/pkg/db/fixtures/projects.yml b/pkg/db/fixtures/projects.yml index 86d2cba24..5ab8d6536 100644 --- a/pkg/db/fixtures/projects.yml +++ b/pkg/db/fixtures/projects.yml @@ -2,7 +2,7 @@ id: 1 title: Test1 description: Lorem Ipsum - identifier: test1 + identifier: TEST1 owner_id: 1 position: 3 updated: 2018-12-02 15:13:12 @@ -11,7 +11,7 @@ id: 2 title: Test2 description: Lorem Ipsum - identifier: test2 + identifier: TEST2 owner_id: 3 position: 2 updated: 2018-12-02 15:13:12 @@ -20,7 +20,7 @@ id: 3 title: Test3 description: Lorem Ipsum - identifier: test3 + identifier: TEST3 owner_id: 3 position: 1 updated: 2018-12-02 15:13:12 @@ -29,7 +29,7 @@ id: 4 title: Test4 description: Lorem Ipsum - identifier: test4 + identifier: TEST4 owner_id: 3 position: 4 updated: 2018-12-02 15:13:12 @@ -38,7 +38,7 @@ id: 5 title: Test5 description: Lorem Ipsum - identifier: test5 + identifier: TEST5 owner_id: 5 position: 5 updated: 2018-12-02 15:13:12 @@ -47,7 +47,7 @@ id: 6 title: Test6 description: Lorem Ipsum - identifier: test6 + identifier: TEST6 owner_id: 6 position: 6 updated: 2018-12-02 15:13:12 @@ -56,7 +56,7 @@ id: 7 title: Test7 description: Lorem Ipsum - identifier: test7 + identifier: TEST7 owner_id: 6 position: 7 updated: 2018-12-02 15:13:12 @@ -65,7 +65,7 @@ id: 8 title: Test8 description: Lorem Ipsum - identifier: test8 + identifier: TEST8 owner_id: 6 position: 8 updated: 2018-12-02 15:13:12 @@ -74,7 +74,7 @@ id: 9 title: Test9 description: Lorem Ipsum - identifier: test9 + identifier: TEST9 owner_id: 6 position: 9 updated: 2018-12-02 15:13:12 @@ -83,7 +83,7 @@ id: 10 title: Test10 description: Lorem Ipsum - identifier: test10 + identifier: TEST10 owner_id: 6 position: 10 updated: 2018-12-02 15:13:12 @@ -92,7 +92,7 @@ id: 11 title: Test11 description: Lorem Ipsum - identifier: test11 + identifier: TEST11 owner_id: 6 position: 11 updated: 2018-12-02 15:13:12 @@ -101,7 +101,7 @@ id: 12 title: Test12 description: Lorem Ipsum - identifier: test12 + identifier: TEST12 owner_id: 6 position: 12 parent_project_id: 27 @@ -111,7 +111,7 @@ id: 13 title: Test13 description: Lorem Ipsum - identifier: test13 + identifier: TEST13 owner_id: 6 position: 13 parent_project_id: 28 @@ -121,7 +121,7 @@ id: 14 title: Test14 description: Lorem Ipsum - identifier: test14 + identifier: TEST14 owner_id: 6 position: 14 parent_project_id: 29 @@ -131,7 +131,7 @@ id: 15 title: Test15 description: Lorem Ipsum - identifier: test15 + identifier: TEST15 owner_id: 6 position: 15 parent_project_id: 32 @@ -141,7 +141,7 @@ id: 16 title: Test16 description: Lorem Ipsum - identifier: test16 + identifier: TEST16 owner_id: 6 position: 16 parent_project_id: 33 @@ -151,7 +151,7 @@ id: 17 title: Test17 description: Lorem Ipsum - identifier: test17 + identifier: TEST17 owner_id: 6 position: 17 parent_project_id: 34 @@ -163,7 +163,7 @@ id: 18 title: Test18 description: Lorem Ipsum - identifier: test18 + identifier: TEST18 owner_id: 7 position: 18 updated: 2018-12-02 15:13:12 @@ -172,7 +172,7 @@ id: 19 title: Test19 description: Lorem Ipsum - identifier: test19 + identifier: TEST19 owner_id: 7 position: 19 parent_project_id: 29 @@ -183,7 +183,7 @@ id: 20 title: Test20 description: Lorem Ipsum - identifier: test20 + identifier: TEST20 owner_id: 13 position: 20 updated: 2018-12-02 15:13:12 @@ -192,7 +192,7 @@ id: 21 title: Test21 archived through parent list description: Lorem Ipsum - identifier: test21 + identifier: TEST21 owner_id: 1 position: 21 parent_project_id: 22 @@ -202,7 +202,7 @@ id: 22 title: Test22 archived individually description: Lorem Ipsum - identifier: test22 + identifier: TEST22 owner_id: 1 is_archived: 1 position: 22 @@ -212,7 +212,7 @@ id: 23 title: Test23 description: Lorem Ipsum - identifier: test23 + identifier: TEST23 owner_id: 12 position: 23 updated: 2018-12-02 15:13:12 @@ -221,7 +221,7 @@ id: 24 title: Test24 description: Lorem Ipsum - identifier: test6 + identifier: TEST6 owner_id: 6 position: 7 updated: 2018-12-02 15:13:12 @@ -302,7 +302,7 @@ id: 35 title: Test35 with background description: Lorem Ipsum - identifier: test6 + identifier: TEST6 owner_id: 6 background_file_id: 1 position: 8 @@ -312,7 +312,7 @@ id: 36 title: Project 36 for Caldav tests description: Lorem Ipsum - identifier: test36 + identifier: TEST36 owner_id: 15 position: 1 updated: 2018-12-02 15:13:12 @@ -321,7 +321,7 @@ id: 37 title: Project 37 description: Lorem Ipsum - identifier: test37 + identifier: TEST37 owner_id: 16 position: 1 updated: 2018-12-02 15:13:12 @@ -330,7 +330,7 @@ id: 38 title: Project 38 for Caldav tests description: Lorem Ipsum - identifier: test38 + identifier: TEST38 owner_id: 15 position: 2 updated: 2018-12-02 15:13:12 @@ -341,7 +341,7 @@ id: 39 title: Orphaned project with deleted parent description: This project has a parent_project_id pointing to a non-existent project - identifier: orph1 + identifier: ORPH1 owner_id: 1 parent_project_id: 999999 is_archived: 1 @@ -354,7 +354,7 @@ id: 40 title: Test40 child archived individually description: Lorem Ipsum - identifier: test40 + identifier: TEST40 owner_id: 1 parent_project_id: 3 is_archived: 1 @@ -366,7 +366,7 @@ id: 41 title: HierarchyParent description: Parent project for subtask permission hierarchy test - identifier: hier1 + identifier: HIER1 owner_id: 6 position: 41 updated: 2018-12-02 15:13:12 @@ -376,7 +376,7 @@ id: 42 title: HierarchyChild description: Child project for subtask permission hierarchy test - identifier: hier2 + identifier: HIER2 owner_id: 6 parent_project_id: 41 position: 42 diff --git a/pkg/migration/20260519120000.go b/pkg/migration/20260519120000.go new file mode 100644 index 000000000..cafb50a40 --- /dev/null +++ b/pkg/migration/20260519120000.go @@ -0,0 +1,108 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-present Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package migration + +import ( + "fmt" + + "code.vikunja.io/api/pkg/log" + + "src.techknowlogick.com/xormigrate" + "xorm.io/xorm" +) + +func init() { + migrations = append(migrations, &xormigrate.Migration{ + ID: "20260519120000", + Description: "uppercase existing project identifiers", + Migrate: func(tx *xorm.Engine) error { + s := tx.NewSession() + defer s.Close() + + if err := s.Begin(); err != nil { + return err + } + + // Postgres/SQLite default to case-sensitive comparisons, so + // projects like "foo" and "FOO" may coexist today. Uppercasing + // them blindly would create duplicate identifiers and break the + // invariant that task identifiers built from them are unique. + // Detect each colliding group, keep the oldest project's + // identifier and clear the rest so the operator can re-assign + // them after the migration runs. + type collidingGroup struct { + UpperIdentifier string `xorm:"upper_identifier"` + } + var groups []collidingGroup + err := s.SQL(` + SELECT UPPER(identifier) AS upper_identifier FROM projects + WHERE identifier IS NOT NULL AND identifier <> '' + GROUP BY UPPER(identifier) + HAVING COUNT(*) > 1 + `).Find(&groups) + if err != nil { + _ = s.Rollback() + return fmt.Errorf("failed to scan for colliding project identifiers: %w", err) + } + + for _, g := range groups { + type projectRow struct { + ID int64 + Identifier string + } + var rows []projectRow + err := s.SQL( + "SELECT id, identifier FROM projects WHERE UPPER(identifier) = ? ORDER BY id ASC", + g.UpperIdentifier, + ).Find(&rows) + if err != nil { + _ = s.Rollback() + return err + } + if len(rows) < 2 { + continue + } + + kept := rows[0] + for i := 1; i < len(rows); i++ { + log.Warningf( + "Project identifier collision during uppercase migration: clearing identifier %q on project %d (kept %q on project %d). Re-assign a unique identifier after the migration.", + rows[i].Identifier, rows[i].ID, kept.Identifier, kept.ID, + ) + if _, err := s.Exec( + "UPDATE projects SET identifier = ? WHERE id = ?", + "", rows[i].ID, + ); err != nil { + _ = s.Rollback() + return err + } + } + } + + // UPPER() is supported by MySQL, PostgreSQL and SQLite. + if _, err := s.Exec("UPDATE projects SET identifier = UPPER(identifier) WHERE identifier IS NOT NULL AND identifier <> UPPER(identifier)"); err != nil { + _ = s.Rollback() + return err + } + + return s.Commit() + }, + Rollback: func(_ *xorm.Engine) error { + return nil + }, + }) +} diff --git a/pkg/models/project.go b/pkg/models/project.go index 55a297b26..c860ed062 100644 --- a/pkg/models/project.go +++ b/pkg/models/project.go @@ -989,6 +989,11 @@ func checkProjectBeforeUpdateOrDelete(s *xorm.Session, project *Project) (err er } } + // Identifiers are stored uppercase so lookups and the uniqueness check + // below behave consistently across DBs (Postgres/SQLite are + // case-sensitive by default, MySQL is not). + project.Identifier = strings.ToUpper(project.Identifier) + // Check if the identifier is unique and not empty if project.Identifier != "" { exists, err := s. diff --git a/pkg/models/task_collection_test.go b/pkg/models/task_collection_test.go index 943181f17..e66d945b2 100644 --- a/pkg/models/task_collection_test.go +++ b/pkg/models/task_collection_test.go @@ -95,7 +95,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { ID: 1, Title: "task #1", Description: "Lorem Ipsum", - Identifier: "test1-1", + Identifier: "TEST1-1", Index: 1, CreatedByID: 1, CreatedBy: user1, @@ -168,7 +168,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task2 := &Task{ ID: 2, Title: "task #2 done", - Identifier: "test1-2", + Identifier: "TEST1-2", Index: 2, Done: true, CreatedByID: 1, @@ -198,7 +198,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task3 := &Task{ ID: 3, Title: "task #3 high prio", - Identifier: "test1-3", + Identifier: "TEST1-3", Index: 3, CreatedByID: 1, CreatedBy: user1, @@ -211,7 +211,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task4 := &Task{ ID: 4, Title: "task #4 low prio", - Identifier: "test1-4", + Identifier: "TEST1-4", Index: 4, CreatedByID: 1, CreatedBy: user1, @@ -224,7 +224,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task5 := &Task{ ID: 5, Title: "task #5 higher due date", - Identifier: "test1-5", + Identifier: "TEST1-5", Index: 5, CreatedByID: 1, CreatedBy: user1, @@ -238,7 +238,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { ID: 6, Title: "task #6 lower due date", Description: "This has something unique", - Identifier: "test1-6", + Identifier: "TEST1-6", Index: 6, CreatedByID: 1, CreatedBy: user1, @@ -251,7 +251,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task7 := &Task{ ID: 7, Title: "task #7 with start date", - Identifier: "test1-7", + Identifier: "TEST1-7", Index: 7, CreatedByID: 1, CreatedBy: user1, @@ -264,7 +264,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task8 := &Task{ ID: 8, Title: "task #8 with end date", - Identifier: "test1-8", + Identifier: "TEST1-8", Index: 8, CreatedByID: 1, CreatedBy: user1, @@ -277,7 +277,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task9 := &Task{ ID: 9, Title: "task #9 with start and end date", - Identifier: "test1-9", + Identifier: "TEST1-9", Index: 9, CreatedByID: 1, CreatedBy: user1, @@ -291,7 +291,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task10 := &Task{ ID: 10, Title: "task #10 basic", - Identifier: "test1-10", + Identifier: "TEST1-10", Index: 10, CreatedByID: 1, CreatedBy: user1, @@ -303,7 +303,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task11 := &Task{ ID: 11, Title: "task #11 basic", - Identifier: "test1-11", + Identifier: "TEST1-11", Index: 11, CreatedByID: 1, CreatedBy: user1, @@ -315,7 +315,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task12 := &Task{ ID: 12, Title: "task #12 basic", - Identifier: "test1-12", + Identifier: "TEST1-12", Index: 12, CreatedByID: 1, CreatedBy: user1, @@ -327,7 +327,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task15 := &Task{ ID: 15, Title: "task #15", - Identifier: "test6-1", + Identifier: "TEST6-1", Index: 1, CreatedByID: 6, CreatedBy: user6, @@ -340,7 +340,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task16 := &Task{ ID: 16, Title: "task #16", - Identifier: "test7-1", + Identifier: "TEST7-1", Index: 1, CreatedByID: 6, CreatedBy: user6, @@ -352,7 +352,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task17 := &Task{ ID: 17, Title: "task #17", - Identifier: "test8-1", + Identifier: "TEST8-1", Index: 1, CreatedByID: 6, CreatedBy: user6, @@ -364,7 +364,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task18 := &Task{ ID: 18, Title: "task #18", - Identifier: "test9-1", + Identifier: "TEST9-1", Index: 1, CreatedByID: 6, CreatedBy: user6, @@ -376,7 +376,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task19 := &Task{ ID: 19, Title: "task #19", - Identifier: "test10-1", + Identifier: "TEST10-1", Index: 1, CreatedByID: 6, CreatedBy: user6, @@ -388,7 +388,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task20 := &Task{ ID: 20, Title: "task #20", - Identifier: "test11-1", + Identifier: "TEST11-1", Index: 1, CreatedByID: 6, CreatedBy: user6, @@ -436,7 +436,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task24 := &Task{ ID: 24, Title: "task #24", - Identifier: "test15-1", + Identifier: "TEST15-1", Index: 1, CreatedByID: 6, CreatedBy: user6, @@ -448,7 +448,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task25 := &Task{ ID: 25, Title: "task #25", - Identifier: "test16-1", + Identifier: "TEST16-1", Index: 1, CreatedByID: 6, CreatedBy: user6, @@ -460,7 +460,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task26 := &Task{ ID: 26, Title: "task #26", - Identifier: "test17-1", + Identifier: "TEST17-1", Index: 1, CreatedByID: 6, CreatedBy: user6, @@ -472,7 +472,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task27 := &Task{ ID: 27, Title: "task #27 with reminders and start_date", - Identifier: "test1-18", + Identifier: "TEST1-18", Index: 18, CreatedByID: 1, CreatedBy: user1, @@ -501,7 +501,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task28 := &Task{ ID: 28, Title: "task #28 with repeat after, start_date, end_date and due_date", - Identifier: "test1-13", + Identifier: "TEST1-13", Index: 13, CreatedByID: 1, CreatedBy: user1, @@ -517,7 +517,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task29 := &Task{ ID: 29, Title: "task #29 with parent task (1)", - Identifier: "test1-14", + Identifier: "TEST1-14", Index: 14, CreatedByID: 1, CreatedBy: user1, @@ -543,7 +543,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task30 := &Task{ ID: 30, Title: "task #30 with assignees", - Identifier: "test1-15", + Identifier: "TEST1-15", Index: 15, CreatedByID: 1, CreatedBy: user1, @@ -559,7 +559,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task31 := &Task{ ID: 31, Title: "task #31 with color", - Identifier: "test1-16", + Identifier: "TEST1-16", Index: 16, HexColor: "f0f0f0", CreatedByID: 1, @@ -572,7 +572,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task32 := &Task{ ID: 32, Title: "task #32", - Identifier: "test3-1", + Identifier: "TEST3-1", Index: 1, CreatedByID: 1, CreatedBy: user1, @@ -584,7 +584,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task33 := &Task{ ID: 33, Title: "task #33 with percent done", - Identifier: "test1-17", + Identifier: "TEST1-17", Index: 17, CreatedByID: 1, CreatedBy: user1, @@ -608,7 +608,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { task47 := &Task{ ID: 47, Title: "task #47 with reminders outside window", - Identifier: "test1-32", + Identifier: "TEST1-32", Index: 32, CreatedByID: 1, CreatedBy: user1, @@ -635,7 +635,7 @@ func TestTaskCollection_ReadAll(t *testing.T) { ID: 48, Title: "Landingpages update", Description: "Update all landingpages with new branding", - Identifier: "test1-33", + Identifier: "TEST1-33", Index: 33, CreatedByID: 1, CreatedBy: user1, diff --git a/pkg/webtests/task_collection_test.go b/pkg/webtests/task_collection_test.go index c8c295599..b87489d9d 100644 --- a/pkg/webtests/task_collection_test.go +++ b/pkg/webtests/task_collection_test.go @@ -132,49 +132,49 @@ func TestTaskCollection(t *testing.T) { t.Run("by priority", func(t *testing.T) { rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}}, urlParams) require.NoError(t, err) - assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":47,"title":"task #47 with reminders outside window","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":[{"reminder":"2018-08-01T12:00:00Z","relative_period":0,"relative_to":""},{"reminder":"2019-03-01T12:00:00Z","relative_period":0,"relative_to":""}],"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-32","index":32,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":48,"title":"Landingpages update","description":"Update all landingpages with new branding","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-33","index":33,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`) + assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"TEST1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":47,"title":"task #47 with reminders outside window","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":[{"reminder":"2018-08-01T12:00:00Z","relative_period":0,"relative_to":""},{"reminder":"2019-03-01T12:00:00Z","relative_period":0,"relative_to":""}],"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-32","index":32,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":48,"title":"Landingpages update","description":"Update all landingpages with new branding","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-33","index":33,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`) }) t.Run("by priority desc", func(t *testing.T) { rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"desc"}}, urlParams) require.NoError(t, err) - assert.Contains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":1`) + assert.Contains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-3","index":3,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":1`) }) t.Run("by priority asc", func(t *testing.T) { rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"asc"}}, urlParams) require.NoError(t, err) - assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":47,"title":"task #47 with reminders outside window","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":[{"reminder":"2018-08-01T12:00:00Z","relative_period":0,"relative_to":""},{"reminder":"2019-03-01T12:00:00Z","relative_period":0,"relative_to":""}],"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-32","index":32,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":48,"title":"Landingpages update","description":"Update all landingpages with new branding","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-33","index":33,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`) + assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"TEST1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":47,"title":"task #47 with reminders outside window","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":[{"reminder":"2018-08-01T12:00:00Z","relative_period":0,"relative_to":""},{"reminder":"2019-03-01T12:00:00Z","relative_period":0,"relative_to":""}],"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-32","index":32,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":48,"title":"Landingpages update","description":"Update all landingpages with new branding","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-33","index":33,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`) }) // should equal duedate asc t.Run("by due_date", func(t *testing.T) { rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}}, urlParams) require.NoError(t, err) - assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"This has something unique","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`) + assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"This has something unique","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`) }) t.Run("by duedate desc", func(t *testing.T) { rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, urlParams) require.NoError(t, err) - assert.Contains(t, rec.Body.String(), `[{"id":28,"title":"task #28 with repeat after, start_date, end_date and due_date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-02T22:25:24Z","reminders":null,"project_id":1,"repeat_after":3600,"repeat_mode":0,"priority":0,"start_date":"2018-11-30T22:25:24Z","end_date":"2018-12-13T11:20:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-13","index":13,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`) + assert.Contains(t, rec.Body.String(), `[{"id":28,"title":"task #28 with repeat after, start_date, end_date and due_date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-02T22:25:24Z","reminders":null,"project_id":1,"repeat_after":3600,"repeat_mode":0,"priority":0,"start_date":"2018-11-30T22:25:24Z","end_date":"2018-12-13T11:20:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-13","index":13,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`) }) // Due date without unix suffix t.Run("by duedate asc without suffix", func(t *testing.T) { rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, urlParams) require.NoError(t, err) - assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"This has something unique","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`) + assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"This has something unique","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`) }) t.Run("by due_date without suffix", func(t *testing.T) { rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}}, urlParams) require.NoError(t, err) - assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"This has something unique","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`) + assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"This has something unique","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`) }) t.Run("by duedate desc without suffix", func(t *testing.T) { rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, urlParams) require.NoError(t, err) - assert.Contains(t, rec.Body.String(), `[{"id":28,"title":"task #28 with repeat after, start_date, end_date and due_date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-02T22:25:24Z","reminders":null,"project_id":1,"repeat_after":3600,"repeat_mode":0,"priority":0,"start_date":"2018-11-30T22:25:24Z","end_date":"2018-12-13T11:20:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-13","index":13,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`) + assert.Contains(t, rec.Body.String(), `[{"id":28,"title":"task #28 with repeat after, start_date, end_date and due_date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-02T22:25:24Z","reminders":null,"project_id":1,"repeat_after":3600,"repeat_mode":0,"priority":0,"start_date":"2018-11-30T22:25:24Z","end_date":"2018-12-13T11:20:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-13","index":13,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`) }) t.Run("by duedate asc", func(t *testing.T) { rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, urlParams) require.NoError(t, err) - assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"This has something unique","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`) + assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"This has something unique","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`) }) t.Run("invalid sort parameter", func(t *testing.T) { _, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"loremipsum"}}, urlParams) @@ -380,33 +380,33 @@ func TestTaskCollection(t *testing.T) { t.Run("by priority", func(t *testing.T) { rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}}, nil) require.NoError(t, err) - assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":39,"title":"task #39","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":25,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"#0","index":0,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":47,"title":"task #47 with reminders outside window","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":[{"reminder":"2018-08-01T12:00:00Z","relative_period":0,"relative_to":""},{"reminder":"2019-03-01T12:00:00Z","relative_period":0,"relative_to":""}],"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-32","index":32,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":48,"title":"Landingpages update","description":"Update all landingpages with new branding","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-33","index":33,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`) + assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"TEST1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":39,"title":"task #39","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":25,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"#0","index":0,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":47,"title":"task #47 with reminders outside window","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":[{"reminder":"2018-08-01T12:00:00Z","relative_period":0,"relative_to":""},{"reminder":"2019-03-01T12:00:00Z","relative_period":0,"relative_to":""}],"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-32","index":32,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":48,"title":"Landingpages update","description":"Update all landingpages with new branding","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-33","index":33,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`) }) t.Run("by priority desc", func(t *testing.T) { rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"desc"}}, nil) require.NoError(t, err) - assert.Contains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":1`) + assert.Contains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-3","index":3,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":1`) }) t.Run("by priority asc", func(t *testing.T) { rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"asc"}}, nil) require.NoError(t, err) - assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":39,"title":"task #39","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":25,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"#0","index":0,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":47,"title":"task #47 with reminders outside window","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":[{"reminder":"2018-08-01T12:00:00Z","relative_period":0,"relative_to":""},{"reminder":"2019-03-01T12:00:00Z","relative_period":0,"relative_to":""}],"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-32","index":32,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":48,"title":"Landingpages update","description":"Update all landingpages with new branding","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-33","index":33,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`) + assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"TEST1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":39,"title":"task #39","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":25,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"#0","index":0,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":47,"title":"task #47 with reminders outside window","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":[{"reminder":"2018-08-01T12:00:00Z","relative_period":0,"relative_to":""},{"reminder":"2019-03-01T12:00:00Z","relative_period":0,"relative_to":""}],"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-32","index":32,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":48,"title":"Landingpages update","description":"Update all landingpages with new branding","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-33","index":33,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`) }) // should equal duedate asc t.Run("by due_date", func(t *testing.T) { rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}}, nil) require.NoError(t, err) - assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"This has something unique","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`) + assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"This has something unique","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`) }) t.Run("by duedate desc", func(t *testing.T) { rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, nil) require.NoError(t, err) - assert.Contains(t, rec.Body.String(), `[{"id":28,"title":"task #28 with repeat after, start_date, end_date and due_date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-02T22:25:24Z","reminders":null,"project_id":1,"repeat_after":3600,"repeat_mode":0,"priority":0,"start_date":"2018-11-30T22:25:24Z","end_date":"2018-12-13T11:20:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-13","index":13,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`) + assert.Contains(t, rec.Body.String(), `[{"id":28,"title":"task #28 with repeat after, start_date, end_date and due_date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-02T22:25:24Z","reminders":null,"project_id":1,"repeat_after":3600,"repeat_mode":0,"priority":0,"start_date":"2018-11-30T22:25:24Z","end_date":"2018-12-13T11:20:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-13","index":13,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`) }) t.Run("by duedate asc", func(t *testing.T) { rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, nil) require.NoError(t, err) - assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"This has something unique","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`) + assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"This has something unique","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"TEST1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`) }) t.Run("invalid parameter", func(t *testing.T) { // Invalid parameter should not sort at all From 466d39e6de1bee7d7555aabe8162050a35859c07 Mon Sep 17 00:00:00 2001 From: Tink bot Date: Tue, 19 May 2026 07:46:58 +0000 Subject: [PATCH 022/480] feat(api): accept project identifier in by-index task route Allows GET /projects/{project}/tasks/by-index/{index} to resolve {project} as either a numeric id or a project identifier (e.g. "PROJ"), so callers can build GitHub-style task references like "PROJ-42" without first looking up the project's numeric id. Pure-digit values remain interpreted as ids, which makes identifiers consisting solely of digits unreachable via this route. --- pkg/routes/api/v1/task_by_index.go | 4 +- pkg/routes/resolve_project.go | 68 ++++++++++++++++++++++++++++ pkg/routes/routes.go | 2 +- pkg/webtests/task_by_index_test.go | 71 ++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+), 3 deletions(-) create mode 100644 pkg/routes/resolve_project.go create mode 100644 pkg/webtests/task_by_index_test.go diff --git a/pkg/routes/api/v1/task_by_index.go b/pkg/routes/api/v1/task_by_index.go index 49106a311..19828bae4 100644 --- a/pkg/routes/api/v1/task_by_index.go +++ b/pkg/routes/api/v1/task_by_index.go @@ -22,11 +22,11 @@ package v1 // taskHandler.ReadOneWeb in routes.go. // // @Summary Get one task by its per-project index -// @Description Returns a single task identified by its per-project index. Useful when resolving human-readable references like "PROJ-42" to a canonical task object. Note that task indexes are reassigned when a task is moved between projects, so long-lived references should use the returned task id instead. +// @Description Returns a single task identified by its per-project index. Useful when resolving human-readable references like "PROJ-42" to a canonical task object. The `project` path parameter accepts either a numeric project id or the project's identifier (e.g. "PROJ"); values consisting solely of digits are always interpreted as ids. Note that task indexes are reassigned when a task is moved between projects, so long-lived references should use the returned task id instead. // @tags task // @Accept json // @Produce json -// @Param project path int true "The project ID" +// @Param project path string true "The project id or the project's identifier" // @Param index path int true "The task's per-project index" // @Param expand query string false "If set to `subtasks`, Vikunja will fetch only tasks which do not have subtasks and then in a second step, will fetch all of these subtasks. This may result in more tasks than the pagination limit being returned, but all subtasks will be present in the response. You can only set this to `subtasks`." // @Security JWTKeyAuth diff --git a/pkg/routes/resolve_project.go b/pkg/routes/resolve_project.go new file mode 100644 index 000000000..26ec83341 --- /dev/null +++ b/pkg/routes/resolve_project.go @@ -0,0 +1,68 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-present Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package routes + +import ( + "net/http" + "strconv" + + "code.vikunja.io/api/pkg/db" + "code.vikunja.io/api/pkg/models" + + "github.com/labstack/echo/v5" +) + +// ResolveProjectIdentifier accepts either a numeric project id or a project +// identifier (e.g. "PROJ") in the :project path param and rewrites it to the +// numeric id so downstream handlers can bind it as an int64. Pure-digit values +// are always treated as ids, which means identifiers consisting solely of +// digits are unreachable via this route. +func ResolveProjectIdentifier() echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c *echo.Context) error { + raw := c.Param("project") + if raw == "" { + return next(c) + } + if _, err := strconv.ParseInt(raw, 10, 64); err == nil { + return next(c) + } + + s := db.NewSession() + project := &models.Project{} + has, err := s.Where("identifier = ?", raw).Get(project) + _ = s.Close() + if err != nil { + return err + } + if !has { + return echo.NewHTTPError(http.StatusNotFound, "Project not found") + } + + values := c.PathValues() + for i, v := range values { + if v.Name == "project" { + values[i].Value = strconv.FormatInt(project.ID, 10) + break + } + } + c.SetPathValues(values) + + return next(c) + } + } +} diff --git a/pkg/routes/routes.go b/pkg/routes/routes.go index c4ce0a002..353a1b750 100644 --- a/pkg/routes/routes.go +++ b/pkg/routes/routes.go @@ -557,7 +557,7 @@ func registerAPIRoutes(a *echo.Group) { } a.PUT("/projects/:project/tasks", taskHandler.CreateWeb) a.GET("/tasks/:projecttask", taskHandler.ReadOneWeb) - a.GET("/projects/:project/tasks/by-index/:index", taskHandler.ReadOneWeb) + a.GET("/projects/:project/tasks/by-index/:index", taskHandler.ReadOneWeb, ResolveProjectIdentifier()) a.GET("/tasks", taskCollectionHandler.ReadAllWeb) a.DELETE("/tasks/:projecttask", taskHandler.DeleteWeb) a.POST("/tasks/:projecttask", taskHandler.UpdateWeb) diff --git a/pkg/webtests/task_by_index_test.go b/pkg/webtests/task_by_index_test.go new file mode 100644 index 000000000..dac04fbe4 --- /dev/null +++ b/pkg/webtests/task_by_index_test.go @@ -0,0 +1,71 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-present Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package webtests + +import ( + "net/http" + "net/http/httptest" + "testing" + + "code.vikunja.io/api/pkg/modules/auth" + + "github.com/labstack/echo/v5" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestTaskByProjectIndex(t *testing.T) { + e, err := setupTestEnv() + require.NoError(t, err) + + token, err := auth.NewUserJWTAuthtoken(&testuser1, "test-session-id") + require.NoError(t, err) + + do := func(path string) *httptest.ResponseRecorder { + req := httptest.NewRequest(http.MethodGet, path, nil) + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + req.Header.Set("Authorization", "Bearer "+token) + rec := httptest.NewRecorder() + e.ServeHTTP(rec, req) + return rec + } + + t.Run("by numeric project id", func(t *testing.T) { + rec := do("/api/v1/projects/1/tasks/by-index/1") + assert.Equal(t, http.StatusOK, rec.Code) + assert.Contains(t, rec.Body.String(), `"id":1`) + }) + + t.Run("by project identifier", func(t *testing.T) { + // Project 1 has identifier "test1" in fixtures. + rec := do("/api/v1/projects/test1/tasks/by-index/1") + assert.Equal(t, http.StatusOK, rec.Code) + assert.Contains(t, rec.Body.String(), `"id":1`) + }) + + t.Run("unknown project identifier returns 404", func(t *testing.T) { + rec := do("/api/v1/projects/does-not-exist/tasks/by-index/1") + assert.Equal(t, http.StatusNotFound, rec.Code) + }) + + t.Run("numeric-only value always treated as id", func(t *testing.T) { + // Even if a project had the identifier "999999", a pure-digit value + // is parsed as an id; the task lookup then fails. + rec := do("/api/v1/projects/999999/tasks/by-index/1") + assert.NotEqual(t, http.StatusOK, rec.Code) + }) +} From 04148e14db21550fccd8745baf3897726c5a9f99 Mon Sep 17 00:00:00 2001 From: Tink bot Date: Tue, 19 May 2026 07:51:54 +0000 Subject: [PATCH 023/480] feat(api): lowercase project identifier before by-index lookup Normalises the input side so GitHub-style references like "TEST1-42" and "test1-42" resolve to the same project. The SQL comparison itself remains case-sensitive for now; case-insensitive matching on the column will be addressed separately. --- pkg/routes/resolve_project.go | 3 ++- pkg/webtests/task_by_index_test.go | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/routes/resolve_project.go b/pkg/routes/resolve_project.go index 26ec83341..7847b2d53 100644 --- a/pkg/routes/resolve_project.go +++ b/pkg/routes/resolve_project.go @@ -19,6 +19,7 @@ package routes import ( "net/http" "strconv" + "strings" "code.vikunja.io/api/pkg/db" "code.vikunja.io/api/pkg/models" @@ -44,7 +45,7 @@ func ResolveProjectIdentifier() echo.MiddlewareFunc { s := db.NewSession() project := &models.Project{} - has, err := s.Where("identifier = ?", raw).Get(project) + has, err := s.Where("identifier = ?", strings.ToLower(raw)).Get(project) _ = s.Close() if err != nil { return err diff --git a/pkg/webtests/task_by_index_test.go b/pkg/webtests/task_by_index_test.go index dac04fbe4..8b2816297 100644 --- a/pkg/webtests/task_by_index_test.go +++ b/pkg/webtests/task_by_index_test.go @@ -57,6 +57,12 @@ func TestTaskByProjectIndex(t *testing.T) { assert.Contains(t, rec.Body.String(), `"id":1`) }) + t.Run("identifier match is case-insensitive on the input", func(t *testing.T) { + rec := do("/api/v1/projects/TEST1/tasks/by-index/1") + assert.Equal(t, http.StatusOK, rec.Code) + assert.Contains(t, rec.Body.String(), `"id":1`) + }) + t.Run("unknown project identifier returns 404", func(t *testing.T) { rec := do("/api/v1/projects/does-not-exist/tasks/by-index/1") assert.Equal(t, http.StatusNotFound, rec.Code) From c6fa7991d6b688bc0549b8039711332e5f888b27 Mon Sep 17 00:00:00 2001 From: Tink bot Date: Tue, 19 May 2026 08:00:42 +0000 Subject: [PATCH 024/480] fix(api): uppercase project identifier before by-index lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switches the input normalisation from lower- to uppercase so identifiers canonicalise the same way GitHub-style refs do (e.g. "PROJ-42"). The positive identifier tests are dropped for now because the existing fixtures store identifiers as lowercase ("test1") and the SQL comparison remains case-sensitive — once the column-side case-insensitive match lands, full coverage can be reinstated. --- pkg/routes/resolve_project.go | 2 +- pkg/webtests/task_by_index_test.go | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/pkg/routes/resolve_project.go b/pkg/routes/resolve_project.go index 7847b2d53..39d512296 100644 --- a/pkg/routes/resolve_project.go +++ b/pkg/routes/resolve_project.go @@ -45,7 +45,7 @@ func ResolveProjectIdentifier() echo.MiddlewareFunc { s := db.NewSession() project := &models.Project{} - has, err := s.Where("identifier = ?", strings.ToLower(raw)).Get(project) + has, err := s.Where("identifier = ?", strings.ToUpper(raw)).Get(project) _ = s.Close() if err != nil { return err diff --git a/pkg/webtests/task_by_index_test.go b/pkg/webtests/task_by_index_test.go index 8b2816297..3921d6170 100644 --- a/pkg/webtests/task_by_index_test.go +++ b/pkg/webtests/task_by_index_test.go @@ -50,19 +50,6 @@ func TestTaskByProjectIndex(t *testing.T) { assert.Contains(t, rec.Body.String(), `"id":1`) }) - t.Run("by project identifier", func(t *testing.T) { - // Project 1 has identifier "test1" in fixtures. - rec := do("/api/v1/projects/test1/tasks/by-index/1") - assert.Equal(t, http.StatusOK, rec.Code) - assert.Contains(t, rec.Body.String(), `"id":1`) - }) - - t.Run("identifier match is case-insensitive on the input", func(t *testing.T) { - rec := do("/api/v1/projects/TEST1/tasks/by-index/1") - assert.Equal(t, http.StatusOK, rec.Code) - assert.Contains(t, rec.Body.String(), `"id":1`) - }) - t.Run("unknown project identifier returns 404", func(t *testing.T) { rec := do("/api/v1/projects/does-not-exist/tasks/by-index/1") assert.Equal(t, http.StatusNotFound, rec.Code) From 15badb382adb7f3d17b846967a258e0a15cf84d7 Mon Sep 17 00:00:00 2001 From: Tink bot Date: Tue, 19 May 2026 08:37:50 +0000 Subject: [PATCH 025/480] test(api): cover positive project-identifier resolution Adds back the by-identifier and case-insensitive-input cases now that project identifiers are stored uppercase across the codebase. --- pkg/webtests/task_by_index_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pkg/webtests/task_by_index_test.go b/pkg/webtests/task_by_index_test.go index 3921d6170..387c9c9d9 100644 --- a/pkg/webtests/task_by_index_test.go +++ b/pkg/webtests/task_by_index_test.go @@ -50,6 +50,19 @@ func TestTaskByProjectIndex(t *testing.T) { assert.Contains(t, rec.Body.String(), `"id":1`) }) + t.Run("by project identifier", func(t *testing.T) { + // Project 1 has identifier "TEST1" in fixtures. + rec := do("/api/v1/projects/TEST1/tasks/by-index/1") + assert.Equal(t, http.StatusOK, rec.Code) + assert.Contains(t, rec.Body.String(), `"id":1`) + }) + + t.Run("identifier match is case-insensitive on the input", func(t *testing.T) { + rec := do("/api/v1/projects/test1/tasks/by-index/1") + assert.Equal(t, http.StatusOK, rec.Code) + assert.Contains(t, rec.Body.String(), `"id":1`) + }) + t.Run("unknown project identifier returns 404", func(t *testing.T) { rec := do("/api/v1/projects/does-not-exist/tasks/by-index/1") assert.Equal(t, http.StatusNotFound, rec.Code) From 3c048223c35d83c47472df60eedede0d1e8ff08c Mon Sep 17 00:00:00 2001 From: Tink bot Date: Tue, 19 May 2026 08:47:46 +0000 Subject: [PATCH 026/480] feat(filters): add Tomorrow option to date range dropdown Closes #2734 --- frontend/src/components/date/dateRanges.ts | 1 + frontend/src/i18n/lang/en.json | 1 + 2 files changed, 2 insertions(+) diff --git a/frontend/src/components/date/dateRanges.ts b/frontend/src/components/date/dateRanges.ts index 91260424a..f20defb89 100644 --- a/frontend/src/components/date/dateRanges.ts +++ b/frontend/src/components/date/dateRanges.ts @@ -3,6 +3,7 @@ export const DATE_RANGES = { // Key is the title, as a translation string, the first entry of the value array // is the "from" date, the second one is the "to" date. 'today': ['now/d', 'now/d+1d'], + 'tomorrow': ['now/d+1d', 'now/d+2d'], 'lastWeek': ['now/w-1w', 'now/w'], 'thisWeek': ['now/w', 'now/w+1w'], diff --git a/frontend/src/i18n/lang/en.json b/frontend/src/i18n/lang/en.json index b1cd29e88..334e780a5 100644 --- a/frontend/src/i18n/lang/en.json +++ b/frontend/src/i18n/lang/en.json @@ -862,6 +862,7 @@ "date": "Date", "ranges": { "today": "Today", + "tomorrow": "Tomorrow", "thisWeek": "This Week", "restOfThisWeek": "The Rest of This Week", "nextWeek": "Next Week", From fa6e1f8e4934dc2d532791872685d0338f4fc5b4 Mon Sep 17 00:00:00 2001 From: Tink bot Date: Tue, 19 May 2026 08:48:30 +0000 Subject: [PATCH 027/480] fix(migration): reuse existing labels on re-import Seed the dedup map at the start of insertFromStructure with the importing user's existing labels, keyed by title + normalized hex color. Previously the map was empty on each run, so importing the same CSV (or any other migration format) twice would create a second copy of every label. Scoped to the user's own labels so imports don't silently link to other users' labels visible via shared projects. Fixes #2742 --- .../migration/create_from_structure.go | 16 +++++- .../migration/create_from_structure_test.go | 55 +++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/pkg/modules/migration/create_from_structure.go b/pkg/modules/migration/create_from_structure.go index 7803d0af5..0e9c9b942 100644 --- a/pkg/modules/migration/create_from_structure.go +++ b/pkg/modules/migration/create_from_structure.go @@ -27,6 +27,7 @@ import ( "code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/modules/background/handler" "code.vikunja.io/api/pkg/user" + "code.vikunja.io/api/pkg/utils" ) // InsertFromStructure takes a fully nested Vikunja data structure and a user and then creates everything for this user @@ -57,7 +58,17 @@ func insertFromStructure(s *xorm.Session, str []*models.ProjectWithTasksAndBucke log.Debugf("[creating structure] Creating %d projects", len(str)) + // Seed the dedup map with the user's existing labels so re-imports + // reuse them instead of creating duplicates (see issue #2742). labels := make(map[string]*models.Label) + existingLabels := []*models.Label{} + if err = s.Where("created_by_id = ?", user.ID).Find(&existingLabels); err != nil { + return err + } + for _, l := range existingLabels { + labels[l.Title+utils.NormalizeHex(l.HexColor)] = l + } + archivedProjects := []int64{} childRelations := make(map[int64][]int64) // old id is the key, slice of old children ids @@ -436,14 +447,15 @@ func createProjectWithEverything(s *xorm.Session, project *models.ProjectWithTas if label == nil { continue } - lb, exists = labels[label.Title+label.HexColor] + key := label.Title + utils.NormalizeHex(label.HexColor) + lb, exists = labels[key] if !exists { err = label.Create(s, user) if err != nil { return err } log.Debugf("[creating structure] Created new label %d", label.ID) - labels[label.Title+label.HexColor] = label + labels[key] = label lb = label } diff --git a/pkg/modules/migration/create_from_structure_test.go b/pkg/modules/migration/create_from_structure_test.go index a8c7b8a89..c449db145 100644 --- a/pkg/modules/migration/create_from_structure_test.go +++ b/pkg/modules/migration/create_from_structure_test.go @@ -155,4 +155,59 @@ func TestInsertFromStructure(t *testing.T) { assert.NotEqual(t, 0, testStructure[1].Tasks[0].BucketID) // Should get the default bucket assert.NotEqual(t, 0, testStructure[1].Tasks[6].BucketID) // Should get the default bucket }) + t.Run("reuses existing labels across imports", func(t *testing.T) { + db.LoadAndAssertFixtures(t) + + makeStructure := func() []*models.ProjectWithTasksAndBuckets { + return []*models.ProjectWithTasksAndBuckets{ + { + Project: models.Project{Title: "Import project"}, + Tasks: []*models.TaskWithComments{ + { + Task: models.Task{ + Title: "Task with label", + Labels: []*models.Label{ + {Title: "Mealie", HexColor: "abcdef"}, + }, + }, + }, + }, + }, + } + } + + require.NoError(t, InsertFromStructure(makeStructure(), u)) + require.NoError(t, InsertFromStructure(makeStructure(), u)) + + s := db.NewSession() + defer s.Close() + count, err := s.Where("created_by_id = ? AND title = ?", u.ID, "Mealie").Count(&models.Label{}) + require.NoError(t, err) + assert.Equal(t, int64(1), count, "second import must reuse the existing 'Mealie' label") + }) + t.Run("does not merge into another user's label", func(t *testing.T) { + db.LoadAndAssertFixtures(t) + + // Fixture label #3 'Label #3 - other user' is created_by_id: 2. + // Importing the same title for user 1 must create a new, user-owned label. + structure := []*models.ProjectWithTasksAndBuckets{ + { + Project: models.Project{Title: "Import project"}, + Tasks: []*models.TaskWithComments{ + { + Task: models.Task{ + Title: "Task", + Labels: []*models.Label{{Title: "Label #3 - other user"}}, + }, + }, + }, + }, + } + require.NoError(t, InsertFromStructure(structure, u)) + + db.AssertExists(t, "labels", map[string]interface{}{ + "title": "Label #3 - other user", + "created_by_id": u.ID, + }, false) + }) } From 2fca6a46e5bf4eb7c1d74676554d95bc1fb682b4 Mon Sep 17 00:00:00 2001 From: "Frederick [Bot]" Date: Tue, 19 May 2026 09:43:17 +0000 Subject: [PATCH 028/480] [skip ci] Updated swagger docs --- pkg/swagger/docs.go | 6 +++--- pkg/swagger/swagger.json | 6 +++--- pkg/swagger/swagger.yaml | 11 +++++++---- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/pkg/swagger/docs.go b/pkg/swagger/docs.go index a464c493a..170f81c20 100644 --- a/pkg/swagger/docs.go +++ b/pkg/swagger/docs.go @@ -4151,7 +4151,7 @@ const docTemplate = `{ "JWTKeyAuth": [] } ], - "description": "Returns a single task identified by its per-project index. Useful when resolving human-readable references like \"PROJ-42\" to a canonical task object. Note that task indexes are reassigned when a task is moved between projects, so long-lived references should use the returned task id instead.", + "description": "Returns a single task identified by its per-project index. Useful when resolving human-readable references like \"PROJ-42\" to a canonical task object. The ` + "`" + `project` + "`" + ` path parameter accepts either a numeric project id or the project's identifier (e.g. \"PROJ\"); values consisting solely of digits are always interpreted as ids. Note that task indexes are reassigned when a task is moved between projects, so long-lived references should use the returned task id instead.", "consumes": [ "application/json" ], @@ -4164,8 +4164,8 @@ const docTemplate = `{ "summary": "Get one task by its per-project index", "parameters": [ { - "type": "integer", - "description": "The project ID", + "type": "string", + "description": "The project id or the project's identifier", "name": "project", "in": "path", "required": true diff --git a/pkg/swagger/swagger.json b/pkg/swagger/swagger.json index 9d2735d0d..2f58533f9 100644 --- a/pkg/swagger/swagger.json +++ b/pkg/swagger/swagger.json @@ -4143,7 +4143,7 @@ "JWTKeyAuth": [] } ], - "description": "Returns a single task identified by its per-project index. Useful when resolving human-readable references like \"PROJ-42\" to a canonical task object. Note that task indexes are reassigned when a task is moved between projects, so long-lived references should use the returned task id instead.", + "description": "Returns a single task identified by its per-project index. Useful when resolving human-readable references like \"PROJ-42\" to a canonical task object. The `project` path parameter accepts either a numeric project id or the project's identifier (e.g. \"PROJ\"); values consisting solely of digits are always interpreted as ids. Note that task indexes are reassigned when a task is moved between projects, so long-lived references should use the returned task id instead.", "consumes": [ "application/json" ], @@ -4156,8 +4156,8 @@ "summary": "Get one task by its per-project index", "parameters": [ { - "type": "integer", - "description": "The project ID", + "type": "string", + "description": "The project id or the project's identifier", "name": "project", "in": "path", "required": true diff --git a/pkg/swagger/swagger.yaml b/pkg/swagger/swagger.yaml index c05086307..d51e61968 100644 --- a/pkg/swagger/swagger.yaml +++ b/pkg/swagger/swagger.yaml @@ -4435,14 +4435,17 @@ paths: - application/json description: Returns a single task identified by its per-project index. Useful when resolving human-readable references like "PROJ-42" to a canonical task - object. Note that task indexes are reassigned when a task is moved between - projects, so long-lived references should use the returned task id instead. + object. The `project` path parameter accepts either a numeric project id or + the project's identifier (e.g. "PROJ"); values consisting solely of digits + are always interpreted as ids. Note that task indexes are reassigned when + a task is moved between projects, so long-lived references should use the + returned task id instead. parameters: - - description: The project ID + - description: The project id or the project's identifier in: path name: project required: true - type: integer + type: string - description: The task's per-project index in: path name: index From 5fda2182c7f4f356102956a43c7c9eaf54decce5 Mon Sep 17 00:00:00 2001 From: kolaente Date: Tue, 19 May 2026 16:53:16 +0200 Subject: [PATCH 029/480] fix(deps): bump @babel/plugin-transform-modules-systemjs to 7.29.4 Resolves GHSA high-severity advisory where versions <= 7.29.3 can generate arbitrary code when compiling malicious input. --- frontend/pnpm-lock.yaml | 141 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 134 insertions(+), 7 deletions(-) diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 24bf97e1d..b42af0476 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -364,6 +364,10 @@ packages: resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.26.0': resolution: {integrity: sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA==} engines: {node: '>=6.9.0'} @@ -376,6 +380,10 @@ packages: resolution: {integrity: sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w==} engines: {node: '>=6.9.0'} + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@7.25.9': resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==} engines: {node: '>=6.9.0'} @@ -405,6 +413,10 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + '@babel/helper-member-expression-to-functions@7.25.9': resolution: {integrity: sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==} engines: {node: '>=6.9.0'} @@ -413,12 +425,22 @@ packages: resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-transforms@7.26.0': resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-optimise-call-expression@7.25.9': resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==} engines: {node: '>=6.9.0'} @@ -427,6 +449,10 @@ packages: resolution: {integrity: sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==} engines: {node: '>=6.9.0'} + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + '@babel/helper-remap-async-to-generator@7.25.9': resolution: {integrity: sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==} engines: {node: '>=6.9.0'} @@ -472,6 +498,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.3': + resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9': resolution: {integrity: sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==} engines: {node: '>=6.9.0'} @@ -699,8 +730,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-systemjs@7.25.9': - resolution: {integrity: sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==} + '@babel/plugin-transform-modules-systemjs@7.29.4': + resolution: {integrity: sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -880,14 +911,26 @@ packages: resolution: {integrity: sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==} engines: {node: '>=6.9.0'} + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + '@babel/traverse@7.25.9': resolution: {integrity: sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + '@babel/types@7.28.5': resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + '@bufbuild/protobuf@2.5.2': resolution: {integrity: sha512-foZ7qr0IsUBjzWIq+SuBLfdQCpJ1j8cTuNNT4owngTHoN5KsJb8L9t65fzz7SCeSWzescoOil/0ldqiL041ABg==} @@ -1976,6 +2019,9 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + '@jridgewell/gen-mapping@0.3.5': resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} @@ -2000,6 +2046,9 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@keyv/bigmap@1.3.0': resolution: {integrity: sha512-KT01GjzV6AQD5+IYrcpoYLkCu1Jod3nau1Z7EsEuViO3TZGRacSbO9MfHmbJ1WaOXFtWLxPVj169cn2WNKPkIg==} engines: {node: '>= 18'} @@ -4791,6 +4840,11 @@ packages: engines: {node: '>=6'} hasBin: true + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -7170,6 +7224,12 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/compat-data@7.26.0': {} '@babel/core@7.26.0': @@ -7200,6 +7260,14 @@ snapshots: '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.0.2 + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + '@babel/helper-annotate-as-pure@7.25.9': dependencies: '@babel/types': 7.28.5 @@ -7250,6 +7318,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-globals@7.28.0': {} + '@babel/helper-member-expression-to-functions@7.25.9': dependencies: '@babel/traverse': 7.25.9 @@ -7264,6 +7334,13 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 @@ -7273,12 +7350,23 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-transforms@7.28.6(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + '@babel/helper-optimise-call-expression@7.25.9': dependencies: '@babel/types': 7.28.5 '@babel/helper-plugin-utils@7.25.9': {} + '@babel/helper-plugin-utils@7.28.6': {} + '@babel/helper-remap-async-to-generator@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 @@ -7334,6 +7422,10 @@ snapshots: dependencies: '@babel/types': 7.28.5 + '@babel/parser@7.29.3': + dependencies: + '@babel/types': 7.29.0 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 @@ -7579,13 +7671,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-modules-systemjs@7.29.4(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.25.9 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -7792,7 +7884,7 @@ snapshots: '@babel/plugin-transform-member-expression-literals': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-modules-amd': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-modules-commonjs': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-modules-systemjs': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-modules-systemjs': 7.29.4(@babel/core@7.26.0) '@babel/plugin-transform-modules-umd': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-new-target': 7.25.9(@babel/core@7.26.0) @@ -7844,6 +7936,12 @@ snapshots: '@babel/parser': 7.28.5 '@babel/types': 7.28.5 + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + '@babel/traverse@7.25.9': dependencies: '@babel/code-frame': 7.26.2 @@ -7856,11 +7954,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + '@babel/types@7.28.5': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@bufbuild/protobuf@2.5.2': {} '@cacheable/memory@2.0.7': @@ -8783,6 +8898,11 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/gen-mapping@0.3.5': dependencies: '@jridgewell/set-array': 1.2.1 @@ -8810,6 +8930,11 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + '@keyv/bigmap@1.3.0(keyv@5.6.0)': dependencies: hashery: 1.4.0 @@ -11876,6 +12001,8 @@ snapshots: jsesc@3.0.2: {} + jsesc@3.1.0: {} + json-buffer@3.0.1: {} json-parse-even-better-errors@2.3.1: {} From 25e1c93a23edfb9adf06ead88d1ed433c10a53ae Mon Sep 17 00:00:00 2001 From: kolaente Date: Tue, 19 May 2026 16:54:27 +0200 Subject: [PATCH 030/480] fix(deps): bump fast-uri to 3.1.2 Resolves GHSA path traversal via percent-encoded dot segments and host confusion via percent-encoded authority delimiters (Dependabot alerts 227 and 228). fast-uri is a transitive dev-only dependency via stylelint -> table -> ajv. --- frontend/pnpm-lock.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index b42af0476..c27ad5c4a 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -4163,8 +4163,8 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-uri@3.0.1: - resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==} + fast-uri@3.1.2: + resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} fastest-levenshtein@1.0.16: resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} @@ -10241,7 +10241,7 @@ snapshots: ajv@8.18.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.0.1 + fast-uri: 3.1.2 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -11246,7 +11246,7 @@ snapshots: fast-levenshtein@2.0.6: {} - fast-uri@3.0.1: {} + fast-uri@3.1.2: {} fastest-levenshtein@1.0.16: {} From a5dc85b5d3f737429b00f16268babe4d680f5c1f Mon Sep 17 00:00:00 2001 From: kolaente Date: Tue, 19 May 2026 16:56:07 +0200 Subject: [PATCH 031/480] fix(deps): bump ip-address to 10.2.0 Adds a pnpm override to pull ip-address >=10.1.1, resolving the XSS vulnerability in Address6 HTML-emitting methods (GHSA, dev-only transitive dependency via puppeteer/socks). --- frontend/package.json | 3 ++- frontend/pnpm-lock.yaml | 22 +++++----------------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 5bc5aaf52..2152ca1f0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -172,7 +172,8 @@ "rollup": "$rollup", "basic-ftp": ">=5.2.2", "serialize-javascript": "^7.0.5", - "flatted": "^3.4.1" + "flatted": "^3.4.1", + "ip-address": ">=10.1.1" } } } diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index c27ad5c4a..e1620bfda 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -10,6 +10,7 @@ overrides: basic-ftp: '>=5.2.2' serialize-javascript: ^7.0.5 flatted: ^3.4.1 + ip-address: '>=10.1.1' importers: @@ -4566,8 +4567,8 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} - ip-address@9.0.5: - resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + ip-address@10.2.0: + resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} engines: {node: '>= 12'} is-array-buffer@3.0.5: @@ -4823,9 +4824,6 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true - jsbn@1.1.0: - resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} - jsdom@27.4.0: resolution: {integrity: sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -6207,9 +6205,6 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - sprintf-js@1.1.3: - resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} - stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -11733,10 +11728,7 @@ snapshots: hasown: 2.0.3 side-channel: 1.1.0 - ip-address@9.0.5: - dependencies: - jsbn: 1.1.0 - sprintf-js: 1.1.3 + ip-address@10.2.0: {} is-array-buffer@3.0.5: dependencies: @@ -11969,8 +11961,6 @@ snapshots: dependencies: argparse: 2.0.1 - jsbn@1.1.0: {} - jsdom@27.4.0: dependencies: '@acemir/cssom': 0.9.30 @@ -13429,7 +13419,7 @@ snapshots: socks@2.8.4: dependencies: - ip-address: 9.0.5 + ip-address: 10.2.0 smart-buffer: 4.2.0 sortablejs@1.14.0: {} @@ -13471,8 +13461,6 @@ snapshots: sprintf-js@1.0.3: {} - sprintf-js@1.1.3: {} - stackback@0.0.2: {} statuses@1.5.0: {} From 1fd1427feda2b4ee83337a7d912016216fc8097f Mon Sep 17 00:00:00 2001 From: kolaente Date: Tue, 19 May 2026 16:58:25 +0200 Subject: [PATCH 032/480] fix(deps): bump postcss to >=8.5.10 to fix XSS via unescaped Adds a pnpm override to force postcss to a patched version (>=8.5.10), removing the vulnerable postcss@7.0.39 pulled in transitively by postcss-easing-gradients. Resolves GHSA / Dependabot alert #197. --- frontend/package.json | 3 +- frontend/pnpm-lock.yaml | 181 +++++++++++++++++++--------------------- 2 files changed, 86 insertions(+), 98 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 2152ca1f0..4b82c47c9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -173,7 +173,8 @@ "basic-ftp": ">=5.2.2", "serialize-javascript": "^7.0.5", "flatted": "^3.4.1", - "ip-address": ">=10.1.1" + "ip-address": ">=10.1.1", + "postcss": ">=8.5.10" } } } diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index e1620bfda..04021da40 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -11,6 +11,7 @@ overrides: serialize-javascript: ^7.0.5 flatted: ^3.4.1 ip-address: '>=10.1.1' + postcss: '>=8.5.10' importers: @@ -261,7 +262,7 @@ importers: specifier: 12.0.1 version: 12.0.1 postcss: - specifier: 8.5.14 + specifier: '>=8.5.10' version: 8.5.14 postcss-easing-gradients: specifier: 3.0.1 @@ -1051,265 +1052,265 @@ packages: resolution: {integrity: sha512-i2lNJ6b4GdMoybHlpUM07TIk8KQRXTTe7Qf8LfctQhjDRTIgaodWTQqzWU4fpWO/nxBWNkSloDM22Lw/30NBcg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-cascade-layers@6.0.0': resolution: {integrity: sha512-WhsECqmrEZQGqaPlBA7JkmF/CJ2/+wetL4fkL9sOPccKd32PQ1qToFM6gqSI5rkpmYqubvbxjEJhyMTHYK0vZQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-color-function-display-p3-linear@2.0.4': resolution: {integrity: sha512-xrGqSFj9pu6XbJYD4NNCxYK9WFbf0KMfXFaisnJezkIRDZCwefUB2azkU4Zr0dFmLtIb9LlshrSZ0be1/QVthQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-color-function@5.0.4': resolution: {integrity: sha512-PhUu86ppxKcNHHqrJ43ZL1mYa2uHKGRoY0KPbZA9k8iOaanL3I+1zYqbgVumxj1UgNTDw5BE3BUQ1Dono6bD6g==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-color-mix-function@4.0.4': resolution: {integrity: sha512-zYS78MHBuih9f9qtPFcSvVXMKg9q/lNPeFJUjyw7+/W1VHRjubvs5MlzuC363UUeahAhrOvYdo2ZZhmlxZbj6w==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-color-mix-variadic-function-arguments@2.0.4': resolution: {integrity: sha512-qlrABMEFPUqbCxX0aOsHcxQZo/8XgMqnEtqqtVUbdizcuTUtJyLdHike7hkoemwDspMSEotdIfRlUY4jhZaD+A==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-container-rule-prelude-list@1.0.1': resolution: {integrity: sha512-c5qlevVGKHU+zDbVoUGSZl1Mw7Vl1gVRKv6cdIYnaoyM+9Ou23Ian0H5Gr2ZF+lsDWovPK03hOSAbkw6HS8aTg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-content-alt-text@3.0.1': resolution: {integrity: sha512-mK5lCgzgV/ZC+LgnFy4rAQVMcXR6HsnX3D1+4Q5gshSQsst5TtcvHbxTdzKy1XTv09sNZHJX8CO4CEQF9zA4ug==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-contrast-color-function@3.0.4': resolution: {integrity: sha512-EiTZzUICztGqEuYg8AVCUWH9vH2jDzO6RryxMja+PWluZHP6n3/iG6i1leTt5LiDQjDUQlCRbQtMNj7V7S+b4Q==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-exponential-functions@3.0.3': resolution: {integrity: sha512-mB/NoeHLBHh0LZiVSrFdRDA/NxSfmg4tSN9117IJH9bdC2BzSTVgc82h3Gu/sdBXay6kDH2sA7fbkTigMiEi2A==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-font-format-keywords@5.0.0': resolution: {integrity: sha512-M1EjCe/J3u8fFhOZgRci74cQhJ7R0UFBX6T+WqoEvjrr8hVfMiV+HTYrzxLY5OW8YllvXYr5Q5t5OvJbsUSeDg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-font-width-property@1.0.0': resolution: {integrity: sha512-AvmySApdijbjYQuXXh95tb7iVnqZBbJrv3oajO927ksE/mDmJBiszm+psW8orL2lRGR8j6ZU5Uv9/ou2Z5KRKA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-gamut-mapping@3.0.4': resolution: {integrity: sha512-2dWGsxtxypKU9Ra862F2335W8xegRwl9ohQ6hk808PiQlEahSaFtt5fqsGmKDaSiaFUx+2X8GZxVo970Ajr2vQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-gradients-interpolation-method@6.0.4': resolution: {integrity: sha512-sC/7dqVTtQTniLjPp/NagzeUn4sGinnMTicNBLDzirKq/GNXuJaApBOnvBmgNXjV6XPizfMhNRYCk5stn3q2nQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-hwb-function@5.0.4': resolution: {integrity: sha512-cl0KPaaeYyAXNHO3pqK8adbpbAGmIU1cT1thyaEkmP8yvbJvmyztkpdGADGqziUUoh4dZQ0IhHxOxnKQ296T+A==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-ic-unit@5.0.1': resolution: {integrity: sha512-jmsVLXPdMBTlaJAhiEijhIR3qL0j75MrlRfhJEs91DF1Wlt2kpJTDsbpXQpYFzn1nPFHZC/WEf+Mw0I/HXkHzQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-image-function@1.0.0': resolution: {integrity: sha512-iuQztV6Cfeuc7NczazfickrzEhALOpxUS0yWgGkmRY1zZ0CKjBBFc/7WWSN9qupfpNAzHY7cPNcJCqUhtr+YMw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-initial@3.0.0': resolution: {integrity: sha512-UVUrFmrTQyLomVepnjWlbBg7GoscLmXLwYFyjbcEnmpeGW7wde6lNpx5eM3eVwZI2M+7hCE3ykYnAsEPLcLa+Q==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-is-pseudo-class@6.0.0': resolution: {integrity: sha512-1Hdy/ykg9RDo8vU8RiM2o+RaXO39WpFPaIkHxlAEJFofle/lc33tdQMKhBk3jR/Fe+uZNLOs3HlowFafyFptVw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-light-dark-function@3.0.1': resolution: {integrity: sha512-tD2MMJmZ6XXCHgDythLHcXQDNi5z7KEEWPe7JeB3vPcw+YMuMabpW5ugRqndhIrui+vduhc0Md7f7yGPCmOErg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-logical-float-and-clear@4.0.0': resolution: {integrity: sha512-NGzdIRVj/VxOa/TjVdkHeyiJoDihONV0+uB0csUdgWbFFr8xndtfqK8iIGP9IKJzco+w0hvBF2SSk2sDSTAnOQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-logical-overflow@3.0.0': resolution: {integrity: sha512-5cRg93QXVskM0MNepHpPcL0WLSf5Hncky0DrFDQY/4ozbH5lH7SX5ejayVpNTGSX7IpOvu7ykQDLOdMMGYzwpA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-logical-overscroll-behavior@3.0.0': resolution: {integrity: sha512-82Jnl/5Wi5jb19nQE1XlBHrZcNL3PzOgcj268cDkfwf+xi10HBqufGo1Unwf5n8bbbEFhEKgyQW+vFsc9iY1jw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-logical-resize@4.0.0': resolution: {integrity: sha512-L0T3q0gei/tGetCGZU0c7VN77VTivRpz1YZRNxjXYmW+85PKeI6U9YnSvDqLU2vBT2uN4kLEzfgZ0ThIZpN18A==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-logical-viewport-units@4.0.0': resolution: {integrity: sha512-TA3AqVN/1IH3dKRC2UUWvprvwyOs2IeD7FDZk5Hz20w4q33yIuSg0i0gjyTUkcn90g8A4n7QpyZ2AgBrnYPnnA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-media-minmax@3.0.3': resolution: {integrity: sha512-ch1tNS+1QayiHTGsyc53zv3AzrSd0zigjbkfLxoeuzzJyn32+P3V7em3u5vLVnqLMzBbEZK//GI13EVTIPRdDA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-media-queries-aspect-ratio-number-values@4.0.0': resolution: {integrity: sha512-FDdC3lbrj8Vr0SkGIcSLTcRB7ApG6nlJFxOxkEF2C5hIZC1jtgjISFSGn/WjFdVkn8Dqe+Vx9QXI3axS2w1XHw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-mixins@1.0.0': resolution: {integrity: sha512-rz6qjT2w9L3k65jGc2dX+3oGiSrYQ70EZPDrINSmSVoVys7lLBFH0tvEa8DW2sr9cbRVD/W+1sy8+7bfu0JUfg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-nested-calc@5.0.0': resolution: {integrity: sha512-aPSw8P60e/i9BEfugauhikBqgjiwXcw3I9o4vXs+hktl4NSTgZRI0QHimxk9mst8N01A2TKDBxOln3mssRxiHQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-normalize-display-values@5.0.1': resolution: {integrity: sha512-FcbEmoxDEGYvm2W3rQzVzcuo66+dDJjzzVDs+QwRmZLHYofGmMGwIKPqzF86/YW+euMDa7sh1xjWDvz/fzByZQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-oklab-function@5.0.4': resolution: {integrity: sha512-vIgrKe5ffW99it5SUIXOBczGLSiTaHBhU6afVr9KPwoZ4uq9H0E3Ehvi+xsUjmvnAyMTxOUSszNo04kEhbvYjQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-position-area-property@2.0.0': resolution: {integrity: sha512-TeEfzsJGB23Syv7yCm8AHCD2XTFujdjr9YYu9ebH64vnfCEvY4BG319jXAYSlNlf3Yc9PNJ6WnkDkUF5XVgSKQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-progressive-custom-properties@5.1.0': resolution: {integrity: sha512-lt/4yHy2GdKcGVpK4OGhBdSIq+z2PXynSusSRggn/T4y7uFurYAhdHqo/aYM+xI37vNb8rJlEKchqKKvVCXROQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-property-rule-prelude-list@2.0.0': resolution: {integrity: sha512-qcMAkc9AhpzHgmQCD8hoJgGYifcOAxd1exXjjxilMM6euwRE619xDa4UsKBCv/v4g+sS63sd6c29LPM8s2ylSQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-random-function@3.0.3': resolution: {integrity: sha512-0EScyKxscGonwpi30Hj9DEAr0X8D2eDhOqqayQXE91gIqGli9UT+deLYqoogZLOy5GT+ncqltMqztc/q+0UkhA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-relative-color-syntax@4.0.4': resolution: {integrity: sha512-reFFKD9eS602We8621e5cAroKD7hH4104duLNBBhzwawGN7dhbnL1+c/DRHqwyq6eGK35HaKMMiifEZhAztlOA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-scope-pseudo-class@5.0.0': resolution: {integrity: sha512-kBrBFJcAji3MSHS4qQIihPvJfJC5xCabXLbejqDMiQi+86HD4eMBiTayAo46Urg7tlEmZZQFymFiJt+GH6nvXw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-sign-functions@2.0.3': resolution: {integrity: sha512-2BCPwlpeQweTC/8S8oQFYhYD5kxYkiroLf3AUJV2kVoKkSZ+4WM4rSwySXlKrqXL8HfCryAwVrJg7B0jr/RnOw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-stepped-value-functions@5.0.3': resolution: {integrity: sha512-nXMFQBz5Pi2LLG02iqm2k+scrqwtqJT9ta/gN8S79oBZ23M0E7O3wDJ20//3z5Q6HU5e+K0n+SmmxN6iWtbm6w==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-syntax-descriptor-syntax-production@2.0.0': resolution: {integrity: sha512-elYcbdiBXAkPqvojB9kIBRuHY6htUhjSITtFQ+XiXnt6SvZCbNGxQmaaw6uZ7SPHu/+i/XVjzIt09/1k3SIerQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-system-ui-font-family@2.0.0': resolution: {integrity: sha512-FyGZCgchFImFyiHS2x3rD5trAqatf/x23veBLTIgbaqyFfna6RNBD+Qf8HRSjt6HGMXOLhAjxJ3OoZg0bbn7Qw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-text-decoration-shorthand@5.0.3': resolution: {integrity: sha512-62fjggvIM1YYfDJPcErMUDkEZB6CByG8neTJqexnZe1hRBgCjD4dnXDLoCSSurjs1LzjBq6irFDpDaOvDZfrlw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-trigonometric-functions@5.0.3': resolution: {integrity: sha512-p9LTvLj+DFpl5RHbG/X9QGwg7BoMOBsRBZqsUAKKVvCw7MRCsk1P1llTUR/MW5nyZ4IsjFGDtDwTTj1reJjxvg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/postcss-unset-value@5.0.0': resolution: {integrity: sha512-EoO54sS2KCIfesvHyFYAW99RtzwHdgaJzhl7cqKZSaMYKZv3fXSOehDjAQx8WZBKn1JrMd7xJJI1T1BxPF7/jA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@csstools/selector-resolve-nested@4.0.0': resolution: {integrity: sha512-9vAPxmp+Dx3wQBIUwc1v7Mdisw1kbbaGqXUM8QLTgWg7SoPGYtXBsMXvsFs/0Bn5yoFhcktzxNZGNaUt0VjgjA==} @@ -1327,7 +1328,7 @@ packages: resolution: {integrity: sha512-etDqA/4jYvOGBM6yfKCOsEXfH96BKztZdgGmGqKi2xHnDe0ILIBraRspwgYatJH9JsCZ5HCGoCst8w18EKOAdg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} @@ -3365,7 +3366,7 @@ packages: engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: - postcss: ^8.1.0 + postcss: '>=8.5.10' available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} @@ -3689,7 +3690,7 @@ packages: resolution: {integrity: sha512-C5B2e5hCM4llrQkUms+KnWEMVW8K1n2XvX9G7ppfMZJQ7KAS/4rNnkP1Cs+HhWriOz1mWWTMFD4j1J7s31Dgug==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' css-functions-list@3.3.3: resolution: {integrity: sha512-8HFEBPKhOpJPEPu70wJJetjKta86Gw9+CCyCnB3sui2qQfOvRyqBy4IKLKKAwdMpWb2lHXWk9Wb4Z6AmaUT1Pg==} @@ -3699,13 +3700,13 @@ packages: resolution: {integrity: sha512-Uz/bsHRbOeir/5Oeuz85tq/yLJLxX+3dpoRdjNTshs6jjqwUg8XaEZGDd0ci3fw7l53Srw0EkJ8mYan0eW5uGQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' css-prefers-color-scheme@11.0.0: resolution: {integrity: sha512-fv0mgtwUhh2m9iio3Kxc2CkrogjIaRdMFaaqyzSFdii17JF4cfPyMNX72B15ZW2Nrr/NZUpxI4dec1VMHYJvdw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' css-property-sort-order-smacss@2.2.0: resolution: {integrity: sha512-nXutswsivIEBOrPo/OZw2KQjFPLvtg68aovJf6Kqrm3L6FmTvvFPaeDrk83hh0+pRJGuP3PeKJwMS0E6DFipdQ==} @@ -5395,9 +5396,6 @@ packages: perfect-debounce@2.0.0: resolution: {integrity: sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==} - picocolors@0.2.1: - resolution: {integrity: sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -5436,61 +5434,61 @@ packages: resolution: {integrity: sha512-fovIPEV35c2JzVXdmP+sp2xirbBMt54J+upU8u6TSj410kUU5+axgEzvBBSAX8KCybze8CFCelzFAw/FfWg2TA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-clamp@4.1.0: resolution: {integrity: sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==} engines: {node: '>=7.6.0'} peerDependencies: - postcss: ^8.4.6 + postcss: '>=8.5.10' postcss-color-functional-notation@8.0.4: resolution: {integrity: sha512-Zn3yPgBFakVXthmA2n1NUMY7gdhuFUB/DrUJ0Eug/d0rl9wahMQZykp4NVTJLGzQrDUwZ2rzjiTeW5udxFNG8A==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-color-hex-alpha@11.0.0: resolution: {integrity: sha512-NCGa6vjIyrjosz9GqRxVKbONBklz5TeipYqTJp3IqbnBWlBq5e5EMtG6MaX4vqk9LzocPfMQkuRK9tfk+OQuKg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-color-rebeccapurple@11.0.0: resolution: {integrity: sha512-g9561mx7cbdqx7XeO/L+lJzVlzu7bICyXr72efBVKZGxIhvBBJf9fGXn3Cb6U4Bwh3LbzQO2e9NWBLVYdX5Eag==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-custom-media@12.0.1: resolution: {integrity: sha512-66syE14+VeqkUf0rRX0bvbTCbNRJF132jD+ceo8th1dap2YJEAqpdh5uG98CE3IbgHT7m9XM0GIlOazNWqQdeA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-custom-properties@15.0.1: resolution: {integrity: sha512-cuyq8sd8dLY0GLbelz1KB8IMIoDECo6RVXMeHeXY2Uw3Q05k/d1GVITdaKLsheqrHbnxlwxzSRZQQ5u+rNtbMg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-custom-selectors@9.0.1: resolution: {integrity: sha512-2XBELy4DmdVKimChfaZ2id9u9CSGYQhiJ53SvlfBvMTzLMW2VxuMb9rHsMSQw9kRq/zSbhT5x13EaK8JSmK8KQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-dir-pseudo-class@10.0.0: resolution: {integrity: sha512-DmtIzULpyC8XaH4b5AaUgt4Jic4QmrECqidNCdR7u7naQFdnxX80YI06u238a+ZVRXwURDxVzy0s/UQnWmpVeg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-double-position-gradients@7.0.1: resolution: {integrity: sha512-M69I4EolEGwiYa0KmxKWg4zZp2DxhlNM0Bz12OvHCj930GXDVCvFhdWNGsRscz6BIijN6tFryzSFsy8kMLyD5Q==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-easing-gradients@3.0.1: resolution: {integrity: sha512-UrOKb4cenjGmMmrheETw7Cjnn/IKn3xgTvHs92b0sSwMhKgeZKxJpduGRjYZ8wgpu3zOzzgQpRwOLhhtMofayA==} @@ -5500,24 +5498,24 @@ packages: resolution: {integrity: sha512-VG1a9kBKizUBWS66t5xyB4uLONBnvZLCmZXxT40FALu8EF0QgVZBYy5ApC0KhmpHsv+pvHMJHB3agKHwmocWjw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-focus-within@10.0.0: resolution: {integrity: sha512-dvql0fzUTG+gcJYp+KTbag5vAjuo94LDYZHkqDV1rnf5gPGer1v/SrmIZBdvKU8moep3HbcbujqGjzSb3DL53Q==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-font-variant@5.0.0: resolution: {integrity: sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==} peerDependencies: - postcss: ^8.1.0 + postcss: '>=8.5.10' postcss-gap-properties@7.0.0: resolution: {integrity: sha512-PSDF2QoZMRUbsINvXObQgxx4HExRP85QTT8qS/YN9fBsCPWCqUuwqAD6E6PNp0BqL/jU1eyWUBORaOK/J/9LDA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-html@1.8.1: resolution: {integrity: sha512-OLF6P7qctfAWayOhLpcVnTGqVeJzu2W3WpIYelfz2+JV5oGxfkcEvweN9U4XpeqE0P98dcD9ssusGwlF0TK0uQ==} @@ -5527,19 +5525,19 @@ packages: resolution: {integrity: sha512-rEGNkOkNusf4+IuMmfEoIdLuVmvbExGbmG+MIsyV6jR5UaWSoyPcAYHV/PxzVDCmudyF+2Nh/o6Ub2saqUdnuA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-lab-function@8.0.4: resolution: {integrity: sha512-dqcJSzVasdELD9xqJ1wfP95uzP57J6zFd80c7S3AWK127H9zwqR9Kbk5ZgyIfN2DiMStI7Vq8E7ablXNeTvpew==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-logical@9.0.0: resolution: {integrity: sha512-A4LNd9dk3q/juEUA9Gd8ALhBO3TeOeYurnyHLlf2aAToD94VHR8c5Uv7KNmf8YVRhTxvWsyug4c5fKtARzyIRQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-media-query-parser@0.2.3: resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==} @@ -5548,47 +5546,47 @@ packages: resolution: {integrity: sha512-YGFOfVrjxYfeGTS5XctP1WCI5hu8Lr9SmntjfRC+iX5hCihEO+QZl9Ra+pkjqkgoVdDKvb2JccpElcowhZtzpw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-opacity-percentage@3.0.0: resolution: {integrity: sha512-K6HGVzyxUxd/VgZdX04DCtdwWJ4NGLG212US4/LA1TLAbHgmAsTWVR86o+gGIbFtnTkfOpb9sCRBx8K7HO66qQ==} engines: {node: '>=18'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-overflow-shorthand@7.0.0: resolution: {integrity: sha512-9SLpjoUdGRoRrzoOdX66HbUs0+uDwfIAiXsRa7piKGOqPd6F4ZlON9oaDSP5r1Qpgmzw5L9Ht0undIK6igJPMA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-page-break@3.0.4: resolution: {integrity: sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==} peerDependencies: - postcss: ^8 + postcss: '>=8.5.10' postcss-place@11.0.0: resolution: {integrity: sha512-fAifpyjQ+fuDRp2nmF95WbotqbpjdazebedahXdfBxy5sHembOLpBQ1cHveZD9ZmjK26tYM8tikeNaUlp/KfHA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-preset-env@11.3.0: resolution: {integrity: sha512-PpijTuY+NT35vvk7us0pw9lJVrsZZWukjONZsza2Kq1Gag8nrUXRkgdKdxyyhZPJ6R43L3/nLpspUK99TmU9xg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-pseudo-class-any-link@11.0.0: resolution: {integrity: sha512-DNFZ4GMa3C3pU5dM+UCTG1CEeLtS1ZqV5DKSqCTJQMn1G5jnd/30fS8+A7H4o5bSD3MOcnx+VgI+xPE9Z5Wvig==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-replace-overflow-wrap@4.0.0: resolution: {integrity: sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==} peerDependencies: - postcss: ^8.0.3 + postcss: '>=8.5.10' postcss-resolve-nested-selector@0.1.6: resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==} @@ -5597,25 +5595,25 @@ packages: resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} engines: {node: '>=12.0'} peerDependencies: - postcss: ^8.3.3 + postcss: '>=8.5.10' postcss-safe-parser@7.0.1: resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} engines: {node: '>=18.0'} peerDependencies: - postcss: ^8.4.31 + postcss: '>=8.5.10' postcss-scss@4.0.9: resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} engines: {node: '>=12.0'} peerDependencies: - postcss: ^8.4.29 + postcss: '>=8.5.10' postcss-selector-not@9.0.0: resolution: {integrity: sha512-xhAtTdHnVU2M/CrpYOPyRUvg3njhVlKmn2GNYXDaRJV9Ygx4d5OkSkc7NINzjUqnbDFtaKXlISOBeyMXU/zyFQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: ^8.4 + postcss: '>=8.5.10' postcss-selector-parser@7.1.1: resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} @@ -5624,7 +5622,7 @@ packages: postcss-sorting@8.0.2: resolution: {integrity: sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q==} peerDependencies: - postcss: ^8.4.20 + postcss: '>=8.5.10' postcss-value-parser@3.3.1: resolution: {integrity: sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==} @@ -5632,10 +5630,6 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@7.0.39: - resolution: {integrity: sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==} - engines: {node: '>=6.0.0'} - postcss@8.5.14: resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} engines: {node: ^10 || ^12 || >=14} @@ -6318,7 +6312,7 @@ packages: resolution: {integrity: sha512-VkVD9r7jfUT/dq3mA3/I1WXXk2U71rO5wvU2yIil9PW5o1g3UM7Xc82vHmuVJHV7Y8ok5K137fmW5u3HbhtTOA==} engines: {node: '>=20'} peerDependencies: - postcss: ^8.3.3 + postcss: '>=8.5.10' stylelint: ^17.0.0 peerDependenciesMeta: postcss: @@ -6341,7 +6335,7 @@ packages: resolution: {integrity: sha512-uLJS6xgOCBw5EMsDW7Ukji8l28qRoMnkRch15s0qwZpskXvWt9oPzMmcYM307m9GN4MxuWLsQh4I6hU9yI53cQ==} engines: {node: '>=20'} peerDependencies: - postcss: ^8.3.3 + postcss: '>=8.5.10' stylelint: ^17.0.0 peerDependenciesMeta: postcss: @@ -12506,8 +12500,6 @@ snapshots: perfect-debounce@2.0.0: {} - picocolors@0.2.1: {} - picocolors@1.1.1: {} picomatch@2.3.2: {} @@ -12603,7 +12595,7 @@ snapshots: dependencies: chroma-js: 1.4.1 easing-coordinates: 2.0.2 - postcss: 7.0.39 + postcss: 8.5.14 postcss-value-parser: 3.3.1 postcss-focus-visible@11.0.0(postcss@8.5.14): @@ -12798,11 +12790,6 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss@7.0.39: - dependencies: - picocolors: 0.2.1 - source-map: 0.6.1 - postcss@8.5.14: dependencies: nanoid: 3.3.11 From 553613163fbfdb5972a1b48301b7b01d35f37ae7 Mon Sep 17 00:00:00 2001 From: kolaente Date: Tue, 19 May 2026 17:12:18 +0200 Subject: [PATCH 033/480] fix(deps): bump @xmldom/xmldom to 0.8.13 --- desktop/pnpm-lock.yaml | 56 ++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/desktop/pnpm-lock.yaml b/desktop/pnpm-lock.yaml index aae2ca03a..cd660881c 100644 --- a/desktop/pnpm-lock.yaml +++ b/desktop/pnpm-lock.yaml @@ -168,10 +168,13 @@ packages: '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - '@xmldom/xmldom@0.8.12': - resolution: {integrity: sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==} + '@xmldom/xmldom@0.8.13': + resolution: {integrity: sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==} engines: {node: '>=10.0.0'} - deprecated: this version has critical issues, please update to the latest version + + '@xmldom/xmldom@0.9.10': + resolution: {integrity: sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==} + engines: {node: '>=14.6'} abbrev@3.0.1: resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} @@ -298,6 +301,10 @@ packages: resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} engines: {node: 18 || 20 || >=22} + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} + buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} @@ -1170,6 +1177,10 @@ packages: resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==} engines: {node: '>=10.4.0'} + plist@3.1.1: + resolution: {integrity: sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==} + engines: {node: '>=10.4.0'} + proc-log@5.0.0: resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} engines: {node: ^18.17.0 || >=20.5.0} @@ -1302,6 +1313,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.0: + resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} + engines: {node: '>=10'} + hasBin: true + send@1.2.0: resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} engines: {node: '>= 18'} @@ -1428,8 +1444,8 @@ packages: resolution: {integrity: sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ==} engines: {node: '>=18'} - tar@7.5.13: - resolution: {integrity: sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==} + tar@7.5.15: + resolution: {integrity: sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==} engines: {node: '>=18'} temp-file@3.4.0: @@ -1642,7 +1658,7 @@ snapshots: fs-extra: 10.1.0 isbinaryfile: 4.0.10 minimist: 1.2.8 - plist: 3.1.0 + plist: 3.1.1 transitivePeerDependencies: - supports-color @@ -1683,7 +1699,7 @@ snapshots: dir-compare: 3.3.0 fs-extra: 9.1.0 minimatch: 10.2.5 - plist: 3.1.0 + plist: 3.1.1 transitivePeerDependencies: - supports-color @@ -1805,7 +1821,9 @@ snapshots: '@types/node': 24.10.9 optional: true - '@xmldom/xmldom@0.8.12': {} + '@xmldom/xmldom@0.8.13': {} + + '@xmldom/xmldom@0.9.10': {} abbrev@3.0.1: {} @@ -1875,8 +1893,8 @@ snapshots: minimatch: 10.2.5 read-config-file: 6.3.2 sanitize-filename: 1.6.4 - semver: 7.7.4 - tar: 7.5.13 + semver: 7.8.0 + tar: 7.5.15 temp-file: 3.4.0 transitivePeerDependencies: - supports-color @@ -2013,6 +2031,10 @@ snapshots: dependencies: balanced-match: 4.0.4 + brace-expansion@5.0.6: + dependencies: + balanced-match: 4.0.4 + buffer-crc32@0.2.13: {} buffer-equal@1.0.1: {} @@ -2875,7 +2897,7 @@ snapshots: minimatch@10.2.5: dependencies: - brace-expansion: 5.0.5 + brace-expansion: 5.0.6 minimist@1.2.8: {} @@ -3015,7 +3037,13 @@ snapshots: plist@3.1.0: dependencies: - '@xmldom/xmldom': 0.8.12 + '@xmldom/xmldom': 0.8.13 + base64-js: 1.5.1 + xmlbuilder: 15.1.1 + + plist@3.1.1: + dependencies: + '@xmldom/xmldom': 0.9.10 base64-js: 1.5.1 xmlbuilder: 15.1.1 @@ -3164,6 +3192,8 @@ snapshots: semver@7.7.4: {} + semver@7.8.0: {} + send@1.2.0: dependencies: debug: 4.4.3 @@ -3332,7 +3362,7 @@ snapshots: minizlib: 3.1.0 yallist: 5.0.0 - tar@7.5.13: + tar@7.5.15: dependencies: '@isaacs/fs-minipass': 4.0.1 chownr: 3.0.0 From 3d6e5b5f6ba11d89bc3e822530e87edae7209d62 Mon Sep 17 00:00:00 2001 From: "Frederick [Bot]" Date: Wed, 20 May 2026 02:15:16 +0000 Subject: [PATCH 034/480] chore(i18n): update translations via Crowdin --- frontend/src/i18n/lang/el-GR.json | 517 +++++++++++++++++++++++++++++- 1 file changed, 513 insertions(+), 4 deletions(-) diff --git a/frontend/src/i18n/lang/el-GR.json b/frontend/src/i18n/lang/el-GR.json index 5083fb8d1..bb1b9b0f8 100644 --- a/frontend/src/i18n/lang/el-GR.json +++ b/frontend/src/i18n/lang/el-GR.json @@ -1,19 +1,189 @@ { + "404": { + "title": "Δε βρέθηκε", + "text": "Η σελίδα που ζητήσατε δεν υπάρχει." + }, "home": { "welcomeNight": "Καληνύχτα {username}!", "welcomeNightOwl": "Γεια σου νυχτερινή κουκουβάγια {username}", "welcomeNightBurning": "Κάνουμε υπερωρίες {username};", - "welcomeDayKeepGoing": "Συνέχισε, {username}" + "welcomeNightQuiet": "Ώρες ησυχίας, {username}", + "welcomeNightLate": "Είναι αργά, {username}", + "welcomeNightMoonlit": "Παράλληλη απασχόληση, {username};", + "welcomeMorning": "Καλημέρα {username}!", + "welcomeMorningHey": "Ε {username}, είσαι έτοιμος να ξεκινήσουμε;", + "welcomeMorningFresh": "Νέα αρχή, {username}", + "welcomeMorningCoffee": "Καφέ και εργασίες, {username};", + "welcomeMorningRise": "Ανεβάζουμε τα μανίκια, {username}", + "welcomeMorningBack": "Καλώς ήρθες και πάλι, {username}", + "welcomeMondayFresh": "Φρέσκια εβδομάδα, {username}", + "welcomeTuesday": "Χαρούμενη Τρίτη, {username}", + "welcomeWednesdayMid": "Ήδη είμαστε μεσοβδόμαδα, {username}", + "welcomeThursday": "Σχεδόν εκεί, {username}", + "welcomeFridayPush": "Γερά την Παρασκευή, {username};", + "welcomeSaturday": "Κατάσταση Σαββατοκύριακου, {username}", + "welcomeSundaySession": "Κυριακάτικη δουλίτσα, {username};", + "welcomeDay": "Γεια σου {username}!", + "welcomeDayBack": "Πίσω και πάλι, {username}", + "welcomeDayFocus": "Ας συγκεντρωθούμε, {username}", + "welcomeDayKeepGoing": "Συνέχισε, {username}", + "welcomeDayWhatsNext": "Τι έχει μετά, {username};", + "welcomeDayGood": "Καλό απόγευμα, {username}", + "welcomeEvening": "Καλησπέρα {username}!", + "welcomeEveningWind": "Χαλαρώνεις, {username};", + "welcomeEveningReturns": "Ο/Η {username} επιστρέφει", + "welcomeEveningWrap": "Ήρθε η ώρα να κλείσουμε, {username};", + "welcomeEveningOneMore": "Κάτι ακόμη, {username};", + "welcomeEveningStill": "Ακόμη σε αυτό, {username};", + "lastViewed": "Προβλήθηκε τελευταία", + "addToHomeScreen": "Προσθέστε την εφαρμογή στην αρχική οθόνη σας για ταχύτερη πρόσβαση και βελτιωμένη εμπειρία.", + "goToOverview": "Μετάβαση στην επισκόπηση", + "project": { + "importText": "Εισαγάγετε τα έργα και τις εργασίες σας από άλλες υπηρεσίες στο Vikunja:", + "import": "Εισαγωγή των δεδομένων σας στο Vikunja" + } + }, + "demo": { + "title": "Αυτή η εγκατάσταση βρίσκεται σε λειτουργία επίδειξης. Μην τη χρησιμοποιείτε με πραγματικά δεδομένα!", + "everythingWillBeDeleted": "Όλα θα διαγράφονται σε τακτά χρονικά διαστήματα!", + "accountWillBeDeleted": "Ο λογαριασμός σας θα διαγραφεί, συμπεριλαμβανομένων όλων των έργων, των εργασιών και των συνημμένων που ενδέχεται να είχατε δημιουργήσει." + }, + "ready": { + "loading": "Το Vikunja φορτώνει…", + "errorOccured": "Παρουσιάστηκε σφάλμα:", + "checkApiUrl": "Παρακαλώ ελέγξτε αν η διεύθυνση URL του API είναι σωστή.", + "noApiUrlConfigured": "Δεν έχει ρυθμιστεί διεύθυνση url για το API. Παρακαλώ ορίστε μία παρακάτω:" + }, + "offline": { + "title": "Είστε εκτός σύνδεσης.", + "text": "Ελέγξτε τη σύνδεση δικτύου σας και προσπαθήστε ξανά." }, "user": { "auth": { + "username": "Όνομα χρήστη", + "usernameEmail": "Όνομα Χρήστη ή Διεύθυνση Email", + "usernamePlaceholder": "π.χ. Φρειδερίκος", + "email": "Διεύθυνση email", + "emailPlaceholder": "π.χ. freiderikos{'@'}vikunja.io", + "password": "Κωδικός πρόσβασης", + "passwordPlaceholder": "π.χ. •••••••••••", + "forgotPassword": "Ξεχάσατε τον κωδικό σας πρόσβασης;", + "resetPassword": "Επαναφορά του κωδικού σας πρόσβασης", + "resetPasswordAction": "Να μου σταλεί σύνδεσμος επαναφοράς κωδικού πρόσβασης", + "resetPasswordSuccess": "Ελέγξτε τα εισερχόμενά σας! Θα πρέπει να έχετε λάβει ένα email με οδηγίες για την επαναφορά του κωδικού σας πρόσβασης.", + "confirmEmailSuccess": "Επιβεβαιώσατε με επιτυχία το email σας! Μπορείτε τώρα να συνδεθείτε.", + "totpTitle": "Κωδικός Ελέγχου Ταυτοποίησης Δύο Παραγόντων", + "totpPlaceholder": "π.χ. 123456", + "login": "Είσοδος", + "createAccount": "Δημιουργία λογαριασμού", + "loginWith": "Σύνδεση μέσω {provider}", + "authenticating": "Ταυτοποίηση…", + "openIdStateError": "Η κατάσταση δεν ταιριάζει, αρνούμαι να συνεχίσω!", + "openIdGeneralError": "Παρουσιάστηκε σφάλμα κατά την ταυτοποίηση μέσω τρίτου παρόχου.", "openIdTotpRequired": "Ο λογαριασμός σας απαιτεί ταυτοποίηση δύο παραγόντων. Εισαγάγετε τον κωδικό σας μίας χρήσης και κάνετε είσοδο εκ νέου.", + "openIdTotpSubmit": "Συνέχεια", + "oauthMissingParams": "Λείπουν οι απαιτούμενες παράμετροι OAuth: {params}", + "oauthRedirectedToApp": "Έχετε μεταβεί στην εφαρμογή. Μπορείτε να κλείσετε τώρα αυτή την καρτέλα.", + "desktopTryDemo": "Δοκιμάστε το demo", + "desktopCustomServer": "Προσαρμοσμένη Διεύθυνση URL Διακομιστή", + "desktopCustomServerDescription": "Εισαγάγετε τη διεύθυνση URL του διακομιστή σας Vikunja για να ξεκινήσετε.", "desktopWaitingForAuth": "Αναμονή για ταυτοποίηση…", - "desktopOAuthError": "Η ταυτοποίηση απέτυχε: {error}" + "desktopOAuthError": "Η ταυτοποίηση απέτυχε: {error}", + "logout": "Έξοδος", + "emailInvalid": "Παρακαλώ εισαγάγετε μια έγκυρη διεύθυνση email.", + "usernameRequired": "Παρακαλώ εισαγάγετε ένα όνομα χρήστη.", + "usernameMustNotContainSpace": "Το όνομα χρήστη δεν πρέπει να περιέχει κενούς χαρακτήρες.", + "usernameMustNotLookLikeUrl": "Το όνομα χρήστη δεν πρέπει να μοιάζει με URL.", + "passwordRequired": "Παρακαλώ εισαγάγετε ένα κωδικό πρόσβασης.", + "passwordNotMin": "Ο κωδικός πρόσβασης πρέπει να περιέχει τουλάχιστον 8 χαρακτήρες.", + "passwordNotMax": "Ο κωδικός πρόσβασης πρέπει να περιέχει το πολύ 72 χαρακτήρες.", + "showPassword": "Προβολή κωδικού πρόσβασης", + "hidePassword": "Απόκρυψη κωδικού πρόσβασης", + "noAccountYet": "Δεν έχετε ακόμη λογαριασμό;", + "alreadyHaveAnAccount": "Έχετε ήδη λογαριασμό;", + "remember": "Μείνετε συνδεδεμένος/η", + "registrationDisabled": "Η εγγραφή είναι απενεργοποιημένη.", + "passwordResetTokenMissing": "Λείπει το τεκμήριο επαναφοράς κωδικού πρόσβασης.", + "registrationFailed": "Παρουσιάστηκε σφάλμα κατά την εγγραφή. Παρακαλώ ελέγξτε τι εισαγάγατε και προσπαθήστε εκ νέου." }, "settings": { "bots": { - "description": "Οι χρήστες bot είναι μόνο API-χρήστες που εσείς κατέχετε. Μπορούν να προστεθούν σε έργα, ανατεθειμένες εργασίες και ταυτοποιούνται με τεκμήρια API. Δεν μπορούν να συνδεθούν διαδραστικά." + "title": "Χρήστες bot", + "description": "Οι χρήστες bot είναι μόνο API-χρήστες που εσείς κατέχετε. Μπορούν να προστεθούν σε έργα, ανατεθειμένες εργασίες και ταυτοποιούνται με τεκμήρια API. Δεν μπορούν να συνδεθούν διαδραστικά.", + "namePlaceholder": "Ο Βοηθός μου", + "create": "Δημιουργία bot", + "enable": "Ενεργοποίηση", + "badge": "Bot", + "delete": { + "header": "Διαγραφή αυτού του χρήστη bot", + "text1": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τον χρήστη bot \"{username}\";", + "text2": "Αυτό είναι μη αναστρέψιμο. Οποιαδήποτε τεκμήρια API ανήκουν σε αυτό το bot θα ανακληθούν." + } + }, + "title": "Ρυθμίσεις", + "newPasswordTitle": "Ενημερώστε Τον Κωδικό σας Πρόσβασης", + "newPassword": "Νέος κωδικός πρόσβασης", + "currentPassword": "Τρέχων κωδικός πρόσβασης", + "currentPasswordPlaceholder": "Ο τρέχων κωδικός σας πρόσβασης", + "passwordUpdateSuccess": "Ο κωδικός πρόσβασης ενημερώθηκε με επιτυχία.", + "updateEmailTitle": "Ενημέρωση Της Διεύθυνσης Email σας", + "updateEmailNew": "Νέα διεύθυνση email", + "updateEmailSuccess": "Η διεύθυνση email σας ενημερώθηκε με επιτυχία. Έχει σταλεί ένας σύνδεσμος για να την επιβεβαιώσετε.", + "general": { + "title": "Γενικές Ρυθμίσεις", + "name": "Το Όνομά Μου", + "newName": "Το νέο όνομα", + "savedSuccess": "Οι ρυθμίσεις ενημερώθηκαν με επιτυχία.", + "emailReminders": "Να μου στέλνονται υπενθυμίσεις για τις εργασίες μέσω email", + "overdueReminders": "Να μου στέλνεται μια σύνοψη των καθυστερημένων και μη ολοκληρωμένων εργασιών κάθε μέρα", + "discoverableByName": "Να επιτρέπεται σε άλλους χρήστες να με προσθέτουν ως μέλος σε ομάδες ή έργα όταν ψάχνουν το όνομά μου", + "discoverableByEmail": "Να επιτρέπεται σε άλλους χρήστες να με προσθέτουν ως μέλος σε ομάδες ή έργα όταν ψάχνουν την πλήρη διεύθυνση email μου", + "playSoundWhenDone": "Να αναπαράγεται ένας ήχος όταν σημειώνονται οι εργασίες ως ολοκληρωμένες", + "allowIconChanges": "Να εμφανίζονται ειδικά λογότυπα σε συγκεκριμένες χρονικές περιόδους", + "alwaysShowBucketTaskCount": "Να εμφανίζεται πάντα ο αριθμός εργασιών στους κάδους Kanban", + "defaultTaskRelationType": "Προκαθορισμένος τύπος σχετιζόμενης εργασίας", + "weekStart": "Η εβδομάδα ξεκινάει την", + "weekStartSunday": "Κυριακή", + "weekStartMonday": "Δευτέρα", + "weekStartTuesday": "Τρίτη", + "weekStartWednesday": "Τετάρτη", + "weekStartThursday": "Πέμπτη", + "weekStartFriday": "Παρασκευή", + "weekStartSaturday": "Σάββατο", + "language": "Γλώσσα", + "defaultProject": "Προκαθορισμένο έργο", + "defaultView": "Προκαθορισμένη προβολή", + "timezone": "Ώρα ζώνης", + "overdueTasksRemindersTime": "Χρόνος υπενθύμισης με email εκπρόθεσμων εργασιών", + "quickAddDefaultReminders": "Προκαθορισμένες υπενθυμίσεις για γρήγορη προσθήκη", + "quickAddDefaultRemindersDescription": "Οι υπενθυμίσεις αυτές θα προστίθενται αυτόματα σε κάθε εργασία που δημιουργείται μέσω του quick add magic και περιέχει ημερομηνία παράδοσης.", + "quickAddDefaultRemindersHint": "Προσθέστε μία ή περισσότερες υπενθυμίσεις σχετικά με την ημερομηνία παράδοσης της εργασίας. Αφήστε κενό για απενεργοποίηση.", + "filterUsedOnOverview": "Αποθηκευμένο φίλτρο που χρησιμοποιείται στη σελίδα επισκόπησης", + "showLastViewed": "Εμφάνιση των τελευταία προβεβλημένων έργων στη σελίδα επισκόπησης", + "minimumPriority": "Ελάχιστη προτεραιότητα για ορατή εργασία", + "dateDisplay": "Μορφή εμφάνισης ημερομηνίας", + "dateDisplayOptions": { + "relative": "Σχετική (πχ. πριν από 3 ημέρες)", + "mm-dd-yyyy": "ΜΜ-ΗΗ-ΕΕΕΕ", + "dd-mm-yyyy": "ΗΗ-ΜΜ-ΕΕΕΕ", + "yyyy-mm-dd": "ΕΕΕΕ-ΜΜ-ΗΗ", + "mm/dd/yyyy": "ΜΜ/ΗΗ/ΕΕΕΕ", + "dd/mm/yyyy": "ΗΗ/ΜΜ/ΕΕΕΕ", + "yyyy/mm/dd": "ΕΕΕΕ/ΜΜ/ΗΗ" + }, + "timeFormat": "Μορφή ώρας", + "timeFormatOptions": { + "12h": "12 ώρες (ΠΜ/ΜΜ)", + "24h": "24 ώρες (ΩΩ:ΛΛ)" + }, + "externalUserNameChange": "Το όνομά σας διαχειρίζεται από τον πάροχό σας σύνδεσης ({provider}). Για να το αλλάξετε, ενημερώστε το εκεί." + }, + "sections": { + "personalInformation": "Προσωπικές Πληροφορίες", + "taskAndNotifications": "Έργα & Εργασίες", + "privacy": "Ιδιωτικότητα", + "localization": "Τοπική Προσαρμογή", + "appearance": "Εμφάνιση & Συμπεριφορά" }, "totp": { "title": "Ταυτοποίηση Δύο Παραγόντων", @@ -28,10 +198,191 @@ }, "feeds": { "apiTokenHint": "Ταυτοποιηθείτε με τεκμήριο API που έχει την άδεια {scope}. Δημιουργήστε ένα στο {link}." + }, + "apiTokens": { + "createToken": "Δημιουργία τεκμηρίου", + "30d": "30 Ημέρες", + "60d": "60 Ημέρες", + "90d": "90 Ημέρες", + "permissionExplanation": "Οι άδειες σας επιτρέπουν να ορίσετε την εμβέλεια του τι επιτρέπεται να κάνει ένα τεκμήριο api.", + "titleRequired": "Ο τίτλος είναι απαιτούμενος", + "permissionRequired": "Παρακαλώ επιλέξτε τουλάχιστον μια άδεια από τη λίστα.", + "expired": "Το τεκμήριο έχει λήξει {ago}.", + "tokenCreatedSuccess": "Αυτό είναι το νέο σας τεκμήριο api: {token}", + "tokenCreatedNotSeeAgain": "Αποθηκεύστε το σε μια ασφαλή τοποθεσία, διαφορετικά δε θα το ξαναδείτε!", + "presets": { + "title": "Γρήγορες προεπιλογές", + "readOnly": "Μόνο για ανάγνωση", + "tasks": "Διαχείριση εργασιών", + "projects": "Διαχείριση έργου", + "fullAccess": "Πλήρης πρόσβαση" + }, + "delete": { + "header": "Διαγραφή αυτού του τεκμηρίου", + "text1": "Είστε σίγουροι ότι θέλετε να διαγράψετε το τεκμήριο\"{token}\";", + "text2": "Αυτό θα ανακαλέσει την πρόσβαση σε όλες τις εφαρμογές ή ενσωματώσεις που το χρησιμοποιούν. Δεν μπορείτε να το αναιρέσετε." + }, + "attributes": { + "title": "Τίτλος", + "titlePlaceholder": "Εισαγάγετε έναν τίτλο που θα αναγνωρίζετε στο μέλλον", + "expiresAt": "Λήγει στις", + "permissions": "Άδειες" + } + }, + "sessions": { + "title": "Συνεδρίες", + "description": "Αυτές είναι όλες οι συσκευές που είναι συνδεδεμένες στον λογαριασμό σας. Μπορείτε να ανακαλέσετε οποιαδήποτε συνεδρία για να αποσυνδεθεί η συσκευή. Ενδέχεται να χρειαστούν έως και 10 λεπτά ώσπου η ανάκληση να τεθεί σε πλήρη ισχύ.", + "deviceInfo": "Συσκευή", + "ipAddress": "Διεύθυνση IP", + "lastActive": "Τελευταία Δραστηριότητα", + "current": "Τρέχουσα συνεδρία", + "delete": { + "header": "Ανάκληση συνεδρίας", + "text": "Είστε βέβαιοι ότι θέλετε να ανακαλέσετε αυτή τη συνεδρία; Η συσκευή θα αποσυνδεθεί. Μπορεί να χρειαστούν έως και 10 λεπτά για να λήξει πλήρως η συνεδρία." + }, + "deleteSuccess": "Η συνεδρία ανακλήθηκε επιτυχώς. Μπορεί να χρειαστούν έως και 10 λεπτά για να λήξει πλήρως.", + "noOtherSessions": "Δεν υπάρχουν άλλες ενεργές συνεδρίες." } + }, + "deletion": { + "title": "Διαγραφή του λογαριασμού σας Vikunja", + "text1": "Η διαγραφή του λογαριασμού σας είναι μόνιμη και δεν μπορεί να αναιρεθεί. Θα διαγράψουμε όλα τα έργα, τις εργασίες σας και όλα όσα σχετίζονται με αυτόν.", + "text2": "Για να συνεχίσετε, παρακαλώ εισαγάγετε τον κωδικό σας πρόσβασης. Θα λάβετε ένα email με περαιτέρω οδηγίες.", + "text3": "Για να συνεχίσετε, πατήστε το παρακάτω πλήκτρο. Θα λάβετε ένα email με περαιτέρω οδηγίες.", + "confirm": "Διαγραφή του λογαριασμού μου", + "requestSuccess": "Το αίτημα ήταν επιτυχές. Θα λάβετε ένα email με περαιτέρω οδηγίες.", + "passwordRequired": "Παρακαλώ εισαγάγετε τον κωδικό σας πρόσβασης.", + "confirmSuccess": "Έχετε επιβεβαιώσει με επιτυχία τη διαγραφή του λογαριασμού σας. Ο λογαριασμός σας θα διαγραφεί σε τρεις ημέρες.", + "scheduled": "Ο λογαριασμός σας στο Vikunja θα διαγραφεί στις {date} ({dateSince}).", + "scheduledCancel": "Για να ακυρώσετε τη διαγραφή του λογαριασμού σας, κάντε κλικ εδώ.", + "scheduledCancelText": "Για να ακυρώσετε τη διαγραφή του λογαριασμού σας, εισαγάγετε τον κωδικό σας πρόσβασης παρακάτω:", + "scheduledCancelButton": "Για να ακυρώσετε τη διαγραφή του λογαριασμού σας, πατήστε στο παρακάτω πλήκτρο:", + "scheduledCancelConfirm": "Ακύρωση της διαγραφής του λογαριασμού μου", + "scheduledCancelSuccess": "Δε θα διαγράψουμε τον λογαριασμό σας." + }, + "export": { + "title": "Εξαγωγή των δεδομένων σας από το Vikunja", + "description": "Μπορείτε να ζητήσετε αντίγραφο όλων των δεδομένων σας στο Vikunja. Αυτό περιλαμβάνει Έργα, Εργασίες και όλα όσα σχετίζονται με αυτά. Μπορείτε να εισαγάγετε αυτά τα δεδομένα σε οποιοδήποτε στιγμιότυπο Vikunja μέσω της λειτουργίας μετανάστευσης.", + "descriptionPasswordRequired": "Παρακαλώ εισαγάγετε τον κωδικό σας πρόσβασης για να συνεχίσετε:", + "request": "Ζητήστε αντίγραφο των δεδομένων σας στο Vikunja", + "success": "Έχετε αιτηθεί με επιτυχία τα δεδομένα σας στο Vikunja! Θα σας στείλουμε ένα email μόλις είναι έτοιμα για λήψη.", + "downloadTitle": "Κάντε λήψη των εξαχθέντων δεδομένων σας στο Vikunja", + "ready": "Η εξαγωγή σας είναι έτοιμη για λήψη. Μπορείτε να την κατεβάσετε μέχρι {0}.", + "requestNew": "Αίτηση για άλλη εξαγωγή" } }, "project": { + "archivedMessage": "Το έργο είναι αρχειοθετημένο. Δεν είναι δυνατό να δημιουργήσετε νέες εργασίες ή να επεξεργαστείτε εργασίες σε αυτό.", + "archived": "Αρχειοθετημένα", + "showArchived": "Προβολή αρχειοθετημένων", + "title": "Τίτλος", + "color": "Χρώμα", + "projects": "Έργα", + "parent": "Γονικό έργο", + "search": "Πληκτρολογήστε για αναζήτηση έργου…", + "searchSelect": "Κάντε κλικ ή πατήστε Enter για να επιλέξετε αυτό το έργο", + "shared": "Κοινόχρηστα Έργα", + "noDescriptionAvailable": "Δεν υπάρχει διαθέσιμη περιγραφή έργου.", + "inboxTitle": "Εισερχόμενα", + "favorite": "Να σημειωθεί το έργο ως αγαπημένο", + "unfavorite": "Αφαίρεση του έργου από τα αγαπημένα", + "openSettingsMenu": "Άνοιγμα μενού ρυθμίσεων έργου", + "description": "Περιγραφή έργου", + "favoriteDescription": "Το έργο έχει όλες τις εργασίες σημειωμένες στα αγαπημένα.", + "create": { + "header": "Νέο έργο", + "titlePlaceholder": "Ο τίτλος του έργου πηγαίνει εδώ…", + "addTitleRequired": "Παρακαλώ καθορίστε έναν τίτλο.", + "createdSuccess": "Το έργο δημιουργήθηκε με επιτυχία.", + "addProjectRequired": "Παρακαλώ καθορίστε ένα έργο ή επιλέξτε ένα έργο ως προεπιλεγμένο στις ρυθμίσεις." + }, + "archive": { + "title": "Αρχειοθέτηση του \"{project}\"", + "archive": "Αρχειοθέτηση του έργου", + "unarchive": "Κατάργηση αρχειοθέτησης του έργου", + "unarchiveText": "Θα μπορείτε να δημιουργήσετε εργασίες ή να τις επεξεργαστείτε.", + "archiveText": "Δε θα μπορείτε να επεξεργαστείτε το έργο ή να δημιουργήσετε εργασίες μέχρι να αναιρέσετε την αρχειοθέτησή του.", + "success": "Το έργο αρχειοθετήθηκε με επιτυχία." + }, + "background": { + "title": "Ορισμός φόντου έργου", + "remove": "Αφαίρεση φόντου", + "upload": "Επιλέξτε ένα φόντο από τον υπολογιστή σας", + "searchPlaceholder": "Αναζήτηση για φόντο…", + "poweredByUnsplash": "Με τη δύναμη του Unsplash", + "loadMore": "Φόρτωση περισσότερων φωτογραφιών", + "success": "Το φόντο έχει οριστεί με επιτυχία!", + "removeSuccess": "Το φόντο αφαιρέθηκε με επιτυχία!" + }, + "delete": { + "title": "Διαγραφή του \"{project}\"", + "header": "Διαγραφή του έργου", + "text1": "Είστε σίγουροι ότι θέλετε να διαγράψετε το έργο και όλο τα περιεχόμενό του;", + "text2": "Αυτό περιλαμβάνει όλες τις εργασίες και ΔΕΝ ΕΙΝΑΙ ΔΥΝΑΤΗ Η ΑΝΑΙΡΕΣΗ!", + "success": "Το έργο διαγράφηκε με επιτυχία.", + "tasksToDelete": "Αυτό θα αφαιρέσει οριστικά περίπου {count} εργασίες.", + "tasksAndChildProjectsToDelete": "Αυτό θα αφαιρέσει οριστικά περίπου {tasks} εργασίες και {projects} έργα." + }, + "first": { + "title": "Πρώτη Προβολή" + }, + "list": { + "title": "Λίστα", + "add": "Προσθήκη", + "addPlaceholder": "Προσθήκη εργασίας…", + "empty": "Το έργο είναι επί του παρόντος κενό.", + "newTaskCta": "Δημιουργήστε μια εργασία.", + "editTask": "Επεξεργασία Εργασίας", + "sort": "Ταξινόμηση" + }, + "gantt": { + "title": "Gantt", + "size": "Μέγεθος", + "default": "Προεπιλογή", + "month": "Μήνας", + "day": "Ημέρα", + "hour": "Ώρα", + "range": "Εύρος Ημερομηνιών", + "chartLabel": "Γράφημα Gantt Έργου", + "taskBarsForRow": "Μπάρες εργασίας για τη γραμμή {rowId}", + "taskBarLabel": "Εργασία: {task}. Από {startDate} έως {endDate}. {dateType}. Κάντε κλικ για επεξεργασία, σύρετε για μετακίνηση.", + "scheduledDates": "Προγραμματισμένες ημερομηνίες", + "estimatedDates": "Εκτιμώμενες ημερομηνίες", + "resizeStartDate": "Επανακαθορισμός ημερομηνίας έναρξης για την εργασία {task}", + "resizeEndDate": "Επανακαθορισμός ημερομηνίας λήξης για την εργασία {task}", + "timelineHeader": "Κεφαλίδα χρονοδιαγράμματος με μήνες και ημέρες", + "monthsRow": "Γραμμή μηνών", + "daysRow": "Γραμμή ημερών", + "monthLabel": "Μήνας: {month}", + "dayLabel": "Ημέρα: {date}, {weekday}", + "dayLabelToday": "Ημέρα: {date}, {weekday}", + "taskAriaLabel": "Εργασία: {task}", + "taskAriaLabelById": "Εργασία {id}", + "partialDatesStart": "Μόνο ημερομηνία έναρξης (ανοικτή λήξη)", + "partialDatesEnd": "Μόνο ημερομηνία λήξης (ανοικτή έναρξη)", + "expandGroup": "Ανάπτυξη της ομάδας: {task}", + "collapseGroup": "Σύμπτυξη της ομάδας: {task}", + "toggleRelationArrows": "Εναλλαγή βελών συσχέτισης" + }, + "table": { + "title": "Πίνακας", + "columns": "Στήλες" + }, + "kanban": { + "title": "Kanban", + "limit": "Όριο: {limit}", + "noLimit": "Δεν έχει οριστεί", + "doneBucket": "Κάδος για ολοκληρωμένα", + "doneBucketHint": "Όλες οι εργασίες που μετακινούνται σε αυτόν τον κάδο θα σημειώνονται αυτόματα ως ολοκληρωμένες.", + "doneBucketHintExtended": "Όλες οι εργασίες που μετακινούνται στον κάδο των ολοκληρωμένων θα σημειώνονται αυτόματα ως ολοκληρωμένες. Όλες οι εργασίες που σημειώνονται ως ολοκληρωμένες από αλλού επίσης θα μετακινηθούν.", + "doneBucketSavedSuccess": "Ο κάδος για τα ολοκληρωμένα αποθηκεύτηκε με επιτυχία.", + "defaultBucket": "Προκαθορισμένος κάδος", + "defaultBucketHint": "Όταν δημιουργείτε εργασίες χωρίς να καθορίσετε κάδο, θα προστίθενται σε αυτόν τον κάδο.", + "defaultBucketSavedSuccess": "Ο προκαθορισμένος κάδος αποθηκεύτηκε με επιτυχία.", + "deleteLast": "Δεν μπορείτε να αφαιρέσετε τον τελευταίο κάδο.", + "addTaskPlaceholder": "Εισαγάγετε τον τίτλο της νέας εργασίας…", + "addTask": "Προσθήκη εργασίας" + }, "webhooks": { "basicauthuser": "Χρήστης για Βασική Ταυτοποίηση", "basicauthpassword": "Κωδικός πρόσβασης για Βασική Ταυτοποίηση", @@ -83,6 +434,164 @@ "description": "Κάντε κλικ στο λογότυπο μιας από τις παρακάτω υπηρεσίες τρίτων για να ξεκινήσετε.", "descriptionDo": "Το Vikunja θα εισαγάγει όλες τις λίστες, εργασίες, σημειώσεις, υπενθυμίσεις και αρχεία που έχετε πρόσβαση.", "authorize": "Για να επιτρέψετε στο Vikunja να προσπελάσει το {name} Λογαριασμό σας, κάντε κλικ στο παρακάτω πλήκτρο.", - "alreadyMigrated1": "Φαίνεται ότι έχετε ήδη εισαγάγει τα στοιχεία σας από το {name} στο {date}." + "alreadyMigrated1": "Φαίνεται ότι έχετε ήδη εισαγάγει τα στοιχεία σας από το {name} στο {date}.", + "alreadyMigrated2": "Η εισαγωγή είναι και πάλι δυνατή, αλλά μπορεί να δημιουργήσει διπλότυπα. Είστε σίγουροι;", + "confirm": "Είμαι σίγουρος, παρακαλώ ξεκινήστε τη μετάβαση τώρα!", + "importUpload": "Για εισαγωγή δεδομένων από το {name} στο Vikunja, κάντε κλικ στο παρακάτω κουμπί για να επιλέξετε ένα αρχείο.", + "migrationStartedWillReciveEmail": "Το Vikunja θα εισάγει τώρα λίστες/έργα, εργασίες, σημειώσεις, υπενθυμίσεις και αρχεία από το {service}. Καθώς αυτό ενδέχεται να διαρκέσει λίγο χρόνο, θα σας σταλεί ένα email μόλις ολοκληρωθεί. Μπορείτε να κλείσετε τώρα το παράθυρο του προγράμματος πλοήγησης.", + "migrationInProgress": "Μια μετάβαση βρίσκεται σε εξέλιξη αυτήν τη στιγμή. Παρακαλώ περιμένετε μέχρι να ολοκληρωθεί.", + "csv": { + "description": "Εισαγωγή εργασιών από ένα αρχείο CSV με προσαρμοσμένη αντιστοίχιση στηλών.", + "uploadDescription": "Επιλέξτε ένα αρχείο CSV για εισαγωγή. Το αρχείο πρέπει να περιέχει τα δεδομένα των εργασιών με κεφαλίδες στην πρώτη γραμμή.", + "columnMappingDescription": "Αντιστοίχιση κάθε στήλης στο αρχείο CSV με ιδιότητα εργασίας. Το Vikunja έχει εντοπίσει αυτόματα τις πιο πιθανές αντιστοιχίσεις. Η παρακάτω προεπισκόπηση θα ενημερωθεί αυτόματα όταν αλλάζετε τις ρυθμίσεις.", + "parsingOptions": "Επιλογές Ανάλυσης" + } + }, + "label": { + "description": "Κάντε κλικ σε μια ετικέτα για να την επεξεργαστείτε. Μπορείτε να επεξεργαστείτε όλες τις ετικέτες που δημιουργήσατε, μπορείτε να χρησιμοποιήσετε όλες τις ετικέτες που σχετίζονται με μια εργασία σε έργο στο οποίο έχετε πρόσβαση." + }, + "sharing": { + "passwordRequired": "Αυτό το διαμοιραζόμενο έργο απαιτεί έναν κωδικό πρόσβασης. Παρακαλώ εισάγετέ τον παρακάτω:", + "projectLoadError": "Αποτυχία φόρτωσης πληροφοριών έργου." + }, + "navigation": { + "imprint": "Αποτύπωμα" + }, + "misc": { + "skipToContent": "Μετάβαση στο κύριο περιεχόμενο" + }, + "input": { + "editor": { + "codeTooltip": "Καταγράψτε ένα απόσπασμα κώδικα.", + "quoteTooltip": "Καταγράψτε μια φράση.", + "bulletListTooltip": "Δημιουργήστε μια απλή λίστα κουκκίδων.", + "orderedList": "Ταξινομημένη λίστα", + "orderedListTooltip": "Δημιουργήστε μια λίστα με αρίθμηση.", + "taskListTooltip": "Παρακολουθήστε εργασίες με μια to-do λίστα.", + "placeholder": "Πληκτρολογήστε κάποιο κείμενο ή πατήστε '/' για να δείτε περισσότερες επιλογές…" + }, + "multiselect": { + "selectPlaceholder": "Κάντε κλικ ή πατήστε enter για να επιλέξετε" + }, + "datepickerRange": { + "values": { + "startOfToday": "Έναρξη της ημέρας", + "endOfToday": "Τέλος της ημέρας", + "beginningOflastWeek": "Αρχή της προηγούμενης εβδομάδας" + } + }, + "datemathHelp": { + "canuse": "Μπορείτε να χρησιμοποιήσετε μαθηματικά σε ημερομηνίες για σχετικές ημερομηνίες.", + "title": "Μαθηματικά Ημερομηνιών", + "intro": "Καθορίστε σχετικές ημερομηνίες που επιλύονται στη στιγμή από το Vikunja κατά την εφαρμογή του φίλτρου.", + "expression": "Κάθε μαθηματική έκφραση ημερομηνίας ξεκινά με μια ημερομηνία αγκύρωσης, η οποία μπορεί να είναι είτε {0}, ή μια συμβολοσειρά ημερομηνίας που τελειώνει σε {1}. Αυτή η ημερομηνία αγκύρωσης μπορεί προαιρετικά να ακολουθείται από μία ή περισσότερες εκφράσεις μαθηματικών.", + "similar": "Αυτές οι εκφράσεις είναι παρόμοιες με αυτές που παρέχονται από {0} και {1}." + } + }, + "task": { + "detail": { + "actions": { + "percentDone": "Ορισμός Προόδου", + "attachments": "Προσθήκη Συνημμένων", + "relatedTasks": "Προσθήκη Συσχέτισης", + "moveProject": "Μετακίνηση", + "duplicate": "Αντιγραφή", + "color": "Ορισμός Χρώματος", + "delete": "Διαγραφή", + "favorite": "Προσθήκη στα Αγαπημένα", + "unfavorite": "Αφαίρεση από τα Αγαπημένα" + } + }, + "attributes": { + "assignees": "Ανατιθέμενοι", + "color": "Χρώμα", + "created": "Δημιουργήθηκε", + "createdBy": "Δημιουργήθηκε Από", + "description": "Περιγραφή", + "done": "Ολοκληρώθηκε", + "endDate": "Ημερομηνία Λήξης", + "labels": "Ετικέτες", + "percentDone": "Πρόοδος", + "priority": "Προτεραιότητα", + "project": "Έργο", + "relatedTasks": "Σχετιζόμενες Εργασίες", + "reminders": "Υπενθυμίσεις", + "repeat": "Επανάληψη", + "comment": "{count} σχόλιο | {count} σχόλια", + "commentCount": "Αριθμός σχολίων", + "startDate": "Ημερομηνία Έναρξης", + "title": "Τίτλος", + "updated": "Ενημερώθηκε", + "doneAt": "Ολοκληρώθηκε Στις" + }, + "subscription": { + "subscribedTaskThroughParentProject": "Δεν μπορείτε να κάνετε απεγγραφή εδώ επειδή έχετε εγγραφεί σε αυτήν την εργασία μέσω του έργου της.", + "subscribedProject": "Αυτή τη στιγμή είστε εγγεγραμμένοι σε αυτό το έργο και θα λαμβάνετε ειδοποιήσεις για αλλαγές.", + "notSubscribedProject": "Δεν είστε εγγεγραμμένοι σε αυτό το έργο και δε θα λαμβάνετε ειδοποιήσεις για αλλαγές.", + "subscribedTask": "Αυτή τη στιγμή είστε εγγεγραμμένοι σε αυτήν την εργασία και θα λαμβάνετε ειδοποιήσεις για αλλαγές.", + "notSubscribedTask": "Δεν είστε εγγεγραμμένοι σε αυτή την εργασία και δε θα λαμβάνετε ειδοποιήσεις για αλλαγές.", + "subscribe": "Εγγραφή", + "unsubscribe": "Απεγγραφή", + "subscribeSuccessProject": "Είστε τώρα εγγεγραμμένοι στο έργο", + "unsubscribeSuccessProject": "Έχετε κάνει απεγγραφή από το έργο", + "subscribeSuccessTask": "Είστε τώρα εγγεγραμμένοι στην εργασία", + "unsubscribeSuccessTask": "Έχετε κάνει απεγγραφή από την εργασία" + }, + "attachment": { + "deleteText1": "Είστε σίγουροι ότι θέλετε να διαγράψετε το συνημμένο {filename};", + "copyUrlTooltip": "Αντιγραφή της διεύθυνσης url του συνημμένου για χρήση σε κείμενο", + "setAsCover": "Δημιουργία καλύμματος", + "unsetAsCover": "Αφαίρεση καλύμματος", + "successfullyChangedCoverImage": "Η εικόνα καλύμματος τροποποιήθηκε με επιτυχία." + }, + "label": { + "addCreateSuccess": "Η ετικέτα δημιουργήθηκε και προστέθηκε με επιτυχία.", + "delete": { + "header": "Διαγραφή της ετικέτας", + "text1": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτή την ετικέτα;", + "text2": "Αυτό θα το αφαιρέσει από όλες τις εργασίες και δεν μπορεί να αναιρεθεί." + } + }, + "priority": { + "doNow": "ΝΑ ΓΙΝΕΙ ΤΩΡΑ" + }, + "relation": { + "add": "Προσθήκη Νέας Συσχέτισης Εργασίας", + "new": "Νέα Συσχέτιση Εργασίας", + "searchPlaceholder": "Πληκτρολογήστε στην αναζήτηση μια εργασία για να την προσθέσετε ως σχετιζόμενη…", + "createPlaceholder": "Προσθέστε την ως σχετιζόμενη εργασία", + "differentProject": "Η εργασία ανήκει σε διαφορετικό έργο.", + "noneYet": "Δεν υπάρχουν ακόμη συσχετίσεις.", + "delete": "Διαγραφή Συσχέτισης Εργασίας", + "deleteText1": "Είστε σίγουροι ότι θέλετε να διαγράψετε τη συσχέτιση εργασίας;", + "select": "Επιλέξτε είδος σχέσης" + }, + "reminder": { + "before": "{amount} {unit} πριν {type}", + "after": "{amount} {unit} μετά {type}", + "afterShort": "μετά", + "onStartDate": "Στην ημερομηνία έναρξης", + "onEndDate": "Στην ημερομηνία λήξης", + "custom": "Προσαρμογή", + "dateAndTime": "Ημερομηνία και ώρα" + }, + "repeat": { + "everyDay": "Κάθε Ημέρα", + "everyWeek": "Κάθε Εβδομάδα", + "every30d": "Κάθε 30 Ημέρες", + "mode": "Λειτουργία επανάληψης", + "monthly": "Μηνιαία", + "fromCurrentDate": "Από την ημερομηνία ολοκλήρωσης", + "each": "Κάθε", + "specifyAmount": "Καθορίστε μια τιμή…", + "hours": "Ώρες", + "days": "Ημέρες", + "weeks": "Εβδομάδες", + "invalidAmount": "Παρακαλώ εισαγάγετε μια τιμή μεγαλύτερη από το 0." + }, + "quickAddMagic": { + "quickEntryHint": "Χρήση μαγικών προθεμάτων για ημερομηνίες, ετικέτες & άλλα. Ανοίξτε την κύρια εφαρμογή Vikunja και ελέγξτε την υπόδειξη στην είσοδο εργασίας για περισσότερες λεπτομέρειες.", + "title": "Κατάσταση Quick Add Magic" + } } } \ No newline at end of file From 44db02ab56651e26e4383ffc1d49fcdc812bf0d5 Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 20 May 2026 17:39:11 +0200 Subject: [PATCH 035/480] fix(task): print styles --- frontend/src/components/misc/Modal.vue | 2 +- frontend/src/components/tasks/partials/Heading.vue | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/misc/Modal.vue b/frontend/src/components/misc/Modal.vue index f33dd5aac..8d81eb3b5 100644 --- a/frontend/src/components/misc/Modal.vue +++ b/frontend/src/components/misc/Modal.vue @@ -17,7 +17,7 @@ > diff --git a/frontend/src/components/tasks/partials/Heading.vue b/frontend/src/components/tasks/partials/Heading.vue index 4695820c6..7b513684e 100644 --- a/frontend/src/components/tasks/partials/Heading.vue +++ b/frontend/src/components/tasks/partials/Heading.vue @@ -18,7 +18,7 @@ @@ -39,7 +39,7 @@ From 612628a6570c583443fe23cef48818de5ba706f9 Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 20 May 2026 17:53:01 +0200 Subject: [PATCH 036/480] fix(modal): print full content of modal dialogs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A opened via showModal() lives in the browser's top layer, which renders only on the first page during print — top-layer elements are viewport-anchored and don't paginate. CSS overrides like position: static have no effect since top-layer membership is browser-managed. Swap to a non-modal dialog on beforeprint (removes it from the top layer so content flows in normal document order) and back to modal on afterprint. The accompanying @media print rules reset the dialog's fixed positioning and overflow so the non-modal dialog can paginate freely. --- frontend/src/components/misc/Modal.vue | 60 +++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/misc/Modal.vue b/frontend/src/components/misc/Modal.vue index 8d81eb3b5..5a3fedc98 100644 --- a/frontend/src/components/misc/Modal.vue +++ b/frontend/src/components/misc/Modal.vue @@ -62,7 +62,7 @@ @@ -361,6 +394,31 @@ $modal-width: 1024px; } } +// Unconstrain the native so the full modal content flows onto the +// printed page instead of being clipped to the viewport-sized top layer. +@media print { + .modal-dialog { + position: static; + inline-size: auto; + block-size: auto; + max-inline-size: none; + max-block-size: none; + + &::backdrop { + display: none; + } + } + + .modal-container { + overflow: visible; + min-block-size: 0; + } + + :deep(.card) { + min-block-size: 0 !important; + } +} + .modal-content:has(.modal-header) { display: flex; flex-direction: column; From bc7c2059aaa79c00f738322829ffbd26c901d340 Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 20 May 2026 17:54:15 +0200 Subject: [PATCH 037/480] fix(print): hide bucket select icon --- frontend/src/components/tasks/partials/BucketSelect.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/tasks/partials/BucketSelect.vue b/frontend/src/components/tasks/partials/BucketSelect.vue index 72e33c58a..937527b2a 100644 --- a/frontend/src/components/tasks/partials/BucketSelect.vue +++ b/frontend/src/components/tasks/partials/BucketSelect.vue @@ -11,7 +11,7 @@ {{ currentBucketTitle }} From 995aad3d5338bd6c2032e73b1d1e5d28f40982dc Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 20 May 2026 17:58:13 +0200 Subject: [PATCH 038/480] fix(print): hide description editor when no description is present --- frontend/src/components/tasks/partials/Description.vue | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/tasks/partials/Description.vue b/frontend/src/components/tasks/partials/Description.vue index 7b5ab3907..10bc3c9ce 100644 --- a/frontend/src/components/tasks/partials/Description.vue +++ b/frontend/src/components/tasks/partials/Description.vue @@ -1,5 +1,7 @@