diff --git a/.changeset/lazy-otters-shave.md b/.changeset/lazy-otters-shave.md
index 8b62691..a4ddcfb 100644
--- a/.changeset/lazy-otters-shave.md
+++ b/.changeset/lazy-otters-shave.md
@@ -16,4 +16,3 @@ the listeners down:
…
```
-
diff --git a/.changeset/soft-pianos-repeat.md b/.changeset/soft-pianos-repeat.md
new file mode 100644
index 0000000..a261ace
--- /dev/null
+++ b/.changeset/soft-pianos-repeat.md
@@ -0,0 +1,25 @@
+---
+'@ixirjs/pulse': minor
+---
+
+Move transform animations off the main thread.
+
+`animate()` drives transforms through CSS custom properties so independent
+animations compose on one element, but Chromium cannot run a custom-property
+animation on the compositor, and one such property pins everything animating
+alongside it. A dialog morph was a style recalc and a full repaint every frame.
+
+Three changes, all automatic and with no new options:
+
+- Transform-animated elements get a `will-change` hint while an animation is in
+ flight, so they are no longer repainted every frame. Every gesture takes the
+ same hint for as long as it is writing, and hands it back after.
+- When nothing else is composing on an element, the variable keyframes are
+ folded into real `translate` / `scale` / `rotate` keyframes. The fold reverses
+ itself the moment anything else touches the element, so composition is
+ unchanged.
+- Props are no longer grouped across the compositability boundary, so a
+ `width` can no longer drag a sibling `opacity` onto the main thread.
+
+One visible consequence: an `animate()` call mixing compositable and layout
+properties now produces two entries in `controller.animations` instead of one.
diff --git a/.gitignore b/.gitignore
index 294b385..1998b13 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,6 @@ Thumbs.db
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
+
+# Generated code graph (graphify update .)
+graphify-out
diff --git a/.size-limit.json b/.size-limit.json
index 5dc1964..5fb81b2 100644
--- a/.size-limit.json
+++ b/.size-limit.json
@@ -2,16 +2,16 @@
{
"name": "Root public API",
"path": "./dist/index.js",
- "limit": "19 kB"
+ "limit": "20 kB"
},
{
"name": "animate only",
"path": "./dist/animate/index.js",
- "limit": "8 kB"
+ "limit": "9 kB"
},
{
"name": "flip only",
"path": "./dist/flip/index.js",
- "limit": "7 kB"
+ "limit": "9 kB"
}
]
diff --git a/bun.lock b/bun.lock
index a4c24dd..8c7af79 100644
--- a/bun.lock
+++ b/bun.lock
@@ -6,15 +6,9 @@
"name": "@ixirjs/pulse",
"devDependencies": {
"@changesets/cli": "^2.31.0",
- "@chromatic-com/storybook": "^5.1.2",
"@eslint/compat": "^2.0.4",
"@eslint/js": "^10.0.1",
"@size-limit/preset-small-lib": "^12.1.0",
- "@storybook/addon-a11y": "^10.3.6",
- "@storybook/addon-docs": "^10.3.6",
- "@storybook/addon-svelte-csf": "^5.1.2",
- "@storybook/addon-vitest": "^10.3.6",
- "@storybook/sveltekit": "^10.3.6",
"@sveltejs/adapter-auto": "^7.0.1",
"@sveltejs/kit": "^2.57.0",
"@sveltejs/package": "^2.5.7",
@@ -25,7 +19,6 @@
"@vitest/coverage-v8": "^4.1.3",
"eslint": "^10.2.0",
"eslint-config-prettier": "^10.1.8",
- "eslint-plugin-storybook": "^10.3.6",
"eslint-plugin-svelte": "^3.17.0",
"globals": "^17.4.0",
"mdsvex": "^0.12.7",
@@ -35,7 +28,6 @@
"prettier-plugin-tailwindcss": "^0.7.2",
"publint": "^0.3.18",
"size-limit": "^12.1.0",
- "storybook": "^10.3.6",
"svelte": "^5.55.2",
"svelte-check": "^4.4.6",
"tailwindcss": "^4.2.2",
@@ -51,10 +43,6 @@
},
},
"packages": {
- "@adobe/css-tools": ["@adobe/css-tools@4.4.4", "", {}, "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg=="],
-
- "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
-
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
@@ -103,65 +91,63 @@
"@changesets/write": ["@changesets/write@0.4.0", "", { "dependencies": { "@changesets/types": "^6.1.0", "fs-extra": "^7.0.1", "human-id": "^4.1.1", "prettier": "^2.7.1" } }, "sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q=="],
- "@chromatic-com/storybook": ["@chromatic-com/storybook@5.1.2", "", { "dependencies": { "@neoconfetti/react": "^1.0.0", "chromatic": "^13.3.4", "filesize": "^10.0.12", "jsonfile": "^6.1.0", "strip-ansi": "^7.1.0" }, "peerDependencies": { "storybook": "^0.0.0-0 || ^10.1.0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0" } }, "sha512-H/hgvwC3E+OtseP2OT2QYUJH2VfnzT6wM3pWOkaNV6g7QI+VUdWJbeJ3o2jFqvEPQNqzhQKWDOlvM4lu+7is6g=="],
-
"@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="],
"@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
- "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="],
+ "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA=="],
- "@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="],
+ "@esbuild/android-arm": ["@esbuild/android-arm@0.28.0", "", { "os": "android", "cpu": "arm" }, "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ=="],
- "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.7", "", { "os": "android", "cpu": "arm64" }, "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ=="],
+ "@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.0", "", { "os": "android", "cpu": "arm64" }, "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw=="],
- "@esbuild/android-x64": ["@esbuild/android-x64@0.27.7", "", { "os": "android", "cpu": "x64" }, "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg=="],
+ "@esbuild/android-x64": ["@esbuild/android-x64@0.28.0", "", { "os": "android", "cpu": "x64" }, "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA=="],
- "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw=="],
+ "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q=="],
- "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ=="],
+ "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ=="],
- "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.7", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w=="],
+ "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q=="],
- "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ=="],
+ "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw=="],
- "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.7", "", { "os": "linux", "cpu": "arm" }, "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA=="],
+ "@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.0", "", { "os": "linux", "cpu": "arm" }, "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw=="],
- "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A=="],
+ "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A=="],
- "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.7", "", { "os": "linux", "cpu": "ia32" }, "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg=="],
+ "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ=="],
- "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q=="],
+ "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg=="],
- "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw=="],
+ "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w=="],
- "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.7", "", { "os": "linux", "cpu": "ppc64" }, "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ=="],
+ "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg=="],
- "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ=="],
+ "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ=="],
- "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.7", "", { "os": "linux", "cpu": "s390x" }, "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw=="],
+ "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q=="],
- "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.7", "", { "os": "linux", "cpu": "x64" }, "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA=="],
+ "@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.0", "", { "os": "linux", "cpu": "x64" }, "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ=="],
- "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w=="],
+ "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.0", "", { "os": "none", "cpu": "arm64" }, "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw=="],
- "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.7", "", { "os": "none", "cpu": "x64" }, "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw=="],
+ "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.0", "", { "os": "none", "cpu": "x64" }, "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw=="],
- "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.7", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A=="],
+ "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.0", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g=="],
- "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.7", "", { "os": "openbsd", "cpu": "x64" }, "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg=="],
+ "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA=="],
- "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw=="],
+ "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.0", "", { "os": "none", "cpu": "arm64" }, "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w=="],
- "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.7", "", { "os": "sunos", "cpu": "x64" }, "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA=="],
+ "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.0", "", { "os": "sunos", "cpu": "x64" }, "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw=="],
- "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA=="],
+ "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA=="],
- "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.7", "", { "os": "win32", "cpu": "ia32" }, "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw=="],
+ "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA=="],
- "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="],
+ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.0", "", { "os": "win32", "cpu": "x64" }, "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw=="],
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="],
@@ -207,12 +193,8 @@
"@manypkg/get-packages": ["@manypkg/get-packages@1.1.3", "", { "dependencies": { "@babel/runtime": "^7.5.5", "@changesets/types": "^4.0.1", "@manypkg/find-root": "^1.1.0", "fs-extra": "^8.1.0", "globby": "^11.0.0", "read-yaml-file": "^1.1.0" } }, "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A=="],
- "@mdx-js/react": ["@mdx-js/react@3.1.1", "", { "dependencies": { "@types/mdx": "^2.0.0" }, "peerDependencies": { "@types/react": ">=16", "react": ">=16" } }, "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw=="],
-
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="],
- "@neoconfetti/react": ["@neoconfetti/react@1.0.0", "", {}, "sha512-klcSooChXXOzIm+SE5IISIAn3bYzYfPjbX7D7HoqZL84oAfgREeSg5vSIaSFH+DaGzzvImTyWe1OyrJ67vik4A=="],
-
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
@@ -265,32 +247,6 @@
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
- "@storybook/addon-a11y": ["@storybook/addon-a11y@10.3.6", "", { "dependencies": { "@storybook/global": "^5.0.0", "axe-core": "^4.2.0" }, "peerDependencies": { "storybook": "^10.3.6" } }, "sha512-cbwXIT5CeHZ9AFbTKQ6YB7Ct6TAl/kKOgALbvzzVtFfRvm51JYygGaiJaB7PbPWn9wgJP2olJcFt+erlEc6cRw=="],
-
- "@storybook/addon-docs": ["@storybook/addon-docs@10.3.6", "", { "dependencies": { "@mdx-js/react": "^3.0.0", "@storybook/csf-plugin": "10.3.6", "@storybook/icons": "^2.0.1", "@storybook/react-dom-shim": "10.3.6", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^10.3.6" } }, "sha512-TvIdADVPtauxW0LzXIpIv7X6GxwetorhyNh+6+7MHC27XSBCWVxxRUwL63YeLlHTuXsIk0quG3b1xgwVRzWOJA=="],
-
- "@storybook/addon-svelte-csf": ["@storybook/addon-svelte-csf@5.1.2", "", { "dependencies": { "@storybook/csf": "^0.1.13", "dedent": "^1.5.3", "es-toolkit": "^1.26.1", "esrap": "^1.2.2", "magic-string": "^0.30.12", "svelte-ast-print": "^0.4.0", "zimmerframe": "^1.1.2" }, "peerDependencies": { "@storybook/svelte": "^0.0.0-0 || ^8.2.0 || ^9.0.0 || ^9.1.0-0 || ^10.0.0-0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0", "@sveltejs/vite-plugin-svelte": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", "storybook": "^0.0.0-0 || ^8.2.0 || ^9.0.0 || ^9.1.0-0 || ^10.0.0-0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0", "svelte": "^5.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-NpImknEb48J7yr/ArTYpvhDSvGUrgm5Nuybu9PCicjSKTACsXX7cln2R19572ORtns399RTE+t20BBOKxSPm2g=="],
-
- "@storybook/addon-vitest": ["@storybook/addon-vitest@10.3.6", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.1" }, "peerDependencies": { "@vitest/browser": "^3.0.0 || ^4.0.0", "@vitest/browser-playwright": "^4.0.0", "@vitest/runner": "^3.0.0 || ^4.0.0", "storybook": "^10.3.6", "vitest": "^3.0.0 || ^4.0.0" }, "optionalPeers": ["@vitest/browser", "@vitest/browser-playwright", "@vitest/runner", "vitest"] }, "sha512-HXj7RrPJY+xzoNjL+xZu2oLw1fI5BA87Noh1NAXMPuECHR5R5fuRM/tTsJuIGXHFMO06FjSi/rekDIfCj1fL4w=="],
-
- "@storybook/builder-vite": ["@storybook/builder-vite@10.3.6", "", { "dependencies": { "@storybook/csf-plugin": "10.3.6", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^10.3.6", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-gpvR/sE4BcrFtmQZ+Ker7zD23oQzoVeqD9nF6cK6yzY+Q0svJXyX2EPmFG4y+EwygD5/vNzDpP84gGMut8VRwg=="],
-
- "@storybook/csf": ["@storybook/csf@0.1.13", "", { "dependencies": { "type-fest": "^2.19.0" } }, "sha512-7xOOwCLGB3ebM87eemep89MYRFTko+D8qE7EdAAq74lgdqRR5cOUtYWJLjO2dLtP94nqoOdHJo6MdLLKzg412Q=="],
-
- "@storybook/csf-plugin": ["@storybook/csf-plugin@10.3.6", "", { "dependencies": { "unplugin": "^2.3.5" }, "peerDependencies": { "esbuild": "*", "rollup": "*", "storybook": "^10.3.6", "vite": "*", "webpack": "*" }, "optionalPeers": ["esbuild", "rollup", "vite", "webpack"] }, "sha512-9kBf7VRdRqTSIYo+rPtVn5yjYYyK8kP2QhEYx3oiXvfwy4RexmbJnhk/tXa/lNiTqukA1TqaWQ2+5MqF4fu6YQ=="],
-
- "@storybook/global": ["@storybook/global@5.0.0", "", {}, "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ=="],
-
- "@storybook/icons": ["@storybook/icons@2.0.2", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-KZBCpXsshAIjczYNXR/rlxEtCUX/eAbpFNwKi8bcOomrLA4t/SyPz5RF+lVPO2oZBUE4sAkt43mfJUevQDSEEw=="],
-
- "@storybook/react-dom-shim": ["@storybook/react-dom-shim@10.3.6", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "storybook": "^10.3.6" } }, "sha512-/Tu1gPu+Fw+zOnAGmxRmOD30FX3a04LxcTAKflEtdpmtIMVR5bA3qpjy+f5YhoyDCecbXyKmL1OeIU2FIIZHqQ=="],
-
- "@storybook/svelte": ["@storybook/svelte@10.3.6", "", { "dependencies": { "ts-dedent": "^2.0.0", "type-fest": "~2.19" }, "peerDependencies": { "storybook": "^10.3.6", "svelte": "^5.0.0" } }, "sha512-XE+wNIiztpX6SapuJjYOgZajYWKDMDy/4LVbcqqypOoiYXnO/YOO2p9RdDgD8ta+J88Nap+/qiP7rBbzKOOrOA=="],
-
- "@storybook/svelte-vite": ["@storybook/svelte-vite@10.3.6", "", { "dependencies": { "@storybook/builder-vite": "10.3.6", "@storybook/svelte": "10.3.6", "magic-string": "^0.30.0", "svelte2tsx": "^0.7.44", "typescript": "^4.9.4 || ^5.0.0" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", "storybook": "^10.3.6", "svelte": "^5.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-R+Z0TMqLe9XI7yJyfUel9qeZCh2pXUCl4C7SVWec53j9q0qEVcWVleh4Yob1p0dL0cBNMfU5bQN6d56nKVrwTA=="],
-
- "@storybook/sveltekit": ["@storybook/sveltekit@10.3.6", "", { "dependencies": { "@storybook/builder-vite": "10.3.6", "@storybook/svelte": "10.3.6", "@storybook/svelte-vite": "10.3.6" }, "peerDependencies": { "storybook": "^10.3.6", "svelte": "^5.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-pyQxMcJRhirtF2aWUNEfuSM5MCg4FXcis0Xk9duHIizOQ3pT0rPcY533g9EByzmi3Rm9xYOZVRKllla+G0kV1A=="],
-
"@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.9", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA=="],
"@sveltejs/adapter-auto": ["@sveltejs/adapter-auto@7.0.1", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-dvuPm1E7M9NI/+canIQ6KKQDU2AkEefEZ2Dp7cY6uKoPq9Z/PhOXABe526UdW2mN986gjVkuSLkOYIBnS/M2LQ=="],
@@ -331,18 +287,10 @@
"@tailwindcss/vite": ["@tailwindcss/vite@4.3.0", "", { "dependencies": { "@tailwindcss/node": "4.3.0", "@tailwindcss/oxide": "4.3.0", "tailwindcss": "4.3.0" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw=="],
- "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="],
-
- "@testing-library/jest-dom": ["@testing-library/jest-dom@6.9.1", "", { "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", "picocolors": "^1.1.1", "redent": "^3.0.0" } }, "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA=="],
-
"@testing-library/svelte-core": ["@testing-library/svelte-core@1.0.0", "", { "peerDependencies": { "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0" } }, "sha512-VkUePoLV6oOYwSUvX6ShA8KLnJqZiYMIbP2JW2t0GLWLkJxKGvuH5qrrZBV/X7cXFnLGuFQEC7RheYiZOW68KQ=="],
- "@testing-library/user-event": ["@testing-library/user-event@14.6.1", "", { "peerDependencies": { "@testing-library/dom": ">=7.21.4" } }, "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw=="],
-
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="],
- "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="],
-
"@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="],
"@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
@@ -357,12 +305,8 @@
"@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
- "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="],
-
"@types/node": ["@types/node@24.12.4", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA=="],
- "@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
-
"@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
"@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
@@ -393,7 +337,7 @@
"@vitest/coverage-v8": ["@vitest/coverage-v8@4.1.6", "", { "dependencies": { "@bcoe/v8-coverage": "^1.0.2", "@vitest/utils": "4.1.6", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.2.0", "magicast": "^0.5.2", "obug": "^2.1.1", "std-env": "^4.0.0-rc.1", "tinyrainbow": "^3.1.0" }, "peerDependencies": { "@vitest/browser": "4.1.6", "vitest": "4.1.6" }, "optionalPeers": ["@vitest/browser"] }, "sha512-36l628fQ/9a/8ihy97eOtEnvWQEdqULQOJtcaxtoNq0G1w3Mxd4szSahOaMM9/NGyZ+hyKcMtIW/WIxq0XQViQ=="],
- "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="],
+ "@vitest/expect": ["@vitest/expect@4.1.6", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.6", "@vitest/utils": "4.1.6", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg=="],
"@vitest/mocker": ["@vitest/mocker@4.1.6", "", { "dependencies": { "@vitest/spy": "4.1.6", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ=="],
@@ -403,12 +347,10 @@
"@vitest/snapshot": ["@vitest/snapshot@4.1.6", "", { "dependencies": { "@vitest/pretty-format": "4.1.6", "@vitest/utils": "4.1.6", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw=="],
- "@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="],
+ "@vitest/spy": ["@vitest/spy@4.1.6", "", {}, "sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg=="],
"@vitest/utils": ["@vitest/utils@4.1.6", "", { "dependencies": { "@vitest/pretty-format": "4.1.6", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ=="],
- "@webcontainer/env": ["@webcontainer/env@1.1.1", "", {}, "sha512-6aN99yL695Hi9SuIk1oC88l9o0gmxL1nGWWQ/kNy81HigJ0FoaoTXpytCj6ItzgyCEwA9kF1wixsTuv5cjsgng=="],
-
"acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="],
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
@@ -417,9 +359,7 @@
"ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="],
- "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
-
- "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
+ "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
@@ -429,12 +369,8 @@
"assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="],
- "ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="],
-
"ast-v8-to-istanbul": ["ast-v8-to-istanbul@1.0.0", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.31", "estree-walker": "^3.0.3", "js-tokens": "^10.0.0" } }, "sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg=="],
- "axe-core": ["axe-core@4.11.4", "", {}, "sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA=="],
-
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
"balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
@@ -445,20 +381,14 @@
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
- "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
-
"bytes-iec": ["bytes-iec@3.1.1", "", {}, "sha512-fey6+4jDK7TFtFg/klGSvNKJctyU7n2aQdnM+CO0ruLPbqqMOM8Tio0Pc+deqUeVKX1tL5DQep1zQ7+37aTAsA=="],
- "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="],
+ "chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="],
"chardet": ["chardet@2.1.1", "", {}, "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ=="],
- "check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="],
-
"chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
- "chromatic": ["chromatic@13.3.5", "", { "peerDependencies": { "@chromatic-com/cypress": "^0.*.* || ^1.0.0", "@chromatic-com/playwright": "^0.*.* || ^1.0.0" }, "optionalPeers": ["@chromatic-com/cypress", "@chromatic-com/playwright"], "bin": { "chroma": "dist/bin.js", "chromatic": "dist/bin.js", "chromatic-cli": "dist/bin.js" } }, "sha512-MzPhxpl838qJUo0A55osCF2ifwPbjcIPeElr1d4SHcjnHoIcg7l1syJDrAYK/a+PcCBrOGi06jPNpQAln5hWgw=="],
-
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
@@ -467,32 +397,16 @@
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
- "css.escape": ["css.escape@1.5.1", "", {}, "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg=="],
-
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
- "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
-
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
- "dedent": ["dedent@1.7.2", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA=="],
-
"dedent-js": ["dedent-js@1.0.1", "", {}, "sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ=="],
- "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
-
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
- "default-browser": ["default-browser@5.5.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="],
-
- "default-browser-id": ["default-browser-id@5.0.1", "", {}, "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q=="],
-
- "define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="],
-
- "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
-
"detect-indent": ["detect-indent@6.1.0", "", {}, "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA=="],
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
@@ -501,17 +415,13 @@
"dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="],
- "dom-accessibility-api": ["dom-accessibility-api@0.6.3", "", {}, "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="],
-
"enhanced-resolve": ["enhanced-resolve@5.21.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-QyL119InA+XXEkNLNTPCXPugSvOfhwv0JOlGNzvxs0hZaiHLNvXSpudUWsOlsXGWJh8G6ckCScEkVHfX3kw/2Q=="],
"enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="],
"es-module-lexer": ["es-module-lexer@2.1.0", "", {}, "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ=="],
- "es-toolkit": ["es-toolkit@1.46.1", "", {}, "sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ=="],
-
- "esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="],
+ "esbuild": ["esbuild@0.28.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.0", "@esbuild/android-arm": "0.28.0", "@esbuild/android-arm64": "0.28.0", "@esbuild/android-x64": "0.28.0", "@esbuild/darwin-arm64": "0.28.0", "@esbuild/darwin-x64": "0.28.0", "@esbuild/freebsd-arm64": "0.28.0", "@esbuild/freebsd-x64": "0.28.0", "@esbuild/linux-arm": "0.28.0", "@esbuild/linux-arm64": "0.28.0", "@esbuild/linux-ia32": "0.28.0", "@esbuild/linux-loong64": "0.28.0", "@esbuild/linux-mips64el": "0.28.0", "@esbuild/linux-ppc64": "0.28.0", "@esbuild/linux-riscv64": "0.28.0", "@esbuild/linux-s390x": "0.28.0", "@esbuild/linux-x64": "0.28.0", "@esbuild/netbsd-arm64": "0.28.0", "@esbuild/netbsd-x64": "0.28.0", "@esbuild/openbsd-arm64": "0.28.0", "@esbuild/openbsd-x64": "0.28.0", "@esbuild/openharmony-arm64": "0.28.0", "@esbuild/sunos-x64": "0.28.0", "@esbuild/win32-arm64": "0.28.0", "@esbuild/win32-ia32": "0.28.0", "@esbuild/win32-x64": "0.28.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw=="],
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
@@ -519,8 +429,6 @@
"eslint-config-prettier": ["eslint-config-prettier@10.1.8", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w=="],
- "eslint-plugin-storybook": ["eslint-plugin-storybook@10.3.6", "", { "dependencies": { "@typescript-eslint/utils": "^8.48.0" }, "peerDependencies": { "eslint": ">=8", "storybook": "^10.3.6" } }, "sha512-8udrL+Rmp5LFaZvgRe4J226X1MYls25bWCyHuzR5X8s2qbFTryX+wKC+o/0Ato4A1AvwnDg8OOMPc6yWJ9JpcA=="],
-
"eslint-plugin-svelte": ["eslint-plugin-svelte@3.17.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.6.1", "@jridgewell/sourcemap-codec": "^1.5.0", "esutils": "^2.0.3", "globals": "^16.0.0", "known-css-properties": "^0.37.0", "postcss": "^8.4.49", "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^7.0.0", "semver": "^7.6.3", "svelte-eslint-parser": "^1.4.0" }, "peerDependencies": { "eslint": "^8.57.1 || ^9.0.0 || ^10.0.0", "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-NyiXHtS3Ni7e532RBwS9OXlMKDIrENg3gY+/+ODjZzQx2xhU3NlJ+nIl1a93iUUQeiJL3lS8KLmY+W8hklzweQ=="],
"eslint-scope": ["eslint-scope@9.1.2", "", { "dependencies": { "@types/esrecurse": "^4.3.1", "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ=="],
@@ -535,7 +443,7 @@
"esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="],
- "esrap": ["esrap@1.4.9", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-3OMlcd0a03UGuZpPeUC1HxR3nA23l+HEyCiZw3b3FumJIN9KphoGzDJKMXI1S72jVS1dsenDyQC0kJlO1U9E1g=="],
+ "esrap": ["esrap@2.2.8", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, "peerDependencies": { "@typescript-eslint/types": "^8.2.0" }, "optionalPeers": ["@typescript-eslint/types"] }, "sha512-MPweq2EvEGj8jwOI7Hgycw/QIHzqA1EbAM8lG7p+FBfZbZq/hQ6h3AMsqnu/djzisH1KVWNzbb7LSgIVtMlPSg=="],
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
@@ -563,8 +471,6 @@
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
- "filesize": ["filesize@10.1.6", "", {}, "sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w=="],
-
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
"find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
@@ -597,16 +503,10 @@
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
- "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="],
-
- "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
-
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
- "is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="],
-
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
@@ -615,8 +515,6 @@
"is-windows": ["is-windows@1.0.2", "", {}, "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="],
- "is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="],
-
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"istanbul-lib-coverage": ["istanbul-lib-coverage@3.2.2", "", {}, "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg=="],
@@ -637,7 +535,7 @@
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
- "jsonfile": ["jsonfile@6.2.1", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q=="],
+ "jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="],
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
@@ -679,10 +577,6 @@
"lodash.startcase": ["lodash.startcase@4.4.0", "", {}, "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg=="],
- "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="],
-
- "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="],
-
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
"magicast": ["magicast@0.5.2", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "source-map-js": "^1.2.1" } }, "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ=="],
@@ -695,8 +589,6 @@
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
- "min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="],
-
"minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
"mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
@@ -713,8 +605,6 @@
"obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="],
- "open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
-
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
"outdent": ["outdent@0.5.0", "", {}, "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q=="],
@@ -739,8 +629,6 @@
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
- "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="],
-
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
@@ -771,8 +659,6 @@
"prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.7.4", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-svelte"] }, "sha512-UKii4RjY05SNt/WQi6/NcOn/LsT0/ILLXsxygjbRg5/YZelsSu5jTqorYHPDGq4nZy5q5hpCu+XdGZ1xaJEQgw=="],
- "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="],
-
"prism-svelte": ["prism-svelte@0.4.7", "", {}, "sha512-yABh19CYbM24V7aS7TuPYRNMqthxwbvx6FF/Rw920YbyBWO3tnyPIqRMgHuSVsLmuHkkBS1Akyof463FVdkeDQ=="],
"prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="],
@@ -785,36 +671,22 @@
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
- "react": ["react@19.2.6", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="],
-
- "react-dom": ["react-dom@19.2.6", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.6" } }, "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g=="],
-
- "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="],
-
"read-yaml-file": ["read-yaml-file@1.1.0", "", { "dependencies": { "graceful-fs": "^4.1.5", "js-yaml": "^3.6.1", "pify": "^4.0.1", "strip-bom": "^3.0.0" } }, "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA=="],
"readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
- "recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="],
-
- "redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="],
-
"resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="],
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
"rolldown": ["rolldown@1.0.0", "", { "dependencies": { "@oxc-project/types": "=0.129.0", "@rolldown/pluginutils": "1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0", "@rolldown/binding-darwin-arm64": "1.0.0", "@rolldown/binding-darwin-x64": "1.0.0", "@rolldown/binding-freebsd-x64": "1.0.0", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0", "@rolldown/binding-linux-arm64-gnu": "1.0.0", "@rolldown/binding-linux-arm64-musl": "1.0.0", "@rolldown/binding-linux-ppc64-gnu": "1.0.0", "@rolldown/binding-linux-s390x-gnu": "1.0.0", "@rolldown/binding-linux-x64-gnu": "1.0.0", "@rolldown/binding-linux-x64-musl": "1.0.0", "@rolldown/binding-openharmony-arm64": "1.0.0", "@rolldown/binding-wasm32-wasi": "1.0.0", "@rolldown/binding-win32-arm64-msvc": "1.0.0", "@rolldown/binding-win32-x64-msvc": "1.0.0" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-yD986aXDESFGS95spT1LAv0jssywP4npMEjmMHyN2/5+eE8qQJUype2AaKkRiLgBgyD0LFlubwAht7VmY8rGoA=="],
- "run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="],
-
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
- "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
-
"scule": ["scule@1.3.0", "", {}, "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g=="],
"semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="],
@@ -835,8 +707,6 @@
"slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
- "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
-
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
"spawndamnit": ["spawndamnit@3.0.1", "", { "dependencies": { "cross-spawn": "^7.0.5", "signal-exit": "^4.0.1" } }, "sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg=="],
@@ -847,20 +717,14 @@
"std-env": ["std-env@4.1.0", "", {}, "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ=="],
- "storybook": ["storybook@10.3.6", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/user-event": "^14.6.1", "@vitest/expect": "3.2.4", "@vitest/spy": "3.2.4", "@webcontainer/env": "^1.1.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0", "open": "^10.2.0", "recast": "^0.23.5", "semver": "^7.7.3", "use-sync-external-store": "^1.5.0", "ws": "^8.18.0" }, "peerDependencies": { "prettier": "^2 || ^3", "vite-plus": "^0.1.15" }, "optionalPeers": ["prettier", "vite-plus"], "bin": "./dist/bin/dispatcher.js" }, "sha512-vbSz7g/1rGMC1uAULqMZjALkIuLu2QABqfhRYhyr/11kzyesi+vAmwyJLukZP1FfecxGOgMwOh6GS0YsGpHAvQ=="],
-
- "strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
+ "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="],
- "strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="],
-
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"svelte": ["svelte@5.55.5", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "@types/trusted-types": "^2.0.7", "acorn": "^8.12.1", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.6.4", "esm-env": "^1.2.1", "esrap": "^2.2.4", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-2uCs/LZ9us+AktdzYJM8OcxQ8qnPS1kpaO7syGT/MgO+6Qr1Ybl+TqPq+97u7PHqmmMlye5ZkoyXONy5mjjAbw=="],
- "svelte-ast-print": ["svelte-ast-print@0.4.2", "", { "dependencies": { "esrap": "1.2.2", "zimmerframe": "1.1.2" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-hRHHufbJoArFmDYQKCpCvc0xUuIEfwYksvyLYEQyH+1xb5LD5sM/IthfooCdXZQtOIqXz6xm7NmaqdfwG4kh6w=="],
-
"svelte-check": ["svelte-check@4.4.8", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-67adfgBox5eNSNIvIIwgFizKGdcRrGpiMoNO2obHcYuLz7iTa8Xgm/NGU3ntMFnNm8K1grFOIG6HhMLX/vcN8w=="],
"svelte-eslint-parser": ["svelte-eslint-parser@1.6.1", "", { "dependencies": { "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.0.0", "espree": "^10.0.0", "postcss": "^8.4.49", "postcss-scss": "^4.0.9", "postcss-selector-parser": "^7.0.0", "semver": "^7.7.2" }, "peerDependencies": { "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-hhvSH6kRj46UzrBVO5TaotD+Iuvruj5ccKBcO4wAhVcPTLmIc/c32D8UllBTYO0on4LzYuM0rNzf1lM/gBlkSQ=="],
@@ -873,8 +737,6 @@
"term-size": ["term-size@2.2.1", "", {}, "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg=="],
- "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="],
-
"tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
"tinyexec": ["tinyexec@1.1.2", "", {}, "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA=="],
@@ -883,22 +745,16 @@
"tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="],
- "tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="],
-
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
"totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="],
"ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="],
- "ts-dedent": ["ts-dedent@2.2.0", "", {}, "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ=="],
-
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
- "type-fest": ["type-fest@2.19.0", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="],
-
"typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="],
"typescript-eslint": ["typescript-eslint@8.59.3", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.59.3", "@typescript-eslint/parser": "8.59.3", "@typescript-eslint/typescript-estree": "8.59.3", "@typescript-eslint/utils": "8.59.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg=="],
@@ -915,12 +771,8 @@
"universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="],
- "unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="],
-
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
- "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="],
-
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
"vfile-message": ["vfile-message@2.0.4", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-stringify-position": "^2.0.0" } }, "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ=="],
@@ -933,8 +785,6 @@
"vitest-browser-svelte": ["vitest-browser-svelte@2.1.1", "", { "dependencies": { "@testing-library/svelte-core": "^1.0.0" }, "peerDependencies": { "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0", "vitest": "^4.0.0" } }, "sha512-qbunYRSm+N92r9bfTkdDTpBZESLmp4QFz2SluV3n/x8U7ysosfeXYJZ4vXbJ0Y0LzoqqDnV5LHprmFgn4Eo+Ug=="],
- "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
-
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
@@ -943,16 +793,12 @@
"ws": ["ws@8.20.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w=="],
- "wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
-
"yaml": ["yaml@1.10.3", "", {}, "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA=="],
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
"zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="],
- "@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
-
"@changesets/apply-release-plan/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="],
"@changesets/write/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="],
@@ -969,10 +815,6 @@
"@manypkg/get-packages/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="],
- "@size-limit/esbuild/esbuild": ["esbuild@0.28.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.0", "@esbuild/android-arm": "0.28.0", "@esbuild/android-arm64": "0.28.0", "@esbuild/android-x64": "0.28.0", "@esbuild/darwin-arm64": "0.28.0", "@esbuild/darwin-x64": "0.28.0", "@esbuild/freebsd-arm64": "0.28.0", "@esbuild/freebsd-x64": "0.28.0", "@esbuild/linux-arm": "0.28.0", "@esbuild/linux-arm64": "0.28.0", "@esbuild/linux-ia32": "0.28.0", "@esbuild/linux-loong64": "0.28.0", "@esbuild/linux-mips64el": "0.28.0", "@esbuild/linux-ppc64": "0.28.0", "@esbuild/linux-riscv64": "0.28.0", "@esbuild/linux-s390x": "0.28.0", "@esbuild/linux-x64": "0.28.0", "@esbuild/netbsd-arm64": "0.28.0", "@esbuild/netbsd-x64": "0.28.0", "@esbuild/openbsd-arm64": "0.28.0", "@esbuild/openbsd-x64": "0.28.0", "@esbuild/openharmony-arm64": "0.28.0", "@esbuild/sunos-x64": "0.28.0", "@esbuild/win32-arm64": "0.28.0", "@esbuild/win32-ia32": "0.28.0", "@esbuild/win32-x64": "0.28.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw=="],
-
- "@storybook/svelte-vite/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
-
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
@@ -985,46 +827,22 @@
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
- "@testing-library/dom/aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="],
-
- "@testing-library/dom/dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="],
-
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
- "@vitest/expect/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="],
-
- "@vitest/expect/tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="],
-
- "@vitest/mocker/@vitest/spy": ["@vitest/spy@4.1.6", "", {}, "sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg=="],
-
- "enquirer/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
-
"eslint-plugin-svelte/globals": ["globals@16.5.0", "", {}, "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ=="],
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
- "fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="],
-
- "jsonfile/universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
-
"micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
"postcss/nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="],
"postcss-load-config/lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="],
- "pretty-format/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
-
"publint/package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="],
"read-yaml-file/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="],
- "svelte/esrap": ["esrap@2.2.8", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, "peerDependencies": { "@typescript-eslint/types": "^8.2.0" }, "optionalPeers": ["@typescript-eslint/types"] }, "sha512-MPweq2EvEGj8jwOI7Hgycw/QIHzqA1EbAM8lG7p+FBfZbZq/hQ6h3AMsqnu/djzisH1KVWNzbb7LSgIVtMlPSg=="],
-
- "svelte-ast-print/esrap": ["esrap@1.2.2", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15", "@types/estree": "^1.0.1" } }, "sha512-F2pSJklxx1BlQIQgooczXCPHmcWpn6EsP5oo73LQfonG9fIlIENQ8vMmfGXeojP9MrkzUNAfyU5vdFlR9shHAw=="],
-
- "svelte-ast-print/zimmerframe": ["zimmerframe@1.1.2", "", {}, "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w=="],
-
"svelte-check/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
"svelte-eslint-parser/eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
@@ -1035,78 +853,12 @@
"vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
- "vitest/@vitest/expect": ["@vitest/expect@4.1.6", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.6", "@vitest/utils": "4.1.6", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg=="],
-
- "vitest/@vitest/spy": ["@vitest/spy@4.1.6", "", {}, "sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg=="],
-
"@manypkg/find-root/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
- "@manypkg/find-root/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="],
-
- "@manypkg/get-packages/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.28.0", "", { "os": "android", "cpu": "arm" }, "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.0", "", { "os": "android", "cpu": "arm64" }, "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.28.0", "", { "os": "android", "cpu": "x64" }, "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.0", "", { "os": "linux", "cpu": "arm" }, "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.0", "", { "os": "linux", "cpu": "x64" }, "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.0", "", { "os": "none", "cpu": "arm64" }, "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.0", "", { "os": "none", "cpu": "x64" }, "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.0", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.0", "", { "os": "none", "cpu": "arm64" }, "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.0", "", { "os": "sunos", "cpu": "x64" }, "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA=="],
-
- "@size-limit/esbuild/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.0", "", { "os": "win32", "cpu": "x64" }, "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw=="],
-
- "@vitest/expect/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="],
-
- "enquirer/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
-
"read-yaml-file/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
"svelte-check/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
- "vitest/@vitest/expect/chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="],
-
"@manypkg/find-root/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
"@manypkg/find-root/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
diff --git a/src/lib/animate/core/animate.ts b/src/lib/animate/core/animate.ts
index a0c2a55..46eaf15 100644
--- a/src/lib/animate/core/animate.ts
+++ b/src/lib/animate/core/animate.ts
@@ -22,12 +22,21 @@
import { createController, noopController } from './controller';
import { buildKeyframes, type KeyframeGroup } from '../keyframes/keyframes';
import { normalizeInput } from '../keyframes/normalize';
-import { deregisterTransformAnimation, registerTransformAnimation } from '../properties/properties';
+import {
+ demoteFoldedTransforms,
+ deregisterTransformAnimation,
+ hasActiveTransforms,
+ registerFoldedTransforms,
+ registerTransformAnimation,
+ type FoldedTransform
+} from '../properties/properties';
import {
ensurePropertiesRegistered,
ensureTransformWired,
+ isTransformOwned,
wireTransform
} from '../properties/transform-setup';
+import { applyFolds, planFolds } from '../keyframes/fold';
import type { AnimateDefaults, AnimateProps, AnimationController, MotionElement } from '../types';
import { isBrowser, shouldReduceMotion } from '../../shared/browser';
import { formatValue, resolveProp } from '../properties/prop-utils';
@@ -60,23 +69,74 @@ export const animate = (
return noopController(element, defaults);
}
+ // Read before registering: an element with no transform channel in flight is
+ // one whose transform we can drive directly for the length of this call.
+ const canFold = needsTransform && !hasActiveTransforms(element);
+
if (needsTransform) {
ensureTransformWired(element);
registerTransformAnimation(element, transformBits);
}
+ const folds = canFold ? foldTransforms(element, groups) : [];
+ const animations = buildAnimations(element, groups, defaults);
+ if (folds.length > 0) {
+ registerFoldedTransforms(
+ element,
+ folds.map(({ groupIndex, varFrames, targets }) => ({
+ animation: animations[groupIndex]!,
+ varFrames,
+ targets
+ }))
+ );
+ }
+
return createController({
element,
- animations: buildAnimations(element, groups, defaults),
+ animations,
defaults,
finalStyles,
restorations,
onTeardown: needsTransform
- ? () => deregisterTransformAnimation(element, transformBits)
+ ? () => {
+ demoteFoldedTransforms(element);
+ deregisterTransformAnimation(element, transformBits);
+ }
: undefined
});
};
+type PendingFold = Omit & { groupIndex: number };
+
+/**
+ * Collapse this call's transform-variable keyframes into direct
+ * `translate` / `scale` / `rotate` keyframes wherever possible, so Chromium can
+ * run them on the compositor instead of recalculating style every frame.
+ * Mutates `groups`, and returns what the tracker needs to undo the fold.
+ */
+const foldTransforms = (element: MotionElement, groups: KeyframeGroup[]): PendingFold[] => {
+ const computed = window.getComputedStyle(element);
+ const plans = planFolds(
+ groups,
+ (name) => computed.getPropertyValue(name).trim(),
+ // An unset inline value means the template never took (no CSS typed-OM
+ // support for these properties), leaving nothing to fold onto.
+ (target) => isTransformOwned(element, target) && !!element.style.getPropertyValue(target)
+ );
+ if (plans.length === 0) return [];
+ const originals = applyFolds(groups, plans);
+ return [...originals].map(([groupIndex, keyframes]) => {
+ const { offset } = groups[groupIndex]!;
+ return {
+ groupIndex,
+ varFrames: (offset ? { ...keyframes, offset } : keyframes) as PropertyIndexedKeyframes,
+ targets: plans
+ .filter((plan) => plan.groupIndex === groupIndex)
+ .map((plan) => [plan.target, element.style.getPropertyValue(plan.target)] as const)
+ };
+ });
+};
+
/**
* Materialize each keyframe group into a WAAPI `Animation`, applying the shared
* effect options (fill / composite / iterations / direction / …) from `defaults`.
diff --git a/src/lib/animate/core/controller.ts b/src/lib/animate/core/controller.ts
index 5761ef4..078d0cf 100644
--- a/src/lib/animate/core/controller.ts
+++ b/src/lib/animate/core/controller.ts
@@ -4,6 +4,7 @@
*/
import { isBrowser } from '../../shared/browser';
+import { demoteFoldedTransforms } from '../properties/properties';
import { playbackControls } from '../../shared/playback';
import type { AnimateDefaults, AnimationController, MotionElement } from '../types';
import type { CssWrite } from '../keyframes/keyframes';
@@ -188,7 +189,15 @@ export const createController = ({
},
cancel: teardown,
stop: () => {
- if (isBrowser()) commitComputedStyles(element, finalStyles);
+ if (isBrowser()) {
+ // `commitComputedStyles` reads the animated custom properties. While
+ // folded, those hold their pre-animation values and the live position
+ // lives on `translate`/`scale` instead, so hand the animation back to
+ // the variable path first — it keeps its current time, so the values
+ // read back are the ones on screen.
+ demoteFoldedTransforms(element);
+ commitComputedStyles(element, finalStyles);
+ }
teardown();
},
...playbackControls(forEachAnim)
diff --git a/src/lib/animate/core/fold-equivalence.svelte.test.ts b/src/lib/animate/core/fold-equivalence.svelte.test.ts
new file mode 100644
index 0000000..1f8d3e6
--- /dev/null
+++ b/src/lib/animate/core/fold-equivalence.svelte.test.ts
@@ -0,0 +1,73 @@
+import { afterEach, describe, expect, it } from 'vitest';
+import { animate } from './animate';
+
+const nodes: HTMLElement[] = [];
+const mount = (css = ''): HTMLElement => {
+ const el = document.createElement('div');
+ el.style.cssText = `position:fixed;top:50px;left:50px;width:100px;height:80px;${css}`;
+ document.body.appendChild(el);
+ nodes.push(el);
+ return el;
+};
+afterEach(() => {
+ for (const n of nodes.splice(0)) n.remove();
+});
+
+const FLIP = { flipX: [120, 0], flipY: [40, 0], flipScaleX: [0.5, 1], flipScaleY: [0.25, 1] };
+const rect = (el: Element) => {
+ const r = el.getBoundingClientRect();
+ return [r.left, r.top, r.width, r.height].map((n) => Math.round(n * 100) / 100);
+};
+
+describe('fold equivalence', () => {
+ it('renders the same frame as the variable path', () => {
+ for (const css of ['', 'transform-origin:0 0;', '--motion-x:15px;--motion-scale:1.5;']) {
+ const a = mount(css);
+ const b = mount(css);
+ const folded = animate(a, FLIP as never, { duration: 400, easing: (t: number) => t });
+ const vars = animate(b, FLIP as never, { duration: 400, easing: (t: number) => t });
+ // A second animation demotes b onto the variable path.
+ const nudge = animate(b, { rotate: [0, 0] }, { duration: 400 });
+ for (const c of [folded, vars]) {
+ c.pause();
+ c.seek(150);
+ }
+ expect(rect(a)).toEqual(rect(b));
+ folded.cancel();
+ vars.cancel();
+ nudge.cancel();
+ }
+ });
+
+ it('stop() freezes a folded animation where it is on screen', () => {
+ const el = mount();
+ const c = animate(el, FLIP as never, { duration: 400, easing: (t: number) => t });
+ c.pause();
+ c.seek(100);
+ const before = rect(el);
+ c.stop();
+ expect(rect(el)).toEqual(before);
+ expect(parseFloat(el.style.getPropertyValue('--flip-x'))).toBeCloseTo(90, 1);
+ });
+
+ it('lands on the end state and cleans up', async () => {
+ const el = mount();
+ const resting = rect(el);
+ const c = animate(el, { ...FLIP, width: [50, 100] } as never, { duration: 60 });
+ expect(c.animations).toHaveLength(2);
+ await c.finished;
+ expect(rect(el)).toEqual(resting);
+ expect(el.style.getPropertyValue('will-change')).toBe('');
+ expect(el.getAnimations()).toHaveLength(0);
+ });
+
+ it('inline writes to a motion var show up mid-fold', async () => {
+ const el = mount();
+ const c = animate(el, { flipX: [0, 0] }, { duration: 400 });
+ const before = rect(el);
+ el.style.setProperty('--motion-x', '25px');
+ await new Promise((r) => setTimeout(r, 0));
+ expect(rect(el)[0]).toBeCloseTo(before[0]! + 25, 1);
+ c.cancel();
+ });
+});
diff --git a/src/lib/animate/core/fold.svelte.test.ts b/src/lib/animate/core/fold.svelte.test.ts
new file mode 100644
index 0000000..c696173
--- /dev/null
+++ b/src/lib/animate/core/fold.svelte.test.ts
@@ -0,0 +1,104 @@
+/**
+ * The transform fold, end to end in a real browser. Runs in the `client`
+ * project, where `style.translate` and registered custom properties actually
+ * exist — the unit tests in `keyframes/fold.test.ts` cover the substitution
+ * itself, this covers whether `animate()` engages it and gives it back.
+ *
+ * Nothing here asserts that an animation is composited: Chromium exposes no
+ * API for that. What is assertable is the shape the compositor requires —
+ * real transform properties in the effect and no custom properties beside
+ * them — plus the promise that the fold is invisible to everything else.
+ */
+
+import { afterEach, describe, expect, it } from 'vitest';
+import { animate } from './animate';
+import { measureWithoutAncestorTransforms } from '../properties/properties';
+
+let node: HTMLElement | null = null;
+
+const mount = (): HTMLElement => {
+ const el = document.createElement('div');
+ el.style.cssText = 'position:fixed;top:50px;left:50px;width:100px;height:100px';
+ document.body.appendChild(el);
+ node = el;
+ return el;
+};
+
+afterEach(() => {
+ node?.remove();
+ node = null;
+});
+
+/** Keys WAAPI adds to every computed keyframe that are not animated properties. */
+const KEYFRAME_METADATA = new Set(['offset', 'computedOffset', 'easing', 'composite']);
+
+/** The property names an effect animates, as WAAPI reports them back. */
+const animatedProps = (animation: Animation): string[] => [
+ ...new Set(
+ (animation.effect as KeyframeEffect)
+ .getKeyframes()
+ .flatMap((frame: Keyframe) => Object.keys(frame))
+ .filter((key: string) => !KEYFRAME_METADATA.has(key))
+ )
+];
+
+describe('transform folding', () => {
+ it('drives real transform properties instead of custom properties', () => {
+ const el = mount();
+
+ const controller = animate(
+ el,
+ {
+ flipX: [120, 0],
+ flipY: [40, 0],
+ flipScaleX: [0.5, 1],
+ flipScaleY: [0.5, 1],
+ opacity: [0, 1]
+ },
+ { duration: 400 }
+ );
+
+ const props = animatedProps(controller.animations[0]!);
+ expect(props).toContain('translate');
+ expect(props).toContain('scale');
+ expect(props.some((prop) => prop.startsWith('--'))).toBe(false);
+ controller.cancel();
+ });
+
+ it('hands the element back to the variables when a second animation starts', () => {
+ const el = mount();
+ const first = animate(el, { flipX: [120, 0] }, { duration: 400 });
+ expect(animatedProps(first.animations[0]!)).toContain('translate');
+
+ // A sibling animation owns `--motion-x`; the fold would mask it.
+ const second = animate(el, { x: [0, 30] }, { duration: 400 });
+
+ expect(animatedProps(first.animations[0]!)).toEqual(['--flip-x']);
+ expect(first.animations[0]!.playState).toBe('running');
+ first.cancel();
+ second.cancel();
+ });
+
+ it('still measures the resting box while folded', () => {
+ const el = mount();
+ const resting = el.getBoundingClientRect();
+
+ const controller = animate(el, { flipX: [200, 0] }, { duration: 400 });
+
+ // The element is visibly displaced, but FLIP must still read its slot.
+ expect(measureWithoutAncestorTransforms(el).left).toBeCloseTo(resting.left, 1);
+ controller.cancel();
+ });
+
+ it('leaves the transform alone when the caller set their own', () => {
+ const el = mount();
+ el.style.translate = '10px 10px';
+
+ const controller = animate(el, { flipX: [120, 0] }, { duration: 400 });
+
+ // Folding here would animate a value we do not own.
+ expect(animatedProps(controller.animations[0]!)).toEqual(['--flip-x']);
+ expect(el.style.translate).toBe('10px 10px');
+ controller.cancel();
+ });
+});
diff --git a/src/lib/animate/keyframes/fold.test.ts b/src/lib/animate/keyframes/fold.test.ts
new file mode 100644
index 0000000..851c58a
--- /dev/null
+++ b/src/lib/animate/keyframes/fold.test.ts
@@ -0,0 +1,82 @@
+import { describe, expect, it } from 'vitest';
+import { applyFolds, planFolds } from './fold';
+import type { KeyframeGroup } from './keyframes';
+
+const group = (keyframes: Record, duration = 400): KeyframeGroup => ({
+ timing: { duration, easing: 'linear', delay: 0 },
+ keyframes,
+ compositable: true
+});
+
+const ownAll = () => true;
+const noVars = () => '';
+
+describe('planFolds()', () => {
+ it('collapses a FLIP group into direct translate and scale keyframes', () => {
+ const groups = [
+ group({
+ '--flip-x': ['120px', '0px'],
+ '--flip-y': ['40px', '0px'],
+ '--flip-scale-x': ['0.5', '1'],
+ '--flip-scale-y': ['0.5', '1'],
+ opacity: ['0', '1']
+ })
+ ];
+
+ const plans = planFolds(groups, noVars, ownAll);
+
+ expect(plans.map((plan) => plan.target)).toEqual(['translate', 'scale']);
+ const [translate, scale] = plans;
+ // Vars with no animation fall back to their template defaults, so the
+ // stop is a plain transform value with nothing left to resolve.
+ expect(translate!.stops[0]).toBe('calc(0px + 120px + 0px) calc(0px + 40px + 0px) 0px');
+ expect(translate!.stops[1]).toBe('calc(0px + 0px + 0px) calc(0px + 0px + 0px) 0px');
+ expect(scale!.stops[0]).toBe('calc(0.5 * 1 * 1) calc(0.5 * 1 * 1)');
+ expect(translate!.varKeys).toEqual(['--flip-x', '--flip-y']);
+ });
+
+ it('substitutes the live value of vars this call does not animate', () => {
+ const groups = [group({ '--motion-x': ['0px', '50px'] })];
+
+ const [plan] = planFolds(
+ groups,
+ (name) => (name === '--motion-reorder-x' ? '12px' : ''),
+ ownAll
+ );
+
+ expect(plan!.stops[1]).toBe('calc(50px + 0px + 12px) calc(0px + 0px + 0px) 0px');
+ });
+
+ it('leaves a target alone when its channels have different timings', () => {
+ const groups = [
+ group({ '--motion-x': ['0px', '50px'] }, 400),
+ group({ '--motion-y': ['0px', '50px'] }, 900)
+ ];
+
+ expect(planFolds(groups, noVars, ownAll).map((plan) => plan.target)).toEqual([]);
+ });
+
+ it('leaves a target alone when the caller owns its transform', () => {
+ const groups = [group({ '--motion-x': ['0px', '50px'], '--motion-rotate': ['0deg', '90deg'] })];
+
+ const plans = planFolds(groups, noVars, (target) => target !== 'translate');
+
+ expect(plans.map((plan) => plan.target)).toEqual(['rotate']);
+ });
+
+ it('ignores targets with no animated channel', () => {
+ expect(planFolds([group({ opacity: ['0', '1'] })], noVars, ownAll)).toEqual([]);
+ });
+});
+
+describe('applyFolds()', () => {
+ it('swaps the variable keys for the folded property and reports the originals', () => {
+ const groups = [group({ '--flip-x': ['10px', '0px'], opacity: ['0', '1'] })];
+ const plans = planFolds(groups, noVars, ownAll);
+
+ const originals = applyFolds(groups, plans);
+
+ expect(Object.keys(groups[0]!.keyframes)).toEqual(['opacity', 'translate']);
+ expect(originals.get(0)).toEqual({ '--flip-x': ['10px', '0px'], opacity: ['0', '1'] });
+ });
+});
diff --git a/src/lib/animate/keyframes/fold.ts b/src/lib/animate/keyframes/fold.ts
new file mode 100644
index 0000000..8a3ea41
--- /dev/null
+++ b/src/lib/animate/keyframes/fold.ts
@@ -0,0 +1,141 @@
+/**
+ * Fold transform-variable keyframes into direct `translate` / `scale` /
+ * `rotate` keyframes.
+ *
+ * `animate()` drives transforms through registered custom properties so that
+ * independent animations compose on one element — FLIP offsets, a drag, and a
+ * sibling `animate()` each own their own var and never clobber a shared
+ * `transform`. Chromium cannot run a custom-property animation on the
+ * compositor, and one such property in an effect pins the whole effect
+ * (opacity included) to the main thread. A dialog morph therefore costs a style
+ * recalc and a full repaint every frame.
+ *
+ * When nothing else is composing on the element, the same motion can be
+ * expressed directly: evaluate the transform template once per keyframe stop,
+ * substituting each animated var with its stop value and every other var with
+ * its current computed value. The result is a plain transform keyframe the
+ * compositor accepts. The fold is reversible — the tracker demotes back to the
+ * variable form as soon as anything else touches the element — so the
+ * composition contract still holds.
+ */
+
+import { TRANSFORM_TEMPLATES } from '../properties/properties';
+import type { KeyframeGroup } from './keyframes';
+
+/** A CSS property whose value is composed from motion vars. */
+export type TransformTarget = keyof typeof TRANSFORM_TEMPLATES;
+
+export const TRANSFORM_TARGETS = Object.keys(TRANSFORM_TEMPLATES) as TransformTarget[];
+
+/** A template is a run of literal text and `var()` references. */
+type TemplatePart = string | { name: string; fallback: string };
+
+const VAR_RE = /var\(\s*(--[a-z0-9-]+)\s*(?:,\s*([^)]*))?\)/gi;
+
+const parseTemplate = (template: string): TemplatePart[] => {
+ const parts: TemplatePart[] = [];
+ let last = 0;
+ for (const match of template.matchAll(VAR_RE)) {
+ const at = match.index;
+ if (at > last) parts.push(template.slice(last, at));
+ parts.push({ name: match[1]!, fallback: (match[2] ?? '').trim() });
+ last = at + match[0].length;
+ }
+ if (last < template.length) parts.push(template.slice(last));
+ return parts;
+};
+
+const TEMPLATE_PARTS = Object.fromEntries(
+ TRANSFORM_TARGETS.map((target) => [target, parseTemplate(TRANSFORM_TEMPLATES[target])])
+) as Record;
+
+/** One transform property that can be driven directly for this `animate()` call. */
+export interface FoldPlan {
+ target: TransformTarget;
+ /** Index into the groups array — the single group holding every animated var. */
+ groupIndex: number;
+ /** Variable keyframe keys the folded keyframe replaces. */
+ varKeys: string[];
+ /** The substituted template, one entry per keyframe stop. */
+ stops: string[];
+}
+
+/**
+ * Work out which transform properties can be driven directly.
+ *
+ * A target is foldable only when we installed its template (a caller's own
+ * `translate` stays authoritative), at least one of its vars is animated, and
+ * every animated var it reads sits in one timing group with the same number of
+ * stops — per-axis springs keep their independent timing on the variable path.
+ *
+ * Pure: reads the DOM only through `readVar`, and mutates nothing.
+ */
+export const planFolds = (
+ groups: readonly KeyframeGroup[],
+ readVar: (name: string) => string,
+ isOwned: (target: TransformTarget) => boolean
+): FoldPlan[] => {
+ const plans: FoldPlan[] = [];
+ for (const target of TRANSFORM_TARGETS) {
+ if (!isOwned(target)) continue;
+ const parts = TEMPLATE_PARTS[target]!;
+ const varKeys: string[] = [];
+ let groupIndex = -1;
+ let stopCount = 0;
+ let foldable = true;
+ for (const part of parts) {
+ if (typeof part === 'string') continue;
+ const index = groups.findIndex((group) => part.name in group.keyframes);
+ if (index === -1) continue;
+ const stops = groups[index]!.keyframes[part.name]!;
+ if (groupIndex === -1) {
+ groupIndex = index;
+ stopCount = stops.length;
+ } else if (index !== groupIndex || stops.length !== stopCount) {
+ // Channels of one target on different timings cannot collapse into
+ // a single keyframe list.
+ foldable = false;
+ break;
+ }
+ varKeys.push(part.name);
+ }
+ if (!foldable || groupIndex === -1) continue;
+
+ const { keyframes } = groups[groupIndex]!;
+ const stops: string[] = [];
+ for (let stop = 0; stop < stopCount; stop++) {
+ let value = '';
+ for (const part of parts) {
+ if (typeof part === 'string') {
+ value += part;
+ continue;
+ }
+ const animated = keyframes[part.name];
+ value += animated ? animated[stop]! : readVar(part.name) || part.fallback;
+ }
+ stops.push(value);
+ }
+ plans.push({ target, groupIndex, varKeys, stops });
+ }
+ return plans;
+};
+
+/**
+ * Rewrite the groups in place per plan, returning each touched group's original
+ * keyframes so the fold can be undone later. The variable keys are removed, not
+ * merely shadowed: leaving a custom property in the effect would pin it to the
+ * main thread again and defeat the whole exercise.
+ */
+export const applyFolds = (
+ groups: KeyframeGroup[],
+ plans: readonly FoldPlan[]
+): Map> => {
+ const originals = new Map>();
+ for (const plan of plans) {
+ const group = groups[plan.groupIndex]!;
+ if (!originals.has(plan.groupIndex)) originals.set(plan.groupIndex, { ...group.keyframes });
+ for (const key of plan.varKeys) delete group.keyframes[key];
+ group.keyframes[plan.target] = plan.stops;
+ }
+ return originals;
+};
diff --git a/src/lib/animate/keyframes/keyframes.test.ts b/src/lib/animate/keyframes/keyframes.test.ts
index 8bb35bd..c1f1dd9 100644
--- a/src/lib/animate/keyframes/keyframes.test.ts
+++ b/src/lib/animate/keyframes/keyframes.test.ts
@@ -142,3 +142,43 @@ describe('buildKeyframes() — multi-stop sequences', () => {
expect(groups[0]!.offset).toBeUndefined();
});
});
+
+describe('buildKeyframes() — compositability grouping', () => {
+ it('keeps a layout-animating prop out of the compositable group', () => {
+ const { groups } = buildKeyframes(
+ el,
+ { opacity: ['0', '1'], width: ['0px', '100px'] },
+ { duration: 300 }
+ );
+
+ // One effect per class: a `width` in the same effect would pin the
+ // opacity to the main thread alongside it.
+ expect(groups).toHaveLength(2);
+ expect(groups.find((group) => group.compositable)!.keyframes).toEqual({
+ opacity: ['0', '1']
+ });
+ expect(groups.find((group) => !group.compositable)!.keyframes).toEqual({
+ width: ['0px', '100px']
+ });
+ });
+
+ it('still merges props that are compositable together', () => {
+ const { groups } = buildKeyframes(
+ el,
+ { opacity: ['0', '1'], x: ['0px', '100px'], filter: ['none', 'blur(2px)'] },
+ { duration: 300 }
+ );
+
+ expect(groups).toHaveLength(1);
+ });
+
+ it('groups several layout props together rather than one effect each', () => {
+ const { groups } = buildKeyframes(
+ el,
+ { width: ['0px', '10px'], height: ['0px', '10px'] },
+ { duration: 300 }
+ );
+
+ expect(groups).toHaveLength(1);
+ });
+});
diff --git a/src/lib/animate/keyframes/keyframes.ts b/src/lib/animate/keyframes/keyframes.ts
index 11b4ff6..499dccc 100644
--- a/src/lib/animate/keyframes/keyframes.ts
+++ b/src/lib/animate/keyframes/keyframes.ts
@@ -10,6 +10,7 @@ import { isBrowser } from '../../shared/browser';
import { isAutoKeyword, measureKeywordValue } from './keyword';
import {
formatValue,
+ isCompositable,
readCurrentValue,
resolveProp,
toKeyframeKey
@@ -18,6 +19,13 @@ import {
export interface KeyframeGroup {
timing: { duration: number; easing: string; delay: number };
keyframes: Record;
+ /**
+ * Whether every prop in this group can be composited. Props are never merged
+ * across this boundary, so the compositable half of a mixed `animate()` call
+ * keeps its own effect instead of being pinned to the main thread by a
+ * sibling that animates layout.
+ */
+ compositable: boolean;
/**
* Explicit keyframe offsets (0–1) for a multi-stop sequence. Present only
* for groups carrying a single offset-bearing prop, so it never conflicts
@@ -84,12 +92,14 @@ const resolveStop = (
* to the single prop that requested it. */
const findGroup = (
groups: KeyframeGroup[],
- timing: KeyframeGroup['timing']
+ timing: KeyframeGroup['timing'],
+ compositable: boolean
): KeyframeGroup | undefined => {
for (const group of groups) {
const t = group.timing;
if (
group.offset === undefined &&
+ group.compositable === compositable &&
t.duration === timing.duration &&
t.delay === timing.delay &&
t.easing === timing.easing
@@ -146,10 +156,12 @@ export const buildKeyframes = (
}
finalStyles.push({ css: def.css, value: values[values.length - 1]! });
- // Offset-bearing props get a private group; everything else merges by timing.
- let group = offset ? undefined : findGroup(groups, timing);
+ // Offset-bearing props get a private group; everything else merges by
+ // timing, within its compositability class.
+ const compositable = isCompositable(def);
+ let group = offset ? undefined : findGroup(groups, timing, compositable);
if (!group) {
- group = { timing, keyframes: {}, offset };
+ group = { timing, keyframes: {}, offset, compositable };
groups.push(group);
}
// WAAPI keyframes are keyed by camelCased IDL names; hyphenated multi-word
diff --git a/src/lib/animate/properties/prop-utils.ts b/src/lib/animate/properties/prop-utils.ts
index 798dc76..a8ab118 100644
--- a/src/lib/animate/properties/prop-utils.ts
+++ b/src/lib/animate/properties/prop-utils.ts
@@ -28,6 +28,22 @@ export const formatValue = (value: AnimatableValue, def: PropDef): string =>
export const toKeyframeKey = (css: string): string =>
css.startsWith('--') ? css : css.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());
+/**
+ * Properties Chromium can run on the compositor. Transform components qualify
+ * through `def.transform`, so only the non-transform ones are listed here.
+ */
+const COMPOSITABLE_CSS = new Set(['opacity', 'filter', 'backdrop-filter']);
+
+/**
+ * Whether an animation of this property can run off the main thread.
+ *
+ * Compositing is decided per effect, not per property: one non-compositable
+ * property pins everything animating alongside it, so a `width` would drag a
+ * sibling `opacity` down with it. Grouping keeps the two apart.
+ */
+export const isCompositable = (def: PropDef): boolean =>
+ def.transform === true || COMPOSITABLE_CSS.has(def.css);
+
/** Cache passthrough PropDefs for unknown keys to avoid repeat regex/string work. */
const UNKNOWN_PROP_CACHE = new Map();
diff --git a/src/lib/animate/properties/properties.ts b/src/lib/animate/properties/properties.ts
index 739f866..45053f4 100644
--- a/src/lib/animate/properties/properties.ts
+++ b/src/lib/animate/properties/properties.ts
@@ -208,5 +208,10 @@ export {
VAR_BIT,
registerTransformAnimation,
deregisterTransformAnimation,
- measureWithoutAncestorTransforms
+ registerFoldedTransforms,
+ hintTransformLayer,
+ demoteFoldedTransforms,
+ hasActiveTransforms,
+ measureWithoutAncestorTransforms,
+ type FoldedTransform
} from './transform-tracker';
diff --git a/src/lib/animate/properties/transform-setup.ts b/src/lib/animate/properties/transform-setup.ts
index e752285..fd3b24d 100644
--- a/src/lib/animate/properties/transform-setup.ts
+++ b/src/lib/animate/properties/transform-setup.ts
@@ -37,7 +37,13 @@ export const ensurePropertiesRegistered = (): void => {
}
};
-const ELEMENTS_WITH_TRANSFORM = new WeakSet();
+/**
+ * Which transform properties we installed a template into, per element. A
+ * caller's own `translate` is left alone at wiring time and must also never be
+ * folded into (see `keyframes/fold`) — we would be animating a value we do not
+ * own.
+ */
+const ELEMENTS_WITH_TRANSFORM = new WeakMap>();
/**
* Make sure the element's `translate` / `scale` / `rotate` styles are wired
@@ -45,13 +51,22 @@ const ELEMENTS_WITH_TRANSFORM = new WeakSet();
*/
export const ensureTransformWired = (element: MotionElement): void => {
if (ELEMENTS_WITH_TRANSFORM.has(element)) return;
- ELEMENTS_WITH_TRANSFORM.add(element);
const { style } = element;
- style.translate ||= TRANSFORM_TEMPLATES.translate;
- style.scale ||= TRANSFORM_TEMPLATES.scale;
- style.rotate ||= TRANSFORM_TEMPLATES.rotate;
+ const owned: Record = {
+ translate: !style.translate,
+ scale: !style.scale,
+ rotate: !style.rotate
+ };
+ ELEMENTS_WITH_TRANSFORM.set(element, owned);
+ if (owned.translate) style.translate = TRANSFORM_TEMPLATES.translate;
+ if (owned.scale) style.scale = TRANSFORM_TEMPLATES.scale;
+ if (owned.rotate) style.rotate = TRANSFORM_TEMPLATES.rotate;
};
+/** True when the template on this transform property is ours to animate. */
+export const isTransformOwned = (element: Element, target: string): boolean =>
+ ELEMENTS_WITH_TRANSFORM.get(element)?.[target] === true;
+
/**
* Both halves of the setup an element needs before anything writes `--motion-*`
* to it: global property registration plus this element's transform chain.
diff --git a/src/lib/animate/properties/transform-tracker.svelte.test.ts b/src/lib/animate/properties/transform-tracker.svelte.test.ts
index d4dec42..130798d 100644
--- a/src/lib/animate/properties/transform-tracker.svelte.test.ts
+++ b/src/lib/animate/properties/transform-tracker.svelte.test.ts
@@ -2,6 +2,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest';
import {
deregisterTransformAnimation,
measureWithoutAncestorTransforms,
+ registerFoldedTransforms,
registerTransformAnimation,
VAR_BIT
} from './transform-tracker';
@@ -44,3 +45,76 @@ describe('measureWithoutAncestorTransforms()', () => {
expect(observed[1]).toEqual(['20px', '']);
});
});
+
+describe('will-change hint', () => {
+ it('is set for the first channel, held across overlap, and restored after the last', () => {
+ const element = document.createElement('div');
+ document.body.appendChild(element);
+ mounted.push(element);
+ element.style.setProperty('will-change', 'opacity');
+ const x = VAR_BIT['--motion-x']!;
+ const y = VAR_BIT['--motion-y']!;
+
+ registerTransformAnimation(element, x);
+ expect(element.style.getPropertyValue('will-change')).toBe('translate, scale, rotate');
+
+ registerTransformAnimation(element, y);
+ deregisterTransformAnimation(element, x);
+ expect(element.style.getPropertyValue('will-change')).toBe('translate, scale, rotate');
+
+ deregisterTransformAnimation(element, y);
+ expect(element.style.getPropertyValue('will-change')).toBe('opacity');
+ });
+});
+
+describe('folded transforms', () => {
+ const fold = (element: HTMLElement) => {
+ const animation = { effect: { setKeyframes: vi.fn() } } as unknown as Animation;
+ registerFoldedTransforms(element, [
+ {
+ animation,
+ varFrames: { '--flip-x': ['10px', '0px'] },
+ targets: [['translate', 'calc(var(--flip-x, 0px)) 0px 0px']]
+ }
+ ]);
+ return animation;
+ };
+
+ it('reinstates the variable keyframes when another transform animation starts', () => {
+ const element = document.createElement('div');
+ document.body.appendChild(element);
+ mounted.push(element);
+ registerTransformAnimation(element, VAR_BIT['--flip-x']!);
+ const animation = fold(element);
+
+ registerTransformAnimation(element, VAR_BIT['--motion-x']!);
+
+ expect((animation.effect as KeyframeEffect).setKeyframes).toHaveBeenCalledWith({
+ '--flip-x': ['10px', '0px']
+ });
+ });
+
+ it('measures at rest by reinstating the template the fold overrides', () => {
+ const parent = document.createElement('div');
+ const child = document.createElement('div');
+ parent.appendChild(child);
+ document.body.appendChild(parent);
+ mounted.push(parent);
+ parent.style.setProperty('--flip-x', '10px');
+ registerTransformAnimation(parent, VAR_BIT['--flip-x']!);
+ fold(parent);
+ let observed: [string, string] | undefined;
+ vi.spyOn(child, 'getBoundingClientRect').mockImplementation(() => {
+ observed = [
+ parent.style.getPropertyValue('translate'),
+ parent.style.getPropertyPriority('translate')
+ ];
+ return new DOMRect();
+ });
+
+ measureWithoutAncestorTransforms(child);
+
+ expect(observed).toEqual(['calc(var(--flip-x, 0px)) 0px 0px', 'important']);
+ expect(parent.style.getPropertyValue('translate')).toBe('');
+ });
+});
diff --git a/src/lib/animate/properties/transform-tracker.ts b/src/lib/animate/properties/transform-tracker.ts
index 0cb3074..1dcd865 100644
--- a/src/lib/animate/properties/transform-tracker.ts
+++ b/src/lib/animate/properties/transform-tracker.ts
@@ -6,6 +6,13 @@
* has a running `animate()` transform animation, so `getBoundingClientRect()`
* returns the element's "at rest" layout position even mid-animation.
*
+ * The tracker also owns the two things that keep a transform animation off the
+ * main thread: the `will-change` hint that gets the element its own layer, and
+ * the registry of *folded* animations — ones that drive `translate` / `scale` /
+ * `rotate` directly instead of the motion vars (see `keyframes/fold`). A fold
+ * is only valid while nothing else touches the element's transform, so this
+ * module demotes it back to the variable path the moment that stops holding.
+ *
* The tracker is intentionally separate from the property registry so the
* static registry data and the dynamic runtime state live in different modules.
*/
@@ -58,12 +65,90 @@ const forEachBit = (bits: number, fn: (i: number) => void): void => {
}
};
+/**
+ * Can we write inline styles to this node? Duck-typed rather than an
+ * `instanceof HTMLElement` check: the ref-counting entry points also run under
+ * SSR, where those globals do not exist, and the capability is what we actually
+ * need — it also holds for elements from another realm, such as an iframe.
+ */
+const isMutableElement = (n: Element): n is MotionElement =>
+ typeof (n as Partial).style?.setProperty === 'function';
+
+/** True while the element has at least one `animate()` transform channel running. */
+export const hasActiveTransforms = (element: Element): boolean =>
+ (activeTransformBits.get(element) ?? 0) !== 0;
+
+const WILL_CHANGE = 'will-change';
+/**
+ * Hint the three properties the motion templates drive. Chromium promotes an
+ * element for an active *transform* animation, but a custom-property animation
+ * earns no layer, so without this the element repaints every frame — shadows,
+ * borders and all. The element already has non-`none` `translate`/`scale` once
+ * wired, so it is a stacking context either way and the hint changes nothing
+ * observable.
+ */
+const WILL_CHANGE_VALUE = 'translate, scale, rotate';
+const savedWillChange = new WeakMap();
+/**
+ * Ref-count for the hint itself, separate from the per-channel transform
+ * counts: a `animate()` call and a drag can both want the layer at once, and
+ * whichever finishes first must not take it away from the other.
+ */
+const layerHintCounts = new WeakMap();
+
+const retainLayerHint = (element: Element): void => {
+ const count = layerHintCounts.get(element) ?? 0;
+ layerHintCounts.set(element, count + 1);
+ if (count > 0 || !isMutableElement(element)) return;
+ savedWillChange.set(element, saveStyleProp(element.style, WILL_CHANGE));
+ element.style.setProperty(WILL_CHANGE, WILL_CHANGE_VALUE);
+};
+
+const releaseLayerHint = (element: Element): void => {
+ const count = layerHintCounts.get(element) ?? 0;
+ if (count === 0) return;
+ if (count > 1) {
+ layerHintCounts.set(element, count - 1);
+ return;
+ }
+ layerHintCounts.delete(element);
+ const saved = savedWillChange.get(element);
+ if (!saved || !isMutableElement(element)) return;
+ savedWillChange.delete(element);
+ restoreStyleProp(element.style, WILL_CHANGE, saved);
+};
+
+/**
+ * Hold a compositor layer for an element whose `--motion-*` values are being
+ * written directly, which is what every gesture does: they drive the vars by
+ * hand rather than through `animate()`, so nothing else would hint them and a
+ * dragged card with a shadow repaints on every pointer event.
+ *
+ * Scope the call to the active gesture, not to attachment setup — a permanent
+ * hint is a permanent layer, and a long list would hold one per row. The
+ * returned release is idempotent, so a gesture that ends twice is safe.
+ */
+export const hintTransformLayer = (element: Element): (() => void) => {
+ retainLayerHint(element);
+ let released = false;
+ return () => {
+ if (released) return;
+ released = true;
+ releaseLayerHint(element);
+ };
+};
+
/** Mark that an element has started a WAAPI transform animation. */
export const registerTransformAnimation = (element: Element, bits: number): void => {
+ // A fold owns `translate`/`scale`/`rotate` outright, which would mask the
+ // variable channel the incoming animation is about to drive. Hand the
+ // element back to the vars before it starts.
+ demoteFoldedTransforms(element);
let counts = activeTransformCounts.get(element);
if (!counts) {
counts = new Uint8Array(N_TRANSFORM_VARS);
activeTransformCounts.set(element, counts);
+ retainLayerHint(element);
}
let activeBits = activeTransformBits.get(element) ?? 0;
forEachBit(bits, (i) => {
@@ -83,11 +168,94 @@ export const deregisterTransformAnimation = (element: Element, bits: number): vo
if (activeBits === 0) {
activeTransformCounts.delete(element);
activeTransformBits.delete(element);
+ releaseLayerHint(element);
} else {
activeTransformBits.set(element, activeBits);
}
};
+/**
+ * One WAAPI animation that has been folded onto a real transform property.
+ */
+export interface FoldedTransform {
+ animation: Animation;
+ /** The pre-fold, variable-driven keyframes, reinstated on demotion. */
+ varFrames: PropertyIndexedKeyframes;
+ /**
+ * Transform properties this animation drives directly, each paired with the
+ * inline template it temporarily overrides. Measurement reinstates the
+ * template to read the element's resting box.
+ */
+ targets: ReadonlyArray;
+}
+
+const foldedTransforms = new WeakMap();
+const foldObservers = new WeakMap();
+const foldVarSnapshots = new WeakMap();
+
+/** Inline (not computed) motion-var values — no style recalc, safe to poll. */
+const readInlineVars = (element: MotionElement): string[] =>
+ MOTION_TRANSFORM_IDENTITIES.map(([name]) => element.style.getPropertyValue(name));
+
+/**
+ * Take ownership of an element's folded animations.
+ *
+ * A fold bakes the element's *other* motion vars into its keyframes, so any
+ * inline write to one of them leaves the animation showing a stale value. A
+ * style-attribute observer watches for exactly that and demotes, which is why
+ * gestures, presets and consumer code can keep writing `--motion-x` without
+ * knowing folds exist. Other style mutations — `pointer-events`, `opacity`, a
+ * measurement's save/restore round-trip — leave the baked values correct and
+ * are ignored.
+ */
+export const registerFoldedTransforms = (
+ element: MotionElement,
+ folds: readonly FoldedTransform[]
+): void => {
+ if (folds.length === 0) return;
+ foldedTransforms.set(element, [...folds]);
+ if (typeof MutationObserver === 'undefined') return;
+ const before = readInlineVars(element);
+ foldVarSnapshots.set(element, before);
+ const observer = new MutationObserver(() => {
+ const snapshot = foldVarSnapshots.get(element);
+ if (!snapshot) return;
+ const now = readInlineVars(element);
+ for (let i = 0; i < now.length; i++) {
+ if (now[i] !== snapshot[i]) {
+ demoteFoldedTransforms(element);
+ return;
+ }
+ }
+ });
+ observer.observe(element, { attributes: true, attributeFilter: ['style'] });
+ foldObservers.set(element, observer);
+};
+
+/**
+ * Put folded animations back on the variable path, in place.
+ *
+ * `setKeyframes` swaps an effect's values without touching its timing, so
+ * `currentTime`, playback state and the `finished` promise all survive and the
+ * animation carries on from the same visual position. Safe to call on an
+ * element that has no folds, and safe to call twice.
+ *
+ * ponytail: demotion is per element, not per transform property — a write to
+ * `--motion-x` also gives back a folded `rotate` that nothing touched. Split it
+ * per target if a trace ever shows that costing a frame.
+ */
+export const demoteFoldedTransforms = (element: Element): void => {
+ foldObservers.get(element)?.disconnect();
+ foldObservers.delete(element);
+ foldVarSnapshots.delete(element);
+ const folds = foldedTransforms.get(element);
+ if (!folds) return;
+ foldedTransforms.delete(element);
+ for (const { animation, varFrames } of folds) {
+ (animation.effect as KeyframeEffect | null)?.setKeyframes(varFrames);
+ }
+};
+
/**
* Like `element.getBoundingClientRect()` but walks up the ancestor chain
* and temporarily suppresses the motion CSS vars on every ancestor that
@@ -107,9 +275,6 @@ export const deregisterTransformAnimation = (element: Element, bits: number): vo
type SavedProp = { name: string; saved: SavedStyleProp };
type Suppressed = { node: MotionElement; props: SavedProp[] };
-const isMutableElement = (n: Element): n is MotionElement =>
- n instanceof HTMLElement || n instanceof SVGElement;
-
export const measureWithoutAncestorTransforms = (
el: Element,
{ suppressSelf = true }: { suppressSelf?: boolean } = {}
@@ -125,6 +290,17 @@ export const measureWithoutAncestorTransforms = (
props.push({ name, saved: saveStyleProp(target.style, name) });
target.style.setProperty(name, identity, 'important');
});
+ // A folded animation drives `translate`/`scale`/`rotate` itself, so
+ // forcing the vars to identity no longer reaches it. Reinstating the
+ // template with `!important` does: the template reads the vars we just
+ // neutralised, so the element measures at rest exactly as on the
+ // variable path.
+ for (const { targets } of foldedTransforms.get(target) ?? []) {
+ for (const [name, template] of targets) {
+ props.push({ name, saved: saveStyleProp(target.style, name) });
+ target.style.setProperty(name, template, 'important');
+ }
+ }
if (props.length > 0) suppressed.push({ node: target, props });
};
diff --git a/src/lib/gestures/draggable.svelte.test.ts b/src/lib/gestures/draggable.svelte.test.ts
index eb715a4..b6fd008 100644
--- a/src/lib/gestures/draggable.svelte.test.ts
+++ b/src/lib/gestures/draggable.svelte.test.ts
@@ -69,4 +69,23 @@ describe('draggable()', () => {
cleanup?.();
expect(el.style.touchAction).toBe('auto');
});
+
+ it('holds a compositor layer for the drag and releases it once settled', async () => {
+ const el = mount();
+ el.style.setProperty('will-change', 'opacity');
+ const cleanup = draggable({ snapToOrigin: true })(el);
+
+ el.dispatchEvent(pointer('pointerdown', 0, 0));
+ el.dispatchEvent(pointer('pointermove', 50, 30));
+ expect(el.style.getPropertyValue('will-change')).toBe('translate, scale, rotate');
+
+ el.dispatchEvent(pointer('pointerup', 50, 30));
+ // The flick keeps writing after the pointer is gone, so the layer outlives
+ // pointerup and is handed back only when the spring stops.
+ expect(el.style.getPropertyValue('will-change')).toBe('translate, scale, rotate');
+ await expect
+ .poll(() => el.style.getPropertyValue('will-change'), { timeout: 3000 })
+ .toBe('opacity');
+ cleanup?.();
+ });
});
diff --git a/src/lib/gestures/draggable.ts b/src/lib/gestures/draggable.ts
index a6d1155..c8b54b5 100644
--- a/src/lib/gestures/draggable.ts
+++ b/src/lib/gestures/draggable.ts
@@ -19,6 +19,7 @@ import type { SpringOptions } from '../shared/types';
import { createSpringValue } from '../animate/spring-value';
import { capture, lockTouchAction, release } from './pointer-capture';
import { wireTransform } from '../animate/properties/transform-setup';
+import { hintTransformLayer } from '../animate/properties/properties';
import { applyConstraint, xBounds, yBounds, type DragConstraints } from './constraints';
export type DragAxis = 'x' | 'y' | 'both';
@@ -85,6 +86,14 @@ export const draggable = (options: DraggableOptions = {}): Attachment element.style.setProperty('--motion-y', `${v}px`));
let dragging = false;
+ // Held from pointerdown until the spring stops writing, which is after the
+ // release flick has settled — not at pointerup.
+ let dropLayer: (() => void) | null = null;
+ const releaseLayer = (): void => {
+ const drop = dropLayer;
+ dropLayer = null;
+ drop?.();
+ };
let pointerId = -1;
let startX = 0;
let startY = 0;
@@ -103,6 +112,7 @@ export const draggable = (options: DraggableOptions = {}): Attachment {
if (dragging || event.button !== 0) return;
dragging = true;
+ dropLayer ??= hintTransformLayer(element);
pointerId = event.pointerId;
capture(element, pointerId);
// Resume from wherever the spring currently sits (interrupt-friendly).
@@ -146,6 +156,10 @@ export const draggable = (options: DraggableOptions = {}): Attachment {
+ if (!dragging) releaseLayer();
+ });
};
const unlisten = listen(element, {
@@ -161,6 +175,7 @@ export const draggable = (options: DraggableOptions = {}): Attachment {
expect(onMove).not.toHaveBeenCalled();
cleanup?.();
});
+
+ it('holds a compositor layer from pointerenter through the spring back', async () => {
+ const el = mount();
+ const cleanup = moveable({ applyTransform: true })(el);
+
+ el.dispatchEvent(pointer('pointerenter', 150, 100));
+ expect(el.style.getPropertyValue('will-change')).toBe('translate, scale, rotate');
+
+ el.dispatchEvent(pointer('pointerleave', 150, 100));
+ await expect.poll(() => el.style.getPropertyValue('will-change'), { timeout: 3000 }).toBe('');
+ cleanup?.();
+ });
});
diff --git a/src/lib/gestures/move.ts b/src/lib/gestures/move.ts
index 9aee447..0de14e8 100644
--- a/src/lib/gestures/move.ts
+++ b/src/lib/gestures/move.ts
@@ -24,6 +24,7 @@ import type { MotionElement } from '../animate';
import type { SpringOptions } from '../shared/types';
import { createSpringValue } from '../animate/spring-value';
import { wireTransform } from '../animate/properties/transform-setup';
+import { hintTransformLayer } from '../animate/properties/properties';
export interface MoveInfo {
/** Pointer position in client coordinates. */
@@ -81,10 +82,21 @@ export const moveable = (options: MoveableOptions = {}): Attachment element.style.setProperty('--motion-y', `${v}px`));
}
+ // Held while the pointer is over the element and through the spring-back
+ // after it leaves, which is still writing `--motion-x`.
+ let dropLayer: (() => void) | null = null;
+ const releaseLayer = (): void => {
+ const drop = dropLayer;
+ dropLayer = null;
+ drop?.();
+ };
+ let hovering = false;
+
const ignore = (event: PointerEvent): boolean => !includeTouch && event.pointerType === 'touch';
- const measure = (event: PointerEvent): MoveInfo => {
- const rect = element.getBoundingClientRect();
+ // The rect is passed in, not read here: `pull` needs the same one, and a
+ // spring write between two reads would force a second layout.
+ const measure = (event: PointerEvent, rect: DOMRect): MoveInfo => {
const localX = event.clientX - rect.left;
const localY = event.clientY - rect.top;
return {
@@ -97,28 +109,36 @@ export const moveable = (options: MoveableOptions = {}): Attachment {
+ const pull = (info: MoveInfo, rect: DOMRect): void => {
// Magnetic offset is the pointer's distance from centre, scaled. Written
// live with jump() (synchronous) — the pointer path is already smooth.
- springX!.jump((info.nx * element.getBoundingClientRect().width * strength) / 2);
- springY!.jump((info.ny * element.getBoundingClientRect().height * strength) / 2);
+ springX!.jump((info.nx * rect.width * strength) / 2);
+ springY!.jump((info.ny * rect.height * strength) / 2);
};
const onEnter = (event: PointerEvent): void => {
if (ignore(event)) return;
- onMoveStart?.(measure(event), element);
+ hovering = true;
+ if (applyTransform) dropLayer ??= hintTransformLayer(element);
+ onMoveStart?.(measure(event, element.getBoundingClientRect()), element);
};
const onPointerMove = (event: PointerEvent): void => {
if (ignore(event)) return;
- const info = measure(event);
- if (applyTransform) pull(info);
+ const rect = element.getBoundingClientRect();
+ const info = measure(event, rect);
+ if (applyTransform) pull(info, rect);
onMove?.(info, element);
};
const onLeave = (event: PointerEvent): void => {
if (ignore(event)) return;
+ hovering = false;
// Spring back to rest, carrying any momentum.
springX?.set(0);
springY?.set(0);
+ // Re-entering before the spring-back finishes keeps the layer.
+ void Promise.all([springX?.finished, springY?.finished]).then(() => {
+ if (!hovering) releaseLayer();
+ });
onMoveEnd?.(element);
};
@@ -130,6 +150,7 @@ export const moveable = (options: MoveableOptions = {}): Attachment {
unlisten();
+ releaseLayer();
unsubX();
unsubY();
springX?.stop();
diff --git a/src/lib/gestures/pinch.svelte.test.ts b/src/lib/gestures/pinch.svelte.test.ts
index 6978f96..66affd4 100644
--- a/src/lib/gestures/pinch.svelte.test.ts
+++ b/src/lib/gestures/pinch.svelte.test.ts
@@ -105,4 +105,20 @@ describe('pinchable()', () => {
expect(onMove).not.toHaveBeenCalled();
cleanup?.();
});
+
+ it('holds a compositor layer while both pointers are down', () => {
+ const el = mount();
+ const cleanup = pinchable()(el);
+
+ el.dispatchEvent(pointer('pointerdown', 1, 0, 0));
+ expect(el.style.getPropertyValue('will-change')).toBe('');
+
+ // The gesture, and the layer, begin on the second pointer.
+ el.dispatchEvent(pointer('pointerdown', 2, 100, 0));
+ expect(el.style.getPropertyValue('will-change')).toBe('translate, scale, rotate');
+
+ el.dispatchEvent(pointer('pointerup', 2, 100, 0));
+ expect(el.style.getPropertyValue('will-change')).toBe('');
+ cleanup?.();
+ });
});
diff --git a/src/lib/gestures/pinch.ts b/src/lib/gestures/pinch.ts
index 6a2ceb5..0d6dfcb 100644
--- a/src/lib/gestures/pinch.ts
+++ b/src/lib/gestures/pinch.ts
@@ -17,6 +17,7 @@ import { applyConstraint, type AxisBounds } from './constraints';
import type { MotionElement } from '../animate';
import { capture, lockTouchAction, release } from './pointer-capture';
import { wireTransform } from '../animate/properties/transform-setup';
+import { hintTransformLayer } from '../animate/properties/properties';
export interface PinchInfo {
/** Distance ratio of the two pointers relative to gesture start. */
@@ -62,6 +63,14 @@ export const pinchable = (options: PinchableOptions = {}): Attachment();
let pinching = false;
+ // The scale/rotate writes are synchronous with the pointers, so the layer
+ // is wanted for exactly as long as two fingers are down.
+ let dropLayer: (() => void) | null = null;
+ const releaseLayer = (): void => {
+ const drop = dropLayer;
+ dropLayer = null;
+ drop?.();
+ };
let startDistance = 0;
let startAngle = 0;
@@ -91,6 +100,7 @@ export const pinchable = (options: PinchableOptions = {}): Attachment {
unlisten();
points.clear();
+ releaseLayer();
unlockTouchAction();
};
};
diff --git a/src/lib/gestures/reorder.svelte.test.ts b/src/lib/gestures/reorder.svelte.test.ts
index 521ab9e..a259ba1 100644
--- a/src/lib/gestures/reorder.svelte.test.ts
+++ b/src/lib/gestures/reorder.svelte.test.ts
@@ -193,4 +193,22 @@ describe('reorder()', () => {
els[0]!.dispatchEvent(pointer('pointerup', 120));
expect(order).toEqual(['a', 'b', 'c']);
});
+
+ it('holds a compositor layer on every row for the duration of a drag', () => {
+ const { values, els } = setup();
+ let order = [...values];
+ const r = reorder({ items: () => order, onReorder: (next) => (order = next) });
+ els.forEach((el, i) => r.item(values[i]!)(el));
+
+ els[0]!.dispatchEvent(pointer('pointerdown', 20));
+ // Siblings slide into the vacated slot, so they are hinted too.
+ expect(els.map((el) => el.style.getPropertyValue('will-change'))).toEqual([
+ 'translate, scale, rotate',
+ 'translate, scale, rotate',
+ 'translate, scale, rotate'
+ ]);
+
+ els[0]!.dispatchEvent(pointer('pointerup', 20));
+ expect(els.map((el) => el.style.getPropertyValue('will-change'))).toEqual(['', '', '']);
+ });
});
diff --git a/src/lib/gestures/reorder.ts b/src/lib/gestures/reorder.ts
index 3db5b66..e8f8b13 100644
--- a/src/lib/gestures/reorder.ts
+++ b/src/lib/gestures/reorder.ts
@@ -27,6 +27,7 @@ import { listen } from '../shared/listen';
import { snapshotRect, flipFrom } from '../flip';
import type { EasingFn } from '../shared/types';
import { wireTransform } from '../animate/properties/transform-setup';
+import { hintTransformLayer } from '../animate/properties/properties';
import { restoreStyleProp, saveStyleProp, type SavedStyleProp } from '../shared/inline-style';
import { capture, lockTouchAction, release } from './pointer-capture';
@@ -103,6 +104,8 @@ interface DragState {
styles: Map;
/** Detaches the move/up/cancel listeners this drag installed. */
unlisten: () => void;
+ /** Releases the compositor-layer hint held on every row for this drag. */
+ dropLayers: Array<() => void>;
}
/** Create a reorder controller for one list. */
@@ -130,6 +133,9 @@ export const reorder = (options: ReorderOptions): ReorderHandle => {
const restoreDrag = (state: DragState): void => {
for (const node of state.els) restoreDragStyles(node, state.styles.get(node)!);
+ // The settle FLIP that follows goes through `animate()`, which takes its
+ // own hint, so there is no gap between the two.
+ for (const drop of state.dropLayers) drop();
};
/** Slide siblings into the dragged row's actual slot, including variable item sizes and gaps. */
@@ -224,7 +230,10 @@ export const reorder = (options: ReorderOptions): ReorderHandle => {
pointermove: onMove,
pointerup: endDrag,
pointercancel: endDrag
- })
+ }),
+ // Every row moves during a drag: the dragged one follows the pointer,
+ // the rest slide into its vacated slot.
+ dropLayers: rows.map((node) => hintTransformLayer(node))
};
// Reorder owns only its dedicated motion channel and temporary drag
diff --git a/src/lib/gestures/wheel.svelte.test.ts b/src/lib/gestures/wheel.svelte.test.ts
index fbdfb2f..aa56bd5 100644
--- a/src/lib/gestures/wheel.svelte.test.ts
+++ b/src/lib/gestures/wheel.svelte.test.ts
@@ -91,4 +91,16 @@ describe('wheelable()', () => {
expect(onMove).toHaveBeenCalledOnce();
cleanup?.();
});
+
+ it('holds a compositor layer until the wheel goes quiet', async () => {
+ const el = mount();
+ const cleanup = wheelable({ speed: 0.01, endDelay: 20 })(el);
+
+ el.dispatchEvent(wheel(-100));
+ expect(el.style.getPropertyValue('will-change')).toBe('translate, scale, rotate');
+
+ // Released only after the idle timer fires and the smoothing spring rests.
+ await expect.poll(() => el.style.getPropertyValue('will-change'), { timeout: 3000 }).toBe('');
+ cleanup?.();
+ });
});
diff --git a/src/lib/gestures/wheel.ts b/src/lib/gestures/wheel.ts
index 0a55df9..282f22f 100644
--- a/src/lib/gestures/wheel.ts
+++ b/src/lib/gestures/wheel.ts
@@ -25,6 +25,7 @@ import type { MotionElement } from '../animate';
import { createSpringValue } from '../animate/spring-value';
import type { SpringOptions } from '../shared/types';
import { wireTransform } from '../animate/properties/transform-setup';
+import { hintTransformLayer } from '../animate/properties/properties';
export interface WheelInfo {
/** Accumulated scale since the element mounted (baseline 1). */
@@ -103,6 +104,15 @@ export const wheelable = (options: WheelableOptions = {}): Attachment void) | null = null;
+ const releaseLayer = (): void => {
+ const drop = dropLayer;
+ dropLayer = null;
+ drop?.();
+ };
+
let detachSpring: (() => void) | null = null;
if (applyTransform) {
wireTransform(element);
@@ -120,6 +130,10 @@ export const wheelable = (options: WheelableOptions = {}): Attachment {
endTimer = null;
active = false;
+ // A notch arriving during the settle restarts the gesture and keeps it.
+ void Promise.resolve(springScale?.finished).then(() => {
+ if (!active) releaseLayer();
+ });
onEnd?.(info(0, 0), element);
};
@@ -133,6 +147,7 @@ export const wheelable = (options: WheelableOptions = {}): Attachment