[
  {
    "path": ".cursorrules",
    "content": "### Project overview\n- **Purpose**: Zero-config motion for DOM list changes. Given a parent element, automatically animates its immediate children when they are added, removed, or moved.\n- **Core entry**: `src/index.ts` exports `autoAnimate(el, configOrPlugin)` and types, plus a Vue directive `vAutoAnimate`.\n- **Framework adapters**: Thin wrappers providing hooks/directives for Vue, React, Solid, Preact, Angular, Qwik under `src/<framework>/index.ts`.\n- **Build**: TS → ESM via Rollup; built artifacts land in `dist/`. Package `exports` map serves `index.mjs` and framework subpaths.\n\n### Mental model (how it works)\n- **Observers**\n  - MutationObserver on the parent watches childList changes. For each mutation, `getElements` collects the parent and child elements that changed (including removed ones), tagging each with a non-enumerable target marker `__aa_tgt`.\n  - ResizeObserver on `document.documentElement` (root) and on each observed element updates cached positions when sizes change.\n  - IntersectionObserver is created per element to detect position changes while scrolling; a dynamic `rootMargin` derived from the last known coordinates triggers recalculation. Occasional low-priority polling backs this up.\n- **State tracking** (all with WeakMap/WeakSet to avoid leaks)\n  - `coords`: last known absolute coordinates adjusted for nearest scrolling ancestor.\n  - `siblings`: prev/next references for elements removed from the DOM so they can be temporarily reinserted for exit animations.\n  - `animations`: current Web Animations API `Animation` per element.\n  - `intersections`, `intervals`, `debounces`: observers/intervals/timers per element.\n  - `options`: the per-parent config or plugin; `enabled`: parents with animations enabled.\n- **Deciding what to animate**\n  - `animate(el)` selects one of: `add`, `remain`, or `remove` based on whether the element has cached coordinates and whether it’s still connected.\n  - Flags: `__aa_new` marks newly reinserted nodes to ensure their next cycle is treated as an entry; `__aa_del` marks nodes currently animating out.\n- **Effects**\n  - Default behavior uses Web Animations API: `add` scales/ fades in; `remain` uses FLIP-style transforms (translate from old to new), also animates width/height deltas respecting `box-sizing`; `remove` temporarily re-inserts the node near its original position, absolutely positions it, and scales/fades out.\n  - Plugins: if `config` is a function, it’s treated as an `AutoAnimationPlugin`. Plugin functions return a `KeyframeEffect` or `[KeyframeEffect, { styleReset | false }]`. The library will construct and play an `Animation` from the effect and handle optional style resets.\n- **Scroll handling**\n  - Coordinates are absolute plus the nearest scrollable ancestor’s offset.\n  - During removal at page bottom, the library compensates for scroll jumps by snapshotting pre/post window scroll and performing a manual scroll animation to match the configured duration/easing.\n- **Options & accessibility**\n  - Defaults: `{ duration: 250, easing: 'ease-in-out' }`.\n  - Respects `prefers-reduced-motion: reduce` unless `disrespectUserMotionPreference: true` or a plugin is used.\n  - `AnimationController` controls enable/disable per parent.\n\n### Public API surface\n- **Default export**: `autoAnimate(el: HTMLElement, config?: Partial<AutoAnimateOptions> | AutoAnimationPlugin): AnimationController`\n- **Types**: `AutoAnimateOptions`, `AutoAnimationPlugin`, `AutoAnimationPluginOptions`, `AnimationController`.\n- **Vue**: `vAutoAnimate` directive, `autoAnimatePlugin`, `useAutoAnimate` hook.\n- **React/Preact**: `useAutoAnimate` hook returns `[refCallback | refObject, setEnabled]`.\n- **Solid**: `createAutoAnimate` and `createAutoAnimateDirective`.\n- **Angular**: `AutoAnimateDirective` (`[auto-animate]`).\n- **Qwik**: `useAutoAnimate` returns `[Signal<HTMLElement>, setEnabledQRL]`.\n- **Nuxt**: module that registers the Vue directive and auto-imports `autoAnimate` and `useAutoAnimate`.\n\n### Key invariants and conventions\n- Do not mutate children beyond adding style resets for exit animations; remove those styles in `cleanUp`.\n- All per-node state is in WeakMaps/Sets to prevent memory leaks; never store strong references to DOM nodes outside these registries.\n- Only immediate children of the target parent are animated; traversal utilities (`forEach`) visit the parent first, then its children.\n- Always schedule position updates after animations finish (`animation.addEventListener('finish', updatePos)`), and debounce updates using the current duration (or 500ms for plugins).\n- When computing size deltas, use `getTransitionSizes` to respect `box-sizing: content-box` padding/borders.\n- Tagging properties `__aa_tgt`, `__aa_del`, `__aa_new` are non-enumerable and must be defined via `Object.defineProperty`.\n- Core must remain framework-agnostic; adapters import from `../index` only.\n\n### Files and what lives where\n- `src/index.ts`: All core logic and types; also exports a Vue directive for convenience.\n- `src/debug-utils.ts`: Optional dev-time helpers (e.g., `drawMargins`) to visualize IntersectionObserver margins.\n- `src/{vue,react,preact,solid,angular,qwik}/index.ts`: Framework bindings.\n- `src/nuxt/{module.ts,runtime/plugin.ts}`: Nuxt module and runtime plugin.\n- `docs/`: Vite/Vue demo and docs site.\n- `cypress/`: E2E tests for docs/examples.\n- `rollup.config.js`: Build configuration; `FRAMEWORK`, `DECLARATIONS`, and `MIN` env flags control outputs.\n- `package.json`: Exports map and scripts.\n\n### How to run\n- **Install**: `pnpm install`.\n- **Docs (playground)**: `pnpm dev` (runs Vite in `docs/`).\n- **Build library**: `pnpm build` (runs `node ./build/build.mjs`).\n- **Build docs**: `pnpm build-docs`.\n- **Cypress**: in one terminal `pnpm cypress:server` (docs dev server), in another `pnpm cypress:open`.\n- **Nuxt playground**: `pnpm nuxt:dev` (if playground present).\n\n### Adding or changing behavior\n- **Modify default animations**: adjust `add`, `remain`, `remove` in `src/index.ts`. Maintain invariants around style resets and cleanup. Ensure `updatePos` is called after each animation to keep `coords` fresh and observers connected.\n- **Plugins**: The plugin signature is the extension point. If adding plugin options, keep the tuple return backwards-compatible. Only treat `config` as plugin when it’s a function.\n- **Performance**: Be careful changing IntersectionObserver `threshold`/`rootMargin`; it’s tuned to avoid excessive updates and to catch movement. Use `lowPriority` for non-critical updates.\n- **Accessibility**: Keep the `prefers-reduced-motion` guard; only bypass if explicitly configured.\n\n### Common pitfalls\n- Forgetting to re-observe or update positions after animation causes stale `coords` and erratic flips. Always call `updatePos` on `finish`.\n- Mutating layout-affecting styles outside of animations can desync computed sizes. Prefer transform/opacity for motion; animate width/height only through `getTransitionSizes` path.\n- Not accounting for scroll in coords leads to incorrect deltas. Always use `getCoords` and `getScrollOffset` utilities.\n- Exit animations must temporarily reinsert the element and absolutely position it; skipping style reset or cleanup leaks styles into future reuses of that node.\n\n### Coding style\n- TypeScript strict; avoid `any` in public APIs.\n- Clear, descriptive names; early returns for edge cases.\n- Keep core free of framework-specific imports.\n- Comments explain “why” when logic is subtle (e.g., scroll compensation), not trivial “how”.\n\n### Testing guidance\n- Prefer adding example scenarios in `docs/` for manual verification.\n- E2E flows live in `cypress/integration`; add/extend specs that exercise add/remove/reorder, variable heights, nested scroll containers, and reduced-motion.\n\n### Triage checklist for bugs\n- Repro in `docs/` with a minimal list/grid.\n- Check if prefers-reduced-motion disabled animations.\n- Verify parent element has `position` not left `static` when required (core sets to `relative` if static).\n- Inspect `coords` freshness (use `updatePos` logs or `debug-utils`).\n- Confirm scroll compensation path when removing near viewport bottom.\n\n### External APIs used\n- Web Animations API (`Element.prototype.animate`, `Animation`, `KeyframeEffect`).\n- `MutationObserver`, `ResizeObserver`, `IntersectionObserver`.\n- `requestIdleCallback` (fallback to `requestAnimationFrame`).\n\n### Release & packaging\n- Build uses Rollup; env flags:\n  - `FRAMEWORK=index|vue|react|preact|solid|angular|qwik` controls entry file.\n  - `DECLARATIONS=true` emits types only.\n  - `MIN=true` emits minified `.min.js`.\n- `package.json#exports` serves subpath entries (`./vue`, `./react`, etc.).\n\n### Security & SSR\n- Core guards on `window` and `ResizeObserver` presence; no-ops in unsupported/non-browser contexts.\n- Vue directive exposes `getSSRProps` to be SSR-safe.\n\n### Docs site (Vite + Vue SSG)\n- **Architecture**: A separate Vue 3 SPA in `docs/` built and statically generated with Vite + `vite-ssg`. It produces a fully static site (no server required at runtime).\n- **Entry points**:\n  - `docs/index.html` is the HTML shell that loads `/src/main.ts`.\n  - `docs/src/main.ts` uses `ViteSSG(App, { routes }, setup)` to register routes and install the FormKit Vue plugin.\n  - `docs/src/App.vue` renders a `RouterView` and site chrome (header/footer/announcement).\n- **Routing**:\n  - Routes are declared in `docs/src/main.ts` via an array of `RouteRecordRaw`.\n  - Current routes: `/` → `PageHome.vue`, `/lists` → `PageList.vue`, `/tests` → `PageTests.vue`, `/tests-keep-alive` → `PageTestKeepAlive.vue` (some loaded via dynamic import for code-splitting).\n  - `vite.config.ts` sets `ssgOptions.includeAllRoutes = true`, so all known routes are pre-rendered during `vite-ssg build`.\n- **Content structure**:\n  - `docs/src/pages/`: top-level pages composing sections and examples.\n  - `docs/src/sections/`: content sections used on the home page (installation, usage, examples, plugins, why).\n  - `docs/src/components/`: UI components (navigation, header/footer, icons, code example wrapper, etc.).\n  - `docs/src/examples/`: live examples/demos per framework, showing how to use `@formkit/auto-animate` in practice.\n  - Global styles under `docs/assets/` (`main.css`, `variables.css`, `prism.css`), images under `docs/assets/img/`, and fonts under `docs/assets/fonts/`.\n- **Third-party integrations**:\n  - `@vitejs/plugin-vue` for Vue SFCs.\n  - `vite-ssg` for static generation.\n  - `@formkit/vue` plugin registered in the SSG setup.\n  - PrismJS loaded via `<script>` in `index.html` for code highlighting; GitHub buttons via their script; a site script tag is included for analytics.\n- **Dev/build commands** (from `package.json`):\n  - Dev playground: `pnpm dev` → `cd docs && vite --config=./vite.config.ts`.\n  - Static build: `pnpm build-docs` → `cd docs && vite-ssg build --config=./vite.config.ts` (outputs static assets to the default Vite output directory).\n  - Cypress E2E: start docs dev server `pnpm cypress:server` (port 5555), then run `pnpm cypress:open`.\n- **How examples reference the library**:\n  - Examples import from `@formkit/auto-animate` (and subpaths) to demonstrate public API usage. During local development, ensure the library is built (`pnpm build`) if the examples need the latest compiled artifacts; otherwise Vite may resolve from the package cache.\n- **Deployment**:\n  - The generated static output can be deployed to any static host (Netlify, Cloudflare Pages, GitHub Pages). No server logic is required.\n"
  },
  {
    "path": ".dmux-hooks/AGENTS.md",
    "content": "# dmux Hooks System - Agent Reference\n\n**Auto-generated documentation for AI agents**\n\nThis document contains everything an AI agent needs to create, modify, and understand dmux hooks. It is automatically generated from the dmux source code and embedded in the binary.\n\n## What You're Working On\n\nYou are editing hooks for **dmux**, a tmux pane manager that creates AI-powered development workflows. Each pane runs in its own git worktree with an AI agent (Claude Code or opencode).\n\n## Your Goal\n\nCreate executable bash scripts in `.dmux-hooks/` that run automatically at key lifecycle events.\n\n## Quick Start\n\n1. **Create a hook file**: `touch .dmux-hooks/worktree_created`\n2. **Make it executable**: `chmod +x .dmux-hooks/worktree_created`\n3. **Add shebang**: Start with `#!/bin/bash`\n4. **Use environment variables**: Access `$DMUX_ROOT`, `$DMUX_WORKTREE_PATH`, etc.\n5. **Test it**: Set env vars manually and run the script\n\n## Hook Execution Model\n\n- **Non-blocking**: Hooks run in background (detached processes)\n- **Silent failures**: Hook errors are logged but don't stop dmux\n- **Environment-based**: All context passed via environment variables\n- **Version controlled**: Hooks in `.dmux-hooks/` are shared with team\n- **Priority resolution**: `.dmux-hooks/` → `.dmux/hooks/` → `~/.dmux/hooks/`\n\n## Available Hooks\n\n### Pane Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_pane_create` | Before pane creation | Validation, notifications, pre-flight checks |\n| `pane_created` | After pane, before worktree | Configure tmux settings, prepare environment |\n| `worktree_created` | After full setup | Install deps, copy configs, setup git |\n| `before_pane_close` | Before closing | Save state, backup uncommitted work |\n| `pane_closed` | After closed | Cleanup resources, analytics, notifications |\n\n### Worktree Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_worktree_remove` | Before worktree removal | Archive worktree, save artifacts |\n| `worktree_removed` | After worktree removed | Cleanup external references |\n\n### Merge Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `pre_merge` | Before merge operation | Run final tests, create backups |\n| `post_merge` | After successful merge | Deploy, close issues, notify team |\n\n### Interactive Hooks (with HTTP callbacks)\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `run_test` | When tests triggered | Run test suite, report status via HTTP |\n| `run_dev` | When dev server triggered | Start dev server, create tunnel, report URL |\n\n\n## Environment Variables\n\n### Always Available\n```bash\nDMUX_ROOT=\"/path/to/project\"           # Project root directory\nDMUX_SERVER_PORT=\"3142\"                # HTTP server port\n```\n\n### Pane Context (most hooks)\n```bash\nDMUX_PANE_ID=\"dmux-1234567890\"         # dmux pane identifier\nDMUX_SLUG=\"fix-auth-bug\"               # Branch/worktree name\nDMUX_PROMPT=\"Fix authentication bug\"   # User's prompt\nDMUX_AGENT=\"claude\"                    # Agent type (claude|opencode)\nDMUX_TMUX_PANE_ID=\"%38\"                # tmux pane ID\n```\n\n### Worktree Context\n```bash\nDMUX_WORKTREE_PATH=\"/path/.dmux/worktrees/fix-auth-bug\"\nDMUX_BRANCH=\"fix-auth-bug\"             # Same as slug\n```\n\n### Merge Context\n```bash\nDMUX_TARGET_BRANCH=\"main\"              # Branch being merged into\n```\n\n## HTTP Callback API\n\nInteractive hooks (`run_test` and `run_dev`) can update dmux UI via HTTP.\n\n### Update Test Status\n```bash\ncurl -X PUT \"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\"   -H \"Content-Type: application/json\"   -d '{\"status\": \"running\", \"output\": \"optional test output\"}'\n\n# Status values: \"running\" | \"passed\" | \"failed\"\n```\n\n### Update Dev Server\n```bash\ncurl -X PUT \"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\"   -H \"Content-Type: application/json\"   -d '{\"status\": \"running\", \"url\": \"http://localhost:3000\"}'\n\n# Status values: \"running\" | \"stopped\"\n# url: Can be localhost or tunnel URL (ngrok, cloudflared, etc.)\n```\n\n## Common Patterns\n\n### Pattern 1: Install Dependencies\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\ncd \"$DMUX_WORKTREE_PATH\"\n\nif [ -f \"pnpm-lock.yaml\" ]; then\n  pnpm install --prefer-offline &\nelif [ -f \"package-lock.json\" ]; then\n  npm install &\nelif [ -f \"yarn.lock\" ]; then\n  yarn install &\nelif [ -f \"Gemfile\" ]; then\n  bundle install &\nelif [ -f \"requirements.txt\" ]; then\n  pip install -r requirements.txt &\nelif [ -f \"Cargo.toml\" ]; then\n  cargo build &\nfi\n```\n\n### Pattern 2: Copy Configuration\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\n# Copy environment file\nif [ -f \"$DMUX_ROOT/.env.local\" ]; then\n  cp \"$DMUX_ROOT/.env.local\" \"$DMUX_WORKTREE_PATH/.env.local\"\nfi\n\n# Copy other config files\nfor file in .env.development .npmrc .yarnrc; do\n  if [ -f \"$DMUX_ROOT/$file\" ]; then\n    cp \"$DMUX_ROOT/$file\" \"$DMUX_WORKTREE_PATH/$file\"\n  fi\ndone\n```\n\n### Pattern 3: Run Tests with Status Updates\n```bash\n#!/bin/bash\n# .dmux-hooks/run_test\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\"\n\n# Update: starting\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d '{\"status\": \"running\"}' > /dev/null\n\n# Run tests and capture output\nOUTPUT_FILE=\"/tmp/dmux-test-$DMUX_PANE_ID.txt\"\nif pnpm test > \"$OUTPUT_FILE\" 2>&1; then\n  STATUS=\"passed\"\nelse\n  STATUS=\"failed\"\nfi\n\n# Get output (truncate if too long)\nOUTPUT=$(head -c 5000 \"$OUTPUT_FILE\")\n\n# Update: complete\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"$(jq -n --arg status \"$STATUS\" --arg output \"$OUTPUT\"     '{status: $status, output: $output}')\" > /dev/null\n\nrm -f \"$OUTPUT_FILE\"\n```\n\n### Pattern 4: Dev Server with Tunnel\n```bash\n#!/bin/bash\n# .dmux-hooks/run_dev\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\"\n\n# Start dev server in background\nLOG_FILE=\"/tmp/dmux-dev-$DMUX_PANE_ID.log\"\npnpm dev > \"$LOG_FILE\" 2>&1 &\nDEV_PID=$!\n\n# Wait for server to start\nsleep 5\n\n# Detect port from logs\nPORT=$(grep -oP 'localhost:Kd+' \"$LOG_FILE\" | head -1)\n[ -z \"$PORT\" ] && PORT=3000\n\n# Optional: Create tunnel with cloudflared\nif command -v cloudflared &> /dev/null; then\n  TUNNEL=$(cloudflared tunnel --url \"http://localhost:$PORT\" 2>&1 |     grep -oP 'https://[a-z0-9-]+.trycloudflare.com' | head -1)\n  URL=\"${TUNNEL:-http://localhost:$PORT}\"\nelse\n  URL=\"http://localhost:$PORT\"\nfi\n\n# Report status\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"{\"status\": \"running\", \"url\": \"$URL\"}\" > /dev/null\n\necho \"[Hook] Dev server running at $URL (PID: $DEV_PID)\"\n```\n\n### Pattern 5: Post-Merge Deployment\n```bash\n#!/bin/bash\n# .dmux-hooks/post_merge\n\nset -e\ncd \"$DMUX_ROOT\"\n\n# Only deploy from main/master\nif [ \"$DMUX_TARGET_BRANCH\" != \"main\" ] && [ \"$DMUX_TARGET_BRANCH\" != \"master\" ]; then\n  exit 0\nfi\n\n# Push to remote\ngit push origin \"$DMUX_TARGET_BRANCH\"\n\n# Trigger deployment (example: Vercel)\nif [ -n \"$VERCEL_TOKEN\" ]; then\n  curl -s -X POST \"https://api.vercel.com/v1/deployments\"     -H \"Authorization: Bearer $VERCEL_TOKEN\"     -H \"Content-Type: application/json\"     -d '{\"name\": \"my-project\"}' > /dev/null\nfi\n\n# Close GitHub issue if prompt contains #123\nISSUE=$(echo \"$DMUX_PROMPT\" | grep -oP '#Kd+' | head -1)\nif [ -n \"$ISSUE\" ] && command -v gh &> /dev/null; then\n  gh issue close \"$ISSUE\"     -c \"Resolved in $DMUX_SLUG, merged to $DMUX_TARGET_BRANCH\"     2>/dev/null || true\nfi\n```\n\n## Best Practices\n\n1. **Always start with shebang**: `#!/bin/bash`\n2. **Set error handling**: `set -e` (exit on error)\n3. **Make executable**: `chmod +x .dmux-hooks/hook_name`\n4. **Background long operations**: Append `&` to avoid blocking\n5. **Check for required tools**: `command -v tool &> /dev/null`\n6. **Log for debugging**: `echo \"[Hook] message\" >> \"$DMUX_ROOT/.dmux/hooks.log\"`\n7. **Handle missing vars gracefully**: `[ -z \"$VAR\" ] && exit 0`\n8. **Use silent curl**: `curl -s` to avoid noise in logs\n9. **Clean up temp files**: Remove files in `/tmp/`\n10. **Test before committing**: Run hooks manually with mock env vars\n\n## Testing Hooks\n\n### Manual Testing\n```bash\n# 1. Set environment variables\nexport DMUX_ROOT=\"$(pwd)\"\nexport DMUX_PANE_ID=\"test-pane\"\nexport DMUX_SLUG=\"test-branch\"\nexport DMUX_WORKTREE_PATH=\"$(pwd)\"\nexport DMUX_SERVER_PORT=\"3142\"\nexport DMUX_AGENT=\"claude\"\nexport DMUX_PROMPT=\"Test prompt\"\n\n# 2. Run hook directly\n./.dmux-hooks/worktree_created\n\n# 3. Check exit code\necho $?  # Should be 0 for success\n```\n\n### Syntax Check\n```bash\n# Check for syntax errors without running\nbash -n ./.dmux-hooks/worktree_created\n```\n\n### Shellcheck (if available)\n```bash\nshellcheck ./.dmux-hooks/worktree_created\n```\n\n## Project Context Analysis\n\nBefore creating hooks, analyze these files in the project:\n\n### Package Manager Detection\n```bash\n# Check which package manager is used\nif [ -f \"pnpm-lock.yaml\" ]; then\n  # Use: pnpm install, pnpm test, pnpm dev\nelif [ -f \"package-lock.json\" ]; then\n  # Use: npm install, npm test, npm run dev\nelif [ -f \"yarn.lock\" ]; then\n  # Use: yarn install, yarn test, yarn dev\nfi\n```\n\n### Test Command Discovery\n```bash\n# Read package.json to find test command\ncat package.json | grep '\"test\"'\n# Or with jq:\njq -r '.scripts.test' package.json\n```\n\n### Dev Command Discovery\n```bash\n# Read package.json to find dev command\ncat package.json | grep '\"dev\"'\n# Or with jq:\njq -r '.scripts.dev' package.json\n```\n\n### Environment Variables\n```bash\n# Check for .env files to copy\nls -la | grep '.env'\n```\n\n### Build System\n```bash\n# Detect build system\nif [ -f \"vite.config.ts\" ]; then\n  # Vite project\nelif [ -f \"next.config.js\" ]; then\n  # Next.js project\nelif [ -f \"nuxt.config.ts\" ]; then\n  # Nuxt project\nfi\n```\n\n## Common Mistakes to Avoid\n\n❌ **Blocking operations**: `sleep 60` (blocks dmux)\n✅ **Background long tasks**: `slow_operation &`\n\n❌ **Hardcoded paths**: `/Users/me/project`\n✅ **Use variables**: `\"$DMUX_ROOT\"`\n\n❌ **Assuming tools exist**: `pnpm install`\n✅ **Check first**: `command -v pnpm && pnpm install`\n\n❌ **No error handling**: Script fails silently\n✅ **Set error mode**: `set -e` or check exit codes\n\n❌ **Forgetting executable bit**: Hook won't run\n✅ **Make executable**: `chmod +x`\n\n❌ **Noisy output**: Clutters dmux logs\n✅ **Silent operations**: `curl -s`, `> /dev/null 2>&1`\n\n❌ **Not testing**: Deploy and hope\n✅ **Test manually**: Run with mock env vars first\n\n## Debugging\n\nIf a hook isn't working:\n\n1. **Check if file exists**: `ls -la .dmux-hooks/`\n2. **Check permissions**: Should show `x` in `rwxr-xr-x`\n3. **Check syntax**: `bash -n .dmux-hooks/hook_name`\n4. **Test manually**: Set env vars and run\n5. **Check logs**: dmux logs to stderr with `[Hooks]` prefix\n6. **Simplify**: Remove complex parts, test basic version\n7. **Check tool availability**: `command -v required_tool`\n\n### Debug Mode\n```bash\n#!/bin/bash\n# Add to top of hook for debugging\nset -x  # Print each command before executing\nset -e  # Exit on error\n\n# Your hook logic here\n```\n\n## Summary Checklist\n\nWhen creating a new hook:\n\n- [ ] Create file in `.dmux-hooks/`\n- [ ] Add shebang: `#!/bin/bash`\n- [ ] Make executable: `chmod +x`\n- [ ] Add `set -e` for error handling\n- [ ] Use environment variables (never hardcode paths)\n- [ ] Background long operations with `&`\n- [ ] Check for required tools before using\n- [ ] Test manually with mock env vars\n- [ ] Add comments explaining what it does\n- [ ] Commit to version control\n\n## Getting Help\n\n- **Full documentation**: See `HOOKS.md` in project root\n- **Claude-specific tips**: See `CLAUDE.md` in `.dmux-hooks/`\n- **Examples**: Check `.dmux-hooks/examples/` directory\n- **dmux API**: See `API.md` for REST endpoints\n\n---\n\n*This documentation was auto-generated from dmux source code.*\n*Version: 2025-12-14*\n"
  },
  {
    "path": ".dmux-hooks/CLAUDE.md",
    "content": "# dmux Hooks System - Agent Reference\n\n**Auto-generated documentation for AI agents**\n\nThis document contains everything an AI agent needs to create, modify, and understand dmux hooks. It is automatically generated from the dmux source code and embedded in the binary.\n\n## What You're Working On\n\nYou are editing hooks for **dmux**, a tmux pane manager that creates AI-powered development workflows. Each pane runs in its own git worktree with an AI agent (Claude Code or opencode).\n\n## Your Goal\n\nCreate executable bash scripts in `.dmux-hooks/` that run automatically at key lifecycle events.\n\n## Quick Start\n\n1. **Create a hook file**: `touch .dmux-hooks/worktree_created`\n2. **Make it executable**: `chmod +x .dmux-hooks/worktree_created`\n3. **Add shebang**: Start with `#!/bin/bash`\n4. **Use environment variables**: Access `$DMUX_ROOT`, `$DMUX_WORKTREE_PATH`, etc.\n5. **Test it**: Set env vars manually and run the script\n\n## Hook Execution Model\n\n- **Non-blocking**: Hooks run in background (detached processes)\n- **Silent failures**: Hook errors are logged but don't stop dmux\n- **Environment-based**: All context passed via environment variables\n- **Version controlled**: Hooks in `.dmux-hooks/` are shared with team\n- **Priority resolution**: `.dmux-hooks/` → `.dmux/hooks/` → `~/.dmux/hooks/`\n\n## Available Hooks\n\n### Pane Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_pane_create` | Before pane creation | Validation, notifications, pre-flight checks |\n| `pane_created` | After pane, before worktree | Configure tmux settings, prepare environment |\n| `worktree_created` | After full setup | Install deps, copy configs, setup git |\n| `before_pane_close` | Before closing | Save state, backup uncommitted work |\n| `pane_closed` | After closed | Cleanup resources, analytics, notifications |\n\n### Worktree Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_worktree_remove` | Before worktree removal | Archive worktree, save artifacts |\n| `worktree_removed` | After worktree removed | Cleanup external references |\n\n### Merge Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `pre_merge` | Before merge operation | Run final tests, create backups |\n| `post_merge` | After successful merge | Deploy, close issues, notify team |\n\n### Interactive Hooks (with HTTP callbacks)\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `run_test` | When tests triggered | Run test suite, report status via HTTP |\n| `run_dev` | When dev server triggered | Start dev server, create tunnel, report URL |\n\n\n## Environment Variables\n\n### Always Available\n```bash\nDMUX_ROOT=\"/path/to/project\"           # Project root directory\nDMUX_SERVER_PORT=\"3142\"                # HTTP server port\n```\n\n### Pane Context (most hooks)\n```bash\nDMUX_PANE_ID=\"dmux-1234567890\"         # dmux pane identifier\nDMUX_SLUG=\"fix-auth-bug\"               # Branch/worktree name\nDMUX_PROMPT=\"Fix authentication bug\"   # User's prompt\nDMUX_AGENT=\"claude\"                    # Agent type (claude|opencode)\nDMUX_TMUX_PANE_ID=\"%38\"                # tmux pane ID\n```\n\n### Worktree Context\n```bash\nDMUX_WORKTREE_PATH=\"/path/.dmux/worktrees/fix-auth-bug\"\nDMUX_BRANCH=\"fix-auth-bug\"             # Same as slug\n```\n\n### Merge Context\n```bash\nDMUX_TARGET_BRANCH=\"main\"              # Branch being merged into\n```\n\n## HTTP Callback API\n\nInteractive hooks (`run_test` and `run_dev`) can update dmux UI via HTTP.\n\n### Update Test Status\n```bash\ncurl -X PUT \"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\"   -H \"Content-Type: application/json\"   -d '{\"status\": \"running\", \"output\": \"optional test output\"}'\n\n# Status values: \"running\" | \"passed\" | \"failed\"\n```\n\n### Update Dev Server\n```bash\ncurl -X PUT \"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\"   -H \"Content-Type: application/json\"   -d '{\"status\": \"running\", \"url\": \"http://localhost:3000\"}'\n\n# Status values: \"running\" | \"stopped\"\n# url: Can be localhost or tunnel URL (ngrok, cloudflared, etc.)\n```\n\n## Common Patterns\n\n### Pattern 1: Install Dependencies\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\ncd \"$DMUX_WORKTREE_PATH\"\n\nif [ -f \"pnpm-lock.yaml\" ]; then\n  pnpm install --prefer-offline &\nelif [ -f \"package-lock.json\" ]; then\n  npm install &\nelif [ -f \"yarn.lock\" ]; then\n  yarn install &\nelif [ -f \"Gemfile\" ]; then\n  bundle install &\nelif [ -f \"requirements.txt\" ]; then\n  pip install -r requirements.txt &\nelif [ -f \"Cargo.toml\" ]; then\n  cargo build &\nfi\n```\n\n### Pattern 2: Copy Configuration\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\n# Copy environment file\nif [ -f \"$DMUX_ROOT/.env.local\" ]; then\n  cp \"$DMUX_ROOT/.env.local\" \"$DMUX_WORKTREE_PATH/.env.local\"\nfi\n\n# Copy other config files\nfor file in .env.development .npmrc .yarnrc; do\n  if [ -f \"$DMUX_ROOT/$file\" ]; then\n    cp \"$DMUX_ROOT/$file\" \"$DMUX_WORKTREE_PATH/$file\"\n  fi\ndone\n```\n\n### Pattern 3: Run Tests with Status Updates\n```bash\n#!/bin/bash\n# .dmux-hooks/run_test\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\"\n\n# Update: starting\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d '{\"status\": \"running\"}' > /dev/null\n\n# Run tests and capture output\nOUTPUT_FILE=\"/tmp/dmux-test-$DMUX_PANE_ID.txt\"\nif pnpm test > \"$OUTPUT_FILE\" 2>&1; then\n  STATUS=\"passed\"\nelse\n  STATUS=\"failed\"\nfi\n\n# Get output (truncate if too long)\nOUTPUT=$(head -c 5000 \"$OUTPUT_FILE\")\n\n# Update: complete\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"$(jq -n --arg status \"$STATUS\" --arg output \"$OUTPUT\"     '{status: $status, output: $output}')\" > /dev/null\n\nrm -f \"$OUTPUT_FILE\"\n```\n\n### Pattern 4: Dev Server with Tunnel\n```bash\n#!/bin/bash\n# .dmux-hooks/run_dev\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\"\n\n# Start dev server in background\nLOG_FILE=\"/tmp/dmux-dev-$DMUX_PANE_ID.log\"\npnpm dev > \"$LOG_FILE\" 2>&1 &\nDEV_PID=$!\n\n# Wait for server to start\nsleep 5\n\n# Detect port from logs\nPORT=$(grep -oP 'localhost:Kd+' \"$LOG_FILE\" | head -1)\n[ -z \"$PORT\" ] && PORT=3000\n\n# Optional: Create tunnel with cloudflared\nif command -v cloudflared &> /dev/null; then\n  TUNNEL=$(cloudflared tunnel --url \"http://localhost:$PORT\" 2>&1 |     grep -oP 'https://[a-z0-9-]+.trycloudflare.com' | head -1)\n  URL=\"${TUNNEL:-http://localhost:$PORT}\"\nelse\n  URL=\"http://localhost:$PORT\"\nfi\n\n# Report status\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"{\"status\": \"running\", \"url\": \"$URL\"}\" > /dev/null\n\necho \"[Hook] Dev server running at $URL (PID: $DEV_PID)\"\n```\n\n### Pattern 5: Post-Merge Deployment\n```bash\n#!/bin/bash\n# .dmux-hooks/post_merge\n\nset -e\ncd \"$DMUX_ROOT\"\n\n# Only deploy from main/master\nif [ \"$DMUX_TARGET_BRANCH\" != \"main\" ] && [ \"$DMUX_TARGET_BRANCH\" != \"master\" ]; then\n  exit 0\nfi\n\n# Push to remote\ngit push origin \"$DMUX_TARGET_BRANCH\"\n\n# Trigger deployment (example: Vercel)\nif [ -n \"$VERCEL_TOKEN\" ]; then\n  curl -s -X POST \"https://api.vercel.com/v1/deployments\"     -H \"Authorization: Bearer $VERCEL_TOKEN\"     -H \"Content-Type: application/json\"     -d '{\"name\": \"my-project\"}' > /dev/null\nfi\n\n# Close GitHub issue if prompt contains #123\nISSUE=$(echo \"$DMUX_PROMPT\" | grep -oP '#Kd+' | head -1)\nif [ -n \"$ISSUE\" ] && command -v gh &> /dev/null; then\n  gh issue close \"$ISSUE\"     -c \"Resolved in $DMUX_SLUG, merged to $DMUX_TARGET_BRANCH\"     2>/dev/null || true\nfi\n```\n\n## Best Practices\n\n1. **Always start with shebang**: `#!/bin/bash`\n2. **Set error handling**: `set -e` (exit on error)\n3. **Make executable**: `chmod +x .dmux-hooks/hook_name`\n4. **Background long operations**: Append `&` to avoid blocking\n5. **Check for required tools**: `command -v tool &> /dev/null`\n6. **Log for debugging**: `echo \"[Hook] message\" >> \"$DMUX_ROOT/.dmux/hooks.log\"`\n7. **Handle missing vars gracefully**: `[ -z \"$VAR\" ] && exit 0`\n8. **Use silent curl**: `curl -s` to avoid noise in logs\n9. **Clean up temp files**: Remove files in `/tmp/`\n10. **Test before committing**: Run hooks manually with mock env vars\n\n## Testing Hooks\n\n### Manual Testing\n```bash\n# 1. Set environment variables\nexport DMUX_ROOT=\"$(pwd)\"\nexport DMUX_PANE_ID=\"test-pane\"\nexport DMUX_SLUG=\"test-branch\"\nexport DMUX_WORKTREE_PATH=\"$(pwd)\"\nexport DMUX_SERVER_PORT=\"3142\"\nexport DMUX_AGENT=\"claude\"\nexport DMUX_PROMPT=\"Test prompt\"\n\n# 2. Run hook directly\n./.dmux-hooks/worktree_created\n\n# 3. Check exit code\necho $?  # Should be 0 for success\n```\n\n### Syntax Check\n```bash\n# Check for syntax errors without running\nbash -n ./.dmux-hooks/worktree_created\n```\n\n### Shellcheck (if available)\n```bash\nshellcheck ./.dmux-hooks/worktree_created\n```\n\n## Project Context Analysis\n\nBefore creating hooks, analyze these files in the project:\n\n### Package Manager Detection\n```bash\n# Check which package manager is used\nif [ -f \"pnpm-lock.yaml\" ]; then\n  # Use: pnpm install, pnpm test, pnpm dev\nelif [ -f \"package-lock.json\" ]; then\n  # Use: npm install, npm test, npm run dev\nelif [ -f \"yarn.lock\" ]; then\n  # Use: yarn install, yarn test, yarn dev\nfi\n```\n\n### Test Command Discovery\n```bash\n# Read package.json to find test command\ncat package.json | grep '\"test\"'\n# Or with jq:\njq -r '.scripts.test' package.json\n```\n\n### Dev Command Discovery\n```bash\n# Read package.json to find dev command\ncat package.json | grep '\"dev\"'\n# Or with jq:\njq -r '.scripts.dev' package.json\n```\n\n### Environment Variables\n```bash\n# Check for .env files to copy\nls -la | grep '.env'\n```\n\n### Build System\n```bash\n# Detect build system\nif [ -f \"vite.config.ts\" ]; then\n  # Vite project\nelif [ -f \"next.config.js\" ]; then\n  # Next.js project\nelif [ -f \"nuxt.config.ts\" ]; then\n  # Nuxt project\nfi\n```\n\n## Common Mistakes to Avoid\n\n❌ **Blocking operations**: `sleep 60` (blocks dmux)\n✅ **Background long tasks**: `slow_operation &`\n\n❌ **Hardcoded paths**: `/Users/me/project`\n✅ **Use variables**: `\"$DMUX_ROOT\"`\n\n❌ **Assuming tools exist**: `pnpm install`\n✅ **Check first**: `command -v pnpm && pnpm install`\n\n❌ **No error handling**: Script fails silently\n✅ **Set error mode**: `set -e` or check exit codes\n\n❌ **Forgetting executable bit**: Hook won't run\n✅ **Make executable**: `chmod +x`\n\n❌ **Noisy output**: Clutters dmux logs\n✅ **Silent operations**: `curl -s`, `> /dev/null 2>&1`\n\n❌ **Not testing**: Deploy and hope\n✅ **Test manually**: Run with mock env vars first\n\n## Debugging\n\nIf a hook isn't working:\n\n1. **Check if file exists**: `ls -la .dmux-hooks/`\n2. **Check permissions**: Should show `x` in `rwxr-xr-x`\n3. **Check syntax**: `bash -n .dmux-hooks/hook_name`\n4. **Test manually**: Set env vars and run\n5. **Check logs**: dmux logs to stderr with `[Hooks]` prefix\n6. **Simplify**: Remove complex parts, test basic version\n7. **Check tool availability**: `command -v required_tool`\n\n### Debug Mode\n```bash\n#!/bin/bash\n# Add to top of hook for debugging\nset -x  # Print each command before executing\nset -e  # Exit on error\n\n# Your hook logic here\n```\n\n## Summary Checklist\n\nWhen creating a new hook:\n\n- [ ] Create file in `.dmux-hooks/`\n- [ ] Add shebang: `#!/bin/bash`\n- [ ] Make executable: `chmod +x`\n- [ ] Add `set -e` for error handling\n- [ ] Use environment variables (never hardcode paths)\n- [ ] Background long operations with `&`\n- [ ] Check for required tools before using\n- [ ] Test manually with mock env vars\n- [ ] Add comments explaining what it does\n- [ ] Commit to version control\n\n## Getting Help\n\n- **Full documentation**: See `HOOKS.md` in project root\n- **Claude-specific tips**: See `CLAUDE.md` in `.dmux-hooks/`\n- **Examples**: Check `.dmux-hooks/examples/` directory\n- **dmux API**: See `API.md` for REST endpoints\n\n---\n\n*This documentation was auto-generated from dmux source code.*\n*Version: 2025-12-14*\n"
  },
  {
    "path": ".dmux-hooks/README.md",
    "content": "# dmux Hooks\n\nThis directory contains hooks that run automatically at key lifecycle events in dmux.\n\n## Quick Start\n\n1. **Read the documentation**:\n   - `AGENTS.md` - Complete reference (for any AI agent)\n   - `CLAUDE.md` - Same content (Claude Code looks for this filename)\n\n2. **Check examples**:\n   - `examples/` directory contains starter templates\n\n3. **Create a hook**:\n   ```bash\n   touch worktree_created\n   chmod +x worktree_created\n   nano worktree_created\n   ```\n\n4. **Test it**:\n   ```bash\n   export DMUX_ROOT=\"$(pwd)\"\n   export DMUX_WORKTREE_PATH=\"$(pwd)\"\n   ./worktree_created\n   ```\n\n## Available Hooks\n\n- `before_pane_create` - Before pane creation\n- `pane_created` - After pane created\n- `worktree_created` - After worktree setup\n- `before_pane_close` - Before closing\n- `pane_closed` - After closed\n- `before_worktree_remove` - Before worktree removal\n- `worktree_removed` - After worktree removed\n- `pre_merge` - Before merge\n- `post_merge` - After merge\n- `run_test` - When running tests\n- `run_dev` - When starting dev server\n\n## Documentation\n\nSee `AGENTS.md` or `CLAUDE.md` for complete documentation including:\n- Environment variables\n- HTTP callback API\n- Common patterns\n- Best practices\n- Testing strategies\n\n## Note\n\nThis directory is **version controlled**. Hooks you create here will be shared with your team.\n"
  },
  {
    "path": ".dmux-hooks/examples/post_merge.example",
    "content": "#!/bin/bash\n# Example: post_merge hook\n#\n# This hook runs after a successful merge into the target branch.\n# Use it to trigger deployments, close issues, notify teams, etc.\n\nset -e\n\necho \"[Hook] Post-merge processing for $DMUX_SLUG → $DMUX_TARGET_BRANCH\"\n\ncd \"$DMUX_ROOT\"\n\n# Push to remote if merging to main/master\nif [ \"$DMUX_TARGET_BRANCH\" = \"main\" ] || [ \"$DMUX_TARGET_BRANCH\" = \"master\" ]; then\n  echo \"[Hook] Pushing to origin/$DMUX_TARGET_BRANCH\"\n  git push origin \"$DMUX_TARGET_BRANCH\"\n\n  # Optional: Trigger deployment\n  # if [ -n \"$VERCEL_TOKEN\" ]; then\n  #   echo \"[Hook] Triggering Vercel deployment...\"\n  #   curl -X POST \"https://api.vercel.com/v1/deployments\" \\\n  #     -H \"Authorization: Bearer $VERCEL_TOKEN\" \\\n  #     -H \"Content-Type: application/json\" \\\n  #     -d '{\n  #       \"name\": \"my-project\",\n  #       \"gitSource\": {\n  #         \"type\": \"github\",\n  #         \"ref\": \"main\"\n  #       }\n  #     }'\n  # fi\nfi\n\n# Close related GitHub issue (if prompt contains #123 format)\nISSUE_NUM=$(echo \"$DMUX_PROMPT\" | grep -oP '#\\K\\d+' | head -1)\nif [ -n \"$ISSUE_NUM\" ]; then\n  echo \"[Hook] Closing GitHub issue #$ISSUE_NUM\"\n  if command -v gh &> /dev/null; then\n    gh issue close \"$ISSUE_NUM\" \\\n      -c \"Resolved in branch $DMUX_SLUG, merged to $DMUX_TARGET_BRANCH\" \\\n      2>/dev/null || echo \"[Hook] Warning: Failed to close issue (maybe already closed?)\"\n  else\n    echo \"[Hook] GitHub CLI (gh) not found, skipping issue close\"\n  fi\nfi\n\n# Send notification to Slack\n# if [ -n \"$SLACK_WEBHOOK\" ]; then\n#   echo \"[Hook] Sending Slack notification\"\n#   curl -s -X POST \"$SLACK_WEBHOOK\" \\\n#     -H \"Content-Type: application/json\" \\\n#     -d \"{\n#       \\\"text\\\": \\\"Merged: $DMUX_SLUG → $DMUX_TARGET_BRANCH\\\",\n#       \\\"blocks\\\": [\n#         {\n#           \\\"type\\\": \\\"section\\\",\n#           \\\"text\\\": {\n#             \\\"type\\\": \\\"mrkdwn\\\",\n#             \\\"text\\\": \\\"*Branch Merged* :rocket:\\n\\n*From:* \\`$DMUX_SLUG\\`\\n*To:* \\`$DMUX_TARGET_BRANCH\\`\\n*Task:* $DMUX_PROMPT\\\"\n#           }\n#         }\n#       ]\n#     }\" > /dev/null\n# fi\n\necho \"[Hook] Post-merge processing complete\"\n"
  },
  {
    "path": ".dmux-hooks/examples/run_dev.example",
    "content": "#!/bin/bash\n# Example: run_dev hook\n#\n# This hook starts a dev server and optionally creates a tunnel for sharing.\n# It reports the server URL back to dmux via the HTTP API.\n\nset -e\n\necho \"[Hook] Starting dev server for $DMUX_SLUG\"\n\ncd \"$DMUX_WORKTREE_PATH\"\nAPI_URL=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\"\n\n# Update status: starting\ncurl -s -X PUT \"$API_URL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"status\": \"running\"}' > /dev/null\n\n# Start dev server in background\n# Adjust the command for your project (pnpm dev, npm run dev, vite, etc.)\nLOG_FILE=\"/tmp/dmux-dev-$DMUX_PANE_ID.log\"\npnpm dev > \"$LOG_FILE\" 2>&1 &\nDEV_PID=$!\n\n# Wait for server to be ready\necho \"[Hook] Waiting for dev server to start...\"\nsleep 5\n\n# Detect port from log output\n# Adjust the grep pattern for your dev server's output format\nPORT=$(grep -oP '(?<=localhost:)\\d+' \"$LOG_FILE\" | head -1)\n\nif [ -z \"$PORT\" ]; then\n  echo \"[Hook] Warning: Could not detect port from logs, using default 3000\"\n  PORT=3000\nfi\n\nLOCAL_URL=\"http://localhost:$PORT\"\necho \"[Hook] Dev server running at $LOCAL_URL\"\n\n# Optional: Create a public tunnel (uncomment to enable)\n# Requires ngrok, cloudflared, or another tunneling tool\n\n# Example with cloudflared:\n# TUNNEL_URL=$(cloudflared tunnel --url \"$LOCAL_URL\" 2>&1 | \\\n#   grep -oP 'https://[a-z0-9-]+\\.trycloudflare\\.com' | head -1)\n\n# Example with ngrok:\n# TUNNEL_URL=$(ngrok http $PORT --log=stdout 2>&1 | \\\n#   grep -oP 'url=https://[^\"]+' | head -1 | cut -d= -f2)\n\n# For now, just use local URL (uncomment tunnel code above to enable)\nFINAL_URL=\"$LOCAL_URL\"\n\n# Report status back to dmux\ncurl -s -X PUT \"$API_URL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d \"{\\\"status\\\": \\\"running\\\", \\\"url\\\": \\\"$FINAL_URL\\\"}\" > /dev/null\n\necho \"[Hook] Dev server ready at: $FINAL_URL\"\necho \"[Hook] Dev server PID: $DEV_PID\"\necho \"[Hook] Log file: $LOG_FILE\"\n"
  },
  {
    "path": ".dmux-hooks/examples/run_test.example",
    "content": "#!/bin/bash\n# Example: run_test hook\n#\n# This hook runs tests and reports the status back to dmux via the HTTP API.\n# Status updates appear in real-time in the dmux UI.\n\nset -e\n\necho \"[Hook] Running tests for $DMUX_SLUG\"\n\ncd \"$DMUX_WORKTREE_PATH\"\nAPI_URL=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\"\n\n# Update status: running\ncurl -s -X PUT \"$API_URL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"status\": \"running\"}' > /dev/null\n\necho \"[Hook] Running test suite...\"\n\n# Capture test output\nOUTPUT_FILE=\"/tmp/dmux-test-$DMUX_PANE_ID.txt\"\n\n# Run tests (adjust command for your project)\n# Examples:\n#   - pnpm test\n#   - npm test\n#   - vitest run\n#   - jest\n#   - pytest\n#   - cargo test\nif pnpm test > \"$OUTPUT_FILE\" 2>&1; then\n  STATUS=\"passed\"\n  echo \"[Hook] Tests passed ✓\"\nelse\n  STATUS=\"failed\"\n  echo \"[Hook] Tests failed ✗\"\nfi\n\n# Get output (truncate if too long)\nOUTPUT=$(head -c 5000 \"$OUTPUT_FILE\")\n\n# Report results back to dmux\ncurl -s -X PUT \"$API_URL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d \"$(jq -n \\\n    --arg status \"$STATUS\" \\\n    --arg output \"$OUTPUT\" \\\n    '{status: $status, output: $output}')\" > /dev/null\n\n# Cleanup\nrm -f \"$OUTPUT_FILE\"\n\necho \"[Hook] Test results reported to dmux\"\n\n# Exit with test status\nif [ \"$STATUS\" = \"passed\" ]; then\n  exit 0\nelse\n  exit 1\nfi\n"
  },
  {
    "path": ".dmux-hooks/examples/worktree_created.example",
    "content": "#!/bin/bash\n# Example: worktree_created hook\n#\n# This hook runs after a new worktree is created and the agent is launched.\n# Use it to set up the worktree environment (install deps, copy configs, etc.)\n\nset -e  # Exit on error\n\necho \"[Hook] Setting up worktree: $DMUX_SLUG\"\n\ncd \"$DMUX_WORKTREE_PATH\"\n\n# Install dependencies in background (don't block dmux)\nif [ -f \"pnpm-lock.yaml\" ]; then\n  echo \"[Hook] Installing dependencies with pnpm...\"\n  pnpm install --prefer-offline &\nelif [ -f \"package-lock.json\" ]; then\n  echo \"[Hook] Installing dependencies with npm...\"\n  npm install &\nelif [ -f \"yarn.lock\" ]; then\n  echo \"[Hook] Installing dependencies with yarn...\"\n  yarn install &\nfi\n\n# Copy environment file if it exists\nif [ -f \"$DMUX_ROOT/.env.local\" ]; then\n  echo \"[Hook] Copying .env.local\"\n  cp \"$DMUX_ROOT/.env.local\" \"$DMUX_WORKTREE_PATH/.env.local\"\nfi\n\n# Set custom git config for this worktree\necho \"[Hook] Configuring git\"\ngit config user.name \"dmux-agent/$DMUX_SLUG\"\ngit config user.email \"agent@dmux.local\"\n\n# Create a log entry\necho \"[$(date)] Created worktree: $DMUX_SLUG | Agent: $DMUX_AGENT | Prompt: $DMUX_PROMPT\" \\\n  >> \"$DMUX_ROOT/.dmux/worktree_history.log\"\n\necho \"[Hook] Worktree setup complete!\"\n"
  },
  {
    "path": ".editorconfig",
    "content": "# editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[*.md]\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: formkit\n"
  },
  {
    "path": ".github/workflows/main.yml",
    "content": "name: CI\non:\n  push:\n    branches:\n      - master\n      - 'release/*'\n  pull_request:\n    branches:\n      - master\n      - 'release/*'\n  workflow_dispatch:\n\njobs:\n  e2e:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n      \n      - name: Setup pnpm\n        uses: pnpm/action-setup@v4\n        # version is read from package.json#packageManager\n      \n      - name: Setup Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 20\n          cache: 'pnpm'\n      \n      - name: Get pnpm store directory\n        id: pnpm-cache\n        shell: bash\n        run: |\n          echo \"STORE_PATH=$(pnpm store path)\" >> $GITHUB_OUTPUT\n      \n      - name: Setup pnpm cache\n        uses: actions/cache@v4\n        with:\n          path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}\n          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}\n          restore-keys: |\n            ${{ runner.os }}-pnpm-store-\n      \n      - name: Get Playwright version\n        id: playwright-version\n        run: echo \"PLAYWRIGHT_VERSION=$(pnpm ls @playwright/test --json | jq -r '.[0].devDependencies.\"@playwright/test\".version')\" >> $GITHUB_OUTPUT\n      \n      - name: Cache Playwright browsers\n        id: playwright-cache\n        uses: actions/cache@v4\n        with:\n          path: ~/.cache/ms-playwright\n          key: ${{ runner.os }}-playwright-${{ steps.playwright-version.outputs.PLAYWRIGHT_VERSION }}\n          restore-keys: |\n            ${{ runner.os }}-playwright-\n      \n      - name: Install dependencies\n        run: pnpm install --frozen-lockfile=false\n      \n      - name: Install Playwright Browsers\n        if: steps.playwright-cache.outputs.cache-hit != 'true'\n        run: pnpm exec playwright install --with-deps\n      \n      - name: Install Playwright dependencies only\n        if: steps.playwright-cache.outputs.cache-hit == 'true'\n        run: pnpm exec playwright install-deps\n      \n      - name: Build project\n        run: NO_NUXT=1 pnpm build\n      \n      - name: Run Playwright tests\n        run: pnpm test:e2e\n      - name: Upload Playwright report\n        if: always()\n        uses: actions/upload-artifact@v4\n        with:\n          name: playwright-report\n          path: playwright-report\n      - name: Upload Playwright videos (visual)\n        if: always()\n        uses: actions/upload-artifact@v4\n        with:\n          name: playwright-videos\n          path: test-results/**/video.webm\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\ndist\n*.log\n.DS_Store\n\n# Playwright artifacts\nplaywright-report/\nplaywright-report/index.html\ntest-results/\n\ndist\n.DS_Store\nnode_modules\ncoverage\ntemp\n*.log\n.vscode\n/.aws/credentials\n.yalc\n.idea\n.nuxt\n*.local.json\n.node_modules_tmp\n"
  },
  {
    "path": ".npmignore",
    "content": "src\napi-extractor.json\n__tests__\n.editorconfig\n.gitignore\nrollup.config.js\ntsconfig.json\nyarn.lock\ndocs\n.prettierrc\n.build.sh\n.github\n"
  },
  {
    "path": ".npmrc",
    "content": "shamefully-hoist=true"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"tabWidth\": 2,\n  \"useTabs\": false,\n  \"semi\": false\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2022 FormKit Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<img width=\"300\" height=\"37\" alt=\"AutoAnimate Logo\" src=\"https://cdn.formk.it/web-assets/logo-auto-animate.svg\" >\n\n![Latest Build](https://github.com/formkit/auto-animate/actions/workflows/main.yml/badge.svg) ![GitHub Sponsors](https://img.shields.io/github/sponsors/formkit)\n\n# Add motion to your apps with a single line of code.\n\nAutoAnimate is a zero-config, drop-in animation utility that adds smooth transitions to your web app. You can use it with Vue, React, Solid or any other JavaScript application.\n\n<a href=\"https://auto-animate.formkit.com\"><img width=\"180\" height=\"42\" src=\"https://cdn.formk.it/web-assets/read-docs-auto-animate-v2.svg\" alt=\"Examples & Docs\"></a>\n\nWith one line of code, you can improve your interfaces, for example:\n\n<a href=\"https://auto-animate.formkit.com\">\n  <img src=\"https://cdn.formk.it/web-assets/motion.gif\" alt=\"Simple example of AutoAnimation motion\">\n</a>\n\n## Installation\n\nInstall using your package manager of choice.\n\n```bash\n# yarn\nyarn add @formkit/auto-animate\n# npm\nnpm install @formkit/auto-animate\n# pnpm\npnpm add @formkit/auto-animate\n```\n\nBoom! Done. That was fast! 🐇\n\n## Usage\n\n📖 [View the documentation site for usage instructions](https://auto-animate.formkit.com#usage).\n\n## Examples\n\n📖 [View the documentation site for examples](https://auto-animate.formkit.com#examples).\n\n## Plugins\n\n📖 [View the documentation site for plugin instructions](https://auto-animate.formkit.com#plugins).\n\n## Support us\n\nIs AutoAnimate saving you time?\n\nPlease consider [supporting us with a recurring or one-time donation](https://github.com/sponsors/formkit)! 🙏\n\n## Contributing\n\nThank you for your willingness to contribute to this free and open source project! When contributing, consider first discussing your desired change with the core team via <a href=\"https://github.com/formkit/auto-animate/issues\">GitHub issues</a>, <a href=\"https://discord.gg/SHYT8pyeNm\">Discord</a>, or other method.\n"
  },
  {
    "path": "build/build-nuxt.ts",
    "content": "import { existsSync, promises as fsp } from \"fs\"\nimport { pathToFileURL } from \"url\"\nimport { resolve } from \"pathe\"\nimport { consola } from \"consola\"\nimport type { ModuleMeta, NuxtModule, NuxtConfig } from \"@nuxt/schema\"\nimport { findExports } from \"mlly\"\n\ninterface BuildModuleOptions {\n  rootDir: string\n  srcDir?: string\n  sourcemap?: boolean\n  stub?: boolean\n  outDir?: string\n}\n\ninterface PrepareModuleOptions {\n  rootDir: string\n  srcDir: string\n}\n\n/**\n * Original source: https://github.com/nuxt/module-builder/blob/main/src/prepare.ts\n * @param options\n */\nasync function prepareModule (options: PrepareModuleOptions) {\n  const { runCommand } = await import('nuxi')\n\n  return runCommand('prepare', [resolve(options.rootDir, 'build/nuxt-playground')], {\n    overrides: {\n      typescript: {\n        builder: 'shared'\n      },\n      imports: {\n        autoImport: false\n      },\n      modules: [\n        resolve(options.rootDir, `${options.srcDir}/module`),\n        function (_options, nuxt) {\n          nuxt.hooks.hook('app:templates', (app) => {\n            for (const template of app.templates) {\n              template.write = true\n            }\n          })\n        }\n      ]\n    } satisfies NuxtConfig\n  })\n}\n\n\n/**\n * Original source: https://github.com/nuxt/module-builder/blob/main/src/build.ts\n * @param opts - options\n */\nexport async function buildModule(opts: BuildModuleOptions) {\n  const { build } = await import(\"unbuild\")\n\n  const outDir = opts.outDir || \"dist\"\n  const srcDir = opts.srcDir || \"src\"\n\n  await prepareModule({ rootDir: opts.rootDir, srcDir })\n\n  await build(opts.rootDir, false, {\n    clean: false, // auto-animate’s build does its own cleaning\n    failOnWarn: false, // unbuild will validate the package.json, but we don’t want to fail on warnings\n    declaration: true,\n    sourcemap: opts.sourcemap,\n    stub: opts.stub,\n    outDir,\n    entries: [\n      { input: `${srcDir}/module`, outDir: `${outDir}/nuxt` },\n      { input: `${srcDir}/runtime/`, outDir: `${outDir}/nuxt/runtime`, ext: \"mjs\" },\n    ],\n    rollup: {\n      emitCJS: false,\n      cjsBridge: false,\n      dts: {\n        tsconfig: \"./build/nuxt-playground/tsconfig.json\",\n      },\n    },\n    externals: [\n      \"@nuxt/schema\",\n      \"@nuxt/schema-edge\",\n      \"@nuxt/kit\",\n      \"@nuxt/kit-edge\",\n      \"nuxt\",\n      \"nuxt-edge\",\n      \"nuxt3\",\n      \"vue\",\n      \"vue-demi\",\n    ],\n    hooks: {\n      async \"rollup:done\"(ctx) {\n        const outDir = resolve(ctx.options.outDir, 'nuxt')\n        // Generate CommonJS stub\n        await writeCJSStub(outDir)\n\n        // Load module meta\n        const moduleEntryPath = resolve(outDir, \"module.mjs\")\n        const moduleFn: NuxtModule<any> = await import(\n          pathToFileURL(moduleEntryPath).toString()\n        )\n          .then((r) => r.default || r)\n          .catch((err) => {\n            consola.error(err)\n            consola.error(\n              \"Cannot load module. Please check dist:\",\n              moduleEntryPath\n            )\n            return null\n          })\n\n        if (!moduleFn || !moduleFn.getMeta) {\n          return\n        }\n        const moduleMeta = await moduleFn.getMeta()\n\n        // Enhance meta using package.json\n        if (ctx.pkg) {\n          if (!moduleMeta.name) {\n            moduleMeta.name = ctx.pkg.name\n          }\n          if (!moduleMeta.version) {\n            moduleMeta.version = ctx.pkg.version\n          }\n        }\n\n        // Write meta\n        const metaFile = resolve(outDir, \"module.json\")\n        await fsp.writeFile(\n          metaFile,\n          JSON.stringify(moduleMeta, null, 2),\n          \"utf8\"\n        )\n\n        // Generate types\n        await writeTypes(outDir, moduleMeta)\n      },\n    },\n  })\n}\n\nasync function writeTypes(distDir: string, meta: ModuleMeta) {\n  const dtsFile = resolve(distDir, \"types.d.ts\")\n  const dtsFileMts = resolve(distDir, \"types.d.mts\")\n  if (existsSync(dtsFile)) {\n    return\n  }\n\n  // Read generated module types\n  const moduleTypesFile = resolve(distDir, \"module.d.ts\")\n  const moduleTypes = await fsp\n    .readFile(moduleTypesFile, \"utf8\")\n    .catch(() => \"\")\n  const typeExports = findExports(\n    // Replace `export { type Foo }` with `export { Foo }`\n    moduleTypes.replace(/export\\s*{.*?}/gs, (match) =>\n      match.replace(/\\btype\\b/g, \"\")\n    )\n  )\n  const isStub = moduleTypes.includes(\"export *\")\n\n  const schemaShims = []\n  const moduleImports = []\n\n  const hasTypeExport = (name: string) =>\n    isStub || typeExports.find((exp) => exp.names.includes(name))\n\n  if (meta.configKey && hasTypeExport(\"ModuleOptions\")) {\n    moduleImports.push(\"ModuleOptions\")\n    schemaShims.push(\n      `  interface NuxtConfig { ['${meta.configKey}']?: Partial<ModuleOptions> }`\n    )\n    schemaShims.push(\n      `  interface NuxtOptions { ['${meta.configKey}']?: ModuleOptions }`\n    )\n  }\n  if (hasTypeExport(\"ModuleHooks\")) {\n    moduleImports.push(\"ModuleHooks\")\n    schemaShims.push(\"  interface NuxtHooks extends ModuleHooks {}\")\n  }\n  if (hasTypeExport(\"ModuleRuntimeConfig\")) {\n    moduleImports.push(\"ModuleRuntimeConfig\")\n    schemaShims.push(\"  interface RuntimeConfig extends ModuleRuntimeConfig {}\")\n  }\n  if (hasTypeExport(\"ModulePublicRuntimeConfig\")) {\n    moduleImports.push(\"ModulePublicRuntimeConfig\")\n    schemaShims.push(\n      \"  interface PublicRuntimeConfig extends ModulePublicRuntimeConfig {}\"\n    )\n  }\n\n  const dtsContents = `\nimport { ${moduleImports.join(\", \")} } from './module'\n\n${\n  schemaShims.length\n    ? `declare module '@nuxt/schema' {\\n${schemaShims.join(\"\\n\")}\\n}\\n`\n    : \"\"\n}\n${\n  schemaShims.length\n    ? `declare module 'nuxt/schema' {\\n${schemaShims.join(\"\\n\")}\\n}\\n`\n    : \"\"\n}\n\nexport { ${typeExports[0].names.join(\", \")} } from './module'\n`\n\n  await fsp.writeFile(dtsFile, dtsContents, \"utf8\")\n  if (!existsSync(dtsFileMts)) {\n    await fsp.writeFile(dtsFileMts, dtsContents, \"utf8\")\n  }\n}\n\nasync function writeCJSStub(distDir: string) {\n  const cjsStubFile = resolve(distDir, \"module.cjs\")\n  if (existsSync(cjsStubFile)) {\n    return\n  }\n  const cjsStub = `module.exports = function(...args) {\n  return import('./module.mjs').then(m => m.default.call(this, ...args))\n}\nconst _meta = module.exports.meta = require('./module.json')\nmodule.exports.getMeta = () => Promise.resolve(_meta)\n`\n  await fsp.writeFile(cjsStubFile, cjsStub, \"utf8\")\n}\n"
  },
  {
    "path": "build/build.mjs",
    "content": "import createJITI from \"jiti\"\nimport { fileURLToPath } from \"url\"\nimport { dirname, resolve } from \"path\"\n\nconst __filename = fileURLToPath(import.meta.url)\n\nconst jiti = createJITI(__filename, {\n  esmResolve: true,\n})\n\njiti(\"./bundle.ts\")\n"
  },
  {
    "path": "build/bundle.ts",
    "content": "import { dirname, resolve } from \"path\"\nimport { fileURLToPath } from \"url\"\nimport { readFile, writeFile } from \"fs/promises\"\nimport { execa } from \"execa\"\nimport { execSync } from \"child_process\"\nimport chalk from \"chalk\"\nimport prompts from \"prompts\"\nimport { sync } from \"brotli-size\"\nimport prettyBytes from \"pretty-bytes\"\nimport { buildModule } from \"./build-nuxt\"\n\nconst info = (m: string) => console.log(chalk.blue(m))\nconst error = (m: string) => console.log(chalk.red(m))\nconst success = (m: string) => console.log(chalk.green(m))\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\nconst rootDir = resolve(`${__dirname}/../`)\nconst isPublishing = process.argv[2] === \"--publish\"\n\nasync function clean() {\n  await execa(\"shx\", [\"rm\", \"-rf\", `${rootDir}/dist`])\n}\n\nasync function baseBuild() {\n  info(\"Rolling up primary package\")\n  await execa(\"npx\", [\"rollup\", \"-c\", \"rollup.config.js\"])\n}\n\nasync function baseBuildMin() {\n  info(\"Minifying primary package\")\n  await execa(\"npx\", [\n    \"rollup\",\n    \"-c\",\n    \"rollup.config.js\",\n    \"--environment\",\n    \"MIN:true\",\n  ])\n}\n\nasync function reactBuild() {\n  info(\"Rolling up React package\")\n  await execa(\"npx\", [\n    \"rollup\",\n    \"-c\",\n    \"rollup.config.js\",\n    \"--environment\",\n    \"FRAMEWORK:react\",\n  ])\n  /**\n   * This is a super hack — for some reason these imports need to be explicitly\n   * to .mjs files so...we make it so.\n   */\n  let raw = await readFile(resolve(rootDir, \"dist/react/index.mjs\"), \"utf8\")\n  raw = raw.replace(\"from '../index'\", \"from '../index.mjs'\")\n  await writeFile(resolve(rootDir, \"dist/react/index.mjs\"), raw)\n}\nasync function solidBuild() {\n  info(\"Rolling up Solid package\")\n  await execa(\"npx\", [\n    \"rollup\",\n    \"-c\",\n    \"rollup.config.js\",\n    \"--environment\",\n    \"FRAMEWORK:solid\",\n  ])\n  /**\n   * This is a super hack — for some reason these imports need to be explicitly\n   * to .mjs files so...we make it so.\n   */\n  let raw = await readFile(resolve(rootDir, \"dist/solid/index.mjs\"), \"utf8\")\n  raw = raw.replace(\"from '../index'\", \"from '../index.mjs'\")\n  await writeFile(resolve(rootDir, \"dist/solid/index.mjs\"), raw)\n}\n\nasync function preactBuild() {\n  info(\"Rolling up Preact package\")\n  await execa(\"npx\", [\n    \"rollup\",\n    \"-c\",\n    \"rollup.config.js\",\n    \"--environment\",\n    \"FRAMEWORK:preact\",\n  ])\n  /**\n   * This is a super hack — for some reason these imports need to be explicitly\n   * to .mjs files so...we make it so.\n   */\n  let raw = await readFile(resolve(rootDir, \"dist/preact/index.mjs\"), \"utf8\")\n  raw = raw.replace(\"from '../index'\", \"from '../index.mjs'\")\n  await writeFile(resolve(rootDir, \"dist/preact/index.mjs\"), raw)\n}\n\nasync function vueBuild() {\n  info(\"Rolling up Vue package\")\n  await execa(\"npx\", [\n    \"rollup\",\n    \"-c\",\n    \"rollup.config.js\",\n    \"--environment\",\n    \"FRAMEWORK:vue\",\n  ])\n  /**\n   * This is a super hack — for some reason these imports need to be explicitly\n   * to .mjs files so...we make it so.\n   */\n  let raw = await readFile(resolve(rootDir, \"dist/vue/index.mjs\"), \"utf8\")\n  raw = raw.replace(\"from '../index'\", \"from '../index.mjs'\")\n  await writeFile(resolve(rootDir, \"dist/vue/index.mjs\"), raw)\n}\n\nasync function angularBuild() {\n  info(\"Rolling up Angular package\")\n  await execa(\"npx\", [\n    \"rollup\",\n    \"-c\",\n    \"rollup.config.js\",\n    \"--environment\",\n    \"FRAMEWORK:angular\",\n  ])\n  /**\n   * This is a super hack — for some reason these imports need to be explicitly\n   * to .mjs files so...we make it so.\n   */\n  let raw = await readFile(resolve(rootDir, \"dist/angular/index.mjs\"), \"utf8\")\n  raw = raw.replace(\n    \"import autoAnimate from '../index';\",\n    \"import autoAnimate from '../index.mjs';\"\n  )\n  await writeFile(resolve(rootDir, \"dist/angular/index.mjs\"), raw)\n}\n\n// async function qwikBuild() {\n//   info(\"Rolling up Qwik package\")\n//   await execa(\"npx\", [\n//     \"rollup\",\n//     \"-c\",\n//     \"rollup.config.js\",\n//     \"--environment\",\n//     \"FRAMEWORK:qwik\",\n//   ])\n//   /**\n//    * This is a super hack — for some reason these imports need to be explicitly\n//    * to .mjs files so...we make it so.\n//    */\n//   let raw = await readFile(resolve(rootDir, \"dist/qwik/index.mjs\"), \"utf8\")\n//   raw = raw.replace(\"from '../index'\", \"from '../index.mjs'\")\n//   await writeFile(resolve(rootDir, \"dist/qwik/index.mjs\"), raw)\n// }\n\nasync function nuxtBuild() {\n  info(\"Building nuxt module\")\n  await buildModule({\n    rootDir,\n    srcDir: \"src/nuxt\",\n  })\n}\n\nasync function declarationsBuild() {\n  info(\"Outputting declarations\")\n  await execa(\"npx\", [\n    \"rollup\",\n    \"-c\",\n    \"rollup.config.js\",\n    \"--environment\",\n    \"DECLARATIONS:true\",\n  ])\n}\n\nasync function bundleDeclarations() {\n  info(\"Bundling declarations\")\n  await execa(\"shx\", [\n    \"mv\",\n    `${rootDir}/dist/src/index.d.ts`,\n    `${rootDir}/dist/index.d.ts`,\n  ])\n  await execa(\"shx\", [\n    \"mv\",\n    `${rootDir}/dist/src/react/index.d.ts`,\n    `${rootDir}/dist/react/index.d.ts`,\n  ])\n  await execa(\"shx\", [\n    \"mv\",\n    `${rootDir}/dist/src/preact/index.d.ts`,\n    `${rootDir}/dist/preact/index.d.ts`,\n  ])\n  await execa(\"shx\", [\n    \"mv\",\n    `${rootDir}/dist/src/solid/index.d.ts`,\n    `${rootDir}/dist/solid/index.d.ts`,\n  ])\n  await execa(\"shx\", [\n    \"mv\",\n    `${rootDir}/dist/src/vue/index.d.ts`,\n    `${rootDir}/dist/vue/index.d.ts`,\n  ])\n  await execa(\"shx\", [\n    \"mv\",\n    `${rootDir}/dist/src/angular/index.d.ts`,\n    `${rootDir}/dist/angular/index.d.ts`,\n  ])\n  await execa(\"shx\", [\n    \"mv\",\n    `${rootDir}/dist/src/qwik/index.d.ts`,\n    `${rootDir}/dist/qwik/index.d.ts`,\n  ])\n  await execa(\"shx\", [\"rm\", \"-rf\", `${rootDir}/dist/src`])\n  await execa(\"shx\", [\"rm\", `${rootDir}/dist/index.js`])\n}\n\nasync function addPackageJSON() {\n  info(\"Writing package.json\")\n  const raw = await readFile(resolve(rootDir, \"package.json\"), \"utf8\")\n  const packageJSON = JSON.parse(raw)\n  delete packageJSON.private\n  delete packageJSON.devDependencies\n  delete packageJSON.scripts\n  await writeFile(\n    resolve(rootDir, \"dist/package.json\"),\n    JSON.stringify(packageJSON, null, 2)\n  )\n}\n\nasync function addAssets() {\n  info(\"Writing readme and license.\")\n  await execa(\"shx\", [\n    \"cp\",\n    `${rootDir}/README.md`,\n    `${rootDir}/dist/README.md`,\n  ])\n  await execa(\"shx\", [\"cp\", `${rootDir}/LICENSE`, `${rootDir}/dist/LICENSE`])\n}\n\nasync function prepareForPublishing() {\n  info(\"Preparing for publication\")\n  if (\n    process.env.npm_execpath &&\n    !/pnpm\\.cjs$/.test(process.env.npm_execpath)\n  ) {\n    error(`⚠️ You must run this command with pnpm.`)\n    info(\"Please try again with:\\n\\n» pnpm run publish\\n\\n\")\n    process.exit()\n  }\n  const isClean = !execSync(`git status --untracked-files=no --porcelain`, {\n    encoding: \"utf-8\",\n  })\n  if (!isClean) {\n    error(\"Commit your changes before publishing.\")\n    process.exit()\n  }\n  // Version confirmation is now handled by bumpp in the release script\n}\n\nasync function publish() {\n  const raw = await readFile(resolve(rootDir, \"package.json\"), \"utf8\")\n  const packageJSON = JSON.parse(raw)\n  const response = await prompts([\n    {\n      type: \"confirm\",\n      name: \"value\",\n      message: `Project is build. Ready to publish?`,\n      initial: false,\n    },\n  ])\n  if (response.value) {\n    execSync(\"pnpm publish ./dist\")\n    execSync(`git tag ${packageJSON.version}`)\n    execSync(`git push origin --tags`)\n  }\n}\n\nasync function outputSize() {\n  const raw = await readFile(resolve(rootDir, \"dist/index.min.js\"), \"utf8\")\n  console.log(\"Brotli size: \" + prettyBytes(sync(raw)))\n}\n\nasync function main() {\n  if (isPublishing) await prepareForPublishing()\n  await clean()\n  await baseBuild()\n  await baseBuildMin()\n  await reactBuild()\n  await preactBuild()\n  await solidBuild()\n  await vueBuild()\n  await angularBuild()\n  // await qwikBuild()\n  await declarationsBuild()\n  await bundleDeclarations()\n  // Skip nuxt module build in CI or when NO_NUXT is set\n  if (!process.env.NO_NUXT) {\n    await nuxtBuild()\n  }\n  await addPackageJSON()\n  await addAssets()\n  await outputSize()\n  isPublishing ? await publish() : success(\"Build complete\")\n}\n\nmain()\n"
  },
  {
    "path": "build/nuxt-playground/app.vue",
    "content": "<script lang=\"ts\" setup>\nconst items = ref(['A', 'B', 'C'])\n\nfunction add () {\n  items.value.push(`${items.value.length + 1}`)\n}\n</script>\n\n<template>\n  <ul v-auto-animate>\n    This is the way...\n  </ul>\n  <button @click.prevent=\"add\">Add item</button>\n</template>\n"
  },
  {
    "path": "build/nuxt-playground/nuxt.config.ts",
    "content": "export default defineNuxtConfig({\n  modules: [\"../../src/nuxt/module.ts\"],\n  autoAnimate: {},\n  devtools: { enabled: false },\n})\n"
  },
  {
    "path": "build/nuxt-playground/package.json",
    "content": "{\n  \"private\": true,\n  \"name\": \"auto-animate-nuxt-module\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"nuxi dev\",\n    \"build\": \"nuxi build\",\n    \"generate\": \"nuxi generate\"\n  },\n  \"devDependencies\": {\n    \"nuxt\": \"latest\"\n  }\n}\n"
  },
  {
    "path": "build/nuxt-playground/tsconfig.json",
    "content": "{\n  \"extends\": \"./.nuxt/tsconfig.json\"\n}\n"
  },
  {
    "path": "docs/assets/fonts.css",
    "content": "\n@font-face {\n  font-family: 'silka';\n  src: url('./fonts/silka/silka-medium-webfont.eot');\n  src: url('./fonts/silka/silka-medium-webfont.eot?#iefix') format('embedded-opentype'),\n       url('./fonts/silka/silka-medium-webfont.woff2') format('woff2'),\n       url('./fonts/silka/silka-medium-webfont.woff') format('woff'),\n       url('./fonts/silka/silka-medium-webfont.ttf') format('truetype');\n  font-weight: 400;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: 'silka';\n  src: url('./fonts/silka/silka-regular-webfont.eot');\n  src: url('./fonts/silka/silka-regular-webfont.eot?#iefix') format('embedded-opentype'),\n       url('./fonts/silka/silka-regular-webfont.woff2') format('woff2'),\n       url('./fonts/silka/silka-regular-webfont.woff') format('woff'),\n       url('./fonts/silka/silka-regular-webfont.ttf') format('truetype');\n  font-weight: 300;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: 'silka';\n  src: url('./fonts/silka/silka-semibold-webfont.eot');\n  src: url('./fonts/silka/silka-semibold-webfont.eot?#iefix') format('embedded-opentype'),\n       url('./fonts/silka/silka-semibold-webfont.woff2') format('woff2'),\n       url('./fonts/silka/silka-semibold-webfont.woff') format('woff'),\n       url('./fonts/silka/silka-semibold-webfont.ttf') format('truetype');\n  font-weight: 500;\n  font-style: normal;\n  font-display: swap;\n}\n"
  },
  {
    "path": "docs/assets/main.css",
    "content": "@import './fonts.css';\n@import './variables.css';\n\nbody {\n  font-family: 'silka', sans-serif;\n  padding: 0;\n  margin: 0;\n  color: var(--text);\n}\n\n::selection {\n  background: var(--purple-m); /* WebKit/Blink Browsers */\n  color: #fff;\n}\n::-moz-selection {\n  background: var(--purple-m); /* Gecko Browsers */\n  color: #fff;\n}\n\nhtml {\n  min-height: 100%;\n  background-image:\n    url('./img/pattern.svg'),\n    linear-gradient(to bottom, rgb(103 0 255 / 4%), transparent),\n    linear-gradient(to bottom left, rgb(255 0 201 / 2%), transparent);\n  background-size: 4px 4px, 100vw 200vh, 100vw 200vh;\n  background-repeat: repeat, no-repeat, no-repeat;\n  scroll-behavior: smooth;\n}\n\nhtml[data-dark-mode=\"true\"] {\n  background: #160430;\n  background-image:\n    url('./img/pattern-dark.svg'),\n    linear-gradient(to bottom, rgb(103 0 255 / 15%), transparent),\n    linear-gradient(to bottom left, rgb(255 0 201 / 10%), transparent 50%);\n  background-size: 4px 4px, 100vw 200vh, 100vw 200vh;\n  background-repeat: repeat, no-repeat, no-repeat;\n}\n\n.container {\n  width: calc(100% - 2em);\n  max-width: 70em;\n  margin: 0 auto;\n}\n\n@media (min-width: 28em) {\n  .container {\n    width: calc(100% - 4em);\n  }\n}\n\nh1, h2, h3, h4{\n  margin: 0 0 .75em;\n  letter-spacing: -0.045em;\n  line-height: 1.2;\n}\n\nh2 {\n  padding-top: 1em;\n}\n\n#demo > h2:first-child {\n  padding-top: 2.5em;\n}\n\n@media (min-width: 42em) {\n  #demo > h2:first-child {\n    padding-top: 0;\n  }\n}\n\nh2.no-margin {\n  padding-top: 0 !important;\n}\n\nh3 {\n  margin-top: 2.5em;\n}\n\nh3 a:not(.button) {\n  color: var(--text);\n  display: inline-flex;\n  align-items: center;\n}\n\nh3 a:not(.button) svg {\n  width: .875em;\n  display: block;\n  margin-left: .5em;\n}\n\nh3 a:not(.button):hover svg {\n  fill: var(--primary);\n}\n\n@media (min-width: 20em) {\n  h2 {\n    font-size: 1.75em;\n  }\n}\n\n@media (min-width: 42em) {\n  h2 {\n    font-size: 2em;\n  }\n}\np {\n  font-weight: 300;\n  line-height: 1.5;\n}\n\ncode {\n  font-size: 0.9375em;\n  padding: .1em .2em;\n  background-color: rgba(0, 0, 0, .1);\n  border-radius: .25em;\n  line-height: 1;\n  font-family: courier, monospace;\n}\n[data-dark-mode=\"true\"] p code {\n  background-color: rgba(255, 255, 255, .15);\n  border: 1px solid var(--purple-md);\n}\n[data-dark-mode=\"true\"] code > a:not(.button),\n[data-dark-mode=\"true\"] a:not(.button) > code {\n  color: var(--purple-lm);\n  text-decoration: underline;\n}\nsection {\n  margin-bottom: 3em;\n}\n\nsection > ul {\n  line-height: 1.5;\n  list-style-type: none;\n  margin: 1.5em 0;\n}\nsection > ul li {\n  margin-bottom: .5em;\n  font-size: 1rem;\n  font-weight: 300;\n  color: currentColor;\n}\nsection > ul li::before {\n  content: '';\n  display: block;\n  width: 5px;\n  height: 5px;\n  border-radius: 5px;\n  background-color: var(--primary);\n  position: absolute;\n  margin-left: -1em;\n  margin-top: .55em;\n}\n[data-dark-mode=\"true\"] section > ul li::before {\n  background-color: var(--purple-m);\n}\nsection h3 {\n  margin: 1.5em 0 1em 0;\n}\naside {\n  display: block;\n  padding: 1em;\n  background-color: var(--purple-l);\n  border-radius: .5em;\n  margin: 2em 0;\n}\n[data-dark-mode=\"true\"] aside {\n  background-color: transparent;\n  border: 2px solid var(--ui-green);\n}\n\naside p {\n  margin: 0;\n  vertical-align: middle;\n  font-size: .9em;\n}\n\naside svg {\n  width: 1.5em;\n  display: inline-block;\n  vertical-align: middle;\n  fill: var(--purple-d);\n}\n[data-dark-mode=\"true\"] aside svg {\n  fill: var(--ui-green);\n}\naside code {\n  display: inline-block;\n}\n\n.button {\n  appearance: none;\n  background-color: var(--primary);\n  border: 2px solid var(--primary);\n  color: white;\n  padding: 0.75em 2em;\n  border-radius: 0.5em;\n  box-shadow: 0 0 1em rgba(90, 41, 228, 0.5);\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  cursor: pointer;\n  margin-bottom: 1em;\n  text-decoration: none;\n}\n\n.button:hover {\n  opacity: .9;\n}\n\n.button svg {\n  width: 1em;\n  fill: currentColor;\n  display: block;\n  margin-left: .5em;\n}\n\n@media (min-width: 23em) {\n  .button {\n    display: inline-flex;\n    margin-right: 1em;\n  }\n}\n\n.button--alt {\n  background-color: rgba(255, 255, 255, .5);\n  border-color: currentColor;\n  color: grey;\n  box-shadow: none;\n}\n[data-dark-mode=\"true\"] .button--alt {\n  background-color: var(--gray-l);\n  border-color: #ddd;\n  color: var(--gray-d);\n}\n\n.button--alt:hover {\n  color: var(--grey-md);\n}\n[data-dark-mode=\"true\"] .button--alt:hover {\n  color: var(--gray-d);\n  background: #fff;\n}\n\na:not(.button) {\n  color: var(--primary);\n  text-decoration: none;\n}\n[data-dark-mode=\"true\"] a:not(.button) {\n  color: var(--purple-m);\n}\na:not(.button):hover {\n  text-decoration: underline;\n}\n\n\n.important {\n  display: block;\n  margin: 1.5em 2em;\n  line-height: 1.4;\n  font-size: 1.25em;\n  opacity: 0.75;\n}\n\n.example {\n  margin-bottom: 2em;\n}\n\n.example li {\n  margin-bottom: .5em;\n}\n\n.formkit-outer {\n  margin-bottom: 1em;\n}\n\n.formkit-label {\n  display: block;\n  margin-bottom: .25em;\n}\n\n.formkit-outer textarea,\n.formkit-outer input {\n  appearance: none;\n  border: 1px solid var(--gray-l);\n  padding: 0.75em;\n  font-size: 0.875rem;\n  width: 250px;\n  max-width: 100%;\n  border-radius: 0.5em;\n  font-family: \"silka\";\n  font-weight: 300;\n}\n\n.formkit-help {\n  font-size: .75em;\n  color: var(--gray-m);\n  font-weight: 300;\n  margin-top: .5em;\n}\n\n.formkit-outer label {\n  font-weight: bold;\n  font-size: 0.875em;\n}\n\n.formkit-outer .formkit-outer {\n  margin-bottom: 0.5em;\n}\n\n.formkit-messages {\n  list-style-type: none;\n  padding: 0;\n  margin: 0;\n}\n\nli.formkit-message {\n  font-size: .75em;\n  padding-top: .25em;\n  color: red;\n  font-weight: 300;\n  margin: 0;\n}\n\n.formkit-actions {\n  margin-top: 0;\n  transition: margin-top .25s;\n}\n\n.framework-jumplinks {\n  padding: 0;\n  margin: 2em 0;\n  list-style-type: none;\n  display: flex;\n}\n.framework-jumplinks li::before {\n  display: none;\n}\n.framework-jumplinks a {\n  width: 1.5em;\n  height: 1.5em;\n  padding: 1em;\n  display: flex;\n  border: 1px solid var(--purple-l);\n  border-radius: .5em;\n  margin-right: 1em;\n  align-items: center;\n  overflow: hidden;\n  justify-content: center;\n}\n@media (min-width: 42em) {\n  .framework-jumplinks a {\n    width: 2em;\n    height: 2em;\n    padding: 1.25em;\n    margin-right: 1.5em;\n  }\n}\n\n.framework-jumplinks a:hover {\n  background-color: var(--purple-ll);\n}\n.framework-jumplinks li:last-child a {\n  margin-right: 0;\n}\n.framework-jumplinks a svg {\n  width: 2.25em;\n  aspect-ratio: 1;\n  height: auto;\n}\n.framework-jumplinks a span {\n  position: absolute;\n  left: -999px;\n}\n.balls {\n  background: url('./img/triangle.svg');\n}\n"
  },
  {
    "path": "docs/assets/prism.css",
    "content": "/* PrismJS 1.28.0\nhttps://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript */\ncode[class*=language-],pre[class*=language-]{color:#ccc;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}\n"
  },
  {
    "path": "docs/assets/prism.js",
    "content": "/* PrismJS 1.28.0\nhttps://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript+bash+jsx+tsx+typescript */\nvar _self=\"undefined\"!=typeof window?window:\"undefined\"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(e){var n=/(?:^|\\s)lang(?:uage)?-([\\w-]+)(?=\\s|$)/i,t=0,r={},a={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof i?new i(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\").replace(/\\u00a0/g,\" \")},type:function(e){return Object.prototype.toString.call(e).slice(8,-1)},objId:function(e){return e.__id||Object.defineProperty(e,\"__id\",{value:++t}),e.__id},clone:function e(n,t){var r,i;switch(t=t||{},a.util.type(n)){case\"Object\":if(i=a.util.objId(n),t[i])return t[i];for(var l in r={},t[i]=r,n)n.hasOwnProperty(l)&&(r[l]=e(n[l],t));return r;case\"Array\":return i=a.util.objId(n),t[i]?t[i]:(r=[],t[i]=r,n.forEach((function(n,a){r[a]=e(n,t)})),r);default:return n}},getLanguage:function(e){for(;e;){var t=n.exec(e.className);if(t)return t[1].toLowerCase();e=e.parentElement}return\"none\"},setLanguage:function(e,t){e.className=e.className.replace(RegExp(n,\"gi\"),\"\"),e.classList.add(\"language-\"+t)},currentScript:function(){if(\"undefined\"==typeof document)return null;if(\"currentScript\"in document)return document.currentScript;try{throw new Error}catch(r){var e=(/at [^(\\r\\n]*\\((.*):[^:]+:[^:]+\\)$/i.exec(r.stack)||[])[1];if(e){var n=document.getElementsByTagName(\"script\");for(var t in n)if(n[t].src==e)return n[t]}return null}},isActive:function(e,n,t){for(var r=\"no-\"+n;e;){var a=e.classList;if(a.contains(n))return!0;if(a.contains(r))return!1;e=e.parentElement}return!!t}},languages:{plain:r,plaintext:r,text:r,txt:r,extend:function(e,n){var t=a.util.clone(a.languages[e]);for(var r in n)t[r]=n[r];return t},insertBefore:function(e,n,t,r){var i=(r=r||a.languages)[e],l={};for(var o in i)if(i.hasOwnProperty(o)){if(o==n)for(var s in t)t.hasOwnProperty(s)&&(l[s]=t[s]);t.hasOwnProperty(o)||(l[o]=i[o])}var u=r[e];return r[e]=l,a.languages.DFS(a.languages,(function(n,t){t===u&&n!=e&&(this[n]=l)})),l},DFS:function e(n,t,r,i){i=i||{};var l=a.util.objId;for(var o in n)if(n.hasOwnProperty(o)){t.call(n,o,n[o],r||o);var s=n[o],u=a.util.type(s);\"Object\"!==u||i[l(s)]?\"Array\"!==u||i[l(s)]||(i[l(s)]=!0,e(s,t,o,i)):(i[l(s)]=!0,e(s,t,null,i))}}},plugins:{},highlightAll:function(e,n){a.highlightAllUnder(document,e,n)},highlightAllUnder:function(e,n,t){var r={callback:t,container:e,selector:'code[class*=\"language-\"], [class*=\"language-\"] code, code[class*=\"lang-\"], [class*=\"lang-\"] code'};a.hooks.run(\"before-highlightall\",r),r.elements=Array.prototype.slice.apply(r.container.querySelectorAll(r.selector)),a.hooks.run(\"before-all-elements-highlight\",r);for(var i,l=0;i=r.elements[l++];)a.highlightElement(i,!0===n,r.callback)},highlightElement:function(n,t,r){var i=a.util.getLanguage(n),l=a.languages[i];a.util.setLanguage(n,i);var o=n.parentElement;o&&\"pre\"===o.nodeName.toLowerCase()&&a.util.setLanguage(o,i);var s={element:n,language:i,grammar:l,code:n.textContent};function u(e){s.highlightedCode=e,a.hooks.run(\"before-insert\",s),s.element.innerHTML=s.highlightedCode,a.hooks.run(\"after-highlight\",s),a.hooks.run(\"complete\",s),r&&r.call(s.element)}if(a.hooks.run(\"before-sanity-check\",s),(o=s.element.parentElement)&&\"pre\"===o.nodeName.toLowerCase()&&!o.hasAttribute(\"tabindex\")&&o.setAttribute(\"tabindex\",\"0\"),!s.code)return a.hooks.run(\"complete\",s),void(r&&r.call(s.element));if(a.hooks.run(\"before-highlight\",s),s.grammar)if(t&&e.Worker){var c=new Worker(a.filename);c.onmessage=function(e){u(e.data)},c.postMessage(JSON.stringify({language:s.language,code:s.code,immediateClose:!0}))}else u(a.highlight(s.code,s.grammar,s.language));else u(a.util.encode(s.code))},highlight:function(e,n,t){var r={code:e,grammar:n,language:t};if(a.hooks.run(\"before-tokenize\",r),!r.grammar)throw new Error('The language \"'+r.language+'\" has no grammar.');return r.tokens=a.tokenize(r.code,r.grammar),a.hooks.run(\"after-tokenize\",r),i.stringify(a.util.encode(r.tokens),r.language)},tokenize:function(e,n){var t=n.rest;if(t){for(var r in t)n[r]=t[r];delete n.rest}var a=new s;return u(a,a.head,e),o(e,a,n,a.head,0),function(e){for(var n=[],t=e.head.next;t!==e.tail;)n.push(t.value),t=t.next;return n}(a)},hooks:{all:{},add:function(e,n){var t=a.hooks.all;t[e]=t[e]||[],t[e].push(n)},run:function(e,n){var t=a.hooks.all[e];if(t&&t.length)for(var r,i=0;r=t[i++];)r(n)}},Token:i};function i(e,n,t,r){this.type=e,this.content=n,this.alias=t,this.length=0|(r||\"\").length}function l(e,n,t,r){e.lastIndex=n;var a=e.exec(t);if(a&&r&&a[1]){var i=a[1].length;a.index+=i,a[0]=a[0].slice(i)}return a}function o(e,n,t,r,s,g){for(var f in t)if(t.hasOwnProperty(f)&&t[f]){var h=t[f];h=Array.isArray(h)?h:[h];for(var d=0;d<h.length;++d){if(g&&g.cause==f+\",\"+d)return;var v=h[d],p=v.inside,m=!!v.lookbehind,y=!!v.greedy,k=v.alias;if(y&&!v.pattern.global){var x=v.pattern.toString().match(/[imsuy]*$/)[0];v.pattern=RegExp(v.pattern.source,x+\"g\")}for(var b=v.pattern||v,w=r.next,A=s;w!==n.tail&&!(g&&A>=g.reach);A+=w.value.length,w=w.next){var E=w.value;if(n.length>e.length)return;if(!(E instanceof i)){var P,L=1;if(y){if(!(P=l(b,A,e,m))||P.index>=e.length)break;var S=P.index,O=P.index+P[0].length,j=A;for(j+=w.value.length;S>=j;)j+=(w=w.next).value.length;if(A=j-=w.value.length,w.value instanceof i)continue;for(var C=w;C!==n.tail&&(j<O||\"string\"==typeof C.value);C=C.next)L++,j+=C.value.length;L--,E=e.slice(A,j),P.index-=A}else if(!(P=l(b,0,E,m)))continue;S=P.index;var N=P[0],_=E.slice(0,S),M=E.slice(S+N.length),W=A+E.length;g&&W>g.reach&&(g.reach=W);var z=w.prev;if(_&&(z=u(n,z,_),A+=_.length),c(n,z,L),w=u(n,z,new i(f,p?a.tokenize(N,p):N,k,N)),M&&u(n,w,M),L>1){var I={cause:f+\",\"+d,reach:W};o(e,n,t,w.prev,A,I),g&&I.reach>g.reach&&(g.reach=I.reach)}}}}}}function s(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function u(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function c(e,n,t){for(var r=n.next,a=0;a<t&&r!==e.tail;a++)r=r.next;n.next=r,r.prev=n,e.length-=a}if(e.Prism=a,i.stringify=function e(n,t){if(\"string\"==typeof n)return n;if(Array.isArray(n)){var r=\"\";return n.forEach((function(n){r+=e(n,t)})),r}var i={type:n.type,content:e(n.content,t),tag:\"span\",classes:[\"token\",n.type],attributes:{},language:t},l=n.alias;l&&(Array.isArray(l)?Array.prototype.push.apply(i.classes,l):i.classes.push(l)),a.hooks.run(\"wrap\",i);var o=\"\";for(var s in i.attributes)o+=\" \"+s+'=\"'+(i.attributes[s]||\"\").replace(/\"/g,\"&quot;\")+'\"';return\"<\"+i.tag+' class=\"'+i.classes.join(\" \")+'\"'+o+\">\"+i.content+\"</\"+i.tag+\">\"},!e.document)return e.addEventListener?(a.disableWorkerMessageHandler||e.addEventListener(\"message\",(function(n){var t=JSON.parse(n.data),r=t.language,i=t.code,l=t.immediateClose;e.postMessage(a.highlight(i,a.languages[r],r)),l&&e.close()}),!1),a):a;var g=a.util.currentScript();function f(){a.manual||a.highlightAll()}if(g&&(a.filename=g.src,g.hasAttribute(\"data-manual\")&&(a.manual=!0)),!a.manual){var h=document.readyState;\"loading\"===h||\"interactive\"===h&&g&&g.defer?document.addEventListener(\"DOMContentLoaded\",f):window.requestAnimationFrame?window.requestAnimationFrame(f):window.setTimeout(f,16)}return a}(_self);\"undefined\"!=typeof module&&module.exports&&(module.exports=Prism),\"undefined\"!=typeof global&&(global.Prism=Prism);\nPrism.languages.markup={comment:{pattern:/<!--(?:(?!<!--)[\\s\\S])*?-->/,greedy:!0},prolog:{pattern:/<\\?[\\s\\S]+?\\?>/,greedy:!0},doctype:{pattern:/<!DOCTYPE(?:[^>\"'[\\]]|\"[^\"]*\"|'[^']*')+(?:\\[(?:[^<\"'\\]]|\"[^\"]*\"|'[^']*'|<(?!!--)|<!--(?:[^-]|-(?!->))*-->)*\\]\\s*)?>/i,greedy:!0,inside:{\"internal-subset\":{pattern:/(^[^\\[]*\\[)[\\s\\S]+(?=\\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/\"[^\"]*\"|'[^']*'/,greedy:!0},punctuation:/^<!|>$|[[\\]]/,\"doctype-tag\":/^DOCTYPE/i,name:/[^\\s<>'\"]+/}},cdata:{pattern:/<!\\[CDATA\\[[\\s\\S]*?\\]\\]>/i,greedy:!0},tag:{pattern:/<\\/?(?!\\d)[^\\s>\\/=$<%]+(?:\\s(?:\\s*[^\\s>\\/=]+(?:\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+(?=[\\s>]))|(?=[\\s/>])))+)?\\s*\\/?>/,greedy:!0,inside:{tag:{pattern:/^<\\/?[^\\s>\\/]+/,inside:{punctuation:/^<\\/?/,namespace:/^[^\\s>\\/:]+:/}},\"special-attr\":[],\"attr-value\":{pattern:/=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:\"attr-equals\"},{pattern:/^(\\s*)[\"']|[\"']$/,lookbehind:!0}]}},punctuation:/\\/?>/,\"attr-name\":{pattern:/[^\\s>\\/]+/,inside:{namespace:/^[^\\s>\\/:]+:/}}}},entity:[{pattern:/&[\\da-z]{1,8};/i,alias:\"named-entity\"},/&#x?[\\da-f]{1,8};/i]},Prism.languages.markup.tag.inside[\"attr-value\"].inside.entity=Prism.languages.markup.entity,Prism.languages.markup.doctype.inside[\"internal-subset\"].inside=Prism.languages.markup,Prism.hooks.add(\"wrap\",(function(a){\"entity\"===a.type&&(a.attributes.title=a.content.replace(/&amp;/,\"&\"))})),Object.defineProperty(Prism.languages.markup.tag,\"addInlined\",{value:function(a,e){var s={};s[\"language-\"+e]={pattern:/(^<!\\[CDATA\\[)[\\s\\S]+?(?=\\]\\]>$)/i,lookbehind:!0,inside:Prism.languages[e]},s.cdata=/^<!\\[CDATA\\[|\\]\\]>$/i;var t={\"included-cdata\":{pattern:/<!\\[CDATA\\[[\\s\\S]*?\\]\\]>/i,inside:s}};t[\"language-\"+e]={pattern:/[\\s\\S]+/,inside:Prism.languages[e]};var n={};n[a]={pattern:RegExp(\"(<__[^>]*>)(?:<!\\\\[CDATA\\\\[(?:[^\\\\]]|\\\\](?!\\\\]>))*\\\\]\\\\]>|(?!<!\\\\[CDATA\\\\[)[^])*?(?=</__>)\".replace(/__/g,(function(){return a})),\"i\"),lookbehind:!0,greedy:!0,inside:t},Prism.languages.insertBefore(\"markup\",\"cdata\",n)}}),Object.defineProperty(Prism.languages.markup.tag,\"addAttribute\",{value:function(a,e){Prism.languages.markup.tag.inside[\"special-attr\"].push({pattern:RegExp(\"(^|[\\\"'\\\\s])(?:\"+a+\")\\\\s*=\\\\s*(?:\\\"[^\\\"]*\\\"|'[^']*'|[^\\\\s'\\\">=]+(?=[\\\\s>]))\",\"i\"),lookbehind:!0,inside:{\"attr-name\":/^[^\\s=]+/,\"attr-value\":{pattern:/=[\\s\\S]+/,inside:{value:{pattern:/(^=\\s*([\"']|(?![\"'])))\\S[\\s\\S]*(?=\\2$)/,lookbehind:!0,alias:[e,\"language-\"+e],inside:Prism.languages[e]},punctuation:[{pattern:/^=/,alias:\"attr-equals\"},/\"|'/]}}}})}}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup,Prism.languages.xml=Prism.languages.extend(\"markup\",{}),Prism.languages.ssml=Prism.languages.xml,Prism.languages.atom=Prism.languages.xml,Prism.languages.rss=Prism.languages.xml;\n!function(s){var e=/(?:\"(?:\\\\(?:\\r\\n|[\\s\\S])|[^\"\\\\\\r\\n])*\"|'(?:\\\\(?:\\r\\n|[\\s\\S])|[^'\\\\\\r\\n])*')/;s.languages.css={comment:/\\/\\*[\\s\\S]*?\\*\\//,atrule:{pattern:RegExp(\"@[\\\\w-](?:[^;{\\\\s\\\"']|\\\\s+(?!\\\\s)|\"+e.source+\")*?(?:;|(?=\\\\s*\\\\{))\"),inside:{rule:/^@[\\w-]+/,\"selector-function-argument\":{pattern:/(\\bselector\\s*\\(\\s*(?![\\s)]))(?:[^()\\s]|\\s+(?![\\s)])|\\((?:[^()]|\\([^()]*\\))*\\))+(?=\\s*\\))/,lookbehind:!0,alias:\"selector\"},keyword:{pattern:/(^|[^\\w-])(?:and|not|only|or)(?![\\w-])/,lookbehind:!0}}},url:{pattern:RegExp(\"\\\\burl\\\\((?:\"+e.source+\"|(?:[^\\\\\\\\\\r\\n()\\\"']|\\\\\\\\[^])*)\\\\)\",\"i\"),greedy:!0,inside:{function:/^url/i,punctuation:/^\\(|\\)$/,string:{pattern:RegExp(\"^\"+e.source+\"$\"),alias:\"url\"}}},selector:{pattern:RegExp(\"(^|[{}\\\\s])[^{}\\\\s](?:[^{};\\\"'\\\\s]|\\\\s+(?![\\\\s{])|\"+e.source+\")*(?=\\\\s*\\\\{)\"),lookbehind:!0},string:{pattern:e,greedy:!0},property:{pattern:/(^|[^-\\w\\xA0-\\uFFFF])(?!\\s)[-_a-z\\xA0-\\uFFFF](?:(?!\\s)[-\\w\\xA0-\\uFFFF])*(?=\\s*:)/i,lookbehind:!0},important:/!important\\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\\()/i,lookbehind:!0},punctuation:/[(){};:,]/},s.languages.css.atrule.inside.rest=s.languages.css;var t=s.languages.markup;t&&(t.tag.addInlined(\"style\",\"css\"),t.tag.addAttribute(\"style\",\"css\"))}(Prism);\nPrism.languages.clike={comment:[{pattern:/(^|[^\\\\])\\/\\*[\\s\\S]*?(?:\\*\\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\\\:])\\/\\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/([\"'])(?:\\\\(?:\\r\\n|[\\s\\S])|(?!\\1)[^\\\\\\r\\n])*\\1/,greedy:!0},\"class-name\":{pattern:/(\\b(?:class|extends|implements|instanceof|interface|new|trait)\\s+|\\bcatch\\s+\\()[\\w.\\\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\\\]/}},keyword:/\\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\\b/,boolean:/\\b(?:false|true)\\b/,function:/\\b\\w+(?=\\()/,number:/\\b0x[\\da-f]+\\b|(?:\\b\\d+(?:\\.\\d*)?|\\B\\.\\d+)(?:e[+-]?\\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\\+\\+?|&&?|\\|\\|?|[?*/~^%]/,punctuation:/[{}[\\];(),.:]/};\nPrism.languages.javascript=Prism.languages.extend(\"clike\",{\"class-name\":[Prism.languages.clike[\"class-name\"],{pattern:/(^|[^$\\w\\xA0-\\uFFFF])(?!\\s)[_$A-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*(?=\\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\\})\\s*)catch\\b/,lookbehind:!0},{pattern:/(^|[^.]|\\.\\.\\.\\s*)\\b(?:as|assert(?=\\s*\\{)|async(?=\\s*(?:function\\b|\\(|[$\\w\\xA0-\\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\\s*(?:\\{|$))|for|from(?=\\s*(?:['\"]|$))|function|(?:get|set)(?=\\s*(?:[#\\[$\\w\\xA0-\\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\\b/,lookbehind:!0}],function:/#?(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*(?=\\s*(?:\\.\\s*(?:apply|bind|call)\\s*)?\\()/,number:{pattern:RegExp(\"(^|[^\\\\w$])(?:NaN|Infinity|0[bB][01]+(?:_[01]+)*n?|0[oO][0-7]+(?:_[0-7]+)*n?|0[xX][\\\\dA-Fa-f]+(?:_[\\\\dA-Fa-f]+)*n?|\\\\d+(?:_\\\\d+)*n|(?:\\\\d+(?:_\\\\d+)*(?:\\\\.(?:\\\\d+(?:_\\\\d+)*)?)?|\\\\.\\\\d+(?:_\\\\d+)*)(?:[Ee][+-]?\\\\d+(?:_\\\\d+)*)?)(?![\\\\w$])\"),lookbehind:!0},operator:/--|\\+\\+|\\*\\*=?|=>|&&=?|\\|\\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\\.{3}|\\?\\?=?|\\?\\.?|[~:]/}),Prism.languages.javascript[\"class-name\"][0].pattern=/(\\b(?:class|extends|implements|instanceof|interface|new)\\s+)[\\w.\\\\]+/,Prism.languages.insertBefore(\"javascript\",\"keyword\",{regex:{pattern:RegExp(\"((?:^|[^$\\\\w\\\\xA0-\\\\uFFFF.\\\"'\\\\])\\\\s]|\\\\b(?:return|yield))\\\\s*)/(?:(?:\\\\[(?:[^\\\\]\\\\\\\\\\r\\n]|\\\\\\\\.)*\\\\]|\\\\\\\\.|[^/\\\\\\\\\\\\[\\r\\n])+/[dgimyus]{0,7}|(?:\\\\[(?:[^[\\\\]\\\\\\\\\\r\\n]|\\\\\\\\.|\\\\[(?:[^[\\\\]\\\\\\\\\\r\\n]|\\\\\\\\.|\\\\[(?:[^[\\\\]\\\\\\\\\\r\\n]|\\\\\\\\.)*\\\\])*\\\\])*\\\\]|\\\\\\\\.|[^/\\\\\\\\\\\\[\\r\\n])+/[dgimyus]{0,7}v[dgimyus]{0,7})(?=(?:\\\\s|/\\\\*(?:[^*]|\\\\*(?!/))*\\\\*/)*(?:$|[\\r\\n,.;:})\\\\]]|//))\"),lookbehind:!0,greedy:!0,inside:{\"regex-source\":{pattern:/^(\\/)[\\s\\S]+(?=\\/[a-z]*$)/,lookbehind:!0,alias:\"language-regex\",inside:Prism.languages.regex},\"regex-delimiter\":/^\\/|\\/$/,\"regex-flags\":/^[a-z]+$/}},\"function-variable\":{pattern:/#?(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*(?=\\s*[=:]\\s*(?:async\\s*)?(?:\\bfunction\\b|(?:\\((?:[^()]|\\([^()]*\\))*\\)|(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*)\\s*=>))/,alias:\"function\"},parameter:[{pattern:/(function(?:\\s+(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*)?\\s*\\(\\s*)(?!\\s)(?:[^()\\s]|\\s+(?![\\s)])|\\([^()]*\\))+(?=\\s*\\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(^|[^$\\w\\xA0-\\uFFFF])(?!\\s)[_$a-z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*(?=\\s*=>)/i,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(\\(\\s*)(?!\\s)(?:[^()\\s]|\\s+(?![\\s)])|\\([^()]*\\))+(?=\\s*\\)\\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\\b|\\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\\w\\xA0-\\uFFFF]))(?:(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*\\s*)\\(\\s*|\\]\\s*\\(\\s*)(?!\\s)(?:[^()\\s]|\\s+(?![\\s)])|\\([^()]*\\))+(?=\\s*\\)\\s*\\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\\b[A-Z](?:[A-Z_]|\\dx?)*\\b/}),Prism.languages.insertBefore(\"javascript\",\"string\",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:\"comment\"},\"template-string\":{pattern:/`(?:\\\\[\\s\\S]|\\$\\{(?:[^{}]|\\{(?:[^{}]|\\{[^}]*\\})*\\})+\\}|(?!\\$\\{)[^\\\\`])*`/,greedy:!0,inside:{\"template-punctuation\":{pattern:/^`|`$/,alias:\"string\"},interpolation:{pattern:/((?:^|[^\\\\])(?:\\\\{2})*)\\$\\{(?:[^{}]|\\{(?:[^{}]|\\{[^}]*\\})*\\})+\\}/,lookbehind:!0,inside:{\"interpolation-punctuation\":{pattern:/^\\$\\{|\\}$/,alias:\"punctuation\"},rest:Prism.languages.javascript}},string:/[\\s\\S]+/}},\"string-property\":{pattern:/((?:^|[,{])[ \\t]*)([\"'])(?:\\\\(?:\\r\\n|[\\s\\S])|(?!\\2)[^\\\\\\r\\n])*\\2(?=\\s*:)/m,lookbehind:!0,greedy:!0,alias:\"property\"}}),Prism.languages.insertBefore(\"javascript\",\"operator\",{\"literal-property\":{pattern:/((?:^|[,{])[ \\t]*)(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*(?=\\s*:)/m,lookbehind:!0,alias:\"property\"}}),Prism.languages.markup&&(Prism.languages.markup.tag.addInlined(\"script\",\"javascript\"),Prism.languages.markup.tag.addAttribute(\"on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)\",\"javascript\")),Prism.languages.js=Prism.languages.javascript;\n!function(e){var t=\"\\\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\\\b\",n={pattern:/(^([\"']?)\\w+\\2)[ \\t]+\\S.*/,lookbehind:!0,alias:\"punctuation\",inside:null},a={bash:n,environment:{pattern:RegExp(\"\\\\$\"+t),alias:\"constant\"},variable:[{pattern:/\\$?\\(\\([\\s\\S]+?\\)\\)/,greedy:!0,inside:{variable:[{pattern:/(^\\$\\(\\([\\s\\S]+)\\)\\)/,lookbehind:!0},/^\\$\\(\\(/],number:/\\b0x[\\dA-Fa-f]+\\b|(?:\\b\\d+(?:\\.\\d*)?|\\B\\.\\d+)(?:[Ee]-?\\d+)?/,operator:/--|\\+\\+|\\*\\*=?|<<=?|>>=?|&&|\\|\\||[=!+\\-*/%<>^&|]=?|[?~:]/,punctuation:/\\(\\(?|\\)\\)?|,|;/}},{pattern:/\\$\\((?:\\([^)]+\\)|[^()])+\\)|`[^`]+`/,greedy:!0,inside:{variable:/^\\$\\(|^`|\\)$|`$/}},{pattern:/\\$\\{[^}]+\\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\\/]|##?|%%?|\\^\\^?|,,?/,punctuation:/[\\[\\]]/,environment:{pattern:RegExp(\"(\\\\{)\"+t),lookbehind:!0,alias:\"constant\"}}},/\\$(?:\\w+|[#?*!@$])/],entity:/\\\\(?:[abceEfnrtv\\\\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/};e.languages.bash={shebang:{pattern:/^#!\\s*\\/.*/,alias:\"important\"},comment:{pattern:/(^|[^\"{\\\\$])#.*/,lookbehind:!0},\"function-name\":[{pattern:/(\\bfunction\\s+)[\\w-]+(?=(?:\\s*\\(?:\\s*\\))?\\s*\\{)/,lookbehind:!0,alias:\"function\"},{pattern:/\\b[\\w-]+(?=\\s*\\(\\s*\\)\\s*\\{)/,alias:\"function\"}],\"for-or-select\":{pattern:/(\\b(?:for|select)\\s+)\\w+(?=\\s+in\\s)/,alias:\"variable\",lookbehind:!0},\"assign-left\":{pattern:/(^|[\\s;|&]|[<>]\\()\\w+(?=\\+?=)/,inside:{environment:{pattern:RegExp(\"(^|[\\\\s;|&]|[<>]\\\\()\"+t),lookbehind:!0,alias:\"constant\"}},alias:\"variable\",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\\s*)(\\w+)\\s[\\s\\S]*?(?:\\r?\\n|\\r)\\2/,lookbehind:!0,greedy:!0,inside:a},{pattern:/((?:^|[^<])<<-?\\s*)([\"'])(\\w+)\\2\\s[\\s\\S]*?(?:\\r?\\n|\\r)\\3/,lookbehind:!0,greedy:!0,inside:{bash:n}},{pattern:/(^|[^\\\\](?:\\\\\\\\)*)\"(?:\\\\[\\s\\S]|\\$\\([^)]+\\)|\\$(?!\\()|`[^`]+`|[^\"\\\\`$])*\"/,lookbehind:!0,greedy:!0,inside:a},{pattern:/(^|[^$\\\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\\$'(?:[^'\\\\]|\\\\[\\s\\S])*'/,greedy:!0,inside:{entity:a.entity}}],environment:{pattern:RegExp(\"\\\\$?\"+t),alias:\"constant\"},variable:a.variable,function:{pattern:/(^|[\\s;|&]|[<>]\\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\\s;|&]|[<>]\\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\\s;|&]|[<>]\\()(?:\\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\\s;|&])/,lookbehind:!0,alias:\"class-name\"},boolean:{pattern:/(^|[\\s;|&]|[<>]\\()(?:false|true)(?=$|[)\\s;|&])/,lookbehind:!0},\"file-descriptor\":{pattern:/\\B&\\d\\b/,alias:\"important\"},operator:{pattern:/\\d?<>|>\\||\\+=|=[=~]?|!=?|<<[<-]?|[&\\d]?>>|\\d[<>]&?|[<>][&=]?|&[>&]?|\\|[&|]?/,inside:{\"file-descriptor\":{pattern:/^\\d/,alias:\"important\"}}},punctuation:/\\$?\\(\\(?|\\)\\)?|\\.\\.|[{}[\\];\\\\]/,number:{pattern:/(^|\\s)(?:[1-9]\\d*|0)(?:[.,]\\d+)?\\b/,lookbehind:!0}},n.inside=e.languages.bash;for(var o=[\"comment\",\"function-name\",\"for-or-select\",\"assign-left\",\"string\",\"environment\",\"function\",\"keyword\",\"builtin\",\"boolean\",\"file-descriptor\",\"operator\",\"punctuation\",\"number\"],s=a.variable[1].inside,i=0;i<o.length;i++)s[o[i]]=e.languages.bash[o[i]];e.languages.shell=e.languages.bash}(Prism);\n!function(t){var n=t.util.clone(t.languages.javascript),e=\"(?:\\\\{<S>*\\\\.{3}(?:[^{}]|<BRACES>)*\\\\})\";function a(t,n){return t=t.replace(/<S>/g,(function(){return\"(?:\\\\s|//.*(?!.)|/\\\\*(?:[^*]|\\\\*(?!/))\\\\*/)\"})).replace(/<BRACES>/g,(function(){return\"(?:\\\\{(?:\\\\{(?:\\\\{[^{}]*\\\\}|[^{}])*\\\\}|[^{}])*\\\\})\"})).replace(/<SPREAD>/g,(function(){return e})),RegExp(t,n)}e=a(e).source,t.languages.jsx=t.languages.extend(\"markup\",n),t.languages.jsx.tag.pattern=a(\"</?(?:[\\\\w.:-]+(?:<S>+(?:[\\\\w.:$-]+(?:=(?:\\\"(?:\\\\\\\\[^]|[^\\\\\\\\\\\"])*\\\"|'(?:\\\\\\\\[^]|[^\\\\\\\\'])*'|[^\\\\s{'\\\"/>=]+|<BRACES>))?|<SPREAD>))*<S>*/?)?>\"),t.languages.jsx.tag.inside.tag.pattern=/^<\\/?[^\\s>\\/]*/,t.languages.jsx.tag.inside[\"attr-value\"].pattern=/=(?!\\{)(?:\"(?:\\\\[\\s\\S]|[^\\\\\"])*\"|'(?:\\\\[\\s\\S]|[^\\\\'])*'|[^\\s'\">]+)/,t.languages.jsx.tag.inside.tag.inside[\"class-name\"]=/^[A-Z]\\w*(?:\\.[A-Z]\\w*)*$/,t.languages.jsx.tag.inside.comment=n.comment,t.languages.insertBefore(\"inside\",\"attr-name\",{spread:{pattern:a(\"<SPREAD>\"),inside:t.languages.jsx}},t.languages.jsx.tag),t.languages.insertBefore(\"inside\",\"special-attr\",{script:{pattern:a(\"=<BRACES>\"),alias:\"language-javascript\",inside:{\"script-punctuation\":{pattern:/^=(?=\\{)/,alias:\"punctuation\"},rest:t.languages.jsx}}},t.languages.jsx.tag);var s=function(t){return t?\"string\"==typeof t?t:\"string\"==typeof t.content?t.content:t.content.map(s).join(\"\"):\"\"},g=function(n){for(var e=[],a=0;a<n.length;a++){var o=n[a],i=!1;if(\"string\"!=typeof o&&(\"tag\"===o.type&&o.content[0]&&\"tag\"===o.content[0].type?\"</\"===o.content[0].content[0].content?e.length>0&&e[e.length-1].tagName===s(o.content[0].content[1])&&e.pop():\"/>\"===o.content[o.content.length-1].content||e.push({tagName:s(o.content[0].content[1]),openedBraces:0}):e.length>0&&\"punctuation\"===o.type&&\"{\"===o.content?e[e.length-1].openedBraces++:e.length>0&&e[e.length-1].openedBraces>0&&\"punctuation\"===o.type&&\"}\"===o.content?e[e.length-1].openedBraces--:i=!0),(i||\"string\"==typeof o)&&e.length>0&&0===e[e.length-1].openedBraces){var r=s(o);a<n.length-1&&(\"string\"==typeof n[a+1]||\"plain-text\"===n[a+1].type)&&(r+=s(n[a+1]),n.splice(a+1,1)),a>0&&(\"string\"==typeof n[a-1]||\"plain-text\"===n[a-1].type)&&(r=s(n[a-1])+r,n.splice(a-1,1),a--),n[a]=new t.Token(\"plain-text\",r,null,r)}o.content&&\"string\"!=typeof o.content&&g(o.content)}};t.hooks.add(\"after-tokenize\",(function(t){\"jsx\"!==t.language&&\"tsx\"!==t.language||g(t.tokens)}))}(Prism);\n!function(e){e.languages.typescript=e.languages.extend(\"javascript\",{\"class-name\":{pattern:/(\\b(?:class|extends|implements|instanceof|interface|new|type)\\s+)(?!keyof\\b)(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*(?:\\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\\b/}),e.languages.typescript.keyword.push(/\\b(?:abstract|declare|is|keyof|readonly|require)\\b/,/\\b(?:asserts|infer|interface|module|namespace|type)\\b(?=\\s*(?:[{_$a-zA-Z\\xA0-\\uFFFF]|$))/,/\\btype\\b(?=\\s*(?:[\\{*]|$))/),delete e.languages.typescript.parameter,delete e.languages.typescript[\"literal-property\"];var s=e.languages.extend(\"typescript\",{});delete s[\"class-name\"],e.languages.typescript[\"class-name\"].inside=s,e.languages.insertBefore(\"typescript\",\"function\",{decorator:{pattern:/@[$\\w\\xA0-\\uFFFF]+/,inside:{at:{pattern:/^@/,alias:\"operator\"},function:/^[\\s\\S]+/}},\"generic-function\":{pattern:/#?(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*\\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\\s*\\()/,greedy:!0,inside:{function:/^#?(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*/,generic:{pattern:/<[\\s\\S]+/,alias:\"class-name\",inside:s}}}}),e.languages.ts=e.languages.typescript}(Prism);\n!function(e){var a=e.util.clone(e.languages.typescript);e.languages.tsx=e.languages.extend(\"jsx\",a),delete e.languages.tsx.parameter,delete e.languages.tsx[\"literal-property\"];var t=e.languages.tsx.tag;t.pattern=RegExp(\"(^|[^\\\\w$]|(?=</))(?:\"+t.pattern.source+\")\",t.pattern.flags),t.lookbehind=!0}(Prism);\n"
  },
  {
    "path": "docs/assets/variables.css",
    "content": ":root {\n  --system-stack: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n  --primary: #4711de;\n  --ui-green: #28c840;\n  --ui-red: #ff5f57;\n  --ui-yellow: #febc2e;\n  --ui-light: #f8f2fb;\n  --gray-l: #cbcbcb;\n  --gray-m: #878787;\n  --gray-md: #585a51;\n  --gray-d: #414339;\n  --text: #333;\n  --text-d: #333;\n  --purple-ll: #f6f2ff;\n  --purple-l: #ece3ff;\n  --purple-lm: #deceff;\n  --purple-m: #7335d7;\n  --purple-d: #6500bf;\n  --orange: #fcaa5e;\n}\n\nhtml[data-dark-mode=\"true\"] {\n  --ui-green: #5bc553;\n  --text: #f5f5f5;\n  --primary: #6232e6;\n  --purple-m: #8c57e3;\n  --purple-md: #432d68;\n  --purple-d: #350b5a;\n  --gray-l: #c6c4d0;\n  --gray-m: #7d7483;\n  --gray-md: #55515a;\n  --gray-d: #3d3943;\n}\n"
  },
  {
    "path": "docs/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" href=\"/assets/favicon.ico\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/assets/img/favicon.svg\">\n    <link rel=\"icon\" type=\"image/png\" href=\"/assets/img/favicon.png\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <script async defer src=\"https://buttons.github.io/buttons.js\"></script>\n    <title>AutoAnimate - Add motion to your apps with a single line of code</title>\n    <meta name=\"description\" content=\"A zero-config, drop-in animation utility that automatically adds smooth transitions to your web app. Use it with React, Solid, Vue, Svelte, or any other JavaScript application.\" />\n    <meta property=\"og:title\" content=\"AutoAnimate - Add motion to your apps with a single line of code\" />\n    <meta property=\"og:description\" content=\"A zero-config, drop-in animation utility that automatically adds smooth transitions to your web app. Use it with React, Solid, Vue, Svelte, or any other JavaScript application.\" />\n    <meta property=\"og:image\" content=\"https://cdn.formk.it/web-assets/auto-animate-og.png\" />\n    <meta property=\"og:url\" content=\"https://auto-animate.formkit.com/\" />\n    <meta property=\"og:type\" content=\"article\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <meta name=\"twitter:creator\" content=\"@useFormKit\" />\n    <meta property=\"twitter:image\" content=\"https://cdn.formk.it/web-assets/auto-animate-og.png\" />\n    <script src=\"https://smile-innovative.formkit.com/script.js\" data-site=\"KMEKCZYO\" defer></script>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n    <script src=\"https://cdn.formk.it/web-assets/prism.js\" data-manual></script>\n    <script async defer src=\"https://buttons.github.io/buttons.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/src/App.vue",
    "content": "<script setup lang=\"ts\">\nimport { RouterView } from \"vue-router\"\nimport TheHeader from \"./components/TheHeader.vue\"\n</script>\n\n<template>\n  <div class=\"site-wide-announcement\" ref=\"announcement\">\n    <div class=\"container\">\n      <div class=\"announcement-content\">\n        <p>\n          <strong>Introducing KickStart</strong> —  AI generated FormKit forms\n          in seconds. Generate from a screenshot, edit with drag-and-drop or\n          conversational AI, copy & paste as components or schema!<br />\n          <a\n            class=\"button button--pro-outline\"\n            href=\"https://kickstart.formkit.com\"\n          >\n            Try for free\n            <span class=\"formkit-icon\"\n              ><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\">\n                <path\n                  fill=\"currentColor\"\n                  d=\"M440.6 273.4c4.7-4.5 7.4-10.8 7.4-17.4s-2.7-12.8-7.4-17.4l-176-168c-9.6-9.2-24.8-8.8-33.9.8s-8.8 24.8.8 33.9L364.1 232H24c-13.3 0-24 10.7-24 24s10.7 24 24 24h340.1L231.4 406.6c-9.6 9.2-9.9 24.3-.8 33.9s24.3 9.9 33.9.8l176-168z\"\n                ></path></svg\n            ></span>\n          </a>\n        </p>\n      </div>\n    </div>\n  </div>\n  <div class=\"container\">\n    <RouterView />\n  </div>\n</template>\n\n<style scoped>\n.site-wide-announcement {\n  position: relative;\n  padding: 1em;\n  text-align: center;\n  font-size: 0.9em;\n  line-height: 1.5;\n  border-bottom: 1px solid var(--ui-border-light);\n  overflow: clip;\n\n  .container {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    max-width: 800px;\n    margin: 0 auto;\n  }\n\n  p {\n    margin: 0;\n    text-wrap: balance;\n    font-size: 0.85rem;\n\n    @media screen and (min-width: 768px) {\n      font-size: 0.95rem;\n    }\n  }\n\n  a {\n    font-size: 0.85rem;\n    padding: 0.33rem 1.15rem;\n    margin-top: 1em;\n    display: inline-flex;\n\n    .formkit-icon {\n      display: inline-block;\n      margin-left: 0em;\n      margin-right: 0.25em;\n      width: 0.9em;\n    }\n  }\n\n  &::before,\n  &::after {\n    content: \"\";\n    position: absolute;\n    top: 0;\n    left: 0;\n    right: 0;\n    bottom: 0;\n    width: 100dvw;\n    z-index: -1;\n  }\n\n  &::before {\n    top: 0;\n    left: 0;\n    width: 33%;\n    height: 100%;\n    background: linear-gradient(90deg, rgb(218, 231, 245), transparent);\n  }\n  &::after {\n    top: 0;\n    right: 0;\n    height: 100%;\n    background: linear-gradient(-90deg, rgb(247, 211, 249), transparent);\n  }\n}\n\n[data-dark-mode=\"true\"] .site-wide-announcement {\n  &::before,\n  &::after {\n    opacity: 0.75;\n    mix-blend-mode: overlay;\n  }\n\n  &::before {\n    background: linear-gradient(90deg, rgb(101, 196, 233), transparent);\n  }\n  &::after {\n    background: linear-gradient(-90deg, rgb(255, 108, 255), transparent);\n  }\n}\n</style>\n"
  },
  {
    "path": "docs/src/components/AsideTip.vue",
    "content": "<script setup>\nimport IconLightbulb from \"./IconLightbulb.vue\"\n</script>\n\n<template>\n  <aside class=\"tip\">\n    <IconLightbulb />\n    <p><slot /></p>\n  </aside>\n</template>\n\n<style scoped>\n.tip {\n  display: flex;\n  align-items: flex-start;\n}\n.tip svg {\n  margin-right: 1em;\n  margin-top: 0.25em;\n  width: 1.5em;\n  height: 1.5em;\n  flex-shrink: 0;\n}\n</style>\n"
  },
  {
    "path": "docs/src/components/CodeExample.vue",
    "content": "<script setup lang=\"ts\">\nimport IconVue from \"./IconVue.vue\"\nimport IconReact from \"./IconReact.vue\"\nimport IconPreact from \"./IconPreact.vue\"\nimport IconSolid from \"./IconSolid.vue\"\nimport IconQwik from \"./IconQwik.vue\"\nimport IconHTML from \"./IconHTML.vue\"\nimport IconYarn from \"./IconYarn.vue\"\nimport IconNPM from \"./IconNPM.vue\"\nimport IconPNPM from \"./IconPNPM.vue\"\nimport IconJavaScript from \"./IconJavaScript.vue\"\nimport IconSvelte from \"./IconSvelte.vue\"\nimport IconAngular from \"./IconAngular.vue\"\nimport IconNuxt from \"./IconNuxt.vue\"\nimport IconBun from \"./IconBun.vue\"\nimport { computed, ref } from \"vue\"\nimport { vAutoAnimate } from \"../../../src\"\nimport \"../../assets/prism.css\"\n\ntype LanguageOption =\n  | \"react\"\n  | \"preact\"\n  | \"vue\"\n  | \"html\"\n  | \"solid\"\n  | \"svelte\"\n  | \"angular\"\n  | \"qwik\"\n  | \"js\"\n  | \"yarn\"\n  | \"npm\"\n  | \"pnpm\"\n  | \"nuxt\"\n  | \"bun\"\n\ntype Language = {\n  ext: \"jsx\" | \"vue\" | \"html\"\n  example: string\n  title?: string\n  language: string\n}\n\ntype ParsedExamplesType = {\n  [T in LanguageOption]?: Language & { highlighted: string }\n}\n\nconst props = defineProps<{\n  title: string\n  examples: { [T in LanguageOption]: Language }\n}>()\n\nconst current = ref<LanguageOption>(\n  Object.keys(props.examples)[0] as LanguageOption\n)\n\nconst type = computed(() => {\n  return props.examples[current.value]\n})\nconst allExamples = computed(() => {\n  if (typeof window === \"undefined\") return \"\"\n  const parsedExamples: ParsedExamplesType = {}\n  const keys = Object.keys(props.examples) as (keyof typeof props.examples)[]\n  for (let i = 0; i < keys.length; i++) {\n    const key = keys[i]\n    const example = props.examples[key]\n    parsedExamples[key] = {\n      ...example,\n      highlighted: window.Prism.highlight(\n        example.example,\n        window.Prism.languages[example.language],\n        example.language\n      ),\n    }\n  }\n  return parsedExamples\n})\n\nconst copyStatus = ref(false)\nfunction copyCode(value: string) {\n  window.navigator.clipboard.writeText(value)\n  copyStatus.value = true\n  setTimeout(() => {\n    copyStatus.value = false\n  }, 2000)\n}\n</script>\n\n<template>\n  <div class=\"window\">\n    <div class=\"window-header\">\n      <div class=\"control control--close\"></div>\n      <div class=\"control control--minimize\"></div>\n      <div class=\"control control--expand\"></div>\n      <span class=\"control-title\">{{\n        type.title || `${title}.${type.ext}`\n      }}</span>\n    </div>\n    <div v-if=\"allExamples\" class=\"code-block\" v-auto-animate>\n      <template v-for=\"(currentExample, key) in allExamples\" :key=\"key\">\n        <code\n          v-if=\"key === current\"\n          class=\"code-example\"\n          :key=\"key\"\n          v-html=\"currentExample!.highlighted\"\n          @click=\"copyCode(currentExample!.example)\"\n        ></code>\n      </template>\n      <template v-if=\"copyStatus\">\n        <span class=\"copy-status\">Copied!</span>\n      </template>\n    </div>\n\n    <div class=\"window-footer\">\n      <ul class=\"frameworks\">\n        <li\n          v-if=\"'react' in props.examples\"\n          @click=\"current = 'react'\"\n          :data-selected=\"current === 'react' || null\"\n        >\n          <IconReact />React\n        </li>\n        <li\n          v-if=\"'vue' in props.examples\"\n          @click=\"current = 'vue'\"\n          :data-selected=\"current === 'vue' || null\"\n        >\n          <IconVue />Vue\n        </li>\n        <li\n          v-if=\"'preact' in props.examples\"\n          @click=\"current = 'preact'\"\n          :data-selected=\"current === 'preact' || null\"\n        >\n          <IconPreact />Preact\n        </li>\n        <li\n          v-if=\"'solid' in props.examples\"\n          @click=\"current = 'solid'\"\n          :data-selected=\"current === 'solid' || null\"\n        >\n          <IconSolid />Solid\n        </li>\n        <li\n          v-if=\"'svelte' in props.examples\"\n          @click=\"current = 'svelte'\"\n          :data-selected=\"current === 'svelte' || null\"\n        >\n          <IconSvelte />Svelte\n        </li>\n        <li\n          v-if=\"'angular' in props.examples\"\n          @click=\"current = 'angular'\"\n          :data-selected=\"current === 'angular' || null\"\n        >\n          <IconAngular />Angular\n        </li>\n        <li\n          v-if=\"'qwik' in props.examples\"\n          @click=\"current = 'qwik'\"\n          :data-selected=\"current === 'qwik' || null\"\n        >\n          <IconQwik />Qwik\n        </li>\n        <li\n          v-if=\"'js' in props.examples\"\n          @click=\"current = 'js'\"\n          :data-selected=\"current === 'js' || null\"\n        >\n          <IconJavaScript />Native JS\n        </li>\n        <li\n          v-if=\"'html' in props.examples\"\n          @click=\"current = 'html'\"\n          :data-selected=\"current === 'html' || null\"\n        >\n          <IconHTML />HTML\n        </li>\n        <li\n          v-if=\"'yarn' in props.examples\"\n          @click=\"current = 'yarn'\"\n          :data-selected=\"current === 'yarn' || null\"\n        >\n          <IconYarn />yarn\n        </li>\n        <li\n          v-if=\"'npm' in props.examples\"\n          @click=\"current = 'npm'\"\n          :data-selected=\"current === 'npm' || null\"\n        >\n          <IconNPM />npm\n        </li>\n        <li\n          v-if=\"'pnpm' in props.examples\"\n          @click=\"current = 'pnpm'\"\n          :data-selected=\"current === 'pnpm' || null\"\n        >\n          <IconPNPM />pnpm\n        </li>\n        <li\n          v-if=\"'bun' in props.examples\"\n          @click=\"current = 'bun'\"\n          :data-selected=\"current === 'bun' || null\"\n        >\n          <IconBun />bun\n        </li>\n        <li\n          v-if=\"'nuxt' in props.examples\"\n          @click=\"current = 'nuxt'\"\n          :data-selected=\"current === 'nuxt' || null\"\n        >\n          <IconNuxt />Nuxt\n        </li>\n      </ul>\n    </div>\n  </div>\n</template>\n\n<style scoped>\n.window {\n  flex-grow: 1;\n  background-color: rgba(0, 0, 0, 0.85);\n  border-radius: 0.75em;\n  color: white;\n  padding: 1em;\n  font-family: courier, monospace;\n  box-shadow: 0 0 2em rgba(0, 0, 0, 0.3);\n  display: flex;\n  flex-direction: column;\n  margin: 2em 0;\n}\n\n.window-header {\n  font-family: var(--system-stack);\n  display: flex;\n  margin: -1em -1em 0 -1em;\n  padding: 0.75em 1em;\n  margin-bottom: 0.5em;\n  font-weight: 300;\n  align-items: center;\n  justify-content: flex-start;\n  color: var(--gray-l);\n}\n.control-title {\n  font-size: 14px;\n  justify-self: center;\n  margin: 0 auto;\n  padding-right: 2.4rem;\n}\n\n.code-block {\n  flex-grow: 1;\n  overflow: hidden;\n  display: flex;\n}\n\n.code-example {\n  display: block;\n  white-space: pre;\n  font-size: 0.9rem;\n  overflow: auto;\n  background-color: transparent;\n  line-height: 1.25;\n}\n\n.code-example::-webkit-scrollbar {\n  display: none;\n}\n\n.copy-status {\n  padding-left: 1em;\n  color: var(--gray-l);\n  font-family: var(--system-stack);\n}\n\n.control {\n  width: 0.8em;\n  height: 0.8em;\n  border-radius: 1em;\n  background-color: transparent;\n  margin-right: 0.5em;\n}\n.control--close {\n  background-color: var(--ui-red);\n}\n.control--minimize {\n  background-color: var(--ui-yellow);\n}\n.control--expand {\n  background-color: var(--ui-green);\n}\n\n.window-footer {\n  padding: 0;\n  border-radius: 0 0 0.5em 0.5em;\n  background-color: var(--gray-d);\n  margin: 1em -1em -1em -1em;\n  font-family: var(--system-stack);\n  -webkit-font-smoothing: anti-aliased;\n  overflow: hidden;\n}\n\n.frameworks {\n  -webkit-overflow-scrolling: touch;\n  -ms-overflow-style: none;\n  scrollbar-width: none;\n  display: flex;\n  align-items: center;\n  list-style-type: none;\n  padding: 0;\n  margin: 0;\n  overflow: auto;\n}\n\n.frameworks::-webkit-scrollbar {\n  display: none;\n}\n\n.frameworks li {\n  display: flex;\n  align-items: center;\n  font-size: 0.75em;\n  padding: 0.75em 1em;\n  cursor: pointer;\n  margin-bottom: 0;\n  white-space: nowrap;\n}\n.frameworks li:hover {\n  background-color: var(--gray-m);\n}\n.frameworks li[data-selected] {\n  background-color: rgba(255, 255, 255, 0.1);\n}\n.frameworks svg {\n  display: block;\n  width: 0.9em;\n  margin-right: 0.5em;\n  font-size: 1rem;\n  max-height: 1em;\n}\n</style>\n"
  },
  {
    "path": "docs/src/components/ColorModeToggle.vue",
    "content": "<script setup>\nimport { ref, onMounted, watch } from 'vue'\nimport IconMoon from './IconMoon.vue'\nimport IconSun from './IconSun.vue'\n\nconst isDark = ref(false)\nconst toggleDarkMode = () =>{\n  isDark.value = !isDark.value\n  localStorage.setItem('darkMode', isDark.value);\n}\nconst setColorAttribute = () => {\n  if (typeof document !== undefined) {\n    document.documentElement.setAttribute('data-dark-mode', isDark.value)\n  }\n}\n\nonMounted(() => {\n  if (localStorage.getItem('darkMode')) {\n    isDark.value = localStorage.getItem('darkMode') === 'true'\n  } else {\n    isDark.value = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches\n  }\n  setColorAttribute()\n})\n\nwatch(isDark, setColorAttribute)\n</script>\n\n<template>\n  <span @click=\"toggleDarkMode\">\n    <IconSun v-show=\"!isDark\" />\n    <IconMoon v-show=\"isDark\" />\n  </span>\n</template>\n\n<style scoped>\nspan {\n  cursor: pointer;\n}\nspan svg {\n  width: 1.5em;\n  height: 1.5em;\n}\n[data-dark-mode=\"true\"] span svg {\n  height: 1.25em;\n}\n</style>\n"
  },
  {
    "path": "docs/src/components/FormKitLogo.vue",
    "content": "<template>\n  <svg\n    viewBox=\"0 0 26 26\"\n    version=\"1.1\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n  >\n    <g stroke=\"none\" stroke-width=\"1\" fill-rule=\"evenodd\">\n      <g transform=\"translate(-49.000000, -47.000000)\">\n        <g transform=\"translate(49.000000, 47.000000)\">\n          <path\n            d=\"M24,21.01824 L24,25.01824 L12,25.01824 L12,21.01824 L24,21.01824 Z M4,17.01824 L4,21.01824 L0,21.01824 L0,17.01824 L4,17.01824 Z M8,13.01824 L8,17.01824 L4,17.01824 L4,13.01824 L8,13.01824 Z M12,9.01824 L12,13.01824 L8,13.01824 L8,9.01824 L12,9.01824 Z M8,5.01824 L8,9.01824 L4,9.01824 L4,5.01824 L8,5.01824 Z M4,1.01824 L4,5.01824 L0,5.01824 L0,1.01824 L4,1.01824 Z\"\n          />\n        </g>\n      </g>\n    </g>\n  </svg>\n</template>\n\n<script></script>\n"
  },
  {
    "path": "docs/src/components/GitHubLogo.vue",
    "content": "<template>\n  <svg\n    data-name=\"Layer 1\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n    viewBox=\"0 0 120.78 117.79\"\n  >\n    <path\n      class=\"cls-1\"\n      d=\"M60.39,0A60.39,60.39,0,0,0,41.3,117.69c3,.56,4.12-1.31,4.12-2.91,0-1.44-.05-6.19-.08-11.24C28.54,107.19,25,96.42,25,96.42c-2.75-7-6.71-8.84-6.71-8.84-5.48-3.75.41-3.67.41-3.67,6.07.43,9.26,6.22,9.26,6.22,5.39,9.23,14.13,6.57,17.57,5,.55-3.9,2.11-6.56,3.84-8.07C36,85.55,21.85,80.37,21.85,57.23A23.35,23.35,0,0,1,28.08,41c-.63-1.52-2.7-7.66.58-16,0,0,5.07-1.62,16.61,6.19a57.36,57.36,0,0,1,30.25,0C87,23.42,92.11,25,92.11,25c3.28,8.32,1.22,14.46.59,16a23.34,23.34,0,0,1,6.21,16.21c0,23.2-14.12,28.3-27.57,29.8,2.16,1.87,4.09,5.55,4.09,11.18,0,8.08-.06,14.58-.06,16.57,0,1.61,1.08,3.49,4.14,2.9A60.39,60.39,0,0,0,60.39,0Z\"\n    />\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/HeroTitle.vue",
    "content": "<script setup>\nimport { onMounted, ref } from \"vue\"\nimport { vAutoAnimate } from \"../../../src/index\"\nimport TheLogo from \"./TheLogo.vue\"\nimport GitHubLogo from \"./GitHubLogo.vue\"\nimport IconArrow from \"./IconArrow.vue\"\nimport FormKitLogo from \"./FormKitLogo.vue\"\nimport CodeExample from \"./CodeExample.vue\"\nimport introExample from \"../examples/intro\"\n</script>\n\n<template>\n  <section id=\"hero\">\n    <div class=\"intro\">\n      <h1 class=\"title\">\n        Add <span class=\"highlight\">motion</span> to your apps with a single\n        line of code.\n      </h1>\n      <p>\n        AutoAnimate is a zero-config, drop-in animation utility that adds smooth\n        transitions to your web app. You can use it with React, Solid, Vue,\n        Svelte, or any other JavaScript application.\n      </p>\n      <div>\n        <a class=\"button\" href=\"#installation\">Get started<IconArrow /></a>\n        <a\n          class=\"button button--alt\"\n          href=\"https://github.com/formkit/auto-animate\"\n          >GitHub<GitHubLogo\n        /></a>\n      </div>\n      <a class=\"byline\" href=\"https://www.formkit.com\">\n        <FormKitLogo class=\"formkit\" width=\"16\" height=\"16\" /> made with ♥ by\n        the FormKit team.\n      </a>\n    </div>\n    <CodeExample title=\"App\" :examples=\"introExample\" class=\"example\" />\n  </section>\n</template>\n\n<style scoped>\nsection {\n  display: block;\n  margin-top: 1em;\n  margin-bottom: 3em;\n}\n\n.intro {\n  width: 100%;\n}\n\n.title {\n  font-size: 2em;\n}\n\n@media (min-width: 24em) {\n  .title {\n    font-size: 2.25em;\n  }\n}\n\n.title .highlight {\n  font-size: inherit;\n  color: var(--primary);\n  background-image: linear-gradient(-45deg, var(--primary), var(--purple-m));\n  -webkit-background-clip: text;\n  -webkit-text-fill-color: transparent;\n}\n\n.title .highlight::selection {\n  color: white;\n  -webkit-text-fill-color: white;\n}\n\n@media (min-width: 28em) {\n  section {\n    margin-top: 4em;\n    margin-bottom: 3em;\n  }\n  .intro {\n    max-width: 34em;\n  }\n  .title {\n    font-size: 2.5em;\n  }\n}\n\n.intro p {\n  display: block;\n  padding-bottom: 2em;\n  line-height: 1.4;\n  font-weight: 300;\n  margin: 0;\n  max-width: 32em;\n}\n\n@media (min-width: 60em) {\n  section {\n    display: flex;\n  }\n  .example {\n    margin-left: 2.5em;\n    margin-top: 0;\n    margin-bottom: 0;\n    max-width: 26.7em;\n    flex-shrink: 0;\n    width: 50%;\n  }\n}\n\n@media (min-width: 65em) {\n  .example {\n    max-width: none;\n  }\n}\n\n.subtitle span {\n  display: inline-block;\n}\n.title span {\n  font-size: 1.3em;\n  display: inline-block;\n}\n\n@media (min-width: 22em) {\n  .title span {\n    font-size: 1.4em;\n  }\n}\n.logo {\n  display: inline-block;\n  width: 15vw;\n  margin-top: 1em;\n}\n\n.byline {\n  display: flex;\n  vertical-align: flex-start;\n  font-size: 0.875em;\n  color: var(--gray-m);\n  filter: grayscale(1);\n  transition: filter 0.15s ease-out;\n  font-weight: 300;\n  margin-top: 1em;\n}\n[data-dark-mode=\"true\"] .byline {\n  color: var(--gray-l);\n}\n.byline span,\n.byline svg {\n  position: relative;\n}\n.byline span {\n  font-size: 1em;\n  max-height: 16px;\n  margin-right: 0.4em;\n  margin-left: 0.25em;\n}\n\n.byline svg {\n  top: 0.15em;\n}\n\n.byline:hover {\n  text-decoration: none;\n  color: var(--primary);\n  filter: grayscale(0);\n}\n[data-dark-mode=\"true\"] .byline:hover {\n  color: var(--text);\n}\n.formkit {\n  display: block;\n  width: 1rem;\n  fill: var(--gray-m);\n  margin-right: 0.5em;\n}\n\n.byline:hover .formkit {\n  fill: var(--orange);\n}\n</style>\n"
  },
  {
    "path": "docs/src/components/IconAngular.vue",
    "content": "<template>\n  <svg\n    width=\"2370\"\n    height=\"2500\"\n    viewBox=\"0 0 256 270\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n    preserveAspectRatio=\"xMinYMin meet\"\n  >\n    <path\n      d=\"M127.606.341L.849 44.95 20.88 211.022l106.86 58.732 107.412-59.528L255.175 44.16 127.606.341z\"\n      fill=\"#B3B3B3\"\n    />\n    <path\n      d=\"M242.532 53.758L127.31 14.466v241.256l96.561-53.441 18.66-148.523z\"\n      fill=\"#A6120D\"\n    />\n    <path\n      d=\"M15.073 54.466l17.165 148.525 95.07 52.731V14.462L15.074 54.465z\"\n      fill=\"#DD1B16\"\n    />\n    <path\n      d=\"M159.027 142.898L127.31 157.73H93.881l-15.714 39.305-29.228.54L127.31 23.227l31.717 119.672zm-3.066-7.467l-28.44-56.303-23.329 55.334h23.117l28.652.97z\"\n      fill=\"#F2F2F2\"\n    />\n    <path\n      d=\"M127.309 23.226l.21 55.902 26.47 55.377h-26.62l-.06 23.189 36.81.035 17.204 39.852 27.967.518-81.981-174.873z\"\n      fill=\"#B3B3B3\"\n    />\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconArrow.vue",
    "content": "<template>\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    data-name=\"Layer 1\"\n    viewBox=\"0 0 100 100\"\n    x=\"0px\"\n    y=\"0px\"\n  >\n    <path\n      d=\"M97.64,44.1,64.72,11.18a8.06,8.06,0,1,0-11.4,11.39L72.78,42H8.06a8.06,8.06,0,0,0,0,16.12H72.6L53.32,77.43a8.06,8.06,0,0,0,11.4,11.39L97.64,55.9A8,8,0,0,0,100,50.2a1.27,1.27,0,0,0,0-.2,1.41,1.41,0,0,0,0-.2A8.07,8.07,0,0,0,97.64,44.1Z\"\n    ></path>\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconBun.vue",
    "content": "<template>\n  <svg id=\"Bun\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 80 70\">\n    <title>Bun Logo</title>\n    <path\n      id=\"Shadow\"\n      d=\"M71.09,20.74c-.16-.17-.33-.34-.5-.5s-.33-.34-.5-.5-.33-.34-.5-.5-.33-.34-.5-.5-.33-.34-.5-.5-.33-.34-.5-.5-.33-.34-.5-.5A26.46,26.46,0,0,1,75.5,35.7c0,16.57-16.82,30.05-37.5,30.05-11.58,0-21.94-4.23-28.83-10.86l.5.5.5.5.5.5.5.5.5.5.5.5.5.5C19.55,65.3,30.14,69.75,42,69.75c20.68,0,37.5-13.48,37.5-30C79.5,32.69,76.46,26,71.09,20.74Z\"\n    />\n    <g id=\"Body\">\n      <path\n        id=\"Background\"\n        d=\"M73,35.7c0,15.21-15.67,27.54-35,27.54S3,50.91,3,35.7C3,26.27,9,17.94,18.22,13S33.18,3,38,3s8.94,4.13,19.78,10C67,17.94,73,26.27,73,35.7Z\"\n        style=\"fill: #fbf0df\"\n      />\n      <path\n        id=\"Bottom_Shadow\"\n        data-name=\"Bottom Shadow\"\n        d=\"M73,35.7a21.67,21.67,0,0,0-.8-5.78c-2.73,33.3-43.35,34.9-59.32,24.94A40,40,0,0,0,38,63.24C57.3,63.24,73,50.89,73,35.7Z\"\n        style=\"fill: #f6dece\"\n      />\n      <path\n        id=\"Light_Shine\"\n        data-name=\"Light Shine\"\n        d=\"M24.53,11.17C29,8.49,34.94,3.46,40.78,3.45A9.29,9.29,0,0,0,38,3c-2.42,0-5,1.25-8.25,3.13-1.13.66-2.3,1.39-3.54,2.15-2.33,1.44-5,3.07-8,4.7C8.69,18.13,3,26.62,3,35.7c0,.4,0,.8,0,1.19C9.06,15.48,20.07,13.85,24.53,11.17Z\"\n        style=\"fill: #fffefc\"\n      />\n      <path\n        id=\"Top\"\n        d=\"M35.12,5.53A16.41,16.41,0,0,1,29.49,18c-.28.25-.06.73.3.59,3.37-1.31,7.92-5.23,6-13.14C35.71,5,35.12,5.12,35.12,5.53Zm2.27,0A16.24,16.24,0,0,1,39,19c-.12.35.31.65.55.36C41.74,16.56,43.65,11,37.93,5,37.64,4.74,37.19,5.14,37.39,5.49Zm2.76-.17A16.42,16.42,0,0,1,47,17.12a.33.33,0,0,0,.65.11c.92-3.49.4-9.44-7.17-12.53C40.08,4.54,39.82,5.08,40.15,5.32ZM21.69,15.76a16.94,16.94,0,0,0,10.47-9c.18-.36.75-.22.66.18-1.73,8-7.52,9.67-11.12,9.45C21.32,16.4,21.33,15.87,21.69,15.76Z\"\n        style=\"fill: #ccbea7; fill-rule: evenodd\"\n      />\n      <path\n        id=\"Outline\"\n        d=\"M38,65.75C17.32,65.75.5,52.27.5,35.7c0-10,6.18-19.33,16.53-24.92,3-1.6,5.57-3.21,7.86-4.62,1.26-.78,2.45-1.51,3.6-2.19C32,1.89,35,.5,38,.5s5.62,1.2,8.9,3.14c1,.57,2,1.19,3.07,1.87,2.49,1.54,5.3,3.28,9,5.27C69.32,16.37,75.5,25.69,75.5,35.7,75.5,52.27,58.68,65.75,38,65.75ZM38,3c-2.42,0-5,1.25-8.25,3.13-1.13.66-2.3,1.39-3.54,2.15-2.33,1.44-5,3.07-8,4.7C8.69,18.13,3,26.62,3,35.7,3,50.89,18.7,63.25,38,63.25S73,50.89,73,35.7C73,26.62,67.31,18.13,57.78,13,54,11,51.05,9.12,48.66,7.64c-1.09-.67-2.09-1.29-3-1.84C42.63,4,40.42,3,38,3Z\"\n      />\n    </g>\n    <g id=\"Mouth\">\n      <g id=\"Background-2\" data-name=\"Background\">\n        <path\n          d=\"M45.05,43a8.93,8.93,0,0,1-2.92,4.71,6.81,6.81,0,0,1-4,1.88A6.84,6.84,0,0,1,34,47.71,8.93,8.93,0,0,1,31.12,43a.72.72,0,0,1,.8-.81H44.26A.72.72,0,0,1,45.05,43Z\"\n          style=\"fill: #b71422\"\n        />\n      </g>\n      <g id=\"Tongue\">\n        <path\n          id=\"Background-3\"\n          data-name=\"Background\"\n          d=\"M34,47.79a6.91,6.91,0,0,0,4.12,1.9,6.91,6.91,0,0,0,4.11-1.9,10.63,10.63,0,0,0,1-1.07,6.83,6.83,0,0,0-4.9-2.31,6.15,6.15,0,0,0-5,2.78C33.56,47.4,33.76,47.6,34,47.79Z\"\n          style=\"fill: #ff6164\"\n        />\n        <path\n          id=\"Outline-2\"\n          data-name=\"Outline\"\n          d=\"M34.16,47a5.36,5.36,0,0,1,4.19-2.08,6,6,0,0,1,4,1.69c.23-.25.45-.51.66-.77a7,7,0,0,0-4.71-1.93,6.36,6.36,0,0,0-4.89,2.36A9.53,9.53,0,0,0,34.16,47Z\"\n        />\n      </g>\n      <path\n        id=\"Outline-3\"\n        data-name=\"Outline\"\n        d=\"M38.09,50.19a7.42,7.42,0,0,1-4.45-2,9.52,9.52,0,0,1-3.11-5.05,1.2,1.2,0,0,1,.26-1,1.41,1.41,0,0,1,1.13-.51H44.26a1.44,1.44,0,0,1,1.13.51,1.19,1.19,0,0,1,.25,1h0a9.52,9.52,0,0,1-3.11,5.05A7.42,7.42,0,0,1,38.09,50.19Zm-6.17-7.4c-.16,0-.2.07-.21.09a8.29,8.29,0,0,0,2.73,4.37A6.23,6.23,0,0,0,38.09,49a6.28,6.28,0,0,0,3.65-1.73,8.3,8.3,0,0,0,2.72-4.37.21.21,0,0,0-.2-.09Z\"\n      />\n    </g>\n    <g id=\"Face\">\n      <ellipse\n        id=\"Right_Blush\"\n        data-name=\"Right Blush\"\n        cx=\"53.22\"\n        cy=\"40.18\"\n        rx=\"5.85\"\n        ry=\"3.44\"\n        style=\"fill: #febbd0\"\n      />\n      <ellipse\n        id=\"Left_Bluch\"\n        data-name=\"Left Bluch\"\n        cx=\"22.95\"\n        cy=\"40.18\"\n        rx=\"5.85\"\n        ry=\"3.44\"\n        style=\"fill: #febbd0\"\n      />\n      <path\n        id=\"Eyes\"\n        d=\"M25.7,38.8a5.51,5.51,0,1,0-5.5-5.51A5.51,5.51,0,0,0,25.7,38.8Zm24.77,0A5.51,5.51,0,1,0,45,33.29,5.5,5.5,0,0,0,50.47,38.8Z\"\n        style=\"fill-rule: evenodd\"\n      />\n      <path\n        id=\"Iris\"\n        d=\"M24,33.64a2.07,2.07,0,1,0-2.06-2.07A2.07,2.07,0,0,0,24,33.64Zm24.77,0a2.07,2.07,0,1,0-2.06-2.07A2.07,2.07,0,0,0,48.75,33.64Z\"\n        style=\"fill: #fff; fill-rule: evenodd\"\n      />\n    </g>\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconDiscord.vue",
    "content": "<template>\n  <svg\n    width=\"71\"\n    height=\"55\"\n    viewBox=\"0 0 71 55\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n  >\n    <g clip-path=\"url(#clip0)\">\n      <path\n        d=\"M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z\"\n      />\n    </g>\n    <defs>\n      <clipPath id=\"clip0\">\n        <rect width=\"71\" height=\"55\" fill=\"white\" />\n      </clipPath>\n    </defs>\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconDown.vue",
    "content": "<template>\n  <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 128 128\">\n    <path\n      d=\"M113.95,36.55c-2.73-2.73-7.17-2.73-9.9,0l-40.05,40.05L23.95,36.55c-2.73-2.73-7.17-2.73-9.9,0-2.73,2.73-2.73,7.17,0,9.9l45,45c1.37,1.37,3.16,2.05,4.95,2.05s3.58-.68,4.95-2.05l45-45c2.73-2.73,2.73-7.17,0-9.9Z\"\n    />\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconHTML.vue",
    "content": "<template>\n  <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\">\n    <title>HTML5 Logo Badge</title>\n    <path fill=\"#E34F26\" d=\"M71,460 L30,0 481,0 440,460 255,512\" />\n    <path fill=\"#EF652A\" d=\"M256,472 L405,431 440,37 256,37\" />\n    <path\n      fill=\"#EBEBEB\"\n      d=\"M256,208 L181,208 176,150 256,150 256,94 255,94 114,94 115,109 129,265 256,265zM256,355 L255,355 192,338 188,293 158,293 132,293 139,382 255,414 256,414z\"\n    />\n    <path\n      fill=\"#FFF\"\n      d=\"M255,208 L255,265 325,265 318,338 255,355 255,414 371,382 372,372 385,223 387,208 371,208zM255,94 L255,129 255,150 255,150 392,150 392,150 392,150 393,138 396,109 397,94z\"\n    />\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconJavaScript.vue",
    "content": "<template>\n  <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 630 630\">\n    <rect width=\"630\" height=\"630\" fill=\"#f7df1e\" />\n    <path\n      d=\"m423.2 492.19c12.69 20.72 29.2 35.95 58.4 35.95 24.53 0 40.2-12.26 40.2-29.2 0-20.3-16.1-27.49-43.1-39.3l-14.8-6.35c-42.72-18.2-71.1-41-71.1-89.2 0-44.4 33.83-78.2 86.7-78.2 37.64 0 64.7 13.1 84.2 47.4l-46.1 29.6c-10.15-18.2-21.1-25.37-38.1-25.37-17.34 0-28.33 11-28.33 25.37 0 17.76 11 24.95 36.4 35.95l14.8 6.34c50.3 21.57 78.7 43.56 78.7 93 0 53.3-41.87 82.5-98.1 82.5-54.98 0-90.5-26.2-107.88-60.54zm-209.13 5.13c9.3 16.5 17.76 30.45 38.1 30.45 19.45 0 31.72-7.61 31.72-37.2v-201.3h59.2v202.1c0 61.3-35.94 89.2-88.4 89.2-47.4 0-74.85-24.53-88.81-54.075z\"\n    />\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconLightbulb.vue",
    "content": "<template>\n  <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 50 50\">\n    <path\n      d=\"M24.99,2c-8.26,0-14.99,6.66-14.99,14.82,0,4.41,1.98,9.7,5.1,13.65v3.71c0,1.13,.74,2.04,1.65,2.04h16.5c.91,0,1.65-.91,1.65-2.04v-3.72c3.12-3.94,5.1-9.24,5.1-13.65,0-8.16-6.74-14.82-15.01-14.82Zm0,3.26c6.48,0,11.7,5.16,11.7,11.56,0,3.47-1.48,7.91-3.89,11.25h-15.61c-2.41-3.35-3.89-7.78-3.89-11.25,0-6.4,5.21-11.56,11.69-11.56Z\"\n    />\n    <path\n      d=\"M17.99,37.84c-.91,0-1.65,.73-1.65,1.63s.74,1.63,1.65,1.63h14.02c.91,0,1.65-.73,1.65-1.63s-.74-1.63-1.65-1.63h-14.02Z\"\n    />\n    <path\n      d=\"M19.93,42.74c-.91,.04-1.61,.81-1.57,1.71,.04,.9,.82,1.59,1.73,1.55h9.9c.91,0,1.65-.73,1.65-1.63s-.74-1.63-1.65-1.63h-9.9c-.05,0-.11,0-.16,0Z\"\n    />\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconMoon.vue",
    "content": "<template>\n<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill=\"currentColor\" d=\"M32 256c0-123.8 100.3-224 223.8-224c11.36 0 29.7 1.668 40.9 3.746c9.616 1.777 11.75 14.63 3.279 19.44C245 86.5 211.2 144.6 211.2 207.8c0 109.7 99.71 193 208.3 172.3c9.561-1.805 16.28 9.324 10.11 16.95C387.9 448.6 324.8 480 255.8 480C132.1 480 32 379.6 32 256z\"/></svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconNPM.vue",
    "content": "<template>\n  <svg\n    version=\"1.1\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n    x=\"0px\"\n    y=\"0px\"\n    width=\"540px\"\n    height=\"210px\"\n    viewBox=\"0 0 18 7\"\n  >\n    <path\n      fill=\"#CB3837\"\n      d=\"M0,0h18v6H9v1H5V6H0V0z M1,5h2V2h1v3h1V1H1V5z M6,1v5h2V5h2V1H6z M8,2h1v2H8V2z M11,1v4h2V2h1v3h1V2h1v3h1V1H11z\"\n    />\n    <polygon fill=\"#FFFFFF\" points=\"1,5 3,5 3,2 4,2 4,5 5,5 5,1 1,1 \" />\n    <path fill=\"#FFFFFF\" d=\"M6,1v5h2V5h2V1H6z M9,4H8V2h1V4z\" />\n    <polygon\n      fill=\"#FFFFFF\"\n      points=\"11,1 11,5 13,5 13,2 14,2 14,5 15,5 15,2 16,2 16,5 17,5 17,1 \"\n    />\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconNuxt.vue",
    "content": "<template>\n  <svg\n    width=\"900\"\n    height=\"900\"\n    viewBox=\"0 0 900 900\"\n    fill=\"none\"\n  >\n    <path\n      d=\"M504.908 750H839.476C850.103 750.001 860.542 747.229 869.745 741.963C878.948 736.696 886.589 729.121 891.9 719.999C897.211 710.876 900.005 700.529 900 689.997C899.995 679.465 897.193 669.12 891.873 660.002L667.187 274.289C661.876 265.169 654.237 257.595 645.036 252.329C635.835 247.064 625.398 244.291 614.773 244.291C604.149 244.291 593.711 247.064 584.511 252.329C575.31 257.595 567.67 265.169 562.36 274.289L504.908 372.979L392.581 179.993C387.266 170.874 379.623 163.301 370.42 158.036C361.216 152.772 350.777 150 340.151 150C329.525 150 319.086 152.772 309.883 158.036C300.679 163.301 293.036 170.874 287.721 179.993L8.12649 660.002C2.80743 669.12 0.00462935 679.465 5.72978e-06 689.997C-0.00461789 700.529 2.78909 710.876 8.10015 719.999C13.4112 729.121 21.0523 736.696 30.255 741.963C39.4576 747.229 49.8973 750.001 60.524 750H270.538C353.748 750 415.112 713.775 457.336 643.101L559.849 467.145L614.757 372.979L779.547 655.834H559.849L504.908 750ZM267.114 655.737L120.551 655.704L340.249 278.586L449.87 467.145L376.474 593.175C348.433 639.03 316.577 655.737 267.114 655.737Z\"\n      fill=\"#00DC82\"\n    />\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconOutbound.vue",
    "content": "<template>\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n    version=\"1.1\"\n    x=\"0px\"\n    y=\"0px\"\n    viewBox=\"0 0 1024 1024\"\n    style=\"enable-background: new 0 0 1024 1024\"\n    xml:space=\"preserve\"\n    fill=\"currentColor\"\n  >\n    <g>\n      <g>\n        <g>\n          <path\n            d=\"M631.3,160.8c30.6,0,61.2,0,91.7,0c48.9,0,97.8,0,146.7,0c11.2,0,22.3,0,33.5,0     c-13.3-13.3-26.7-26.7-40-40c0,30.6,0,61.2,0,91.7c0,48.9,0,97.8,0,146.7c0,11.2,0,22.3,0,33.5c0,20.9,18.4,41,40,40     c21.7-1,40-17.6,40-40c0-30.6,0-61.2,0-91.7c0-48.9,0-97.8,0-146.7c0-11.2,0-22.3,0-33.5c0-21.6-18.3-40-40-40     c-30.6,0-61.2,0-91.7,0c-48.9,0-97.8,0-146.7,0c-11.2,0-22.3,0-33.5,0c-20.9,0-41,18.4-40,40     C592.2,142.4,608.8,160.8,631.3,160.8L631.3,160.8z\"\n          ></path>\n        </g>\n      </g>\n      <g>\n        <g>\n          <path\n            d=\"M480.3,600.3c15-15,29.9-29.9,44.9-44.9c35.9-35.9,71.8-71.8,107.8-107.8     c43.5-43.5,86.9-86.9,130.4-130.4c37.6-37.6,75.1-75.1,112.7-112.7c18.3-18.3,36.8-36.3,54.8-54.8c0.3-0.3,0.5-0.5,0.8-0.8     c14.8-14.8,15.9-42,0-56.6c-16-14.7-40.8-15.8-56.6,0c-15,15-29.9,29.9-44.9,44.9c-35.9,35.9-71.8,71.8-107.8,107.8     C678.9,288.5,635.4,332,592,375.5c-37.6,37.6-75.1,75.1-112.7,112.7c-18.3,18.3-36.8,36.3-54.8,54.8c-0.3,0.3-0.5,0.5-0.8,0.8     c-14.8,14.8-15.9,42,0,56.6C439.7,614.9,464.5,616.1,480.3,600.3L480.3,600.3z\"\n          ></path>\n        </g>\n      </g>\n      <g>\n        <g>\n          <path\n            d=\"M863.3,572.8c0,30.4,0,60.7,0,91.1c0,47.1,0,94.2,0,141.4c0,12.5,1,25.6-0.7,38.1c0.5-3.5,1-7.1,1.4-10.6     c-0.9,5.8-2.4,11.3-4.6,16.8c1.3-3.2,2.7-6.4,4-9.6c-2.4,5.7-5.5,10.9-9.2,15.9c2.1-2.7,4.2-5.4,6.3-8.1     c-3.8,4.8-8.1,9.1-12.9,12.9c2.7-2.1,5.4-4.2,8.1-6.3c-4.9,3.7-10.2,6.8-15.9,9.2c3.2-1.3,6.4-2.7,9.6-4     c-5.4,2.2-11,3.7-16.8,4.6c3.5-0.5,7.1-1,10.6-1.4c-8.2,1.1-16.7,0.7-25,0.7c-15.5,0-30.9,0-46.4,0c-51.6,0-103.2,0-154.8,0     c-62.7,0-125.5,0-188.2,0c-54,0-108,0-162,0c-25.4,0-50.8,0.2-76.1,0c-3.4,0-6.8-0.2-10.2-0.7c3.5,0.5,7.1,1,10.6,1.4     c-5.8-0.9-11.3-2.4-16.8-4.6c3.2,1.3,6.4,2.7,9.6,4c-5.7-2.4-10.9-5.5-15.9-9.2c2.7,2.1,5.4,4.2,8.1,6.3     c-4.8-3.8-9.1-8.1-12.9-12.9c2.1,2.7,4.2,5.4,6.3,8.1c-3.7-4.9-6.8-10.2-9.2-15.9c1.3,3.2,2.7,6.4,4,9.6     c-2.2-5.4-3.7-11-4.6-16.8c0.5,3.5,1,7.1,1.4,10.6c-1.1-8.2-0.7-16.7-0.7-25c0-15.5,0-30.9,0-46.4c0-51.6,0-103.2,0-154.8     c0-62.7,0-125.5,0-188.2c0-54,0-108,0-162c0-25.4-0.2-50.8,0-76.1c0-3.4,0.2-6.8,0.7-10.2c-0.5,3.5-1,7.1-1.4,10.6     c0.9-5.8,2.4-11.3,4.6-16.8c-1.3,3.2-2.7,6.4-4,9.6c2.4-5.7,5.5-10.9,9.2-15.9c-2.1,2.7-4.2,5.4-6.3,8.1     c3.8-4.8,8.1-9.1,12.9-12.9c-2.7,2.1-5.4,4.2-8.1,6.3c4.9-3.7,10.2-6.8,15.9-9.2c-3.2,1.3-6.4,2.7-9.6,4c5.4-2.2,11-3.7,16.8-4.6     c-3.5,0.5-7.1,1-10.6,1.4c10.6-1.4,21.6-0.7,32.2-0.7c20.6,0,41.2,0,61.9,0c47.8,0,95.5,0,143.3,0c11.1,0,22.1,0,33.2,0     c20.9,0,41-18.4,40-40c-1-21.7-17.6-40-40-40c-27.2,0-54.4,0-81.6,0c-45.4,0-90.9,0-136.3,0c-13.1,0-26.2,0-39.2,0     c-30.7,0-60.5,10.7-82.1,33.1c-20.3,21.1-31.2,49-31.2,78.2c0,6.1,0,12.2,0,18.2c0,28.4,0,56.7,0,85.1c0,42.6,0,85.2,0,127.8     c0,48,0,96.1,0,144.1c0,44.7,0,89.3,0,134c0,32.5,0,65,0,97.6c0,18.9-0.8,37.8,2.3,56.5c3.5,21.1,15.5,42,30.8,56.7     c7.8,7.5,17,14.5,26.9,19.2c13.9,6.5,26.1,9.9,41.3,11.5c5.7,0.6,11.3,0.5,17,0.5c23.5,0,47.1,0,70.6,0c39.9,0,79.9,0,119.8,0     c47.5,0,95,0,142.5,0c46.2,0,92.3,0,138.5,0c36.4,0,72.9,0,109.3,0c17.5,0,35,0,52.5,0c30-0.1,60.1-11.8,80.7-34     c7.2-7.8,14.1-16.8,18.6-26.5c6.4-13.9,9.6-25.6,11.3-40.7c0.5-4.3,0.5-8.6,0.5-12.9c0-35.3,0-70.6,0-105.9     c0-42.8,0-85.6,0-128.4c0-7.3,0-14.7,0-22c0-20.9-18.4-41-40-40C881.6,533.7,863.3,550.3,863.3,572.8L863.3,572.8z\"\n          ></path>\n        </g>\n      </g>\n    </g>\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconPNPM.vue",
    "content": "<template>\n  <svg\n    version=\"1.1\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n    preserveAspectRatio=\"xMidYMid meet\"\n    viewBox=\"76.58987244897958 44 164.00775510204068 164\"\n    width=\"160.01\"\n    height=\"160\"\n  >\n    <defs>\n      <path\n        d=\"M237.6 95L187.6 95L187.6 45L237.6 45L237.6 95Z\"\n        id=\"arNRoK435\"\n      ></path>\n      <path\n        d=\"M182.59 95L132.59 95L132.59 45L182.59 45L182.59 95Z\"\n        id=\"a3H2WU7Px\"\n      ></path>\n      <path\n        d=\"M127.59 95L77.59 95L77.59 45L127.59 45L127.59 95Z\"\n        id=\"b1DInM56vl\"\n      ></path>\n      <path\n        d=\"M237.6 150L187.6 150L187.6 100L237.6 100L237.6 150Z\"\n        id=\"a7LFlgQIwu\"\n      ></path>\n      <path\n        d=\"M182.59 150L132.59 150L132.59 100L182.59 100L182.59 150Z\"\n        id=\"amwLiZcuo\"\n      ></path>\n      <path\n        d=\"M182.59 205L132.59 205L132.59 155L182.59 155L182.59 205Z\"\n        id=\"f3Peu5RWan\"\n      ></path>\n      <path\n        d=\"M237.6 205L187.6 205L187.6 155L237.6 155L237.6 205Z\"\n        id=\"a6DXBfqPa\"\n      ></path>\n      <path\n        d=\"M127.59 205L77.59 205L77.59 155L127.59 155L127.59 205Z\"\n        id=\"c1GWSTH1z7\"\n      ></path>\n    </defs>\n    <g>\n      <g>\n        <use\n          xlink:href=\"#arNRoK435\"\n          opacity=\"1\"\n          fill=\"#f9ad00\"\n          fill-opacity=\"1\"\n        ></use>\n      </g>\n      <g>\n        <use\n          xlink:href=\"#a3H2WU7Px\"\n          opacity=\"1\"\n          fill=\"#f9ad00\"\n          fill-opacity=\"1\"\n        ></use>\n      </g>\n      <g>\n        <use\n          xlink:href=\"#b1DInM56vl\"\n          opacity=\"1\"\n          fill=\"#f9ad00\"\n          fill-opacity=\"1\"\n        ></use>\n      </g>\n      <g>\n        <use\n          xlink:href=\"#a7LFlgQIwu\"\n          opacity=\"1\"\n          fill=\"#f9ad00\"\n          fill-opacity=\"1\"\n        ></use>\n      </g>\n      <g>\n        <use\n          xlink:href=\"#amwLiZcuo\"\n          opacity=\"1\"\n          fill=\"#4e4e4e\"\n          fill-opacity=\"1\"\n        ></use>\n      </g>\n      <g>\n        <use\n          xlink:href=\"#f3Peu5RWan\"\n          opacity=\"1\"\n          fill=\"#4e4e4e\"\n          fill-opacity=\"1\"\n        ></use>\n      </g>\n      <g>\n        <use\n          xlink:href=\"#a6DXBfqPa\"\n          opacity=\"1\"\n          fill=\"#4e4e4e\"\n          fill-opacity=\"1\"\n        ></use>\n      </g>\n      <g>\n        <use\n          xlink:href=\"#c1GWSTH1z7\"\n          opacity=\"1\"\n          fill=\"#4e4e4e\"\n          fill-opacity=\"1\"\n        ></use>\n      </g>\n    </g>\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconPreact.vue",
    "content": "<template>\n  <svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"296px\" height=\"296px\" viewBox=\"-20 0 296 296\" version=\"1.1\" preserveAspectRatio=\"xMidYMid\">\n\t<g>\n\t\t<polygon fill=\"#673AB8\" points=\"128 0 256 73.8999491 256 221.699847 128 295.599796 0 221.699847 0 73.8999491\"/>\n\t\t<path d=\"M34.8647584,220.478469 C51.8814262,242.25881 105.959701,225.662965 157.014868,185.774297 C208.070035,145.885628 237.255632,97.428608 220.238964,75.6482664 C203.222296,53.8679249 149.144022,70.4637701 98.0888543,110.352439 C47.0336869,150.241107 17.8480906,198.698127 34.8647584,220.478469 Z M42.1343351,214.798853 C36.4908625,207.575537 38.9565723,193.395881 49.7081913,175.544904 C61.0297348,156.747677 80.2490923,135.997367 103.76847,117.622015 C127.287848,99.2466634 152.071368,85.6181573 173.049166,79.1803727 C192.970945,73.066665 207.325915,74.1045667 212.969387,81.3278822 C218.61286,88.5511977 216.14715,102.730854 205.395531,120.581832 C194.073987,139.379058 174.85463,160.129368 151.335252,178.50472 C127.815874,196.880072 103.032354,210.508578 82.054556,216.946362 C62.1327769,223.06007 47.7778077,222.022168 42.1343351,214.798853 Z\" fill=\"#FFFFFF\"/>\n\t\t<path d=\"M220.238964,220.478469 C237.255632,198.698127 208.070035,150.241107 157.014868,110.352439 C105.959701,70.4637701 51.8814262,53.8679249 34.8647584,75.6482664 C17.8480906,97.428608 47.0336869,145.885628 98.0888543,185.774297 C149.144022,225.662965 203.222296,242.25881 220.238964,220.478469 Z M212.969387,214.798853 C207.325915,222.022168 192.970945,223.06007 173.049166,216.946362 C152.071368,210.508578 127.287848,196.880072 103.76847,178.50472 C80.2490923,160.129368 61.0297348,139.379058 49.7081913,120.581832 C38.9565723,102.730854 36.4908625,88.5511977 42.1343351,81.3278822 C47.7778077,74.1045667 62.1327769,73.066665 82.054556,79.1803727 C103.032354,85.6181573 127.815874,99.2466634 151.335252,117.622015 C174.85463,135.997367 194.073987,156.747677 205.395531,175.544904 C216.14715,193.395881 218.61286,207.575537 212.969387,214.798853 Z\" fill=\"#FFFFFF\"/>\n\t\t<path d=\"M127.551861,167.666971 C138.378632,167.666971 147.155465,158.890139 147.155465,148.063368 C147.155465,137.236596 138.378632,128.459764 127.551861,128.459764 C116.72509,128.459764 107.948257,137.236596 107.948257,148.063368 C107.948257,158.890139 116.72509,167.666971 127.551861,167.666971 L127.551861,167.666971 Z\" fill=\"#FFFFFF\"/>\n\t</g>\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconQwik.vue",
    "content": "<template>\n  <svg width=\"256px\" height=\"272px\" viewBox=\"0 0 256 272\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" preserveAspectRatio=\"xMidYMid\">\n    <title>Qwik</title>\n    <g>\n        <path d=\"M224.802633,271.548066 L176.04373,223.065139 L175.29865,223.17158 L175.29865,222.639928 L71.6062201,120.251664 L97.1567871,95.5849889 L82.1459867,9.46423235 L10.923994,97.7115967 C-1.21249511,109.938494 -3.44814914,129.82085 5.28159772,144.493127 L49.7819901,218.280821 C56.5954198,229.657294 68.9446684,236.461781 82.2524269,236.355341 L104.289921,236.14246 L224.802633,271.548066 Z\" fill=\"#18B6F6\"></path>\n        <path d=\"M251.41376,96.0096521 L241.619075,77.935132 L236.508852,68.685266 L234.486489,65.0701426 L234.273609,65.2830228 L207.445212,18.8201544 C200.738387,7.12474433 188.176258,-0.105173442 174.549179,4.00642427e-13 L151.021523,0.639029547 L80.8637673,0.851690324 C67.5565576,0.958020713 55.4201892,7.97527771 48.7129802,19.3517514 L6.02242644,103.983883 L82.3544777,9.25113264 L182.427394,119.187811 L164.648054,137.15644 L175.29426,223.171031 L175.4007,222.958151 L175.4007,223.171031 L175.18782,223.171031 L175.4007,223.383362 L183.704676,231.464033 L224.053163,270.909425 C225.756753,272.504381 228.524745,270.590653 227.353355,268.570486 L202.441978,219.555907 L245.877777,139.283048 L247.261498,137.688092 C247.793699,137.05 248.3259,136.412457 248.752209,135.774364 C257.269064,124.18501 258.439906,108.66231 251.41376,96.0096521 Z\" fill=\"#AC7EF4\"></path>\n        <polygon fill=\"#FFFFFF\" points=\"182.745617 118.763148 82.3533804 9.35773736 96.6190999 95.053337 71.0685329 119.826452 175.080283 223.065139 165.711906 137.36932\"></polygon>\n    </g>\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconReact.vue",
    "content": "<template>\n  <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"-11.5 -10.23174 23 20.46348\">\n    <title>React Logo</title>\n    <circle cx=\"0\" cy=\"0\" r=\"2.05\" fill=\"#61dafb\" />\n    <g stroke=\"#61dafb\" stroke-width=\"1\" fill=\"none\">\n      <ellipse rx=\"11\" ry=\"4.2\" />\n      <ellipse rx=\"11\" ry=\"4.2\" transform=\"rotate(60)\" />\n      <ellipse rx=\"11\" ry=\"4.2\" transform=\"rotate(120)\" />\n    </g>\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconRemove.vue",
    "content": "<template>\n  <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" x=\"0px\" y=\"0px\">\n    <path\n      d=\"M27.19,7.64a2,2,0,1,0-2.83-2.83l-6.95,6.95a2,2,0,0,1-2.83,0L7.64,4.81A2,2,0,0,0,4.81,7.64l6.95,6.95a2,2,0,0,1,0,2.83L4.81,24.36a2,2,0,1,0,2.83,2.83l6.95-6.95a2,2,0,0,1,2.83,0l6.95,6.95a2,2,0,1,0,2.83-2.83l-6.95-6.95a2,2,0,0,1,0-2.83Z\"\n    ></path>\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconSolid.vue",
    "content": "<template>\n  <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 166 155.3\">\n    <title>Solid Logo</title>\n    <defs>\n      <linearGradient\n        id=\"a\"\n        gradientUnits=\"userSpaceOnUse\"\n        x1=\"27.5\"\n        y1=\"3\"\n        x2=\"152\"\n        y2=\"63.5\"\n      >\n        <stop offset=\".1\" stop-color=\"#76b3e1\" />\n        <stop offset=\".3\" stop-color=\"#dcf2fd\" />\n        <stop offset=\"1\" stop-color=\"#76b3e1\" />\n      </linearGradient>\n      <linearGradient\n        id=\"b\"\n        gradientUnits=\"userSpaceOnUse\"\n        x1=\"95.8\"\n        y1=\"32.6\"\n        x2=\"74\"\n        y2=\"105.2\"\n      >\n        <stop offset=\"0\" stop-color=\"#76b3e1\" />\n        <stop offset=\".5\" stop-color=\"#4377bb\" />\n        <stop offset=\"1\" stop-color=\"#1f3b77\" />\n      </linearGradient>\n      <linearGradient\n        id=\"c\"\n        gradientUnits=\"userSpaceOnUse\"\n        x1=\"18.4\"\n        y1=\"64.2\"\n        x2=\"144.3\"\n        y2=\"149.8\"\n      >\n        <stop offset=\"0\" stop-color=\"#315aa9\" />\n        <stop offset=\".5\" stop-color=\"#518ac8\" />\n        <stop offset=\"1\" stop-color=\"#315aa9\" />\n      </linearGradient>\n      <linearGradient\n        id=\"d\"\n        gradientUnits=\"userSpaceOnUse\"\n        x1=\"75.2\"\n        y1=\"74.5\"\n        x2=\"24.4\"\n        y2=\"260.8\"\n      >\n        <stop offset=\"0\" stop-color=\"#4377bb\" />\n        <stop offset=\".5\" stop-color=\"#1a336b\" />\n        <stop offset=\"1\" stop-color=\"#1a336b\" />\n      </linearGradient>\n    </defs>\n    <path\n      d=\"M163 35S110-4 69 5l-3 1c-6 2-11 5-14 9l-2 3-15 26 26 5c11 7 25 10 38 7l46 9 18-30z\"\n      fill=\"#76b3e1\"\n    />\n    <path\n      d=\"M163 35S110-4 69 5l-3 1c-6 2-11 5-14 9l-2 3-15 26 26 5c11 7 25 10 38 7l46 9 18-30z\"\n      opacity=\".3\"\n      fill=\"url(#a)\"\n    />\n    <path\n      d=\"M52 35l-4 1c-17 5-22 21-13 35 10 13 31 20 48 15l62-21S92 26 52 35z\"\n      fill=\"#518ac8\"\n    />\n    <path\n      d=\"M52 35l-4 1c-17 5-22 21-13 35 10 13 31 20 48 15l62-21S92 26 52 35z\"\n      opacity=\".3\"\n      fill=\"url(#b)\"\n    />\n    <path\n      d=\"M134 80a45 45 0 00-48-15L24 85 4 120l112 19 20-36c4-7 3-15-2-23z\"\n      fill=\"url(#c)\"\n    />\n    <path\n      d=\"M114 115a45 45 0 00-48-15L4 120s53 40 94 30l3-1c17-5 23-21 13-34z\"\n      fill=\"url(#d)\"\n    />\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconSun.vue",
    "content": "<template>\n<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill=\"currentColor\" d=\"M256 144C194.1 144 144 194.1 144 256c0 61.86 50.14 112 112 112s112-50.14 112-112C368 194.1 317.9 144 256 144zM256 320c-35.29 0-64-28.71-64-64c0-35.29 28.71-64 64-64s64 28.71 64 64C320 291.3 291.3 320 256 320zM256 112c13.25 0 24-10.75 24-24v-64C280 10.75 269.3 0 256 0S232 10.75 232 24v64C232 101.3 242.8 112 256 112zM256 400c-13.25 0-24 10.75-24 24v64C232 501.3 242.8 512 256 512s24-10.75 24-24v-64C280 410.8 269.3 400 256 400zM488 232h-64c-13.25 0-24 10.75-24 24s10.75 24 24 24h64C501.3 280 512 269.3 512 256S501.3 232 488 232zM112 256c0-13.25-10.75-24-24-24h-64C10.75 232 0 242.8 0 256s10.75 24 24 24h64C101.3 280 112 269.3 112 256zM391.8 357.8c-9.344-9.375-24.56-9.372-33.94 .0031s-9.375 24.56 0 33.93l45.25 45.28c4.672 4.688 10.83 7.031 16.97 7.031s12.28-2.344 16.97-7.031c9.375-9.375 9.375-24.56 0-33.94L391.8 357.8zM120.2 154.2c4.672 4.688 10.83 7.031 16.97 7.031S149.5 158.9 154.2 154.2c9.375-9.375 9.375-24.56 0-33.93L108.9 74.97c-9.344-9.375-24.56-9.375-33.94 0s-9.375 24.56 0 33.94L120.2 154.2zM374.8 161.2c6.141 0 12.3-2.344 16.97-7.031l45.25-45.28c9.375-9.375 9.375-24.56 0-33.94s-24.59-9.375-33.94 0l-45.25 45.28c-9.375 9.375-9.375 24.56 0 33.93C362.5 158.9 368.7 161.2 374.8 161.2zM120.2 357.8l-45.25 45.28c-9.375 9.375-9.375 24.56 0 33.94c4.688 4.688 10.83 7.031 16.97 7.031s12.3-2.344 16.97-7.031l45.25-45.28c9.375-9.375 9.375-24.56 0-33.93S129.6 348.4 120.2 357.8z\"/></svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconSvelte.vue",
    "content": "<template>\n  <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 308\">\n    <title>Svelte Logo</title>\n    <path\n      fill=\"#FF3E00\"\n      d=\"M239.682 40.707C211.113-.182 154.69-12.301 113.895 13.69L42.247 59.356a82.198 82.198 0 0 0-37.135 55.056a86.566 86.566 0 0 0 8.536 55.576a82.425 82.425 0 0 0-12.296 30.719a87.596 87.596 0 0 0 14.964 66.244c28.574 40.893 84.997 53.007 125.787 27.016l71.648-45.664a82.182 82.182 0 0 0 37.135-55.057a86.601 86.601 0 0 0-8.53-55.577a82.409 82.409 0 0 0 12.29-30.718a87.573 87.573 0 0 0-14.963-66.244\"\n    />\n    <path\n      fill=\"#FFF\"\n      d=\"M106.889 270.841c-23.102 6.007-47.497-3.036-61.103-22.648a52.685 52.685 0 0 1-9.003-39.85a49.978 49.978 0 0 1 1.713-6.693l1.35-4.115l3.671 2.697a92.447 92.447 0 0 0 28.036 14.007l2.663.808l-.245 2.659a16.067 16.067 0 0 0 2.89 10.656a17.143 17.143 0 0 0 18.397 6.828a15.786 15.786 0 0 0 4.403-1.935l71.67-45.672a14.922 14.922 0 0 0 6.734-9.977a15.923 15.923 0 0 0-2.713-12.011a17.156 17.156 0 0 0-18.404-6.832a15.78 15.78 0 0 0-4.396 1.933l-27.35 17.434a52.298 52.298 0 0 1-14.553 6.391c-23.101 6.007-47.497-3.036-61.101-22.649a52.681 52.681 0 0 1-9.004-39.849a49.428 49.428 0 0 1 22.34-33.114l71.664-45.677a52.218 52.218 0 0 1 14.563-6.398c23.101-6.007 47.497 3.036 61.101 22.648a52.685 52.685 0 0 1 9.004 39.85a50.559 50.559 0 0 1-1.713 6.692l-1.35 4.116l-3.67-2.693a92.373 92.373 0 0 0-28.037-14.013l-2.664-.809l.246-2.658a16.099 16.099 0 0 0-2.89-10.656a17.143 17.143 0 0 0-18.398-6.828a15.786 15.786 0 0 0-4.402 1.935l-71.67 45.674a14.898 14.898 0 0 0-6.73 9.975a15.9 15.9 0 0 0 2.709 12.012a17.156 17.156 0 0 0 18.404 6.832a15.841 15.841 0 0 0 4.402-1.935l27.345-17.427a52.147 52.147 0 0 1 14.552-6.397c23.101-6.006 47.497 3.037 61.102 22.65a52.681 52.681 0 0 1 9.003 39.848a49.453 49.453 0 0 1-22.34 33.12l-71.664 45.673a52.218 52.218 0 0 1-14.563 6.398\"\n    />\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconTwitter.vue",
    "content": "<template>\n  <svg id=\"Logo\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 248 204\">\n    <path\n      id=\"Logo-2\"\n      data-name=\"Logo\"\n      d=\"M221.95,51.29c.15,2.17,.15,4.34,.15,6.53,0,66.73-50.8,143.69-143.69,143.69v-.04c-27.44,.04-54.31-7.82-77.41-22.64,3.99,.48,8,.72,12.02,.73,22.74,.02,44.83-7.61,62.72-21.66-21.61-.41-40.56-14.5-47.18-35.07,7.57,1.46,15.37,1.16,22.8-.87-23.56-4.76-40.51-25.46-40.51-49.5v-.64c7.02,3.91,14.88,6.08,22.92,6.32C11.58,63.31,4.74,33.79,18.14,10.71c25.64,31.55,63.47,50.73,104.08,52.76-4.07-17.54,1.49-35.92,14.61-48.25,20.34-19.12,52.33-18.14,71.45,2.19,11.31-2.23,22.15-6.38,32.07-12.26-3.77,11.69-11.66,21.62-22.2,27.93,10.01-1.18,19.79-3.86,29-7.95-6.78,10.14-15.31,18.99-25.19,26.14Z\"\n    />\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconVue.vue",
    "content": "<template>\n  <svg\n    version=\"1.1\"\n    viewBox=\"0 0 261.76 226.69\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n  >\n    <g transform=\"matrix(1.3333 0 0 -1.3333 -76.311 313.34)\">\n      <g transform=\"translate(178.06 235.01)\">\n        <path\n          d=\"m0 0-22.669-39.264-22.669 39.264h-75.491l98.16-170.02 98.16 170.02z\"\n          fill=\"#41b883\"\n        />\n      </g>\n      <g transform=\"translate(178.06 235.01)\">\n        <path\n          d=\"m0 0-22.669-39.264-22.669 39.264h-36.227l58.896-102.01 58.896 102.01z\"\n          fill=\"#34495e\"\n        />\n      </g>\n    </g>\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/IconYarn.vue",
    "content": "<template>\n  <svg\n    width=\"256px\"\n    height=\"256px\"\n    viewBox=\"0 0 256 256\"\n    version=\"1.1\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n    preserveAspectRatio=\"xMidYMid\"\n  >\n    <g>\n      <path\n        d=\"M128,0 C57.3281853,0 0,57.3281853 0,128 C0,198.671815 57.3281853,256 128,256 C198.671815,256 256,198.671815 256,128 C256,57.3281853 198.671815,0 128,0\"\n        fill=\"#368FB9\"\n      ></path>\n      <path\n        d=\"M203.317375,174.060232 C195.410039,175.938224 191.40695,177.667954 181.621622,184.043243 C166.350579,193.927413 149.646332,198.523552 149.646332,198.523552 C149.646332,198.523552 148.262548,200.599228 144.259459,201.538224 C137.340541,203.218533 111.295753,204.651737 108.923552,204.701158 C102.548263,204.750579 98.6440154,203.07027 97.5567568,200.450965 C94.2455598,192.543629 102.301158,189.08417 102.301158,189.08417 C102.301158,189.08417 100.522008,187.996911 99.4841699,187.008494 C98.5451737,186.069498 97.5567568,184.191506 97.2602317,184.883398 C96.0247104,187.898069 95.3822394,195.261776 92.0710425,198.572973 C87.5243243,203.169112 78.9250965,201.637066 73.834749,198.96834 C68.2501931,196.003089 74.2301158,189.034749 74.2301158,189.034749 C74.2301158,189.034749 71.215444,190.8139 68.7938224,187.156757 C66.619305,183.796139 64.5930502,178.06332 65.1366795,170.996139 C65.7297297,162.940541 74.7243243,155.132046 74.7243243,155.132046 C74.7243243,155.132046 73.1428571,143.221622 78.3320463,131.014672 C83.027027,119.894981 95.6787645,110.949807 95.6787645,110.949807 C95.6787645,110.949807 85.0532819,99.1876448 89.0069498,88.611583 C91.576834,81.6926641 92.6146718,81.7420849 93.4548263,81.4455598 C96.4200772,80.3088803 99.2864865,79.0733591 101.411583,76.7505792 C112.037066,65.2849421 125.578378,67.4594595 125.578378,67.4594595 C125.578378,67.4594595 132.003089,47.9382239 137.933591,51.7436293 C139.762162,52.9297297 146.335135,67.5583012 146.335135,67.5583012 C146.335135,67.5583012 153.352896,63.4563707 154.143629,64.988417 C158.393822,73.2416988 158.888031,89.0069498 157.010039,98.5945946 C153.847104,114.409266 145.939768,122.909653 142.776834,128.247104 C142.035521,129.482625 151.27722,133.386873 157.10888,149.54749 C162.495753,164.324324 157.701931,176.728958 158.542085,178.112741 C158.690347,178.359846 158.739768,178.458687 158.739768,178.458687 C158.739768,178.458687 164.917375,178.952896 177.322008,171.292664 C183.944402,167.190734 191.802317,162.594595 200.74749,162.495753 C209.396139,162.34749 209.840927,172.478764 203.317375,174.060232 L203.317375,174.060232 Z M215.079537,166.795367 C214.189961,159.777606 208.259459,154.934363 200.648649,155.033205 C189.281853,155.181467 179.743629,161.062548 173.417761,164.966795 C170.946718,166.498842 168.821622,167.635521 166.99305,168.475676 C167.388417,162.742857 167.042471,155.230888 164.07722,146.977606 C160.469498,137.093436 155.626255,131.014672 152.166795,127.505792 C156.169884,121.674131 161.655598,113.173745 164.225483,100.027799 C166.449421,88.8092664 165.757529,71.3637066 160.667181,61.5783784 C159.629344,59.6015444 157.899614,58.1683398 155.725097,57.5752896 C154.835521,57.3281853 153.155212,56.8339768 149.844015,57.772973 C144.85251,47.4440154 143.12278,46.3567568 141.788417,45.4671815 C139.020849,43.6880309 135.759073,43.2926641 132.694981,44.4293436 C128.59305,45.9119691 125.08417,49.8656371 121.772973,56.8833977 C121.278764,57.9212355 120.833977,58.9096525 120.43861,59.8980695 C114.162162,60.3428571 104.277992,62.6162162 95.9258687,71.6602317 C94.8880309,72.7969112 92.8617761,73.6370656 90.7366795,74.4277992 L90.7861004,74.4277992 C86.4370656,75.9598456 84.4602317,79.5181467 82.03861,85.9428571 C78.6779923,94.9374517 82.1374517,103.783784 85.5474903,109.516602 C80.9019305,113.667954 74.7243243,120.290347 71.4625483,128.049421 C67.4100386,137.637066 66.965251,147.027027 67.1135135,152.117375 C63.6540541,155.774517 58.3166023,162.644015 57.7235521,170.353668 C56.9328185,181.127413 60.8370656,188.441699 62.5667954,191.110425 C63.0610039,191.901158 63.6046332,192.543629 64.1976834,193.1861 C64,194.520463 63.9505792,195.953668 64.2471042,197.436293 C64.8895753,200.895753 67.0640927,203.712741 70.3752896,205.491892 C76.8988417,208.951351 85.992278,210.433977 93.0100386,206.925097 C95.5305019,209.593822 100.126641,212.163707 108.478764,212.163707 L108.972973,212.163707 C111.098069,212.163707 138.081853,210.730502 145.939768,208.803089 C149.448649,207.962934 151.87027,206.480309 153.451737,205.145946 C158.492664,203.564479 172.429344,198.820077 185.57529,190.319691 C194.866409,184.290347 198.078764,183.005405 204.997683,181.325097 C211.718919,179.694208 215.919691,173.566023 215.079537,166.795367 L215.079537,166.795367 Z\"\n        fill=\"#FFFFFF\"\n      ></path>\n    </g>\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/components/KickstartPromo.vue",
    "content": "<template>\n  <div class=\"kickstart-promo\">\n    <a\n      href=\"https://kickstart.formkit.com/?utm_source=auto_animate_kickstart_promo&utm_medium=web\"\n      target=\"_blank\"\n      class=\"kickstart-promo__cta\"\n    />\n\n    <span class=\"announcement-icon\">\n      <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 576 512\">\n        <path\n          fill=\"currentColor\"\n          d=\"M544 32c17.7 0 32 14.3 32 32V448c0 17.7-14.3 32-32 32s-32-14.3-32-32V64c0-17.7 14.3-32 32-32zM64 190.3L480 64V448L348.9 408.2C338.2 449.5 300.7 480 256 480c-53 0-96-43-96-96c0-11 1.9-21.7 5.3-31.5L64 321.7C63.1 338.6 49.1 352 32 352c-17.7 0-32-14.3-32-32V192c0-17.7 14.3-32 32-32c17.1 0 31.1 13.4 32 30.3zm239 203.9l-91.6-27.8c-2.1 5.4-3.3 11.4-3.3 17.6c0 26.5 21.5 48 48 48c23 0 42.2-16.2 46.9-37.8z\"\n        />\n      </svg>\n    </span>\n\n    <div class=\"kickstart-promo__image\" aria-label=\"Kickstart\">\n      <svg\n        id=\"b\"\n        data-name=\"Layer 2\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n        width=\"562.4\"\n        height=\"100.3\"\n        xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n        viewBox=\"0 0 562.4 100.3\"\n      >\n        <defs>\n          <linearGradient\n            id=\"d\"\n            x1=\"3.1\"\n            y1=\"-330.7\"\n            x2=\"67.8\"\n            y2=\"-430.7\"\n            gradientTransform=\"translate(0 -319.9) scale(1 -1)\"\n            gradientUnits=\"userSpaceOnUse\"\n          >\n            <stop offset=\"0\" stop-color=\"#f79259\" />\n            <stop offset=\"1\" stop-color=\"#f8ce68\" />\n          </linearGradient>\n        </defs>\n        <g id=\"c\" data-name=\"Layer 2\">\n          <path\n            class=\"e\"\n            d=\"m16.7,0H0v16.7h16.7V0Zm0,66.7H0v16.7h16.7v-16.7Zm33.4,16.7h50v16.7h-50v-16.7ZM33.4,16.7h-16.7v16.7h16.7v16.7h16.7v-16.7h-16.7v-16.7Zm-16.7,33.3h16.7v16.7h-16.7v-16.7Z\"\n          />\n          <g>\n            <path\n              class=\"f\"\n              d=\"m131.3,100h-11.2v-17.6h11.2v2.4h-8.4v5.2h8v2.3h-8v5.5h8.4v2.4Z\"\n            />\n            <path\n              class=\"f\"\n              d=\"m149.8,95.3h-6.7l-1.6,4.8h-2.8l6.4-17.6h3l6.4,17.6h-2.9l-1.6-4.8Zm-6-2.2h5.3l-2.6-7.7h0l-2.6,7.7Z\"\n            />\n            <path\n              class=\"f\"\n              d=\"m164.9,100h-2.7v-17.6h6.9c3.6,0,5.9,2.1,5.9,5.4s-1.3,4.3-3.4,5l4,7.2h-3.2l-3.6-6.8h-3.9v6.8Zm0-9h3.9c2.1,0,3.3-1.2,3.3-3.2s-1.3-3.2-3.4-3.2h-3.9v6.3Z\"\n            />\n            <path class=\"f\" d=\"m194.4,100h-10.9v-17.6h2.7v15.2h8.2v2.4Z\" />\n            <path\n              class=\"f\"\n              d=\"m205,100v-7.2l-6.2-10.4h3.1l4.5,7.8h0l4.5-7.8h3l-6.2,10.4v7.2h-2.7Z\"\n            />\n            <path\n              class=\"f\"\n              d=\"m243.2,95.3h-6.7l-1.6,4.8h-2.8l6.4-17.6h3l6.4,17.6h-2.9l-1.6-4.8Zm-6-2.2h5.3l-2.6-7.7h0l-2.6,7.7Z\"\n            />\n            <path\n              class=\"f\"\n              d=\"m254.3,91.2c0-5.6,3.2-9.1,8.1-9.1s7,2.5,7.5,6.1h-2.7c-.5-2.3-2.4-3.7-4.8-3.7s-5.3,2.6-5.3,6.7,2.1,6.7,5.3,6.7,4.2-1.3,4.7-3.4h2.7c-.7,3.6-3.4,5.8-7.5,5.8s-8.1-3.5-8.1-9.1Z\"\n            />\n            <path\n              class=\"f\"\n              d=\"m277.5,91.2c0-5.6,3.2-9.1,8.1-9.1s7,2.5,7.5,6.1h-2.7c-.5-2.3-2.4-3.7-4.8-3.7s-5.3,2.6-5.3,6.7,2.1,6.7,5.3,6.7,4.2-1.3,4.7-3.4h2.7c-.7,3.6-3.4,5.8-7.5,5.8s-8.1-3.5-8.1-9.1Z\"\n            />\n            <path\n              class=\"f\"\n              d=\"m312.6,100h-11.2v-17.6h11.2v2.4h-8.4v5.2h8v2.3h-8v5.5h8.4v2.4Z\"\n            />\n            <path\n              class=\"f\"\n              d=\"m323.2,95.1c.2,1.7,1.9,2.9,4.2,2.9s3.8-1.1,3.8-2.7-1-2.2-3.2-2.7l-2.3-.5c-3.2-.8-4.7-2.3-4.7-4.7s2.6-5.1,6.4-5.1,6.3,2.1,6.4,5.1h-2.7c-.2-1.8-1.6-2.8-3.7-2.8s-3.6,1.1-3.6,2.6.9,2,3.2,2.5l1.9.5c3.6.9,5.1,2.3,5.1,4.9s-2.6,5.4-6.8,5.4-6.5-2-6.7-5.2h2.7Z\"\n            />\n            <path\n              class=\"f\"\n              d=\"m344.3,95.1c.2,1.7,1.9,2.9,4.2,2.9s3.8-1.1,3.8-2.7-1-2.2-3.2-2.7l-2.3-.5c-3.2-.8-4.7-2.3-4.7-4.7s2.6-5.1,6.4-5.1,6.3,2.1,6.4,5.1h-2.7c-.2-1.8-1.6-2.8-3.7-2.8s-3.6,1.1-3.6,2.6.9,2,3.2,2.5l1.9.5c3.6.9,5.1,2.3,5.1,4.9s-2.6,5.4-6.8,5.4-6.5-2-6.7-5.2h2.7Z\"\n            />\n          </g>\n          <g>\n            <path\n              class=\"g\"\n              d=\"m120.1,75.9V5.5h17.9v31h.4l24-31h19.2l-24.6,31.3,26,39.2h-21l-18-27.5-6,7.6v19.9h-17.9Z\"\n            />\n            <path\n              class=\"g\"\n              d=\"m187.4,8.5c0-4.9,4-8.5,8.9-8.5s8.9,3.7,8.9,8.5-4,8.5-8.9,8.5-8.9-3.6-8.9-8.5Zm.2,13h17.3v54.3h-17.3V21.6Z\"\n            />\n            <path\n              class=\"g\"\n              d=\"m249.1,42.9c-.6-5.6-4.1-9.3-9.4-9.3s-10.2,5.6-10.2,15.1,3.8,15.1,10.2,15.1,8.7-3.2,9.4-8.9h15.9c-.5,13.7-10.3,22.1-25.5,22.1s-27.5-10.7-27.5-28.4,10.5-28.3,27.4-28.3,25,9,25.5,22.5h-15.9Z\"\n            />\n            <path\n              class=\"g\"\n              d=\"m305.5,21.6h19.3l-19.5,23.3,20.7,31.1h-19.9l-13.5-20.7-3.3,3.9v16.8h-17.3V2h17.3v40.4h.3l16-20.8Z\"\n            />\n            <path\n              class=\"g\"\n              d=\"m344.7,55.5c.6,4.8,6.1,8.1,12.7,8.1s11.5-3.1,11.5-7.4-3-5.8-11-7.5l-9.2-1.8c-13-2.5-19.7-9.5-19.7-20.2s11.7-22.5,28-22.5,28,8.5,28.2,21.8h-16.5c-.3-5.1-5.1-8.2-11.5-8.2s-10.4,2.8-10.4,7.1,3.1,5.9,10.5,7.4l9.1,1.7c13.9,2.7,20.2,9,20.2,20.1s-11.6,22.9-29.7,22.9-29.1-8-29.2-21.6h17Z\"\n            />\n            <path\n              class=\"g\"\n              d=\"m415.7,9.6v12h9.8v12.8h-9.8v23.2c0,3.8,2.1,5.7,6.3,5.7s2.5,0,3.5-.2v12.3c-1.7.4-4.2.7-7.6.7-14,0-19.5-4.3-19.5-15v-26.7h-7.3v-12.8h7.3v-12h17.3Z\"\n            />\n            <path\n              class=\"g\"\n              d=\"m430.9,60.2c0-10.2,7.8-15.8,22-16.7l12.7-.8v-3.1c0-4.4-3.2-6.8-8.1-6.8s-7.9,2.5-8.3,5.9h-15.6c.4-11,10.1-18.3,24.8-18.3s24.1,7.4,24.1,18.3v37.2h-16.8v-7.8h-.3c-2.9,5.5-9.6,8.5-16.2,8.5-10.2,0-18.4-6.7-18.4-16.5Zm34.8-3.9v-3.8l-10,.7c-4.9.3-7.6,2.4-7.6,5.8s2.9,5.6,7.3,5.6,10.3-3.3,10.3-8.3Z\"\n            />\n            <path\n              class=\"g\"\n              d=\"m491.2,21.6h16.8v10.2h.3c2-7.2,6.4-10.9,12.8-10.9s3.3.3,4.4.7v14.8c-1.5-.6-3.5-1-5.6-1-7.4,0-11.5,4.2-11.5,11.8v28.7h-17.3V21.6Z\"\n            />\n            <path\n              class=\"g\"\n              d=\"m552.6,9.6v12h9.8v12.8h-9.8v23.2c0,3.8,2.1,5.7,6.3,5.7s2.5,0,3.5-.2v12.3c-1.7.4-4.2.7-7.6.7-14,0-19.5-4.3-19.5-15v-26.7h-7.3v-12.8h7.3v-12h17.3Z\"\n            />\n          </g>\n        </g>\n      </svg>\n    </div>\n    <div class=\"kickstart-promo__content\">\n      <h2 class=\"kickstart-promo__title\">\n        AI-generated forms in seconds\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\">\n          <path\n            fill=\"currentColor\"\n            d=\"M320 0c-17.7 0-32 14.3-32 32s14.3 32 32 32h82.7L201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L448 109.3V192c0 17.7 14.3 32 32 32s32-14.3 32-32V32c0-17.7-14.3-32-32-32H320zM80 32C35.8 32 0 67.8 0 112V432c0 44.2 35.8 80 80 80H400c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32V432c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V112c0-8.8 7.2-16 16-16H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H80z\"\n          />\n        </svg>\n      </h2>\n      <p class=\"kickstart-promo__description\">\n        KickStart your next FormKit form in seconds. Generate from a prompt,\n        image, or text attachment. Copy & paste as Vue components or FormKit\n        schema.\n      </p>\n    </div>\n  </div>\n</template>\n\n<style scoped>\n.kickstart-promo {\n  background: #1f478d;\n  border: 1px solid #b1e2fd;\n  padding: 1rem;\n  border-radius: 0.5rem;\n  display: flex;\n  flex-direction: column;\n  position: relative;\n  max-width: 900px;\n  margin: 0 auto;\n  margin-bottom: 2em;\n  box-shadow: none;\n  transition: all 0.2s ease;\n  overflow: hidden;\n  cursor: pointer;\n}\n.kickstart-promo::before {\n  content: \"\";\n  position: absolute;\n  inset: 0;\n  z-index: 2;\n  filter: brightness(80%) contrast(100%);\n  mix-blend-mode: overlay;\n  background-size: cover;\n  background-image: radial-gradient(\n      circle at 50% 40%,\n      rgba(0, 0, 0, 0),\n      rgba(0, 0, 0, 0.25)\n    ),\n    url(\"data:image/svg+xml,%3Csvg viewBox='0 0 10000 10000' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E\");\n}\n.kickstart-promo:hover {\n  transform: translateY(-0.25rem);\n  box-shadow: rgba(50, 50, 93, 0.25) 0px 50px 100px -20px,\n    rgba(0, 0, 0, 0.3) 0px 30px 60px -30px;\n  filter: brightness(120%);\n}\n[data-dark-mode=\"true\"] .kickstart-promo {\n  background: #5d0667;\n  border: 1px solid #650a75;\n}\n@media (min-width: 768px) {\n  .kickstart-promo {\n    flex-direction: row;\n  }\n}\n@media (min-width: 960px) {\n  .kickstart-promo {\n    margin-bottom: -2em;\n  }\n}\n\n.announcement-icon {\n  position: absolute;\n  top: 0.5rem;\n  right: 0.5rem;\n  padding: 0.5rem;\n  background: #fff;\n  color: #1f2a8d;\n  border-radius: 50%;\n  font-size: 2rem;\n  width: 1rem;\n  height: 1rem;\n}\n.announcement-icon svg {\n  display: block;\n  width: 100%;\n  height: 100%;\n}\n\n.kickstart-promo__cta {\n  display: block;\n  width: 100%;\n  height: 100%;\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: 5;\n}\n\n.kickstart-promo__image {\n  width: 100%;\n  max-width: 10rem;\n  aspect-ratio: 523/191;\n  mix-blend-mode: screen;\n  position: relative;\n  z-index: 1;\n}\n.kickstart-promo__image svg {\n  width: 100%;\n  height: 100%;\n  filter: invert(1);\n}\n\n.kickstart-promo__content {\n  color: white;\n  padding: 1rem;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  position: relative;\n  z-index: 2;\n}\n@media (min-width: 768px) {\n  .kickstart-promo__content {\n    justify-content: flex-start;\n    align-items: flex-start;\n    padding-left: 2rem;\n  }\n}\n\n.kickstart-promo__title {\n  margin-top: 0.25rem;\n  margin-bottom: 0.1rem;\n  padding-top: 0;\n  font-size: 1.25rem;\n  color: #fff;\n  letter-spacing: -0.05rem;\n}\n@media (min-width: 768px) {\n  .kickstart-promo {\n    font-size: 1.33rem;\n  }\n}\n\n.kickstart-promo__title svg {\n  width: 0.75em;\n  height: 0.75em;\n  margin-left: 0.25em;\n}\n\n.kickstart-promo__description {\n  opacity: 0.66;\n  font-size: 0.85rem;\n}\n.kickstart-promo__content p:last-child {\n  margin-bottom: 0;\n}\n</style>\n"
  },
  {
    "path": "docs/src/components/Navigation.vue",
    "content": "<script setup lang=\"ts\">\nimport { ref } from \"vue\"\nimport IconDown from \"./IconDown.vue\"\nimport PoliteAd from \"./PoliteAd.vue\"\nimport { vAutoAnimate } from \"../../../src/index\"\n\nconst show = ref(false)\nconst activeTitle = ref(\"Docs Navigation\")\n\nfunction applySizing() {\n  if (window.innerWidth >= 672) {\n    show.value = true\n  } else {\n    show.value = false\n  }\n}\n\nconst handleClick = () => {\n  show.value = window.innerWidth >= 672\n}\n\nlet resizeTimer\nif (typeof window !== \"undefined\") {\n  show.value = window.innerWidth >= 672\n  clearTimeout(resizeTimer)\n  window.addEventListener(\"resize\", () => {\n    resizeTimer = setTimeout(applySizing, 200)\n  })\n}\n</script>\n\n<template>\n  <nav v-auto-animate>\n    <div class=\"active\" @click=\"show = !show\">\n      <span class=\"active-title\">{{ activeTitle }}</span>\n      <IconDown class=\"down\" />\n    </div>\n    <ul v-if=\"show\" @click=\"handleClick\">\n      <li><a href=\"#installation\">Installation</a></li>\n      <li><a href=\"#usage\">Usage</a></li>\n      <li><a href=\"#usage-react\">React</a></li>\n      <li><a href=\"#usage-vue\">Vue</a></li>\n      <li><a href=\"#usage-preact\">Preact</a></li>\n      <li><a href=\"#usage-solid\">Solid</a></li>\n      <li><a href=\"#usage-svelte\">Svelte</a></li>\n      <li><a href=\"#usage-angular\">Angular</a></li>\n      <!-- <li><a href=\"#usage-qwik\">Qwik</a></li> -->\n      <li><a href=\"#usage-disable\">Disable</a></li>\n      <li><a href=\"#examples\">Examples</a></li>\n      <li><a href=\"#plugins\">Plugins</a></li>\n      <li><a href=\"#why-not\">Why not...?</a></li>\n    </ul>\n  </nav>\n</template>\n\n<style scoped>\nnav {\n  background-color: white;\n  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.025), 0 2px 4px rgba(0, 0, 0, 0.025),\n    0 4px 8px rgba(0, 0, 0, 0.025), 0 8px 16px rgba(0, 0, 0, 0.025),\n    0 16px 32px rgba(0, 0, 0, 0.025), 0 32px 64px rgba(0, 0, 0, 0.025);\n  border-radius: 0 0 0.25em 0.25em;\n  padding: 0 2em;\n  position: sticky;\n  top: 0;\n  left: 100%;\n  opacity: 0.98;\n  float: left;\n  width: calc(100% + 2em);\n  margin-left: calc(-200vw);\n  margin-right: -3em;\n  z-index: 100;\n  user-select: none;\n}\n[data-dark-mode=\"true\"] nav {\n  background-color: var(--purple-d);\n}\n\n@media (min-width: 42em) {\n  nav {\n    top: 2em;\n    width: 10em;\n    margin-left: -12em;\n    margin-right: 0;\n    box-sizing: border-box;\n    margin-top: 0;\n    left: 0;\n    padding: 0 1em;\n    border-radius: 0.75em;\n  }\n  .active {\n    display: none;\n  }\n  nav ul {\n    border-top: none;\n  }\n  nav a {\n    padding-left: 0;\n    padding-right: 0;\n  }\n}\n\n@media (min-width: 50em) {\n  nav {\n    width: 14em;\n    margin-left: -18em;\n    padding: 0.5em 2em;\n  }\n}\n\nul {\n  list-style-type: none;\n  padding: 0.5em 0;\n  margin: 0;\n  position: relative;\n  z-index: 2;\n  border-top: 1px solid var(--ui-light);\n}\n[data-dark-mode=\"true\"] ul {\n  border-top: none;\n}\n\na,\n.active-title {\n  display: block;\n  padding: 0.5em 1em;\n  color: var(--primary);\n}\n[data-dark-mode=\"true\"] li a,\n[data-dark-mode=\"true\"] .active-title {\n  color: var(--text);\n}\n\nli:last-child {\n  padding-bottom: 0.5em;\n}\n\n.active-title {\n  padding: 0 1em;\n}\n.active {\n  padding-top: 0.75em;\n  padding-bottom: 0.75em;\n  cursor: pointer;\n  position: relative;\n  padding-right: 2em;\n}\n\n.down {\n  fill: var(--primary);\n  width: 1em;\n  position: absolute;\n  top: 50%;\n  transform: translateY(-50%);\n  right: 1em;\n}\n[data-dark-mode=\"true\"] .down {\n  fill: var(--text);\n}\n</style>\n"
  },
  {
    "path": "docs/src/components/PoliteAd.vue",
    "content": "<script setup>\n\n</script>\n\n<template>\n  <section class=\"polite-area\">\n    <div class=\"polite-header\">\n      <h5>Sponsored Ad</h5>\n      <a class=\"polite-sponsored\" href=\"https://formkit.com/ads\">Want this Spot? Current bid: $4/day →</a>\n    </div>\n    <a href=\"https://formvalidation.io/\">\n      <picture>\n        <source\n          srcset=\"https://cdn.formk.it/polite/FormValidation-1500x250.png\"\n          media=\"(min-width: 750px)\"\n        >\n        <source\n          srcset=\"https://cdn.formk.it/polite/FormValidation-750x250.png\"\n          media=\"(max-width: 749px)\"\n        >\n        <img class=\"polite-spot\" src=\"https://cdn.formk.it/polite/FormValidation-1500x250.png\" alt=\"FormValidation.io poster\" />\n      </picture>\n    </a>\n    <a v-if=\"false\" href=\"mailto:ads@formkit.com\">\n      <div class=\"your-logo-here\">\n        Your image here!<br>\n        <small>\n          Promote your product to a JavaScript audience — over 30,000 visitors in the last 30 days.<br>\n          Click through to contact for details.<br>\n          Current bid: $3/day\n        </small>\n      </div>\n    </a>\n  </section>\n</template>\n\n<style scoped>\nh5 {\n  margin-bottom: 0.25em;\n  margin-top: 0;\n}\n\n.polite-area {\n  margin-bottom: 2em;\n}\n\n.polite-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n}\n\n.polite-sponsored {\n  font-size: 11px;\n  text-transform: uppercase;\n  letter-spacing: .025em;\n  filter: grayscale(1);\n  transition: filter 0.15s ease-out;\n}\n\n.polite-sponsored:hover {\n  text-decoration: none;\n  color: var(--primary);\n  filter: grayscale(0);\n}\n.polite-spot {\n  max-width: 100%;\n  border-radius: 1em;\n}\n.your-logo-here {\n  border: dashed 2px var(--gray-l);\n  border-radius: 1em;\n  padding: 3em;\n  text-align: center;\n  margin-bottom: 2em;\n  aspect-ratio: 5/1;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n}\nsmall {\n  color: var(--text);\n  opacity: 0.66;\n}\n\n[data-dark-mode=\"true\"] .your-logo-here {\n  border-color: var(--gray-m);\n}\n\n@media (min-width: 42em) {\n  .polite-area {\n    margin-left: -12em;\n    margin-bottom: 6em;\n  }\n  .your-logo-here {\n    aspect-ratio: 12/1;\n    margin-bottom: 6em;\n  }\n}\n@media (min-width: 50em) {\n  .polite-area {\n    margin-left: -18em;\n  }\n}\n</style>\n"
  },
  {
    "path": "docs/src/components/ReducedMotionWarning.vue",
    "content": "<template>\n  <div class=\"motion-notice\">\n    <p>\n      <strong>Notice</strong>: Your current device has its \"reduce motion\" accessibility option enabled.\n      Out of respect for this setting AutoAnimate animations will not play.\n      Please disable your device's \"reduce motion\" setting if you wish to see AutoAnimate in action.\n    </p>\n  </div>\n</template>\n\n<style lang=\"css\">\n.motion-notice {\n  padding-top: 3em;\n}\n.motion-notice p {\n  display: block;\n  padding: 2em;\n  color: var(--ui-red);\n  border: 1px solid var(--ui-red);\n  border-radius: 0.5em;\n}\n@media (min-width: 42em) {\n  .motion-notice {\n    padding-top: 0;\n  }\n}\n</style>\n"
  },
  {
    "path": "docs/src/components/TempoPromo.vue",
    "content": "<template>\n  <div class=\"tempo-promo\">\n    <a\n      href=\"https://tempo.formkit.com/?utm_source=auto_animate_tempo_promo&utm_medium=web\"\n      target=\"_blank\"\n      class=\"tempo-promo__cta\"\n    />\n\n    <span class=\"announcement-icon\">\n      <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 576 512\">\n        <path\n          fill=\"currentColor\"\n          d=\"M544 32c17.7 0 32 14.3 32 32V448c0 17.7-14.3 32-32 32s-32-14.3-32-32V64c0-17.7 14.3-32 32-32zM64 190.3L480 64V448L348.9 408.2C338.2 449.5 300.7 480 256 480c-53 0-96-43-96-96c0-11 1.9-21.7 5.3-31.5L64 321.7C63.1 338.6 49.1 352 32 352c-17.7 0-32-14.3-32-32V192c0-17.7 14.3-32 32-32c17.1 0 31.1 13.4 32 30.3zm239 203.9l-91.6-27.8c-2.1 5.4-3.3 11.4-3.3 17.6c0 26.5 21.5 48 48 48c23 0 42.2-16.2 46.9-37.8z\"\n        />\n      </svg>\n    </span>\n\n    <div class=\"tempo-promo__image\" aria-label=\"Tempo\"></div>\n    <div class=\"tempo-promo__content\">\n      <h2 class=\"tempo-promo__title\">\n        The easiest way to work with dates in JavaScript\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\">\n          <path\n            fill=\"currentColor\"\n            d=\"M320 0c-17.7 0-32 14.3-32 32s14.3 32 32 32h82.7L201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L448 109.3V192c0 17.7 14.3 32 32 32s32-14.3 32-32V32c0-17.7-14.3-32-32-32H320zM80 32C35.8 32 0 67.8 0 112V432c0 44.2 35.8 80 80 80H400c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32V432c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V112c0-8.8 7.2-16 16-16H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H80z\"\n          />\n        </svg>\n      </h2>\n      <p class=\"tempo-promo__description\">\n        Tempo is a new open-source library by FormKit that makes working with\n        dates — including complex operations across timezones — a breeze. It’s\n        lightweight, fast, and easy to use.\n      </p>\n    </div>\n  </div>\n</template>\n\n<style scoped>\n.tempo-promo {\n  background: #5c1f8d;\n  border: 1px solid #b376e6;\n  padding: 1rem;\n  border-radius: 0.5rem;\n  display: flex;\n  flex-direction: column;\n  position: relative;\n  max-width: 900px;\n  margin: 0 auto;\n  margin-bottom: 2em;\n  box-shadow: none;\n  transition: all 0.2s ease;\n  overflow: hidden;\n  cursor: pointer;\n}\n.tempo-promo::before {\n  content: \"\";\n  position: absolute;\n  inset: 0;\n  z-index: 2;\n  filter: brightness(80%) contrast(100%);\n  mix-blend-mode: overlay;\n  background-size: cover;\n  background-image: radial-gradient(\n      circle at 50% 40%,\n      rgba(0, 0, 0, 0),\n      rgba(0, 0, 0, 0.25)\n    ),\n    url(\"data:image/svg+xml,%3Csvg viewBox='0 0 10000 10000' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E\");\n}\n.tempo-promo:hover {\n  transform: translateY(-0.25rem);\n  box-shadow: rgba(50, 50, 93, 0.25) 0px 50px 100px -20px,\n    rgba(0, 0, 0, 0.3) 0px 30px 60px -30px;\n  filter: brightness(120%);\n}\n[data-dark-mode=\"true\"] .tempo-promo {\n  background: #38085f;\n  border: 1px solid #450a75;\n}\n@media (min-width: 768px) {\n  .tempo-promo {\n    flex-direction: row;\n  }\n}\n@media (min-width: 960px) {\n  .tempo-promo {\n    margin-bottom: -2em;\n  }\n}\n\n.announcement-icon {\n  position: absolute;\n  top: 0.5rem;\n  right: 0.5rem;\n  padding: 0.5rem;\n  background: #fff;\n  color: #5c1f8d;\n  border-radius: 50%;\n  font-size: 2rem;\n  width: 1rem;\n  height: 1rem;\n}\n.announcement-icon svg {\n  display: block;\n  width: 100%;\n  height: 100%;\n}\n\n.tempo-promo__cta {\n  display: block;\n  width: 100%;\n  height: 100%;\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: 5;\n}\n\n.tempo-promo__image {\n  width: 100%;\n  max-width: 10rem;\n  aspect-ratio: 523/191;\n  mix-blend-mode: lighten;\n  position: relative;\n  z-index: 1;\n  background: url(\"/img/tempo.png\") no-repeat center center;\n  background-size: contain;\n}\n\n.tempo-promo__content {\n  color: white;\n  padding: 1rem;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  position: relative;\n  z-index: 2;\n}\n@media (min-width: 768px) {\n  .tempo-promo__content {\n    justify-content: flex-start;\n    align-items: flex-start;\n    padding-left: 2rem;\n  }\n}\n\n.tempo-promo__title {\n  margin-top: 0.25rem;\n  margin-bottom: 0.1rem;\n  padding-top: 0;\n  font-size: 1.25rem;\n  color: #fff;\n  letter-spacing: -0.05rem;\n}\n@media (min-width: 768px) {\n  .tempo-promo {\n    font-size: 1.33rem;\n  }\n}\n\n.tempo-promo__title svg {\n  width: 0.75em;\n  height: 0.75em;\n  margin-left: 0.25em;\n}\n\n.tempo-promo__description {\n  opacity: 0.66;\n  font-size: 0.85rem;\n}\n.tempo-promo__content p:last-child {\n  margin-bottom: 0;\n}\n</style>\n"
  },
  {
    "path": "docs/src/components/TheFooter.vue",
    "content": "<script setup>\nconst year = new Date().getFullYear()\n</script>\n\n<template>\n  <footer>\n    <p class=\"copyright\">\n      Released under the <a href=\"https://opensource.org/licenses/MIT\">MIT License</a>.<br>\n      Copyright &copy; {{ year }} FormKit, Inc.\n    </p>\n  </footer>\n</template>\n\n<style scoped>\nfooter {\n  display: flex;\n  justify-content: center;\n  width: 100%;\n  margin-top: 6em;\n  border-top: 1px solid rgba(0, 0, 0, 0.1);\n}\n[data-dark-mode=\"true\"] footer {\n  border-top-color: rgba(255, 255, 255, 0.15);\n}\n.copyright {\n  display: inline-block;\n  font-size: 0.8rem;\n  margin: 0 auto;\n  padding: 3em 2em;\n  text-align: center;\n}\n</style>\n"
  },
  {
    "path": "docs/src/components/TheHeader.vue",
    "content": "<script setup lang=\"ts\">\nimport TheLogo from \"./TheLogo.vue\"\nimport IconDiscord from \"./IconDiscord.vue\"\nimport IconTwitter from \"./IconTwitter.vue\"\nimport ColorModeToggle from \"./ColorModeToggle.vue\"\nimport GithubButton from \"vue-github-button\"\n</script>\n\n<template>\n  <header>\n    <TheLogo class=\"logo\" />\n    <div class=\"buttons\">\n      <a href=\"https://discord.gg/SHYT8pyeNm\" aria-label=\"Discord\"\n        ><IconDiscord class=\"icon\"\n      /></a>\n      <a href=\"https://twitter.com/FormKit\" aria-label=\"Twitter\"\n        ><IconTwitter class=\"icon\"\n      /></a>\n      <GithubButton\n        href=\"https://github.com/formkit/auto-animate\"\n        data-size=\"large\"\n        data-show-count=\"true\"\n        data-icon=\"star\"\n        aria-label=\"Star formkit/auto-animate on GitHub\"\n        >Star</GithubButton\n      >\n      <GithubButton\n        href=\"https://github.com/sponsors/formkit\"\n        data-size=\"large\"\n        data-icon=\"heart\"\n        aria-label=\"Sponsor FormKit on GitHub\"\n        >Sponsor</GithubButton\n      >\n      <ColorModeToggle />\n    </div>\n  </header>\n</template>\n\n<style scoped>\nheader {\n  height: 4em;\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  padding-top: 2em;\n  margin-bottom: 6em;\n  justify-content: space-between;\n  user-select: none;\n}\n.logo {\n  width: 90%;\n  max-width: 15em;\n  margin-right: 1em;\n  margin-bottom: 1em;\n}\n\n.buttons {\n  display: flex;\n  align-items: center;\n}\n\n.buttons a:hover {\n  text-decoration: none;\n}\n\n.icon {\n  width: 1.25em;\n  fill: var(--gray-m);\n  margin-right: 1em;\n}\n[data-dark-mode=\"true\"] .icon {\n  fill: var(--gray-l);\n}\n\n.buttons span {\n  margin-right: 1em;\n}\n.buttons span:last-child {\n  margin-right: 0;\n}\n\n.buttons a:hover .icon {\n  fill: var(--primary);\n}\n\n@media (min-width: 700px) {\n  header {\n    padding-top: 1em;\n    margin-bottom: 3em;\n  }\n  .logo {\n    max-width: 12em;\n    margin-bottom: 0;\n  }\n}\n</style>\n"
  },
  {
    "path": "docs/src/components/TheLogo.vue",
    "content": "<script setup lang=\"ts\">\nimport { onMounted, ref } from \"vue\"\nimport { vAutoAnimate } from \"../../../src\"\n\nconst sequence = ref(0)\n\nconst duration = 50\n\nonMounted(() => {\n  const interval = setInterval(() => {\n    sequence.value++\n    if (sequence.value > 11) {\n      clearInterval(interval)\n    }\n  }, duration)\n})\n</script>\n\n<template>\n  <svg\n    id=\"logo\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n    viewBox=\"0 0 2493.5 306.9\"\n  >\n    <defs>\n      <linearGradient\n        id=\"linear-gradient\"\n        x1=\"0\"\n        y1=\"145.3\"\n        x2=\"300\"\n        y2=\"145.3\"\n        gradientTransform=\"translate(0 302.2) scale(1 -1)\"\n        gradientUnits=\"userSpaceOnUse\"\n      >\n        <stop offset=\"0\" stop-color=\"#4711de\" />\n        <stop offset=\".29\" stop-color=\"#4811da\" />\n        <stop offset=\".6\" stop-color=\"#4a11cd\" />\n        <stop offset=\".92\" stop-color=\"#4e11b8\" />\n        <stop offset=\"1\" stop-color=\"#4f11b1\" />\n      </linearGradient>\n    </defs>\n    <g id=\"letters\" class=\"cls-3\">\n      <path\n        v-if=\"sequence > 0\"\n        class=\"cls-2\"\n        d=\"M426.6,13.1h45.3l89.9,242.9h-39.4l-19-52.7h-108.9l-19,52.7h-38.4L426.6,13.1Zm65,156.5l-41.5-114.5h-2.4l-41.5,114.5h85.4Z\"\n      />\n      <path\n        v-if=\"sequence > 1\"\n        class=\"cls-2\"\n        d=\"M583.6,191.2V74.6h36.3v110c0,25.7,17.6,42.3,40.4,42.3,25.2,0,43.9-19.4,43.9-45.8V74.6h36.3v181.5h-35.3v-24.3h-2.4c-7.3,14.9-25.9,28.8-52.9,28.8-41.1,0-66.3-30.2-66.3-69.4Z\"\n      />\n      <path\n        v-if=\"sequence > 2\"\n        class=\"cls-2\"\n        d=\"M803.4,198.4V104.4h-32.5v-29.5h23.9c6.2,0,10.7-4.5,10.7-11.8V23.2h34.2v51.4h52.5v29.8h-52.5v93c0,17.3,7.3,27.8,26.3,27.8h24.5v30.9h-30.1c-35.5-.1-57-19.8-57-57.7Z\"\n      />\n      <path\n        v-if=\"sequence > 3\"\n        class=\"cls-2\"\n        d=\"M914.8,165.5c0-54.5,40.1-95.4,95.8-95.4s95.1,41.3,95.1,95.4-40.1,95.1-95.4,95.1-95.5-40.6-95.5-95.1Zm153.8,0c0-36.1-24.2-62.1-58.4-62.1s-58.4,26.4-58.4,62.1,24.5,62.1,58.4,62.1,58.4-26.4,58.4-62.1Z\"\n      />\n      <path\n        v-if=\"sequence > 4\"\n        class=\"cls-2\"\n        d=\"M1215,13.1h45.3l89.9,242.9h-39.4l-19-52.7h-108.9l-19,52.7h-38.4L1215,13.1Zm65,156.5l-41.4-114.5h-2.4l-41.5,114.5h85.3Z\"\n      />\n      <path\n        v-if=\"sequence > 5\"\n        class=\"cls-2\"\n        d=\"M1376.5,74.6h35.3v24.3h2.4c7.3-14.9,25.9-28.8,52.9-28.8,41.1,0,66.4,30.2,66.4,69.4v116.6h-36.3v-109.7c0-25.3-17.6-42.3-40.4-42.3-25.2,0-43.9,19.8-43.9,46.2v105.8h-36.3l-.1-181.5h0Z\"\n      />\n      <path\n        v-if=\"sequence > 6\"\n        class=\"cls-2\"\n        d=\"M1572.2,22.9c0-13.2,10-22.9,23.9-22.9s24.2,9.7,24.2,22.9c0,13.9-10,23.2-24.2,23.2s-23.9-9.4-23.9-23.2Zm5.5,51.7h36.3v181.5h-36.3V74.6Z\"\n      />\n      <path\n        v-if=\"sequence > 7\"\n        class=\"cls-2\"\n        d=\"M1658.2,74.6h35.3v23.9h2.4c8-15.3,24.9-28.5,50.8-28.5s44.9,12.1,55.3,30.5h2.4c12.1-21.5,31.1-30.5,55.7-30.5,39.8,0,66,27.8,66,67v119h-36.3v-106.5c0-29.5-14.2-45.5-38-45.5s-41.1,17-41.1,46.2v105.8h-36.3v-110.6c0-25.3-16.2-41.3-39.1-41.3s-40.8,18.4-40.8,45.1v106.9h-36.3V74.6Z\"\n      />\n      <path\n        v-if=\"sequence > 8\"\n        class=\"cls-2\"\n        d=\"M1957.6,165.8c0-56.2,35.6-95.8,86.1-95.8,34.6,0,53.2,21.5,59.1,30.9h2.4v-26.4h36.3V256h-35.5v-25.7h-2.1c-4.8,6.9-22.5,30.2-58.4,30.2-51.9,.1-87.9-38.5-87.9-94.7Zm148.7-.3c0-39.2-24.2-62.1-56-62.1s-55.7,25.7-55.7,62.1,22.8,62.1,56,62.1,55.7-27.8,55.7-62.1Z\"\n      />\n      <path\n        v-if=\"sequence > 9\"\n        class=\"cls-2\"\n        d=\"M2203.4,198.4V104.4h-32.5v-29.5h23.9c6.2,0,10.7-4.5,10.7-11.8V23.2h34.2v51.4h52.5v29.8h-52.5v93c0,17.3,7.3,27.8,26.3,27.8h24.5v30.9h-30.1c-35.5-.1-57-19.8-57-57.7Z\"\n      />\n      <path\n        v-if=\"sequence > 10\"\n        class=\"cls-2\"\n        d=\"M2314.8,165.8c0-57.6,38.7-95.8,90.2-95.8,56.4,0,88.5,41.6,88.5,93.3v13.5h-142.4c1.4,31.6,22.5,53.1,55.7,53.1,22.8,0,42.5-11.1,49.8-28.8h35.3c-9.3,35-41.5,59.3-86.1,59.3-54.4,.2-91-40.1-91-94.6Zm142.7-17c-2.4-30.5-24.5-48.2-52.5-48.2s-50.5,19.8-53.2,48.2h105.7Z\"\n      />\n    </g>\n    <path\n      id=\"mark\"\n      class=\"cls-1\"\n      d=\"M300,256.9v50H150v-50h150ZM50,206.9v50H0v-50H50Zm50-50v50H50v-50h50Zm50-50v50h-50v-50h50Zm-50-50v50H50V56.9h50ZM50,6.9V56.9H0V6.9H50Z\"\n    />\n  </svg>\n</template>\n\n<style scoped>\n.cls-1 {\n  fill: url(#linear-gradient);\n  fill-rule: evenodd;\n}\n\n.cls-2 {\n  fill: #111;\n}\n[data-dark-mode=\"true\"] .cls-2 {\n  fill: #eee;\n}\n\n.cls-3 {\n  isolation: isolate;\n}\n</style>\n"
  },
  {
    "path": "docs/src/examples/accordion/ActualAccordion.vue",
    "content": "<script setup lang=\"ts\">\nimport { ref } from \"vue\"\nimport { vAutoAnimate } from \"../../../../src\"\n\nconst faq = ref([\n  {\n    id: \"why\",\n    question: \"Why did you create it?\",\n    answer: `We needed a way to animate DOM elements without adding to the\n    virtual DOM in Vue and React. And it turned out our solution was gonna work\n    great for lots of other use cases as well.`,\n  },\n  {\n    id: \"license\",\n    question: \"Is it open source?\",\n    answer: `It sure is! AutoAnimate is MIT licensed, which basically means you\n    are free to take it and do whatever you want with it. If you find it useful\n    consider <a href=\"https://github.com/sponsors/formkit\">supporting our open source efforts</a>.`,\n  },\n  {\n    id: \"who\",\n    question: \"Who made this?\",\n    answer: `AutoAnimate is a team effort 💪 . Most of the code was written by\n      <a href='https://twitter.com/jpschroeder'>Justin Schroeder</a>,\n      <a href='https://twitter.com/0xBOYD'>Andrew Boyd</a> played a key\n      role on the docs, and the rest of the <a href='https://formkit.com'>FormKit</a>\n      team held down the fort.`,\n  },\n])\n\nconst current = ref(faq.value[0].id)\n</script>\n\n<template>\n  <div class=\"example accordion-example\">\n    <ul class=\"accordion\">\n      <li class=\"accordion-item\" v-for=\"q in faq\" :key=\"q.id\" v-auto-animate>\n        <div\n          class=\"question\"\n          @click=\"current = current === q.id ? false : q.id\"\n        >\n          {{ q.question }}\n        </div>\n        <p class=\"answer\" v-if=\"q.id === current\" v-html=\"q.answer\" />\n      </li>\n    </ul>\n  </div>\n</template>\n\n<style scoped>\nul {\n  list-style-type: none;\n  margin: 0;\n  padding: 0;\n}\n\nli {\n  display: block;\n  background-color: var(--ui-light);\n  border-radius: 0.5em;\n  overflow: hidden;\n}\n[data-dark-mode=\"true\"] li {\n  background-color: var(--purple-d);\n}\n\n.question {\n  padding: 1em;\n  display: flex;\n  align-items: center;\n  cursor: pointer;\n}\n.question::before {\n  content: \"Q\";\n  font-size: 1.5em;\n  margin-right: 0.5em;\n  color: var(--primary);\n}\n[data-dark-mode=\"true\"] .question::before {\n  color: var(--purple-m);\n}\n.answer {\n  margin: 0 1rem;\n  padding: 0 0 1rem 0;\n  font-size: 0.875em;\n}\n</style>\n"
  },
  {
    "path": "docs/src/examples/angular/ActualAngularApp.vue",
    "content": "<script setup>\nimport { vAutoAnimate } from \"../../../../src/index\"\nimport { ref } from \"vue\"\n\nconst storyA = {title: 'The Ant and The Grasshopper', showStory: false, story: \"The ant and the grasshopper were good friends. In the summer, the ant works hard to fill his storage with food. While the grasshopper was enjoying the fine weather and playing all day. When winter came, the ant was lying cozily in his home surrounded by the food he stored during the summer. While the grasshopper was in his home, hungry and freezing. He asked the ant for food and the ant gave him some. But it wasn’t enough to last the entire winter. When he tried to ask the ant again, the latter replied: \\\"I'm sorry my friend but my food is just enough for my family to last until the end of winter. If I give you more, we too will starve. We had the entire summer to prepare for the winter but you chose to play instead.\\\"\"}\nconst storyB = {title: 'The Boy Who Cried Wolf', showStory: false, story: \"There was once a shepherd boy who liked to play tricks. One day, while he was watching over the herd, the boy decided to play a trick and cried “wolf! wolf!”. The people who heard rushed over to help him. But they were disappointed when they saw that there was no wolf and the boy was laughing at them. The next day, he did it again and people rushed to his aid only to be disappointed once again. On the third day, the boy saw a wolf devouring one of his sheep and cried for help. But the people who heard him thought this is just another of the boy’s pranks so no one came to help him. That day, the boy lost some of his sheep to the wolf.\"}\nconst stories = ref([storyA, storyB]);\n\n</script>\n\n<template>\n  <div class=\"example angular-example grid\">\n    <div v-for=\"(story, key) in stories\" :key=\"key\">\n      <div class=\"story-card\" v-auto-animate>\n        <h2>{{ story.title }}</h2>\n        <div v-if=\"story.showStory\">{{ story.story }}</div>\n        <button class=\"button toggle-story-btn\" @click=\"story.showStory = !story.showStory\">Toggle story</button>\n      </div>\n    </div>\n  </div>\n</template>\n\n<style scoped>\n.grid { display: grid; grid-template-columns: repeat(2, 1fr); grid-gap: 10px; font-size: 0.875em; }\n.story-card { background-color: var(--purple-md); padding: 0.75em; border-radius: 0.5em; box-shadow: 0 0 0.5em rgb(0 0 0 / 10%); }\n.story-card h2 { margin: 0; padding: 0; font-size: 1.5em}\n.toggle-story-btn { margin: 1em 0 0; }\n</style>\n"
  },
  {
    "path": "docs/src/examples/angular/index.ts",
    "content": "const angularDirectiveMain = {\n  angular: {\n    ext: \"angular\",\n    language: \"jsx\",\n    example: `import { Component } from '@angular/core';\nimport { AutoAnimateDirective } from '@formkit/auto-animate/angular';\n\n@Component({\n  selector: 'app-root',\n  standalone: true,\n  imports: [AutoAnimateDirective],\n  template: \\`\n    @for (story of stories; track story.title) {\n      <div>\n        <div class=\"story-card\" auto-animate>\n          <h2>{{ story.title }}</h2>\n          @if (story.showStory) {\n            <div>{{ story.story }}</div>\n          }\n          <button (click)=\"story.showStory = !story.showStory\">Toggle story</button>\n        </div>\n      </div>\n    }\n  \\`,\n})\nexport class AppComponent {\n  readonly stories = [\n    {title: 'The Ant and The Grasshopper', showStory: false, story: \"The ant and the grasshopper were good friends...\"},\n    {title: 'The Boy Who Cried Wolf', showStory: false, story: \"There was once a shepherd boy who liked to play tricks...\"},\n  ];\n}\n`,\n  },\n}\nexport { angularDirectiveMain }\n"
  },
  {
    "path": "docs/src/examples/basic/basic.ts",
    "content": "import autoAnimate from \"@formkit/auto-animate\"\n\nautoAnimate(Element)\n"
  },
  {
    "path": "docs/src/examples/basic/index.ts",
    "content": "import example from \"./basic.ts?raw\"\n\nexport default {\n  ts: {\n    language: \"typescript\",\n    ext: \"ts\",\n    example,\n  },\n}\n"
  },
  {
    "path": "docs/src/examples/boxes/ActualBoxes.vue",
    "content": "<script setup lang=\"ts\">\nimport { ref } from \"vue\"\nimport { vAutoAnimate } from \"../../../../src\"\n\nconst numbers = ref<number[]>(new Array(50).fill(\"\").map((_, i) => i))\nconst randomize = () => {\n  numbers.value.sort(() => (Math.random() > 0.5 ? 1 : -1))\n}\n</script>\n\n<template>\n  <div class=\"example boxes-example\">\n    <div class=\"boxes\" v-auto-animate=\"{ duration: 500 }\">\n      <div\n        class=\"box\"\n        v-for=\"number in numbers\"\n        :key=\"number\"\n        v-text=\"number\"\n      />\n    </div>\n    <button class=\"button button--alt\" @click=\"randomize\">Randomize</button>\n  </div>\n</template>\n\n<style scoped>\n.boxes {\n  display: flex;\n  flex-wrap: wrap;\n  margin: 2em -0.25em 2em -0.25em;\n}\n\n.box {\n  box-sizing: border-box;\n  width: calc(10% - 0.5em);\n  margin: 0.25em;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 0.875em;\n  font-weight: 300;\n  aspect-ratio: 1;\n  border: 1px solid var(--gray-l);\n}\n</style>\n"
  },
  {
    "path": "docs/src/examples/cards/ActualCards.vue",
    "content": "<script setup>\nimport { ref } from \"vue\"\nimport { vAutoAnimate } from \"../../../../src/index\"\n\nconst showForm = ref(false)\nconst cards = ref([\n  {\n    id: Math.random(),\n    title: \"Employee Lunch\",\n    date: \"March 31st\",\n    description: \"Join us for an all hands meeting.\",\n  },\n  {\n    id: Math.random(),\n    title: \"Engineering scrum\",\n    date: \"April 10th\",\n    description: \"Engineering team is playing rugby.\",\n  },\n])\n\nfunction createCard(card) {\n  cards.value.unshift(card)\n  setTimeout(() => {\n    showForm.value = false\n  }, 300)\n}\n</script>\n\n<template>\n  <div class=\"example card-example\" v-auto-animate>\n    <button\n      class=\"button button--alt\"\n      v-if=\"!showForm\"\n      @click=\"showForm = true\"\n    >\n      + Add Event\n    </button>\n    <div class=\"form\" v-if=\"showForm\">\n      <FormKit\n        type=\"form\"\n        submit-label=\"Add Event\"\n        @submit=\"createCard\"\n        :submit-attrs=\"{ inputClass: 'button button--alt' }\"\n      >\n        <FormKit name=\"title\" label=\"Title\" value=\"New years party\" />\n        <FormKit name=\"date\" label=\"Date\" value=\"January 1st\" />\n        <FormKit\n          name=\"description\"\n          type=\"textarea\"\n          label=\"Description\"\n          value=\"We’ll all sing yuletide carols.\"\n        />\n      </FormKit>\n    </div>\n    <ul class=\"cards\" v-auto-animate>\n      <li v-for=\"card in cards\" :key=\"card.id\" class=\"card\">\n        <h4 class=\"title\">{{ card.title }}</h4>\n        <div class=\"date\">{{ card.date }}</div>\n        <div class=\"description\">{{ card.description }}</div>\n      </li>\n    </ul>\n  </div>\n</template>\n\n<style scoped>\n.cards {\n  list-style-type: none;\n  padding: 0;\n  margin: 0.5em -0.5em;\n  display: flex;\n  flex-wrap: wrap;\n}\n\n.example {\n  margin-top: 2em;\n}\n.card {\n  display: block;\n  padding: 1em;\n  border-radius: 0.5em;\n  background: linear-gradient(\n    to bottom,\n    rgba(0, 0, 255, 0.02),\n    rgba(0, 0, 255, 0.07)\n  );\n  border: 2px solid rgba(0, 0, 255, 0.1);\n  box-shadow: 0 0 1em rgba(0, 0, 0, 0.1);\n  box-sizing: border-box;\n  width: 100%;\n  margin: 0.5em;\n}\n[data-dark-mode=\"true\"] .card {\n  background: linear-gradient(\n    to bottom,\n    rgba(170, 170, 239, 0.2),\n    rgba(91, 91, 228, 0.1)\n  );\n  border: 2px solid rgba(159, 159, 254, 0.1);\n}\n\n@media (min-width: 38em) {\n  .card {\n    width: calc(50% - 1em);\n    flex: calc(50% - 1em) 0 0;\n  }\n}\n\n@media (min-width: 61em) {\n  .card {\n    width: calc(33% - 3em);\n    flex: calc(33% - 3em) 0 0;\n  }\n}\n\n.title {\n  margin: 0;\n}\n.date {\n  color: var(--gray-m);\n  font-weight: 300;\n  font-size: 0.75em;\n  margin-bottom: 1em;\n}\n\n.description {\n  font-size: 0.875em;\n}\n.cards li::before {\n  display: none;\n}\n</style>\n"
  },
  {
    "path": "docs/src/examples/config/index.ts",
    "content": "export default {\n  js: {\n    language: \"typescript\",\n    title: \"MyComponent.ts\",\n    example: `autoAnimate(el, {\n  // Animation duration in milliseconds (default: 250)\n  duration: 250,\n  // Easing for motion (default: 'ease-in-out')\n  easing: 'ease-in-out'\n  // When true, this will enable animations even if the user has indicated\n  // they don’t want them via prefers-reduced-motion.\n  disrespectUserMotionPreference: false\n})`,\n  },\n}\n"
  },
  {
    "path": "docs/src/examples/disable/ActualDisable.vue",
    "content": "<script setup lang=\"ts\">\nimport { ref } from \"vue\"\nimport { useAutoAnimate } from \"../../../../src/vue\"\n\nconst balls = ref([\"red\", \"green\", \"blue\"])\nconst [parent, enable] = useAutoAnimate({ duration: 500 })\n\nlet isEnabled = ref(true)\nfunction toggle() {\n  isEnabled.value ? enable(false) : enable(true)\n  isEnabled.value = !isEnabled.value\n}\nsetInterval(() => {\n  balls.value.push(balls.value.shift()!)\n}, 600)\n</script>\n\n<template>\n  <ul class=\"balls\" ref=\"parent\">\n    <li v-for=\"color in balls\" :key=\"color\" :class=\"color\">\n      {{ color }}\n    </li>\n  </ul>\n  <button @click=\"toggle\" class=\"button button--alt\" id=\"disable\">\n    {{ isEnabled ? \"🚫 Disable\" : \"✅ Enable\" }} animations\n  </button>\n</template>\n\n<style scoped>\n.balls {\n  aspect-ratio: 1;\n  margin: 2em auto;\n  width: 100%;\n  max-width: 15em;\n  position: relative;\n  padding: 0;\n  background-size: contain;\n  background-repeat: none;\n}\n\nsection > ul.balls > li {\n  width: 4em;\n  height: 4em;\n  border-radius: 4em;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 0.875em;\n  font-weight: 500;\n  color: white;\n  margin-bottom: 0;\n}\n\n.balls li::before {\n  display: none;\n  margin-bottom: 0;\n}\n\n.balls li:first-child {\n  position: absolute;\n  top: 0;\n  left: calc(50% - 2em);\n}\n\n.balls li:nth-child(2) {\n  position: absolute;\n  bottom: 0;\n  right: 0;\n}\n\n.balls li:nth-child(3) {\n  position: absolute;\n  bottom: 0;\n  left: 0;\n}\n\n.actions {\n  margin-bottom: 1em;\n}\n.red {\n  background-color: #eb2e1d;\n}\n.blue {\n  background-color: #5411ef;\n}\n.green {\n  background-color: #4fd726;\n}\n\nbutton {\n  display: block;\n  margin-left: auto;\n  margin-right: auto;\n}\n</style>\n"
  },
  {
    "path": "docs/src/examples/disable/disable.html",
    "content": "<ul class=\"balls\" id=\"juggle\">\n  <li class=\"red\">Red</li>\n  <li class=\"green\">Green</li>\n  <li class=\"blue\">Blue</li>\n</ul>\n<button id=\"toggle\">🚫 Disable animations</button>\n\n<script type=\"module\">\n  import autoAnimate from \"./js/autoAnimate.js\";\n  const parent = document.getElementById(\"juggle\");\n  const toggleEl = document.getElementById(\"toggle\");\n  const controller = autoAnimate(juggle, { duration: 500 });\n  setInterval(() => {\n    parent.appendChild(parent.children[0]);\n  }, 600);\n\n  toggleEl.addEventListener(\"click\", () => {\n    controller.isEnabled() ? controller.disable() : controller.enable();\n    toggleEl.innerHTML = controller.isEnabled()\n      ? \"🚫 Disable animations\"\n      : \"✅ Enable animations\";\n  });\n</script>\n"
  },
  {
    "path": "docs/src/examples/disable/disable.jsx",
    "content": "import { useEffect, useState } from 'react'\nimport { useAutoAnimate } from '@formkit/auto-animate/react'\n\nfunction Juggle() {\n  const [balls, setBalls] = useState(['red', 'green', 'blue'])\n  const [isEnabled, setIsEnabled] = useState(true)\n  const [parent, enable] = useAutoAnimate({ duration: 500 })\n  function toggle () {\n    enable(!isEnabled)\n    setIsEnabled(!isEnabled)\n  }\n  useEffect(() => {\n    setTimeout(() => {\n      const juggled = [...balls]\n      juggled.push(juggled.shift())\n      setBalls(juggled)\n    }, 600)\n  }, [balls])\n  return (\n    <>\n      <ul ref={parent} className=\"balls\">\n        { balls.map(color => <li key={color} className={color}>{ color }</li>) }\n      </ul>\n      <button onClick={toggle}>\n        { isEnabled ? \"🚫 Disable\" : \"✅ Enable\" } animations\n      </button>\n    </>\n  )\n}\n\nexport default Juggle\n"
  },
  {
    "path": "docs/src/examples/disable/disable.vue",
    "content": "<script setup>\nimport { ref } from \"vue\"\nimport { useAutoAnimate } from \"../../../../src/vue\"\n\nconst balls = ref([\"red\", \"green\", \"blue\"])\nconst [parent, enable] = useAutoAnimate({ duration: 500 })\n\nlet isEnabled = ref(true)\nfunction toggle() {\n  isEnabled.value ? enable(false) : enable(true)\n  isEnabled.value = !isEnabled.value\n}\nsetInterval(() => {\n  balls.value.push(balls.value.shift()!)\n}, 600)\n</script>\n\n<template>\n  <ul class=\"balls\" ref=\"parent\">\n    <li v-for=\"color in balls\" :key=\"color\" :class=\"color\">\n      {{ color }}\n    </li>\n  </ul>\n  <button @click=\"toggle\" class=\"button button--alt\">\n    {{ isEnabled ? \"🚫 Disable\" : \"✅ Enable\" }} animations\n  </button>\n</template>\n"
  },
  {
    "path": "docs/src/examples/disable/index.ts",
    "content": "import vueExample from \"./disable.vue?raw\"\nimport reactExample from \"./disable.jsx?raw\"\n// import htmlExample from \"./disable.html?raw\"\n\nexport default {\n  react: {\n    example: reactExample,\n    ext: \"jsx\",\n    language: \"jsx\",\n  },\n  vue: {\n    ext: \"vue\",\n    language: \"html\",\n    example: vueExample,\n  },\n  // js: {\n  //   example: htmlExample,\n  //   ext: \"html\",\n  //   language: \"html\",\n  // },\n}\n"
  },
  {
    "path": "docs/src/examples/dropdown/ActualDropdown.vue",
    "content": "<script setup>\nimport { ref, onMounted } from \"vue\"\nimport autoAnimate from \"../../../../src/index\"\n\nconst dropdown = ref() // we need a DOM node\nconst show = ref(false)\n\nonMounted(() => {\n  autoAnimate(dropdown.value) // thats it!\n})\n</script>\n\n<template>\n  <div ref=\"dropdown\" class=\"dropdown\">\n    <strong class=\"dropdown-label\" @click=\"show = !show\"\n      >Click me to open!</strong\n    >\n    <p class=\"dropdown-content\" v-if=\"show\">\n      Sea of Tranquility a mote of dust suspended in a sunbeam hundreds of\n      thousands concept of the number one realm of the galaxies radio telescope.\n      As a patch of light descended from astronomers two ghostly white figures\n      in coveralls and helmets are softly dancing emerged into consciousness\n      Orion's sword encyclopaedia galactica. Another world bits of moving fluff\n      network of wormholes muse about network of wormholes with pretty stories\n      for which there's little good evidence and billions upon billions upon\n      billions upon billions upon billions upon billions upon billions.\n    </p>\n  </div>\n</template>\n\n<style scoped>\n.dropdown {\n  border: 2px solid var(--gray-l);\n  border-radius: 0.5em;\n  background-color: white;\n  margin-bottom: 2em;\n}\n[data-dark-mode=\"true\"] .dropdown {\n  border-color: var(--purple-md);\n  background-color: var(--purple-d);\n}\n.dropdown strong {\n  display: block;\n  padding: 1em;\n  cursor: pointer;\n}\n.dropdown p {\n  margin: 1em;\n}\n</style>\n"
  },
  {
    "path": "docs/src/examples/dropdown/dropdown-solid.tsx",
    "content": "import { createAutoAnimate } from \"@formkit/auto-animate/solid\"\nimport { createSignal, Show } from \"solid-js\"\n\nconst Dropdown = () => {\n  const [show, setShow] = createSignal(false)\n  const reveal = () => setShow(!show())\n\n  const [parent, setEnabled] = createAutoAnimate(/* optional config */)\n\n  return (\n    <div ref={parent}>\n      <strong class=\"dropdown-label\" onClick={reveal}>\n        Click me to open!\n      </strong>\n      <Show when={show()} keyed>\n        <p class=\"dropdown-content\">Lorum ipsum...</p>\n      </Show>\n    </div>\n  )\n}\n\nexport default Dropdown\n"
  },
  {
    "path": "docs/src/examples/dropdown/dropdown.html",
    "content": "<div id=\"dropdown\" class=\"dropdown\">\n  <strong class=\"dropdown-label\" onclick=\"toggle\">\n    Click me to open!\n  </strong>\n</div>\n\n<script type=\"module\">\n  import autoAnimate from './js/autoAnimate.js'\n  const dropdown = document.getElementById('dropdown')\n  autoAnimate(dropdown)\n\n  const p = document.createElement('p')\n  p.innerText = \"Lorum ipsum...\"\n\n  function toggle () {\n    dropdown.contains(p) ? p.remove() : dropdown.appendChild(p)\n  }\n</script>\n"
  },
  {
    "path": "docs/src/examples/dropdown/dropdown.jsx",
    "content": "import { useState, useRef, useEffect } from 'react'\nimport autoAnimate from '@formkit/auto-animate'\n\nconst Dropdown = () => {\n  const [show, setShow] = useState(false)\n  const parent = useRef(null)\n\n  useEffect(() => {\n    parent.current && autoAnimate(parent.current)\n  }, [parent])\n\n  const reveal = () => setShow(!show)\n\n  return <div ref={parent}>\n    <strong className=\"dropdown-label\" onClick={reveal}>Click me to open!</strong>\n    { show && <p className=\"dropdown-content\" >Lorum ipsum...</p> }\n  </div>\n}\n\nexport default Dropdown\n"
  },
  {
    "path": "docs/src/examples/dropdown/dropdown.vue",
    "content": "<script setup>\nimport { ref, onMounted } from \"vue\"\nimport autoAnimate from \"@formkit/auto-animate\"\n\nconst dropdown = ref() // we need a DOM node\nconst show = ref(false)\n\nonMounted(() => {\n  autoAnimate(dropdown.value) // thats it!\n})\n</script>\n\n<template>\n  <div ref=\"dropdown\" class=\"dropdown\">\n    <strong class=\"dropdown-label\" @click=\"show = !show\">\n      Click me to open!\n    </strong>\n    <p class=\"dropdown-content\" v-if=\"show\">Lorum ipsum...</p>\n  </div>\n</template>\n"
  },
  {
    "path": "docs/src/examples/dropdown/index.ts",
    "content": "import vueExample from \"./dropdown.vue?raw\"\nimport reactExample from \"./dropdown.jsx?raw\"\nimport solidExample from \"./dropdown-solid.tsx?raw\"\nimport nativeExample from \"./dropdown.html?raw\"\nexport default {\n  react: {\n    language: \"jsx\",\n    ext: \"jsx\",\n    example: reactExample,\n  },\n  solid: {\n    language: \"tsx\",\n    ext: \"tsx\",\n    example: solidExample,\n  },\n  vue: {\n    language: \"html\",\n    ext: \"vue\",\n    example: vueExample,\n  },\n  js: {\n    language: \"html\",\n    ext: \"html\",\n    example: nativeExample,\n  },\n}\n"
  },
  {
    "path": "docs/src/examples/first-demo/ActualDemo.vue",
    "content": "<script setup>\nimport FrameworkList from \"./FrameworkList.vue\"\n</script>\n\n<template>\n  <div class=\"comparison-app\">\n    <FrameworkList>\n      Native&nbsp;JavaScript&nbsp;😐\n    </FrameworkList>\n    <FrameworkList :animated=\"true\">\n      AutoAnimate&nbsp;🤩\n    </FrameworkList>\n  </div>\n</template>\n\n<style lang=\"css\">\n.comparison-app {\n  display: flex;\n  flex-direction: column;\n  margin-top: 2em;\n}\n@media (min-width: 48em) {\n  .comparison-app {\n    margin-top: 2em;\n    flex-direction: row;\n    margin-bottom: -1.25em;\n  }\n}\n\n.comparison-app ul {\n  list-style-type: none;\n  padding: 0;\n}\n\n.comparison-app .stage {\n  margin: 0 0.5em 2em 0.5em;\n  flex-grow: 1;\n}\n\n.comparison-app .stage:not([data-has-animation=\"true\"]) ul {\n  opacity: 0.85;\n  filter: grayscale(0.85)\n}\n.comparison-app .stage:not([data-has-animation=\"true\"]) li:last-child button {\n  opacity: 0.66;\n}\n.comparison-app .stage:not([data-has-animation=\"true\"]) li:last-child button[data-disabled=\"true\"] {\n  opacity: 0.33;\n}\n\n.comparison-app li {\n  padding: 0.75em;\n  background-color: #e3e9ff;\n  border-radius: 0.5em;\n  margin-bottom: 0.5em;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n}\n[data-dark-mode=\"true\"] .comparison-app li {\n  background-color: var(--purple-lm);\n  color: #444;\n}\n\nli:first-child .action-icons button:first-child,\n.comparison-app li:nth-last-child(2) .action-icons button:nth-last-child(2) {\n  opacity: 0.1;\n  pointer-events: none;\n}\n\n.comparison-app li svg {\n  width: 1em;\n  fill: #4711de;\n}\n\n.comparison-app li svg.down {\n  transform: rotate(180deg);\n  fill: #4711de;\n}\n\n.comparison-app li > span {\n  text-overflow: ellipsis;\n  width: 100%;\n  flex-grow: 1;\n}\n\n.comparison-app .action-icons {\n  display: flex;\n  align-items: center;\n}\n\n.comparison-app .action-icons button {\n  appearance: none;\n  border: 0;\n  padding: 0;\n  background: transparent;\n  border-radius: 0;\n  margin-left: 0.75em;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n}\n\n.comparison-app li input {\n  appearance: none;\n  border: 1px solid #4711de;\n  border-radius: 0.25em;\n  background-color: rgba(255, 255, 255, 0.4);\n  padding: 0.5em;\n  flex-grow: 1;\n  min-width: none;\n  width: 100%;\n  display: block;\n}\n\n.comparison-app li input:focus {\n  outline: 1px solid #4711de;\n}\n\n.comparison-app li button {\n  appearance: none;\n  border: 1px solid #4711de;\n  background-color: #4711de;\n  border-radius: 0.25em;\n  color: white;\n  font-weight: 600;\n  padding: 0.5em 1em;\n  margin-left: 1em;\n  cursor: pointer;\n}\n\n.comparison-app li:last-child button[data-disabled=\"true\"] {\n  opacity: 0.33;\n  pointer-events: none;\n  cursor: not-allowed;\n}\n\n.comparison-app li form {\n  display: flex;\n  flex-grow: 1;\n  align-items: center;\n  justify-content: space-between;\n}\n\n.comparison-app .comparison {\n  justify-content: space-between;\n  margin: 0 -0.5em;\n}\n\n.comparison-app .logo {\n  width: calc(100% - 1em);\n  max-height: 1.1em;\n  margin: 0 auto;\n  text-align: center;\n}\n.comparison-app .logo img {\n  display: block;\n  width: 100%;\n  height: 100%;\n}\n\n.comparison-app .stage:not([data-has-animation]) .logo {\n  opacity: 0.75;\n  position: relative;\n  aspect-ratio: 7.008108;\n  text-align: center;\n}\n\n.comparison-app .action-icons .remove {\n  margin-left: 1.5em;\n}\n\n.comparison-app .action-icons .remove svg {\n  fill: red;\n}\n</style>\n"
  },
  {
    "path": "docs/src/examples/first-demo/Arrow.vue",
    "content": "<script setup>\ndefineProps({\n  direction: {\n    type: String,\n    default: \"\",\n  },\n})\n</script>\n\n<template>\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    viewBox=\"0 0 24 24\"\n    :class=\"direction\"\n  >\n    <path\n      d=\"M13.64,6.59l7.54,7.54c.91,.91,.91,2.38,0,3.29-.44,.44-1.03,.68-1.64,.68H4.46c-1.28,0-2.32-1.04-2.32-2.32,0-.62,.24-1.21,.68-1.64l7.54-7.54c.91-.91,2.38-.91,3.29,0h0Z\"\n    />\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/examples/first-demo/Close.vue",
    "content": "<template>\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    viewBox=\"0 0 60 60\"\n  >\n    <path d=\"M34.2,30l13.5-13.4c.6-.6,.9-1.4,.9-2.2s-.4-1.5-.9-2c-1.3-1.1-3.2-1.1-4.2,0l-13.5,13.4-13.4-13.5c-.6-.6-1.4-.9-2.2-.9s-1.5,.4-2,.9c-.5,.6-.8,1.3-.8,2.1,0,.9,.3,1.6,.9,2.1l13.3,13.5-13.5,13.4c-.6,.6-.9,1.4-.9,2.2s.4,1.5,.9,2c.6,.5,1.3,.8,2.1,.8s1.6-.3,2.1-.9l13.5-13.3,13.4,13.5c.6,.6,1.3,.9,2.1,.9h.1c.8,0,1.5-.4,2-.9,1.1-1.2,1.1-3.1,.1-4.2l-13.5-13.5Z\" />\n  </svg>\n</template>\n"
  },
  {
    "path": "docs/src/examples/first-demo/FrameworkList.vue",
    "content": "<script setup>\nimport { ref, onMounted, computed } from \"vue\"\nimport autoAnimate from \"../../../../src/index\"\nimport Arrow from \"./Arrow.vue\"\nimport Close from \"./Close.vue\"\n\nconst props = defineProps({\n  animated: {\n    type: Boolean,\n    default: false,\n  },\n})\n\nconst list = ref()\nconst inputValue = ref(\"AdonisJs\")\nconst frameworks = ref([\n  { id: 0, name: \"Nuxt.js\" },\n  { id: 1, name: \"Next.js\" },\n  { id: 2, name: \"SvelteKit\" },\n  { id: 3, name: \"Remix\" },\n  { id: 4, name: \"Meteor\" },\n])\n\nconst disableSort = computed(() => {\n  let isSorted = true\n  if (frameworks.value[0]) {\n    let currentValue = frameworks.value[0].name\n    frameworks.value.forEach((framework) => {\n      if (currentValue > framework.name) {\n        isSorted = false\n      }\n      currentValue = framework.name\n    })\n  }\n  return isSorted\n})\n\nconst addItem = (e) => {\n  e.preventDefault()\n  frameworks.value.push({ id: Math.random(), name: inputValue.value })\n  inputValue.value = \"\"\n}\n\nconst sortUp = (item) => {\n  const index = frameworks.value.findIndex((i) => item.id === i.id)\n  if (index) {\n    frameworks.value.splice(index - 1, 0, ...frameworks.value.splice(index, 1))\n  }\n}\n\nconst sortDown = (item) => {\n  const index = frameworks.value.findIndex((i) => item.id === i.id)\n  if (index < frameworks.value.length - 1) {\n    frameworks.value.splice(index + 1, 0, ...frameworks.value.splice(index, 1))\n  }\n}\n\nconst sortList = () => {\n  frameworks.value = [...frameworks.value].sort((a, b) => {\n    const nameA = a.name.toLowerCase().trim()\n    const nameB = b.name.toLowerCase().trim()\n    return nameA < nameB ? -1 : nameA > nameB ? 1 : 0\n  })\n}\n\nconst remove = (item) => {\n  frameworks.value = frameworks.value.filter((i) => i.id !== item.id)\n}\n\nonMounted(() => {\n  if (props.animated) {\n    autoAnimate(list.value)\n  }\n})\n</script>\n\n<template>\n  <div className=\"stage\" :data-has-animation=\"animated\">\n    <div className=\"logo\">\n      <slot />\n    </div>\n    <ul ref=\"list\">\n      <li v-for=\"item in frameworks\" :key=\"item.id\">\n        <span>{{ item.name }}</span>\n        <div className=\"action-icons\">\n          <button @click=\"sortUp(item)\">\n            <Arrow direction=\"up\" />\n          </button>\n          <button @click=\"sortDown(item)\">\n            <Arrow direction=\"down\" />\n          </button>\n          <button className=\"remove\" @click=\"remove(item)\">\n            <Close />\n          </button>\n        </div>\n      </li>\n      <li>\n        <form @submit=\"addItem\">\n          <input\n            placeholder=\"Add another...\"\n            type=\"text\"\n            v-model=\"inputValue\"\n          />\n          <button type=\"submit\">Add</button>\n          <button\n            type=\"button\"\n            @click=\"sortList\"\n            :data-disabled=\"disableSort\"\n            :disabled=\"disableSort\"\n          >\n            Sort\n          </button>\n        </form>\n      </li>\n    </ul>\n  </div>\n</template>\n"
  },
  {
    "path": "docs/src/examples/formkit/ActualFormKit.vue",
    "content": "<script setup>\nimport { ref, onMounted } from \"vue\"\nimport autoAnimate from \"../../../../src\"\n\nconst example = ref()\n\nonMounted(() => {\n  example.value.querySelectorAll(\".formkit-outer\").forEach(autoAnimate)\n})\n\nconst submit = () => {\n  alert(\"Success!\")\n}\n</script>\n\n<template>\n  <div class=\"example formkit-example\" ref=\"example\">\n    <FormKit\n      type=\"form\"\n      :submit-attrs=\"{ inputClass: 'button' }\"\n      @submit=\"submit\"\n    >\n      <FormKit\n        type=\"text\"\n        label=\"Username\"\n        help=\"Pick a new username.\"\n        validation=\"required|length:5,15|matches:/[0-9]/\"\n        validation-visibility=\"dirty\"\n        :validation-messages=\"{\n          matches: 'Must include at least one number',\n        }\"\n      />\n      <FormKit\n        type=\"password\"\n        label=\"Password\"\n        name=\"password\"\n        help=\"Pick a new password.\"\n        validation=\"required|length:5,15|matches:/[0-9]/\"\n        validation-visibility=\"dirty\"\n        :validation-messages=\"{\n          matches: 'Must include at least one number',\n        }\"\n      />\n      <FormKit\n        type=\"password\"\n        name=\"password_confirm\"\n        label=\"Confirm password\"\n        help=\"Pick a new password.\"\n        validation=\"required|confirm\"\n        validation-label=\"password confirmation\"\n        validation-visibility=\"dirty\"\n        :validation-messages=\"{\n          matches: 'Must include at least one number',\n        }\"\n      />\n    </FormKit>\n  </div>\n</template>\n\n<style scoped>\n.example {\n  box-shadow: 0 0 1em rgba(0, 0, 0, 0.1);\n  padding: 2em;\n  display: block;\n  border-radius: 0.5em;\n  width: 100%;\n  box-sizing: border-box;\n  max-width: 24.375em;\n  margin: 2em auto;\n}\n[data-dark-mode=\"true\"] .example {\n  background: var(--purple-d);\n}\n\n[data-dark-mode=\"true\"] .example:deep(.formkit-help) {\n  color: var(--gray-l)\n}\n[data-dark-mode=\"true\"] .example:deep(.formkit-message[data-message-type=\"validation\"]),\n[data-dark-mode=\"true\"] .example:deep(.formkit-message[data-message-type=\"ui\"]) {\n  color: var(--ui-red)\n}\n\n.example:deep(button) {\n  margin-bottom: 0;\n}\n\n.example:deep(.formkit-outer[data-type=\"submit\"]) {\n  margin-bottom: 0;\n}\n\n.example:deep(input) {\n  width: 100%;\n  box-sizing: border-box;\n}\n.example:deep(.formkit-messages + .formkit-actions) {\n  margin-top: 1em;\n}\n</style>\n"
  },
  {
    "path": "docs/src/examples/installation/index.ts",
    "content": "export default {\n  yarn: {\n    example: \"yarn add @formkit/auto-animate\",\n    language: \"shell\",\n    title: \"~/my-app\",\n  },\n  npm: {\n    example: \"npm install @formkit/auto-animate\",\n    language: \"shell\",\n    title: \"~/my-app\",\n  },\n  pnpm: {\n    example: \"pnpm add @formkit/auto-animate\",\n    language: \"shell\",\n    title: \"~/my-app\",\n  },\n  bun:{\n    example:\"bun install @formkit/auto-animate\",\n    language:\"shell\",\n    title:\"~/my-app\",\n  },\n}\n"
  },
  {
    "path": "docs/src/examples/intro/index.ts",
    "content": "import vueExample from \"./intro.vue?raw\"\nimport reactExample from \"./intro.jsx?raw\"\nimport preactExample from \"./intro-preact.jsx?raw\"\nimport solidExample from \"./intro-solid.jsx?raw\"\nimport htmlExample from \"./intro.html?raw\"\nimport svelteExample from \"./intro.svelte?raw\"\nimport angularExample from \"./intro.angular?raw\"\n\nexport default {\n  react: {\n    example: reactExample,\n    ext: \"jsx\",\n    language: \"jsx\",\n  },\n  vue: {\n    ext: \"vue\",\n    language: \"html\",\n    example: vueExample,\n  },\n  solid: {\n    example: solidExample,\n    ext: \"jsx\",\n    language: \"jsx\",\n  },\n  preact: {\n    example: preactExample,\n    ext: \"jsx\",\n    language: \"jsx\",\n  },\n  svelte: {\n    example: svelteExample,\n    ext: \"svelte\",\n    language: \"html\",\n  },\n  angular: {\n    example: angularExample,\n    ext: \"angular\",\n    language: \"html\",\n  },\n  js: {\n    example: htmlExample,\n    ext: \"html\",\n    language: \"html\",\n  },\n}\n"
  },
  {
    "path": "docs/src/examples/intro/intro-preact.jsx",
    "content": "import { useAutoAnimate } from '@formkit/auto-animate/preact'\n\nfunction MyList () {\n  const [animationParent] = useAutoAnimate()\n  return (\n    <ul ref={animationParent}>\n      {/* 🪄 Magic animations for your list */}\n    </ul>\n  )\n}\n"
  },
  {
    "path": "docs/src/examples/intro/intro-solid.jsx",
    "content": "import { createAutoAnimate } from '@formkit/auto-animate/solid'\n\nfunction MyList () {\n  const [animationParent] = createAutoAnimate()\n  return (\n    <ul ref={animationParent}>\n      {/* 🪄 Magic animations for your list */}\n    </ul>\n  )\n}\n"
  },
  {
    "path": "docs/src/examples/intro/intro.angular",
    "content": "<ul auto-animate>\n  <!-- 🪄 Magic animations for your list -->\n</ul>\n"
  },
  {
    "path": "docs/src/examples/intro/intro.html",
    "content": "<ul id=\"myList\">\n  <!-- 🪄 Magic animations for your list -->\n</ul>\n\n<script type=\"module\">\n  import autoAnimate from 'https://cdn.jsdelivr.net/npm/@formkit/auto-animate'\n  autoAnimate(document.getElementById('myList'))\n</script>\n"
  },
  {
    "path": "docs/src/examples/intro/intro.jsx",
    "content": "import { useAutoAnimate } from '@formkit/auto-animate/react'\n\nfunction MyList () {\n  const [animationParent] = useAutoAnimate()\n  return (\n    <ul ref={animationParent}>\n      {/* 🪄 Magic animations for your list */}\n    </ul>\n  )\n}\n"
  },
  {
    "path": "docs/src/examples/intro/intro.svelte",
    "content": "<script>\n  import autoAnimate from \"@formkit/auto-animate\"\n</script>\n\n<ul use:autoAnimate>\n  <!-- 🪄 Magic animations for your list -->\n</ul>\n"
  },
  {
    "path": "docs/src/examples/intro/intro.vue",
    "content": "<template>\n  <ul v-auto-animate>\n    <!-- 🪄 Magic animations for your list -->\n  </ul>\n</template>\n"
  },
  {
    "path": "docs/src/examples/list/ActualList.vue",
    "content": "<script setup lang=\"ts\">\nimport { onMounted, ref } from \"vue\"\nimport autoAnimate from \"../../../../src\"\nimport IconRemove from \"../../components/IconRemove.vue\"\n\nconst items = ref([\"🍎 Apple\", \"🍌 Banana\", \"🍒 Cherry\"])\n\nconst list = ref<HTMLElement>()\n\nconst fruitBasket = [\n  \"🍓 Strawberry\",\n  \"🥥 Coconut\",\n  \"🥝 Kiwi\",\n  \"🍇 Grape\",\n  \"🍉 Watermelon\",\n  \"🍍 Pineapple\",\n  \"🍐 Pear\",\n  \"🍑 Peach\",\n  \"🫐 Blueberry\",\n  \"🍊 Orange\",\n  \"🥭 Mango\",\n]\n\nconst remove = (item) => {\n  items.value = items.value.filter((fruit) => {\n    if (fruit === item) {\n      fruitBasket.push(fruit)\n      return false\n    }\n    return true\n  })\n}\n\nconst add = () => {\n  if (fruitBasket.length) {\n    items.value.splice(\n      Math.round(Math.random() * items.value.length - 1),\n      0,\n      fruitBasket.shift()\n    )\n  } else {\n    alert(\"Out of fruit!\")\n  }\n}\n\nconst randomize = () => items.value.sort(() => (Math.random() > 0.5 ? 1 : -1))\n\nonMounted(() => autoAnimate(list.value))\n</script>\n\n<template>\n  <div class=\"example list-example\">\n    <ul ref=\"list\">\n      <li v-for=\"item in items\" :key=\"item\">\n        <span>{{ item }}</span>\n        <button @click.prevent=\"remove(item)\" aria-label=\"Remove Fruit\">\n          <IconRemove />\n        </button>\n      </li>\n    </ul>\n\n    <button class=\"button button--add button--alt\" @click=\"add\">+ Add Fruit</button>\n    <button class=\"button button--random button--alt\" @click=\"randomize\">Randomize</button>\n  </div>\n</template>\n\n<style scoped>\nul {\n  list-style-type: none;\n  padding: 0;\n  max-width: 300px;\n}\n\nli {\n  display: flex;\n  justify-content: space-between;\n  padding: 0.75em;\n  background-color: white;\n  margin-bottom: 0.5em;\n  border-radius: 0.5em;\n  box-shadow: 0 0 0.5em rgba(0, 0, 0, 0.1);\n  font-size: 0.875em;\n}\n[data-dark-mode=\"true\"] li {\n  background-color: var(--purple-md);\n}\n\nli::before {\n  display: none;\n}\n\nli button {\n  appearance: none;\n  border: none;\n  padding: none;\n  margin: none;\n  background-color: transparent;\n  display: flex;\n  align-items: center;\n  cursor: pointer;\n}\n\nli button svg {\n  width: 1.2em;\n  fill: red;\n}\n[data-dark-mode=\"true\"] li button svg {\n  fill: rgb(244, 67, 67);\n}\n</style>\n"
  },
  {
    "path": "docs/src/examples/plugin/ActualPlugin.vue",
    "content": "<script setup lang=\"ts\">\nimport { onMounted, ref } from \"vue\"\nimport autoAnimate, { getTransitionSizes } from \"../../../../src\"\nimport IconRemove from \"../../components/IconRemove.vue\"\n\nconst items = ref([\"🍎 Apple\", \"🍌 Banana\", \"🍒 Cherry\"])\n\nconst list = ref<HTMLElement>()\n\nconst fruitBasket = [\n  \"🍓 Strawberry\",\n  \"🥥 Coconut\",\n  \"🥝 Kiwi\",\n  \"🍇 Grape\",\n  \"🍉 Watermelon\",\n  \"🍍 Pineapple\",\n  \"🍐 Pear\",\n  \"🍑 Peach\",\n  \"🫐 Blueberry\",\n  \"🍊 Orange\",\n  \"🥭 Mango\",\n]\n\nconst remove = (item) => {\n  items.value = items.value.filter((fruit) => {\n    if (fruit === item) {\n      fruitBasket.push(fruit)\n      return false\n    }\n    return true\n  })\n}\n\nconst add = () => {\n  if (fruitBasket.length) {\n    items.value.splice(\n      Math.round(Math.random() * items.value.length - 1),\n      0,\n      fruitBasket.shift()\n    )\n  } else {\n    alert(\"Out of fruit!\")\n  }\n}\n\nconst randomize = () => items.value.sort(() => (Math.random() > 0.5 ? 1 : -1))\n\nonMounted(() => autoAnimate(list.value, (el, action, oldCoords, newCoords) => {\n  let keyframes\n  if (action === 'add') {\n    keyframes = [\n      { transform: 'scale(0)', opacity: 0 },\n      { transform: 'scale(1.15)', opacity: 1, offset: 0.75 },\n      { transform: 'scale(1)', opacity: 1 }\n    ]\n  }\n  if (action === 'remove') {\n    keyframes = [\n      { transform: 'scale(1)', opacity: 1 },\n      { transform: 'scale(1.15)', opacity: 1, offset: 0.33 },\n      { transform: 'scale(0.75)', opacity: 0.1, offset: 0.5 },\n      { transform: 'scale(0.5)', opacity: 0 }\n    ]\n  }\n  if (action === 'remain') {\n    const deltaX = oldCoords.left - newCoords.left\n    const deltaY = oldCoords.top - newCoords.top\n    const [widthFrom, widthTo, heightFrom, heightTo] = getTransitionSizes(el, oldCoords, newCoords)\n    const start = { transform: `translate(${deltaX}px, ${deltaY}px)` }\n    const mid = { transform: `translate(${deltaX * -0.15}px, ${deltaY * -0.15}px)`, offset: 0.75 }\n    const end = { transform: `translate(0, 0)` }\n    if (widthFrom !== widthTo) {\n      start.width = `${widthFrom}px`\n      mid.width = `${widthFrom >= widthTo ? widthTo / 1.05 : widthTo * 1.05}px`\n      end.width = `${widthTo}px`\n    }\n    if (heightFrom !== heightTo) {\n      start.height = `${heightFrom}px`\n      mid.height = `${heightFrom >= heightTo ? heightTo / 1.05 : heightTo * 1.05}px`\n      end.height = `${heightTo}px`\n    }\n    keyframes = [start, mid, end]\n  }\n  return new KeyframeEffect(el, keyframes, { duration: 600, easing: 'ease-out' })\n}))\n</script>\n\n<template>\n  <div class=\"example\">\n    <h3>List example with \"bouncy\" keyframes</h3>\n    <ul ref=\"list\">\n      <li v-for=\"item in items\" :key=\"item\">\n        <span>{{ item }}</span>\n        <button @click.prevent=\"remove(item)\" aria-label=\"Remove Fruit\"><IconRemove /></button>\n      </li>\n    </ul>\n\n    <button class=\"button button--alt\" @click=\"add\">+ Add Fruit</button>\n    <button class=\"button button--alt\" @click=\"randomize\">Randomize</button>\n  </div>\n</template>\n\n<style scoped>\nul {\n  list-style-type: none;\n  padding: 0;\n  max-width: 300px;\n}\n\nli {\n  display: flex;\n  justify-content: space-between;\n  padding: 0.75em;\n  background-color: white;\n  margin-bottom: 0.5em;\n  border-radius: 0.5em;\n  box-shadow: 0 0 0.5em rgba(0, 0, 0, 0.1);\n  font-size: 0.875em;\n}\n[data-dark-mode=\"true\"] li {\n  background-color: var(--purple-md);\n}\n\nli::before {\n  display: none;\n}\n\nli button {\n  appearance: none;\n  border: none;\n  padding: none;\n  margin: none;\n  background-color: transparent;\n  display: flex;\n  align-items: center;\n  cursor: pointer;\n}\n\nli button svg {\n  width: 1.2em;\n  fill: red;\n}\n[data-dark-mode=\"true\"] li button svg {\n  fill: rgb(244, 67, 67);\n}\n</style>\n"
  },
  {
    "path": "docs/src/examples/plugin/index.ts",
    "content": "const pluginMain = {\n  js: {\n    ext: \"js\",\n    language: \"javascript\",\n    example: `import autoAnimate, { getTransitionSizes } from '@formkit/auto-animate'\n\nautoAnimate(parentElement, (el, action, oldCoords, newCoords) => {\n  let keyframes\n  // supply a different set of keyframes for each action\n  if (action === 'add') {\n    keyframes = [\n      { transform: 'scale(0)', opacity: 0 },\n      { transform: 'scale(1.15)', opacity: 1, offset: 0.75 },\n      { transform: 'scale(1)', opacity: 1 }\n    ]\n  }\n  // keyframes can have as many \"steps\" as you prefer\n  // and you can use the 'offset' key to tune the timing\n  if (action === 'remove') {\n    keyframes = [\n      { transform: 'scale(1)', opacity: 1 },\n      { transform: 'scale(1.15)', opacity: 1, offset: 0.33 },\n      { transform: 'scale(0.75)', opacity: 0.1, offset: 0.5 },\n      { transform: 'scale(0.5)', opacity: 0 }\n    ]\n  }\n  if (action === 'remain') {\n    // for items that remain, calculate the delta\n    // from their old position to their new position\n    const deltaX = oldCoords.left - newCoords.left\n    const deltaY = oldCoords.top - newCoords.top\n    // use the getTransitionSizes() helper function to\n    // get the old and new widths of the elements\n    const [widthFrom, widthTo, heightFrom, heightTo] = getTransitionSizes(el, oldCoords, newCoords)\n    // set up our steps with our positioning keyframes\n    const start = { transform: \\`translate(\\${deltaX}px, \\${deltaY}px)\\` }\n    const mid = { transform: \\`translate(\\${deltaX * -0.15}px, \\${deltaY * -0.15}px)\\`, offset: 0.75 }\n    const end = { transform: \\`translate(0, 0)\\` }\n    // if the dimensions changed, animate them too.\n    if (widthFrom !== widthTo) {\n      start.width = \\`\\${widthFrom}px\\`\n      mid.width = \\`\\${widthFrom >= widthTo ? widthTo / 1.05 : widthTo * 1.05}px\\`\n      end.width = \\`\\${widthTo}px\\`\n    }\n    if (heightFrom !== heightTo) {\n      start.height = \\`\\${heightFrom}px\\`\n      mid.height = \\`\\${heightFrom >= heightTo ? heightTo / 1.05 : heightTo * 1.05}px\\`\n      end.height = \\`\\${heightTo}px\\`\n    }\n    keyframes = [start, mid, end]\n  }\n  // return our KeyframeEffect() and pass\n  // it the chosen keyframes.\n  return new KeyframeEffect(el, keyframes, { duration: 600, easing: 'ease-out' })\n}))\n`,\n  },\n}\n\nexport default pluginMain\n"
  },
  {
    "path": "docs/src/examples/preact/ActualPreactApp.vue",
    "content": "<script setup>\nimport { useAutoAnimate } from \"../../../../src/vue/index.ts\"\nimport { ref } from \"vue\"\nconst [parent] = useAutoAnimate()\nconst items = ref([\"🎁\", \"📦\", \"🚚\", \"💪\", \"🐽\", \"🐸\", \"🐻\", \"🪱\", \"🪳\"])\nfunction cycle() {\n  items.value.unshift(items.value.pop())\n}\n</script>\n\n<template>\n  <div class=\"example preact-example\">\n    <ul ref=\"parent\">\n      <li v-for=\"item in items\" :key=\"item\">{{ item }}</li>\n    </ul>\n  </div>\n  <button @click=\"cycle\" class=\"button button--alt\">Cycle Emoji</button>\n</template>\n\n<style scoped>\nul {\n  list-style-type: none;\n  padding: 0;\n}\nli {\n  display: inline-block;\n  margin: 0.5em;\n}\nli::before {\n  display: none;\n}\n</style>\n"
  },
  {
    "path": "docs/src/examples/preact/index.ts",
    "content": "export default {\n  preact: {\n    language: \"jsx\",\n    ext: \"jsx\",\n    example: `import { useState } from 'preact/hooks'\nimport { useAutoAnimate } from '@formkit/auto-animate/preact'\n\nconst App = function () {\n  const [items, setItems] = useState([\"🎁\", \"📦\", \"🚚\", \"💪\", \"🐽\", \"🐸\", \"🐻\", \"🪱\", \"🪳\"])\n  const [parent] = useAutoAnimate(/* optional config */)\n\n  function cycle() {\n    items.unshift(items.pop())\n    setItems(items)\n  }\n\n  return <>\n  <ul ref={parent}>\n    {items.map(\n      item => <li key={item}>{ item }</li>\n    )}\n  </ul>\n  <button onClick={cycle}>Cycle Emoji</button>\n</>\n}\n\nexport default App`,\n  },\n}\n"
  },
  {
    "path": "docs/src/examples/qwik/index.ts",
    "content": "export default {\n  qwik: {\n    language: \"jsx\",\n    ext: \"jsx\",\n    example: `import { useSignal } from '@builder.io/qwik'\nimport { useAutoAnimate } from '@formkit/auto-animate/qwik'\n\nconst App = function () {\n  const items = useSignal([0, 1, 2])\n  const [parentRef, enableAnimations$] = useAutoAnimate(/* optional config */)\n  return <>\n  <ul ref={parentRef}>\n    {items.value.map(\n      item => <li key={item}>{ item }</li>\n    )}\n  </ul>\n  <button onClick$={() => (items.value = [...items.value, items.value.length])}>Add number</button>\n  <button onClick$={() => enableAnimations$(false)}>Disable</button>\n</>\n}\n\nexport default App`,\n  },\n}\n"
  },
  {
    "path": "docs/src/examples/react/ActualReactApp.vue",
    "content": "<script setup>\nimport { useAutoAnimate } from \"../../../../src/vue/index.ts\"\nimport { ref } from \"vue\"\nconst [parent, setEnabled] = useAutoAnimate()\nconst items = ref([0, 1, 2])\n</script>\n\n<template>\n  <div class=\"example react-example\">\n    <ul ref=\"parent\">\n      <li v-for=\"item in items\" :key=\"item\">{{ item }}</li>\n    </ul>\n    <button @click=\"items.push(items.length)\" class=\"button button--alt add\">\n      Add number\n    </button>\n    <button @click=\"setEnabled(false)\" class=\"button button--alt\">\n      Disable\n    </button>\n  </div>\n</template>\n\n<style scoped>\nul {\n  list-style-type: disc;\n}\nli::before {\n  display: none;\n}\n</style>\n"
  },
  {
    "path": "docs/src/examples/react/index.ts",
    "content": "export default {\n  react: {\n    language: \"jsx\",\n    ext: \"jsx\",\n    example: `import { useState } from 'react'\nimport { useAutoAnimate } from '@formkit/auto-animate/react'\n\nconst App = function () {\n  const [items, setItems] = useState([0, 1, 2])\n  const [parent, enableAnimations] = useAutoAnimate(/* optional config */)\n  const add = () => setItems([...items, items.length])\n  return <>\n  <ul ref={parent}>\n    {items.map(\n      item => <li key={item}>{ item }</li>\n    )}\n  </ul>\n  <button onClick={add}>Add number</button>\n  <button onClick={() => enableAnimations(false)}>Disable</button>\n</>\n}\n\nexport default App`,\n  },\n}\n"
  },
  {
    "path": "docs/src/examples/solid/ActualSolidApp.vue",
    "content": "<script setup>\nimport { useAutoAnimate } from \"../../../../src/vue/index.ts\"\nimport { ref } from \"vue\"\nconst [parent] = useAutoAnimate()\nconst menuItems = [\"Home\", \"Settings\", \"Logout\"]\nconst isExpanded = ref(true)\n</script>\n\n<template>\n  <div class=\"example solid-example\">\n    <div class=\"parent\" ref=\"parent\">\n      <ul v-if=\"isExpanded\" class=\"drawer\">\n        <li v-for=\"item of menuItems\" key=\"item\" class=\"item\">{{ item }}</li>\n      </ul>\n      <div class=\"content\">\n        <button\n          class=\"button button--alt\"\n          type=\"button\"\n          @click=\"isExpanded = !isExpanded\"\n        >\n          Toggle Drawer\n        </button>\n      </div>\n    </div>\n  </div>\n</template>\n\n<style scoped>\n.content {\n  padding: 2rem;\n}\n\n.drawer {\n  padding-right: 1rem;\n  border-right: 1px solid currentColor;\n}\n\n.parent {\n  height: 180px;\n  display: flex;\n  position: relative;\n}\n\n.item {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  padding: 1rem 2rem;\n  border-radius: 1rem;\n  transition: background-color 0.2s;\n}\n\n.item:hover {\n  background-color: #efefef;\n  color: purple;\n}\n</style>\n"
  },
  {
    "path": "docs/src/examples/solid/index.ts",
    "content": "const solidPrimitive = {\n  solid: {\n    language: \"tsx\",\n    ext: \"tsx\",\n    example: `import { createSignal, For, Show } from 'solid-js'\nimport { createAutoAnimate } from '@formkit/auto-animate/solid'\n\nconst App = function () {\n  const [parent, setEnabled] = createAutoAnimate(/* optional config */)\n\n  const menuItems = [\"Home\", \"Settings\", \"Logout\"]\n  const [isExpanded, setIsExpanded] = createSignal(true)\n\n  return <div class=\"parent\" ref={parent}>\n    <Show when={isExpanded()} keyed>\n      <ul class=\"drawer\">\n        <For each={menuItems}>\n          {item => <li class=\"item\">{item}</li>}\n        </For>\n      </ul>\n    </Show>\n    <div class=\"content\">\n      <button\n        class=\"button button--alt\"\n        type=\"button\"\n        onClick={() => setIsExpanded(isExpanded => !isExpanded)}\n      >\n        Toggle Drawer\n      </button>\n    </div>\n  </div>\n}\n\nexport default App\n`,\n  },\n}\n\nconst solidDirective = {\n  solid: {\n    language: \"tsx\",\n    ext: \"tsx\",\n    example: `import { createSignal, For, Show } from 'solid-js'\nimport { createAutoAnimateDirective } from '@formkit/auto-animate/solid'\n\nconst App = function () {\n  const autoAnimate = createAutoAnimateDirective()\n  const menuItems = [\"Home\", \"Settings\", \"Logout\"]\n  const [isExpanded, setIsExpanded] = createSignal(true)\n\n  return <div use:autoAnimate={/* optional config */} class=\"parent\">\n    <Show when={isExpanded()} keyed>\n      <ul class=\"drawer\">\n        <For each={menuItems}>\n          {item => <li class=\"item\">{item}</li>}\n        </For>\n      </ul>\n    </Show>\n    <div class=\"content\">\n      <button\n        class=\"button button--alt\"\n        type=\"button\"\n        onClick={() => setIsExpanded(isExpanded => !isExpanded)}\n      >\n        Toggle Drawer\n      </button>\n    </div>\n  </div>\n}\n\nexport default App`,\n  },\n}\n\nexport { solidPrimitive, solidDirective }\n"
  },
  {
    "path": "docs/src/examples/svelte/ActualSvelteApp.vue",
    "content": "<script setup>\nimport IconRemove from \"../../components/IconRemove.vue\"\nimport { vAutoAnimate } from \"../../../../src/index\"\nimport { ref } from \"vue\"\nconst tags = ref([\"Rock\", \"Punk\"])\nfunction addItem(e) {\n  tags.value.push(e.target.value)\n  e.target.value = \"\"\n}\nfunction remove(target) {\n  tags.value = tags.value.filter((tag) => target !== tag)\n}\n</script>\n\n<template>\n  <label class=\"input-label\" for=\"add-tag-input\">Add a genre</label>\n  <label class=\"tag-input\" for=\"add-tag-input\">\n    <ul v-auto-animate>\n      <li class=\"tag\" v-for=\"tag in tags\" :key=\"tag\">\n        <span>{{ tag }}</span>\n        <IconRemove @click=\"remove(tag)\" />\n      </li>\n      <li>\n        <input\n          type=\"text\"\n          placeholder=\"Add a tag...\"\n          @keydown.enter=\"addItem\"\n        />\n      </li>\n    </ul>\n  </label>\n</template>\n\n<style scoped>\n.tag-input {\n  display: flex;\n  align-items: flex-start;\n  border: 1px solid lightgray;\n  box-sizing: border-box;\n  padding: 0.25em 0.75em;\n  border-radius: 0.5em;\n  width: 100%;\n  max-width: 450px;\n  box-sizing: border-box;\n}\n\nul {\n  list-style-type: none;\n  padding: 0;\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  margin: 0;\n  min-height: 0.9em;\n  overflow: hidden;\n  width: 100%;\n}\n\n.tag {\n  display: inline-flex;\n  align-items: center;\n  padding: 0.25em 0.75em;\n  font-size: 0.75em;\n  background-color: #ece3ff;\n  border-radius: 1em;\n  margin: 0.25em 0.75em 0.25em 0;\n  user-select: none;\n  color: var(--text-d);\n}\n\n.tag svg {\n  width: 0.833em;\n  margin-left: 0.75em;\n  fill: red;\n}\n\n.tag span {\n  white-space: nowrap;\n}\nli::before {\n  display: none;\n}\n\n.tag-input:focus-within {\n  border-color: #4b17df;\n}\n\n.input-label {\n  font-size: 0.9em;\n  display: block;\n  margin-bottom: 0.5em;\n}\n\ninput {\n  appearance: none;\n  border: 0;\n  padding: 0;\n  margin: 0;\n  flex-grow: 1;\n  width: 5em;\n  background-color: transparent;\n  display: block;\n  padding: 0.25em 0;\n  color: var(--text);\n  /* margin: 0.75em 0; */\n}\n\n@media (min-width: 600px) {\n  input {\n    width: 10em;\n  }\n}\n\ninput:focus {\n  outline: 0;\n}\n</style>\n"
  },
  {
    "path": "docs/src/examples/svelte/index.ts",
    "content": "export default {\n  svelte: {\n    ext: \"svelte\",\n    language: \"html\",\n    example: `<script>\n  import autoAnimate from '@formkit/auto-animate';\n  let tags = ['Rock', 'Punk'];\n  function addItem(e) {\n    if (e.which === 13) {\n      tags.push(e.target.value);\n      tags = tags;\n      e.target.value = '';\n    }\n  }\n  function remove(target) {\n    tags = tags.filter((tag) => target !== tag);\n  }\n</script>\n\n<label for=\"add-tag-input\" class=\"tag-input\">\n  <ul use:autoAnimate> <!-- 👀 thats it folks! -->\n    {#each tags as tag (tag)}\n      <li class=\"tag\">\n        <span>{tag}</span>\n        <span on:click={() => remove(tag)}>x</span>\n      </li>\n    {/each}\n    <li>\n      <input\n        id=\"add-tag-input\"\n        type=\"text\"\n        placeholder=\"Add a tag...\"\n        on:keydown={addItem}\n      />\n    </li>\n  </ul>\n</label>\n`,\n  },\n}\n"
  },
  {
    "path": "docs/src/examples/vue/ActualVueApp.vue",
    "content": "<script setup>\nimport { vAutoAnimate } from \"../../../../src/index\"\nimport { ref } from \"vue\"\nconst items = ref([\n  \"😏\",\n  \"😐\",\n  \"😑\",\n  \"😒\",\n  \"😕\",\n  \"😖\",\n  \"😗\",\n  \"😘\",\n  \"😙\",\n  \"😚\",\n  \"😛\",\n  \"😜\",\n  \"😟\",\n  \"😠\",\n  \"😦\",\n  \"😨\",\n  \"😬\",\n  \"😭\",\n  \"😮\",\n  \"😰\",\n  \"😴\",\n  \"😵\",\n  \"😶\",\n])\n\nfunction removeItem(toRemove) {\n  items.value = items.value.filter((item) => item !== toRemove)\n}\n</script>\n\n<template>\n  <div class=\"example vue-example\">\n    <span class=\"label\">Click emojis to remove them.</span>\n    <ul v-auto-animate>\n      <li v-for=\"item in items\" :key=\"item\" @click=\"removeItem(item)\">\n        <span>{{ item }}</span>\n      </li>\n    </ul>\n  </div>\n</template>\n\n<style scoped>\nul {\n  list-style-type: none;\n  display: flex;\n  flex-wrap: wrap;\n  padding: 0;\n  user-select: none;\n  position: relative;\n}\nli {\n  border: 1px solid var(--gray-m);\n  border-radius: 5px;\n  width: 2em;\n  height: 2em;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  margin-right: 0.5em;\n  cursor: pointer;\n}\nli span {\n  margin-top: -0.1em;\n}\nli:hover {\n  background: var(--purple-l);\n  border-color: var(--purple-m);\n}\nli::before {\n  display: none;\n}\n.label {\n  font-size: 0.83em;\n  font-weight: bold;\n}\n</style>\n"
  },
  {
    "path": "docs/src/examples/vue/ActualVueAppHook.vue",
    "content": "<script setup>\nimport { ref } from \"vue\"\nimport { useAutoAnimate } from \"../../../../src/vue\"\n\nconst items = ref([\"React\", \"Solid\", \"Vue\", \"Svelte\", \"Angular\"])\n\nfunction sortAsc() {\n  items.value.sort()\n}\nfunction sortDesc() {\n  items.value.sort().reverse()\n}\n\nconst [parent] = useAutoAnimate()\n</script>\n\n<template>\n  <div>\n    <button class=\"button\" @click=\"sortAsc\">Sort A-Z ↑</button>\n    <button class=\"button\" @click=\"sortDesc\">Sort Z-A ↓</button>\n  </div>\n  <ul ref=\"parent\">\n    <li v-for=\"item in items\" :key=\"item\">\n      {{ item }}\n    </li>\n  </ul>\n</template>\n"
  },
  {
    "path": "docs/src/examples/vue/index.ts",
    "content": "const vueDirectiveMain = {\n  js: {\n    ext: \"ts\",\n    language: \"typescript\",\n    example: `import { createApp } from 'vue'\nimport { autoAnimatePlugin } from '@formkit/auto-animate/vue'\nimport App from 'App.vue'\n\ncreateApp(App).use(autoAnimatePlugin).mount('#app')\n`,\n  },\n  nuxt: {\n    language: \"typescript\",\n    ext: \"ts\",\n    example: `// nuxt.config.ts\nexport default defineNuxtConfig({\n  modules: ['@formkit/auto-animate/nuxt'],\n})\n`,\n  },\n}\n\nconst vueDirectiveApp = {\n  vue: {\n    ext: \"vue\",\n    language: \"html\",\n    example: `<script setup>\nimport { ref } from 'vue'\nconst items = ref([\"😏\",\"😐\",\"😑\",\"😒\",\"😕\", ... ])\nfunction removeItem(toRemove) {\n  items.value = items.value.filter((item) => item !== toRemove)\n}\n</script>\n\n<template>\n  <h5>Click emojis to remove them.</h5>\n  <ul v-auto-animate>\n    <li\n      v-for=\"item in items\"\n      :key=\"item\"\n      @click=\"removeItem(item)\"\n    >\n      {{ item }}\n    </li>\n  </ul>\n</template>`,\n  },\n}\n\nconst vueComposable = {\n  vue: {\n    ext: \"vue\",\n    language: \"html\",\n    example: `<script setup>\nimport { ref } from 'vue'\nimport { useAutoAnimate } from '@formkit/auto-animate/vue'\n\nconst items = ref([\"React\", \"Vue\", \"Svelte\", \"Angular\"])\n\nfunction sortAsc() {\n  items.value.sort()\n}\nfunction sortDesc() {\n  items.value.sort().reverse()\n}\n\nconst [parent] = useAutoAnimate()\n</script>\n\n<template>\n  <div>\n    <button @click=\"sortAsc\">Sort A-Z ↑</button>\n    <button @click=\"sortDesc\">Sort Z-A ↓</button>\n  </div>\n  <ul ref=\"parent\">\n    <li\n      v-for=\"item in items\"\n      :key=\"item\"\n    >\n      {{ item }}\n    </li>\n  </ul>\n</template>`,\n  },\n}\n\nexport { vueDirectiveMain, vueDirectiveApp, vueComposable }\n"
  },
  {
    "path": "docs/src/global.d.ts",
    "content": "interface Window {\n  Prism: any\n}\n"
  },
  {
    "path": "docs/src/main.ts",
    "content": "import { ViteSSG } from \"vite-ssg\"\nimport { plugin, defaultConfig } from \"@formkit/vue\"\nimport App from \"./App.vue\"\nimport PageHome from \"./pages/PageHome.vue\"\nimport type { RouteRecordRaw } from \"vue-router\"\nimport \"../assets/main.css\"\n\nconst routes: RouteRecordRaw[] = [\n  { path: \"/\", component: PageHome },\n  { path: \"/lists\", component: () => import(\"./pages/PageList.vue\") },\n  { path: \"/tests\", component: () => import(\"./pages/PageTests.vue\") },\n  {\n    path: \"/tests-keep-alive\",\n    component: () => import(\"./pages/PageTestKeepAlive.vue\"),\n  },\n  {\n    path: \"/bottom-jump-test\",\n    component: () => import(\"./pages/PageBottomJumpTest.vue\"),\n  },\n  {\n    path: \"/:catchAll(.*)\",\n    redirect: \"/\",\n  },\n]\n\nexport const createApp = ViteSSG(App, { routes }, ({ app }) => {\n  app.use(plugin, defaultConfig)\n})\n"
  },
  {
    "path": "docs/src/pages/PageBottomJumpTest.vue",
    "content": "<script setup lang=\"ts\">\nimport { onMounted, ref } from 'vue'\nimport { vAutoAnimate } from \"../../../src/index\"\n\nconst items = ref([1, 2, 3, 4, 5])\nconst remove = (n: number) => (items.value = items.value.filter((item) => item !== n))\nconst reset = () => items.value = [1, 2, 3, 4, 5]\n\nconst list = ref()\nonMounted(() => {\n  if (list.value) {\n    // Import the default export function\n    import('../../../src/index').then(({ default: autoAnimate }) => {\n      autoAnimate(list.value, { duration: 1000 })\n    })\n  }\n})\n</script>\n\n<template>\n  <div class=\"test-container\">\n    <ul ref=\"list\" class=\"bottom-aligned-list\">\n      <li v-for=\"item in items\" :key=\"item\" class=\"list-item\">\n        <span>Item {{ item }}</span>\n        <button @click=\"remove(item)\" class=\"remove-btn\">\n          click to remove\n        </button>\n      </li>\n    </ul>\n    <button @click=\"reset\" class=\"reset-btn\">Reset list</button>\n  </div>\n</template>\n\n<style scoped>\n.test-container {\n  position: relative;\n  height: 100vh;\n  padding: 20px;\n}\n\n.bottom-aligned-list {\n  border: 1px solid red;\n  position: fixed;\n  right: 0;\n  bottom: 0;\n  width: 120px;\n  margin: 0;\n  padding: 0;\n  list-style: none;\n  background: white;\n}\n\n.list-item {\n  display: block;\n  padding: 8px;\n  border-bottom: 1px solid #eee;\n}\n\n.list-item:last-child {\n  border-bottom: none;\n}\n\n.remove-btn {\n  display: block;\n  width: 100%;\n  margin-top: 4px;\n  padding: 2px;\n  font-size: 10px;\n  cursor: pointer;\n}\n\n.reset-btn {\n  position: fixed;\n  top: 20px;\n  left: 20px;\n  padding: 10px;\n  font-size: 14px;\n  cursor: pointer;\n}\n</style>"
  },
  {
    "path": "docs/src/pages/PageHome.vue",
    "content": "<script setup lang=\"ts\">\nimport { ref, onMounted } from \"vue\"\nimport TheHeader from \"../components/TheHeader.vue\"\nimport TheFooter from \"../components/TheFooter.vue\"\nimport HeroTitle from \"../components/HeroTitle.vue\"\nimport Navigation from \"../components/Navigation.vue\"\nimport SectionInstallation from \"../sections/SectionInstallation.vue\"\nimport SectionWhy from \"../sections/SectionWhy.vue\"\nimport SectionUsage from \"../sections/SectionUsage.vue\"\nimport SectionExamples from \"../sections/SectionExamples.vue\"\nimport SectionPlugins from \"../sections/SectionPlugins.vue\"\nimport ReducedMotionWarning from \"../components/ReducedMotionWarning.vue\"\nimport KickstartPromo from \"../components/KickstartPromo.vue\"\n\nconst prefersReducedMotion = ref(false)\nonMounted(() => {\n  prefersReducedMotion.value = window.matchMedia(\n    \"(prefers-reduced-motion: reduce)\"\n  ).matches\n})\n</script>\n\n<template>\n  <TheHeader />\n  <HeroTitle />\n  <KickstartPromo />\n  <div class=\"documentation\">\n    <Navigation />\n    <ReducedMotionWarning v-if=\"prefersReducedMotion\" />\n    <SectionInstallation />\n    <SectionUsage />\n    <SectionExamples />\n    <SectionPlugins />\n    <SectionWhy />\n  </div>\n  <TheFooter />\n</template>\n\n<style scoped>\n.documentation {\n  position: relative;\n}\n\n@media (min-width: 42em) {\n  .documentation {\n    padding-left: 12em;\n  }\n}\n\n@media (min-width: 50em) {\n  .documentation {\n    padding-left: 18em;\n  }\n}\n\n@media (min-width: 60em) {\n  .documentation {\n    margin-top: 6em;\n  }\n}\n</style>\n"
  },
  {
    "path": "docs/src/pages/PageList.vue",
    "content": "<script setup lang=\"ts\">\nimport { onMounted, ref } from \"vue\"\nimport autoAnimate from \"../../../src\"\n\nconst items = ref([\n  \"🍎 Apple\",\n  \"🍌 Banana\",\n  \"🍒 Cherry\",\n  \"🫐 Blueberry\",\n  \"🍊 Orange\",\n  \"🥭 Mango\",\n])\n\nconst list = ref<HTMLElement>()\n\nconst fruitBasket = [\n  \"🍓 Strawberry\",\n  \"🥥 Coconut\",\n  \"🥝 Kiwi\",\n  \"🍇 Grape\",\n  \"🍉 Watermelon\",\n  \"🍍 Pineapple\",\n  \"🍐 Pear\",\n  \"🍑 Peach\",\n]\n\nconst sortItems = () => {\n  items.value.sort(() => (Math.random() > 0.5 ? 1 : -1))\n}\n\nconst remove = (item) => {\n  items.value = items.value.filter((fruit) => {\n    if (fruit === item) {\n      fruitBasket.push(fruit)\n      return false\n    }\n    return true\n  })\n}\n\nconst add = () => {\n  if (fruitBasket.length) {\n    items.value.splice(\n      Math.round(Math.random() * items.value.length - 1),\n      0,\n      fruitBasket.shift()\n    )\n  } else {\n    alert(\"Out of fruit!\")\n  }\n}\n\nconst replace = () => {\n  if (fruitBasket.length) {\n    const fruitsRemoved = items.value.splice(\n      Math.round(Math.random() * items.value.length - 1),\n      1,\n      fruitBasket.shift()\n    )\n    fruitsRemoved.forEach((fruit) => fruitBasket.push(fruit))\n  } else {\n    alert(\"Out of fruit!\")\n  }\n}\n\nonMounted(() => autoAnimate(list.value, { duration: 250 }))\n</script>\n\n<template>\n  <ul ref=\"list\">\n    <li v-for=\"item in items\" :key=\"item\">\n      <span>{{ item }}</span>\n      <button @click.prevent=\"remove(item)\">Remove</button>\n    </li>\n  </ul>\n\n  <button @click=\"sortItems\">Random Sort</button>\n  <button @click=\"add\">Add Fruit</button>\n  <button @click=\"replace\">Replace Fruit</button>\n</template>\n\n<style scoped>\nul {\n  list-style-type: none;\n  padding: 0;\n}\n\nli {\n  display: flex;\n  justify-content: space-between;\n  padding: 0.75em;\n  background-color: lavender;\n  margin-bottom: 0.5em;\n  border-radius: 0.5em;\n  box-shadow: 0 0 0.5em rgba(0, 0, 0, 0.1);\n}\n</style>\n"
  },
  {
    "path": "docs/src/pages/PageTestKeepAlive.vue",
    "content": "\n<script setup lang=\"ts\">\nimport CompOne from \"./test-components/CompOne.vue\";\nimport CompTwo from \"./test-components/CompTwo.vue\";\nimport { KeepAlive, ref } from 'vue'\nimport { vAutoAnimate } from \"../../../src\";\n\nconst toggle = ref(true)\n</script>\n\n<template>\n  <div id=\"test-app\">\n    <div v-auto-animate>\n      <KeepAlive>\n        <CompOne key=\"a\" v-if=\"toggle\" />\n      </KeepAlive>\n\n      <KeepAlive>\n        <CompTwo key=\"b\" v-if=\"!toggle\" />\n      </KeepAlive>\n    </div>\n\n    <button @click=\"toggle = !toggle\">Toggle</button>\n  </div>\n</template>\n\n\n<style>\n#test-app {\n  font-family: Avenir, Helvetica, Arial, sans-serif;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n  text-align: center;\n  color: #2c3e50;\n  margin-top: 60px;\n}\n\n.comp {\n  width: 200px;\n  height: 200px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  background-color: #C8E6C9;\n  margin: 0 auto 30px;\n  font-weight: 700;\n}\n\nbutton {\n  position: fixed;\n  top: 10px;\n  left: 10px;\n}\n</style>\n"
  },
  {
    "path": "docs/src/pages/PageTests.vue",
    "content": "<script setup lang=\"ts\">\nimport { ref } from \"vue\"\nimport { vAutoAnimate } from \"../../../src/index\"\nconst show = ref(false)\nconst records = ref([\n  { id: 1, name: \"Billy\", career: \"Fireman\" },\n  { id: 2, name: \"Jane\", career: \"Pilot\" },\n  { id: 3, name: \"Sally\", career: \"Engineer\" },\n  { id: 4, name: \"Todd\", career: \"Chef\" },\n])\n\nconst items = ref(new Array(100).fill(0).map((_, i) => ({ key: Math.random(), value: i, height: `${Math.floor(Math.random() * 200)}px` })))\n\nfunction remove (item: { key: number; value: number }) {\n  items.value.splice(items.value.indexOf(item), 1)\n}\n</script>\n\n<template>\n  <table border=\"1\" style=\"table-layout: fixed\">\n    <thead>``\n      <tr>\n        <th>Name</th>\n        <th>Career</th>\n        <th></th>\n      </tr>\n    </thead>\n    <tbody v-auto-animate=\"{ duration: 5000 }\">\n      <tr\n        v-for=\"(record, index) in records\"\n        :data-index=\"index\"\n        :key=\"record.id\"\n      >\n        <td>{{ record.name }}</td>\n        <td>{{ record.career }}</td>\n        <td>\n          <a href=\"#remove\" @click.prevent=\"records.splice(index, 1)\">Remove</a>\n        </td>\n      </tr>\n    </tbody>\n  </table>\n  <textarea class=\"\"></textarea>\n  <div style=\"display: flex\">\n    <textarea />\n    <div class=\"dropdown\" @click=\"show = !show\" v-auto-animate>\n      <strong>This is a dropdown.</strong>\n      <div class=\"dropdown-content\" v-if=\"show\">\n        Lorem Ipsum is simply dummy text of the printing and typesetting\n        industry. Lorem Ipsum has been the industry's standard dummy text ever\n        since the 1500s, when an unknown printer took a galley of type and\n        scrambled it to make a type specimen book. It has survived not only five\n        centuries, but also the leap into electronic typesetting, remaining\n        essentially unchanged. It was popularised in the 1960s with the release\n        of Letraset sheets containing Lorem Ipsum passages, and more recently\n        with desktop publishing software like Aldus PageMaker including versions\n        of Lorem Ipsum.\n      </div>\n    </div>\n  </div>\n  <ul v-auto-animate=\"{}\">\n    <li v-for=\"item in items\" :key=\"item.key\" class=\"pusher\" :style=\"{ minHeight: item.height }\">Item {{ item.value }} <button @click.prevent=\"remove(item)\">Remove item</button></li>\n  </ul>\n  <div class=\"box-background\"></div>\n</template>\n<style>\nbody {\n  /* overflow: hidden; */\n}\n</style>\n<style scoped>\n.dropdown {\n  width: 500px;\n  background-color: white;\n  border: 2px solid var(--gray-m);\n  border-radius: 0.75em;\n  padding: 1em;\n}\n.dropdown-content {\n  margin-top: 1em;\n}\n\n.pusher {\n  border: 2px solid var(--gray-m);\n}\n.box-background {\n  width: 500px;\n  height: 100px;\n  background-image: linear-gradient(to right, grey 1px, transparent 1px), linear-gradient(to bottom, grey 1px, transparent 1px);\n  background-size: 20px 20px;\n}\n\n/* [data-index=\"1\"] {\n  position: absolute;\n  width: 100%;\n} */\n</style>\n"
  },
  {
    "path": "docs/src/pages/test-components/CompOne.vue",
    "content": "<template>\n  <div class=\"comp\">Comp One</div>\n</template>\n\n<script lang=\"ts\">\nexport default {\n  name: \"CompOne\",\n  props: {\n    msg: String,\n  },\n};\n</script>\n\n<style scoped></style>\n"
  },
  {
    "path": "docs/src/pages/test-components/CompTwo.vue",
    "content": "<template>\n  <div class=\"comp\">Comp Two</div>\n</template>\n\n<script lang=\"ts\">\nexport default {\n  name: \"CompTwo\",\n  props: {\n    msg: String,\n  },\n};\n</script>\n\n<!-- Add \"scoped\" attribute to limit CSS to this component only -->\n<style scoped></style>\n"
  },
  {
    "path": "docs/src/sections/SectionExamples.vue",
    "content": "<script setup>\nimport ActualCards from \"../examples/cards/ActualCards.vue\"\nimport ActualList from \"../examples/list/ActualList.vue\"\nimport ActualBoxes from \"../examples/boxes/ActualBoxes.vue\"\nimport ActualAccordion from \"../examples/accordion/ActualAccordion.vue\"\nimport ActualFormKit from \"../examples/formkit/ActualFormKit.vue\"\nimport IconOutbound from \"../components/IconOutbound.vue\"\n</script>\n\n<template>\n  <section id=\"examples\">\n    <h2>Examples</h2>\n\n    <h3 id=\"example-list\">\n      <a\n        href=\"https://github.com/formkit/auto-animate/blob/master/docs/src/examples/list/ActualList.vue\"\n      >\n        List example<IconOutbound\n      /></a>\n    </h3>\n    <p>\n      Add and remove fruits from a list. Note that fruits are placed at random\n      locations in the list to simulate injecting items rather than just pushing\n      and unshifting.\n    </p>\n    <ActualList />\n\n    <h3 id=\"example-cards\">\n      <a\n        href=\"https://github.com/formkit/auto-animate/blob/master/docs/src/examples/cards/ActualCards.vue\"\n        >Cards example<IconOutbound\n      /></a>\n    </h3>\n    <p>\n      Simulates showing a form to add an event to a list of event cards. Notice\n      that it pushes it to the front of the list and all the cards animate to\n      their destination, and the parent element (invisible) resizes smoothly to\n      accommodate.\n    </p>\n    <ActualCards />\n\n    <h3 id=\"example-boxes\">\n      <a\n        href=\"https://github.com/formkit/auto-animate/blob/master/docs/src/examples/boxes/ActualBoxes.vue\"\n      >\n        Boxes (x/y axis) example<IconOutbound\n      /></a>\n    </h3>\n    <p>\n      An important feature of AutoAnimate is that it operates on both the x and\n      y axis. Items that are moved in the DOM from one location to another, or\n      are wrapping at the end of a line will automatically be translated to\n      their respective position.\n    </p>\n    <ActualBoxes />\n\n    <h3 id=\"example-accordion\">\n      <a\n        href=\"https://github.com/formkit/auto-animate/blob/master/docs/src/examples/accordion/ActualAccordion.vue\"\n      >\n        FAQ accordion example<IconOutbound\n      /></a>\n    </h3>\n    <p>\n      We’ve all had to create accordions before. Pretty easy on the surface, but\n      adding motion to the open/close sequence can be frustrating to say the\n      least, and if you hack it together using\n      <code>max-height</code> transitions, you are sure to lose your easing. No\n      more!\n    </p>\n    <ActualAccordion />\n    <h3 id=\"example-validation\">\n      <a\n        href=\"https://github.com/formkit/auto-animate/blob/master/docs/src/examples/formkit/ActualFormKit.vue\"\n      >\n        FormKit validation example<IconOutbound\n      /></a>\n    </h3>\n    <p>\n      You don’t have to use <a href=\"https://formkit.com\">FormKit</a> to use\n      AutoAnimate, but if you are a FormKit user, there are some neat things you\n      can do. For example, you can use AutoAnimate to animate validation\n      messages being added and removed from an input:\n    </p>\n    <ActualFormKit />\n  </section>\n</template>\n"
  },
  {
    "path": "docs/src/sections/SectionInstallation.vue",
    "content": "<script setup>\nimport HomepageDemo from \"../examples/first-demo/ActualDemo.vue\"\nimport installation from \"../examples/installation\"\nimport CodeExample from \"../components/CodeExample.vue\"\nimport AsideTip from \"../components/AsideTip.vue\"\n</script>\n\n<template>\n  <section id=\"demo\">\n    <h2>A quick demo</h2>\n    <p>\n      AutoAnimate adds automatic animations to your JavaScript applications with\n      a single line of code. Here's the same list component with and without\n      AutoAnimate applied to the wrapping element:\n    </p>\n    <HomepageDemo />\n    <p>\n      That's a notable improvement in UX with no additional developer effort! 🎉\n    </p>\n    <AsideTip>\n      Importantly, our list component did not need to be written with\n      AutoAnimate in mind. AutoAnimate can be applied retroactively to any\n      markup in your codebase, including 3rd-party code — just supply the parent\n      DOM node and let AutoAnimate do the rest! 🤯\n    </AsideTip>\n  </section>\n\n  <section id=\"installation\">\n    <h2>Installation</h2>\n    <p>\n      Install using your package manager of choice to add\n      <code>@formkit/auto-animate</code> to your project:\n    </p>\n    <CodeExample title=\"~/my-app\" :examples=\"installation\"></CodeExample>\n    <p>Aaaaand, you’re done! That was fast. 🐇</p>\n  </section>\n</template>\n\n<style scoped>\nsection:nth-child(3) h2 {\n  padding-top: 3em;\n}\n\ncode {\n  display: inline-block;\n}\n\n@media (min-width: 42em) {\n  section:nth-child(3) h2 {\n    padding-top: 0em;\n  }\n}\n</style>\n"
  },
  {
    "path": "docs/src/sections/SectionPlugins.vue",
    "content": "<script setup>\nimport CodeExample from \"../components/CodeExample.vue\"\nimport Bouncy from '../examples/plugin'\nimport ActualPlugin from '../examples/plugin/ActualPlugin.vue'\n</script>\n\n<template>\n  <section id=\"plugins\">\n    <h2>Plugins</h2>\n    <p>\n      While AutoAnimate is intended to be used with its zero-config defaults,\n      some users may need to replace the default animation keyframes\n      with custom ones. To do this, provide a function as the second\n      argument of the <code>autoAnimate</code> function. This function will be\n      called before every animation and must return a\n      <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/KeyframeEffect\"\n        ><code>KeyframeEffect</code></a\n      >.\n    </p>\n    <p>\n      Here we will create a new set of keyframes for the <code>add</code>, <code>remove</code> and <code>remain</code>\n      actions that overshoot their destinations to create a \"bouncy\" animation effect.\n    </p>\n    <CodeExample title=\"~/bouncy\" :examples=\"Bouncy\"></CodeExample>\n    <p>\n      We can use our newly defined keyframes in any previous example to see the\n      effect it has. Here's our previous list example with the \"bouncy\" keyframes\n      applied to it:\n    </p>\n    <ActualPlugin />\n  </section>\n</template>\n"
  },
  {
    "path": "docs/src/sections/SectionUsage.vue",
    "content": "<script setup>\nimport CodeExample from \"../components/CodeExample.vue\"\nimport AsideTip from \"../components/AsideTip.vue\"\nimport dropdown from \"../examples/dropdown\"\nimport config from \"../examples/config\"\nimport {\n  vueDirectiveMain,\n  vueDirectiveApp,\n  vueComposable,\n} from \"../examples/vue\"\nimport { angularDirectiveMain } from \"../examples/angular\"\nimport { solidPrimitive, solidDirective } from \"../examples/solid\"\nimport reactHook from \"../examples/react\"\nimport preactHook from \"../examples/preact\"\nimport disabledExamples from \"../examples/disable\"\nimport ActualReactApp from \"../examples/react/ActualReactApp.vue\"\nimport ActualPreactApp from \"../examples/preact/ActualPreactApp.vue\"\nimport ActualSolidApp from \"../examples/solid/ActualSolidApp.vue\"\nimport ActualDropdown from \"../examples/dropdown/ActualDropdown.vue\"\nimport svelteAction from \"../examples/svelte\"\nimport qwikHook from \"../examples/qwik\"\nimport ActualSvelteApp from \"../examples/svelte/ActualSvelteApp.vue\"\nimport ActualVueApp from \"../examples/vue/ActualVueApp.vue\"\nimport ActualVueAppHook from \"../examples/vue/ActualVueAppHook.vue\"\nimport ActualAngularApp from \"../examples/angular/ActualAngularApp.vue\"\nimport ActualDisable from \"../examples/disable/ActualDisable.vue\"\nimport IconReact from \"../components/IconReact.vue\"\nimport IconPreact from \"../components/IconPreact.vue\"\nimport IconVue from \"../components/IconVue.vue\"\nimport IconAngular from \"../components/IconAngular.vue\"\nimport IconSvelte from \"../components/IconSvelte.vue\"\nimport IconSolid from \"../components/IconSolid.vue\"\nimport IconQwik from \"../components/IconQwik.vue\"\n</script>\n<template>\n  <section id=\"usage\">\n    <h2>Usage</h2>\n    <p>\n      AutoAnimate is fundamentally a single function —\n      <code>autoAnimate</code> — that accepts a parent element. Automatic\n      animations will be applied to the parent element and its immediate\n      children. Animations are specifically triggered when one of three events\n      occurs:\n    </p>\n    <ul>\n      <li>A child is <strong>added</strong> in the DOM.</li>\n      <li>A child is <strong>removed</strong> in the DOM.</li>\n      <li>A child is <strong>moved</strong> in the DOM.</li>\n    </ul>\n    <p>\n      Let’s see what this looks like in practice. For now we'll use the\n      <code>autoAnimate</code> function directly. React and Vue users — you’ll\n      get some additional syntactic sugar later on — but for now let's learn the\n      fundamentals:\n    </p>\n    <CodeExample :examples=\"dropdown\" title=\"Dropdown\" />\n    <ActualDropdown />\n    <p>\n      Too easy! A gentle, smooth shift without adding any transition classes or\n      custom CSS. This is a notable upgrade for end users with minimal developer\n      effort required. <a href=\"#examples\">Checkout the examples</a>\n      to see other use cases.\n    </p>\n\n    <h3>Tips for success</h3>\n    <ul>\n      <li>\n        It’s still ok to use other kinds of transitions. For example, if you are\n        making stylistic changes with just CSS (such as a hover effect), then\n        use standard CSS transitions for these kinds of styling tweaks.\n      </li>\n      <li>\n        Animations are only triggered when immediate children of the parent\n        element (the one you passed to <code>autoAnimate</code>) are added,\n        removed, or moved.\n      </li>\n      <li>\n        The parent element will automatically receive\n        <code>position: relative</code> if it is statically positioned. Keep\n        this in mind when writing your styles.\n      </li>\n      <li>\n        Sometimes flexbox layouts don’t resize their children immediately. A\n        child with a <code>flex-grow: 1</code> property waits for the\n        surrounding content before snapping to its full width. AutoAnimate\n        doesn’t work well in these cases, but if you give the element a more\n        explicit width it should work like a charm.\n      </li>\n    </ul>\n    <AsideTip\n      >AutoAnimate respects a user’s <code>prefers-reduced-motion</code> setting\n      and will automatic disable if the user has indicated they want reduced\n      motion. Checkout the\n      <a\n        href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion\"\n        >MDN docs for more information</a\n      >\n      on this media feature.\n    </AsideTip>\n    <h3>Configuration</h3>\n    <p>\n      AutoAnimate is intended to be used with <em>zero-configuration</em>. We\n      believe the default configuration falls in line with the project’s\n      objective:\n      <em class=\"important\"\n        >AutoAnimate’s goal is to substantially improve an application’s\n        user-experience without impacting the developer’s implementation time or\n        performance budget.</em\n      >\n      That said, some minor configuration options are available. AutoAnimate\n      allows you to pass a second argument to <code>autoAnimate</code> with the\n      following options:\n    </p>\n    <CodeExample :examples=\"config\" title=\"config\" />\n    <p>\n      If your project’s specific requirements make it necessary to dramatically\n      change the default animations, then you should\n      <a href=\"#plugins\">check out the plugins documentation</a>. Next, checkout\n      the usage documentation for your framework.\n    </p>\n    <ul class=\"framework-jumplinks\">\n      <li>\n        <a href=\"#usage-react\"\n          ><span>React</span>\n          <IconReact />\n        </a>\n      </li>\n      <li>\n        <a href=\"#usage-vue\"><span>Vue</span><IconVue /></a>\n      </li>\n      <li>\n        <a href=\"#usage-preact\"><span>Preact</span><IconPreact /></a>\n      </li>\n      <li>\n        <a href=\"#usage-solid\"><span>Solid</span><IconSolid /></a>\n      </li>\n      <li>\n        <a href=\"#usage-svelte\"><span>Svelte</span><IconSvelte /></a>\n      </li>\n      <li>\n        <a href=\"#usage-angular\"><span>Angular</span><IconAngular /></a>\n      </li>\n      <!-- <li>\n        <a href=\"#usage-qwik\"><span>Qwik</span><IconQwik /></a>\n      </li> -->\n    </ul>\n    <h2 id=\"usage-react\">React hook</h2>\n    <p>\n      React users can use the hook <code>useAutoAnimate</code> by importing it\n      from <code>@formkit/auto-animate/react</code>. This hook returns a ref to\n      apply to the parent element, as well as a function to\n      <a href=\"#usage-disable\">enable or disable</a> animations.\n    </p>\n    <CodeExample :examples=\"reactHook\" title=\"App\" />\n    <ActualReactApp />\n    <AsideTip>\n      Please bare in mind that the value passed to <code>key</code> should be a\n      unique value, otherwise animation will not work as expected.\n    </AsideTip>\n    <h2 id=\"usage-vue\">Vue directive</h2>\n    <p>\n      Vue users can globally register the\n      <code>v-auto-animate</code> directive or install the Nuxt module. This\n      makes adding transitions and animations as easy as applying an attribute.\n      Import the Vue plugin from <code>@formkit/auto-animate/vue</code> and\n      register it with your Vue app:\n    </p>\n    <CodeExample :examples=\"vueDirectiveMain\" title=\"main\" />\n    <AsideTip>\n      If you prefer to not register the Vue directive globally, you can import\n      it directly into the component where you want to use it\n      <code>import { vAutoAnimate } from '@formkit/auto-animate'</code>.\n    </AsideTip>\n    <p>\n      Once you’ve registered the plugin, it can be applied anywhere in your\n      application by adding the <code>v-auto-animate</code> directive to the\n      parent element:\n    </p>\n    <CodeExample :examples=\"vueDirectiveApp\" title=\"App\" />\n    <ActualVueApp />\n    <AsideTip>\n      Please bare in mind that the value passed to <code>:key</code> should be a\n      unique value, otherwise animation will not work as expected.\n    </AsideTip>\n    <AsideTip>\n      Vue users can pass options by directly setting the directive’s value\n      <code>&lt;ul v-auto-animate=\"{ duration: 100 }\"&gt;</code>\n    </AsideTip>\n\n    <h3 id=\"usage-vue-composable\">Vue composable</h3>\n    <p>\n      As an alternative to the <code>v-auto-animate</code> directive, Vue devs\n      can use the <code>useAutoAnimate</code> composable. This composable\n      supports all the same great features, but also provides a mechanism to\n      <a href=\"#usage-disable\">enable and disable</a> animations.\n    </p>\n    <p>\n      Import the composable from <code>@formkit/auto-animate/vue</code> (the\n      Nuxt module will automatically import <code>useAutoAnimate</code> for\n      you), and call it in <code>script setup</code> to create a\n      <a\n        href=\"https://vuejs.org/guide/essentials/template-refs.html#template-refs\"\n        target=\"_blank\"\n        rel=\"noopener noreferrer\"\n        >template ref</a\n      >. Use the <code>ref</code> attribute on your parent element to store it\n      in the template ref:\n    </p>\n    <CodeExample :examples=\"vueComposable\" title=\"App\" />\n    <ActualVueAppHook />\n    <AsideTip>\n      Vue users can pass options directly to the composable: <br />\n      <code>useAutoAnimate({ duration: 100 })</code>\n    </AsideTip>\n\n    <h2 id=\"usage-preact\">Preact hook</h2>\n    <p>\n      Preact users can use the hook <code>useAutoAnimate</code> by importing it\n      from <code>@formkit/auto-animate/preact</code>. This hook returns a ref to\n      apply to the parent element, as well as a function to\n      <a href=\"#usage-disable\">enable or disable</a> animations.\n    </p>\n    <CodeExample :examples=\"preactHook\" title=\"App\" />\n    <ActualPreactApp />\n\n    <h2 id=\"usage-solid\">Solid Primitive</h2>\n    <p>\n      Solid users can use the function <code>createAutoAnimate</code> by\n      importing it from <code>@formkit/auto-animate/solid</code>. This hook\n      returns a ref to apply to the parent element, as well as a function to\n      <a href=\"#usage-disable\">enable or disable</a> animations.\n    </p>\n    <CodeExample :examples=\"solidPrimitive\" title=\"App\" />\n    <ActualSolidApp />\n\n    <h3 id=\"usage-solid-directive\">Solid Directive</h3>\n    <p>\n      Solid users can also use the directive <code>autoAnimate</code> by\n      importing it from <code>@formkit/auto-animate/solid</code>.\n    </p>\n    <CodeExample :examples=\"solidDirective\" title=\"App\" />\n\n    <h2 id=\"usage-svelte\">Svelte action</h2>\n    <p>\n      The root <code>autoAnimate</code> function can be directly used as a\n      Svelte action. Just import <code>autoAnimate</code> and use it as a\n      directive on the parent element.\n    </p>\n    <CodeExample :examples=\"svelteAction\" title=\"App\" />\n    <ActualSvelteApp />\n    <AsideTip>\n      Svelte users can pass options by directly setting the action’s value\n      <code\n        >&lt;ul use:autoAnimate=&#123;&#123; duration: 1000\n        &#125;&#125;&gt;</code\n      >\n    </AsideTip>\n\n    <h2 id=\"usage-angular\">Angular directive</h2>\n    <p>\n      Import <code>AutoAnimateDirective</code> from\n      <code>@formkit/auto-animate/angular</code> into a module or a standalone\n      component to easily add transitions and animations by applying the\n      <code>auto-animate</code> attribute to the parent element in your\n      template:\n    </p>\n    <CodeExample :examples=\"angularDirectiveMain\" title=\"App\" />\n    <ActualAngularApp />\n    <AsideTip>\n      Angular users can pass options by directly setting the options input\n      <code>&lt;ul auto-animate [options]=\"{ duration: 100 }\"&gt;</code>\n    </AsideTip>\n    <AsideTip>\n      In Angular versions &lt;16 you can only import\n      <code>AutoAnimateModule</code> in <code>NgModule</code>s. And you have to\n      use auto-animate v0.8.2 or earlier. Angular v16 isn't directly supported,\n      but you can easily write a wrapper.\n    </AsideTip>\n\n    <!-- <h2 id=\"usage-qwik\">Qwik hook</h2>\n    <p>\n      Qwik users can use the hook <code>useAutoAnimate</code> by importing it\n      from <code>@formkit/auto-animate/qwik</code>. This hook returns a ref to\n      apply to the parent element, as well as a function to\n      <a href=\"#usage-disable\">enable or disable</a> animations.\n    </p>\n    <CodeExample :examples=\"qwikHook\" title=\"App\" /> -->\n\n    <h2 id=\"usage-disable\">Disable animations</h2>\n    <p>\n      Occasionally it may be necessary to temporarily disable animations and\n      then re-enable them later on. The <code>autoAnimate</code> function\n      returns an animation controller with <code>enable()</code> and\n      <code>disable()</code> methods for this purpose, and the Vue and React\n      hooks (<code>useAutoAnimate</code>) return a tuple with the second value\n      being an enable/disable function that accepts a boolean to enable or\n      disable animations.\n    </p>\n    <CodeExample :examples=\"disabledExamples\" title=\"Juggle\" />\n    <ActualDisable />\n  </section>\n</template>\n"
  },
  {
    "path": "docs/src/sections/SectionWhy.vue",
    "content": "<script setup lang=\"ts\">\nimport CodeExample from \"../components/CodeExample.vue\"\nimport examples from \"../examples/basic\"\nimport GithubButton from \"vue-github-button\"\n</script>\n<template>\n  <section id=\"why-not\">\n    <h2>Why not...?</h2>\n    <p>\n      There are loads of Animation libraries available for JavaScript and each\n      has its place. AutoAnimate is not a traditional animation library — think\n      of it more as \"Prettier for motion\" — it is an easy-to-implement standard\n      you can quickly apply to any project.\n    </p>\n    <p>\n      AutoAnimate’s goal is to smooth out changes to the DOM that are otherwise\n      confusing to end-users. For example, when a user sorts a list with no\n      animations, it is difficult to visualize how the items in the list have\n      actually changed — but with AutoAnimate the changes suddenly make\n      intuitive sense. AutoAnimate was made to solve this category of problem\n      via a drop-in solution with zero-config.\n    </p>\n  </section>\n  <section id=\"support\">\n    <h2>Support us</h2>\n    <p>\n      Is AutoAnimate saving you time?<br>\n      Please consider supporting our open-source efforts with a recurring or one-time donation! 🙏\n    </p>\n    <GithubButton\n      href=\"https://github.com/sponsors/formkit\"\n      data-size=\"large\"\n      data-icon=\"heart\"\n      aria-label=\"Sponsor FormKit on GitHub\"\n    >\n      Sponsor\n    </GithubButton>\n  </section>\n</template>\n"
  },
  {
    "path": "docs/vite.config.ts",
    "content": "import { defineConfig } from \"vite\"\nimport vue from \"@vitejs/plugin-vue\"\n\nexport default defineConfig({\n  root: \"./\",\n  plugins: [vue()],\n  ssgOptions: {\n    includeAllRoutes: true,\n  },\n  assetsInclude: [\"**/*.svg\", \"**/*.png\"],\n})\n"
  },
  {
    "path": "docs/vite.config.ts.js",
    "content": "// vite.config.ts\nimport { defineConfig } from \"vite\";\nimport vue from \"@vitejs/plugin-vue\";\nvar vite_config_default = defineConfig({\n  root: \"./\",\n  plugins: [vue()],\n  ssgOptions: {\n    includeAllRoutes: true\n  },\n  assetsInclude: [\"**/*.svg\", \"**/*.png\"]\n});\nexport {\n  vite_config_default as default\n};\n//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImltcG9ydCB7IGRlZmluZUNvbmZpZyB9IGZyb20gXCJ2aXRlXCJcbmltcG9ydCB2dWUgZnJvbSBcIkB2aXRlanMvcGx1Z2luLXZ1ZVwiXG5cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyh7XG4gIHJvb3Q6IFwiLi9cIixcbiAgcGx1Z2luczogW3Z1ZSgpXSxcbiAgc3NnT3B0aW9uczoge1xuICAgIGluY2x1ZGVBbGxSb3V0ZXM6IHRydWUsXG4gIH0sXG4gIGFzc2V0c0luY2x1ZGU6IFtcIioqLyouc3ZnXCIsIFwiKiovKi5wbmdcIl0sXG59KVxuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUFBLFNBQVMsb0JBQW9CO0FBQzdCLE9BQU8sU0FBUztBQUVoQixJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMxQixNQUFNO0FBQUEsRUFDTixTQUFTLENBQUMsSUFBSSxDQUFDO0FBQUEsRUFDZixZQUFZO0FBQUEsSUFDVixrQkFBa0I7QUFBQSxFQUNwQjtBQUFBLEVBQ0EsZUFBZSxDQUFDLFlBQVksVUFBVTtBQUN4QyxDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo=\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"@formkit/auto-animate\",\n  \"version\": \"0.9.0\",\n  \"description\": \"Add motion to your apps with a single line of code.\",\n  \"private\": true,\n  \"keywords\": [\n    \"animation\",\n    \"transition\",\n    \"react\",\n    \"preact\",\n    \"vue\",\n    \"angular\",\n    \"svelte\",\n    \"solid\"\n  ],\n  \"type\": \"module\",\n  \"main\": \"index.mjs\",\n  \"module\": \"index.mjs\",\n  \"types\": \"index.d.ts\",\n  \"repository\": \"https://github.com/formkit/auto-animate.git\",\n  \"author\": \"Justin Schroeder <justin@formkit.com>\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"dev\": \"cd docs && vite --config=./vite.config.ts\",\n    \"build\": \"node ./build/build.mjs\",\n    \"build-docs\": \"cd docs && vite-ssg build --config=./vite.config.ts\",\n    \"release\": \"bumpp && node ./build/build.mjs --publish\",\n    \"nuxt:dev\": \"nuxi dev build/nuxt-playground\",\n    \"playwright:install\": \"pnpm exec playwright install\",\n    \"test:e2e\": \"playwright test\",\n    \"test:e2e:headed\": \"playwright test --headed --project=chromium\",\n    \"test:e2e:ui\": \"playwright test --ui\",\n    \"test:e2e:leak\": \"LEAK_STRICT=1 playwright test -g memory --project=chromium\",\n    \"test:e2e:visual\": \"VISUAL=1 playwright test -g 'Visual:' --project=chromium\"\n  },\n  \"exports\": {\n    \"./vue\": {\n      \"types\": \"./vue/index.d.ts\",\n      \"import\": \"./vue/index.mjs\",\n      \"default\": \"./vue/index.mjs\"\n    },\n    \"./preact\": {\n      \"types\": \"./preact/index.d.ts\",\n      \"import\": \"./preact/index.mjs\",\n      \"default\": \"./preact/index.mjs\"\n    },\n    \"./react\": {\n      \"types\": \"./react/index.d.ts\",\n      \"import\": \"./react/index.mjs\",\n      \"default\": \"./react/index.mjs\"\n    },\n    \"./solid\": {\n      \"types\": \"./solid/index.d.ts\",\n      \"import\": \"./solid/index.mjs\",\n      \"default\": \"./solid/index.mjs\"\n    },\n    \"./angular\": {\n      \"types\": \"./angular/index.d.ts\",\n      \"import\": \"./angular/index.mjs\",\n      \"default\": \"./angular/index.mjs\"\n    },\n    \"./nuxt\": {\n      \"types\": \"./nuxt/module.d.ts\",\n      \"import\": \"./nuxt/module.mjs\",\n      \"require\": \"./nuxt/module.cjs\",\n      \"default\": \"./nuxt/module.mjs\"\n    },\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.mjs\",\n      \"default\": \"./index.mjs\"\n    }\n  },\n  \"devDependencies\": {\n    \"@angular/core\": \"^17.1.0\",\n    \"@formkit/vue\": \"^1.6.5\",\n    \"@nuxt/kit\": \"^3.12.4\",\n    \"@nuxt/module-builder\": \"^0.8.3\",\n    \"@nuxt/schema\": \"^3.12.4\",\n    \"@playwright/test\": \"^1.47.2\",\n    \"@rollup/plugin-terser\": \"^0.4.4\",\n    \"@rollup/plugin-typescript\": \"^11.1.6\",\n    \"@types/node\": \"^20.14.15\",\n    \"@types/prismjs\": \"^1.26.4\",\n    \"@types/prompts\": \"^2.4.9\",\n    \"@types/react\": \"^19.0.0\",\n    \"@vitejs/plugin-vue\": \"^6.0.0\",\n    \"@vueuse/head\": \"^2.0.0\",\n    \"brotli-size\": \"^4.0.0\",\n    \"bumpp\": \"^10.2.3\",\n    \"chalk\": \"^5.3.0\",\n    \"consola\": \"^3.2.3\",\n    \"execa\": \"^6.1.0\",\n    \"jiti\": \"^1.21.6\",\n    \"mlly\": \"^1.7.1\",\n    \"nuxi\": \"^3.12.0\",\n    \"pathe\": \"^1.1.2\",\n    \"preact\": \"^10.23.2\",\n    \"pretty-bytes\": \"^6.1.1\",\n    \"prompts\": \"^2.4.2\",\n    \"react\": \"^19.0.0\",\n    \"rollup\": \"^4.21.0\",\n    \"shx\": \"^0.3.4\",\n    \"solid-js\": \"^1.8.21\",\n    \"tslib\": \"^2.6.3\",\n    \"typescript\": \"^5.9.2\",\n    \"unbuild\": \"^2.0.0\",\n    \"vite\": \"^7.0.0\",\n    \"vite-ssg\": \"^28.1.0\",\n    \"vue\": \"^3.5.0\",\n    \"vue-github-button\": \"^3.1.3\",\n    \"vue-router\": \"^4.4.3\",\n    \"pixelmatch\": \"^5.3.0\",\n    \"pngjs\": \"^7.0.0\"\n  },\n  \"packageManager\": \"pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748\"\n}\n"
  },
  {
    "path": "playwright.config.ts",
    "content": "import { defineConfig, devices } from '@playwright/test'\n\nexport default defineConfig({\n  testDir: './tests/e2e',\n  fullyParallel: true,\n  forbidOnly: !!process.env.CI,\n  retries: process.env.CI ? 2 : 0,\n  workers: process.env.CI ? 2 : undefined,\n  expect: {\n    toHaveScreenshot: {\n      // Allow small rendering differences across environments\n      maxDiffPixelRatio: 0.03,\n      animations: 'allow',\n    },\n  },\n  reporter: [['list'], ['html', { open: 'never' }]],\n  use: {\n    baseURL: 'http://localhost:5173',\n    trace: 'retain-on-failure',\n    video: 'retain-on-failure',\n    screenshot: 'only-on-failure',\n    viewport: { width: 1200, height: 900 },\n    deviceScaleFactor: 1,\n  },\n  webServer: {\n    command: 'cd docs && vite --config=./vite.config.ts --port=5173',\n    port: 5173,\n    stdout: 'pipe',\n    stderr: 'pipe',\n    reuseExistingServer: !process.env.CI,\n    timeout: 60 * 1000,\n  },\n  projects: [\n    {\n      name: 'chromium',\n      use: { ...devices['Desktop Chrome'] },\n    },\n    {\n      name: 'firefox',\n      use: { ...devices['Desktop Firefox'] },\n    },\n    {\n      name: 'webkit',\n      use: { ...devices['Desktop Safari'] },\n    },\n  ],\n})\n\n\n"
  },
  {
    "path": "rollup.config.js",
    "content": "import typescript from \"@rollup/plugin-typescript\"\nimport terser from \"@rollup/plugin-terser\"\n\nconst FRAMEWORK = process.env.FRAMEWORK || \"index\"\nconst DECLARATIONS = process.env.DECLARATIONS || false\nconst MIN = process.env.MIN || false\n\nconst external = [\n  \"vue\",\n  \"preact\",\n  \"react\",\n  \"angular\",\n  \"process\",\n  \"solid-js\",\n  \"../index\",\n]\n\nfunction createOutput() {\n  if (DECLARATIONS) {\n    return {\n      dir: \"./dist\",\n      format: \"esm\",\n    }\n  }\n  return {\n    file: `./dist/${FRAMEWORK !== \"index\" ? FRAMEWORK + \"/\" : \"\"}index.${\n      MIN ? \"min.js\" : \"mjs\"\n    }`,\n    format: \"esm\",\n  }\n}\n\nconst plugins = [\n  typescript({\n    tsconfig: \"tsconfig.json\",\n    compilerOptions: DECLARATIONS\n      ? {\n          declaration: true,\n          emitDeclarationOnly: true,\n        }\n      : {},\n    rootDir: \"./\",\n    outDir: `./dist`,\n    include: [\"./src/**/*\"],\n    exclude: [\"./docs\"],\n  }),\n]\n\nif (MIN) {\n  plugins.push(terser())\n}\n\nexport default {\n  external,\n  input: `./src/${FRAMEWORK === \"index\" ? \"\" : FRAMEWORK + \"/\"}index.ts`,\n  output: createOutput(),\n  plugins,\n}\n"
  },
  {
    "path": "src/angular/index.ts",
    "content": "import {\n  Directive,\n  ElementRef,\n  effect,\n  input,\n} from \"@angular/core\";\nimport autoAnimate, { AutoAnimateOptions } from \"../index\"\n\n\n@Directive({\n  selector: '[auto-animate]',\n  standalone: true,\n})\n\nexport class AutoAnimateDirective {\n  readonly options = input<Partial<AutoAnimateOptions>>({});\n\n  constructor(el: ElementRef) {\n    effect(() => {\n      autoAnimate(el.nativeElement, this.options());\n    });\n  }\n}\n"
  },
  {
    "path": "src/debug-utils.ts",
    "content": "function raw(str: string): number {\n  return Number(str.replace(/[^0-9.\\-]/g, \"\"))\n}\n\nconst lines = new WeakMap<\n  IntersectionObserver,\n  [Element, Element, Element, Element]\n>()\n\n/**\n * TODO - remove, for debugging only\n * @param rootMargin - Draw the root margins\n */\nexport function drawMargins(\n  observer: IntersectionObserver,\n  oldObserver?: IntersectionObserver\n) {\n  const color = \"#ff0000\"\n  if (oldObserver && lines.has(oldObserver)) {\n    lines.get(oldObserver)?.map((line) => line.remove())\n  }\n  lines.set(\n    observer,\n    observer.rootMargin\n      .split(\" \")\n      .map(raw)\n      .map((value) => -1 * value)\n      .map((pos, i) => {\n        const line = document.createElement(\"div\")\n        let bottomLine\n        if (i === 2) {\n          bottomLine = document.documentElement.offsetHeight - pos\n        }\n        line.setAttribute(\n          \"style\",\n          `box-sizing: border-box; border: 1px solid ${color}; opacity: .5; z-index: 100; pointer-events:none; position: absolute; ${\n            [\"top\", \"right\", \"top\", \"left\"][i]\n          }: ${bottomLine || pos}px; ${\n            i % 2\n              ? `height: ${document.documentElement.offsetHeight}px;`\n              : \"width: 100%\"\n          }`\n        )\n        line.setAttribute(\"data-deets\", `pos(${pos}) i(${i})`)\n        document.body.prepend(line)\n        return line\n      }) as [HTMLDivElement, HTMLDivElement, HTMLDivElement, HTMLDivElement]\n  )\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "/**\n * Absolute coordinate positions adjusted for scroll.\n */\ninterface Coordinates {\n  top: number\n  left: number\n  width: number\n  height: number\n}\n\n/**\n * Allows start/stop control of the animation\n */\nexport interface AnimationController<P = unknown> {\n  /**\n   * The original animation parent.\n   */\n  readonly parent: Element\n  /**\n   * A function that enables future animations.\n   */\n  enable: () => void\n  /**\n   * A function that disables future animations.\n   */\n  disable: () => void\n  /**\n   * A function that returns true if the animations are currently enabled.\n   */\n  isEnabled: () => boolean\n  /**\n   * (Svelte Specific) A function that runs if the parameters are changed.\n   */\n  update?: (newParams: P) => void\n  /**\n   * (Svelte Specific) A function that runs when the component is removed from the DOM.\n   */\n  destroy?: () => void\n}\n\n/**\n * A set of all the parents currently being observe. This is the only non weak\n * registry.\n */\nconst parents = new Set<Element>()\n/**\n * Element coordinates that is constantly kept up to date.\n */\nconst coords = new WeakMap<Element, Coordinates>()\n/**\n * Siblings of elements that have been removed from the dom.\n */\nconst siblings = new WeakMap<Element, [prev: Node | null, next: Node | null]>()\n/**\n * Animations that are currently running.\n */\nconst animations = new WeakMap<Element, Animation>()\n/**\n * A map of existing intersection observers used to track element movements.\n */\nconst intersections = new WeakMap<Element, IntersectionObserver>()\n/**\n * A map of existing mutation observers used to track element movements.\n */\nconst mutationObservers = new WeakMap<Element, MutationObserver>()\n/**\n * Intervals for automatically checking the position of elements occasionally.\n */\nconst intervals = new WeakMap<Element, NodeJS.Timeout>()\n/**\n * The configuration options for each group of elements.\n */\nconst options = new WeakMap<Element, AutoAnimateOptions | AutoAnimationPlugin>()\n/**\n * Debounce counters by id, used to debounce calls to update positions.\n */\nconst debounces = new WeakMap<Element, NodeJS.Timeout>()\n/**\n * All parents that are currently enabled are tracked here.\n */\nconst enabled = new WeakSet<Element>()\n/**\n * The document used to calculate transitions.\n */\nlet root: HTMLElement\n\n/**\n * The root’s XY scroll positions.\n */\nlet scrollX = 0\nlet scrollY = 0\n/**\n * Used to sign an element as the target.\n */\nconst TGT = \"__aa_tgt\"\n/**\n * Used to sign an element as being part of a removal.\n */\nconst DEL = \"__aa_del\"\n/**\n * Used to sign an element as being \"new\". When an element is removed from the\n * dom, but may cycle back in we can sign it with new to ensure the next time\n * it is recognized we consider it new.\n */\nconst NEW = \"__aa_new\"\n\n/**\n * Callback for handling all mutations.\n * @param mutations - A mutation list\n */\nconst handleMutations: MutationCallback = (mutations) => {\n  const elements = getElements(mutations)\n  // If elements is \"false\" that means this mutation that should be ignored.\n  if (elements) {\n    elements.forEach((el) => animate(el))\n  }\n}\n\n/**\n *\n * @param entries - Elements that have been resized.\n */\nconst handleResizes: ResizeObserverCallback = (entries) => {\n  entries.forEach((entry) => {\n    if (entry.target === root) updateAllPos()\n    if (coords.has(entry.target)) updatePos(entry.target)\n  })\n}\n\n/**\n * Determine if an element is fully outside of the current viewport.\n * @param el - Element to test\n */\nfunction isOffscreen(el: Element): boolean {\n  const rect = (el as HTMLElement).getBoundingClientRect()\n  const vw = root?.clientWidth || 0\n  const vh = root?.clientHeight || 0\n  return rect.bottom < 0 || rect.top > vh || rect.right < 0 || rect.left > vw\n}\n\n/**\n * Observe this elements position.\n * @param el - The element to observe the position of.\n */\nfunction observePosition(el: Element) {\n  const oldObserver = intersections.get(el)\n  oldObserver?.disconnect()\n  let rect = coords.get(el)\n  let invocations = 0\n  const buffer = 5\n  if (!rect) {\n    rect = getCoords(el)\n    coords.set(el, rect)\n  }\n  const { offsetWidth, offsetHeight } = root\n  const rootMargins = [\n    rect.top - buffer,\n    offsetWidth - (rect.left + buffer + rect.width),\n    offsetHeight - (rect.top + buffer + rect.height),\n    rect.left - buffer,\n  ]\n  const rootMargin = rootMargins\n    .map((px) => `${-1 * Math.floor(px)}px`)\n    .join(\" \")\n  const observer = new IntersectionObserver(\n    () => {\n      ++invocations > 1 && updatePos(el)\n    },\n    {\n      root,\n      threshold: 1,\n      rootMargin,\n    },\n  )\n  observer.observe(el)\n  intersections.set(el, observer)\n}\n\n/**\n * Update the exact position of a given element.\n * @param el - An element to update the position of.\n * @param debounce - Whether or not to debounce the update. After an animation is finished, it should update as soon as possible to prevent flickering on quick toggles.\n */\nfunction updatePos(el: Element, debounce = true) {\n  clearTimeout(debounces.get(el))\n  const optionsOrPlugin = getOptions(el)\n  const delay = debounce\n    ? isPlugin(optionsOrPlugin)\n      ? 500\n      : optionsOrPlugin.duration\n    : 0\n  debounces.set(\n    el,\n    setTimeout(async () => {\n      const currentAnimation = animations.get(el)\n\n      try {\n        await currentAnimation?.finished\n\n        coords.set(el, getCoords(el))\n        observePosition(el)\n      } catch {\n        // ignore errors as the `.finished` promise is rejected when animations were cancelled\n      }\n    }, delay),\n  )\n}\n\n/**\n * Updates all positions that are currently being tracked.\n */\nfunction updateAllPos() {\n  clearTimeout(debounces.get(root))\n  debounces.set(\n    root,\n    setTimeout(() => {\n      parents.forEach((parent) =>\n        forEach(parent, (el) => lowPriority(() => updatePos(el))),\n      )\n    }, 100),\n  )\n}\n\n/**\n * Its possible for a quick scroll or other fast events to get past the\n * intersection observer, so occasionally we need want \"cold-poll\" for the\n * latests and greatest position. We try to do this in the most non-disruptive\n * fashion possible. First we only do this ever couple seconds, staggard by a\n * random offset.\n * @param el - Element\n */\nfunction poll(el: Element) {\n  setTimeout(\n    () => {\n      intervals.set(\n        el,\n        setInterval(() => lowPriority(updatePos.bind(null, el)), 2000),\n      )\n    },\n    Math.round(2000 * Math.random()),\n  )\n}\n\n/**\n * Perform some operation that is non critical at some point.\n * @param callback\n */\nfunction lowPriority(callback: CallableFunction) {\n  if (typeof requestIdleCallback === \"function\") {\n    requestIdleCallback(() => callback())\n  } else {\n    requestAnimationFrame(() => callback())\n  }\n}\n\n/**\n * The mutation observer responsible for watching each root element.\n */\nlet mutations: MutationObserver | undefined\n\n/**\n * A resize observer, responsible for recalculating elements on resize.\n */\nlet resize: ResizeObserver | undefined\n\n/**\n * Ensure the browser is supported.\n */\nconst supportedBrowser =\n  typeof window !== \"undefined\" && \"ResizeObserver\" in window\n\n/**\n * If this is in a browser, initialize our Web APIs\n */\nif (supportedBrowser) {\n  root = document.documentElement\n  mutations = new MutationObserver(handleMutations)\n  resize = new ResizeObserver(handleResizes)\n  window.addEventListener(\"scroll\", () => {\n    scrollY = window.scrollY\n    scrollX = window.scrollX\n  })\n  resize.observe(root)\n}\n/**\n * Retrieves all the elements that may have been affected by the last mutation\n * including ones that have been removed and are no longer in the DOM.\n * @param mutations - A mutation list.\n * @returns\n */\nfunction getElements(mutations: MutationRecord[]): Set<Element> | false {\n  const observedNodes = mutations.reduce((nodes: Node[], mutation) => {\n    return [\n      ...nodes,\n      ...Array.from(mutation.addedNodes),\n      ...Array.from(mutation.removedNodes),\n    ]\n  }, [])\n  // Short circuit if _only_ comment nodes are observed\n  const onlyCommentNodesObserved = observedNodes.every(\n    (node) => node.nodeName === \"#comment\",\n  )\n\n  if (onlyCommentNodesObserved) return false\n\n  return mutations.reduce((elements: Set<Element> | false, mutation) => {\n    // Short circuit if we find a purposefully deleted node.\n    if (elements === false) return false\n\n    if (mutation.target instanceof Element) {\n      target(mutation.target)\n      if (!elements.has(mutation.target)) {\n        elements.add(mutation.target)\n        for (let i = 0; i < mutation.target.children.length; i++) {\n          const child = mutation.target.children.item(i)\n          if (!child) continue\n          if (DEL in child) {\n            return false\n          }\n          target(mutation.target, child)\n          elements.add(child)\n        }\n      }\n      if (mutation.removedNodes.length) {\n        for (let i = 0; i < mutation.removedNodes.length; i++) {\n          const child = mutation.removedNodes[i]\n          if (DEL in child) {\n            return false\n          }\n          if (child instanceof Element) {\n            elements.add(child)\n            target(mutation.target, child)\n            siblings.set(child, [\n              mutation.previousSibling,\n              mutation.nextSibling,\n            ])\n          }\n        }\n      }\n    }\n    return elements\n  }, new Set<Element>())\n}\n\n/**\n * Assign the target to an element.\n * @param el - The root element\n * @param child\n */\nfunction target(el: Element, child?: Element) {\n  if (!child && !(TGT in el)) Object.defineProperty(el, TGT, { value: el })\n  else if (child && !(TGT in child))\n    Object.defineProperty(child, TGT, { value: el })\n}\n\n/**\n * Determines what kind of change took place on the given element and then\n * performs the proper animation based on that.\n * @param el - The specific element to animate.\n */\nfunction animate(el: Element) {\n  const isMounted = el.isConnected\n  const preExisting = coords.has(el)\n  if (isMounted && siblings.has(el)) siblings.delete(el)\n\n  if (animations.get(el)?.playState !== \"finished\") {\n    animations.get(el)?.cancel()\n  }\n  if (NEW in el) {\n    add(el)\n  } else if (preExisting && isMounted) {\n    remain(el)\n  } else if (preExisting && !isMounted) {\n    remove(el)\n  } else {\n    add(el)\n  }\n}\n\n/**\n * Removes all non-digits from a string and casts to a number.\n * @param str - A string containing a pixel value.\n * @returns\n */\nfunction raw(str: string): number {\n  return Number(str.replace(/[^0-9.\\-]/g, \"\"))\n}\n\n/**\n * Get the scroll offset of elements\n * @param el - Element\n * @returns\n */\nfunction getScrollOffset(el: Element) {\n  let p = el.parentElement\n  while (p) {\n    if (p.scrollLeft || p.scrollTop) {\n      return { x: p.scrollLeft, y: p.scrollTop }\n    }\n    p = p.parentElement\n  }\n  return { x: 0, y: 0 }\n}\n\n/**\n * Get the coordinates of elements adjusted for scroll position.\n * @param el - Element\n * @returns\n */\nfunction getCoords(el: Element): Coordinates {\n  const rect = el.getBoundingClientRect()\n  const { x, y } = getScrollOffset(el)\n  return {\n    top: rect.top + y,\n    left: rect.left + x,\n    width: rect.width,\n    height: rect.height,\n  }\n}\n\n/**\n * Returns the width/height that the element should be transitioned between.\n * This takes into account box-sizing.\n * @param el - Element being animated\n * @param oldCoords - Old set of Coordinates coordinates\n * @param newCoords - New set of Coordinates coordinates\n * @returns\n */\nexport function getTransitionSizes(\n  el: Element,\n  oldCoords: Coordinates,\n  newCoords: Coordinates,\n) {\n  let widthFrom = oldCoords.width\n  let heightFrom = oldCoords.height\n  let widthTo = newCoords.width\n  let heightTo = newCoords.height\n  const styles = getComputedStyle(el)\n  const sizing = styles.getPropertyValue(\"box-sizing\")\n\n  if (sizing === \"content-box\") {\n    const paddingY =\n      raw(styles.paddingTop) +\n      raw(styles.paddingBottom) +\n      raw(styles.borderTopWidth) +\n      raw(styles.borderBottomWidth)\n    const paddingX =\n      raw(styles.paddingLeft) +\n      raw(styles.paddingRight) +\n      raw(styles.borderRightWidth) +\n      raw(styles.borderLeftWidth)\n    widthFrom -= paddingX\n    widthTo -= paddingX\n    heightFrom -= paddingY\n    heightTo -= paddingY\n  }\n\n  return [widthFrom, widthTo, heightFrom, heightTo].map(Math.round)\n}\n\n/**\n * Retrieves animation options for the current element.\n * @param el - Element to retrieve options for.\n * @returns\n */\nfunction getOptions(el: Element): AutoAnimateOptions | AutoAnimationPlugin {\n  return TGT in el && options.has((el as Element & { __aa_tgt: Element })[TGT])\n    ? options.get((el as Element & { __aa_tgt: Element })[TGT])!\n    : { duration: 250, easing: \"ease-in-out\" }\n}\n\n/**\n * Returns the target of a given animation (generally the parent).\n * @param el - An element to check for a target\n * @returns\n */\nfunction getTarget(el: Element): Element | undefined {\n  if (TGT in el) return (el as Element & { __aa_tgt: Element })[TGT]\n  return undefined\n}\n\n/**\n * Checks if animations are enabled or disabled for a given element.\n * @param el - Any element\n * @returns\n */\nfunction isEnabled(el: Element): boolean {\n  const target = getTarget(el)\n  return target ? enabled.has(target) : false\n}\n\n/**\n * Iterate over the children of a given parent.\n * @param parent - A parent element\n * @param callback - A callback\n */\nfunction forEach(\n  parent: Element,\n  ...callbacks: Array<(el: Element, isRoot?: boolean) => void>\n) {\n  callbacks.forEach((callback) => callback(parent, options.has(parent)))\n  for (let i = 0; i < parent.children.length; i++) {\n    const child = parent.children.item(i)\n    if (child) {\n      callbacks.forEach((callback) => callback(child, options.has(child)))\n    }\n  }\n}\n\n/**\n * Always return tuple to provide consistent interface\n */\nfunction getPluginTuple(\n  pluginReturn: ReturnType<AutoAnimationPlugin>,\n): [KeyframeEffect, AutoAnimationPluginOptions] | [KeyframeEffect] {\n  if (Array.isArray(pluginReturn)) return pluginReturn\n\n  return [pluginReturn]\n}\n\n/**\n * Determine if config is plugin\n */\nfunction isPlugin(\n  config: Partial<AutoAnimateOptions> | AutoAnimationPlugin,\n): config is AutoAnimationPlugin {\n  return typeof config === \"function\"\n}\n\n/**\n * The element in question is remaining in the DOM.\n * @param el - Element to flip\n * @returns\n */\nfunction remain(el: Element) {\n  const oldCoords = coords.get(el)\n  const newCoords = getCoords(el)\n  if (!isEnabled(el)) return coords.set(el, newCoords)\n  if (isOffscreen(el)) {\n    // When element is offscreen, skip FLIP to avoid broken transforms\n    coords.set(el, newCoords)\n    observePosition(el)\n    return\n  }\n  let animation: Animation\n  if (!oldCoords) return\n  const pluginOrOptions = getOptions(el)\n  if (typeof pluginOrOptions !== \"function\") {\n    let deltaLeft = oldCoords.left - newCoords.left\n    let deltaTop = oldCoords.top - newCoords.top\n    const deltaRight =\n      oldCoords.left + oldCoords.width - (newCoords.left + newCoords.width)\n    const deltaBottom =\n      oldCoords.top + oldCoords.height - (newCoords.top + newCoords.height)\n\n    // element is probably anchored and doesn't need to be offset\n    if (deltaBottom == 0) deltaTop = 0\n    if (deltaRight == 0) deltaLeft = 0\n\n    const [widthFrom, widthTo, heightFrom, heightTo] = getTransitionSizes(\n      el,\n      oldCoords,\n      newCoords,\n    )\n    const start: Record<string, any> = {\n      transform: `translate(${deltaLeft}px, ${deltaTop}px)`,\n    }\n    const end: Record<string, any> = {\n      transform: `translate(0, 0)`,\n    }\n    if (widthFrom !== widthTo) {\n      start.width = `${widthFrom}px`\n      end.width = `${widthTo}px`\n    }\n    if (heightFrom !== heightTo) {\n      start.height = `${heightFrom}px`\n      end.height = `${heightTo}px`\n    }\n    animation = el.animate([start, end], {\n      duration: (pluginOrOptions as AutoAnimateOptions).duration,\n      easing: (pluginOrOptions as AutoAnimateOptions).easing,\n    })\n  } else {\n    const [keyframes] = getPluginTuple(\n      pluginOrOptions(el, \"remain\", oldCoords, newCoords),\n    )\n    animation = new Animation(keyframes)\n    animation.play()\n  }\n  animations.set(el, animation)\n  coords.set(el, newCoords)\n  animation.addEventListener(\"finish\", updatePos.bind(null, el, false), {\n    once: true,\n  })\n}\n\n/**\n * Adds the element with a transition.\n * @param el - Animates the element being added.\n */\nfunction add(el: Element) {\n  if (NEW in el) delete (el as any)[NEW]\n  const newCoords = getCoords(el)\n  coords.set(el, newCoords)\n  const pluginOrOptions = getOptions(el)\n  if (!isEnabled(el)) return\n  if (isOffscreen(el)) {\n    // Skip entry animation if element is not visible in viewport\n    observePosition(el)\n    return\n  }\n  let animation: Animation\n  if (typeof pluginOrOptions !== \"function\") {\n    animation = (el as HTMLElement).animate(\n      [\n        { transform: \"scale(.98)\", opacity: 0 },\n        { transform: \"scale(0.98)\", opacity: 0, offset: 0.5 },\n        { transform: \"scale(1)\", opacity: 1 },\n      ],\n      {\n        duration: (pluginOrOptions as AutoAnimateOptions).duration * 1.5,\n        easing: \"ease-in\",\n      },\n    )\n  } else {\n    const [keyframes] = getPluginTuple(pluginOrOptions(el, \"add\", newCoords))\n    animation = new Animation(keyframes)\n    animation.play()\n  }\n  animations.set(el, animation)\n  animation.addEventListener(\"finish\", updatePos.bind(null, el, false), {\n    once: true,\n  })\n}\n\n/**\n * Clean up after removing an element from the dom.\n * @param el - Element being removed\n * @param styles - Optional styles that should be removed from the element.\n */\nfunction cleanUp(el: Element, styles?: Partial<CSSStyleDeclaration>) {\n  el.remove()\n  coords.delete(el)\n  siblings.delete(el)\n  animations.delete(el)\n  intersections.get(el)?.disconnect()\n  setTimeout(() => {\n    if (DEL in el) delete (el as any)[DEL]\n    Object.defineProperty(el, NEW, { value: true, configurable: true })\n    if (styles && el instanceof HTMLElement) {\n      for (const style in styles) {\n        ;(el.style as any)[style as any] = \"\"\n      }\n    }\n  }, 0)\n}\n\n/**\n * Animates the removal of an element.\n * @param el - Element to remove\n */\nfunction remove(el: Element) {\n  if (!siblings.has(el) || !coords.has(el)) return\n\n  const [prev, next] = siblings.get(el)!\n  Object.defineProperty(el, DEL, { value: true, configurable: true })\n  const finalX = window.scrollX\n  const finalY = window.scrollY\n\n  if (\n    next &&\n    (next.parentNode as any) &&\n    (next.parentNode as any) instanceof Element\n  ) {\n    ;(next.parentNode as Element).insertBefore(el, next)\n  } else if (prev && (prev.parentNode as any)) {\n    ;(prev.parentNode as Element).appendChild(el)\n  } else {\n    getTarget(el)?.appendChild(el)\n  }\n  if (!isEnabled(el)) return cleanUp(el)\n\n  const [top, left, width, height] = deletePosition(el)\n  const optionsOrPlugin = getOptions(el)\n  const oldCoords = coords.get(el)!\n  if (finalX !== scrollX || finalY !== scrollY) {\n    adjustScroll(el, finalX, finalY, optionsOrPlugin)\n  }\n\n  let animation: Animation\n  let styleReset: Partial<CSSStyleDeclaration> = {\n    position: \"absolute\",\n    top: `${top}px`,\n    left: `${left}px`,\n    width: `${width}px`,\n    height: `${height}px`,\n    margin: \"0\",\n    pointerEvents: \"none\",\n    transformOrigin: \"center\",\n    zIndex: \"100\",\n  }\n\n  if (!isPlugin(optionsOrPlugin)) {\n    Object.assign((el as HTMLElement).style, styleReset)\n    animation = (el as HTMLElement).animate(\n      [\n        {\n          transform: \"scale(1)\",\n          opacity: 1,\n        },\n        {\n          transform: \"scale(.98)\",\n          opacity: 0,\n        },\n      ],\n      {\n        duration: (optionsOrPlugin as AutoAnimateOptions).duration,\n        easing: \"ease-out\",\n      },\n    )\n  } else {\n    const [keyframes, options] = getPluginTuple(\n      (optionsOrPlugin as AutoAnimationPlugin)(el, \"remove\", oldCoords),\n    )\n    if (\n      (options as AutoAnimationPluginOptions | undefined)?.styleReset !== false\n    ) {\n      styleReset =\n        (options as AutoAnimationPluginOptions | undefined)?.styleReset ||\n        styleReset\n      Object.assign((el as HTMLElement).style, styleReset)\n    }\n    animation = new Animation(keyframes)\n    animation.play()\n  }\n  animations.set(el, animation)\n  animation.addEventListener(\"finish\", () => cleanUp(el, styleReset), {\n    once: true,\n  })\n}\n\n/**\n * If the element being removed is at the very bottom of the page, and the\n * the page was scrolled into a space being \"made available\" by the element\n * that was removed, the page scroll will have jumped up some amount. We need\n * to offset the jump by the amount that the page was \"automatically\" scrolled\n * up. We can do this by comparing the scroll position before and after the\n * element was removed, and then offsetting by that amount.\n *\n * @param el - The element being deleted\n * @param finalX - The final X scroll position\n * @param finalY - The final Y scroll position\n * @param optionsOrPlugin - The options or plugin\n * @returns\n */\nfunction adjustScroll(\n  el: Element,\n  finalX: number,\n  finalY: number,\n  optionsOrPlugin: AutoAnimateOptions | AutoAnimationPlugin,\n) {\n  const scrollDeltaX = scrollX - finalX\n  const scrollDeltaY = scrollY - finalY\n  const scrollBefore = document.documentElement.style.scrollBehavior\n  const scrollBehavior = getComputedStyle(root).scrollBehavior\n  if (scrollBehavior === \"smooth\") {\n    document.documentElement.style.scrollBehavior = \"auto\"\n  }\n  window.scrollTo(window.scrollX + scrollDeltaX, window.scrollY + scrollDeltaY)\n  if (!(el as any).parentElement) return\n  const parent = (el as HTMLElement).parentElement!\n  let lastHeight = parent.clientHeight\n  let lastWidth = parent.clientWidth\n  const startScroll = performance.now()\n  // Here we use a manual scroll animation to keep the element using the same\n  // easing and timing as the parent’s scroll animation.\n  function smoothScroll() {\n    requestAnimationFrame(() => {\n      if (!isPlugin(optionsOrPlugin)) {\n        const deltaY = lastHeight - parent.clientHeight\n        const deltaX = lastWidth - parent.clientWidth\n        if (\n          startScroll + (optionsOrPlugin as AutoAnimateOptions).duration >\n          performance.now()\n        ) {\n          window.scrollTo({\n            left: window.scrollX - (deltaX as number),\n            top: window.scrollY - (deltaY as number),\n          })\n          lastHeight = parent.clientHeight\n          lastWidth = parent.clientWidth\n          smoothScroll()\n        } else {\n          document.documentElement.style.scrollBehavior = scrollBefore\n        }\n      }\n    })\n  }\n  smoothScroll()\n}\n\n/**\n * Determines the position of the element being removed.\n * @param el - The element being deleted\n * @returns\n */\nfunction deletePosition(\n  el: Element,\n): [top: number, left: number, width: number, height: number] {\n  const oldCoords = coords.get(el)!\n  const [width, , height] = getTransitionSizes(el, oldCoords, getCoords(el))\n\n  let offsetParent: Element | null = (el as HTMLElement).parentElement\n  while (\n    offsetParent &&\n    (getComputedStyle(offsetParent).position === \"static\" ||\n      offsetParent instanceof HTMLBodyElement)\n  ) {\n    offsetParent = (offsetParent as HTMLElement).parentElement\n  }\n  if (!offsetParent) offsetParent = document.body\n  const parentStyles = getComputedStyle(offsetParent)\n  const parentCoords =\n    !animations.has(el) || animations.get(el)?.playState === \"finished\"\n      ? getCoords(offsetParent)\n      : coords.get(offsetParent)!\n\n  const top =\n    Math.round(oldCoords.top - parentCoords.top) -\n    raw(parentStyles.borderTopWidth)\n  const left =\n    Math.round(oldCoords.left - parentCoords.left) -\n    raw(parentStyles.borderLeftWidth)\n  return [top, left, width, height]\n}\n\nexport interface AutoAnimateOptions {\n  /**\n   * The time it takes to run a single sequence of animations in milliseconds.\n   */\n  duration: number\n  /**\n   * The type of easing to use.\n   * Default: ease-in-out\n   */\n  easing: \"linear\" | \"ease-in\" | \"ease-out\" | \"ease-in-out\" | ({} & string)\n  /**\n   * Ignore a user’s \"reduce motion\" setting and enable animations. It is not\n   * recommended to use this.\n   */\n  disrespectUserMotionPreference?: boolean\n}\n\n/**\n * A custom plugin config object\n */\nexport interface AutoAnimationPluginOptions {\n  // provide your own css styles or disable style reset\n  styleReset: CSSStyleDeclaration | false\n}\n\n/**\n * A custom plugin that determines what the effects to run\n */\nexport interface AutoAnimationPlugin {\n  <T extends \"add\" | \"remove\" | \"remain\">(\n    el: Element,\n    action: T,\n    newCoordinates?: T extends \"add\" | \"remain\" | \"remove\"\n      ? Coordinates\n      : undefined,\n    oldCoordinates?: T extends \"remain\" ? Coordinates : undefined,\n  ): KeyframeEffect | [KeyframeEffect, AutoAnimationPluginOptions]\n}\n\n/**\n * A function that automatically adds animation effects to itself and its\n * immediate children. Specifically it adds effects for adding, moving, and\n * removing DOM elements.\n * @param el - A parent element to add animations to.\n * @param options - An optional object of options.\n */\nexport default function autoAnimate(\n  el: HTMLElement,\n  config: Partial<AutoAnimateOptions> | AutoAnimationPlugin = {},\n): AnimationController {\n  if (supportedBrowser && resize) {\n    const mediaQuery = window.matchMedia(\"(prefers-reduced-motion: reduce)\")\n    const isDisabledDueToReduceMotion =\n      mediaQuery.matches &&\n      !isPlugin(config) &&\n      !(config as Partial<AutoAnimateOptions>).disrespectUserMotionPreference\n    if (!isDisabledDueToReduceMotion) {\n      enabled.add(el)\n      if (getComputedStyle(el).position === \"static\") {\n        Object.assign(el.style, { position: \"relative\" })\n      }\n      forEach(el, updatePos, poll, (element) => resize?.observe(element))\n      if (isPlugin(config)) {\n        options.set(el, config as AutoAnimationPlugin)\n      } else {\n        options.set(el, {\n          duration: 250,\n          easing: \"ease-in-out\",\n          ...(config as Partial<AutoAnimateOptions>),\n        })\n      }\n      const mo = new MutationObserver(handleMutations)\n      mo.observe(el, { childList: true })\n      mutationObservers.set(el, mo)\n      parents.add(el)\n    }\n  }\n  const controller: AnimationController = Object.freeze({\n    parent: el,\n    enable: () => {\n      enabled.add(el)\n    },\n    disable: () => {\n      enabled.delete(el)\n      // Cancel any in-flight animations and pending timers for immediate effect\n      forEach(el, (node) => {\n        const a = animations.get(node)\n        try {\n          a?.cancel()\n        } catch {}\n        animations.delete(node)\n        const d = debounces.get(node)\n        if (d) clearTimeout(d)\n        debounces.delete(node)\n        const i = intervals.get(node)\n        if (i) clearInterval(i)\n        intervals.delete(node)\n      })\n    },\n    isEnabled: () => enabled.has(el),\n    destroy: () => {\n      enabled.delete(el)\n      parents.delete(el)\n      options.delete(el)\n      const mo = mutationObservers.get(el)\n      mo?.disconnect()\n      mutationObservers.delete(el)\n      forEach(el, (node) => {\n        // unobserve resize\n        resize?.unobserve(node)\n        // cancel animations\n        const a = animations.get(node)\n        try {\n          a?.cancel()\n        } catch {}\n        animations.delete(node)\n        // disconnect observers\n        const io = intersections.get(node)\n        io?.disconnect()\n        intersections.delete(node)\n        // clear intervals and debounces\n        const i = intervals.get(node)\n        if (i) clearInterval(i)\n        intervals.delete(node)\n        const d = debounces.get(node)\n        if (d) clearTimeout(d)\n        debounces.delete(node)\n        // clear state\n        coords.delete(node)\n        siblings.delete(node)\n      })\n    },\n  })\n  return controller\n}\n\n// Also provide a named export for environments expecting it\nexport { autoAnimate }\n\n/**\n * The vue directive.\n */\nexport const vAutoAnimate = {\n  mounted: (\n    el: HTMLElement,\n    binding: {\n      value: Partial<AutoAnimateOptions> | AutoAnimationPlugin | undefined\n    },\n  ) => {\n    const ctl = autoAnimate(el, binding.value || {})\n    Object.defineProperty(el, \"__aa_ctl\", { value: ctl, configurable: true })\n  },\n  unmounted: (el: HTMLElement) => {\n    const ctl = (el as any)[\"__aa_ctl\"] as AnimationController | undefined\n    ctl?.destroy?.()\n    try {\n      delete (el as any)[\"__aa_ctl\"]\n    } catch {}\n  },\n  getSSRProps: () => ({}),\n}\n"
  },
  {
    "path": "src/nuxt/module.ts",
    "content": "import {\n  defineNuxtModule,\n  addImports,\n  addPlugin,\n  createResolver,\n} from \"@nuxt/kit\"\n\n// Module options TypeScript interface definition\n// export interface ModuleOptions {}\n\nexport default defineNuxtModule({\n  meta: {\n    name: \"auto-animate\",\n    configKey: \"autoAnimate\",\n  },\n  // Default configuration options of the Nuxt module\n  defaults: {},\n  setup() {\n    const resolver = createResolver(import.meta.url)\n\n    // Register the directive\n    addPlugin(resolver.resolve(\"./runtime/plugin\"))\n\n    // Register auto-loaded components\n    addImports([\n      {\n        name: \"autoAnimate\",\n        from: \"@formkit/auto-animate\",\n      },\n      {\n        name: \"useAutoAnimate\",\n        from: \"@formkit/auto-animate/vue\",\n      },\n    ])\n  },\n})\n"
  },
  {
    "path": "src/nuxt/runtime/plugin.ts",
    "content": "import { defineNuxtPlugin } from \"#imports\"\nimport { vAutoAnimate } from \"@formkit/auto-animate/vue\"\n\nexport default defineNuxtPlugin((app) => {\n  // Register the directive\n  app.vueApp.directive(\"auto-animate\", vAutoAnimate)\n})\n"
  },
  {
    "path": "src/preact/index.ts",
    "content": "import { useEffect, useState, useRef } from \"preact/hooks\"\nimport autoAnimate, {\n  AutoAnimateOptions,\n  AutoAnimationPlugin,\n  AnimationController,\n} from \"../index\"\n\n/**\n * AutoAnimate hook for adding dead-simple transitions and animations to preact.\n * @param options - Auto animate options or a plugin\n * @returns\n */\nexport function useAutoAnimate<T extends Element>(\n  options?: Partial<AutoAnimateOptions> | AutoAnimationPlugin\n): [Object<T>, (enabled: boolean) => void] {\n  const element = useRef<T>(null)\n  const [controller, setController] = useState<\n    AnimationController | undefined\n  >()\n  const setEnabled = (enabled: boolean) => {\n    if (controller) {\n      enabled ? controller.enable() : controller.disable()\n    }\n  }\n  useEffect(() => {\n    if (element.current instanceof HTMLElement)\n      setController(autoAnimate(element.current, options || {}))\n  }, [])\n  useEffect(() => {\n    return () => {\n      controller?.destroy?.()\n    }\n  }, [controller])\n  return [element, setEnabled]\n}\n"
  },
  {
    "path": "src/qwik/index.ts",
    "content": "import {\n  $,\n  NoSerialize,\n  noSerialize,\n  QRL,\n  Signal,\n  useSignal,\n  useVisibleTask$,\n} from \"@builder.io/qwik\"\n\nimport { isBrowser } from \"@builder.io/qwik/build\"\nimport autoAnimate, {\n  AnimationController,\n  AutoAnimateOptions,\n  AutoAnimationPlugin,\n} from \"../index\"\n\n/**\n * AutoAnimate hook for adding dead-simple transitions and animations to qwik.\n * @param options - Auto animate options or a plugin\n * @returns\n */\nexport function useAutoAnimate<T extends HTMLElement>(\n  options?: Partial<AutoAnimateOptions> | AutoAnimationPlugin\n): [\n  Signal<T | undefined>,\n  QRL<(enabled: boolean | ((isEnabled: boolean) => boolean)) => void>\n] {\n  const parentRef = useSignal<T>()\n  const controller = useSignal<NoSerialize<AnimationController | undefined>>()\n\n  useVisibleTask$(({ track, cleanup }) => {\n    const element = track(() => parentRef.value)\n    if (element && isBrowser) {\n      controller.value = noSerialize(autoAnimate(element, options))\n    }\n    cleanup(() => controller.value?.destroy?.())\n  })\n\n  const setEnabled = $(\n    (enabled: boolean | ((isEnabled: boolean) => boolean)) => {\n      const ctl = controller.value\n      if (ctl) {\n        typeof enabled === \"function\"\n          ? enabled(ctl.isEnabled())\n            ? ctl.enable()\n            : ctl.disable()\n          : enabled\n          ? ctl.enable()\n          : ctl.disable()\n      }\n    }\n  )\n  return [parentRef, setEnabled]\n}\n"
  },
  {
    "path": "src/react/index.ts",
    "content": "import { useState, useCallback, RefCallback, useMemo, useEffect } from \"react\"\nimport autoAnimate, {\n  AutoAnimateOptions,\n  AutoAnimationPlugin,\n  AnimationController,\n} from \"../index\"\n\n/**\n * AutoAnimate hook for adding dead-simple transitions and animations to react.\n * @param options - Auto animate options or a plugin\n * @returns\n */\nexport function useAutoAnimate<T extends Element>(\n  options?: Partial<AutoAnimateOptions> | AutoAnimationPlugin\n): [RefCallback<T>, (enabled: boolean) => void] {\n  const [controller, setController] = useState<\n    AnimationController | undefined\n  >()\n  const memoizedOptions = useMemo(() => options, [])\n  const element = useCallback(\n    (node: T) => {\n      if (node instanceof HTMLElement) {\n        setController(autoAnimate(node, memoizedOptions))\n      } else {\n        setController(undefined)\n      }\n    },\n    [memoizedOptions]\n  )\n  const setEnabled = useCallback(\n    (enabled: boolean) => {\n      if (controller) {\n        enabled ? controller.enable() : controller.disable()\n      }\n    },\n    [controller]\n  )\n\n  useEffect(() => {\n    return () => {\n      controller?.destroy?.()\n    }\n  }, [controller])\n\n  return [element, setEnabled]\n}\n"
  },
  {
    "path": "src/solid/index.ts",
    "content": "import autoAnimate, {\n  AutoAnimateOptions,\n  AutoAnimationPlugin,\n  AnimationController,\n} from \"../index\"\nimport { createSignal, onMount, Setter, Accessor, onCleanup } from \"solid-js\"\n\ndeclare module \"solid-js\" {\n  namespace JSX {\n    interface Directives {\n      autoAnimate: Partial<AutoAnimateOptions> | AutoAnimationPlugin | true\n    }\n  }\n}\n\nexport const createAutoAnimate = <T extends HTMLElement>(\n  options: Partial<AutoAnimateOptions> | AutoAnimationPlugin = {}\n): [Setter<T | null>, (enabled: boolean) => void] => {\n  const [element, setElement] = createSignal<T | null>(null)\n\n  let controller: AnimationController | undefined\n  let active = true\n\n  onMount(() => {\n    const el = element()\n    if (el) {\n      controller = autoAnimate(el, options)\n      if (active) controller.enable()\n      else controller.disable()\n      onCleanup(() => controller?.destroy?.())\n    }\n  })\n\n  const setEnabled = (enabled: boolean) => {\n    active = enabled\n    if (controller) {\n      enabled ? controller.enable() : controller.disable()\n    }\n  }\n\n  return [setElement, setEnabled]\n}\n\nexport const createAutoAnimateDirective = () => {\n  return (\n    el: HTMLElement,\n    options: Accessor<Partial<AutoAnimateOptions> | AutoAnimationPlugin | true>\n  ) => {\n    let optionsValue = options()\n    let resolvedOptions: Partial<AutoAnimateOptions> | AutoAnimationPlugin = {}\n    if (optionsValue !== true) resolvedOptions = optionsValue\n    const controller = autoAnimate(el, resolvedOptions)\n    onCleanup(() => controller.destroy?.())\n  }\n}\n"
  },
  {
    "path": "src/vue/index.ts",
    "content": "import { ref, onMounted, watchEffect, Plugin, Ref, onBeforeUnmount } from \"vue\"\nimport type { Component, Directive } from \"vue\"\nimport autoAnimate, {\n  vAutoAnimate as autoAnimateDirective,\n  AutoAnimateOptions,\n  AutoAnimationPlugin,\n  AnimationController,\n} from \"../index\"\n\nexport const vAutoAnimate: Directive<\n  HTMLElement | Component,\n  Partial<AutoAnimateOptions>\n> = autoAnimateDirective as unknown as Directive<\n  HTMLElement | Component,\n  Partial<AutoAnimateOptions>\n>\n\n/**\n * Create a Vue directive instance that merges provided defaults with per-use binding.\n */\nexport function createVAutoAnimate(\n  defaults?: Partial<AutoAnimateOptions> | AutoAnimationPlugin\n): Directive<HTMLElement, Partial<AutoAnimateOptions> | AutoAnimationPlugin> {\n  return {\n    mounted(el, binding) {\n      let resolved: Partial<AutoAnimateOptions> | AutoAnimationPlugin = {}\n      const local = binding.value\n      if (typeof local === \"function\") {\n        resolved = local\n      } else if (typeof defaults === \"function\") {\n        resolved = defaults\n      } else {\n        resolved = { ...(defaults || {}), ...(local || {}) }\n      }\n      const ctl = autoAnimate(el, resolved)\n      Object.defineProperty(el, \"__aa_ctl\", { value: ctl, configurable: true })\n    },\n    unmounted(el) {\n      const ctl = (el as any)[\"__aa_ctl\"] as AnimationController | undefined\n      ctl?.destroy?.()\n      try {\n        delete (el as any)[\"__aa_ctl\"]\n      } catch {}\n    },\n    getSSRProps: () => ({}),\n  } as unknown as Directive<\n    HTMLElement,\n    Partial<AutoAnimateOptions> | AutoAnimationPlugin\n  >\n}\n\nexport const autoAnimatePlugin: Plugin = {\n  install(app, defaults?: Partial<AutoAnimateOptions> | AutoAnimationPlugin) {\n    app.directive(\"auto-animate\", createVAutoAnimate(defaults))\n  },\n}\n\n/**\n * AutoAnimate hook for adding dead-simple transitions and animations to Vue.\n * @param options - Auto animate options or a plugin\n * @returns A template ref. Use the `ref` attribute of your parent element\n * to store the element in this template ref.\n */\nexport function useAutoAnimate<T extends Element | Component>(\n  options?: Partial<AutoAnimateOptions> | AutoAnimationPlugin,\n): [Ref<T>, (enabled: boolean) => void] {\n  const element = ref<T>()\n  let controller: AnimationController | undefined\n  function setEnabled(enabled: boolean) {\n    if (controller) {\n      enabled ? controller.enable() : controller.disable()\n    }\n  }\n  onMounted(() => {\n    watchEffect((onCleanup) => {\n      let el: HTMLElement | undefined\n      if (element.value instanceof HTMLElement) {\n        el = element.value\n      } else if (\n        element.value &&\n        \"$el\" in element.value &&\n        element.value.$el instanceof HTMLElement\n      ) {\n        el = element.value.$el\n      }\n      if (el) {\n        controller = autoAnimate(el, options || {})\n        onCleanup(() => {\n          controller?.destroy?.()\n          controller = undefined\n        })\n      }\n    })\n  })\n  onBeforeUnmount(() => {\n    controller?.destroy?.()\n    controller = undefined\n  })\n\n  return [element as Ref<T>, setEnabled]\n}\n"
  },
  {
    "path": "tests/e2e/animations.spec.ts",
    "content": "import { test, expect } from '@playwright/test'\nimport { assertNoConsoleErrors, withAnimationObserver } from './utils'\n\ntest.describe('Animations on examples', () => {\n  test('list page animates on add/remove', async ({ page }) => {\n    const assertNoErrorsLater = await assertNoConsoleErrors(page)\n    await page.goto('/lists')\n\n    const observer = await withAnimationObserver(page, 'ul')\n    // Ensure initial render\n    await expect(page.locator('ul li')).toHaveCount(6)\n\n    // Trigger add → should create at least one running animation on the list subtree\n    await page.getByRole('button', { name: 'Add Fruit' }).click()\n    await page.waitForTimeout(50)\n    expect(await observer.count()).toBeGreaterThan(0)\n\n    // Trigger remove → should animate\n    await page.locator('ul li button', { hasText: 'Remove' }).first().click()\n    await page.waitForTimeout(50)\n    expect(await observer.count()).toBeGreaterThan(0)\n\n    // No console errors\n    await assertNoErrorsLater()\n  })\n\n  test('tests page dropdown animates', async ({ page }) => {\n    const assertNoErrorsLater = await assertNoConsoleErrors(page)\n    await page.goto('/tests')\n    const observer = await withAnimationObserver(page, '.dropdown')\n    await page.locator('.dropdown').click()\n    await page.waitForTimeout(50)\n    expect(await observer.count()).toBeGreaterThan(0)\n    await assertNoErrorsLater()\n  })\n})\n\n\n"
  },
  {
    "path": "tests/e2e/bottom-jump-fix.spec.ts",
    "content": "import { test, expect } from '@playwright/test'\nimport { assertNoConsoleErrors, withAnimationObserver } from './utils'\n\ntest.describe('Bottom-aligned jumping fix (PR #211)', () => {\n  test('should not create extra spacing when animating bottom-aligned list', async ({ page }) => {\n    const assertNoErrorsLater = await assertNoConsoleErrors(page)\n    await page.goto('/bottom-jump-test')\n\n    // Wait for the list to be visible\n    const list = page.locator('.bottom-aligned-list')\n    await expect(list).toBeVisible()\n\n    // Get initial list dimensions and position\n    const initialBox = await list.boundingBox()\n    expect(initialBox).not.toBeNull()\n    \n    const initialHeight = initialBox!.height\n    const initialBottom = initialBox!.y + initialBox!.height\n\n    // Set up animation observer\n    const observer = await withAnimationObserver(page, '.bottom-aligned-list')\n\n    // Get initial item count\n    await expect(page.locator('.bottom-aligned-list .list-item')).toHaveCount(5)\n\n    // Remove an item to trigger animation\n    await page.locator('.list-item .remove-btn').first().click()\n\n    // Wait for animation to start\n    await page.waitForTimeout(50)\n    expect(await observer.count()).toBeGreaterThan(0)\n\n    // During animation, check that the list hasn't grown unexpectedly large\n    // The list should not become significantly taller during the animation\n    const duringAnimationBox = await list.boundingBox()\n    expect(duringAnimationBox).not.toBeNull()\n    \n    const duringAnimationHeight = duringAnimationBox!.height\n    const duringAnimationBottom = duringAnimationBox!.y + duringAnimationBox!.height\n\n    // The list should not grow to be more than 50% taller during animation\n    // This guards against the \"jumping\" issue where extra spacing appears\n    expect(duringAnimationHeight).toBeLessThan(initialHeight * 1.5)\n    \n    // The bottom position should remain relatively stable (within 20px)\n    // This ensures the list stays anchored to the bottom\n    expect(Math.abs(duringAnimationBottom - initialBottom)).toBeLessThan(20)\n\n    // Wait for animation to complete\n    await page.waitForTimeout(1200) // Duration is 1000ms + buffer\n\n    // Verify final state - should have 4 items\n    await expect(page.locator('.bottom-aligned-list .list-item')).toHaveCount(4)\n\n    // Final dimensions should be smaller than initial (one less item)\n    const finalBox = await list.boundingBox()\n    expect(finalBox).not.toBeNull()\n    \n    const finalHeight = finalBox!.height\n    const finalBottom = finalBox!.y + finalBox!.height\n    \n    expect(finalHeight).toBeLessThan(initialHeight)\n    \n    // Bottom should remain anchored (within 5px of original)\n    expect(Math.abs(finalBottom - initialBottom)).toBeLessThan(5)\n\n    await assertNoErrorsLater()\n  })\n\n  test('should animate smoothly when removing multiple items from bottom-aligned list', async ({ page }) => {\n    const assertNoErrorsLater = await assertNoConsoleErrors(page)\n    await page.goto('/bottom-jump-test')\n\n    const list = page.locator('.bottom-aligned-list')\n    await expect(list).toBeVisible()\n\n    const observer = await withAnimationObserver(page, '.bottom-aligned-list')\n\n    // Remove multiple items in sequence\n    for (let i = 0; i < 3; i++) {\n      const initialBox = await list.boundingBox()\n      expect(initialBox).not.toBeNull()\n      \n      const initialBottom = initialBox!.y + initialBox!.height\n\n      // Remove an item\n      await page.locator('.list-item .remove-btn').first().click()\n      \n      // Wait for animation to start\n      await page.waitForTimeout(50)\n      expect(await observer.count()).toBeGreaterThan(0)\n\n      // Check that bottom position doesn't jump during animation\n      const duringAnimationBox = await list.boundingBox()\n      expect(duringAnimationBox).not.toBeNull()\n      \n      const duringAnimationBottom = duringAnimationBox!.y + duringAnimationBox!.height\n      expect(Math.abs(duringAnimationBottom - initialBottom)).toBeLessThan(20)\n\n      // Wait for this animation to complete before next removal\n      await page.waitForTimeout(1200)\n    }\n\n    // Should have 2 items remaining\n    await expect(page.locator('.bottom-aligned-list .list-item')).toHaveCount(2)\n\n    await assertNoErrorsLater()\n  })\n\n  test('should maintain correct positioning after reset', async ({ page }) => {\n    const assertNoErrorsLater = await assertNoConsoleErrors(page)\n    await page.goto('/bottom-jump-test')\n\n    const list = page.locator('.bottom-aligned-list')\n    await expect(list).toBeVisible()\n\n    // Get initial state\n    const initialBox = await list.boundingBox()\n    expect(initialBox).not.toBeNull()\n    \n    const initialBottom = initialBox!.y + initialBox!.height\n\n    // Remove some items\n    await page.locator('.list-item .remove-btn').first().click()\n    await page.waitForTimeout(1200)\n    \n    await page.locator('.list-item .remove-btn').first().click()\n    await page.waitForTimeout(1200)\n\n    // Reset the list\n    await page.locator('.reset-btn').click()\n    await page.waitForTimeout(1200)\n\n    // Should be back to 5 items\n    await expect(page.locator('.bottom-aligned-list .list-item')).toHaveCount(5)\n\n    // Position should be close to original\n    const resetBox = await list.boundingBox()\n    expect(resetBox).not.toBeNull()\n    \n    const resetBottom = resetBox!.y + resetBox!.height\n    expect(Math.abs(resetBottom - initialBottom)).toBeLessThan(10)\n\n    await assertNoErrorsLater()\n  })\n\n  test('should handle rapid interactions without visual glitches', async ({ page }) => {\n    const assertNoErrorsLater = await assertNoConsoleErrors(page)\n    await page.goto('/bottom-jump-test')\n\n    const list = page.locator('.bottom-aligned-list')\n    await expect(list).toBeVisible()\n\n    const observer = await withAnimationObserver(page, '.bottom-aligned-list')\n\n    // Rapidly remove and reset to stress test\n    for (let i = 0; i < 3; i++) {\n      // Remove item\n      await page.locator('.list-item .remove-btn').first().click()\n      await page.waitForTimeout(100) // Short wait\n      \n      // Reset immediately\n      await page.locator('.reset-btn').click()\n      await page.waitForTimeout(100)\n    }\n\n    // Wait for all animations to settle\n    await page.waitForTimeout(2000)\n\n    // Should have stable final state\n    await expect(page.locator('.bottom-aligned-list .list-item')).toHaveCount(5)\n\n    // List should still be properly positioned at bottom\n    const finalBox = await list.boundingBox()\n    expect(finalBox).not.toBeNull()\n    \n    // Should be within viewport and bottom-aligned\n    const viewportHeight = page.viewportSize()?.height || 900\n    expect(finalBox!.y + finalBox!.height).toBeGreaterThan(viewportHeight - 100)\n\n    await assertNoErrorsLater()\n  })\n})"
  },
  {
    "path": "tests/e2e/disable.spec.ts",
    "content": "import { test, expect } from '@playwright/test'\nimport { assertNoConsoleErrors, withAnimationObserver } from './utils'\n\ntest.describe('Disable cancels animations immediately', () => {\n  test('toggling disable stops animations in-flight', async ({ page }) => {\n    const assertNoErrorsLater = await assertNoConsoleErrors(page)\n    await page.goto('/')\n    await page.locator('#usage-disable').scrollIntoViewIfNeeded()\n    const observer = await withAnimationObserver(page, '.balls')\n\n    // Wait for periodic animation to start\n    await page.waitForTimeout(650)\n    const runningBefore = await observer.count()\n    expect(runningBefore).toBeGreaterThanOrEqual(0)\n\n    // Click disable button\n    await page.locator('#disable').click()\n    await page.waitForTimeout(30)\n    const runningAfter = await observer.count()\n\n    // Should be zero because disable cancels running animations\n    expect(runningAfter).toBe(0)\n\n    await assertNoErrorsLater()\n  })\n})\n\n"
  },
  {
    "path": "tests/e2e/exports.spec.ts",
    "content": "import { test, expect } from '@playwright/test'\n\ntest.describe('ESM exports', () => {\n  test('named export autoAnimate is available and equals default', async () => {\n    const url = new URL('../../dist/index.mjs', import.meta.url).href\n    const mod = await import(url)\n    expect(typeof mod.default).toBe('function')\n    expect(mod.autoAnimate).toBeDefined()\n    expect(mod.autoAnimate).toBe(mod.default)\n  })\n})\n\n"
  },
  {
    "path": "tests/e2e/framework-animations.spec.ts",
    "content": "import { test, expect } from '@playwright/test'\nimport { assertNoConsoleErrors, withAnimationObserver } from './utils'\n\ntest.describe('Framework examples animate on interaction', () => {\n  test.beforeEach(async ({ page }) => {\n    await page.goto('/')\n    await expect(page.locator('#usage')).toBeVisible()\n  })\n\n  test('Vue example animates on remove', async ({ page }) => {\n    const assertNoErrorsLater = await assertNoConsoleErrors(page)\n    await page.locator('.vue-example').scrollIntoViewIfNeeded()\n    await page.waitForTimeout(100)\n    const observer = await withAnimationObserver(page, '.vue-example ul')\n    await page.locator('.vue-example ul li').first().click()\n    \n    // Check multiple times to catch animations on slow systems\n    let animationCount = 0\n    for (let i = 0; i < 10; i++) {\n      animationCount = await observer.count()\n      if (animationCount > 0) break\n      await page.waitForTimeout(50)\n    }\n    \n    expect(animationCount).toBeGreaterThan(0)\n    await assertNoErrorsLater()\n  })\n\n  test('React example animates on add', async ({ page }) => {\n    const assertNoErrorsLater = await assertNoConsoleErrors(page)\n    const observer = await withAnimationObserver(page, '.react-example ul')\n    await page.getByRole('button', { name: 'Add number' }).click()\n    await page.waitForTimeout(50)\n    expect(await observer.count()).toBeGreaterThan(0)\n    await assertNoErrorsLater()\n  })\n\n  test('Preact example animates on cycle', async ({ page }) => {\n    const assertNoErrorsLater = await assertNoConsoleErrors(page)\n    const observer = await withAnimationObserver(page, '.preact-example ul')\n    await page.getByRole('button', { name: 'Cycle Emoji' }).click()\n    await page.waitForTimeout(50)\n    expect(await observer.count()).toBeGreaterThan(0)\n    await assertNoErrorsLater()\n  })\n\n  test('Solid example animates on toggle', async ({ page, browserName }) => {\n    const assertNoErrorsLater = await assertNoConsoleErrors(page)\n    // Scroll to make sure the Solid example is visible\n    await page.locator('.solid-example').scrollIntoViewIfNeeded()\n    // WebKit needs more time on slow systems\n    await page.waitForTimeout(browserName === 'webkit' ? 1000 : 500)\n    \n    // Set up observer before clicking to catch animations\n    const observer = await withAnimationObserver(page, '.solid-example .parent')\n    \n    // Click and immediately start checking for animations\n    await page.getByRole('button', { name: 'Toggle Drawer' }).click()\n    \n    // Check multiple times to catch animations on slow systems\n    let animationCount = 0\n    for (let i = 0; i < 20; i++) { // More attempts for slow systems\n      animationCount = await observer.count()\n      if (animationCount > 0) break\n      await page.waitForTimeout(50)\n    }\n    \n    expect(animationCount).toBeGreaterThan(0)\n    await assertNoErrorsLater()\n  })\n\n  test('Svelte example animates on add', async ({ page }) => {\n    const assertNoErrorsLater = await assertNoConsoleErrors(page)\n    // Scroll to make sure the Svelte example is visible\n    await page.locator('.tag-input').scrollIntoViewIfNeeded()\n    await page.waitForTimeout(100) // Give time for any scroll-triggered layouts\n    const observer = await withAnimationObserver(page, '.tag-input ul')\n    const input = page.locator('.tag-input input')\n    await input.fill('Jazz')\n    await input.press('Enter')\n    await page.waitForTimeout(100) // Increased wait time for animation to start\n    expect(await observer.count()).toBeGreaterThan(0)\n    await assertNoErrorsLater()\n  })\n\n  test('Angular example animates on toggle', async ({ page }) => {\n    const assertNoErrorsLater = await assertNoConsoleErrors(page)\n    // Scroll to make sure the Angular example is visible\n    await page.locator('.angular-example').scrollIntoViewIfNeeded()\n    await page.waitForTimeout(200) // Give time for any scroll-triggered layouts\n    const observer = await withAnimationObserver(page, '.angular-example')\n    await page.locator('.angular-example .toggle-story-btn').first().click()\n    \n    // Check multiple times to catch animations on slow systems\n    let animationCount = 0\n    for (let i = 0; i < 10; i++) {\n      animationCount = await observer.count()\n      if (animationCount > 0) break\n      await page.waitForTimeout(50)\n    }\n    \n    expect(animationCount).toBeGreaterThan(0)\n    await assertNoErrorsLater()\n  })\n})\n\n\n"
  },
  {
    "path": "tests/e2e/frameworks.spec.ts",
    "content": "import { test, expect } from '@playwright/test'\nimport { assertNoConsoleErrors } from './utils'\n\n// The home page mounts many framework examples as Vue components that internally\n// import the framework adapters from the library. At minimum ensure the page\n// renders without console errors and the examples are present.\ntest('home page renders examples without console errors', async ({ page }) => {\n  const assertNoErrorsLater = await assertNoConsoleErrors(page)\n  await page.goto('/')\n  // Examples section exists\n  await expect(page.locator('#examples')).toBeVisible()\n  // At least one example list item renders\n  const itemCount = await page.locator('#examples ul li').count()\n  expect(itemCount).toBeGreaterThan(0)\n  await assertNoErrorsLater()\n})\n\n\n"
  },
  {
    "path": "tests/e2e/memory.spec.ts",
    "content": "import { test, expect } from '@playwright/test'\nimport { approximateMemoryUsage, forceGC } from './utils'\n\n// This is a coarse-grained smoke test for memory growth after repeated operations.\n// It is expected to fail currently due to known leaks.\ntest('memory does not grow unbounded after repeated add/remove', async ({ page, browserName }) => {\n  test.skip(browserName !== 'chromium', 'Coarse memory check limited to Chromium')\n  await page.goto('/lists')\n\n  // Warm-up interactions\n  for (let i = 0; i < 10; i++) {\n    await page.getByRole('button', { name: 'Add Fruit' }).click()\n  }\n  await page.waitForTimeout(100)\n  await forceGC(page)\n\n  const before = await approximateMemoryUsage(page)\n\n  // Heavy interactions\n  for (let i = 0; i < 100; i++) {\n    if (i % 2 === 0) {\n      await page.getByRole('button', { name: 'Add Fruit' }).click()\n    } else {\n      const removeBtn = page.locator('ul li button:has-text(\"Remove\")').first()\n      if (await removeBtn.count()) await removeBtn.click()\n    }\n  }\n  await page.waitForTimeout(500)\n  await forceGC(page)\n\n  const after = await approximateMemoryUsage(page)\n\n  test.skip(!before || !after, 'performance.memory not available')\n\n  const growth = after!.usedJSHeapSize - before!.usedJSHeapSize\n  // Allow some growth, but fail on suspiciously large deltas.\n  // Use a stricter threshold when LEAK_STRICT=1 to help surface known leaks.\n  const strict = process.env.LEAK_STRICT === '1'\n  const threshold = strict ? 1 * 1024 * 1024 : 10 * 1024 * 1024\n  expect(growth).toBeLessThan(threshold)\n})\n\n\n"
  },
  {
    "path": "tests/e2e/offscreen.spec.ts",
    "content": "import { test, expect } from '@playwright/test'\nimport { assertNoConsoleErrors } from './utils'\n\ntest.describe('Offscreen elements skip animations', () => {\n  test('add/remain offscreen does not animate', async ({ page }) => {\n    const assertNoErrorsLater = await assertNoConsoleErrors(page)\n    await page.goto('/lists')\n\n    const list = page.locator('ul')\n    await expect(list).toBeVisible()\n\n    // Scroll the list out of view (above the viewport)\n    await page.evaluate(() => {\n      const ul = document.querySelector('ul')!\n      const rect = ul.getBoundingClientRect()\n      window.scrollBy({ top: rect.top - 200 })\n    })\n    await page.waitForTimeout(50)\n\n    // Trigger add while offscreen\n    const beforeCount = await page.locator('ul li').count()\n    await page.getByRole('button', { name: 'Add Fruit' }).click()\n    await page.waitForTimeout(100)\n    const afterCount = await page.locator('ul li').count()\n    expect(afterCount).toBe(beforeCount + 1)\n\n    // Verify the newly added element has no running animations\n    const lastAnimations = await page.evaluate(() => {\n      const ul = document.querySelector('ul')!\n      const last = ul.querySelector('li:last-child') as HTMLElement\n      const anims = last?.getAnimations ? last.getAnimations({ subtree: true }) : []\n      return anims.filter(a => a.playState === 'running' || (a.currentTime && a.effect)).length\n    })\n    expect(lastAnimations).toBeLessThanOrEqual(1)\n\n    await assertNoErrorsLater()\n  })\n})\n\n"
  },
  {
    "path": "tests/e2e/utils.ts",
    "content": "import { expect, Page } from '@playwright/test'\n\nexport async function assertNoConsoleErrors(page: Page) {\n  const errors: string[] = []\n  page.on('console', (msg) => {\n    if (msg.type() === 'error') {\n      errors.push(msg.text())\n    }\n  })\n  // Give tests time to run interactions before asserting later\n  return () => expect(errors, `Console errors: \\n${errors.join('\\n')}`).toEqual([])\n}\n\nexport async function withAnimationObserver(page: Page, selector: string) {\n  await page.waitForSelector(selector)\n  await page.evaluate(() => {\n    const w = window as any\n    if (!w.__aa__) {\n      w.__aa__ = {\n        activeAnimations(element: Element) {\n          const anims = (element.getAnimations ? element.getAnimations({ subtree: true }) : []) as Animation[]\n          return anims.filter((a) => a.playState === 'running' || (a.currentTime && a.effect)).length\n        },\n      }\n    }\n  })\n  return {\n    count: async () => page.evaluate((sel) => (window as any).__aa__.activeAnimations(document.querySelector(sel)!), selector),\n  }\n}\n\nexport async function waitForActiveAnimations(page: Page, selector: string, timeoutMs = 1000) {\n  await page.waitForFunction(\n    (sel) => {\n      const el = document.querySelector(sel)\n      if (!el) return false\n      const anims = (el as any).getAnimations ? (el as any).getAnimations({ subtree: true }) : []\n      return anims.some((a: Animation) => a.playState !== 'finished')\n    },\n    selector,\n    { timeout: timeoutMs }\n  )\n}\n\nexport async function approximateMemoryUsage(page: Page) {\n  // Not all browsers support performance.memory; guard and return undefined when missing\n  const mem = await page.evaluate(() => {\n    const anyWindow = window as any\n    const perf = anyWindow.performance as any\n    if (perf && perf.memory) {\n      return {\n        usedJSHeapSize: perf.memory.usedJSHeapSize,\n        totalJSHeapSize: perf.memory.totalJSHeapSize,\n      }\n    }\n    return undefined\n  })\n  return mem as { usedJSHeapSize: number; totalJSHeapSize: number } | undefined\n}\n\nexport async function forceGC(page: Page) {\n  // Try to nudge GC; works in headless Chromium with --js-flags=--expose-gc, but we don't rely on it\n  await page.evaluate(() => {\n    const anyWindow = window as any\n    if (typeof anyWindow.gc === 'function') anyWindow.gc()\n  })\n}\n\n\n"
  },
  {
    "path": "tests/e2e/visual-animation-video.spec.ts",
    "content": "import { test, expect } from '@playwright/test'\nimport path from 'node:path'\nimport fs from 'node:fs/promises'\nimport { getVideoDurationSec, extractFrameAt, diffImages } from './visual-video.util'\nimport { PNG } from 'pngjs'\nimport pixelmatch from 'pixelmatch'\n\n// Record a deterministic video of the animation by scrubbing frames programmatically.\n// This produces a stable artifact for human review without relying on image snapshots.\n// Always keep video for this spec\ntest.use({ video: 'on' })\n\ntest.describe('Visual video: list animation', () => {\n  test.skip(!process.env.VISUAL, 'Visual tests require VISUAL=1')\n\n  test('record add/remove sequence', async ({ page }, testInfo) => {\n    await page.addInitScript(() => {\n      // Stabilize randomness and slow animations\n      Math.random = () => 0.123456789\n      const w = window as any\n      if (w.__aaPatchedAnimate) return\n      const original = Element.prototype.animate\n      Element.prototype.animate = function (...args: any[]) {\n        try {\n          if (typeof args[1] === 'number') args[1] = (args[1] as number) * 3\n          else if (args[1] && typeof args[1] === 'object') {\n            const timing = args[1] as any\n            if (typeof timing.duration === 'number') timing.duration = timing.duration * 3\n          }\n        } catch {}\n        const anim = original.apply(this, args as any)\n        try {\n          const effect: any = anim.effect as any\n          if (effect?.updateTiming) effect.updateTiming({ fill: 'both' })\n        } catch {}\n        return anim\n      }\n      w.__aaPatchedAnimate = true\n    })\n\n    await page.goto('/lists')\n    const list = page.locator('ul')\n    await list.waitFor()\n\n    // Interact to produce an animation and wait a bit so it is captured in video\n    await page.getByRole('button', { name: 'Add Fruit' }).click()\n    await page.waitForTimeout(1200)\n    await page.locator('ul li button:has-text(\"Remove\")').first().click()\n    await page.waitForTimeout(800)\n\n    // Smoke-assert there are running animations at some point\n    const hadAnimations = await page.evaluate(() => {\n      const root = document.querySelector('ul')!\n      const anims = root.getAnimations({ subtree: true })\n      return anims.length > 0\n    })\n    expect(hadAnimations).toBeTruthy()\n\n    // Frame-based snapshot: scrub and capture frames to compare with baseline images\n    async function scrub(fraction: number) {\n      await page.evaluate((fraction) => {\n        const root = document.querySelector('ul')!\n        const anims = root.getAnimations({ subtree: true })\n        for (const a of anims) {\n          try {\n            a.pause()\n            const effect: any = a.effect as any\n            const timing = effect?.getTiming ? effect.getTiming() : effect?.getComputedTiming?.() || { duration: 0, delay: 0 }\n            const duration = typeof timing.duration === 'number' ? timing.duration : 0\n            const delay = typeof timing.delay === 'number' ? timing.delay : 0\n            a.currentTime = delay + duration * fraction\n          } catch {}\n        }\n        ;(root as HTMLElement).offsetWidth\n      }, fraction)\n      await page.evaluate(() => new Promise((r) => requestAnimationFrame(() => r(null))))\n    }\n\n    const box = await list.boundingBox()\n    if (!box) throw new Error('List bounding box not available')\n    const clip = {\n      x: Math.floor(box.x),\n      y: Math.floor(box.y),\n      width: Math.floor(box.width),\n      height: Math.floor(box.height),\n    }\n\n    const baselineDir = path.resolve(testInfo.project.outputDir, '..', 'visual-baseline', 'list-animation')\n    const currentDir = path.resolve(testInfo.outputDir, 'frames-current')\n    await fs.mkdir(currentDir, { recursive: true })\n\n    const frames = [\n      { name: 'add-10.png', action: async () => scrub(0.1) },\n      { name: 'add-50.png', action: async () => scrub(0.5) },\n      { name: 'add-100.png', action: async () => scrub(1.0) },\n      { name: 'remove-50.png', action: async () => { await page.locator('ul li button:has-text(\"Remove\")').first().click(); await page.waitForFunction(() => document.querySelector('ul')!.getAnimations({ subtree: true }).length > 0); await scrub(0.5) } },\n    ]\n\n    for (const f of frames) {\n      await f.action()\n      const buf = await page.screenshot({ clip, fullPage: false })\n      const outPath = path.join(currentDir, f.name)\n      await fs.writeFile(outPath, buf)\n      if (process.env.VISUAL_UPDATE === '1') {\n        await fs.mkdir(baselineDir, { recursive: true })\n        await fs.writeFile(path.join(baselineDir, f.name), buf)\n      } else {\n        try {\n          const basePath = path.join(baselineDir, f.name)\n          const [aBuf, bBuf] = await Promise.all([fs.readFile(basePath), fs.readFile(outPath)])\n          const imgA = PNG.sync.read(aBuf)\n          const imgB = PNG.sync.read(bBuf)\n          const { width, height } = imgA\n          const diff = new PNG({ width, height })\n          const pixels = pixelmatch(imgA.data, imgB.data, diff.data, width, height, { threshold: 0.1 })\n          const ratio = pixels / (width * height)\n          expect(ratio).toBeLessThan(0.08)\n        } catch (e) {\n          // Baseline missing; attach info but do not fail\n          testInfo.attachments.push({ name: `baseline-missing-${f.name}`, contentType: 'text/plain', body: Buffer.from(String(e)) })\n        }\n      }\n    }\n\n    // Video is recorded automatically for human review; frames above are compared to PNG baselines.\n  })\n})\n\n\n"
  },
  {
    "path": "tests/e2e/visual-animation.spec.ts",
    "content": "import { test, expect } from '@playwright/test'\nimport pixelmatch from 'pixelmatch'\nimport { PNG } from 'pngjs'\n\n// Visual snapshot of a slowed animation sequence.\n// We use a long duration and capture intermediate frames.\n// Always keep video for this spec so humans can inspect the animation\ntest.use({ video: 'on' })\n\ntest.describe('Visual: list animation sequence', () => {\n  test.skip(!process.env.VISUAL, 'Visual tests require VISUAL=1')\n  test('captures keyframes during add/remove sequence', async ({ page }) => {\n    await page.emulateMedia({ reducedMotion: 'no-preference' })\n    // Monkey patch Element.animate early to make sure animations are WAAPI and controllable\n    await page.addInitScript(() => {\n      // Stabilize randomness used by demo code\n      Math.random = () => 0.123456789\n      const w = window as any\n      if (w.__aaPatchedAnimate) return\n      const original = Element.prototype.animate\n      Element.prototype.animate = function (...args: any[]) {\n        // Scale duration to slow animations for clearer frames\n        try {\n          if (typeof args[1] === 'number') args[1] = (args[1] as number) * 4\n          else if (args[1] && typeof args[1] === 'object') {\n            const timing = args[1] as any\n            if (typeof timing.duration === 'number') timing.duration = timing.duration * 4\n          }\n        } catch {}\n        const anim = original.apply(this, args as any)\n        try {\n          // Force fill mode so scrubbing produces visible frames\n          const effect: any = anim.effect as any\n          if (effect?.updateTiming) effect.updateTiming({ fill: 'both' })\n        } catch {}\n        return anim\n      }\n      w.__aaPatchedAnimate = true\n    })\n    await page.goto('/lists')\n    await page.evaluate(() => document.body.setAttribute('data-visual', '1'))\n    await page.addStyleTag({ content: `\n      body[data-visual] ul { width: 500px !important; height: 420px !important; overflow: hidden !important; }\n      body[data-visual] ul li { white-space: nowrap }\n    ` })\n\n    const list = page.locator('ul')\n    await expect(list).toBeVisible()\n\n    // Helper to pause and scrub all WAAPI animations under the list\n    async function scrub(fraction: number) {\n      await page.evaluate((fraction) => {\n        const root = document.querySelector('ul')!\n        const anims = root.getAnimations({ subtree: true })\n        for (const a of anims) {\n          try {\n            a.pause()\n            const effect: any = a.effect as any\n            const timing = effect?.getTiming ? effect.getTiming() : effect?.getComputedTiming?.() || { duration: 0, delay: 0 }\n            const duration = typeof timing.duration === 'number' ? timing.duration : 0\n            const delay = typeof timing.delay === 'number' ? timing.delay : 0\n            a.currentTime = delay + duration * fraction\n          } catch {}\n        }\n        // Force reflow so the new keyframe is painted\n        // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n        ;(root as HTMLElement).offsetWidth\n      }, fraction)\n      // Wait one RAF for paint to commit\n      await page.evaluate(() => new Promise((r) => requestAnimationFrame(() => r(null))))\n    }\n\n    // Wait a tick so the page settles; capture a baseline image buffer using a fixed clip rect\n    await page.waitForTimeout(50)\n    const box = await list.boundingBox()\n    if (!box) throw new Error('List bounding box not available')\n    const clip = {\n      x: Math.floor(box.x),\n      y: Math.floor(box.y),\n      width: Math.floor(box.width),\n      height: Math.floor(box.height),\n    }\n    const baselinePngBuf = await page.screenshot({ clip, fullPage: false })\n\n    // Trigger add and immediately scrub to 10%, 50%, 100%\n    await page.getByRole('button', { name: 'Add Fruit' }).click()\n    await page.waitForFunction(() => document.querySelector('ul')!.getAnimations({ subtree: true }).length > 0)\n    await scrub(0.1)\n    const add10Buf = await page.screenshot({ clip, fullPage: false })\n    const imgA = PNG.sync.read(baselinePngBuf)\n    const img10 = PNG.sync.read(add10Buf)\n    const { width, height } = imgA\n    const diff10 = new PNG({ width, height })\n    const diffPixels10 = pixelmatch(imgA.data, img10.data, diff10.data, width, height, { threshold: 0.1 })\n    const ratio10 = diffPixels10 / (width * height)\n    expect(ratio10).toBeGreaterThan(0.01)\n    await scrub(0.5)\n    const add50Buf = await page.screenshot({ clip, fullPage: false })\n    const img50 = PNG.sync.read(add50Buf)\n    const diff50 = new PNG({ width, height })\n    const diffPixels50 = pixelmatch(imgA.data, img50.data, diff50.data, width, height, { threshold: 0.1 })\n    const ratio50 = diffPixels50 / (width * height)\n    expect(ratio50).toBeGreaterThan(0.01)\n    await scrub(1.0)\n    const add100Buf = await page.screenshot({ clip, fullPage: false })\n    const img100 = PNG.sync.read(add100Buf)\n    const diff100 = new PNG({ width, height })\n    const diffPixels100 = pixelmatch(imgA.data, img100.data, diff100.data, width, height, { threshold: 0.1 })\n    const ratio100 = diffPixels100 / (width * height)\n    expect(ratio100).toBeGreaterThan(0.01)\n\n    // Trigger remove and scrub mid-way\n    await page.locator('ul li button:has-text(\"Remove\")').first().click()\n    await page.waitForFunction(() => document.querySelector('ul')!.getAnimations({ subtree: true }).length > 0)\n    await scrub(0.5)\n    const remove50Buf = await page.screenshot({ clip, fullPage: false })\n    const imgR50 = PNG.sync.read(remove50Buf)\n    const diffR50 = new PNG({ width, height })\n    const diffPixelsR50 = pixelmatch(img100.data, imgR50.data, diffR50.data, width, height, { threshold: 0.1 })\n    const ratioR50 = diffPixelsR50 / (width * height)\n    expect(ratioR50).toBeGreaterThan(0.01)\n  })\n})\n\n\n"
  },
  {
    "path": "tests/e2e/visual-video.util.ts",
    "content": "import { execa } from 'execa'\nimport { PNG } from 'pngjs'\nimport pixelmatch from 'pixelmatch'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\n\nexport async function getVideoDurationSec(videoPath: string): Promise<number> {\n  const { stdout } = await execa('ffprobe', [\n    '-v', 'error',\n    '-show_entries', 'format=duration',\n    '-of', 'default=nw=1:nk=1',\n    videoPath,\n  ])\n  return parseFloat(stdout.trim())\n}\n\nexport async function extractFrameAt(videoPath: string, timeSec: number, outPath: string): Promise<void> {\n  await fs.mkdir(path.dirname(outPath), { recursive: true })\n  await execa('ffmpeg', [\n    '-loglevel', 'error',\n    '-y',\n    '-ss', timeSec.toFixed(3),\n    '-i', videoPath,\n    '-frames:v', '1',\n    '-q:v', '2',\n    outPath,\n  ])\n}\n\nexport async function diffImages(aPath: string, bPath: string): Promise<{ ratio: number; pixels: number }> {\n  const [aBuf, bBuf] = await Promise.all([fs.readFile(aPath), fs.readFile(bPath)])\n  const imgA = PNG.sync.read(aBuf)\n  const imgB = PNG.sync.read(bBuf)\n  if (imgA.width !== imgB.width || imgA.height !== imgB.height) {\n    throw new Error(`Image size mismatch: ${imgA.width}x${imgA.height} vs ${imgB.width}x${imgB.height}`)\n  }\n  const { width, height } = imgA\n  const diff = new PNG({ width, height })\n  const pixels = pixelmatch(imgA.data, imgB.data, diff.data, width, height, { threshold: 0.1 })\n  const ratio = pixels / (width * height)\n  return { ratio, pixels }\n}\n\n\n"
  },
  {
    "path": "tests/e2e/vue-plugin.spec.ts",
    "content": "import { test, expect } from '@playwright/test'\nimport { assertNoConsoleErrors, withAnimationObserver } from './utils'\n\ntest.describe('Vue plugin defaults', () => {\n  test('directive still animates with global defaults', async ({ page }) => {\n    const assertNoErrorsLater = await assertNoConsoleErrors(page)\n    await page.goto('/')\n    const observer = await withAnimationObserver(page, '.vue-example ul')\n    await page.locator('.vue-example ul li').first().click()\n    await page.waitForTimeout(50)\n    expect(await observer.count()).toBeGreaterThan(0)\n    await assertNoErrorsLater()\n  })\n})\n\n"
  },
  {
    "path": "tests/e2e/vue-vif.spec.ts",
    "content": "import { test, expect } from '@playwright/test'\nimport { assertNoConsoleErrors, withAnimationObserver } from './utils'\n\ntest.describe('Vue useAutoAnimate with v-if toggles', () => {\n  test('cleanup and re-init works without residual animations', async ({ page }) => {\n    const assertNoErrorsLater = await assertNoConsoleErrors(page)\n    await page.goto('/tests')\n    // This page has many toggles; use the dropdown which uses v-if in demos\n    const observer = await withAnimationObserver(page, '.dropdown')\n    await page.locator('.dropdown').click()\n    await page.waitForTimeout(50)\n    expect(await observer.count()).toBeGreaterThanOrEqual(0)\n    // Toggle closed and open again to ensure cleanup/reinit does not error\n    await page.locator('.dropdown').click()\n    await page.waitForTimeout(10)\n    await page.locator('.dropdown').click()\n    await page.waitForTimeout(50)\n    expect(await observer.count()).toBeGreaterThanOrEqual(0)\n    await assertNoErrorsLater()\n  })\n})\n\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"experimentalDecorators\": true,\n    \"module\": \"esnext\",\n    \"target\": \"es2019\",\n    \"lib\": [\"es2019\", \"dom\"],\n    \"strict\": true,\n    \"allowJs\": false,\n    \"moduleResolution\": \"node\",\n    \"allowSyntheticDefaultImports\": true,\n    \"outDir\": \"dist\",\n    \"types\": [\n      \"prismjs\",\n      \"node\"\n    ]\n  },\n  \"exclude\": [\n    \"*.vue\",\n    \"examples\"\n  ]\n}\n"
  }
]