From 414032fdce64b9513a16ce99f948f1d1db5465f5 Mon Sep 17 00:00:00 2001 From: Justin Schroeder Date: Fri, 8 Aug 2025 16:36:57 -0400 Subject: [PATCH 01/16] test(e2e): add Playwright visual tests (video + pixel diffs); stabilize animation waits; upload videos in CI --- .github/workflows/main.yml | 6 + package.json | 7 +- playwright.config.ts | 9 + pnpm-lock.yaml | 1655 +++++----------------- tests/e2e/framework-animations.spec.ts | 8 +- tests/e2e/utils.ts | 13 + tests/e2e/visual-animation-video.spec.ts | 129 ++ tests/e2e/visual-animation.spec.ts | 125 ++ tests/e2e/visual-video.util.ts | 44 + 9 files changed, 710 insertions(+), 1286 deletions(-) create mode 100644 tests/e2e/visual-animation-video.spec.ts create mode 100644 tests/e2e/visual-animation.spec.ts create mode 100644 tests/e2e/visual-video.util.ts diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6c53643..d7bf462 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -36,3 +36,9 @@ jobs: with: name: playwright-report path: playwright-report + - name: Upload Playwright videos (visual) + if: always() + uses: actions/upload-artifact@v4 + with: + name: playwright-videos + path: test-results/**/video.webm diff --git a/package.json b/package.json index f4b91ea..b2627c5 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "test:e2e": "playwright test", "test:e2e:headed": "playwright test --headed --project=chromium", "test:e2e:ui": "playwright test --ui", - "test:e2e:leak": "LEAK_STRICT=1 playwright test -g memory --project=chromium" + "test:e2e:leak": "LEAK_STRICT=1 playwright test -g memory --project=chromium", + "test:e2e:visual": "VISUAL=1 playwright test -g 'Visual:' --project=chromium" }, "exports": { "./vue": { @@ -107,7 +108,9 @@ "vite-ssg": "^0.24.0", "vue": "^3.5.0", "vue-github-button": "^3.1.3", - "vue-router": "^4.4.3" + "vue-router": "^4.4.3", + "pixelmatch": "^5.3.0", + "pngjs": "^7.0.0" }, "packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748" } diff --git a/playwright.config.ts b/playwright.config.ts index 6a31615..9f8c1ae 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -6,12 +6,21 @@ export default defineConfig({ forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 2 : undefined, + expect: { + toHaveScreenshot: { + // Allow small rendering differences across environments + maxDiffPixelRatio: 0.03, + animations: 'allow', + }, + }, reporter: [['list'], ['html', { open: 'never' }]], use: { baseURL: 'http://localhost:5173', trace: 'retain-on-failure', video: 'retain-on-failure', screenshot: 'only-on-failure', + viewport: { width: 1200, height: 900 }, + deviceScaleFactor: 1, }, webServer: { command: 'cd docs && vite --config=./vite.config.ts --port=5173', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 44dc96a..7eb5f9f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,9 +26,12 @@ importers: '@playwright/test': specifier: ^1.47.2 version: 1.54.2 + '@rollup/plugin-terser': + specifier: ^0.4.4 + version: 0.4.4(rollup@4.46.2) '@rollup/plugin-typescript': specifier: ^11.1.6 - version: 11.1.6(rollup@3.29.5)(tslib@2.8.1)(typescript@5.9.2) + version: 11.1.6(rollup@4.46.2)(tslib@2.8.1)(typescript@5.9.2) '@types/node': specifier: ^20.14.15 version: 20.19.9 @@ -39,11 +42,11 @@ importers: specifier: ^2.4.9 version: 2.4.9 '@types/react': - specifier: ^18.3.3 - version: 18.3.23 + specifier: ^19.0.0 + version: 19.1.9 '@vitejs/plugin-vue': - specifier: ^5.1.2 - version: 5.2.4(vite@5.4.1(@types/node@20.19.9)(terser@5.43.1))(vue@3.5.18(typescript@5.9.2)) + specifier: ^6.0.0 + version: 6.0.1(vite@7.1.1(@types/node@20.19.9)(jiti@1.21.7)(terser@5.43.1))(vue@3.5.18(typescript@5.9.2)) '@vueuse/head': specifier: ^2.0.0 version: 2.0.0(vue@3.5.18(typescript@5.9.2)) @@ -56,9 +59,6 @@ importers: consola: specifier: ^3.2.3 version: 3.4.2 - cypress: - specifier: ^9.7.0 - version: 9.7.0 execa: specifier: ^6.1.0 version: 6.1.0 @@ -74,6 +74,12 @@ importers: pathe: specifier: ^1.1.2 version: 1.1.2 + pixelmatch: + specifier: ^5.3.0 + version: 5.3.0 + pngjs: + specifier: ^7.0.0 + version: 7.0.0 preact: specifier: ^10.23.2 version: 10.27.0 @@ -84,14 +90,11 @@ importers: specifier: ^2.4.2 version: 2.4.2 react: - specifier: ^18.3.1 - version: 18.3.1 + specifier: ^19.0.0 + version: 19.1.1 rollup: - specifier: ^3.29.4 - version: 3.29.5 - rollup-plugin-terser: - specifier: ^7.0.2 - version: 7.0.2(rollup@3.29.5) + specifier: ^4.21.0 + version: 4.46.2 shx: specifier: ^0.3.4 version: 0.3.4 @@ -102,19 +105,19 @@ importers: specifier: ^2.6.3 version: 2.8.1 typescript: - specifier: ^5.5.4 + specifier: ^5.9.2 version: 5.9.2 unbuild: specifier: ^2.0.0 version: 2.0.0(typescript@5.9.2) vite: - specifier: 5.4.1 - version: 5.4.1(@types/node@20.19.9)(terser@5.43.1) + specifier: ^7.0.0 + version: 7.1.1(@types/node@20.19.9)(jiti@1.21.7)(terser@5.43.1) vite-ssg: - specifier: ^0.23.8 - version: 0.23.8(vite@5.4.1(@types/node@20.19.9)(terser@5.43.1))(vue-router@4.5.1(vue@3.5.18(typescript@5.9.2)))(vue@3.5.18(typescript@5.9.2)) + specifier: ^0.24.0 + version: 0.24.3(vite@7.1.1(@types/node@20.19.9)(jiti@1.21.7)(terser@5.43.1))(vue-router@4.5.1(vue@3.5.18(typescript@5.9.2)))(vue@3.5.18(typescript@5.9.2)) vue: - specifier: ^3.4.38 + specifier: ^3.5.0 version: 3.5.18(typescript@5.9.2) vue-github-button: specifier: ^3.1.3 @@ -210,10 +213,6 @@ packages: resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} engines: {node: '>=6.9.0'} - '@colors/colors@1.5.0': - resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} - engines: {node: '>=0.1.90'} - '@csstools/color-helpers@5.0.2': resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==} engines: {node: '>=18'} @@ -242,27 +241,20 @@ packages: resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} engines: {node: '>=18'} - '@cypress/request@2.88.12': - resolution: {integrity: sha512-tOn+0mDZxASFM+cuAP9szGUGPI1HwWVSvdzm7V4cCsPdFTx6qMj29CwaQmRAMIEhORIUBFBsYROYJcveK4uOjA==} - engines: {node: '>= 6'} - - '@cypress/xvfb@1.2.4': - resolution: {integrity: sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==} - '@esbuild/aix-ppc64@0.19.12': resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} engines: {node: '>=12'} cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} + '@esbuild/aix-ppc64@0.24.2': + resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.24.2': - resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} + '@esbuild/aix-ppc64@0.25.8': + resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -273,14 +265,14 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} + '@esbuild/android-arm64@0.24.2': + resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} + engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.24.2': - resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} + '@esbuild/android-arm64@0.25.8': + resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -291,14 +283,14 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} + '@esbuild/android-arm@0.24.2': + resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} + engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-arm@0.24.2': - resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} + '@esbuild/android-arm@0.25.8': + resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -309,14 +301,14 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} + '@esbuild/android-x64@0.24.2': + resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} + engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/android-x64@0.24.2': - resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} + '@esbuild/android-x64@0.25.8': + resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -327,14 +319,14 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} + '@esbuild/darwin-arm64@0.24.2': + resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.24.2': - resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} + '@esbuild/darwin-arm64@0.25.8': + resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -345,14 +337,14 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} + '@esbuild/darwin-x64@0.24.2': + resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.24.2': - resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} + '@esbuild/darwin-x64@0.25.8': + resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -363,14 +355,14 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} + '@esbuild/freebsd-arm64@0.24.2': + resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.24.2': - resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} + '@esbuild/freebsd-arm64@0.25.8': + resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -381,14 +373,14 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} + '@esbuild/freebsd-x64@0.24.2': + resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.24.2': - resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} + '@esbuild/freebsd-x64@0.25.8': + resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -399,14 +391,14 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} + '@esbuild/linux-arm64@0.24.2': + resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.24.2': - resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} + '@esbuild/linux-arm64@0.25.8': + resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -417,14 +409,14 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} + '@esbuild/linux-arm@0.24.2': + resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} + engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.24.2': - resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} + '@esbuild/linux-arm@0.25.8': + resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -435,14 +427,14 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} + '@esbuild/linux-ia32@0.24.2': + resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.24.2': - resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} + '@esbuild/linux-ia32@0.25.8': + resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -453,14 +445,14 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} + '@esbuild/linux-loong64@0.24.2': + resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.24.2': - resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} + '@esbuild/linux-loong64@0.25.8': + resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -471,14 +463,14 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} + '@esbuild/linux-mips64el@0.24.2': + resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.24.2': - resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} + '@esbuild/linux-mips64el@0.25.8': + resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -489,14 +481,14 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} + '@esbuild/linux-ppc64@0.24.2': + resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.24.2': - resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} + '@esbuild/linux-ppc64@0.25.8': + resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -507,14 +499,14 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} + '@esbuild/linux-riscv64@0.24.2': + resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.24.2': - resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} + '@esbuild/linux-riscv64@0.25.8': + resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -525,14 +517,14 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} + '@esbuild/linux-s390x@0.24.2': + resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.24.2': - resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} + '@esbuild/linux-s390x@0.25.8': + resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -543,14 +535,14 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.24.2': + resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.24.2': - resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} + '@esbuild/linux-x64@0.25.8': + resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==} engines: {node: '>=18'} cpu: [x64] os: [linux] @@ -561,20 +553,26 @@ packages: cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.25.8': + resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.19.12': resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} + '@esbuild/netbsd-x64@0.24.2': + resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.24.2': - resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} + '@esbuild/netbsd-x64@0.25.8': + resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] @@ -585,38 +583,50 @@ packages: cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.25.8': + resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.19.12': resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} + '@esbuild/openbsd-x64@0.24.2': + resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.24.2': - resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} + '@esbuild/openbsd-x64@0.25.8': + resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] + '@esbuild/openharmony-arm64@0.25.8': + resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.19.12': resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} engines: {node: '>=12'} cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} + '@esbuild/sunos-x64@0.24.2': + resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.24.2': - resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} + '@esbuild/sunos-x64@0.25.8': + resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -627,14 +637,14 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} + '@esbuild/win32-arm64@0.24.2': + resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.24.2': - resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} + '@esbuild/win32-arm64@0.25.8': + resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -645,14 +655,14 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} + '@esbuild/win32-ia32@0.24.2': + resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.24.2': - resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} + '@esbuild/win32-ia32@0.25.8': + resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -663,14 +673,14 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} + '@esbuild/win32-x64@0.24.2': + resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} + engines: {node: '>=18'} cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.24.2': - resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} + '@esbuild/win32-x64@0.25.8': + resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -766,6 +776,9 @@ packages: engines: {node: '>=18'} hasBin: true + '@rolldown/pluginutils@1.0.0-beta.29': + resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==} + '@rollup/plugin-alias@5.1.1': resolution: {integrity: sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ==} engines: {node: '>=14.0.0'} @@ -811,6 +824,15 @@ packages: rollup: optional: true + '@rollup/plugin-terser@0.4.4': + resolution: {integrity: sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + '@rollup/plugin-typescript@11.1.6': resolution: {integrity: sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==} engines: {node: '>=14.0.0'} @@ -936,9 +958,6 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/node@14.18.63': - resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==} - '@types/node@20.19.9': resolution: {integrity: sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==} @@ -948,24 +967,12 @@ packages: '@types/prompts@2.4.9': resolution: {integrity: sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==} - '@types/prop-types@15.7.15': - resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} - - '@types/react@18.3.23': - resolution: {integrity: sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==} + '@types/react@19.1.9': + resolution: {integrity: sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==} '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} - '@types/sinonjs__fake-timers@8.1.1': - resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==} - - '@types/sizzle@2.3.9': - resolution: {integrity: sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w==} - - '@types/yauzl@2.10.3': - resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - '@unhead/dom@1.11.20': resolution: {integrity: sha512-jgfGYdOH+xHJF/j8gudjsYu3oIjFyXhCWcgKaw3vQnT616gSqyqnGQGOItL+BQtQZACKNISwIfx5PuOtztMKLA==} @@ -983,11 +990,11 @@ packages: peerDependencies: vue: '>=2.7 || >=3' - '@vitejs/plugin-vue@5.2.4': - resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} - engines: {node: ^18.0.0 || >=20.0.0} + '@vitejs/plugin-vue@6.0.1': + resolution: {integrity: sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==} + engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: - vite: ^5.0.0 || ^6.0.0 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 vue: ^3.2.25 '@vue/compiler-core@3.5.18': @@ -1036,18 +1043,6 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} - aggregate-error@3.1.0: - resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} - engines: {node: '>=8'} - - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1056,30 +1051,9 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - arch@2.2.0: - resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} - - asn1@0.2.6: - resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} - - assert-plus@1.0.0: - resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} - engines: {node: '>=0.8'} - - astral-regex@2.0.0: - resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} - engines: {node: '>=8'} - - async@3.2.6: - resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - at-least-node@1.0.0: - resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} - engines: {node: '>= 4.0.0'} - autoprefixer@10.4.21: resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} engines: {node: ^10 || ^12 || >=14} @@ -1087,27 +1061,9 @@ packages: peerDependencies: postcss: ^8.1.0 - aws-sign2@0.7.0: - resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} - - aws4@1.13.2: - resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - bcrypt-pbkdf@1.0.2: - resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} - - blob-util@2.0.2: - resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==} - - bluebird@3.7.2: - resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} - boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -1130,15 +1086,9 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - c12@3.2.0: resolution: {integrity: sha512-ixkEtbYafL56E6HiFuonMm1ZjoKtIo7TH68/uiEq4DAwv9NcUX2nJ95F8TrbMeNjqIkZpruo3ojXQJ+MGG5gcQ==} peerDependencies: @@ -1147,18 +1097,10 @@ packages: magicast: optional: true - cachedir@2.4.0: - resolution: {integrity: sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==} - engines: {node: '>=6'} - call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - camel-case@4.1.2: resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} @@ -1168,29 +1110,14 @@ packages: caniuse-lite@1.0.30001733: resolution: {integrity: sha512-e4QKw/O2Kavj2VQTKZWrwzkt3IxOmIlU6ajRb6LP64LHpBo1J67k2Hi4Vu/TgJWsNtynurfS0uK3MaUTCPfu5Q==} - caseless@0.12.0: - resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - chalk@5.5.0: resolution: {integrity: sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - check-more-types@2.24.0: - resolution: {integrity: sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==} - engines: {node: '>= 0.8.0'} - chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} - ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - citty@0.1.6: resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} @@ -1198,22 +1125,6 @@ packages: resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} engines: {node: '>= 10.0'} - clean-stack@2.2.0: - resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} - engines: {node: '>=6'} - - cli-cursor@3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} - engines: {node: '>=8'} - - cli-table3@0.6.5: - resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} - engines: {node: 10.* || >= 12.*} - - cli-truncate@2.1.0: - resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} - engines: {node: '>=8'} - cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -1228,9 +1139,6 @@ packages: colord@2.9.3: resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} - colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -1246,14 +1154,6 @@ packages: commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - commander@5.1.0: - resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} - engines: {node: '>= 6'} - - common-tags@1.8.2: - resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} - engines: {node: '>=4.0.0'} - commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} @@ -1273,9 +1173,6 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - core-util-is@1.0.2: - resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1335,30 +1232,10 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - cypress@9.7.0: - resolution: {integrity: sha512-+1EE1nuuuwIt/N1KXRR2iWHU+OiIt7H28jJDyyI4tiUftId/DrXYEwoDa5+kH2pki1zxnA0r6HrUGHV5eLbF5Q==} - engines: {node: '>=12.0.0'} - hasBin: true - - dashdash@1.14.1: - resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} - engines: {node: '>=0.10'} - data-urls@5.0.0: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} - dayjs@1.11.13: - resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} - - debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.4.1: resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} @@ -1416,22 +1293,12 @@ packages: duplexer@0.1.1: resolution: {integrity: sha512-sxNZ+ljy+RA1maXoUReeqBBpBC6RLKmg5ewzV+x+mSETmWNoKdZN6vcQjpFROemza23hGFskJtFNoUWUaQ+R4Q==} - ecc-jsbn@0.1.2: - resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} - electron-to-chromium@1.5.199: resolution: {integrity: sha512-3gl0S7zQd88kCAZRO/DnxtBKuhMO4h0EaQIN3YgZfV6+pW+5+bf2AdQeHNESCoaQqo/gjGVYEf2YM4O5HJQqpQ==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - - enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} - entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -1464,24 +1331,20 @@ packages: engines: {node: '>=12'} hasBin: true - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} - hasBin: true - esbuild@0.24.2: resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} engines: {node: '>=18'} hasBin: true + esbuild@0.25.8: + resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - escape-string-regexp@5.0.0: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} @@ -1492,36 +1355,13 @@ packages: estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - eventemitter2@6.4.9: - resolution: {integrity: sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==} - - execa@4.1.0: - resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} - engines: {node: '>=10'} - execa@6.1.0: resolution: {integrity: sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - executable@4.1.1: - resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==} - engines: {node: '>=4'} - exsolve@1.0.7: resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} - extend@3.0.2: - resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - - extract-zip@2.0.1: - resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} - engines: {node: '>= 10.17.0'} - hasBin: true - - extsprintf@1.3.0: - resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} - engines: {'0': node >=0.6.0} - fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} @@ -1529,9 +1369,6 @@ packages: fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - fd-slicer@1.1.0: - resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} - fdir@6.4.6: resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} peerDependencies: @@ -1540,21 +1377,10 @@ packages: picomatch: optional: true - figures@3.2.0: - resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} - engines: {node: '>=8'} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - forever-agent@0.6.1: - resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} - - form-data@2.3.3: - resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} - engines: {node: '>= 0.12'} - form-data@4.0.4: resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} @@ -1566,10 +1392,6 @@ packages: resolution: {integrity: sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==} engines: {node: '>=14.14'} - fs-extra@9.1.0: - resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} - engines: {node: '>=10'} - fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -1602,20 +1424,10 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-stream@5.2.0: - resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} - engines: {node: '>=8'} - get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - getos@3.2.1: - resolution: {integrity: sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==} - - getpass@0.1.7: - resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} - giget@2.0.0: resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==} hasBin: true @@ -1636,10 +1448,6 @@ packages: engines: {node: '>=12'} deprecated: Glob versions prior to v9 are no longer supported - global-dirs@3.0.1: - resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} - engines: {node: '>=10'} - globby@13.2.2: resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1651,10 +1459,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} @@ -1686,18 +1490,10 @@ packages: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} - http-signature@1.3.6: - resolution: {integrity: sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==} - engines: {node: '>=0.10'} - https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} - human-signals@1.1.1: - resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} - engines: {node: '>=8.12.0'} - human-signals@3.0.1: resolution: {integrity: sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==} engines: {node: '>=12.20.0'} @@ -1706,9 +1502,6 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1717,10 +1510,6 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} - indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -1728,18 +1517,10 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - ini@2.0.0: - resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} - engines: {node: '>=10'} - interpret@1.4.0: resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} engines: {node: '>= 0.10'} - is-ci@3.0.1: - resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} - hasBin: true - is-core-module@2.16.1: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} @@ -1756,10 +1537,6 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-installed-globally@0.4.0: - resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} - engines: {node: '>=10'} - is-module@1.0.0: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} @@ -1767,41 +1544,19 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - is-stream@3.0.0: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - is-typedarray@1.0.0: - resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} - - is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isstream@0.1.2: - resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} - - jest-worker@26.6.2: - resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} - engines: {node: '>= 10.13.0'} - jiti@1.21.7: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true @@ -1816,11 +1571,8 @@ packages: js-tokens@9.0.1: resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} - jsbn@0.1.1: - resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} - - jsdom@24.1.3: - resolution: {integrity: sha512-MyL55p3Ut3cXbeBEG7Hcv0mVM8pp8PBNWxRqchZnSfAiES1v1mRnMeFfaHWIPULpwsYfvO+ZmMZz5tGCnjzDUQ==} + jsdom@25.0.1: + resolution: {integrity: sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==} engines: {node: '>=18'} peerDependencies: canvas: ^2.11.2 @@ -1833,12 +1585,6 @@ packages: engines: {node: '>=6'} hasBin: true - json-schema@0.4.0: - resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} - - json-stringify-safe@5.0.1: - resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -1847,10 +1593,6 @@ packages: jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - jsprim@2.0.2: - resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==} - engines: {'0': node >=0.6.0} - kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} @@ -1865,23 +1607,10 @@ packages: kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} - lazy-ass@1.6.0: - resolution: {integrity: sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==} - engines: {node: '> 0.8'} - lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} - listr2@3.14.0: - resolution: {integrity: sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==} - engines: {node: '>=10.0.0'} - peerDependencies: - enquirer: '>= 2.3.0 < 3' - peerDependenciesMeta: - enquirer: - optional: true - local-pkg@1.1.1: resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==} engines: {node: '>=14'} @@ -1889,27 +1618,9 @@ packages: lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} - lodash.once@4.1.1: - resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} - lodash.uniq@4.5.0: resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - - log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} - - log-update@4.0.0: - resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} - engines: {node: '>=10'} - - loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} @@ -1951,10 +1662,6 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - mimic-fn@4.0.0: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} @@ -2008,10 +1715,6 @@ packages: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - npm-run-path@5.3.0: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2032,31 +1735,16 @@ packages: engines: {node: ^14.16.0 || >=16.10.0} hasBin: true - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - onetime@6.0.0: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} - ospath@1.2.2: - resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==} - - p-map@4.0.0: - resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} - engines: {node: '>=10'} - packrup@0.1.2: resolution: {integrity: sha512-ZcKU7zrr5GlonoS9cxxrb5HVswGnyj6jQvwFBa6p5VFw7G71VAHcUKL5wyZSU/ECtPM/9gacWxy2KFQKt1gMNA==} @@ -2094,15 +1782,9 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pend@1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} - perfect-debounce@1.0.0: resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} - performance-now@2.1.0: - resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2114,9 +1796,9 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} - pify@2.3.0: - resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} - engines: {node: '>=0.10.0'} + pixelmatch@5.3.0: + resolution: {integrity: sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==} + hasBin: true pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} @@ -2134,6 +1816,14 @@ packages: engines: {node: '>=18'} hasBin: true + pngjs@6.0.0: + resolution: {integrity: sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==} + engines: {node: '>=12.13.0'} + + pngjs@7.0.0: + resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} + engines: {node: '>=14.19.0'} + postcss-calc@10.1.1: resolution: {integrity: sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==} engines: {node: ^18.12 || ^20.9 || >=22.0} @@ -2325,10 +2015,6 @@ packages: engines: {node: '>=14'} hasBin: true - pretty-bytes@5.6.0: - resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} - engines: {node: '>=6'} - pretty-bytes@6.1.1: resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} engines: {node: ^14.13.1 || >=16.0.0} @@ -2337,29 +2023,13 @@ packages: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} - proxy-from-env@1.0.0: - resolution: {integrity: sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==} - - psl@1.15.0: - resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} - - pump@3.0.3: - resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - qs@6.10.4: - resolution: {integrity: sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==} - engines: {node: '>=0.6'} - quansync@0.2.10: resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} - querystringify@2.2.0: - resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} - queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -2369,8 +2039,8 @@ packages: rc9@2.1.2: resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} - react@18.3.1: - resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + react@19.1.1: + resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==} engines: {node: '>=0.10.0'} readdirp@4.1.2: @@ -2385,32 +2055,19 @@ packages: resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} engines: {node: '>= 0.10'} - request-progress@3.0.0: - resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==} - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - requires-port@1.0.0: - resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - resolve@1.22.10: resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} engines: {node: '>= 0.4'} hasBin: true - restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} - reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rollup-plugin-dts@6.2.1: resolution: {integrity: sha512-sR3CxYUl7i2CHa0O7bA45mCrgADyAQ0tVtGSqi3yvH28M+eg1+g5d7kQ9hLvEz5dorK3XVsH5L2jwHLQf72DzA==} engines: {node: '>=16'} @@ -2418,12 +2075,6 @@ packages: rollup: ^3.29.4 || ^4 typescript: ^4.5 || ^5.0 - rollup-plugin-terser@7.0.2: - resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} - deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser - peerDependencies: - rollup: ^2.0.0 - rollup@3.29.5: resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} @@ -2471,8 +2122,8 @@ packages: engines: {node: '>=10'} hasBin: true - serialize-javascript@4.0.0: - resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==} + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} seroval-plugins@1.3.2: resolution: {integrity: sha512-0QvCV2lM3aj/U3YozDiVwx9zpH0q8A60CTWIv4Jszj/givcudPb48B+rkU5D51NJ0pTpweGMttHjboPa9/zoIQ==} @@ -2502,22 +2153,6 @@ packages: engines: {node: '>=6'} hasBin: true - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -2528,13 +2163,8 @@ packages: resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} engines: {node: '>=12'} - slice-ansi@3.0.0: - resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} - engines: {node: '>=8'} - - slice-ansi@4.0.0: - resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} - engines: {node: '>=10'} + smob@1.5.0: + resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==} solid-js@1.9.8: resolution: {integrity: sha512-zF9Whfqk+s8wWuyDKnE7ekl+dJburjdZq54O6X1k4XChA57uZ5FOauYAa0s4I44XkBOM3CZmPrZC0DGjH9fKjQ==} @@ -2550,11 +2180,6 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - sshpk@1.18.0: - resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==} - engines: {node: '>=0.10.0'} - hasBin: true - std-env@3.9.0: resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} @@ -2566,10 +2191,6 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - strip-final-newline@3.0.0: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} @@ -2583,14 +2204,6 @@ packages: peerDependencies: postcss: ^8.4.32 - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -2608,12 +2221,6 @@ packages: engines: {node: '>=10'} hasBin: true - throttleit@1.0.1: - resolution: {integrity: sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==} - - through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - tinyexec@1.0.1: resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} @@ -2621,17 +2228,20 @@ packages: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} - tmp@0.2.4: - resolution: {integrity: sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==} - engines: {node: '>=14.14'} + tldts-core@6.1.86: + resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} + + tldts@6.1.86: + resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} + hasBin: true to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - tough-cookie@4.1.4: - resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} - engines: {node: '>=6'} + tough-cookie@5.1.2: + resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} + engines: {node: '>=16'} tr46@5.1.1: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} @@ -2640,16 +2250,6 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tunnel-agent@0.6.0: - resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} - - tweetnacl@0.14.5: - resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} - - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - typescript@5.9.2: resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} engines: {node: '>=14.17'} @@ -2680,10 +2280,6 @@ packages: resolution: {integrity: sha512-bTuAMMOOqIAyjV4i4UH7P07pO+EsVxmhOzQ2YJ290J6mkLUdozNhb5I/YoOEheeNADC03ent3Qj07X0fWfUpmw==} engines: {node: '>=18.12.0'} - universalify@0.2.0: - resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} - engines: {node: '>= 4.0.0'} - universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -2696,10 +2292,6 @@ packages: resolution: {integrity: sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==} engines: {node: '>=18.12.0'} - untildify@4.0.0: - resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} - engines: {node: '>=8'} - untyped@1.5.2: resolution: {integrity: sha512-eL/8PlhLcMmlMDtNPKhyyz9kEBDS3Uk4yMu/ewlkT2WFbtzScjHWPJLdQLmaGPUKjXzwe9MumOtOgc4Fro96Kg==} hasBin: true @@ -2714,51 +2306,48 @@ packages: peerDependencies: browserslist: '>= 4.21.0' - url-parse@1.5.10: - resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} - util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - hasBin: true - - verror@1.10.0: - resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} - engines: {'0': node >=0.6.0} - - vite-ssg@0.23.8: - resolution: {integrity: sha512-uWjdxL2PrvmbUxj7K+YFR8hTuhUZ90r2PrP73evsN/XarjQzKvIbbopqczyGUSAdRXggN3C4sdnk4jqDOGbF4A==} + vite-ssg@0.24.3: + resolution: {integrity: sha512-LEoJGJD495Hol3vfPJpugee5GkBnhy5Zh67YO7ITxZrSrxqKDqIsE/l5Rtuu5dXSlXKdu+q1yk4xc+vymfzKSQ==} engines: {node: '>=14.0.0'} hasBin: true peerDependencies: + beasties: ^0.2.0 critters: ^0.0.24 - vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 + vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0 vue: ^3.2.10 vue-router: ^4.0.1 peerDependenciesMeta: + beasties: + optional: true critters: optional: true vue-router: optional: true - vite@5.4.1: - resolution: {integrity: sha512-1oE6yuNXssjrZdblI9AfBbHCC41nnyoVoEZxQnID6yvQZAFBzxxkqoFLtHUMkYunL8hwOLEjgTuxpkRxvba3kA==} - engines: {node: ^18.0.0 || >=20.0.0} + vite@7.1.1: + resolution: {integrity: sha512-yJ+Mp7OyV+4S+afWo+QyoL9jFWD11QFH0i5i7JypnfTcA1rmgxCbiA8WwAICDEtZ1Z1hzrVhN8R8rGTqkTY8ZQ==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true + jiti: + optional: true less: optional: true lightningcss: @@ -2773,6 +2362,10 @@ packages: optional: true terser: optional: true + tsx: + optional: true + yaml: + optional: true vue-github-button@3.1.3: resolution: {integrity: sha512-ZdOnUuYJL/wUsxISsC96TARzCdf1twaWooZoI14+g4RHsJltPY+Agw6Ox6pjuMqMX0uhSK1JOMFUFNCQGlcZGA==} @@ -2818,10 +2411,6 @@ packages: engines: {node: '>= 8'} hasBin: true - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -2863,9 +2452,6 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} - yauzl@2.10.0: - resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} - zhead@2.2.4: resolution: {integrity: sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag==} @@ -2914,7 +2500,7 @@ snapshots: '@babel/traverse': 7.28.0 '@babel/types': 7.28.2 convert-source-map: 2.0.0 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -2986,7 +2572,7 @@ snapshots: '@babel/parser': 7.28.0 '@babel/template': 7.27.2 '@babel/types': 7.28.2 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 transitivePeerDependencies: - supports-color @@ -2995,9 +2581,6 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@colors/colors@1.5.0': - optional: true - '@csstools/color-helpers@5.0.2': {} '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': @@ -3018,247 +2601,228 @@ snapshots: '@csstools/css-tokenizer@3.0.4': {} - '@cypress/request@2.88.12': - dependencies: - aws-sign2: 0.7.0 - aws4: 1.13.2 - caseless: 0.12.0 - combined-stream: 1.0.8 - extend: 3.0.2 - forever-agent: 0.6.1 - form-data: 2.3.3 - http-signature: 1.3.6 - is-typedarray: 1.0.0 - isstream: 0.1.2 - json-stringify-safe: 5.0.1 - mime-types: 2.1.35 - performance-now: 2.1.0 - qs: 6.10.4 - safe-buffer: 5.2.1 - tough-cookie: 4.1.4 - tunnel-agent: 0.6.0 - uuid: 8.3.2 - - '@cypress/xvfb@1.2.4(supports-color@8.1.1)': - dependencies: - debug: 3.2.7(supports-color@8.1.1) - lodash.once: 4.1.1 - transitivePeerDependencies: - - supports-color - '@esbuild/aix-ppc64@0.19.12': optional: true - '@esbuild/aix-ppc64@0.21.5': + '@esbuild/aix-ppc64@0.24.2': optional: true - '@esbuild/aix-ppc64@0.24.2': + '@esbuild/aix-ppc64@0.25.8': optional: true '@esbuild/android-arm64@0.19.12': optional: true - '@esbuild/android-arm64@0.21.5': + '@esbuild/android-arm64@0.24.2': optional: true - '@esbuild/android-arm64@0.24.2': + '@esbuild/android-arm64@0.25.8': optional: true '@esbuild/android-arm@0.19.12': optional: true - '@esbuild/android-arm@0.21.5': + '@esbuild/android-arm@0.24.2': optional: true - '@esbuild/android-arm@0.24.2': + '@esbuild/android-arm@0.25.8': optional: true '@esbuild/android-x64@0.19.12': optional: true - '@esbuild/android-x64@0.21.5': + '@esbuild/android-x64@0.24.2': optional: true - '@esbuild/android-x64@0.24.2': + '@esbuild/android-x64@0.25.8': optional: true '@esbuild/darwin-arm64@0.19.12': optional: true - '@esbuild/darwin-arm64@0.21.5': + '@esbuild/darwin-arm64@0.24.2': optional: true - '@esbuild/darwin-arm64@0.24.2': + '@esbuild/darwin-arm64@0.25.8': optional: true '@esbuild/darwin-x64@0.19.12': optional: true - '@esbuild/darwin-x64@0.21.5': + '@esbuild/darwin-x64@0.24.2': optional: true - '@esbuild/darwin-x64@0.24.2': + '@esbuild/darwin-x64@0.25.8': optional: true '@esbuild/freebsd-arm64@0.19.12': optional: true - '@esbuild/freebsd-arm64@0.21.5': + '@esbuild/freebsd-arm64@0.24.2': optional: true - '@esbuild/freebsd-arm64@0.24.2': + '@esbuild/freebsd-arm64@0.25.8': optional: true '@esbuild/freebsd-x64@0.19.12': optional: true - '@esbuild/freebsd-x64@0.21.5': + '@esbuild/freebsd-x64@0.24.2': optional: true - '@esbuild/freebsd-x64@0.24.2': + '@esbuild/freebsd-x64@0.25.8': optional: true '@esbuild/linux-arm64@0.19.12': optional: true - '@esbuild/linux-arm64@0.21.5': + '@esbuild/linux-arm64@0.24.2': optional: true - '@esbuild/linux-arm64@0.24.2': + '@esbuild/linux-arm64@0.25.8': optional: true '@esbuild/linux-arm@0.19.12': optional: true - '@esbuild/linux-arm@0.21.5': + '@esbuild/linux-arm@0.24.2': optional: true - '@esbuild/linux-arm@0.24.2': + '@esbuild/linux-arm@0.25.8': optional: true '@esbuild/linux-ia32@0.19.12': optional: true - '@esbuild/linux-ia32@0.21.5': + '@esbuild/linux-ia32@0.24.2': optional: true - '@esbuild/linux-ia32@0.24.2': + '@esbuild/linux-ia32@0.25.8': optional: true '@esbuild/linux-loong64@0.19.12': optional: true - '@esbuild/linux-loong64@0.21.5': + '@esbuild/linux-loong64@0.24.2': optional: true - '@esbuild/linux-loong64@0.24.2': + '@esbuild/linux-loong64@0.25.8': optional: true '@esbuild/linux-mips64el@0.19.12': optional: true - '@esbuild/linux-mips64el@0.21.5': + '@esbuild/linux-mips64el@0.24.2': optional: true - '@esbuild/linux-mips64el@0.24.2': + '@esbuild/linux-mips64el@0.25.8': optional: true '@esbuild/linux-ppc64@0.19.12': optional: true - '@esbuild/linux-ppc64@0.21.5': + '@esbuild/linux-ppc64@0.24.2': optional: true - '@esbuild/linux-ppc64@0.24.2': + '@esbuild/linux-ppc64@0.25.8': optional: true '@esbuild/linux-riscv64@0.19.12': optional: true - '@esbuild/linux-riscv64@0.21.5': + '@esbuild/linux-riscv64@0.24.2': optional: true - '@esbuild/linux-riscv64@0.24.2': + '@esbuild/linux-riscv64@0.25.8': optional: true '@esbuild/linux-s390x@0.19.12': optional: true - '@esbuild/linux-s390x@0.21.5': + '@esbuild/linux-s390x@0.24.2': optional: true - '@esbuild/linux-s390x@0.24.2': + '@esbuild/linux-s390x@0.25.8': optional: true '@esbuild/linux-x64@0.19.12': optional: true - '@esbuild/linux-x64@0.21.5': + '@esbuild/linux-x64@0.24.2': optional: true - '@esbuild/linux-x64@0.24.2': + '@esbuild/linux-x64@0.25.8': optional: true '@esbuild/netbsd-arm64@0.24.2': optional: true - '@esbuild/netbsd-x64@0.19.12': + '@esbuild/netbsd-arm64@0.25.8': optional: true - '@esbuild/netbsd-x64@0.21.5': + '@esbuild/netbsd-x64@0.19.12': optional: true '@esbuild/netbsd-x64@0.24.2': optional: true + '@esbuild/netbsd-x64@0.25.8': + optional: true + '@esbuild/openbsd-arm64@0.24.2': optional: true - '@esbuild/openbsd-x64@0.19.12': + '@esbuild/openbsd-arm64@0.25.8': optional: true - '@esbuild/openbsd-x64@0.21.5': + '@esbuild/openbsd-x64@0.19.12': optional: true '@esbuild/openbsd-x64@0.24.2': optional: true - '@esbuild/sunos-x64@0.19.12': + '@esbuild/openbsd-x64@0.25.8': + optional: true + + '@esbuild/openharmony-arm64@0.25.8': optional: true - '@esbuild/sunos-x64@0.21.5': + '@esbuild/sunos-x64@0.19.12': optional: true '@esbuild/sunos-x64@0.24.2': optional: true - '@esbuild/win32-arm64@0.19.12': + '@esbuild/sunos-x64@0.25.8': optional: true - '@esbuild/win32-arm64@0.21.5': + '@esbuild/win32-arm64@0.19.12': optional: true '@esbuild/win32-arm64@0.24.2': optional: true - '@esbuild/win32-ia32@0.19.12': + '@esbuild/win32-arm64@0.25.8': optional: true - '@esbuild/win32-ia32@0.21.5': + '@esbuild/win32-ia32@0.19.12': optional: true '@esbuild/win32-ia32@0.24.2': optional: true - '@esbuild/win32-x64@0.19.12': + '@esbuild/win32-ia32@0.25.8': optional: true - '@esbuild/win32-x64@0.21.5': + '@esbuild/win32-x64@0.19.12': optional: true '@esbuild/win32-x64@0.24.2': optional: true + '@esbuild/win32-x64@0.25.8': + optional: true + '@formkit/core@1.6.9': dependencies: '@formkit/utils': 1.6.9 @@ -3405,6 +2969,8 @@ snapshots: dependencies: playwright: 1.54.2 + '@rolldown/pluginutils@1.0.0-beta.29': {} + '@rollup/plugin-alias@5.1.1(rollup@3.29.5)': optionalDependencies: rollup: 3.29.5 @@ -3443,13 +3009,21 @@ snapshots: optionalDependencies: rollup: 3.29.5 - '@rollup/plugin-typescript@11.1.6(rollup@3.29.5)(tslib@2.8.1)(typescript@5.9.2)': + '@rollup/plugin-terser@0.4.4(rollup@4.46.2)': dependencies: - '@rollup/pluginutils': 5.2.0(rollup@3.29.5) + serialize-javascript: 6.0.2 + smob: 1.5.0 + terser: 5.43.1 + optionalDependencies: + rollup: 4.46.2 + + '@rollup/plugin-typescript@11.1.6(rollup@4.46.2)(tslib@2.8.1)(typescript@5.9.2)': + dependencies: + '@rollup/pluginutils': 5.2.0(rollup@4.46.2) resolve: 1.22.10 typescript: 5.9.2 optionalDependencies: - rollup: 3.29.5 + rollup: 4.46.2 tslib: 2.8.1 '@rollup/pluginutils@5.2.0(rollup@3.29.5)': @@ -3460,6 +3034,14 @@ snapshots: optionalDependencies: rollup: 3.29.5 + '@rollup/pluginutils@5.2.0(rollup@4.46.2)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.46.2 + '@rollup/rollup-android-arm-eabi@4.46.2': optional: true @@ -3522,8 +3104,6 @@ snapshots: '@types/estree@1.0.8': {} - '@types/node@14.18.63': {} - '@types/node@20.19.9': dependencies: undici-types: 6.21.0 @@ -3535,24 +3115,12 @@ snapshots: '@types/node': 20.19.9 kleur: 3.0.3 - '@types/prop-types@15.7.15': {} - - '@types/react@18.3.23': + '@types/react@19.1.9': dependencies: - '@types/prop-types': 15.7.15 csstype: 3.1.3 '@types/resolve@1.20.2': {} - '@types/sinonjs__fake-timers@8.1.1': {} - - '@types/sizzle@2.3.9': {} - - '@types/yauzl@2.10.3': - dependencies: - '@types/node': 20.19.9 - optional: true - '@unhead/dom@1.11.20': dependencies: '@unhead/schema': 1.11.20 @@ -3581,9 +3149,10 @@ snapshots: unhead: 1.11.20 vue: 3.5.18(typescript@5.9.2) - '@vitejs/plugin-vue@5.2.4(vite@5.4.1(@types/node@20.19.9)(terser@5.43.1))(vue@3.5.18(typescript@5.9.2))': + '@vitejs/plugin-vue@6.0.1(vite@7.1.1(@types/node@20.19.9)(jiti@1.21.7)(terser@5.43.1))(vue@3.5.18(typescript@5.9.2))': dependencies: - vite: 5.4.1(@types/node@20.19.9)(terser@5.43.1) + '@rolldown/pluginutils': 1.0.0-beta.29 + vite: 7.1.1(@types/node@20.19.9)(jiti@1.21.7)(terser@5.43.1) vue: 3.5.18(typescript@5.9.2) '@vue/compiler-core@3.5.18': @@ -3654,39 +3223,14 @@ snapshots: agent-base@7.1.4: {} - aggregate-error@3.1.0: - dependencies: - clean-stack: 2.2.0 - indent-string: 4.0.0 - - ansi-colors@4.1.3: {} - - ansi-escapes@4.3.2: - dependencies: - type-fest: 0.21.3 - ansi-regex@5.0.1: {} ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - arch@2.2.0: {} - - asn1@0.2.6: - dependencies: - safer-buffer: 2.1.2 - - assert-plus@1.0.0: {} - - astral-regex@2.0.0: {} - - async@3.2.6: {} - asynckit@0.4.0: {} - at-least-node@1.0.0: {} - autoprefixer@10.4.21(postcss@8.5.6): dependencies: browserslist: 4.25.1 @@ -3697,22 +3241,8 @@ snapshots: postcss: 8.5.6 postcss-value-parser: 4.2.0 - aws-sign2@0.7.0: {} - - aws4@1.13.2: {} - balanced-match@1.0.2: {} - base64-js@1.5.1: {} - - bcrypt-pbkdf@1.0.2: - dependencies: - tweetnacl: 0.14.5 - - blob-util@2.0.2: {} - - bluebird@3.7.2: {} - boolbase@1.0.0: {} brace-expansion@1.1.12: @@ -3739,15 +3269,8 @@ snapshots: node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.25.1) - buffer-crc32@0.2.13: {} - buffer-from@1.1.2: {} - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - c12@3.2.0: dependencies: chokidar: 4.0.3 @@ -3763,18 +3286,11 @@ snapshots: pkg-types: 2.2.0 rc9: 2.1.2 - cachedir@2.4.0: {} - call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - camel-case@4.1.2: dependencies: pascal-case: 3.1.2 @@ -3789,23 +3305,12 @@ snapshots: caniuse-lite@1.0.30001733: {} - caseless@0.12.0: {} - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - chalk@5.5.0: {} - check-more-types@2.24.0: {} - chokidar@4.0.3: dependencies: readdirp: 4.1.2 - ci-info@3.9.0: {} - citty@0.1.6: dependencies: consola: 3.4.2 @@ -3814,23 +3319,6 @@ snapshots: dependencies: source-map: 0.6.1 - clean-stack@2.2.0: {} - - cli-cursor@3.1.0: - dependencies: - restore-cursor: 3.1.0 - - cli-table3@0.6.5: - dependencies: - string-width: 4.2.3 - optionalDependencies: - '@colors/colors': 1.5.0 - - cli-truncate@2.1.0: - dependencies: - slice-ansi: 3.0.0 - string-width: 4.2.3 - cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -3845,8 +3333,6 @@ snapshots: colord@2.9.3: {} - colorette@2.0.20: {} - combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 @@ -3857,10 +3343,6 @@ snapshots: commander@2.20.3: {} - commander@5.1.0: {} - - common-tags@1.8.2: {} - commondir@1.0.1: {} concat-map@0.0.1: {} @@ -3873,8 +3355,6 @@ snapshots: convert-source-map@2.0.0: {} - core-util-is@1.0.2: {} - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -3962,73 +3442,14 @@ snapshots: csstype@3.1.3: {} - cypress@9.7.0: - dependencies: - '@cypress/request': 2.88.12 - '@cypress/xvfb': 1.2.4(supports-color@8.1.1) - '@types/node': 14.18.63 - '@types/sinonjs__fake-timers': 8.1.1 - '@types/sizzle': 2.3.9 - arch: 2.2.0 - blob-util: 2.0.2 - bluebird: 3.7.2 - buffer: 5.7.1 - cachedir: 2.4.0 - chalk: 4.1.2 - check-more-types: 2.24.0 - cli-cursor: 3.1.0 - cli-table3: 0.6.5 - commander: 5.1.0 - common-tags: 1.8.2 - dayjs: 1.11.13 - debug: 4.4.1(supports-color@8.1.1) - enquirer: 2.4.1 - eventemitter2: 6.4.9 - execa: 4.1.0 - executable: 4.1.1 - extract-zip: 2.0.1(supports-color@8.1.1) - figures: 3.2.0 - fs-extra: 9.1.0 - getos: 3.2.1 - is-ci: 3.0.1 - is-installed-globally: 0.4.0 - lazy-ass: 1.6.0 - listr2: 3.14.0(enquirer@2.4.1) - lodash: 4.17.21 - log-symbols: 4.1.0 - minimist: 1.2.8 - ospath: 1.2.2 - pretty-bytes: 5.6.0 - proxy-from-env: 1.0.0 - request-progress: 3.0.0 - semver: 7.7.2 - supports-color: 8.1.1 - tmp: 0.2.4 - untildify: 4.0.0 - yauzl: 2.10.0 - - dashdash@1.14.1: - dependencies: - assert-plus: 1.0.0 - data-urls@5.0.0: dependencies: whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 - dayjs@1.11.13: {} - - debug@3.2.7(supports-color@8.1.1): - dependencies: - ms: 2.1.3 - optionalDependencies: - supports-color: 8.1.1 - - debug@4.4.1(supports-color@8.1.1): + debug@4.4.1: dependencies: ms: 2.1.3 - optionalDependencies: - supports-color: 8.1.1 decimal.js@10.6.0: {} @@ -4077,24 +3498,10 @@ snapshots: duplexer@0.1.1: {} - ecc-jsbn@0.1.2: - dependencies: - jsbn: 0.1.1 - safer-buffer: 2.1.2 - electron-to-chromium@1.5.199: {} emoji-regex@8.0.0: {} - end-of-stream@1.4.5: - dependencies: - once: 1.4.0 - - enquirer@2.4.1: - dependencies: - ansi-colors: 4.1.3 - strip-ansi: 6.0.1 - entities@4.5.0: {} entities@6.0.1: {} @@ -4142,32 +3549,6 @@ snapshots: '@esbuild/win32-ia32': 0.19.12 '@esbuild/win32-x64': 0.19.12 - esbuild@0.21.5: - optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 - esbuild@0.24.2: optionalDependencies: '@esbuild/aix-ppc64': 0.24.2 @@ -4196,9 +3577,36 @@ snapshots: '@esbuild/win32-ia32': 0.24.2 '@esbuild/win32-x64': 0.24.2 - escalade@3.2.0: {} + esbuild@0.25.8: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.8 + '@esbuild/android-arm': 0.25.8 + '@esbuild/android-arm64': 0.25.8 + '@esbuild/android-x64': 0.25.8 + '@esbuild/darwin-arm64': 0.25.8 + '@esbuild/darwin-x64': 0.25.8 + '@esbuild/freebsd-arm64': 0.25.8 + '@esbuild/freebsd-x64': 0.25.8 + '@esbuild/linux-arm': 0.25.8 + '@esbuild/linux-arm64': 0.25.8 + '@esbuild/linux-ia32': 0.25.8 + '@esbuild/linux-loong64': 0.25.8 + '@esbuild/linux-mips64el': 0.25.8 + '@esbuild/linux-ppc64': 0.25.8 + '@esbuild/linux-riscv64': 0.25.8 + '@esbuild/linux-s390x': 0.25.8 + '@esbuild/linux-x64': 0.25.8 + '@esbuild/netbsd-arm64': 0.25.8 + '@esbuild/netbsd-x64': 0.25.8 + '@esbuild/openbsd-arm64': 0.25.8 + '@esbuild/openbsd-x64': 0.25.8 + '@esbuild/openharmony-arm64': 0.25.8 + '@esbuild/sunos-x64': 0.25.8 + '@esbuild/win32-arm64': 0.25.8 + '@esbuild/win32-ia32': 0.25.8 + '@esbuild/win32-x64': 0.25.8 - escape-string-regexp@1.0.5: {} + escalade@3.2.0: {} escape-string-regexp@5.0.0: {} @@ -4208,20 +3616,6 @@ snapshots: dependencies: '@types/estree': 1.0.8 - eventemitter2@6.4.9: {} - - execa@4.1.0: - dependencies: - cross-spawn: 7.0.6 - get-stream: 5.2.0 - human-signals: 1.1.1 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - execa@6.1.0: dependencies: cross-spawn: 7.0.6 @@ -4234,26 +3628,8 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 3.0.0 - executable@4.1.1: - dependencies: - pify: 2.3.0 - exsolve@1.0.7: {} - extend@3.0.2: {} - - extract-zip@2.0.1(supports-color@8.1.1): - dependencies: - debug: 4.4.1(supports-color@8.1.1) - get-stream: 5.2.0 - yauzl: 2.10.0 - optionalDependencies: - '@types/yauzl': 2.10.3 - transitivePeerDependencies: - - supports-color - - extsprintf@1.3.0: {} - fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -4266,30 +3642,14 @@ snapshots: dependencies: reusify: 1.1.0 - fd-slicer@1.1.0: - dependencies: - pend: 1.2.0 - fdir@6.4.6(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 - figures@3.2.0: - dependencies: - escape-string-regexp: 1.0.5 - fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - forever-agent@0.6.1: {} - - form-data@2.3.3: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - form-data@4.0.4: dependencies: asynckit: 0.4.0 @@ -4306,13 +3666,6 @@ snapshots: jsonfile: 6.1.0 universalify: 2.0.1 - fs-extra@9.1.0: - dependencies: - at-least-node: 1.0.0 - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.1 - fs.realpath@1.0.0: {} fsevents@2.3.2: @@ -4345,20 +3698,8 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - get-stream@5.2.0: - dependencies: - pump: 3.0.3 - get-stream@6.0.1: {} - getos@3.2.1: - dependencies: - async: 3.2.6 - - getpass@0.1.7: - dependencies: - assert-plus: 1.0.0 - giget@2.0.0: dependencies: citty: 0.1.6 @@ -4391,10 +3732,6 @@ snapshots: minimatch: 5.1.6 once: 1.4.0 - global-dirs@3.0.1: - dependencies: - ini: 2.0.0 - globby@13.2.2: dependencies: dir-glob: 3.0.1 @@ -4407,8 +3744,6 @@ snapshots: graceful-fs@4.2.11: {} - has-flag@4.0.0: {} - has-symbols@1.1.0: {} has-tostringtag@1.0.2: @@ -4442,39 +3777,27 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 transitivePeerDependencies: - supports-color - http-signature@1.3.6: - dependencies: - assert-plus: 1.0.0 - jsprim: 2.0.2 - sshpk: 1.18.0 - https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 transitivePeerDependencies: - supports-color - human-signals@1.1.1: {} - human-signals@3.0.1: {} iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 - ieee754@1.2.1: {} - ignore@5.3.2: {} ignore@7.0.5: {} - indent-string@4.0.0: {} - inflight@1.0.6: dependencies: once: 1.4.0 @@ -4482,14 +3805,8 @@ snapshots: inherits@2.0.4: {} - ini@2.0.0: {} - interpret@1.4.0: {} - is-ci@3.0.1: - dependencies: - ci-info: 3.9.0 - is-core-module@2.16.1: dependencies: hasown: 2.0.2 @@ -4502,41 +3819,20 @@ snapshots: dependencies: is-extglob: 2.1.1 - is-installed-globally@0.4.0: - dependencies: - global-dirs: 3.0.1 - is-path-inside: 3.0.3 - is-module@1.0.0: {} is-number@7.0.0: {} - is-path-inside@3.0.3: {} - is-potential-custom-element-name@1.0.1: {} is-reference@1.2.1: dependencies: '@types/estree': 1.0.8 - is-stream@2.0.1: {} - is-stream@3.0.0: {} - is-typedarray@1.0.0: {} - - is-unicode-supported@0.1.0: {} - isexe@2.0.0: {} - isstream@0.1.2: {} - - jest-worker@26.6.2: - dependencies: - '@types/node': 20.19.9 - merge-stream: 2.0.0 - supports-color: 7.2.0 - jiti@1.21.7: {} jiti@2.5.1: {} @@ -4545,9 +3841,7 @@ snapshots: js-tokens@9.0.1: {} - jsbn@0.1.1: {} - - jsdom@24.1.3: + jsdom@25.0.1: dependencies: cssstyle: 4.6.0 data-urls: 5.0.0 @@ -4562,7 +3856,7 @@ snapshots: rrweb-cssom: 0.7.1 saxes: 6.0.0 symbol-tree: 3.2.4 - tough-cookie: 4.1.4 + tough-cookie: 5.1.2 w3c-xmlserializer: 5.0.0 webidl-conversions: 7.0.0 whatwg-encoding: 3.1.1 @@ -4577,10 +3871,6 @@ snapshots: jsesc@3.1.0: {} - json-schema@0.4.0: {} - - json-stringify-safe@5.0.1: {} - json5@2.2.3: {} jsonfile@6.1.0: @@ -4589,13 +3879,6 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 - jsprim@2.0.2: - dependencies: - assert-plus: 1.0.0 - extsprintf: 1.3.0 - json-schema: 0.4.0 - verror: 1.10.0 - kleur@3.0.3: {} klona@2.0.6: {} @@ -4604,23 +3887,8 @@ snapshots: kolorist@1.8.0: {} - lazy-ass@1.6.0: {} - lilconfig@3.1.3: {} - listr2@3.14.0(enquirer@2.4.1): - dependencies: - cli-truncate: 2.1.0 - colorette: 2.0.20 - log-update: 4.0.0 - p-map: 4.0.0 - rfdc: 1.4.1 - rxjs: 7.8.2 - through: 2.3.8 - wrap-ansi: 7.0.0 - optionalDependencies: - enquirer: 2.4.1 - local-pkg@1.1.1: dependencies: mlly: 1.7.4 @@ -4629,28 +3897,8 @@ snapshots: lodash.memoize@4.1.2: {} - lodash.once@4.1.1: {} - lodash.uniq@4.5.0: {} - lodash@4.17.21: {} - - log-symbols@4.1.0: - dependencies: - chalk: 4.1.2 - is-unicode-supported: 0.1.0 - - log-update@4.0.0: - dependencies: - ansi-escapes: 4.3.2 - cli-cursor: 3.1.0 - slice-ansi: 4.0.0 - wrap-ansi: 6.2.0 - - loose-envify@1.4.0: - dependencies: - js-tokens: 4.0.0 - lower-case@2.0.2: dependencies: tslib: 2.8.1 @@ -4686,8 +3934,6 @@ snapshots: dependencies: mime-db: 1.52.0 - mimic-fn@2.1.0: {} - mimic-fn@4.0.0: {} minimatch@3.1.2: @@ -4740,10 +3986,6 @@ snapshots: normalize-range@0.1.2: {} - npm-run-path@4.0.1: - dependencies: - path-key: 3.1.1 - npm-run-path@5.3.0: dependencies: path-key: 4.0.0 @@ -4764,28 +4006,16 @@ snapshots: pkg-types: 2.2.0 tinyexec: 1.0.1 - object-inspect@1.13.4: {} - ohash@2.0.11: {} once@1.4.0: dependencies: wrappy: 1.0.2 - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - onetime@6.0.0: dependencies: mimic-fn: 4.0.0 - ospath@1.2.2: {} - - p-map@4.0.0: - dependencies: - aggregate-error: 3.1.0 - packrup@0.1.2: {} param-case@3.0.4: @@ -4816,19 +4046,17 @@ snapshots: pathe@2.0.3: {} - pend@1.2.0: {} - perfect-debounce@1.0.0: {} - performance-now@2.1.0: {} - picocolors@1.1.1: {} picomatch@2.3.1: {} picomatch@4.0.3: {} - pify@2.3.0: {} + pixelmatch@5.3.0: + dependencies: + pngjs: 6.0.0 pkg-types@1.3.1: dependencies: @@ -4850,6 +4078,10 @@ snapshots: optionalDependencies: fsevents: 2.3.2 + pngjs@6.0.0: {} + + pngjs@7.0.0: {} + postcss-calc@10.1.1(postcss@8.5.6): dependencies: postcss: 8.5.6 @@ -5026,8 +4258,6 @@ snapshots: prettier@3.6.2: {} - pretty-bytes@5.6.0: {} - pretty-bytes@6.1.1: {} prompts@2.4.2: @@ -5035,27 +4265,10 @@ snapshots: kleur: 3.0.3 sisteransi: 1.0.5 - proxy-from-env@1.0.0: {} - - psl@1.15.0: - dependencies: - punycode: 2.3.1 - - pump@3.0.3: - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - punycode@2.3.1: {} - qs@6.10.4: - dependencies: - side-channel: 1.1.0 - quansync@0.2.10: {} - querystringify@2.2.0: {} - queue-microtask@1.2.3: {} randombytes@2.1.0: @@ -5067,9 +4280,7 @@ snapshots: defu: 6.1.4 destr: 2.0.5 - react@18.3.1: - dependencies: - loose-envify: 1.4.0 + react@19.1.1: {} readdirp@4.1.2: {} @@ -5079,29 +4290,16 @@ snapshots: relateurl@0.2.7: {} - request-progress@3.0.0: - dependencies: - throttleit: 1.0.1 - require-directory@2.1.1: {} - requires-port@1.0.0: {} - resolve@1.22.10: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - restore-cursor@3.1.0: - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - reusify@1.1.0: {} - rfdc@1.4.1: {} - rollup-plugin-dts@6.2.1(rollup@3.29.5)(typescript@5.9.2): dependencies: magic-string: 0.30.17 @@ -5110,14 +4308,6 @@ snapshots: optionalDependencies: '@babel/code-frame': 7.27.1 - rollup-plugin-terser@7.0.2(rollup@3.29.5): - dependencies: - '@babel/code-frame': 7.27.1 - jest-worker: 26.6.2 - rollup: 3.29.5 - serialize-javascript: 4.0.0 - terser: 5.43.1 - rollup@3.29.5: optionalDependencies: fsevents: 2.3.3 @@ -5176,7 +4366,7 @@ snapshots: semver@7.7.2: {} - serialize-javascript@4.0.0: + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 @@ -5203,51 +4393,13 @@ snapshots: minimist: 1.2.8 shelljs: 0.8.5 - side-channel-list@1.0.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.0 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - signal-exit@3.0.7: {} sisteransi@1.0.5: {} slash@4.0.0: {} - slice-ansi@3.0.0: - dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 - - slice-ansi@4.0.0: - dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 + smob@1.5.0: {} solid-js@1.9.8: dependencies: @@ -5264,18 +4416,6 @@ snapshots: source-map@0.6.1: {} - sshpk@1.18.0: - dependencies: - asn1: 0.2.6 - assert-plus: 1.0.0 - bcrypt-pbkdf: 1.0.2 - dashdash: 1.14.1 - ecc-jsbn: 0.1.2 - getpass: 0.1.7 - jsbn: 0.1.1 - safer-buffer: 2.1.2 - tweetnacl: 0.14.5 - std-env@3.9.0: {} string-width@4.2.3: @@ -5288,8 +4428,6 @@ snapshots: dependencies: ansi-regex: 5.0.1 - strip-final-newline@2.0.0: {} - strip-final-newline@3.0.0: {} strip-literal@3.0.0: @@ -5302,14 +4440,6 @@ snapshots: postcss: 8.5.6 postcss-selector-parser: 7.1.0 - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - supports-color@8.1.1: - dependencies: - has-flag: 4.0.0 - supports-preserve-symlinks-flag@1.0.0: {} svgo@4.0.0: @@ -5331,10 +4461,6 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 - throttleit@1.0.1: {} - - through@2.3.8: {} - tinyexec@1.0.1: {} tinyglobby@0.2.14: @@ -5342,18 +4468,19 @@ snapshots: fdir: 6.4.6(picomatch@4.0.3) picomatch: 4.0.3 - tmp@0.2.4: {} + tldts-core@6.1.86: {} + + tldts@6.1.86: + dependencies: + tldts-core: 6.1.86 to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - tough-cookie@4.1.4: + tough-cookie@5.1.2: dependencies: - psl: 1.15.0 - punycode: 2.3.1 - universalify: 0.2.0 - url-parse: 1.5.10 + tldts: 6.1.86 tr46@5.1.1: dependencies: @@ -5361,14 +4488,6 @@ snapshots: tslib@2.8.1: {} - tunnel-agent@0.6.0: - dependencies: - safe-buffer: 5.2.1 - - tweetnacl@0.14.5: {} - - type-fest@0.21.3: {} - typescript@5.9.2: {} ufo@1.6.1: {} @@ -5439,8 +4558,6 @@ snapshots: unplugin: 2.3.5 unplugin-utils: 0.2.5 - universalify@0.2.0: {} - universalify@2.0.1: {} unplugin-utils@0.2.5: @@ -5454,8 +4571,6 @@ snapshots: picomatch: 4.0.3 webpack-virtual-modules: 0.6.2 - untildify@4.0.0: {} - untyped@1.5.2: dependencies: '@babel/core': 7.28.0 @@ -5483,32 +4598,19 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - url-parse@1.5.10: - dependencies: - querystringify: 2.2.0 - requires-port: 1.0.0 - util-deprecate@1.0.2: {} - uuid@8.3.2: {} - - verror@1.10.0: - dependencies: - assert-plus: 1.0.0 - core-util-is: 1.0.2 - extsprintf: 1.3.0 - - vite-ssg@0.23.8(vite@5.4.1(@types/node@20.19.9)(terser@5.43.1))(vue-router@4.5.1(vue@3.5.18(typescript@5.9.2)))(vue@3.5.18(typescript@5.9.2)): + vite-ssg@0.24.3(vite@7.1.1(@types/node@20.19.9)(jiti@1.21.7)(terser@5.43.1))(vue-router@4.5.1(vue@3.5.18(typescript@5.9.2)))(vue@3.5.18(typescript@5.9.2)): dependencies: '@unhead/dom': 1.11.20 '@unhead/vue': 1.11.20(vue@3.5.18(typescript@5.9.2)) fs-extra: 11.3.1 html-minifier-terser: 7.2.0 html5parser: 2.0.2 - jsdom: 24.1.3 + jsdom: 25.0.1 kolorist: 1.8.0 prettier: 3.6.2 - vite: 5.4.1(@types/node@20.19.9)(terser@5.43.1) + vite: 7.1.1(@types/node@20.19.9)(jiti@1.21.7)(terser@5.43.1) vue: 3.5.18(typescript@5.9.2) yargs: 17.7.2 optionalDependencies: @@ -5519,14 +4621,18 @@ snapshots: - supports-color - utf-8-validate - vite@5.4.1(@types/node@20.19.9)(terser@5.43.1): + vite@7.1.1(@types/node@20.19.9)(jiti@1.21.7)(terser@5.43.1): dependencies: - esbuild: 0.21.5 + esbuild: 0.25.8 + fdir: 6.4.6(picomatch@4.0.3) + picomatch: 4.0.3 postcss: 8.5.6 rollup: 4.46.2 + tinyglobby: 0.2.14 optionalDependencies: '@types/node': 20.19.9 fsevents: 2.3.3 + jiti: 1.21.7 terser: 5.43.1 vue-github-button@3.1.3: @@ -5571,12 +4677,6 @@ snapshots: dependencies: isexe: 2.0.0 - wrap-ansi@6.2.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -5607,11 +4707,6 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yauzl@2.10.0: - dependencies: - buffer-crc32: 0.2.13 - fd-slicer: 1.1.0 - zhead@2.2.4: {} zone.js@0.11.8: diff --git a/tests/e2e/framework-animations.spec.ts b/tests/e2e/framework-animations.spec.ts index ec50469..2737175 100644 --- a/tests/e2e/framework-animations.spec.ts +++ b/tests/e2e/framework-animations.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test' -import { assertNoConsoleErrors, withAnimationObserver } from './utils' +import { assertNoConsoleErrors, withAnimationObserver, waitForActiveAnimations } from './utils' test.describe('Framework examples animate on interaction', () => { test.beforeEach(async ({ page }) => { @@ -38,7 +38,7 @@ test.describe('Framework examples animate on interaction', () => { const assertNoErrorsLater = await assertNoConsoleErrors(page) const observer = await withAnimationObserver(page, '.solid-example .parent') await page.getByRole('button', { name: 'Toggle Drawer' }).click() - await page.waitForTimeout(50) + await waitForActiveAnimations(page, '.solid-example .parent') expect(await observer.count()).toBeGreaterThan(0) await assertNoErrorsLater() }) @@ -49,7 +49,7 @@ test.describe('Framework examples animate on interaction', () => { const input = page.locator('.tag-input input') await input.fill('Jazz') await input.press('Enter') - await page.waitForTimeout(50) + await waitForActiveAnimations(page, '.tag-input ul') expect(await observer.count()).toBeGreaterThan(0) await assertNoErrorsLater() }) @@ -58,7 +58,7 @@ test.describe('Framework examples animate on interaction', () => { const assertNoErrorsLater = await assertNoConsoleErrors(page) const observer = await withAnimationObserver(page, '.angular-example') await page.locator('.angular-example .toggle-story-btn').first().click() - await page.waitForTimeout(50) + await waitForActiveAnimations(page, '.angular-example') expect(await observer.count()).toBeGreaterThan(0) await assertNoErrorsLater() }) diff --git a/tests/e2e/utils.ts b/tests/e2e/utils.ts index a0794bb..581bdce 100644 --- a/tests/e2e/utils.ts +++ b/tests/e2e/utils.ts @@ -29,6 +29,19 @@ export async function withAnimationObserver(page: Page, selector: string) { } } +export async function waitForActiveAnimations(page: Page, selector: string, timeoutMs = 1000) { + await page.waitForFunction( + (sel) => { + const el = document.querySelector(sel) + if (!el) return false + const anims = (el as any).getAnimations ? (el as any).getAnimations({ subtree: true }) : [] + return anims.some((a: Animation) => a.playState !== 'finished') + }, + selector, + { timeout: timeoutMs } + ) +} + export async function approximateMemoryUsage(page: Page) { // Not all browsers support performance.memory; guard and return undefined when missing const mem = await page.evaluate(() => { diff --git a/tests/e2e/visual-animation-video.spec.ts b/tests/e2e/visual-animation-video.spec.ts new file mode 100644 index 0000000..6e3618f --- /dev/null +++ b/tests/e2e/visual-animation-video.spec.ts @@ -0,0 +1,129 @@ +import { test, expect } from '@playwright/test' +import path from 'node:path' +import fs from 'node:fs/promises' +import { getVideoDurationSec, extractFrameAt, diffImages } from './visual-video.util' +import { PNG } from 'pngjs' +import pixelmatch from 'pixelmatch' + +// Record a deterministic video of the animation by scrubbing frames programmatically. +// This produces a stable artifact for human review without relying on image snapshots. +// Always keep video for this spec +test.use({ video: 'on' }) + +test.describe('Visual video: list animation', () => { + test.skip(!process.env.VISUAL, 'Visual tests require VISUAL=1') + + test('record add/remove sequence', async ({ page }, testInfo) => { + await page.addInitScript(() => { + // Stabilize randomness and slow animations + Math.random = () => 0.123456789 + const w = window as any + if (w.__aaPatchedAnimate) return + const original = Element.prototype.animate + Element.prototype.animate = function (...args: any[]) { + try { + if (typeof args[1] === 'number') args[1] = (args[1] as number) * 3 + else if (args[1] && typeof args[1] === 'object') { + const timing = args[1] as any + if (typeof timing.duration === 'number') timing.duration = timing.duration * 3 + } + } catch {} + const anim = original.apply(this, args as any) + try { + const effect: any = anim.effect as any + if (effect?.updateTiming) effect.updateTiming({ fill: 'both' }) + } catch {} + return anim + } + w.__aaPatchedAnimate = true + }) + + await page.goto('/lists') + const list = page.locator('ul') + await list.waitFor() + + // Interact to produce an animation and wait a bit so it is captured in video + await page.getByRole('button', { name: 'Add Fruit' }).click() + await page.waitForTimeout(1200) + await page.locator('ul li button:has-text("Remove")').first().click() + await page.waitForTimeout(800) + + // Smoke-assert there are running animations at some point + const hadAnimations = await page.evaluate(() => { + const root = document.querySelector('ul')! + const anims = root.getAnimations({ subtree: true }) + return anims.length > 0 + }) + expect(hadAnimations).toBeTruthy() + + // Frame-based snapshot: scrub and capture frames to compare with baseline images + async function scrub(fraction: number) { + await page.evaluate((fraction) => { + const root = document.querySelector('ul')! + const anims = root.getAnimations({ subtree: true }) + for (const a of anims) { + try { + a.pause() + const effect: any = a.effect as any + const timing = effect?.getTiming ? effect.getTiming() : effect?.getComputedTiming?.() || { duration: 0, delay: 0 } + const duration = typeof timing.duration === 'number' ? timing.duration : 0 + const delay = typeof timing.delay === 'number' ? timing.delay : 0 + a.currentTime = delay + duration * fraction + } catch {} + } + ;(root as HTMLElement).offsetWidth + }, fraction) + await page.evaluate(() => new Promise((r) => requestAnimationFrame(() => r(null)))) + } + + const box = await list.boundingBox() + if (!box) throw new Error('List bounding box not available') + const clip = { + x: Math.floor(box.x), + y: Math.floor(box.y), + width: Math.floor(box.width), + height: Math.floor(box.height), + } + + const baselineDir = path.resolve(testInfo.project.outputDir, '..', 'visual-baseline', 'list-animation') + const currentDir = path.resolve(testInfo.outputDir, 'frames-current') + await fs.mkdir(currentDir, { recursive: true }) + + const frames = [ + { name: 'add-10.png', action: async () => scrub(0.1) }, + { name: 'add-50.png', action: async () => scrub(0.5) }, + { name: 'add-100.png', action: async () => scrub(1.0) }, + { name: 'remove-50.png', action: async () => { await page.locator('ul li button:has-text("Remove")').first().click(); await page.waitForFunction(() => document.querySelector('ul')!.getAnimations({ subtree: true }).length > 0); await scrub(0.5) } }, + ] + + for (const f of frames) { + await f.action() + const buf = await page.screenshot({ clip, fullPage: false }) + const outPath = path.join(currentDir, f.name) + await fs.writeFile(outPath, buf) + if (process.env.VISUAL_UPDATE === '1') { + await fs.mkdir(baselineDir, { recursive: true }) + await fs.writeFile(path.join(baselineDir, f.name), buf) + } else { + try { + const basePath = path.join(baselineDir, f.name) + const [aBuf, bBuf] = await Promise.all([fs.readFile(basePath), fs.readFile(outPath)]) + const imgA = PNG.sync.read(aBuf) + const imgB = PNG.sync.read(bBuf) + const { width, height } = imgA + const diff = new PNG({ width, height }) + const pixels = pixelmatch(imgA.data, imgB.data, diff.data, width, height, { threshold: 0.1 }) + const ratio = pixels / (width * height) + expect(ratio).toBeLessThan(0.08) + } catch (e) { + // Baseline missing; attach info but do not fail + testInfo.attachments.push({ name: `baseline-missing-${f.name}`, contentType: 'text/plain', body: Buffer.from(String(e)) }) + } + } + } + + // Video is recorded automatically for human review; frames above are compared to PNG baselines. + }) +}) + + diff --git a/tests/e2e/visual-animation.spec.ts b/tests/e2e/visual-animation.spec.ts new file mode 100644 index 0000000..5647c2f --- /dev/null +++ b/tests/e2e/visual-animation.spec.ts @@ -0,0 +1,125 @@ +import { test, expect } from '@playwright/test' +import pixelmatch from 'pixelmatch' +import { PNG } from 'pngjs' + +// Visual snapshot of a slowed animation sequence. +// We use a long duration and capture intermediate frames. +// Always keep video for this spec so humans can inspect the animation +test.use({ video: 'on' }) + +test.describe('Visual: list animation sequence', () => { + test.skip(!process.env.VISUAL, 'Visual tests require VISUAL=1') + test('captures keyframes during add/remove sequence', async ({ page }) => { + await page.emulateMedia({ reducedMotion: 'no-preference' }) + // Monkey patch Element.animate early to make sure animations are WAAPI and controllable + await page.addInitScript(() => { + // Stabilize randomness used by demo code + Math.random = () => 0.123456789 + const w = window as any + if (w.__aaPatchedAnimate) return + const original = Element.prototype.animate + Element.prototype.animate = function (...args: any[]) { + // Scale duration to slow animations for clearer frames + try { + if (typeof args[1] === 'number') args[1] = (args[1] as number) * 4 + else if (args[1] && typeof args[1] === 'object') { + const timing = args[1] as any + if (typeof timing.duration === 'number') timing.duration = timing.duration * 4 + } + } catch {} + const anim = original.apply(this, args as any) + try { + // Force fill mode so scrubbing produces visible frames + const effect: any = anim.effect as any + if (effect?.updateTiming) effect.updateTiming({ fill: 'both' }) + } catch {} + return anim + } + w.__aaPatchedAnimate = true + }) + await page.goto('/lists') + await page.evaluate(() => document.body.setAttribute('data-visual', '1')) + await page.addStyleTag({ content: ` + body[data-visual] ul { width: 500px !important; height: 420px !important; overflow: hidden !important; } + body[data-visual] ul li { white-space: nowrap } + ` }) + + const list = page.locator('ul') + await expect(list).toBeVisible() + + // Helper to pause and scrub all WAAPI animations under the list + async function scrub(fraction: number) { + await page.evaluate((fraction) => { + const root = document.querySelector('ul')! + const anims = root.getAnimations({ subtree: true }) + for (const a of anims) { + try { + a.pause() + const effect: any = a.effect as any + const timing = effect?.getTiming ? effect.getTiming() : effect?.getComputedTiming?.() || { duration: 0, delay: 0 } + const duration = typeof timing.duration === 'number' ? timing.duration : 0 + const delay = typeof timing.delay === 'number' ? timing.delay : 0 + a.currentTime = delay + duration * fraction + } catch {} + } + // Force reflow so the new keyframe is painted + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + ;(root as HTMLElement).offsetWidth + }, fraction) + // Wait one RAF for paint to commit + await page.evaluate(() => new Promise((r) => requestAnimationFrame(() => r(null)))) + } + + // Wait a tick so the page settles; capture a baseline image buffer using a fixed clip rect + await page.waitForTimeout(50) + const box = await list.boundingBox() + if (!box) throw new Error('List bounding box not available') + const clip = { + x: Math.floor(box.x), + y: Math.floor(box.y), + width: Math.floor(box.width), + height: Math.floor(box.height), + } + const baselinePngBuf = await page.screenshot({ clip, fullPage: false }) + + // Trigger add and immediately scrub to 10%, 50%, 100% + await page.getByRole('button', { name: 'Add Fruit' }).click() + await page.waitForFunction(() => document.querySelector('ul')!.getAnimations({ subtree: true }).length > 0) + await scrub(0.1) + const add10Buf = await page.screenshot({ clip, fullPage: false }) + const imgA = PNG.sync.read(baselinePngBuf) + const img10 = PNG.sync.read(add10Buf) + const { width, height } = imgA + const diff10 = new PNG({ width, height }) + const diffPixels10 = pixelmatch(imgA.data, img10.data, diff10.data, width, height, { threshold: 0.1 }) + const ratio10 = diffPixels10 / (width * height) + expect(ratio10).toBeGreaterThan(0.01) + await scrub(0.5) + const add50Buf = await page.screenshot({ clip, fullPage: false }) + const img50 = PNG.sync.read(add50Buf) + const diff50 = new PNG({ width, height }) + const diffPixels50 = pixelmatch(imgA.data, img50.data, diff50.data, width, height, { threshold: 0.1 }) + const ratio50 = diffPixels50 / (width * height) + expect(ratio50).toBeGreaterThan(0.01) + await scrub(1.0) + const add100Buf = await page.screenshot({ clip, fullPage: false }) + const img100 = PNG.sync.read(add100Buf) + const diff100 = new PNG({ width, height }) + const diffPixels100 = pixelmatch(imgA.data, img100.data, diff100.data, width, height, { threshold: 0.1 }) + const ratio100 = diffPixels100 / (width * height) + expect(ratio100).toBeGreaterThan(0.01) + + // Trigger remove and scrub mid-way + await page.locator('ul li button:has-text("Remove")').first().click() + await page.waitForFunction(() => document.querySelector('ul')!.getAnimations({ subtree: true }).length > 0) + await scrub(0.5) + const remove50Buf = await page.screenshot({ clip, fullPage: false }) + const imgR50 = PNG.sync.read(remove50Buf) + const diffR50 = new PNG({ width, height }) + const diffPixelsR50 = pixelmatch(img100.data, imgR50.data, diffR50.data, width, height, { threshold: 0.1 }) + const ratioR50 = diffPixelsR50 / (width * height) + expect(ratioR50).toBeGreaterThan(0.01) + }) +}) + + diff --git a/tests/e2e/visual-video.util.ts b/tests/e2e/visual-video.util.ts new file mode 100644 index 0000000..6bd9b7b --- /dev/null +++ b/tests/e2e/visual-video.util.ts @@ -0,0 +1,44 @@ +import { execa } from 'execa' +import { PNG } from 'pngjs' +import pixelmatch from 'pixelmatch' +import fs from 'node:fs/promises' +import path from 'node:path' + +export async function getVideoDurationSec(videoPath: string): Promise { + const { stdout } = await execa('ffprobe', [ + '-v', 'error', + '-show_entries', 'format=duration', + '-of', 'default=nw=1:nk=1', + videoPath, + ]) + return parseFloat(stdout.trim()) +} + +export async function extractFrameAt(videoPath: string, timeSec: number, outPath: string): Promise { + await fs.mkdir(path.dirname(outPath), { recursive: true }) + await execa('ffmpeg', [ + '-loglevel', 'error', + '-y', + '-ss', timeSec.toFixed(3), + '-i', videoPath, + '-frames:v', '1', + '-q:v', '2', + outPath, + ]) +} + +export async function diffImages(aPath: string, bPath: string): Promise<{ ratio: number; pixels: number }> { + const [aBuf, bBuf] = await Promise.all([fs.readFile(aPath), fs.readFile(bPath)]) + const imgA = PNG.sync.read(aBuf) + const imgB = PNG.sync.read(bBuf) + if (imgA.width !== imgB.width || imgA.height !== imgB.height) { + throw new Error(`Image size mismatch: ${imgA.width}x${imgA.height} vs ${imgB.width}x${imgB.height}`) + } + const { width, height } = imgA + const diff = new PNG({ width, height }) + const pixels = pixelmatch(imgA.data, imgB.data, diff.data, width, height, { threshold: 0.1 }) + const ratio = pixels / (width * height) + return { ratio, pixels } +} + + From bf45988e61002ab9cecd6ef79b0fffbfd7a300a1 Mon Sep 17 00:00:00 2001 From: Maik Kowol Date: Wed, 27 Aug 2025 16:55:14 +0200 Subject: [PATCH 02/16] fix: jumping when aligned at bottom (#69) (#211) * fix: jumping when aligned at bottom (#69) * chore: use const instead of let * test(e2e): add Playwright visual tests (video + pixel diffs); stabilize animation waits; upload videos in CI --------- Co-authored-by: Justin Schroeder --- .github/workflows/main.yml | 6 ++ package.json | 7 +- playwright.config.ts | 9 ++ pnpm-lock.yaml | 26 +++++ src/index.ts | 92 ++++++++++------ tests/e2e/framework-animations.spec.ts | 8 +- tests/e2e/utils.ts | 13 +++ tests/e2e/visual-animation-video.spec.ts | 129 +++++++++++++++++++++++ tests/e2e/visual-animation.spec.ts | 125 ++++++++++++++++++++++ tests/e2e/visual-video.util.ts | 44 ++++++++ 10 files changed, 420 insertions(+), 39 deletions(-) create mode 100644 tests/e2e/visual-animation-video.spec.ts create mode 100644 tests/e2e/visual-animation.spec.ts create mode 100644 tests/e2e/visual-video.util.ts diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6c53643..d7bf462 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -36,3 +36,9 @@ jobs: with: name: playwright-report path: playwright-report + - name: Upload Playwright videos (visual) + if: always() + uses: actions/upload-artifact@v4 + with: + name: playwright-videos + path: test-results/**/video.webm diff --git a/package.json b/package.json index 609ef0f..4be1a85 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "test:e2e": "playwright test", "test:e2e:headed": "playwright test --headed --project=chromium", "test:e2e:ui": "playwright test --ui", - "test:e2e:leak": "LEAK_STRICT=1 playwright test -g memory --project=chromium" + "test:e2e:leak": "LEAK_STRICT=1 playwright test -g memory --project=chromium", + "test:e2e:visual": "VISUAL=1 playwright test -g 'Visual:' --project=chromium" }, "exports": { "./vue": { @@ -108,7 +109,9 @@ "vite-ssg": "^0.24.0", "vue": "^3.5.0", "vue-github-button": "^3.1.3", - "vue-router": "^4.4.3" + "vue-router": "^4.4.3", + "pixelmatch": "^5.3.0", + "pngjs": "^7.0.0" }, "packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748" } diff --git a/playwright.config.ts b/playwright.config.ts index 6a31615..9f8c1ae 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -6,12 +6,21 @@ export default defineConfig({ forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 2 : undefined, + expect: { + toHaveScreenshot: { + // Allow small rendering differences across environments + maxDiffPixelRatio: 0.03, + animations: 'allow', + }, + }, reporter: [['list'], ['html', { open: 'never' }]], use: { baseURL: 'http://localhost:5173', trace: 'retain-on-failure', video: 'retain-on-failure', screenshot: 'only-on-failure', + viewport: { width: 1200, height: 900 }, + deviceScaleFactor: 1, }, webServer: { command: 'cd docs && vite --config=./vite.config.ts --port=5173', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ab84bfc..e2c42e4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -77,6 +77,12 @@ importers: pathe: specifier: ^1.1.2 version: 1.1.2 + pixelmatch: + specifier: ^5.3.0 + version: 5.3.0 + pngjs: + specifier: ^7.0.0 + version: 7.0.0 preact: specifier: ^10.23.2 version: 10.27.0 @@ -1847,6 +1853,10 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + pixelmatch@5.3.0: + resolution: {integrity: sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==} + hasBin: true + pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} @@ -1863,6 +1873,14 @@ packages: engines: {node: '>=18'} hasBin: true + pngjs@6.0.0: + resolution: {integrity: sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==} + engines: {node: '>=12.13.0'} + + pngjs@7.0.0: + resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} + engines: {node: '>=14.19.0'} + postcss-calc@10.1.1: resolution: {integrity: sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==} engines: {node: ^18.12 || ^20.9 || >=22.0} @@ -4196,6 +4214,10 @@ snapshots: picomatch@4.0.3: {} + pixelmatch@5.3.0: + dependencies: + pngjs: 6.0.0 + pkg-types@1.3.1: dependencies: confbox: 0.1.8 @@ -4216,6 +4238,10 @@ snapshots: optionalDependencies: fsevents: 2.3.2 + pngjs@6.0.0: {} + + pngjs@7.0.0: {} + postcss-calc@10.1.1(postcss@8.5.6): dependencies: postcss: 8.5.6 diff --git a/src/index.ts b/src/index.ts index 1296832..90c8790 100644 --- a/src/index.ts +++ b/src/index.ts @@ -159,7 +159,7 @@ function observePosition(el: Element) { root, threshold: 1, rootMargin, - } + }, ) observer.observe(el) intersections.set(el, observer) @@ -168,11 +168,16 @@ function observePosition(el: Element) { /** * Update the exact position of a given element. * @param el - An element to update the position of. + * @param debounce - Whether or not to debounce the update. After an animation is finished, it should update as soon as possible to prevent flickering on quick toggles. */ -function updatePos(el: Element) { +function updatePos(el: Element, debounce = true) { clearTimeout(debounces.get(el)) const optionsOrPlugin = getOptions(el) - const delay = isPlugin(optionsOrPlugin) ? 500 : optionsOrPlugin.duration + const delay = debounce + ? isPlugin(optionsOrPlugin) + ? 500 + : optionsOrPlugin.duration + : 0 debounces.set( el, setTimeout(async () => { @@ -186,7 +191,7 @@ function updatePos(el: Element) { } catch { // ignore errors as the `.finished` promise is rejected when animations were cancelled } - }, delay) + }, delay), ) } @@ -199,9 +204,9 @@ function updateAllPos() { root, setTimeout(() => { parents.forEach((parent) => - forEach(parent, (el) => lowPriority(() => updatePos(el))) + forEach(parent, (el) => lowPriority(() => updatePos(el))), ) - }, 100) + }, 100), ) } @@ -214,12 +219,15 @@ function updateAllPos() { * @param el - Element */ function poll(el: Element) { - setTimeout(() => { - intervals.set( - el, - setInterval(() => lowPriority(updatePos.bind(null, el)), 2000) - ) - }, Math.round(2000 * Math.random())) + setTimeout( + () => { + intervals.set( + el, + setInterval(() => lowPriority(updatePos.bind(null, el)), 2000), + ) + }, + Math.round(2000 * Math.random()), + ) } /** @@ -279,7 +287,7 @@ function getElements(mutations: MutationRecord[]): Set | false { }, []) // Short circuit if _only_ comment nodes are observed const onlyCommentNodesObserved = observedNodes.every( - (node) => node.nodeName === "#comment" + (node) => node.nodeName === "#comment", ) if (onlyCommentNodesObserved) return false @@ -343,7 +351,8 @@ function animate(el: Element) { const isMounted = el.isConnected const preExisting = coords.has(el) if (isMounted && siblings.has(el)) siblings.delete(el) - if (animations.has(el)) { + + if (animations.get(el)?.playState !== "finished") { animations.get(el)?.cancel() } if (NEW in el) { @@ -409,7 +418,7 @@ function getCoords(el: Element): Coordinates { export function getTransitionSizes( el: Element, oldCoords: Coordinates, - newCoords: Coordinates + newCoords: Coordinates, ) { let widthFrom = oldCoords.width let heightFrom = oldCoords.height @@ -491,7 +500,7 @@ function forEach( * Always return tuple to provide consistent interface */ function getPluginTuple( - pluginReturn: ReturnType + pluginReturn: ReturnType, ): [KeyframeEffect, AutoAnimationPluginOptions] | [KeyframeEffect] { if (Array.isArray(pluginReturn)) return pluginReturn @@ -502,7 +511,7 @@ function getPluginTuple( * Determine if config is plugin */ function isPlugin( - config: Partial | AutoAnimationPlugin + config: Partial | AutoAnimationPlugin, ): config is AutoAnimationPlugin { return typeof config === "function" } @@ -520,15 +529,24 @@ function remain(el: Element) { if (!oldCoords) return const pluginOrOptions = getOptions(el) if (typeof pluginOrOptions !== "function") { - const deltaX = oldCoords.left - newCoords.left - const deltaY = oldCoords.top - newCoords.top + let deltaLeft = oldCoords.left - newCoords.left + let deltaTop = oldCoords.top - newCoords.top + const deltaRight = + oldCoords.left + oldCoords.width - (newCoords.left + newCoords.width) + const deltaBottom = + oldCoords.top + oldCoords.height - (newCoords.top + newCoords.height) + + // element is probably anchored and doesn't need to be offset + if (deltaBottom == 0) deltaTop = 0 + if (deltaRight == 0) deltaLeft = 0 + const [widthFrom, widthTo, heightFrom, heightTo] = getTransitionSizes( el, oldCoords, - newCoords + newCoords, ) const start: Record = { - transform: `translate(${deltaX}px, ${deltaY}px)`, + transform: `translate(${deltaLeft}px, ${deltaTop}px)`, } const end: Record = { transform: `translate(0, 0)`, @@ -547,14 +565,16 @@ function remain(el: Element) { }) } else { const [keyframes] = getPluginTuple( - pluginOrOptions(el, "remain", oldCoords, newCoords) + pluginOrOptions(el, "remain", oldCoords, newCoords), ) animation = new Animation(keyframes) animation.play() } animations.set(el, animation) coords.set(el, newCoords) - animation.addEventListener("finish", () => updatePos(el), { once: true }) + animation.addEventListener("finish", updatePos.bind(null, el, false), { + once: true, + }) } /** @@ -578,7 +598,7 @@ function add(el: Element) { { duration: (pluginOrOptions as AutoAnimateOptions).duration * 1.5, easing: "ease-in", - } + }, ) } else { const [keyframes] = getPluginTuple(pluginOrOptions(el, "add", newCoords)) @@ -586,7 +606,9 @@ function add(el: Element) { animation.play() } animations.set(el, animation) - animation.addEventListener("finish", () => updatePos(el), { once: true }) + animation.addEventListener("finish", updatePos.bind(null, el, false), { + once: true, + }) } /** @@ -672,11 +694,11 @@ function remove(el: Element) { { duration: (optionsOrPlugin as AutoAnimateOptions).duration, easing: "ease-out", - } + }, ) } else { const [keyframes, options] = getPluginTuple( - (optionsOrPlugin as AutoAnimationPlugin)(el, "remove", oldCoords) + (optionsOrPlugin as AutoAnimationPlugin)(el, "remove", oldCoords), ) if ( (options as AutoAnimationPluginOptions | undefined)?.styleReset !== false @@ -713,7 +735,7 @@ function adjustScroll( el: Element, finalX: number, finalY: number, - optionsOrPlugin: AutoAnimateOptions | AutoAnimationPlugin + optionsOrPlugin: AutoAnimateOptions | AutoAnimationPlugin, ) { const scrollDeltaX = scrollX - finalX const scrollDeltaY = scrollY - finalY @@ -761,7 +783,7 @@ function adjustScroll( * @returns */ function deletePosition( - el: Element + el: Element, ): [top: number, left: number, width: number, height: number] { const oldCoords = coords.get(el)! const [width, , height] = getTransitionSizes(el, oldCoords, getCoords(el)) @@ -776,7 +798,11 @@ function deletePosition( } if (!offsetParent) offsetParent = document.body const parentStyles = getComputedStyle(offsetParent) - const parentCoords = coords.get(offsetParent) || getCoords(offsetParent) + const parentCoords = + !animations.has(el) || animations.get(el)?.playState === "finished" + ? getCoords(offsetParent) + : coords.get(offsetParent)! + const top = Math.round(oldCoords.top - parentCoords.top) - raw(parentStyles.borderTopWidth) @@ -821,7 +847,7 @@ export interface AutoAnimationPlugin { newCoordinates?: T extends "add" | "remain" | "remove" ? Coordinates : undefined, - oldCoordinates?: T extends "remain" ? Coordinates : undefined + oldCoordinates?: T extends "remain" ? Coordinates : undefined, ): KeyframeEffect | [KeyframeEffect, AutoAnimationPluginOptions] } @@ -834,7 +860,7 @@ export interface AutoAnimationPlugin { */ export default function autoAnimate( el: HTMLElement, - config: Partial | AutoAnimationPlugin = {} + config: Partial | AutoAnimationPlugin = {}, ): AnimationController { if (supportedBrowser && resize) { const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)") @@ -916,7 +942,7 @@ export const vAutoAnimate = { el: HTMLElement, binding: { value: Partial | AutoAnimationPlugin | undefined - } + }, ) => { const ctl = autoAnimate(el, binding.value || {}) Object.defineProperty(el, "__aa_ctl", { value: ctl, configurable: true }) diff --git a/tests/e2e/framework-animations.spec.ts b/tests/e2e/framework-animations.spec.ts index ec50469..2737175 100644 --- a/tests/e2e/framework-animations.spec.ts +++ b/tests/e2e/framework-animations.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test' -import { assertNoConsoleErrors, withAnimationObserver } from './utils' +import { assertNoConsoleErrors, withAnimationObserver, waitForActiveAnimations } from './utils' test.describe('Framework examples animate on interaction', () => { test.beforeEach(async ({ page }) => { @@ -38,7 +38,7 @@ test.describe('Framework examples animate on interaction', () => { const assertNoErrorsLater = await assertNoConsoleErrors(page) const observer = await withAnimationObserver(page, '.solid-example .parent') await page.getByRole('button', { name: 'Toggle Drawer' }).click() - await page.waitForTimeout(50) + await waitForActiveAnimations(page, '.solid-example .parent') expect(await observer.count()).toBeGreaterThan(0) await assertNoErrorsLater() }) @@ -49,7 +49,7 @@ test.describe('Framework examples animate on interaction', () => { const input = page.locator('.tag-input input') await input.fill('Jazz') await input.press('Enter') - await page.waitForTimeout(50) + await waitForActiveAnimations(page, '.tag-input ul') expect(await observer.count()).toBeGreaterThan(0) await assertNoErrorsLater() }) @@ -58,7 +58,7 @@ test.describe('Framework examples animate on interaction', () => { const assertNoErrorsLater = await assertNoConsoleErrors(page) const observer = await withAnimationObserver(page, '.angular-example') await page.locator('.angular-example .toggle-story-btn').first().click() - await page.waitForTimeout(50) + await waitForActiveAnimations(page, '.angular-example') expect(await observer.count()).toBeGreaterThan(0) await assertNoErrorsLater() }) diff --git a/tests/e2e/utils.ts b/tests/e2e/utils.ts index a0794bb..581bdce 100644 --- a/tests/e2e/utils.ts +++ b/tests/e2e/utils.ts @@ -29,6 +29,19 @@ export async function withAnimationObserver(page: Page, selector: string) { } } +export async function waitForActiveAnimations(page: Page, selector: string, timeoutMs = 1000) { + await page.waitForFunction( + (sel) => { + const el = document.querySelector(sel) + if (!el) return false + const anims = (el as any).getAnimations ? (el as any).getAnimations({ subtree: true }) : [] + return anims.some((a: Animation) => a.playState !== 'finished') + }, + selector, + { timeout: timeoutMs } + ) +} + export async function approximateMemoryUsage(page: Page) { // Not all browsers support performance.memory; guard and return undefined when missing const mem = await page.evaluate(() => { diff --git a/tests/e2e/visual-animation-video.spec.ts b/tests/e2e/visual-animation-video.spec.ts new file mode 100644 index 0000000..6e3618f --- /dev/null +++ b/tests/e2e/visual-animation-video.spec.ts @@ -0,0 +1,129 @@ +import { test, expect } from '@playwright/test' +import path from 'node:path' +import fs from 'node:fs/promises' +import { getVideoDurationSec, extractFrameAt, diffImages } from './visual-video.util' +import { PNG } from 'pngjs' +import pixelmatch from 'pixelmatch' + +// Record a deterministic video of the animation by scrubbing frames programmatically. +// This produces a stable artifact for human review without relying on image snapshots. +// Always keep video for this spec +test.use({ video: 'on' }) + +test.describe('Visual video: list animation', () => { + test.skip(!process.env.VISUAL, 'Visual tests require VISUAL=1') + + test('record add/remove sequence', async ({ page }, testInfo) => { + await page.addInitScript(() => { + // Stabilize randomness and slow animations + Math.random = () => 0.123456789 + const w = window as any + if (w.__aaPatchedAnimate) return + const original = Element.prototype.animate + Element.prototype.animate = function (...args: any[]) { + try { + if (typeof args[1] === 'number') args[1] = (args[1] as number) * 3 + else if (args[1] && typeof args[1] === 'object') { + const timing = args[1] as any + if (typeof timing.duration === 'number') timing.duration = timing.duration * 3 + } + } catch {} + const anim = original.apply(this, args as any) + try { + const effect: any = anim.effect as any + if (effect?.updateTiming) effect.updateTiming({ fill: 'both' }) + } catch {} + return anim + } + w.__aaPatchedAnimate = true + }) + + await page.goto('/lists') + const list = page.locator('ul') + await list.waitFor() + + // Interact to produce an animation and wait a bit so it is captured in video + await page.getByRole('button', { name: 'Add Fruit' }).click() + await page.waitForTimeout(1200) + await page.locator('ul li button:has-text("Remove")').first().click() + await page.waitForTimeout(800) + + // Smoke-assert there are running animations at some point + const hadAnimations = await page.evaluate(() => { + const root = document.querySelector('ul')! + const anims = root.getAnimations({ subtree: true }) + return anims.length > 0 + }) + expect(hadAnimations).toBeTruthy() + + // Frame-based snapshot: scrub and capture frames to compare with baseline images + async function scrub(fraction: number) { + await page.evaluate((fraction) => { + const root = document.querySelector('ul')! + const anims = root.getAnimations({ subtree: true }) + for (const a of anims) { + try { + a.pause() + const effect: any = a.effect as any + const timing = effect?.getTiming ? effect.getTiming() : effect?.getComputedTiming?.() || { duration: 0, delay: 0 } + const duration = typeof timing.duration === 'number' ? timing.duration : 0 + const delay = typeof timing.delay === 'number' ? timing.delay : 0 + a.currentTime = delay + duration * fraction + } catch {} + } + ;(root as HTMLElement).offsetWidth + }, fraction) + await page.evaluate(() => new Promise((r) => requestAnimationFrame(() => r(null)))) + } + + const box = await list.boundingBox() + if (!box) throw new Error('List bounding box not available') + const clip = { + x: Math.floor(box.x), + y: Math.floor(box.y), + width: Math.floor(box.width), + height: Math.floor(box.height), + } + + const baselineDir = path.resolve(testInfo.project.outputDir, '..', 'visual-baseline', 'list-animation') + const currentDir = path.resolve(testInfo.outputDir, 'frames-current') + await fs.mkdir(currentDir, { recursive: true }) + + const frames = [ + { name: 'add-10.png', action: async () => scrub(0.1) }, + { name: 'add-50.png', action: async () => scrub(0.5) }, + { name: 'add-100.png', action: async () => scrub(1.0) }, + { name: 'remove-50.png', action: async () => { await page.locator('ul li button:has-text("Remove")').first().click(); await page.waitForFunction(() => document.querySelector('ul')!.getAnimations({ subtree: true }).length > 0); await scrub(0.5) } }, + ] + + for (const f of frames) { + await f.action() + const buf = await page.screenshot({ clip, fullPage: false }) + const outPath = path.join(currentDir, f.name) + await fs.writeFile(outPath, buf) + if (process.env.VISUAL_UPDATE === '1') { + await fs.mkdir(baselineDir, { recursive: true }) + await fs.writeFile(path.join(baselineDir, f.name), buf) + } else { + try { + const basePath = path.join(baselineDir, f.name) + const [aBuf, bBuf] = await Promise.all([fs.readFile(basePath), fs.readFile(outPath)]) + const imgA = PNG.sync.read(aBuf) + const imgB = PNG.sync.read(bBuf) + const { width, height } = imgA + const diff = new PNG({ width, height }) + const pixels = pixelmatch(imgA.data, imgB.data, diff.data, width, height, { threshold: 0.1 }) + const ratio = pixels / (width * height) + expect(ratio).toBeLessThan(0.08) + } catch (e) { + // Baseline missing; attach info but do not fail + testInfo.attachments.push({ name: `baseline-missing-${f.name}`, contentType: 'text/plain', body: Buffer.from(String(e)) }) + } + } + } + + // Video is recorded automatically for human review; frames above are compared to PNG baselines. + }) +}) + + diff --git a/tests/e2e/visual-animation.spec.ts b/tests/e2e/visual-animation.spec.ts new file mode 100644 index 0000000..5647c2f --- /dev/null +++ b/tests/e2e/visual-animation.spec.ts @@ -0,0 +1,125 @@ +import { test, expect } from '@playwright/test' +import pixelmatch from 'pixelmatch' +import { PNG } from 'pngjs' + +// Visual snapshot of a slowed animation sequence. +// We use a long duration and capture intermediate frames. +// Always keep video for this spec so humans can inspect the animation +test.use({ video: 'on' }) + +test.describe('Visual: list animation sequence', () => { + test.skip(!process.env.VISUAL, 'Visual tests require VISUAL=1') + test('captures keyframes during add/remove sequence', async ({ page }) => { + await page.emulateMedia({ reducedMotion: 'no-preference' }) + // Monkey patch Element.animate early to make sure animations are WAAPI and controllable + await page.addInitScript(() => { + // Stabilize randomness used by demo code + Math.random = () => 0.123456789 + const w = window as any + if (w.__aaPatchedAnimate) return + const original = Element.prototype.animate + Element.prototype.animate = function (...args: any[]) { + // Scale duration to slow animations for clearer frames + try { + if (typeof args[1] === 'number') args[1] = (args[1] as number) * 4 + else if (args[1] && typeof args[1] === 'object') { + const timing = args[1] as any + if (typeof timing.duration === 'number') timing.duration = timing.duration * 4 + } + } catch {} + const anim = original.apply(this, args as any) + try { + // Force fill mode so scrubbing produces visible frames + const effect: any = anim.effect as any + if (effect?.updateTiming) effect.updateTiming({ fill: 'both' }) + } catch {} + return anim + } + w.__aaPatchedAnimate = true + }) + await page.goto('/lists') + await page.evaluate(() => document.body.setAttribute('data-visual', '1')) + await page.addStyleTag({ content: ` + body[data-visual] ul { width: 500px !important; height: 420px !important; overflow: hidden !important; } + body[data-visual] ul li { white-space: nowrap } + ` }) + + const list = page.locator('ul') + await expect(list).toBeVisible() + + // Helper to pause and scrub all WAAPI animations under the list + async function scrub(fraction: number) { + await page.evaluate((fraction) => { + const root = document.querySelector('ul')! + const anims = root.getAnimations({ subtree: true }) + for (const a of anims) { + try { + a.pause() + const effect: any = a.effect as any + const timing = effect?.getTiming ? effect.getTiming() : effect?.getComputedTiming?.() || { duration: 0, delay: 0 } + const duration = typeof timing.duration === 'number' ? timing.duration : 0 + const delay = typeof timing.delay === 'number' ? timing.delay : 0 + a.currentTime = delay + duration * fraction + } catch {} + } + // Force reflow so the new keyframe is painted + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + ;(root as HTMLElement).offsetWidth + }, fraction) + // Wait one RAF for paint to commit + await page.evaluate(() => new Promise((r) => requestAnimationFrame(() => r(null)))) + } + + // Wait a tick so the page settles; capture a baseline image buffer using a fixed clip rect + await page.waitForTimeout(50) + const box = await list.boundingBox() + if (!box) throw new Error('List bounding box not available') + const clip = { + x: Math.floor(box.x), + y: Math.floor(box.y), + width: Math.floor(box.width), + height: Math.floor(box.height), + } + const baselinePngBuf = await page.screenshot({ clip, fullPage: false }) + + // Trigger add and immediately scrub to 10%, 50%, 100% + await page.getByRole('button', { name: 'Add Fruit' }).click() + await page.waitForFunction(() => document.querySelector('ul')!.getAnimations({ subtree: true }).length > 0) + await scrub(0.1) + const add10Buf = await page.screenshot({ clip, fullPage: false }) + const imgA = PNG.sync.read(baselinePngBuf) + const img10 = PNG.sync.read(add10Buf) + const { width, height } = imgA + const diff10 = new PNG({ width, height }) + const diffPixels10 = pixelmatch(imgA.data, img10.data, diff10.data, width, height, { threshold: 0.1 }) + const ratio10 = diffPixels10 / (width * height) + expect(ratio10).toBeGreaterThan(0.01) + await scrub(0.5) + const add50Buf = await page.screenshot({ clip, fullPage: false }) + const img50 = PNG.sync.read(add50Buf) + const diff50 = new PNG({ width, height }) + const diffPixels50 = pixelmatch(imgA.data, img50.data, diff50.data, width, height, { threshold: 0.1 }) + const ratio50 = diffPixels50 / (width * height) + expect(ratio50).toBeGreaterThan(0.01) + await scrub(1.0) + const add100Buf = await page.screenshot({ clip, fullPage: false }) + const img100 = PNG.sync.read(add100Buf) + const diff100 = new PNG({ width, height }) + const diffPixels100 = pixelmatch(imgA.data, img100.data, diff100.data, width, height, { threshold: 0.1 }) + const ratio100 = diffPixels100 / (width * height) + expect(ratio100).toBeGreaterThan(0.01) + + // Trigger remove and scrub mid-way + await page.locator('ul li button:has-text("Remove")').first().click() + await page.waitForFunction(() => document.querySelector('ul')!.getAnimations({ subtree: true }).length > 0) + await scrub(0.5) + const remove50Buf = await page.screenshot({ clip, fullPage: false }) + const imgR50 = PNG.sync.read(remove50Buf) + const diffR50 = new PNG({ width, height }) + const diffPixelsR50 = pixelmatch(img100.data, imgR50.data, diffR50.data, width, height, { threshold: 0.1 }) + const ratioR50 = diffPixelsR50 / (width * height) + expect(ratioR50).toBeGreaterThan(0.01) + }) +}) + + diff --git a/tests/e2e/visual-video.util.ts b/tests/e2e/visual-video.util.ts new file mode 100644 index 0000000..6bd9b7b --- /dev/null +++ b/tests/e2e/visual-video.util.ts @@ -0,0 +1,44 @@ +import { execa } from 'execa' +import { PNG } from 'pngjs' +import pixelmatch from 'pixelmatch' +import fs from 'node:fs/promises' +import path from 'node:path' + +export async function getVideoDurationSec(videoPath: string): Promise { + const { stdout } = await execa('ffprobe', [ + '-v', 'error', + '-show_entries', 'format=duration', + '-of', 'default=nw=1:nk=1', + videoPath, + ]) + return parseFloat(stdout.trim()) +} + +export async function extractFrameAt(videoPath: string, timeSec: number, outPath: string): Promise { + await fs.mkdir(path.dirname(outPath), { recursive: true }) + await execa('ffmpeg', [ + '-loglevel', 'error', + '-y', + '-ss', timeSec.toFixed(3), + '-i', videoPath, + '-frames:v', '1', + '-q:v', '2', + outPath, + ]) +} + +export async function diffImages(aPath: string, bPath: string): Promise<{ ratio: number; pixels: number }> { + const [aBuf, bBuf] = await Promise.all([fs.readFile(aPath), fs.readFile(bPath)]) + const imgA = PNG.sync.read(aBuf) + const imgB = PNG.sync.read(bBuf) + if (imgA.width !== imgB.width || imgA.height !== imgB.height) { + throw new Error(`Image size mismatch: ${imgA.width}x${imgA.height} vs ${imgB.width}x${imgB.height}`) + } + const { width, height } = imgA + const diff = new PNG({ width, height }) + const pixels = pixelmatch(imgA.data, imgB.data, diff.data, width, height, { threshold: 0.1 }) + const ratio = pixels / (width * height) + return { ratio, pixels } +} + + From 269d98c43cc5bfd0ec80d280430bf408c3991b3b Mon Sep 17 00:00:00 2001 From: Justin Schroeder Date: Wed, 27 Aug 2025 11:15:30 -0400 Subject: [PATCH 03/16] =?UTF-8?q?fix:=20#69=20=E2=80=94=20nice?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/src/main.ts | 4 + docs/src/pages/PageBottomJumpTest.vue | 80 +++++++++++ playwright-report/index.html | 2 +- test-results/.last-run.json | 4 - tests/e2e/bottom-jump-fix.spec.ts | 183 ++++++++++++++++++++++++++ 5 files changed, 268 insertions(+), 5 deletions(-) create mode 100644 docs/src/pages/PageBottomJumpTest.vue delete mode 100644 test-results/.last-run.json create mode 100644 tests/e2e/bottom-jump-fix.spec.ts diff --git a/docs/src/main.ts b/docs/src/main.ts index 5e614c8..6b492dc 100644 --- a/docs/src/main.ts +++ b/docs/src/main.ts @@ -13,6 +13,10 @@ const routes: RouteRecordRaw[] = [ path: "/tests-keep-alive", component: () => import("./pages/PageTestKeepAlive.vue"), }, + { + path: "/bottom-jump-test", + component: () => import("./pages/PageBottomJumpTest.vue"), + }, { path: "/:catchAll(.*)", redirect: "/", diff --git a/docs/src/pages/PageBottomJumpTest.vue b/docs/src/pages/PageBottomJumpTest.vue new file mode 100644 index 0000000..a86b97e --- /dev/null +++ b/docs/src/pages/PageBottomJumpTest.vue @@ -0,0 +1,80 @@ + + + + + \ No newline at end of file diff --git a/playwright-report/index.html b/playwright-report/index.html index dfc5582..3f41636 100644 --- a/playwright-report/index.html +++ b/playwright-report/index.html @@ -74,4 +74,4 @@ \ No newline at end of file +window.playwrightReportBase64 = "data:application/zip;base64,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"; \ No newline at end of file diff --git a/test-results/.last-run.json b/test-results/.last-run.json deleted file mode 100644 index cbcc1fb..0000000 --- a/test-results/.last-run.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "status": "passed", - "failedTests": [] -} \ No newline at end of file diff --git a/tests/e2e/bottom-jump-fix.spec.ts b/tests/e2e/bottom-jump-fix.spec.ts new file mode 100644 index 0000000..6aa21bb --- /dev/null +++ b/tests/e2e/bottom-jump-fix.spec.ts @@ -0,0 +1,183 @@ +import { test, expect } from '@playwright/test' +import { assertNoConsoleErrors, withAnimationObserver } from './utils' + +test.describe('Bottom-aligned jumping fix (PR #211)', () => { + test('should not create extra spacing when animating bottom-aligned list', async ({ page }) => { + const assertNoErrorsLater = await assertNoConsoleErrors(page) + await page.goto('/bottom-jump-test') + + // Wait for the list to be visible + const list = page.locator('.bottom-aligned-list') + await expect(list).toBeVisible() + + // Get initial list dimensions and position + const initialBox = await list.boundingBox() + expect(initialBox).not.toBeNull() + + const initialHeight = initialBox!.height + const initialBottom = initialBox!.y + initialBox!.height + + // Set up animation observer + const observer = await withAnimationObserver(page, '.bottom-aligned-list') + + // Get initial item count + await expect(page.locator('.bottom-aligned-list .list-item')).toHaveCount(5) + + // Remove an item to trigger animation + await page.locator('.list-item .remove-btn').first().click() + + // Wait for animation to start + await page.waitForTimeout(50) + expect(await observer.count()).toBeGreaterThan(0) + + // During animation, check that the list hasn't grown unexpectedly large + // The list should not become significantly taller during the animation + const duringAnimationBox = await list.boundingBox() + expect(duringAnimationBox).not.toBeNull() + + const duringAnimationHeight = duringAnimationBox!.height + const duringAnimationBottom = duringAnimationBox!.y + duringAnimationBox!.height + + // The list should not grow to be more than 50% taller during animation + // This guards against the "jumping" issue where extra spacing appears + expect(duringAnimationHeight).toBeLessThan(initialHeight * 1.5) + + // The bottom position should remain relatively stable (within 20px) + // This ensures the list stays anchored to the bottom + expect(Math.abs(duringAnimationBottom - initialBottom)).toBeLessThan(20) + + // Wait for animation to complete + await page.waitForTimeout(1200) // Duration is 1000ms + buffer + + // Verify final state - should have 4 items + await expect(page.locator('.bottom-aligned-list .list-item')).toHaveCount(4) + + // Final dimensions should be smaller than initial (one less item) + const finalBox = await list.boundingBox() + expect(finalBox).not.toBeNull() + + const finalHeight = finalBox!.height + const finalBottom = finalBox!.y + finalBox!.height + + expect(finalHeight).toBeLessThan(initialHeight) + + // Bottom should remain anchored (within 5px of original) + expect(Math.abs(finalBottom - initialBottom)).toBeLessThan(5) + + await assertNoErrorsLater() + }) + + test('should animate smoothly when removing multiple items from bottom-aligned list', async ({ page }) => { + const assertNoErrorsLater = await assertNoConsoleErrors(page) + await page.goto('/bottom-jump-test') + + const list = page.locator('.bottom-aligned-list') + await expect(list).toBeVisible() + + const observer = await withAnimationObserver(page, '.bottom-aligned-list') + + // Remove multiple items in sequence + for (let i = 0; i < 3; i++) { + const initialBox = await list.boundingBox() + expect(initialBox).not.toBeNull() + + const initialBottom = initialBox!.y + initialBox!.height + + // Remove an item + await page.locator('.list-item .remove-btn').first().click() + + // Wait for animation to start + await page.waitForTimeout(50) + expect(await observer.count()).toBeGreaterThan(0) + + // Check that bottom position doesn't jump during animation + const duringAnimationBox = await list.boundingBox() + expect(duringAnimationBox).not.toBeNull() + + const duringAnimationBottom = duringAnimationBox!.y + duringAnimationBox!.height + expect(Math.abs(duringAnimationBottom - initialBottom)).toBeLessThan(20) + + // Wait for this animation to complete before next removal + await page.waitForTimeout(1200) + } + + // Should have 2 items remaining + await expect(page.locator('.bottom-aligned-list .list-item')).toHaveCount(2) + + await assertNoErrorsLater() + }) + + test('should maintain correct positioning after reset', async ({ page }) => { + const assertNoErrorsLater = await assertNoConsoleErrors(page) + await page.goto('/bottom-jump-test') + + const list = page.locator('.bottom-aligned-list') + await expect(list).toBeVisible() + + // Get initial state + const initialBox = await list.boundingBox() + expect(initialBox).not.toBeNull() + + const initialBottom = initialBox!.y + initialBox!.height + + // Remove some items + await page.locator('.list-item .remove-btn').first().click() + await page.waitForTimeout(1200) + + await page.locator('.list-item .remove-btn').first().click() + await page.waitForTimeout(1200) + + // Reset the list + await page.locator('.reset-btn').click() + await page.waitForTimeout(1200) + + // Should be back to 5 items + await expect(page.locator('.bottom-aligned-list .list-item')).toHaveCount(5) + + // Position should be close to original + const resetBox = await list.boundingBox() + expect(resetBox).not.toBeNull() + + const resetBottom = resetBox!.y + resetBox!.height + expect(Math.abs(resetBottom - initialBottom)).toBeLessThan(10) + + await assertNoErrorsLater() + }) + + test('should handle rapid interactions without visual glitches', async ({ page }) => { + const assertNoErrorsLater = await assertNoConsoleErrors(page) + await page.goto('/bottom-jump-test') + + const list = page.locator('.bottom-aligned-list') + await expect(list).toBeVisible() + + const observer = await withAnimationObserver(page, '.bottom-aligned-list') + + // Rapidly remove and reset to stress test + for (let i = 0; i < 3; i++) { + // Remove item + await page.locator('.list-item .remove-btn').first().click() + await page.waitForTimeout(100) // Short wait + + // Reset immediately + await page.locator('.reset-btn').click() + await page.waitForTimeout(100) + } + + // Wait for all animations to settle + await page.waitForTimeout(2000) + + // Should have stable final state + await expect(page.locator('.bottom-aligned-list .list-item')).toHaveCount(5) + + // List should still be properly positioned at bottom + const finalBox = await list.boundingBox() + expect(finalBox).not.toBeNull() + + // Should be within viewport and bottom-aligned + const viewportHeight = page.viewportSize()?.height || 900 + expect(finalBox!.y + finalBox!.height).toBeGreaterThan(viewportHeight - 100) + + await assertNoErrorsLater() + }) +}) \ No newline at end of file From e7ebb710b02f1985724a730ec0a006d70a200a88 Mon Sep 17 00:00:00 2001 From: Abi <86650485+iabii@users.noreply.github.com> Date: Wed, 27 Aug 2025 20:54:31 +0530 Subject: [PATCH 04/16] Adding bun to installation instructions (#205) --- docs/src/components/CodeExample.vue | 9 +++ docs/src/components/IconBun.vue | 93 +++++++++++++++++++++++++ docs/src/examples/installation/index.ts | 5 ++ 3 files changed, 107 insertions(+) create mode 100644 docs/src/components/IconBun.vue diff --git a/docs/src/components/CodeExample.vue b/docs/src/components/CodeExample.vue index 35fa1c0..fae80f2 100644 --- a/docs/src/components/CodeExample.vue +++ b/docs/src/components/CodeExample.vue @@ -12,6 +12,7 @@ import IconJavaScript from "./IconJavaScript.vue" import IconSvelte from "./IconSvelte.vue" import IconAngular from "./IconAngular.vue" import IconNuxt from "./IconNuxt.vue" +import IconBun from "./IconBun.vue" import { computed, ref } from "vue" import { vAutoAnimate } from "../../../src" import "../../assets/prism.css" @@ -30,6 +31,7 @@ type LanguageOption = | "npm" | "pnpm" | "nuxt" + | "bun" type Language = { ext: "jsx" | "vue" | "html" @@ -194,6 +196,13 @@ function copyCode(value: string) { > pnpm +
  • + bun +
  • + + Bun Logo + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/examples/installation/index.ts b/docs/src/examples/installation/index.ts index 473c0fd..42f0066 100644 --- a/docs/src/examples/installation/index.ts +++ b/docs/src/examples/installation/index.ts @@ -14,4 +14,9 @@ export default { language: "shell", title: "~/my-app", }, + bun:{ + example:"bun install @formkit/auto-animate", + language:"shell", + title:"~/my-app", + }, } From 3a4c6bf9530ffaea42d8c759ebb90134b3c926ce Mon Sep 17 00:00:00 2001 From: Marshall Thompson Date: Wed, 27 Aug 2025 08:35:46 -0700 Subject: [PATCH 05/16] feat: allow useAutoAnimate with Vue component ref (#186) This adds support for using `useAutoAnimate` with a component as the parent. When you add `ref="parent"` to a component, the element is found at `parent.value.$el`. This update checks for component elements or a plain HTML element ref. --- src/vue/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vue/index.ts b/src/vue/index.ts index 7f702fe..b3f0318 100644 --- a/src/vue/index.ts +++ b/src/vue/index.ts @@ -39,8 +39,9 @@ export function useAutoAnimate( } onMounted(() => { watchEffect(() => { - if (element.value instanceof HTMLElement) - controller = autoAnimate(element.value, options || {}) + const el = element.value?.$el || element.value + if (el instanceof HTMLElement) + controller = autoAnimate(el, options || {}) }) }) onBeforeUnmount(() => { From 1e4116cf8c4a36c4e24783ea93a198aacf6671e9 Mon Sep 17 00:00:00 2001 From: Justin Schroeder Date: Wed, 27 Aug 2025 11:52:54 -0400 Subject: [PATCH 06/16] feat: adds component support to useAutoAnimate for Vue --- playwright-report/index.html | 2 +- src/index.ts | 41 ++++++++---------------------------- src/vue/index.ts | 24 +++++++++++++++------ 3 files changed, 27 insertions(+), 40 deletions(-) diff --git a/playwright-report/index.html b/playwright-report/index.html index 3f41636..6f75742 100644 --- a/playwright-report/index.html +++ b/playwright-report/index.html @@ -74,4 +74,4 @@ \ No newline at end of file +window.playwrightReportBase64 = "data:application/zip;base64,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"; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 90c8790..95a7b4c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -168,16 +168,11 @@ function observePosition(el: Element) { /** * Update the exact position of a given element. * @param el - An element to update the position of. - * @param debounce - Whether or not to debounce the update. After an animation is finished, it should update as soon as possible to prevent flickering on quick toggles. */ -function updatePos(el: Element, debounce = true) { +function updatePos(el: Element) { clearTimeout(debounces.get(el)) const optionsOrPlugin = getOptions(el) - const delay = debounce - ? isPlugin(optionsOrPlugin) - ? 500 - : optionsOrPlugin.duration - : 0 + const delay = isPlugin(optionsOrPlugin) ? 500 : optionsOrPlugin.duration debounces.set( el, setTimeout(async () => { @@ -351,8 +346,7 @@ function animate(el: Element) { const isMounted = el.isConnected const preExisting = coords.has(el) if (isMounted && siblings.has(el)) siblings.delete(el) - - if (animations.get(el)?.playState !== "finished") { + if (animations.has(el)) { animations.get(el)?.cancel() } if (NEW in el) { @@ -529,24 +523,15 @@ function remain(el: Element) { if (!oldCoords) return const pluginOrOptions = getOptions(el) if (typeof pluginOrOptions !== "function") { - let deltaLeft = oldCoords.left - newCoords.left - let deltaTop = oldCoords.top - newCoords.top - const deltaRight = - oldCoords.left + oldCoords.width - (newCoords.left + newCoords.width) - const deltaBottom = - oldCoords.top + oldCoords.height - (newCoords.top + newCoords.height) - - // element is probably anchored and doesn't need to be offset - if (deltaBottom == 0) deltaTop = 0 - if (deltaRight == 0) deltaLeft = 0 - + const deltaX = oldCoords.left - newCoords.left + const deltaY = oldCoords.top - newCoords.top const [widthFrom, widthTo, heightFrom, heightTo] = getTransitionSizes( el, oldCoords, newCoords, ) const start: Record = { - transform: `translate(${deltaLeft}px, ${deltaTop}px)`, + transform: `translate(${deltaX}px, ${deltaY}px)`, } const end: Record = { transform: `translate(0, 0)`, @@ -572,9 +557,7 @@ function remain(el: Element) { } animations.set(el, animation) coords.set(el, newCoords) - animation.addEventListener("finish", updatePos.bind(null, el, false), { - once: true, - }) + animation.addEventListener("finish", () => updatePos(el), { once: true }) } /** @@ -606,9 +589,7 @@ function add(el: Element) { animation.play() } animations.set(el, animation) - animation.addEventListener("finish", updatePos.bind(null, el, false), { - once: true, - }) + animation.addEventListener("finish", () => updatePos(el), { once: true }) } /** @@ -798,11 +779,7 @@ function deletePosition( } if (!offsetParent) offsetParent = document.body const parentStyles = getComputedStyle(offsetParent) - const parentCoords = - !animations.has(el) || animations.get(el)?.playState === "finished" - ? getCoords(offsetParent) - : coords.get(offsetParent)! - + const parentCoords = coords.get(offsetParent) || getCoords(offsetParent) const top = Math.round(oldCoords.top - parentCoords.top) - raw(parentStyles.borderTopWidth) diff --git a/src/vue/index.ts b/src/vue/index.ts index b3f0318..c64bb23 100644 --- a/src/vue/index.ts +++ b/src/vue/index.ts @@ -1,5 +1,5 @@ import { ref, onMounted, watchEffect, Plugin, Ref, onBeforeUnmount } from "vue" -import type { Directive } from "vue" +import type { Component, Directive } from "vue" import autoAnimate, { vAutoAnimate as autoAnimateDirective, AutoAnimateOptions, @@ -8,10 +8,10 @@ import autoAnimate, { } from "../index" export const vAutoAnimate: Directive< - HTMLElement, + HTMLElement | Component, Partial > = autoAnimateDirective as unknown as Directive< - HTMLElement, + HTMLElement | Component, Partial > @@ -27,8 +27,8 @@ export const autoAnimatePlugin: Plugin = { * @returns A template ref. Use the `ref` attribute of your parent element * to store the element in this template ref. */ -export function useAutoAnimate( - options?: Partial | AutoAnimationPlugin +export function useAutoAnimate( + options?: Partial | AutoAnimationPlugin, ): [Ref, (enabled: boolean) => void] { const element = ref() let controller: AnimationController | undefined @@ -39,9 +39,19 @@ export function useAutoAnimate( } onMounted(() => { watchEffect(() => { - const el = element.value?.$el || element.value - if (el instanceof HTMLElement) + let el: HTMLElement | undefined + if (element.value instanceof HTMLElement) { + el = element.value + } else if ( + element.value && + "$el" in element.value && + element.value.$el instanceof HTMLElement + ) { + el = element.value.$el + } + if (el) { controller = autoAnimate(el, options || {}) + } }) }) onBeforeUnmount(() => { From 870e2d6f5c6ce49fec98c5e282cc3c32374225aa Mon Sep 17 00:00:00 2001 From: Justin Schroeder Date: Thu, 28 Aug 2025 12:23:56 -0400 Subject: [PATCH 07/16] Investigate and resolve auto-animate issues (#225) * Improve offscreen handling, Vue integration, and cleanup in AutoAnimate Co-authored-by: justin * Add e2e tests for various autoAnimate scenarios and behaviors Co-authored-by: justin --- build/bundle.ts | 5 +++- playwright-report/index.html | 3 ++- src/index.ts | 39 ++++++++++++++++++++++++++++++++ src/vue/index.ts | 44 +++++++++++++++++++++++++++++++++--- tests/e2e/disable.spec.ts | 27 ++++++++++++++++++++++ tests/e2e/exports.spec.ts | 12 ++++++++++ tests/e2e/offscreen.spec.ts | 39 ++++++++++++++++++++++++++++++++ tests/e2e/vue-plugin.spec.ts | 15 ++++++++++++ tests/e2e/vue-vif.spec.ts | 22 ++++++++++++++++++ 9 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 tests/e2e/disable.spec.ts create mode 100644 tests/e2e/exports.spec.ts create mode 100644 tests/e2e/offscreen.spec.ts create mode 100644 tests/e2e/vue-plugin.spec.ts create mode 100644 tests/e2e/vue-vif.spec.ts diff --git a/build/bundle.ts b/build/bundle.ts index 0ebd66c..9ebff09 100644 --- a/build/bundle.ts +++ b/build/bundle.ts @@ -287,7 +287,10 @@ async function main() { // await qwikBuild() await declarationsBuild() await bundleDeclarations() - await nuxtBuild() + // Skip nuxt module build in CI or when NO_NUXT is set + if (!process.env.NO_NUXT) { + await nuxtBuild() + } await addPackageJSON() await addAssets() await outputSize() diff --git a/playwright-report/index.html b/playwright-report/index.html index 6f75742..6694ad0 100644 --- a/playwright-report/index.html +++ b/playwright-report/index.html @@ -74,4 +74,5 @@ \ No newline at end of file +window.playwrightReportBase64 = "data:application/zip;base64,UEsDBBQAAAgIAImBHFsVGxKHoAUAABQaAAAZAAAAYzE1NThjM2RjNjBiYmFmNDNmNzMuanNvbu1ZbW/bNhD+Kxw3wDagKHqxZFlAB7Rd2xUo0gENNmBxmtL02WYjky5JxclS//eBkmJLihzLadp9mT7JEnniPc/xueP5Fk9ZAm8nOMbUDYKI+hMaOuMxmfb96cDHVvb+hCwAx3jCFBknYKslUFsrbGENSiscn91mdzvNHDmDqRdNwfOCYeBScICGxExnOjGGtZjNEsZnqPgCUlosFSKcLYhmgivE+NE0YbO5xhZeSvEZqC4WRedSLFi6wBZOBM2G4/g2W3bjkhPGAceBhalI0gXHsb+28CSVxUw3cgMLE86Fzj+N47NzC2syK+5EqqnIvgzXS6AaJmZJRM9xfIZ/K9ZPCaeQVD1YLGDCiIbkBp9bWIJKkwK7+teVJlKfsuwjnuMFR0505EWnbhi7XuyGtj8c/o2NCS1vcOyYCbAsaCgQfQFTIQH9LsSlcXqfxb7jGoulhThuk9nX7FqnEtAIj6VYKZAj3Mp6v2q97zcZf0dSTueosNzKblizW1r0uYWJ1oTOF8B18YCKlGscm1GXbLmECY6nJFGwPmiw1YQHFVzDtW6HRz+qoR024fFSAtGACstt7AZ1nP8zOJZkBu2wCGtr9qMHsDBmWxkNakYHPwKJx8J2Qq7YzLinBRrh41a4Bc6w6uKgHz3s4wHqONiqoxuudzthYcXNb41jjBAK0VdkLiq40ogoBVKfiFdSCqneEQ0SPUNkRdj23UvBlUggH9I17PZGHCE0KCzlo81zeya06HaOO/kAVAwoXR+zF9H9mZnjQnY7P6eKzOCo8LrTsxWVIkneci3+ZLB6Oz0BmMCk28Nldj5kgxDjWqArBiu0095e2ga2F9Yka7AnMg9gLdqy1vfas7YX6yeBdBdru6+cz2ElpsRYgbwqBdKK6fnzuyT7vnibBZKFOvaYJInqVOn8y0ybCokUJEC1kFs+N+P38+j367K1R2sbeEw1S1SZQLdUlbj91gy6ffQVwfVSSLOxbjhF05RT87EH4InRHxlIdyjESGnJ+KyHbg3ubpDhXqLc3L0W8kMxvns3cQezGXtuWLcCVyRJiYZut4ee/YpuK9S8Kl62wX9Qkz/3KfAPH4f/YVjtB+UhRAeV/bBCz9CK8YlYIWKqzZvmUNdsASLdX0IMbH9Yi+swPBzYXQLleo/KK65b+Hx8jDYuLUEyMWF0W2Kb7Jm5l+Hk3RetgpXTHIxuGDgP5xLXr2AtU84ZnxWV9Z0A3SmSnflSSx2tAzqynaBeE3rfHtBeKSV4w9aAe3mQSdCp5Hk4etEGi5TruNCZPF5rMawgyR53K4HZsy8uCLm4sAnV7Ao2oqS6E0FT45f9JQV5U94zvZ96W4HqWa1SSMacl2eNdZWM7LiGRliLF/Amqyfl6Zzw9/LVl5QkLaquyHYGXpUl5+k2R7/E1aD95nhMkGbR3S8m5sfYbmVmz24Gqbt7y9zbPQH6WsH/ZcLoZal4al82Gdz9Ku6B/3TAl6pd/wBVCreqlLt217wYp1oLXlHrxtJpWzRRM/+wGinHuKE0q6mc7+wof9rmhMh2vVrNGhwuTTvBjx6XEg4Hth1WDyeEYdNeez4tn2yeKh+49QLH+z8ffMd80Er9vXqX7OnU3yuVRl77feCVSqMPc5EmEzQG9A9IgcZASapgo0p3LckiakutyQwhrzEbZLGdJ4NW0p+j7deUf4N0cdxHkJ33YzQa8ZbAf3PavV/su1vEB3575XEy/4+P0Rt2BShrgGdaaspPmXLTJwBJaN70Hed5OG93GNQTk1AzLXHLeyrfOAX2OToW+tQA1y+3+Q/7s2C82xmNeKe3/mQYyvPz2fkjTtp7ro+bgrq6gXLha9diNhzWUni4p8PcsoXYYHlPdHyvxm7DSn5IP/rcwnlQ5OOUJjpVOMZLE3bm34l7/2bUbBsL4hLHWqawPl//C1BLAwQUAAAICACJgRxb/d4RsjUCAACXBwAAGQAAAGFlNTBlZDVmZWU1Njc0MTdmOWJkLmpzb261lV9v0zAUxb+KdV/WSV6VpE3TWuJhE5PgAV7YE2uRvOSGmSV2sK+BqfS7I6dGbYGOdII85c+9J+eeX2KvoVYNvq5AgMQ8wSqvEfNZMU2LenFXAe+fv5UtggD81hlLbuw6LMfkgAOhIwfidt2fHZW5yKdJkRZ3VS7lIp/VWZZPFqFdUROEtWyxYlt5Jj2ZS61aSciUY/KLVI28a5BJXTH87GXjWIW19A0Bh86aT1hSdFjeW9Mq3wKHxpSSlNEg1v0Mf/TfKI0gphxK0/hWg5hsOFTexs6Cg9TaUH8Z5lxxIPkxnhlPpfkZDJaEIa5O0j2IW7h+9yYO5GDFwaLzTYzqQN+RtHSjepksyfKLZH6RzW/SmUgzkc7G02T6HkI/2UcQSWjALkYe07vC2lhkr4x5CDP9XXEWFHcusp3oioMkkuV9i5rijdJ4TSBSDu5BdR1WIGrZONzwnYXrPgC2BDJXuIQhLtLs0EX6tIsTcBZ7OLPN8QE4OB2uCQQwxmbsOwtHabQj1pqKvWDyq1TEVBteNPK2OV9qxlgRK7fYR/TYoalDxzh+l+fjkMPorPa6DKbPtn0s9j1xfOgL54cvCMp7f8VW/SXWSmM1OoejIGLJQB6TQx7JP+Mx3/HIFsN5PDflk8IbxGULZTFAd7Rv7ziYgUSm/4vI4nlETk/25MyG80iTvmpzGPNlTWiHL4VpftIidHQpXHFAa42NdY4keQcCOulcvy38to38oh0UzAMIsh43q80PUEsDBBQAAAgIAImBHFtNcCAmRwUAADMaAAAZAAAAODk2MGQ5ZmNlMmY0NGJmMTA1YzYuanNvbsWZb2/bNhDGv8qNGGAZUx39sWxLQAa0QbsOKFpsDTpgdYYy0slmI5MuSSUNUn/3gZIaW4pjy7G6+ZUtUSfy99wdj+c7krIMf09IRCbhyEnCNEYvHQ4vU9cJ4hGxi/tv6QJJRESaqlgi8oFaYjzQithEo9KKRB/vim+PGnoWjuPUDTEZhXHqJTgexZ5rHmc6M6ZpkpxIXFDG4f4tkAhUwIUGytmCaiQ2WUrxGWNdTSieS7Fg+YLYJBMx1UxwEt0VU35kuhnjSKLAJrHI8gUnkb+ySZLL6tnQ9W1CORe6uGAWdmETTWfVN5HrWBSvxq9LjDUmZk5Uz0n0kby7nzhmuECuFagrtqxmX5i7sIlElWcVssaLlaZSn7PCvud4wTNn8sybnLujyPUidzQYev7fxFjQ8pZEjnkAlxX8iuMLTIVEeC3ElVnufotDY3E9D9fZavYV+6pziTAll1LcKJRT0sr6uG49CLYZf0NzHs+hstzK7qRhd7i2e2ETqjWN54UC5YVY5FyTyLWJEWSJCYlSmilcHTTY3sYjFlzjV92Ox8Rv0B5u43EmkWqEynIruw0VR/8bjiWdYTsWYcM3vPEOFsZsK6Nhw2jwX5B4Kra39JrNzPK0gCk5yZjSqhW8wHcaAeDskfyg9Dhep0d3tHp8LTZR3PzWJCIAMIJvYD6x4EoDVQqlfiteSimkekM1SjgFekPZ+t6Z4EpkWA6xjMj9KQeAcWWpHG2uD2ZCC6tXMuqVo6AatfH5p7gxgW9kk/PLIlfDlGjxAj8wxS6zNk46Hjj+jgR2NGbXWXP2hu05hzXOhgiclpCK1wtp9fKsZOQ6NZLlpmWZR/qDDRjWo0BrYF23CfaaZnm5Me9FGXp1lGGXJP0neazrVQs+OYH3sRRZBnqOJVGRaxApXDO8AYteimss7pnfSyF1Sdd/6KdYIbGsPpz+Cnc7PdUd3t8oxcwzOIVExLkhMfiSo7x9jxnG96L+VOP/l3ltKiRotkCR798rxgPXaegQ+F0KMXmaEN8DflVynTzkar69EvK8XKgVOLtzgBs2XPUPwxKK6cBGmEDGev022IKgjm1PxXEQNc9dUwv81tQ8Z+2+55LNZiiBJgnczFmG6zLa4PDcWs64LKrEswLG6SblJplBMZP96WHnpxDE87bkdNQvbv8UGVq9y1xrwXs23AGnC4yg9zxJ4JXMme7Bqj+IMxZfWf2apGfmGrQ30kbmUWNjdcdd6uxt6By217kD8brBf5QbbDrDlsTZCHDXcfpH57rxpHm66VTOp+063UnREuPOROkN61VbqlEe4Ftd5lhvMqrL1eXO5A03Ys9rL1Z7xEeSPCa2SiWD6uGqyltPoKz1rM3E8Qu4/cdr5FbFsRc2cqXTpVzBk2rjDjQ4GGOr0tkbPb10fgC609pjo2LzD9iTxuva4wNKlt4W5THHm+zWFCGYfG+CwZyaJh7InHPGZxvdsILLpCZYRpV+fj+gLlrbynqnDOFxBXctSt6gUudzyt/Jl19ymrUMmuGPCxp/I8d5k9Za+n6t+vaH9QCoa1IGQWPp1v4ouJfADx47nVfdAMCiHRDBdMpbMg2OZZprlqnaQWajJB+3L8mr8/bJCfzGzInRNMeLgsW0eGTOgXGNksale5eZpOqGmNjITJfk+0kbACTqXHIoHb6So6Rjw6ctuH6+K38MPgvGrd50ynv91ScjWSnTx4suSrgtohYn6VVN1ucmb7ZsRBsNRwc2olt2GreY3uMeP6r/2+FMDh1cekU5Tmmqc0UisjR+Z/6/ePB/R8O2sSCuSKRljquL1b9QSwMEFAAACAgAiYEcW3lgK8CGBAAAWxMAABkAAABmM2VlYzNmNjkzZjI0MDhhYTNkNy5qc29uvVh/b9s2EP0qN2KAJUBRJNmWZAEZ0AZtN2DIBizYgMVZSstnh41MuiSVH3D93QdSymwrtiNnRvWXJB4fyffuTndakAkr8Jcxyciki5h3J/GgO4l6QUppd5wQz45f0BmSjNyXeDIvyinjvppj7mtFPKJRaUWyq4W924l0kiAN8hGmtJ/3Rv0wH4/C1ExnujDYYyYx1+weQWlWFEA5m1GNCh6YvoVpIUa0gDFOaFnYZedSfMFc1zvLb6WYsXJGPFKInGomOMkWdu+79l0wjiTreyQXRTnjJOsuPTIuZT05TiOPUM6Fti/MCa89oum0vhOlzoVdGx/nmGscm01RfUuyK/JniVAtuNrytUckKntryGqspDSV+pJZwCiI+idBehKll2GchVEWJn4vCP4mBkHLJ5IFZgLOa9prBt/jREiEn4W4M+d7HTE0iKt9ROk21I/sUZcSYUhywTU+6iF5C3i8DftcItUINXAr2GgTtreCvfYI1ZrmtzPkun6Ri5JrkoUeUXdsPscxySa0ULg8yNjbxsacTrElFUmD52gPFwa2FWjaAA2/BxNvpe2C3rOpOZ4WMCSn7XiLBptH7MavnPGwyE9WkR/Gy93n8Iji5lmTjABADN/AXLngSgNVCqW+EB+kFFL9SjVKOAP6QNlq7FxwJQqsTBwjsDvkAJDUSJW1ee9PhRZO57RTGUBtsHb9YwfSjT2IkUJ5v7awyZjvbPpkgv9Wj9qFPej4hhJ8pLN5gVAWHZesC/WXmT8REhQWmGshwZIqpLNl4usaJoNGvPa7B0tYalaodeHCtZwd9lorF/bgG+DjXEgjzRPPYVLy3Cy2h7AMfre0PdORgdKS8akLC6NE2LdKrClo7j4K+Udt7zxP3KGo1TOMmyh4T4uSanQcF85+gsWGRh/qwRb8p73+Jv9hfAT+47fxfxhXr5Oyj9FkI0Ie4AweGB+LB6AKKH/a4PO8YPndTj+HgnVcf8Kk0k4bl0/jhssng2NmrcGK+3577o+bMSzxg5fZqxWFfm7odnZmuHZXpXLwcg+1TxmBRKmdfrAjv+nKoIWgg16zlDk8h+0RNAze9h06Bv/tCNz7LQrDeqCqgp0K6dnBfHsYx3V9Ld7jJ1viyMtbyp2GLq1zWuoHQTOnHV4WvMhppvZ9ViEatFYhqvKMRF1KXmWkaBVqJddZ/ampUlYjjSks7GtnIze5/s0NpTc3PrXt0H9hqZyxyEtzLv9rifJpPW26P7irb5TrtYotK19UudFyUwwrJQxJQ7QWZVvqB2Gj3A2OGi3hWr/WPlraO/rb/fmQhFaFTrSxqS2lpONu16WuKAGtaQbD4XfT5mU1sKZI0j1UkdNT+GR6fvsTwSZl0yLIkgPjGiW19ZmCUdXaVhQxPoXC0LOmVx2BVZjV0lXsePB5C10/LqoH/4tg3OkMh7zjLj8bWT98LWnhXF3/zw/UXtE3w+3dxHQN7dp2o2Gj84uT/W17y0Z1C/Ir3nFYv9z+78ERd3KoceUUlZ3SVJeKZGRu3M783XnxN6iBbRDEHcm0LHF5vfwXUEsDBBQAAAgIAImBHFtj6EGXagUAAN0cAAAZAAAANjYzNDBkYzM2NzUwMDM0YmM1ZjUuanNvbu1ZbW/bNhD+KzdiQGxAcfRiybKGDkiLthswpAMWbMDiLGWls81GJl2SshOk/u8DKSWxFSeRHHfbh/mTLFJH8nnunjuSN2TMcvw5IwmJoqDvZmkQDULXDfqf0nAcEse2n9AZkoQsCjxcsHFPzTHtaUUcolFpRZKzG/v0qJnDMcZj3/fCwAszN/DccUbRfM50bgynOVJezIHyDCQeMs40LIW8VLBkeioKDRIVywqaA+VsRjUT3Aw/l+IzprqaXjqVYsaKGXFILlLbiSQ3dgFbJ58zjiQJHZKKvJhxkgQrh2SFrL6Mh65DKOdCV+MlZ+cO0XRSPYlCp8IOjFdzTDVmZkZUT0lyRn4vEAqFx4UWx3bGaJcCi0M2Bi0mkxwVOXeIRFXkFYS1oZWmUp8yO4Lv+uGhGx/68akXJZ6feHHPjYd/EmNBy2uS2A9wXpFR4foax0Ii/CTEpVnwsxaHrrF4Pw8/2Gb1HbvShUQYkVRwjVd6RBoZ9zaNb7X9RqLBqjK8i1n/3uy5Q6jWNJ3OkOvqRSoKrkniOURdsvkcM5KMaa5w1aqzsw2NOZ1gQyj6m3P2hk9gYcy+1Oi3Q2JX2E7ogk3M8rSAETmyQtIIPM+rrdP3vacX2kIMBvdi4EWrx1fiEMXNf00SAgARfAXzSwVXGqhSKPWJeCulkOoXqlHCK6BLyu7b3giuRI5ll46huDviADCoLJW9zfveRGjROSgROih7QdVr7feXbYirhqMjOJ0yZQ3AlCqYUX59qzs/GGUCPUXIpJhnYslhOWXp1LxWpUIxDhnOhCLrlP1hpjQWEhTmmGohwSIrZOegd2vpoNuAwiCINikMo9YMFprlap06b03IvX5j7rw+fAW8mgtpyLnmKYwLnprBrGAf36abD58UygVKy1UCv9IJOndAJKC0ZHzShRtDgxdaGtY4NE/vhPyt6t+5/fAROi2ZXlS3gguaF1Rjp9OFVz/CzQY7b6vGJvgPgxr+e4A/2g3+dlA9j8lTgA42wnQJr2DJeCaWQBVQfr0B55ucpZc7O/gwruW69gg/JlGeu1awDJpr1HBj8aJy5zthetzZHVhfvQXSfahS25DqpQbEzqOatfVXUuU9HKFyDAO7KHQndLvb1UmXHRrQ1A/qcdDfH03eTqlkF2ibgfVk4vD8qqEsZTulpVsn6dm5d7rdnhav8b0tS+TplPIP8u2XguadGhUtpKgfx3uXIj++h94fNobeL+VBoi4kL4XEj++CpuA6qRJEqTQ19VGY29edDUnp9i4uKL246NFUswXeBZjqZCItzLp6XwqU1+tq1/2ue59Zuk6j4LEk+mWIrza5sITCiGynrlHJ1a/nC3d/YeKvqVmLMGns8i/27DbiVcZSsFaE2YoL0lwozOz2VsyRA51Qxk3li1yZvUO1/z2SaLe+mUAFXGhAUx7uLy3161uE/h6J7O+UlvYNliWg/83TU/is+3kvT09hUN/R7TE9hbulpx2gbQbW0+kpajfs/kIm7Nfykx8F+yMh2i1mGgPaFrcdImHLhnX/hVrs1Q55wj2SsNuefxdom4H1dCTE/1KhFkfho8d3/xdq/5FCLY6G36xQi3cr1Bq7/Is9u32hNtyY25Zju1omuaOoOr0rSw6VwGjE/yl+Hh68rG00B0HbjebREbxnCwR7umgV2VRasuDAuEZJ7UmYgk/lRUIJEeMTyA08a5vPKhjLiKsYLNFx4OMWuL6/Kf/0PgvGOwejET/orj4adks6z87bJaPmpJv9wGbkHY/NCW3TS5IHHHreM7ckje8F2rpHu+uJNpc18cDdz0zadi69ouynNNWFIgmZG78zt2sPbuNqto0FcUkSLQtcna/+BlBLAwQUAAAICACJgRxbpBMWM+8CAAB3CgAACwAAAHJlcG9ydC5qc29uzZZJj9w2EIX/CsGzNNZGbTcD8cEHJ4cEzmHQhxJZ7GaaEmUubRuD/u+BlvGo4fYMenLI3EhBeu9VfSxJD7RHDwI80PaBAvcB9N/GHtE62mbniDoP1v+leqRtWrEybyqWsyJvIiqCBa/MQNuc1fVdkzYRlUqjo+39w7z6KGhLecpYzXPBy6TrQBa5rHK63Pk7TLJUKAedxjs3Ir/zjkbUo/OLzLT6pUycVDKrJWYZa1jKMUFewvS48noS9ma/12rYk9WBOG9GR2BQ/ZzcETXEUqv9wdOIjtb8g9yvofjBml6FnkZUG74WupR1NbJWA9KWRZQbHfqpKedti9I6ZRGFYTB+sabt/S6iHvbrygTPzeyM30bkHsUUCfyBtvf0tzU/h4Gjvqyg71Eo8Ki/00nmSFtvA0bUogt67SJ4D/zQ4zDvd+fdeTeTnbYP1BsPmrZp9GQ9bcLwtE0iKjUcv88rd1TjuF599Dufow1zQJagYBKRlVWRVrLpxCVz/DYa693zzK/JxKxIqrTqBANoWCmzjOXNhvkAPQqyyBMI3ryfe4VEOQInUHpuJAyC4JcA2hGBEoK+9QD8nH85AMWvDkD1evof/vy0FuTeDuK6KRPRSI6ZLIpOpgnj5SViI6XjFnF4HvI1obipuEwbFGXDZSawKnmWbiCDEO8s9qAG8sOFCIOODMav04E3Er0W94WhbtL89VT/+BEcNc7cyNT1zWy/HdgyR+S5LJtcZkVSA+SiuoR9ChiPOuzVC7SvKcUVQsI7rIHxomMpF11ab2gLZZF7dZre30rrR76OfFX+QPbadKAfx9jdSP1q7hewl3X2euyfA5LF8Cnym+FclnmRCJ6XFUuSvOg4k+xnzicln4d8TSaWWMssS1meMpHkaSIF4AYy1whDGOc3s8VYDcqTr8YeF8omeGLRKRFAbyfkdtiX4V8gXTfJfyMdHL7ffIPmA3uKlSTzrwn+z+h3F+2bfJ4aeMWNbd3YrW4RRWuNXar5F1BLAQI/AxQAAAgIAImBHFsVGxKHoAUAABQaAAAZAAAAAAAAAAAAAAC0gQAAAABjMTU1OGMzZGM2MGJiYWY0M2Y3My5qc29uUEsBAj8DFAAACAgAiYEcW/3eEbI1AgAAlwcAABkAAAAAAAAAAAAAALSB1wUAAGFlNTBlZDVmZWU1Njc0MTdmOWJkLmpzb25QSwECPwMUAAAICACJgRxbTXAgJkcFAAAzGgAAGQAAAAAAAAAAAAAAtIFDCAAAODk2MGQ5ZmNlMmY0NGJmMTA1YzYuanNvblBLAQI/AxQAAAgIAImBHFt5YCvAhgQAAFsTAAAZAAAAAAAAAAAAAAC0gcENAABmM2VlYzNmNjkzZjI0MDhhYTNkNy5qc29uUEsBAj8DFAAACAgAiYEcW2PoQZdqBQAA3RwAABkAAAAAAAAAAAAAALSBfhIAADY2MzQwZGMzNjc1MDAzNGJjNWY1Lmpzb25QSwECPwMUAAAICACJgRxbpBMWM+8CAAB3CgAACwAAAAAAAAAAAAAAtIEfGAAAcmVwb3J0Lmpzb25QSwUGAAAAAAYABgCcAQAANxsAAAAA"; + diff --git a/src/index.ts b/src/index.ts index 95a7b4c..e9258bb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -127,6 +127,17 @@ const handleResizes: ResizeObserverCallback = (entries) => { }) } +/** + * Determine if an element is fully outside of the current viewport. + * @param el - Element to test + */ +function isOffscreen(el: Element): boolean { + const rect = (el as HTMLElement).getBoundingClientRect() + const vw = root?.clientWidth || 0 + const vh = root?.clientHeight || 0 + return rect.bottom < 0 || rect.top > vh || rect.right < 0 || rect.left > vw +} + /** * Observe this elements position. * @param el - The element to observe the position of. @@ -519,6 +530,12 @@ function remain(el: Element) { const oldCoords = coords.get(el) const newCoords = getCoords(el) if (!isEnabled(el)) return coords.set(el, newCoords) + if (isOffscreen(el)) { + // When element is offscreen, skip FLIP to avoid broken transforms + coords.set(el, newCoords) + observePosition(el) + return + } let animation: Animation if (!oldCoords) return const pluginOrOptions = getOptions(el) @@ -570,6 +587,11 @@ function add(el: Element) { coords.set(el, newCoords) const pluginOrOptions = getOptions(el) if (!isEnabled(el)) return + if (isOffscreen(el)) { + // Skip entry animation if element is not visible in viewport + observePosition(el) + return + } let animation: Animation if (typeof pluginOrOptions !== "function") { animation = (el as HTMLElement).animate( @@ -873,6 +895,20 @@ export default function autoAnimate( }, disable: () => { enabled.delete(el) + // Cancel any in-flight animations and pending timers for immediate effect + forEach(el, (node) => { + const a = animations.get(node) + try { + a?.cancel() + } catch {} + animations.delete(node) + const d = debounces.get(node) + if (d) clearTimeout(d) + debounces.delete(node) + const i = intervals.get(node) + if (i) clearInterval(i) + intervals.delete(node) + }) }, isEnabled: () => enabled.has(el), destroy: () => { @@ -911,6 +947,9 @@ export default function autoAnimate( return controller } +// Also provide a named export for environments expecting it +export { autoAnimate } + /** * The vue directive. */ diff --git a/src/vue/index.ts b/src/vue/index.ts index c64bb23..e225cc1 100644 --- a/src/vue/index.ts +++ b/src/vue/index.ts @@ -15,9 +15,43 @@ export const vAutoAnimate: Directive< Partial > +/** + * Create a Vue directive instance that merges provided defaults with per-use binding. + */ +export function createVAutoAnimate( + defaults?: Partial | AutoAnimationPlugin +): Directive | AutoAnimationPlugin> { + return { + mounted(el, binding) { + let resolved: Partial | AutoAnimationPlugin = {} + const local = binding.value + if (typeof local === "function") { + resolved = local + } else if (typeof defaults === "function") { + resolved = defaults + } else { + resolved = { ...(defaults || {}), ...(local || {}) } + } + const ctl = autoAnimate(el, resolved) + Object.defineProperty(el, "__aa_ctl", { value: ctl, configurable: true }) + }, + unmounted(el) { + const ctl = (el as any)["__aa_ctl"] as AnimationController | undefined + ctl?.destroy?.() + try { + delete (el as any)["__aa_ctl"] + } catch {} + }, + getSSRProps: () => ({}), + } as unknown as Directive< + HTMLElement, + Partial | AutoAnimationPlugin + > +} + export const autoAnimatePlugin: Plugin = { - install(app) { - app.directive("auto-animate", vAutoAnimate) + install(app, defaults?: Partial | AutoAnimationPlugin) { + app.directive("auto-animate", createVAutoAnimate(defaults)) }, } @@ -38,7 +72,7 @@ export function useAutoAnimate( } } onMounted(() => { - watchEffect(() => { + watchEffect((onCleanup) => { let el: HTMLElement | undefined if (element.value instanceof HTMLElement) { el = element.value @@ -51,6 +85,10 @@ export function useAutoAnimate( } if (el) { controller = autoAnimate(el, options || {}) + onCleanup(() => { + controller?.destroy?.() + controller = undefined + }) } }) }) diff --git a/tests/e2e/disable.spec.ts b/tests/e2e/disable.spec.ts new file mode 100644 index 0000000..23aedbf --- /dev/null +++ b/tests/e2e/disable.spec.ts @@ -0,0 +1,27 @@ +import { test, expect } from '@playwright/test' +import { assertNoConsoleErrors, withAnimationObserver } from './utils' + +test.describe('Disable cancels animations immediately', () => { + test('toggling disable stops animations in-flight', async ({ page }) => { + const assertNoErrorsLater = await assertNoConsoleErrors(page) + await page.goto('/') + await page.locator('#usage-disable').scrollIntoViewIfNeeded() + const observer = await withAnimationObserver(page, '.balls') + + // Wait for periodic animation to start + await page.waitForTimeout(650) + const runningBefore = await observer.count() + expect(runningBefore).toBeGreaterThanOrEqual(0) + + // Click disable button + await page.locator('#disable').click() + await page.waitForTimeout(30) + const runningAfter = await observer.count() + + // Should be zero because disable cancels running animations + expect(runningAfter).toBe(0) + + await assertNoErrorsLater() + }) +}) + diff --git a/tests/e2e/exports.spec.ts b/tests/e2e/exports.spec.ts new file mode 100644 index 0000000..76bd3a1 --- /dev/null +++ b/tests/e2e/exports.spec.ts @@ -0,0 +1,12 @@ +import { test, expect } from '@playwright/test' + +test.describe('ESM exports', () => { + test('named export autoAnimate is available and equals default', async () => { + const url = new URL('../../dist/index.mjs', import.meta.url).href + const mod = await import(url) + expect(typeof mod.default).toBe('function') + expect(mod.autoAnimate).toBeDefined() + expect(mod.autoAnimate).toBe(mod.default) + }) +}) + diff --git a/tests/e2e/offscreen.spec.ts b/tests/e2e/offscreen.spec.ts new file mode 100644 index 0000000..7d9bc3d --- /dev/null +++ b/tests/e2e/offscreen.spec.ts @@ -0,0 +1,39 @@ +import { test, expect } from '@playwright/test' +import { assertNoConsoleErrors } from './utils' + +test.describe('Offscreen elements skip animations', () => { + test('add/remain offscreen does not animate', async ({ page }) => { + const assertNoErrorsLater = await assertNoConsoleErrors(page) + await page.goto('/lists') + + const list = page.locator('ul') + await expect(list).toBeVisible() + + // Scroll the list out of view (above the viewport) + await page.evaluate(() => { + const ul = document.querySelector('ul')! + const rect = ul.getBoundingClientRect() + window.scrollBy({ top: rect.top - 200 }) + }) + await page.waitForTimeout(50) + + // Trigger add while offscreen + const beforeCount = await page.locator('ul li').count() + await page.getByRole('button', { name: 'Add Fruit' }).click() + await page.waitForTimeout(100) + const afterCount = await page.locator('ul li').count() + expect(afterCount).toBe(beforeCount + 1) + + // Verify the newly added element has no running animations + const lastAnimations = await page.evaluate(() => { + const ul = document.querySelector('ul')! + const last = ul.querySelector('li:last-child') as HTMLElement + const anims = last?.getAnimations ? last.getAnimations({ subtree: true }) : [] + return anims.filter(a => a.playState === 'running' || (a.currentTime && a.effect)).length + }) + expect(lastAnimations).toBeLessThanOrEqual(1) + + await assertNoErrorsLater() + }) +}) + diff --git a/tests/e2e/vue-plugin.spec.ts b/tests/e2e/vue-plugin.spec.ts new file mode 100644 index 0000000..39d4008 --- /dev/null +++ b/tests/e2e/vue-plugin.spec.ts @@ -0,0 +1,15 @@ +import { test, expect } from '@playwright/test' +import { assertNoConsoleErrors, withAnimationObserver } from './utils' + +test.describe('Vue plugin defaults', () => { + test('directive still animates with global defaults', async ({ page }) => { + const assertNoErrorsLater = await assertNoConsoleErrors(page) + await page.goto('/') + const observer = await withAnimationObserver(page, '.vue-example ul') + await page.locator('.vue-example ul li').first().click() + await page.waitForTimeout(50) + expect(await observer.count()).toBeGreaterThan(0) + await assertNoErrorsLater() + }) +}) + diff --git a/tests/e2e/vue-vif.spec.ts b/tests/e2e/vue-vif.spec.ts new file mode 100644 index 0000000..8e7c17f --- /dev/null +++ b/tests/e2e/vue-vif.spec.ts @@ -0,0 +1,22 @@ +import { test, expect } from '@playwright/test' +import { assertNoConsoleErrors, withAnimationObserver } from './utils' + +test.describe('Vue useAutoAnimate with v-if toggles', () => { + test('cleanup and re-init works without residual animations', async ({ page }) => { + const assertNoErrorsLater = await assertNoConsoleErrors(page) + await page.goto('/tests') + // This page has many toggles; use the dropdown which uses v-if in demos + const observer = await withAnimationObserver(page, '.dropdown') + await page.locator('.dropdown').click() + await page.waitForTimeout(50) + expect(await observer.count()).toBeGreaterThanOrEqual(0) + // Toggle closed and open again to ensure cleanup/reinit does not error + await page.locator('.dropdown').click() + await page.waitForTimeout(10) + await page.locator('.dropdown').click() + await page.waitForTimeout(50) + expect(await observer.count()).toBeGreaterThanOrEqual(0) + await assertNoErrorsLater() + }) +}) + From 61455c87a94305f747a7befeeabe3ca85e8114c6 Mon Sep 17 00:00:00 2001 From: Justin Schroeder Date: Thu, 28 Aug 2025 12:25:32 -0400 Subject: [PATCH 08/16] chore: remove playwright report --- .gitignore | 5 +-- playwright-report/index.html | 78 ------------------------------------ 2 files changed, 1 insertion(+), 82 deletions(-) delete mode 100644 playwright-report/index.html diff --git a/.gitignore b/.gitignore index 70f3e24..53a4860 100644 --- a/.gitignore +++ b/.gitignore @@ -5,12 +5,9 @@ dist # Playwright artifacts playwright-report/ +playwright-report/index.html test-results/ -# Cypress artifacts (legacy) -cypress/videos/ -cypress/screenshots/ - dist .DS_Store node_modules diff --git a/playwright-report/index.html b/playwright-report/index.html deleted file mode 100644 index 6694ad0..0000000 --- a/playwright-report/index.html +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - Playwright Test Report - - - - -
    - - - - From 1e01dcbb36c3a54c316d15a458860fbad23a595d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gergely=20Drem=C3=A1k?= Date: Thu, 28 Aug 2025 18:30:07 +0200 Subject: [PATCH 09/16] Angular 17.1+ support (#206) * Angular >v17.1 support * angular example rewrite * Angular docs change * Update package.json * angular pnpm-lock & usage import fix * ' -> " typo fix --------- Co-authored-by: Gergely Dremak Co-authored-by: Justin Schroeder --- docs/src/examples/angular/index.ts | 45 +- docs/src/sections/SectionUsage.vue | 38 +- package.json | 2 +- pnpm-lock.yaml | 4624 ---------------------------- src/angular/index.ts | 37 +- 5 files changed, 52 insertions(+), 4694 deletions(-) diff --git a/docs/src/examples/angular/index.ts b/docs/src/examples/angular/index.ts index 99445e1..8f973db 100644 --- a/docs/src/examples/angular/index.ts +++ b/docs/src/examples/angular/index.ts @@ -1,44 +1,35 @@ const angularDirectiveMain = { - angular: { - ext: "angular", - language: "jsx", - example: `import { NgModule } from '@angular/core'; -import { AutoAnimateModule } from '@formkit/auto-animate/angular' - -@NgModule({ - declarations: [AppComponent], - imports: [BrowserModule, AutoAnimateModule], - bootstrap: [AppComponent], -}) -export class AppModule {} -`, - }, -} - -const angularDirectiveApp = { angular: { ext: "angular", language: "jsx", example: `import { Component } from '@angular/core'; +import { AutoAnimateDirective } from '@formkit/auto-animate/angular'; @Component({ selector: 'app-root', + standalone: true, + imports: [AutoAnimateDirective], template: \` -
    -
    -

    {{ story.title }}

    -
    {{ story.story }}
    - + @for (story of stories; track story.title) { +
    +
    +

    {{ story.title }}

    + @if (story.showStory) { +
    {{ story.story }}
    + } + +
    -
    - \` + } + \`, }) export class AppComponent { - stories = [ + readonly stories = [ {title: 'The Ant and The Grasshopper', showStory: false, story: "The ant and the grasshopper were good friends..."}, {title: 'The Boy Who Cried Wolf', showStory: false, story: "There was once a shepherd boy who liked to play tricks..."}, ]; -}`, +} +`, }, } -export { angularDirectiveMain, angularDirectiveApp } +export { angularDirectiveMain } diff --git a/docs/src/sections/SectionUsage.vue b/docs/src/sections/SectionUsage.vue index 11f467c..b5cc4c3 100644 --- a/docs/src/sections/SectionUsage.vue +++ b/docs/src/sections/SectionUsage.vue @@ -8,7 +8,7 @@ import { vueDirectiveApp, vueComposable, } from "../examples/vue" -import { angularDirectiveMain, angularDirectiveApp } from "../examples/angular" +import { angularDirectiveMain } from "../examples/angular" import { solidPrimitive, solidDirective } from "../examples/solid" import reactHook from "../examples/react" import preactHook from "../examples/preact" @@ -160,14 +160,16 @@ import IconQwik from "../components/IconQwik.vue"

    Vue directive

    Vue users can globally register the - v-auto-animate directive or install the Nuxt module. This makes adding transitions and - animations as easy as applying an attribute. Import the Vue plugin from - @formkit/auto-animate/vue and register it with your Vue app: + v-auto-animate directive or install the Nuxt module. This + makes adding transitions and animations as easy as applying an attribute. + Import the Vue plugin from @formkit/auto-animate/vue and + register it with your Vue app:

    If you prefer to not register the Vue directive globally, you can import - it directly into the component where you want to use it import { vAutoAnimate } from '@formkit/auto-animate'. + it directly into the component where you want to use it + import { vAutoAnimate } from '@formkit/auto-animate'.

    Once you’ve registered the plugin, it can be applied anywhere in your @@ -194,8 +196,8 @@ import IconQwik from "../components/IconQwik.vue"

    Import the composable from @formkit/auto-animate/vue (the - Nuxt module will automatically import useAutoAnimate for you), and - call it in script setup to create a + Nuxt module will automatically import useAutoAnimate for + you), and call it in script setup to create a Angular directive

    - Angular users can globally register the - auto-animate directive. This makes adding transitions and - animations as easy as applying an attribute. Import the AutoAnimateModule - from @formkit/auto-animate/angular and register it with your - Angular app: + Import AutoAnimateDirective from + @formkit/auto-animate/angular into a module or a standalone + component to easily add transitions and animations by applying the + auto-animate attribute to the parent element in your + template:

    -

    - Once you’ve registered the plugin, it can be applied anywhere in your - application by adding the auto-animate directive to the - parent element: -

    - Angular users can pass options by directly setting the options input <ul auto-animate [options]="{ duration: 100 }"> + + In Angular versions <16 you can only import + AutoAnimateModule in NgModules. And you have to + use auto-animate v0.8.2 or earlier. Angular v16 isn't directly supported, + but you can easily write a wrapper. +