{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"agent-rules","type":"registry:component","title":"Agent Rules","description":"Wiring rules an AI agent reads before using Atelier components.","meta":{"pro":false},"dependencies":[],"registryDependencies":[],"files":[{"path":"registry/agent-rules/.claude/skills/atelier-ui/SKILL.md","type":"registry:file","target":"~/.claude/skills/atelier-ui/SKILL.md","content":"---\nname: atelier-ui\ndescription: >\n    Install and wire Atelier UI components (atelier-ui.com) into a React project:\n    shadcn registry setup, the @atelier pro registry and license key, the shared WebGL\n    canvas, smooth scroll, and page transitions. Use this skill when adding, moving, or\n    removing an Atelier component, when running `npx shadcn add` against @atelier or an\n    atelier-ui.com registry URL, and when troubleshooting an installed Atelier component\n    that renders nothing, throws a React Compiler or react-hooks lint error, or breaks\n    scrolling or page transitions - even when the user names only the effect and never\n    says \"Atelier\", such as a background shader, a cursor or hover effect, a scroll\n    effect, a WebGL image, video or text plane, or a page transition.\ncompatibility: Requires Node.js, network access, and a React 19 project using Tailwind CSS v4, such as Next.js or Vite. Page transitions are the exception and require Next.js 15.3 or later.\nmetadata:\n    version: \"1.5.0\"\n---\n\n# Atelier UI\n\nAtelier UI (atelier-ui.com) is a WebGL and Motion system for React. It covers shader\nbackgrounds, cursor and hover effects, scroll-driven animation, WebGL image, video and\ntext planes, 3D galleries, and page transitions for Next.js.\n\nThree things are shared across the catalog instead of living inside each component: one\nWebGL canvas every effect draws into, one Lenis smooth scroll driven on Motion's frame\nloop, and one page transition system. That is what lets many effects coexist on a\nscrolling page without exhausting the browser's WebGL context cap or fighting each other\nfor the scroll position.\n\n## Finding a component\n\nWhen the request names an effect rather than a component, search the catalog first. There\nis nothing to set up: `@atelier` is in the shadcn registry directory, so the CLI resolves\nit before `components.json` has any entry for it.\n\n```sh\nnpx shadcn@latest search @atelier -q \"scroll\"\n```\n\nThat covers all 51 items and writes nothing.\n\nNever run `npx shadcn view` on an Atelier item. It inlines the component's full source, a\nthousand lines of shader code for most of them, and tells you nothing the search result\nand the `Usage:` markdown don't already cover.\n\n## Paths\n\nFiles land in `components/<name>/<name>.tsx`, imported as `@/components/<name>/<name>`.\nThe path repeats the name, so `@/components/webgl-provider` is wrong and\n`@/components/webgl-provider/webgl-provider` is right.\n\nShared hooks and helpers are the exception: they land at `hooks/` and `lib/`, outside\n`components/`. The installed files import them relatively and already resolve. Leave\nthose imports alone.\n\nA `Props:` line in the prompt holds the preview's props as JSX attributes. Pass them as written.\n\nThe add command prints a `Usage:` URL for every item it installs. Fetch it. It returns a\nfew KB of plain markdown holding the images, the props and the mount, ready to paste.\n\nIf no `Usage:` line was printed, the URL is `https://www.atelier-ui.com/r/<name>.md`. Try it\nonce. A 404 there means the doc is genuinely missing, so fall back to the installed source\nand say so in your summary rather than probing for other paths.\n\nFetch it raw, with `curl -fsSL` or whatever returns the bytes unchanged. Follow redirects,\nthe URL may not be on the canonical host. A tool that reads a page through a model and\nhands back a summary will drop image URLs and prop values without saying so, and the\nresult looks fine until it doesn't compile.\n\nDon't reconstruct usage by reading the component source, it's a thousand lines of shader\ncode and the answer isn't in there. Don't open the `Docs:` URL either, that one is the\nrendered page for humans.\n\n## Where to stop\n\nInstall the component, wire it up, confirm the project's build passes. That's the whole job.\n\nDon't install a browser driver, a screenshot tool, or anything else to look at the result.\nDon't fix lint errors inside the installed files, see the lint section below.\nDon't reformat or refactor them either, they're overwritten on the next install.\n\n## Images\n\nSome components take a required array of media, usually `items={[{ src, alt }]}`.\nThey render nothing when it's empty, so the prop is not optional in practice.\n\nThe `Usage:` markdown has working image sources. Use them as written. Don't invent\nplaceholders, don't generate SVGs, and don't go hunting through the repo for assets.\n\n## Install order\n\nIf the add command has already run, the checks below are spent. Skip to Installing, read\nthe `Usage:` markdown, wire the component up, and run the build. Re-running the\nprerequisite and license steps after a successful install verifies nothing.\n\n- [ ] 1. Tailwind CSS v4 present, stop and ask if not\n- [ ] 2. `components.json` at the project root, create it if absent\n- [ ] 3. Pro or free, from what the prompt says, without looking anything up\n- [ ] 4. Pro items only: `ATELIER_PRO_KEY` present, stop and ask if not, then give the `@atelier` entry its license header\n- [ ] 5. Run the add command\n- [ ] 6. Add the lint override, before anything runs a linter\n- [ ] 7. Wire it up, then run the project's build\n\nSteps 1 and 4 are hard stops, never run the add command past a failed one.\nThe sections below cover each step in this order.\n\n## Prerequisites\n\n**Tailwind CSS v4.** Check for `tailwindcss` in dependencies and `@import \"tailwindcss\"` in the global stylesheet.\n\n**Missing:** stop. Say the project has no Tailwind, that Atelier components need it to render, and ask whether to install it. Do not install without an answer, it touches the build config and the global stylesheet. On yes, follow https://tailwindcss.com/docs/installation.\n\n**Present:** continue.\n\n**Next.js 15.3 or later, page transitions only.** Every other component runs on any React 19 setup, Next.js or Vite alike. Page transitions are the exception. Check `next` in package.json, and stop and say so if it is absent or older.\n\n**components.json.** Needed at the project root. Create with `npx shadcn@latest init -d` if absent, no need to ask.\n\n## Pro license\n\nFree and pro items install the same way, but a pro one without a key fails with a 401.\n\nDon't look it up. A copied prompt says so: a pro one tells you to install `agent-rules`\nfirst and set up the license, a free one gives you a single add command. Follow what the\nprompt says and skip the rest of this section when it says nothing about a license.\n\nWith no prompt to go on, install anyway. A pro item without a key fails with a 401 whose\nbody names the variable, which is cheaper than a lookup on every install. Handle it then.\n\nCheck for the key first. The CLI reads `.env.local`, `.env.development.local`, `.env.development`, `.env`:\n\n```sh\ncat .env.local .env.development.local .env.development .env 2>/dev/null | grep -q \"ATELIER_PRO_KEY=.\"\n```\n\nIt exits `0` when a key is set and `1` when it is absent or empty. Don't reach for\n`grep -l` over the same list, that exits `2` either way because most of those files are\nmissing, and a missing key reads exactly like a present one.\n\n**Missing:** stop. Ask whether they added their license key, from their account page, into a git-ignored `.env.local` as `ATELIER_PRO_KEY=...`. Do not add the entry, do not install. An entry referencing an empty var fails free installs too.\n\n**Present:** write the entry below, then install. Do not stop, do not mention the key.\n\n```json\n{\n    \"registries\": {\n        \"@atelier\": {\n            \"url\": \"https://www.atelier-ui.com/r/{name}.json\",\n            \"headers\": { \"Authorization\": \"Bearer ${ATELIER_PRO_KEY}\" }\n        }\n    }\n}\n```\n\nA free install leaves `\"@atelier\"` set to the URL string alone, with no headers. That entry\ncounts as present and carries no key, so a pro add returns a 401. Overwrite it with the\nobject above rather than skipping the step because a `@atelier` key is already there.\n\nNever ask for the key value. Never write it into a file yourself.\n\n## Installing\n\nBoth tiers install by namespace. `@atelier` is in the shadcn registry directory, so the CLI\nresolves it with no setup and writes the entry into `components.json` itself:\n\n```sh\nnpx shadcn@latest add @atelier/<name>\n```\n\nWhat it writes is the bare URL string, which is everything a free item needs. A pro item\nneeds the object form from the section above, in place before the add command runs.\n\nDependencies resolve automatically.\n\n## WebGL\n\nAll WebGL components share one canvas owned by `WebglProvider`. Mount once in the root layout:\n\n```tsx\nimport { WebglProvider } from \"@/components/webgl-provider/webgl-provider\"\n\nexport default function RootLayout({ children }) {\n    return (\n        <html lang=\"en\">\n            <body>\n                <WebglProvider>{children}</WebglProvider>\n            </body>\n        </html>\n    )\n}\n```\n\n- Exactly one `WebglProvider`. Reuse an existing one, never add a second.\n- Never add your own `<Canvas>` from `@react-three/fiber`.\n- Mounted check: `<div data-atelier-webgl>` exists.\n\n## Scroll\n\nScroll components need `SmoothScroll`, mounted once in the root layout. Order vs `WebglProvider` does not matter:\n\n```tsx\n<SmoothScroll>\n    <WebglProvider>{children}</WebglProvider>\n</SmoothScroll>\n```\n\n- Exactly one `SmoothScroll`. It drives Lenis with `autoRaf: false` on Motion's loop, so scroll, animation, and canvas share a frame.\n- Existing Lenis or Locomotive Scroll v5: replace it, or set `autoRaf: false` and drive it from Motion's `frame.update`. Never two Lenis roots.\n- GSAP ScrollSmoother: unsupported with WebGL media. It transforms content away from `window.scrollY`, which positions the planes.\n\n## WebGL media\n\n`WebglImage`, `WebglVideo`, `WebglText` render on top of the real DOM element.\n\n- The element stays at `opacity: 0`. Never `display: none`, it breaks layout, SEO, and pointer events.\n- It needs a size from CSS. No measurable box renders nothing.\n- Fullscreen fixed canvas, so parent `overflow: hidden` does not clip. Order planes with `zIndex`.\n- `object-fit` is read from computed style.\n- `autoReflow` only when an animated parent moves the element. Costs a layout read per frame.\n\n## Page transitions\n\nPut the transition in the layout owning the routes it affects. Root layout covers every route, a route group layout covers that group.\n\n```tsx\nimport { ClipTransition } from \"@/components/clip-transition/clip-transition\"\nimport { TransitionLink, TransitionPage } from \"@/components/page-transition/page-transition\"\n\nexport default function Layout({ children }) {\n    return (\n        <ClipTransition>\n            <nav>\n                <TransitionLink href=\"/work\">Work</TransitionLink>\n            </nav>\n            <TransitionPage>{children}</TransitionPage>\n        </ClipTransition>\n    )\n}\n```\n\n- Needs Next.js 15.3 or later. `TransitionLink` is built on the `Link` `onNavigate` prop,\n  which does not exist before that. Check `next` in package.json first, and stop and say so\n  if it is absent or older. The other components have no such floor.\n- Route content must be wrapped in `TransitionPage`, or nothing animates.\n- Use `TransitionLink`, not the Next.js `Link`.\n- Never nest transitions.\n- Cover `z-index: 60`, band `70`, loading slot `75`. Anything fixed above `75` stays visible.\n- `data-atelier-transitioning` is set on `<html>` while running. Use it to hide persistent UI.\n\n## Lint\n\nThis section applies only to projects linting with `eslint-config-next` 16 or later, which\nis what `create-next-app` sets up. Check for an `eslint.config.*` at the project root and\n`eslint-config-next` in package.json. Biome, oxlint, or no linter at all: skip the rest of\nthis section, there is nothing to do.\n\nreact-three-fiber mutates `camera` and `gl` and reads refs inside `useFrame`.\nThe React Compiler rules in `eslint-config-next` 16 report all of that as errors in the\ninstalled files. The code is correct, the rules just don't model a second reconciler.\n\nWrite the override as part of the install, right after the add command. The errors are\ncertain, so running the linter first only buys you the same list at the cost of reading it.\n\nScope it to the installed paths rather than editing the files.\nThe globs below match both layouts, since the files land under `src/components/` or\n`components/` depending on the project. The second and third cover the shared hooks and\nhelpers, which land outside `components/` and trip the same rules:\n\n```js\n{\n    files: [\n        \"**/components/<name>/**\",\n        \"**/hooks/use-{dom-plane,pointer-uv,render,frame-loop}.ts\",\n        \"**/lib/object-fit.ts\",\n    ],\n    rules: {\n        \"react-hooks/immutability\": \"off\",\n        \"react-hooks/refs\": \"off\",\n        \"react-hooks/preserve-manual-memoization\": \"off\",\n        \"@next/next/no-img-element\": \"off\",\n    },\n}\n```\n\nThe `no-img-element` line is for the `sr-only` fallback list, which is an accessibility\nlayer and not something `next/image` should touch.\n\n## Verify\n\nThe project's build passes. For WebGL components, `[data-atelier-webgl]` appears exactly once.\n\nOpen a browser only if a driver is already set up in the project. Never install one for\nthis, and never sample canvas pixels to prove it drew: the drawing buffer isn't preserved,\nso a working canvas reads back as empty.\n"},{"path":"registry/agent-rules/.agents/skills/atelier-ui/SKILL.md","type":"registry:file","target":"~/.agents/skills/atelier-ui/SKILL.md","content":"---\nname: atelier-ui\ndescription: >\n    Install and wire Atelier UI components (atelier-ui.com) into a React project:\n    shadcn registry setup, the @atelier pro registry and license key, the shared WebGL\n    canvas, smooth scroll, and page transitions. Use this skill when adding, moving, or\n    removing an Atelier component, when running `npx shadcn add` against @atelier or an\n    atelier-ui.com registry URL, and when troubleshooting an installed Atelier component\n    that renders nothing, throws a React Compiler or react-hooks lint error, or breaks\n    scrolling or page transitions - even when the user names only the effect and never\n    says \"Atelier\", such as a background shader, a cursor or hover effect, a scroll\n    effect, a WebGL image, video or text plane, or a page transition.\ncompatibility: Requires Node.js, network access, and a React 19 project using Tailwind CSS v4, such as Next.js or Vite. Page transitions are the exception and require Next.js 15.3 or later.\nmetadata:\n    version: \"1.5.0\"\n---\n\n# Atelier UI\n\nAtelier UI (atelier-ui.com) is a WebGL and Motion system for React. It covers shader\nbackgrounds, cursor and hover effects, scroll-driven animation, WebGL image, video and\ntext planes, 3D galleries, and page transitions for Next.js.\n\nThree things are shared across the catalog instead of living inside each component: one\nWebGL canvas every effect draws into, one Lenis smooth scroll driven on Motion's frame\nloop, and one page transition system. That is what lets many effects coexist on a\nscrolling page without exhausting the browser's WebGL context cap or fighting each other\nfor the scroll position.\n\n## Finding a component\n\nWhen the request names an effect rather than a component, search the catalog first. There\nis nothing to set up: `@atelier` is in the shadcn registry directory, so the CLI resolves\nit before `components.json` has any entry for it.\n\n```sh\nnpx shadcn@latest search @atelier -q \"scroll\"\n```\n\nThat covers all 51 items and writes nothing.\n\nNever run `npx shadcn view` on an Atelier item. It inlines the component's full source, a\nthousand lines of shader code for most of them, and tells you nothing the search result\nand the `Usage:` markdown don't already cover.\n\n## Paths\n\nFiles land in `components/<name>/<name>.tsx`, imported as `@/components/<name>/<name>`.\nThe path repeats the name, so `@/components/webgl-provider` is wrong and\n`@/components/webgl-provider/webgl-provider` is right.\n\nShared hooks and helpers are the exception: they land at `hooks/` and `lib/`, outside\n`components/`. The installed files import them relatively and already resolve. Leave\nthose imports alone.\n\nA `Props:` line in the prompt holds the preview's props as JSX attributes. Pass them as written.\n\nThe add command prints a `Usage:` URL for every item it installs. Fetch it. It returns a\nfew KB of plain markdown holding the images, the props and the mount, ready to paste.\n\nIf no `Usage:` line was printed, the URL is `https://www.atelier-ui.com/r/<name>.md`. Try it\nonce. A 404 there means the doc is genuinely missing, so fall back to the installed source\nand say so in your summary rather than probing for other paths.\n\nFetch it raw, with `curl -fsSL` or whatever returns the bytes unchanged. Follow redirects,\nthe URL may not be on the canonical host. A tool that reads a page through a model and\nhands back a summary will drop image URLs and prop values without saying so, and the\nresult looks fine until it doesn't compile.\n\nDon't reconstruct usage by reading the component source, it's a thousand lines of shader\ncode and the answer isn't in there. Don't open the `Docs:` URL either, that one is the\nrendered page for humans.\n\n## Where to stop\n\nInstall the component, wire it up, confirm the project's build passes. That's the whole job.\n\nDon't install a browser driver, a screenshot tool, or anything else to look at the result.\nDon't fix lint errors inside the installed files, see the lint section below.\nDon't reformat or refactor them either, they're overwritten on the next install.\n\n## Images\n\nSome components take a required array of media, usually `items={[{ src, alt }]}`.\nThey render nothing when it's empty, so the prop is not optional in practice.\n\nThe `Usage:` markdown has working image sources. Use them as written. Don't invent\nplaceholders, don't generate SVGs, and don't go hunting through the repo for assets.\n\n## Install order\n\nIf the add command has already run, the checks below are spent. Skip to Installing, read\nthe `Usage:` markdown, wire the component up, and run the build. Re-running the\nprerequisite and license steps after a successful install verifies nothing.\n\n- [ ] 1. Tailwind CSS v4 present, stop and ask if not\n- [ ] 2. `components.json` at the project root, create it if absent\n- [ ] 3. Pro or free, from what the prompt says, without looking anything up\n- [ ] 4. Pro items only: `ATELIER_PRO_KEY` present, stop and ask if not, then give the `@atelier` entry its license header\n- [ ] 5. Run the add command\n- [ ] 6. Add the lint override, before anything runs a linter\n- [ ] 7. Wire it up, then run the project's build\n\nSteps 1 and 4 are hard stops, never run the add command past a failed one.\nThe sections below cover each step in this order.\n\n## Prerequisites\n\n**Tailwind CSS v4.** Check for `tailwindcss` in dependencies and `@import \"tailwindcss\"` in the global stylesheet.\n\n**Missing:** stop. Say the project has no Tailwind, that Atelier components need it to render, and ask whether to install it. Do not install without an answer, it touches the build config and the global stylesheet. On yes, follow https://tailwindcss.com/docs/installation.\n\n**Present:** continue.\n\n**Next.js 15.3 or later, page transitions only.** Every other component runs on any React 19 setup, Next.js or Vite alike. Page transitions are the exception. Check `next` in package.json, and stop and say so if it is absent or older.\n\n**components.json.** Needed at the project root. Create with `npx shadcn@latest init -d` if absent, no need to ask.\n\n## Pro license\n\nFree and pro items install the same way, but a pro one without a key fails with a 401.\n\nDon't look it up. A copied prompt says so: a pro one tells you to install `agent-rules`\nfirst and set up the license, a free one gives you a single add command. Follow what the\nprompt says and skip the rest of this section when it says nothing about a license.\n\nWith no prompt to go on, install anyway. A pro item without a key fails with a 401 whose\nbody names the variable, which is cheaper than a lookup on every install. Handle it then.\n\nCheck for the key first. The CLI reads `.env.local`, `.env.development.local`, `.env.development`, `.env`:\n\n```sh\ncat .env.local .env.development.local .env.development .env 2>/dev/null | grep -q \"ATELIER_PRO_KEY=.\"\n```\n\nIt exits `0` when a key is set and `1` when it is absent or empty. Don't reach for\n`grep -l` over the same list, that exits `2` either way because most of those files are\nmissing, and a missing key reads exactly like a present one.\n\n**Missing:** stop. Ask whether they added their license key, from their account page, into a git-ignored `.env.local` as `ATELIER_PRO_KEY=...`. Do not add the entry, do not install. An entry referencing an empty var fails free installs too.\n\n**Present:** write the entry below, then install. Do not stop, do not mention the key.\n\n```json\n{\n    \"registries\": {\n        \"@atelier\": {\n            \"url\": \"https://www.atelier-ui.com/r/{name}.json\",\n            \"headers\": { \"Authorization\": \"Bearer ${ATELIER_PRO_KEY}\" }\n        }\n    }\n}\n```\n\nA free install leaves `\"@atelier\"` set to the URL string alone, with no headers. That entry\ncounts as present and carries no key, so a pro add returns a 401. Overwrite it with the\nobject above rather than skipping the step because a `@atelier` key is already there.\n\nNever ask for the key value. Never write it into a file yourself.\n\n## Installing\n\nBoth tiers install by namespace. `@atelier` is in the shadcn registry directory, so the CLI\nresolves it with no setup and writes the entry into `components.json` itself:\n\n```sh\nnpx shadcn@latest add @atelier/<name>\n```\n\nWhat it writes is the bare URL string, which is everything a free item needs. A pro item\nneeds the object form from the section above, in place before the add command runs.\n\nDependencies resolve automatically.\n\n## WebGL\n\nAll WebGL components share one canvas owned by `WebglProvider`. Mount once in the root layout:\n\n```tsx\nimport { WebglProvider } from \"@/components/webgl-provider/webgl-provider\"\n\nexport default function RootLayout({ children }) {\n    return (\n        <html lang=\"en\">\n            <body>\n                <WebglProvider>{children}</WebglProvider>\n            </body>\n        </html>\n    )\n}\n```\n\n- Exactly one `WebglProvider`. Reuse an existing one, never add a second.\n- Never add your own `<Canvas>` from `@react-three/fiber`.\n- Mounted check: `<div data-atelier-webgl>` exists.\n\n## Scroll\n\nScroll components need `SmoothScroll`, mounted once in the root layout. Order vs `WebglProvider` does not matter:\n\n```tsx\n<SmoothScroll>\n    <WebglProvider>{children}</WebglProvider>\n</SmoothScroll>\n```\n\n- Exactly one `SmoothScroll`. It drives Lenis with `autoRaf: false` on Motion's loop, so scroll, animation, and canvas share a frame.\n- Existing Lenis or Locomotive Scroll v5: replace it, or set `autoRaf: false` and drive it from Motion's `frame.update`. Never two Lenis roots.\n- GSAP ScrollSmoother: unsupported with WebGL media. It transforms content away from `window.scrollY`, which positions the planes.\n\n## WebGL media\n\n`WebglImage`, `WebglVideo`, `WebglText` render on top of the real DOM element.\n\n- The element stays at `opacity: 0`. Never `display: none`, it breaks layout, SEO, and pointer events.\n- It needs a size from CSS. No measurable box renders nothing.\n- Fullscreen fixed canvas, so parent `overflow: hidden` does not clip. Order planes with `zIndex`.\n- `object-fit` is read from computed style.\n- `autoReflow` only when an animated parent moves the element. Costs a layout read per frame.\n\n## Page transitions\n\nPut the transition in the layout owning the routes it affects. Root layout covers every route, a route group layout covers that group.\n\n```tsx\nimport { ClipTransition } from \"@/components/clip-transition/clip-transition\"\nimport { TransitionLink, TransitionPage } from \"@/components/page-transition/page-transition\"\n\nexport default function Layout({ children }) {\n    return (\n        <ClipTransition>\n            <nav>\n                <TransitionLink href=\"/work\">Work</TransitionLink>\n            </nav>\n            <TransitionPage>{children}</TransitionPage>\n        </ClipTransition>\n    )\n}\n```\n\n- Needs Next.js 15.3 or later. `TransitionLink` is built on the `Link` `onNavigate` prop,\n  which does not exist before that. Check `next` in package.json first, and stop and say so\n  if it is absent or older. The other components have no such floor.\n- Route content must be wrapped in `TransitionPage`, or nothing animates.\n- Use `TransitionLink`, not the Next.js `Link`.\n- Never nest transitions.\n- Cover `z-index: 60`, band `70`, loading slot `75`. Anything fixed above `75` stays visible.\n- `data-atelier-transitioning` is set on `<html>` while running. Use it to hide persistent UI.\n\n## Lint\n\nThis section applies only to projects linting with `eslint-config-next` 16 or later, which\nis what `create-next-app` sets up. Check for an `eslint.config.*` at the project root and\n`eslint-config-next` in package.json. Biome, oxlint, or no linter at all: skip the rest of\nthis section, there is nothing to do.\n\nreact-three-fiber mutates `camera` and `gl` and reads refs inside `useFrame`.\nThe React Compiler rules in `eslint-config-next` 16 report all of that as errors in the\ninstalled files. The code is correct, the rules just don't model a second reconciler.\n\nWrite the override as part of the install, right after the add command. The errors are\ncertain, so running the linter first only buys you the same list at the cost of reading it.\n\nScope it to the installed paths rather than editing the files.\nThe globs below match both layouts, since the files land under `src/components/` or\n`components/` depending on the project. The second and third cover the shared hooks and\nhelpers, which land outside `components/` and trip the same rules:\n\n```js\n{\n    files: [\n        \"**/components/<name>/**\",\n        \"**/hooks/use-{dom-plane,pointer-uv,render,frame-loop}.ts\",\n        \"**/lib/object-fit.ts\",\n    ],\n    rules: {\n        \"react-hooks/immutability\": \"off\",\n        \"react-hooks/refs\": \"off\",\n        \"react-hooks/preserve-manual-memoization\": \"off\",\n        \"@next/next/no-img-element\": \"off\",\n    },\n}\n```\n\nThe `no-img-element` line is for the `sr-only` fallback list, which is an accessibility\nlayer and not something `next/image` should touch.\n\n## Verify\n\nThe project's build passes. For WebGL components, `[data-atelier-webgl]` appears exactly once.\n\nOpen a browser only if a driver is already set up in the project. Never install one for\nthis, and never sample canvas pixels to prove it drew: the drawing buffer isn't preserved,\nso a working canvas reads back as empty.\n"}]}