[
  {
    "path": ".changeset/README.md",
    "content": "# Changesets\n\nHello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works\nwith multi-package repos, or single-package repos to help you version and publish your code. You can\nfind the full documentation for it [in our repository](https://github.com/changesets/changesets)\n\nWe have a quick list of common questions to get you started engaging with this project in\n[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)\n"
  },
  {
    "path": ".changeset/config.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/@changesets/config@1.6.0/schema.json\",\n  \"changelog\": \"@changesets/changelog-git\",\n  \"commit\": true,\n  \"linked\": [],\n  \"access\": \"public\",\n  \"baseBranch\": \"master\",\n  \"updateInternalDependencies\": \"minor\",\n  \"ignore\": [],\n  \"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH\": {\n    \"onlyUpdatePeerDependentsWhenOutOfRange\": true\n  }\n}\n"
  },
  {
    "path": ".codesandbox/ci.json",
    "content": "{\n  \"sandboxes\": [\"/example\"],\n  \"node\": \"18\"\n}\n"
  },
  {
    "path": ".eslintignore",
    "content": "dist/\nnode_modules/\n.yarn/"
  },
  {
    "path": ".eslintrc.json",
    "content": "{\n  \"env\": {\n    \"browser\": true,\n    \"es6\": true,\n    \"node\": true\n  },\n  \"extends\": [\n    \"prettier\",\n    \"plugin:prettier/recommended\",\n    \"plugin:react-hooks/recommended\",\n    \"plugin:import/recommended\",\n    \"plugin:@react-three/recommended\"\n  ],\n  \"plugins\": [\"@typescript-eslint\", \"react\", \"react-hooks\", \"import\", \"jest\", \"prettier\", \"@react-three\"],\n  \"parser\": \"@typescript-eslint/parser\",\n  \"parserOptions\": {\n    \"ecmaFeatures\": {\n      \"jsx\": true\n    },\n    \"ecmaVersion\": 2018,\n    \"sourceType\": \"module\",\n    \"rules\": {\n      \"curly\": [\"warn\", \"multi-line\", \"consistent\"],\n      \"no-console\": \"off\",\n      \"no-empty-pattern\": \"warn\",\n      \"no-duplicate-imports\": \"error\",\n      \"import/no-unresolved\": \"off\",\n      \"import/export\": \"error\",\n      // https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/FAQ.md#eslint-plugin-import\n      // We recommend you do not use the following import/* rules, as TypeScript provides the same checks as part of standard type checking:\n      \"import/named\": \"off\",\n      \"import/namespace\": \"off\",\n      \"import/default\": \"off\",\n      \"no-unused-vars\": [\"warn\", { \"argsIgnorePattern\": \"^_\", \"varsIgnorePattern\": \"^_\" }],\n      \"@typescript-eslint/no-unused-vars\": [\"warn\", { \"argsIgnorePattern\": \"^_\", \"varsIgnorePattern\": \"^_\" }],\n      \"@typescript-eslint/no-use-before-define\": \"off\",\n      \"@typescript-eslint/no-empty-function\": \"off\",\n      \"@typescript-eslint/no-empty-interface\": \"off\",\n      \"@typescript-eslint/no-explicit-any\": \"off\",\n      \"jest/consistent-test-it\": [\"error\", { \"fn\": \"it\", \"withinDescribe\": \"it\" }]\n    }\n  },\n  \"settings\": {\n    \"react\": {\n      \"version\": \"detect\"\n    },\n    \"import/extensions\": [\".js\", \".jsx\", \".ts\", \".tsx\"],\n    \"import/parsers\": {\n      \"@typescript-eslint/parser\": [\".js\", \".jsx\", \".ts\", \".tsx\"]\n    },\n    \"import/resolver\": {\n      \"node\": {\n        \"extensions\": [\".js\", \".jsx\", \".ts\", \".tsx\", \".json\"],\n        \"paths\": [\"src\"]\n      },\n      \"alias\": {\n        \"extensions\": [\".js\", \".jsx\", \".ts\", \".tsx\", \".json\"],\n        \"map\": [[\"@react-three/fiber\", \"./packages/fiber/src/web\"]]\n      }\n    }\n  },\n  \"overrides\": [\n    {\n      \"files\": [\"src\"],\n      \"parserOptions\": {\n        \"project\": \"./tsconfig.json\"\n      }\n    }\n  ],\n  \"rules\": {\n    \"import/no-unresolved\": \"off\",\n    \"import/named\": \"off\",\n    \"import/namespace\": \"off\",\n    \"import/no-named-as-default-member\": \"off\"\n  }\n}\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: drcmda\nopen_collective: react-three-fiber\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n---\n\n👋 hi there, for issues that aren't that pressing, that could be related to threejs etc, please consider [github discussions](https://github.com/pmndrs/react-three-fiber/discussions).\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n---\n"
  },
  {
    "path": ".github/issue_template.md",
    "content": "Hi, 👋\n\nif this is about a bug, before you go ahead, please do us a favour and make sure to check the [threejs issue tracker](https://github.com/mrdoob/three.js/issues) for the problem you are experiencing. This library is just a soft wrap around threejs without direct dependencies. So if something is flipped upside down, or doesn't project the way you intent to, there's a good chance others will have experienced the same issue with plain threejs.\n"
  },
  {
    "path": ".github/workflows/canary.yml",
    "content": "name: Canary Release\n\non:\n  push:\n    branches: [v10]\n  workflow_dispatch:\n\npermissions:\n  id-token: write # Required for npm OIDC\n  contents: read\n\njobs:\n  canary:\n    name: Publish canary\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v4\n\n      - name: Install pnpm\n        uses: pnpm/action-setup@v4\n        with:\n          run_install: false\n\n      - name: Use Node 22\n        uses: actions/setup-node@v4\n        with:\n          node-version: 22\n          cache: 'pnpm'\n          registry-url: 'https://registry.npmjs.org'\n\n      - name: Update npm for OIDC\n        run: npm install -g npm@latest\n\n      - name: Install deps\n        run: pnpm install\n\n      - name: Build\n        run: pnpm build\n\n      - name: Set canary versions\n        run: |\n          CANARY_VERSION=\"10.0.0-canary.$(git rev-parse --short HEAD)\"\n          echo \"Publishing canary version: $CANARY_VERSION\"\n          cd packages/fiber && npm version $CANARY_VERSION --no-git-tag-version\n          cd ../eslint-plugin && npm version $CANARY_VERSION --no-git-tag-version\n          cd ../test-renderer && npm version $CANARY_VERSION --no-git-tag-version\n\n      - name: Publish to npm\n        run: |\n          pnpm --filter @react-three/fiber publish --tag canary --no-git-checks --provenance\n          pnpm --filter @react-three/eslint-plugin publish --tag canary --no-git-checks --provenance\n          pnpm --filter @react-three/test-renderer publish --tag canary --no-git-checks --provenance\n"
  },
  {
    "path": ".github/workflows/docs.yml",
    "content": "name: Build documentation and deploy to GitHub Pages\non:\n  push:\n    branches: ['master']\n  workflow_dispatch:\n\n# Cancel previous run (see: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency)\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  build:\n    uses: pmndrs/docs/.github/workflows/build.yml@v3\n    with:\n      mdx: 'docs'\n      libname: 'React Three Fiber'\n      libname_short: 'r3f'\n      home_redirect: '/getting-started/introduction'\n      icon: '🇨🇭'\n      logo: '/logo.jpg'\n      github: 'https://github.com/pmndrs/react-three-fiber'\n      discord: 'https://discord.com/channels/740090768164651008/740093168770613279'\n\n  deploy:\n    needs: build\n    runs-on: ubuntu-latest\n\n    # Grant GITHUB_TOKEN the permissions required to make a Pages deployment\n    permissions:\n      pages: write # to deploy to Pages\n      id-token: write # to verify the deployment originates from an appropriate source\n\n    # Deploy to the github-pages environment\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n\n    steps:\n      - id: deployment\n        uses: actions/deploy-pages@v4\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Test\non:\n  push:\n    branches:\n      - 'master'\n  pull_request: {}\njobs:\n  build:\n    name: Build, lint, and test (React ${{ matrix.react-version }})\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        react-version:\n          - 19.0.0\n          - latest\n\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v4\n\n      - name: Use Node 20\n        uses: actions/setup-node@v4\n        with:\n          node-version: 20\n\n      - name: Cache node_modules and Yarn cache\n        uses: actions/cache@v4\n        with:\n          path: |\n            **/node_modules\n            .yarn/cache\n          key: >\n            ${{ runner.os }}-node20-react-${{ matrix.react-version }}-${{ hashFiles('**/yarn.lock') }}\n          restore-keys: |\n            ${{ runner.os }}-node20-react-${{ matrix.react-version }}-\n\n      - name: Install deps and build (with cache)\n        uses: bahmutov/npm-install@v1\n        with:\n          install-command: yarn --immutable --silent\n\n      - name: Override React version (${{ matrix.react-version }})\n        run: |\n          yarn add @types/react@${{ matrix.react-version }} react@${{ matrix.react-version }} @types/react-dom@${{ matrix.react-version }} react-dom@${{ matrix.react-version }} --dev -W\n\n      - name: Check types\n        run: yarn run typecheck\n\n      - name: Check lint\n        run: yarn run eslint\n\n      - name: Build\n        run: yarn run build\n\n      - name: Rsbuild (strict)\n        run: yarn add @rsbuild/core -D -W && yarn rsbuild build --root ./packages/fiber\n\n      - name: Jest run\n        run: yarn run dev && yarn run test\n\n      - name: Report Fiber size\n        run: yarn run analyze-fiber\n\n      - name: Report Test Renderer size\n        run: yarn run analyze-test\n\n      - name: Check formatting\n        run: yarn run format\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules/\r\ncoverage/\r\ndist/\r\nbuild/\r\ntypes/\r\npackages/fiber/react-reconciler/\r\n# commit types in src\r\n!packages/*/src/types/\r\nThumbs.db\r\nehthumbs.db\r\nDesktop.ini\r\n$RECYCLE.BIN/\r\n.DS_Store\r\n.vscode\r\n.docz/\r\npackage-lock.json\r\ncoverage/\r\n.idea\r\nyarn-error.log\r\n.size-snapshot.json\r\n__tests__/__image_snapshots__/__diff_output__\r\n"
  },
  {
    "path": ".husky/.gitignore",
    "content": "_\n"
  },
  {
    "path": ".husky/pre-commit",
    "content": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nnpx pretty-quick --staged\n"
  },
  {
    "path": ".prettierignore",
    "content": "dist/\ncoverage/\nnode_modules/\npackages/fiber/react-reconciler/\n.yarn/\n*.gltf\n*.mdx"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"semi\": false,\n  \"trailingComma\": \"all\",\n  \"singleQuote\": true,\n  \"tabWidth\": 2,\n  \"printWidth\": 120,\n  \"bracketSameLine\": true,\n  \"endOfLine\": \"auto\"\n}\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "## Contributing\n\nThis project uses [semantic commits](https://conventionalcommits.org) and [semver](https://semver.org).\n\nTo get started, make sure you have [Node](https://nodejs.org) and [Yarn 1](https://classic.yarnpkg.com) (newer versions of Yarn do not work) installed. Install dependencies with:\n\n```bash\nyarn\n```\n\n[Preconstruct](https://github.com/preconstruct/preconstruct) will automatically build and link packages for local development via symlinks. If you ever need to do this manually, try running:\n\n```bash\nyarn dev\n```\n\n> **Note**: Some Windows users may need to [enable developer mode](https://howtogeek.com/292914/what-is-developer-mode-in-windows-10) if experiencing `EPERM: operation not permitted, symlink` with Preconstruct. If this persists, you might be running on an unsupported drive/format. In which case, consider using [Docker](https://docs.docker.com/docker-for-windows).\n\n### Development\n\nLocally run examples against the library with:\n\n```bash\nyarn examples\n```\n\n### Testing\n\nRun test suites against the library with:\n\n```bash\nyarn test\n\n# or, to test live against changes\nyarn test:watch\n```\n\nIf your code invalidates a snapshot, you can update it with:\n\n```bash\nyarn test -u\n```\n\n> **Note**: Use discretion when updating snapshots, as they represent the integrity of the package.\n>\n> If the difference is complex or you're unsure of the changes, leave it for review and we'll unblock it.\n\n### Publishing\n\nWe use [atlassian/changesets](https://github.com/atlassian/changesets) to publish our packages, which will automatically document and version changes.\n\nTo publish a release on NPM, run the following and complete the dialog (see [FAQ](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)):\n\n```bash\n# Describe the changes you've made as you would semantic commits for CHANGELOG.md\nyarn changeset:add\n\n# Tag which packages should receive an update and be published.\nyarn vers\n\n# Commit and publish changes to NPM.\nyarn release\n```\n\nWe don't have automatic CI deployments yet, so make sure to [create a release](https://github.com/pmndrs/react-three-fiber/releases/new) on GitHub to notify people when it's ready. Choose or create the version generated by your changeset, and you can leave the rest to auto-fill via the \"Generate release notes\" button to describe PRs since the last release.\n\n### Prerelease\n\nFollow the same steps as before, but specify a tag for [prerelease mode](https://github.com/changesets/changesets/blob/main/docs/prereleases.md) with:\n\n```bash\nyarn changeset pre enter <alpha | beta | rc>\n```\n\nTo cancel or leave prerelease mode, try running:\n\n```bash\nyarn changeset pre exit\n```\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019-2025 Poimandres\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "babel.config.js",
    "content": "module.exports = {\n  plugins: [],\n  presets: [\n    [\n      '@babel/preset-env',\n      {\n        include: [\n          '@babel/plugin-proposal-class-properties',\n          '@babel/plugin-proposal-optional-chaining',\n          '@babel/plugin-proposal-nullish-coalescing-operator',\n          '@babel/plugin-proposal-numeric-separator',\n          '@babel/plugin-proposal-logical-assignment-operators',\n        ],\n        bugfixes: true,\n        loose: true,\n        modules: false,\n        targets: '> 1%, not dead, not ie 11, not op_mini all',\n      },\n    ],\n    ['@babel/preset-react', { runtime: 'automatic' }],\n    '@babel/preset-typescript',\n  ],\n}\n"
  },
  {
    "path": "docs/API/additional-exports.mdx",
    "content": "---\ntitle: Additional Exports\nnav: 8\n---\n\n| export               | usage                                                          |\n| -------------------- | -------------------------------------------------------------- |\n| `addEffect`          | Adds a global render callback which is called each frame       |\n| `addAfterEffect`     | Adds a global after-render callback which is called each frame |\n| `addTail`            | Adds a global callback which is called when rendering stops    |\n| `buildGraph`         | Collects nodes and materials from a THREE.Object3D             |\n| `flushGlobalEffects` | Flushes global render-effects for when manually driving a loop |\n| `flushSync`          | Force React to flush any updates synchronously and immediately |\n| `invalidate`         | Forces view global invalidation                                |\n| `advance`            | Advances the frameloop (given that it's set to 'never')        |\n| `extend`             | Extends the native-object catalogue                            |\n| `createPortal`       | Creates a portal (it's a React feature for re-parenting)       |\n| `createRoot`         | Creates a root that can render three JSX into a canvas         |\n| `events`             | Dom pointer-event system                                       |\n| `applyProps`         | `applyProps(element, props)` sets element properties,          |\n| `act`                | usage with react-testing                                       |\n| `useInstanceHandle`  | Exposes react-internal local state from `instance.__r3f`       |\n|                      |                                                                |\n"
  },
  {
    "path": "docs/API/canvas.mdx",
    "content": "---\ntitle: Canvas\ndescription: The Canvas object is your portal into three.js\nnav: 4\n---\n\nThe `Canvas` object is where you start to define your React Three Fiber Scene.\n\n```jsx\nimport React from 'react'\nimport { Canvas } from '@react-three/fiber'\n\nconst App = () => (\n  <Canvas>\n    <pointLight position={[10, 10, 10]} />\n    <mesh>\n      <sphereGeometry />\n      <meshStandardMaterial color=\"hotpink\" />\n    </mesh>\n  </Canvas>\n)\n```\n\n## Properties\n\n| Prop            | Description                                                                                                                                       | Default                                                    |\n| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |\n| children        | three.js JSX elements or regular components                                                                                                       |                                                            |\n| fallback        | optional DOM JSX elements or regular components in case GL is not supported                                                                       |                                                            |\n| gl              | Props that go into the default renderer. Accepts sync/async callback with default props `gl={defaults => new Renderer({ ...defaults })}`          | `{}`                                                       |\n| camera          | Props that go into the default camera, or your own `THREE.Camera`                                                                                 | `{ fov: 75, near: 0.1, far: 1000, position: [0, 0, 5] }`   |\n| scene           | Props that go into the default scene, or your own `THREE.Scene`                                                                                   | `{}`                                                       |\n| shadows         | Props that go into `gl.shadowMap`, can be set true for `PCFsoft` or one of the following: 'basic', 'percentage', 'soft', 'variance'               | `false`                                                    |\n| raycaster       | Props that go into the default raycaster                                                                                                          | `{}`                                                       |\n| frameloop       | Render mode: always, demand, never                                                                                                                | `always`                                                   |\n| resize          | Resize config, see react-use-measure's options                                                                                                    | `{ scroll: true, debounce: { scroll: 50, resize: 0 } }`    |\n| orthographic    | Creates an orthographic camera                                                                                                                    | `false`                                                    |\n| dpr             | Pixel-ratio, use `window.devicePixelRatio`, or automatic: [min, max]                                                                              | `[1, 2]`                                                   |\n| legacy          | Enables THREE.ColorManagement in three r139 or later                                                                                              | `false`                                                    |\n| linear          | Switch off automatic sRGB color space and gamma correction                                                                                        | `false`                                                    |\n| events          | Configuration for the event manager, as a function of state                                                                                       | `import { events } from \"@react-three/fiber\"`              |\n| eventSource     | The source where events are being subscribed to, HTMLElement                                                                                      | `React.RefObject<HTMLElement>`, `gl.domElement.parentNode` |\n| eventPrefix     | The event prefix that is cast into canvas pointer x/y events                                                                                      | `offset`                                                   |\n| flat            | Use `THREE.NoToneMapping` instead of `THREE.ACESFilmicToneMapping`                                                                                | `false`                                                    |\n| onCreated       | Callback after the canvas has rendered (but not yet committed)                                                                                    | `(state) => {}`                                            |\n| onPointerMissed | Response for pointer clicks that have missed any target                                                                                           | `(event) => {}`                                            |\n\n## Defaults\n\nCanvas uses [createRoot](#createroot) which will create a translucent `THREE.WebGLRenderer` with the following constructor args:\n\n- antialias=true\n- alpha=true\n- powerPreference=\"high-performance\"\n\nand with the following properties:\n\n- outputColorSpace = THREE.SRGBColorSpace\n- toneMapping = THREE.ACESFilmicToneMapping\n\nIt will also create the following scene internals:\n\n- A `THREE.Perspective` camera\n- A `THREE.Orthographic` cam if `orthographic` is true\n- A `THREE.PCFSoftShadowMap` if `shadows` is true\n- A `THREE.Scene` (into which all the JSX is rendered) and a `THREE.Raycaster`\n\nIn recent versions of threejs, `THREE.ColorManagement.enabled` will be set to `true` to enable automatic conversion of colors according to the renderer's configured color space. R3F will handle texture color space conversion. For more on this topic, see [https://threejs.org/docs/#manual/en/introduction/Color-management](https://threejs.org/docs/#manual/en/introduction/Color-management).\n\n## Errors and fallbacks\n\nOn some systems WebGL may not be supported, you can provide a fallback component that will be rendered instead of the canvas:\n\n```jsx\n<Canvas fallback={<div>Sorry no WebGL supported!</div>}>\n  <mesh />\n</Canvas>\n```\n\nYou should also safeguard the canvas against WebGL context crashes, for instance if users have the GPU disabled or GPU drivers are faulty.\n\n```jsx\nimport { useErrorBoundary } from 'use-error-boundary'\n\nfunction App() {\n  const { ErrorBoundary, didCatch, error } = useErrorBoundary()\n  return didCatch ? (\n    <div>{error.message}</div>\n  ) : (\n    <ErrorBoundary>\n      <Canvas>\n        <mesh />\n      </Canvas>\n    </ErrorBoundary>\n  )\n}\n```\n\n> [!NOTE]\n> Ideally, and if possible, your fallback is a seamless, visual replacement for what the canvas would have otherwise rendered.\n\n## WebGPU\n\nRecent Three.js now includes a WebGPU renderer. While still a work in progress and not fully backward-compatible with all of Three's features, the renderer requires an async initialization method. R3F streamlines this by allowing the gl prop to return a promise.\n\n```tsx\nimport * as THREE from 'three/webgpu'\nimport * as TSL from 'three/tsl'\nimport { Canvas, extend, useFrame, useThree } from '@react-three/fiber'\n\ndeclare module '@react-three/fiber' {\n  interface ThreeElements extends ThreeToJSXElements<typeof THREE> {}\n}\n\nextend(THREE as any)\n\nexport default () => (\n  <Canvas\n    gl={async (props) => {\n      const renderer = new THREE.WebGPURenderer(props as any)\n      await renderer.init()\n      return renderer\n    }}>\n      <mesh>\n        <meshBasicNodeMaterial />\n        <boxGeometry />\n      </mesh>\n  </Canvas>\n)\n```\n\n## Custom Canvas\n\nR3F can render to a root, similar to how `react-dom` and all the other React renderers work. This allows you to shave off `react-dom` (~40kb), `react-use-measure` (~3kb) and, if you don't need them, `pointer-events` (~7kb) (you need to explicitly import `events` and add them to the config otherwise).\n\nRoots have the same options and properties as `Canvas`, but you are responsible for resizing it. It requires an existing DOM `<canvas>` object into which it renders.\n\n### CreateRoot\n\nCreates a root targeting a canvas, rendering JSX.\n\n```jsx\nimport * as THREE from 'three'\nimport { extend, createRoot, events } from '@react-three/fiber'\n\n// Register the THREE namespace as native JSX elements.\n// See below for notes on tree-shaking\nextend(THREE)\n\n// Create a react root\nconst root = createRoot(document.querySelector('canvas'))\n\nasync function app() {\n  // Configure the root, inject events optionally, set camera, etc\n  // This *must* be called before render, and it must be awaited\n  await root.configure({ events, camera: { position: [0, 0, 50] } })\n\n  // createRoot by design is not responsive, you have to take care of resize yourself\n  window.addEventListener('resize', () => {\n    root.configure({ size: { width: window.innerWidth, height: window.innerHeight } })\n  })\n\n  // Trigger resize\n  window.dispatchEvent(new Event('resize'))\n\n  // Render entry point\n  root.render(<App />)\n\n  // Unmount and dispose of memory\n  // root.unmount()\n}\n\napp()\n```\n\n## Tree-shaking\n\nNew with v8, the underlying reconciler no longer pulls in the THREE namespace automatically.\n\nThis enables a granular catalogue which also enables tree-shaking via the `extend` API:\n\n```jsx\nimport { extend, createRoot } from '@react-three/fiber'\nimport { Mesh, BoxGeometry, MeshStandardMaterial } from 'three'\n\nextend({ Mesh, BoxGeometry, MeshStandardMaterial })\n\ncreateRoot(canvas).render(\n  <>\n    <mesh>\n      <boxGeometry />\n      <meshStandardMaterial />\n    </mesh>\n  </>,\n)\n```\n\nThere's an [official babel plugin](https://github.com/pmndrs/react-three-babel) which will do this for you automatically:\n\n```jsx\n// In:\n\nimport { createRoot } from '@react-three/fiber'\n\ncreateRoot(canvasNode).render(\n  <mesh>\n    <boxGeometry />\n    <meshStandardMaterial />\n  </mesh>,\n)\n\n// Out:\n\nimport { createRoot, extend } from '@react-three/fiber'\nimport { Mesh as _Mesh, BoxGeometry as _BoxGeometry, MeshStandardMaterial as _MeshStandardMaterial } from 'three'\n\nextend({\n  Mesh: _Mesh,\n  BoxGeometry: _BoxGeometry,\n  MeshStandardMaterial: _MeshStandardMaterial,\n})\n\ncreateRoot(canvasNode).render(\n  <mesh>\n    <boxGeometry />\n    <meshStandardMaterial />\n  </mesh>,\n)\n```\n"
  },
  {
    "path": "docs/API/events.mdx",
    "content": "---\ntitle: Events\ndescription: All the events you can hook up to\nnav: 7\n---\n\n`three.js` objects that implement their own `raycast` method (meshes, lines, etc) can be interacted with by declaring events on them. We support pointer events, clicks and wheel-scroll. Events contain the browser event as well as the `three.js` event data (object, point, distance, etc). You may want to [polyfill](https://github.com/jquery/PEP) them, if that's a concern.\n\nAdditionally, there's a special `onUpdate` that is called every time the object gets fresh props, which is good for things like `self => (self.verticesNeedUpdate = true)`.\n\nAlso notice the `onPointerMissed` on the canvas element, which fires on clicks that haven't hit _any_ meshes.\n\n```jsx\n<mesh\n  onClick={(e) => console.log('click')}\n  onContextMenu={(e) => console.log('context menu')}\n  onDoubleClick={(e) => console.log('double click')}\n  onWheel={(e) => console.log('wheel spins')}\n  onPointerUp={(e) => console.log('up')}\n  onPointerDown={(e) => console.log('down')}\n  onPointerOver={(e) => console.log('over')}\n  onPointerOut={(e) => console.log('out')}\n  onPointerEnter={(e) => console.log('enter')} // see note 1\n  onPointerLeave={(e) => console.log('leave')} // see note 1\n  onPointerMove={(e) => console.log('move')}\n  onPointerMissed={() => console.log('missed')}\n  onUpdate={(self) => console.log('props have been updated')}\n/>\n```\n\n### Event data\n\n```jsx\n({\n  ...DomEvent                   // All the original event data\n  ...Intersection                 // All of Three's intersection data - see note 2\n  intersections: Intersection[]    // The first intersection of each intersected object\n  object: Object3D              // The object that was actually hit\n  eventObject: Object3D         // The object that registered the event\n  unprojectedPoint: Vector3     // Camera-unprojected point\n  ray: Ray                      // The ray that was used to strike the object\n  camera: Camera                // The camera that was used in the raycaster\n  sourceEvent: DomEvent         // A reference to the host event\n  delta: number                 // Distance between mouse down and mouse up event in pixels\n}) => ...\n```\n\n### How the event-system works, bubbling and capture\n\n> [!NOTE]\n> - `pointerenter` and `pointerleave` events work exactly the same as pointerover and pointerout.\n> - `pointerenter` and `pointerleave` semantics are not implemented.\n\n> [!NOTE]\n> Some events (such as `pointerout`) happen when there is no intersection between `eventObject` and\nthe ray. When this happens, the event will contain intersection data from a previous event with\nthis object.\n\n### Event propagation (bubbling)\n\nPropagation works a bit differently to the DOM because objects can occlude each other in 3D. The `intersections` array in the event includes all objects intersecting the ray, not just the nearest. Only the first intersection with each object is included.\nThe event is first delivered to the object nearest the camera, and then bubbles up through its ancestors like in the DOM. After that, it is delivered to the next nearest object, and then its ancestors, and so on. This means objects are transparent to pointer events by default, even if the object handles the event.\n\n`event.stopPropagation()` doesn't just stop this event from bubbling up, it also stops it from being delivered to farther objects (objects behind this one). All other objects, nearer or farther, no longer count as being hit while the pointer is over this object. If they were previously delivered pointerover events, they will immediately be delivered pointerout events. If you want an object to block pointer events from objects behind it, it needs to have an event handler as follows:\n\n```jsx\nonPointerOver={e => {\n  e.stopPropagation()\n  // ...\n}}\n```\n\neven if you don't want this object to respond to the pointer event. If you do want to handle the event as well as using `stopPropagation()`, remember that the pointerout events will happen **during** the `stopPropagation()` call. You probably want your other event handling to happen after this.\n\n### Pointer capture\n\nBecause events go to all intersected objects, capturing the pointer also works differently. In the DOM, the capturing object **replaces** the hit test, but in React Three Fiber, the capturing object is **added** to the hit test result: if the capturing object was not hit, then all of the hit objects (and their ancestors) get the event first, followed by the capturing object and its ancestors. The capturing object can also use `event.stopPropagation()` so that objects that really were hit get pointerout events.\n\nNote that you can access the `setPointerCapture` and `releasePointerCapture` methods **only** via `event.target`: they don't get added to the `Object3D` instances in the scene graph.\n\n`setPointerCapture` and `releasePointerCapture` take a `pointerId` parameter like in the DOM, but for now they don't have support for multiple active pointers. PRs are welcome!\n\n```jsx\nonPointerDown={e => {\n  // Only the mesh closest to the camera will be processed\n  e.stopPropagation()\n  // You may optionally capture the target\n  e.target.setPointerCapture(e.pointerId)\n}}\nonPointerUp={e => {\n  e.stopPropagation()\n  // Optionally release capture\n  e.target.releasePointerCapture(e.pointerId)\n}}\n```\n\n### Customizing the event settings\n\nFor some advanced usage it's possible to customize the setting of the event manager globally with the `events` prop on `<Canvas/>`:\n\n```tsx\nimport { Canvas, events } from '@react-three/fiber'\n\nconst eventManagerFactory: Parameters<typeof Canvas>[0]['events'] = (state) => ({\n  // Default configuration\n  ...events(state),\n\n  // Determines if the event layer is active\n  enabled: true,\n\n  // Event layer priority, higher prioritized layers come first and may stop(-propagate) lower layer\n  priority: 1,\n\n  // The filter can re-order or re-structure the intersections\n  filter: (items: THREE.Intersection[], state: RootState) => items,\n\n  // The compute defines how pointer events are translated into the raycaster and pointer vector2\n  compute: (event: DomEvent, state: RootState, previous?: RootState) => {\n    state.pointer.set((event.offsetX / state.size.width) * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1)\n    state.raycaster.setFromCamera(state.pointer, state.camera)\n  },\n\n  // Find more configuration default on ./packages/fiber/src/web/events.ts\n  // And type definitions in ./packages/fiber/src/core/events.ts\n})\n\nfunction App() {\n  return (\n    <Canvas events={eventManagerFactory}>\n}\n```\n\n### Using a different target element\n\nThere are cases in which you may want to connect the event handlers to another DOM element instead of the canvas. This is usually done to have events on a shared parent, which allows both the canvas, and dom overlays to receive events.\n\nYou can either use the event manager:\n\n```jsx\nconst events => useThree(state => state.events)\nuseEffect(() => {\n  state.events.connect(domNode)\n```\n\nOr, the `eventSource` shortcut on the canvas (DOM only), which accepts dom-nodes and React.RefObjects to dom-nodes.\n\n```jsx\nfunction App() {\n  const target = useRef()\n  return (\n    <div ref={target}>\n      <Canvas eventSource={target.current}>\n```\n\n### Using a different prefix (DOM only)\n\nBy default Fiber will use offsetX/offsetY to set up the raycaster. You can change this with the `eventPrefix` shortcut.\n\n```jsx\nfunction App() {\n  return (\n    <Canvas eventPrefix=\"client\">\n```\n\n### Allow raycast without user interaction\n\nBy default Fiber will only raycast when the user is interacting with the canvas. If, for instance, the camera moves a hoverable object underneath the cursor it will not trigger a hover event. If this is wanted behaviour you can force a raycast by executing `update()`, call it whenever necessary.\n\n```jsx\nconst events => useThree(state => state.events)\nuseEffect(() => {\n  // Will trigger a onPointerMove with the last-known pointer event\n  state.events.update()\n```\n\nYou can abstract this into more complex logic.\n\n```jsx\nfunction RaycastWhenCameraMoves() {\n  const matrix = new THREE.Matrix4()\n  useFrame((state) => {\n    // Act only when the camera has moved\n    if (!matrix.equals(state.camera.matrixWorld)) {\n      state.events.update()\n      matrix.copy(state.camera.matrixWorld)\n    }\n  })\n}\n```\n"
  },
  {
    "path": "docs/API/hooks.mdx",
    "content": "---\ntitle: Hooks\ndescription: Hooks are the heart of react-three-fiber\nnav: 6\n---\n\nHooks allow you to tie or request specific information to your component. For instance, components that want to participate in the renderloop can use `useFrame`, components that need to be informed of three.js specifics can use `useThree` and so on. All hooks clean up after themselves once the component unmounts.\n\n> [!NOTE]\n> Hooks can only be used inside the Canvas element because they rely on context!\n\n❌ You cannot expect something like this to work:\n\n```jsx\nimport { useThree } from '@react-three/fiber'\n\nfunction App() {\n  const { size } = useThree() // This will just crash\n  return (\n    <Canvas>\n      <mesh>\n```\n\n✅ Do this instead:\n\n```jsx\nfunction Foo() {\n  const { size } = useThree()\n  ...\n}\n\nfunction App() {\n  return (\n    <Canvas>\n      <Foo />\n```\n\n## `useThree`\n\nThis hook gives you access to the state model which contains the default renderer, the scene, your camera, and so on. It also gives you the current size of the canvas in screen and viewport coordinates.\n\n```jsx\nimport { useThree } from '@react-three/fiber'\n\nfunction Foo() {\n  const state = useThree()\n```\n\nThe hook is reactive, if you resize the browser for instance, you get fresh measurements, same applies to any of the state objects that may change.\n\n### `state` properties\n\n| Prop              | Description                                                                   | Type                                                                                                                                                                                                           |\n| ----------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `gl`              | Renderer                                                                      | `THREE.WebGLRenderer`                                                                                                                                                                                          |\n| `scene`           | Scene                                                                         | `THREE.Scene`                                                                                                                                                                                                  |\n| `camera`          | Camera                                                                        | `THREE.PerspectiveCamera`                                                                                                                                                                                      |\n| `raycaster`       | Default raycaster                                                             | `THREE.Raycaster`                                                                                                                                                                                              |\n| `pointer`         | Contains updated, normalized, centric pointer coordinates                     | `THREE.Vector2`                                                                                                                                                                                                |\n| `mouse`           | Note: this is deprecated, use `pointer` instead! Normalized event coordinates | `THREE.Vector2`                                                                                                                                                                                                |\n| `clock`           | Running system clock                                                          | `THREE.Clock`                                                                                                                                                                                                  |\n| `linear`          | True when the colorspace is linear                                            | `boolean`                                                                                                                                                                                                      |\n| `flat`            | True when no tonemapping is used                                              | `boolean`                                                                                                                                                                                                      |\n| `legacy`          | Disables global color management via `THREE.ColorManagement`                  | `boolean`                                                                                                                                                                                                      |\n| `frameloop`       | Render mode: always, demand, never                                            | `always`, `demand`, `never`                                                                                                                                                                                    |\n| `performance`     | System regression                                                             | `{ current: number, min: number, max: number, debounce: number, regress: () => void }`                                                                                                                         |\n| `size`            | Canvas size in pixels                                                         | `{ width: number, height: number, top: number, left: number }`                                                                                                                                                 |\n| `viewport`        | Canvas viewport size in three.js units. Note: This is different from [`gl.getViewport`](https://threejs.org/docs/#api/en/renderers/WebGLRenderer.getViewport) which returns the drawbuffer size                                               | `{ width: number, height: number, initialDpr: number, dpr: number, factor: number, distance: number, aspect: number, getCurrentViewport: (camera?: Camera, target?: THREE.Vector3, size?: Size) => Viewport }` |\n| `xr`              | XR interface, manages WebXR rendering                                         | `{ connect: () => void, disconnect: () => void }`                                                                                                                                                              |\n| `set`             | Allows you to set any state property                                          | `(state: SetState<RootState>) => void`                                                                                                                                                                         |\n| `get`             | Allows you to retrieve any state property non-reactively                      | `() => GetState<RootState>`                                                                                                                                                                                    |\n| `invalidate`      | Request a new render, given that `frameloop === 'demand'`                     | `() => void`                                                                                                                                                                                                   |\n| `advance`         | Advance one tick, given that `frameloop === 'never'`                          | `(timestamp: number, runGlobalEffects?: boolean) => void`                                                                                                                                                      |\n| `setSize`         | Resize the canvas                                                             | `(width: number, height: number, top?: number, left?: number) => void`                                                                                                                                         |\n| `setDpr`          | Set the pixel-ratio                                                           | `(dpr: number) => void`                                                                                                                                                                                        |\n| `setFrameloop`    | Shortcut to set the current render mode                                       | `(frameloop?: 'always', 'demand', 'never') => void`                                                                                                                                                            |\n| `setEvents`       | Shortcut to setting the event layer                                           | `(events: Partial<EventManager<any>>) => void`                                                                                                                                                                 |\n| `onPointerMissed` | Response for pointer clicks that have missed a target                         | `() => void`                                                                                                                                                                                                   |\n| `events`          | Pointer-event handling                                                        | `{ connected: TargetNode, handlers: Events, connect: (target: TargetNode) => void, disconnect: () => void }`                                                                                                   |\n\n### Selector\n\nYou can also select properties, this allows you to avoid needless re-render for components that are interested only in particulars. Reactivity does not include deeper three.js internals!\n\n```jsx\n// Will only trigger re-render when the default camera is exchanged\nconst camera = useThree((state) => state.camera)\n// Will only re-render on resize changes\nconst viewport = useThree((state) => state.viewport)\n// ❌ You cannot expect reactivity from three.js internals!\nconst zoom = useThree((state) => state.camera.zoom)\n```\n\n### Reading state from outside of the component cycle\n\n```jsx\nfunction Foo() {\n  const get = useThree((state) => state.get)\n  ...\n  get() // Get fresh state from anywhere you want\n```\n\n### Exchanging defaults\n\n```jsx\nfunction Foo() {\n  const set = useThree((state) => state.set)\n  ...\n  useEffect(() => {\n    set({ camera: new THREE.OrthographicCamera(...) })\n  }, [])\n```\n\n## `useFrame`\n\nThis hook allows you to execute code on every rendered frame, like running effects, updating controls, and so on. You receive the state (same as `useThree`) and a clock delta. Your callback function will be invoked just before a frame is rendered. When the component unmounts it is unsubscribed automatically from the render-loop.\n\n```jsx\nimport { useFrame } from '@react-three/fiber'\n\nfunction Foo() {\n  useFrame((state, delta, xrFrame) => {\n    // This function runs at the native refresh rate inside of a shared render-loop\n  })\n```\n\n> [!CAUTION]\n> Be careful about what you do inside useFrame! You should never setState in there! Your calculations should be slim and\n> you should mind all the commonly known pitfalls when dealing with loops in general, like re-use of variables, etc.\n\n### Taking over the render-loop\n\nIf you need more control you may pass a numerical `renderPriority` value. This will cause React Three Fiber to disable automatic rendering altogether. It will now be your responsibility to render, which is useful when you're working with effect composers, heads-up displays, etc.\n\n```jsx\nfunction Render() {\n  // Takes over the render-loop, the user has the responsibility to render\n  useFrame(({ gl, scene, camera }) => {\n    gl.render(scene, camera)\n  }, 1)\n\nfunction RenderOnTop() {\n  // This will execute *after* Render's useframe\n  useFrame(({ gl, ... }) => {\n    gl.render(...)\n  }, 2)\n```\n\n> [!NOTE]\n> Callbacks will be executed in order of ascending priority values (lowest first, highest last.), similar to the DOM's z-order.\n\n### Negative indices\n\nUsing negative indices will **not take over the render loop**, but it can be useful if you really must order the sequence of useFrames across the component tree.\n\n```jsx\nfunction A() {\n  // This will execute first\n  useFrame(() => ..., -2)\n\nfunction B() {\n  // This useFrame will execute *after* A's\n  useFrame(() => ..., -1)\n```\n\n## `useLoader`\n\nThis hook loads assets and suspends for easier fallback- and error-handling. It can take any three.js loader as its first argument: GLTFLoader, OBJLoader, TextureLoader, FontLoader, etc. It is based on [React.Suspense](https://react.dev/reference/react/Suspense), so fallback-handling and [error-handling](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) happen at the parental level.\n\n```jsx\nimport { Suspense } from 'react'\nimport { useLoader } from '@react-three/fiber'\nimport { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'\n\nfunction Model() {\n  const result = useLoader(GLTFLoader, '/model.glb')\n  // You don't need to check for the presence of the result, when we're here\n  // the result is guaranteed to be present since useLoader suspends the component\n  return <primitive object={result.scene} />\n}\n\nfunction App() {\n  return (\n    <Suspense fallback={<FallbackComponent /> /* or null */}>\n      <Model />\n    </Suspense>\n  )\n}\n```\n\n> [!TIP]\n> Internally, `useLoader` relies on [`suspend-react`](https://github.com/pmndrs/suspend-react).\n\n> [!NOTE]\n> Assets loaded with useLoader are cached by default. The urls given serve as cache-keys. This allows you to re-use loaded data everywhere in the component tree.\n\n> [!WARNING]\n> Be very careful with mutating or disposing of loaded assets, especially when you plan to re-use them. Refer to the automatic disposal section in the API.\n\n### Loader extensions\n\nYou can provide a callback as the third argument if you need to configure your loader:\n\n```jsx\nimport { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'\n\nuseLoader(GLTFLoader, url, (loader) => {\n  const dracoLoader = new DRACOLoader()\n  dracoLoader.setDecoderPath('/draco-gltf/')\n  loader.setDRACOLoader(dracoLoader)\n})\n```\n\n### Loading multiple assets at once\n\nIt can also make multiple requests in parallel:\n\n```jsx\nconst [bumpMap, specMap, normalMap] = useLoader(TextureLoader, [url1, url2, url2])\n```\n\n### Loading status\n\nYou can get the loading status from a callback you provide as the fourth argument. Though consider alternatives like THREE.DefaultLoadingManager or better yet, [Drei's](https://github.com/pmndrs/drei) loading helpers.\n\n```jsx\nuseLoader(loader, url, extensions, (xhr) => {\n  console.log((xhr.loaded / xhr.total) * 100 + '% loaded')\n})\n```\n\n### Special treatment of GLTFLoaders and all loaders that return a scene prop\n\nIf a `result.scene` prop is found the hook will automatically create a object & material collection: `{ nodes, materials }`. This lets you build immutable scene graphs selectively. You can also specifically alter the data without having to traverse it. [GLTFJSX](https://github.com/pmndrs/gltfjsx) specifically relies on this data.\n\n```jsx\nconst { nodes, materials } = useLoader(GLTFLoader, url)\n```\n\n### Pre-loading assets\n\nYou can pre-load assets in global space so that models can be loaded in anticipation before they're mounted in the component tree.\n\n```jsx\nuseLoader.preload(GLTFLoader, '/model.glb' /* extensions */)\n```\n\n## `useGraph`\n\nConvenience hook which creates a memoized, named object/material collection from any [`Object3D`](https://threejs.org/docs/#api/en/core/Object3D).\n\n```jsx\nimport { useLoader, useGraph } from '@react-three/fiber'\n\nfunction Model(url) {\n  const scene = useLoader(OBJLoader, url)\n  const { nodes, materials } = useGraph(scene)\n  return <mesh geometry={nodes.robot.geometry} material={materials.metal} />\n}\n```\n"
  },
  {
    "path": "docs/API/objects.mdx",
    "content": "---\ntitle: Objects, properties and constructor arguments\ndescription: All the effective ways of using React Three Fiber\nnav: 5\n---\n\n## Declaring objects\n\nYou can use [three.js's entire object catalogue and all properties](https://threejs.org/docs). When in doubt, always consult the docs.\n\n❌ You could lay out an object like this:\n\n```jsx\n<mesh\n  visible\n  userData={{ hello: 'world' }}\n  position={new THREE.Vector3(1, 2, 3)}\n  rotation={new THREE.Euler(Math.PI / 2, 0, 0)}\n  geometry={new THREE.SphereGeometry(1, 16, 16)}\n  material={new THREE.MeshBasicMaterial({ color: new THREE.Color('hotpink'), transparent: true })}\n/>\n```\n\n✅ The problem is that all of these properties will always be re-created. Instead, you should define properties declaratively.\n\n```jsx\n<mesh visible userData={{ hello: 'world' }} position={[1, 2, 3]} rotation={[Math.PI / 2, 0, 0]}>\n  <sphereGeometry args={[1, 16, 16]} />\n  <meshStandardMaterial color=\"hotpink\" transparent />\n</mesh>\n```\n\n## Constructor arguments\n\nIn three.js objects are classes that are instantiated. These classes can receive one-time constructor arguments (`new THREE.SphereGeometry(1, 32)`), and properties (`someObject.visible = true`). In React Three Fiber, constructor arguments are always passed as an array via `args`. If args change later on, the object must naturally get reconstructed from scratch!\n\n```jsx\n<sphereGeometry args={[1, 32]} />\n```\n\n## Shortcuts\n\n### Set\n\nAll properties whose underlying object has a `.set()` method can directly receive the same arguments that `set` would otherwise take. For example [`THREE.Color.set`](https://threejs.org/docs/#api/en/math/Color.set) can take a color string, so instead of `color={new THREE.Color('hotpink')}` you can simply write `color=\"hotpink\"`. Some `set` methods take multiple arguments, for instance [`THREE.Vector3`](https://threejs.org/docs/#api/en/math/Vector3.set), give it an array in that case `position={[100, 0, 0]}`.\n\n```jsx\n<mesh position={[1, 2, 3]} />\n  <meshStandardMaterial color=\"hotpink\" />\n```\n\n> [!NOTE]\n> If you do link up an existing object to a property, for instance a THREE.Vector3() to a position, be aware that this will end up copying the object in most cases as it calls .copy() on the target. This only applies to objects that expose both .set() and .copy() (Vectors, Eulers, Matrix, ...). If you link up an existing material or geometry on the other hand, it will overwrite, because more these objects do not have a .set() method.\n\n### SetScalar\n\nProperties that have a `setScalar` method (for instance `Vector3`) can be set like so:\n\n```jsx\n// Translates to <mesh scale={[1, 1, 1]} />\n<mesh scale={1} />\n```\n\n## Piercing into nested properties\n\nIf you want to reach into nested attributes (for instance: `mesh.rotation.x`), just use dash-case.\n\n```jsx\n<mesh rotation-x={1} material-uniforms-resolution-value={[512, 512]} />\n```\n\n## Dealing with non-scene objects\n\nYou can put non-Object3D primitives (geometries, materials, etc) into the render tree as well. They take the same properties and constructor arguments they normally would.\n\nYou might be wondering why you would want to put something in the \"scene\" that normally would not be part of it, in a vanilla three.js app at least. For the same reason you declare any object: it becomes managed, reactive and auto-disposes. These objects are not technically part of the scene, but they \"attach\" to a parent which is.\n\n### Attach\n\nUse `attach` to bind objects to their parent. If you unmount the attached object it will be taken off its parent automatically.\n\nThe following attaches a material to the `material` property of a mesh and a geometry to the `geometry` property:\n\n```jsx\n<mesh>\n  <meshBasicMaterial attach=\"material\" />\n  <boxGeometry attach=\"geometry\" />\n```\n\n> [!NOTE]\n> All objects extending `THREE.Material` receive `attach=\"material\"`, and all objects extending `THREE.BufferGeometry` receive `attach=\"geometry\"`. You do not have to type it out!\n\n```jsx\n<mesh>\n  <meshBasicMaterial />\n  <boxGeometry />\n```\n\nYou can also deeply nest attach through piercing. The following adds a buffer-attribute to `geometry.attributes.position` and then adds the buffer geometry to `mesh.geometry`.\n\n```jsx\n<mesh>\n  <bufferGeometry>\n    <bufferAttribute attach=\"attributes-position\" args={[v, 3]} />\n```\n\n#### More examples\n\n```jsx\n// Attach bar to foo.a\n<foo>\n  <bar attach=\"a\" />\n\n// Attach bar to foo.a.b and foo.a.b.c (nested object attach)\n<foo>\n  <bar attach=\"a-b\" />\n  <bar attach=\"a-b-c\" />\n\n// Attach bar to foo.a[0] and foo.a[1] (array attach is just object attach)\n<foo>\n  <bar attach=\"a-0\" />\n  <bar attach=\"a-1\" />\n\n// Attach bar to foo via explicit add/remove functions\n<foo>\n  <bar attach={(parent, self) => {\n    parent.add(self)\n    return () => parent.remove(self)\n  }} />\n\n// The same as a one liner\n<foo>\n  <bar attach={(parent, self) => (parent.add(self), () => parent.remove(self))} />\n```\n\n#### Real-world use-cases:\n\nAttaching to nested objects, for instance a shadow-camera:\n\n```diff\n- <directionalLight\n-   castShadow\n-   position={[2.5, 8, 5]}\n-   shadow-mapSize={[1024, 1024]}\n-   shadow-camera-far={50}\n-   shadow-camera-left={-10}\n-   shadow-camera-right={10}\n-   shadow-camera-top={10}\n-   shadow-camera-bottom={-10}\n- />\n+ <directionalLight castShadow position={[2.5, 8, 5]} shadow-mapSize={[1024, 1024]}>\n+   <orthographicCamera attach=\"shadow-camera\" args={[-10, 10, 10, -10]} />\n+ </directionalLight>\n```\n\nArrays must have explicit order, for instance multi-materials:\n\n```jsx\n<mesh>\n  {colors.map((color, index) => <meshBasicMaterial key={index} attach={`material-${index}`} color={color} />}\n</mesh>\n```\n\n## Putting already existing objects into the scene-graph\n\nYou can use the `primitive` placeholder for that. You can still give it properties or attach nodes to it. Never add the same object multiple times, this is not allowed in three.js! Primitives will not dispose of the object they carry on unmount, you are responsible for disposing of it!\n\n```jsx\nconst mesh = new THREE.Mesh(geometry, material)\n\nfunction Component() {\n  return <primitive object={mesh} position={[10, 0, 0]} />\n```\n\n> [!NOTE]\n> Scene objects can only ever be added once in Threejs. If you attempt to add one and the same object in two places Threejs will remove the first instance automatically. This will also happen with primitive! If you want to re-use an existing object, you must clone it first.\n\n## Using 3rd-party objects declaratively\n\nThe `extend` function extends React Three Fiber's catalogue of JSX elements. Components added this way can then be referenced in the scene-graph using camel casing similar to other primitives.\n\n```jsx\nimport { extend } from '@react-three/fiber'\nimport { OrbitControls, TransformControls } from 'three-stdlib'\nextend({ OrbitControls, TransformControls })\n\n// ...\nreturn (\n  <>\n    <orbitControls />\n    <transformControls />\n```\n\nIf you're using TypeScript, you'll also need to [extend the JSX namespace](/tutorials/v9-migration-guide#threeelements).\n\n## Disposal\n\nFreeing resources is a [manual chore in `three.js`](https://threejs.org/docs/#manual/en/introduction/How-to-dispose-of-objects), but React is aware of object-lifecycles, hence React Three Fiber will attempt to free resources for you by calling `object.dispose()`, if present, on all unmounted objects.\n\nIf you manage assets by yourself, globally or in a cache, this may _not_ be what you want. You can switch it off by placing `dispose={null}` onto meshes, materials, etc, or even on parent containers like groups, it is now valid for the entire tree.\n\n```jsx\nconst globalGeometry = new THREE.BoxGeometry()\nconst globalMaterial = new THREE.MeshBasicMaterial()\n\nfunction Mesh() {\n  return (\n    <group dispose={null}>\n      <mesh geometry={globalGeometry} material={globalMaterial} />\n```\n"
  },
  {
    "path": "docs/API/testing.mdx",
    "content": "---\ntitle: 'Testing'\ndescription: How to handle unit tests\nnav: 10\n---\n\nLike with every other application testing is an important factor when it comes to releasing an application into the wild and when it comes to React Three Fiber we can use React Three Test Renderer to achieve this.\n\nWe will be testing the [sandbox](https://codesandbox.io/s/98ppy) we created in [events and interactions](events-and-interaction).\n\n## How to test React Three Fiber\n\nLet's start by installing the React Three Test Renderer:\n\n```bash\nnpm install @react-three/test-renderer --save-dev\n```\n\nAfterwards, if you are using Create React App you can just add a file that ends in `.test.js` and start writing your code, because React Three Test Renderer is testing library agnostic, so it works with libraries such as `jest`, `jasmine` etc.\n\nLet's create an `App.test.js` and set up all our test cases:\n\n```jsx\nimport ReactThreeTestRenderer from '@react-three/test-renderer'\nimport { MyRotatingBox } from './App'\n\ntest('mesh to have two children', async () => {\n  const renderer = await ReactThreeTestRenderer.create(<MyRotatingBox />)\n})\n\ntest('click event makes box bigger', async () => {\n  const renderer = await ReactThreeTestRenderer.create(<MyRotatingBox />)\n})\n```\n\nIn here we created three tests and in each we made sure we created the renderer by using the `create` function.\n\nLet's start with the first test and make sure our mesh has two children, the material and cube.\n\nWe can start by getting the scene and it's children from the test instance we just created like so:\n\n```js\nconst meshChildren = renderer.scene.children\n```\n\nIf you log this mesh out you can see that it returns an array of one element since that's all we have in the scene.\n\nUsing this we can make sure to get that first child and use the `allChildren` property on it like so:\n\n```js\nconst meshChildren = renderer.scene.children[0].allChildren\n```\n\nThere is also one property called `children` but this one is meant to be used for things like groups as this one does not return the geometry and the materials, for that we need `allChildren`.\n\nNow to create our assertion:\n\n```js\nexpect(meshChildren.length).toBe(2)\n```\n\nOur first test case looks like this:\n\n```js\ntest('mesh to have two children', async () => {\n  const renderer = await ReactThreeTestRenderer.create(<MyRotatingBox />)\n  const mesh = renderer.scene.children[0].allChildren\n  expect(mesh.length).toBe(2)\n})\n```\n\n## Testing interactions\n\nNow that we have gotten the first test out of the way we can test our interaction and make sure that when we click on the mesh it does indeed update the scale.\n\nWe can do that by utilizing the `fireEvent` method existing in a test instance.\n\nWe know we can get the mesh with:\n\n```js\nconst mesh = renderer.scene.children[0]\n```\n\nSince we already have that we can fire an event in it like so:\n\n```js\nawait renderer.fireEvent(mesh, 'click')\n```\n\nWith that done, all that's left to do is the tree demonstration of our scene and make sure the scale prop on our mesh has updated:\n\n```js\nexpect(mesh.props.scale).toBe(1.5)\n```\n\nIn the end our test looks something like this:\n\n```js\ntest('click event makes box bigger', async () => {\n  const renderer = await ReactThreeTestRenderer.create(<MyRotatingBox />)\n  const mesh = renderer.scene.children[0]\n  expect(mesh.props.scale).toBe(1)\n  await renderer.fireEvent(mesh, 'click')\n  expect(mesh.props.scale).toBe(1.5)\n})\n```\n\nIf you want to learn more about React Three Test Renderer you can checkout the repo and their docs:\n\n- [Repo](https://github.com/pmndrs/react-three-fiber/blob/master/packages/test-renderer)\n- [React Three Test Renderer API](https://github.com/pmndrs/react-three-fiber/blob/master/packages/test-renderer/markdown/rttr.md#create)\n- [React Three Test Instance API](https://github.com/pmndrs/react-three-fiber/blob/master/packages/test-renderer/markdown/rttr-instance.md)\n\n## Exercises\n\n- Check the color of the Box we created\n- Check the rotation using the `advanceFrames` method.\n\n<Codesandbox id=\"hqut4\" tests />\n"
  },
  {
    "path": "docs/API/typescript.mdx",
    "content": "---\ntitle: TypeScript\ndescription: Common scenarios and how to approach them with TypeScript\nnav: 9\n---\n\n## Typing with `useRef`\n\nReact's `useRef` won't automatically infer types despite pointing it to a typed ref.\n\nYou can type the ref yourself by passing a type through `useRef`'s generics:\n\n```tsx\nimport { useRef, useEffect } from 'react'\nimport { Mesh } from 'three'\n\nfunction Box(props) {\n  const meshRef = useRef<Mesh>(null!)\n\n  useEffect(() => {\n    console.log(Boolean(meshRef.current))\n  }, [])\n\n  return (\n    <mesh {...props} ref={meshRef}>\n      <boxGeometry />\n      <meshBasicMaterial />\n    </mesh>\n  )\n}\n```\n\nThe exclamation mark is a non-null assertion that will let TS know that `ref.current` is defined when we access it in effects, useFrame et al. You do not need to check against null, the element is assumed to exist.\n\n## Accessing typed three-elements\n\nWhenever you want to spread props or type components that rely on three elements, you can use the `ThreeElements` interface to extract the mesh, group, or any other three element, including custom elements.\n\n```tsx\nimport { ThreeElements } from '@react-three/fiber'\n\ntype FooProps = ThreeElements['mesh'] & { bar: boolean }\n\nfunction Foo({ bar, ...props}: FooProps) {\n  useEffect(() => {\n    console.log(bar)\n  }, [bar])\n  return <mesh {...props} />\n}\n```\n\n## Extend usage\n\nreact-three-fiber can also accept third-party elements and extend them into its internal catalogue.\n\n```tsx\nimport { useRef, useEffect } from 'react'\nimport { GridHelper } from 'three'\nimport { extend } from '@react-three/fiber'\n\n// Create our custom element\nclass CustomElement extends GridHelper {}\n\n// Extend so the reconciler will learn about it\nextend({ CustomElement })\n\n<customElement />\n```\n\nThe catalogue teaches the underlying reconciler how to create fibers for these elements and treat them within the scene.\n\nYou can then declaratively create custom elements with primitives, but TypeScript won't know about them nor their props.\n\n```html\n// error: 'customElement' does not exist on type 'JSX.IntrinsicElements'\n\n<customElement />\n```\n\n### Extending ThreeElements\n\nTo define our element in JSX, we'll use the `ThreeElement` interface to extend `ThreeElements`. This interface describes three.js classes that are available in the R3F catalog and can be used as native elements.\n\n```tsx\nimport { useRef, useEffect } from 'react'\nimport { GridHelper } from 'three'\nimport { extend, ThreeElement } from '@react-three/fiber'\n\n// Create our custom element\nclass CustomElement extends GridHelper {}\n\n// Extend so the reconciler will learn about it\nextend({ CustomElement })\n\n// Add types to ThreeElements elements so primitives pick up on it\ndeclare module '@react-three/fiber' {\n  interface ThreeElements {\n    customElement: ThreeElement<typeof CustomElement>\n  }\n}\n\n// react-three-fiber will create your custom component and TypeScript will understand it\n<customComponent />\n```\n\nYou can shorten element definition by using the `extend` factory signature, which will automatically extend the element locally. This will also prevent namespace bleeding.\n\n```tsx\n// Create our custom element\nclass CustomElement extends GridHelper {}\n\n// Extend so the reconciler will learn about it, types will be inferred\nconst Element = extend(CustomElement)\n\n// react-three-fiber will create your custom component and TypeScript will understand it\n<Element />\n```\n\n## Extending three default elements\n\nIf you open your own root instead of using `<Canvas>`, you can extend the default elements with `extend`. But keep in mind that the `* as THREE` namespace contains classes, functions, numbers, strings. At the moment we suggest you use `any` or `@ts-ignore` unless you extract the exact classes you need (`extend({ Mesh, Group, ... })`).\n\n```tsx\nimport * as THREE from 'three'\nimport { extend, createRoot, events } from '@react-three/fiber'\n\n// Register the THREE namespace as native JSX elements.\nextend(THREE as any)\n\n// Create a react root\nconst root = createRoot(document.querySelector('canvas'))\n```\n\n## Exported types\n\nreact-three-fiber is extensible and exports types for its internals, such as render props, canvas props, and events:\n\n```tsx\n// Event raycaster intersection\nIntersection\n\n// `useFrame` internal subscription and render callback\nSubscription\nRenderCallback\n\n// `useThree`'s returned internal state\nRootState\nPerformance\nDpr\nSize\nViewport\nCamera\n\n// Canvas props\nCanvasProps\n\n// Supported events\nEvents\n\n// Event manager signature (is completely modular)\nEventManager\n\n// Wraps a platform event as it's passed through the event manager\nThreeEvent\n```\n"
  },
  {
    "path": "docs/advanced/pitfalls.mdx",
    "content": "---\ntitle: Performance pitfalls\ndescription: Performance 1x1\nnav: 12\n---\n\n## Tips and Tricks\n\nThis is a good overview: https://discoverthreejs.com/tips-and-tricks\n\nThe most important gotcha in three.js is that creating objects can be expensive, think twice before you mount/unmount things! Every material or light that you put into the scene has to compile, every geometry you create will be processed. Share materials and geometries if you can, either in global scope or locally:\n\n```jsx\nconst geom = useMemo(() => new BoxGeometry(), [])\nconst mat = useMemo(() => new MeshBasicMaterial(), [])\nreturn items.map(i => <mesh geometry={geom} material={mat} ...\n```\n\nTry to use [instancing](https://codesandbox.io/s/r3f-instanced-colors-8fo01) as much as you can when you need to display many objects of a similar type!\n\n## Avoid setState in loops\n\nTLDR, don't, mutate inside `useFrame`!\n\n- Threejs has a render-loop, it does not work like the DOM does. **Fast updates are carried out in `useFrame` by mutation**. `useFrame` is your per-component render-loop.\n\n- It is not enough to set values in succession, _you need frame deltas_. Instead of `position.x += 0.1` consider `position.x += delta` or your project will run at different speeds depending on the end-users system. Many updates in threejs need to be paired with update flags (`.needsUpdate = true`), or imperative functions (`.updateProjectionMatrix()`).\n\n- You might be tempted to setState inside `useFrame` but there is no reason to. You would only complicate something as simple as an update by routing it through React's scheduler, triggering component render etc.\n\n### ❌ `setState` in loops is bad\n\n```jsx\nuseEffect(() => {\n  const interval = setInterval(() => setX((x) => x + 0.1), 1)\n  return () => clearInterval(interval)\n}, [])\n```\n\n### ❌ `setState` in useFrame is bad\n\n```jsx\nconst [x, setX] = useState(0)\nuseFrame(() => setX((x) => x + 0.1))\nreturn <mesh position-x={x} />\n```\n\n### ❌ `setState` in fast events is bad\n\n```jsx\n<mesh onPointerMove={(e) => setX((x) => e.point.x)} />\n```\n\n### ✅ Instead, just mutate, use deltas\n\nIn general you should prefer useFrame. Consider mutating props safe as long as the component is the only entity that mutates. Use deltas instead of fixed values so that your app is refresh-rate independent and runs at the same speed everywhere!\n\n```jsx\nconst meshRef = useRef()\nuseFrame((state, delta) => (meshRef.current.position.x += delta))\nreturn <mesh ref={meshRef} />\n```\n\nSame goes for events, use references.\n\n```jsx\n<mesh onPointerMove={(e) => (ref.current.position.x = e.point.x)} />\n```\n\nIf you must use intervals, use references as well, but keep in mind that this is not refresh-rate independent.\n\n```jsx\nuseEffect(() => {\n  const interval = setInterval(() => ref.current.position.x += 0.1, 1)\n  return () => clearInterval(interval)\n}, [])\n```\n\n## Handle animations in loops\n\nThe frame loop is where you should place your animations. For instance using lerp, or damp.\n\n### ✅ Use `lerp` + `useFrame`\n\n```jsx\nfunction Signal({ active }) {\n  const meshRef = useRef()\n  useFrame((state, delta) => {\n    meshRef.current.position.x = THREE.MathUtils.lerp(meshRef.current.position.x, active ? 100 : 0, 0.1)\n  })\n  return <mesh ref={meshRef} />\n```\n\n### ✅ Or react-spring\n\nOr, use animation libraries. React-spring has its own frame-loop and animates outside of React. Framer-motion is another popular alternative.\n\n```jsx\nimport { a, useSpring } from '@react-spring/three'\n\nfunction Signal({ active }) {\n  const { x } = useSpring({ x: active ? 100 : 0 })\n  return <a.mesh position-x={x} />\n```\n\n## Do not bind to fast state reactively\n\nUsing state-managers and selective state is fine, but not for updates that happen rapidly for the same reason as above.\n\n### ❌ Don't bind reactive fast-state\n\n```jsx\nimport { useSelector } from 'react-redux'\n\n// Assuming that x gets animated inside the store 60fps\nconst x = useSelector((state) => state.x)\nreturn <mesh position-x={x} />\n```\n\n### ✅ Fetch state directly\n\nFor instance using [Zustand](https://github.com/pmndrs/zustand) (same in Redux et al).\n\n```jsx\nuseFrame(() => (ref.current.position.x = api.getState().x))\nreturn <mesh ref={ref} />\n```\n\n## Don't mount indiscriminately\n\nIn threejs it is very common to not re-mount at all, see the [\"disposing of things\"](https://discoverthreejs.com/tips-and-tricks/) section in discover-three. This is because buffers and materials get re-initialized/compiled, which can be expensive.\n\n### ❌ Avoid mounting runtime\n\n```jsx\n{\n  stage === 1 && <Stage1 />\n}\n{\n  stage === 2 && <Stage2 />\n}\n{\n  stage === 3 && <Stage3 />\n}\n```\n\n### ✅ Consider using visibility instead\n\n```jsx\n<Stage1 visible={stage === 1} />\n<Stage2 visible={stage === 2} />\n<Stage3 visible={stage === 3} />\n\nfunction Stage1(props) {\n  return (\n    <group {...props}>\n      ...\n```\n\n### ✅ Use `startTransition` for expensive ops\n\nReact 18 introduces the `startTransition` and `useTransition` APIs to defer and schedule work and state updates. Use these to de-prioritize expensive operations.\n\nSince version 8 of Fiber canvases use concurrent mode by default, which means React will schedule and defer expensive operations. You don't need to do anything, but you can play around with the [experimental scheduler](https://github.com/drcmda/scheduler-test) and see if marking ops with a lesser priority makes a difference.\n\n```jsx\nimport { useTransition } from 'react'\nimport { Points } from '@react-three/drei'\n\nconst [isPending, startTransition] = useTransition()\nconst [radius, setRadius] = useState(1)\nconst positions = calculatePositions(radius)\nconst colors = calculateColors(radius)\nconst sizes = calculateSizes(radius)\n\n<Points\n  positions={positions}\n  colors={colors}\n  sizes={sizes}\n  onPointerOut={() => {\n    startTransition(() => {\n      setRadius(prev => prev + 1)\n    })\n  }}\n>\n  <meshBasicMaterial vertexColors />\n</Points>\n```\n\n## Don't re-create objects in loops\n\nTry to avoid creating too much effort for the garbage collector, re-pool objects when you can!\n\n### ❌ Bad news for the GC\n\nThis creates a new vector 60 times a second, which allocates memory and forces the GC to eventually kick in.\n\n```jsx\nuseFrame(() => {\n  ref.current.position.lerp(new THREE.Vector3(x, y, z), 0.1)\n})\n```\n\n### ✅ Better re-use object\n\nSet up re-used objects in global or local space, now the GC will be silent.\n\n```jsx\nfunction Foo(props)\n  const vec = new THREE.Vector()\n  useFrame(() => {\n    ref.current.position.lerp(vec.set(x, y, z), 0.1)\n  })\n```\n\n## `useLoader` instead of plain loaders\n\nThreejs loaders give you the ability to load async assets (models, textures, etc), but if you do not re-use assets it can quickly become problematic.\n\n### ❌ No re-use is bad for perf\n\nThis re-fetches, re-parses for every component instance.\n\n```jsx\nfunction Component() {\n  const [texture, set] = useState()\n  useEffect(() => void new TextureLoader().load(url, set), [])\n  return texture ? (\n    <mesh>\n      <sphereGeometry />\n      <meshBasicMaterial map={texture} />\n    </mesh>\n  ) : null\n}\n```\n\nInstead use useLoader, which caches assets and makes them available throughout the scene.\n\n### ✅ Cache and re-use objects\n\n```jsx\nfunction Component() {\n  const texture = useLoader(TextureLoader, url)\n  return (\n    <mesh>\n      <sphereGeometry />\n      <meshBasicMaterial map={texture} />\n    </mesh>\n  )\n}\n```\n\nRegarding GLTF's try to use [GLTFJSX](https://github.com/pmndrs/gltfjsx) as much as you can, this will create immutable JSX graphs which allow you to even re-use full models.\n"
  },
  {
    "path": "docs/advanced/scaling-performance.mdx",
    "content": "---\ntitle: Scaling performance\ndescription: This is a short primer on how to scale performance.\nnav: 11\n---\n\nRunning WebGL can be quite expensive depending on how powerful your devices are. In order to mitigate this, especially if you want to make your application available to a broad variety of devices, including weaker options, you should look into performance optimizations. This article goes through a couple of them.\n\n## On-demand rendering\n\nthree.js apps usually run in a game-loop that executes 60 times a second, React Three Fiber is no different. This is perfectly fine when your scene has _constantly_ moving parts in it. This is what generally drains batteries the most and makes fans spin up.\n\nBut if the moving parts in your scene are allowed to come to rest, then it would be wasteful to keep rendering. In such cases you can opt into on-demand rendering, which will only render when necessary. This saves battery and keeps noisy fans in check.\n\nOpen the sandbox below in a full screen and look into dev tools, you will see that it is completely idle when nothing is going on. It renders only when you move the model.\n\n<Codesandbox id=\"wvgxp\" />\n\nAll you need to do is set the canvas `frameloop` prop to `demand`. It will render frames whenever it detects prop changes throughout the component tree.\n\n```jsx\n<Canvas frameloop=\"demand\">\n```\n\n### Triggering manual frames\n\nOne major caveat is that if anything in the tree _mutates_ props, then React cannot be aware of it and the display would be stale. For instance, camera controls just grab into the camera and mutate its values. Here you can use React Three Fiber's `invalidate` function to trigger frames manually.\n\n```jsx\nfunction Controls() {\n  const orbitControlsRef = useRef()\n  const { invalidate, camera, gl } = useThree()\n  useEffect(() => {\n    orbitControlsRef.current.addEventListener('change', invalidate)\n    return () => orbitControlsRef.current.removeEventListener('change', invalidate)\n  }, [])\n  return <orbitControls ref={orbitControlsRef} args={[camera, gl.domElement]} />\n```\n\n> [!NOTE]\n> Drei's controls do this automatically for you.\n\nGenerally you can call invalidate whenever you need to render:\n\n```jsx\ninvalidate()\n```\n\n> [!IMPORTANT]\n> Calling `invalidate()` will not render immediately, it merely requests a\nnew frame to be rendered out. Calling invalidate multiple times will not render multiple times.\nThink of it as a flag to tell the system that something has changed.\n\n### Sync animations with on-demand-rendering and invalidate\n\nSince `invalidate()` is only a flag that schedules render, you might bump into syncing issues when you run animations that are synchronous (as in, they start immediately). By the time fiber renders the first frame the animation has already progressed which leads to a visible jump. In such cases you should pre-emptively schedule a render and then start the animation in the next frame.\n\n```jsx\n<mesh\n  onClick={() => {\n    // Pre-emptively schedule a render\n    invalidate()\n    // Wait for the next frame to start the animation\n    requestAnimationFrame(() => controls.dolly(1, true))\n  }}\n```\n\n## Re-using geometries and materials\n\nEach geometry and material means additional overhead for the GPU. You should try to re-use resources if you know they will repeat.\n\nYou could do this globally:\n\n```jsx\nconst red = new THREE.MeshLambertMaterial({ color: \"red\" })\nconst sphere = new THREE.SphereGeometry(1, 28, 28)\n\nfunction Scene() {\n  return (\n    <>\n      <mesh geometry={sphere} material={red} />\n      <mesh position={[1, 2, 3]} geometry={sphere} material={red} />\n```\n\nIf you create a material or color in global space - outside of React Three Fiber's `Canvas` context - you should enable [ColorManagement](https://threejs.org/docs/#manual/en/introduction/Color-management) in three.js. This will allow certain conversions (for hexadecimal and CSS colors in sRGB) to be made automatically, producing correct colors in all cases.\n\n```jsx\nimport * as THREE from 'three'\n\n// r150\nTHREE.ColorManagement.enabled = true\n\n// r139-r149\nTHREE.ColorManagement.legacyMode = false\n```\n\n### Caching with `useLoader`\n\n> [!NOTE]\n> Every resource that is loaded with useLoader is cached automatically!\n\nIf you access a resource via useLoader with the same URL, throughout the component tree, then you will always refer to the same asset and thereby re-use it. This is especially useful if you run your GLTF assets through [GLTFJSX](https://github.com/pmndrs/gltfjsx) because it links up geometries and materials and thereby creates re-usable models.\n\n<Codesandbox id=\"dix1y\" />\n\n```jsx\nfunction Shoe(props) {\n  const { nodes, materials } = useLoader(GLTFLoader, \"/shoe.glb\")\n  return (\n    <group {...props} dispose={null}>\n      <mesh geometry={nodes.shoe.geometry} material={materials.canvas} />\n    </group>\n  )\n}\n\n<Shoe position={[1, 2, 3]} />\n<Shoe position={[4, 5, 6]} />\n```\n\n## Instancing\n\nEach mesh is a draw call, you should be mindful of how many of these you employ: no more than 1000 as the very maximum, and optimally a few hundred or less. You can win performance back by reducing draw calls, for example by instancing repeating objects. This way you can have hundreds of thousands of objects in a single draw call.\n\n<Codesandbox id=\"h873k\" />\n\nSetting up instancing is not so hard, consult [the three.js docs](https://threejs.org/docs/#api/en/objects/InstancedMesh) if you need help.\n\n```jsx\nfunction Instances({ count = 100000, temp = new THREE.Object3D() }) {\n  const instancedMeshRef = useRef()\n  useEffect(() => {\n    // Set positions\n    for (let i = 0; i < count; i++) {\n      temp.position.set(Math.random(), Math.random(), Math.random())\n      temp.updateMatrix()\n      instancedMeshRef.current.setMatrixAt(i, temp.matrix)\n    }\n    // Update the instance\n    instancedMeshRef.current.instanceMatrix.needsUpdate = true\n  }, [])\n  return (\n    <instancedMesh ref={instancedMeshRef} args={[null, null, count]}>\n      <boxGeometry />\n      <meshPhongMaterial />\n    </instancedMesh>\n  )\n}\n```\n\n## Level of detail\n\nSometimes it can be beneficial to reduce the quality of an object the further it is away from the camera. Why would you display it full resolution if it is barely visible. This can be a good strategy to reduce the overall vertex-count which means less work for the GPU.\n\nScroll in and out to see the effect:\n\n<Codesandbox id=\"12nmp\" />\n\nThere is a small component in Drei called `<Detailed />` which sets up LOD without boilerplate. You load or prepare a couple of resolution stages, as many as you like, and then give them the same amount of distances from the camera, starting from highest quality to lowest.\n\n```jsx\nimport { Detailed, useGLTF } from '@react-three/drei'\n\nfunction Model() {\n  const [low, mid, high] = useGLTF([\"/low.glb\", \"/mid.glb\", \"/high.glb\"])\n  return (\n    <Detailed distances={[0, 10, 20]}>\n      <mesh geometry={high} />\n      <mesh geometry={mid} />\n      <mesh geometry={low} />\n    <Detailed/>\n  )\n}\n```\n\n## Nested loading\n\nNested loading means that lesser textures and models are loaded first, higher-resolution later.\n\nThe following sandbox goes through three loading stages:\n\n- A loading indicator\n- Low quality\n- High quality\n\n<Codesandbox id=\"7duy8\" />\n\nAnd this is how easy it is to achieve it, you can nest suspense and even use it as a fallback:\n\n```jsx\nfunction App() {\n  return (\n    <Suspense fallback={<span>loading...</span>}>\n      <Canvas>\n        <Suspense fallback={<Model url=\"/low-quality.glb\" />}>\n          <Model url=\"/high-quality.glb\" />\n        </Suspense>\n      </Canvas>\n    </Suspense>\n  )\n}\n\nfunction Model({ url }) {\n  const { scene } = useGLTF(url)\n  return <primitive object={scene} />\n}\n```\n\n## Performance monitoring\n\nDrei has a new component [PerformanceMonitor](https://github.com/pmndrs/drei#performancemonitor) that allows you to monitor, and adapt to, device performance. This component will collect the average fps (frames per second) over time. If after a couple of iterations the averages are below or above a threshold it will trigger onIncline and onDecline callbacks that allow you to respond. Typically you would reduce the quality of your scene, the resolution, effects, the amount of stuff to render, or, increase it if you have enough framerate to fill.\n\nSince this would normally cause ping-ponging between the two callbacks you define upper and lower framerate bounds, as long as you stay within that margin nothing will trigger. Ideally your app should find its way into that margin by gradually altering quality.\n\nA simple example for regulating the resolution. It starts out with 1.5, if the system falls below the bounds it goes to 1, if it's fast enough it goes to 2.\n\n```jsx\nfunction App() {\n  const [dpr, setDpr] = useState(1.5)\n  return (\n    <Canvas dpr={dpr}>\n      <PerformanceMonitor onIncline={() => setDpr(2)} onDecline={() => setDpr(1)} >\n```\n\nYou can also use the onChange callback to get notified when the average changes in whichever direction. This allows you to make gradual changes. It gives you a factor between 0 and 1, which is increased by incline and decreased by decline. The factor is initially 0.5 by default.\n\n```jsx\nimport round from 'lodash/round'\n\nconst [dpr, setDpr] = useState(1)\nreturn (\n <Canvas dpr={dpr}>\n  <PerformanceMonitor onChange={({ factor }) => setDpr(round(0.5 + 1.5 * factor, 1))}>\n```\n\nIf you still experience flip flops despite the bounds you can define a limit of flipflops. If it is met onFallback will be triggered which typically sets a lowest possible baseline for the app. After the fallback has been called PerformanceMonitor will shut down.\n\n```jsx\n<PerformanceMonitor flipflops={3} onFallback={() => setDpr(1)}>\n```\n\nPerformanceMonitor can also have children, if you wrap your app in it you get to use usePerformanceMonitor which allows individual components down the nested tree to respond to performance changes on their own.\n\n```jsx\n;<PerformanceMonitor>\n  <Effects />\n</PerformanceMonitor>\n\nfunction Effects() {\n  usePerformanceMonitor({ onIncline, onDecline, onFallback, onChange })\n  // ...\n}\n```\n\n## Movement regression\n\nWebsites like Sketchfab make sure the scene is always fluid, running at 60 fps, and responsive, no matter which device is being used or how expensive a loaded model is. They do this by regressing movement, where effects, textures, shadows will slightly reduce quality until still-stand\n\nThe following sandbox uses expensive lights and post-processing. In order for it to run relatively smooth it will scale the pixel ratio on movement and also skip heavy post-processing effects like ambient occlusion.\n\n<Codesandbox id=\"pz0q6\" />\n\nWhen you inspect the state model you will notice an object called `performance`.\n\n```jsx\nperformance: {\n  current: 1,\n  min: 0.1,\n  max: 1,\n  debounce: 200,\n  regress: () => void,\n},\n```\n\n- `current`: Performance factor alternates between min and max\n- `min`: Performance lower bound (should be less than 1)\n- `max`: Performance upper bound (no higher than 1)\n- `debounce`: Debounce timeout until it goes to upper bound (1) again\n- `regress()`: Function that temporarily regresses performance\n\nYou can define defaults like so:\n\n```jsx\n<Canvas performance={{ min: 0.5 }}>...</Canvas>\n```\n\n### This is how you can put the system into regression\n\nThe only thing you have to do is call `regress()`. When exactly you do that, that is up to you, but it could be when the mouse moves, or the scene is moving, for instance when controls fire their change-event.\n\nSay you are using controls, then the following code puts the system in regress when they are active:\n\n```jsx\nconst regress = useThree((state) => state.performance.regress)\nuseEffect(() => {\n  controls.current?.addEventListener('change', regress)\n```\n\n### This is how you can respond to it\n\n> [!NOTE]\n> Mere calls to `regress()` will not change or affect anything!\n\nYour app has to opt into performance scaling by listening to the performance `current`! The number itself will tell you what to do. 1 (max) means everything is ok, the default. Less than 1 (min) means a regression is requested and the number itself tells you how far you should go when scaling down.\n\nFor instance, you could simply multiply `current` with the pixel ratio to cut down on resolution. If you have defined `min: 0.5` that would mean it will half the resolution for at least 200ms (delay) when regress is called. It can be used for anything else, too: switching off lights when `current < 1`, using lower-res textures, skip post-processing effects, etc. You could of course also animate/lerp these changes.\n\nHere is a small prototype component that scales the pixel ratio:\n\n```jsx\nfunction AdaptivePixelRatio() {\n  const current = useThree((state) => state.performance.current)\n  const setPixelRatio = useThree((state) => state.setDpr)\n  useEffect(() => {\n    setPixelRatio(window.devicePixelRatio * current)\n  }, [current])\n  return null\n}\n```\n\nDrop this component into the scene, combine it with the code above that calls `regress()`, and you have adaptive resolution:\n\n```jsx\n<AdaptivePixelRatio />\n```\n\nThere are pre-made components for this already in the [Drei library](https://github.com/pmndrs/drei/#performance).\n\n## Enable concurrency\n\nReact 18 introduces concurrent scheduling, specifically time slicing via `startTransition` and `useTransition`. This will virtualize the component graph, which then allows you to prioritise components and actions. Think of how a virtual list avoids scaling issues because it only renders as many items as the screen can take, it is not affected by the amount of items it has to render, be it 10 or 100.000.000.\n\nReact 18 functions very similar to this, it can potentially defer load and heavy tasks in ways that would be hard or impossible to achieve in a vanilla application. It thereby holds on to a stable framerate even in the most demanding situations.\n\nThe following benchmark shows how powerful concurrency can be: https://github.com/drcmda/scheduler-test\n\nIt simulates heavy load by creating hundreds of THREE.TextGeometry instances (510 to be exact). This class, like many others in three.js, is expensive and takes a while to construct. If all 510 instances are created the same time **it will cause approximately 1.5 seconds of pure jank** (Apple M1), the tab would normally freeze. It runs in an interval and **will execute every 2 seconds**.\n\n|          | Distributed | At-once |\n| -------- | ----------- | ------- |\n| three.js | ~20fps      | ~5fps   |\n| React    | ~60fps      | ~60fps  |\n\n<p align=\"center\">\n  <img\n    aria-label=\"three.js distributed\"\n    style={{ width: '50%', display: 'inline-block' }}\n    src=\"https://github.com/drcmda/scheduler-test/raw/master/assets/three-distributed.jpg\"\n  />\n  <img\n    aria-label=\"three.js at once\"\n    style={{ width: '50%', display: 'inline-block' }}\n    src=\"https://github.com/drcmda/scheduler-test/raw/master/assets/three-at-once.jpg\"\n  />\n  <img\n    aria-label=\"React distributed\"\n    style={{ width: '50%', display: 'inline-block' }}\n    src=\"https://github.com/drcmda/scheduler-test/raw/master/assets/react-distributed.jpg\"\n  />\n  <img\n    aria-label=\"React at once\"\n    style={{ width: '50%', display: 'inline-block' }}\n    src=\"https://github.com/drcmda/scheduler-test/raw/master/assets/react-at-once.jpg\"\n  />\n</p>\n\nFor more on how to use this API, see [use startTransition for expensive ops](/docs/advanced/pitfalls#use-starttransition-for-expensive-ops).\n"
  },
  {
    "path": "docs/getting-started/basic-example-sandpack/index.jsx",
    "content": "import { createRoot } from 'react-dom/client'\nimport React, { useRef, useState } from 'react'\nimport { Canvas, useFrame } from '@react-three/fiber'\nimport './styles.css'\n\nfunction Box(props) {\n  // This reference will give us direct access to the mesh\n  const meshRef = useRef()\n  // Set up state for the hovered and active state\n  const [hovered, setHover] = useState(false)\n  const [active, setActive] = useState(false)\n  // Subscribe this component to the render-loop, rotate the mesh every frame\n  useFrame((state, delta) => (meshRef.current.rotation.x += delta))\n  // Return view, these are regular three.js elements expressed in JSX\n  return (\n    <mesh\n      {...props}\n      ref={meshRef}\n      scale={active ? 1.5 : 1}\n      onClick={(event) => setActive(!active)}\n      onPointerOver={(event) => setHover(true)}\n      onPointerOut={(event) => setHover(false)}>\n      <boxGeometry args={[1, 1, 1]} />\n      <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />\n    </mesh>\n  )\n}\n\ncreateRoot(document.getElementById('root')).render(\n  <Canvas>\n    <ambientLight intensity={Math.PI / 2} />\n    <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />\n    <pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />\n    <Box position={[-1.2, 0, 0]} />\n    <Box position={[1.2, 0, 0]} />\n  </Canvas>,\n)\n"
  },
  {
    "path": "docs/getting-started/basic-example-sandpack/styles.css",
    "content": "html,\nbody,\n#root {\n  height: 100%;\n  margin: unset;\n}\n"
  },
  {
    "path": "docs/getting-started/community-r3f-components.mdx",
    "content": "---\ntitle: Community R3F Components\ndescription: This page showcases some React Three Fiber/r3f community components that have not been merged to the drei components collection or to another pmndrs project. \nnav: 4\n---\n\nIf you'd like to list new community components, please make a PR to [this doc](https://github.com/pmndrs/react-three-fiber/tree/master/docs/getting-started/community-r3f-components.mdx) file. It showcases some r3f community components that have not been merged to to the [drei](http://drei.docs.pmnd.rs) components collection or to another [pmndrs](https://github.com/pmndrs) project.\n\n## R3F community components \n\n### Repos and docs\n\nThis page showcases some React Three Fiber/r3f community components that have not been merged to the [drei](http://drei.docs.pmnd.rs) components collection or to another [pmndrs](https://github.com/pmndrs) project. If you'd like to list new community components, please make a PR to [this doc](https://github.com/pmndrs/react-three-fiber/tree/master/docs/getting-started/community-r3f-components.mdx) file. \n\n#### Data sources\n\n - [NASA-AMMOS/3DTilesRendererJS](https://github.com/NASA-AMMOS/3DTilesRendererJS/) (r3f [readme](https://github.com/NASA-AMMOS/3DTilesRendererJS/blob/master/src/r3f/README.md)) repo with doc\n - Luma Labs [Gaussian Splats](https://cdn-luma.com/public/lumalabs.ai/luma-web-library/0.2/fefe154/index.html#react-three-fiber) renderer doc with demos\n - [NYTimes/three-loader-3dtiles](https://nytimes.github.io/three-loader-3dtiles) \n\n#### Renderers & frameworks\n\n - Takram three-geospatial [clouds doc](https://github.com/takram-design-engineering/three-geospatial/tree/main/packages/clouds) and [demos](https://takram-design-engineering.github.io/three-geospatial/?path=/story/clouds-3d-tiles-renderer-integration--tokyo), and [atmosphere doc](https://github.com/takram-design-engineering/three-geospatial/tree/main/packages/atmosphere)\n - [Looking Glass](https://docs.lookingglassfactory.com/developer-tools/webxr/react-three-fiber) doc and demos\n - [Theatre-js](https://github.com/theatre-js/theatre) repo and [doc](https://www.theatrejs.com/docs/latest)\n\n#### Materials\n\n - [FarazzShaikh/CustomShaderMaterial](https://github.com/FarazzShaikh/THREE-CustomShaderMaterial)\n - [pmndrs/THREE.MeshLine](https://github.com/pmndrs/meshline)\n - [ektogamat/R3F-Ultimate-Lens-Flare](https://github.com/ektogamat/R3F-Ultimate-Lens-Flare)\n - [troika-three-text](https://github.com/protectwise/troika/tree/main/packages/troika-three-text)\n\n#### Utilities\n\n - [utsuboco/r3f-perf](https://github.com/utsuboco/r3f-perf)\n\n\n### Codesandboxes and demos\n\n<Grid cols={3}>\n  <li>\n    [NASA-AMMOS/3DTilesRendererJS](https://github.com/NASA-AMMOS/3DTilesRendererJS)\n\n    [<Img src=\"https://raw.githubusercontent.com/NASA-AMMOS/3DTilesRendererJS/master/images/header-mars.png\" alt=\"3DTilesRendererJS r3f demo\" className=\"aspect-[16/9] object-cover\"  width=\"1763\" height=\"926\" />](https://nasa-ammos.github.io/3DTilesRendererJS/example/bundle/r3f/basic.html)\n  </li>\n  <li>\n    [Takram three-geospatial clouds](https://github.com/takram-design-engineering/three-geospatial/tree/main/packages/clouds)\n\n    [<Img src=\"https://github.com/takram-design-engineering/three-geospatial/raw/main/packages/clouds/docs/london.jpg\" alt=\"Takram three-geospatial clouds\" className=\"aspect-[16/9] object-cover\"  width=\"1763\" height=\"926\" />](https://takram-design-engineering.github.io/three-geospatial/?path=/story/clouds-3d-tiles-renderer-integration--tokyo)\n  </li>\n  <li>\n    [Luma Gaussian Splats](https://cdn-luma.com/public/lumalabs.ai/luma-web-library/0.2/fefe154/index.html#react-three-fiber)\n    <Codesandbox id=\"h2fkgq\" />\n  </li>\n  <li>\n    [NYTimes/three-loader-3dtiles](https://github.com/nytimes/three-loader-3dtiles/blob/dev/examples/r3f/src/index.tsx)\n\n    [<Img src=\"https://rd.nytimes.com/static/1ffcbe7f02d6168aecebdb7a84929dcf/1b9fc/7579e21f-3af0-46bb-a073-28c4a97594f9_Demo4.jpg\" alt=\"three-loader-3dtiles r3f demo\" className=\"aspect-[16/9] object-cover\"  width=\"1763\" height=\"926\" />](https://nytimes.github.io/three-loader-3dtiles/dist/web/examples/demos/realitycapture/)\n  </li>\n  <li>\n    [Looking Glass](https://docs.lookingglassfactory.com/developer-tools/webxr/react-three-fiber)\n    <Codesandbox id=\"xzlmzz\" screenshot_url=\"https://blog.lookingglassfactory.com/content/images/size/w2000/2024/05/LKG-32-Spatial-Display-Portrait-Cleopatra-1.jpg\"/> \n  </li> \n  <li>\n    [Theatre-js](https://github.com/theatre-js/theatre)\n    <Codesandbox id=\"6xfrsv\" screenshot_url=\"https://www.theatrejs.com/images/docs/0.5/manual/studio/ui.png\" />\n  </li>\n  <li>\n    [Farazz/CustomShaderMaterial](https://github.com/FarazzShaikh/THREE-CustomShaderMaterial)\n\n    [<Img src=\"https://raw.githubusercontent.com/FarazzShaikh/THREE-CustomShaderMaterial/main/assets/waves-demo.png\" alt=\"THREE-CustomShaderMaterial r3f demo\" className=\"aspect-[16/9] object-cover\"  width=\"1763\" height=\"926\" />](https://farazzshaikh.github.io/THREE-CustomShaderMaterial/#/caustics)\n  </li>\n  <li>\n    [Takram three-geospatial atmosphere](https://github.com/takram-design-engineering/three-geospatial/tree/main/packages/atmosphere)\n\n    [<Img src=\"https://media.githubusercontent.com/media/takram-design-engineering/three-geospatial/main/packages/atmosphere/docs/manhattan.jpg\" alt=\"Takram three-geospatial atmosphere\" className=\"aspect-[16/9] object-cover\"  width=\"1763\" height=\"926\" />](https://takram-design-engineering.github.io/three-geospatial/?path=/story/atmosphere-3d-tiles-renderer-integration--manhattan)\n  </li>\n  <li>\n    [utsuboco/r3f-perf](https://github.com/utsuboco/r3f-perf)\n    <Codesandbox id=\"ykfpwf\" />\n  </li>\n  <li>\n    [pmndrs/THREE.MeshLine](https://github.com/pmndrs/meshline)\n    <Codesandbox id=\"vl221\" />\n  </li>\n  <li>\n    [ektogamat/R3F-Ultimate-Lens-Flare](https://github.com/ektogamat/R3F-Ultimate-Lens-Flare)\n\n    [<Img src=\"https://raw.githubusercontent.com/ektogamat/R3F-Ultimate-Lens-Flare/main/thumbnail.png\" alt=\"R3F-Ultimate-Lens-Flare demo\" className=\"aspect-[16/9] object-cover\"  width=\"1763\" height=\"926\" />](https://ultimate-lens-flare.vercel.app/)\n  </li>\n</Grid>\n"
  },
  {
    "path": "docs/getting-started/examples.mdx",
    "content": "---\ntitle: Examples\ndescription: A few examples that demonstrate what you can do with React Three Fiber\nnav: 3\n---\n\n## Showcase\n\n<Grid cols={1}>\n  <li>\n    <Codesandbox id=\"6d97z4\" tags={['selection', 'tiltshift']} />\n  </li>\n</Grid>\n<Grid cols={2}>\n  <li>\n    <Codesandbox id=\"9s2wd9\" tags={['border-radius']} />\n  </li>\n  <li>\n    <Codesandbox id=\"7qytdw\" tags={['bruno', 'simon', 'threejs-journey', 'fisheye']} />\n  </li>\n  <li>\n    <Codesandbox id=\"xy8c8z\" tags={['lusion', 'n8ao']} />\n  </li>\n  <li>\n    <Codesandbox id=\"nvk9pf\" tags={['ecctrl', 'character-controller']} />\n  </li>\n  <li>\n    <Codesandbox id=\"bst0cy\" tags={['effects', 'bloom', 'dof', 'reflections']} />\n  </li>\n  <li>\n    <Codesandbox id=\"2ycs3\" tags={['effects', 'dof', 'bananas']} />\n  </li>\n  <li>\n    <Codesandbox id=\"ioxywi\" tags={['configurator', 't-shirt', 'soft-shadows']} />\n  </li>\n  <li>\n    <Codesandbox id=\"szj6p7\" tags={['caustics', 'effects', 'soft-shadows']} />\n  </li>\n</Grid>\n<Grid cols={4}>\n  <li>\n    <Codesandbox id=\"5w35n6\" tags={['ssgi', 'rapier']} />\n  </li>\n  <li>\n    <Codesandbox id=\"gwthnh\" tags={['clouds']} />\n  </li>\n  <li>\n    <Codesandbox id=\"2y73c6\" tags={['motion-path', 'clouds']} />\n  </li>\n  <li>\n    <Codesandbox id=\"ykfpwf\" tags={['caustics', 'effects', 'soft-shadows']} />\n  </li>\n  <li>\n    <Codesandbox id=\"yggpw5\" tags={['godrays', 'reflections']} />\n  </li>\n  <li>\n    <Codesandbox id=\"9m4tpc\" tags={['portals']} />\n  </li>\n  <li>\n    <Codesandbox id=\"qvk72r\" tags={['portals']} />\n  </li>\n  <li>\n    <Codesandbox id=\"drc6qg\" tags={['portals']} />\n  </li>\n  <li>\n    <Codesandbox id=\"hmbn1l\" tags={['video', 'cookies', 'caustics']} />\n  </li>\n  <li>\n    <Codesandbox id=\"dq6wwe\" tags={['glass', 'transmission', 'bloom']} />\n  </li>\n  <li>\n    <Codesandbox id=\"dc5fjy\" tags={['cards', 'image']} />\n  </li>\n  <li>\n    <Codesandbox id=\"3ywzzx\" tags={['refraction']} />\n  </li>\n  <li>\n    <Codesandbox id=\"lx2h8\" tags={['reflections', 'annotations']} />\n  </li>\n  <li>\n    <Codesandbox id=\"l4klb\" tags={['scroll', 'controls']} />\n  </li>\n  <li>\n    <Codesandbox id=\"zxpv7\" tags={['physics']} />\n  </li>\n  <li>\n    <Codesandbox id=\"0n9it\" tags={['html', 'annotations']} />\n  </li>\n  <li>\n    <Codesandbox id=\"imn42\" tags={['frosted', 'glass', 'transmission']} />\n  </li>\n  <li>\n    <Codesandbox id=\"pbwi6i\" tags={['gltfjsx', 'effects', 'bloom', 'soft-shadows']} />\n  </li>\n  <li>\n    <Codesandbox id=\"fslt99\" tags={['effects', 'reflections', 'ssr', 'bloom']} />\n  </li>\n  <li>\n    <Codesandbox id=\"2qfxj4\" tags={['rapier', 'physics', 'soft-shadows']} />\n  </li>\n  <li>\n    <Codesandbox id=\"2n98yj\" tags={['scroll', 'refraction', 'lens']} />\n  </li>\n  <li>\n    <Codesandbox id=\"e662p3\" tags={['effects', 'bloom', 'reflections']} />\n  </li>\n  <li>\n    <Codesandbox id=\"j3ycvl\" tags={['effects', 'bloom']} />\n  </li>\n  <li>\n    <Codesandbox id=\"lwo219\" tags={['custom', 'environments']} />\n  </li>\n  <li>\n    <Codesandbox id=\"qxjoj\" tags={['gltfjsx', 'configurator']} />\n  </li>\n  <li>\n    <Codesandbox id=\"dvokj\" tags={['audio', 'analyser']} />\n  </li>\n  <li>\n    <Codesandbox id=\"bfplr\" tags={['ground', 'reflections', 'video-texture']} />\n  </li>\n  <li>\n    <Codesandbox id=\"ni6v4\" tags={['bruno-simon', 'threejs-journey']} />\n  </li>\n  <li>\n    <Codesandbox id=\"9keg6\" tags={['html', 'iframe']} />\n  </li>\n  <li>\n    <Codesandbox id=\"f79ucc\" tags={['splinetool', 'iframe']} />\n  </li>\n  <li>\n    <Codesandbox id=\"zqrreo\" tags={['refraction']} />\n  </li>\n  <li>\n    <Codesandbox id=\"4gy946\" tags={['refraction', 'instanced']} />\n  </li>\n  <li>\n    <Codesandbox id=\"q48jgy\" tags={['ground-projected-env']} />\n  </li>\n  <li>\n    <Codesandbox id=\"ju368j\" tags={['splinetool', 'transmission']} />\n  </li>\n  <li>\n    <Codesandbox id=\"mlgzsc\" tags={['transmission', 'csg']} />\n  </li>\n  <li>\n    <Codesandbox id=\"y52tmt\" tags={['csg']} />\n  </li>\n  <li>\n    <Codesandbox id=\"hg3ejl\" tags={['scroll', 'animation', 'effects', 'tiltshift']} />\n  </li>\n  <li>\n    <Codesandbox id=\"ssbdsw\" tags={['physics', 'effects', 'n8ao']} />\n  </li>\n  <li>\n    <Codesandbox id=\"024uom\" tags={['html', 'input']} />\n  </li>\n  <li>\n    <Codesandbox id=\"gsm1y\" tags={['scroll']} />\n  </li>\n  <li>\n    <Codesandbox id=\"x8gvs\" tags={['scroll']} />\n  </li>\n  <li>\n    <Codesandbox id=\"yjhzv\" tags={['scroll']} />\n  </li>\n  <li>\n    <Codesandbox id=\"qpfgyp\" tags={['effects', 'particles']} />\n  </li>\n  <li>\n    <Codesandbox id=\"62o18n\" tags={['cross-fade', 'transitions']} />\n  </li>\n  <li>\n    <Codesandbox id=\"8j36ok\" tags={['transmission', 'portals', 'physics']} />\n  </li>\n  <li>\n    <Codesandbox id=\"n7jf0f\" tags={['transmission', 'portals']} />\n  </li>\n  <li>\n    <Codesandbox id=\"ik11ln\" tags={['portals', 'blend']} />\n  </li>\n  <li>\n    <Codesandbox id=\"lxvqek\" />\n  </li>\n  <li>\n    <Codesandbox id=\"if9crg\" />\n  </li>\n  <li>\n    <Codesandbox id=\"xzi6ps\" />\n  </li>\n  <li>\n    <Codesandbox id=\"qvb1vk\" />\n  </li>\n  <li>\n    <Codesandbox id=\"hxcc1x\" />\n  </li>\n  <li>\n    <Codesandbox id=\"8pbw1f\" />\n  </li>\n  <li>\n    <Codesandbox id=\"mw0dtc\" />\n  </li>\n  <li>\n    <Codesandbox id=\"8flefh\" />\n  </li>\n  <li>\n    <Codesandbox id=\"7e9y1b\" />\n  </li>\n  <li>\n    <Codesandbox id=\"whnhyr\" />\n  </li>\n  <li>\n    <Codesandbox id=\"0c5hv9\" />\n  </li>\n  <li>\n    <Codesandbox id=\"0fqow2\" />\n  </li>\n  <li>\n    <Codesandbox id=\"2ij9u\" />\n  </li>\n  <li>\n    <Codesandbox id=\"42glz0\" />\n  </li>\n  <li>\n    <Codesandbox id=\"ledhe1\" />\n  </li>\n  <li>\n    <Codesandbox id=\"nurp5t\" />\n  </li>\n  <li>\n    <Codesandbox id=\"2csbr1\" />\n  </li>\n  <li>\n    <Codesandbox id=\"go0b4w\" />\n  </li>\n  <li>\n    <Codesandbox id=\"s006f\" />\n  </li>\n  <li>\n    <Codesandbox id=\"l900i\" />\n  </li>\n  <li>\n    <Codesandbox id=\"qyz5r\" />\n  </li>\n  <li>\n    <Codesandbox id=\"kv7tv\" />\n  </li>\n  <li>\n    <Codesandbox id=\"ls503\" />\n  </li>\n  <li>\n    <Codesandbox id=\"n60qg\" />\n  </li>\n  <li>\n    <Codesandbox id=\"4jr4p\" />\n  </li>\n  <li>\n    <Codesandbox id=\"kud9p\" />\n  </li>\n  <li>\n    <Codesandbox id=\"zgsyn\" />\n  </li>\n  <li>\n    <Codesandbox id=\"i6t0j\" />\n  </li>\n  <li>\n    <Codesandbox id=\"h8o2d\" />\n  </li>\n  <li>\n    <Codesandbox id=\"wu51m\" />\n  </li>\n  <li>\n    <Codesandbox id=\"tu24h\" />\n  </li>\n  <li>\n    <Codesandbox id=\"jz9l97qn89\" />\n  </li>\n  <li>\n    <Codesandbox id=\"prb9t\" />\n  </li>\n  <li>\n    <Codesandbox id=\"pecl6\" />\n  </li>\n  <li>\n    <Codesandbox id=\"sbf2i\" />\n  </li>\n  <li>\n    <Codesandbox id=\"t4l0f\" />\n  </li>\n  <li>\n    <Codesandbox id=\"wdzv4\" />\n  </li>\n  <li>\n    <Codesandbox id=\"6hi1y\" />\n  </li>\n  <li>\n    <Codesandbox id=\"1sccp\" />\n  </li>\n  <li>\n    <Codesandbox id=\"q23sw\" />\n  </li>\n  <li>\n    <Codesandbox id=\"gpioq\" />\n  </li>\n  <li>\n    <Codesandbox id=\"3rjsl\" />\n  </li>\n  <li>\n    <Codesandbox id=\"4j2q2\" />\n  </li>\n  <li>\n    <Codesandbox id=\"dh2jc\" />\n  </li>\n  <li>\n    <Codesandbox id=\"gkfhr\" />\n  </li>\n  <li>\n    <Codesandbox id=\"0buje\" />\n  </li>\n  <li>\n    <Codesandbox id=\"5oufp\" />\n  </li>\n  <li>\n    <Codesandbox id=\"f1ixt\" />\n  </li>\n  <li>\n    <Codesandbox id=\"7psew\" />\n  </li>\n  <li>\n    <Codesandbox id=\"vl221\" />\n  </li>\n  <li>\n    <Codesandbox id=\"oep9o\" />\n  </li>\n  <li>\n    <Codesandbox id=\"tx1pq\" />\n  </li>\n</Grid>\n\n## Game prototypes\n\n<Grid>\n  <li>\n    <Codesandbox id=\"lo6kp\" />\n  </li>\n  <li>\n    <Codesandbox id=\"rmfcq\" />\n  </li>\n  <li>\n    <Codesandbox id=\"i2160\" />\n  </li>\n  <li>\n    <Codesandbox id=\"vkgi6\" />\n  </li>\n  <li>\n    <Codesandbox id=\"2yqpv\" />\n  </li>\n  <li>\n    <Codesandbox id=\"ptdgrn\" />\n  </li>\n  <li>\n    <Codesandbox id=\"66cd7\" />\n  </li>\n</Grid>\n\n## Basic examples\n\n<Grid>\n  <li>\n    <Codesandbox id=\"rrppl0y8l4\" />\n  </li>\n  <li>\n    <Codesandbox id=\"yup2o\" />\n  </li>\n  <li>\n    <Codesandbox id=\"0z8i2c\" />\n  </li>\n  <li>\n    <Codesandbox id=\"txzeq8\" />\n  </li>\n  <li>\n    <Codesandbox id=\"pj7zjq\" />\n  </li>\n  <li>\n    <Codesandbox id=\"wlz1o0\" />\n  </li>\n  <li>\n    <Codesandbox id=\"7n2yru\" />\n  </li>\n  <li>\n    <Codesandbox id=\"z3f2mw\" />\n  </li>\n  <li>\n    <Codesandbox id=\"btsbj\" />\n  </li>\n  <li>\n    <Codesandbox id=\"rz2g0\" />\n  </li>\n  <li>\n    <Codesandbox id=\"8fo01\" />\n  </li>\n  <li>\n    <Codesandbox id=\"7duy8\" />\n  </li>\n  <li>\n    <Codesandbox id=\"bp6tmc\" tags={['views', 'portals']} />\n  </li>\n  <li>\n    <Codesandbox id=\"r9w2ob\" tags={['views', 'portals']} />\n  </li>\n  <li>\n    <Codesandbox id=\"p9umgf\" tags={['html', 'scroll']} />\n  </li>\n  <li>\n    <Codesandbox id=\"k8phr\" />\n  </li>\n  <li>\n    <Codesandbox id=\"dix1y\" />\n  </li>\n  <li>\n    <Codesandbox id=\"zcuqh\" />\n  </li>\n  <li>\n    <Codesandbox id=\"7ucso\" />\n  </li>\n  <li>\n    <Codesandbox id=\"py4db\" />\n  </li>\n  <li>\n    <Codesandbox id=\"hf1cs\" />\n  </li>\n  <li>\n    <Codesandbox id=\"39hg8\" />\n  </li>\n  <li>\n    <Codesandbox id=\"wbrfs\" />\n  </li>\n  <li>\n    <Codesandbox id=\"itfgk\" />\n  </li>\n  <li>\n    <Codesandbox id=\"iup24\" />\n  </li>\n  <li>\n    <Codesandbox id=\"zu2wo\" />\n  </li>\n  <li>\n    <Codesandbox id=\"1g4qq\" />\n  </li>\n  <li>\n    <Codesandbox id=\"z8e6m\" />\n  </li>\n  <li>\n    <Codesandbox id=\"h545c\" />\n  </li>\n  <li>\n    <Codesandbox id=\"0k27n\" />\n  </li>\n  <li>\n    <Codesandbox id=\"d36mw\" />\n  </li>\n  <li>\n    <Codesandbox id=\"h873k\" />\n  </li>\n  <li>\n    <Codesandbox id=\"08s1u\" />\n  </li>\n  <li>\n    <Codesandbox id=\"wvgxp\" />\n  </li>\n  <li>\n    <Codesandbox id=\"5xho4\" />\n  </li>\n  <li>\n    <Codesandbox id=\"mbfzf\" />\n  </li>\n  <li>\n    <Codesandbox id=\"mkq8e\" />\n  </li>\n  <li>\n    <Codesandbox id=\"12nmp\" />\n  </li>\n  <li>\n    <Codesandbox id=\"6oei7\" />\n  </li>\n  <li>\n    <Codesandbox id=\"3k4g6\" />\n  </li>\n  <li>\n    <Codesandbox id=\"3878x\" />\n  </li>\n  <li>\n    <Codesandbox id=\"1b40u\" />\n  </li>\n  <li>\n    <Codesandbox id=\"0ycwe\" />\n  </li>\n  <li>\n    <Codesandbox id=\"ib0jc\" />\n  </li>\n</Grid>\n"
  },
  {
    "path": "docs/getting-started/installation.mdx",
    "content": "---\ntitle: Installation\ndescription: Learn how to install react-three-fiber\nnav: 1\n---\n\n```bash\nnpm install three @react-three/fiber\n```\n\n> [!WARNING]  \n> Fiber is compatible with React v18 and v19 and works with ReactDOM and React Native. Fiber is a React renderer, it must pair with a major version of React, just like react-dom, react-native, etc. @react-three/fiber@8 pairs with react@18, @react-three/fiber@9 pairs with react@19.\n\nGetting started with React Three Fiber is not nearly as hard as you might have thought, but various frameworks may require particular attention.\n\nWe've put together guides for getting started with each popular framework:\n\n- Vite.js\n- Next.js\n- CDN w/o build tools\n- React Native\n\nIf you just want to give it a try, fork this [example on codesandbox](https://codesandbox.io/s/rrppl0y8l4?file=/src/App.js)!\n\n## Vite.js\n\n`vite` will also work out of the box.\n\n```bash\n# Create app\nnpm create vite my-app\n\n# Select react as framework\n\n# Install dependencies\ncd my-app\nnpm install three @react-three/fiber\n\n# Start development server\nnpm run dev\n```\n\n## Next.js\n\nIt should work out of the box but you will encounter untranspiled add-ons in the three.js ecosystem, in that case,\n\n### Next.js 13.1 or latest version\n\nYou need to add three to `transpilePackages` property in `next.config.js`:\n\n```js\ntranspilePackages: ['three'],\n```\n\n### Next.js 13.0 or oldest version\n\nYou can install the `next-transpile-modules` module:\n\n```bash\nnpm install next-transpile-modules --save-dev\n```\n\nthen, add this to your `next.config.js`\n\n```js\nconst withTM = require('next-transpile-modules')(['three'])\nmodule.exports = withTM()\n```\n\nMake sure to check out our [official next.js starter](https://github.com/pmndrs/react-three-next), too!\n\n## Without build tools\n\nYou can use React Three Fiber with browser-ready ES Modules from [esm.sh](https://esm.sh) and a JSX-like syntax powered by [htm](https://github.com/developit/htm).\n\n```jsx\nimport ReactDOM from 'https://esm.sh/react-dom'\nimport React, { useRef, useState } from 'https://esm.sh/react'\nimport { Canvas, useFrame } from 'https://esm.sh/@react-three/fiber'\nimport htm from 'https://esm.sh/htm'\n\nconst html = htm.bind(React.createElement)\nReactDOM.render(html`<${Canvas}>...<//>`, document.getElementById('root'))\n```\n\n<details>\n<summary>Full example</summary>\n\n```jsx\nimport ReactDOM from 'https://esm.sh/react-dom'\nimport React, { useRef, useState } from 'https://esm.sh/react'\nimport { Canvas, useFrame } from 'https://esm.sh/@react-three/fiber'\nimport htm from 'https://esm.sh/htm'\n\nconst html = htm.bind(React.createElement)\n\nfunction Box(props) {\n  const meshRef = useRef()\n  const [hovered, setHover] = useState(false)\n  const [active, setActive] = useState(false)\n  useFrame(() => (meshRef.current.rotation.x = meshRef.current.rotation.y += 0.01))\n  return html` <mesh\n    ...${props}\n    ref=${meshRef}\n    scale=${active ? 1.5 : 1}\n    onClick=${() => setActive(!active)}\n    onPointerOver=${() => setHover(true)}\n    onPointerOut=${() => setHover(false)}\n  >\n    <boxGeometry args=${[1, 1, 1]} />\n    <meshStandardMaterial color=${hovered ? 'hotpink' : 'orange'} />\n  </mesh>`\n}\n\nReactDOM.render(\n  html` <${Canvas}>\n    <ambientLight />\n    <pointLight position=${[10, 10, 10]} />\n    <${Box} position=${[-1.2, 0, 0]} />\n    <${Box} position=${[1.2, 0, 0]} />\n  <//>`,\n  document.getElementById('root'),\n)\n```\n\n</details>\n\n## React Native\n\nR3F v8 adds support for react-native and can be imported from `@react-three/fiber/native`. We use `expo-gl` and `expo-asset` under the hood for WebGL2 bindings and ensuring interplay between Metro and three.js loaders.\n\nTo get started, create an app via `expo` or `react-native`:\n\n```bash\n# Create a managed/bare app\nnpx create-expo-app\ncd my-app\n\n# or\n\n# Create and link bare app\nnpx react-native init my-app\nnpx install-expo-modules@latest\ncd my-app\n```\n\nThen install dependencies (for manual installation or migration, see [expo modules installation](https://docs.expo.dev/bare/installing-expo-modules)):\n\n```bash\n# Automatically install\nexpo install expo-gl\n\n# Install NPM dependencies\nnpm install three @react-three/fiber\n```\n\nSome configuration may be required to tell the Metro bundler about your assets if you use `useLoader` or Drei abstractions like `useGLTF` and `useTexture`:\n\n```js\n// metro.config.js\nmodule.exports = {\n  resolver: {\n    sourceExts: ['js', 'jsx', 'json', 'ts', 'tsx', 'cjs', 'mjs'],\n    assetExts: ['glb', 'gltf', 'png', 'jpg'],\n  },\n}\n```\n\nR3F's API is completely x-platform, so you can use [events](/api/events) and [hooks](/api/hooks) just as you would on the web.\n\n> [!NOTE]\n> Make sure to import from `@react-three/fiber/native` or `@react-three/drei/native` for correct IntelliSense and platform-specific abstractions.\n\n> [!NOTE]\n> iOS simulators often have incomplete or unreliable OpenGL ES support, which can cause EXC_BAD_ACCESS crashes when rendering 3D content. Always test on a physical iOS device (iPhone/iPad running iOS 16 or later) to ensure stable WebGL rendering.\n\n```jsx\nimport { Suspense } from 'react'\nimport { Canvas } from '@react-three/fiber/native'\nimport { useGLTF } from '@react-three/drei/native'\nimport modelPath from './path/to/model.glb'\n\nfunction Model(props) {\n  const gltf = useGLTF(modelPath)\n  return <primitive {...props} object={gltf.scene} />\n}\n\nexport default function App() {\n  return (\n    <Canvas>\n      <ambientLight />\n      <Suspense>\n        <Model />\n      </Suspense>\n    </Canvas>\n  )\n}\n```\n"
  },
  {
    "path": "docs/getting-started/introduction.mdx",
    "content": "---\ntitle: Introduction\ndescription: React-three-fiber is a React renderer for three.js.\nnav: 0\n---\n\n<Intro>\n  Build your scene declaratively with re-usable, self-contained components that react to state, are readily interactive and can participate in [React](https://react.dev)'s ecosystem.\n</Intro>\n\n[![React Three Fiber banner](../banner-r3f.jpg)](/getting-started/examples)\n\n```bash\nnpm install three @types/three @react-three/fiber\n```\n> [!WARNING]  \n> Three-fiber is a React renderer, it must pair with a major version of React, just like react-dom, react-native, etc. @react-three/fiber@8 pairs with react@18, @react-three/fiber@9 pairs with react@19.\n\n## Does it have limitations?\n\nNone. Everything that works in Threejs will work here without exception.\n\n## Is it slower than plain Threejs?\n\nNo. There is no overhead. Components render outside of React. It outperforms Threejs in scale due to React's scheduling abilities.\n\n## Can it keep up with frequent feature updates to Threejs?\n\nYes. It merely expresses Threejs in JSX, `<mesh />` dynamically turns into `new THREE.Mesh()`. If a new Threejs version adds, removes or changes features, it will be available to you instantly without depending on updates to this library.\n\n## What does it look like?\n\nLet's make a re-usable component that has its own state, reacts to user-input and participates in the render-loop:\n\n<Sandpack\n  customSetup={{\n    dependencies: {\n      'react': 'latest',\n      'react-dom': 'latest',\n      'three': 'latest',\n      '@react-three/fiber': 'latest'\n    },\n    entry: '/index.jsx',\n  }}\n  folder=\"basic-example-sandpack\"\n/>\n\n<details>\n  <summary>Show TypeScript example</summary>\n\n```bash\nnpm install @types/three\n```\n\n<Sandpack\n  customSetup={{\n    dependencies: {\n      'react': 'latest',\n      'react-dom': 'latest',\n      'three': 'latest',\n      '@types/three': 'latest',\n      '@react-three/fiber': 'latest'\n    },\n    entry: '/index.tsx',\n    main: '/index.tsx',\n  }}\n  files={{\n    '/styles.css': `html,body,#root {height:100%;margin:unset;}`,\n    '/index.tsx': `import * as THREE from 'three'\nimport { createRoot } from 'react-dom/client'\nimport React, { useRef, useState } from 'react'\nimport { Canvas, useFrame, ThreeElements } from '@react-three/fiber'\nimport './styles.css'\n\nfunction Box(props: ThreeElements['mesh']) {\n  const meshRef = useRef<THREE.Mesh>(null!)\n  const [hovered, setHover] = useState(false)\n  const [active, setActive] = useState(false)\n  useFrame((state, delta) => (meshRef.current.rotation.x += delta))\n  return (\n    <mesh\n      {...props}\n      ref={meshRef}\n      scale={active ? 1.5 : 1}\n      onClick={(event) => setActive(!active)}\n      onPointerOver={(event) => setHover(true)}\n      onPointerOut={(event) => setHover(false)}>\n      <boxGeometry args={[1, 1, 1]} />\n      <meshStandardMaterial color={hovered ? 'hotpink' : '#2f74c0'} />\n    </mesh>\n  )\n}\n\ncreateRoot(document.getElementById('root')).render(\n  <Canvas>\n    <ambientLight intensity={Math.PI / 2} />\n    <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />\n    <pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />\n    <Box position={[-1.2, 0, 0]} />\n    <Box position={[1.2, 0, 0]} />\n  </Canvas>,\n)`,\n  }}\n/>\n\n</details>\n\n<details>\n  <summary>Show React Native example</summary>\n\nFor installation instructions see [react native installation instructions](/getting-started/installation#react-native).\n\n```jsx\nimport React, { useRef, useState } from 'react'\nimport { Canvas, useFrame } from '@react-three/fiber/native'\n\nfunction Box(props) {\n  const meshRef = useRef(null)\n  const [hovered, setHover] = useState(false)\n  const [active, setActive] = useState(false)\n  useFrame((state, delta) => (meshRef.current.rotation.x += delta))\n  return (\n    <mesh\n      {...props}\n      ref={meshRef}\n      scale={active ? 1.5 : 1}\n      onClick={(event) => setActive(!active)}\n      onPointerOver={(event) => setHover(true)}\n      onPointerOut={(event) => setHover(false)}>\n      <boxGeometry args={[1, 1, 1]} />\n      <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />\n    </mesh>\n  )\n}\n\nexport default function App() {\n  return (\n    <Canvas>\n      <ambientLight intensity={Math.PI / 2} />\n      <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />\n      <pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />\n      <Box position={[-1.2, 0, 0]} />\n      <Box position={[1.2, 0, 0]} />\n    </Canvas>\n  )\n}\n```\n\n</details>\n\n## First steps\n\nYou need to be versed in both React and Threejs before rushing into this. If you are unsure about React consult the official [React docs](https://react.dev/learn), especially [the section about hooks](https://react.dev/reference/react). As for Threejs, make sure you at least glance over the following links:\n\n1. Make sure you have a [basic grasp of Threejs](https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene). Keep that site open.\n2. When you know what a scene is, a camera, mesh, geometry, material, fork the [demo above](https://github.com/pmndrs/react-three-fiber#what-does-it-look-like).\n3. [Look up](https://threejs.org/docs/index.html#api/en/objects/Mesh) the JSX elements that you see (mesh, ambientLight, etc), _all_ threejs exports are native to three-fiber.\n4. Try changing some values, scroll through our [API](/api/canvas) to see what the various settings and hooks do.\n\nSome helpful material:\n\n- [Threejs-docs](https://threejs.org/docs) and [examples](https://threejs.org/examples)\n- [Discover Threejs](https://discoverthreejs.com), especially the [Tips and Tricks](https://discoverthreejs.com/tips-and-tricks) chapter for best practices\n- [Bruno Simons Threejs Journey](https://threejs-journey.com), arguably the best learning resource, now includes a full [R3F chapter](https://threejs-journey.com/lessons/what-are-react-and-react-three-fiber)\n\n<a href=\"https://threejs-journey.com\">\n  <img src=\"../banner-journey.jpg\" />\n</a>\n\n## Eco system\n\nThere is a vibrant and extensive eco system around three-fiber, full of libraries, helpers and abstractions.\n\n- [`@react-three/drei`](https://github.com/pmndrs/drei) &ndash; useful helpers, this is an eco system in itself\n- [`@react-three/gltfjsx`](https://github.com/pmndrs/gltfjsx) &ndash; turns GLTFs into JSX components\n- [`@react-three/postprocessing`](https://github.com/pmndrs/react-postprocessing) &ndash; post-processing effects\n- [`@react-three/test-renderer`](https://github.com/pmndrs/react-three-fiber/tree/master/packages/test-renderer) &ndash; for unit tests in node\n- [`@react-three/flex`](https://github.com/pmndrs/react-three-flex) &ndash; flexbox for react-three-fiber\n- [`@react-three/xr`](https://github.com/pmndrs/react-xr) &ndash; VR/AR controllers and events\n- [`@react-three/csg`](https://github.com/pmndrs/react-three-csg) &ndash; constructive solid geometry\n- [`@react-three/rapier`](https://github.com/pmndrs/react-three-rapier) &ndash; 3D physics using Rapier\n- [`@react-three/cannon`](https://github.com/pmndrs/use-cannon) &ndash; 3D physics using Cannon\n- [`@react-three/p2`](https://github.com/pmndrs/use-p2) &ndash; 2D physics using P2\n- [`@react-three/a11y`](https://github.com/pmndrs/react-three-a11y) &ndash; real a11y for your scene\n- [`@react-three/gpu-pathtracer`](https://github.com/pmndrs/react-three-gpu-pathtracer) &ndash; realistic path tracing\n- [`create-r3f-app next`](https://github.com/pmndrs/react-three-next) &ndash; nextjs starter\n- [`lamina`](https://github.com/pmndrs/lamina) &ndash; layer based shader materials\n- [`zustand`](https://github.com/pmndrs/zustand) &ndash; flux based state management\n- [`jotai`](https://github.com/pmndrs/jotai) &ndash; atoms based state management\n- [`valtio`](https://github.com/pmndrs/valtio) &ndash; proxy based state management\n- [`react-spring`](https://github.com/pmndrs/react-spring) &ndash; a spring-physics-based animation library\n- [`framer-motion-3d`](https://www.framer.com/docs/three-introduction/) &ndash; framer motion, a popular animation library\n- [`use-gesture`](https://github.com/pmndrs/react-use-gesture) &ndash; mouse/touch gestures\n- [`leva`](https://github.com/pmndrs/leva) &ndash; create GUI controls in seconds\n- [`maath`](https://github.com/pmndrs/maath) &ndash; a kitchen sink for math helpers\n- [`miniplex`](https://github.com/hmans/miniplex) &ndash; ECS (entity management system)\n- [`composer-suite`](https://github.com/hmans/composer-suite) &ndash; composing shaders, particles, effects and game mechanics\n\n## How to contribute\n\nIf you like this project, please consider helping out. All contributions are welcome as well as donations to [Opencollective](https://opencollective.com/react-three-fiber), or in crypto `BTC: 36fuguTPxGCNnYZSRdgdh6Ea94brCAjMbH`, `ETH: 0x6E3f79Ea1d0dcedeb33D3fC6c34d2B1f156F2682`.\n\n## Backers\n\nThank you to all our backers! 🙏\n\n<a href=\"https://opencollective.com/react-three-fiber#backers\" target=\"_blank\">\n  <img src=\"https://opencollective.com/react-three-fiber/backers.svg?width=890\" width=\"890\" height=\"207\" />\n</a>\n\n## Contributors\n\nThis project exists thanks to all the people who contribute.\n\n<a href=\"https://github.com/pmndrs/react-three-fiber/graphs/contributors\">\n  <img src=\"https://opencollective.com/react-three-fiber/contributors.svg?width=890\" width=\"890\" height=\"411\" />\n</a>\n"
  },
  {
    "path": "docs/getting-started/your-first-scene.mdx",
    "content": "---\ntitle: Your first scene\ndescription: This guide will help you setup your first React Three Fiber scene and introduce you to its core concepts.\nnav: 2\n---\n\nThis tutorial will assume some React knowledge.\n\n## Setting up the Canvas\n\nWe'll start by importing the `<Canvas />` component from `@react-three/fiber` and putting it in our React tree.\n\n```jsx\nimport { createRoot } from 'react-dom/client'\nimport { Canvas } from '@react-three/fiber'\n\nfunction App() {\n  return (\n    <div id=\"canvas-container\">\n      <Canvas />\n    </div>\n  )\n}\n\ncreateRoot(document.getElementById('root')).render(<App />)\n```\n\nThe Canvas component does some important setup work behind the scenes:\n\n- It sets up a **Scene** and a **Camera**, the basic building blocks necessary for rendering\n- It renders our scene every frame, you do not need a traditional render-loop\n\n> [!NOTE]\n> Canvas is responsive to fit the parent node, so you can control how big it is by changing the parents width and height, in this case #canvas-container.\n\n## Adding a Mesh Component\n\nTo actually see something in our scene, we'll add a lowercase `<mesh />` native element, which is the direct equivalent to new THREE.Mesh().\n\n```js\n<Canvas>\n  <mesh />\n```\n\n> [!NOTE]\n> Note that we don't need to import anything, All three.js objects will be treated as native JSX elements, just like you can just write &lt;div /&gt; or &lt;span /&gt; in regular ReactDOM. The general rule is that Fiber components are available under the camel-case version of their name in three.js.\n\nA [`Mesh`](https://threejs.org/docs/#api/en/objects/Mesh) is a basic scene object in three.js, and it's used to hold the geometry and the material needed to represent a shape in 3D space.\nWe'll create a new mesh using a **BoxGeometry** and a **MeshStandardMaterial** which [automatically attach](/api/objects#attach) to their parent.\n\n```jsx\n<Canvas>\n  <mesh>\n    <boxGeometry />\n    <meshStandardMaterial />\n  </mesh>\n</Canvas>\n```\n\nLet's pause for a moment to understand exactly what is happening here. The code we just wrote is the equivalent to this three.js code:\n\n```jsx\nconst scene = new THREE.Scene()\nconst camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)\n\nconst renderer = new THREE.WebGLRenderer()\nrenderer.setSize(width, height)\ndocument.querySelector('#canvas-container').appendChild(renderer.domElement)\n\nconst mesh = new THREE.Mesh()\nmesh.geometry = new THREE.BoxGeometry()\nmesh.material = new THREE.MeshStandardMaterial()\n\nscene.add(mesh)\n\nfunction animate() {\n  requestAnimationFrame(animate)\n  renderer.render(scene, camera)\n}\n\nanimate()\n```\n\n### Constructor arguments\n\nAccording to the [docs for `BoxGeometry`](https://threejs.org/docs/#api/en/geometries/BoxGeometry) we can optionally pass three arguments for: width, length and depth:\n\n```js\nnew THREE.BoxGeometry(2, 2, 2)\n```\n\nIn order to do this in Fiber we use the `args` prop, which _always_ takes an array whose items represent the constructor arguments.\n\n```jsx\n<boxGeometry args={[2, 2, 2]} />\n```\n\n> [!NOTE]\n> Note that every time you change args, the object must be re-constructed!\n\n## Adding lights\n\nNext, we will add some lights to our scene, by putting these components into our canvas.\n\n```jsx\n<Canvas>\n  <ambientLight intensity={0.1} />\n  <directionalLight color=\"red\" position={[0, 0, 5]} />\n```\n\n### Props\n\nThis introduces us to the last fundamental concept of Fiber, how React `props` work on three.js objects. When you set any prop on a Fiber component, it will set the property of the same name on the three.js instance.\n\nLet's focus on our `ambientLight`, whose [documentation](https://threejs.org/docs/#api/en/lights/AmbientLight) tells us that we can optionally construct it with a color, but it can also receive props.\n\n```jsx\n<ambientLight intensity={0.1} />\n```\n\nWhich is the equivalent to:\n\n```jsx\nconst light = new THREE.AmbientLight()\nlight.intensity = 0.1\n```\n\n### Shortcuts\n\nThere are a few shortcuts for props that have a `.set()` method (colors, vectors, etc).\n\n```jsx\nconst light = new THREE.DirectionalLight()\nlight.position.set(0, 0, 5)\nlight.color.set('red')\n```\n\nWhich is the same as the following in JSX:\n\n```jsx\n<directionalLight position={[0, 0, 5]} color=\"red\" />\n```\n\nPlease refer to the API for [a deeper explanation](/api/objects).\n\n## The result\n\n<Sandpack\n  template=\"react\"\n  customSetup={{\n    dependencies: {\n      'react': 'latest',\n      'react-dom': 'latest',\n      'three': 'latest',\n      '@types/three': 'latest',\n      '@react-three/fiber': 'latest'\n    },\n  }}\n  files={{\n    '/styles.css': `html,body,#root {height:100%;margin:unset;}`,\n    '/App.js': `import { Canvas } from \"@react-three/fiber\";\n\nexport default function App() {\n  return (\n      <Canvas>\n        <mesh>\n          <boxGeometry args={[2, 2, 2]} />\n          <meshPhongMaterial />\n        </mesh>\n        <ambientLight intensity={0.1} />\n        <directionalLight position={[0, 0, 5]} color=\"red\" />\n      </Canvas>\n  );\n}`,\n  }}\n/>\n\n> [!TIP]\n> You can live-edit the code above:\n> - try different materials, like [`MeshNormalMaterial`](https://threejs.org/docs/#api/en/materials/MeshNormalMaterial) or [`MeshBasicMaterial`](https://threejs.org/docs/#api/en/materials/MeshBasicMaterial), give them a color\n> - try different geometries, like [`SphereGeometry`](https://threejs.org/docs/#api/en/geometries/SphereGeometry) or [`OctahedronGeometry`](https://threejs.org/docs/#api/en/geometries/OctahedronGeometry)\n> - try changing the `position` on our `mesh` component, by setting the prop with the same name\n> - try extracting our mesh to a new component\n"
  },
  {
    "path": "docs/tutorials/basic-animations.mdx",
    "content": "---\ntitle: Basic Animations\ndescription: This guide will help you understand refs, useFrame and how to make basic animations with Fiber\nnav: 17\n---\n\nThis tutorial will assume some React knowledge, and will be based on [this starter codesandbox](https://codesandbox.io/s/getting-started-01-12q81?from-embed), so just fork it and follow along!\n\nWe will build a really small, continuous animation loop, that will be the basic building block of more advanced animations later on.\n\n## `useFrame`\n\n`useFrame` is a Fiber hook that lets you execute code on every frame of Fiber's render loop. This can have a lot of uses, but we will focus on building an animation with it.\n\nIt's important to remember that **Fiber hooks can only be called inside a `<Canvas />` parent**!\n\n```jsx\nimport { useFrame } from '@react-three/fiber'\n\nfunction MyAnimatedBox() {\n  useFrame(() => {\n    console.log(\"Hey, I'm executing every frame!\")\n  })\n  return (\n    <mesh>\n      <boxGeometry />\n      <meshBasicMaterial color=\"royalblue\" />\n    </mesh>\n  )\n}\n```\n\nThis loop is the basic building block of our animation, the callback we pass to `useFrame` will be executed every frame and it will be passed an object containing the state of our Fiber scene:\n\nFor example, we can extract time information from the `clock` parameter, to know how much time has elapsed in our application, and use that time to animate a value:\n\n```jsx\nuseFrame(({ clock }) => {\n  const a = clock.elapsedTime\n  console.log(a) // the value will be 0 at scene initialization and grow each frame\n})\n```\n\n`clock` is a [three.js Clock](https://threejs.org/docs/#api/en/core/Clock) object, from which we are getting the total elapsed time, which will be key for our animations.\n\n## Animating with Refs\n\nIt would be tempting to just update the state of our component via `setState` and let it change the `mesh` via props, but going through state isn't ideal, when dealing with continuous updates, commonly know as [transient updates]().\nInstead, we want to **directly mutate our mesh each frame**. First, we'll have to get a `reference` to it, via the `useRef` React hook:\n\n```jsx\nimport React from 'react'\n\nfunction MyAnimatedBox() {\n  const myMesh = React.useRef()\n  return (\n    <mesh ref={myMesh}>\n      <boxGeometry />\n      <meshBasicMaterial color=\"royalblue\" />\n    </mesh>\n  )\n}\n```\n\n`myMesh` will now hold a reference to the actual three.js object, which we can now freely mutate in `useFrame`, without having to worry about React:\n\n```jsx\nuseFrame(({ clock }) => {\n  myMesh.current.rotation.x = clock.elapsedTime\n})\n```\n\nLet's have a closer look:\n\n- We are destructuring `clock` from the argument passed to `useFrame`, which we know is the state of our Fiber scene.\n- We are accessing the `rotation.x` property of `myMesh.current` object, which is a reference to our mesh object\n- We are assigning our time-dependent value `a` to the `rotation` on the `x` axis, meaning our object will now infinitely rotate between -1 and 1 radians around the x axis!\n\n<Codesandbox id=\"29gxw\" />\n\n**Exercises**\n\n- Try `Math.sin(clock.elapsedTime)` and see how your animation changes\n\n## Next steps\n\nNow that you understand the basic technique for animating in Fiber, [learn how event works](/tutorials/events-and-interaction)!\n\nIf you want to go deeper into animations, check these out:\n\n- [Animating with React Spring](/tutorials/using-with-react-spring)\n"
  },
  {
    "path": "docs/tutorials/events-and-interaction.mdx",
    "content": "---\ntitle: 'Events and Interaction'\ndescription: Let's make our meshes react to user input.\nnav: 14\n---\n\nThis tutorial will assume some React knowledge, and will be based on [this starter codesandbox](https://codesandbox.io/s/getting-started-01-12q81?from-embed), so just fork it and follow along!\n\nAfter we have our continuous loop running the next step would be to allow our mesh to react to user interaction, so in this part let's attach a click handler to the cube and make it bigger on click.\n\n## User Interaction\n\nAny Object3D that has a raycast method can receive a large number of events, for instance a mesh:\n\n```jsx\n<mesh\n  onClick={(e) => console.log('click')}\n  onContextMenu={(e) => console.log('context menu')}\n  onDoubleClick={(e) => console.log('double click')}\n  onWheel={(e) => console.log('wheel spins')}\n  onPointerUp={(e) => console.log('up')}\n  onPointerDown={(e) => console.log('down')}\n  onPointerOver={(e) => console.log('over')}\n  onPointerOut={(e) => console.log('out')}\n  onPointerEnter={(e) => console.log('enter')}\n  onPointerLeave={(e) => console.log('leave')}\n  onPointerMove={(e) => console.log('move')}\n  onPointerMissed={() => console.log('missed')}\n  onUpdate={(self) => console.log('props have been updated')}\n/>\n```\n\nFrom this we can see that what we need to do is use the old `onClick` event we use on any DOM element to react to a user clicking the mesh.\n\nLet's add it then:\n\n```jsx\n<mesh onClick={() => alert('Hellooo')}>\n  <boxGeometry />\n  <meshPhongMaterial color=\"royalblue\" />\n</mesh>\n```\n\nWe did it! We created the most boring interaction in the story of 3D and we made an alert show up. Now let's make it actually animate our mesh.\n\nLet's start by setting some state to check if the mesh is active:\n\n```jsx\nconst [active, setActive] = useState(false)\n```\n\nAfter we have this we can set the scale with a ternary operator like so:\n\n```jsx\n<mesh scale={active ? 1.5 : 1} onClick={() => setActive(!active)}>\n  <boxGeometry />\n  <meshPhongMaterial color=\"royalblue\" />\n</mesh>\n```\n\nIf you try to click on your mesh now, it scales up and down. We just made our first interactive 3D mesh!\n\nWhat we did in this chapter was:\n\n- Attached a click handler to our mesh\n- Added some state to track if the mesh is currently active\n- Changed the scale based on that state\n\n<Codesandbox id=\"98ppy\" />\n\n**Exercises**\n\n- Change other props of the mesh like the `position` or even the `color` of the material.\n- Use `onPointerOver` and `onPointerOut` to change the props of the mesh on hover events.\n\n## Next steps\n\nWe just made our mesh react to user interaction but it looks pretty bland without any transition, right?\nIn the next chapter let's integrate `react-spring` into our project to make this into an actual animation.\n"
  },
  {
    "path": "docs/tutorials/how-it-works.mdx",
    "content": "---\ntitle: How does it work?\ndescription: This is an advanced guide on the inner workings of Fiber, if you are just getting started, take a\n  look at our introduction!\nnav: 18\n---\n\nReact Three Fiber is a React <a href=\"https://reactjs.org/docs/codebase-overview.html#renderers\">renderer</a> for **three.js**.\n\nThis means that each Fiber component will effectively create a new THREE object that will be added to a `scene`.\nUnderstanding how this works is not necessarily needed to use Fiber, but it will better arm you to deal with anything that you might need in your projects, reading other people's Fiber code and even help you contribute.\n\nLet's take a small React example:\n\n```jsx\nimport { Canvas } from '@react-three/fiber'\n\nfunction MyApp() {\n  return (\n    <Canvas>\n      <group>\n        <mesh>\n          <meshNormalMaterial />\n          <boxGeometry args={[2, 2, 2]} />\n        </mesh>\n      </group>\n    </Canvas>\n  )\n}\n```\n\nIn three.js, this is equivalent to:\n\n```js\nimport * as THREE from 'three'\n\nconst scene = new THREE.Scene() // <Canvas>\n\nconst group = new THREE.Group() // <group>\n\nconst mesh = new THREE.Mesh() // <mesh />\nconst material = new THREE.MeshNormalMaterial() // <meshNormalMaterial />\nconst geometry = new THREE.BoxGeometry(2, 2, 2) // <boxGeometry />\n\nmesh.material = material\nmesh.geometry = geometry\n\ngroup.add(mesh)\nscene.add(group)\n```\n\nOur `Canvas` element will create a new scene, and Fiber will instantiate new objects for each component and correctly compose them together in a scene graph!\n\nAdditionally, Fiber will:\n\n- Setup a new perspective camera at [0, 0, 0] and set it as default\n- Setup a **render loop** with automatic render to screen\n- Setup pointer events via raycasting on all meshes with `onPointer` props\n- Setup tone mapping\n- Automatically handle window resize\n\n**Let's break this down!**\n\n## Creating THREE objects\n\nIn three.js, we can create new object using the classic JS API:\n\n```js\nconst myBox = new THREE.BoxGeometry(1, 2, 3)\n```\n\nObject creation is handled transparently by the Fiber renderer, the name of the constructor `BoxGeometry` is equivalent to the camel case component `<boxGeometry />`, while the constructor arguments - in our example `[1, 2, 3]` - are passed via the `args` prop:\n\n```jsx\n<boxGeometry args={[1, 2, 3]} />\n```\n\n> [!NOTE]\n> Note that the object will be created only when first adding the component to the React tree!\n\n## The `attach` props\n\nFiber always tries to correctly infer the relationship between components and their parents, for example:\n\n```jsx\n<group>\n  <mesh />\n</group>\n```\n\nHere, we always know that a group can only have children, so Fiber just calls the `add` method on the group:\n\n```js\ngroup.add(mesh)\n```\n\nFor meshes and other three.js objects, rules can be different. Looking at the three.js documentation, we can see how a `THREE.Mesh` object is constructed using a `material` and a `geometry`.\n\nWith the `attach` prop, we can precisely tell the renderer what property to attach each component to:\n\n```jsx\n<mesh>\n  <meshNormalMaterial attach=\"material\" />\n  <boxGeometry attach=\"geometry\" />\n</mesh>\n```\n\nThis will _explicitly_ tell Fiber to render like this:\n\n```js\nmesh.material = new THREE.MeshNormalMaterial()\nmesh.geometry = new THREE.BoxGeometry()\n```\n\nAs you can see, the `attach` prop is telling Fiber to set the parent's `material` property to a reference to our `<meshNormalMaterial />` object.\n\nNote that while we used geometry and material for this example, Fiber also infers the attach property from the\nconstructor name, so anything with `material` or `geometry` will automatically get attached to the correct property of\nits parent.\n\n## Props\n\nWith Fiber, you can pass any three.js property as a React property, and it will be assigned to the constructed object:\n\n```jsx\n<meshBasicMaterial color=\"red\" />\n```\n\nis equivalent to:\n\n```jsx\nconst material = new THREE.MeshBasicMaterial()\nmaterial.color = 'red'\n```\n\nFiber will check the type of the property value and either:\n\n- assign the new value directly\n- if the value is an object with a `set` method, call that\n- construct a new object if needed.\n- convert between formats\n\n```jsx\n<mesh scale={[1, 2, 3]} />\n```\n\nis equivalent to:\n\n```jsx\nconst mesh = new THREE.Mesh()\nmesh.scale = new THREE.Vector3(1, 2, 3)\n\n// on update, it will instead `set()` the vector\nmesh.scale.set(3, 4, 5)\n```\n\n## Pointer Events\n\n[Pointer Events](/api/events) are transparently handled by Fiber. On startup, it will create a [raycaster](https://threejs.org/docs/#api/en/core/Raycaster) for mouse picking.\n\nEvery object with `onPointer` props will be added to the array of objects checked every frame by the raycaster:\n\n```jsx\n<mesh onPointerDown={console.log}>...</mesh>\n```\n\nThe ray's `origin` and `direction` are updated every time the mouse moves on the `<Canvas />` element or the window is resized.\nFiber also handles camera switching, meaning that the raycaster will always use the currently active camera.\n\nWhen using the `raycast` prop, the object will instead be picked using a custom ray:\n\n```jsx\nimport { useCamera } from '@react-three/drei'\n\nreturn <mesh raycast={useCamera(anotherCamera)} />\n```\n\n## Render Loop\n\nBy default, Fiber will setup a render loop that renders the default `scene` from the default `camera` to a [WebGLRenderer](https://threejs.org/docs/#api/en/renderers/WebGLRenderer).\n\nThe loop is setup using [setAnimationLoop](https://threejs.org/docs/#api/en/renderers/WebGLRenderer.setAnimationLoop), which will execute its callback every time a new frame is renderable. This is what will happen every render:\n\n1. All global before effects are executed\n2. Clock delta is saved - implying all `useFrame` calls will share the same `delta`\n3. `useFrame` callbacks are executed in order\n4. `renderer.render(scene, camera)` is called, effectively rendering the scene to screen\n5. All global after effects are executed\n"
  },
  {
    "path": "docs/tutorials/loading-models.mdx",
    "content": "---\ntitle: 'Loading Models'\ndescription: 3D Software to the web!\nnav: 15\n---\n\n> All the models in this page were created by Sara Vieira and are freely available to download from any of the sandboxes.\n\nThere are many types of 3D model extensions, in this page we will focus on loading the three most common ones: `GLTF`, `FBX` and `OBJ`. All of these will use the `useLoader` function but in slightly different ways.\n\nThis whole section will assume you have placed your models in the public folder or in a place in your application where you can import them easily.\n\n## Loading GLTF models\n\nStarting with the open standard and the one that has more support in React Three Fiber we will load a `.gltf` model.\n\nLet's start by importing the two things we need:\n\n```js\nimport { useLoader } from '@react-three/fiber'\nimport { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'\n```\n\nWith this we can create a Model component and place it in our scene like so:\n\n```jsx\nfunction Scene() {\n  const gltf = useLoader(GLTFLoader, '/Poimandres.gltf')\n  return <primitive object={gltf.scene} />\n}\n```\n\nYou can play with the sandbox and see how it looks here after I added an HDRI background:\n\n<Codesandbox id=\"6etx1\" />\n\n### Loading GLTF models as JSX Components\n\nHere comes the really fancy part, you can transform these models into React components and then use them as you would any React component.\n\nTo do this, grab your `GLTF` model and head over to [https://gltf.pmnd.rs/](https://gltf.pmnd.rs/) and drop your `GLTF`, after that you should see something like:\n\n![gltfjsx](gltfjsx.png)\n\nLet's now copy the code and move it over to `Model.js`:\n\n```jsx\n/*\nAuto-generated by: https://github.com/pmndrs/gltfjsx\n*/\n\nimport React, { useRef } from 'react'\nimport { useGLTF } from '@react-three/drei'\n\nexport default function Model(props) {\n  const groupRef = useRef()\n  const { nodes, materials } = useGLTF('/Poimandres.gltf')\n  return (\n    <group ref={groupRef} {...props} dispose={null}>\n      <mesh castShadow receiveShadow geometry={nodes.Curve007_1.geometry} material={materials['Material.001']} />\n      <mesh castShadow receiveShadow geometry={nodes.Curve007_2.geometry} material={materials['Material.002']} />\n    </group>\n  )\n}\n\nuseGLTF.preload('/Poimandres.gltf')\n```\n\nNow we can import our model like we would import any React component and use it in our app:\n\n```jsx\nimport { Suspense } from 'react'\nimport { Canvas } from '@react-three/fiber'\nimport { Environment } from '@react-three/drei'\n\nimport Model from './Model'\n\nexport default function App() {\n  return (\n    <div className=\"App\">\n      <Canvas>\n        <Suspense fallback={null}>\n          <Model />\n          <Environment preset=\"sunset\" background />\n        </Suspense>\n      </Canvas>\n    </div>\n  )\n}\n```\n\nYou can play with the sandbox here:\n\n<Codesandbox id=\"vbnbf\" />\n\n## Loading OBJ models\n\nIn this case, we will use the trusted `useLoader` hook but in combination with `three.js` `OBJLoader`.\n\n```js\nimport { OBJLoader } from 'three/addons/loaders/OBJLoader.js'\nimport { useLoader } from '@react-three/fiber'\n```\n\nWith these imported let's get the mesh into our scene:\n\n```jsx\nfunction Scene() {\n  const obj = useLoader(OBJLoader, '/Poimandres.obj')\n  return <primitive object={obj} />\n}\n```\n\nAnd here we go, we have an OBJ model showing on the web! Pretty cool ah?\n\nYou can play with the sandbox here:\n\n<Codesandbox id=\"51zks\" />\n\n## Loading FBX models\n\nLet's again use the trusted `useLoader` but this time with the `FBXLoader` that comes from `three.js`\n\n```js\nimport { useLoader } from '@react-three/fiber'\nimport { FBXLoader } from 'three/addons/loaders/FBXLoader.js'\n```\n\nTo create our scene we can get the FBX as a return value of the useLoader by passing the `FBXloader` and the location of our file like so:\n\n```jsx\nfunction Scene() {\n  const fbx = useLoader(FBXLoader, '/Poimandres.fbx')\n  return <primitive object={fbx} />\n}\n```\n\nYou can play with the sandbox here:\n\n<Codesandbox id=\"ssrfg\" />\n\n### Loading FBX models using `useFBX`\n\n[@react-three/drei](https://github.com/pmndrs/drei) exports a very useful helper when it comes to loading FBX models and it's called `useFBX`, in this case there is no need to import anything from `three.js` as it is all done behind the scenes and we can just pass the location of the file to `useFBX` like so:\n\n```jsx\nfunction Scene() {\n  const fbx = useFBX('/Poimandres.fbx')\n  return <primitive object={fbx} />\n}\n```\n\nYou can play with the sandbox here:\n\n<Codesandbox id=\"m6p73\" />\n\n## Showing a loader\n\nIf your model is big and takes a while to load, it's always good to show a small loader of how much is already is loaded and again [@react-three/drei](https://github.com/pmndrs/drei) is here to help with `Html` and `useProgress`.\n\n- `Html` allows you place plain ol' HTML in your canvas and render it like you would a normal DOM element.\n- `useProgress` is a hook that gives you a bunch of information about the loading status of your model.\n\nWith these two things, we can create a very bare-bones loading component like so:\n\n```jsx\nimport { Html, useProgress } from '@react-three/drei'\n\nfunction Loader() {\n  const { progress } = useProgress()\n  return <Html center>{progress} % loaded</Html>\n}\n```\n\nWe can then wrap our model in it using `Suspense` like so:\n\n```jsx\nexport default function App() {\n  return (\n    <Canvas>\n      <Suspense fallback={<Loader />}>\n        <Model />\n      </Suspense>\n    </Canvas>\n  )\n}\n```\n\nThe hook returns much more than just the progress so there is a lot you can do there to give the user more information about the loading status of the application. You can play with all of them in this sandbox:\n\n<Codesandbox id=\"nn2m7\" />\n"
  },
  {
    "path": "docs/tutorials/loading-textures.mdx",
    "content": "---\ntitle: 'Loading Textures'\ndescription: Let's load some fancy textures.\nnav: 16\n---\n\n> All textures used in this chapter were downloaded from [cc0textures](https://cc0textures.com/).\n\n## Using `TextureLoader` and `useLoader`\n\nTo load the textures we will use the `TextureLoader` from three.js in combination with `useLoader` that will allow us to pass the location of the texture and get the map back.\n\nIt's better to explain with code, let's say you downloaded [this texture](https://cc0textures.com/view?id=PavingStones092) and placed it in the public folder of your site, to get the color map from it you could do:\n\n```js\nconst colorMap = useLoader(TextureLoader, 'PavingStones092_1K_Color.jpg')\n```\n\nLet's then with this information create a small scene where we can use this texture:\n\n```jsx\nimport { Suspense } from 'react'\nimport { Canvas, useLoader } from '@react-three/fiber'\nimport { TextureLoader } from 'three'\n\nfunction Scene() {\n  const colorMap = useLoader(TextureLoader, 'PavingStones092_1K_Color.jpg')\n  return (\n    <>\n      <ambientLight intensity={0.2} />\n      <directionalLight />\n      <mesh>\n        <sphereGeometry args={[1, 32, 32]} />\n        <meshStandardMaterial />\n      </mesh>\n    </>\n  )\n}\n\nexport default function App() {\n  return (\n    <Canvas>\n      <Suspense fallback={null}>\n        <Scene />\n      </Suspense>\n    </Canvas>\n  )\n}\n```\n\nIf everything went according to plan, you should now be able to apply this texture to the sphere like so:\n\n```jsx\n<meshStandardMaterial map={colorMap} />\n```\n\nAwesome! That works but we have a lot more textures to import and do we have to create a different useLoader for each of them?\n\nThat's the great part! You don't, the second argument is an array where you can pass all the textures you have and the maps will be returned and ready to use:\n\n```js\nconst [colorMap, displacementMap, normalMap, roughnessMap, aoMap] = useLoader(TextureLoader, [\n  'PavingStones092_1K_Color.jpg',\n  'PavingStones092_1K_Displacement.jpg',\n  'PavingStones092_1K_Normal.jpg',\n  'PavingStones092_1K_Roughness.jpg',\n  'PavingStones092_1K_AmbientOcclusion.jpg',\n])\n```\n\nNow we can place them in our mesh like so:\n\n```jsx\n<meshStandardMaterial\n  map={colorMap}\n  displacementMap={displacementMap}\n  normalMap={normalMap}\n  roughnessMap={roughnessMap}\n  aoMap={aoMap}\n/>\n```\n\nThe displacement will probably be too much, usually setting it to 0.2 will make it look good. Our final code would look something like:\n\n```jsx\nfunction Scene() {\n  const [colorMap, displacementMap, normalMap, roughnessMap, aoMap] = useLoader(TextureLoader, [\n    'PavingStones092_1K_Color.jpg',\n    'PavingStones092_1K_Displacement.jpg',\n    'PavingStones092_1K_Normal.jpg',\n    'PavingStones092_1K_Roughness.jpg',\n    'PavingStones092_1K_AmbientOcclusion.jpg',\n  ])\n  return (\n    <mesh>\n      {/* Width and height segments for displacementMap */}\n      <sphereGeometry args={[1, 100, 100]} />\n      <meshStandardMaterial\n        displacementScale={0.2}\n        map={colorMap}\n        displacementMap={displacementMap}\n        normalMap={normalMap}\n        roughnessMap={roughnessMap}\n        aoMap={aoMap}\n      />\n    </mesh>\n  )\n}\n```\n\n## Using `useTexture`\n\nAnother way to import these is using `useTexture` from [`@react-three/drei`](https://github.com/pmndrs/drei), that will make it slightly easier and there is no need to import the `TextureLoader`, our code would look like:\n\n```js\nimport { useTexture } from \"@react-three/drei\"\n\n...\n\nconst [colorMap, displacementMap, normalMap, roughnessMap, aoMap] = useTexture([\n  'PavingStones092_1K_Color.jpg',\n  'PavingStones092_1K_Displacement.jpg',\n  'PavingStones092_1K_Normal.jpg',\n  'PavingStones092_1K_Roughness.jpg',\n  'PavingStones092_1K_AmbientOcclusion.jpg',\n])\n```\n\nYou can also use object-notation which is the most convenient:\n\n```jsx\nconst props = useTexture({\n  map: 'PavingStones092_1K_Color.jpg',\n  displacementMap: 'PavingStones092_1K_Displacement.jpg',\n  normalMap: 'PavingStones092_1K_Normal.jpg',\n  roughnessMap: 'PavingStones092_1K_Roughness.jpg',\n  aoMap: 'PavingStones092_1K_AmbientOcclusion.jpg',\n})\n\nreturn (\n  <mesh>\n    <sphereGeometry args={[1, 32, 32]} />\n    <meshStandardMaterial {...props} />\n  </mesh>\n)\n```\n\nYou can play with the sandbox and see how it looks:\n\n<Codesandbox id=\"rusfd\" />\n"
  },
  {
    "path": "docs/tutorials/v9-migration-guide.mdx",
    "content": "---\ntitle: 'v9 Migration Guide'\ndescription: Changes and new features with v9 and React 19\nnav: 13\n---\n\nThis is a compatibility release for React 19, which brings further performance, stability, and type improvements. You can check out the React 19 changelog [here](https://react.dev/blog/2024/04/25/react-19).\n\nWe would like to express our gratitude to the community for their continued support, as well as to all our contributors, including the React team at Meta and Vercel, for ensuring this upgrade went smoothly. 🎉\n\n> [!WARNING]  \n> This release contains breaking changes when using Strict Mode, which can highlight bugs during development. See [StrictMode](#strictmode).\n\n## Features\n\n### useLoader Accepts Loader Instance\n\n`useLoader` now supports re-use of external loader instances for more controlled pooling and setup.\n\n```jsx\nimport { GLTFLoader } from 'three/addons'\nimport { useLoader } from '@react-three/fiber'\n\nfunction Model() {\n  const gltf = useLoader(GLTFLoader, '/path/to/model.glb')\n  // ...\n}\n\n// or,\n\nconst loader = new GLTFLoader()\nfunction Model() {\n  const gltf = useLoader(loader, '/path/to/model.glb')\n  // ...\n}\n```\n\n### Factory extend Signature\n\n`extend` can now produce a component when a three.js class is passed to it individually instead of a catalog of named classes. This is backwards compatible and reduces TypeScript boilerplate and JSX collisions. We recommend libraries migrate to this signature so internal components don't clash with user-land declarations.\n\n```tsx\nimport { OrbitControls } from 'three/addons'\nimport { type ThreeElement, type ThreeElements } from '@react-three/fiber'\n\ndeclare module '@react-three/fiber' {\n  interface ThreeElements {\n    orbitControls: ThreeElement<typeof OrbitControls>\n  }\n}\n\nextend({ OrbitControls })\n\n<orbitControls args={[camera, gl.domElement]}>\n\n// or,\n\nconst Controls = extend(OrbitControls)\n<Controls args={[camera, gl.domElement]} />\n```\n\n### Async GL prop\n\nThe Canvas GL prop accepts constructor parameters, properties, or a renderer instance via a callback. The callback now passes constructor parameters instead of just a canvas reference.\n\n```diff\n<Canvas\n  gl={{ reverseDepthBuffer: true }}\n- gl={(canvas) => new WebGLRenderer({ canvas })}\n+ gl={(props) => new WebGLRenderer(props)}\n>\n```\n\nFurther, a callback passed to GL can now return a promise for async constructors like `WebGPURenderer` (see [WebGPU](#webgpu)).\n\n```tsx\n<Canvas\n  gl={async (props) => {\n    // ...\n    return renderer\n  }}\n>\n````\n\n## WebGPU\n\nRecent Three.js now includes a WebGPU renderer. While still a work in progress and not fully backward-compatible with all of Three's features, the renderer requires an async initialization method. R3F streamlines this by allowing the gl prop to return a promise.\n\n```tsx\nimport * as THREE from 'three/webgpu'\nimport * as TSL from 'three/tsl'\nimport { Canvas, extend, useFrame, useThree } from '@react-three/fiber'\n\ndeclare module '@react-three/fiber' {\n  interface ThreeElements extends ThreeToJSXElements<typeof THREE> {}\n}\n\nextend(THREE as any)\n\nexport default () => (\n  <Canvas\n    gl={async (props) => {\n      const renderer = new THREE.WebGPURenderer(props as any)\n      await renderer.init()\n      return renderer\n    }}>\n      <mesh>\n        <meshBasicNodeMaterial />\n        <boxGeometry />\n      </mesh>\n  </Canvas>\n)\n```\n\n## Fixes\n\n### Color Management of Textures\n\nAutomatic sRGB conversion of texture props has been removed. Color textures are now handled automatically for built-in materials, aligning with vanilla Three.js behavior. This prevents issues where data textures (e.g., normals or displacement) become corrupted or non-linear. For custom materials or shaders, annotate color textures with `texture.colorSpace = THREE.SRGBColorSpace` or `texture-colorSpace={THREE.SRGBColorSpace}` in JSX.\n\nFor more details, see https://threejs.org/docs/#manual/en/introduction/Color-management.\n\n### Suspense and Side-Effects\n\nThe handling of Suspense and fallback content has improved in React and R3F. Side-effects like attach and constructor effects (e.g., controls adding event listeners) no longer fire repeatedly without proper cleanup during suspension.\n\n```jsx\nimport { ThreeElement, useThree } from '@react-three/fiber'\nimport { OrbitControls } from 'three/addons'\n\ndeclare module '@react-three/fiber' {\n  interface ThreeElements {\n    OrbitControls: ThreeElement<typeof OrbitControls>\n  }\n}\n\nextend({ OrbitControls })\n\nfunction Controls() {\n  const camera = useThree((state) => state.camera)\n  const gl = useThree((state) => state.gl)\n\n  // Will only initialize when tree is connected to screen\n  return <orbitControls args={[camera, gl.domElement]}>\n}\n\n<Suspense>\n  <Controls />\n  <AsyncComponent />\n</Suspense>\n```\n\n### Swapping with args and primitives\n\nSwapping elements when changing the `args` or primitive `object` prop has been improved for structured children like arrays or iterators (React supports both, including async iterators). Previously, primitives sharing an object could update out of order or be removed from the scene along with their children.\n\nSee: https://github.com/pmndrs/react-three-fiber/pull/3272\n\n## TypeScript Changes\n\n### Props renamed to CanvasProps\n\nCanvas `Props` is now called `CanvasProps` for clarity. These were aliased in v8 for forward compatibility, but `Props` is removed with v9.\n\n```diff\n-function Canvas(props: Props)\n+function Canvas(props: CanvasProps)\n```\n\n### Dynamic JSX Types\n\nSince R3F's creation, JSX types had to be maintained in accordance with additions to three core API. Although missing or future types could be ignored or resilient for forward and backwards compatibility, this was a major maintenance burden for us and those extending three. Furthermore, libraries which wrap or bind to the known catalog of elements (e.g. React Spring `<animated.mesh />`) had no way of knowing which elements belonged to a renderer.\n\nSince v8, we've added a catalog of known elements to a `ThreeElements` interface, and with v9 automatically map three API to JSX types. As types are now dynamically mapped, hardcoded exports like `MeshProps` have been removed, and can be accessed as `ThreeElements['mesh']`. Helper types like `Color` or `Vector3` remain to reflect the JSX `MathType` API for shorthand expression.\n\n```diff\n-import { MeshProps } from '@react-three/fiber'\n-type Props = MeshProps\n\n+import { ThreeElements } from '@react-three/fiber'\n+type Props = ThreeElements['mesh']\n```\n\n### Node Helpers\n\nSpecialized `Node` type helpers for extending JSX (`Node`, `Object3DNode`, `BufferGeometryNode`, `MaterialNode`, `LightNode`) are removed and combined into 'ThreeElement', which accepts a single type representing the extended element instance.\n\n```tsx\nimport { type ThreeElement } from '@react-three/fiber'\n\ndeclare module '@react-three/fiber' {\n  interface ThreeElements {\n    customElement: ThreeElement<typeof CustomElement>\n  }\n}\n\nextend({ CustomElement })\n```\n\n### ThreeElements\n\n`ThreeElements` was added as an interface since v8.1.0 and is the current way of declaring or accessing JSX within R3F since React's depreciation of `global` JSX (see https://react.dev/blog/2024/04/25/react-19-upgrade-guide#the-jsx-namespace-in-typescript). All JSX types belonging to R3F are accessible from `ThreeElements`.\n\n```diff\n-import { type Node } from '@react-three/fiber'\n-\n-declare global {\n-  namespace JSX {\n-    interface IntrinsicElements {\n-      customElement: Node<CustomElement, typeof CustomElement>\n-    }\n-  }\n-}\n-\n-extend({ CustomElement })\n\n+import { type ThreeElement } from '@react-three/fiber'\n+\n+declare module '@react-three/fiber' {\n+  interface ThreeElements {\n+    customElement: ThreeElement<typeof CustomElement>\n+  }\n+}\n+\n+extend({ CustomElement })\n```\n\n## Testing\n\n### StrictMode\n\n`StrictMode` is now correctly inherited from a parent renderer like react-dom. Previously, `<StrictMode />` mounted in another root such as react-dom would not affect the R3F canvas, so it had to be redeclared within the canvas as well.\n\n```diff\n<StrictMode>\n  <Canvas>\n-    <StrictMode>\n-      // ...\n-    </StrictMode>\n+    // ...\n  </Canvas>\n</StrictMode>\n```\n\nKeep in mind, this change may affect the behavior of your application. If you encounter anything that worked before and fails now, profile it first in dev and then production. If it works in prod then strict mode has flushed out a side-effect in your code.\n\n### Act\n\n`act` is now exported from React itself and can be used for all renderers. It will return the contents of a passed async callback like before and recursively flush async effects to synchronously test React output.\n\n```tsx\nimport { act } from 'react'\nimport { createRoot } from '@react-three/fiber'\n\nconst store = await act(async () => createRoot(canvas).render(<App />))\nconsole.log(store.getState())\n```\n"
  },
  {
    "path": "example/.gitignore",
    "content": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\n"
  },
  {
    "path": "example/CHANGELOG.md",
    "content": "# example\n\n## 1.1.0\n\n### Minor Changes\n\n- 85c80e70: eventsource and eventprefix on the canvas component\n\n## 1.0.0\n\n### Major Changes\n\n- 385ba9c: v8 major, react-18 compat\n- 04c07b8: v8 major, react-18 compat\n\n## 1.0.0-beta.0\n\n### Major Changes\n\n- 385ba9c: v8 major, react-18 compat\n"
  },
  {
    "path": "example/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"favicon.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>react-three-fiber example</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/index.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "example/package.json",
    "content": "{\n  \"name\": \"example\",\n  \"version\": \"0.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"serve\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@react-three/drei\": \"^9.105.5\",\n    \"@use-gesture/react\": \"latest\",\n    \"react\": \"19.2.0\",\n    \"react-dom\": \"19.2.0\",\n    \"react-use-refs\": \"^1.0.1\",\n    \"three\": \"^0.172.0\",\n    \"three-stdlib\": \"^2.35.16\",\n    \"use-error-boundary\": \"^2.0.6\",\n    \"wouter\": \"^2.12.1\",\n    \"zustand\": \"^4.4.7\"\n  },\n  \"devDependencies\": {\n    \"@types/three\": \"^0.172.0\",\n    \"@types/react\": \"^19.2.7\",\n    \"@types/react-dom\": \"^19.2.3\",\n    \"@vitejs/plugin-react-refresh\": \"^1.3.6\",\n    \"@vitejs/plugin-react\": \"^4.2.1\",\n    \"typescript\": \"^5.3.3\",\n    \"vite\": \"^5.2.10\"\n  }\n}\n"
  },
  {
    "path": "example/public/apple.gltf",
    "content": "{\n  \"extensionsUsed\": [\"KHR_materials_unlit\", \"KHR_draco_mesh_compression\"],\n  \"asset\": { \"generator\": \"UniGLTF-1.27\", \"version\": \"2.0\" },\n  \"buffers\": [\n    {\n      \"name\": \"buffer\",\n      \"byteLength\": 1696,\n      \"uri\": \"data:application/octet-stream;base64,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\"\n    }\n  ],\n  \"bufferViews\": [\n    { \"buffer\": 0, \"byteOffset\": 0, \"byteLength\": 733 },\n    { \"buffer\": 0, \"byteOffset\": 736, \"byteLength\": 249 },\n    { \"buffer\": 0, \"byteOffset\": 988, \"byteLength\": 303 },\n    { \"buffer\": 0, \"byteOffset\": 1292, \"byteLength\": 216 },\n    { \"buffer\": 0, \"byteOffset\": 1508, \"byteLength\": 188 }\n  ],\n  \"accessors\": [\n    { \"type\": \"SCALAR\", \"componentType\": 5125, \"count\": 270, \"min\": [0], \"max\": [135] },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 136,\n      \"max\": [0.09829112117938082, 0.15707400633348856, 0.09824317429100063],\n      \"min\": [-0.00009589377676037153, -0.00009589377676037153, -0.09824317429100064]\n    },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 136,\n      \"min\": [-1.007843137254902, -1.007843137254902, -0.9083239440824471],\n      \"max\": [0.9058823529411764, 1.007843137254902, 0.9112838990548078]\n    },\n    {\n      \"type\": \"VEC2\",\n      \"componentType\": 5126,\n      \"count\": 136,\n      \"min\": [-3.872480570978666, -5.190146624750639],\n      \"max\": [3.868810683984095, 3.4346615669198286]\n    },\n    { \"type\": \"SCALAR\", \"componentType\": 5125, \"count\": 30, \"min\": [0], \"max\": [19] },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 20,\n      \"max\": [0.00963633247975553, 0.19077478181780055, 0.009620332180030559],\n      \"min\": [-0.000021366590864202948, 0.1469946371370487, -0.009630966188616297]\n    },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 20,\n      \"min\": [-1.0078249400737238, -0.10404930024170408, -1.0031846588733149],\n      \"max\": [1.003184658873315, 1.007092190227088, 1.0000000000000002]\n    },\n    {\n      \"type\": \"VEC2\",\n      \"componentType\": 5126,\n      \"count\": 20,\n      \"min\": [-0.38604318408089766, -6.51924526003915],\n      \"max\": [0.3852044690039851, 1.386043184080898]\n    },\n    { \"type\": \"SCALAR\", \"componentType\": 5125, \"count\": 78, \"min\": [0], \"max\": [23] },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 24,\n      \"max\": [0.00007905138808456816, 0.1404482741236439, 0.08098814709264007],\n      \"min\": [-0.00007905138808456816, 0.016574748995125614, -0.08098814709264009]\n    },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 24,\n      \"min\": [-1.003921568627451, -0.00392156862745098, -0.00392156862745098],\n      \"max\": [-0.996078431372549, 0.00392156862745098, 0.00392156862745098]\n    },\n    {\n      \"type\": \"VEC2\",\n      \"componentType\": 5126,\n      \"count\": 24,\n      \"min\": [-3.191625186308621, -4.532298156126736],\n      \"max\": [3.191625186308621, 0.35010993375806176]\n    },\n    { \"type\": \"SCALAR\", \"componentType\": 5125, \"count\": 12, \"min\": [0], \"max\": [7] },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 8,\n      \"max\": [0.00002426540176054298, 0.09019649050616613, 0.024859904103676285],\n      \"min\": [-0.00002426540176054298, 0.06954663360794405, -0.024859904103676285]\n    },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 8,\n      \"min\": [-1.003921568627451, -0.00392156862745098, -0.00392156862745098],\n      \"max\": [-0.996078431372549, 0.00392156862745098, 0.00392156862745098]\n    },\n    {\n      \"type\": \"VEC2\",\n      \"componentType\": 5126,\n      \"count\": 8,\n      \"min\": [-0.9796926527778425, -2.552443459586314],\n      \"max\": [0.9796926527778425, -1.7361922152231166]\n    },\n    { \"type\": \"SCALAR\", \"componentType\": 5125, \"count\": 6, \"min\": [0], \"max\": [5] },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 6,\n      \"max\": [0.005384004925540091, 0.147021261545223, 0.00538663383419514],\n      \"min\": [-0.000005257817310097745, 0.1470107459106028, -0.00538663383419514]\n    },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 6,\n      \"min\": [-0.00392156862745098, 0.996078431372549, -0.00392156862745098],\n      \"max\": [0.00392156862745098, 1.003921568627451, 0.00392156862745098]\n    },\n    {\n      \"type\": \"VEC2\",\n      \"componentType\": 5126,\n      \"count\": 6,\n      \"min\": [-0.38001393852345755, -6.511660094834492],\n      \"max\": [0.21416659508982017, -4.786348347090556]\n    }\n  ],\n  \"materials\": [\n    {\n      \"name\": \"red\",\n      \"pbrMetallicRoughness\": {\n        \"baseColorFactor\": [0.9098039, 0.333333343, 0.3254902, 1],\n        \"metallicFactor\": 1,\n        \"roughnessFactor\": 1\n      },\n      \"doubleSided\": false,\n      \"alphaMode\": \"OPAQUE\",\n      \"emissiveFactor\": [0, 0, 0]\n    },\n    {\n      \"name\": \"brown\",\n      \"pbrMetallicRoughness\": {\n        \"baseColorFactor\": [0.827451, 0.5647059, 0.403921574, 1],\n        \"metallicFactor\": 1,\n        \"roughnessFactor\": 1\n      },\n      \"doubleSided\": false,\n      \"alphaMode\": \"OPAQUE\",\n      \"emissiveFactor\": [0, 0, 0]\n    },\n    {\n      \"name\": \"brownLight\",\n      \"pbrMetallicRoughness\": {\n        \"baseColorFactor\": [0.9764706, 0.772549033, 0.549019635, 1],\n        \"metallicFactor\": 1,\n        \"roughnessFactor\": 1\n      },\n      \"doubleSided\": false,\n      \"alphaMode\": \"OPAQUE\",\n      \"emissiveFactor\": [0, 0, 0]\n    },\n    {\n      \"name\": \"brownDark\",\n      \"pbrMetallicRoughness\": {\n        \"baseColorFactor\": [0.6392157, 0.3882353, 0.2784314, 1],\n        \"metallicFactor\": 1,\n        \"roughnessFactor\": 1\n      },\n      \"doubleSided\": false,\n      \"alphaMode\": \"OPAQUE\",\n      \"emissiveFactor\": [0, 0, 0]\n    },\n    {\n      \"name\": \"_defaultMat\",\n      \"pbrMetallicRoughness\": { \"baseColorFactor\": [1, 1, 1, 1], \"metallicFactor\": 1, \"roughnessFactor\": 1 },\n      \"doubleSided\": false,\n      \"alphaMode\": \"OPAQUE\",\n      \"emissiveFactor\": [0, 0, 0]\n    }\n  ],\n  \"meshes\": [\n    {\n      \"name\": \"Mesh appleHalf\",\n      \"primitives\": [\n        {\n          \"mode\": 4,\n          \"indices\": 0,\n          \"attributes\": { \"POSITION\": 1, \"NORMAL\": 2, \"TEXCOORD_0\": 3 },\n          \"material\": 0,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 0,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        },\n        {\n          \"mode\": 4,\n          \"indices\": 4,\n          \"attributes\": { \"POSITION\": 5, \"NORMAL\": 6, \"TEXCOORD_0\": 7 },\n          \"material\": 1,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 1,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        },\n        {\n          \"mode\": 4,\n          \"indices\": 8,\n          \"attributes\": { \"POSITION\": 9, \"NORMAL\": 10, \"TEXCOORD_0\": 11 },\n          \"material\": 2,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 2,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        },\n        {\n          \"mode\": 4,\n          \"indices\": 12,\n          \"attributes\": { \"POSITION\": 13, \"NORMAL\": 14, \"TEXCOORD_0\": 15 },\n          \"material\": 3,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 3,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        },\n        {\n          \"mode\": 4,\n          \"indices\": 16,\n          \"attributes\": { \"POSITION\": 17, \"NORMAL\": 18, \"TEXCOORD_0\": 19 },\n          \"material\": 4,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 4,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        }\n      ]\n    }\n  ],\n  \"nodes\": [\n    { \"children\": [1], \"name\": \"tmpParent\", \"translation\": [0, 0, 0], \"rotation\": [0, 0, 0, 1], \"scale\": [1, 1, 1] },\n    { \"name\": \"appleHalf\", \"translation\": [0, 0, 0], \"rotation\": [0, 0, 0, 1], \"scale\": [1, 1, 1], \"mesh\": 0 }\n  ],\n  \"scenes\": [{ \"nodes\": [1] }],\n  \"scene\": 0,\n  \"extensionsRequired\": [\"KHR_draco_mesh_compression\"]\n}\n"
  },
  {
    "path": "example/public/bottle.gltf",
    "content": "{\n  \"extensionsUsed\": [\"KHR_materials_unlit\", \"KHR_draco_mesh_compression\"],\n  \"asset\": { \"generator\": \"UniGLTF-1.27\", \"version\": \"2.0\" },\n  \"buffers\": [\n    {\n      \"name\": \"buffer\",\n      \"byteLength\": 1004,\n      \"uri\": \"data:application/octet-stream;base64,RFJBQ08CAgEBAAAAJDQCMwMCGQ8ZCwMQ79auW9NWVfEva0vXddGiAIACAHFkC4gpS5BuMmCDL1WAZAuIKUuQbjJggy9VgAP/AAEAAQABAQABAAkDAAACAQEJAwABAwEDCQIAAgIEAQEADAPJARPlCFUVHQeRE1UFDCNY15ilc8hSzX+8Xw48sQv+QBEc5E7EKhDg+xP/AYACUHwHAPgEAPgHADgKABwFAb5B8R8AngAOQQEoqgCQFRQAQBAAHFIAAPALsHPUBgQCYCUAELwBwMUCAIAbAAAAgDNtALBYAF1+ucTMJ4DFJwBGEgEAABcBNBEFAKgBwG0AAA63BNHgA4EaWQWKsQfEQgarA7/KggRAA0CIgAAAAAD/BwAA5dK4vQAAAADnD6C9uDkFPwsGAwEBBP8B4Sy5Cf///+9pCSHsJJGNFX1HBAI97/XOnNfjTSqhdGtLEH5V8IWKRsmHS4D/AAAAfwAAAP8CGEgIBQEBAAsDKQi9GUEBC+EBJQM5F6Ac4zcnvBmY1hE3kfWoH7ZVdyCtMA/1+1Uf6eVPgI6JnaMhYg6H8Bqi8K6h6DuH8BoiKQHAa4jCu4aigP+j6Ugk6pKmi9QCSJoOUQsgabpILYCk6SI8mo6oGXjhOhIYrl/ArpjyukrsIqSzxy6qdSXxYSOKe5EduyBeiJgSOyIRQErsiEQAKbGDDOB67CJK6nb0IiIUQEcvqnj0ggA0AAAAAQKCQwAAAAD/AwAAXsAvwPpQmcGLV7RBCgBEUkFDTwICAQEAAAAeKAIoAQEnCwEMX7tu7fK1bakWVS0t/wEiiAnoNmQvEM0Sa4CICeg2ZC8QzRJrgAP/AAEAAQABAQABAAkDAAACAQEJAwABAwEDCQIAAgIEAQEADAOJCA/NDEUEAzUTaQbNDAuvfqTVRnKEpXCJbjoGgLSQIGoADCfUlBoBPisAnj0CAJ8B8CwSQg0A4oQaACTQawXgAwkApGPUlgEAADxtAdgJ4DGAtQAA3AKwE7gA8LT1mCfwANgJ3AHAswPqHwA6wP4BAAqzA5uFTBxlBg5hgZ55gAAAAAAAAP8HAAAA1aq9mwUaPuPxk72E69E+CwYDAQEEAkE4wQcL0OIw5NWXSnUq+5b/AAAAfwAAAP8C3UQIBQEBAAsDhQ9VFekCA/gD+K0K8QG1CRbt1EYh/80XV9wKBm+OKvgrGjszv/+VFP3JQJIgRUpOlCQ6kZIUJYlyEABFSaIwUZLoREpSlCTKENIA2oD44ICKAmDin6IAoKIAkAioKAAm/ikKACoKvvkVEIguxiIrJgAAAAECf0IAAAAA/wMAAF0t0r/6gajBJXy6QQo=\"\n    }\n  ],\n  \"bufferViews\": [\n    { \"buffer\": 0, \"byteOffset\": 0, \"byteLength\": 571 },\n    { \"buffer\": 0, \"byteOffset\": 572, \"byteLength\": 432 }\n  ],\n  \"accessors\": [\n    { \"type\": \"SCALAR\", \"componentType\": 5125, \"count\": 156, \"min\": [0], \"max\": [101] },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 102,\n      \"max\": [0.09051262757649467, 0.5206661997740596, 0.07845129908718129],\n      \"min\": [-0.09050023093870575, -0.00025423154285842753, -0.07840956285646851]\n    },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 102,\n      \"min\": [-0.8701851585332085, -1.0078100779477288, -1.0035950162831475],\n      \"max\": [0.8714622238103082, 0.38238379674799305, 1.0035950162831475]\n    },\n    {\n      \"type\": \"VEC2\",\n      \"componentType\": 5126,\n      \"count\": 102,\n      \"min\": [-2.768152080789456, -19.186575256601223],\n      \"max\": [2.762863699408687, 3.400242172494778]\n    },\n    { \"type\": \"SCALAR\", \"componentType\": 5125, \"count\": 120, \"min\": [0], \"max\": [65] },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 66,\n      \"max\": [0.0836303700134704, 0.5606122827917177, 0.07237291420137934],\n      \"min\": [-0.0836143708616335, 0.15021171506346873, -0.07243899915514775]\n    },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 66,\n      \"min\": [-0.8738685851003609, -1.007843137254902, -1.007843137254902],\n      \"max\": [0.8751510227427763, 1.007843137254902, 1.007843137254902]\n    },\n    {\n      \"type\": \"VEC2\",\n      \"componentType\": 5126,\n      \"count\": 66,\n      \"min\": [-1.6647959047981256, -21.086251645726776],\n      \"max\": [1.6620370837134235, 2.2699388558097837]\n    }\n  ],\n  \"materials\": [\n    {\n      \"name\": \"brownDark\",\n      \"pbrMetallicRoughness\": {\n        \"baseColorFactor\": [0.6392157, 0.3882353, 0.2784314, 1],\n        \"metallicFactor\": 1,\n        \"roughnessFactor\": 1\n      },\n      \"doubleSided\": false,\n      \"alphaMode\": \"OPAQUE\",\n      \"emissiveFactor\": [0, 0, 0]\n    },\n    {\n      \"name\": \"red\",\n      \"pbrMetallicRoughness\": {\n        \"baseColorFactor\": [0.9098039, 0.333333343, 0.3254902, 1],\n        \"metallicFactor\": 1,\n        \"roughnessFactor\": 1\n      },\n      \"doubleSided\": false,\n      \"alphaMode\": \"OPAQUE\",\n      \"emissiveFactor\": [0, 0, 0]\n    }\n  ],\n  \"meshes\": [\n    {\n      \"name\": \"Mesh sodaBottle\",\n      \"primitives\": [\n        {\n          \"mode\": 4,\n          \"indices\": 0,\n          \"attributes\": { \"POSITION\": 1, \"NORMAL\": 2, \"TEXCOORD_0\": 3 },\n          \"material\": 0,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 0,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        },\n        {\n          \"mode\": 4,\n          \"indices\": 4,\n          \"attributes\": { \"POSITION\": 5, \"NORMAL\": 6, \"TEXCOORD_0\": 7 },\n          \"material\": 1,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 1,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        }\n      ]\n    }\n  ],\n  \"nodes\": [\n    { \"children\": [1], \"name\": \"tmpParent\", \"translation\": [0, 0, 0], \"rotation\": [0, 0, 0, 1], \"scale\": [1, 1, 1] },\n    { \"name\": \"sodaBottle\", \"translation\": [0, 0, 0], \"rotation\": [0, 0, 0, 1], \"scale\": [1, 1, 1], \"mesh\": 0 }\n  ],\n  \"scenes\": [{ \"nodes\": [1] }],\n  \"scene\": 0,\n  \"extensionsRequired\": [\"KHR_draco_mesh_compression\"]\n}\n"
  },
  {
    "path": "example/public/farm.gltf",
    "content": "{\n  \"asset\": { \"generator\": \"Khronos glTF Blender I/O v1.5.17\", \"version\": \"2.0\" },\n  \"scene\": 0,\n  \"scenes\": [{ \"name\": \"Scene\", \"nodes\": [0, 1, 2, 3, 4, 5, 6, 7, 8] }],\n  \"nodes\": [\n    {\n      \"mesh\": 0,\n      \"name\": \"Circle\",\n      \"scale\": [3.7607693672180176, 3.7607693672180176, 3.7607693672180176],\n      \"translation\": [0.47165822982788086, 0, -0.6066201329231262],\n      \"rotation\": [0, 0, 0, 1]\n    },\n    {\n      \"mesh\": 1,\n      \"name\": \"WindMill.001\",\n      \"scale\": [0.4821113348007202, 0.4821113348007202, 0.4821113348007202],\n      \"translation\": [1.263749599456787, 1.900821566581726, 0.1711728870868683],\n      \"rotation\": [0, 0, 0, 1]\n    },\n    {\n      \"mesh\": 2,\n      \"name\": \"Fence_White.013\",\n      \"rotation\": [0, 0.7071068286895752, 0, 0.7071068286895752],\n      \"scale\": [0.09888678789138794, 0.09888678789138794, 0.09888678789138794],\n      \"translation\": [1.3952089548110962, -0.005745698232203722, -2.4713940620422363]\n    },\n    {\n      \"mesh\": 3,\n      \"name\": \"Wooden Box\",\n      \"scale\": [0.4007795751094818, 0.4007795751094818, 0.4007795751094818],\n      \"translation\": [0.19056254625320435, 0.05061200261116028, 0.21082305908203125],\n      \"rotation\": [0, 0, 0, 1]\n    },\n    {\n      \"mesh\": 4,\n      \"name\": \"Plane\",\n      \"scale\": [0.0304397102445364, 0.0304397102445364, 0.0304397102445364],\n      \"translation\": [-0.16933752596378326, 1.0484503507614136, 0.31743738055229187],\n      \"rotation\": [0, 0, 0, 1]\n    },\n    {\n      \"mesh\": 5,\n      \"name\": \"Well\",\n      \"scale\": [0.1421276479959488, 0.12818977236747742, 0.1421276479959488],\n      \"translation\": [2.077780246734619, 0.22757045924663544, -0.05814981460571289],\n      \"rotation\": [0, 0, 0, 1]\n    },\n    {\n      \"mesh\": 6,\n      \"name\": \"Trees\",\n      \"scale\": [62.94956588745117, 62.94956588745117, 62.94956588745117],\n      \"translation\": [0.37176066637039185, 0.14777842164039612, -1.3625603914260864],\n      \"rotation\": [0, 0, 0, 1]\n    },\n    {\n      \"mesh\": 7,\n      \"name\": \"Barn_01\",\n      \"scale\": [1.1511201858520508, 1.1511201858520508, 1.1511201858520508],\n      \"translation\": [-0.17597436904907227, 0.4125245213508606, -0.17850732803344727],\n      \"rotation\": [0, 0, 0, 1]\n    },\n    {\n      \"mesh\": 8,\n      \"name\": \"WindMill\",\n      \"scale\": [0.4821113348007202, 0.4821113348007202, 0.4821113348007202],\n      \"translation\": [1.2636184692382812, 1.564571499824524, 0.07810831815004349],\n      \"rotation\": [0, 0, 0, 1]\n    }\n  ],\n  \"materials\": [\n    {\n      \"doubleSided\": true,\n      \"name\": \"Farm_Pack_001\",\n      \"pbrMetallicRoughness\": {\n        \"baseColorTexture\": { \"index\": 0, \"texCoord\": 0 },\n        \"metallicFactor\": 0,\n        \"roughnessFactor\": 0.9863636493682861,\n        \"baseColorFactor\": [1, 1, 1, 1]\n      },\n      \"emissiveFactor\": [0, 0, 0],\n      \"alphaMode\": \"OPAQUE\"\n    },\n    { \"name\": \"default\", \"emissiveFactor\": [0, 0, 0], \"alphaMode\": \"OPAQUE\", \"doubleSided\": false }\n  ],\n  \"meshes\": [\n    {\n      \"name\": \"Circle\",\n      \"primitives\": [\n        {\n          \"attributes\": { \"POSITION\": 1, \"NORMAL\": 2 },\n          \"indices\": 0,\n          \"mode\": 4,\n          \"material\": 1,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": { \"bufferView\": 0, \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1 } }\n          }\n        }\n      ]\n    },\n    {\n      \"name\": \"Cube.029\",\n      \"primitives\": [\n        {\n          \"attributes\": { \"POSITION\": 4, \"NORMAL\": 5, \"TEXCOORD_0\": 6 },\n          \"indices\": 3,\n          \"material\": 0,\n          \"mode\": 4,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 1,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        }\n      ]\n    },\n    {\n      \"name\": \"Cube.027\",\n      \"primitives\": [\n        {\n          \"attributes\": { \"POSITION\": 8, \"NORMAL\": 9, \"TEXCOORD_0\": 10 },\n          \"indices\": 7,\n          \"material\": 0,\n          \"mode\": 4,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 2,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        }\n      ]\n    },\n    {\n      \"name\": \"Cube.005\",\n      \"primitives\": [\n        {\n          \"attributes\": { \"POSITION\": 12, \"NORMAL\": 13, \"TEXCOORD_0\": 14 },\n          \"indices\": 11,\n          \"material\": 0,\n          \"mode\": 4,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 3,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        }\n      ]\n    },\n    {\n      \"name\": \"Plane.003\",\n      \"primitives\": [\n        {\n          \"attributes\": { \"POSITION\": 16, \"NORMAL\": 17, \"TEXCOORD_0\": 18 },\n          \"indices\": 15,\n          \"material\": 0,\n          \"mode\": 4,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 4,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        }\n      ]\n    },\n    {\n      \"name\": \"Cylinder.001\",\n      \"primitives\": [\n        {\n          \"attributes\": { \"POSITION\": 20, \"NORMAL\": 21, \"TEXCOORD_0\": 22 },\n          \"indices\": 19,\n          \"material\": 0,\n          \"mode\": 4,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 5,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        }\n      ]\n    },\n    {\n      \"name\": \"Plane.002\",\n      \"primitives\": [\n        {\n          \"attributes\": { \"POSITION\": 24, \"NORMAL\": 25, \"TEXCOORD_0\": 26 },\n          \"indices\": 23,\n          \"material\": 0,\n          \"mode\": 4,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 6,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        }\n      ]\n    },\n    {\n      \"name\": \"Cube.002\",\n      \"primitives\": [\n        {\n          \"attributes\": { \"POSITION\": 28, \"NORMAL\": 29, \"TEXCOORD_0\": 30 },\n          \"indices\": 27,\n          \"material\": 0,\n          \"mode\": 4,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 7,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        }\n      ]\n    },\n    {\n      \"name\": \"Cube\",\n      \"primitives\": [\n        {\n          \"attributes\": { \"POSITION\": 32, \"NORMAL\": 33, \"TEXCOORD_0\": 34 },\n          \"indices\": 31,\n          \"material\": 0,\n          \"mode\": 4,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 8,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        }\n      ]\n    }\n  ],\n  \"textures\": [{ \"sampler\": 0, \"source\": 0 }],\n  \"images\": [{ \"bufferView\": 9, \"mimeType\": \"image/jpeg\", \"name\": \"Diffuse_palette_noise\" }],\n  \"accessors\": [\n    { \"componentType\": 5123, \"count\": 90, \"type\": \"SCALAR\" },\n    {\n      \"componentType\": 5126,\n      \"count\": 32,\n      \"max\": [0.8007816435886583, 0.0007816316677292907, 0.8007816435886583],\n      \"min\": [-0.8007816435886582, -0.0007816316677292907, -0.8007816435886582],\n      \"type\": \"VEC3\"\n    },\n    { \"componentType\": 5126, \"count\": 32, \"type\": \"VEC3\" },\n    { \"componentType\": 5123, \"count\": 2076, \"type\": \"SCALAR\" },\n    {\n      \"componentType\": 5126,\n      \"count\": 1322,\n      \"max\": [0.8385503353814003, 0.8385502161721108, 0.05095415724360425],\n      \"min\": [-0.8385503353814002, -0.8385504545906898, -0.1684030803183433],\n      \"type\": \"VEC3\"\n    },\n    { \"componentType\": 5126, \"count\": 1322, \"type\": \"VEC3\" },\n    { \"componentType\": 5126, \"count\": 1322, \"type\": \"VEC2\" },\n    { \"componentType\": 5123, \"count\": 3060, \"type\": \"SCALAR\" },\n    {\n      \"componentType\": 5126,\n      \"count\": 2000,\n      \"max\": [0.10322994759563642, 1.6502795766030693, 2.2154936462014696],\n      \"min\": [-39.21565765908245, -0.019190028951344522, -23.70925850441937],\n      \"type\": \"VEC3\"\n    },\n    { \"componentType\": 5126, \"count\": 2000, \"type\": \"VEC3\" },\n    { \"componentType\": 5126, \"count\": 2000, \"type\": \"VEC2\" },\n    { \"componentType\": 5123, \"count\": 2292, \"type\": \"SCALAR\" },\n    {\n      \"componentType\": 5126,\n      \"count\": 1694,\n      \"max\": [0.18579399166423982, 0.1461265524262385, 0.14491311928442063],\n      \"min\": [-0.14491329809835496, -0.1464015217702681, -0.9569067778270537],\n      \"type\": \"VEC3\"\n    },\n    { \"componentType\": 5126, \"count\": 1694, \"type\": \"VEC3\" },\n    { \"componentType\": 5126, \"count\": 1694, \"type\": \"VEC2\" },\n    { \"componentType\": 5123, \"count\": 822, \"type\": \"SCALAR\" },\n    {\n      \"componentType\": 5126,\n      \"count\": 624,\n      \"max\": [4.812053583748002, 0.0031038604833231176, 0.24871478110741096],\n      \"min\": [-5.050960444099564, -8.329193495399613, -0.3048462795087286],\n      \"type\": \"VEC3\"\n    },\n    { \"componentType\": 5126, \"count\": 624, \"type\": \"VEC3\" },\n    { \"componentType\": 5126, \"count\": 624, \"type\": \"VEC2\" },\n    { \"componentType\": 5123, \"count\": 2370, \"type\": \"SCALAR\" },\n    {\n      \"componentType\": 5126,\n      \"count\": 1493,\n      \"max\": [1.5453098778265772, 1.1136803437164486, 0.867781436321033],\n      \"min\": [-1.307572226612872, -1.810559492199725, -1.2458345700195492],\n      \"type\": \"VEC3\"\n    },\n    { \"componentType\": 5126, \"count\": 1493, \"type\": \"VEC3\" },\n    { \"componentType\": 5126, \"count\": 1493, \"type\": \"VEC2\" },\n    { \"componentType\": 5123, \"count\": 768, \"type\": \"SCALAR\" },\n    {\n      \"componentType\": 5126,\n      \"count\": 526,\n      \"max\": [0.016896564860131663, 0.017585191424480177, 0.004240338889400612],\n      \"min\": [-0.013106122959639949, -0.0036612214727812227, -0.01328685355582468],\n      \"type\": \"VEC3\"\n    },\n    { \"componentType\": 5126, \"count\": 526, \"type\": \"VEC3\" },\n    { \"componentType\": 5126, \"count\": 526, \"type\": \"VEC2\" },\n    { \"componentType\": 5123, \"count\": 2226, \"type\": \"SCALAR\" },\n    {\n      \"componentType\": 5126,\n      \"count\": 1338,\n      \"max\": [0.32661417775988055, 0.3417208139262784, 0.47399327198237284],\n      \"min\": [-0.3267274502870451, -0.36584189573496684, -0.44418025056094035],\n      \"type\": \"VEC3\"\n    },\n    { \"componentType\": 5126, \"count\": 1338, \"type\": \"VEC3\" },\n    { \"componentType\": 5126, \"count\": 1338, \"type\": \"VEC2\" },\n    { \"componentType\": 5123, \"count\": 984, \"type\": \"SCALAR\" },\n    {\n      \"componentType\": 5126,\n      \"count\": 638,\n      \"max\": [0.7299346994293803, 0.958772202963706, 0.4743475615832653],\n      \"min\": [-0.7322520222614961, -3.2909023966740327, -1.4544944968174653],\n      \"type\": \"VEC3\"\n    },\n    { \"componentType\": 5126, \"count\": 638, \"type\": \"VEC3\" },\n    { \"componentType\": 5126, \"count\": 638, \"type\": \"VEC2\" }\n  ],\n  \"bufferViews\": [\n    { \"buffer\": 0, \"byteOffset\": 0, \"byteLength\": 251 },\n    { \"buffer\": 0, \"byteOffset\": 252, \"byteLength\": 3939 },\n    { \"buffer\": 0, \"byteOffset\": 4192, \"byteLength\": 2986 },\n    { \"buffer\": 0, \"byteOffset\": 7180, \"byteLength\": 2912 },\n    { \"buffer\": 0, \"byteOffset\": 10092, \"byteLength\": 1908 },\n    { \"buffer\": 0, \"byteOffset\": 12000, \"byteLength\": 4237 },\n    { \"buffer\": 0, \"byteOffset\": 16240, \"byteLength\": 1432 },\n    { \"buffer\": 0, \"byteOffset\": 17672, \"byteLength\": 2925 },\n    { \"buffer\": 0, \"byteOffset\": 20600, \"byteLength\": 1771 },\n    { \"buffer\": 0, \"byteOffset\": 22372, \"byteLength\": 85348 }\n  ],\n  \"samplers\": [{ \"magFilter\": 9729, \"minFilter\": 9987, \"wrapS\": 10497, \"wrapT\": 10497 }],\n  \"buffers\": [\n    {\n      \"name\": \"buffer\",\n      \"byteLength\": 107720,\n      \"uri\": \"data:application/octet-stream;base64,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dG9tbG9uZwAAAAAAAAAMY3JvcFJlY3RMZWZ0bG9uZwAAAAAAAAANY3JvcFJlY3RSaWdodGxvbmcAAAAAAAAAC2Nyb3BSZWN0VG9wbG9uZwAAAAAAOEJJTQPtAAAAAAAQAEgAAAABAAEASAAAAAEAAThCSU0EJgAAAAAADgAAAAAAAAAAAAA/gAAAOEJJTQQNAAAAAAAEAAAAHjhCSU0EGQAAAAAABAAAAB44QklNA/MAAAAAAAkAAAAAAAAAAAEAOEJJTScQAAAAAAAKAAEAAAAAAAAAAThCSU0D9QAAAAAASAAvZmYAAQBsZmYABgAAAAAAAQAvZmYAAQChmZoABgAAAAAAAQAyAAAAAQBaAAAABgAAAAAAAQA1AAAAAQAtAAAABgAAAAAAAThCSU0D+AAAAAAAcAAA/////////////////////////////wPoAAAAAP////////////////////////////8D6AAAAAD/////////////////////////////A+gAAAAA/////////////////////////////wPoAAA4QklNBAAAAAAAAAIAAjhCSU0EAgAAAAAABgAAAAAAADhCSU0EMAAAAAAAAwEBAQA4QklNBC0AAAAAAAYAAQAAAAU4QklNBAgAAAAAABAAAAABAAACQAAAAkAAAAAAOEJJTQQeAAAAAAAEAAAAADhCSU0EGgAAAAADcQAAAAYAAAAAAAAAAAAAAgAAAAIAAAAAHgBEAGkAZgBmAHUAcwBlAF8AcABhAGwAZQB0AHQAZQBfAG4AbwBpAHMAZQBfAGcAcgBhAGQAaQBlAG4AdAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAQAAAAAAAG51bGwAAAACAAAABmJvdW5kc09iamMAAAABAAAAAAAAUmN0MQAAAAQAAAAAVG9wIGxvbmcAAAAAAAAAAExlZnRsb25nAAAAAAAAAABCdG9tbG9uZwAAAgAAAAAAUmdodGxvbmcAAAIAAAAABnNsaWNlc1ZsTHMAAAABT2JqYwAAAAEAAAAAAAVzbGljZQAAABIAAAAHc2xpY2VJRGxvbmcAAAAAAAAAB2dyb3VwSURsb25nAAAAAAAA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IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4zLWMwMTEgNjYuMTQ1NjYxLCAyMDEyLzAyLzA2LTE0OjU2OjI3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE4LTA1LTE5VDE5OjMxOjQzKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOC0wNi0xNFQyMzoyNjowNiswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOC0wNi0xNFQyMzoyNjowNiswMTowMCIgeG1wTU06RG9jdW1lbnRJRD0iRDIyRjk4N0IyQjFDNUE5MEI4MUFDNzhBNkZDMjAxQjkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RkY2RDcxMTkyMTcwRTgxMUFBRDVGQjRFNTZDOUNFOTQiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0iRDIyRjk4N0IyQjFDNUE5MEI4MUFDNzhBNkZDMjAxQjkiIGRjOmZvcm1hdD0iaW1hZ2UvanBlZyIgcGhvdG9zaG9wOkxlZ2FjeUlQVENEaWdlc3Q9IkQzQzk5RjRCM0Q4REVBMjg4NTQ4MzU2QTg0MTU5QzJCIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MEJDRjQ1REM2QjVDRTgxMUI3Q0RFOUIwMEI2NjFFNTAiIHN0RXZ0OndoZW49IjIwMTgtMDUtMjBUMjE6MjQ6NTYrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDozNzc3RjMxNDExNkZFODExQUNENjk5OEE5QzcwNzk5NSIgc3RFdnQ6d2hlbj0iMjAxOC0wNi0xM1QxNDo1NjoyNSswMTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNvbnZlcnRlZCIgc3RFdnQ6cGFyYW1ldGVycz0iZnJvbSBpbWFnZS9qcGVnIHRvIGFwcGxpY2F0aW9uL3ZuZC5hZG9iZS5waG90b3Nob3AiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImRlcml2ZWQiIHN0RXZ0OnBhcmFtZXRlcnM9ImNvbnZlcnRlZCBmcm9tIGltYWdlL2pwZWcgdG8gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6Mzg3N0YzMTQxMTZGRTgxMUFDRDY5OThBOUM3MDc5OTUiIHN0RXZ0OndoZW49IjIwMTgtMDYtMTNUMTQ6NTY6MjUrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpGRTZENzExOTIxNzBFODExQUFENUZCNEU1NkM5Q0U5NCIgc3RFdnQ6d2hlbj0iMjAxOC0wNi0xNFQyMzoyNjowNiswMTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNvbnZlcnRlZCIgc3RFdnQ6cGFyYW1ldGVycz0iZnJvbSBhcHBsaWNhdGlvbi92bmQuYWRvYmUucGhvdG9zaG9wIHRvIGltYWdlL2pwZWciLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImRlcml2ZWQiIHN0RXZ0OnBhcmFtZXRlcnM9ImNvbnZlcnRlZCBmcm9tIGFwcGxpY2F0aW9uL3ZuZC5hZG9iZS5waG90b3Nob3AgdG8gaW1hZ2UvanBlZyIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6RkY2RDcxMTkyMTcwRTgxMUFBRDVGQjRFNTZDOUNFOTQiIHN0RXZ0OndoZW49IjIwMTgtMDYtMTRUMjM6MjY6MDYrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGRTZENzExOTIxNzBFODExQUFENUZCNEU1NkM5Q0U5NCIgc3RSZWY6ZG9jdW1lbnRJRD0iRDIyRjk4N0IyQjFDNUE5MEI4MUFDNzhBNkZDMjAxQjkiIHN0UmVmOm9yaWdpbmFsRG9jdW1lbnRJRD0iRDIyRjk4N0IyQjFDNUE5MEI4MUFDNzhBNkZDMjAxQjkiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPD94cGFja2V0IGVuZD0idyI/Pv/uACFBZG9iZQBkQAAAAAEDABADAgMGAAAAAAAAAAAAAAAA/9sAhAACAgICAgICAgICAwICAgMEAwICAwQFBAQEBAQFBgUFBQUFBQYGBwcIBwcGCQkKCgkJDAwMDAwMDAwMDAwMDAwMAQMDAwUEBQkGBgkNCgkKDQ8ODg4ODw8MDAwMDA8PDAwMDAwMDwwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wgARCAIAAgADAREAAhEBAxEB/8QBHgAAAgIDAQEBAQAAAAAAAAAAAQIABwMGCAkFBAoBAQACAwEBAQAAAAAAAAAAAAABBQIEBgMHCBAAAQMBBQYEBAYBBAMBAQAAAQARAgMSBAUGBxAgEzM0CCEyFBUxJRYXMCMkNRgJIiY2JzhBNyhAGREAAQIDBAQCDRIPDAkFAQEAAQARIQIDMUEEBVFhEgZxBxCBkaGx0SIy0hMzsxTBQpKyI3ODk9MkNHSEtBUlNRYg8OFSckNjZJSFlbUmNjdigqLCo0RU1GV1dhfxU6RFVUZWlnfDpWYnOMQYEgABAgEIBggEBAUEAQUAAAABAAIgMXGBscEycgMRkaHRsjMwQCFBgpJzBFFhEjTwIsKzEEJSg5NQ4dITYmCiI1Nj/9oADAMBAQIRAxEAAADViiSCiCjnoCevDH8xCECFP0Dzi5/oqS1dpBjIlh4dH79f0hh56trbBIQgTZ9rX3n11/l6vsEggSI+nseXlLzv3DQda0AgwUw60u+Cq901O6XTRBmSNBjq22+fWJbc9Wnl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\"\n    }\n  ],\n  \"extensionsRequired\": [\"KHR_draco_mesh_compression\"],\n  \"extensionsUsed\": [\"KHR_draco_mesh_compression\"]\n}\n"
  },
  {
    "path": "example/public/lightning.gltf",
    "content": "{\n  \"asset\": {\n    \"generator\": \"Khronos glTF Blender I/O v1.5.17\",\n    \"version\": \"2.0\"\n  },\n  \"scene\": 0,\n  \"scenes\": [\n    {\n      \"name\": \"Scene\",\n      \"nodes\": [0]\n    }\n  ],\n  \"nodes\": [\n    {\n      \"mesh\": 0,\n      \"name\": \"lightning\",\n      \"rotation\": [0.7071068286895752, 0, 0, 0.7071067094802856]\n    }\n  ],\n  \"materials\": [\n    {\n      \"doubleSided\": true,\n      \"name\": \"Yellow.026\",\n      \"pbrMetallicRoughness\": {\n        \"baseColorFactor\": [1, 0.5052202939987183, 0.017695415765047073, 1],\n        \"metallicFactor\": 0,\n        \"roughnessFactor\": 0.20000000298023224\n      }\n    }\n  ],\n  \"meshes\": [\n    {\n      \"name\": \"Cube.1372\",\n      \"primitives\": [\n        {\n          \"attributes\": {\n            \"POSITION\": 0,\n            \"NORMAL\": 1,\n            \"TEXCOORD_0\": 2\n          },\n          \"indices\": 3,\n          \"material\": 0\n        }\n      ]\n    }\n  ],\n  \"accessors\": [\n    {\n      \"bufferView\": 0,\n      \"componentType\": 5126,\n      \"count\": 206,\n      \"max\": [0.3984317481517792, 0.1398487091064453, 0.6621757745742798],\n      \"min\": [-0.4041382372379303, -0.1398487091064453, -0.5749139785766602],\n      \"type\": \"VEC3\"\n    },\n    {\n      \"bufferView\": 1,\n      \"componentType\": 5126,\n      \"count\": 206,\n      \"type\": \"VEC3\"\n    },\n    {\n      \"bufferView\": 2,\n      \"componentType\": 5126,\n      \"count\": 206,\n      \"type\": \"VEC2\"\n    },\n    {\n      \"bufferView\": 3,\n      \"componentType\": 5123,\n      \"count\": 684,\n      \"type\": \"SCALAR\"\n    }\n  ],\n  \"bufferViews\": [\n    {\n      \"buffer\": 0,\n      \"byteLength\": 2472,\n      \"byteOffset\": 0\n    },\n    {\n      \"buffer\": 0,\n      \"byteLength\": 2472,\n      \"byteOffset\": 2472\n    },\n    {\n      \"buffer\": 0,\n      \"byteLength\": 1648,\n      \"byteOffset\": 4944\n    },\n    {\n      \"buffer\": 0,\n      \"byteLength\": 1368,\n      \"byteOffset\": 6592\n    }\n  ],\n  \"buffers\": [\n    {\n      \"byteLength\": 7960,\n      \"uri\": \"data:application/octet-stream;base64,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mQCdAKQAnQCoAJwAowCnAKkArgCxAKkAsQCvAK4AswC0AK4AtACxAKoAsgC0AKoAtACzALIAuAC7ALIAuwC0AK0AtwC7AK0AuwC6ALcAsACxALcAsQC7ALEAtAC7AL4AwgDGAL4AxgDFAMEAyADJAMEAyQDGAL8AxwDJAL8AyQDIAMcAzADNAMcAzQDJAMAAygDNAMAAzQDMAMsAxQDGAMsAxgDNAMYAyQDNAIYAdAB6AIYAegCJAIkAegCAAIkAgACIAIgAgAB3AIgAdwCFAFEAhgCJAFEAiQBUAFQAiQCHAFQAhwBSAFIAhwCEAFIAhABPAGcASQBMAGcATABsAGwATABNAGwATQBoAGkATgBKAGkASgBlAFAAbQBwAFAAcABTAFMAcABzAFMAcwBVAFQAcgBvAFQAbwBRAJUAigCNAJUAjQClAKUAjQCRAKUAkQCbAJoAkACMAJoAjACTADoAlACfADoAnwA/AD8AnwCWAD8AlgA7ADwAlwCSADwAkgA4ADkAVwBbADkAWwA9AD0AWwBcAD0AXAA+AD8AXQBYAD8AWAA6AF8AQQBFAF8ARQBjAGMARQBHAGMARwBiAGEARgBCAGEAQgBeAIoAQgBGAIoARgCNAI0ARgBEAI0ARACOAI8AQwBAAI8AQACLAHUASgBOAHUATgB8AHwATgBLAHwASwCBAIEASwBIAIEASAB2AKsAZgBrAKsAawC2ALUAagBpALUAaQCvAK8AaQBlAK8AZQCpAMAAWABdAMAAXQDKAMoAXQBZAMoAWQDDAMQAWgBWAMQAVgC8AF4AvwDIAF4AyABhAGEAyADBAGEAwQBkAGQAwQC9AGQAvQBgAG4ArAC5AG4AuQBxAHEAuQCyAHEAsgByAHIAsgCqAHIAqgBvAL8AlQCmAL8ApgDHAMcApgCeAMcAngDMAMwAngCUAMwAlADAAHQAqgCzAHQAswB4AHgAswCuAHgArgB9AH0ArgCpAH0AqQB1ADgAkgAqADgAKgACAA==\"\n    }\n  ]\n}\n"
  },
  {
    "path": "example/public/ramen.gltf",
    "content": "{\n  \"extensionsUsed\": [\"KHR_materials_unlit\", \"KHR_draco_mesh_compression\"],\n  \"asset\": { \"generator\": \"UniGLTF-1.27\", \"version\": \"2.0\" },\n  \"buffers\": [\n    {\n      \"name\": \"buffer\",\n      \"byteLength\": 2576,\n      \"uri\": \"data:application/octet-stream;base64,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\"\n    }\n  ],\n  \"bufferViews\": [\n    { \"buffer\": 0, \"byteOffset\": 0, \"byteLength\": 716 },\n    { \"buffer\": 0, \"byteOffset\": 716, \"byteLength\": 240 },\n    { \"buffer\": 0, \"byteOffset\": 956, \"byteLength\": 329 },\n    { \"buffer\": 0, \"byteOffset\": 1288, \"byteLength\": 372 },\n    { \"buffer\": 0, \"byteOffset\": 1660, \"byteLength\": 332 },\n    { \"buffer\": 0, \"byteOffset\": 1992, \"byteLength\": 377 },\n    { \"buffer\": 0, \"byteOffset\": 2372, \"byteLength\": 201 }\n  ],\n  \"accessors\": [\n    { \"type\": \"SCALAR\", \"componentType\": 5125, \"count\": 192, \"min\": [0], \"max\": [157] },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 158,\n      \"max\": [0.2703543085118999, 0.23026423868925677, 0.31204459330309997],\n      \"min\": [-0.2702793206857598, -0.0003045823263085407, -0.3120445933030999]\n    },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 158,\n      \"min\": [-0.9165827636625252, -1.007843137254902, -0.794834673872181],\n      \"max\": [0.9187113539845335, 1.007843137254902, 0.797322188872917]\n    },\n    {\n      \"type\": \"VEC2\",\n      \"componentType\": 5126,\n      \"count\": 158,\n      \"min\": [-6.149720065638, -8.065039985224182],\n      \"max\": [6.143466845518915, 5.368346564814026]\n    },\n    { \"type\": \"SCALAR\", \"componentType\": 5125, \"count\": 36, \"min\": [0], \"max\": [23] },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 24,\n      \"max\": [0.2703543085118999, 0.2302325277224831, 0.31204459330309997],\n      \"min\": [-0.2702793206857598, 0.15743735173474188, -0.3120445933030999]\n    },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 24,\n      \"min\": [-1.007843137254902, -0.00784313725490196, -0.8738685851003609],\n      \"max\": [1.007843137254902, 0.00784313725490196, 0.8751510227427763]\n    },\n    {\n      \"type\": \"VEC2\",\n      \"componentType\": 5126,\n      \"count\": 24,\n      \"min\": [-6.14861161361575, -8.063931533201933],\n      \"max\": [6.148611613615751, -5.196578995340148]\n    },\n    { \"type\": \"SCALAR\", \"componentType\": 5125, \"count\": 72, \"min\": [0], \"max\": [33] },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 34,\n      \"max\": [0.21549624639222492, 0.22689459351690794, 0.24872712232627459],\n      \"min\": [-0.2154365424112279, 0.1919344123407405, -0.24872712232627459]\n    },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 34,\n      \"min\": [-1.007843137254902, -0.00784313725490196, -1.007843137254902],\n      \"max\": [1.007843137254902, 1.011764705882353, 1.007843137254902]\n    },\n    {\n      \"type\": \"VEC2\",\n      \"componentType\": 5126,\n      \"count\": 34,\n      \"min\": [-8.491321428546923, -8.801974161396044],\n      \"max\": [8.492391850125639, 10.801974161396046]\n    },\n    { \"type\": \"SCALAR\", \"componentType\": 5125, \"count\": 66, \"min\": [0], \"max\": [41] },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 42,\n      \"max\": [0.13901636096715753, 0.23425834950625635, 0.1509711552151075],\n      \"min\": [0.02081975380698529, 0.19204527552048054, -0.11932443415006312]\n    },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 42,\n      \"min\": [-0.9195546440049713, 0.4021469720438415, -0.7973163656159943],\n      \"max\": [0.9195546440049713, 1.0104126898681416, 0.7984866352642284]\n    },\n    {\n      \"type\": \"VEC2\",\n      \"componentType\": 5126,\n      \"count\": 42,\n      \"min\": [-5.983753844789746, -8.90267436175752],\n      \"max\": [5.98749032957463, 6.097967788271191]\n    },\n    { \"type\": \"SCALAR\", \"componentType\": 5125, \"count\": 102, \"min\": [0], \"max\": [27] },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 28,\n      \"max\": [0.17486788937389763, 0.19237419829667926, 0.20183358953298447],\n      \"min\": [-0.17481939361394763, 0.19198018445669293, -0.2018335895329845]\n    },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 28,\n      \"min\": [-0.00392156862745098, 0.996078431372549, -0.00392156862745098],\n      \"max\": [0.00392156862745098, 1.003921568627451, 0.00392156862745098]\n    },\n    {\n      \"type\": \"VEC2\",\n      \"componentType\": 5126,\n      \"count\": 28,\n      \"min\": [-6.890417417356579, -6.9539678896161],\n      \"max\": [6.891286945529464, 8.9539678896161]\n    },\n    { \"type\": \"SCALAR\", \"componentType\": 5125, \"count\": 108, \"min\": [0], \"max\": [59] },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 60,\n      \"max\": [0.12390678747578487, 0.13753989200564087, 0.000066826357513039],\n      \"min\": [-0.00006715815039337933, -0.00006715815039337933, -0.13411515812845887]\n    },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 60,\n      \"min\": [-0.7602580790426217, -0.7602580790426217, -1.007843137254902],\n      \"max\": [0.7613105484083587, 0.7613105484083587, 1.007843137254902]\n    },\n    {\n      \"type\": \"VEC2\",\n      \"componentType\": 5126,\n      \"count\": 60,\n      \"min\": [-4.006472232404692, -2.4216574454936812],\n      \"max\": [0.003912570539458038, 1.0057543470712704]\n    },\n    { \"type\": \"SCALAR\", \"componentType\": 5125, \"count\": 12, \"min\": [0], \"max\": [5] },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 6,\n      \"max\": [0.06386241567984013, 0.08609991803816254, -0.03234913807251305],\n      \"min\": [0.011097060068331669, 0.025854675582431267, -0.10169885818026214]\n    },\n    {\n      \"type\": \"VEC3\",\n      \"componentType\": 5126,\n      \"count\": 6,\n      \"min\": [-0.7563365104151707, 0.6547679302739162, -0.00392156862745098],\n      \"max\": [-0.7484933731602688, 0.6626110675288182, 0.00392156862745098]\n    },\n    {\n      \"type\": \"VEC2\",\n      \"componentType\": 5126,\n      \"count\": 6,\n      \"min\": [-4.0056384558318765, -3.208225106680265],\n      \"max\": [-1.2716694134537891, -0.052461290872225064]\n    }\n  ],\n  \"materials\": [\n    {\n      \"name\": \"_defaultMat\",\n      \"pbrMetallicRoughness\": { \"baseColorFactor\": [1, 1, 1, 1], \"metallicFactor\": 1, \"roughnessFactor\": 1 },\n      \"doubleSided\": false,\n      \"alphaMode\": \"OPAQUE\",\n      \"emissiveFactor\": [0, 0, 0]\n    },\n    {\n      \"name\": \"purple\",\n      \"pbrMetallicRoughness\": {\n        \"baseColorFactor\": [0.623529434, 0.5568628, 0.9098039, 1],\n        \"metallicFactor\": 1,\n        \"roughnessFactor\": 1\n      },\n      \"doubleSided\": false,\n      \"alphaMode\": \"OPAQUE\",\n      \"emissiveFactor\": [0, 0, 0]\n    },\n    {\n      \"name\": \"brown\",\n      \"pbrMetallicRoughness\": {\n        \"baseColorFactor\": [0.827451, 0.5647059, 0.403921574, 1],\n        \"metallicFactor\": 1,\n        \"roughnessFactor\": 1\n      },\n      \"doubleSided\": false,\n      \"alphaMode\": \"OPAQUE\",\n      \"emissiveFactor\": [0, 0, 0]\n    },\n    {\n      \"name\": \"green\",\n      \"pbrMetallicRoughness\": {\n        \"baseColorFactor\": [0.34117648, 0.7372549, 0.5921569, 1],\n        \"metallicFactor\": 1,\n        \"roughnessFactor\": 1\n      },\n      \"doubleSided\": false,\n      \"alphaMode\": \"OPAQUE\",\n      \"emissiveFactor\": [0, 0, 0]\n    },\n    {\n      \"name\": \"brownLight\",\n      \"pbrMetallicRoughness\": {\n        \"baseColorFactor\": [0.9764706, 0.772549033, 0.549019635, 1],\n        \"metallicFactor\": 1,\n        \"roughnessFactor\": 1\n      },\n      \"doubleSided\": false,\n      \"alphaMode\": \"OPAQUE\",\n      \"emissiveFactor\": [0, 0, 0]\n    },\n    {\n      \"name\": \"yellow\",\n      \"pbrMetallicRoughness\": {\n        \"baseColorFactor\": [0.9607843, 0.7254902, 0.258823544, 1],\n        \"metallicFactor\": 1,\n        \"roughnessFactor\": 1\n      },\n      \"doubleSided\": false,\n      \"alphaMode\": \"OPAQUE\",\n      \"emissiveFactor\": [0, 0, 0]\n    }\n  ],\n  \"meshes\": [\n    {\n      \"name\": \"Mesh bowlBroth\",\n      \"primitives\": [\n        {\n          \"mode\": 4,\n          \"indices\": 0,\n          \"attributes\": { \"POSITION\": 1, \"NORMAL\": 2, \"TEXCOORD_0\": 3 },\n          \"material\": 0,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 0,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        },\n        {\n          \"mode\": 4,\n          \"indices\": 4,\n          \"attributes\": { \"POSITION\": 5, \"NORMAL\": 6, \"TEXCOORD_0\": 7 },\n          \"material\": 1,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 1,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        },\n        {\n          \"mode\": 4,\n          \"indices\": 8,\n          \"attributes\": { \"POSITION\": 9, \"NORMAL\": 10, \"TEXCOORD_0\": 11 },\n          \"material\": 2,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 2,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        },\n        {\n          \"mode\": 4,\n          \"indices\": 12,\n          \"attributes\": { \"POSITION\": 13, \"NORMAL\": 14, \"TEXCOORD_0\": 15 },\n          \"material\": 3,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 3,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        },\n        {\n          \"mode\": 4,\n          \"indices\": 16,\n          \"attributes\": { \"POSITION\": 17, \"NORMAL\": 18, \"TEXCOORD_0\": 19 },\n          \"material\": 4,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 4,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        }\n      ]\n    },\n    {\n      \"name\": \"Mesh Group 495\",\n      \"primitives\": [\n        {\n          \"mode\": 4,\n          \"indices\": 20,\n          \"attributes\": { \"POSITION\": 21, \"NORMAL\": 22, \"TEXCOORD_0\": 23 },\n          \"material\": 0,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 5,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        },\n        {\n          \"mode\": 4,\n          \"indices\": 24,\n          \"attributes\": { \"POSITION\": 25, \"NORMAL\": 26, \"TEXCOORD_0\": 27 },\n          \"material\": 5,\n          \"extensions\": {\n            \"KHR_draco_mesh_compression\": {\n              \"bufferView\": 6,\n              \"attributes\": { \"POSITION\": 0, \"NORMAL\": 1, \"TEXCOORD_0\": 2 }\n            }\n          }\n        }\n      ]\n    }\n  ],\n  \"nodes\": [\n    { \"children\": [1], \"name\": \"tmpParent\", \"translation\": [0, 0, 0], \"rotation\": [0, 0, 0, 1], \"scale\": [1, 1, 1] },\n    {\n      \"children\": [2, 3],\n      \"name\": \"bowlBroth\",\n      \"translation\": [0, 0, 0],\n      \"rotation\": [0, 0, 0, 1],\n      \"scale\": [0.93, 0.93, 0.93],\n      \"mesh\": 0\n    },\n    {\n      \"name\": \"Group\",\n      \"translation\": [0.143740281, 0.164340109, -0.104827814],\n      \"rotation\": [0, 0.49999997, 0, 0.866025448],\n      \"scale\": [1.07526886, 1.07526886, 1.07526886],\n      \"mesh\": 1\n    },\n    {\n      \"name\": \"Group\",\n      \"translation\": [0.1626537, 0.164340109, 0.07206881],\n      \"rotation\": [0, 0, 0, 1],\n      \"scale\": [1.07526886, 1.07526886, 1.07526886],\n      \"mesh\": 1\n    }\n  ],\n  \"scenes\": [{ \"nodes\": [1] }],\n  \"scene\": 0,\n  \"extensionsRequired\": [\"KHR_draco_mesh_compression\"]\n}\n"
  },
  {
    "path": "example/src/App.tsx",
    "content": "import * as React from 'react'\nimport { useErrorBoundary } from 'use-error-boundary'\nimport { Redirect, Route, useRoute } from 'wouter'\n\nimport { DemoPanel, Dot, Error, Loading, Page } from './components'\nimport './styles.css'\n\nimport * as demos from './demos'\n\nconst DEFAULT_COMPONENT_NAME = 'Portals'\nconst visibleComponents: any = Object.entries(demos).reduce((acc, [name, item]) => ({ ...acc, [name]: item }), {})\n\nfunction ErrorBoundary({ children, fallback, name }: any) {\n  const { ErrorBoundary, didCatch, error } = useErrorBoundary()\n  return didCatch ? fallback(error) : <ErrorBoundary key={name}>{children}</ErrorBoundary>\n}\n\nfunction Demo() {\n  const [match, params] = useRoute('/demo/:name')\n  const compName = match ? params.name : DEFAULT_COMPONENT_NAME\n  const { Component } = visibleComponents[compName]\n\n  return (\n    <ErrorBoundary key={compName} fallback={(e: any) => <Error>{e.message}</Error>}>\n      <Component />\n    </ErrorBoundary>\n  )\n}\n\nfunction Dots() {\n  const [match, params] = useRoute('/demo/:name')\n  if (!match) return null\n\n  return (\n    <>\n      <DemoPanel>\n        {Object.entries(visibleComponents).map(function mapper([name, item]) {\n          const background = params.name === name ? 'salmon' : '#fff'\n          return <Dot key={name} to={`/demo/${name}`} style={{ background }} />\n        })}\n      </DemoPanel>\n      <span style={{ color: 'white' }}>{params.name}</span>\n    </>\n  )\n}\n\nexport default function App() {\n  const dev = new URLSearchParams(location.search).get('dev')\n\n  return (\n    <Page>\n      <React.Suspense fallback={<Loading />}>\n        <Route path=\"/\" children={<Redirect to={`/demo/${DEFAULT_COMPONENT_NAME}`} />} />\n        <Route path=\"/demo/:name\">\n          <Demo />\n        </Route>\n      </React.Suspense>\n      {dev === null && <Dots />}\n    </Page>\n  )\n}\n"
  },
  {
    "path": "example/src/components.tsx",
    "content": "import * as React from 'react'\nimport { type LinkProps, Link } from 'wouter'\n\nexport const Page = (props: { children?: React.ReactNode }) => <div {...props} className=\"Page\" />\n\nexport const DemoPanel = (props: { children?: React.ReactNode }) => <div {...props} className=\"DemoPanel\" />\n\nexport const Dot = (props: React.PropsWithChildren<LinkProps>) => <Link {...props} className=\"Dot\" />\n\nexport const Loading = () => {\n  return (\n    <div className=\"LoadingContainer\">\n      <div className=\"LoadingMessage\">Loading.</div>\n    </div>\n  )\n}\n\nexport const Error = ({ children }: { children?: React.ReactNode }) => {\n  return <div className=\"Error\">{children}</div>\n}\n"
  },
  {
    "path": "example/src/demos/Activity.tsx",
    "content": "import { useRef, useEffectEvent, Suspense, use, useState, Activity } from 'react'\nimport { useFrame, Canvas } from '@react-three/fiber'\nimport { Mesh, Group } from 'three'\nimport { DRACOLoader, GLTFLoader } from 'three-stdlib'\nimport { Environment } from '@react-three/drei'\n\nconst colors = ['orange', 'hotpink', 'cyan', 'lime', 'yellow', 'red', 'blue', 'purple', 'green', 'coral']\n\nfunction SceneA({ onSelect }: { onSelect: Function }) {\n  const ref = useRef<Mesh>(null)\n  const [scale, setScale] = useState(1)\n  const [color, setColor] = useState(colors[0])\n\n  // Stable event handler using the new React 19.2 API\n  const handleSelect = useEffectEvent(() => onSelect())\n\n  useFrame((_, dt) => {\n    if (ref.current) ref.current.rotation.y += dt * 1.2\n  })\n\n  return (\n    <mesh\n      ref={ref}\n      scale={scale}\n      position={[-1.5, 0, 0]}\n      onClick={handleSelect}\n      onPointerOver={() => {\n        setScale(1.2)\n        setColor(colors[Math.floor(Math.random() * colors.length)])\n      }}\n      onPointerOut={() => setScale(1)}>\n      <boxGeometry />\n      <meshStandardMaterial color={color} />\n    </mesh>\n  )\n}\n\nconst dracoLoader = new DRACOLoader()\ndracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.5/')\n\nconst gltfLoader = new GLTFLoader()\ngltfLoader.setDRACOLoader(dracoLoader)\n\nconst modelPromise = gltfLoader.loadAsync('/apple.gltf')\n\nfunction SceneB({ onSelect }: { onSelect: Function }) {\n  const gltf = use(modelPromise)\n  const ref = useRef<Group>(null)\n\n  const [scale, setScale] = useState(5)\n  const handleSelect = useEffectEvent(() => onSelect())\n\n  useFrame((_, dt) => {\n    if (ref.current) ref.current.rotation.y -= dt * 0.8\n  })\n\n  return (\n    <primitive\n      object={gltf.scene}\n      ref={ref}\n      scale={scale}\n      position={[1.5, 0, 0]}\n      onClick={handleSelect}\n      onPointerOver={() => setScale(6)}\n      onPointerOut={() => setScale(5)}\n    />\n  )\n}\n\nexport default function App() {\n  const [active, setActive] = useState('A')\n\n  return (\n    <Canvas camera={{ position: [4, 3, 6], fov: 50 }}>\n      <ambientLight intensity={Math.PI} />\n\n      <Environment preset=\"apartment\" />\n\n      <Activity mode={active === 'A' ? 'visible' : 'hidden'}>\n        <Suspense fallback={null}>\n          <SceneA onSelect={() => setActive('B')} />\n        </Suspense>\n      </Activity>\n\n      <Activity mode={active === 'B' ? 'visible' : 'hidden'}>\n        <Suspense fallback={null}>\n          <SceneB onSelect={() => setActive('A')} />\n        </Suspense>\n      </Activity>\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/AutoDispose.tsx",
    "content": "import { Canvas, type ThreeElements, useFrame } from '@react-three/fiber'\nimport { useRef, useState } from 'react'\nimport * as THREE from 'three'\n\ntype BoxProps = ThreeElements['object3D'] & {\n  setActive: (active: boolean) => void\n  active: boolean\n}\n\nfunction Box1(props: BoxProps) {\n  const mesh = useRef<THREE.Mesh>(null!)\n  const [hovered, setHover] = useState(false)\n  useFrame((state) => (mesh.current.position.y = Math.sin(state.clock.elapsedTime)))\n\n  return (\n    <mesh\n      {...props}\n      ref={mesh}\n      onClick={(e) => props.setActive(!props.active)}\n      onPointerOver={(e) => setHover(true)}\n      onPointerOut={(e) => setHover(false)}>\n      <boxGeometry />\n      <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />\n    </mesh>\n  )\n}\n\nfunction Box2(props: BoxProps) {\n  const mesh = useRef<THREE.Mesh>(null!)\n  const [hovered, setHover] = useState(false)\n  useFrame((state) => (mesh.current.position.y = Math.sin(state.clock.elapsedTime)))\n\n  return (\n    <group {...props}>\n      <mesh\n        {...props}\n        ref={mesh}\n        onClick={(e) => props.setActive(!props.active)}\n        onPointerOver={(e) => setHover(true)}\n        onPointerOut={(e) => setHover(false)}>\n        <boxGeometry />\n        <meshStandardMaterial color={hovered ? 'green' : 'blue'} />\n      </mesh>\n    </group>\n  )\n}\n\nfunction Switcher() {\n  const [active, setActive] = useState(false)\n\n  return (\n    <>\n      {active && <Box1 active={active} setActive={setActive} position={[-0.5, 0, 0]} />}\n      {!active && <Box2 active={active} setActive={setActive} position={[0.25, 0, 0]} />}\n    </>\n  )\n}\n\nexport default function App() {\n  return (\n    <Canvas orthographic camera={{ zoom: 100 }}>\n      <ambientLight intensity={Math.PI} />\n      <Switcher />\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/ChangeTexture.tsx",
    "content": "import { useTexture } from '@react-three/drei'\nimport { Canvas } from '@react-three/fiber'\nimport { useDeferredValue, useEffect, useState } from 'react'\n\nexport default function App() {\n  return (\n    <Canvas>\n      <Plane />\n    </Canvas>\n  )\n}\n\nfunction Plane() {\n  const textures = ['/pmndrs.png', '/react.png', '/three.png']\n  const [index, setIndex] = useState(0)\n  const deferred = useDeferredValue(index)\n  const texture = useTexture(textures[deferred])\n\n  useEffect(() => {\n    const interval = setInterval(() => setIndex((i) => (i + 1) % textures.length), 1000)\n    return () => clearInterval(interval)\n  }, [])\n\n  return (\n    <mesh scale={2}>\n      <planeGeometry />\n      <meshBasicMaterial transparent map={texture} />\n    </mesh>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/ClickAndHover.tsx",
    "content": "import { Canvas, type ThreeElements, useFrame } from '@react-three/fiber'\nimport { useRef, useState } from 'react'\nimport * as THREE from 'three'\n\nconst mesh = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshBasicMaterial({ color: 'red' }))\nconst group = new THREE.Group()\ngroup.add(mesh)\n\nfunction Box(props: ThreeElements['mesh']) {\n  const ref = useRef<THREE.Mesh>(null!)\n  const [hovered, setHovered] = useState(false)\n  const [clicked, setClicked] = useState(false)\n\n  useFrame((state) => {\n    ref.current.position.y = Math.sin(state.clock.elapsedTime) / 3\n  })\n\n  return (\n    <mesh\n      ref={ref}\n      onPointerOver={(e) => setHovered(true)}\n      onPointerOut={(e) => setHovered(false)}\n      onClick={() => setClicked(!clicked)}\n      scale={clicked ? [1.5, 1.5, 1.5] : [1, 1, 1]}\n      {...props}>\n      <boxGeometry />\n      <meshBasicMaterial color={hovered ? 'hotpink' : 'aquamarine'} />\n    </mesh>\n  )\n}\n\nfunction Box2(props: ThreeElements['group']) {\n  return <primitive object={group} {...props} onClick={() => console.log('hi')} />\n}\n\nexport default function App() {\n  return (\n    <Canvas>\n      <group>\n        <Box position={[-0.5, 0, 0]} />\n      </group>\n      <Box2 position={[0.5, 0, 0]} />\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/ContextMenuOverride.tsx",
    "content": "import { Canvas } from '@react-three/fiber'\nimport { useState } from 'react'\n\nexport default function App() {\n  const [state, setState] = useState(false)\n\n  return (\n    <Canvas\n      orthographic\n      camera={{ zoom: 150, fov: 75, position: [0, 0, 25] }}\n      onPointerMissed={() => console.log('canvas.missed')}>\n      <ambientLight intensity={Math.PI} />\n      <pointLight decay={0} position={[10, 10, 10]} />\n      <mesh\n        scale={[2, 2, 2]}\n        position={[1, 0, 0]}\n        onContextMenu={(ev) => {\n          ev.nativeEvent.preventDefault()\n          setState((value) => !value)\n        }}\n        onPointerMissed={() => console.log('mesh.missed')}>\n        <boxGeometry args={[1, 1, 1]} />\n        <meshPhysicalMaterial color={state ? 'hotpink' : 'blue'} />\n      </mesh>\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/FlushSync.tsx",
    "content": "import { Canvas, flushSync, useThree } from '@react-three/fiber'\nimport { useCallback, useRef, useState } from 'react'\n\nconst colors = ['orange', 'hotpink', 'cyan', 'lime', 'yellow', 'red', 'blue', 'purple', 'green', 'coral']\n\nfunction Capture() {\n  const [color, setColor] = useState(colors[0])\n  const { gl } = useThree()\n  const wantToCapture = useRef(false)\n\n  const handleClick = useCallback(() => {\n    // Use flushSync to ensure the color is updated immediately\n    flushSync(() => setColor(colors[Math.floor(Math.random() * colors.length)]))\n    wantToCapture.current = true\n  }, [])\n\n  const captureScreenshot = useCallback(() => {\n    if (wantToCapture.current) {\n      wantToCapture.current = false\n\n      // Takes a screenshot of the canvas and downloads it\n      const link = document.createElement('a')\n      link.href = gl.domElement.toDataURL()\n      link.download = 'screenshot.png'\n      link.click()\n    }\n  }, [gl])\n\n  return (\n    <mesh onClick={handleClick} onAfterRender={captureScreenshot}>\n      <dodecahedronGeometry args={[1, 0]} />\n      <meshStandardMaterial color={color} />\n    </mesh>\n  )\n}\n\nexport default function App() {\n  return (\n    <Canvas>\n      <ambientLight intensity={Math.PI * 0.5} />\n      <spotLight decay={0} position={[10, 10, 10]} angle={0.15} penumbra={1} />\n      <Capture />\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/Gestures.tsx",
    "content": "import { Canvas, useFrame, useThree } from '@react-three/fiber'\nimport { useDrag } from '@use-gesture/react'\nimport { useRef, useState } from 'react'\nimport type * as THREE from 'three'\n\nfunction Object({ scale = 1, z = 0, opacity = 1 }) {\n  const { viewport } = useThree()\n  const [hovered, hover] = useState(false)\n  const [position, set] = useState<[number, number, number]>([0, 0, z])\n  const bind = useDrag(({ event, offset: [x, y] }) => {\n    event.stopPropagation()\n    const aspect = viewport.getCurrentViewport().factor\n    set([x / aspect, -y / aspect, z])\n  })\n\n  const mesh = useRef<THREE.Mesh>(null!)\n\n  useFrame(() => {\n    mesh.current!.rotation.x = mesh.current!.rotation.y += 0.01\n  })\n\n  return (\n    <mesh\n      ref={mesh}\n      position={position}\n      {...(bind() as any)}\n      onPointerOver={(e) => {\n        e.stopPropagation()\n        hover(true)\n      }}\n      onPointerOut={(e) => {\n        e.stopPropagation()\n        hover(false)\n      }}\n      onClick={(e) => {\n        e.stopPropagation()\n        console.log('clicked', { z })\n      }}\n      castShadow\n      scale={scale}>\n      <dodecahedronGeometry args={[1, 0]} />\n      <meshStandardMaterial transparent opacity={opacity} color={hovered ? 'hotpink' : 'orange'} />\n    </mesh>\n  )\n}\n\nexport default function App() {\n  return (\n    <Canvas>\n      <ambientLight intensity={0.5 * Math.PI} />\n      <spotLight decay={0} position={[10, 10, 10]} angle={0.15} penumbra={1} />\n      <pointLight decay={0} position={[-10, -10, -10]} />\n      <Object z={-1} scale={0.5} />\n      <Object opacity={0.8} />\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/Gltf.tsx",
    "content": "import { Canvas, useLoader } from '@react-three/fiber'\nimport { Suspense, useEffect, useReducer } from 'react'\nimport { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'\n\nfunction Test() {\n  const [flag, toggle] = useReducer((state) => !state, true)\n  const { scene } = useLoader(GLTFLoader, flag ? '/Stork.glb' : '/Parrot.glb')\n\n  useEffect(() => {\n    const interval = setInterval(toggle, 1000)\n    return () => clearInterval(interval)\n  }, [])\n\n  return <primitive object={scene} />\n}\n\nexport default function App() {\n  return (\n    <Canvas>\n      <ambientLight intensity={Math.PI} />\n      <Suspense fallback={null}>\n        <Test />\n      </Suspense>\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/Inject.tsx",
    "content": "import { Canvas, createPortal, getRootState, type ThreeElements, useThree } from '@react-three/fiber'\nimport { useEffect, useReducer, useRef, useState } from 'react'\nimport * as THREE from 'three'\n\ntype CubeProps = ThreeElements['mesh'] & { color: string }\n\nconst customCamera1 = new THREE.PerspectiveCamera()\nconst customCamera2 = new THREE.PerspectiveCamera()\n\nexport default function App() {\n  const [scene1] = useState(() => new THREE.Scene())\n  const [scene2] = useState(() => new THREE.Scene())\n  const [mounted, mount] = useReducer(() => true, false)\n\n  useEffect(() => {\n    const timeout = setTimeout(mount, 1000)\n    return () => clearTimeout(timeout)\n  }, [])\n\n  return (\n    <Canvas>\n      <Cube position={[-0.5, 0, 0]} color=\"hotpink\" />\n      {createPortal(\n        <group>\n          {mounted && <Cube position={[0, 0.5, 0]} color=\"lightblue\" />}\n          {createPortal(<Cube position={[0.5, 0, 0]} color=\"aquamarine\" />, scene2, { camera: customCamera2 })}\n        </group>,\n        scene1,\n        { camera: customCamera1 },\n      )}\n      <primitive object={scene1} />\n      <primitive object={scene2} />\n    </Canvas>\n  )\n}\n\nfunction Cube({ color, ...props }: CubeProps) {\n  const camera = useThree((state) => state.camera)\n  const ref = useRef<THREE.Mesh>(null!)\n\n  useEffect(() => {\n    console.log(`from within ${color}.useEffect`, getRootState(ref.current)?.camera, 'camera', camera.uuid)\n  }, [])\n\n  return (\n    <mesh ref={ref} {...props}>\n      <boxGeometry />\n      <meshBasicMaterial color={color} toneMapped={false} />\n    </mesh>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/Layers.tsx",
    "content": "import { Canvas, type ThreeElements } from '@react-three/fiber'\nimport { useEffect, useReducer } from 'react'\nimport * as THREE from 'three'\n\nconst invisibleLayer = new THREE.Layers()\ninvisibleLayer.set(4)\n\nconst visibleLayers = new THREE.Layers()\nvisibleLayers.enableAll()\nvisibleLayers.disable(4)\n\nfunction Box(props: ThreeElements['mesh']) {\n  return (\n    <mesh {...props}>\n      <boxGeometry />\n      <meshBasicMaterial color=\"lightblue\" toneMapped={false} />\n    </mesh>\n  )\n}\n\nfunction Sphere(props: ThreeElements['mesh']) {\n  return (\n    <mesh {...props}>\n      <sphereGeometry args={[0.5, 32, 32]} />\n      <meshBasicMaterial color=\"aquamarine\" toneMapped={false} />\n    </mesh>\n  )\n}\n\nexport default function App() {\n  const [visible, toggle] = useReducer((state) => !state, false)\n\n  useEffect(() => {\n    const interval = setInterval(toggle, 1000)\n    return () => clearInterval(interval)\n  })\n\n  return (\n    <Canvas camera={{ layers: visibleLayers }}>\n      <Box position={[-0.5, 0, 0]} layers={!visible ? invisibleLayer : visibleLayers} />\n      <Sphere position={[0.5, 0, 0]} layers={visible ? invisibleLayer : visibleLayers} />\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/Lines.tsx",
    "content": "import { Line } from '@react-three/drei'\nimport { Canvas, type ThreeElements, type ThreeEvent, type Vector3 } from '@react-three/fiber'\nimport { useState } from 'react'\n\nfunction PolyLine({\n  defaultStart,\n  defaultEnd,\n}: {\n  defaultStart: [number, number, number]\n  defaultEnd: [number, number, number]\n}) {\n  const [start, setStart] = useState(defaultStart)\n  const [end, setEnd] = useState(defaultEnd)\n\n  return (\n    <>\n      <Line points={[...start, ...end]} lineWidth={3} color=\"lightgray\" />\n      <EndPoint position={start} onDrag={setStart} />\n      <EndPoint position={end} onDrag={setEnd} />\n    </>\n  )\n}\n\nfunction EndPoint({\n  position,\n  onDrag,\n}: ThreeElements['mesh'] & { onDrag: (position: [number, number, number]) => void }) {\n  const [active, setActive] = useState(false)\n  const [hovered, setHover] = useState(false)\n\n  const down = (event: ThreeEvent<PointerEvent>) => {\n    event.stopPropagation()\n    ;(event.target as HTMLElement).setPointerCapture(event.pointerId)\n    setActive(true)\n  }\n\n  const up = (event: ThreeEvent<PointerEvent>) => {\n    setActive(false)\n  }\n\n  const move = (event: ThreeEvent<PointerEvent>) => {\n    if (active && onDrag) onDrag(event.unprojectedPoint.toArray())\n  }\n\n  return (\n    <mesh\n      position={position}\n      onPointerOver={() => setHover(true)}\n      onPointerOut={() => setHover(false)}\n      onPointerDown={down}\n      onLostPointerCapture={up}\n      onPointerUp={up}\n      onPointerMove={move}>\n      <sphereGeometry args={[10, 16, 16]} />\n      <meshBasicMaterial color={hovered ? 'hotpink' : 'orange'} />\n    </mesh>\n  )\n}\n\nexport default function App() {\n  return (\n    <Canvas\n      orthographic\n      raycaster={{ params: { Line: { threshold: 5 } } as any }}\n      camera={{ position: [0, 0, 500], zoom: 1 }}>\n      <PolyLine defaultStart={[-100, -100, 0]} defaultEnd={[0, 100, 0]} />\n      <PolyLine defaultStart={[0, 100, 0]} defaultEnd={[100, -100, 0]} />\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/MultiMaterial.tsx",
    "content": "import { Canvas, type ThreeElements } from '@react-three/fiber'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport * as THREE from 'three'\n\nconst redMaterial = new THREE.MeshBasicMaterial({ color: 'aquamarine', toneMapped: false })\n\nfunction ReuseMaterial(props: ThreeElements['mesh']) {\n  return (\n    <mesh {...props}>\n      <sphereGeometry args={[0.25, 64, 64]} />\n      <primitive attach=\"material\" object={redMaterial} />\n    </mesh>\n  )\n}\n\nfunction TestReuse() {\n  const [okay, setOkay] = useState(true)\n\n  useEffect(() => {\n    const interval = setInterval(() => setOkay((okay) => !okay), 1000)\n    return () => clearInterval(interval)\n  }, [])\n\n  return (\n    <>\n      {okay && <ReuseMaterial position={[-1.5, 0, 0]} />}\n      <ReuseMaterial position={[1.5, 0, 0]} />\n    </>\n  )\n}\n\nfunction TestMultiMaterial(props: ThreeElements['mesh']) {\n  const ref = useRef<THREE.Mesh>(null!)\n  const [okay, setOkay] = useState(true)\n\n  useEffect(() => {\n    const interval = setInterval(() => setOkay((okay) => !okay), 1000)\n    return () => clearInterval(interval)\n  }, [])\n\n  useEffect(() => {\n    console.log(ref.current.material)\n  }, [okay])\n\n  return (\n    <mesh ref={ref} {...props}>\n      <boxGeometry args={[0.75, 0.75, 0.75]} />\n      <meshBasicMaterial attach=\"material-0\" color=\"hotpink\" toneMapped={false} />\n      <meshBasicMaterial attach=\"material-1\" color=\"lightgreen\" toneMapped={false} />\n      {okay ? (\n        <meshBasicMaterial attach=\"material-2\" color=\"lightblue\" toneMapped={false} />\n      ) : (\n        <meshNormalMaterial attach=\"material-2\" />\n      )}\n      <meshBasicMaterial attach=\"material-3\" color=\"pink\" toneMapped={false} />\n      <meshBasicMaterial attach=\"material-4\" color=\"orange\" toneMapped={false} />\n      <meshBasicMaterial attach=\"material-5\" color=\"lavender\" toneMapped={false} />\n    </mesh>\n  )\n}\n\nfunction TestMultiDelete(props: ThreeElements['mesh']) {\n  const ref = useRef<THREE.Mesh>(null!)\n  const [okay, setOkay] = useState(true)\n\n  useEffect(() => {\n    const interval = setInterval(() => setOkay((okay) => !okay), 1000)\n    return () => clearInterval(interval)\n  }, [])\n\n  useEffect(() => {\n    console.log(ref.current.material)\n  }, [okay])\n\n  return (\n    <mesh ref={ref} {...props}>\n      <boxGeometry args={[0.75, 0.75, 0.75]} />\n      <meshBasicMaterial attach=\"material-0\" color=\"hotpink\" side={THREE.DoubleSide} toneMapped={false} />\n      <meshBasicMaterial attach=\"material-1\" color=\"lightgreen\" side={THREE.DoubleSide} toneMapped={false} />\n      {okay && <meshBasicMaterial attach=\"material-2\" color=\"lightblue\" side={THREE.DoubleSide} toneMapped={false} />}\n      <meshBasicMaterial attach=\"material-3\" color=\"pink\" side={THREE.DoubleSide} toneMapped={false} />\n      <meshBasicMaterial attach=\"material-4\" color=\"orange\" side={THREE.DoubleSide} toneMapped={false} />\n      <meshBasicMaterial attach=\"material-5\" color=\"lavender\" side={THREE.DoubleSide} toneMapped={false} />\n    </mesh>\n  )\n}\n\nfunction TestMix(props: ThreeElements['mesh']) {\n  const [size, setSize] = useState(0.1)\n  const geometry = useMemo(() => new THREE.SphereGeometry(size, 64, 64), [size])\n\n  useEffect(() => {\n    const timeout = setInterval(\n      () =>\n        setSize((s) => {\n          return s < 0.4 ? s + 0.025 : 0\n        }),\n      1000,\n    )\n    return () => clearTimeout(timeout)\n  }, [])\n\n  return (\n    <mesh args={[geometry]} {...props}>\n      <meshBasicMaterial color=\"hotpink\" toneMapped={false} />\n    </mesh>\n  )\n}\n\nexport default function Test() {\n  return (\n    <Canvas camera={{ position: [2, 2, 2] }}>\n      <TestMultiMaterial position={[0, 0, 0.5]} />\n      <TestMultiDelete position={[0, 0, -0.5]} />\n      <TestReuse />\n      <TestMix position={[0, 1, 0]} />\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/MultiRender.tsx",
    "content": "import { Canvas, useFrame } from '@react-three/fiber'\nimport { useEffect, useRef, useState } from 'react'\nimport * as THREE from 'three'\n\nconst CanvasStyle = {\n  width: '100%',\n  height: '50%',\n}\n\nconst Object = () => {\n  const meshRef = useRef<THREE.Mesh>(null!)\n\n  useFrame(() => {\n    if (meshRef.current) {\n      meshRef.current.rotation.y += 0.03\n    }\n  })\n\n  return (\n    <mesh ref={meshRef}>\n      <boxGeometry args={[1, 1, 1]} />\n      <meshNormalMaterial />\n    </mesh>\n  )\n}\n\nconst SpinningScene = () => (\n  <div style={CanvasStyle}>\n    <Canvas>\n      <Object />\n    </Canvas>\n  </div>\n)\n\nconst StaticScene = () => (\n  <div style={CanvasStyle}>\n    <Canvas>\n      <mesh>\n        <boxGeometry args={[1, 1, 1]} />\n        <meshNormalMaterial />\n      </mesh>\n    </Canvas>\n  </div>\n)\n\nexport default function App() {\n  const [secondScene, setSecondScene] = useState(false)\n\n  useEffect(() => {\n    setTimeout(() => setSecondScene(true), 500)\n  }, [])\n\n  return (\n    <div style={{ width: '100%', height: '100%' }}>\n      <SpinningScene />\n      {secondScene && <StaticScene />}\n    </div>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/MultiView.tsx",
    "content": "import {\n  ArcballControls,\n  Bounds,\n  CameraShake,\n  Environment,\n  OrbitControls,\n  OrthographicCamera,\n  PerspectiveCamera,\n  TransformControls,\n  useGLTF,\n} from '@react-three/drei'\nimport {\n  Canvas,\n  type ComputeFunction,\n  createPortal,\n  type ThreeElements,\n  type ThreeEvent,\n  useFrame,\n  useThree,\n} from '@react-three/fiber'\nimport { ComponentProps, useCallback, useEffect, useState } from 'react'\nimport * as THREE from 'three'\n\nfunction useHover() {\n  const [hovered, setHovered] = useState(false)\n  const props = {\n    onPointerOver: (e: ThreeEvent<PointerEvent>) => {\n      e.stopPropagation()\n      setHovered(true)\n    },\n    onPointerOut: () => setHovered(false),\n  }\n  return [hovered, props] as const\n}\n\nexport function Soda(props: ThreeElements['group']) {\n  const [hovered, hoverProps] = useHover()\n  const { meshes, materials } = useGLTF('/bottle.gltf')\n\n  return (\n    <group {...props} {...hoverProps} dispose={null}>\n      <mesh geometry={meshes.Mesh_sodaBottle.geometry}>\n        <meshStandardMaterial color={hovered ? 'red' : 'green'} roughness={0} metalness={0.8} envMapIntensity={2} />\n      </mesh>\n      <mesh geometry={meshes.Mesh_sodaBottle_1.geometry} material={materials.red} material-envMapIntensity={0} />\n    </group>\n  )\n}\n\nfunction View({\n  index = 1,\n  children,\n  clearColor,\n  placement,\n}: {\n  index: number\n  children: React.ReactNode\n  clearColor: THREE.Color\n  placement: string\n}) {\n  const { events, size } = useThree()\n  const [scene] = useState(() => new THREE.Scene())\n  const [position] = useState(() => new THREE.Vector2())\n  const [el] = useState(() => {\n    const div = document.createElement('div')\n    div.style.zIndex = index.toString()\n    div.style.position = 'absolute'\n    div.style.width = div.style.height = '50%'\n    return div\n  })\n\n  useEffect(() => {\n    switch (placement) {\n      case 'topright':\n        position.set(1, 1)\n        el.style.top = el.style.right = '0px'\n        break\n      case 'topleft':\n        position.set(0, 1)\n        el.style.top = el.style.left = '0px'\n        break\n      case 'bottomright':\n        position.set(1, 0)\n        el.style.bottom = el.style.right = '0px'\n        break\n      case 'bottomleft':\n      default:\n        position.set(0, 0)\n        el.style.bottom = el.style.left = '0px'\n        break\n    }\n  }, [placement])\n\n  useEffect(() => {\n    if (events.connected) {\n      const target = events.connected\n      target.appendChild(el)\n      return () => void target.removeChild(el)\n    }\n  }, [events, el])\n\n  const compute = useCallback<ComputeFunction>((event, state) => {\n    if (event.target === el) {\n      const width = state.size.width\n      const height = state.size.height\n      state.pointer.set((event.offsetX / width) * 2 - 1, -(event.offsetY / height) * 2 + 1)\n      state.raycaster.setFromCamera(state.pointer, state.camera)\n    }\n  }, [])\n\n  return (\n    <>\n      {createPortal(\n        <Container index={index} clearColor={clearColor} position={position}>\n          {children}\n        </Container>,\n        scene,\n        {\n          events: { compute, priority: events.priority + index, connected: el },\n          size: { width: size.width / 2, height: size.height / 2, top: 0, left: 0 },\n        },\n      )}\n    </>\n  )\n}\n\nfunction Container({\n  children,\n  index,\n  clearColor,\n  position,\n}: {\n  children: React.ReactNode\n  index: number\n  clearColor: THREE.Color\n  position: THREE.Vector2\n}) {\n  const { size, camera, scene } = useThree()\n\n  useFrame((state) => {\n    const left = Math.floor(size.width * position.x)\n    const bottom = Math.floor(size.height * position.y)\n    const width = Math.floor(size.width)\n    const height = Math.floor(size.height)\n    state.gl.setViewport(left, bottom, width, height)\n    state.gl.setScissor(left, bottom, width, height)\n    state.gl.setScissorTest(true)\n    if (clearColor) state.gl.setClearColor(clearColor)\n    state.gl.render(scene, camera)\n  }, index)\n\n  return <>{children}</>\n}\n\nconst App = () => (\n  <Canvas dpr={[1, 2]} camera={{ position: [0, 0, 5] }}>\n    <View index={1} placement=\"bottomleft\" clearColor={new THREE.Color('orange').convertLinearToSRGB()}>\n      <OrthographicCamera makeDefault zoom={100} />\n      <Scene preset=\"lobby\" />\n      <OrbitControls makeDefault />\n    </View>\n    <View index={2} placement=\"bottomright\" clearColor={new THREE.Color('hotpink').convertLinearToSRGB()}>\n      <PerspectiveCamera makeDefault />\n      <Scene preset=\"city\" />\n      <ArcballControls />\n    </View>\n    <View index={3} placement=\"topleft\" clearColor={new THREE.Color('aquamarine').convertLinearToSRGB()}>\n      <PerspectiveCamera makeDefault />\n      <Scene preset=\"dawn\" />\n      <OrbitControls makeDefault />\n      <CameraShake intensity={2} />\n    </View>\n    <View index={4} placement=\"topright\" clearColor={new THREE.Color('lightblue').convertLinearToSRGB()}>\n      <PerspectiveCamera makeDefault />\n      <Scene preset=\"warehouse\" />\n      <OrbitControls makeDefault />\n      <TransformControls scale={3} position={[0, -0.75, 2]}>\n        <Soda />\n      </TransformControls>\n    </View>\n  </Canvas>\n)\n\nfunction Scene({\n  children,\n  preset,\n}: {\n  children?: React.ReactNode\n  preset: ComponentProps<typeof Environment>['preset']\n}) {\n  return (\n    <Bounds fit clip observe>\n      <ambientLight intensity={Math.PI} />\n      <pointLight decay={0} position={[20, 30, 10]} />\n      <pointLight decay={0} position={[-10, -10, -10]} color=\"blue\" />\n      <Soda scale={3} position={[-1, -0.75, 1]} />\n      <Soda scale={3} position={[1, -0.75, 1]} />\n      <Soda scale={3} position={[0, -0.75, 0]} />\n      <Environment preset={preset} />\n      {children}\n    </Bounds>\n  )\n}\n\nexport default App\n"
  },
  {
    "path": "example/src/demos/Pointcloud.tsx",
    "content": "import { Canvas, ThreeEvent, extend } from '@react-three/fiber'\nimport { useCallback, useMemo, useRef } from 'react'\nimport * as THREE from 'three'\n\nclass DotMaterialImpl extends THREE.ShaderMaterial {\n  constructor() {\n    super({\n      transparent: true,\n      uniforms: { size: { value: 15 }, scale: { value: 1 } },\n      vertexShader: THREE.ShaderLib.points.vertexShader,\n      fragmentShader: `\n      varying vec3 vColor;\n      void main() {\n        gl_FragColor = vec4(vColor, step(length(gl_PointCoord.xy - vec2(0.5)), 0.5));\n      }`,\n    })\n  }\n}\n\nconst DotMaterial = extend(DotMaterialImpl)\n\nconst white = new THREE.Color('white')\nconst hotpink = new THREE.Color('hotpink')\n\nfunction Particles({ pointCount }: { pointCount: number }) {\n  const [positions, colors] = useMemo(() => {\n    const positions = [...new Array(pointCount * 3)].map(() => 5 - Math.random() * 10)\n    const colors = [...new Array(pointCount)].flatMap(() => hotpink.toArray())\n    return [new Float32Array(positions), new Float32Array(colors)]\n  }, [pointCount])\n\n  const points = useRef<THREE.Points>(null!)\n\n  const hover = useCallback((e: ThreeEvent<PointerEvent>) => {\n    e.stopPropagation()\n    white.toArray(points.current.geometry.attributes.color.array, e.index! * 3)\n    points.current.geometry.attributes.color.needsUpdate = true\n  }, [])\n\n  const unhover = useCallback((e: ThreeEvent<PointerEvent>) => {\n    hotpink.toArray(points.current.geometry.attributes.color.array, e.index! * 3)\n    points.current.geometry.attributes.color.needsUpdate = true\n  }, [])\n\n  return (\n    <points ref={points} onPointerOver={hover} onPointerOut={unhover}>\n      <bufferGeometry>\n        <bufferAttribute attach=\"attributes-position\" args={[positions, 3]} />\n        <bufferAttribute attach=\"attributes-color\" args={[colors, 3]} />\n      </bufferGeometry>\n      <DotMaterial vertexColors depthWrite={false} />\n    </points>\n  )\n}\n\nexport default function App() {\n  return (\n    <Canvas\n      orthographic\n      camera={{ zoom: 40, position: [0, 0, 100] }}\n      raycaster={{ params: { Points: { threshold: 0.2 } } as any }}>\n      <Particles pointCount={1000} />\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/Portals.tsx",
    "content": "import { Environment, OrbitControls, useFBO, useGLTF } from '@react-three/drei'\nimport { Canvas, ComputeFunction, createPortal, type ThreeElements, useFrame, useThree } from '@react-three/fiber'\nimport { useCallback, useLayoutEffect, useRef, useState } from 'react'\nimport * as THREE from 'three'\n\nexport function Lights() {\n  return (\n    <>\n      <color attach=\"background\" args={['#f0f0f0']} />\n      <ambientLight intensity={Math.PI} />\n      <pointLight decay={0} position={[20, 30, 10]} />\n      <pointLight decay={0} position={[-10, -10, -10]} color=\"blue\" />\n    </>\n  )\n}\n\nexport function Farm(props: ThreeElements['group']) {\n  const { scene } = useGLTF('/farm.gltf')\n  return <primitive object={scene} {...props} />\n}\n\nexport function Ramen(props: ThreeElements['group']) {\n  const { scene } = useGLTF('/ramen.gltf')\n  return <primitive object={scene} {...props} />\n}\n\nexport function Soda(props: ThreeElements['group']) {\n  const [hovered, spread] = useHover()\n  const { meshes, materials } = useGLTF('/bottle.gltf')\n  return (\n    <group {...(spread as any)} {...props} dispose={null}>\n      <mesh geometry={meshes.Mesh_sodaBottle.geometry}>\n        <meshStandardMaterial color={hovered ? 'red' : 'green'} />\n      </mesh>\n      <mesh geometry={meshes.Mesh_sodaBottle_1.geometry} material={materials.red} />\n    </group>\n  )\n}\n\nfunction useHover() {\n  const [hovered, hover] = useState(false)\n  return [hovered, { onPointerOver: (e: any) => (e.stopPropagation(), hover(true)), onPointerOut: () => hover(false) }]\n}\n\nfunction Portal({\n  children,\n  scale = [1, 1, 1],\n  clearColor = 'white',\n  ...props\n}: {\n  children: React.ReactNode\n  clearColor?: string\n} & ThreeElements['mesh']) {\n  const ref = useRef<THREE.Mesh>(null!)\n  const fbo = useFBO()\n  const { events } = useThree()\n  // The portal will render into this scene\n  const [scene] = useState(() => new THREE.Scene())\n  // We have our own camera in here, separate from the default\n  const [camera] = useState(() => new THREE.PerspectiveCamera(50, 1, 0.1, 1000))\n\n  useLayoutEffect(() => {\n    camera.aspect = ref.current.scale.x / ref.current.scale.y\n    camera.updateProjectionMatrix()\n  }, [scale])\n\n  useFrame((state) => {\n    // Copy the default cameras whereabouts\n    camera.position.copy(state.camera.position)\n    camera.rotation.copy(state.camera.rotation)\n    camera.scale.copy(state.camera.scale)\n    // Render into a WebGLRenderTarget as a texture (the FBO above)\n    state.gl.clearColor()\n    state.gl.setRenderTarget(fbo)\n    state.gl.render(scene, camera)\n    state.gl.setRenderTarget(null)\n  })\n\n  // This is a custom raycast-compute function, it controls how the raycaster functions.\n  const compute = useCallback<ComputeFunction>((event, state, previous) => {\n    if (!previous) return false\n    // First we call the previous state-onion-layers compute, this is what makes it possible to nest portals\n    if (!previous.raycaster.camera) previous.events.compute!(event, previous, previous?.previousRoot?.getState())\n    // We run a quick check against the textured plane itself, if it isn't hit there's no need to raycast at all\n    const [intersection] = previous?.raycaster.intersectObject(ref.current)\n    if (!intersection) return false\n    // We take that hits uv coords, set up this layers raycaster, et voilà, we have raycasting with perspective shift\n    const uv = intersection.uv!\n    state.raycaster.setFromCamera(state.pointer.set(uv.x * 2 - 1, uv.y * 2 - 1), camera)\n  }, [])\n\n  return (\n    <>\n      {/* This mesh receives the render-targets texture and draws it onto a plane */}\n      <mesh scale={scale} ref={ref} {...props}>\n        <planeGeometry />\n        <meshBasicMaterial map={fbo.texture} map-encoding={THREE.SRGBColorSpace} />\n      </mesh>\n      {/* A portal by default now has its own state, separate from the root state.\n          The third argument to createPortal allows you to override parts of it, in here for example\n          we place our own camera and override the events definition with a lower priority than\n          the previous layer, and our custom compute function. */}\n      {createPortal(children, scene, { camera, events: { compute, priority: events.priority - 1 } })}\n    </>\n  )\n}\n\nfunction Test() {\n  const controls = useThree((state) => state.controls)\n  console.log(controls)\n  return null\n}\n\nexport default function App() {\n  return (\n    <Canvas dpr={[1, 2]} camera={{ position: [0, 3, 7] }}>\n      <group position={[0, -1, 0]}>\n        <Lights />\n        {/* First layer, a portal */}\n        <Portal scale={[4, 5, 1]} position={[0, 2.5, 0]} rotation={[0, 0, 0]}>\n          <Lights />\n          <Farm scale={10} rotation={[0, 0, 0]} position={[-1, -2, -10]} />\n          <Soda scale={5} position={[2, -2, -1.5]} />\n          <Portal scale={[4, 5, 1]} position={[2, 0, -5]} rotation={[0, 0, 0]}>\n            <Test />\n            <Lights />\n            <Soda scale={8} position={[0, -2, -1.5]} />\n            <Environment preset=\"city\" background=\"only\" />\n          </Portal>\n        </Portal>\n        <Ramen scale={4} position={[-2, 0, 2]} />\n        <Soda scale={5} position={[1.5, 0, 3]} />\n      </group>\n      <OrbitControls makeDefault />\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/Reparenting.tsx",
    "content": "import { Canvas, createPortal } from '@react-three/fiber'\nimport { useCallback, useEffect, useReducer, useState } from 'react'\nimport * as THREE from 'three'\n\nfunction Icosahedron() {\n  const [active, setActive] = useState(false)\n  const handleClick = useCallback(() => setActive((state) => !state), [])\n  return (\n    <mesh scale={active ? [2, 2, 2] : [1, 1, 1]} onClick={handleClick}>\n      <icosahedronGeometry args={[1, 0]} />\n      <meshNormalMaterial />\n    </mesh>\n  )\n}\n\nfunction RenderToPortal({ targets }: { targets: THREE.Group[] }) {\n  const [target, toggle] = useReducer((state) => (state + 1) % targets.length, 0)\n\n  useEffect(() => {\n    const interval = setInterval(toggle, 1000)\n    return () => clearInterval(interval)\n  }, [targets])\n\n  return <>{createPortal(<Icosahedron />, targets[target])}</>\n}\n\nexport default function Group() {\n  const [ref1, set1] = useState<THREE.Group>(null!)\n  const [ref2, set2] = useState<THREE.Group>(null!)\n\n  return (\n    <Canvas onCreated={() => console.log('onCreated')}>\n      <group>\n        <group ref={set1 as any} position={[-2, 0, 0]} />\n        <mesh position={[0, 0, 0]}>\n          <sphereGeometry args={[0.5, 16, 16]} />\n          <meshNormalMaterial />\n        </mesh>\n        <group ref={set2 as any} position={[2, 0, 0]} />\n        {ref1 && ref2 && <RenderToPortal targets={[ref1, ref2]} />}\n      </group>\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/ResetProps.tsx",
    "content": "import { OrbitControls } from '@react-three/drei'\nimport { Canvas, useFrame, useThree } from '@react-three/fiber'\nimport { useEffect, useRef, useState } from 'react'\nimport * as THREE from 'three'\n\nfunction AdaptivePixelRatio() {\n  const gl = useThree((state) => state.gl)\n  const current = useThree((state) => state.performance.current)\n  const initialDpr = useThree((state) => state.viewport.initialDpr)\n  const setDpr = useThree((state) => state.setDpr)\n\n  // Restore initial pixelratio on unmount\n  useEffect(() => {\n    const domElement = gl.domElement\n    return () => {\n      setDpr(initialDpr)\n      domElement.style.imageRendering = 'auto'\n    }\n  }, [])\n\n  // Set adaptive pixelratio\n  useEffect(() => {\n    setDpr(current * initialDpr)\n    gl.domElement.style.imageRendering = current === 1 ? 'auto' : 'pixelated'\n  }, [current])\n\n  return null\n}\n\nfunction AdaptiveEvents() {\n  const get = useThree((state) => state.get)\n  const current = useThree((state) => state.performance.current)\n\n  useEffect(() => {\n    const enabled = get().events.enabled\n    return () => void (get().events.enabled = enabled)\n  }, [])\n\n  useEffect(() => void (get().events.enabled = current === 1), [current])\n\n  return null\n}\n\nfunction Scene() {\n  const group = useRef<THREE.Group>(null!)\n  const [showCube, setShowCube] = useState(false)\n  const [hovered, setHovered] = useState(false)\n\n  useEffect(() => {\n    const interval = setInterval(() => setShowCube((showCube) => !showCube), 1000)\n    return () => clearInterval(interval)\n  }, [])\n\n  useFrame(({ clock }) => group.current?.rotation.set(Math.sin(clock.elapsedTime), 0, 0))\n\n  return (\n    <>\n      <ambientLight intensity={0.5 * Math.PI} />\n      <pointLight decay={0} position={[10, 10, 10]} intensity={2} />\n      <pointLight decay={0} position={[-10, -10, -10]} color=\"red\" intensity={4} />\n      <mesh scale={hovered ? 1.25 : 1} onPointerOver={() => setHovered(true)} onPointerOut={() => setHovered(false)}>\n        <sphereGeometry args={[0.5, 32, 32]} />\n        <meshStandardMaterial color={showCube ? 'white' : 'red'} />\n      </mesh>\n      <group ref={group}>\n        {showCube ? (\n          <mesh position={[1.5, 0, 0]}>\n            <boxGeometry args={[1, 1]} />\n            <meshNormalMaterial transparent opacity={0.5} />\n          </mesh>\n        ) : (\n          <mesh>\n            <icosahedronGeometry args={[1]} />\n            <meshStandardMaterial color=\"orange\" transparent opacity={0.5} />\n          </mesh>\n        )}\n        <mesh position={[-2, -2, 0]}>\n          <sphereGeometry args={[0.2, 32, 32]} />\n          <meshPhongMaterial>\n            {showCube ? <color attach=\"color\" args={[0, 0, 1]} /> : <color attach=\"color\" args={[1, 0, 0]} />}\n          </meshPhongMaterial>\n        </mesh>\n      </group>\n      <OrbitControls regress />\n      <AdaptivePixelRatio />\n      <AdaptiveEvents />\n    </>\n  )\n}\n\nexport default function App() {\n  return (\n    <Canvas dpr={[1, 2]} frameloop=\"always\" performance={{ min: 0.1 }}>\n      <Scene />\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/SVGRenderer.tsx",
    "content": "import { Canvas, useFrame } from '@react-three/fiber'\nimport { useEffect, useRef, useState } from 'react'\nimport * as THREE from 'three'\nimport { SVGRenderer } from 'three-stdlib'\n\nfunction TorusKnot() {\n  const [hovered, setHovered] = useState(false)\n  const ref = useRef<THREE.Mesh>(null!)\n\n  useFrame((state) => {\n    const t = state.clock.elapsedTime / 2\n    ref.current.rotation.set(t, t, t)\n  })\n\n  return (\n    <mesh ref={ref} onPointerOver={() => setHovered(true)} onPointerOut={() => setHovered(false)}>\n      <torusKnotGeometry args={[10, 3, 128, 16]} />\n      <meshBasicMaterial color={hovered ? 'orange' : 'hotpink'} />\n    </mesh>\n  )\n}\n\nconst gl = new SVGRenderer()\ngl.domElement.style.position = 'absolute'\ngl.domElement.style.top = '0'\ngl.domElement.style.left = '0'\n\nexport default function () {\n  useEffect(() => {\n    document.body.appendChild(gl.domElement)\n    return () => void document.body.removeChild(gl.domElement)\n  }, [])\n\n  return (\n    <Canvas gl={gl} camera={{ position: [0, 0, 50] }} eventSource={gl.domElement as unknown as HTMLElement}>\n      <color attach=\"background\" args={['#dedddf']} />\n      <TorusKnot />\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/Selection.tsx",
    "content": "import { Canvas } from '@react-three/fiber'\nimport { useState } from 'react'\n\nfunction Sphere() {\n  const [hovered, setHovered] = useState(false)\n  console.log('sphere', hovered)\n  return (\n    <mesh onPointerOver={(e) => (e.stopPropagation(), setHovered(true))} onPointerOut={(e) => setHovered(false)}>\n      <sphereGeometry args={[0.5, 64, 64]} />\n      <meshBasicMaterial color={hovered ? 'hotpink' : 'indianred'} />\n    </mesh>\n  )\n}\n\nfunction Circle() {\n  const [hovered, setHovered] = useState(false)\n  console.log('circle', hovered)\n  return (\n    <mesh onPointerOver={(e) => (e.stopPropagation(), setHovered(true))} onPointerOut={(e) => setHovered(false)}>\n      <circleGeometry args={[1, 64]} />\n      <meshBasicMaterial color={hovered ? 'lightgreen' : 'grey'} />\n    </mesh>\n  )\n}\n\nexport default function App() {\n  return (\n    <Canvas orthographic camera={{ position: [0, 0, 20], zoom: 150 }} style={{ background: '#272730' }}>\n      <group\n        position={[-1.25, 0, 0]}\n        onPointerOver={(e) => console.log('group1 over')}\n        onPointerOut={(e) => console.log('group1 out')}>\n        <group\n          onPointerOver={(e) => console.log('      group2 over')}\n          onPointerOut={(e) => console.log('      group2 out')}>\n          <mesh\n            renderOrder={8}\n            onPointerOver={(e) => console.log('      white mesh over')}\n            onPointerOut={(e) => console.log('      white mesh out')}>\n            <sphereGeometry args={[1, 32, 32]} />\n            <meshBasicMaterial color=\"white\" transparent opacity={0.2} />\n          </mesh>\n          <mesh\n            renderOrder={7}\n            onPointerOver={(e) => console.log('        black mesh over')}\n            onPointerOut={(e) => console.log('        black mesh out')}>\n            <sphereGeometry args={[0.7, 32, 32]} />\n            <meshBasicMaterial color=\"black\" transparent opacity={0.2} />\n          </mesh>\n        </group>\n      </group>\n      <group position={[1.25, 0, 0]}>\n        <Circle />\n        <Sphere />\n      </group>\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/StopPropagation.tsx",
    "content": "import { Environment, OrbitControls, useGLTF } from '@react-three/drei'\nimport { Canvas, createPortal, type ThreeElements, type ThreeEvent, useFrame, useThree } from '@react-three/fiber'\nimport { Suspense, useCallback, useState } from 'react'\nimport * as THREE from 'three'\n\nfunction useHover() {\n  const [hovered, setHovered] = useState(false)\n  const props = {\n    onPointerOver: (e: ThreeEvent<PointerEvent>) => {\n      e.stopPropagation()\n      setHovered(true)\n    },\n    onPointerOut: () => setHovered(false),\n  }\n  return [hovered, props] as const\n}\n\nfunction Soda(props: ThreeElements['group']) {\n  const [hovered, spread] = useHover()\n  const { meshes, materials } = useGLTF('/bottle.gltf')\n\n  return (\n    <group {...props} {...spread} dispose={null}>\n      <mesh geometry={meshes.Mesh_sodaBottle.geometry}>\n        <meshStandardMaterial color={hovered ? 'red' : 'green'} metalness={0.6} roughness={0} />\n      </mesh>\n      <mesh geometry={meshes.Mesh_sodaBottle_1.geometry} material={materials.red} />\n    </group>\n  )\n}\n\nfunction Hud({ priority = 1, children }: { priority?: number; children: React.ReactNode }) {\n  const { gl, scene: defaultScene, camera: defaultCamera } = useThree()\n  const [scene] = useState(() => new THREE.Scene())\n\n  useFrame(() => {\n    if (priority === 1) {\n      gl.autoClear = true\n      gl.render(defaultScene, defaultCamera)\n      gl.autoClear = false\n    }\n    gl.clearDepth()\n    gl.render(scene, defaultCamera)\n  }, priority)\n\n  return <>{createPortal(children, scene, { events: { priority: priority + 1 } })}</>\n}\n\nfunction Plane({\n  stop = false,\n  color,\n  position,\n}: {\n  stop?: boolean\n  color: string\n  position: [number, number, number]\n}) {\n  const [hovered, setHovered] = useState(false)\n\n  const onPointerOver = useCallback((e: ThreeEvent<PointerEvent>) => {\n    if (stop) e.stopPropagation()\n    setHovered(true)\n  }, [])\n\n  const onPointerOut = useCallback((e: ThreeEvent<PointerEvent>) => {\n    if (stop) e.stopPropagation()\n    setHovered(false)\n  }, [])\n\n  return (\n    <mesh name={color} position={position} onPointerOver={onPointerOver} onPointerOut={onPointerOut}>\n      <planeGeometry />\n      <meshPhysicalMaterial color={hovered ? 'orange' : color} toneMapped={false} side={THREE.DoubleSide} />\n    </mesh>\n  )\n}\n\nexport default function App() {\n  return (\n    <Canvas camera={{ fov: 75, position: [0, 0, -2.25] }}>\n      <Suspense fallback={null}>\n        <ambientLight intensity={Math.PI} />\n        <pointLight decay={0} position={[10, 10, 10]} />\n        <Plane color=\"lightblue\" position={[0.5, 0, -1]} />\n        <Plane stop color=\"aquamarine\" position={[0, 0, -0.5]} />\n        <Plane color=\"hotpink\" position={[-0.5, 0, 0]} />\n        <Hud priority={1}>\n          <Soda position={[0, -0.5, 0]} scale={2} />\n          <Environment preset=\"warehouse\" />\n        </Hud>\n        <Hud priority={2}>\n          <Soda position={[0, -0.5, 0]} scale={1.5} />\n          <Environment preset=\"dawn\" />\n        </Hud>\n      </Suspense>\n      <OrbitControls />\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/SuspenseAndErrors.tsx",
    "content": "import * as React from 'react'\nimport { useState, useEffect } from 'react'\nimport { Canvas, useLoader } from '@react-three/fiber'\nimport { suspend } from 'suspend-react'\nimport { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'\n\n// Suspends the scene for 2 seconds, simulating loading an async asset\nfunction AsyncComponent({ cacheKey }: { cacheKey: string }) {\n  suspend(() => new Promise((res) => setTimeout(res, 2000)), [cacheKey])\n  return null\n}\n\n// Loads a file that does not exist\nfunction SimulateError() {\n  useLoader(GLTFLoader, '/doesnotexist.glb')\n  return null\n}\n\nexport default function App() {\n  const [load, setLoad] = useState(false)\n\n  useEffect(() => {\n    const timeout = setTimeout(() => setLoad(true), 3000)\n    return () => clearTimeout(timeout)\n  }, [])\n\n  return (\n    <Canvas orthographic camera={{ position: [10, 10, 10], zoom: 100 }}>\n      <ambientLight intensity={Math.PI} />\n      <pointLight decay={0} position={[10, 10, 5]} intensity={2} />\n      <mesh>\n        <boxGeometry />\n        <meshStandardMaterial color=\"orange\" />\n      </mesh>\n      <AsyncComponent cacheKey=\"1\" />\n      {load && <SimulateError />}\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/SuspenseMaterial.tsx",
    "content": "import { Canvas } from '@react-three/fiber'\nimport { Suspense, useReducer } from 'react'\nimport { suspend } from 'suspend-react'\n\nfunction SlowMaterial({ arg = 0 }) {\n  suspend(() => new Promise((res) => setTimeout(res, 1000)), [arg])\n  return <meshStandardMaterial name=\"main\" color=\"salmon\" toneMapped={false} />\n}\n\nfunction FallbackMaterial() {\n  return <meshStandardMaterial name=\"fallback\" color=\"white\" />\n}\n\nexport default function App() {\n  const [arg, inc] = useReducer((x) => x + 1, 0)\n  return (\n    <Canvas>\n      <ambientLight intensity={Math.PI} />\n      <directionalLight />\n      <mesh onClick={inc}>\n        <sphereGeometry args={[1, 64, 32]} />\n        <Suspense fallback={<FallbackMaterial />}>\n          <SlowMaterial arg={arg} />\n        </Suspense>\n      </mesh>\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/Test.tsx",
    "content": "import * as THREE from 'three'\nimport { Canvas, useFrame } from '@react-three/fiber'\nimport { OrbitControls, Hud } from '@react-three/drei'\nimport { useEffect, useRef, useState } from 'react'\n\nfunction Box({ color = 'orange', ...props }) {\n  const ref = useRef<THREE.Mesh>(null!)\n  const [hovered, setHovered] = useState(false)\n  const [clicked, setClicked] = useState(false)\n\n  useFrame((state, delta) => (ref.current.rotation.x += delta))\n\n  return (\n    <mesh\n      {...props}\n      ref={ref}\n      scale={clicked ? 1.5 : 1}\n      onClick={() => setClicked(!clicked)}\n      onPointerOver={(event) => (event.stopPropagation(), setHovered(true))}\n      onPointerOut={() => setHovered(false)}>\n      <boxGeometry />\n      <meshStandardMaterial color={hovered ? 'hotpink' : color} />\n    </mesh>\n  )\n}\n\nexport default function App() {\n  const [visible, setVisible] = useState(true)\n\n  useEffect(() => {\n    setTimeout(() => setVisible(false), 2000)\n    setTimeout(() => setVisible(true), 4000)\n  }, [])\n\n  return (\n    <Canvas>\n      <ambientLight intensity={Math.PI / 2} />\n      <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />\n      <pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />\n      <Box position={[-1.2, 0, 0]} />\n      <Hud>\n        <ambientLight intensity={Math.PI / 2} />\n        <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />\n        <pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />\n        {visible && <Box color=\"skyblue\" position={[1.2, 0, 0]} />}\n      </Hud>\n      <OrbitControls />\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/ViewTracking.tsx",
    "content": "import { Environment, OrbitControls, PerspectiveCamera, Preload, TransformControls, useGLTF } from '@react-three/drei'\nimport {\n  Canvas,\n  type ComputeFunction,\n  createPortal,\n  type ThreeElements,\n  type ThreeEvent,\n  useFrame,\n  useThree,\n} from '@react-three/fiber'\nimport { useCallback, useEffect, useReducer, useRef, useState } from 'react'\nimport useRefs from 'react-use-refs'\nimport * as THREE from 'three'\n\nfunction useHover() {\n  const [hovered, setHovered] = useState(false)\n  const props = {\n    onPointerOver: (e: ThreeEvent<PointerEvent>) => {\n      e.stopPropagation()\n      setHovered(true)\n    },\n    onPointerOut: () => setHovered(false),\n  }\n  return [hovered, props] as const\n}\n\nfunction Soda(props: ThreeElements['group']) {\n  const ref = useRef<THREE.Group>(null!)\n  const [hovered, spread] = useHover()\n  const { meshes, materials } = useGLTF('/bottle.gltf')\n\n  useFrame((state, delta) => (ref.current.rotation.y += delta))\n\n  return (\n    <group ref={ref} {...props} {...spread} dispose={null}>\n      <mesh geometry={meshes.Mesh_sodaBottle.geometry}>\n        <meshStandardMaterial color={hovered ? 'red' : 'green'} roughness={0} metalness={0.8} envMapIntensity={2} />\n      </mesh>\n      <mesh geometry={meshes.Mesh_sodaBottle_1.geometry} material={materials.red} material-envMapIntensity={0} />\n    </group>\n  )\n}\n\nfunction Duck(props: ThreeElements['group']) {\n  const { scene } = useGLTF('https://vazxmixjsiawhamofees.supabase.co/storage/v1/object/public/models/duck/model.gltf')\n  useFrame((state, delta) => (scene.rotation.x = scene.rotation.y += delta))\n  return <primitive object={scene} {...props} />\n}\n\nfunction Candy(props: ThreeElements['group']) {\n  const { scene } = useGLTF(\n    'https://vazxmixjsiawhamofees.supabase.co/storage/v1/object/public/models/candy-bucket/model.gltf',\n  )\n  useFrame((state, delta) => (scene.rotation.z = scene.rotation.y += delta))\n  return <primitive object={scene} {...props} />\n}\n\nfunction Flash(props: ThreeElements['group']) {\n  const { scene } = useGLTF('/lightning.gltf')\n  useFrame((state, delta) => (scene.rotation.y += delta))\n  return <primitive object={scene} {...props} />\n}\n\nfunction Apple(props: ThreeElements['group']) {\n  const { scene } = useGLTF('/apple.gltf')\n  useFrame((state, delta) => (scene.rotation.x = scene.rotation.y += delta))\n  return <primitive object={scene} {...props} />\n}\n\nconst isOrthographicCamera = (def: THREE.Camera): def is THREE.OrthographicCamera =>\n  def && (def as THREE.OrthographicCamera).isOrthographicCamera\nconst col = new THREE.Color()\n\nfunction Container({\n  scene,\n  index,\n  children,\n  frames,\n  rect,\n  track,\n}: {\n  scene: THREE.Scene\n  index: number\n  children: React.ReactNode\n  frames: number\n  rect: React.RefObject<DOMRect>\n  track: React.RefObject<HTMLElement>\n}) {\n  const { camera } = useThree()\n\n  let frameCount = 0\n  useFrame((state) => {\n    if (frames === Infinity || frameCount <= frames) {\n      rect.current = track.current?.getBoundingClientRect()\n      frameCount++\n    }\n\n    const { left = 0, right = 0, top = 0, bottom = 0, width = 0, height = 0 } = rect.current || {}\n    const isOffscreen = bottom < 0 || top > state.size.height || right < 0 || left > state.size.width\n\n    const positiveYUpBottom = state.size.height - bottom\n    const aspect = width / height\n\n    if (isOrthographicCamera(camera)) {\n      camera.left = width / -2\n      camera.right = width / 2\n      camera.top = height / 2\n      camera.bottom = height / -2\n    } else {\n      camera.aspect = aspect\n    }\n\n    camera.updateProjectionMatrix()\n    state.gl.setViewport(left, positiveYUpBottom, width, height)\n    state.gl.setScissor(left, positiveYUpBottom, width, height)\n    state.gl.setScissorTest(true)\n\n    if (isOffscreen) {\n      state.gl.getClearColor(col)\n      state.gl.setClearColor(col, state.gl.getClearAlpha())\n      state.gl.clear(true, true)\n      return\n    }\n\n    state.gl.render(scene, camera)\n  }, index)\n\n  const get = useThree((state) => state.get)\n  const setEvents = useThree((state) => state.setEvents)\n  const [ready, toggle] = useReducer(() => true, false)\n\n  useEffect(() => {\n    const old = get().events.connected\n    setEvents({ connected: track.current })\n    toggle()\n    return () => setEvents({ connected: old })\n  }, [])\n\n  return ready && children\n}\n\nexport const View = ({ track, index = 1, frames = Infinity, children, ...props }: any) => {\n  const rect = useRef<DOMRect>(null!)\n  const [scene] = useState(() => new THREE.Scene())\n\n  const compute = useCallback<ComputeFunction>(\n    (event, state) => {\n      if (track.current && event.target === track.current) {\n        if (!rect.current) return\n        const { width, height, left, top } = rect.current\n        const x = event.clientX - left\n        const y = event.clientY - top\n        state.pointer.set((x / width) * 2 - 1, -(y / height) * 2 + 1)\n        state.raycaster.setFromCamera(state.pointer, state.camera)\n      }\n    },\n    [rect],\n  )\n\n  return (\n    <>\n      {createPortal(\n        <Container frames={frames} scene={scene} track={track} rect={rect} index={index}>\n          {children}\n        </Container>,\n        scene,\n        { events: { compute } },\n      )}\n    </>\n  )\n}\n\nfunction Scene() {\n  return (\n    <>\n      <ambientLight intensity={Math.PI} />\n      <pointLight decay={0} position={[20, 30, 10]} intensity={1} />\n      <pointLight decay={0} position={[-10, -10, -10]} color=\"blue\" />\n      <Environment preset=\"dawn\" />\n    </>\n  )\n}\n\nexport default function App() {\n  const ref = useRef<HTMLDivElement>(null!)\n  const [view1, view2, view3, view4, view5] = useRefs() as any\n  return (\n    <div ref={ref} className=\"container\">\n      <div className=\"text\">\n        Work on version 8 has begun 3 Sep 2021.\n        <div\n          ref={view1}\n          className=\"translateX\"\n          style={{ margin: '0.2em', width: 400, height: 200, display: 'inline-block' }}\n        />\n        This is perhaps the biggest update to Fiber yet.\n        <div\n          ref={view2}\n          className=\"scale\"\n          style={{ margin: '0.2em', width: 400, height: 200, display: 'inline-block' }}\n        />\n        We've tried our best to keep breaking-changes to a minimum,\n        <div\n          ref={view3}\n          className=\"translateY\"\n          style={{ margin: '0.2em', width: 400, height: 200, display: 'inline-block' }}\n        />\n        they mostly affect rarely used api's like attach.\n        <div\n          ref={view4}\n          className=\"scale\"\n          style={{ margin: '0.2em', width: 400, height: 200, display: 'inline-block' }}\n        />\n        This release brings a ton of performance related fixes,\n        <div\n          ref={view5}\n          className=\"translateX\"\n          style={{ margin: '0.2em', width: 400, height: 200, display: 'inline-block' }}\n        />\n        but also includes some new and ground-breaking features.\n      </div>\n      <Canvas\n        onCreated={(state) => state.events.connect?.(ref.current)}\n        style={{\n          pointerEvents: 'none',\n          position: 'fixed',\n          top: 0,\n          left: 0,\n          width: '100vw',\n          height: '100vh',\n        }}>\n        <View track={view1}>\n          <color attach=\"background\" args={['lightpink']} />\n          <Scene />\n          <TransformControls>\n            <Soda scale={6} position={[0, -1.6, 0]} />\n          </TransformControls>\n          <PerspectiveCamera makeDefault fov={40} position={[0, 0, 6]} />\n          <OrbitControls makeDefault />\n        </View>\n        <View track={view2}>\n          <color attach=\"background\" args={['lightblue']} />\n          <Scene />\n          <TransformControls>\n            <Apple scale={10} />\n          </TransformControls>\n          <PerspectiveCamera makeDefault fov={40} position={[0, 0, 6]} />\n        </View>\n        <View track={view3}>\n          <color attach=\"background\" args={['lightgreen']} />\n          <Scene />\n          <Duck scale={2} />\n          <PerspectiveCamera makeDefault fov={40} position={[0, 0, 6]} />\n        </View>\n        <View track={view4}>\n          <color attach=\"background\" args={['peachpuff']} />\n          <Scene />\n          <Candy scale={3} />\n          <PerspectiveCamera makeDefault fov={40} position={[0, 0, 6]} />\n        </View>\n        <View track={view5}>\n          <color attach=\"background\" args={['orange']} />\n          <Scene />\n          <Flash scale={3} />\n          <PerspectiveCamera makeDefault fov={40} position={[0, 0, 6]} />\n        </View>\n        <Preload all />\n      </Canvas>\n    </div>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/Viewcube.tsx",
    "content": "import { OrbitControls } from '@react-three/drei'\nimport { Canvas, createPortal, useFrame, useThree } from '@react-three/fiber'\nimport { useLayoutEffect, useMemo, useRef, useState } from 'react'\nimport * as THREE from 'three'\n\nfunction Viewcube() {\n  const { gl, scene: defaultScene, camera: defaultCamera, size, events } = useThree()\n  const scene = useMemo(() => new THREE.Scene(), [])\n  const camera = useMemo(() => new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 1000), [])\n\n  useLayoutEffect(() => {\n    camera.left = -size.width / 2\n    camera.right = size.width / 2\n    camera.top = size.height / 2\n    camera.bottom = -size.height / 2\n    camera.position.set(0, 0, 100)\n    camera.updateProjectionMatrix()\n  }, [size])\n\n  const ref = useRef<THREE.Mesh>(null!)\n  const [hover, setHover] = useState<number | null>(null)\n  const matrix = new THREE.Matrix4()\n\n  useFrame(() => {\n    matrix.copy(defaultCamera.matrix).invert()\n    ref.current.quaternion.setFromRotationMatrix(matrix)\n    gl.autoClear = true\n    gl.render(defaultScene, defaultCamera)\n    gl.autoClear = false\n    gl.clearDepth()\n    gl.render(scene, camera)\n  }, 1)\n\n  return (\n    <>\n      {createPortal(\n        <group>\n          <mesh\n            ref={ref}\n            position={[size.width / 2 - 120, size.height / 2 - 120, 0]}\n            onPointerOut={(e) => setHover(null)}\n            onPointerMove={(e) => setHover(Math.floor((e.faceIndex || 0) / 2))}>\n            {[...Array(6)].map((_, index) => (\n              <meshLambertMaterial\n                attach={`material-${index}`}\n                key={index}\n                color={hover === index ? 'hotpink' : 'white'}\n              />\n            ))}\n            <boxGeometry args={[80, 80, 80]} />\n          </mesh>\n          <ambientLight intensity={0.5 * Math.PI} />\n          <pointLight decay={0} position={[10, 10, 10]} intensity={0.5} />\n        </group>,\n        scene,\n        { camera, events: { priority: events.priority + 1 } },\n      )}\n    </>\n  )\n}\n\nexport default function App() {\n  return (\n    <Canvas>\n      <mesh>\n        <torusGeometry args={[1, 0.5, 32, 100]} />\n        <meshNormalMaterial />\n      </mesh>\n      <Viewcube />\n      <OrbitControls />\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/WebGPU.tsx",
    "content": "import { Canvas, extend, type ThreeToJSXElements, useFrame, type ThreeElements } from '@react-three/fiber'\nimport { easing } from 'maath'\nimport { useMemo, useState } from 'react'\nimport { color, mix, positionLocal, sin, time, uniform, vec3 } from 'three/tsl'\nimport * as THREE from 'three/webgpu'\n\ndeclare module '@react-three/fiber' {\n  interface ThreeElements extends ThreeToJSXElements<typeof THREE> {}\n}\n\nextend(THREE as any)\n\nfunction Plane(props: ThreeElements['mesh']) {\n  const [hovered, hover] = useState(false)\n  const { key, uHovered, colorNode, positionNode } = useMemo(() => {\n    const uHovered = uniform(0.0)\n    const col1 = color('orange')\n    const col2 = color('hotpink')\n    const col3 = color('aquamarine')\n    const currentTime = time.mul(2)\n    const colorNode = mix(mix(col1, col2, sin(currentTime).add(1).div(2)), col3, uHovered)\n    const positionNode = positionLocal.add(vec3(0, sin(currentTime).mul(0.05), 0))\n    return { key: uHovered.uuid, uHovered, colorNode, positionNode }\n  }, [])\n\n  useFrame((state, delta) => {\n    easing.damp(uHovered, 'value', hovered ? 1 : 0, 0.1, delta)\n  })\n\n  return (\n    <mesh onPointerOver={() => hover(true)} onPointerOut={() => hover(false)} {...props}>\n      <planeGeometry />\n      <meshBasicNodeMaterial key={key} colorNode={colorNode} positionNode={positionNode} />\n    </mesh>\n  )\n}\n\nexport default function App() {\n  return (\n    <Canvas\n      gl={async (props) => {\n        const renderer = new THREE.WebGPURenderer(props as any)\n        await renderer.init()\n        return renderer\n      }}>\n      <ambientLight intensity={Math.PI} />\n      <Plane scale={1.5} position={[-1.5, 2.5, -3]} />\n      <Plane scale={1.5} position={[-1.3, 0, 0]} />\n      <Plane scale={1.5} position={[0.6, 0, 2]} />\n    </Canvas>\n  )\n}\n"
  },
  {
    "path": "example/src/demos/index.tsx",
    "content": "import { lazy } from 'react'\n\nconst Activity = { Component: lazy(() => import('./Activity')) }\nconst AutoDispose = { Component: lazy(() => import('./AutoDispose')) }\nconst ClickAndHover = { Component: lazy(() => import('./ClickAndHover')) }\nconst ContextMenuOverride = { Component: lazy(() => import('./ContextMenuOverride')) }\nconst Gestures = { Component: lazy(() => import('./Gestures')) }\nconst Gltf = { Component: lazy(() => import('./Gltf')) }\nconst Inject = { Component: lazy(() => import('./Inject')) }\nconst Layers = { Component: lazy(() => import('./Layers')) }\nconst Lines = { Component: lazy(() => import('./Lines')) }\nconst MultiMaterial = { Component: lazy(() => import('./MultiMaterial')) }\nconst MultiRender = { Component: lazy(() => import('./MultiRender')) }\nconst MultiView = { Component: lazy(() => import('./MultiView')) }\nconst Pointcloud = { Component: lazy(() => import('./Pointcloud')) }\nconst Reparenting = { Component: lazy(() => import('./Reparenting')) }\nconst ResetProps = { Component: lazy(() => import('./ResetProps')) }\nconst Selection = { Component: lazy(() => import('./Selection')) }\nconst StopPropagation = { Component: lazy(() => import('./StopPropagation')) }\nconst SuspenseAndErrors = { Component: lazy(() => import('./SuspenseAndErrors')) }\nconst SuspenseMaterial = { Component: lazy(() => import('./SuspenseMaterial')) }\nconst SVGRenderer = { Component: lazy(() => import('./SVGRenderer')) }\nconst Test = { Component: lazy(() => import('./Test')) }\nconst Viewcube = { Component: lazy(() => import('./Viewcube')) }\nconst Portals = { Component: lazy(() => import('./Portals')) }\nconst ViewTracking = { Component: lazy(() => import('./ViewTracking')) }\nconst ChangeTexture = { Component: lazy(() => import('./ChangeTexture')) }\nconst WebGPU = { Component: lazy(() => import('./WebGPU')) }\nconst FlushSync = { Component: lazy(() => import('./FlushSync')) }\n\nexport {\n  Activity,\n  AutoDispose,\n  ClickAndHover,\n  ContextMenuOverride,\n  Gestures,\n  Gltf,\n  Inject,\n  Layers,\n  Lines,\n  MultiMaterial,\n  MultiRender,\n  Pointcloud,\n  Reparenting,\n  ResetProps,\n  Selection,\n  StopPropagation,\n  SuspenseAndErrors,\n  SuspenseMaterial,\n  SVGRenderer,\n  Test,\n  Viewcube,\n  MultiView,\n  Portals,\n  ViewTracking,\n  ChangeTexture,\n  WebGPU,\n  FlushSync,\n}\n"
  },
  {
    "path": "example/src/index.tsx",
    "content": "import * as ReactDOM from 'react-dom/client'\nimport App from './App'\n\nReactDOM.createRoot(document.getElementById('root')!).render(<App />)\n"
  },
  {
    "path": "example/src/styles.css",
    "content": "/* @import url('@pmndrs/branding/styles.css'); */\n@import url('https://rsms.me/inter/inter.css');\n\n.pmndrs-menu {\n  font-family: 'Inter var', sans-serif;\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n  display: flex;\n  color: #808080;\n  padding: 40px;\n  pointer-events: none;\n  justify-content: flex-start;\n  align-items: flex-end;\n  flex-direction: row;\n  font-size: 10px;\n  line-height: 1.5em;\n}\n\n.pmndrs-menu > div {\n  word-wrap: none;\n  word-break: none;\n  white-space: pre;\n  padding-left: 25px;\n  padding-right: 25px;\n  display: flex;\n  justify-content: flex-start;\n  align-items: flex-start;\n  flex-direction: column;\n}\n\n.pmndrs-menu > div b {\n  font-weight: 600;\n  color: #b0b0b0;\n}\n\n.pmndrs-menu a {\n  pointer-events: all;\n  cursor: pointer;\n  color: inherit;\n  text-decoration: none;\n}\n\n.pmndrs-menu a:hover {\n  text-decoration: underline;\n  color: inherit;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nhtml,\nbody,\n#root {\n  width: 100%;\n  height: 100%;\n  margin: 0;\n  padding: 0;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n  -khtml-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  overflow: hidden;\n}\n\n#root {\n  overflow: auto;\n}\n\nbody {\n  position: fixed;\n  overflow: hidden;\n  overscroll-behavior-y: none;\n  font-family: 'Inter var', sans-serif;\n  color: black;\n  background: #dedddf !important;\n}\n\ncanvas {\n  touch-action: none;\n}\n\n.container {\n  position: relative;\n  width: 100%;\n  height: 100%;\n}\n\n.text {\n  line-height: 1em;\n  text-align: left;\n  font-size: 8em;\n  word-break: break-word;\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n}\n\n.Page {\n  position: relative;\n  width: 100%;\n  height: 100vh;\n\n  & > h1 {\n    font-family: 'Roboto', sans-serif;\n    font-weight: 900;\n    font-size: 8em;\n    margin: 0;\n    color: white;\n    line-height: 0.59em;\n    letter-spacing: -2px;\n  }\n\n  & > h1 {\n    position: absolute;\n    top: 70px;\n    left: 60px;\n  }\n\n  & > span {\n    position: absolute;\n    bottom: 60px;\n    right: 60px;\n  }\n\n  @media only screen and (max-width: 1000px) {\n    & > h1 {\n      font-size: 5em;\n      letter-spacing: -1px;\n    }\n  }\n\n  & > a {\n    margin: 0;\n    color: white;\n    text-decoration: none;\n  }\n}\n\n.DemoPanel {\n  z-index: 1000;\n  position: absolute;\n  bottom: 50px;\n  left: 50px;\n  max-width: 250px;\n}\n\n.Dot {\n  display: inline-block;\n  width: 20px;\n  height: 20px;\n  border-radius: 50%;\n  margin: 8px;\n}\n\n.LoadingContainer {\n  position: fixed;\n  inset: 0;\n  z-index: 100;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n\n  background-color: #dedddf;\n  color: white;\n}\n\n.LoadingMessage {\n  font-family: 'Inter', Helvetica, sans-serif;\n}\n\n.Error {\n  position: absolute;\n  padding: 10px 20px;\n  bottom: unset;\n  right: unset;\n  top: 60px;\n  left: 60px;\n  max-width: 380px;\n  border: 2px solid #ff5050;\n  color: #ff5050;\n}\n"
  },
  {
    "path": "example/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"lib\": [\"DOM\", \"DOM.Iterable\", \"ESNext\"],\n    \"allowJs\": false,\n    \"skipLibCheck\": false,\n    \"esModuleInterop\": false,\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"bundler\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\"\n  },\n  \"include\": [\"./src\", \"./typings/*.d.ts\", \"../packages/*\"]\n}\n"
  },
  {
    "path": "example/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\nexport default defineConfig({\n  optimizeDeps: {\n    exclude: ['@react-three/fiber'],\n  },\n  plugins: [react()],\n})\n"
  },
  {
    "path": "jest.config.js",
    "content": "/** @type {import('jest').Config} */\nmodule.exports = {\n  preset: 'ts-jest',\n  testEnvironment: 'jsdom',\n  testPathIgnorePatterns: ['/node_modules/'],\n  globals: {\n    'ts-jest': {\n      tsconfig: {\n        allowJs: true,\n      },\n    },\n  },\n  transform: {\n    '^.+\\\\.jsx?$': ['ts-jest', { useESM: true }],\n  },\n  moduleNameMapper: {\n    '^three$': '<rootDir>/node_modules/three/build/three.cjs',\n  },\n  coveragePathIgnorePatterns: [\n    '<rootDir>/node_modules/',\n    '<rootDir>/packages/fiber/dist',\n    '<rootDir>/packages/fiber/src/index',\n    '<rootDir>/packages/test-renderer/dist',\n  ],\n  coverageDirectory: './coverage/',\n  collectCoverage: false,\n  moduleFileExtensions: ['js', 'ts', 'tsx'],\n  verbose: false,\n  testTimeout: 30000,\n  setupFilesAfterEnv: ['<rootDir>/packages/shared/setupTests.ts'],\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-three-fiber--root\",\n  \"version\": \"0.0.0\",\n  \"license\": \"MIT\",\n  \"private\": true,\n  \"workspaces\": [\n    \"packages/*\",\n    \"example\"\n  ],\n  \"husky\": {\n    \"hooks\": {\n      \"pre-commit\": \"lint-staged\"\n    }\n  },\n  \"preconstruct\": {\n    \"packages\": [\n      \"packages/*\"\n    ]\n  },\n  \"lint-staged\": {\n    \"*.{js,jsx,ts,tsx}\": [\n      \"eslint --fix\"\n    ]\n  },\n  \"scripts\": {\n    \"changeset:add\": \"changeset add\",\n    \"postinstall\": \"preconstruct dev && yarn patch-react-reconciler\",\n    \"patch-react-reconciler\": \"vite build\",\n    \"build\": \"preconstruct build\",\n    \"examples\": \"yarn workspace example dev\",\n    \"dev\": \"preconstruct dev\",\n    \"prepare\": \"husky install\",\n    \"eslint\": \"eslint packages/**/src/**/*.{ts,tsx}\",\n    \"eslint:fix\": \"yarn run eslint --fix\",\n    \"test\": \"jest --coverage\",\n    \"test:watch\": \"jest --watchAll\",\n    \"typecheck\": \"tsc --noEmit --emitDeclarationOnly false --strict\",\n    \"validate\": \"preconstruct validate\",\n    \"release\": \"yarn build && yarn changeset publish\",\n    \"vers\": \"yarn changeset version\",\n    \"codegen:eslint\": \"cd packages/eslint-plugin && yarn codegen\",\n    \"analyze-fiber\": \"cd packages/fiber && npm publish --dry-run\",\n    \"analyze-test\": \"cd packages/test-renderer && npm publish --dry-run\",\n    \"format\": \"prettier --check .\",\n    \"format:fix\": \"prettier --write .\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"7.17.8\",\n    \"@babel/preset-env\": \"7.16.11\",\n    \"@babel/preset-react\": \"7.16.7\",\n    \"@babel/preset-typescript\": \"^7.16.7\",\n    \"@changesets/changelog-git\": \"^0.1.11\",\n    \"@changesets/cli\": \"^2.22.0\",\n    \"@preconstruct/cli\": \"^2.1.5\",\n    \"@testing-library/react\": \"^15.0.2\",\n    \"@types/jest\": \"^29.2.5\",\n    \"@types/react\": \"^19.2.7\",\n    \"@types/react-dom\": \"^19.2.3\",\n    \"@types/react-native\": \"0.69.5\",\n    \"@types/scheduler\": \"0.23.0\",\n    \"@types/three\": \"^0.172.0\",\n    \"@typescript-eslint/eslint-plugin\": \"^5.17.0\",\n    \"@typescript-eslint/parser\": \"^5.17.0\",\n    \"eslint\": \"^8.12.0\",\n    \"eslint-config-prettier\": \"^8.5.0\",\n    \"eslint-import-resolver-alias\": \"^1.1.2\",\n    \"eslint-plugin-import\": \"^2.25.4\",\n    \"eslint-plugin-jest\": \"^27.2.1\",\n    \"eslint-plugin-prettier\": \"^4.0.0\",\n    \"eslint-plugin-react\": \"^7.29.4\",\n    \"eslint-plugin-react-hooks\": \"^4.4.0\",\n    \"expo-asset\": \"^8.6.0\",\n    \"expo-file-system\": \"^15.4.3\",\n    \"expo-gl\": \"^11.4.0\",\n    \"husky\": \"^7.0.4\",\n    \"jest\": \"^29.7.0\",\n    \"jest-cli\": \"^27.5.1\",\n    \"lint-staged\": \"^12.3.7\",\n    \"prettier\": \"^2.6.1\",\n    \"pretty-quick\": \"^3.1.3\",\n    \"react\": \"^19.2.0\",\n    \"react-dom\": \"^19.2.0\",\n    \"react-native\": \"0.69.3\",\n    \"react-nil\": \"^2.0.0\",\n    \"three\": \"^0.172.0\",\n    \"three-stdlib\": \"^2.35.16\",\n    \"ts-jest\": \"^29.1.2\",\n    \"typescript\": \"^4.6.3\",\n    \"vite\": \"^6.4.1\"\n  },\n  \"packageManager\": \"yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e\"\n}\n"
  },
  {
    "path": "packages/eslint-plugin/.npmignore",
    "content": "scripts/\nsrc/\nindex.js\n"
  },
  {
    "path": "packages/eslint-plugin/CHANGELOG.md",
    "content": "# @react-three/eslint-plugin\n\n## 0.1.2\n\n### Patch Changes\n\n- 6c907263: fix(eslint-plugin): include type declare files\n\n## 0.1.0\n\n### Minor Changes\n\n- 75521d21: Initial release of the eslint plugin containing two rules, `no-clone-in-loop` and `no-new-in-loop`.\n"
  },
  {
    "path": "packages/eslint-plugin/README.md",
    "content": "# @react-three/eslint-plugin\n\n[![Version](https://img.shields.io/npm/v/@react-three/eslint-plugin?style=flat&colorA=000000&colorB=000000)](https://npmjs.com/package/@react-three/eslint-plugin)\n[![Twitter](https://img.shields.io/twitter/follow/pmndrs?label=%40pmndrs&style=flat&colorA=000000&colorB=000000&logo=twitter&logoColor=000000)](https://twitter.com/pmndrs)\n[![Discord](https://img.shields.io/discord/740090768164651008?style=flat&colorA=000000&colorB=000000&label=discord&logo=discord&logoColor=000000)](https://discord.gg/ZZjjNvJ)\n[![Open Collective](https://img.shields.io/opencollective/all/react-three-fiber?style=flat&colorA=000000&colorB=000000)](https://opencollective.com/react-three-fiber)\n[![ETH](https://img.shields.io/badge/ETH-f5f5f5?style=flat&colorA=000000&colorB=000000)](https://blockchain.com/eth/address/0x6E3f79Ea1d0dcedeb33D3fC6c34d2B1f156F2682)\n[![BTC](https://img.shields.io/badge/BTC-f5f5f5?style=flat&colorA=000000&colorB=000000)](https://blockchain.com/btc/address/36fuguTPxGCNnYZSRdgdh6Ea94brCAjMbH)\n\nAn ESLint plugin which provides lint rules for [@react-three/fiber](https://github.com/pmndrs/react-three-fiber).\n\n## Installation\n\n```bash\nnpm install @react-three/eslint-plugin --save-dev\n```\n\n## Configuration\n\nUse the recommended [config](#recommended) to get reasonable defaults:\n\n```json\n\"extends\": [\n  \"plugin:@react-three/recommended\"\n]\n```\n\nIf you do not use a config you will need to specify individual rules and add extra configuration.\n\nAdd \"@react-three\" to the plugins section.\n\n```json\n\"plugins\": [\n  \"@react-three\"\n]\n```\n\nEnable the rules that you would like to use.\n\n```json\n\"rules\": {\n  \"@react-three/no-clone-in-frame-loop\": \"error\"\n}\n```\n\n## Rules\n\n✅ Enabled in the `recommended` [configuration](#recommended).<br>\n🔧 Automatically fixable by the `--fix` [CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).<br>\n💡 Manually fixable by [editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).\n\n<!-- START_RULE_CODEGEN -->\n<!-- @command yarn codegen:eslint -->\n\n| Rule                                                            | Description                                                                                | ✅  | 🔧  | 💡  |\n| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | --- | --- | --- |\n| <a href=\"./docs/rules/no-clone-in-loop.md\">no-clone-in-loop</a> | Disallow cloning vectors in the frame loop which can cause performance problems.           | ✅  |     |     |\n| <a href=\"./docs/rules/no-new-in-loop.md\">no-new-in-loop</a>     | Disallow instantiating new objects in the frame loop which can cause performance problems. | ✅  |     |     |\n\n<!-- END_CODEGEN -->\n\n## Shareable configs\n\n### Recommended\n\nThis plugin exports a `recommended` configuration that enforces rules appropriate for everyone using React Three Fiber.\n\n```json\n\"extends\": [\n  \"plugin:@react-three/recommended\"\n]\n```\n\n### All\n\nThis plugin also exports an `all` configuration that includes every available rule.\n\n```json\n\"extends\": [\n  \"plugin:@react-three/all\"\n]\n```\n"
  },
  {
    "path": "packages/eslint-plugin/docs/rules/no-clone-in-loop.md",
    "content": "Cloning vectors in the frame loop instantiates new objects wasting large amounts of memory,\nwhich is especially bad for Three.js classes.\nInstead create once in a `useMemo` or a single shared reference outside of the component.\n\n#### ❌ Incorrect\n\nThis creates a new vector 60+ times a second allocating large amounts of memory.\n\n```js\nfunction Direction({ targetPosition }) {\n  const ref = useRef()\n\n  useFrame(() => {\n    const direction = ref.current.position.clone().sub(targetPosition).normalize()\n  })\n\n  return <mesh ref={ref} />\n}\n```\n\n#### ✅ Correct\n\nThis creates a vector outside of the frame loop to be reused each frame.\n\n```js\nconst tempVec = new THREE.Vector3()\n\nfunction Direction({ targetPosition }) {\n  const ref = useRef()\n\n  useFrame(() => {\n    const direction = tempVec.copy(ref.current.position).sub(targetPosition).normalize()\n  })\n\n  return <mesh ref={ref} />\n}\n```\n\nThis creates a vector once outside of the frame loop inside a `useMemo` to be reused each frame.\n\n```js\nfunction Direction({ targetPosition }) {\n  const ref = useRef()\n  const tempVec = useMemo(() => new THREE.Vector3())\n\n  useFrame(() => {\n    const direction = tempVec.copy(ref.current.position).sub(targetPosition).normalize()\n  })\n\n  return <mesh ref={ref} />\n}\n```\n"
  },
  {
    "path": "packages/eslint-plugin/docs/rules/no-new-in-loop.md",
    "content": "Instantiating new objects in the frame loop can waste large amounts of memory,\nwhich is especially bad for large CPU containers such as Three.js classes and any GPU resource as there is no reliable garbage collection.\nInstead create once in a `useMemo` or a single shared reference outside of the component.\n\n#### ❌ Incorrect\n\nThis creates a new vector 60+ times a second allocating large amounts of memory.\n\n```js\nfunction MoveTowards({ x, y, z }) {\n  const ref = useRef()\n\n  useFrame(() => {\n    ref.current.position.lerp(new THREE.Vector3(x, y, z), 0.1)\n  })\n\n  return <mesh ref={ref} />\n}\n```\n\n#### ✅ Correct\n\nThis creates a vector outside of the frame loop to be reused each frame.\n\n```js\nconst tempVec = new THREE.Vector3()\n\nfunction MoveTowards({ x, y, z }) {\n  const ref = useRef()\n\n  useFrame(() => {\n    ref.current.position.lerp(tempVec.set(x, y, z), 0.1)\n  })\n\n  return <mesh ref={ref} />\n}\n```\n\nThis creates a vector once outside of the frame loop inside a `useMemo` to be reused each frame.\n\n```js\nfunction MoveTowards({ x, y, z }) {\n  const ref = useRef()\n  const tempVec = useMemo(() => new THREE.Vector3())\n\n  useFrame(() => {\n    ref.current.position.lerp(tempVec.set(x, y, z), 0.1)\n  })\n\n  return <mesh ref={ref} />\n}\n```\n"
  },
  {
    "path": "packages/eslint-plugin/package.json",
    "content": "{\n  \"name\": \"@react-three/eslint-plugin\",\n  \"version\": \"0.1.2\",\n  \"description\": \"An ESLint plugin which provides lint rules for @react-three/fiber.\",\n  \"keywords\": [\n    \"react\",\n    \"renderer\",\n    \"fiber\",\n    \"three\",\n    \"threejs\",\n    \"eslint\"\n  ],\n  \"author\": \"Michael Dougall (https://github.com/itsdouges)\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/pmndrs/react-three-fiber/issues\"\n  },\n  \"homepage\": \"https://github.com/pmndrs/react-three-fiber/packages/eslint-plugin#readme\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/pmndrs/react-three-fiber.git\"\n  },\n  \"collective\": {\n    \"type\": \"opencollective\",\n    \"url\": \"https://opencollective.com/react-three-fiber\"\n  },\n  \"main\": \"dist/react-three-eslint-plugin.cjs.js\",\n  \"module\": \"dist/react-three-eslint-plugin.esm.js\",\n  \"types\": \"dist/react-three-eslint-plugin.cjs.d.ts\",\n  \"files\": [\n    \"dist\"\n  ],\n  \"sideEffects\": false,\n  \"preconstruct\": {\n    \"entrypoints\": [\n      \"index.ts\"\n    ]\n  },\n  \"dependencies\": {\n    \"@babel/runtime\": \"^7.17.8\",\n    \"eslint\": \"^8.12.0\"\n  },\n  \"devDependencies\": {\n    \"@types/eslint\": \"^8.4.10\",\n    \"@types/lodash\": \"^4.14.191\",\n    \"lodash\": \"^4.17.19\",\n    \"prettier\": \"^2.6.1\",\n    \"ts-node\": \"^10.9.1\"\n  },\n  \"scripts\": {\n    \"codegen\": \"ts-node scripts/codegen.ts\"\n  }\n}\n"
  },
  {
    "path": "packages/eslint-plugin/scripts/codegen.ts",
    "content": "import type { Rule } from 'eslint'\nimport fs from 'fs/promises'\nimport { join, extname, relative } from 'path'\nimport { camelCase } from 'lodash'\nimport { format, resolveConfig } from 'prettier'\n\nconst jsHeader = (file: string) =>\n  `// THIS FILE WAS GENERATED DO NOT MODIFY BY HAND\n// @command yarn codegen:eslint\n` + file\n\ninterface FoundRule {\n  module: Rule.RuleModule\n  moduleName: string\n}\n\ninterface GeneratedConfig {\n  name: string\n  path: string\n}\n\nconst ignore = ['index.ts']\nconst srcDir = join(__dirname, '../src')\nconst docsDir = join(__dirname, '../docs/rules')\nconst rulesDir = join(srcDir, 'rules')\nconst configsDir = join(srcDir, 'configs')\nconst generatedConfigs: GeneratedConfig[] = []\n\nasync function ruleDocsPath(name: string): Promise<string> {\n  const absolutePath = join(docsDir, name + '.md')\n  const relativePath = '.' + absolutePath.replace(process.cwd(), '')\n\n  try {\n    await fs.readFile(absolutePath)\n    return relativePath\n  } catch (_) {\n    throw new Error(`invariant: rule ${name} should have docs at ${absolutePath}`)\n  }\n}\n\nasync function generateConfig(name: string, rules: FoundRule[]) {\n  const code = `\n    export default {\n      plugins: ['@react-three'],\n      rules: {\n        ${rules.map((rule) => `'@react-three/${rule.moduleName}': 'error'`).join(',')}\n      },\n    }\n  `\n\n  const filepath = join(configsDir, `${name}.ts`)\n  await writeFile(filepath, code)\n\n  generatedConfigs.push({ name: camelCase(name), path: './' + relative(srcDir, join(configsDir, name)) })\n}\n\nasync function writeFile(filepath: string, code: string) {\n  const config = await resolveConfig(filepath)\n  await fs.writeFile(filepath, format(extname(filepath) === '.md' ? code : jsHeader(code), { ...config, filepath }))\n}\n\nasync function generateRuleIndex(rules: FoundRule[]) {\n  const code = `\n    ${rules.map((rule) => `import ${camelCase(rule.moduleName)} from './${rule.moduleName}'`).join('\\n')}\n\n    export default {\n    ${rules.map((rule) => `'${rule.moduleName}': ${camelCase(rule.moduleName)}`).join(',')}\n    }\n  `\n\n  const filepath = join(rulesDir, 'index.ts')\n  await writeFile(filepath, code)\n}\n\nasync function generatePluginIndex() {\n  const code = `\n    ${generatedConfigs.map((config) => `import ${config.name} from '${config.path}'`).join('\\n')}\n\n    export { default as rules } from './rules/index'\n\n    export const configs = {\n      ${generatedConfigs.map((config) => `${config.name}`).join(',')}\n    }\n  `\n\n  const filepath = join(srcDir, 'index.ts')\n  await writeFile(filepath, code)\n}\n\nconst conditional = (cond: string, content?: boolean | string) => (content ? cond : '')\nconst link = (content: string, url?: string) => (url ? `<a href=\"${url}\">${content}</a>` : content)\n\nasync function generateReadme(rules: FoundRule[]) {\n  const filepath = join(srcDir, '../', 'README.md')\n  const readme = await fs.readFile(filepath, 'utf-8')\n\n  const rows: string[] = []\n\n  for (const rule of rules) {\n    const docsPath = await ruleDocsPath(rule.moduleName)\n    const row = `| ${link(rule.moduleName, docsPath)} | ${rule.module.meta?.docs?.description} | ${conditional(\n      '✅',\n      rule.module.meta?.docs?.recommended,\n    )} | ${conditional('🔧', rule.module.meta?.fixable)} | ${conditional('💡', rule.module.meta?.hasSuggestions)} |`\n\n    rows.push(row)\n  }\n\n  const code = `\n| Rule | Description | ✅ | 🔧 | 💡 | \n| ---- | -- | -- | -- | -- |\n${rows.join('\\n')}\n  `\n\n  const found = /<!-- START_RULE_CODEGEN -->(.|\\n)*<!-- END_CODEGEN -->/.exec(readme)\n\n  if (!found) {\n    throw new Error('invariant')\n  }\n\n  const newReadme = readme.replace(\n    found[0],\n    '<!-- START_RULE_CODEGEN -->' + '\\n<!-- @command yarn codegen:eslint -->' + code + '\\n<!-- END_CODEGEN -->',\n  )\n\n  await writeFile(filepath, newReadme)\n}\n\nasync function generate() {\n  const rulePaths = await fs.readdir(rulesDir)\n  const recommended: FoundRule[] = []\n  const rules: FoundRule[] = []\n\n  for (const moduleName of rulePaths) {\n    if (ignore.includes(moduleName)) {\n      continue\n    }\n\n    const rule: Rule.RuleModule = (await import(join(rulesDir, moduleName))).default\n    const foundRule = { module: rule, moduleName: moduleName.replace(extname(moduleName), '') }\n    rules.push(foundRule)\n\n    if (rule.meta?.docs?.recommended) {\n      recommended.push(foundRule)\n    }\n  }\n\n  await generateRuleIndex(rules)\n  await generateConfig('all', rules)\n  await generateConfig('recommended', recommended)\n  await generatePluginIndex()\n  await generateReadme(rules)\n}\n\ngenerate()\n"
  },
  {
    "path": "packages/eslint-plugin/src/configs/all.ts",
    "content": "// THIS FILE WAS GENERATED DO NOT MODIFY BY HAND\n// @command yarn codegen:eslint\n\nexport default {\n  plugins: ['@react-three'],\n  rules: {\n    '@react-three/no-clone-in-loop': 'error',\n    '@react-three/no-new-in-loop': 'error',\n  },\n}\n"
  },
  {
    "path": "packages/eslint-plugin/src/configs/recommended.ts",
    "content": "// THIS FILE WAS GENERATED DO NOT MODIFY BY HAND\n// @command yarn codegen:eslint\n\nexport default {\n  plugins: ['@react-three'],\n  rules: {\n    '@react-three/no-clone-in-loop': 'error',\n    '@react-three/no-new-in-loop': 'error',\n  },\n}\n"
  },
  {
    "path": "packages/eslint-plugin/src/index.ts",
    "content": "// THIS FILE WAS GENERATED DO NOT MODIFY BY HAND\n// @command yarn codegen:eslint\n\nimport all from './configs/all'\nimport recommended from './configs/recommended'\n\nexport { default as rules } from './rules/index'\n\nexport const configs = {\n  all,\n  recommended,\n}\n"
  },
  {
    "path": "packages/eslint-plugin/src/lib/url.ts",
    "content": "export function gitHubUrl(name: string) {\n  return `https://github.com/pmndrs/react-three-fiber/blob/master/packages/eslint-plugin/docs/rules/${name}.md`\n}\n"
  },
  {
    "path": "packages/eslint-plugin/src/rules/index.ts",
    "content": "// THIS FILE WAS GENERATED DO NOT MODIFY BY HAND\n// @command yarn codegen:eslint\n\nimport noCloneInLoop from './no-clone-in-loop'\nimport noNewInLoop from './no-new-in-loop'\n\nexport default {\n  'no-clone-in-loop': noCloneInLoop,\n  'no-new-in-loop': noNewInLoop,\n}\n"
  },
  {
    "path": "packages/eslint-plugin/src/rules/no-clone-in-loop.ts",
    "content": "import type { Rule } from 'eslint'\nimport * as ESTree from 'estree'\nimport { gitHubUrl } from '../lib/url'\n\nconst rule: Rule.RuleModule = {\n  meta: {\n    messages: {\n      noClone:\n        'Cloning vectors in the frame loop can cause performance problems. Instead, create once in a useMemo or a single, shared reference outside of the component.',\n    },\n    docs: {\n      url: gitHubUrl('no-clone-in-loop'),\n      recommended: true,\n      description: 'Disallow cloning vectors in the frame loop which can cause performance problems.',\n    },\n  },\n  create(ctx) {\n    return {\n      ['CallExpression[callee.name=useFrame] CallExpression MemberExpression Identifier[name=clone]'](\n        node: ESTree.NewExpression,\n      ) {\n        ctx.report({\n          messageId: 'noClone',\n          node: node,\n        })\n      },\n    }\n  },\n}\n\nexport default rule\n"
  },
  {
    "path": "packages/eslint-plugin/src/rules/no-new-in-loop.ts",
    "content": "import type { Rule } from 'eslint'\nimport * as ESTree from 'estree'\nimport { gitHubUrl } from '../lib/url'\n\nconst rule: Rule.RuleModule = {\n  meta: {\n    messages: {\n      noNew:\n        'Instantiating new objects in the frame loop can cause performance problems. Instead, create once in a useMemo or a single, shared reference outside of the component.',\n    },\n    docs: {\n      url: gitHubUrl('no-new-in-loop'),\n      recommended: true,\n      description: 'Disallow instantiating new objects in the frame loop which can cause performance problems.',\n    },\n  },\n  create(ctx) {\n    return {\n      ['CallExpression[callee.name=useFrame] NewExpression'](node: ESTree.NewExpression) {\n        ctx.report({\n          messageId: 'noNew',\n          node: node,\n        })\n      },\n    }\n  },\n}\n\nexport default rule\n"
  },
  {
    "path": "packages/eslint-plugin/tests/rules/no-clone-in-loop.test.ts",
    "content": "import { RuleTester } from 'eslint'\nimport rule from '../../src/rules/no-clone-in-loop'\n\nconst tester = new RuleTester({\n  parserOptions: { ecmaVersion: 2015 },\n})\n\ntester.run('no-new-in-loop', rule, {\n  valid: [\n    `\n    const vec = new THREE.Vector3()\n\n    useFrame(() => {\n      ref.current.position.copy(vec)\n    })\n  `,\n    `\n    useFrame(() => {\n      clone()\n    })\n  `,\n    `\n    useFrame(() => {\n      const clone = vec.copy();\n    })\n  `,\n  ],\n  invalid: [\n    {\n      code: `\n        useFrame(() => {\n          ref.current.position.clone()\n        })\n      `,\n      errors: [{ messageId: 'noClone' }],\n    },\n  ],\n})\n"
  },
  {
    "path": "packages/eslint-plugin/tests/rules/no-new-in-loop.test.ts",
    "content": "import { RuleTester } from 'eslint'\nimport rule from '../../src/rules/no-new-in-loop'\n\nconst tester = new RuleTester({\n  parserOptions: { ecmaVersion: 2015 },\n})\n\ntester.run('no-new-in-loop', rule, {\n  valid: [\n    `\n    const vec = new THREE.Vector3()\n\n    useFrame(() => {\n      ref.current.position.copy(vec)\n    })\n  `,\n    `\n    const vec = new THREE.Vector3()\n\n    useFrame(() => {\n      ref.current.position.lerp(vec.set(x, y, z), 0.1)\n    })\n  `,\n    `\n    const vec = new Vector3()\n\n    useFrame(() => {\n      ref.current.position.copy(vec)\n    })\n  `,\n    `\n    const vec = new Vector3()\n\n    useFrame(() => {\n      ref.current.position.lerp(vec.set(x, y, z), 0.1)\n    })\n  `,\n  ],\n  invalid: [\n    {\n      code: `\n        useFrame(() => {\n          ref.current.position.lerp(new THREE.Vector3(x, y, z), 0.1)\n        })\n      `,\n      errors: [{ messageId: 'noNew' }],\n    },\n    {\n      code: `\n        useFrame(() => {\n          ref.current.position.lerp(new Vector3(x, y, z), 0.1)\n        })\n      `,\n      errors: [{ messageId: 'noNew' }],\n    },\n  ],\n})\n"
  },
  {
    "path": "packages/fiber/.npmignore",
    "content": "examples/\nexample/\n.codesandbox/\n.github/\n.husky/\nmarkdown/\n/src/\ntests/\n__mocks__"
  },
  {
    "path": "packages/fiber/CHANGELOG.md",
    "content": "# @react-three/fiber\n\n## 9.5.0\n\n### Minor Changes\n\n- 1050a62bd4bc15e60ab3a65deea08dedfcf24989: Support React 19.2\n\n## 9.4.2\n\n### Patch Changes\n\n- 3d445fd158b25eb380cd27bd2e01304016aa23e3: fix: Expo SDK 54 compatibility through workaround\n\n## 9.4.1\n\n### Patch Changes\n\n- 4f8cec0d79003a9ef6d1ca1e56de94aec4158714: fix: pass DevTools config through createReconciler to fix React DevTools\n\n## 9.4.0\n\n### Minor Changes\n\n- f0976dc14a2d3203af267d0e3524d45a07f3248a: feat: improve applyProps errors, harden pierced props setting\n\n## 9.3.0\n\n### Minor Changes\n\n- 7579c2d79ed60e5c93a259637b65c01971f39d82: feat: add flushSync example\n- 26e5d6e8b8b00b817ebb5242432000abe38bfc2c: fix: update flushSync for new reconciler\n- 0281b6bc4fcf041ed801e64f1876f70c214aa117: fix(native): update react-native deep imports for 0.79 compatibility\n\n## 9.2.0\n\n### Minor Changes\n\n- 94ece53e17a586465b10ec627cf4799cefa72b3a: Export flushSync\n\n## 9.1.4\n\n### Patch Changes\n\n- d491e46087508dff50c48768a99f87bd486b6910: Accept readonly arrays for vector props\n\n## 9.1.3\n\n### Patch Changes\n\n- efd28328f5e612b0592be7da316fad990fdf4675: fix(native) fix crash on rerendering GLView with new arch\n\n## 9.1.2\n\n### Patch Changes\n\n- 053757f45d2411f2929975add76a4c979713e616: fix: reference dev-only act with computed key for Webpack\n\n## 9.1.1\n\n### Patch Changes\n\n- 0deae3fb12b6d62ff083891e388ab09de51330d0: Fix failing builds for production when React.act is unavailable. This fixes issues found in React@19.1.0 and up.\n\n## 9.1.0\n\n### Minor Changes\n\n- cbc79507600b81f6a39de9f3f0ecb3aaf811233f: feat: add meshes to loader graph, misc internal fixes\n\n## 9.0.4\n\n### Patch Changes\n\n- 28ebfbf3e7f9e69dc62f5481965b7fd6a3e3a038: fix(types): exclude type conflicts in React runtime types\n\n## 9.0.3\n\n### Patch Changes\n\n- 01f0f1c8855325aae1c337d5443c846cc46007b0: fix(types): remove recursive references in JSX types\n\n## 9.0.2\n\n### Patch Changes\n\n- 14e133278c3cf1550a3b9aeaaadb624a2aae4781: fix(reconciler): prefer to resolve unprefixed instance types\n\n## 9.0.1\n\n### Patch Changes\n\n- 5d711d103b8d3833b93c3ab74707b2b6db5d274a: fix: add use-sync-external-store dep\n\n## 9.0.0\n\n### Major Changes\n\n- 226d2ec: feat: React 19 support\n\n## 8.18.0\n\n### Minor Changes\n\n- 54a3330f: feat: make children optional in Canvas\n\n## 8.17.14\n\n### Patch Changes\n\n- eeeed16b: fix: update use-measure\n\n## 8.17.13\n\n### Patch Changes\n\n- 0a0f2acd: fix: upstream use-measure\n\n## 8.17.12\n\n### Patch Changes\n\n- ff1a16f1: fix: narrow React peer dep range\n\n## 8.17.11\n\n### Patch Changes\n\n- 7461bf0c: fix: loosen instanceof checks for CSB issue\n\n## 8.17.10\n\n### Patch Changes\n\n- d1a072ac: fix: ThreeEvent should not include initMouseEvent\n\n## 8.17.9\n\n### Patch Changes\n\n- f34de655: fix: add orientation handling\n\n## 8.17.8\n\n### Patch Changes\n\n- 3c885807: fix(types): fix React typings, fault tolerant Node type\n\n## 8.17.7\n\n### Patch Changes\n\n- c20a7d73: fix(native): missing pointerId in pointer events\n\n## 8.17.6\n\n### Patch Changes\n\n- 66c3e9fe: fix(native): don't bind events to GLView\n\n## 8.17.5\n\n### Patch Changes\n\n- 162dbbdd: fix: npmignore ignored types\"\n\n## 8.17.4\n\n### Patch Changes\n\n- 43866f4e: fix: rebuild with types\n\n## 8.17.3\n\n### Patch Changes\n\n- 8363eb7a: fix: rebuild with types\n\n## 8.17.2\n\n### Patch Changes\n\n- 6aa4eb28: fix: rebuild with types\n\n## 8.17.1\n\n### Patch Changes\n\n- e5f3f4f9: fix: rebuild with types\n\n## 8.17.0\n\n### Minor Changes\n\n- 3c22194d: feat: flushSync, native EventTarget\n\n## 8.16.8\n\n### Patch Changes\n\n- 4748b365: fix: update is.equ to compare booleans\n\n## 8.16.7\n\n### Patch Changes\n\n- 4d6408c7: fix(types): revert usage of future module JSX\n\n## 8.16.6\n\n### Patch Changes\n\n- 03ab82fe: fix(applyProps): null check indeterminate instances\n\n## 8.16.5\n\n### Patch Changes\n\n- cb913e01: fix: use fast JSX, future JSX types\n\n## 8.16.4\n\n### Patch Changes\n\n- 1270d24c: fix: missing dependency on inject function\n\n## 8.16.3\n\n### Patch Changes\n\n- 9c83502c: fix(Canvas): don't override camera frustum props\n\n## 8.16.2\n\n### Patch Changes\n\n- e0900489: fix(useLoader): don't dispose of memoized loader\n\n## 8.16.1\n\n### Patch Changes\n\n- 503efc2e: fix: prevent invalidate from piling up frames\n\n## 8.16.0\n\n### Minor Changes\n\n- 6b0ea6eb: feat: add childadded event dispatch\n\n## 8.15.19\n\n### Patch Changes\n\n- 74926b94: fix(types): avoid emitting THREE.XRFrame\n\n## 8.15.18\n\n### Patch Changes\n\n- 8c01939a: fix: correctly pass frames in invalidate\n\n## 8.15.17\n\n### Patch Changes\n\n- 16c2ee97: fix(types): support @types/three@0.162.0\n\n## 8.15.16\n\n### Patch Changes\n\n- 71cd8f96: fix: tonemapping config overwrites userland\n- 0bb12fd1: fix(types): remove usage of THREE.Vector\n\n## 8.15.14\n\n### Patch Changes\n\n- 0afc9c12: fix: portal events, update examples\n\n## 8.15.13\n\n### Patch Changes\n\n- 0a399f6d: fix(native): use MSAA for antialias on iOS\n\n## 8.15.12\n\n### Patch Changes\n\n- 496d6f0d: fix: useLoader and XRFrame type fixes\n\n## 8.15.11\n\n### Patch Changes\n\n- 3d9af04d: fix: update import from three examples\n\n## 8.15.10\n\n### Patch Changes\n\n- 49158164: fix: don't recursively dispose primitives\n\n## 8.15.9\n\n### Patch Changes\n\n- 4cbc5530: fix(native): deopt iOS blob URI path\n\n## 8.15.8\n\n### Patch Changes\n\n- 70680832: fix: revert stable sort\n\n## 8.15.7\n\n### Patch Changes\n\n- 07e39e2e: fix(types): remove use of Object3D generic\n\n## 8.15.6\n\n### Patch Changes\n\n- 7bb2950b: experiment: stable object sort\n\n## 8.15.5\n\n### Patch Changes\n\n- 0e44fd8b: fix(types): preserve deprecated JSX annotations\n\n## 8.15.4\n\n### Patch Changes\n\n- 634e5db5: fix(native): harden Blob URI check for Android\n\n## 8.15.3\n\n### Patch Changes\n\n- beab4344: fix(native): workaround Android content policy for Blob URI\n\n## 8.15.2\n\n### Patch Changes\n\n- 086d3932: fix: size check and raycaster camera\n\n## 8.15.1\n\n### Patch Changes\n\n- 2d39676d: fix: ignore deprecated types, use correct XRFrame definition\n\n## 8.15.0\n\n### Minor Changes\n\n- cca8b6bb: feat: export buildGraph\n\n## 8.14.7\n\n### Patch Changes\n\n- 0f63a287: fix(native): restore polyfill conversions, drop networking\n\n## 8.14.6\n\n### Patch Changes\n\n- 465fa0fb: fix(native): revert usage of networking stack\n\n## 8.14.5\n\n### Patch Changes\n\n- f372a5b5: fix(applyProps): loosen copy identity in dev\n\n## 8.14.4\n\n### Patch Changes\n\n- dc7e5739: fix(native): amend BlobManager over globals\n\n## 8.14.3\n\n### Patch Changes\n\n- d77b0990: fix(native): drop fsstat for react-native-web\n\n## 8.14.2\n\n### Patch Changes\n\n- 33e8baef: fix: native perf, loader types\n\n## 8.14.1\n\n### Patch Changes\n\n- c99907bf: fix(native): prefer local uri for fs\n\n## 8.14.0\n\n### Minor Changes\n\n- 89e96bf4: feat: react-native-web, native globals fixes\n\n## 8.13.9\n\n### Patch Changes\n\n- 44d57b3c: fix(native): TextureLoader should remain consistent with FileLoader\n\n## 8.13.8\n\n### Patch Changes\n\n- 5da26d52: fix(useLoader): dispose loaders\n\n## 8.13.7\n\n### Patch Changes\n\n- 37b9502a: fix(Canvas): pass scene prop\n\n## 8.13.6\n\n### Patch Changes\n\n- 0597495c: fix: harden XR init against Renderer shim\n\n## 8.13.5\n\n### Patch Changes\n\n- 7a3b543b: fix: three type regressions\n\n## 8.13.4\n\n### Patch Changes\n\n- 824ee0f7: fix: safely diff instances\n\n## 8.13.3\n\n### Patch Changes\n\n- ffdb5fc4: revert nested portals, up suspend-react\n\n## 8.13.2\n\n### Patch Changes\n\n- bbabdf07: update suspend-react\n\n## 8.13.1\n\n### Patch Changes\n\n- c9fe03ba: fix: primitive swap and reactive portals\n\n## 8.13.0\n\n### Minor Changes\n\n- ecfc48b7: feat: CanvasProps alias, respect r152 color management\n\n## 8.12.2\n\n### Patch Changes\n\n- c5193468: fix: safeguard window.devicePixelRatio\n\n## 8.12.1\n\n### Patch Changes\n\n- 571f07ac: fix: safeguard window.devicePixelRatio\n\n## 8.12.0\n\n### Minor Changes\n\n- 1928d095: feat: `scene` render prop for custom THREE.Scene\n\n## 8.11.11\n\n### Patch Changes\n\n- f03c6ef8: feat: `scene` render prop for custom THREE.Scene\n\n## 8.11.10\n\n### Patch Changes\n\n- 12907836: fix onpointerlostcapture which fired before pointerup\n\n## 8.11.9\n\n### Patch Changes\n\n- 6b5f572c: fix: treeshake THREE.ColorManagement\n\n## 8.11.8\n\n### Patch Changes\n\n- 350cd3f3: fix(Canvas): inline render-effect\n\n## 8.11.7\n\n### Patch Changes\n\n- 96af62d5: fix: don't overwrite public cameras\n\n## 8.11.6\n\n### Patch Changes\n\n- 7d319c18: Fix is.equ obj:shallow, allow it to test arrays 1 level deep, fix canvas.camera prop being stale\n\n## 8.11.5\n\n### Patch Changes\n\n- c658f763: fix: update canvas prop types\n\n## 8.11.4\n\n### Patch Changes\n\n- 970aa58b: fix: play nice with offscreencanvas\n\n## 8.11.3\n\n### Patch Changes\n\n- 2bce569c: fix: progressively set colormanagement\n\n## 8.11.2\n\n### Patch Changes\n\n- 41d655cd: fix: hmr, srgb encode\n\n## 8.11.1\n\n### Patch Changes\n\n- 58cadeff: fix: skip circular onUpdate calls\n\n## 8.11.0\n\n### Minor Changes\n\n- 2917a47b: events.update, allow raycast w/o user interaction\n- 521bfb07: events.update, allow raycast w/o user interaction\n\n## 8.10.4\n\n### Patch Changes\n\n- d9e6316d: fix: transpile class properties\n\n## 8.10.3\n\n### Patch Changes\n\n- d06d2879: fix: align useLoader type, public fields from builds\n\n## 8.10.2\n\n### Patch Changes\n\n- 564edbbb: fix port inject layers, it should allow root props to overwrite undefined portal props\"\n\n## 8.10.1\n\n### Patch Changes\n\n- bfa0298f: fix memoized identity\n\n## 8.10.0\n\n### Minor Changes\n\n- 24c4dba4: shortcut for shadow type\n\n## 8.9.2\n\n### Patch Changes\n\n- 2aeb6500: fix: primitives are incorrectly swapped on key change in maps\n\n## 8.9.1\n\n### Patch Changes\n\n- 0cf11797: fix(events): type spread event props\n\n## 8.9.0\n\n### Minor Changes\n\n- a458b4dd: fix(loop): export flush methods and types\n\n## 8.8.11\n\n### Patch Changes\n\n- 2068f0cc: fix: events pointerlock, useLoader extension types\n\n## 8.8.10\n\n### Patch Changes\n\n- 00c24718: fix: invalidate pierced props\n\n## 8.8.9\n\n### Patch Changes\n\n- 4254400e: fix(createPortal): use correct JSX type\n\n## 8.8.8\n\n### Patch Changes\n\n- fcb183e3: fix: call onUpdate for attached children prop update\n\n## 8.8.7\n\n### Patch Changes\n\n- bedb16e7: fix: prefer named functions, for loops in hot paths\n\n## 8.8.6\n\n### Patch Changes\n\n- 02a558bb: fix: upgrade deps\n\n## 8.8.5\n\n### Patch Changes\n\n- 530a06d6: fix: upgrade deps to work-around CRA\n\n## 8.8.4\n\n### Patch Changes\n\n- 2f2dc9f9: chore: upgrade bridge to harden suspense behavior\n\n## 8.8.3\n\n### Patch Changes\n\n- 9f571239: fix #2506, events should fall back to rootstate\"\n\n## 8.8.2\n\n### Patch Changes\n\n- dc389ed6: fix(Canvas): prevent remount on context update\n\n## 8.8.1\n\n### Patch Changes\n\n- 370d3ae5: refactor: pull context bridge from its-fine\n\n## 8.8.0\n\n### Minor Changes\n\n- 46d8b440: bridge cross-container context\n\n## 8.7.4\n\n### Patch Changes\n\n- 259c8895: fix: use self to get global context before window\n\n## 8.7.3\n\n### Patch Changes\n\n- eb5a3be4: fix: if there is an eventsource pointerevent will be set to none\n\n## 8.7.2\n\n### Patch Changes\n\n- 7f801e60: fix: events in portals carry the wrong raycaster, camera, etc\n\n## 8.7.1\n\n### Patch Changes\n\n- 962cc270: fix: allow canvas eventsource to be a ref\n\n## 8.7.0\n\n### Minor Changes\n\n- f5db1b78: feat: useInstanceHandle, flushGlobalEffects\n\n## 8.6.2\n\n### Patch Changes\n\n- 57c12e9c: fix(types): @react-three/drei declaration files\n\n## 8.6.1\n\n### Patch Changes\n\n- 7a0b5670: fix(core): don't append to unmounted containers\n\n## 8.6.0\n\n### Minor Changes\n\n- 85c80e70: eventsource and eventprefix on the canvas component\n\n## 8.5.1\n\n### Patch Changes\n\n- 87821d9: fix: null-check instance.children on reconstruct\n\n## 8.5.0\n\n### Minor Changes\n\n- edc8252: feat: handle primitive children, auto-attach via instanceof\n\n## 8.3.1\n\n### Patch Changes\n\n- aaeb2b8: fix(types): accept readonly arrays for vector props\n\n## 8.3.0\n\n### Minor Changes\n\n- 9c450ec: feat: improve errors\n\n## 8.2.3\n\n### Patch Changes\n\n- b8d2eab: fix: improve useLoader signature, initial size on createRoot\n\n## 8.2.2\n\n### Patch Changes\n\n- acd6f04: fix: warn on stray text\n\n## 8.2.1\n\n### Patch Changes\n\n- 25e35a1: fix: prefer useLayoutEffect in react-native\n\n## 8.2.0\n\n### Minor Changes\n\n- 9770d7d: feat: expose ThreeElements interface for JSX elements\n\n## 8.1.0\n\n### Minor Changes\n\n- 8d0f708c: Expose position information in state.size\n\n## 8.0.27\n\n### Patch Changes\n\n- 7940995: fix: resume on xrsession end, export internal events\n\n## 8.0.26\n\n### Patch Changes\n\n- 7b6df9df: fix: infinite loop updating cam viewport\n\n## 8.0.25\n\n### Patch Changes\n\n- b7cd0f42: update viewport on camera changes\n\n## 8.0.24\n\n### Patch Changes\n\n- ee8e785: fix: attach timings\n\n## 8.0.23\n\n### Patch Changes\n\n- 29d03c64: revert multi attach\n\n## 8.0.22\n\n### Patch Changes\n\n- 419e854: fix: always prepare primitives\n\n## 8.0.21\n\n### Patch Changes\n\n- 3098b9b: fix: resizing in worker contexts, copy over attachments on reconstruct\n\n## 8.0.20\n\n### Patch Changes\n\n- 4c87bce: fix: attach, devtools, and perf fixes\n\n## 8.0.19\n\n### Patch Changes\n\n- 360b45a: fix: handle attach on reconstruct\n\n## 8.0.18\n\n### Patch Changes\n\n- be567c1: fix: suspense attach and three compat in webpack\n\n## 8.0.17\n\n### Patch Changes\n\n- 9e3369e: fix dom resize, improve native tree shaking\n\n## 8.0.16\n\n### Patch Changes\n\n- 669c45c: correctly type useLoader results\n\n## 8.0.15\n\n### Patch Changes\n\n- c4715d5f: allow invalidate to preempt more than 1 frame\n\n## 8.0.14\n\n### Patch Changes\n\n- 5559a119: Add support for recoverable errors\n\n## 8.0.13\n\n### Patch Changes\n\n- 9d77d8e2: fix: detach attribute removal\n\n## 8.0.12\n\n### Patch Changes\n\n- 3d10413f: fix portal layers\n\n## 8.0.11\n\n### Patch Changes\n\n- 5167b1e4: memoized.args can be undefined\n\n## 8.0.10\n\n### Patch Changes\n\n- eb321afd: fix: remount bug, allow portals to inject custom size\n\n## 8.0.9\n\n### Patch Changes\n\n- 624df949: fix: canvas unmount race condition\"\n\n## 8.0.8\n\n### Patch Changes\n\n- 952a566: fix: react SSR\n\n## 8.0.7\n\n### Patch Changes\n\n- f63806b: fix: react SSR\n\n## 8.0.6\n\n### Patch Changes\n\n- d4bafb9: fix re-parenting, useframe not working properly in portals, attach crash\n\n## 8.0.5\n\n### Patch Changes\n\n- 227c328: fix pointer for root and portals\n\n## 8.0.4\n\n### Patch Changes\n\n- e981a72: fix: mock three color management, loosen peer dep\n\n## 8.0.3\n\n### Patch Changes\n\n- 3252aed: setevents needs to spread and be mirrored in portals\n\n## 8.0.2\n\n### Patch Changes\n\n- 8035d1f: fix: legacy mode\n\n## 8.0.1\n\n### Patch Changes\n\n- 26db195: add legacy flag to turn of three.colormanagement\n\n## 8.0.0\n\n### Major Changes\n\n- 385ba9c: v8 major, react-18 compat\n- 04c07b8: v8 major, react-18 compat\n\n### Patch Changes\n\n- 347ea79: new beta for library testing\n\n## 8.0.0-beta.0\n\n### Major Changes\n\n- 385ba9c: v8 major, react-18 compat\n\n## 8.0.0-beta.0\n\n### Patch Changes\n\n- cf6316c: new beta for library testing\n\n## 7.0.25\n\n### Patch Changes\n\n- 8698734: Release latest patches\n\n## 7.0.24\n\n### Patch Changes\n\n- 7f46ddf: cleanup captured pointers when released (#1914)\n\n## 7.0.23\n\n### Patch Changes\n\n- 30d38b1: remove logs\n\n## 7.0.22\n\n### Patch Changes\n\n- 259e1fa: add camera:manual\n\n## 7.0.21\n\n### Patch Changes\n\n- 65e4147: up usemeasure, add last event to internals\"\n\n## 7.0.20\n\n### Patch Changes\n\n- 54cb0fd: update react-use-measure, allow it to use the offsetSize\n\n## 7.0.19\n\n### Patch Changes\n\n- 7aa2eab: fix: remove zustand subscribe selector\n\n## 7.0.18\n\n### Patch Changes\n\n- 6780f58: fix unmount pointer capture\n\n## 7.0.17\n\n### Patch Changes\n\n- 894c550: fix: event count\n\n## 7.0.16\n\n### Patch Changes\n\n- c7a4220: patch: applyprops returns the same instance\n\n## 7.0.15\n\n### Patch Changes\n\n- c5645e8: fix primitive leftovers on switch\n\n## 7.0.14\n\n### Patch Changes\n\n- 05af996: fix: revert the is function\n\n## 7.0.13\n\n### Patch Changes\n\n- f256558: fix(core): don't overwrite camera rotation\n- 51e6fc9: fix(core): safely handle external instances\n\n## 7.0.12\n\n### Patch Changes\n\n- 0df6073: fix: missed events\n\n## 7.0.11\n\n### Patch Changes\n\n- 62b0a3a: fix: event order of missed pointers\n\n## 7.0.10\n\n### Patch Changes\n\n- e019dd4: fixes\n\n## 7.0.9\n\n### Patch Changes\n\n- cd266e4: Fix diffProps dashed keys\n\n## 7.0.8\n\n### Patch Changes\n\n- 6f68406: Allow getCurrentViewport to receive an array\n\n## 7.0.7\n\n### Patch Changes\n\n- 0375896: Simplify useframe, support instanced event cancelation, silence disposal\n\n## 7.0.6\n\n### Patch Changes\n\n- fb052ad: Fix babel-env browserslist transpiling into old code\"\n\n## 7.0.5\n\n### Patch Changes\n\n- c97794a: Add useLoader.clear(Loader, input)\n\n## 7.0.4\n\n### Patch Changes\n\n- 974ecfb: Allow elements to define attachFns for specific mount/unmount\n\n## 7.0.2\n\n### Patch Changes\n\n- a97aca3: Add controls state field\n- 4c703d6: fix rttr didn't work with r130\n\n## 7.0.0\n\n### Major Changes\n\n- 96ae1ad: fix javascript interpreting negative renderpriority as positive\n\nThis is a major breaking change that will fix an edge-case. It will only affect you if you used negative useFrame indices, for instance\n\n```jsx\nuseFrame(..., -1)\n```\n\nSurprisingly this disabled auto-rendering although the documentation says positive numbers only. As of v7 this will not take over the render loop.\n\n```jsx\nfunction Render() {\n  // Takes over the render-loop, the user has the responsibility to render\n  useFrame(({ gl, scene, camera }) => {\n    gl.render(scene, camera)\n  }, 1)\n\nfunction RenderOnTop() {\n  // This will render on top of the previous call\n  useFrame(({ gl, ... }) => {\n    gl.render(...)\n  }, 2)\n\nfunction A() {\n  // Will *not* take over the render-loop, negative indices can still be useful for sorting\n  useFrame(() => ..., -1)\n\nfunction B() {\n  // B's useFrame will execute *after* A's\n  useFrame(() => ..., -2)\n```\n\n## 6.2.3\n\n### Patch Changes\n\n- 26bc7eb: typescript changes\n\n## 6.2.2\n\n### Patch Changes\n\n- 4f44a2c: use more helpful name with event handling in rttr\n\n## 6.2.1\n\n### Patch Changes\n\n- Fix stopPropagation logic\n\n## 6.2.0\n\n### Minor Changes\n\n- Allow object3d instances to be attached\n\n## 6.1.5\n\n### Patch Changes\n\n- fix(rttr): if children is undefined return an array to map with\n\n## 6.1.4\n\n### Patch Changes\n\n- 6faa090: Add shape to types, exclude event functions from event data\n\n## 6.1.3\n\n### Patch Changes\n\n- 71e72c0: Fix constructor args with attached children (#1348)\n- 015fc03: Only set up pointer/wheel events as passive\n- a160e08: Fix event setPointerCapture and stopPropagation.\n\n## 6.1.2\n"
  },
  {
    "path": "packages/fiber/__mocks__/expo-asset.ts",
    "content": "class Asset {\n  name = 'test asset'\n  type = 'glb'\n  hash = null\n  localUri: string | null = null\n  uri = 'test://null'\n\n  width = 800\n  height = 400\n  static fromURI = (uri: any) => Object.assign(new Asset(), { uri })\n  static fromModule = (uri: any) => this.fromURI(uri)\n  async downloadAsync() {\n    if (typeof this.uri === 'string' && !this.uri.includes(':')) {\n      this.localUri = 'drawable.png'\n    } else {\n      this.localUri = 'test://null'\n    }\n\n    return this\n  }\n}\n\nexport { Asset }\n"
  },
  {
    "path": "packages/fiber/__mocks__/expo-file-system.ts",
    "content": "export const EncodingType = { UTF8: 'utf8', Base64: 'base64' }\nexport const cacheDirectory = 'file:///test/'\nexport const readAsStringAsync = async () => ''\nexport const writeAsStringAsync = async () => {}\nexport const copyAsync = async () => {}\n"
  },
  {
    "path": "packages/fiber/__mocks__/expo-gl.ts",
    "content": "import * as React from 'react'\nimport type { GLViewProps } from 'expo-gl'\nimport { WebGL2RenderingContext } from '@react-three/test-renderer/src/WebGL2RenderingContext'\n\nexport function GLView({ onContextCreate, ref, ...props }: GLViewProps & any) {\n  React.useLayoutEffect(() => {\n    const gl = new WebGL2RenderingContext({ width: 1280, height: 800 } as HTMLCanvasElement)\n    gl.endFrameEXP = () => {}\n    onContextCreate(gl as any)\n  }, [onContextCreate])\n\n  return React.createElement('glview', props)\n}\n"
  },
  {
    "path": "packages/fiber/__mocks__/react-native.ts",
    "content": "import * as React from 'react'\nimport { ViewProps, LayoutChangeEvent } from 'react-native'\n\nexport class View extends React.Component<Omit<ViewProps, 'children'> & { children: React.ReactNode }> {\n  componentDidMount() {\n    this.props.onLayout?.({\n      nativeEvent: {\n        layout: {\n          x: 0,\n          y: 0,\n          width: 1280,\n          height: 800,\n        },\n      },\n    } as LayoutChangeEvent)\n  }\n\n  render() {\n    const { onLayout, ...props } = this.props\n    return React.createElement('view', props)\n  }\n}\n\nexport const StyleSheet = {\n  absoluteFill: {\n    position: 'absolute',\n    left: 0,\n    right: 0,\n    top: 0,\n    bottom: 0,\n  },\n}\n\nexport const PanResponder = {\n  create: () => ({ panHandlers: {} }),\n}\n\nexport const Image = {\n  getSize(_uri: string, res: Function, rej?: Function) {\n    res(1, 1)\n  },\n}\n\nexport const Platform = {\n  OS: 'web',\n}\n\nexport const NativeModules = {}\n\nexport const PixelRatio = {\n  get() {\n    return 1\n  },\n}\n"
  },
  {
    "path": "packages/fiber/__mocks__/react-use-measure.ts",
    "content": "import * as React from 'react'\n\nexport default function useMeasure() {\n  const element = React.useRef<HTMLElement | null>(null)\n  const [bounds] = React.useState({\n    left: 0,\n    top: 0,\n    width: 1280,\n    height: 800,\n    bottom: 0,\n    right: 0,\n    x: 0,\n    y: 0,\n  })\n  const ref = (node: HTMLElement) => {\n    if (!node || element.current) {\n      return\n    }\n    element.current = node\n  }\n  return [ref, bounds]\n}\n"
  },
  {
    "path": "packages/fiber/native/package.json",
    "content": "{\n  \"main\": \"dist/react-three-fiber-native.cjs.js\",\n  \"module\": \"dist/react-three-fiber-native.esm.js\",\n  \"types\": \"dist/react-three-fiber-native.cjs.d.ts\"\n}\n"
  },
  {
    "path": "packages/fiber/package.json",
    "content": "{\n  \"name\": \"@react-three/fiber\",\n  \"version\": \"9.5.0\",\n  \"description\": \"A React renderer for Threejs\",\n  \"keywords\": [\n    \"react\",\n    \"renderer\",\n    \"fiber\",\n    \"three\",\n    \"threejs\"\n  ],\n  \"author\": \"Paul Henschel (https://github.com/drcmda)\",\n  \"license\": \"MIT\",\n  \"maintainers\": [\n    \"Josh Ellis (https://github.com/joshuaellis)\",\n    \"Cody Bennett (https://github.com/codyjasonbennett)\",\n    \"Kris Baumgarter (https://github.com/krispya)\"\n  ],\n  \"bugs\": {\n    \"url\": \"https://github.com/pmndrs/react-three-fiber/issues\"\n  },\n  \"homepage\": \"https://github.com/pmndrs/react-three-fiber#readme\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/pmndrs/react-three-fiber.git\"\n  },\n  \"collective\": {\n    \"type\": \"opencollective\",\n    \"url\": \"https://opencollective.com/react-three-fiber\"\n  },\n  \"main\": \"dist/react-three-fiber.cjs.js\",\n  \"module\": \"dist/react-three-fiber.esm.js\",\n  \"types\": \"dist/react-three-fiber.cjs.d.ts\",\n  \"react-native\": \"native/dist/react-three-fiber-native.cjs.js\",\n  \"sideEffects\": false,\n  \"preconstruct\": {\n    \"entrypoints\": [\n      \"index.tsx\",\n      \"native.tsx\"\n    ]\n  },\n  \"scripts\": {\n    \"prebuild\": \"cp ../../readme.md readme.md\"\n  },\n  \"devDependencies\": {\n    \"@types/react-reconciler\": \"^0.32.3\",\n    \"react-reconciler\": \"^0.33.0\"\n  },\n  \"dependencies\": {\n    \"@babel/runtime\": \"^7.17.8\",\n    \"@types/webxr\": \"*\",\n    \"base64-js\": \"^1.5.1\",\n    \"buffer\": \"^6.0.3\",\n    \"its-fine\": \"^2.0.0\",\n    \"react-use-measure\": \"^2.1.7\",\n    \"scheduler\": \"^0.27.0\",\n    \"suspend-react\": \"^0.1.3\",\n    \"use-sync-external-store\": \"^1.4.0\",\n    \"zustand\": \"^5.0.3\"\n  },\n  \"peerDependencies\": {\n    \"expo\": \">=43.0\",\n    \"expo-asset\": \">=8.4\",\n    \"expo-file-system\": \">=11.0\",\n    \"expo-gl\": \">=11.0\",\n    \"react\": \">=19 <19.3\",\n    \"react-dom\": \">=19 <19.3\",\n    \"react-native\": \">=0.78\",\n    \"three\": \">=0.156\"\n  },\n  \"peerDependenciesMeta\": {\n    \"react-dom\": {\n      \"optional\": true\n    },\n    \"react-native\": {\n      \"optional\": true\n    },\n    \"expo\": {\n      \"optional\": true\n    },\n    \"expo-asset\": {\n      \"optional\": true\n    },\n    \"expo-file-system\": {\n      \"optional\": true\n    },\n    \"expo-gl\": {\n      \"optional\": true\n    }\n  }\n}\n"
  },
  {
    "path": "packages/fiber/readme.md",
    "content": "<h1>react-three-fiber</h1>\n\n[![Version](https://img.shields.io/npm/v/@react-three/fiber?style=flat&colorA=000000&colorB=000000)](https://npmjs.com/package/@react-three/fiber)\n[![Downloads](https://img.shields.io/npm/dt/react-three-fiber.svg?style=flat&colorA=000000&colorB=000000)](https://npmjs.com/package/@react-three/fiber)\n[![Twitter](https://img.shields.io/twitter/follow/pmndrs?label=%40pmndrs&style=flat&colorA=000000&colorB=000000&logo=twitter&logoColor=000000)](https://twitter.com/pmndrs)\n[![Discord](https://img.shields.io/discord/740090768164651008?style=flat&colorA=000000&colorB=000000&label=discord&logo=discord&logoColor=000000)](https://discord.gg/ZZjjNvJ)\n[![Open Collective](https://img.shields.io/opencollective/all/react-three-fiber?style=flat&colorA=000000&colorB=000000)](https://opencollective.com/react-three-fiber)\n[![ETH](https://img.shields.io/badge/ETH-f5f5f5?style=flat&colorA=000000&colorB=000000)](https://blockchain.com/eth/address/0x6E3f79Ea1d0dcedeb33D3fC6c34d2B1f156F2682)\n[![BTC](https://img.shields.io/badge/BTC-f5f5f5?style=flat&colorA=000000&colorB=000000)](https://blockchain.com/btc/address/36fuguTPxGCNnYZSRdgdh6Ea94brCAjMbH)\n\nreact-three-fiber is a <a href=\"https://reactjs.org/docs/codebase-overview.html#renderers\">React renderer</a> for threejs.\n\nBuild your scene declaratively with re-usable, self-contained components that react to state, are readily interactive and can participate in React's ecosystem.\n\n```bash\nnpm install three @react-three/fiber\n```\n\n#### Does it have limitations?\n\nNone. Everything that works in Threejs will work here without exception.\n\n#### Is it slower than plain Threejs?\n\nNo. There is no overhead. Components render outside of React. It outperforms Threejs in scale due to React's scheduling abilities.\n\n#### Can it keep up with frequent feature updates to Threejs?\n\nYes. It merely expresses Threejs in JSX: `<mesh />` becomes `new THREE.Mesh()`, and that happens dynamically. If a new Threejs version adds, removes or changes features, it will be available to you instantly without depending on updates to this library.\n\n### What does it look like?\n\n<table>\n  <tbody>\n    <tr>\n      <td>Let's make a re-usable component that has its own state, reacts to user-input and participates in the render-loop. (<a href=\"https://codesandbox.io/s/rrppl0y8l4?file=/src/App.js\">live demo</a>).</td>\n      <td>\n        <a href=\"https://codesandbox.io/s/rrppl0y8l4\">\n          <img src=\"https://i.imgur.com/sS4ArrZ.gif\" />\n        </a>\n      </td>\n    </tr>\n  </tbody>\n</table>\n\n```jsx\nimport { createRoot } from 'react-dom/client'\nimport React, { useRef, useState } from 'react'\nimport { Canvas, useFrame } from '@react-three/fiber'\n\nfunction Box(props) {\n  // This reference gives us direct access to the THREE.Mesh object\n  const ref = useRef()\n  // Hold state for hovered and clicked events\n  const [hovered, hover] = useState(false)\n  const [clicked, click] = useState(false)\n  // Subscribe this component to the render-loop, rotate the mesh every frame\n  useFrame((state, delta) => (ref.current.rotation.x += delta))\n  // Return the view, these are regular Threejs elements expressed in JSX\n  return (\n    <mesh\n      {...props}\n      ref={ref}\n      scale={clicked ? 1.5 : 1}\n      onClick={(event) => click(!clicked)}\n      onPointerOver={(event) => hover(true)}\n      onPointerOut={(event) => hover(false)}>\n      <boxGeometry args={[1, 1, 1]} />\n      <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />\n    </mesh>\n  )\n}\n\ncreateRoot(document.getElementById('root')).render(\n  <Canvas>\n    <ambientLight />\n    <pointLight position={[10, 10, 10]} />\n    <Box position={[-1.2, 0, 0]} />\n    <Box position={[1.2, 0, 0]} />\n  </Canvas>,\n)\n```\n\n<details>\n  <summary>Show TypeScript example</summary>\n  \n```bash\nnpm install @types/three\n```\n\n```tsx\nimport * as THREE from 'three'\nimport { createRoot } from 'react-dom/client'\nimport React, { useRef, useState } from 'react'\nimport { Canvas, useFrame, ThreeElements } from '@react-three/fiber'\n\nfunction Box(props: ThreeElements['mesh']) {\n  const ref = useRef<THREE.Mesh>(null!)\n  const [hovered, hover] = useState(false)\n  const [clicked, click] = useState(false)\n  useFrame((state, delta) => (ref.current.rotation.x += delta))\n  return (\n    <mesh\n      {...props}\n      ref={ref}\n      scale={clicked ? 1.5 : 1}\n      onClick={(event) => click(!clicked)}\n      onPointerOver={(event) => hover(true)}\n      onPointerOut={(event) => hover(false)}>\n      <boxGeometry args={[1, 1, 1]} />\n      <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />\n    </mesh>\n  )\n}\n\ncreateRoot(document.getElementById('root') as HTMLElement).render(\n  <Canvas>\n    <ambientLight />\n    <pointLight position={[10, 10, 10]} />\n    <Box position={[-1.2, 0, 0]} />\n    <Box position={[1.2, 0, 0]} />\n  </Canvas>,\n)\n```\n\nLive demo: https://codesandbox.io/s/icy-tree-brnsm?file=/src/App.tsx\n\n</details>\n\n<details>\n  <summary>Show React Native example</summary>\n\nThis example relies on react 18 and uses `expo-cli`, but you can create a bare project with their template or with the `react-native` CLI.\n\n```bash\n# Install expo-cli, this will create our app\nnpm install expo-cli -g\n# Create app and cd into it\nexpo init my-app\ncd my-app\n# Install dependencies\nnpm install three @react-three/fiber react\n# Start\nexpo start\n```\n\nSome configuration may be required to tell the Metro bundler about your assets if you use `useLoader` or Drei abstractions like `useGLTF` and `useTexture`:\n\n```js\n// metro.config.js\nmodule.exports = {\n  resolver: {\n    sourceExts: ['js', 'jsx', 'json', 'ts', 'tsx', 'cjs'],\n    assetExts: ['glb', 'png', 'jpg'],\n  },\n}\n```\n\n```tsx\nimport React, { useRef, useState } from 'react'\nimport { Canvas, useFrame } from '@react-three/fiber/native'\nfunction Box(props) {\n  const mesh = useRef(null)\n  const [hovered, setHover] = useState(false)\n  const [active, setActive] = useState(false)\n  useFrame((state, delta) => (mesh.current.rotation.x += delta))\n  return (\n    <mesh\n      {...props}\n      ref={mesh}\n      scale={active ? 1.5 : 1}\n      onClick={(event) => setActive(!active)}\n      onPointerOver={(event) => setHover(true)}\n      onPointerOut={(event) => setHover(false)}>\n      <boxGeometry args={[1, 1, 1]} />\n      <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />\n    </mesh>\n  )\n}\nexport default function App() {\n  return (\n    <Canvas>\n      <ambientLight />\n      <pointLight position={[10, 10, 10]} />\n      <Box position={[-1.2, 0, 0]} />\n      <Box position={[1.2, 0, 0]} />\n    </Canvas>\n  )\n}\n```\n\n</details>\n\n---\n\n# Documentation, tutorials, examples\n\nVisit [docs.pmnd.rs](https://docs.pmnd.rs/react-three-fiber)\n\n<a href=\"https://docs.pmnd.rs/react-three-fiber\"><img src=\"/docs/preview.jpg\"></a>\n\n# Fundamentals\n\nYou need to be versed in both React and Threejs before rushing into this. If you are unsure about React consult the official [React docs](https://react.dev/learn), especially [the section about hooks](https://react.dev/reference/react). As for Threejs, make sure you at least glance over the following links:\n\n1. Make sure you have a [basic grasp of Threejs](https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene). Keep that site open.\n2. When you know what a scene is, a camera, mesh, geometry, material, fork the [demo above](https://github.com/pmndrs/react-three-fiber#what-does-it-look-like).\n3. [Look up](https://threejs.org/docs/index.html#api/en/objects/Mesh) the JSX elements that you see (mesh, ambientLight, etc), _all_ threejs exports are native to three-fiber.\n4. Try changing some values, scroll through our [API](https://docs.pmnd.rs/react-three-fiber/API) to see what the various settings and hooks do.\n\nSome reading material:\n\n- [Threejs-docs](https://threejs.org/docs)\n- [Threejs-examples](https://threejs.org/examples)\n- [Threejs-fundamentals](https://threejs.org/manual/#en/fundamentals)\n- [three.js-journey](https://threejs-journey.com)\n- [Discover Threejs](https://discoverthreejs.com)\n- [Do's and don'ts](https://discoverthreejs.com/tips-and-tricks) for performance and best practices\n- [react-three-fiber alligator.io tutorial](https://alligator.io/react/react-with-threejs) by [@dghez\\_](https://twitter.com/dghez_)\n\n# Ecosystem\n\n- [`@react-three/gltfjsx`](https://github.com/pmndrs/gltfjsx) &ndash; turns GLTFs into JSX components\n- [`@react-three/drei`](https://github.com/pmndrs/drei) &ndash; useful helpers for react-three-fiber\n- [`@react-three/postprocessing`](https://github.com/pmndrs/react-postprocessing) &ndash; post-processing effects\n- [`@react-three/flex`](https://github.com/pmndrs/react-three-flex) &ndash; flexbox for react-three-fiber\n- [`@react-three/xr`](https://github.com/pmndrs/react-xr) &ndash; VR/AR controllers and events\n- [`@react-three/cannon`](https://github.com/pmndrs/use-cannon) &ndash; physics based hooks\n- [`@react-three/a11y`](https://github.com/pmndrs/react-three-a11y) &ndash; real a11y for your scene\n- [`zustand`](https://github.com/pmndrs/zustand) &ndash; state management\n- [`react-spring`](https://github.com/pmndrs/react-spring) &ndash; a spring-physics-based animation library\n- [`react-use-gesture`](https://github.com/pmndrs/react-use-gesture) &ndash; mouse/touch gestures\n- [`leva`](https://github.com/pmndrs/leva) &ndash; create GUI controls in seconds\n\n# How to contribute\n\nIf you like this project, please consider helping out. All contributions are welcome as well as donations to [Opencollective](https://opencollective.com/react-three-fiber), or in crypto `BTC: 36fuguTPxGCNnYZSRdgdh6Ea94brCAjMbH`, `ETH: 0x6E3f79Ea1d0dcedeb33D3fC6c34d2B1f156F2682`.\n\n#### Backers\n\nThank you to all our backers! 🙏\n\n<a href=\"https://opencollective.com/react-three-fiber#backers\" target=\"_blank\">\n  <img src=\"https://opencollective.com/react-three-fiber/backers.svg?width=890\"/>\n</a>\n\n#### Contributors\n\nThis project exists thanks to all the people who contribute.\n\n<a href=\"https://github.com/pmndrs/react-three-fiber/graphs/contributors\">\n  <img src=\"https://opencollective.com/react-three-fiber/contributors.svg?width=890\" />\n</a>\n"
  },
  {
    "path": "packages/fiber/src/core/events.ts",
    "content": "import * as THREE from 'three'\nimport { type Properties, getRootState } from './utils'\nimport type { Instance } from './reconciler'\nimport type { RootState, RootStore } from './store'\n\nexport interface Intersection extends THREE.Intersection {\n  /** The event source (the object which registered the handler) */\n  eventObject: THREE.Object3D\n}\n\nexport interface IntersectionEvent<TSourceEvent> extends Intersection {\n  /** The event source (the object which registered the handler) */\n  eventObject: THREE.Object3D\n  /** An array of intersections */\n  intersections: Intersection[]\n  /** vec3.set(pointer.x, pointer.y, 0).unproject(camera) */\n  unprojectedPoint: THREE.Vector3\n  /** Normalized event coordinates */\n  pointer: THREE.Vector2\n  /** Delta between first click and this event */\n  delta: number\n  /** The ray that pierced it */\n  ray: THREE.Ray\n  /** The camera that was used by the raycaster */\n  camera: Camera\n  /** stopPropagation will stop underlying handlers from firing */\n  stopPropagation: () => void\n  /** The original host event */\n  nativeEvent: TSourceEvent\n  /** If the event was stopped by calling stopPropagation */\n  stopped: boolean\n}\n\nexport type Camera = THREE.OrthographicCamera | THREE.PerspectiveCamera\nexport type ThreeEvent<TEvent> = IntersectionEvent<TEvent> & Properties<TEvent>\nexport type DomEvent = PointerEvent | MouseEvent | WheelEvent\n\nexport interface Events {\n  onClick: EventListener\n  onContextMenu: EventListener\n  onDoubleClick: EventListener\n  onWheel: EventListener\n  onPointerDown: EventListener\n  onPointerUp: EventListener\n  onPointerLeave: EventListener\n  onPointerMove: EventListener\n  onPointerCancel: EventListener\n  onLostPointerCapture: EventListener\n}\n\nexport interface EventHandlers {\n  onClick?: (event: ThreeEvent<MouseEvent>) => void\n  onContextMenu?: (event: ThreeEvent<MouseEvent>) => void\n  onDoubleClick?: (event: ThreeEvent<MouseEvent>) => void\n  onPointerUp?: (event: ThreeEvent<PointerEvent>) => void\n  onPointerDown?: (event: ThreeEvent<PointerEvent>) => void\n  onPointerOver?: (event: ThreeEvent<PointerEvent>) => void\n  onPointerOut?: (event: ThreeEvent<PointerEvent>) => void\n  onPointerEnter?: (event: ThreeEvent<PointerEvent>) => void\n  onPointerLeave?: (event: ThreeEvent<PointerEvent>) => void\n  onPointerMove?: (event: ThreeEvent<PointerEvent>) => void\n  onPointerMissed?: (event: MouseEvent) => void\n  onPointerCancel?: (event: ThreeEvent<PointerEvent>) => void\n  onWheel?: (event: ThreeEvent<WheelEvent>) => void\n  onLostPointerCapture?: (event: ThreeEvent<PointerEvent>) => void\n}\n\nexport type FilterFunction = (items: THREE.Intersection[], state: RootState) => THREE.Intersection[]\nexport type ComputeFunction = (event: DomEvent, root: RootState, previous?: RootState) => void\n\nexport interface EventManager<TTarget> {\n  /** Determines if the event layer is active */\n  enabled: boolean\n  /** Event layer priority, higher prioritized layers come first and may stop(-propagate) lower layer  */\n  priority: number\n  /** The compute function needs to set up the raycaster and an xy- pointer  */\n  compute?: ComputeFunction\n  /** The filter can re-order or re-structure the intersections  */\n  filter?: FilterFunction\n  /** The target node the event layer is tied to */\n  connected?: TTarget\n  /** All the pointer event handlers through which the host forwards native events */\n  handlers?: Events\n  /** Allows re-connecting to another target */\n  connect?: (target: TTarget) => void\n  /** Removes all existing events handlers from the target */\n  disconnect?: () => void\n  /** Triggers a onPointerMove with the last known event. This can be useful to enable raycasting without\n   *  explicit user interaction, for instance when the camera moves a hoverable object underneath the cursor.\n   */\n  update?: () => void\n}\n\nexport interface PointerCaptureTarget {\n  intersection: Intersection\n  target: Element\n}\n\nfunction makeId(event: Intersection) {\n  return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId\n}\n\n/**\n * Release pointer captures.\n * This is called by releasePointerCapture in the API, and when an object is removed.\n */\nfunction releaseInternalPointerCapture(\n  capturedMap: Map<number, Map<THREE.Object3D, PointerCaptureTarget>>,\n  obj: THREE.Object3D,\n  captures: Map<THREE.Object3D, PointerCaptureTarget>,\n  pointerId: number,\n): void {\n  const captureData: PointerCaptureTarget | undefined = captures.get(obj)\n  if (captureData) {\n    captures.delete(obj)\n    // If this was the last capturing object for this pointer\n    if (captures.size === 0) {\n      capturedMap.delete(pointerId)\n      captureData.target.releasePointerCapture(pointerId)\n    }\n  }\n}\n\nexport function removeInteractivity(store: RootStore, object: THREE.Object3D) {\n  const { internal } = store.getState()\n  // Removes every trace of an object from the data store\n  internal.interaction = internal.interaction.filter((o) => o !== object)\n  internal.initialHits = internal.initialHits.filter((o) => o !== object)\n  internal.hovered.forEach((value, key) => {\n    if (value.eventObject === object || value.object === object) {\n      // Clear out intersects, they are outdated by now\n      internal.hovered.delete(key)\n    }\n  })\n  internal.capturedMap.forEach((captures, pointerId) => {\n    releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId)\n  })\n}\n\nexport function createEvents(store: RootStore) {\n  /** Calculates delta */\n  function calculateDistance(event: DomEvent) {\n    const { internal } = store.getState()\n    const dx = event.offsetX - internal.initialClick[0]\n    const dy = event.offsetY - internal.initialClick[1]\n    return Math.round(Math.sqrt(dx * dx + dy * dy))\n  }\n\n  /** Returns true if an instance has a valid pointer-event registered, this excludes scroll, clicks etc */\n  function filterPointerEvents(objects: THREE.Object3D[]) {\n    return objects.filter((obj) =>\n      ['Move', 'Over', 'Enter', 'Out', 'Leave'].some(\n        (name) =>\n          (obj as Instance<THREE.Object3D>['object']).__r3f?.handlers[('onPointer' + name) as keyof EventHandlers],\n      ),\n    )\n  }\n\n  function intersect(event: DomEvent, filter?: (objects: THREE.Object3D[]) => THREE.Object3D[]) {\n    const state = store.getState()\n    const duplicates = new Set<string>()\n    const intersections: Intersection[] = []\n    // Allow callers to eliminate event objects\n    const eventsObjects = filter ? filter(state.internal.interaction) : state.internal.interaction\n    // Reset all raycaster cameras to undefined\n    for (let i = 0; i < eventsObjects.length; i++) {\n      const state = getRootState(eventsObjects[i])\n      if (state) {\n        state.raycaster.camera = undefined!\n      }\n    }\n\n    if (!state.previousRoot) {\n      // Make sure root-level pointer and ray are set up\n      state.events.compute?.(event, state)\n    }\n\n    function handleRaycast(obj: THREE.Object3D) {\n      const state = getRootState(obj)\n      // Skip event handling when noEvents is set, or when the raycasters camera is null\n      if (!state || !state.events.enabled || state.raycaster.camera === null) return []\n\n      // When the camera is undefined we have to call the event layers update function\n      if (state.raycaster.camera === undefined) {\n        state.events.compute?.(event, state, state.previousRoot?.getState())\n        // If the camera is still undefined we have to skip this layer entirely\n        if (state.raycaster.camera === undefined) state.raycaster.camera = null!\n      }\n\n      // Intersect object by object\n      return state.raycaster.camera ? state.raycaster.intersectObject(obj, true) : []\n    }\n\n    // Collect events\n    let hits: THREE.Intersection<THREE.Object3D>[] = eventsObjects\n      // Intersect objects\n      .flatMap(handleRaycast)\n      // Sort by event priority and distance\n      .sort((a, b) => {\n        const aState = getRootState(a.object)\n        const bState = getRootState(b.object)\n        if (!aState || !bState) return a.distance - b.distance\n        return bState.events.priority - aState.events.priority || a.distance - b.distance\n      })\n      // Filter out duplicates\n      .filter((item) => {\n        const id = makeId(item as Intersection)\n        if (duplicates.has(id)) return false\n        duplicates.add(id)\n        return true\n      })\n\n    // https://github.com/mrdoob/three.js/issues/16031\n    // Allow custom userland intersect sort order, this likely only makes sense on the root filter\n    if (state.events.filter) hits = state.events.filter(hits, state)\n\n    // Bubble up the events, find the event source (eventObject)\n    for (const hit of hits) {\n      let eventObject: THREE.Object3D | null = hit.object\n      // Bubble event up\n      while (eventObject) {\n        if ((eventObject as Instance<THREE.Object3D>['object']).__r3f?.eventCount)\n          intersections.push({ ...hit, eventObject })\n        eventObject = eventObject.parent\n      }\n    }\n\n    // If the interaction is captured, make all capturing targets part of the intersect.\n    if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {\n      for (let captureData of state.internal.capturedMap.get(event.pointerId)!.values()) {\n        if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection)\n      }\n    }\n    return intersections\n  }\n\n  /**  Handles intersections by forwarding them to handlers */\n  function handleIntersects(\n    intersections: Intersection[],\n    event: DomEvent,\n    delta: number,\n    callback: (event: ThreeEvent<DomEvent>) => void,\n  ) {\n    // If anything has been found, forward it to the event listeners\n    if (intersections.length) {\n      const localState = { stopped: false }\n      for (const hit of intersections) {\n        let state = getRootState(hit.object)\n\n        // If the object is not managed by R3F, it might be parented to an element which is.\n        // Traverse upwards until we find a managed parent and use its state instead.\n        if (!state) {\n          hit.object.traverseAncestors((obj) => {\n            const parentState = getRootState(obj)\n            if (parentState) {\n              state = parentState\n              return false\n            }\n          })\n        }\n\n        if (state) {\n          const { raycaster, pointer, camera, internal } = state\n          const unprojectedPoint = new THREE.Vector3(pointer.x, pointer.y, 0).unproject(camera)\n\n          const hasPointerCapture = (id: number) => internal.capturedMap.get(id)?.has(hit.eventObject) ?? false\n\n          const setPointerCapture = (id: number) => {\n            const captureData = { intersection: hit, target: event.target as Element }\n            if (internal.capturedMap.has(id)) {\n              // if the pointerId was previously captured, we add the hit to the\n              // event capturedMap.\n              internal.capturedMap.get(id)!.set(hit.eventObject, captureData)\n            } else {\n              // if the pointerId was not previously captured, we create a map\n              // containing the hitObject, and the hit. hitObject is used for\n              // faster access.\n              internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]))\n            }\n            // Call the original event now\n            ;(event.target as Element).setPointerCapture(id)\n          }\n\n          const releasePointerCapture = (id: number) => {\n            const captures = internal.capturedMap.get(id)\n            if (captures) {\n              releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id)\n            }\n          }\n\n          // Add native event props\n          let extractEventProps: any = {}\n          // This iterates over the event's properties including the inherited ones. Native PointerEvents have most of their props as getters which are inherited, but polyfilled PointerEvents have them all as their own properties (i.e. not inherited). We can't use Object.keys() or Object.entries() as they only return \"own\" properties; nor Object.getPrototypeOf(event) as that *doesn't* return \"own\" properties, only inherited ones.\n          for (let prop in event) {\n            let property = event[prop as keyof DomEvent]\n            // Only copy over atomics, leave functions alone as these should be\n            // called as event.nativeEvent.fn()\n            if (typeof property !== 'function') extractEventProps[prop] = property\n          }\n\n          let raycastEvent: ThreeEvent<DomEvent> = {\n            ...hit,\n            ...extractEventProps,\n            pointer,\n            intersections,\n            stopped: localState.stopped,\n            delta,\n            unprojectedPoint,\n            ray: raycaster.ray,\n            camera: camera,\n            // Hijack stopPropagation, which just sets a flag\n            stopPropagation() {\n              // https://github.com/pmndrs/react-three-fiber/issues/596\n              // Events are not allowed to stop propagation if the pointer has been captured\n              const capturesForPointer = 'pointerId' in event && internal.capturedMap.get(event.pointerId)\n\n              // We only authorize stopPropagation...\n              if (\n                // ...if this pointer hasn't been captured\n                !capturesForPointer ||\n                // ... or if the hit object is capturing the pointer\n                capturesForPointer.has(hit.eventObject)\n              ) {\n                raycastEvent.stopped = localState.stopped = true\n                // Propagation is stopped, remove all other hover records\n                // An event handler is only allowed to flush other handlers if it is hovered itself\n                if (\n                  internal.hovered.size &&\n                  Array.from(internal.hovered.values()).find((i) => i.eventObject === hit.eventObject)\n                ) {\n                  // Objects cannot flush out higher up objects that have already caught the event\n                  const higher = intersections.slice(0, intersections.indexOf(hit))\n                  cancelPointer([...higher, hit])\n                }\n              }\n            },\n            // there should be a distinction between target and currentTarget\n            target: { hasPointerCapture, setPointerCapture, releasePointerCapture },\n            currentTarget: { hasPointerCapture, setPointerCapture, releasePointerCapture },\n            nativeEvent: event,\n          }\n\n          // Call subscribers\n          callback(raycastEvent)\n          // Event bubbling may be interrupted by stopPropagation\n          if (localState.stopped === true) break\n        }\n      }\n    }\n    return intersections\n  }\n\n  function cancelPointer(intersections: Intersection[]) {\n    const { internal } = store.getState()\n    for (const hoveredObj of internal.hovered.values()) {\n      // When no objects were hit or the the hovered object wasn't found underneath the cursor\n      // we call onPointerOut and delete the object from the hovered-elements map\n      if (\n        !intersections.length ||\n        !intersections.find(\n          (hit) =>\n            hit.object === hoveredObj.object &&\n            hit.index === hoveredObj.index &&\n            hit.instanceId === hoveredObj.instanceId,\n        )\n      ) {\n        const eventObject = hoveredObj.eventObject\n        const instance = (eventObject as Instance<THREE.Object3D>['object']).__r3f\n        internal.hovered.delete(makeId(hoveredObj))\n        if (instance?.eventCount) {\n          const handlers = instance.handlers\n          // Clear out intersects, they are outdated by now\n          const data = { ...hoveredObj, intersections }\n          handlers.onPointerOut?.(data as ThreeEvent<PointerEvent>)\n          handlers.onPointerLeave?.(data as ThreeEvent<PointerEvent>)\n        }\n      }\n    }\n  }\n\n  function pointerMissed(event: MouseEvent, objects: THREE.Object3D[]) {\n    for (let i = 0; i < objects.length; i++) {\n      const instance = (objects[i] as Instance<THREE.Object3D>['object']).__r3f\n      instance?.handlers.onPointerMissed?.(event)\n    }\n  }\n\n  function handlePointer(name: string) {\n    // Deal with cancelation\n    switch (name) {\n      case 'onPointerLeave':\n      case 'onPointerCancel':\n        return () => cancelPointer([])\n      case 'onLostPointerCapture':\n        return (event: DomEvent) => {\n          const { internal } = store.getState()\n          if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {\n            // If the object event interface had onLostPointerCapture, we'd call it here on every\n            // object that's getting removed. We call it on the next frame because onLostPointerCapture\n            // fires before onPointerUp. Otherwise pointerUp would never be called if the event didn't\n            // happen in the object it originated from, leaving components in a in-between state.\n            requestAnimationFrame(() => {\n              // Only release if pointer-up didn't do it already\n              if (internal.capturedMap.has(event.pointerId)) {\n                internal.capturedMap.delete(event.pointerId)\n                cancelPointer([])\n              }\n            })\n          }\n        }\n    }\n\n    // Any other pointer goes here ...\n    return function handleEvent(event: DomEvent) {\n      const { onPointerMissed, internal } = store.getState()\n\n      // prepareRay(event)\n      internal.lastEvent.current = event\n\n      // Get fresh intersects\n      const isPointerMove = name === 'onPointerMove'\n      const isClickEvent = name === 'onClick' || name === 'onContextMenu' || name === 'onDoubleClick'\n      const filter = isPointerMove ? filterPointerEvents : undefined\n\n      const hits = intersect(event, filter)\n      const delta = isClickEvent ? calculateDistance(event) : 0\n\n      // Save initial coordinates on pointer-down\n      if (name === 'onPointerDown') {\n        internal.initialClick = [event.offsetX, event.offsetY]\n        internal.initialHits = hits.map((hit) => hit.eventObject)\n      }\n\n      // If a click yields no results, pass it back to the user as a miss\n      // Missed events have to come first in order to establish user-land side-effect clean up\n      if (isClickEvent && !hits.length) {\n        if (delta <= 2) {\n          pointerMissed(event, internal.interaction)\n          if (onPointerMissed) onPointerMissed(event)\n        }\n      }\n      // Take care of unhover\n      if (isPointerMove) cancelPointer(hits)\n\n      function onIntersect(data: ThreeEvent<DomEvent>) {\n        const eventObject = data.eventObject\n        const instance = (eventObject as Instance<THREE.Object3D>['object']).__r3f\n\n        // Check presence of handlers\n        if (!instance?.eventCount) return\n        const handlers = instance.handlers\n\n        /*\n        MAYBE TODO, DELETE IF NOT: \n          Check if the object is captured, captured events should not have intersects running in parallel\n          But wouldn't it be better to just replace capturedMap with a single entry?\n          Also, are we OK with straight up making picking up multiple objects impossible?\n          \n        const pointerId = (data as ThreeEvent<PointerEvent>).pointerId        \n        if (pointerId !== undefined) {\n          const capturedMeshSet = internal.capturedMap.get(pointerId)\n          if (capturedMeshSet) {\n            const captured = capturedMeshSet.get(eventObject)\n            if (captured && captured.localState.stopped) return\n          }\n        }*/\n\n        if (isPointerMove) {\n          // Move event ...\n          if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {\n            // When enter or out is present take care of hover-state\n            const id = makeId(data)\n            const hoveredItem = internal.hovered.get(id)\n            if (!hoveredItem) {\n              // If the object wasn't previously hovered, book it and call its handler\n              internal.hovered.set(id, data)\n              handlers.onPointerOver?.(data as ThreeEvent<PointerEvent>)\n              handlers.onPointerEnter?.(data as ThreeEvent<PointerEvent>)\n            } else if (hoveredItem.stopped) {\n              // If the object was previously hovered and stopped, we shouldn't allow other items to proceed\n              data.stopPropagation()\n            }\n          }\n          // Call mouse move\n          handlers.onPointerMove?.(data as ThreeEvent<PointerEvent>)\n        } else {\n          // All other events ...\n          const handler = handlers[name as keyof EventHandlers] as (event: ThreeEvent<PointerEvent>) => void\n          if (handler) {\n            // Forward all events back to their respective handlers with the exception of click events,\n            // which must use the initial target\n            if (!isClickEvent || internal.initialHits.includes(eventObject)) {\n              // Missed events have to come first\n              pointerMissed(\n                event,\n                internal.interaction.filter((object) => !internal.initialHits.includes(object)),\n              )\n              // Now call the handler\n              handler(data as ThreeEvent<PointerEvent>)\n            }\n          } else {\n            // Trigger onPointerMissed on all elements that have pointer over/out handlers, but not click and weren't hit\n            if (isClickEvent && internal.initialHits.includes(eventObject)) {\n              pointerMissed(\n                event,\n                internal.interaction.filter((object) => !internal.initialHits.includes(object)),\n              )\n            }\n          }\n        }\n      }\n\n      handleIntersects(hits, event, delta, onIntersect)\n    }\n  }\n\n  return { handlePointer }\n}\n"
  },
  {
    "path": "packages/fiber/src/core/hooks.tsx",
    "content": "import * as THREE from 'three'\nimport * as React from 'react'\nimport { suspend, preload, clear } from 'suspend-react'\nimport { context, RootState, RenderCallback, RootStore } from './store'\nimport { buildGraph, ObjectMap, is, useMutableCallback, useIsomorphicLayoutEffect, isObject3D } from './utils'\nimport type { Instance, ConstructorRepresentation } from './reconciler'\n\n/**\n * Exposes an object's {@link Instance}.\n * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#useInstanceHandle\n *\n * **Note**: this is an escape hatch to react-internal fields. Expect this to change significantly between versions.\n */\nexport function useInstanceHandle<T>(ref: React.RefObject<T>): React.RefObject<Instance<T>> {\n  const instance = React.useRef<Instance>(null!)\n  React.useImperativeHandle(instance, () => (ref.current as unknown as Instance<T>['object']).__r3f!, [ref])\n  return instance\n}\n\n/**\n * Returns the R3F Canvas' Zustand store. Useful for [transient updates](https://github.com/pmndrs/zustand#transient-updates-for-often-occurring-state-changes).\n * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usestore\n */\nexport function useStore(): RootStore {\n  const store = React.useContext(context)\n  if (!store) throw new Error('R3F: Hooks can only be used within the Canvas component!')\n  return store\n}\n\n/**\n * Accesses R3F's internal state, containing renderer, canvas, scene, etc.\n * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usethree\n */\nexport function useThree<T = RootState>(\n  selector: (state: RootState) => T = (state) => state as unknown as T,\n  equalityFn?: <T>(state: T, newState: T) => boolean,\n): T {\n  return useStore()(selector, equalityFn)\n}\n\n/**\n * Executes a callback before render in a shared frame loop.\n * Can order effects with render priority or manually render with a positive priority.\n * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe\n */\nexport function useFrame(callback: RenderCallback, renderPriority: number = 0): null {\n  const store = useStore()\n  const subscribe = store.getState().internal.subscribe\n  // Memoize ref\n  const ref = useMutableCallback(callback)\n  // Subscribe on mount, unsubscribe on unmount\n  useIsomorphicLayoutEffect(() => subscribe(ref, renderPriority, store), [renderPriority, subscribe, store])\n  return null\n}\n\n/**\n * Returns a node graph of an object with named nodes & materials.\n * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usegraph\n */\nexport function useGraph(object: THREE.Object3D): ObjectMap {\n  return React.useMemo(() => buildGraph(object), [object])\n}\n\ntype InputLike = string | string[] | string[][] | Readonly<string | string[] | string[][]>\ntype LoaderLike = THREE.Loader<any, InputLike>\ntype GLTFLike = { scene: THREE.Object3D }\n\ntype LoaderInstance<T extends LoaderLike | ConstructorRepresentation<LoaderLike>> =\n  T extends ConstructorRepresentation<LoaderLike> ? InstanceType<T> : T\n\nexport type LoaderResult<T extends LoaderLike | ConstructorRepresentation<LoaderLike>> = Awaited<\n  ReturnType<LoaderInstance<T>['loadAsync']>\n> extends infer R\n  ? R extends GLTFLike\n    ? R & ObjectMap\n    : R\n  : never\n\nexport type Extensions<T extends LoaderLike | ConstructorRepresentation<LoaderLike>> = (\n  loader: LoaderInstance<T>,\n) => void\n\nconst memoizedLoaders = new WeakMap<ConstructorRepresentation<LoaderLike>, LoaderLike>()\n\nconst isConstructor = <T,>(\n  value: unknown,\n): value is ConstructorRepresentation<THREE.Loader<T, string | string[] | string[][]>> =>\n  typeof value === 'function' && value?.prototype?.constructor === value\n\nfunction loadingFn<L extends LoaderLike | ConstructorRepresentation<THREE.Loader<any>>>(\n  extensions?: Extensions<L>,\n  onProgress?: (event: ProgressEvent<EventTarget>) => void,\n) {\n  return function (Proto: L, ...input: string[]) {\n    let loader: LoaderLike\n\n    // Construct and cache loader if constructor was passed\n    if (isConstructor(Proto)) {\n      loader = memoizedLoaders.get(Proto)!\n      if (!loader) {\n        loader = new Proto()\n        memoizedLoaders.set(Proto, loader)\n      }\n    } else {\n      loader = Proto as any\n    }\n\n    // Apply loader extensions\n    if (extensions) extensions(loader as any)\n\n    // Go through the urls and load them\n    return Promise.all(\n      input.map(\n        (input) =>\n          new Promise<LoaderResult<L>>((res, reject) =>\n            loader.load(\n              input,\n              (data) => {\n                if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene))\n                res(data)\n              },\n              onProgress,\n              (error) => reject(new Error(`Could not load ${input}: ${(error as ErrorEvent)?.message}`)),\n            ),\n          ),\n      ),\n    )\n  }\n}\n\n/**\n * Synchronously loads and caches assets with a three loader.\n *\n * Note: this hook's caller must be wrapped with `React.Suspense`\n * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useloader\n */\nexport function useLoader<I extends InputLike, L extends LoaderLike | ConstructorRepresentation<LoaderLike>>(\n  loader: L,\n  input: I,\n  extensions?: Extensions<L>,\n  onProgress?: (event: ProgressEvent<EventTarget>) => void,\n) {\n  // Use suspense to load async assets\n  const keys = (Array.isArray(input) ? input : [input]) as string[]\n  const results = suspend(loadingFn(extensions, onProgress), [loader, ...keys], { equal: is.equ })\n  // Return the object(s)\n  return (Array.isArray(input) ? results : results[0]) as I extends any[] ? LoaderResult<L>[] : LoaderResult<L>\n}\n\n/**\n * Preloads an asset into cache as a side-effect.\n */\nuseLoader.preload = function <I extends InputLike, L extends LoaderLike | ConstructorRepresentation<LoaderLike>>(\n  loader: L,\n  input: I,\n  extensions?: Extensions<L>,\n): void {\n  const keys = (Array.isArray(input) ? input : [input]) as string[]\n  return preload(loadingFn(extensions), [loader, ...keys])\n}\n\n/**\n * Removes a loaded asset from cache.\n */\nuseLoader.clear = function <I extends InputLike, L extends LoaderLike | ConstructorRepresentation<LoaderLike>>(\n  loader: L,\n  input: I,\n): void {\n  const keys = (Array.isArray(input) ? input : [input]) as string[]\n  return clear([loader, ...keys])\n}\n"
  },
  {
    "path": "packages/fiber/src/core/index.tsx",
    "content": "export type {\n  Intersection,\n  ThreeEvent,\n  DomEvent,\n  Events,\n  EventHandlers,\n  FilterFunction,\n  ComputeFunction,\n  EventManager,\n} from './events'\nexport { createEvents } from './events'\nexport * from './hooks'\nexport type { GlobalRenderCallback, GlobalEffectType } from './loop'\nexport { flushGlobalEffects, addEffect, addAfterEffect, addTail, invalidate, advance } from './loop'\nexport type {\n  AttachFnType,\n  AttachType,\n  ConstructorRepresentation,\n  Catalogue,\n  Args,\n  InstanceProps,\n  Instance,\n} from './reconciler'\nexport { extend, reconciler } from './reconciler'\nexport type { ReconcilerRoot, GLProps, CameraProps, RenderProps, InjectState } from './renderer'\nexport { _roots, createRoot, unmountComponentAtNode, createPortal, flushSync } from './renderer'\nexport type {\n  Subscription,\n  Dpr,\n  Size,\n  Viewport,\n  RenderCallback,\n  Frameloop,\n  Performance,\n  Renderer,\n  XRManager,\n  RootState,\n  RootStore,\n} from './store'\nexport { context } from './store'\nexport type { ObjectMap, Camera, Disposable, Act } from './utils'\nexport { applyProps, getRootState, dispose, act, buildGraph } from './utils'\n"
  },
  {
    "path": "packages/fiber/src/core/loop.ts",
    "content": "import { _roots } from './renderer'\nimport type { RootState, Subscription } from './store'\n\nexport type GlobalRenderCallback = (timestamp: number) => void\ninterface SubItem {\n  callback: GlobalRenderCallback\n}\n\nfunction createSubs(callback: GlobalRenderCallback, subs: Set<SubItem>): () => void {\n  const sub = { callback }\n  subs.add(sub)\n  return () => void subs.delete(sub)\n}\n\nconst globalEffects = new Set<SubItem>()\nconst globalAfterEffects = new Set<SubItem>()\nconst globalTailEffects = new Set<SubItem>()\n\n/**\n * Adds a global render callback which is called each frame.\n * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addEffect\n */\nexport const addEffect = (callback: GlobalRenderCallback) => createSubs(callback, globalEffects)\n\n/**\n * Adds a global after-render callback which is called each frame.\n * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addAfterEffect\n */\nexport const addAfterEffect = (callback: GlobalRenderCallback) => createSubs(callback, globalAfterEffects)\n\n/**\n * Adds a global callback which is called when rendering stops.\n * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addTail\n */\nexport const addTail = (callback: GlobalRenderCallback) => createSubs(callback, globalTailEffects)\n\nfunction run(effects: Set<SubItem>, timestamp: number) {\n  if (!effects.size) return\n  for (const { callback } of effects.values()) {\n    callback(timestamp)\n  }\n}\n\nexport type GlobalEffectType = 'before' | 'after' | 'tail'\n\nexport function flushGlobalEffects(type: GlobalEffectType, timestamp: number): void {\n  switch (type) {\n    case 'before':\n      return run(globalEffects, timestamp)\n    case 'after':\n      return run(globalAfterEffects, timestamp)\n    case 'tail':\n      return run(globalTailEffects, timestamp)\n  }\n}\n\nlet subscribers: Subscription[]\nlet subscription: Subscription\n\nfunction update(timestamp: number, state: RootState, frame?: XRFrame) {\n  // Run local effects\n  let delta = state.clock.getDelta()\n\n  // In frameloop='never' mode, clock times are updated using the provided timestamp\n  if (state.frameloop === 'never' && typeof timestamp === 'number') {\n    delta = timestamp - state.clock.elapsedTime\n    state.clock.oldTime = state.clock.elapsedTime\n    state.clock.elapsedTime = timestamp\n  }\n\n  // Call subscribers (useFrame)\n  subscribers = state.internal.subscribers\n  for (let i = 0; i < subscribers.length; i++) {\n    subscription = subscribers[i]\n    subscription.ref.current(subscription.store.getState(), delta, frame)\n  }\n\n  // Render content\n  if (!state.internal.priority && state.gl.render) state.gl.render(state.scene, state.camera)\n\n  // Decrease frame count\n  state.internal.frames = Math.max(0, state.internal.frames - 1)\n  return state.frameloop === 'always' ? 1 : state.internal.frames\n}\n\nlet running = false\nlet useFrameInProgress = false\nlet repeat: number\nlet frame: number\nlet state: RootState\n\nexport function loop(timestamp: number): void {\n  frame = requestAnimationFrame(loop)\n  running = true\n  repeat = 0\n\n  // Run effects\n  flushGlobalEffects('before', timestamp)\n\n  // Render all roots\n  useFrameInProgress = true\n  for (const root of _roots.values()) {\n    state = root.store.getState()\n\n    // If the frameloop is invalidated, do not run another frame\n    if (\n      state.internal.active &&\n      (state.frameloop === 'always' || state.internal.frames > 0) &&\n      !state.gl.xr?.isPresenting\n    ) {\n      repeat += update(timestamp, state)\n    }\n  }\n  useFrameInProgress = false\n\n  // Run after-effects\n  flushGlobalEffects('after', timestamp)\n\n  // Stop the loop if nothing invalidates it\n  if (repeat === 0) {\n    // Tail call effects, they are called when rendering stops\n    flushGlobalEffects('tail', timestamp)\n\n    // Flag end of operation\n    running = false\n    return cancelAnimationFrame(frame)\n  }\n}\n\n/**\n * Invalidates the view, requesting a frame to be rendered. Will globally invalidate unless passed a root's state.\n * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#invalidate\n */\nexport function invalidate(state?: RootState, frames = 1): void {\n  if (!state) return _roots.forEach((root) => invalidate(root.store.getState(), frames))\n  if (state.gl.xr?.isPresenting || !state.internal.active || state.frameloop === 'never') return\n\n  if (frames > 1) {\n    // legacy support for people using frames parameters\n    // Increase frames, do not go higher than 60\n    state.internal.frames = Math.min(60, state.internal.frames + frames)\n  } else {\n    if (useFrameInProgress) {\n      //called from within a useFrame, it means the user wants an additional frame\n      state.internal.frames = 2\n    } else {\n      //the user need a new frame, no need to increment further than 1\n      state.internal.frames = 1\n    }\n  }\n\n  // If the render-loop isn't active, start it\n  if (!running) {\n    running = true\n    requestAnimationFrame(loop)\n  }\n}\n\n/**\n * Advances the frameloop and runs render effects, useful for when manually rendering via `frameloop=\"never\"`.\n * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#advance\n */\nexport function advance(timestamp: number, runGlobalEffects: boolean = true, state?: RootState, frame?: XRFrame): void {\n  if (runGlobalEffects) flushGlobalEffects('before', timestamp)\n  if (!state) for (const root of _roots.values()) update(timestamp, root.store.getState())\n  else update(timestamp, state, frame)\n  if (runGlobalEffects) flushGlobalEffects('after', timestamp)\n}\n"
  },
  {
    "path": "packages/fiber/src/core/reconciler.tsx",
    "content": "import packageData from '../../package.json'\nimport * as THREE from 'three'\nimport * as React from 'react'\nimport Reconciler from '../../react-reconciler/index.js'\nimport {\n  ContinuousEventPriority,\n  DiscreteEventPriority,\n  DefaultEventPriority,\n} from '../../react-reconciler/constants.js'\nimport { unstable_IdlePriority as idlePriority, unstable_scheduleCallback as scheduleCallback } from 'scheduler'\nimport {\n  diffProps,\n  applyProps,\n  invalidateInstance,\n  attach,\n  detach,\n  prepare,\n  isObject3D,\n  findInitialRoot,\n  IsAllOptional,\n} from './utils'\nimport type { RootStore } from './store'\nimport { removeInteractivity, type EventHandlers } from './events'\nimport type { ThreeElement } from '../three-types'\n\ntype Fiber = Omit<Reconciler.Fiber, 'alternate'> & { refCleanup: null | (() => void); alternate: Fiber | null }\n\nfunction createReconciler<\n  Type,\n  Props,\n  Container,\n  Instance,\n  TextInstance,\n  SuspenseInstance,\n  HydratableInstance,\n  FormInstance,\n  PublicInstance,\n  HostContext,\n  ChildSet,\n  TimeoutHandle,\n  NoTimeout,\n  TransitionStatus,\n>(\n  config: Reconciler.HostConfig<\n    Type,\n    Props,\n    Container,\n    Instance,\n    TextInstance,\n    SuspenseInstance,\n    HydratableInstance,\n    FormInstance,\n    PublicInstance,\n    HostContext,\n    ChildSet,\n    TimeoutHandle,\n    NoTimeout,\n    TransitionStatus\n  >,\n): Reconciler.Reconciler<Container, Instance, TextInstance, SuspenseInstance, FormInstance, PublicInstance> {\n  const reconciler = Reconciler(config as any)\n\n  // @ts-ignore DefinitelyTyped is not up to date\n  reconciler.injectIntoDevTools()\n\n  return reconciler as any\n}\n\nconst NoEventPriority = 0\n\nexport interface Root {\n  fiber: Reconciler.FiberRoot\n  store: RootStore\n}\n\nexport type AttachFnType<O = any> = (parent: any, self: O) => () => void\nexport type AttachType<O = any> = string | AttachFnType<O>\n\nexport type ConstructorRepresentation<T = any> = new (...args: any[]) => T\n\nexport interface Catalogue {\n  [name: string]: ConstructorRepresentation\n}\n\n// TODO: handle constructor overloads\n// https://github.com/pmndrs/react-three-fiber/pull/2931\n// https://github.com/microsoft/TypeScript/issues/37079\nexport type Args<T> = T extends ConstructorRepresentation\n  ? T extends typeof THREE.Color\n    ? [r: number, g: number, b: number] | [color: THREE.ColorRepresentation]\n    : ConstructorParameters<T>\n  : any[]\n\ntype ArgsProp<P> = P extends ConstructorRepresentation\n  ? IsAllOptional<ConstructorParameters<P>> extends true\n    ? { args?: Args<P> }\n    : { args: Args<P> }\n  : { args: unknown[] }\n\nexport type InstanceProps<T = any, P = any> = ArgsProp<P> & {\n  object?: T\n  dispose?: null\n  attach?: AttachType<T>\n  onUpdate?: (self: T) => void\n}\n\nexport interface Instance<O = any> {\n  root: RootStore\n  type: string\n  parent: Instance | null\n  children: Instance[]\n  props: InstanceProps<O> & Record<string, unknown>\n  object: O & { __r3f?: Instance<O> }\n  eventCount: number\n  handlers: Partial<EventHandlers>\n  attach?: AttachType<O>\n  previousAttach?: any\n  isHidden: boolean\n}\n\ninterface HostConfig {\n  type: string\n  props: Instance['props']\n  container: RootStore\n  instance: Instance\n  textInstance: void\n  suspenseInstance: Instance\n  hydratableInstance: never\n  formInstance: never\n  publicInstance: Instance['object']\n  hostContext: {}\n  childSet: never\n  timeoutHandle: number | undefined\n  noTimeout: -1\n  TransitionStatus: null\n}\n\nconst catalogue: Catalogue = {}\n\nconst PREFIX_REGEX = /^three(?=[A-Z])/\n\nconst toPascalCase = (type: string): string => `${type[0].toUpperCase()}${type.slice(1)}`\n\nlet i = 0\n\nconst isConstructor = (object: unknown): object is ConstructorRepresentation => typeof object === 'function'\n\nexport function extend<T extends ConstructorRepresentation>(objects: T): React.ExoticComponent<ThreeElement<T>>\nexport function extend<T extends Catalogue>(objects: T): void\nexport function extend<T extends Catalogue | ConstructorRepresentation>(\n  objects: T,\n): React.ExoticComponent<ThreeElement<any>> | void {\n  if (isConstructor(objects)) {\n    const Component = `${i++}`\n    catalogue[Component] = objects\n    return Component as any\n  } else {\n    Object.assign(catalogue, objects)\n  }\n}\n\nfunction validateInstance(type: string, props: HostConfig['props']): void {\n  // Get target from catalogue\n  const name = toPascalCase(type)\n  const target = catalogue[name]\n\n  // Validate element target\n  if (type !== 'primitive' && !target)\n    throw new Error(\n      `R3F: ${name} is not part of the THREE namespace! Did you forget to extend? See: https://docs.pmnd.rs/react-three-fiber/api/objects#using-3rd-party-objects-declaratively`,\n    )\n\n  // Validate primitives\n  if (type === 'primitive' && !props.object) throw new Error(`R3F: Primitives without 'object' are invalid!`)\n\n  // Throw if an object or literal was passed for args\n  if (props.args !== undefined && !Array.isArray(props.args)) throw new Error('R3F: The args prop must be an array!')\n}\n\nfunction createInstance(type: string, props: HostConfig['props'], root: RootStore): HostConfig['instance'] {\n  // Remove three* prefix from elements if native element not present\n  type = toPascalCase(type) in catalogue ? type : type.replace(PREFIX_REGEX, '')\n\n  validateInstance(type, props)\n\n  // Regenerate the R3F instance for primitives to simulate a new object\n  if (type === 'primitive' && props.object?.__r3f) delete props.object.__r3f\n\n  return prepare(props.object, root, type, props)\n}\n\nfunction hideInstance(instance: HostConfig['instance']): void {\n  if (!instance.isHidden) {\n    if (instance.props.attach && instance.parent?.object) {\n      detach(instance.parent, instance)\n    } else if (isObject3D(instance.object)) {\n      instance.object.visible = false\n    }\n\n    instance.isHidden = true\n    invalidateInstance(instance)\n  }\n}\n\nfunction unhideInstance(instance: HostConfig['instance']): void {\n  if (instance.isHidden) {\n    if (instance.props.attach && instance.parent?.object) {\n      attach(instance.parent, instance)\n    } else if (isObject3D(instance.object) && instance.props.visible !== false) {\n      instance.object.visible = true\n    }\n\n    instance.isHidden = false\n    invalidateInstance(instance)\n  }\n}\n\n// https://github.com/facebook/react/issues/20271\n// This will make sure events and attach are only handled once when trees are complete\nfunction handleContainerEffects(parent: Instance, child: Instance, beforeChild?: Instance) {\n  // Bail if tree isn't mounted or parent is not a container.\n  // This ensures that the tree is finalized and React won't discard results to Suspense\n  const state = child.root.getState()\n  if (!parent.parent && parent.object !== state.scene) return\n\n  // Create & link object on first run\n  if (!child.object) {\n    // Get target from catalogue\n    const target = catalogue[toPascalCase(child.type)]\n\n    // Create object\n    child.object = child.props.object ?? new target(...(child.props.args ?? []))\n    child.object.__r3f = child\n  }\n\n  // Set initial props\n  applyProps(child.object, child.props)\n\n  // Append instance\n  if (child.props.attach) {\n    attach(parent, child)\n  } else if (isObject3D(child.object) && isObject3D(parent.object)) {\n    const childIndex = parent.object.children.indexOf(beforeChild?.object)\n    if (beforeChild && childIndex !== -1) {\n      // If the child is already in the parent's children array, move it to the new position\n      // Otherwise, just insert it at the target position\n      const existingIndex = parent.object.children.indexOf(child.object)\n      if (existingIndex !== -1) {\n        parent.object.children.splice(existingIndex, 1)\n        const adjustedIndex = existingIndex < childIndex ? childIndex - 1 : childIndex\n        parent.object.children.splice(adjustedIndex, 0, child.object)\n      } else {\n        child.object.parent = parent.object\n        parent.object.children.splice(childIndex, 0, child.object)\n        child.object.dispatchEvent({ type: 'added' })\n        parent.object.dispatchEvent({ type: 'childadded', child: child.object })\n      }\n    } else {\n      parent.object.add(child.object)\n    }\n  }\n\n  // Link subtree\n  for (const childInstance of child.children) handleContainerEffects(child, childInstance)\n\n  // Tree was updated, request a frame\n  invalidateInstance(child)\n}\n\nfunction appendChild(parent: HostConfig['instance'], child: HostConfig['instance'] | HostConfig['textInstance']) {\n  if (!child) return\n\n  // Link instances\n  child.parent = parent\n  parent.children.push(child)\n\n  // Attach tree once complete\n  handleContainerEffects(parent, child)\n}\n\nfunction insertBefore(\n  parent: HostConfig['instance'],\n  child: HostConfig['instance'] | HostConfig['textInstance'],\n  beforeChild: HostConfig['instance'] | HostConfig['textInstance'],\n) {\n  if (!child || !beforeChild) return\n\n  // Link instances\n  child.parent = parent\n  const childIndex = parent.children.indexOf(beforeChild)\n  if (childIndex !== -1) parent.children.splice(childIndex, 0, child)\n  else parent.children.push(child)\n\n  // Attach tree once complete\n  handleContainerEffects(parent, child, beforeChild)\n}\n\nfunction disposeOnIdle(object: any) {\n  if (typeof object.dispose === 'function') {\n    const handleDispose = () => {\n      try {\n        object.dispose()\n      } catch {\n        // no-op\n      }\n    }\n\n    // In a testing environment, cleanup immediately\n    if (typeof IS_REACT_ACT_ENVIRONMENT !== 'undefined') handleDispose()\n    // Otherwise, using a real GPU so schedule cleanup to prevent stalls\n    else scheduleCallback(idlePriority, handleDispose)\n  }\n}\n\nfunction removeChild(\n  parent: HostConfig['instance'],\n  child: HostConfig['instance'] | HostConfig['textInstance'],\n  dispose?: boolean,\n) {\n  if (!child) return\n\n  // Unlink instances\n  child.parent = null\n  const childIndex = parent.children.indexOf(child)\n  if (childIndex !== -1) parent.children.splice(childIndex, 1)\n\n  // Eagerly tear down tree\n  if (child.props.attach) {\n    detach(parent, child)\n  } else if (isObject3D(child.object) && isObject3D(parent.object)) {\n    parent.object.remove(child.object)\n    removeInteractivity(findInitialRoot(child), child.object)\n  }\n\n  // Allow objects to bail out of unmount disposal with dispose={null}\n  const shouldDispose = child.props.dispose !== null && dispose !== false\n\n  // Recursively remove instance children\n  for (let i = child.children.length - 1; i >= 0; i--) {\n    const node = child.children[i]\n    removeChild(child, node, shouldDispose)\n  }\n  child.children.length = 0\n\n  // Unlink instance object\n  delete child.object.__r3f\n\n  // Dispose object whenever the reconciler feels like it.\n  // Never dispose of primitives because their state may be kept outside of React!\n  // In order for an object to be able to dispose it\n  //   - has a dispose method\n  //   - cannot be a <primitive object={...} />\n  //   - cannot be a THREE.Scene, because three has broken its own API\n  if (shouldDispose && child.type !== 'primitive' && child.object.type !== 'Scene') {\n    disposeOnIdle(child.object)\n  }\n\n  // Tree was updated, request a frame for top-level instance\n  if (dispose === undefined) invalidateInstance(child)\n}\n\nfunction setFiberRef(fiber: Fiber, publicInstance: HostConfig['publicInstance']): void {\n  for (const _fiber of [fiber, fiber.alternate]) {\n    if (_fiber !== null) {\n      if (typeof _fiber.ref === 'function') {\n        _fiber.refCleanup?.()\n        const cleanup = _fiber.ref(publicInstance)\n        if (typeof cleanup === 'function') _fiber.refCleanup = cleanup\n      } else if (_fiber.ref) {\n        _fiber.ref.current = publicInstance\n      }\n    }\n  }\n}\n\nconst reconstructed: [oldInstance: HostConfig['instance'], props: HostConfig['props'], fiber: Fiber][] = []\n\nfunction swapInstances(): void {\n  // Detach instance\n  for (const [instance] of reconstructed) {\n    const parent = instance.parent\n    if (parent) {\n      if (instance.props.attach) {\n        detach(parent, instance)\n      } else if (isObject3D(instance.object) && isObject3D(parent.object)) {\n        parent.object.remove(instance.object)\n      }\n\n      for (const child of instance.children) {\n        if (child.props.attach) {\n          detach(instance, child)\n        } else if (isObject3D(child.object) && isObject3D(instance.object)) {\n          instance.object.remove(child.object)\n        }\n      }\n    }\n\n    // If the old instance is hidden, we need to unhide it.\n    // React assumes it can discard instances since they're pure for DOM.\n    // This isn't true for us since our lifetimes are impure and longliving.\n    // So, we manually check if an instance was hidden and unhide it.\n    if (instance.isHidden) unhideInstance(instance)\n\n    // Dispose of old object if able\n    if (instance.object.__r3f) delete instance.object.__r3f\n    if (instance.type !== 'primitive') disposeOnIdle(instance.object)\n  }\n\n  // Update instance\n  for (const [instance, props, fiber] of reconstructed) {\n    instance.props = props\n\n    const parent = instance.parent\n    if (parent) {\n      // Get target from catalogue\n      const target = catalogue[toPascalCase(instance.type)]\n\n      // Create object\n      instance.object = instance.props.object ?? new target(...(instance.props.args ?? []))\n      instance.object.__r3f = instance\n      setFiberRef(fiber, instance.object)\n\n      // Set initial props\n      applyProps(instance.object, instance.props)\n\n      if (instance.props.attach) {\n        attach(parent, instance)\n      } else if (isObject3D(instance.object) && isObject3D(parent.object)) {\n        parent.object.add(instance.object)\n      }\n\n      for (const child of instance.children) {\n        if (child.props.attach) {\n          attach(instance, child)\n        } else if (isObject3D(child.object) && isObject3D(instance.object)) {\n          instance.object.add(child.object)\n        }\n      }\n\n      // Tree was updated, request a frame\n      invalidateInstance(instance)\n    }\n  }\n\n  reconstructed.length = 0\n}\n\n// Don't handle text instances, make it no-op\nconst handleTextInstance = () => {}\n\nconst NO_CONTEXT: HostConfig['hostContext'] = {}\n\nlet currentUpdatePriority: number = NoEventPriority\n\n// https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberFlags.js\nconst NoFlags = 0\nconst Update = 4\n\nexport const reconciler = /* @__PURE__ */ createReconciler<\n  HostConfig['type'],\n  HostConfig['props'],\n  HostConfig['container'],\n  HostConfig['instance'],\n  HostConfig['textInstance'],\n  HostConfig['suspenseInstance'],\n  HostConfig['hydratableInstance'],\n  HostConfig['formInstance'],\n  HostConfig['publicInstance'],\n  HostConfig['hostContext'],\n  HostConfig['childSet'],\n  HostConfig['timeoutHandle'],\n  HostConfig['noTimeout'],\n  HostConfig['TransitionStatus']\n>({\n  isPrimaryRenderer: false,\n  warnsIfNotActing: false,\n  supportsMutation: true,\n  supportsPersistence: false,\n  supportsHydration: false,\n  createInstance,\n  removeChild,\n  appendChild,\n  appendInitialChild: appendChild,\n  insertBefore,\n  appendChildToContainer(container, child) {\n    const scene = (container.getState().scene as unknown as Instance<THREE.Scene>['object']).__r3f\n    if (!child || !scene) return\n\n    appendChild(scene, child)\n  },\n  removeChildFromContainer(container, child) {\n    const scene = (container.getState().scene as unknown as Instance<THREE.Scene>['object']).__r3f\n    if (!child || !scene) return\n\n    removeChild(scene, child)\n  },\n  insertInContainerBefore(container, child, beforeChild) {\n    const scene = (container.getState().scene as unknown as Instance<THREE.Scene>['object']).__r3f\n    if (!child || !beforeChild || !scene) return\n\n    insertBefore(scene, child, beforeChild)\n  },\n  getRootHostContext: () => NO_CONTEXT,\n  getChildHostContext: () => NO_CONTEXT,\n  commitUpdate(\n    instance: HostConfig['instance'],\n    type: HostConfig['type'],\n    oldProps: HostConfig['props'],\n    newProps: HostConfig['props'],\n    fiber: Fiber,\n  ) {\n    validateInstance(type, newProps)\n\n    let reconstruct = false\n\n    // Reconstruct primitives if object prop changes\n    if (instance.type === 'primitive' && oldProps.object !== newProps.object) reconstruct = true\n    // Reconstruct instance if args were added or removed\n    else if (newProps.args?.length !== oldProps.args?.length) reconstruct = true\n    // Reconstruct instance if args were changed\n    else if (newProps.args?.some((value, index) => value !== oldProps.args?.[index])) reconstruct = true\n\n    // Reconstruct when args or <primitive object={...} have changes\n    if (reconstruct) {\n      reconstructed.push([instance, { ...newProps }, fiber])\n    } else {\n      // Create a diff-set, flag if there are any changes\n      const changedProps = diffProps(instance, newProps)\n      if (Object.keys(changedProps).length) {\n        Object.assign(instance.props, changedProps)\n        applyProps(instance.object, changedProps)\n      }\n    }\n\n    // Flush reconstructed siblings when we hit the last updated child in a sequence\n    const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags\n    if (isTailSibling) swapInstances()\n  },\n  finalizeInitialChildren: () => false,\n  commitMount() {},\n  getPublicInstance: (instance) => instance?.object!,\n  prepareForCommit: () => null,\n  preparePortalMount: (container) => prepare(container.getState().scene, container, '', {}),\n  resetAfterCommit: () => {},\n  shouldSetTextContent: () => false,\n  clearContainer: () => false,\n  hideInstance,\n  unhideInstance,\n  createTextInstance: handleTextInstance,\n  hideTextInstance: handleTextInstance,\n  unhideTextInstance: handleTextInstance,\n  scheduleTimeout: (typeof setTimeout === 'function' ? setTimeout : undefined) as any,\n  cancelTimeout: (typeof clearTimeout === 'function' ? clearTimeout : undefined) as any,\n  noTimeout: -1,\n  getInstanceFromNode: () => null,\n  beforeActiveInstanceBlur() {},\n  afterActiveInstanceBlur() {},\n  detachDeletedInstance() {},\n  prepareScopeUpdate() {},\n  getInstanceFromScope: () => null,\n  shouldAttemptEagerTransition: () => false,\n  trackSchedulerEvent: () => {},\n  resolveEventType: () => null,\n  resolveEventTimeStamp: () => -1.1,\n  requestPostPaintCallback() {},\n  maySuspendCommit: () => false,\n  preloadInstance: () => true, // true indicates already loaded\n  suspendInstance() {},\n  waitForCommitToBeReady: () => null,\n  NotPendingTransition: null,\n  // The reconciler types use the internal ReactContext with all the hidden properties\n  // so we have to cast from the public React.Context type\n  HostTransitionContext: /* @__PURE__ */ React.createContext<HostConfig['TransitionStatus']>(\n    null,\n  ) as unknown as Reconciler.ReactContext<HostConfig['TransitionStatus']>,\n  setCurrentUpdatePriority(newPriority: number) {\n    currentUpdatePriority = newPriority\n  },\n  getCurrentUpdatePriority() {\n    return currentUpdatePriority\n  },\n  resolveUpdatePriority() {\n    if (currentUpdatePriority !== NoEventPriority) return currentUpdatePriority\n\n    switch (typeof window !== 'undefined' && window.event?.type) {\n      case 'click':\n      case 'contextmenu':\n      case 'dblclick':\n      case 'pointercancel':\n      case 'pointerdown':\n      case 'pointerup':\n        return DiscreteEventPriority\n      case 'pointermove':\n      case 'pointerout':\n      case 'pointerover':\n      case 'pointerenter':\n      case 'pointerleave':\n      case 'wheel':\n        return ContinuousEventPriority\n      default:\n        return DefaultEventPriority\n    }\n  },\n  resetFormInstance() {},\n  // @ts-ignore DefinitelyTyped is not up to date\n  rendererPackageName: '@react-three/fiber',\n  rendererVersion: packageData.version,\n\n  // https://github.com/facebook/react/pull/31975\n  // https://github.com/facebook/react/pull/31999\n  applyViewTransitionName(_instance: any, _name: any, _className: any) {},\n  restoreViewTransitionName(_instance: any, _props: any) {},\n  cancelViewTransitionName(_instance: any, _name: any, _props: any) {},\n  cancelRootViewTransitionName(_rootContainer: any) {},\n  restoreRootViewTransitionName(_rootContainer: any) {},\n  InstanceMeasurement: null,\n  measureInstance: (_instance: any) => null,\n  wasInstanceInViewport: (_measurement: any): boolean => true,\n  hasInstanceChanged: (_oldMeasurement: any, _newMeasurement: any): boolean => false,\n  hasInstanceAffectedParent: (_oldMeasurement: any, _newMeasurement: any): boolean => false,\n\n  // https://github.com/facebook/react/pull/32002\n  // https://github.com/facebook/react/pull/34486\n  suspendOnActiveViewTransition(_state: any, _container: any) {},\n\n  // https://github.com/facebook/react/pull/32451\n  // https://github.com/facebook/react/pull/32760\n  startGestureTransition: () => null,\n  startViewTransition: () => null,\n  stopViewTransition(_transition: null) {},\n\n  // https://github.com/facebook/react/pull/32038\n  createViewTransitionInstance: (_name: string): null => null,\n\n  // https://github.com/facebook/react/pull/32379\n  // https://github.com/facebook/react/pull/32786\n  getCurrentGestureOffset(_provider: null): number {\n    throw new Error('startGestureTransition is not yet supported in react-three-fiber.')\n  },\n\n  // https://github.com/facebook/react/pull/32500\n  cloneMutableInstance(instance: any, _keepChildren: any) {\n    return instance\n  },\n  cloneMutableTextInstance(textInstance: any) {\n    return textInstance\n  },\n  cloneRootViewTransitionContainer(_rootContainer: any) {\n    throw new Error('Not implemented.')\n  },\n  removeRootViewTransitionClone(_rootContainer: any, _clone: any) {\n    throw new Error('Not implemented.')\n  },\n\n  // https://github.com/facebook/react/pull/32465\n  createFragmentInstance: (_fiber: any): null => null,\n  updateFragmentInstanceFiber(_fiber: any, _instance: any): void {},\n  commitNewChildToFragmentInstance(_child: any, _fragmentInstance: any): void {},\n  deleteChildFromFragmentInstance(_child: any, _fragmentInstance: any): void {},\n\n  // https://github.com/facebook/react/pull/32653\n  measureClonedInstance: (_instance: any) => null,\n\n  // https://github.com/facebook/react/pull/32819\n  maySuspendCommitOnUpdate: (_type: any, _oldProps: any, _newProps: any) => false,\n  maySuspendCommitInSyncRender: (_type: any, _props: any) => false,\n\n  // https://github.com/facebook/react/pull/34486\n  startSuspendingCommit: () => null,\n\n  // https://github.com/facebook/react/pull/34522\n  getSuspendedCommitReason: (_state: any, _rootContainer: any) => null,\n})\n"
  },
  {
    "path": "packages/fiber/src/core/renderer.tsx",
    "content": "import * as React from 'react'\nimport { ConcurrentRoot } from '../../react-reconciler/constants.js'\nimport * as THREE from 'three'\nimport { createWithEqualityFn } from 'zustand/traditional'\n\nimport type { ThreeElement } from '../three-types'\nimport { ComputeFunction, EventManager } from './events'\nimport { useStore } from './hooks'\nimport { advance, invalidate } from './loop'\nimport { reconciler, Root } from './reconciler'\nimport {\n  context,\n  createStore,\n  Dpr,\n  Frameloop,\n  isRenderer,\n  Performance,\n  Renderer,\n  RootState,\n  RootStore,\n  Size,\n} from './store'\nimport {\n  type Properties,\n  applyProps,\n  calculateDpr,\n  Camera,\n  dispose,\n  EquConfig,\n  is,\n  prepare,\n  updateCamera,\n  useIsomorphicLayoutEffect,\n  useMutableCallback,\n} from './utils'\n\n// Shim for OffscreenCanvas since it was removed from DOM types\n// https://github.com/DefinitelyTyped/DefinitelyTyped/pull/54988\ninterface OffscreenCanvas extends EventTarget {}\n\nexport const _roots = new Map<HTMLCanvasElement | OffscreenCanvas, Root>()\n\nconst shallowLoose = { objects: 'shallow', strict: false } as EquConfig\n\nexport type DefaultGLProps = Omit<THREE.WebGLRendererParameters, 'canvas'> & {\n  canvas: HTMLCanvasElement | OffscreenCanvas\n}\n\nexport type GLProps =\n  | Renderer\n  | ((defaultProps: DefaultGLProps) => Renderer)\n  | ((defaultProps: DefaultGLProps) => Promise<Renderer>)\n  | Partial<Properties<THREE.WebGLRenderer> | THREE.WebGLRendererParameters>\n\nexport type CameraProps = (\n  | Camera\n  | Partial<\n      ThreeElement<typeof THREE.Camera> &\n        ThreeElement<typeof THREE.PerspectiveCamera> &\n        ThreeElement<typeof THREE.OrthographicCamera>\n    >\n) & {\n  /** Flags the camera as manual, putting projection into your own hands */\n  manual?: boolean\n}\n\nexport interface RenderProps<TCanvas extends HTMLCanvasElement | OffscreenCanvas> {\n  /** A threejs renderer instance or props that go into the default renderer */\n  gl?: GLProps\n  /** Dimensions to fit the renderer to. Will measure canvas dimensions if omitted */\n  size?: Size\n  /**\n   * Enables shadows (by default PCFsoft). Can accept `gl.shadowMap` options for fine-tuning,\n   * but also strings: 'basic' | 'percentage' | 'soft' | 'variance'.\n   * @see https://threejs.org/docs/#api/en/renderers/WebGLRenderer.shadowMap\n   */\n  shadows?: boolean | 'basic' | 'percentage' | 'soft' | 'variance' | Partial<THREE.WebGLShadowMap>\n  /**\n   * Disables three r139 color management.\n   * @see https://threejs.org/docs/#manual/en/introduction/Color-management\n   */\n  legacy?: boolean\n  /** Switch off automatic sRGB encoding and gamma correction */\n  linear?: boolean\n  /** Use `THREE.NoToneMapping` instead of `THREE.ACESFilmicToneMapping` */\n  flat?: boolean\n  /** Creates an orthographic camera */\n  orthographic?: boolean\n  /**\n   * R3F's render mode. Set to `demand` to only render on state change or `never` to take control.\n   * @see https://docs.pmnd.rs/react-three-fiber/advanced/scaling-performance#on-demand-rendering\n   */\n  frameloop?: Frameloop\n  /**\n   * R3F performance options for adaptive performance.\n   * @see https://docs.pmnd.rs/react-three-fiber/advanced/scaling-performance#movement-regression\n   */\n  performance?: Partial<Omit<Performance, 'regress'>>\n  /** Target pixel ratio. Can clamp between a range: `[min, max]` */\n  dpr?: Dpr\n  /** Props that go into the default raycaster */\n  raycaster?: Partial<THREE.Raycaster>\n  /** A `THREE.Scene` instance or props that go into the default scene */\n  scene?: THREE.Scene | Partial<THREE.Scene>\n  /** A `THREE.Camera` instance or props that go into the default camera */\n  camera?: CameraProps\n  /** An R3F event manager to manage elements' pointer events */\n  events?: (store: RootStore) => EventManager<HTMLElement>\n  /** Callback after the canvas has rendered (but not yet committed) */\n  onCreated?: (state: RootState) => void\n  /** Response for pointer clicks that have missed any target */\n  onPointerMissed?: (event: MouseEvent) => void\n}\n\nexport interface ReconcilerRoot<TCanvas extends HTMLCanvasElement | OffscreenCanvas> {\n  configure: (config?: RenderProps<TCanvas>) => Promise<ReconcilerRoot<TCanvas>>\n  render: (element: React.ReactNode) => RootStore\n  unmount: () => void\n}\n\nfunction computeInitialSize(canvas: HTMLCanvasElement | OffscreenCanvas, size?: Size): Size {\n  if (\n    !size &&\n    typeof HTMLCanvasElement !== 'undefined' &&\n    canvas instanceof HTMLCanvasElement &&\n    canvas.parentElement\n  ) {\n    const { width, height, top, left } = canvas.parentElement.getBoundingClientRect()\n    return { width, height, top, left }\n  } else if (!size && typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas) {\n    return {\n      width: canvas.width,\n      height: canvas.height,\n      top: 0,\n      left: 0,\n    }\n  }\n\n  return { width: 0, height: 0, top: 0, left: 0, ...size }\n}\n\nexport function createRoot<TCanvas extends HTMLCanvasElement | OffscreenCanvas>(\n  canvas: TCanvas,\n): ReconcilerRoot<TCanvas> {\n  // Check against mistaken use of createRoot\n  const prevRoot = _roots.get(canvas)\n  const prevFiber = prevRoot?.fiber\n  const prevStore = prevRoot?.store\n\n  if (prevRoot) console.warn('R3F.createRoot should only be called once!')\n\n  // Report when an error was detected in a previous render\n  // https://github.com/pmndrs/react-three-fiber/pull/2261\n  const logRecoverableError =\n    typeof reportError === 'function'\n      ? // In modern browsers, reportError will dispatch an error event,\n        // emulating an uncaught JavaScript error.\n        reportError\n      : // In older browsers and test environments, fallback to console.error.\n        console.error\n\n  // Create store\n  const store = prevStore || createStore(invalidate, advance)\n  // Create renderer\n  const fiber =\n    prevFiber ||\n    (reconciler as any).createContainer(\n      store, // container\n      ConcurrentRoot, // tag\n      null, // hydration callbacks\n      false, // isStrictMode\n      null, // concurrentUpdatesByDefaultOverride\n      '', // identifierPrefix\n      logRecoverableError, // onUncaughtError\n      logRecoverableError, // onCaughtError\n      logRecoverableError, // onRecoverableError\n      null, // transitionCallbacks\n    )\n  // Map it\n  if (!prevRoot) _roots.set(canvas, { fiber, store })\n\n  // Locals\n  let onCreated: ((state: RootState) => void) | undefined\n  let lastCamera: RenderProps<TCanvas>['camera']\n\n  let configured = false\n  let pending: Promise<void> | null = null\n\n  return {\n    async configure(props: RenderProps<TCanvas> = {}): Promise<ReconcilerRoot<TCanvas>> {\n      let resolve!: () => void\n      pending = new Promise<void>((_resolve) => (resolve = _resolve))\n\n      let {\n        gl: glConfig,\n        size: propsSize,\n        scene: sceneOptions,\n        events,\n        onCreated: onCreatedCallback,\n        shadows = false,\n        linear = false,\n        flat = false,\n        legacy = false,\n        orthographic = false,\n        frameloop = 'always',\n        dpr = [1, 2],\n        performance,\n        raycaster: raycastOptions,\n        camera: cameraOptions,\n        onPointerMissed,\n      } = props\n\n      let state = store.getState()\n\n      // Set up renderer (one time only!)\n      let gl = state.gl\n      if (!state.gl) {\n        const defaultProps: DefaultGLProps = {\n          canvas: canvas as HTMLCanvasElement,\n          powerPreference: 'high-performance',\n          antialias: true,\n          alpha: true,\n        }\n\n        const customRenderer = (\n          typeof glConfig === 'function' ? await glConfig(defaultProps) : glConfig\n        ) as THREE.WebGLRenderer\n\n        if (isRenderer(customRenderer)) {\n          gl = customRenderer\n        } else {\n          gl = new THREE.WebGLRenderer({\n            ...defaultProps,\n            ...glConfig,\n          })\n        }\n\n        state.set({ gl })\n      }\n\n      // Set up raycaster (one time only!)\n      let raycaster = state.raycaster\n      if (!raycaster) state.set({ raycaster: (raycaster = new THREE.Raycaster()) })\n\n      // Set raycaster options\n      const { params, ...options } = raycastOptions || {}\n      if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, { ...options } as any)\n      if (!is.equ(params, raycaster.params, shallowLoose))\n        applyProps(raycaster, { params: { ...raycaster.params, ...params } } as any)\n\n      // Create default camera, don't overwrite any user-set state\n      if (!state.camera || (state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose))) {\n        lastCamera = cameraOptions\n        const isCamera = (cameraOptions as unknown as THREE.Camera | undefined)?.isCamera\n        const camera = isCamera\n          ? (cameraOptions as Camera)\n          : orthographic\n          ? new THREE.OrthographicCamera(0, 0, 0, 0, 0.1, 1000)\n          : new THREE.PerspectiveCamera(75, 0, 0.1, 1000)\n        if (!isCamera) {\n          camera.position.z = 5\n          if (cameraOptions) {\n            applyProps(camera, cameraOptions as any)\n            // Preserve user-defined frustum if possible\n            // https://github.com/pmndrs/react-three-fiber/issues/3160\n            if (!(camera as any).manual) {\n              if (\n                'aspect' in cameraOptions ||\n                'left' in cameraOptions ||\n                'right' in cameraOptions ||\n                'bottom' in cameraOptions ||\n                'top' in cameraOptions\n              ) {\n                ;(camera as any).manual = true\n                camera.updateProjectionMatrix()\n              }\n            }\n          }\n          // Always look at center by default\n          if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0)\n        }\n        state.set({ camera })\n\n        // Configure raycaster\n        // https://github.com/pmndrs/react-xr/issues/300\n        raycaster.camera = camera\n      }\n\n      // Set up scene (one time only!)\n      if (!state.scene) {\n        let scene: THREE.Scene\n\n        if ((sceneOptions as unknown as THREE.Scene | undefined)?.isScene) {\n          scene = sceneOptions as THREE.Scene\n          prepare(scene, store, '', {})\n        } else {\n          scene = new THREE.Scene()\n          prepare(scene, store, '', {})\n          if (sceneOptions) applyProps(scene as any, sceneOptions as any)\n        }\n\n        state.set({ scene })\n      }\n\n      // Store events internally\n      if (events && !state.events.handlers) state.set({ events: events(store) })\n      // Check size, allow it to take on container bounds initially\n      const size = computeInitialSize(canvas, propsSize)\n      if (!is.equ(size, state.size, shallowLoose)) {\n        state.setSize(size.width, size.height, size.top, size.left)\n      }\n      // Check pixelratio\n      if (dpr && state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr)\n      // Check frameloop\n      if (state.frameloop !== frameloop) state.setFrameloop(frameloop)\n      // Check pointer missed\n      if (!state.onPointerMissed) state.set({ onPointerMissed })\n      // Check performance\n      if (performance && !is.equ(performance, state.performance, shallowLoose))\n        state.set((state) => ({ performance: { ...state.performance, ...performance } }))\n\n      // Set up XR (one time only!)\n      if (!state.xr) {\n        // Handle frame behavior in WebXR\n        const handleXRFrame: XRFrameRequestCallback = (timestamp: number, frame?: XRFrame) => {\n          const state = store.getState()\n          if (state.frameloop === 'never') return\n          advance(timestamp, true, state, frame)\n        }\n\n        // Toggle render switching on session\n        const handleSessionChange = () => {\n          const state = store.getState()\n          state.gl.xr.enabled = state.gl.xr.isPresenting\n\n          state.gl.xr.setAnimationLoop(state.gl.xr.isPresenting ? handleXRFrame : null)\n          if (!state.gl.xr.isPresenting) invalidate(state)\n        }\n\n        // WebXR session manager\n        const xr = {\n          connect() {\n            const gl = store.getState().gl\n            gl.xr.addEventListener('sessionstart', handleSessionChange)\n            gl.xr.addEventListener('sessionend', handleSessionChange)\n          },\n          disconnect() {\n            const gl = store.getState().gl\n            gl.xr.removeEventListener('sessionstart', handleSessionChange)\n            gl.xr.removeEventListener('sessionend', handleSessionChange)\n          },\n        }\n\n        // Subscribe to WebXR session events\n        if (typeof gl.xr?.addEventListener === 'function') xr.connect()\n        state.set({ xr })\n      }\n\n      // Set shadowmap\n      if (gl.shadowMap) {\n        const oldEnabled = gl.shadowMap.enabled\n        const oldType = gl.shadowMap.type\n        gl.shadowMap.enabled = !!shadows\n\n        if (is.boo(shadows)) {\n          gl.shadowMap.type = THREE.PCFSoftShadowMap\n        } else if (is.str(shadows)) {\n          const types = {\n            basic: THREE.BasicShadowMap,\n            percentage: THREE.PCFShadowMap,\n            soft: THREE.PCFSoftShadowMap,\n            variance: THREE.VSMShadowMap,\n          }\n          gl.shadowMap.type = types[shadows] ?? THREE.PCFSoftShadowMap\n        } else if (is.obj(shadows)) {\n          Object.assign(gl.shadowMap, shadows)\n        }\n\n        if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type) gl.shadowMap.needsUpdate = true\n      }\n\n      THREE.ColorManagement.enabled = !legacy\n\n      // Set color space and tonemapping preferences\n      if (!configured) {\n        gl.outputColorSpace = linear ? THREE.LinearSRGBColorSpace : THREE.SRGBColorSpace\n        gl.toneMapping = flat ? THREE.NoToneMapping : THREE.ACESFilmicToneMapping\n      }\n\n      // Update color management state\n      if (state.legacy !== legacy) state.set(() => ({ legacy }))\n      if (state.linear !== linear) state.set(() => ({ linear }))\n      if (state.flat !== flat) state.set(() => ({ flat }))\n\n      // Set gl props\n      if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, gl, shallowLoose))\n        applyProps(gl, glConfig as any)\n\n      // Set locals\n      onCreated = onCreatedCallback\n      configured = true\n      resolve()\n      return this\n    },\n    render(children: React.ReactNode): RootStore {\n      // The root has to be configured before it can be rendered\n      if (!configured && !pending) this.configure()\n\n      pending!.then(() => {\n        reconciler.updateContainer(\n          <Provider store={store} children={children} onCreated={onCreated} rootElement={canvas} />,\n          fiber,\n          null,\n          () => undefined,\n        )\n      })\n\n      return store\n    },\n    unmount(): void {\n      unmountComponentAtNode(canvas)\n    },\n  }\n}\n\ninterface ProviderProps<TCanvas extends HTMLCanvasElement | OffscreenCanvas> {\n  onCreated?: (state: RootState) => void\n  store: RootStore\n  children: React.ReactNode\n  rootElement: TCanvas\n}\n\nfunction Provider<TCanvas extends HTMLCanvasElement | OffscreenCanvas>({\n  store,\n  children,\n  onCreated,\n  rootElement,\n}: ProviderProps<TCanvas>): React.JSX.Element {\n  useIsomorphicLayoutEffect(() => {\n    const state = store.getState()\n    // Flag the canvas active, rendering will now begin\n    state.set((state) => ({ internal: { ...state.internal, active: true } }))\n    // Notify that init is completed, the scene graph exists, but nothing has yet rendered\n    if (onCreated) onCreated(state)\n    // Connect events to the targets parent, this is done to ensure events are registered on\n    // a shared target, and not on the canvas itself\n    if (!store.getState().events.connected) state.events.connect?.(rootElement)\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [])\n  return <context.Provider value={store}>{children}</context.Provider>\n}\n\nexport function unmountComponentAtNode<TCanvas extends HTMLCanvasElement | OffscreenCanvas>(\n  canvas: TCanvas,\n  callback?: (canvas: TCanvas) => void,\n): void {\n  const root = _roots.get(canvas)\n  const fiber = root?.fiber\n  if (fiber) {\n    const state = root?.store.getState()\n    if (state) state.internal.active = false\n    reconciler.updateContainer(null, fiber, null, () => {\n      if (state) {\n        setTimeout(() => {\n          try {\n            state.events.disconnect?.()\n            state.gl?.renderLists?.dispose?.()\n            state.gl?.forceContextLoss?.()\n            if (state.gl?.xr) state.xr.disconnect()\n            dispose(state.scene)\n            _roots.delete(canvas)\n            if (callback) callback(canvas)\n          } catch (e) {\n            /* ... */\n          }\n        }, 500)\n      }\n    })\n  }\n}\n\nexport type InjectState = Partial<\n  Omit<RootState, 'events'> & {\n    events?: {\n      enabled?: boolean\n      priority?: number\n      compute?: ComputeFunction\n      connected?: any\n    }\n  }\n>\n\nexport function createPortal(\n  children: React.ReactNode,\n  container: THREE.Object3D,\n  state?: InjectState,\n): React.JSX.Element {\n  return <Portal children={children} container={container} state={state} />\n}\n\ninterface PortalProps {\n  children: React.ReactNode\n  state?: InjectState\n  container: THREE.Object3D\n}\n\nfunction Portal({ state = {}, children, container }: PortalProps): React.JSX.Element {\n  /** This has to be a component because it would not be able to call useThree/useStore otherwise since\n   *  if this is our environment, then we are not in r3f's renderer but in react-dom, it would trigger\n   *  the \"R3F hooks can only be used within the Canvas component!\" warning:\n   *  <Canvas>\n   *    {createPortal(...)} */\n  const { events, size, ...rest } = state\n  const previousRoot = useStore()\n  const [raycaster] = React.useState(() => new THREE.Raycaster())\n  const [pointer] = React.useState(() => new THREE.Vector2())\n\n  const inject = useMutableCallback((rootState: RootState, injectState: RootState) => {\n    let viewport = undefined\n    if (injectState.camera && size) {\n      const camera = injectState.camera\n      // Calculate the override viewport, if present\n      viewport = rootState.viewport.getCurrentViewport(camera, new THREE.Vector3(), size)\n      // Update the portal camera, if it differs from the previous layer\n      if (camera !== rootState.camera) updateCamera(camera, size)\n    }\n\n    return {\n      // The intersect consists of the previous root state\n      ...rootState,\n      ...injectState,\n      // Portals have their own scene, which forms the root, a raycaster and a pointer\n      scene: container as THREE.Scene,\n      raycaster,\n      pointer,\n      mouse: pointer,\n      // Their previous root is the layer before it\n      previousRoot,\n      // Events, size and viewport can be overridden by the inject layer\n      events: { ...rootState.events, ...injectState.events, ...events },\n      size: { ...rootState.size, ...size },\n      viewport: { ...rootState.viewport, ...viewport },\n      // Layers are allowed to override events\n      setEvents: (events: Partial<EventManager<any>>) =>\n        injectState.set((state) => ({ ...state, events: { ...state.events, ...events } })),\n    } as RootState\n  })\n\n  const usePortalStore = React.useMemo(() => {\n    // Create a mirrored store, based on the previous root with a few overrides ...\n    const store = createWithEqualityFn<RootState>((set, get) => ({ ...rest, set, get } as RootState))\n\n    // Subscribe to previous root-state and copy changes over to the mirrored portal-state\n    const onMutate = (prev: RootState) => store.setState((state) => inject.current(prev, state))\n    onMutate(previousRoot.getState())\n    previousRoot.subscribe(onMutate)\n\n    return store\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [previousRoot, container])\n\n  return (\n    // @ts-ignore, reconciler types are not maintained\n    <>\n      {reconciler.createPortal(\n        <context.Provider value={usePortalStore}>{children}</context.Provider>,\n        usePortalStore,\n        null,\n      )}\n    </>\n  )\n}\n\n/**\n * Force React to flush any updates inside the provided callback synchronously and immediately.\n * All the same caveats documented for react-dom's `flushSync` apply here (see https://react.dev/reference/react-dom/flushSync).\n * Nevertheless, sometimes one needs to render synchronously, for example to keep DOM and 3D changes in lock-step without\n * having to revert to a non-React solution. Note: this will only flush updates within the `Canvas` root.\n */\nexport function flushSync<R>(fn: () => R): R {\n  // @ts-ignore - reconciler types are not maintained\n  return reconciler.flushSyncFromReconciler(fn)\n}\n"
  },
  {
    "path": "packages/fiber/src/core/store.ts",
    "content": "import * as THREE from 'three'\nimport * as React from 'react'\nimport { type StoreApi } from 'zustand'\nimport { createWithEqualityFn, type UseBoundStoreWithEqualityFn } from 'zustand/traditional'\nimport type { DomEvent, EventManager, PointerCaptureTarget, ThreeEvent } from './events'\nimport { calculateDpr, type Camera, isOrthographicCamera, updateCamera } from './utils'\n\nexport interface Intersection extends THREE.Intersection {\n  eventObject: THREE.Object3D\n}\n\nexport type Subscription = {\n  ref: React.RefObject<RenderCallback>\n  priority: number\n  store: RootStore\n}\n\nexport type Dpr = number | [min: number, max: number]\nexport interface Size {\n  width: number\n  height: number\n  top: number\n  left: number\n}\nexport type Frameloop = 'always' | 'demand' | 'never'\nexport interface Viewport extends Size {\n  /** The initial pixel ratio */\n  initialDpr: number\n  /** Current pixel ratio */\n  dpr: number\n  /** size.width / viewport.width */\n  factor: number\n  /** Camera distance */\n  distance: number\n  /** Camera aspect ratio: width / height */\n  aspect: number\n}\n\nexport type RenderCallback = (state: RootState, delta: number, frame?: XRFrame) => void\n\nexport interface Performance {\n  /** Current performance normal, between min and max */\n  current: number\n  /** How low the performance can go, between 0 and max */\n  min: number\n  /** How high the performance can go, between min and max */\n  max: number\n  /** Time until current returns to max in ms */\n  debounce: number\n  /** Sets current to min, puts the system in regression */\n  regress: () => void\n}\n\nexport interface Renderer {\n  render: (scene: THREE.Scene, camera: THREE.Camera) => any\n}\nexport const isRenderer = (def: any) => !!def?.render\n\nexport interface InternalState {\n  interaction: THREE.Object3D[]\n  hovered: Map<string, ThreeEvent<DomEvent>>\n  subscribers: Subscription[]\n  capturedMap: Map<number, Map<THREE.Object3D, PointerCaptureTarget>>\n  initialClick: [x: number, y: number]\n  initialHits: THREE.Object3D[]\n  lastEvent: React.RefObject<DomEvent | null>\n  active: boolean\n  priority: number\n  frames: number\n  subscribe: (callback: React.RefObject<RenderCallback>, priority: number, store: RootStore) => () => void\n}\n\nexport interface XRManager {\n  connect: () => void\n  disconnect: () => void\n}\n\nexport interface RootState {\n  /** Set current state */\n  set: StoreApi<RootState>['setState']\n  /** Get current state */\n  get: StoreApi<RootState>['getState']\n  /** The instance of the renderer */\n  gl: THREE.WebGLRenderer\n  /** Default camera */\n  camera: Camera\n  /** Default scene */\n  scene: THREE.Scene\n  /** Default raycaster */\n  raycaster: THREE.Raycaster\n  /** Default clock */\n  clock: THREE.Clock\n  /** Event layer interface, contains the event handler and the node they're connected to */\n  events: EventManager<any>\n  /** XR interface */\n  xr: XRManager\n  /** Currently used controls */\n  controls: THREE.EventDispatcher | null\n  /** Normalized event coordinates */\n  pointer: THREE.Vector2\n  /** @deprecated Normalized event coordinates, use \"pointer\" instead! */\n  mouse: THREE.Vector2\n  /* Whether to enable r139's THREE.ColorManagement */\n  legacy: boolean\n  /** Shortcut to gl.outputColorSpace = THREE.LinearSRGBColorSpace */\n  linear: boolean\n  /** Shortcut to gl.toneMapping = NoTonemapping */\n  flat: boolean\n  /** Render loop flags */\n  frameloop: Frameloop\n  performance: Performance\n  /** Reactive pixel-size of the canvas */\n  size: Size\n  /** Reactive size of the viewport in threejs units */\n  viewport: Viewport & {\n    getCurrentViewport: (\n      camera?: Camera,\n      target?: THREE.Vector3 | Parameters<THREE.Vector3['set']>,\n      size?: Size,\n    ) => Omit<Viewport, 'dpr' | 'initialDpr'>\n  }\n  /** Flags the canvas for render, but doesn't render in itself */\n  invalidate: (frames?: number) => void\n  /** Advance (render) one step */\n  advance: (timestamp: number, runGlobalEffects?: boolean) => void\n  /** Shortcut to setting the event layer */\n  setEvents: (events: Partial<EventManager<any>>) => void\n  /** Shortcut to manual sizing */\n  setSize: (width: number, height: number, top?: number, left?: number) => void\n  /** Shortcut to manual setting the pixel ratio */\n  setDpr: (dpr: Dpr) => void\n  /** Shortcut to setting frameloop flags */\n  setFrameloop: (frameloop: Frameloop) => void\n  /** When the canvas was clicked but nothing was hit */\n  onPointerMissed?: (event: MouseEvent) => void\n  /** If this state model is layered (via createPortal) then this contains the previous layer */\n  previousRoot?: RootStore\n  /** Internals */\n  internal: InternalState\n}\n\nexport type RootStore = UseBoundStoreWithEqualityFn<StoreApi<RootState>>\n\nexport const context = /* @__PURE__ */ React.createContext<RootStore>(null!)\n\nexport const createStore = (\n  invalidate: (state?: RootState, frames?: number) => void,\n  advance: (timestamp: number, runGlobalEffects?: boolean, state?: RootState, frame?: XRFrame) => void,\n): RootStore => {\n  const rootStore = createWithEqualityFn<RootState>((set, get) => {\n    const position = new THREE.Vector3()\n    const defaultTarget = new THREE.Vector3()\n    const tempTarget = new THREE.Vector3()\n    function getCurrentViewport(\n      camera: Camera = get().camera,\n      target: THREE.Vector3 | Parameters<THREE.Vector3['set']> = defaultTarget,\n      size: Size = get().size,\n    ): Omit<Viewport, 'dpr' | 'initialDpr'> {\n      const { width, height, top, left } = size\n      const aspect = width / height\n      if ((target as THREE.Vector3).isVector3) tempTarget.copy(target as THREE.Vector3)\n      else tempTarget.set(...(target as Parameters<THREE.Vector3['set']>))\n      const distance = camera.getWorldPosition(position).distanceTo(tempTarget)\n      if (isOrthographicCamera(camera)) {\n        return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect }\n      } else {\n        const fov = (camera.fov * Math.PI) / 180 // convert vertical fov to radians\n        const h = 2 * Math.tan(fov / 2) * distance // visible height\n        const w = h * (width / height)\n        return { width: w, height: h, top, left, factor: width / w, distance, aspect }\n      }\n    }\n\n    let performanceTimeout: ReturnType<typeof setTimeout> | undefined = undefined\n    const setPerformanceCurrent = (current: number) =>\n      set((state) => ({ performance: { ...state.performance, current } }))\n\n    const pointer = new THREE.Vector2()\n\n    const rootState: RootState = {\n      set,\n      get,\n\n      // Mock objects that have to be configured\n      gl: null as unknown as THREE.WebGLRenderer,\n      camera: null as unknown as Camera,\n      raycaster: null as unknown as THREE.Raycaster,\n      events: { priority: 1, enabled: true, connected: false },\n      scene: null as unknown as THREE.Scene,\n      xr: null as unknown as XRManager,\n\n      invalidate: (frames = 1) => invalidate(get(), frames),\n      advance: (timestamp: number, runGlobalEffects?: boolean) => advance(timestamp, runGlobalEffects, get()),\n\n      legacy: false,\n      linear: false,\n      flat: false,\n\n      controls: null,\n      clock: new THREE.Clock(),\n      pointer,\n      mouse: pointer,\n\n      frameloop: 'always',\n      onPointerMissed: undefined,\n\n      performance: {\n        current: 1,\n        min: 0.5,\n        max: 1,\n        debounce: 200,\n        regress: () => {\n          const state = get()\n          // Clear timeout\n          if (performanceTimeout) clearTimeout(performanceTimeout)\n          // Set lower bound performance\n          if (state.performance.current !== state.performance.min) setPerformanceCurrent(state.performance.min)\n          // Go back to upper bound performance after a while unless something regresses meanwhile\n          performanceTimeout = setTimeout(\n            () => setPerformanceCurrent(get().performance.max),\n            state.performance.debounce,\n          )\n        },\n      },\n\n      size: { width: 0, height: 0, top: 0, left: 0 },\n      viewport: {\n        initialDpr: 0,\n        dpr: 0,\n        width: 0,\n        height: 0,\n        top: 0,\n        left: 0,\n        aspect: 0,\n        distance: 0,\n        factor: 0,\n        getCurrentViewport,\n      },\n\n      setEvents: (events: Partial<EventManager<any>>) =>\n        set((state) => ({ ...state, events: { ...state.events, ...events } })),\n      setSize: (width: number, height: number, top: number = 0, left: number = 0) => {\n        const camera = get().camera\n        const size = { width, height, top, left }\n        set((state) => ({ size, viewport: { ...state.viewport, ...getCurrentViewport(camera, defaultTarget, size) } }))\n      },\n      setDpr: (dpr: Dpr) =>\n        set((state) => {\n          const resolved = calculateDpr(dpr)\n          return { viewport: { ...state.viewport, dpr: resolved, initialDpr: state.viewport.initialDpr || resolved } }\n        }),\n      setFrameloop: (frameloop: Frameloop = 'always') => {\n        const clock = get().clock\n\n        // if frameloop === \"never\" clock.elapsedTime is updated using advance(timestamp)\n        clock.stop()\n        clock.elapsedTime = 0\n\n        if (frameloop !== 'never') {\n          clock.start()\n          clock.elapsedTime = 0\n        }\n        set(() => ({ frameloop }))\n      },\n      previousRoot: undefined,\n      internal: {\n        // Events\n        interaction: [],\n        hovered: new Map<string, ThreeEvent<DomEvent>>(),\n        subscribers: [],\n        initialClick: [0, 0],\n        initialHits: [],\n        capturedMap: new Map(),\n        lastEvent: React.createRef(),\n\n        // Updates\n        active: false,\n        frames: 0,\n        priority: 0,\n        subscribe: (ref: React.RefObject<RenderCallback>, priority: number, store: RootStore) => {\n          const internal = get().internal\n          // If this subscription was given a priority, it takes rendering into its own hands\n          // For that reason we switch off automatic rendering and increase the manual flag\n          // As long as this flag is positive there can be no internal rendering at all\n          // because there could be multiple render subscriptions\n          internal.priority = internal.priority + (priority > 0 ? 1 : 0)\n          internal.subscribers.push({ ref, priority, store })\n          // Register subscriber and sort layers from lowest to highest, meaning,\n          // highest priority renders last (on top of the other frames)\n          internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority)\n          return () => {\n            const internal = get().internal\n            if (internal?.subscribers) {\n              // Decrease manual flag if this subscription had a priority\n              internal.priority = internal.priority - (priority > 0 ? 1 : 0)\n              // Remove subscriber from list\n              internal.subscribers = internal.subscribers.filter((s) => s.ref !== ref)\n            }\n          }\n        },\n      },\n    }\n\n    return rootState\n  })\n\n  const state = rootStore.getState()\n\n  let oldSize = state.size\n  let oldDpr = state.viewport.dpr\n  let oldCamera = state.camera\n  rootStore.subscribe(() => {\n    const { camera, size, viewport, gl, set } = rootStore.getState()\n\n    // Resize camera and renderer on changes to size and pixelratio\n    if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {\n      oldSize = size\n      oldDpr = viewport.dpr\n      // Update camera & renderer\n      updateCamera(camera, size)\n      if (viewport.dpr > 0) gl.setPixelRatio(viewport.dpr)\n\n      const updateStyle = typeof HTMLCanvasElement !== 'undefined' && gl.domElement instanceof HTMLCanvasElement\n      gl.setSize(size.width, size.height, updateStyle)\n    }\n\n    // Update viewport once the camera changes\n    if (camera !== oldCamera) {\n      oldCamera = camera\n      // Update viewport\n      set((state) => ({ viewport: { ...state.viewport, ...state.viewport.getCurrentViewport(camera) } }))\n    }\n  })\n\n  // Invalidate on any change\n  rootStore.subscribe((state) => invalidate(state))\n\n  // Return root state\n  return rootStore\n}\n"
  },
  {
    "path": "packages/fiber/src/core/utils.tsx",
    "content": "import * as THREE from 'three'\nimport * as React from 'react'\nimport { useFiber, traverseFiber, useContextBridge } from 'its-fine'\nimport { Instance } from './reconciler'\nimport type { EventHandlers } from './events'\nimport type { Dpr, Renderer, RootStore, Size } from './store'\n\nexport type NonFunctionKeys<P> = { [K in keyof P]-?: P[K] extends Function ? never : K }[keyof P]\nexport type Overwrite<P, O> = Omit<P, NonFunctionKeys<O>> & O\nexport type Properties<T> = Pick<T, NonFunctionKeys<T>>\nexport type Mutable<P> = { [K in keyof P]: P[K] | Readonly<P[K]> }\nexport type IsOptional<T> = undefined extends T ? true : false\nexport type IsAllOptional<T extends any[]> = T extends [infer First, ...infer Rest]\n  ? IsOptional<First> extends true\n    ? IsAllOptional<Rest>\n    : false\n  : true\n\n/**\n * Returns the instance's initial (outmost) root.\n */\nexport function findInitialRoot<T>(instance: Instance<T>): RootStore {\n  let root = instance.root\n  while (root.getState().previousRoot) root = root.getState().previousRoot!\n  return root\n}\n\nexport type Act = <T = any>(cb: () => Promise<T>) => Promise<T>\n\n/**\n * Safely flush async effects when testing, simulating a legacy root.\n * @deprecated Import from React instead. import { act } from 'react'\n */\n// Reference with computed key to break Webpack static analysis\n// https://github.com/webpack/webpack/issues/14814\nexport const act: Act = React[('act' + '') as 'act']\n\nexport type Camera = (THREE.OrthographicCamera | THREE.PerspectiveCamera) & { manual?: boolean }\nexport const isOrthographicCamera = (def: Camera): def is THREE.OrthographicCamera =>\n  def && (def as THREE.OrthographicCamera).isOrthographicCamera\nexport const isRef = (obj: any): obj is React.RefObject<unknown> => obj && obj.hasOwnProperty('current')\n\nexport const isColorRepresentation = (value: unknown): value is THREE.ColorRepresentation =>\n  value != null && (typeof value === 'string' || typeof value === 'number' || (value as THREE.Color).isColor)\n\n/**\n * An SSR-friendly useLayoutEffect.\n *\n * React currently throws a warning when using useLayoutEffect on the server.\n * To get around it, we can conditionally useEffect on the server (no-op) and\n * useLayoutEffect elsewhere.\n *\n * @see https://github.com/facebook/react/issues/14927\n */\nexport const useIsomorphicLayoutEffect = /* @__PURE__ */ (() =>\n  typeof window !== 'undefined' && (window.document?.createElement || window.navigator?.product === 'ReactNative'))()\n  ? React.useLayoutEffect\n  : React.useEffect\n\nexport function useMutableCallback<T>(fn: T): React.RefObject<T> {\n  const ref = React.useRef<T>(fn)\n  useIsomorphicLayoutEffect(() => void (ref.current = fn), [fn])\n  return ref\n}\n\nexport type Bridge = React.FC<{ children?: React.ReactNode }>\n\n/**\n * Bridges renderer Context and StrictMode from a primary renderer.\n */\nexport function useBridge(): Bridge {\n  const fiber = useFiber()\n  const ContextBridge = useContextBridge()\n\n  return React.useMemo(\n    () =>\n      ({ children }) => {\n        const strict = !!traverseFiber(fiber, true, (node) => node.type === React.StrictMode)\n        const Root = strict ? React.StrictMode : React.Fragment\n\n        return (\n          <Root>\n            <ContextBridge>{children}</ContextBridge>\n          </Root>\n        )\n      },\n    [fiber, ContextBridge],\n  )\n}\n\nexport type SetBlock = false | Promise<null> | null\nexport type UnblockProps = { set: React.Dispatch<React.SetStateAction<SetBlock>>; children: React.ReactNode }\n\nexport function Block({ set }: Omit<UnblockProps, 'children'>) {\n  useIsomorphicLayoutEffect(() => {\n    set(new Promise(() => null))\n    return () => set(false)\n  }, [set])\n  return null\n}\n\n// NOTE: static members get down-level transpiled to mutations which break tree-shaking\nexport const ErrorBoundary = /* @__PURE__ */ (() =>\n  class ErrorBoundary extends React.Component<\n    { set: React.Dispatch<Error | undefined>; children: React.ReactNode },\n    { error: boolean }\n  > {\n    state = { error: false }\n    static getDerivedStateFromError = () => ({ error: true })\n    componentDidCatch(err: Error) {\n      this.props.set(err)\n    }\n    render() {\n      return this.state.error ? null : this.props.children\n    }\n  })()\n\nexport interface ObjectMap {\n  nodes: { [name: string]: THREE.Object3D }\n  materials: { [name: string]: THREE.Material }\n  meshes: { [name: string]: THREE.Mesh }\n}\n\nexport function calculateDpr(dpr: Dpr): number {\n  // Err on the side of progress by assuming 2x dpr if we can't detect it\n  // This will happen in workers where window is defined but dpr isn't.\n  const target = typeof window !== 'undefined' ? window.devicePixelRatio ?? 2 : 1\n  return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], target), dpr[1]) : dpr\n}\n\n/**\n * Returns instance root state\n */\nexport function getRootState<T extends THREE.Object3D = THREE.Object3D>(obj: T) {\n  return (obj as Instance<T>['object']).__r3f?.root.getState()\n}\n\nexport interface EquConfig {\n  /** Compare arrays by reference equality a === b (default), or by shallow equality */\n  arrays?: 'reference' | 'shallow'\n  /** Compare objects by reference equality a === b (default), or by shallow equality */\n  objects?: 'reference' | 'shallow'\n  /** If true the keys in both a and b must match 1:1 (default), if false a's keys must intersect b's */\n  strict?: boolean\n}\n\n// A collection of compare functions\nexport const is = {\n  obj: (a: any) => a === Object(a) && !is.arr(a) && typeof a !== 'function',\n  fun: (a: any): a is Function => typeof a === 'function',\n  str: (a: any): a is string => typeof a === 'string',\n  num: (a: any): a is number => typeof a === 'number',\n  boo: (a: any): a is boolean => typeof a === 'boolean',\n  und: (a: any) => a === void 0,\n  nul: (a: any) => a === null,\n  arr: (a: any) => Array.isArray(a),\n  equ(a: any, b: any, { arrays = 'shallow', objects = 'reference', strict = true }: EquConfig = {}) {\n    // Wrong type or one of the two undefined, doesn't match\n    if (typeof a !== typeof b || !!a !== !!b) return false\n    // Atomic, just compare a against b\n    if (is.str(a) || is.num(a) || is.boo(a)) return a === b\n    const isObj = is.obj(a)\n    if (isObj && objects === 'reference') return a === b\n    const isArr = is.arr(a)\n    if (isArr && arrays === 'reference') return a === b\n    // Array or Object, shallow compare first to see if it's a match\n    if ((isArr || isObj) && a === b) return true\n    // Last resort, go through keys\n    let i\n    // Check if a has all the keys of b\n    for (i in a) if (!(i in b)) return false\n    // Check if values between keys match\n    if (isObj && arrays === 'shallow' && objects === 'shallow') {\n      for (i in strict ? b : a) if (!is.equ(a[i], b[i], { strict, objects: 'reference' })) return false\n    } else {\n      for (i in strict ? b : a) if (a[i] !== b[i]) return false\n    }\n    // If i is undefined\n    if (is.und(i)) {\n      // If both arrays are empty we consider them equal\n      if (isArr && a.length === 0 && b.length === 0) return true\n      // If both objects are empty we consider them equal\n      if (isObj && Object.keys(a).length === 0 && Object.keys(b).length === 0) return true\n      // Otherwise match them by value\n      if (a !== b) return false\n    }\n    return true\n  },\n}\n\n// Collects nodes and materials from a THREE.Object3D\nexport function buildGraph(object: THREE.Object3D): ObjectMap {\n  const data: ObjectMap = { nodes: {}, materials: {}, meshes: {} }\n  if (object) {\n    object.traverse((obj: any) => {\n      if (obj.name) data.nodes[obj.name] = obj\n      if (obj.material && !data.materials[obj.material.name]) data.materials[obj.material.name] = obj.material\n      if (obj.isMesh && !data.meshes[obj.name]) data.meshes[obj.name] = obj\n    })\n  }\n  return data\n}\n\nexport interface Disposable {\n  type?: string\n  dispose?: () => void\n}\n\n// Disposes an object and all its properties\nexport function dispose<T extends Disposable>(obj: T): void {\n  if (obj.type !== 'Scene') obj.dispose?.()\n  for (const p in obj) {\n    const prop = obj[p] as Disposable | undefined\n    if (prop?.type !== 'Scene') prop?.dispose?.()\n  }\n}\n\nexport const REACT_INTERNAL_PROPS = ['children', 'key', 'ref']\n\n// Gets only instance props from reconciler fibers\nexport function getInstanceProps<T = any>(pendingProps: Record<string, unknown>): Instance<T>['props'] {\n  const props: Instance<T>['props'] = {}\n\n  for (const key in pendingProps) {\n    if (!REACT_INTERNAL_PROPS.includes(key)) props[key] = pendingProps[key]\n  }\n\n  return props\n}\n\n// Each object in the scene carries a small LocalState descriptor\nexport function prepare<T = any>(target: T, root: RootStore, type: string, props: Instance<T>['props']): Instance<T> {\n  const object = target as unknown as Instance['object']\n\n  // Create instance descriptor\n  let instance = object?.__r3f\n  if (!instance) {\n    instance = {\n      root,\n      type,\n      parent: null,\n      children: [],\n      props: getInstanceProps(props),\n      object,\n      eventCount: 0,\n      handlers: {},\n      isHidden: false,\n    }\n\n    if (object) object.__r3f = instance\n  }\n\n  return instance\n}\n\nexport function resolve(root: any, key: string): { root: any; key: string; target: any } {\n  if (!key.includes('-')) return { root, key, target: root[key] }\n\n  // First try the entire key as a single property (e.g., 'foo-bar')\n  if (key in root) {\n    return { root, key, target: root[key] }\n  }\n\n  // Try piercing (e.g., 'material-color' -> material.color)\n  let target = root\n  const parts = key.split('-')\n\n  for (const part of parts) {\n    if (typeof target !== 'object' || target === null) {\n      if (target !== undefined) {\n        // Property exists but has unexpected shape\n        const remaining = parts.slice(parts.indexOf(part)).join('-')\n        return { root: target, key: remaining, target: undefined }\n      }\n      // Property doesn't exist - fallback to original key\n      return { root, key, target: undefined }\n    }\n    key = part\n    root = target\n    target = target[key]\n  }\n\n  return { root, key, target }\n}\n\n// Checks if a dash-cased string ends with an integer\nconst INDEX_REGEX = /-\\d+$/\n\nexport function attach(parent: Instance, child: Instance): void {\n  if (is.str(child.props.attach)) {\n    // If attaching into an array (foo-0), create one\n    if (INDEX_REGEX.test(child.props.attach)) {\n      const index = child.props.attach.replace(INDEX_REGEX, '')\n      const { root, key } = resolve(parent.object, index)\n      if (!Array.isArray(root[key])) root[key] = []\n    }\n\n    const { root, key } = resolve(parent.object, child.props.attach)\n    child.previousAttach = root[key]\n    root[key] = child.object\n  } else if (is.fun(child.props.attach)) {\n    child.previousAttach = child.props.attach(parent.object, child.object)\n  }\n}\n\nexport function detach(parent: Instance, child: Instance): void {\n  if (is.str(child.props.attach)) {\n    const { root, key } = resolve(parent.object, child.props.attach)\n    const previous = child.previousAttach\n    // When the previous value was undefined, it means the value was never set to begin with\n    if (previous === undefined) delete root[key]\n    // Otherwise set the previous value\n    else root[key] = previous\n  } else {\n    child.previousAttach?.(parent.object, child.object)\n  }\n\n  delete child.previousAttach\n}\n\nexport const RESERVED_PROPS = [\n  ...REACT_INTERNAL_PROPS,\n  // Instance props\n  'args',\n  'dispose',\n  'attach',\n  'object',\n  'onUpdate',\n  // Behavior flags\n  'dispose',\n]\n\nconst MEMOIZED_PROTOTYPES = new Map()\n\nfunction getMemoizedPrototype(root: any) {\n  let ctor = MEMOIZED_PROTOTYPES.get(root.constructor)\n  try {\n    if (!ctor) {\n      ctor = new root.constructor()\n      MEMOIZED_PROTOTYPES.set(root.constructor, ctor)\n    }\n  } catch (e) {\n    // ...\n  }\n  return ctor\n}\n\n// This function prepares a set of changes to be applied to the instance\nexport function diffProps<T = any>(instance: Instance<T>, newProps: Instance<T>['props']): Instance<T>['props'] {\n  const changedProps: Instance<T>['props'] = {}\n\n  // Sort through props\n  for (const prop in newProps) {\n    // Skip reserved keys\n    if (RESERVED_PROPS.includes(prop)) continue\n    // Skip if props match\n    if (is.equ(newProps[prop], instance.props[prop])) continue\n\n    // Props changed, add them\n    changedProps[prop] = newProps[prop]\n\n    // Reset pierced props\n    for (const other in newProps) {\n      if (other.startsWith(`${prop}-`)) changedProps[other] = newProps[other]\n    }\n  }\n\n  // Reset removed props for HMR\n  for (const prop in instance.props) {\n    if (RESERVED_PROPS.includes(prop) || newProps.hasOwnProperty(prop)) continue\n\n    const { root, key } = resolve(instance.object, prop)\n\n    // https://github.com/mrdoob/three.js/issues/21209\n    // HMR/fast-refresh relies on the ability to cancel out props, but threejs\n    // has no means to do this. Hence we curate a small collection of value-classes\n    // with their respective constructor/set arguments\n    // For removed props, try to set default values, if possible\n    if (root.constructor && root.constructor.length === 0) {\n      // create a blank slate of the instance and copy the particular parameter.\n      const ctor = getMemoizedPrototype(root)\n      if (!is.und(ctor)) changedProps[key] = ctor[key]\n    } else {\n      // instance does not have constructor, just set it to 0\n      changedProps[key] = 0\n    }\n  }\n\n  return changedProps\n}\n\n// https://github.com/mrdoob/three.js/pull/27042\n// https://github.com/mrdoob/three.js/pull/22748\nconst colorMaps = ['map', 'emissiveMap', 'sheenColorMap', 'specularColorMap', 'envMap']\n\nconst EVENT_REGEX = /^on(Pointer|Click|DoubleClick|ContextMenu|Wheel)/\n\ntype ClassConstructor = { new (): void }\n\n// This function applies a set of changes to the instance\nexport function applyProps<T = any>(object: Instance<T>['object'], props: Instance<T>['props']): Instance<T>['object'] {\n  const instance = object.__r3f\n  const rootState = instance && findInitialRoot(instance).getState()\n  const prevHandlers = instance?.eventCount\n\n  for (const prop in props) {\n    let value = props[prop]\n\n    // Don't mutate reserved keys\n    if (RESERVED_PROPS.includes(prop)) continue\n\n    // Deal with pointer events, including removing them if undefined\n    if (instance && EVENT_REGEX.test(prop)) {\n      if (typeof value === 'function') instance.handlers[prop as keyof EventHandlers] = value as any\n      else delete instance.handlers[prop as keyof EventHandlers]\n      instance.eventCount = Object.keys(instance.handlers).length\n      continue\n    }\n\n    // Ignore setting undefined props\n    // https://github.com/pmndrs/react-three-fiber/issues/274\n    if (value === undefined) continue\n\n    let { root, key, target } = resolve(object, prop)\n\n    // Throw an error if we attempted to set a pierced prop to a non-object\n    if (target === undefined && (typeof root !== 'object' || root === null)) {\n      throw Error(`R3F: Cannot set \"${prop}\". Ensure it is an object before setting \"${key}\".`)\n    }\n\n    // Layers must be written to the mask property\n    if (target instanceof THREE.Layers && value instanceof THREE.Layers) {\n      target.mask = value.mask\n    }\n    // Set colors if valid color representation for automatic conversion (copy)\n    else if (target instanceof THREE.Color && isColorRepresentation(value)) {\n      target.set(value)\n    }\n    // Copy if properties match signatures and implement math interface (likely read-only)\n    else if (\n      target !== null &&\n      typeof target === 'object' &&\n      typeof target.set === 'function' &&\n      typeof target.copy === 'function' &&\n      (value as ClassConstructor | null)?.constructor &&\n      (target as ClassConstructor).constructor === (value as ClassConstructor).constructor\n    ) {\n      target.copy(value)\n    }\n    // Set array types\n    else if (\n      target !== null &&\n      typeof target === 'object' &&\n      typeof target.set === 'function' &&\n      Array.isArray(value)\n    ) {\n      if (typeof target.fromArray === 'function') target.fromArray(value)\n      else target.set(...value)\n    }\n    // Set literal types\n    else if (\n      target !== null &&\n      typeof target === 'object' &&\n      typeof target.set === 'function' &&\n      typeof value === 'number'\n    ) {\n      // Allow setting array scalars\n      if (typeof target.setScalar === 'function') target.setScalar(value)\n      // Otherwise just set single value\n      else target.set(value)\n    }\n    // Else, just overwrite the value\n    else {\n      root[key] = value\n\n      // Auto-convert sRGB texture parameters for built-in materials\n      // https://github.com/pmndrs/react-three-fiber/issues/344\n      // https://github.com/mrdoob/three.js/pull/25857\n      if (\n        rootState &&\n        !rootState.linear &&\n        colorMaps.includes(key) &&\n        (root[key] as unknown as THREE.Texture | undefined)?.isTexture &&\n        // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129\n        root[key].format === THREE.RGBAFormat &&\n        root[key].type === THREE.UnsignedByteType\n      ) {\n        // NOTE: this cannot be set from the renderer (e.g. sRGB source textures rendered to P3)\n        root[key].colorSpace = THREE.SRGBColorSpace\n      }\n    }\n  }\n\n  // Register event handlers\n  if (\n    instance?.parent &&\n    rootState?.internal &&\n    (instance.object as unknown as THREE.Object3D | undefined)?.isObject3D &&\n    prevHandlers !== instance.eventCount\n  ) {\n    const object = instance.object as unknown as THREE.Object3D\n    // Pre-emptively remove the instance from the interaction manager\n    const index = rootState.internal.interaction.indexOf(object)\n    if (index > -1) rootState.internal.interaction.splice(index, 1)\n    // Add the instance to the interaction manager only when it has handlers\n    if (instance.eventCount && object.raycast !== null) {\n      rootState.internal.interaction.push(object)\n    }\n  }\n\n  // Auto-attach geometries and materials\n  if (instance && instance.props.attach === undefined) {\n    if ((instance.object as unknown as THREE.BufferGeometry).isBufferGeometry) instance.props.attach = 'geometry'\n    else if ((instance.object as unknown as THREE.Material).isMaterial) instance.props.attach = 'material'\n  }\n\n  // Instance was updated, request a frame\n  if (instance) invalidateInstance(instance)\n\n  return object\n}\n\nexport function invalidateInstance(instance: Instance): void {\n  if (!instance.parent) return\n\n  instance.props.onUpdate?.(instance.object)\n\n  const state = instance.root?.getState?.()\n  if (state && state.internal.frames === 0) state.invalidate()\n}\n\nexport function updateCamera(camera: Camera, size: Size): void {\n  // Do not mess with the camera if it belongs to the user\n  // https://github.com/pmndrs/react-three-fiber/issues/92\n  if (camera.manual) return\n\n  if (isOrthographicCamera(camera)) {\n    camera.left = size.width / -2\n    camera.right = size.width / 2\n    camera.top = size.height / 2\n    camera.bottom = size.height / -2\n  } else {\n    camera.aspect = size.width / size.height\n  }\n\n  camera.updateProjectionMatrix()\n}\n\nexport const isObject3D = (object: any): object is THREE.Object3D => object?.isObject3D\n"
  },
  {
    "path": "packages/fiber/src/index.tsx",
    "content": "import * as ReactThreeFiber from './three-types'\nexport { ReactThreeFiber }\nexport * from './three-types'\nexport * from './core'\nexport * from './web/Canvas'\nexport { createPointerEvents as events } from './web/events'\n"
  },
  {
    "path": "packages/fiber/src/native/Canvas.tsx",
    "content": "import * as React from 'react'\nimport * as THREE from 'three'\nimport {\n  View,\n  type ViewProps,\n  type ViewStyle,\n  type GestureResponderHandlers,\n  type GestureResponderEvent,\n  PanResponder,\n  type LayoutChangeEvent,\n  StyleSheet,\n  PixelRatio,\n} from 'react-native'\nimport { ExpoWebGLRenderingContext, GLView } from 'expo-gl'\nimport { FiberProvider } from 'its-fine'\nimport { SetBlock, Block, ErrorBoundary, useMutableCallback, useBridge, useIsomorphicLayoutEffect } from '../core/utils'\nimport { extend, createRoot, unmountComponentAtNode, RenderProps, ReconcilerRoot } from '../core'\nimport { createPointerEvents } from '../web/events'\nimport { RootState, Size } from '../core/store'\n\n// TODO: React 19 needs support from react-native\nconst _View = View as any\n\nexport interface CanvasProps extends Omit<RenderProps<HTMLCanvasElement>, 'size' | 'dpr'>, Omit<ViewProps, 'children'> {\n  children?: React.ReactNode\n  style?: ViewStyle\n  ref?: React.Ref<View>\n}\n\nfunction CanvasImpl({\n  children,\n  style,\n  gl,\n  events = createPointerEvents,\n  shadows,\n  linear,\n  flat,\n  legacy,\n  orthographic,\n  frameloop,\n  performance,\n  raycaster,\n  camera,\n  scene,\n  onPointerMissed,\n  onCreated,\n  ref,\n  pointerEvents,\n  ...props\n}: CanvasProps) {\n  // Create a known catalogue of Threejs-native elements\n  // This will include the entire THREE namespace by default, users can extend\n  // their own elements by using the createRoot API instead\n  React.useMemo(() => extend(THREE as any), [])\n\n  const Bridge = useBridge()\n\n  const [{ width, height, top, left }, setSize] = React.useState<Size>({ width: 0, height: 0, top: 0, left: 0 })\n  const [canvas, setCanvas] = React.useState<HTMLCanvasElement | null>(null)\n  const [bind, setBind] = React.useState<GestureResponderHandlers>()\n  React.useImperativeHandle(ref, () => viewRef.current)\n\n  const handlePointerMissed = useMutableCallback(onPointerMissed)\n  const [block, setBlock] = React.useState<SetBlock>(false)\n  const [error, setError] = React.useState<Error | undefined>(undefined)\n\n  // Suspend this component if block is a promise (2nd run)\n  if (block) throw block\n  // Throw exception outwards if anything within canvas throws\n  if (error) throw error\n\n  const viewRef = React.useRef<View>(null!)\n  const root = React.useRef<ReconcilerRoot<HTMLCanvasElement>>(null!)\n\n  const [antialias, setAntialias] = React.useState<boolean>(true)\n\n  const onLayout = React.useCallback((e: LayoutChangeEvent) => {\n    const { width, height, x, y } = e.nativeEvent.layout\n    setSize({ width, height, top: y, left: x })\n  }, [])\n\n  // Called on context create or swap\n  // https://github.com/pmndrs/react-three-fiber/pull/2297\n  const onContextCreate = React.useCallback((context: ExpoWebGLRenderingContext) => {\n    const listeners = new Map<string, EventListener[]>()\n\n    const canvas = {\n      style: {},\n      width: context.drawingBufferWidth,\n      height: context.drawingBufferHeight,\n      clientWidth: context.drawingBufferWidth,\n      clientHeight: context.drawingBufferHeight,\n      getContext: (_: any, { antialias = false }) => {\n        setAntialias(antialias)\n        return context\n      },\n      addEventListener(type: string, listener: EventListener) {\n        let callbacks = listeners.get(type)\n        if (!callbacks) {\n          callbacks = []\n          listeners.set(type, callbacks)\n        }\n\n        callbacks.push(listener)\n      },\n      removeEventListener(type: string, listener: EventListener) {\n        const callbacks = listeners.get(type)\n        if (callbacks) {\n          const index = callbacks.indexOf(listener)\n          if (index !== -1) callbacks.splice(index, 1)\n        }\n      },\n      dispatchEvent(event: Event) {\n        Object.assign(event, { target: this })\n\n        const callbacks = listeners.get(event.type)\n        if (callbacks) {\n          for (const callback of callbacks) {\n            callback(event)\n          }\n        }\n      },\n      setPointerCapture() {\n        // TODO\n      },\n      releasePointerCapture() {\n        // TODO\n      },\n    } as unknown as HTMLCanvasElement\n\n    // TODO: this is wrong but necessary to trick controls\n    // @ts-ignore\n    canvas.ownerDocument = canvas\n    canvas.getRootNode = () => canvas\n\n    root.current = createRoot<HTMLCanvasElement>(canvas)\n    setCanvas(canvas)\n\n    function handleTouch(gestureEvent: GestureResponderEvent, type: string): true {\n      gestureEvent.persist()\n\n      canvas.dispatchEvent(\n        Object.assign(gestureEvent.nativeEvent, {\n          type,\n          offsetX: gestureEvent.nativeEvent.locationX,\n          offsetY: gestureEvent.nativeEvent.locationY,\n          pointerType: 'touch',\n          pointerId: gestureEvent.nativeEvent.identifier,\n        }) as unknown as Event,\n      )\n\n      return true\n    }\n\n    const responder = PanResponder.create({\n      onStartShouldSetPanResponder: () => true,\n      onMoveShouldSetPanResponder: () => true,\n      onMoveShouldSetPanResponderCapture: () => true,\n      onPanResponderTerminationRequest: () => true,\n      onStartShouldSetPanResponderCapture: (e) => handleTouch(e, 'pointercapture'),\n      onPanResponderStart: (e) => handleTouch(e, 'pointerdown'),\n      onPanResponderMove: (e) => handleTouch(e, 'pointermove'),\n      onPanResponderEnd: (e, state) => {\n        handleTouch(e, 'pointerup')\n        if (Math.hypot(state.dx, state.dy) < 20) handleTouch(e, 'click')\n      },\n      onPanResponderRelease: (e) => handleTouch(e, 'pointerleave'),\n      onPanResponderTerminate: (e) => handleTouch(e, 'lostpointercapture'),\n      onPanResponderReject: (e) => handleTouch(e, 'lostpointercapture'),\n    })\n    setBind(responder.panHandlers)\n  }, [])\n\n  useIsomorphicLayoutEffect(() => {\n    if (root.current && width > 0 && height > 0) {\n      async function run() {\n        await root.current.configure({\n          gl,\n          events,\n          shadows,\n          linear,\n          flat,\n          legacy,\n          orthographic,\n          frameloop,\n          performance,\n          raycaster,\n          camera,\n          scene,\n          // expo-gl can only render at native dpr/resolution\n          // https://github.com/expo/expo-three/issues/39\n          dpr: PixelRatio.get(),\n          size: { width, height, top, left },\n          // Pass mutable reference to onPointerMissed so it's free to update\n          onPointerMissed: (...args) => handlePointerMissed.current?.(...args),\n          // Overwrite onCreated to apply RN bindings\n          onCreated: (state: RootState) => {\n            // Bind render to RN bridge\n            const context = state.gl.getContext() as ExpoWebGLRenderingContext\n            const renderFrame = state.gl.render.bind(state.gl)\n            state.gl.render = (scene: THREE.Scene, camera: THREE.Camera) => {\n              renderFrame(scene, camera)\n              context.endFrameEXP()\n            }\n\n            return onCreated?.(state)\n          },\n        })\n        root.current.render(\n          <Bridge>\n            <ErrorBoundary set={setError}>\n              <React.Suspense fallback={<Block set={setBlock} />}>{children ?? null}</React.Suspense>\n            </ErrorBoundary>\n          </Bridge>,\n        )\n      }\n      run()\n    }\n  })\n\n  React.useEffect(() => {\n    if (canvas) {\n      return () => unmountComponentAtNode(canvas!)\n    }\n  }, [canvas])\n\n  return (\n    <_View {...props} ref={viewRef} onLayout={onLayout} style={{ flex: 1, ...style }}>\n      {width > 0 && (\n        <GLView msaaSamples={antialias ? 4 : 0} onContextCreate={onContextCreate} style={StyleSheet.absoluteFill} />\n      )}\n      <_View style={StyleSheet.absoluteFill} pointerEvents={pointerEvents} {...bind} />\n    </_View>\n  )\n}\n\n/**\n * A native canvas which accepts threejs elements as children.\n * @see https://docs.pmnd.rs/react-three-fiber/api/canvas\n */\nexport function Canvas(props: CanvasProps) {\n  return (\n    <FiberProvider>\n      <CanvasImpl {...props} />\n    </FiberProvider>\n  )\n}\n"
  },
  {
    "path": "packages/fiber/src/native/events.ts",
    "content": "import { RootState, RootStore } from '../core/store'\nimport { createEvents, DomEvent, EventManager, Events } from '../core/events'\nimport { type GestureResponderEvent, PanResponder } from 'react-native'\n\n/** Default R3F event manager for react-native */\nexport function createTouchEvents(store: RootStore): EventManager<HTMLElement> {\n  const { handlePointer } = createEvents(store)\n\n  const handleTouch = (event: GestureResponderEvent, name: string): true => {\n    event.persist()\n\n    // Apply offset\n    ;(event as any).nativeEvent.offsetX = event.nativeEvent.locationX\n    ;(event as any).nativeEvent.offsetY = event.nativeEvent.locationY\n\n    // Emulate DOM event\n    const callback = handlePointer(name)\n    callback(event.nativeEvent as any)\n\n    return true\n  }\n\n  const responder = PanResponder.create({\n    onStartShouldSetPanResponder: () => true,\n    onMoveShouldSetPanResponder: () => true,\n    onMoveShouldSetPanResponderCapture: () => true,\n    onPanResponderTerminationRequest: () => true,\n    onStartShouldSetPanResponderCapture: (e) => handleTouch(e, 'onPointerCapture'),\n    onPanResponderStart: (e) => handleTouch(e, 'onPointerDown'),\n    onPanResponderMove: (e) => handleTouch(e, 'onPointerMove'),\n    onPanResponderEnd: (e, state) => {\n      handleTouch(e, 'onPointerUp')\n      if (Math.hypot(state.dx, state.dy) < 20) handleTouch(e, 'onClick')\n    },\n    onPanResponderRelease: (e) => handleTouch(e, 'onPointerLeave'),\n    onPanResponderTerminate: (e) => handleTouch(e, 'onLostPointerCapture'),\n    onPanResponderReject: (e) => handleTouch(e, 'onLostPointerCapture'),\n  })\n\n  return {\n    priority: 1,\n    enabled: true,\n    compute(event: DomEvent, state: RootState, previous?: RootState) {\n      // https://github.com/pmndrs/react-three-fiber/pull/782\n      // Events trigger outside of canvas when moved, use offsetX/Y by default and allow overrides\n      state.pointer.set((event.offsetX / state.size.width) * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1)\n      state.raycaster.setFromCamera(state.pointer, state.camera)\n    },\n\n    connected: undefined,\n    handlers: responder.panHandlers as unknown as Events,\n    update: () => {\n      const { events, internal } = store.getState()\n      if (internal.lastEvent?.current && events.handlers) {\n        handlePointer('onPointerMove')(internal.lastEvent.current)\n      }\n    },\n    connect: () => {\n      const { set, events } = store.getState()\n      events.disconnect?.()\n\n      set((state) => ({ events: { ...state.events, connected: true } }))\n    },\n    disconnect: () => {\n      const { set } = store.getState()\n\n      set((state) => ({ events: { ...state.events, connected: false } }))\n    },\n  }\n}\n"
  },
  {
    "path": "packages/fiber/src/native/polyfills.ts",
    "content": "import * as THREE from 'three'\nimport { Image, NativeModules, Platform } from 'react-native'\nimport { Asset } from 'expo-asset'\nimport { fromByteArray } from 'base64-js'\nimport { Buffer } from 'buffer'\n\n// Conditionally import expo-file-system/legacy to support Expo 54\nconst getFileSystem = () => {\n  try {\n    return require('expo-file-system/legacy')\n  } catch {\n    return require('expo-file-system')\n  }\n}\nconst fs = getFileSystem()\n\n// http://stackoverflow.com/questions/105034\nfunction uuidv4() {\n  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n    const r = (Math.random() * 16) | 0,\n      v = c == 'x' ? r : (r & 0x3) | 0x8\n    return v.toString(16)\n  })\n}\n\nasync function getAsset(input: string | number): Promise<string> {\n  if (typeof input === 'string') {\n    // Don't process storage\n    if (input.startsWith('file:')) return input\n\n    // Unpack Blobs from react-native BlobManager\n    // https://github.com/facebook/react-native/issues/22681#issuecomment-523258955\n    if (input.startsWith('blob:') || input.startsWith(NativeModules.BlobModule?.BLOB_URI_SCHEME)) {\n      const blob = await new Promise<Blob>((res, rej) => {\n        const xhr = new XMLHttpRequest()\n        xhr.open('GET', input as string)\n        xhr.responseType = 'blob'\n        xhr.onload = () => res(xhr.response)\n        xhr.onerror = rej\n        xhr.send()\n      })\n\n      const data = await new Promise<string>((res, rej) => {\n        const reader = new FileReader()\n        reader.onload = () => res(reader.result as string)\n        reader.onerror = rej\n        reader.readAsText(blob)\n      })\n\n      input = `data:${blob.type};base64,${data}`\n    }\n\n    // Create safe URI for JSI serialization\n    if (input.startsWith('data:')) {\n      const [header, data] = input.split(';base64,')\n      const [, type] = header.split('/')\n\n      const uri = fs.cacheDirectory + uuidv4() + `.${type}`\n      await fs.writeAsStringAsync(uri, data, { encoding: fs.EncodingType.Base64 })\n\n      return uri\n    }\n  }\n\n  // Download bundler module or external URL\n  const asset = await Asset.fromModule(input).downloadAsync()\n  let uri = asset.localUri || asset.uri\n\n  // Unpack assets in Android Release Mode\n  if (!uri.includes(':')) {\n    const file = `${fs.cacheDirectory}ExponentAsset-${asset.hash}.${asset.type}`\n    await fs.copyAsync({ from: uri, to: file })\n    uri = file\n  }\n\n  return uri\n}\n\nexport function polyfills() {\n  // Patch Blob for ArrayBuffer and URL if unsupported\n  // https://github.com/facebook/react-native/pull/39276\n  // https://github.com/pmndrs/react-three-fiber/issues/3058\n  if (Platform.OS !== 'web') {\n    try {\n      const blob = new Blob([new ArrayBuffer(4) as any])\n      const url = URL.createObjectURL(blob)\n      URL.revokeObjectURL(url)\n    } catch (_) {\n      const BlobManagerModule = require('react-native/Libraries/Blob/BlobManager.js')\n      const BlobManager = BlobManagerModule.default ?? BlobManagerModule\n\n      const createObjectURL = URL.createObjectURL\n      URL.createObjectURL = function (blob: Blob): string {\n        if ((blob as any).data._base64) {\n          return `data:${blob.type};base64,${(blob as any).data._base64}`\n        }\n\n        return createObjectURL(blob)\n      }\n\n      const createFromParts = BlobManager.createFromParts\n      BlobManager.createFromParts = function (parts: Array<Blob | BlobPart | string>, options: any) {\n        parts = parts.map((part) => {\n          if (part instanceof ArrayBuffer || ArrayBuffer.isView(part)) {\n            part = fromByteArray(new Uint8Array(part as ArrayBuffer))\n          }\n\n          return part\n        })\n\n        const blob = createFromParts(parts, options)\n\n        // Always enable slow but safe path for iOS (previously for Android unauth)\n        // https://github.com/pmndrs/react-three-fiber/issues/3075\n        // if (!NativeModules.BlobModule?.BLOB_URI_SCHEME) {\n        blob.data._base64 = ''\n        for (const part of parts) {\n          blob.data._base64 += (part as any).data?._base64 ?? part\n        }\n        // }\n\n        return blob\n      }\n    }\n  }\n\n  // Don't pre-process urls, let expo-asset generate an absolute URL\n  const extractUrlBase = THREE.LoaderUtils.extractUrlBase.bind(THREE.LoaderUtils)\n  THREE.LoaderUtils.extractUrlBase = (url: string) => (typeof url === 'string' ? extractUrlBase(url) : './')\n\n  // There's no Image in native, so create a data texture instead\n  THREE.TextureLoader.prototype.load = function load(this: THREE.TextureLoader, url, onLoad, onProgress, onError) {\n    if (this.path && typeof url === 'string') url = this.path + url\n\n    const texture = new THREE.Texture()\n\n    getAsset(url)\n      .then(async (uri) => {\n        // https://github.com/expo/expo-three/pull/266\n        const { width, height } = await new Promise<{ width: number; height: number }>((res, rej) =>\n          Image.getSize(uri, (width, height) => res({ width, height }), rej),\n        )\n\n        texture.image = {\n          // Special case for EXGLImageUtils::loadImage\n          data: { localUri: uri },\n          width,\n          height,\n        }\n        texture.flipY = true // Since expo-gl@12.4.0\n        texture.needsUpdate = true\n\n        // Force non-DOM upload for EXGL texImage2D\n        // @ts-expect-error\n        texture.isDataTexture = true\n\n        onLoad?.(texture)\n      })\n      .catch(onError)\n\n    return texture\n  }\n\n  // Fetches assets via FS\n  THREE.FileLoader.prototype.load = function load(this: THREE.FileLoader, url, onLoad, onProgress, onError) {\n    if (this.path && typeof url === 'string') url = this.path + url\n\n    this.manager.itemStart(url)\n\n    getAsset(url)\n      .then(async (uri) => {\n        const base64 = await fs.readAsStringAsync(uri, { encoding: fs.EncodingType.Base64 })\n        const data = Buffer.from(base64, 'base64')\n        onLoad?.(data.buffer)\n      })\n      .catch((error) => {\n        onError?.(error)\n        this.manager.itemError(url)\n      })\n      .finally(() => {\n        this.manager.itemEnd(url)\n      })\n  }\n}\n"
  },
  {
    "path": "packages/fiber/src/native.tsx",
    "content": "import * as ReactThreeFiber from './three-types'\nexport { ReactThreeFiber }\nexport * from './three-types'\nexport * from './core'\nexport * from './native/Canvas'\nexport { createTouchEvents as events } from './native/events'\n\nimport { Platform } from 'react-native'\nimport { polyfills } from './native/polyfills'\n\nif (Platform.OS !== 'web') polyfills()\n"
  },
  {
    "path": "packages/fiber/src/three-types.ts",
    "content": "import type * as THREE from 'three'\nimport type {} from 'react'\nimport type {} from 'react/jsx-runtime'\nimport type {} from 'react/jsx-dev-runtime'\nimport type { Args, EventHandlers, InstanceProps, ConstructorRepresentation } from './core'\nimport type { Overwrite, Mutable } from './core/utils'\n\ntype MutableOrReadonlyParameters<T extends (...args: any) => any> = Parameters<T> | Readonly<Parameters<T>>\n\nexport interface MathRepresentation {\n  set(...args: number[]): any\n}\nexport interface VectorRepresentation extends MathRepresentation {\n  setScalar(value: number): any\n}\nexport type MathTypes = MathRepresentation | THREE.Euler | THREE.Color\n\nexport type MathType<T extends MathTypes> = T extends THREE.Color\n  ? Args<typeof THREE.Color> | THREE.ColorRepresentation\n  : T extends VectorRepresentation | THREE.Layers | THREE.Euler\n  ? T | MutableOrReadonlyParameters<T['set']> | number\n  : T | MutableOrReadonlyParameters<T['set']>\n\nexport type MathProps<P> = {\n  [K in keyof P as P[K] extends MathTypes ? K : never]: P[K] extends MathTypes ? MathType<P[K]> : never\n}\n\nexport type Vector2 = MathType<THREE.Vector2>\nexport type Vector3 = MathType<THREE.Vector3>\nexport type Vector4 = MathType<THREE.Vector4>\nexport type Color = MathType<THREE.Color>\nexport type Layers = MathType<THREE.Layers>\nexport type Quaternion = MathType<THREE.Quaternion>\nexport type Euler = MathType<THREE.Euler>\nexport type Matrix3 = MathType<THREE.Matrix3>\nexport type Matrix4 = MathType<THREE.Matrix4>\n\nexport interface RaycastableRepresentation {\n  raycast(raycaster: THREE.Raycaster, intersects: THREE.Intersection[]): void\n}\nexport type EventProps<P> = P extends RaycastableRepresentation ? Partial<EventHandlers> : {}\n\nexport interface ReactProps<P> {\n  children?: React.ReactNode\n  ref?: React.Ref<P>\n  key?: React.Key\n}\n\nexport type ElementProps<T extends ConstructorRepresentation, P = InstanceType<T>> = Partial<\n  Overwrite<P, MathProps<P> & ReactProps<P> & EventProps<P>>\n>\n\nexport type ThreeElement<T extends ConstructorRepresentation> = Mutable<\n  Overwrite<ElementProps<T>, Omit<InstanceProps<InstanceType<T>, T>, 'object'>>\n>\n\nexport type ThreeToJSXElements<T extends Record<string, any>> = {\n  [K in keyof T & string as Uncapitalize<K>]: T[K] extends ConstructorRepresentation ? ThreeElement<T[K]> : never\n}\n\ntype ThreeExports = typeof THREE\ntype ThreeElementsImpl = ThreeToJSXElements<ThreeExports>\n\nexport interface ThreeElements extends Omit<ThreeElementsImpl, 'audio' | 'source' | 'line' | 'path'> {\n  primitive: Omit<ThreeElement<any>, 'args'> & { object: object }\n  // Conflicts with DOM types can be accessed through a three* prefix\n  threeAudio: ThreeElementsImpl['audio']\n  threeSource: ThreeElementsImpl['source']\n  threeLine: ThreeElementsImpl['line']\n  threePath: ThreeElementsImpl['path']\n}\n\ndeclare module 'react' {\n  namespace JSX {\n    interface IntrinsicElements extends ThreeElements {}\n  }\n}\n\ndeclare module 'react/jsx-runtime' {\n  namespace JSX {\n    interface IntrinsicElements extends ThreeElements {}\n  }\n}\n\ndeclare module 'react/jsx-dev-runtime' {\n  namespace JSX {\n    interface IntrinsicElements extends ThreeElements {}\n  }\n}\n"
  },
  {
    "path": "packages/fiber/src/web/Canvas.tsx",
    "content": "import * as React from 'react'\nimport * as THREE from 'three'\nimport useMeasure, { Options as ResizeOptions } from 'react-use-measure'\nimport { FiberProvider } from 'its-fine'\nimport {\n  isRef,\n  SetBlock,\n  Block,\n  ErrorBoundary,\n  useMutableCallback,\n  useIsomorphicLayoutEffect,\n  useBridge,\n} from '../core/utils'\nimport { ReconcilerRoot, extend, createRoot, unmountComponentAtNode, RenderProps } from '../core'\nimport { createPointerEvents } from './events'\nimport { DomEvent } from '../core/events'\n\nexport interface CanvasProps\n  extends Omit<RenderProps<HTMLCanvasElement>, 'size'>,\n    React.HTMLAttributes<HTMLDivElement> {\n  children?: React.ReactNode\n  ref?: React.Ref<HTMLCanvasElement>\n  /** Canvas fallback content, similar to img's alt prop */\n  fallback?: React.ReactNode\n  /**\n   * Options to pass to useMeasure.\n   * @see https://github.com/pmndrs/react-use-measure#api\n   */\n  resize?: ResizeOptions\n  /** The target where events are being subscribed to, default: the div that wraps canvas */\n  eventSource?: HTMLElement | React.RefObject<HTMLElement>\n  /** The event prefix that is cast into canvas pointer x/y events, default: \"offset\" */\n  eventPrefix?: 'offset' | 'client' | 'page' | 'layer' | 'screen'\n}\n\nfunction CanvasImpl({\n  ref,\n  children,\n  fallback,\n  resize,\n  style,\n  gl,\n  events = createPointerEvents,\n  eventSource,\n  eventPrefix,\n  shadows,\n  linear,\n  flat,\n  legacy,\n  orthographic,\n  frameloop,\n  dpr,\n  performance,\n  raycaster,\n  camera,\n  scene,\n  onPointerMissed,\n  onCreated,\n  ...props\n}: CanvasProps) {\n  // Create a known catalogue of Threejs-native elements\n  // This will include the entire THREE namespace by default, users can extend\n  // their own elements by using the createRoot API instead\n  React.useMemo(() => extend(THREE as any), [])\n\n  const Bridge = useBridge()\n\n  const [containerRef, containerRect] = useMeasure({ scroll: true, debounce: { scroll: 50, resize: 0 }, ...resize })\n  const canvasRef = React.useRef<HTMLCanvasElement>(null!)\n  const divRef = React.useRef<HTMLDivElement>(null!)\n  React.useImperativeHandle(ref, () => canvasRef.current)\n\n  const handlePointerMissed = useMutableCallback(onPointerMissed)\n  const [block, setBlock] = React.useState<SetBlock>(false)\n  const [error, setError] = React.useState<any>(false)\n\n  // Suspend this component if block is a promise (2nd run)\n  if (block) throw block\n  // Throw exception outwards if anything within canvas throws\n  if (error) throw error\n\n  const root = React.useRef<ReconcilerRoot<HTMLCanvasElement>>(null!)\n\n  useIsomorphicLayoutEffect(() => {\n    const canvas = canvasRef.current\n    if (containerRect.width > 0 && containerRect.height > 0 && canvas) {\n      if (!root.current) root.current = createRoot<HTMLCanvasElement>(canvas)\n\n      async function run() {\n        await root.current.configure({\n          gl,\n          scene,\n          events,\n          shadows,\n          linear,\n          flat,\n          legacy,\n          orthographic,\n          frameloop,\n          dpr,\n          performance,\n          raycaster,\n          camera,\n          size: containerRect,\n          // Pass mutable reference to onPointerMissed so it's free to update\n          onPointerMissed: (...args) => handlePointerMissed.current?.(...args),\n          onCreated: (state) => {\n            // Connect to event source\n            state.events.connect?.(\n              eventSource ? (isRef(eventSource) ? eventSource.current : eventSource) : divRef.current,\n            )\n            // Set up compute function\n            if (eventPrefix) {\n              state.setEvents({\n                compute: (event, state) => {\n                  const x = event[(eventPrefix + 'X') as keyof DomEvent] as number\n                  const y = event[(eventPrefix + 'Y') as keyof DomEvent] as number\n                  state.pointer.set((x / state.size.width) * 2 - 1, -(y / state.size.height) * 2 + 1)\n                  state.raycaster.setFromCamera(state.pointer, state.camera)\n                },\n              })\n            }\n            // Call onCreated callback\n            onCreated?.(state)\n          },\n        })\n        root.current.render(\n          <Bridge>\n            <ErrorBoundary set={setError}>\n              <React.Suspense fallback={<Block set={setBlock} />}>{children ?? null}</React.Suspense>\n            </ErrorBoundary>\n          </Bridge>,\n        )\n      }\n      run()\n    }\n  })\n\n  React.useEffect(() => {\n    const canvas = canvasRef.current\n    if (canvas) return () => unmountComponentAtNode(canvas)\n  }, [])\n\n  // When the event source is not this div, we need to set pointer-events to none\n  // Or else the canvas will block events from reaching the event source\n  const pointerEvents = eventSource ? 'none' : 'auto'\n\n  return (\n    <div\n      ref={divRef}\n      style={{\n        position: 'relative',\n        width: '100%',\n        height: '100%',\n        overflow: 'hidden',\n        pointerEvents,\n        ...style,\n      }}\n      {...props}>\n      <div ref={containerRef} style={{ width: '100%', height: '100%' }}>\n        <canvas ref={canvasRef} style={{ display: 'block' }}>\n          {fallback}\n        </canvas>\n      </div>\n    </div>\n  )\n}\n\n/**\n * A DOM canvas which accepts threejs elements as children.\n * @see https://docs.pmnd.rs/react-three-fiber/api/canvas\n */\nexport function Canvas(props: CanvasProps) {\n  return (\n    <FiberProvider>\n      <CanvasImpl {...props} />\n    </FiberProvider>\n  )\n}\n"
  },
  {
    "path": "packages/fiber/src/web/events.ts",
    "content": "import { RootState, RootStore } from '../core/store'\nimport { EventManager, Events, createEvents, DomEvent } from '../core/events'\n\nconst DOM_EVENTS = {\n  onClick: ['click', false],\n  onContextMenu: ['contextmenu', false],\n  onDoubleClick: ['dblclick', false],\n  onWheel: ['wheel', true],\n  onPointerDown: ['pointerdown', true],\n  onPointerUp: ['pointerup', true],\n  onPointerLeave: ['pointerleave', true],\n  onPointerMove: ['pointermove', true],\n  onPointerCancel: ['pointercancel', true],\n  onLostPointerCapture: ['lostpointercapture', true],\n} as const\n\n/** Default R3F event manager for web */\nexport function createPointerEvents(store: RootStore): EventManager<HTMLElement> {\n  const { handlePointer } = createEvents(store)\n\n  return {\n    priority: 1,\n    enabled: true,\n    compute(event: DomEvent, state: RootState, previous?: RootState) {\n      // https://github.com/pmndrs/react-three-fiber/pull/782\n      // Events trigger outside of canvas when moved, use offsetX/Y by default and allow overrides\n      state.pointer.set((event.offsetX / state.size.width) * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1)\n      state.raycaster.setFromCamera(state.pointer, state.camera)\n    },\n\n    connected: undefined,\n    handlers: Object.keys(DOM_EVENTS).reduce(\n      (acc, key) => ({ ...acc, [key]: handlePointer(key) }),\n      {},\n    ) as unknown as Events,\n    update: () => {\n      const { events, internal } = store.getState()\n      if (internal.lastEvent?.current && events.handlers) events.handlers.onPointerMove(internal.lastEvent.current)\n    },\n    connect: (target: HTMLElement) => {\n      const { set, events } = store.getState()\n      events.disconnect?.()\n      set((state) => ({ events: { ...state.events, connected: target } }))\n      if (events.handlers) {\n        for (const name in events.handlers) {\n          const event = events.handlers[name as keyof typeof events.handlers]\n          const [eventName, passive] = DOM_EVENTS[name as keyof typeof DOM_EVENTS]\n          target.addEventListener(eventName, event, { passive })\n        }\n      }\n    },\n    disconnect: () => {\n      const { set, events } = store.getState()\n      if (events.connected) {\n        if (events.handlers) {\n          for (const name in events.handlers) {\n            const event = events.handlers[name as keyof typeof events.handlers]\n            const [eventName] = DOM_EVENTS[name as keyof typeof DOM_EVENTS]\n            events.connected.removeEventListener(eventName, event)\n          }\n        }\n        set((state) => ({ events: { ...state.events, connected: undefined } }))\n      }\n    },\n  }\n}\n"
  },
  {
    "path": "packages/fiber/tests/__snapshots__/canvas.native.test.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`native Canvas should correctly mount 1`] = `\"{\\\\\"type\\\\\":\\\\\"view\\\\\",\\\\\"props\\\\\":{\\\\\"style\\\\\":{\\\\\"flex\\\\\":1}},\\\\\"children\\\\\":[{\\\\\"type\\\\\":\\\\\"glview\\\\\",\\\\\"props\\\\\":{\\\\\"msaaSamples\\\\\":4,\\\\\"style\\\\\":{\\\\\"position\\\\\":\\\\\"absolute\\\\\",\\\\\"left\\\\\":0,\\\\\"right\\\\\":0,\\\\\"top\\\\\":0,\\\\\"bottom\\\\\":0}},\\\\\"children\\\\\":[]},{\\\\\"type\\\\\":\\\\\"view\\\\\",\\\\\"props\\\\\":{\\\\\"style\\\\\":{\\\\\"position\\\\\":\\\\\"absolute\\\\\",\\\\\"left\\\\\":0,\\\\\"right\\\\\":0,\\\\\"top\\\\\":0,\\\\\"bottom\\\\\":0}},\\\\\"children\\\\\":[]}]}\"`;\n"
  },
  {
    "path": "packages/fiber/tests/__snapshots__/canvas.test.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`web Canvas should correctly mount 1`] = `\n<div>\n  <div\n    style=\"position: relative; width: 100%; height: 100%; overflow: hidden; pointer-events: auto;\"\n  >\n    <div\n      style=\"width: 100%; height: 100%;\"\n    >\n      <canvas\n        data-engine=\"three.js r172\"\n        height=\"800\"\n        style=\"display: block; width: 1280px; height: 800px;\"\n        width=\"1280\"\n      />\n    </div>\n  </div>\n</div>\n`;\n"
  },
  {
    "path": "packages/fiber/tests/__snapshots__/index.test.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`exports matches public API 1`] = `\nArray [\n  \"Act\",\n  \"Args\",\n  \"AttachFnType\",\n  \"AttachType\",\n  \"Camera\",\n  \"CameraProps\",\n  \"Canvas\",\n  \"CanvasProps\",\n  \"Catalogue\",\n  \"Color\",\n  \"ComputeFunction\",\n  \"ConstructorRepresentation\",\n  \"Disposable\",\n  \"DomEvent\",\n  \"Dpr\",\n  \"ElementProps\",\n  \"Euler\",\n  \"EventHandlers\",\n  \"EventManager\",\n  \"EventProps\",\n  \"Events\",\n  \"Extensions\",\n  \"FilterFunction\",\n  \"Frameloop\",\n  \"GLProps\",\n  \"GlobalEffectType\",\n  \"GlobalRenderCallback\",\n  \"InjectState\",\n  \"Instance\",\n  \"InstanceProps\",\n  \"Intersection\",\n  \"Layers\",\n  \"LoaderResult\",\n  \"MathProps\",\n  \"MathRepresentation\",\n  \"MathType\",\n  \"MathTypes\",\n  \"Matrix3\",\n  \"Matrix4\",\n  \"ObjectMap\",\n  \"Performance\",\n  \"Quaternion\",\n  \"RaycastableRepresentation\",\n  \"ReactProps\",\n  \"ReactThreeFiber\",\n  \"ReconcilerRoot\",\n  \"RenderCallback\",\n  \"RenderProps\",\n  \"Renderer\",\n  \"RootState\",\n  \"RootStore\",\n  \"Size\",\n  \"Subscription\",\n  \"ThreeElement\",\n  \"ThreeElements\",\n  \"ThreeEvent\",\n  \"ThreeToJSXElements\",\n  \"Vector2\",\n  \"Vector3\",\n  \"Vector4\",\n  \"VectorRepresentation\",\n  \"Viewport\",\n  \"XRManager\",\n  \"_roots\",\n  \"act\",\n  \"addAfterEffect\",\n  \"addEffect\",\n  \"addTail\",\n  \"advance\",\n  \"applyProps\",\n  \"buildGraph\",\n  \"context\",\n  \"createEvents\",\n  \"createPortal\",\n  \"createRoot\",\n  \"dispose\",\n  \"events\",\n  \"extend\",\n  \"flushGlobalEffects\",\n  \"flushSync\",\n  \"getRootState\",\n  \"invalidate\",\n  \"reconciler\",\n  \"unmountComponentAtNode\",\n  \"useFrame\",\n  \"useGraph\",\n  \"useInstanceHandle\",\n  \"useLoader\",\n  \"useStore\",\n  \"useThree\",\n]\n`;\n"
  },
  {
    "path": "packages/fiber/tests/__snapshots__/utils.test.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`updateCamera updates camera matrices 1`] = `\nArray [\n  2.1445069205095586,\n  0,\n  0,\n  0,\n  0,\n  2.1445069205095586,\n  0,\n  0,\n  0,\n  0,\n  -1.00010000500025,\n  -1,\n  0,\n  0,\n  -0.200010000500025,\n  0,\n]\n`;\n\nexports[`updateCamera updates camera matrices 2`] = `\nArray [\n  1,\n  0,\n  0,\n  0,\n  0,\n  1,\n  0,\n  0,\n  0,\n  0,\n  -0.001000050002500125,\n  0,\n  -0,\n  -0,\n  -1.00010000500025,\n  1,\n]\n`;\n"
  },
  {
    "path": "packages/fiber/tests/canvas.native.test.tsx",
    "content": "import * as React from 'react'\nimport { View } from 'react-native'\n// @ts-ignore TS2305 remove with modern TS config\nimport { render } from 'react-nil'\nimport { Canvas, act } from '../src/native'\n\ndescribe('native Canvas', () => {\n  it('should correctly mount', async () => {\n    const container = await act(async () =>\n      render(\n        <Canvas>\n          <group />\n        </Canvas>,\n      ),\n    )\n\n    expect(JSON.stringify(container.head)).toMatchSnapshot()\n  })\n\n  it('should forward ref', async () => {\n    const ref = React.createRef<View>()\n\n    await act(async () =>\n      render(\n        <Canvas ref={ref}>\n          <group />\n        </Canvas>,\n      ),\n    )\n\n    expect(ref.current).toBeInstanceOf(View)\n  })\n\n  it('should forward context', async () => {\n    const ParentContext = React.createContext<boolean>(null!)\n    let receivedValue!: boolean\n\n    function Test() {\n      receivedValue = React.useContext(ParentContext)\n      return null\n    }\n\n    await act(async () => {\n      render(\n        <ParentContext.Provider value={true}>\n          <Canvas>\n            <Test />\n          </Canvas>\n        </ParentContext.Provider>,\n      )\n    })\n\n    expect(receivedValue).toBe(true)\n  })\n\n  it('should correctly unmount', async () => {\n    await act(async () =>\n      render(\n        <Canvas>\n          <group />\n        </Canvas>,\n      ),\n    )\n\n    expect(async () => await act(async () => render(null))).not.toThrow()\n  })\n})\n"
  },
  {
    "path": "packages/fiber/tests/canvas.test.tsx",
    "content": "import React from 'react'\nimport { render } from '@testing-library/react'\nimport { Canvas, act } from '../src'\n\ndescribe('web Canvas', () => {\n  it('should correctly mount', async () => {\n    const renderer = await act(async () =>\n      render(\n        <Canvas>\n          <group />\n        </Canvas>,\n      ),\n    )\n\n    expect(renderer.container).toMatchSnapshot()\n  })\n\n  it('should forward ref', async () => {\n    const ref = React.createRef<HTMLCanvasElement>()\n\n    await act(async () =>\n      render(\n        <Canvas ref={ref}>\n          <group />\n        </Canvas>,\n      ),\n    )\n\n    expect(ref.current).toBeInstanceOf(HTMLCanvasElement)\n  })\n\n  it('should forward context', async () => {\n    const ParentContext = React.createContext<boolean>(null!)\n    let receivedValue!: boolean\n\n    function Test() {\n      receivedValue = React.useContext(ParentContext)\n      return null\n    }\n\n    await act(async () => {\n      render(\n        <ParentContext.Provider value={true}>\n          <Canvas>\n            <Test />\n          </Canvas>\n        </ParentContext.Provider>,\n      )\n    })\n\n    expect(receivedValue).toBe(true)\n  })\n\n  it('should correctly unmount', async () => {\n    const renderer = await act(async () =>\n      render(\n        <Canvas>\n          <group />\n        </Canvas>,\n      ),\n    )\n\n    expect(() => renderer.unmount()).not.toThrow()\n  })\n\n  it('plays nice with react SSR', async () => {\n    const useLayoutEffect = jest.spyOn(React, 'useLayoutEffect')\n\n    await act(async () =>\n      render(\n        <Canvas>\n          <group />\n        </Canvas>,\n      ),\n    )\n\n    expect(useLayoutEffect).not.toHaveBeenCalled()\n  })\n})\n"
  },
  {
    "path": "packages/fiber/tests/events.test.tsx",
    "content": "import * as React from 'react'\nimport { render, fireEvent, RenderResult } from '@testing-library/react'\nimport { Canvas, act, extend } from '../src'\nimport THREE from 'three'\n\nextend(THREE as any)\n\nconst getContainer = () => document.querySelector('canvas')?.parentNode?.parentNode as HTMLDivElement\n\ndescribe('events', () => {\n  it('can handle onPointerDown', async () => {\n    const handlePointerDown = jest.fn()\n\n    await act(async () => {\n      render(\n        <Canvas>\n          <mesh onPointerDown={handlePointerDown}>\n            <boxGeometry args={[2, 2]} />\n            <meshBasicMaterial />\n          </mesh>\n        </Canvas>,\n      )\n    })\n\n    const evt = new PointerEvent('pointerdown')\n    Object.defineProperty(evt, 'offsetX', { get: () => 577 })\n    Object.defineProperty(evt, 'offsetY', { get: () => 480 })\n\n    fireEvent(getContainer(), evt)\n\n    expect(handlePointerDown).toHaveBeenCalled()\n  })\n\n  it('can handle onPointerMissed', async () => {\n    const handleClick = jest.fn()\n    const handleMissed = jest.fn()\n\n    await act(async () => {\n      render(\n        <Canvas>\n          <mesh onPointerMissed={handleMissed} onClick={handleClick}>\n            <boxGeometry args={[2, 2]} />\n            <meshBasicMaterial />\n          </mesh>\n        </Canvas>,\n      )\n    })\n\n    const evt = new MouseEvent('click')\n    Object.defineProperty(evt, 'offsetX', { get: () => 0 })\n    Object.defineProperty(evt, 'offsetY', { get: () => 0 })\n\n    fireEvent(getContainer(), evt)\n\n    expect(handleClick).not.toHaveBeenCalled()\n    expect(handleMissed).toHaveBeenCalledWith(evt)\n  })\n\n  it('should not fire onPointerMissed when same element is clicked', async () => {\n    const handleClick = jest.fn()\n    const handleMissed = jest.fn()\n\n    await act(async () => {\n      render(\n        <Canvas>\n          <mesh onPointerMissed={handleMissed} onClick={handleClick}>\n            <boxGeometry args={[2, 2]} />\n            <meshBasicMaterial />\n          </mesh>\n        </Canvas>,\n      )\n    })\n\n    const down = new PointerEvent('pointerdown')\n    Object.defineProperty(down, 'offsetX', { get: () => 577 })\n    Object.defineProperty(down, 'offsetY', { get: () => 480 })\n\n    fireEvent(getContainer(), down)\n\n    const up = new PointerEvent('pointerup')\n    Object.defineProperty(up, 'offsetX', { get: () => 577 })\n    Object.defineProperty(up, 'offsetY', { get: () => 480 })\n\n    const evt = new MouseEvent('click')\n    Object.defineProperty(evt, 'offsetX', { get: () => 577 })\n    Object.defineProperty(evt, 'offsetY', { get: () => 480 })\n\n    fireEvent(getContainer(), evt)\n\n    expect(handleClick).toHaveBeenCalled()\n    expect(handleMissed).not.toHaveBeenCalled()\n  })\n\n  it('should not fire onPointerMissed on parent when child element is clicked', async () => {\n    const handleClick = jest.fn()\n    const handleMissed = jest.fn()\n\n    await act(async () => {\n      render(\n        <Canvas>\n          <group onPointerMissed={handleMissed}>\n            <mesh onClick={handleClick}>\n              <boxGeometry args={[2, 2]} />\n              <meshBasicMaterial />\n            </mesh>\n          </group>\n        </Canvas>,\n      )\n    })\n\n    const down = new PointerEvent('pointerdown')\n    Object.defineProperty(down, 'offsetX', { get: () => 577 })\n    Object.defineProperty(down, 'offsetY', { get: () => 480 })\n\n    fireEvent(getContainer(), down)\n\n    const up = new PointerEvent('pointerup')\n    Object.defineProperty(up, 'offsetX', { get: () => 577 })\n    Object.defineProperty(up, 'offsetY', { get: () => 480 })\n\n    const evt = new MouseEvent('click')\n    Object.defineProperty(evt, 'offsetX', { get: () => 577 })\n    Object.defineProperty(evt, 'offsetY', { get: () => 480 })\n\n    fireEvent(getContainer(), evt)\n\n    expect(handleClick).toHaveBeenCalled()\n    expect(handleMissed).not.toHaveBeenCalled()\n  })\n\n  it('can handle onPointerMissed on Canvas', async () => {\n    const handleMissed = jest.fn()\n\n    await act(async () => {\n      render(\n        <Canvas onPointerMissed={handleMissed}>\n          <mesh>\n            <boxGeometry args={[2, 2]} />\n            <meshBasicMaterial />\n          </mesh>\n        </Canvas>,\n      )\n    })\n\n    const evt = new MouseEvent('click')\n    Object.defineProperty(evt, 'offsetX', { get: () => 0 })\n    Object.defineProperty(evt, 'offsetY', { get: () => 0 })\n\n    fireEvent(getContainer(), evt)\n    expect(handleMissed).toHaveBeenCalledWith(evt)\n  })\n\n  it('can handle onPointerMove', async () => {\n    const handlePointerMove = jest.fn()\n    const handlePointerOver = jest.fn()\n    const handlePointerEnter = jest.fn()\n    const handlePointerOut = jest.fn()\n\n    await act(async () => {\n      render(\n        <Canvas>\n          <mesh\n            onPointerOut={handlePointerOut}\n            onPointerEnter={handlePointerEnter}\n            onPointerMove={handlePointerMove}\n            onPointerOver={handlePointerOver}>\n            <boxGeometry args={[2, 2]} />\n            <meshBasicMaterial />\n          </mesh>\n        </Canvas>,\n      )\n    })\n\n    const evt1 = new PointerEvent('pointermove')\n    Object.defineProperty(evt1, 'offsetX', { get: () => 577 })\n    Object.defineProperty(evt1, 'offsetY', { get: () => 480 })\n\n    fireEvent(getContainer(), evt1)\n\n    expect(handlePointerMove).toHaveBeenCalled()\n    expect(handlePointerOver).toHaveBeenCalled()\n    expect(handlePointerEnter).toHaveBeenCalled()\n\n    const evt2 = new PointerEvent('pointermove')\n    Object.defineProperty(evt2, 'offsetX', { get: () => 0 })\n    Object.defineProperty(evt2, 'offsetY', { get: () => 0 })\n\n    fireEvent(getContainer(), evt2)\n\n    expect(handlePointerOut).toHaveBeenCalled()\n  })\n\n  it('should handle stopPropagation', async () => {\n    const handlePointerEnter = jest.fn().mockImplementation((e) => {\n      expect(() => e.stopPropagation()).not.toThrow()\n    })\n    const handlePointerLeave = jest.fn()\n\n    await act(async () => {\n      render(\n        <Canvas>\n          <mesh onPointerLeave={handlePointerLeave} onPointerEnter={handlePointerEnter}>\n            <boxGeometry args={[2, 2]} />\n            <meshBasicMaterial />\n          </mesh>\n          <mesh position-z={3}>\n            <boxGeometry args={[2, 2]} />\n            <meshBasicMaterial />\n          </mesh>\n        </Canvas>,\n      )\n    })\n\n    const evt1 = new PointerEvent('pointermove')\n    Object.defineProperty(evt1, 'offsetX', { get: () => 577 })\n    Object.defineProperty(evt1, 'offsetY', { get: () => 480 })\n\n    fireEvent(getContainer(), evt1)\n\n    expect(handlePointerEnter).toHaveBeenCalled()\n\n    const evt2 = new PointerEvent('pointermove')\n    Object.defineProperty(evt2, 'offsetX', { get: () => 0 })\n    Object.defineProperty(evt2, 'offsetY', { get: () => 0 })\n\n    fireEvent(getContainer(), evt2)\n\n    expect(handlePointerLeave).toHaveBeenCalled()\n  })\n\n  it('should handle stopPropagation on click events', async () => {\n    const handleClickFront = jest.fn((e) => e.stopPropagation())\n    const handleClickRear = jest.fn()\n\n    await act(async () => {\n      render(\n        <Canvas>\n          <mesh onClick={handleClickFront}>\n            <boxGeometry args={[2, 2]} />\n            <meshBasicMaterial />\n          </mesh>\n          <mesh onClick={handleClickRear} position-z={-3}>\n            <boxGeometry args={[2, 2]} />\n            <meshBasicMaterial />\n          </mesh>\n        </Canvas>,\n      )\n    })\n\n    const down = new PointerEvent('pointerdown')\n    Object.defineProperty(down, 'offsetX', { get: () => 577 })\n    Object.defineProperty(down, 'offsetY', { get: () => 480 })\n\n    fireEvent(getContainer(), down)\n\n    const up = new PointerEvent('pointerup')\n    Object.defineProperty(up, 'offsetX', { get: () => 577 })\n    Object.defineProperty(up, 'offsetY', { get: () => 480 })\n\n    fireEvent(getContainer(), up)\n\n    const event = new MouseEvent('click')\n    Object.defineProperty(event, 'offsetX', { get: () => 577 })\n    Object.defineProperty(event, 'offsetY', { get: () => 480 })\n\n    fireEvent(getContainer(), event)\n\n    expect(handleClickFront).toHaveBeenCalled()\n    expect(handleClickRear).not.toHaveBeenCalled()\n  })\n\n  describe('web pointer capture', () => {\n    const handlePointerMove = jest.fn()\n    const handlePointerDown = jest.fn((ev) => (ev.target as any).setPointerCapture(ev.pointerId))\n    const handlePointerUp = jest.fn((ev) => (ev.target as any).releasePointerCapture(ev.pointerId))\n    const handlePointerEnter = jest.fn()\n    const handlePointerLeave = jest.fn()\n\n    /* This component lets us unmount the event-handling object */\n    function PointerCaptureTest(props: { hasMesh: boolean; manualRelease?: boolean }) {\n      return (\n        <Canvas>\n          {props.hasMesh && (\n            <mesh\n              onPointerDown={handlePointerDown}\n              onPointerMove={handlePointerMove}\n              onPointerUp={props.manualRelease ? handlePointerUp : undefined}\n              onPointerLeave={handlePointerLeave}\n              onPointerEnter={handlePointerEnter}>\n              <boxGeometry args={[2, 2]} />\n              <meshBasicMaterial />\n            </mesh>\n          )}\n        </Canvas>\n      )\n    }\n\n    const pointerId = 1234\n\n    it('should release when the capture target is unmounted', async () => {\n      let renderResult: RenderResult = undefined!\n      await act(async () => {\n        renderResult = render(<PointerCaptureTest hasMesh={true} />)\n        return renderResult\n      })\n\n      const canvas = getContainer()\n\n      canvas.setPointerCapture = jest.fn()\n      canvas.releasePointerCapture = jest.fn()\n\n      const down = new PointerEvent('pointerdown', { pointerId })\n      Object.defineProperty(down, 'offsetX', { get: () => 577 })\n      Object.defineProperty(down, 'offsetY', { get: () => 480 })\n\n      /* testing-utils/react's fireEvent wraps the event like React does, so it doesn't match how our event handlers are called in production, so we call dispatchEvent directly. */\n      await act(async () => canvas.dispatchEvent(down))\n\n      /* This should have captured the DOM pointer */\n      expect(handlePointerDown).toHaveBeenCalledTimes(1)\n      expect(canvas.setPointerCapture).toHaveBeenCalledWith(pointerId)\n      expect(canvas.releasePointerCapture).not.toHaveBeenCalled()\n\n      /* Now remove the mesh */\n      await act(async () => renderResult.rerender(<PointerCaptureTest hasMesh={false} />))\n\n      expect(canvas.releasePointerCapture).toHaveBeenCalledWith(pointerId)\n\n      const move = new PointerEvent('pointerdown', { pointerId })\n      Object.defineProperty(move, 'offsetX', { get: () => 577 })\n      Object.defineProperty(move, 'offsetY', { get: () => 480 })\n\n      await act(async () => canvas.dispatchEvent(move))\n\n      /* There should now be no pointer capture */\n      expect(handlePointerMove).not.toHaveBeenCalled()\n    })\n\n    it('should not leave when captured', async () => {\n      let renderResult: RenderResult = undefined!\n      await act(async () => {\n        renderResult = render(<PointerCaptureTest hasMesh manualRelease />)\n        return renderResult\n      })\n\n      const canvas = getContainer()\n      canvas.setPointerCapture = jest.fn()\n      canvas.releasePointerCapture = jest.fn()\n\n      const moveIn = new PointerEvent('pointermove', { pointerId })\n      Object.defineProperty(moveIn, 'offsetX', { get: () => 577 })\n      Object.defineProperty(moveIn, 'offsetY', { get: () => 480 })\n\n      const moveOut = new PointerEvent('pointermove', { pointerId })\n      Object.defineProperty(moveOut, 'offsetX', { get: () => -10000 })\n      Object.defineProperty(moveOut, 'offsetY', { get: () => -10000 })\n\n      /* testing-utils/react's fireEvent wraps the event like React does, so it doesn't match how our event handlers are called in production, so we call dispatchEvent directly. */\n      await act(async () => canvas.dispatchEvent(moveIn))\n      expect(handlePointerEnter).toHaveBeenCalledTimes(1)\n      expect(handlePointerMove).toHaveBeenCalledTimes(1)\n\n      const down = new PointerEvent('pointerdown', { pointerId })\n      Object.defineProperty(down, 'offsetX', { get: () => 577 })\n      Object.defineProperty(down, 'offsetY', { get: () => 480 })\n\n      await act(async () => canvas.dispatchEvent(down))\n\n      // If we move the pointer now, when it is captured, it should raise the onPointerMove event even though the pointer is not over the element,\n      // and NOT raise the onPointerLeave event.\n      await act(async () => canvas.dispatchEvent(moveOut))\n      expect(handlePointerMove).toHaveBeenCalledTimes(2)\n      expect(handlePointerLeave).not.toHaveBeenCalled()\n\n      await act(async () => canvas.dispatchEvent(moveIn))\n      expect(handlePointerMove).toHaveBeenCalledTimes(3)\n\n      const up = new PointerEvent('pointerup', { pointerId })\n      Object.defineProperty(up, 'offsetX', { get: () => 577 })\n      Object.defineProperty(up, 'offsetY', { get: () => 480 })\n      const lostpointercapture = new PointerEvent('lostpointercapture', { pointerId })\n\n      await act(async () => canvas.dispatchEvent(up))\n      await act(async () => canvas.dispatchEvent(lostpointercapture))\n\n      // The pointer is still over the element, so onPointerLeave should not have been called.\n      expect(handlePointerLeave).not.toHaveBeenCalled()\n\n      // The element pointer should no longer be captured, so moving it away should call onPointerLeave.\n      await act(async () => canvas.dispatchEvent(moveOut))\n      expect(handlePointerEnter).toHaveBeenCalledTimes(1)\n      expect(handlePointerLeave).toHaveBeenCalledTimes(1)\n    })\n  })\n\n  it('can handle primitives', async () => {\n    const handlePointerDownOuter = jest.fn()\n    const handlePointerDownInner = jest.fn()\n\n    const object = new THREE.Group()\n    object.add(new THREE.Mesh(new THREE.BoxGeometry(2, 2), new THREE.MeshBasicMaterial()))\n\n    await act(async () => {\n      render(\n        <Canvas>\n          <group onPointerDown={handlePointerDownOuter}>\n            <primitive name=\"test\" object={object} onPointerDown={handlePointerDownInner} />\n          </group>\n        </Canvas>,\n      )\n    })\n\n    const evt = new PointerEvent('pointerdown')\n    Object.defineProperty(evt, 'offsetX', { get: () => 577 })\n    Object.defineProperty(evt, 'offsetY', { get: () => 480 })\n\n    fireEvent(getContainer(), evt)\n\n    expect(handlePointerDownOuter).toHaveBeenCalled()\n    expect(handlePointerDownInner).toHaveBeenCalled()\n  })\n\n  it('can handle a DOM offset canvas', async () => {\n    const handlePointerDown = jest.fn()\n    await act(async () => {\n      render(\n        <Canvas\n          onCreated={(state) => {\n            state.size.left = 100\n            state.size.top = 100\n          }}>\n          <mesh onPointerDown={handlePointerDown}>\n            <boxGeometry args={[2, 2]} />\n            <meshBasicMaterial />\n          </mesh>\n        </Canvas>,\n      )\n    })\n\n    const evt = new PointerEvent('pointerdown')\n    Object.defineProperty(evt, 'offsetX', { get: () => 577 })\n    Object.defineProperty(evt, 'offsetY', { get: () => 480 })\n\n    fireEvent(getContainer(), evt)\n\n    expect(handlePointerDown).toHaveBeenCalled()\n  })\n\n  it.todo('can handle different event prefixes')\n})\n"
  },
  {
    "path": "packages/fiber/tests/hooks.test.tsx",
    "content": "import * as React from 'react'\nimport * as THREE from 'three'\nimport { createCanvas } from '@react-three/test-renderer/src/createTestCanvas'\n\nimport {\n  createRoot,\n  advance,\n  useLoader,\n  act,\n  useThree,\n  useGraph,\n  useFrame,\n  ObjectMap,\n  useInstanceHandle,\n  Instance,\n  extend,\n} from '../src'\n\nextend(THREE as any)\nconst root = createRoot(document.createElement('canvas'))\n\ndescribe('hooks', () => {\n  let canvas: HTMLCanvasElement = null!\n\n  beforeEach(() => {\n    canvas = createCanvas()\n  })\n\n  it('can handle useThree hook', async () => {\n    let result = {} as {\n      camera: THREE.Camera\n      scene: THREE.Scene\n      raycaster: THREE.Raycaster\n      size: { width: number; height: number }\n    }\n\n    const Component = () => {\n      /**\n       * this causes an act problem, it'd be\n       * good to figure out the best way to\n       * resolve this at some point\n       */\n      const res = useThree((state) => ({\n        camera: state.camera,\n        scene: state.scene,\n        size: state.size,\n        raycaster: state.raycaster,\n      }))\n\n      result = res\n\n      return <group />\n    }\n\n    await act(async () => root.render(<Component />))\n\n    expect(result.camera instanceof THREE.Camera).toBeTruthy()\n    expect(result.scene instanceof THREE.Scene).toBeTruthy()\n    expect(result.raycaster instanceof THREE.Raycaster).toBeTruthy()\n    expect(result.size).toEqual({ height: 0, width: 0, top: 0, left: 0 })\n  })\n\n  it('can handle useFrame hook', async () => {\n    const frameCalls: number[] = []\n\n    const Component = () => {\n      const ref = React.useRef<THREE.Mesh>(null!)\n      useFrame((_, delta) => {\n        frameCalls.push(delta)\n        ref.current.position.x = 1\n      })\n\n      return (\n        <mesh ref={ref}>\n          <boxGeometry args={[2, 2]} />\n          <meshBasicMaterial />\n        </mesh>\n      )\n    }\n\n    const store = await act(async () => (await root.configure({ frameloop: 'never' })).render(<Component />))\n    const { scene } = store.getState()\n\n    advance(Date.now())\n    expect(scene.children[0].position.x).toEqual(1)\n    expect(frameCalls.length).toBeGreaterThan(0)\n  })\n\n  it('can handle useLoader hook', async () => {\n    const MockMesh = new THREE.Mesh()\n    MockMesh.name = 'Scene'\n\n    interface GLTF {\n      scene: THREE.Object3D\n    }\n    class GLTFLoader extends THREE.Loader<GLTF, string> {\n      load(url: string, onLoad: (gltf: GLTF) => void): void {\n        onLoad({ scene: MockMesh })\n      }\n    }\n\n    let gltf!: GLTF & ObjectMap\n    const Component = () => {\n      gltf = useLoader(GLTFLoader, '/suzanne.glb')\n      return <primitive object={gltf.scene} />\n    }\n\n    const store = await act(async () => root.render(<Component />))\n    const { scene } = store.getState()\n\n    expect(scene.children[0]).toBe(MockMesh)\n    expect(gltf.scene).toBe(MockMesh)\n    expect(gltf.nodes.Scene).toBe(MockMesh)\n  })\n\n  it('can handle useLoader hook with an array of strings', async () => {\n    const MockMesh = new THREE.Mesh()\n\n    const MockGroup = new THREE.Group()\n    const mat1 = new THREE.MeshBasicMaterial()\n    mat1.name = 'Mat 1'\n    const mesh1 = new THREE.Mesh(new THREE.BoxGeometry(2, 2), mat1)\n    mesh1.name = 'Mesh 1'\n    const mat2 = new THREE.MeshBasicMaterial()\n    mat2.name = 'Mat 2'\n    const mesh2 = new THREE.Mesh(new THREE.BoxGeometry(2, 2), mat2)\n    mesh2.name = 'Mesh 2'\n    MockGroup.add(mesh1, mesh2)\n\n    class TestLoader extends THREE.Loader {\n      load = jest\n        .fn()\n        .mockImplementationOnce((_url, onLoad) => {\n          onLoad(MockMesh)\n        })\n        .mockImplementationOnce((_url, onLoad) => {\n          onLoad(MockGroup)\n        })\n    }\n\n    const extensions = jest.fn()\n\n    const Component = () => {\n      const [mockMesh, mockScene] = useLoader(TestLoader, ['/suzanne.glb', '/myModels.glb'], extensions)\n\n      return (\n        <>\n          <primitive object={mockMesh as THREE.Mesh} />\n          <primitive object={mockScene as THREE.Scene} />\n        </>\n      )\n    }\n\n    const store = await act(async () => root.render(<Component />))\n    const { scene } = store.getState()\n\n    expect(scene.children[0]).toBe(MockMesh)\n    expect(scene.children[1]).toBe(MockGroup)\n    expect(extensions).toHaveBeenCalledTimes(1)\n  })\n\n  it('can handle useLoader with an existing loader instance', async () => {\n    class Loader extends THREE.Loader<null, string> {\n      load(_url: string, onLoad: (result: null) => void): void {\n        onLoad(null)\n      }\n    }\n\n    const loader = new Loader()\n    let proto!: Loader\n\n    function Test(): null {\n      return useLoader(loader, '', (loader) => (proto = loader))\n    }\n    await act(async () => root.render(<Test />))\n\n    expect(proto).toBe(loader)\n  })\n\n  it('can handle useLoader with a loader extension', async () => {\n    class Loader extends THREE.Loader<null, string> {\n      load(_url: string, onLoad: (result: null) => void): void {\n        onLoad(null)\n      }\n    }\n\n    let proto!: Loader\n\n    function Test(): null {\n      return useLoader(Loader, '', (loader) => (proto = loader))\n    }\n    await act(async () => root.render(<Test />))\n\n    expect(proto).toBeInstanceOf(Loader)\n  })\n\n  it('can handle useGraph hook', async () => {\n    const group = new THREE.Group()\n    const mat1 = new THREE.MeshBasicMaterial()\n    mat1.name = 'Mat 1'\n    const mesh1 = new THREE.Mesh(new THREE.BoxGeometry(2, 2), mat1)\n    mesh1.name = 'Mesh 1'\n    const mat2 = new THREE.MeshBasicMaterial()\n    mat2.name = 'Mat 2'\n    const mesh2 = new THREE.Mesh(new THREE.BoxGeometry(2, 2), mat2)\n    mesh2.name = 'Mesh 2'\n    const subGroup = new THREE.Group()\n    const mat3 = new THREE.MeshBasicMaterial()\n    mat3.name = 'Mat 3'\n    const mesh3 = new THREE.Mesh(new THREE.BoxGeometry(2, 2), mat3)\n    mesh3.name = 'Mesh 3'\n    const mat4 = new THREE.MeshBasicMaterial()\n    mat4.name = 'Mat 4'\n    const mesh4 = new THREE.Mesh(new THREE.BoxGeometry(2, 2), mat4)\n    mesh4.name = 'Mesh 4'\n\n    subGroup.add(mesh3, mesh4)\n    group.add(mesh1, mesh2, subGroup)\n\n    let result = {} as ObjectMap\n\n    const Component = () => {\n      const data = useGraph(group)\n      result = data\n      return <mesh />\n    }\n\n    await act(async () => root.render(<Component />))\n\n    expect(result).toEqual({\n      nodes: {\n        [mesh1.name]: mesh1,\n        [mesh2.name]: mesh2,\n        [mesh3.name]: mesh3,\n        [mesh4.name]: mesh4,\n      },\n      materials: {\n        [mat1.name]: mat1,\n        [mat2.name]: mat2,\n        [mat3.name]: mat3,\n        [mat4.name]: mat4,\n      },\n      meshes: {\n        [mesh1.name]: mesh1,\n        [mesh2.name]: mesh2,\n        [mesh3.name]: mesh3,\n        [mesh4.name]: mesh4,\n      },\n    })\n  })\n\n  it('can handle useInstanceHandle hook', async () => {\n    const ref = React.createRef<THREE.Group>()\n    let instance!: React.RefObject<Instance>\n\n    const Component = () => {\n      instance = useInstanceHandle(ref)\n      return <group ref={ref} />\n    }\n    await act(async () => root.render(<Component />))\n\n    expect(instance.current).toBe((ref.current as unknown as Instance<THREE.Group>['object']).__r3f)\n  })\n\n  it('can handle future (19.x) hooks without crashing', async () => {\n    function Component() {\n      // @ts-ignore\n      React.useEffectEvent?.(() => {})\n      return null\n    }\n    expect(async () => await act(async () => root.render(<Component />))).not.toThrow()\n  })\n})\n"
  },
  {
    "path": "packages/fiber/tests/index.test.tsx",
    "content": "import * as React from 'react'\nimport * as THREE from 'three'\nimport ts from 'typescript'\nimport * as path from 'path'\nimport { createCanvas } from '@react-three/test-renderer/src/createTestCanvas'\nimport {\n  ReconcilerRoot,\n  createRoot as createRootImpl,\n  act,\n  useFrame,\n  useThree,\n  createPortal,\n  RootState,\n  RootStore,\n  extend,\n} from '../src/index'\n\nextend(THREE as any)\nlet root: ReconcilerRoot<HTMLCanvasElement> = null!\nconst roots: ReconcilerRoot<HTMLCanvasElement>[] = []\n\nfunction createRoot() {\n  const canvas = createCanvas()\n  const root = createRootImpl(canvas)\n  roots.push(root)\n  return root\n}\n\nbeforeEach(() => (root = createRoot()))\n\nafterEach(async () => {\n  for (const root of roots) {\n    await act(async () => root.unmount())\n  }\n  roots.length = 0\n})\n\ndescribe('createRoot', () => {\n  it('should return a Zustand store', async () => {\n    const store = await act(async () => root.render(null))\n    expect(() => store.getState()).not.toThrow()\n  })\n\n  it('will make an Orthographic Camera & set the position', async () => {\n    const store = await act(async () =>\n      (await root.configure({ orthographic: true, camera: { position: [0, 0, 5] } })).render(<group />),\n    )\n    const { camera } = store.getState()\n\n    expect(camera).toBeInstanceOf(THREE.OrthographicCamera)\n    expect(camera.position.z).toEqual(5)\n  })\n\n  // TODO: deprecate\n  it('should handle an performance changing functions', async () => {\n    let store: RootStore = null!\n    await act(async () => {\n      store = (await root.configure({ dpr: [1, 2], performance: { min: 0.2 } })).render(<group />)\n    })\n\n    expect(store.getState().viewport.initialDpr).toEqual(window.devicePixelRatio)\n    expect(store.getState().performance.min).toEqual(0.2)\n    expect(store.getState().performance.current).toEqual(1)\n\n    await act(async () => {\n      store.getState().setDpr(0.1)\n    })\n\n    expect(store.getState().viewport.dpr).toEqual(0.1)\n\n    jest.useFakeTimers()\n\n    await act(async () => {\n      store.getState().performance.regress()\n      jest.advanceTimersByTime(100)\n    })\n\n    expect(store.getState().performance.current).toEqual(0.2)\n\n    await act(async () => {\n      jest.advanceTimersByTime(200)\n    })\n\n    expect(store.getState().performance.current).toEqual(1)\n\n    jest.useRealTimers()\n  })\n\n  it('should handle the DPR prop reactively', async () => {\n    // Initial clamp\n    const store = await act(async () => (await root.configure({ dpr: [1, 2] })).render(<group />))\n    expect(store.getState().viewport.dpr).toEqual(window.devicePixelRatio)\n\n    // Reactive update\n    await act(async () => store.getState().setDpr(0.1))\n    expect(store.getState().viewport.dpr).toEqual(0.1)\n\n    // Reactive clamp\n    await act(async () => store.getState().setDpr([1, 2]))\n    expect(store.getState().viewport.dpr).toEqual(window.devicePixelRatio)\n  })\n\n  it('should set PCFSoftShadowMap as the default shadow map', async () => {\n    const store = await act(async () => (await root.configure({ shadows: true })).render(<group />))\n    const { gl } = store.getState()\n\n    expect(gl.shadowMap.type).toBe(THREE.PCFSoftShadowMap)\n  })\n\n  it('should set tonemapping to ACESFilmicToneMapping and outputColorSpace to SRGBColorSpace if linear is false', async () => {\n    const store = await act(async () => (await root.configure({ linear: false })).render(<group />))\n    const { gl } = store.getState()\n\n    expect(gl.toneMapping).toBe(THREE.ACESFilmicToneMapping)\n    expect(gl.outputColorSpace).toBe(THREE.SRGBColorSpace)\n  })\n\n  it('should toggle render mode in xr', async () => {\n    let state: RootState = null!\n\n    await act(async () => {\n      state = root.render(<group />).getState()\n      state.gl.xr.isPresenting = true\n      state.gl.xr.dispatchEvent({ type: 'sessionstart' })\n    })\n\n    expect(state.gl.xr.enabled).toEqual(true)\n\n    await act(async () => {\n      state.gl.xr.isPresenting = false\n      state.gl.xr.dispatchEvent({ type: 'sessionend' })\n    })\n\n    expect(state.gl.xr.enabled).toEqual(false)\n  })\n\n  it('should respect frameloop=\"never\" in xr', async () => {\n    let respected = true\n\n    const Test = () => useFrame(() => (respected = false))\n\n    await act(async () => {\n      const state = (await root.configure({ frameloop: 'never' })).render(<Test />).getState()\n      state.gl.xr.isPresenting = true\n      state.gl.xr.dispatchEvent({ type: 'sessionstart' })\n    })\n\n    expect(respected).toEqual(true)\n  })\n\n  it('should set renderer props via gl prop', async () => {\n    const store = await act(async () =>\n      (await root.configure({ gl: { logarithmicDepthBuffer: true } })).render(<group />),\n    )\n    const { gl } = store.getState()\n\n    expect(gl.capabilities.logarithmicDepthBuffer).toBe(true)\n  })\n\n  it('should update scene via scene prop', async () => {\n    let scene: THREE.Scene = null!\n\n    await act(async () => {\n      scene = (await root.configure({ scene: { name: 'test' } })).render(<group />).getState().scene\n    })\n\n    expect(scene.name).toBe('test')\n  })\n\n  it('should set a custom scene via scene prop', async () => {\n    let scene: THREE.Scene = null!\n\n    const prop = new THREE.Scene()\n\n    await act(async () => {\n      scene = (await root.configure({ scene: prop })).render(<group />).getState().scene\n    })\n\n    expect(prop).toBe(scene)\n  })\n\n  it('should set a renderer via gl callback', async () => {\n    class Renderer extends THREE.WebGLRenderer {}\n\n    let gl: Renderer = null!\n    await act(async () => {\n      gl = (await root.configure({ gl: (props) => new Renderer(props) })).render(<group />).getState().gl\n    })\n\n    expect(gl instanceof Renderer).toBe(true)\n  })\n\n  it('should respect color management preferences via gl', async () => {\n    let gl: THREE.WebGLRenderer & { outputColorSpace?: string } = null!\n    let texture: THREE.Texture & { colorSpace?: string } = null!\n\n    let key = 0\n    function Test() {\n      gl = useThree((state) => state.gl)\n      texture = new THREE.Texture()\n      return <meshBasicMaterial key={key++} map={texture} />\n    }\n\n    await act(async () => (await createRoot().configure({ linear: true })).render(<Test />))\n    expect(gl.outputColorSpace).toBe(THREE.LinearSRGBColorSpace)\n    expect(texture.colorSpace).toBe(THREE.NoColorSpace)\n\n    await act(async () => (await createRoot().configure({ linear: false })).render(<Test />))\n    expect(gl.outputColorSpace).toBe(THREE.SRGBColorSpace)\n    expect(texture.colorSpace).toBe(THREE.SRGBColorSpace)\n  })\n\n  it('should respect legacy prop', async () => {\n    THREE.ColorManagement.enabled = true\n\n    await act(async () => {\n      ;(await root.configure({ legacy: true })).render(<group />)\n    })\n    expect(THREE.ColorManagement.enabled).toBe(false)\n\n    await act(async () => {\n      ;(await root.configure({ legacy: false })).render(<group />)\n    })\n    expect(THREE.ColorManagement.enabled).toBe(true)\n  })\n})\n\ndescribe('createPortal', () => {\n  it('should create a state enclave', async () => {\n    const scene = new THREE.Scene()\n\n    let state: RootState = null!\n    let portalState: RootState = null!\n\n    const Normal = () => {\n      const three = useThree()\n      state = three\n\n      return <group />\n    }\n\n    const Portal = () => {\n      const three = useThree()\n      portalState = three\n\n      return <group />\n    }\n\n    await act(async () => {\n      root.render(\n        <>\n          <Normal />\n          {createPortal(<Portal />, scene, { scene })}\n        </>,\n      )\n    })\n\n    // Renders into portal target\n    expect(scene.children.length).not.toBe(0)\n\n    // Creates an isolated state enclave\n    expect(state.scene).not.toBe(scene)\n    expect(portalState.scene).toBe(scene)\n  })\n\n  it('should handle unmounted containers', async () => {\n    let groupHandle!: THREE.Group | null\n    function Test(props: any) {\n      const [group, setGroup] = React.useState(null)\n      groupHandle = group\n\n      return (\n        <group {...props} ref={setGroup}>\n          {group && createPortal(<mesh />, group)}\n        </group>\n      )\n    }\n\n    await act(async () => root.render(<Test key={0} />))\n\n    expect(groupHandle).toBeDefined()\n    const prevUUID = groupHandle!.uuid\n\n    await act(async () => root.render(<Test key={1} />))\n\n    expect(groupHandle).toBeDefined()\n    expect(prevUUID).not.toBe(groupHandle!.uuid)\n  })\n})\n\nfunction getExports(source: string): string[] {\n  const program = ts.createProgram([source], { jsx: ts.JsxEmit.React })\n  const checker = program.getTypeChecker()\n  const sourceFile = program.getSourceFile(source)!\n\n  const sourceFileSymbol = checker.getSymbolAtLocation(sourceFile)!\n  const moduleExports = checker.getExportsOfModule(sourceFileSymbol)\n\n  return moduleExports.map(({ escapedName }) => escapedName).sort() as unknown as string[]\n}\n\ndescribe('exports', () => {\n  it('matches public API', () => {\n    const webExports = getExports(path.resolve(__dirname, '../src/index.tsx'))\n    expect(webExports).toMatchSnapshot()\n  })\n\n  it('are consistent between targets', () => {\n    const webExports = getExports(path.resolve(__dirname, '../src/index.tsx'))\n    const nativeExports = getExports(path.resolve(__dirname, '../src/native.tsx'))\n\n    expect(webExports).toStrictEqual(nativeExports)\n  })\n})\n"
  },
  {
    "path": "packages/fiber/tests/polyfills.test.ts",
    "content": "import * as THREE from 'three'\nimport { polyfills } from '../src/native/polyfills'\n\npolyfills()\n\nconst pixel =\n  'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='\n\ndescribe('polyfills', () => {\n  it('loads images via data textures', async () => {\n    const texture = await new THREE.TextureLoader().loadAsync(pixel)\n    expect((texture as any).isDataTexture).toBe(true)\n    expect(texture.image.width).toBe(1)\n    expect(texture.image.height).toBe(1)\n  })\n\n  it('creates a safe image URI for JSI', async () => {\n    const texture = await new THREE.TextureLoader().loadAsync(pixel)\n    expect(texture.image.data.localUri.startsWith('file:///')).toBe(true)\n  })\n\n  it('unpacks drawables in Android APK', async () => {\n    const texture = await new THREE.TextureLoader().loadAsync('drawable.png')\n    expect(texture.image.data.localUri.includes(':')).toBe(true)\n  })\n\n  it('loads files via the file system', async () => {\n    const asset = 1\n    const file = await new THREE.FileLoader().loadAsync(asset as any)\n    expect(typeof (file as ArrayBuffer).byteLength).toBe('number') // TODO: ArrayBuffer instanceof\n  })\n\n  it('loads files via http', async () => {\n    const file = await new THREE.FileLoader().loadAsync('https://example.com/test.png')\n    expect(typeof (file as ArrayBuffer).byteLength).toBe('number') // TODO: ArrayBuffer instanceof\n  })\n})\n"
  },
  {
    "path": "packages/fiber/tests/reconciler.test.ts",
    "content": "import * as THREE from 'three'\nimport { createCanvas } from '@react-three/test-renderer/src/createTestCanvas'\n\nasync function act<T>(fn: () => Promise<T>) {\n  // Silence act warning since we have a custom act implementation\n  const error = console.error\n  console.error = function (message) {\n    if (/was not wrapped in act/.test(message)) return\n    return error.call(this, arguments)\n  }\n\n  const value = fn()\n\n  return new Promise<T>((res) => {\n    requestAnimationFrame(() => requestAnimationFrame(() => requestAnimationFrame(() => res(value))))\n  }).finally(() => {\n    console.error = error\n  })\n}\n\ndescribe('reconciler', () => {\n  const NODE_ENV = process.env.NODE_ENV\n\n  for (const env of ['development', 'production']) {\n    it(`should work with ${env} builds of React`, async () => {\n      jest.resetModules()\n\n      // @ts-ignore\n      if (typeof window !== 'undefined') delete window.__THREE__\n      process.env.NODE_ENV = env\n\n      const React = await import('react')\n      const R3F = await import('../src/index')\n\n      // Ensure that the correct build was loaded\n      expect(typeof React.act === (env === 'production' ? 'undefined' : 'function'))\n\n      R3F.extend(THREE as any)\n      const canvas = createCanvas()\n      const root = R3F.createRoot(canvas)\n\n      const lifecycle: string[] = []\n\n      const object = {}\n      const ref = React.createRef<{}>()\n\n      function Test() {\n        lifecycle.push('render')\n        React.useImperativeHandle(React.useRef(undefined), () => void lifecycle.push('ref'))\n        React.useInsertionEffect(() => void lifecycle.push('useInsertionEffect'), [])\n        React.useLayoutEffect(() => void lifecycle.push('useLayoutEffect'), [])\n        React.useEffect(() => void lifecycle.push('useEffect'), [])\n        return React.createElement('primitive', { ref, object })\n      }\n      await act(async () => root.render(React.createElement(Test)))\n\n      expect(lifecycle).toStrictEqual(['render', 'useInsertionEffect', 'ref', 'useLayoutEffect', 'useEffect'])\n      expect(ref.current).toBe(object)\n\n      await act(async () => root.unmount())\n      expect(ref.current).toBe(null)\n    })\n  }\n\n  // @ts-ignore\n  if (typeof window !== 'undefined') delete window.__THREE__\n  process.env.NODE_ENV = NODE_ENV\n  jest.resetModules()\n})\n"
  },
  {
    "path": "packages/fiber/tests/renderer.test.tsx",
    "content": "import * as React from 'react'\nimport * as THREE from 'three'\nimport { ReconcilerRoot, createRoot, act, extend, ThreeElement, ThreeElements, flushSync, useThree } from '../src/index'\nimport { suspend } from 'suspend-react'\n\nextend(THREE as any)\n\nclass Mock extends THREE.Group {\n  static instances: string[]\n  constructor(name: string = '') {\n    super()\n    this.name = name\n    Mock.instances.push(name)\n  }\n}\n\ndeclare module '@react-three/fiber' {\n  interface ThreeElements {\n    mock: ThreeElement<typeof Mock>\n    threeRandom: ThreeElement<typeof THREE.Group>\n  }\n}\n\nextend({ Mock })\n\ntype ComponentMesh = THREE.Mesh<THREE.BoxGeometry, THREE.MeshBasicMaterial>\n\nconst expectToThrow = async (callback: () => any, message: string) => {\n  let error: Error | undefined\n  try {\n    await callback()\n  } catch (e) {\n    error = e as Error\n  }\n  expect(error?.message).toBe(message)\n}\n\ndescribe('renderer', () => {\n  let root: ReconcilerRoot<HTMLCanvasElement> = null!\n\n  beforeEach(() => {\n    root = createRoot(document.createElement('canvas'))\n    Mock.instances = []\n  })\n  afterEach(async () => act(async () => root.unmount()))\n\n  it('should render empty JSX', async () => {\n    const store = await act(async () => root.render(null))\n    const { scene } = store.getState()\n\n    expect(scene.children.length).toBe(0)\n  })\n\n  it('should render native elements', async () => {\n    const store = await act(async () => root.render(<group name=\"native\" />))\n    const { scene } = store.getState()\n\n    expect(scene.children.length).toBe(1)\n    expect(scene.children[0]).toBeInstanceOf(THREE.Group)\n    expect(scene.children[0].name).toBe('native')\n  })\n\n  it('should render extended elements', async () => {\n    const store = await act(async () => root.render(<mock name=\"mock\" />))\n    const { scene } = store.getState()\n\n    expect(scene.children.length).toBe(1)\n    expect(scene.children[0]).toBeInstanceOf(Mock)\n    expect(scene.children[0].name).toBe('mock')\n\n    const Component = extend(THREE.Mesh)\n    await act(async () => root.render(<Component />))\n\n    expect(scene.children.length).toBe(1)\n    expect(scene.children[0]).toBeInstanceOf(THREE.Mesh)\n  })\n\n  it('should render primitives', async () => {\n    const object = new THREE.Object3D()\n\n    const store = await act(async () => root.render(<primitive name=\"primitive\" object={object} />))\n    const { scene } = store.getState()\n\n    expect(scene.children.length).toBe(1)\n    expect(scene.children[0]).toBe(object)\n    expect(object.name).toBe('primitive')\n  })\n\n  it('should remove children from primitive when unmounted', async () => {\n    const object = new THREE.Group()\n\n    function Parent({ children, show }: { children: React.ReactNode; show: boolean }) {\n      return show ? <primitive object={object}>{children}</primitive> : null\n    }\n\n    function Component({ show }: { show: boolean }) {\n      return (\n        <Parent show={show}>\n          <group name=\"A\" />\n          <group name=\"B\" />\n        </Parent>\n      )\n    }\n\n    const store = await act(async () => root.render(<Component show={true} />))\n    const { scene } = store.getState()\n\n    expect(scene.children.length).toBe(1)\n    expect(scene.children[0]).toBe(object)\n    expect(object.children.length).toBe(2)\n\n    await act(async () => root.render(<Component show={false} />))\n\n    expect(scene.children.length).toBe(0)\n    expect(object.children.length).toBe(0)\n  })\n\n  it('should remove then add children from primitive when key changes', async () => {\n    const object = new THREE.Group()\n\n    function Parent({ children, primitiveKey }: { children: React.ReactNode; primitiveKey: string }) {\n      return (\n        <primitive key={primitiveKey} object={object}>\n          {children}\n        </primitive>\n      )\n    }\n\n    function Component({ primitiveKey }: { primitiveKey: string }) {\n      return (\n        <Parent primitiveKey={primitiveKey}>\n          <group name=\"A\" />\n          <group name=\"B\" />\n        </Parent>\n      )\n    }\n\n    const store = await act(async () => root.render(<Component primitiveKey=\"A\" />))\n    const { scene } = store.getState()\n\n    expect(scene.children.length).toBe(1)\n    expect(scene.children[0]).toBe(object)\n    expect(object.children.length).toBe(2)\n\n    await act(async () => root.render(<Component primitiveKey=\"B\" />))\n\n    expect(scene.children.length).toBe(1)\n    expect(scene.children[0]).toBe(object)\n    expect(object.children.length).toBe(2)\n  })\n\n  it('should go through lifecycle', async () => {\n    const lifecycle: string[] = []\n\n    function Test() {\n      React.useInsertionEffect(() => void lifecycle.push('useInsertionEffect'), [])\n      React.useImperativeHandle(React.useRef(null), () => {\n        lifecycle.push('refCallback')\n        return null\n      })\n      React.useLayoutEffect(() => void lifecycle.push('useLayoutEffect'), [])\n      React.useEffect(() => void lifecycle.push('useEffect'), [])\n      lifecycle.push('render')\n      return <group ref={() => void lifecycle.push('ref')} />\n    }\n    await act(async () => root.render(<Test />))\n\n    expect(lifecycle).toStrictEqual([\n      'render',\n      'useInsertionEffect',\n      'ref',\n      'refCallback',\n      'useLayoutEffect',\n      'useEffect',\n    ])\n  })\n\n  it('should forward ref three object', async () => {\n    // Note: Passing directly should be less strict, and assigning current should be more strict\n    let immutableRef!: React.RefObject<THREE.Mesh | null>\n    let mutableRef!: React.RefObject<THREE.Mesh | null>\n    let mutableRefSpecific!: React.RefObject<THREE.Mesh | null>\n\n    const RefTest = () => {\n      immutableRef = React.createRef()\n      mutableRef = React.useRef(null)\n      mutableRefSpecific = React.useRef(null)\n\n      return (\n        <>\n          <mesh ref={immutableRef} />\n          <mesh ref={mutableRef} />\n          <mesh ref={(r) => (mutableRefSpecific.current = r)} />\n        </>\n      )\n    }\n\n    await act(async () => root.render(<RefTest />))\n\n    expect(immutableRef.current).toBeInstanceOf(THREE.Mesh)\n    expect(mutableRef.current).toBeInstanceOf(THREE.Mesh)\n    expect(mutableRefSpecific.current).toBeInstanceOf(THREE.Mesh)\n  })\n\n  it('should handle children', async () => {\n    const Test = () => (\n      <group>\n        <mesh />\n      </group>\n    )\n    const store = await act(async () => root.render(<Test />))\n    const { scene } = store.getState()\n\n    expect(scene.children.length).toBe(1)\n    expect(scene.children[0]).toBeInstanceOf(THREE.Group)\n    expect(scene.children[0].children.length).toBe(1)\n    expect(scene.children[0].children[0]).toBeInstanceOf(THREE.Mesh)\n  })\n\n  it('should handle attach', async () => {\n    const lifecycle: string[] = []\n\n    const Test = () => {\n      return (\n        <mesh>\n          <boxGeometry />\n          <meshStandardMaterial />\n          <group attach=\"userData-group\" />\n          <group\n            ref={() => void lifecycle.push('mount')}\n            attach={() => (lifecycle.push('attach'), () => lifecycle.push('detach'))}\n          />\n        </mesh>\n      )\n    }\n    const store = await act(async () => root.render(<Test />))\n    const { scene } = store.getState()\n\n    expect(scene.children.length).toBe(1)\n    expect(scene.children[0]).toBeInstanceOf(THREE.Mesh)\n    // Handles geometry & material attach\n    expect((scene.children[0] as ComponentMesh).geometry).toBeInstanceOf(THREE.BoxGeometry)\n    expect((scene.children[0] as ComponentMesh).material).toBeInstanceOf(THREE.MeshStandardMaterial)\n    // Handles nested attach\n    expect(scene.children[0].userData.group).toBeInstanceOf(THREE.Group)\n    // attach bypasses scene-graph\n    expect(scene.children[0].children.length).toBe(0)\n    // attaches before presenting\n    expect(lifecycle).toStrictEqual(['attach', 'mount'])\n  })\n\n  it('should update props reactively', async () => {\n    const store = await act(async () => root.render(<group />))\n    const { scene } = store.getState()\n    const group = scene.children[0] as THREE.Group\n\n    // Initial\n    expect(group.name).toBe(new THREE.Group().name)\n\n    // Set\n    await act(async () => root.render(<group name=\"one\" />))\n    expect(group.name).toBe('one')\n\n    // Update\n    await act(async () => root.render(<group name=\"two\" />))\n    expect(group.name).toBe('two')\n\n    // Unset\n    await act(async () => root.render(<group />))\n    expect(group.name).toBe(new THREE.Group().name)\n  })\n\n  it('should handle event props reactively', async () => {\n    const store = await act(async () => root.render(<mesh />))\n    const { scene, internal } = store.getState()\n    const mesh = scene.children[0] as ComponentMesh\n    mesh.name = 'current'\n\n    // Initial\n    expect(internal.interaction.length).toBe(0)\n\n    // Set\n    await act(async () => root.render(<mesh onClick={() => void 0} />))\n    expect(internal.interaction.length).toBe(1)\n    expect(internal.interaction).toStrictEqual([mesh])\n\n    // Update\n    await act(async () => root.render(<mesh onPointerOver={() => void 0} />))\n    expect(internal.interaction.length).toBe(1)\n    expect(internal.interaction).toStrictEqual([mesh])\n\n    // Unset\n    await act(async () => root.render(<mesh />))\n    expect(internal.interaction.length).toBe(0)\n  })\n\n  it('should handle the args prop reactively', async () => {\n    const ref = React.createRef<ComponentMesh>()\n    const child = React.createRef<THREE.Object3D>()\n    const attachedChild = React.createRef<THREE.Object3D>()\n\n    const Test = (props: ThreeElements['mesh']) => (\n      <mesh {...props} ref={ref}>\n        <object3D ref={child} />\n        <object3D ref={attachedChild} attach=\"userData-attach\" />\n      </mesh>\n    )\n\n    // Initial\n    await act(async () => root.render(<Test />))\n    expect(ref.current!.geometry).toBeInstanceOf(THREE.BufferGeometry)\n    expect(ref.current!.geometry).not.toBeInstanceOf(THREE.BoxGeometry)\n    expect(ref.current!.material).toBeInstanceOf(THREE.Material)\n    expect(ref.current!.material).not.toBeInstanceOf(THREE.MeshStandardMaterial)\n    expect(ref.current!.children[0]).toBe(child.current)\n    expect(ref.current!.userData.attach).toBe(attachedChild.current)\n\n    // Throw on non-array value\n    await expectToThrow(\n      async () => await act(async () => root.render(<Test args={{} as any} />)),\n      'R3F: The args prop must be an array!',\n    )\n\n    // Set\n    const geometry1 = new THREE.BoxGeometry()\n    const material1 = new THREE.MeshStandardMaterial()\n    await act(async () => root.render(<Test args={[geometry1, material1]} />))\n    expect(ref.current!.geometry).toBe(geometry1)\n    expect(ref.current!.material).toBe(material1)\n    expect(ref.current!.children[0]).toBe(child.current)\n    expect(ref.current!.userData.attach).toBe(attachedChild.current)\n\n    // Update\n    const geometry2 = new THREE.BoxGeometry()\n    const material2 = new THREE.MeshStandardMaterial()\n    await act(async () => root.render(<Test args={[geometry2, material2]} />))\n    expect(ref.current!.geometry).toBe(geometry2)\n    expect(ref.current!.material).toBe(material2)\n    expect(ref.current!.children[0]).toBe(child.current)\n    expect(ref.current!.userData.attach).toBe(attachedChild.current)\n\n    // Unset\n    await act(async () => root.render(<Test />))\n    expect(ref.current!.geometry).toBeInstanceOf(THREE.BufferGeometry)\n    expect(ref.current!.geometry).not.toBeInstanceOf(THREE.BoxGeometry)\n    expect(ref.current!.material).toBeInstanceOf(THREE.Material)\n    expect(ref.current!.material).not.toBeInstanceOf(THREE.MeshStandardMaterial)\n    expect(ref.current!.children[0]).toBe(child.current)\n    expect(ref.current!.userData.attach).toBe(attachedChild.current)\n  })\n\n  it('should handle the object prop reactively', async () => {\n    const ref = React.createRef<THREE.Object3D>()\n    const child = React.createRef<THREE.Object3D>()\n    const attachedChild = React.createRef<THREE.Object3D>()\n\n    const Test = (props: ThreeElements['primitive']) => (\n      <primitive {...props} ref={ref}>\n        <object3D ref={child} />\n        <object3D ref={attachedChild} attach=\"userData-attach\" />\n      </primitive>\n    )\n\n    const object1 = new THREE.Object3D()\n    const child1 = new THREE.Object3D()\n    object1.add(child1)\n\n    const object2 = new THREE.Object3D()\n    const child2 = new THREE.Object3D()\n    object2.add(child2)\n\n    // Initial\n    await act(async () => root.render(<Test object={object1} />))\n    expect(ref.current).toBe(object1)\n    expect(ref.current!.children).toStrictEqual([child1, child.current])\n    expect(ref.current!.userData.attach).toBe(attachedChild.current)\n\n    // Throw on undefined\n    await expectToThrow(\n      async () => await act(async () => root.render(<Test object={undefined as any} />)),\n      \"R3F: Primitives without 'object' are invalid!\",\n    )\n\n    // Update\n    await act(async () => root.render(<Test object={object2} />))\n    expect(ref.current).toBe(object2)\n    expect(ref.current!.children).toStrictEqual([child2, child.current])\n    expect(ref.current!.userData.attach).toBe(attachedChild.current)\n\n    // Revert\n    await act(async () => root.render(<Test object={object1} />))\n    expect(ref.current).toBe(object1)\n    expect(ref.current!.children).toStrictEqual([child1, child.current])\n    expect(ref.current!.userData.attach).toBe(attachedChild.current)\n  })\n\n  it('should handle unmount', async () => {\n    const dispose = jest.fn()\n    const childDispose = jest.fn()\n    const attachDispose = jest.fn()\n    const flagDispose = jest.fn()\n\n    const attach = jest.fn()\n    const detach = jest.fn()\n\n    const object = Object.assign(new THREE.Object3D(), { dispose: jest.fn() })\n    const objectExternal = Object.assign(new THREE.Object3D(), { dispose: jest.fn() })\n    object.add(objectExternal)\n\n    const disposeDeclarativePrimitive = jest.fn()\n\n    const Test = (props: ThreeElements['mesh']) => (\n      <mesh\n        {...props}\n        ref={(self: any) => {\n          if (!self) return\n          self.dispose = dispose\n        }}\n        onClick={() => void 0}>\n        <object3D\n          ref={(self: any) => {\n            if (!self) return\n            self.dispose = childDispose\n          }}\n        />\n        <object3D\n          ref={(self: any) => {\n            if (!self) return\n            self.dispose = attachDispose\n          }}\n          attach={() => (attach(), detach)}\n        />\n        <object3D\n          dispose={null}\n          ref={(self: any) => {\n            if (!self) return\n            self.dispose = flagDispose\n          }}\n        />\n        <primitive object={object}>\n          <object3D\n            ref={(self: any) => {\n              if (!self) return\n              self.dispose = disposeDeclarativePrimitive\n            }}\n          />\n        </primitive>\n      </mesh>\n    )\n\n    const store = await act(async () => root.render(<Test />))\n    await act(async () => root.render(null))\n\n    const { scene, internal } = store.getState()\n\n    // Cleans up scene-graph\n    expect(scene.children.length).toBe(0)\n    // Removes events\n    expect(internal.interaction.length).toBe(0)\n    // Calls dispose on top-level instance\n    expect(dispose).toHaveBeenCalled()\n    // Also disposes of children\n    expect(childDispose).toHaveBeenCalled()\n    // Disposes of attached children\n    expect(attachDispose).toHaveBeenCalled()\n    // Properly detaches attached children\n    expect(attach).toHaveBeenCalledTimes(1)\n    expect(detach).toHaveBeenCalledTimes(1)\n    // Respects dispose={null}\n    expect(flagDispose).not.toHaveBeenCalled()\n    // Does not dispose of primitives\n    expect(object.dispose).not.toHaveBeenCalled()\n    // Only disposes of declarative primitive children\n    expect(objectExternal.dispose).not.toHaveBeenCalled()\n    expect(disposeDeclarativePrimitive).toHaveBeenCalled()\n  })\n\n  it('can swap 4 array primitives', async () => {\n    const a = new THREE.Group()\n    a.name = 'a'\n    const b = new THREE.Group()\n    b.name = 'b'\n    const c = new THREE.Group()\n    c.name = 'c'\n    const d = new THREE.Group()\n    d.name = 'd'\n\n    const Test = ({ array }: { array: THREE.Group[] }) => (\n      <>\n        {array.map((group, i) => (\n          <primitive key={i} object={group} />\n        ))}\n      </>\n    )\n\n    const array = [a, b, c, d]\n    const store = await act(async () => root.render(<Test array={array} />))\n    const { scene } = store.getState()\n\n    expect(scene.children.map((o) => o.name)).toStrictEqual(array.map((o) => o.name))\n\n    const reversedArray = [d, c, b, a]\n    await act(async () => root.render(<Test array={reversedArray} />))\n    expect(scene.children.map((o) => o.name)).toStrictEqual(reversedArray.map((o) => o.name))\n\n    const mixedArray = [b, a, d, c]\n    await act(async () => root.render(<Test array={mixedArray} />))\n    expect(scene.children.map((o) => o.name)).toStrictEqual(mixedArray.map((o) => o.name))\n  })\n\n  // https://github.com/pmndrs/react-three-fiber/issues/3125\n  // https://github.com/pmndrs/react-three-fiber/issues/3143\n  it('can swap 4 array primitives via attach', async () => {\n    const a = new THREE.Group()\n    a.name = 'a'\n    const b = new THREE.Group()\n    b.name = 'b'\n    const c = new THREE.Group()\n    c.name = 'c'\n    const d = new THREE.Group()\n    d.name = 'd'\n    const array = [a, b, c, d]\n\n    const Test = ({ array }: { array: THREE.Group[] }) => (\n      <>\n        {array.map((group, i) => (\n          <primitive key={i} attach={`userData-objects-${i}`} object={group} />\n        ))}\n      </>\n    )\n\n    const store = await act(async () => root.render(<Test array={array} />))\n    const { scene } = store.getState()\n\n    expect(scene.children.length).toBe(0)\n    expect(scene.userData.objects.map((o: THREE.Object3D) => o.name)).toStrictEqual(array.map((o) => o.name))\n\n    const reversedArray = [d, c, b, a]\n    await act(async () => root.render(<Test array={reversedArray} />))\n    expect(scene.children.length).toBe(0)\n    expect(scene.userData.objects.map((o: THREE.Object3D) => o.name)).toStrictEqual(reversedArray.map((o) => o.name))\n\n    const mixedArray = [b, a, d, c]\n    await act(async () => root.render(<Test array={mixedArray} />))\n    expect(scene.children.length).toBe(0)\n    expect(scene.userData.objects.map((o: THREE.Object3D) => o.name)).toStrictEqual(mixedArray.map((o) => o.name))\n  })\n\n  it('should gracefully handle text', async () => {\n    // Mount\n    await act(async () => root.render(<>one</>))\n    // Update\n    await act(async () => root.render(<>two</>))\n    // Unmount\n    await act(async () => root.render(<></>))\n    // Suspense\n    const Test = () => suspend(async () => <>four</>, [])\n    await act(async () => root.render(<Test />))\n  })\n\n  it('should gracefully interrupt when building up the tree', async () => {\n    const calls: string[] = []\n    let lastAttached!: string | undefined\n    let lastMounted!: string | undefined\n\n    function SuspenseComponent({ reconstruct = false }: { reconstruct?: boolean }) {\n      suspend(async (reconstruct) => reconstruct, [reconstruct])\n\n      return (\n        <mock key={reconstruct ? 0 : 1} args={['parent']}>\n          <mock\n            args={['child']}\n            ref={(self) => void (lastMounted = self?.uuid)}\n            attach={(_, self) => {\n              calls.push('attach')\n              lastAttached = self.uuid\n              return () => calls.push('detach')\n            }}\n          />\n        </mock>\n      )\n    }\n\n    function Test(props: { reconstruct?: boolean }) {\n      React.useLayoutEffect(() => void calls.push('useLayoutEffect'), [])\n\n      return (\n        <mock args={['suspense']}>\n          <SuspenseComponent {...props} />\n        </mock>\n      )\n    }\n\n    await act(async () => root.render(<Test />))\n\n    // Should complete tree before layout-effects fire\n    expect(calls).toStrictEqual(['attach', 'useLayoutEffect'])\n    expect(lastAttached).toBe(lastMounted)\n    expect(Mock.instances).toStrictEqual(['suspense', 'parent', 'child'])\n\n    await act(async () => root.render(<Test reconstruct />))\n\n    expect(calls).toStrictEqual(['attach', 'useLayoutEffect', 'detach', 'attach'])\n    expect(lastAttached).toBe(lastMounted)\n    expect(Mock.instances).toStrictEqual(['suspense', 'parent', 'child', 'parent', 'child'])\n  })\n\n  it('should toggle visibility during Suspense non-destructively', async () => {\n    const a = Promise.resolve(new THREE.Object3D())\n    const b = Promise.resolve(new THREE.Object3D())\n\n    function AsyncPrimitive({ object }: { object: Promise<THREE.Object3D> }) {\n      return <primitive object={React.use(object)} />\n    }\n\n    for (let i = 0; i < 3; i++) {\n      await act(async () =>\n        (\n          await root.configure()\n        ).render(\n          <React.Suspense fallback={null}>\n            <AsyncPrimitive object={i % 2 === 0 ? a : b} />\n          </React.Suspense>,\n        ),\n      )\n    }\n\n    expect((await a).visible).toBe(true)\n    expect((await b).visible).toBe(true)\n  })\n\n  it('should hide suspended objects when displaying fallback', async () => {\n    const a = new THREE.Object3D()\n    const b = new THREE.Object3D()\n    const fallback = new THREE.Object3D()\n\n    let resolveA: () => void\n    const aPromise = new Promise<THREE.Object3D>((res) => {\n      resolveA = () => res(a)\n    })\n\n    let resolveB: () => void\n    const bPromise = new Promise<THREE.Object3D>((res) => {\n      resolveB = () => res(b)\n    })\n\n    function Fallback() {\n      return <primitive object={fallback} />\n    }\n\n    function AsyncPrimitive({ object }: { object: Promise<THREE.Object3D> }) {\n      return <primitive object={React.use(object)} />\n    }\n\n    // Mount unresolved A promise.\n    // Fallback should be mounted and nothing else.\n    const store = await act(async () =>\n      (\n        await root.configure()\n      ).render(\n        <React.Suspense fallback={<Fallback />}>\n          <AsyncPrimitive object={aPromise} />\n        </React.Suspense>,\n      ),\n    )\n\n    const scene = store.getState().scene as THREE.Scene\n\n    expect(a.visible).toBe(true)\n    expect(b.visible).toBe(true)\n    expect(scene.children.includes(fallback)).toBe(true)\n    expect(scene.children.includes(a)).toBe(false)\n\n    // Resolve A promise.\n    // A should be mounted and visible and fallback should be unmounted.\n    await act(async () => resolveA())\n    await act(async () =>\n      (\n        await root.configure()\n      ).render(\n        <React.Suspense fallback={<Fallback />}>\n          <AsyncPrimitive object={aPromise} />\n        </React.Suspense>,\n      ),\n    )\n\n    expect(a.visible).toBe(true)\n    expect(b.visible).toBe(true)\n    expect(scene.children.includes(fallback)).toBe(false)\n    expect(scene.children.includes(a)).toBe(true)\n\n    // Mount unresolved B promise.\n    // A should remain mounted but be invisible, Fallback is mounted, B is unmounted.\n    await act(async () =>\n      (\n        await root.configure()\n      ).render(\n        <React.Suspense fallback={<Fallback />}>\n          <AsyncPrimitive object={bPromise} />\n        </React.Suspense>,\n      ),\n    )\n\n    expect(a.visible).toBe(false)\n    expect(b.visible).toBe(true)\n    expect(scene.children.includes(fallback)).toBe(true)\n    expect(scene.children.includes(a)).toBe(true)\n    expect(scene.children.includes(b)).toBe(false)\n\n    // Resolve B promise.\n    // B should be mounted and visible, fallback should be unmounted, A also unmounted and unhidden.\n    await act(async () => resolveB())\n    await act(async () =>\n      (\n        await root.configure()\n      ).render(\n        <React.Suspense fallback={<Fallback />}>\n          <AsyncPrimitive object={bPromise} />\n        </React.Suspense>,\n      ),\n    )\n\n    expect(a.visible).toBe(true)\n    expect(b.visible).toBe(true)\n    expect(scene.children.includes(fallback)).toBe(false)\n    expect(scene.children.includes(a)).toBe(false)\n    expect(scene.children.includes(b)).toBe(true)\n\n    // Remount resolved A promise.\n    // A should be mounted and visible, B should be unmounted and visible (not hidden), fallback should be unmounted.\n    await act(async () =>\n      (\n        await root.configure()\n      ).render(\n        <React.Suspense fallback={<Fallback />}>\n          <AsyncPrimitive object={aPromise} />\n        </React.Suspense>,\n      ),\n    )\n\n    expect(a.visible).toBe(true)\n    expect(b.visible).toBe(true)\n    expect(scene.children.includes(fallback)).toBe(false)\n    expect(scene.children.includes(a)).toBe(true)\n    expect(scene.children.includes(b)).toBe(false)\n  })\n\n  it('preserves camera frustum props for perspective', async () => {\n    const store = await act(async () => (await root.configure({ camera: { aspect: 0 } })).render(null))\n    const camera = store.getState().camera as THREE.PerspectiveCamera\n    expect(camera.aspect).toBe(0)\n  })\n\n  it('preserves camera frustum props for orthographic', async () => {\n    const store = await act(async () =>\n      (await root.configure({ orthographic: true, camera: { left: 0, right: 0, top: 0, bottom: 0 } })).render(null),\n    )\n    const camera = store.getState().camera as THREE.OrthographicCamera\n    expect(camera.left).toBe(0)\n    expect(camera.right).toBe(0)\n    expect(camera.top).toBe(0)\n    expect(camera.bottom).toBe(0)\n  })\n\n  it('resolves conflicting and prefixed elements', async () => {\n    extend({ ThreeRandom: THREE.Group })\n\n    const store = await act(async () => root.render(<line />))\n    expect(store.getState().scene.children[0]).toBeInstanceOf(THREE.Line)\n\n    await act(async () => root.render(null))\n    expect(store.getState().scene.children.length).toBe(0)\n\n    await act(async () => root.render(<threeLine />))\n    expect(store.getState().scene.children[0]).toBeInstanceOf(THREE.Line)\n\n    await act(async () => root.render(null))\n    expect(store.getState().scene.children.length).toBe(0)\n\n    await act(async () => root.render(<threeRandom />))\n    expect(store.getState().scene.children[0]).toBeInstanceOf(THREE.Group)\n  })\n\n  it('should properly handle array of components with changing keys and order', async () => {\n    // Component that renders a mesh with a specific ID\n    const MeshComponent = ({ id }: { id: number }) => {\n      return <mesh name={`mesh-${id}`} />\n    }\n\n    // Component that maps over an array of values to render MeshComponents\n    const Test = ({ values }: { values: number[] }) => (\n      <>\n        {values.map((value) => (\n          <MeshComponent key={value} id={value} />\n        ))}\n      </>\n    )\n\n    // Initial render with 4 values\n    const initialValues = [1, 2, 3, 4]\n    const store = await act(async () => root.render(<Test values={initialValues} />))\n    const { scene } = store.getState()\n\n    // Check initial state\n    expect(scene.children.length).toBe(4)\n    const initialNames = scene.children.map((child) => child.name).sort()\n    expect(initialNames).toEqual(['mesh-1', 'mesh-2', 'mesh-3', 'mesh-4'])\n\n    // Update with one less value and different order\n    const updatedValues = [3, 1, 4]\n    await act(async () => root.render(<Test values={updatedValues} />))\n\n    // Check that the scene has exactly the meshes we expect\n    expect(scene.children.length).toBe(3)\n    const updatedNames = scene.children.map((child) => child.name).sort()\n    expect(updatedNames).toEqual(['mesh-1', 'mesh-3', 'mesh-4'])\n\n    // Verify mesh-2 was removed\n    expect(scene.children.find((child) => child.name === 'mesh-2')).toBeUndefined()\n\n    // Verify no duplicates by checking unique names\n    const uniqueNames = new Set(scene.children.map((child) => child.name))\n    expect(uniqueNames.size).toBe(scene.children.length)\n\n    // Update with different order again\n    const reorderedValues = [4, 1]\n    await act(async () => root.render(<Test values={reorderedValues} />))\n\n    // Check final state\n    expect(scene.children.length).toBe(2)\n    const finalNames = scene.children.map((child) => child.name).sort()\n    expect(finalNames).toEqual(['mesh-1', 'mesh-4'])\n\n    // Verify mesh-3 was removed\n    expect(scene.children.find((child) => child.name === 'mesh-3')).toBeUndefined()\n\n    // Verify no duplicates in final state\n    const finalUniqueNames = new Set(scene.children.map((child) => child.name))\n    expect(finalUniqueNames.size).toBe(scene.children.length)\n  })\n\n  it('should update scene synchronously with flushSync', async () => {\n    let updateSynchronously: (value: number) => void\n\n    function TestComponent() {\n      const [positionX, setPositionX] = React.useState(0)\n      const scene = useThree((state) => state.scene)\n\n      updateSynchronously = React.useCallback(\n        (value: number) => {\n          flushSync(() => {\n            setPositionX(value)\n          })\n\n          expect(scene.children.length).toBe(1)\n          expect(scene.children[0].position.x).toBe(value)\n        },\n        [scene, setPositionX],\n      )\n\n      return <mesh position-x={positionX} />\n    }\n\n    await act(async () => root.render(<TestComponent />))\n    await act(async () => updateSynchronously(1))\n  })\n})\n"
  },
  {
    "path": "packages/fiber/tests/utils.test.ts",
    "content": "import * as THREE from 'three'\nimport { type RootStore, type Instance, act, createRoot } from '../src'\nimport {\n  is,\n  dispose,\n  REACT_INTERNAL_PROPS,\n  getInstanceProps,\n  prepare,\n  resolve,\n  attach,\n  detach,\n  RESERVED_PROPS,\n  diffProps,\n  applyProps,\n  updateCamera,\n  findInitialRoot,\n} from '../src/core/utils'\n\nfunction createMockStore(): RootStore {\n  let store!: RootStore\n  try {\n    act(async () => (store = createRoot(document.createElement('canvas')).render(null))).then(() => null)\n  } catch (e) {\n    console.error(e)\n  }\n  return store\n}\n\nconst store = createMockStore()\n\ndescribe('is', () => {\n  const myFunc = () => null\n  const myObj = { myProp: 'test-prop' }\n  const myStr = 'test-string'\n  const myNum = 1\n  const myUnd = undefined\n  const myArr = [1, 2, 3]\n\n  it('should tell me if something is a function', () => {\n    expect(is.fun(myFunc)).toBe(true)\n\n    expect(is.fun(myObj)).toBe(false)\n    expect(is.fun(myStr)).toBe(false)\n    expect(is.fun(myNum)).toBe(false)\n    expect(is.fun(myUnd)).toBe(false)\n    expect(is.fun(myArr)).toBe(false)\n  })\n  it('should tell me if something is an object', () => {\n    expect(is.obj(myFunc)).toBe(false)\n\n    expect(is.obj(myObj)).toBe(true)\n\n    expect(is.obj(myStr)).toBe(false)\n    expect(is.obj(myNum)).toBe(false)\n    expect(is.obj(myUnd)).toBe(false)\n    expect(is.obj(myArr)).toBe(false)\n  })\n  it('should tell me if something is a string', () => {\n    expect(is.str(myFunc)).toBe(false)\n    expect(is.str(myObj)).toBe(false)\n\n    expect(is.str(myStr)).toBe(true)\n\n    expect(is.str(myNum)).toBe(false)\n    expect(is.str(myUnd)).toBe(false)\n    expect(is.str(myArr)).toBe(false)\n  })\n  it('should tell me if something is a number', () => {\n    expect(is.num(myFunc)).toBe(false)\n    expect(is.num(myObj)).toBe(false)\n    expect(is.num(myStr)).toBe(false)\n\n    expect(is.num(myNum)).toBe(true)\n\n    expect(is.num(myUnd)).toBe(false)\n    expect(is.num(myArr)).toBe(false)\n  })\n  it('should tell me if something is undefined', () => {\n    expect(is.und(myFunc)).toBe(false)\n    expect(is.und(myObj)).toBe(false)\n    expect(is.und(myStr)).toBe(false)\n    expect(is.und(myNum)).toBe(false)\n\n    expect(is.und(myUnd)).toBe(true)\n\n    expect(is.und(myArr)).toBe(false)\n  })\n  it('should tell me if something is an array', () => {\n    expect(is.arr(myFunc)).toBe(false)\n    expect(is.arr(myObj)).toBe(false)\n    expect(is.arr(myStr)).toBe(false)\n    expect(is.arr(myNum)).toBe(false)\n    expect(is.arr(myUnd)).toBe(false)\n\n    expect(is.arr(myArr)).toBe(true)\n  })\n  it('should tell me if something is equal', () => {\n    expect(is.equ([], '')).toBe(false)\n\n    expect(is.equ('hello', 'hello')).toBe(true)\n    expect(is.equ(1, 1)).toBe(true)\n\n    const obj = { type: 'Mesh' }\n    expect(is.equ(obj, obj)).toBe(true)\n    expect(is.equ({}, {})).toBe(false)\n    expect(is.equ({}, {}, { objects: 'reference' })).toBe(false)\n    expect(is.equ({}, {}, { objects: 'shallow' })).toBe(true)\n    expect(is.equ({ a: 1 }, { a: 1 })).toBe(false)\n    expect(is.equ({ a: 1 }, { a: 1 }, { objects: 'reference' })).toBe(false)\n    expect(is.equ({ a: 1 }, { a: 1 }, { objects: 'shallow' })).toBe(true)\n    expect(is.equ({ a: 1, b: 1 }, { a: 1 }, { objects: 'shallow' })).toBe(false)\n    expect(is.equ({ a: 1 }, { a: 1, b: 1 }, { objects: 'shallow' })).toBe(false)\n    expect(is.equ({ a: 1 }, { a: 1, b: 1 }, { objects: 'shallow', strict: false })).toBe(true)\n    expect(is.equ({ a: [1, 2, 3] }, { a: [1, 2, 3] }, { arrays: 'reference', objects: 'reference' })).toBe(false)\n    expect(is.equ({ a: [1, 2, 3] }, { a: [1, 2, 3] }, { objects: 'reference' })).toBe(false)\n    expect(is.equ({ a: [1, 2, 3] }, { a: [1, 2, 3] }, { objects: 'shallow' })).toBe(true)\n    expect(is.equ({ a: [1, 2, 3] }, { a: [1, 2, 3, 4] }, { objects: 'shallow' })).toBe(false)\n    expect(is.equ({ a: [1, 2, 3] }, { a: [1, 2, 3, 4] }, { objects: 'shallow', strict: false })).toBe(true)\n    expect(is.equ({ a: [1, 2, 3] }, { a: [1, 2, 3], b: 1 }, { objects: 'shallow' })).toBe(false)\n    expect(is.equ({ a: [1, 2, 3] }, { a: [1, 2, 3], b: 1 }, { objects: 'shallow', strict: false })).toBe(true)\n\n    const arr = [1, 2, 3]\n    expect(is.equ(arr, arr)).toBe(true)\n    expect(is.equ([], [])).toBe(true)\n    expect(is.equ([], [], { arrays: 'reference' })).toBe(false)\n    expect(is.equ([], [], { arrays: 'shallow' })).toBe(true)\n    expect(is.equ([1, 2, 3], [1, 2, 3])).toBe(true)\n    expect(is.equ([1, 2, 3], [1, 2, 3], { arrays: 'shallow' })).toBe(true)\n    expect(is.equ([1, 2, 3], [1, 2, 3], { arrays: 'reference' })).toBe(false)\n    expect(is.equ([1, 2], [1, 2, 3])).toBe(false)\n    expect(is.equ([1, 2, 3, 4], [1, 2, 3])).toBe(false)\n    expect(is.equ([1, 2], [1, 2, 3], { strict: false })).toBe(true)\n  })\n})\n\ndescribe('dispose', () => {\n  it('should dispose of objects and their properties', () => {\n    const mesh = Object.assign(new THREE.Mesh<THREE.BoxGeometry, THREE.MeshBasicMaterial>(), { dispose: jest.fn() })\n    mesh.material.dispose = jest.fn()\n    mesh.geometry.dispose = jest.fn()\n\n    dispose(mesh)\n    expect(mesh.dispose).toHaveBeenCalled()\n    expect(mesh.material.dispose).toHaveBeenCalled()\n    expect(mesh.geometry.dispose).toHaveBeenCalled()\n  })\n\n  it('should not dispose of a THREE.Scene', () => {\n    const scene = Object.assign(new THREE.Scene(), { dispose: jest.fn() })\n\n    dispose(scene)\n    expect(scene.dispose).not.toHaveBeenCalled()\n\n    const disposable = { dispose: jest.fn(), scene }\n    dispose(disposable)\n    expect(disposable.dispose).toHaveBeenCalled()\n    expect(disposable.scene.dispose).not.toHaveBeenCalled()\n  })\n})\n\ndescribe('getInstanceProps', () => {\n  it('should filter internal props without accessing them', () => {\n    const get = jest.fn()\n    const set = jest.fn()\n\n    const props = { foo: true }\n    const filtered = getInstanceProps(\n      REACT_INTERNAL_PROPS.reduce((acc, key) => ({ ...acc, [key]: { get, set } }), props),\n    )\n\n    expect(filtered).toStrictEqual(props)\n    expect(get).not.toHaveBeenCalled()\n    expect(set).not.toHaveBeenCalled()\n  })\n})\n\ndescribe('prepare', () => {\n  it('should create an instance descriptor', () => {\n    const object = new THREE.Object3D()\n    const instance = prepare(object, store, 'object3D', { name: 'object' })\n\n    expect(instance.root).toBe(store)\n    expect(instance.type).toBe('object3D')\n    expect(instance.props.name).toBe('object')\n    expect(instance.object).toBe(object)\n    expect((object as Instance<THREE.Object3D>['object']).__r3f).toBe(instance)\n  })\n\n  it('should not overwrite descriptors', () => {\n    const containerDesc = {}\n    const container = { __r3f: containerDesc }\n\n    const instance = prepare(container, store, 'container', {})\n    expect(container.__r3f).toBe(containerDesc)\n    expect(instance).toBe(containerDesc)\n  })\n})\n\ndescribe('resolve', () => {\n  it('should resolve pierced props', () => {\n    const object = { foo: { bar: 1 } }\n    const { root, key, target } = resolve(object, 'foo-bar')\n\n    expect(root).toBe(object['foo'])\n    expect(key).toBe('bar')\n    expect(target).toBe(root[key])\n  })\n\n  it('should prioritize direct property over piercing', () => {\n    const object = {\n      'foo-bar': 'direct',\n      foo: { bar: 'pierced' },\n    }\n    const { root, key, target } = resolve(object, 'foo-bar')\n\n    expect(root).toBe(object)\n    expect(key).toBe('foo-bar')\n    expect(target).toBe('direct')\n  })\n\n  it('should handle undefined direct property values', () => {\n    const object = { 'foo-bar': undefined }\n    const { root, key, target } = resolve(object, 'foo-bar')\n\n    expect(root).toBe(object)\n    expect(key).toBe('foo-bar')\n    expect(target).toBe(undefined)\n  })\n\n  it('should handle null direct property values', () => {\n    const object = { 'foo-bar': null }\n    const { root, key, target } = resolve(object, 'foo-bar')\n\n    expect(root).toBe(object)\n    expect(key).toBe('foo-bar')\n    expect(target).toBe(null)\n  })\n\n  it('should return non-object as root when piercing fails due to non-object intermediate', () => {\n    const object = { foo: 'not-an-object' }\n    const { root, key, target } = resolve(object, 'foo-bar')\n\n    expect(root).toBe('not-an-object')\n    expect(key).toBe('bar')\n    expect(target).toBe(undefined)\n  })\n\n  it('should return null as root when piercing fails due to null intermediate', () => {\n    const object = { foo: null }\n    const { root, key, target } = resolve(object, 'foo-bar')\n\n    expect(root).toBe(null)\n    expect(key).toBe('bar')\n    expect(target).toBe(undefined)\n  })\n\n  it('should handle non-dashed keys normally', () => {\n    const object = { foo: 'value' }\n    const { root, key, target } = resolve(object, 'foo')\n\n    expect(root).toBe(object)\n    expect(key).toBe('foo')\n    expect(target).toBe('value')\n  })\n})\n\ndescribe('attach / detach', () => {\n  it('should attach & detach using string values', () => {\n    const parent = prepare({ prop: null }, store, '', {})\n    const child = prepare({}, store, '', { attach: 'prop' })\n\n    attach(parent, child)\n    expect(parent.object.prop).toBe(child.object)\n    expect(child.previousAttach).toBe(null)\n\n    detach(parent, child)\n    expect(parent.object.prop).toBe(null)\n    expect(child.previousAttach).toBe(undefined)\n  })\n\n  it('should attach & detach using attachFns', () => {\n    const mount = jest.fn()\n    const unmount = jest.fn()\n\n    const parent = prepare({}, store, '', {})\n    const child = prepare({}, store, '', { attach: () => (mount(), unmount) })\n\n    attach(parent, child)\n    expect(mount).toHaveBeenCalledTimes(1)\n    expect(unmount).toHaveBeenCalledTimes(0)\n    expect(child.previousAttach).toBe(unmount)\n\n    detach(parent, child)\n    expect(mount).toHaveBeenCalledTimes(1)\n    expect(unmount).toHaveBeenCalledTimes(1)\n    expect(child.previousAttach).toBe(undefined)\n  })\n\n  it('should create array when using array-index syntax', () => {\n    const parent = prepare({ prop: null }, store, '', {})\n    const child = prepare({}, store, '', { attach: 'prop-0' })\n\n    attach(parent, child)\n    expect(parent.object.prop).toStrictEqual([child.object])\n    expect(child.previousAttach).toBe(undefined)\n\n    detach(parent, child)\n    expect((parent.object.prop as unknown as Array<never>).length).toBe(1)\n    expect((parent.object.prop as unknown as Array<never>)[0]).toBe(undefined)\n    expect(child.previousAttach).toBe(undefined)\n  })\n})\n\ndescribe('diffProps', () => {\n  it('should filter changed props', () => {\n    const instance = prepare({}, store, '', { foo: true })\n    const newProps = { foo: true, bar: false }\n\n    const filtered = diffProps(instance, newProps)\n    expect(filtered).toStrictEqual({ bar: false })\n  })\n\n  it('invalidates pierced props when root is changed', () => {\n    const texture1 = { needsUpdate: false, name: '' } as THREE.Texture\n    const texture2 = { needsUpdate: false, name: '' } as THREE.Texture\n\n    const oldProps = { map: texture1, 'map-needsUpdate': true, 'map-name': 'test' }\n    const newProps = { map: texture2, 'map-needsUpdate': true, 'map-name': 'test' }\n\n    const instance = prepare({}, store, '', oldProps)\n    const filtered = diffProps(instance, newProps)\n    expect(filtered).toStrictEqual(newProps)\n  })\n\n  it('should pick removed props for HMR', () => {\n    const instance = prepare(new THREE.Object3D(), store, '', { position: [0, 0, 1] })\n    const newProps = {}\n\n    const filtered = diffProps(instance, newProps)\n    expect(filtered).toStrictEqual({ position: new THREE.Object3D().position })\n  })\n\n  it('should reset removed props for HMR', () => {\n    const instance = prepare(new THREE.Object3D(), store, '', { scale: 10 })\n    const filtered = diffProps(instance, {})\n    expect((filtered.scale as THREE.Vector3).toArray()).toStrictEqual([1, 1, 1])\n  })\n\n  it('should filter reserved props without accessing them', () => {\n    const get = jest.fn()\n    const set = jest.fn()\n\n    const props = { foo: true }\n    const filtered = diffProps(\n      prepare({}, store, '', {}),\n      RESERVED_PROPS.reduce((acc, key) => ({ ...acc, [key]: { get, set } }), props),\n    )\n\n    expect(filtered).toStrictEqual(props)\n    expect(get).not.toHaveBeenCalled()\n    expect(set).not.toHaveBeenCalled()\n  })\n})\n\ndescribe('applyProps', () => {\n  it('should apply props to foreign objects', () => {\n    const target = new THREE.Object3D()\n    expect(() => applyProps(target, {})).not.toThrow()\n  })\n\n  it('should not throw when applying unknown props', () => {\n    const target = new THREE.Object3D()\n    applyProps(target, {})\n    expect(() => applyProps(target, { ['foo-bar']: 1 })).not.toThrow()\n    expect((target as any)['foo-bar']).toBe(undefined)\n  })\n\n  it('should throw when applying unknown props due to non-object intermediate', () => {\n    const target = new THREE.Object3D()\n    applyProps(target, { foo: 1 })\n    expect(() => applyProps(target, { ['foo-bar']: 1 })).toThrow()\n  })\n\n  it('should filter reserved props without accessing them', () => {\n    const get = jest.fn()\n    const set = jest.fn()\n\n    const props = { foo: true }\n    const target = {}\n    applyProps(\n      target,\n      RESERVED_PROPS.reduce((acc, key) => ({ ...acc, [key]: { get, set } }), props),\n    )\n\n    expect(target).toStrictEqual(props)\n    expect(get).not.toHaveBeenCalled()\n    expect(set).not.toHaveBeenCalled()\n  })\n\n  it('should overwrite non-atomic properties', () => {\n    const foo = { value: true }\n    const target = { foo }\n    applyProps(target, { foo: { value: false } })\n\n    expect(target.foo).not.toBe(foo)\n    expect(target.foo.value).toBe(false)\n  })\n\n  it('should prefer to copy potentially read-only math classes', () => {\n    const one = new THREE.Vector3(1, 1, 1)\n    const two = new THREE.Vector3(2, 2, 2)\n\n    const target = { test: one }\n    applyProps(target, { test: two })\n\n    expect(target.test).toBe(one)\n    expect(target.test.toArray()).toStrictEqual([2, 2, 2])\n  })\n\n  it('should not copy if props are supersets of another', () => {\n    const copy = jest.fn()\n    const set = jest.fn()\n\n    class Test {\n      copy = copy\n      set = set\n    }\n    class SuperTest extends Test {\n      copy = copy\n      set = set\n    }\n\n    const one = new Test()\n    const two = new SuperTest()\n\n    const target = { test: one }\n    applyProps(target, { test: two })\n\n    expect(one.copy).not.toHaveBeenCalled()\n    expect(two.copy).not.toHaveBeenCalled()\n    expect(one.set).not.toHaveBeenCalled()\n    expect(two.set).not.toHaveBeenCalled()\n    expect(target.test).toBe(two)\n  })\n\n  it('should prefer to set when props are an array', () => {\n    const target = new THREE.Object3D()\n    applyProps(target, { position: [1, 2, 3] })\n\n    expect(target.position.toArray()).toStrictEqual([1, 2, 3])\n  })\n\n  it('should set with scalar shorthand where applicable', () => {\n    // Vector3#setScalar\n    const target = new THREE.Object3D()\n    applyProps(target, { scale: 5 })\n    expect(target.scale.toArray()).toStrictEqual([5, 5, 5])\n\n    // Color#set\n    const material = new THREE.MeshBasicMaterial()\n    applyProps(material, { color: 0x000000 })\n    expect(material.color.getHex()).toBe(0x000000)\n    applyProps(material, { color: 'white' })\n    expect(material.color.getHex()).toBe(0xffffff)\n    applyProps(material, { color: new THREE.Color('red') })\n    expect(material.color.getHex()).toBe(0xff0000)\n\n    // No-op on undefined\n    const mesh = new THREE.Mesh()\n    applyProps(mesh, { position: undefined })\n    expect(mesh.position.toArray()).toStrictEqual([0, 0, 0])\n  })\n\n  it('should pierce into nested properties', () => {\n    const target = new THREE.Mesh<THREE.BufferGeometry, THREE.MeshBasicMaterial>()\n    applyProps(target, { 'material-color': 0x000000 })\n\n    expect(target.material.color.getHex()).toBe(0x000000)\n  })\n\n  it('should not apply a prop if it is undefined', () => {\n    const target = { value: 'initial' }\n    applyProps(target, { value: undefined })\n\n    expect(target.value).toBe('initial')\n  })\n\n  it('should not apply a prop to an instance if it is a reserved event', () => {\n    const target = prepare(new THREE.Object3D(), store, '', {})\n    applyProps(target.object, { onClick: () => null })\n\n    expect('onClick' in target).toBe(false)\n  })\n\n  // https://github.com/pmndrs/koota/issues/47\n  it('should not fallthrough to set/copy for primitive types', () => {\n    const set = jest.fn()\n    const copy = jest.fn()\n\n    // @ts-ignore\n    Number.prototype.set = set\n    // @ts-ignore\n    Number.prototype.copy = copy\n\n    const target = { scale: 1, rotation: new THREE.Vector3(1, 2, 3) }\n    applyProps(target, { scale: 10, 'rotation-z': 4 })\n\n    // @ts-ignore\n    delete Number.prototype.set\n    // @ts-ignore\n    delete Number.prototype.copy\n\n    expect(set).not.toHaveBeenCalled()\n    expect(copy).not.toHaveBeenCalled()\n    expect(target.scale).toBe(10)\n    expect(target.rotation.z).toBe(4)\n  })\n})\n\ndescribe('updateCamera', () => {\n  it('updates camera matrices', () => {\n    const size = { width: 1280, height: 800, left: 0, top: 0 }\n\n    const perspective = new THREE.PerspectiveCamera()\n    perspective.updateProjectionMatrix = jest.fn()\n    updateCamera(perspective, size)\n    expect(perspective.updateProjectionMatrix).toHaveBeenCalled()\n    expect(perspective.projectionMatrix.toArray()).toMatchSnapshot()\n\n    const orthographic = new THREE.OrthographicCamera()\n    orthographic.updateProjectionMatrix = jest.fn()\n    updateCamera(orthographic, size)\n    expect(orthographic.updateProjectionMatrix).toHaveBeenCalled()\n    expect(orthographic.projectionMatrix.toArray()).toMatchSnapshot()\n  })\n\n  it('respects camera.manual', () => {\n    const size = { width: 0, height: 0, left: 0, top: 0 }\n\n    const perspective = Object.assign(new THREE.PerspectiveCamera(), { manual: true })\n    perspective.updateProjectionMatrix = jest.fn()\n    updateCamera(perspective, size)\n    expect(perspective.updateProjectionMatrix).not.toHaveBeenCalled()\n\n    const orthographic = Object.assign(new THREE.OrthographicCamera(), { manual: true })\n    orthographic.updateProjectionMatrix = jest.fn()\n    updateCamera(orthographic, size)\n    expect(orthographic.updateProjectionMatrix).not.toHaveBeenCalled()\n  })\n})\n\ndescribe('findInitialRoot', () => {\n  it('finds the nearest root for portals', () => {\n    const portalStore = createMockStore()\n    portalStore.getState().previousRoot = store\n\n    const instance = prepare(new THREE.Object3D(), portalStore, '', {})\n    const root = findInitialRoot(instance)\n\n    expect(root).toBe(store)\n  })\n\n  it('falls back to the local root', () => {\n    const instance = prepare(new THREE.Object3D(), store, '', {})\n    const root = findInitialRoot(instance)\n\n    expect(root).toBe(store)\n  })\n})\n"
  },
  {
    "path": "packages/shared/setupTests.ts",
    "content": "import * as THREE from 'three'\nimport { WebGL2RenderingContext } from '@react-three/test-renderer/src/WebGL2RenderingContext'\nimport { extend } from '@react-three/fiber'\n\ndeclare global {\n  var IS_REACT_ACT_ENVIRONMENT: boolean\n}\n\n// Let React know that we'll be testing effectful components\nglobal.IS_REACT_ACT_ENVIRONMENT = true\n\n// PointerEvent is not in JSDOM\n// https://github.com/jsdom/jsdom/pull/2666#issuecomment-691216178\n// https://w3c.github.io/pointerevents/#pointerevent-interface\nif (!global.PointerEvent) {\n  global.PointerEvent = class extends MouseEvent implements PointerEvent {\n    readonly pointerId: number = 0\n    readonly width: number = 1\n    readonly height: number = 1\n    readonly pressure: number = 0\n    readonly tangentialPressure: number = 0\n    readonly tiltX: number = 0\n    readonly tiltY: number = 0\n    readonly twist: number = 0\n    readonly pointerType: string = ''\n    readonly isPrimary: boolean = false\n    readonly altitudeAngle: number = 0\n    readonly azimuthAngle: number = 0\n\n    constructor(type: string, params: PointerEventInit = {}) {\n      super(type, params)\n      Object.assign(this, params)\n    }\n\n    getCoalescedEvents = () => []\n    getPredictedEvents = () => []\n  }\n}\n\nglobalThis.WebGL2RenderingContext = WebGL2RenderingContext as any\nglobalThis.WebGLRenderingContext = class WebGLRenderingContext extends WebGL2RenderingContext {} as any\n\nHTMLCanvasElement.prototype.getContext = function (this: HTMLCanvasElement) {\n  return new WebGL2RenderingContext(this) as any\n}\n\n// Extend catalogue for render API in tests\nextend(THREE as any)\n"
  },
  {
    "path": "packages/test-renderer/.npmignore",
    "content": "/src/\nmarkdown/"
  },
  {
    "path": "packages/test-renderer/CHANGELOG.md",
    "content": "# @react-three/test-renderer\n\n## 9.1.0\n\n### Minor Changes\n\n- 31781e5a1fdc464cb67617cc3d7bc5d8690cd4cd: feat(RTTR): handle primitives in test-renderer and fix queries in TestInstances\n\n## 9.0.1\n\n### Patch Changes\n\n- 754861f16ac7ee93844d52057d2b8515b145fdb2: Republish as latest\n\n## 9.0.0\n\n### Major Changes\n\n- 226d2ec: feat: React 19 support\n\n## 8.2.4\n\n### Patch Changes\n\n- dec2cb28: fix(test-renderer): include types in output\n\n## 8.2.3\n\n### Patch Changes\n\n- 2007c19b: fix: republish with types\n\n## 8.2.2\n\n### Patch Changes\n\n- ff1a16f1: fix: narrow React peer dep range\n\n## 8.2.1\n\n### Patch Changes\n\n- 020bb194: fix(RTTR): set initial size for NaN in viewport\n\n## 8.2.0\n\n### Minor Changes\n\n- a5ffb08e: feat(RTTR): waitFor util\n\n## 8.1.5\n\n### Patch Changes\n\n- 673927f7: fix(RTTR): implement HTMLCanvasElement.getContext\n\n## 8.1.4\n\n### Patch Changes\n\n- 53ba22d3: fix(RTTR): fallback to canvas shim\n\n## 8.1.3\n\n### Patch Changes\n\n- 2b0be267: fix: support WebGL2\n\n## 8.1.2\n\n### Patch Changes\n\n- d9e6316d: fix: transpile class properties\n\n## 8.1.1\n\n### Patch Changes\n\n- 9a776b71: fix(RTTR): backport traverse, update fixes\n\n## 8.1.0\n\n### Minor Changes\n\n- 24c4dba4: shortcut for shadow type\n\n## 8.0.17\n\n### Patch Changes\n\n- 786ccb46: fix: restore RTTR version\n\n## 8.0.16\n\n### Patch Changes\n\n- 7b6df9df: fix: infinite loop updating cam viewport\n- Updated dependencies [7b6df9df]\n  - @react-three/fiber@8.0.26\n\n## 8.0.15\n\n### Patch Changes\n\n- b7cd0f42: update viewport on camera changes\n- Updated dependencies [b7cd0f42]\n  - @react-three/fiber@8.0.25\n\n## 8.0.14\n\n### Patch Changes\n\n- 29d03c64: revert multi attach\n- Updated dependencies [29d03c64]\n  - @react-three/fiber@8.0.23\n\n## 8.0.13\n\n### Patch Changes\n\n- 4c87bce: fix: attach, devtools, and perf fixes\n- Updated dependencies [4c87bce]\n  - @react-three/fiber@8.0.20\n\n## 8.0.12\n\n### Patch Changes\n\n- c4715d5f: allow invalidate to preempt more than 1 frame\n- Updated dependencies [c4715d5f]\n  - @react-three/fiber@8.0.15\n\n## 8.0.11\n\n### Patch Changes\n\n- 5559a119: Add support for recoverable errors\n- Updated dependencies [5559a119]\n  - @react-three/fiber@8.0.14\n\n## 8.0.10\n\n### Patch Changes\n\n- 9d77d8e2: fix: detach attribute removal\n- Updated dependencies [9d77d8e2]\n  - @react-three/fiber@8.0.13\n\n## 8.0.9\n\n### Patch Changes\n\n- 3d10413f: fix portal layers\n- Updated dependencies [3d10413f]\n  - @react-three/fiber@8.0.12\n\n## 8.0.8\n\n### Patch Changes\n\n- 5167b1e4: memoized.args can be undefined\n- Updated dependencies [5167b1e4]\n  - @react-three/fiber@8.0.11\n\n## 8.0.7\n\n### Patch Changes\n\n- eb321afd: fix: remount bug, allow portals to inject custom size\n- Updated dependencies [eb321afd]\n  - @react-three/fiber@8.0.10\n\n## 8.0.6\n\n### Patch Changes\n\n- 624df949: fix: canvas unmount race condition\"\n- Updated dependencies [624df949]\n  - @react-three/fiber@8.0.9\n\n## 8.0.5\n\n### Patch Changes\n\n- d4bafb9: fix re-parenting, useframe not working properly in portals, attach crash\n- Updated dependencies [d4bafb9]\n  - @react-three/fiber@8.0.6\n\n## 8.0.4\n\n### Patch Changes\n\n- 227c328: fix pointer for root and portals\n- Updated dependencies [227c328]\n  - @react-three/fiber@8.0.5\n\n## 8.0.3\n\n### Patch Changes\n\n- 3252aed: setevents needs to spread and be mirrored in portals\n- Updated dependencies [3252aed]\n  - @react-three/fiber@8.0.3\n\n## 8.0.2\n\n### Patch Changes\n\n- 8035d1f: fix: legacy mode\n- Updated dependencies [8035d1f]\n  - @react-three/fiber@8.0.2\n\n## 8.0.1\n\n### Patch Changes\n\n- 26db195: add legacy flag to turn of three.colormanagement\n- Updated dependencies [26db195]\n  - @react-three/fiber@8.0.1\n\n## 8.0.0\n\n### Major Changes\n\n- 385ba9c: v8 major, react-18 compat\n- 04c07b8: v8 major, react-18 compat\n\n### Patch Changes\n\n- 347ea79: new beta for library testing\n- Updated dependencies [385ba9c]\n- Updated dependencies [04c07b8]\n- Updated dependencies [347ea79]\n  - @react-three/fiber@8.0.0\n\n## 8.0.0-beta.0\n\n### Major Changes\n\n- 385ba9c: v8 major, react-18 compat\n\n### Patch Changes\n\n- Updated dependencies [385ba9c]\n  - @react-three/fiber@8.0.0-beta.0\n\n## 7.0.25-beta.0\n\n### Patch Changes\n\n- cf6316c: new beta for library testing\n- Updated dependencies [cf6316c]\n  - @react-three/fiber@8.0.0-beta.0\n\n## 7.0.24\n\n### Patch Changes\n\n- 8698734: Release latest patches\n- Updated dependencies [8698734]\n  - @react-three/fiber@7.0.25\n\n## 7.0.23\n\n### Patch Changes\n\n- 7f46ddf: cleanup captured pointers when released (#1914)\n- Updated dependencies [7f46ddf]\n  - @react-three/fiber@7.0.24\n\n## 7.0.22\n\n### Patch Changes\n\n- 30d38b1: remove logs\n- Updated dependencies [30d38b1]\n  - @react-three/fiber@7.0.23\n\n## 7.0.21\n\n### Patch Changes\n\n- 259e1fa: add camera:manual\n- Updated dependencies [259e1fa]\n  - @react-three/fiber@7.0.22\n\n## 7.0.20\n\n### Patch Changes\n\n- 65e4147: up usemeasure, add last event to internals\"\n- Updated dependencies [65e4147]\n  - @react-three/fiber@7.0.21\n\n## 7.0.19\n\n### Patch Changes\n\n- 54cb0fd: update react-use-measure, allow it to use the offsetSize\n- Updated dependencies [54cb0fd]\n  - @react-three/fiber@7.0.20\n\n## 7.0.18\n\n### Patch Changes\n\n- 7aa2eab: fix: remove zustand subscribe selector\n- Updated dependencies [7aa2eab]\n  - @react-three/fiber@7.0.19\n\n## 7.0.17\n\n### Patch Changes\n\n- 6780f58: fix unmount pointer capture\n- Updated dependencies [6780f58]\n  - @react-three/fiber@7.0.18\n\n## 7.0.16\n\n### Patch Changes\n\n- 894c550: fix: event count\n- Updated dependencies [894c550]\n  - @react-three/fiber@7.0.17\n\n## 7.0.15\n\n### Patch Changes\n\n- c7a4220: patch: applyprops returns the same instance\n- Updated dependencies [c7a4220]\n  - @react-three/fiber@7.0.16\n\n## 7.0.14\n\n### Patch Changes\n\n- c5645e8: fix primitive leftovers on switch\n- Updated dependencies [c5645e8]\n  - @react-three/fiber@7.0.15\n\n## 7.0.13\n\n### Patch Changes\n\n- 05af996: fix: revert the is function\n- Updated dependencies [05af996]\n  - @react-three/fiber@7.0.14\n\n## 7.0.12\n\n### Patch Changes\n\n- 0df6073: fix: missed events\n- Updated dependencies [0df6073]\n  - @react-three/fiber@7.0.12\n\n## 7.0.11\n\n### Patch Changes\n\n- 62b0a3a: fix: event order of missed pointers\n- Updated dependencies [62b0a3a]\n  - @react-three/fiber@7.0.11\n\n## 7.0.10\n\n### Patch Changes\n\n- e019dd4: fixes\n- Updated dependencies [e019dd4]\n  - @react-three/fiber@7.0.10\n\n## 7.0.8\n\n### Patch Changes\n\n- cd266e4: Fix diffProps dashed keys\n- Updated dependencies [cd266e4]\n  - @react-three/fiber@7.0.9\n\n## 7.0.7\n\n### Patch Changes\n\n- 0375896: Simplify useframe, support instanced event cancelation, silence disposal\n- Updated dependencies [0375896]\n  - @react-three/fiber@7.0.7\n\n## 7.0.6\n\n### Patch Changes\n\n- fb052ad: Fix babel-env browserslist transpiling into old code\"\n- Updated dependencies [fb052ad]\n  - @react-three/fiber@7.0.6\n\n## 7.0.5\n\n### Patch Changes\n\n- c97794a: Add useLoader.clear(Loader, input)\n- Updated dependencies [c97794a]\n  - @react-three/fiber@7.0.5\n\n## 7.0.4\n\n### Patch Changes\n\n- 974ecfb: Allow elements to define attachFns for specific mount/unmount\n- Updated dependencies [974ecfb]\n  - @react-three/fiber@7.0.4\n\n## 7.0.1\n\n### Patch Changes\n\n- a97aca3: Add controls state field\n- Updated dependencies [a97aca3]\n- 4c703d6: fix rttr didn't work with r130\n- Updated dependencies [4c703d6]\n- @react-three/fiber@7.0.2\n\n## 7.0.0\n\n### Major Changes\n\n- 96ae1ad: fix javascript interpreting renderpriority as positive\n\n### Patch Changes\n\n- Updated dependencies [96ae1ad]\n  - @react-three/fiber@7.0.0\n\n## 6.2.3\n\n### Patch Changes\n\n- 26bc7eb: typescript changes\n- Updated dependencies [26bc7eb]\n  - @react-three/fiber@6.2.3\n\n## 6.2.2\n\n### Patch Changes\n\n- 4f44a2c: use more helpful name with event handling in rttr\n- Updated dependencies [4f44a2c]\n  - @react-three/fiber@6.2.2\n\n## 6.1.5\n\n### Patch Changes\n\n- fix(rttr): if children is undefined return an array to map with\n- Updated dependencies [undefined]\n  - @react-three/fiber@6.1.5\n\n## 6.1.3\n\n### Patch Changes\n\n- 6faa090: Add shape to types, exclude event functions from event data\n- Updated dependencies [6faa090]\n  - @react-three/fiber@6.1.4\n\n## 6.1.2\n\n### Patch Changes\n\n- 71e72c0: Fix constructor args with attached children (#1348)\n- Updated dependencies [71e72c0]\n  - @react-three/fiber@6.1.3\n\n## 6.1.1\n"
  },
  {
    "path": "packages/test-renderer/README.md",
    "content": "# React Three Test Renderer ⚛️🔼🧪\n\n[![Version](https://img.shields.io/npm/v/@react-three/test-renderer?style=flat&colorA=000000&colorB=000000)](https://npmjs.com/package/@react-three/test-renderer)\n[![Downloads](https://img.shields.io/npm/dt/@react-three/test-renderer.svg?style=flat&colorA=000000&colorB=000000)](https://npmjs.com/package/@react-three/test-renderer)\n[![Twitter](https://img.shields.io/twitter/follow/pmndrs?label=%40pmndrs&style=flat&colorA=000000&colorB=000000&logo=twitter&logoColor=000000)](https://twitter.com/pmndrs)\n[![Twitter](https://img.shields.io/twitter/follow/_josh_ellis_?label=%40_josh_ellis_&style=flat&colorA=000000&colorB=000000&logo=twitter&logoColor=000000)](https://twitter.com/_josh_ellis_)\n[![Discord](https://img.shields.io/discord/740090768164651008?style=flat&colorA=000000&colorB=000000&label=discord&logo=discord&logoColor=000000)](https://discord.gg/ZZjjNvJ)\n\n`@react-three/test-renderer` is a React testing <a href=\"https://reactjs.org/docs/codebase-overview.html#renderers\">renderer</a> for threejs in node.\n\n```bash\nyarn add @react-three/fiber three\nyarn add -D @react-three/test-renderer\n```\n\n---\n\n## The problem\n\nYou've written a complex and amazing webgl experience using [`@react-three/fiber`](https://github.com/pmndrs/react-three-fiber) and you want to test it to make sure it works even after you add even more features.\n\nYou go to use `react-dom` but hang on, `THREE` elements aren't in the DOM! You decide to use `@react-three/test-renderer` you can see the container & the canvas but you can't see the tree for the scene!? That's because `@react-three/fiber` renders to a different react root with it's own reconciler.\n\n## The solution\n\nYou use `@react-three/test-renderer` ⚛️-🔼-🧪, an experimental React renderer using `@react-three/fiber` under the hood to expose the scene graph wrapped in a test instance providing helpful utilities to test with.\n\nEssentially, this package makes it easy to grab a snapshot of the Scene Graph rendered by `three` without the need for webgl & browser.\n\n---\n\n## Usage\n\nRTTR is testing library agnostic, so we hope that it works with libraries such as [`jest`](https://jestjs.io/), [`jasmine`](https://jasmine.github.io/) etc.\n\n```tsx\nimport ReactThreeTestRenderer from '@react-three/test-renderer'\n\nconst renderer = await ReactThreeTestRenderer.create(\n  <mesh>\n    <boxGeometry args={[2, 2]} />\n    <meshStandardMaterial\n      args={[\n        {\n          color: 0x0000ff,\n        },\n      ]}\n    />\n  </mesh>,\n)\n\n// assertions using the TestInstance & Scene Graph\nconsole.log(renderer.toGraph())\n```\n\n---\n\n## API\n\n- [React Three Test Renderer API](/packages/test-renderer/markdown/rttr.md)\n- [React Three Test Instance API](/packages/test-renderer/markdown/rttr-instance.md)\n"
  },
  {
    "path": "packages/test-renderer/markdown/rttr-instance.md",
    "content": "# React Three Test Instance API\n\n## Table of Contents\n\n- [`ReactThreeTestInstance`](#instance)\n  - Properties\n    - [`instance`](#instance-prop-instance)\n    - [`type`](#instance-prop-type)\n    - [`props`](#instance-prop-props)\n    - [`parent`](#instance-prop-parent)\n    - [`children`](#instance-prop-children)\n    - [`allChildren`](#instance-prop-allChildren)\n  - Methods\n    - [`find`](#instance-meth-find)\n    - [`findAll`](#instance-meth-findall)\n    - [`findByType`](#instance-meth-findbytype)\n    - [`findAllByType`](#instance-meth-findallbytype)\n    - [`findByProps`](#instance-meth-findbyprops)\n    - [`findAllByProps`](#instance-meth-findallbyprops)\n\n---\n\n## `ReactThreeTestInstance` ⚛️\n\nThis is an internal class that wraps the elements returned from [`ReactThreeTestRenderer.create`](/packages/test-renderer/markdown/rttr.md#create). It has several properties & methods to enhance the testing experience. Similar to the core API, it closely mirrors the API of [`react-test-renderer`](https://reactjs.org/docs/test-renderer.html).\n\n### `instance` <a id=\"instance-prop-instance\"></a>\n\n```ts\ntestInstance.instance\n```\n\nReturns the instance object of the specific testInstance. This will be the `THREE` initialized class.\n\n### `type` <a id=\"instance-prop-type\"></a>\n\n```ts\ntestInstance.type\n```\n\nReturns the `THREE` type of the test instance, e.g `Scene` or `Mesh`.\n\n### `props` <a id=\"instance-prop-props\"></a>\n\n```ts\ntestInstance.props\n```\n\nReturns an object of the props that are currently being passed to the element. This will include hidden ones such as `attach=\"geometry\"` which are automatically applied in the reconciler.\n\n### `parent` <a id=\"instance-prop-parent\"></a>\n\n```ts\ntestInstance.parent\n```\n\nReturns the parent testInstance of this testInstance. If no parent is available, it will return `null`.\n\n### `children` <a id=\"instance-prop-children\"></a>\n\n```ts\ntestInstance.children\n```\n\nReturns the children test instances of this test instance according to the property `children`, this will not include Geometries, Materials etc.\n\n### `allChildren` <a id=\"instance-prop-allChildren\"></a>\n\n```ts\ntestInstance.allChildren\n```\n\nReturns all the children testInstances of this test instance, this will be as thorough as [`testRenderer.toTree()`](/packages/test-renderer/markdown/rttr.md#create-totree) capturing all react components in the tree.\n\n### `find()` <a id=\"instance-meth-find\"></a>\n\n```ts\ntestInstance.find(test)\n```\n\nFind a single test instance for which `test(testInstance)` returns `true`. If `test(testInstance)` does not return `true` for exactly one test instance it will throw an error.\n\n### `findAll()` <a id=\"instance-meth-findall\"></a>\n\n```ts\ntestInstance.findAll(test)\n```\n\nFinds all test instances for which `test(testInstance)` returns `true`. If no test instances are found, it will return an empty array.\n\n### `findByType()` <a id=\"instance-meth-findbytype\"></a>\n\n```ts\ntestInstance.findByType(type)\n```\n\nFind a single test instance with the provided type. If there is not exactly one test instance with the provided type it will throw an error.\n\n### `findAllByType()` <a id=\"instance-meth-findallbytype\"></a>\n\n```ts\ntestInstance.findAllByType(type)\n```\n\nFinds all test instances with the provided type. If no test instances are found, it will return an empty array.\n\n### `findByProps()` <a id=\"instance-meth-findbyprops\"></a>\n\n```ts\ntestInstance.findByProps(props)\n\n// Also accepts RegExp matchers\ntestInstance.findByProps({ [prop]: /^match/i })\n```\n\nFind a single test instance with the provided props. If there is not exactly one test instance with the provided props it will throw an error.\n\n### `findAllByProps()` <a id=\"instance-meth-findallbyprops\"></a>\n\n```ts\ntestInstance.findAllByProps(props)\n\n// Also accepts RegExp matchers\ntestInstance.findAllByProps({ [prop]: /^matches/i })\n```\n\nFinds all test instances with the provided props. If no test instances are found, it will return an empty array.\n"
  },
  {
    "path": "packages/test-renderer/markdown/rttr.md",
    "content": "# React Three Test Renderer API\n\n## Table of Contents\n\n- [`create()`](#create)\n  - [`scene`](#create-scene)\n  - [`getInstance()`](#create-getinstance)\n  - [`toTree()`](#create-totree)\n  - [`toGraph()`](#create-tograph)\n  - [`fireEvent()`](#create-fireevent)\n  - [`advanceFrames()`](#create-advanceframes)\n  - [`update()`](#create-update)\n  - [`unmount()`](#create-unmount)\n- [`act()`](#act)\n\n---\n\n## `create()` 🧪 <a id=\"create\"></a>\n\n```tsx\nconst renderer = ReactThreeTestRenderer.create(element, options)\n```\n\nCreate a ReactThreeTestRenderer instance with a passed `three` element e.g. `<mesh />`. By default, it won't create an actual `THREE.WebGLRenderer` and there will be no loop. But it will still render the complete scene graph. Returns the properties below.\n\n#### CreateOptions\n\n```ts\n// RenderProps is from react-three-fiber\ninterface CreateOptions extends RenderProps<HTMLCanvasElement> {\n  width?: number // width of canvas\n  height?: number // height of canvas\n}\n```\n\n### `scene` <a id=\"create-scene\"></a>\n\n```tsx\nrenderer.scene\n```\n\nReturns the root “react three test instance” object that is useful for making assertions. You can use it to find other “test instances” deeper below.\n\n### `getInstance()` <a id=\"create-getinstance\"></a>\n\n```tsx\nrenderer.getInstance()\n```\n\nReturn the instance corresponding to the root three element, if available. This will not work if the root element is a function component because they don’t have instances.\n\n### `toTree()` <a id=\"create-totree\"></a>\n\n```tsx\nrenderer.toTree()\n```\n\nReturns an object representing the rendered tree similar to [`react-test-renderer`](https://reactjs.org/docs/test-renderer.html#overview). This will include all elements written as react components.\n\n### `toGraph()` <a id=\"create-tograph\"></a>\n\n```tsx\nrenderer.toGraph()\n```\n\nReturns an object representing the [`scene graph`](https://threejs.org/manual/#en/scenegraph). This will not include all elements such as ones that use `attach`.\n\n### `fireEvent()` <a id=\"create-fireevent\"></a>\n\n```tsx\nrenderer.fireEvent(testInstance, eventName, mockEventData)\n```\n\nNative method to fire events on the specific part of the rendered tree through passing an element within the tree and an event name. The third argument is appended to the [`MockSyntheticEvent`](#create-fireevent-mocksyntheticevent) passed to the event handler.\n\nEvent names follow camelCase convention (e.g. `pointerUp`), or you can pass event handler name instead (e.g. `onPointerUp`).\n\n#### `MockSyntheticEvent` <a id=\"create-fireevent-mocksyntheticevent\"></a>\n\n```ts\ntype MockSyntheticEvent = {\n  camera: Camera // the default camera of the rendered scene\n  stopPropagation: () => void\n  target: ReactThreeTestInstance\n  currentTarget: ReactThreeTestInstance\n  sourceEvent: MockEventData\n  ...mockEventData\n}\n```\n\n### `advanceFrames()` <a id=\"create-advanceframes\"></a>\n\n```tsx\nrenderer.advanceFrames(frames, delta)\n```\n\nNative method to advance the frames (therefore running subscribers to the GL Render loop such as `useFrame`). Requires an amount of frames to advance by & a parameter of delta to pass to the subscribers creating a more controlled testing environment.\n\n### `update()` <a id=\"create-update\"></a>\n\n```tsx\nrenderer.update(element)\n```\n\nRerender the tree with the new root element. This simulates a react update at the update, thus updating the children below. If the new element has the same type and key as the previous element, the tree will be updated.\n\n### `unmount()` <a id=\"create-unmount\"></a>\n\n```tsx\nrenderer.unmount()\n```\n\nUnmount the tree, triggering the appropriate lifecycle events.\n\n---\n\n## `act()` ⚛️ <a id=\"act\"></a>\n\n```tsx\nReactThreeTestRenderer.act(callback)\n```\n\nSimilar to the [`act()` in `react-test-renderer`](https://reactjs.org/docs/test-renderer.html#testrendereract). `ReactThreeTestRenderer.act` prepares a component for assertions. Unlike `react-test-renderer` you do not have to wrap calls to `ReactThreeTestRenderer.create` and `renderer.update`.\n\n#### Act example (using jest)\n\n```tsx\nimport ReactThreeTestRenderer from '@react-three/test-renderer'\n\nconst Mesh = () => {\n  const meshRef = React.useRef()\n  useFrame((_, delta) => {\n    meshRef.current.rotation.x += delta\n  })\n\n  return (\n    <mesh ref={meshRef}>\n      <boxGeometry args={[2, 2]} />\n      <meshBasicMaterial />\n    </mesh>\n  )\n}\n\nconst renderer = await ReactThreeTestRenderer.create(<Mesh />)\n\nexpect(renderer.scene.children[0].instance.rotation.x).toEqual(0)\n\nawait ReactThreeTestRenderer.act(async () => {\n  await renderer.advanceFrames(2, 1)\n})\n\nexpect(renderer.scene.children[0].instance.rotation.x).toEqual(2)\n```\n"
  },
  {
    "path": "packages/test-renderer/package.json",
    "content": "{\n  \"name\": \"@react-three/test-renderer\",\n  \"version\": \"9.1.0\",\n  \"description\": \"Test Renderer for react-three-fiber\",\n  \"author\": \"Josh Ellis\",\n  \"license\": \"MIT\",\n  \"private\": false,\n  \"main\": \"dist/react-three-test-renderer.cjs.js\",\n  \"module\": \"dist/react-three-test-renderer.esm.js\",\n  \"types\": \"dist/react-three-test-renderer.cjs.d.ts\",\n  \"bugs\": {\n    \"url\": \"https://github.com/pmndrs/react-three-fiber/issues\"\n  },\n  \"homepage\": \"https://github.com/pmndrs/react-three-fiber/packages/react-three-test-renderer\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/pmndrs/react-three-fiber.git\"\n  },\n  \"preconstruct\": {\n    \"entrypoints\": [\n      \"index.tsx\"\n    ]\n  },\n  \"peerDependencies\": {\n    \"react\": \"^19.0.0\",\n    \"@react-three/fiber\": \">=9.0.0\",\n    \"three\": \">=0.156\"\n  }\n}\n"
  },
  {
    "path": "packages/test-renderer/src/WebGL2RenderingContext.ts",
    "content": "const functions = [\n  'activeTexture',\n  'attachShader',\n  'beginQuery',\n  'beginTransformFeedback',\n  'bindAttribLocation',\n  'bindBufferBase',\n  'bindBufferRange',\n  'bindRenderbuffer',\n  'bindSampler',\n  'bindTransformFeedback',\n  'bindVertexArray',\n  'blendColor',\n  'blendEquation',\n  'blendEquationSeparate',\n  'blendFunc',\n  'blendFuncSeparate',\n  'blitFramebuffer',\n  'bufferData',\n  'bufferSubData',\n  'checkFramebufferStatus',\n  'clientWaitSync',\n  'compileShader',\n  'compressedTexImage2D',\n  'compressedTexImage3D',\n  'compressedTexSubImage2D',\n  'compressedTexSubImage3D',\n  'copyBufferSubData',\n  'copyTexImage2D',\n  'copyTexSubImage2D',\n  'copyTexSubImage3D',\n  'createBuffer',\n  'createFramebuffer',\n  'createProgram',\n  'createQuery',\n  'createRenderbuffer',\n  'createSampler',\n  'createShader',\n  'createTexture',\n  'createTransformFeedback',\n  'createVertexArray',\n  'cullFace',\n  'deleteBuffer',\n  'deleteFramebuffer',\n  'deleteProgram',\n  'deleteQuery',\n  'deleteRenderbuffer',\n  'deleteSampler',\n  'deleteShader',\n  'deleteSync',\n  'deleteTexture',\n  'deleteTransformFeedback',\n  'deleteVertexArray',\n  'depthFunc',\n  'depthMask',\n  'depthRange',\n  'detachShader',\n  'disable',\n  'drawArraysInstanced',\n  'drawElementsInstanced',\n  'drawRangeElements',\n  'enable',\n  'endQuery',\n  'endTransformFeedback',\n  'fenceSync',\n  'finish',\n  'flush',\n  'framebufferRenderbuffer',\n  'framebufferTexture2D',\n  'framebufferTextureLayer',\n  'frontFace',\n  'generateMipmap',\n  'getActiveAttrib',\n  'getActiveUniform',\n  'getActiveUniformBlockName',\n  'getActiveUniformBlockParameter',\n  'getActiveUniforms',\n  'getAttachedShaders',\n  'getAttribLocation',\n  'getBufferParameter',\n  'getBufferSubData',\n  'getContextAttributes',\n  'getError',\n  'getExtension',\n  'getFragDataLocation',\n  'getFramebufferAttachmentParameter',\n  'getIndexedParameter',\n  'getInternalformatParameter',\n  'getParameter',\n  'getProgramInfoLog',\n  'getProgramParameter',\n  'getQuery',\n  'getQueryParameter',\n  'getRenderbufferParameter',\n  'getSamplerParameter',\n  'getShaderInfoLog',\n  'getShaderParameter',\n  'getShaderPrecisionFormat',\n  'getShaderSource',\n  'getSupportedExtensions',\n  'getSyncParameter',\n  'getTexParameter',\n  'getTransformFeedbackVarying',\n  'getUniform',\n  'getUniformBlockIndex',\n  'getUniformIndices',\n  'getUniformLocation',\n  'getVertexAttrib',\n  'getVertexAttribOffset',\n  'hint',\n  'invalidateFramebuffer',\n  'invalidateSubFramebuffer',\n  'isBuffer',\n  'isContextLost',\n  'isEnabled',\n  'isFramebuffer',\n  'isProgram',\n  'isQuery',\n  'isRenderbuffer',\n  'isSampler',\n  'isShader',\n  'isSync',\n  'isTexture',\n  'isTransformFeedback',\n  'isVertexArray',\n  'lineWidth',\n  'linkProgram',\n  'pauseTransformFeedback',\n  'pixelStorei',\n  'polygonOffset',\n  'readBuffer',\n  'readPixels',\n  'renderbufferStorage',\n  'renderbufferStorageMultisample',\n  'resumeTransformFeedback',\n  'sampleCoverage',\n  'samplerParameterf',\n  'samplerParameteri',\n  'shaderSource',\n  'stencilFunc',\n  'stencilFuncSeparate',\n  'stencilMask',\n  'stencilMaskSeparate',\n  'stencilOp',\n  'stencilOpSeparate',\n  'texImage2D',\n  'texImage3D',\n  'texParameterf',\n  'texParameteri',\n  'texStorage2D',\n  'texStorage3D',\n  'texSubImage2D',\n  'texSubImage3D',\n  'transformFeedbackVaryings',\n  'uniform1ui',\n  'uniform2ui',\n  'uniform3ui',\n  'uniform4ui',\n  'uniformBlockBinding',\n  'useProgram',\n  'validateProgram',\n  'vertexAttribDivisor',\n  'vertexAttribI4i',\n  'vertexAttribI4ui',\n  'vertexAttribIPointer',\n  'waitSync',\n  'bindBuffer',\n  'bindFramebuffer',\n  'bindTexture',\n  'clear',\n  'clearBufferfi',\n  'clearBufferfv',\n  'clearBufferiv',\n  'clearBufferuiv',\n  'clearColor',\n  'clearDepth',\n  'clearStencil',\n  'colorMask',\n  'disableVertexAttribArray',\n  'drawArrays',\n  'drawBuffers',\n  'drawElements',\n  'enableVertexAttribArray',\n  'scissor',\n  'uniform1f',\n  'uniform1fv',\n  'uniform1i',\n  'uniform1iv',\n  'uniform1uiv',\n  'uniform2f',\n  'uniform2fv',\n  'uniform2i',\n  'uniform2iv',\n  'uniform2uiv',\n  'uniform3f',\n  'uniform3fv',\n  'uniform3i',\n  'uniform3iv',\n  'uniform3uiv',\n  'uniform4f',\n  'uniform4fv',\n  'uniform4i',\n  'uniform4iv',\n  'uniform4uiv',\n  'uniformMatrix2fv',\n  'uniformMatrix2x3fv',\n  'uniformMatrix2x4fv',\n  'uniformMatrix3fv',\n  'uniformMatrix3x2fv',\n  'uniformMatrix3x4fv',\n  'uniformMatrix4fv',\n  'uniformMatrix4x2fv',\n  'uniformMatrix4x3fv',\n  'vertexAttrib1f',\n  'vertexAttrib1fv',\n  'vertexAttrib2f',\n  'vertexAttrib2fv',\n  'vertexAttrib3f',\n  'vertexAttrib3fv',\n  'vertexAttrib4f',\n  'vertexAttrib4fv',\n  'vertexAttribI4iv',\n  'vertexAttribI4uiv',\n  'vertexAttribPointer',\n  'viewport',\n  'makeXRCompatible',\n]\n\nconst enums: Record<string, number> = {\n  DEPTH_BUFFER_BIT: 256,\n  STENCIL_BUFFER_BIT: 1024,\n  COLOR_BUFFER_BIT: 16384,\n  POINTS: 0,\n  LINES: 1,\n  LINE_LOOP: 2,\n  LINE_STRIP: 3,\n  TRIANGLES: 4,\n  TRIANGLE_STRIP: 5,\n  TRIANGLE_FAN: 6,\n  ZERO: 0,\n  ONE: 1,\n  SRC_COLOR: 768,\n  ONE_MINUS_SRC_COLOR: 769,\n  SRC_ALPHA: 770,\n  ONE_MINUS_SRC_ALPHA: 771,\n  DST_ALPHA: 772,\n  ONE_MINUS_DST_ALPHA: 773,\n  DST_COLOR: 774,\n  ONE_MINUS_DST_COLOR: 775,\n  SRC_ALPHA_SATURATE: 776,\n  FUNC_ADD: 32774,\n  BLEND_EQUATION: 32777,\n  BLEND_EQUATION_RGB: 32777,\n  BLEND_EQUATION_ALPHA: 34877,\n  FUNC_SUBTRACT: 32778,\n  FUNC_REVERSE_SUBTRACT: 32779,\n  BLEND_DST_RGB: 32968,\n  BLEND_SRC_RGB: 32969,\n  BLEND_DST_ALPHA: 32970,\n  BLEND_SRC_ALPHA: 32971,\n  CONSTANT_COLOR: 32769,\n  ONE_MINUS_CONSTANT_COLOR: 32770,\n  CONSTANT_ALPHA: 32771,\n  ONE_MINUS_CONSTANT_ALPHA: 32772,\n  BLEND_COLOR: 32773,\n  ARRAY_BUFFER: 34962,\n  ELEMENT_ARRAY_BUFFER: 34963,\n  ARRAY_BUFFER_BINDING: 34964,\n  ELEMENT_ARRAY_BUFFER_BINDING: 34965,\n  STREAM_DRAW: 35040,\n  STATIC_DRAW: 35044,\n  DYNAMIC_DRAW: 35048,\n  BUFFER_SIZE: 34660,\n  BUFFER_USAGE: 34661,\n  CURRENT_VERTEX_ATTRIB: 34342,\n  FRONT: 1028,\n  BACK: 1029,\n  FRONT_AND_BACK: 1032,\n  TEXTURE_2D: 3553,\n  CULL_FACE: 2884,\n  BLEND: 3042,\n  DITHER: 3024,\n  STENCIL_TEST: 2960,\n  DEPTH_TEST: 2929,\n  SCISSOR_TEST: 3089,\n  POLYGON_OFFSET_FILL: 32823,\n  SAMPLE_ALPHA_TO_COVERAGE: 32926,\n  SAMPLE_COVERAGE: 32928,\n  NO_ERROR: 0,\n  INVALID_ENUM: 1280,\n  INVALID_VALUE: 1281,\n  INVALID_OPERATION: 1282,\n  OUT_OF_MEMORY: 1285,\n  CW: 2304,\n  CCW: 2305,\n  LINE_WIDTH: 2849,\n  ALIASED_POINT_SIZE_RANGE: 33901,\n  ALIASED_LINE_WIDTH_RANGE: 33902,\n  CULL_FACE_MODE: 2885,\n  FRONT_FACE: 2886,\n  DEPTH_RANGE: 2928,\n  DEPTH_WRITEMASK: 2930,\n  DEPTH_CLEAR_VALUE: 2931,\n  DEPTH_FUNC: 2932,\n  STENCIL_CLEAR_VALUE: 2961,\n  STENCIL_FUNC: 2962,\n  STENCIL_FAIL: 2964,\n  STENCIL_PASS_DEPTH_FAIL: 2965,\n  STENCIL_PASS_DEPTH_PASS: 2966,\n  STENCIL_REF: 2967,\n  STENCIL_VALUE_MASK: 2963,\n  STENCIL_WRITEMASK: 2968,\n  STENCIL_BACK_FUNC: 34816,\n  STENCIL_BACK_FAIL: 34817,\n  STENCIL_BACK_PASS_DEPTH_FAIL: 34818,\n  STENCIL_BACK_PASS_DEPTH_PASS: 34819,\n  STENCIL_BACK_REF: 36003,\n  STENCIL_BACK_VALUE_MASK: 36004,\n  STENCIL_BACK_WRITEMASK: 36005,\n  VIEWPORT: 2978,\n  SCISSOR_BOX: 3088,\n  COLOR_CLEAR_VALUE: 3106,\n  COLOR_WRITEMASK: 3107,\n  UNPACK_ALIGNMENT: 3317,\n  PACK_ALIGNMENT: 3333,\n  MAX_TEXTURE_SIZE: 3379,\n  MAX_VIEWPORT_DIMS: 3386,\n  SUBPIXEL_BITS: 3408,\n  RED_BITS: 3410,\n  GREEN_BITS: 3411,\n  BLUE_BITS: 3412,\n  ALPHA_BITS: 3413,\n  DEPTH_BITS: 3414,\n  STENCIL_BITS: 3415,\n  POLYGON_OFFSET_UNITS: 10752,\n  POLYGON_OFFSET_FACTOR: 32824,\n  TEXTURE_BINDING_2D: 32873,\n  SAMPLE_BUFFERS: 32936,\n  SAMPLES: 32937,\n  SAMPLE_COVERAGE_VALUE: 32938,\n  SAMPLE_COVERAGE_INVERT: 32939,\n  COMPRESSED_TEXTURE_FORMATS: 34467,\n  DONT_CARE: 4352,\n  FASTEST: 4353,\n  NICEST: 4354,\n  GENERATE_MIPMAP_HINT: 33170,\n  BYTE: 5120,\n  UNSIGNED_BYTE: 5121,\n  SHORT: 5122,\n  UNSIGNED_SHORT: 5123,\n  INT: 5124,\n  UNSIGNED_INT: 5125,\n  FLOAT: 5126,\n  DEPTH_COMPONENT: 6402,\n  ALPHA: 6406,\n  RGB: 6407,\n  RGBA: 6408,\n  LUMINANCE: 6409,\n  LUMINANCE_ALPHA: 6410,\n  UNSIGNED_SHORT_4_4_4_4: 32819,\n  UNSIGNED_SHORT_5_5_5_1: 32820,\n  UNSIGNED_SHORT_5_6_5: 33635,\n  FRAGMENT_SHADER: 35632,\n  VERTEX_SHADER: 35633,\n  MAX_VERTEX_ATTRIBS: 34921,\n  MAX_VERTEX_UNIFORM_VECTORS: 36347,\n  MAX_VARYING_VECTORS: 36348,\n  MAX_COMBINED_TEXTURE_IMAGE_UNITS: 35661,\n  MAX_VERTEX_TEXTURE_IMAGE_UNITS: 35660,\n  MAX_TEXTURE_IMAGE_UNITS: 34930,\n  MAX_FRAGMENT_UNIFORM_VECTORS: 36349,\n  SHADER_TYPE: 35663,\n  DELETE_STATUS: 35712,\n  LINK_STATUS: 35714,\n  VALIDATE_STATUS: 35715,\n  ATTACHED_SHADERS: 35717,\n  ACTIVE_UNIFORMS: 35718,\n  ACTIVE_ATTRIBUTES: 35721,\n  SHADING_LANGUAGE_VERSION: 35724,\n  CURRENT_PROGRAM: 35725,\n  NEVER: 512,\n  LESS: 513,\n  EQUAL: 514,\n  LEQUAL: 515,\n  GREATER: 516,\n  NOTEQUAL: 517,\n  GEQUAL: 518,\n  ALWAYS: 519,\n  KEEP: 7680,\n  REPLACE: 7681,\n  INCR: 7682,\n  DECR: 7683,\n  INVERT: 5386,\n  INCR_WRAP: 34055,\n  DECR_WRAP: 34056,\n  VENDOR: 7936,\n  RENDERER: 7937,\n  VERSION: 7938,\n  NEAREST: 9728,\n  LINEAR: 9729,\n  NEAREST_MIPMAP_NEAREST: 9984,\n  LINEAR_MIPMAP_NEAREST: 9985,\n  NEAREST_MIPMAP_LINEAR: 9986,\n  LINEAR_MIPMAP_LINEAR: 9987,\n  TEXTURE_MAG_FILTER: 10240,\n  TEXTURE_MIN_FILTER: 10241,\n  TEXTURE_WRAP_S: 10242,\n  TEXTURE_WRAP_T: 10243,\n  TEXTURE: 5890,\n  TEXTURE_CUBE_MAP: 34067,\n  TEXTURE_BINDING_CUBE_MAP: 34068,\n  TEXTURE_CUBE_MAP_POSITIVE_X: 34069,\n  TEXTURE_CUBE_MAP_NEGATIVE_X: 34070,\n  TEXTURE_CUBE_MAP_POSITIVE_Y: 34071,\n  TEXTURE_CUBE_MAP_NEGATIVE_Y: 34072,\n  TEXTURE_CUBE_MAP_POSITIVE_Z: 34073,\n  TEXTURE_CUBE_MAP_NEGATIVE_Z: 34074,\n  MAX_CUBE_MAP_TEXTURE_SIZE: 34076,\n  TEXTURE0: 33984,\n  TEXTURE1: 33985,\n  TEXTURE2: 33986,\n  TEXTURE3: 33987,\n  TEXTURE4: 33988,\n  TEXTURE5: 33989,\n  TEXTURE6: 33990,\n  TEXTURE7: 33991,\n  TEXTURE8: 33992,\n  TEXTURE9: 33993,\n  TEXTURE10: 33994,\n  TEXTURE11: 33995,\n  TEXTURE12: 33996,\n  TEXTURE13: 33997,\n  TEXTURE14: 33998,\n  TEXTURE15: 33999,\n  TEXTURE16: 34000,\n  TEXTURE17: 34001,\n  TEXTURE18: 34002,\n  TEXTURE19: 34003,\n  TEXTURE20: 34004,\n  TEXTURE21: 34005,\n  TEXTURE22: 34006,\n  TEXTURE23: 34007,\n  TEXTURE24: 34008,\n  TEXTURE25: 34009,\n  TEXTURE26: 34010,\n  TEXTURE27: 34011,\n  TEXTURE28: 34012,\n  TEXTURE29: 34013,\n  TEXTURE30: 34014,\n  TEXTURE31: 34015,\n  ACTIVE_TEXTURE: 34016,\n  REPEAT: 10497,\n  CLAMP_TO_EDGE: 33071,\n  MIRRORED_REPEAT: 33648,\n  FLOAT_VEC2: 35664,\n  FLOAT_VEC3: 35665,\n  FLOAT_VEC4: 35666,\n  INT_VEC2: 35667,\n  INT_VEC3: 35668,\n  INT_VEC4: 35669,\n  BOOL: 35670,\n  BOOL_VEC2: 35671,\n  BOOL_VEC3: 35672,\n  BOOL_VEC4: 35673,\n  FLOAT_MAT2: 35674,\n  FLOAT_MAT3: 35675,\n  FLOAT_MAT4: 35676,\n  SAMPLER_2D: 35678,\n  SAMPLER_CUBE: 35680,\n  VERTEX_ATTRIB_ARRAY_ENABLED: 34338,\n  VERTEX_ATTRIB_ARRAY_SIZE: 34339,\n  VERTEX_ATTRIB_ARRAY_STRIDE: 34340,\n  VERTEX_ATTRIB_ARRAY_TYPE: 34341,\n  VERTEX_ATTRIB_ARRAY_NORMALIZED: 34922,\n  VERTEX_ATTRIB_ARRAY_POINTER: 34373,\n  VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: 34975,\n  IMPLEMENTATION_COLOR_READ_TYPE: 35738,\n  IMPLEMENTATION_COLOR_READ_FORMAT: 35739,\n  COMPILE_STATUS: 35713,\n  LOW_FLOAT: 36336,\n  MEDIUM_FLOAT: 36337,\n  HIGH_FLOAT: 36338,\n  LOW_INT: 36339,\n  MEDIUM_INT: 36340,\n  HIGH_INT: 36341,\n  FRAMEBUFFER: 36160,\n  RENDERBUFFER: 36161,\n  RGBA4: 32854,\n  RGB5_A1: 32855,\n  RGB565: 36194,\n  DEPTH_COMPONENT16: 33189,\n  STENCIL_INDEX: 6401,\n  STENCIL_INDEX8: 36168,\n  DEPTH_STENCIL: 34041,\n  RENDERBUFFER_WIDTH: 36162,\n  RENDERBUFFER_HEIGHT: 36163,\n  RENDERBUFFER_INTERNAL_FORMAT: 36164,\n  RENDERBUFFER_RED_SIZE: 36176,\n  RENDERBUFFER_GREEN_SIZE: 36177,\n  RENDERBUFFER_BLUE_SIZE: 36178,\n  RENDERBUFFER_ALPHA_SIZE: 36179,\n  RENDERBUFFER_DEPTH_SIZE: 36180,\n  RENDERBUFFER_STENCIL_SIZE: 36181,\n  FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: 36048,\n  FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: 36049,\n  FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: 36050,\n  FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: 36051,\n  COLOR_ATTACHMENT0: 36064,\n  DEPTH_ATTACHMENT: 36096,\n  STENCIL_ATTACHMENT: 36128,\n  DEPTH_STENCIL_ATTACHMENT: 33306,\n  NONE: 0,\n  FRAMEBUFFER_COMPLETE: 36053,\n  FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 36054,\n  FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 36055,\n  FRAMEBUFFER_INCOMPLETE_DIMENSIONS: 36057,\n  FRAMEBUFFER_UNSUPPORTED: 36061,\n  FRAMEBUFFER_BINDING: 36006,\n  RENDERBUFFER_BINDING: 36007,\n  MAX_RENDERBUFFER_SIZE: 34024,\n  INVALID_FRAMEBUFFER_OPERATION: 1286,\n  UNPACK_FLIP_Y_WEBGL: 37440,\n  UNPACK_PREMULTIPLY_ALPHA_WEBGL: 37441,\n  CONTEXT_LOST_WEBGL: 37442,\n  UNPACK_COLORSPACE_CONVERSION_WEBGL: 37443,\n  BROWSER_DEFAULT_WEBGL: 37444,\n  READ_BUFFER: 3074,\n  UNPACK_ROW_LENGTH: 3314,\n  UNPACK_SKIP_ROWS: 3315,\n  UNPACK_SKIP_PIXELS: 3316,\n  PACK_ROW_LENGTH: 3330,\n  PACK_SKIP_ROWS: 3331,\n  PACK_SKIP_PIXELS: 3332,\n  COLOR: 6144,\n  DEPTH: 6145,\n  STENCIL: 6146,\n  RED: 6403,\n  UNPACK_SKIP_IMAGES: 32877,\n  UNPACK_IMAGE_HEIGHT: 32878,\n  TEXTURE_WRAP_R: 32882,\n  MAX_ELEMENTS_VERTICES: 33000,\n  MAX_ELEMENTS_INDICES: 33001,\n  TEXTURE_MIN_LOD: 33082,\n  TEXTURE_MAX_LOD: 33083,\n  TEXTURE_BASE_LEVEL: 33084,\n  TEXTURE_MAX_LEVEL: 33085,\n  MIN: 32775,\n  MAX: 32776,\n  MAX_TEXTURE_LOD_BIAS: 34045,\n  TEXTURE_COMPARE_MODE: 34892,\n  TEXTURE_COMPARE_FUNC: 34893,\n  CURRENT_QUERY: 34917,\n  QUERY_RESULT: 34918,\n  QUERY_RESULT_AVAILABLE: 34919,\n  STREAM_READ: 35041,\n  STREAM_COPY: 35042,\n  STATIC_READ: 35045,\n  STATIC_COPY: 35046,\n  DYNAMIC_READ: 35049,\n  DYNAMIC_COPY: 35050,\n  MAX_DRAW_BUFFERS: 34852,\n  MAX_FRAGMENT_UNIFORM_COMPONENTS: 35657,\n  MAX_VERTEX_UNIFORM_COMPONENTS: 35658,\n  FRAGMENT_SHADER_DERIVATIVE_HINT: 35723,\n  PIXEL_PACK_BUFFER: 35051,\n  PIXEL_UNPACK_BUFFER: 35052,\n  PIXEL_PACK_BUFFER_BINDING: 35053,\n  PIXEL_UNPACK_BUFFER_BINDING: 35055,\n  SRGB: 35904,\n  COMPARE_REF_TO_TEXTURE: 34894,\n  VERTEX_ATTRIB_ARRAY_INTEGER: 35069,\n  MAX_ARRAY_TEXTURE_LAYERS: 35071,\n  MIN_PROGRAM_TEXEL_OFFSET: 35076,\n  MAX_PROGRAM_TEXEL_OFFSET: 35077,\n  MAX_VARYING_COMPONENTS: 35659,\n  TRANSFORM_FEEDBACK_BUFFER_MODE: 35967,\n  MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS: 35968,\n  TRANSFORM_FEEDBACK_VARYINGS: 35971,\n  TRANSFORM_FEEDBACK_BUFFER_START: 35972,\n  TRANSFORM_FEEDBACK_BUFFER_SIZE: 35973,\n  TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: 35976,\n  RASTERIZER_DISCARD: 35977,\n  MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS: 35978,\n  MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: 35979,\n  INTERLEAVED_ATTRIBS: 35980,\n  SEPARATE_ATTRIBS: 35981,\n  TRANSFORM_FEEDBACK_BUFFER: 35982,\n  TRANSFORM_FEEDBACK_BUFFER_BINDING: 35983,\n  RED_INTEGER: 36244,\n  RGB_INTEGER: 36248,\n  RGBA_INTEGER: 36249,\n  SAMPLER_CUBE_SHADOW: 36293,\n  INT_SAMPLER_CUBE: 36300,\n  UNSIGNED_INT_SAMPLER_CUBE: 36308,\n  FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: 33296,\n  FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: 33297,\n  FRAMEBUFFER_ATTACHMENT_RED_SIZE: 33298,\n  FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: 33299,\n  FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: 33300,\n  FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: 33301,\n  FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: 33302,\n  FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: 33303,\n  FRAMEBUFFER_DEFAULT: 33304,\n  UNSIGNED_NORMALIZED: 35863,\n  DRAW_FRAMEBUFFER_BINDING: 36006,\n  READ_FRAMEBUFFER: 36008,\n  DRAW_FRAMEBUFFER: 36009,\n  READ_FRAMEBUFFER_BINDING: 36010,\n  RENDERBUFFER_SAMPLES: 36011,\n  FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER: 36052,\n  MAX_COLOR_ATTACHMENTS: 36063,\n  FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: 36182,\n  MAX_SAMPLES: 36183,\n  HALF_FLOAT: 5131,\n  RG: 33319,\n  RG_INTEGER: 33320,\n  VERTEX_ARRAY_BINDING: 34229,\n  SIGNED_NORMALIZED: 36764,\n  COPY_READ_BUFFER: 36662,\n  COPY_WRITE_BUFFER: 36663,\n  COPY_READ_BUFFER_BINDING: 36662,\n  COPY_WRITE_BUFFER_BINDING: 36663,\n  UNIFORM_BUFFER: 35345,\n  UNIFORM_BUFFER_BINDING: 35368,\n  UNIFORM_BUFFER_START: 35369,\n  UNIFORM_BUFFER_SIZE: 35370,\n  MAX_VERTEX_UNIFORM_BLOCKS: 35371,\n  MAX_FRAGMENT_UNIFORM_BLOCKS: 35373,\n  MAX_COMBINED_UNIFORM_BLOCKS: 35374,\n  MAX_UNIFORM_BUFFER_BINDINGS: 35375,\n  MAX_UNIFORM_BLOCK_SIZE: 35376,\n  MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS: 35377,\n  MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS: 35379,\n  UNIFORM_BUFFER_OFFSET_ALIGNMENT: 35380,\n  ACTIVE_UNIFORM_BLOCKS: 35382,\n  UNIFORM_TYPE: 35383,\n  UNIFORM_SIZE: 35384,\n  UNIFORM_BLOCK_INDEX: 35386,\n  UNIFORM_OFFSET: 35387,\n  UNIFORM_ARRAY_STRIDE: 35388,\n  UNIFORM_MATRIX_STRIDE: 35389,\n  UNIFORM_IS_ROW_MAJOR: 35390,\n  UNIFORM_BLOCK_BINDING: 35391,\n  UNIFORM_BLOCK_DATA_SIZE: 35392,\n  UNIFORM_BLOCK_ACTIVE_UNIFORMS: 35394,\n  UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: 35395,\n  UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER: 35396,\n  UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER: 35398,\n  INVALID_INDEX: 4294967295,\n  MAX_VERTEX_OUTPUT_COMPONENTS: 37154,\n  MAX_FRAGMENT_INPUT_COMPONENTS: 37157,\n  MAX_SERVER_WAIT_TIMEOUT: 37137,\n  OBJECT_TYPE: 37138,\n  SYNC_CONDITION: 37139,\n  SYNC_STATUS: 37140,\n  SYNC_FLAGS: 37141,\n  SYNC_FENCE: 37142,\n  SYNC_GPU_COMMANDS_COMPLETE: 37143,\n  UNSIGNALED: 37144,\n  SIGNALED: 37145,\n  ALREADY_SIGNALED: 37146,\n  TIMEOUT_EXPIRED: 37147,\n  CONDITION_SATISFIED: 37148,\n  WAIT_FAILED: 37149,\n  SYNC_FLUSH_COMMANDS_BIT: 1,\n  VERTEX_ATTRIB_ARRAY_DIVISOR: 35070,\n  ANY_SAMPLES_PASSED: 35887,\n  ANY_SAMPLES_PASSED_CONSERVATIVE: 36202,\n  SAMPLER_BINDING: 35097,\n  TRANSFORM_FEEDBACK: 36386,\n  TRANSFORM_FEEDBACK_PAUSED: 36387,\n  TRANSFORM_FEEDBACK_ACTIVE: 36388,\n  TRANSFORM_FEEDBACK_BINDING: 36389,\n  TEXTURE_IMMUTABLE_FORMAT: 37167,\n  MAX_ELEMENT_INDEX: 36203,\n  TEXTURE_IMMUTABLE_LEVELS: 33503,\n  TIMEOUT_IGNORED: -1,\n  MAX_CLIENT_WAIT_TIMEOUT_WEBGL: 37447,\n}\n\nconst extensions: { [key: string]: any } = {\n  // ratified\n  OES_texture_float: {},\n  OES_texture_half_float: {},\n  WEBGL_lose_context: {\n    loseContext: () => {},\n  },\n  OES_standard_derivatives: {},\n  OES_vertex_array_object: {\n    createVertexArrayOES: () => {},\n    bindVertexArrayOES: () => {},\n    deleteVertexArrayOES: () => {},\n  },\n  WEBGL_debug_renderer_info: null,\n  WEBGL_debug_shaders: null,\n  WEBGL_compressed_texture_s3tc: null,\n  WEBGL_depth_texture: {},\n  OES_element_index_uint: {},\n  EXT_texture_filter_anisotropic: null,\n  EXT_frag_depth: {},\n  WEBGL_draw_buffers: {},\n  ANGLE_instanced_arrays: null,\n  OES_texture_float_linear: null,\n  OES_texture_half_float_linear: null,\n  EXT_blend_minmax: { MIN_EXT: 0, MAX_EXT: 0 },\n  EXT_shader_texture_lod: null,\n  // community\n  WEBGL_compressed_texture_atc: null,\n  WEBGL_compressed_texture_pvrtc: null,\n  EXT_color_buffer_half_float: null,\n  WEBGL_color_buffer_float: null,\n  EXT_sRGB: null,\n  WEBGL_compressed_texture_etc1: null,\n}\n\nexport class WebGL2RenderingContext {\n  [key: string]: any\n\n  constructor(canvas: HTMLCanvasElement) {\n    this.canvas = canvas\n    this.drawingBufferWidth = canvas.width\n    this.drawingBufferHeight = canvas.height\n\n    for (const method of functions) {\n      this[method] ??= () => {}\n    }\n\n    Object.assign(this, enums)\n  }\n\n  getShaderPrecisionFormat = () => {\n    return {\n      rangeMin: 127,\n      rangeMax: 127,\n      precision: 23,\n    }\n  }\n\n  private GL_VERSION = 7938\n  private SCISSOR_BOX = 3088\n  private VIEWPORT = 2978\n\n  getParameter(paramId: number) {\n    switch (paramId) {\n      case this.GL_VERSION:\n        return ['WebGL2']\n      case this.SCISSOR_BOX:\n      case this.VIEWPORT:\n        return [0, 0, 1, 1]\n    }\n  }\n\n  getExtension(ext: string) {\n    return extensions[ext]\n  }\n\n  getProgramInfoLog = () => ''\n\n  getShaderInfoLog = () => ''\n}\n"
  },
  {
    "path": "packages/test-renderer/src/__tests__/RTTR.core.test.tsx",
    "content": "import { useFrame } from '@react-three/fiber'\nimport * as React from 'react'\nimport * as THREE from 'three'\n\nimport ReactThreeTestRenderer from '../index'\n\ntype ExampleComp = THREE.Mesh<THREE.BoxGeometry, THREE.Material>\n\ndescribe('ReactThreeTestRenderer Core', () => {\n  it('renders JSX', async () => {\n    const Mesh = () => {\n      return (\n        <mesh>\n          <boxGeometry args={[2, 2]} />\n          <meshBasicMaterial />\n        </mesh>\n      )\n    }\n\n    const renderer = await ReactThreeTestRenderer.create(<Mesh />)\n    expect(renderer.scene.children[0].type).toEqual('Mesh')\n    await renderer.update(<Mesh />)\n    expect(renderer.scene.children[0].type).toEqual('Mesh')\n  })\n\n  it('renders a simple component with hooks', async () => {\n    const Mesh = () => {\n      const meshRef = React.useRef<THREE.Mesh<THREE.BoxGeometry, THREE.MeshBasicMaterial>>(null)\n      useFrame(() => void (meshRef.current!.position.x += 0.01))\n      return (\n        <mesh>\n          <boxGeometry args={[2, 2]} />\n          <meshBasicMaterial />\n        </mesh>\n      )\n    }\n\n    const renderer = await ReactThreeTestRenderer.create(<Mesh />)\n    expect(renderer.scene.children[0].type).toEqual('Mesh')\n    await renderer.update(<Mesh />)\n    expect(renderer.scene.children[0].type).toEqual('Mesh')\n  })\n\n  it('renders a simple component with useTransition', async () => {\n    const Mesh = () => {\n      const [name, setName] = React.useState<string>()\n\n      React.useLayoutEffect(() => {\n        React.startTransition(() => void setName('mesh'))\n      })\n\n      return (\n        <mesh name={name}>\n          <boxGeometry args={[2, 2]} />\n          <meshBasicMaterial />\n        </mesh>\n      )\n    }\n    const renderer = await ReactThreeTestRenderer.create(\n      <React.Suspense fallback={null}>\n        <Mesh />\n      </React.Suspense>,\n    )\n\n    expect(renderer.scene.children[0].props.name).toEqual('mesh')\n  })\n\n  it('renders an empty scene', async () => {\n    const Empty = () => {\n      return null\n    }\n    const renderer = await ReactThreeTestRenderer.create(<Empty />)\n\n    expect(renderer.scene.type).toEqual('Scene')\n    expect(renderer.scene.children).toEqual([])\n    expect(renderer.toGraph()).toEqual([])\n  })\n\n  it('can render a composite component & correctly build simple graph', async () => {\n    class Parent extends React.Component {\n      render() {\n        return (\n          <group>\n            <color attach=\"background\" args={[0, 0, 0]} />\n            <Child />\n          </group>\n        )\n      }\n    }\n\n    const Child = () => {\n      return (\n        <mesh>\n          <boxGeometry args={[2, 2]} />\n          <meshBasicMaterial />\n        </mesh>\n      )\n    }\n\n    const renderer = await ReactThreeTestRenderer.create(<Parent />)\n\n    expect(renderer.toGraph()).toMatchSnapshot()\n  })\n\n  it('renders some basics with an update', async () => {\n    let renders = 0\n\n    class Component extends React.PureComponent {\n      state = {\n        pos: 3,\n      }\n\n      componentDidMount() {\n        this.setState({\n          pos: 7,\n        })\n      }\n\n      render() {\n        renders++\n        return (\n          <group position-x={this.state.pos}>\n            <Child />\n            <Null />\n          </group>\n        )\n      }\n    }\n\n    const Child = () => {\n      renders++\n      return <color attach=\"background\" args={[0, 0, 0]} />\n    }\n\n    const Null = () => {\n      renders++\n      return null\n    }\n\n    const renderer = await ReactThreeTestRenderer.create(<Component />)\n\n    expect(renderer.scene.children[0].instance.position.x).toEqual(7)\n    expect(renders).toBe(6)\n  })\n\n  it('updates types & names', async () => {\n    const renderer = await ReactThreeTestRenderer.create(\n      <mesh>\n        <meshBasicMaterial name=\"basicMat\">\n          <color attach=\"color\" args={[0, 0, 0]} />\n        </meshBasicMaterial>\n      </mesh>,\n    )\n\n    let childInstance = renderer.scene.children[0].instance as ExampleComp\n\n    expect(childInstance.material.type).toEqual('MeshBasicMaterial')\n    expect(childInstance.material.name).toEqual('basicMat')\n\n    await renderer.update(\n      <mesh>\n        <meshStandardMaterial name=\"standardMat\">\n          <color attach=\"color\" args={[255, 255, 255]} />\n        </meshStandardMaterial>\n      </mesh>,\n    )\n\n    childInstance = renderer.scene.children[0].instance as ExampleComp\n\n    expect(childInstance.material.type).toEqual('MeshStandardMaterial')\n    expect(childInstance.material.name).toEqual('standardMat')\n  })\n\n  it('exposes the instance', async () => {\n    class Instance extends React.PureComponent {\n      state = { standardMat: false }\n\n      handleStandard() {\n        this.setState({ standardMat: true })\n      }\n\n      render() {\n        return (\n          <mesh>\n            <boxGeometry args={[2, 2]} />\n            {this.state.standardMat ? <meshStandardMaterial /> : <meshBasicMaterial />}\n          </mesh>\n        )\n      }\n    }\n\n    const renderer = await ReactThreeTestRenderer.create(<Instance />)\n\n    expect(renderer.toTree()).toMatchSnapshot()\n\n    const instance = renderer.getInstance() as Instance\n\n    await ReactThreeTestRenderer.act(async () => {\n      instance.handleStandard()\n    })\n\n    expect(renderer.toTree()).toMatchSnapshot()\n  })\n\n  it('updates children', async () => {\n    const renderer = await ReactThreeTestRenderer.create(\n      <group>\n        <mesh key=\"a\" position-z={12}>\n          <boxGeometry args={[2, 2]} />\n          <meshBasicMaterial />\n        </mesh>\n        <mesh key=\"b\" position-y={12}>\n          <boxGeometry args={[4, 4]} />\n          <meshBasicMaterial />\n        </mesh>\n        <mesh key=\"c\" position-x={12}>\n          <boxGeometry args={[6, 6]} />\n          <meshBasicMaterial />\n        </mesh>\n      </group>,\n    )\n\n    expect(renderer.toTree()).toMatchSnapshot()\n\n    await renderer.update(\n      <group>\n        <mesh key=\"d\" rotation-x={1}>\n          <boxGeometry args={[6, 6]} />\n          <meshBasicMaterial />\n        </mesh>\n        <mesh key=\"b\" position-y={12}>\n          <boxGeometry args={[4, 4]} />\n          <meshBasicMaterial />\n        </mesh>\n        <mesh key=\"c\" position-x={12}>\n          <boxGeometry args={[2, 2]} />\n          <meshBasicMaterial />\n        </mesh>\n      </group>,\n    )\n\n    expect(renderer.toTree()).toMatchSnapshot()\n  })\n\n  it('does the full lifecycle', async () => {\n    const log: string[] = []\n    class Log extends React.Component<{ name: string }> {\n      render() {\n        log.push('render ' + this.props.name)\n        return (\n          <mesh>\n            <boxGeometry args={[2, 2]} />\n            <meshStandardMaterial />\n          </mesh>\n        )\n      }\n      componentDidMount() {\n        log.push('mount ' + this.props.name)\n      }\n      componentWillUnmount() {\n        log.push('unmount ' + this.props.name)\n      }\n    }\n\n    const renderer = await ReactThreeTestRenderer.create(<Log key=\"foo\" name=\"Foo\" />)\n    await renderer.update(<Log key=\"bar\" name=\"Bar\" />)\n    await renderer.unmount()\n\n    expect(log).toEqual(['render Foo', 'mount Foo', 'render Bar', 'unmount Foo', 'mount Bar', 'unmount Bar'])\n  })\n\n  it('gives a ref to native components', async () => {\n    const log: THREE.Mesh[] = []\n    await ReactThreeTestRenderer.create(<mesh ref={(r) => log.push(r as THREE.Mesh)} />)\n    expect(log.length).toEqual(1)\n\n    expect(log[0].type).toEqual('Mesh')\n  })\n\n  it('toTree() handles nested Fragments', async () => {\n    const Component = () => (\n      <>\n        <>\n          <group />\n        </>\n      </>\n    )\n    const renderer = await ReactThreeTestRenderer.create(<Component />)\n\n    expect(renderer.toTree()).toMatchSnapshot()\n  })\n\n  it('correctly builds a tree', async () => {\n    const Component = () => {\n      return (\n        <group position={[1, 2, 3]}>\n          <Child col={[0, 0, 255]} />\n          <Mesh />\n          <Null />\n        </group>\n      )\n    }\n\n    const vertices = new Float32Array([\n      -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0,\n    ])\n\n    const Mesh = () => {\n      return (\n        <mesh>\n          <bufferGeometry attach=\"geometry\">\n            <bufferAttribute attach=\"attributes-position\" args={[vertices, 3]} />\n          </bufferGeometry>\n          <meshBasicMaterial attach=\"material\" color=\"hotpink\" />\n        </mesh>\n      )\n    }\n\n    const Child = ({ col }: { col: [number, number, number] }) => {\n      return <color attach=\"background\" args={col} />\n    }\n\n    const Null = () => {\n      return null\n    }\n\n    const renderer = await ReactThreeTestRenderer.create(<Component />)\n\n    expect(renderer.toTree()).toMatchSnapshot()\n  })\n\n  it('toTree() handles complicated tree of fragments', async () => {\n    const renderer = await ReactThreeTestRenderer.create(\n      <>\n        <>\n          <group>\n            <color attach=\"background\" args={[0, 0, 0]} />\n          </group>\n          <>\n            <group>\n              <color attach=\"background\" args={[0, 0, 255]} />\n            </group>\n          </>\n        </>\n        <group>\n          <color attach=\"background\" args={[255, 0, 0]} />\n        </group>\n      </>,\n    )\n\n    expect(renderer.toTree()).toMatchSnapshot()\n  })\n\n  it('correctly searches through multiple levels in regular objects', async () => {\n    // Create a deep tree: group -> mesh -> mesh -> mesh\n    const renderer = await ReactThreeTestRenderer.create(\n      <group name=\"root-group\">\n        <mesh name=\"level1-mesh\">\n          <boxGeometry />\n          <meshBasicMaterial color=\"red\" />\n          <mesh name=\"level2-mesh\">\n            <boxGeometry />\n            <meshBasicMaterial color=\"green\" />\n            <mesh name=\"level3-mesh\">\n              <boxGeometry />\n              <meshBasicMaterial color=\"blue\" />\n            </mesh>\n          </mesh>\n        </mesh>\n      </group>,\n    )\n\n    // Test from the root\n    const allMeshes = renderer.scene.findAllByType('Mesh')\n    expect(allMeshes.length).toBe(3) // Should find all three meshes\n\n    // Test from an intermediate node\n    const topMesh = renderer.scene.find((node) => node.props.name === 'level1-mesh')\n    const nestedMeshes = topMesh.findAllByType('Mesh')\n    expect(nestedMeshes.length).toBe(2) // Should find the two nested meshes\n\n    // Find a deeply nested mesh from an intermediate node by property\n    const level3 = topMesh.find((node) => node.props.name === 'level3-mesh')\n    expect(level3).toBeDefined()\n    expect(level3.type).toBe('Mesh')\n  })\n\n  it('Can search from retrieved primitive Instance', async () => {\n    const group = new THREE.Group()\n    group.name = 'PrimitiveGroup'\n\n    const childMesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 'red' }))\n    childMesh.name = 'PrimitiveChildMesh'\n    group.add(childMesh)\n\n    const nestedMesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 'red' }))\n    nestedMesh.name = 'PrimitiveNestedChildMesh'\n    childMesh.add(nestedMesh)\n\n    const renderer = await ReactThreeTestRenderer.create(<primitive object={group} />)\n\n    const foundGroup = renderer.scene.findByType('Group')\n    const foundMesh = foundGroup.children[0]\n    const foundNestedMesh = foundMesh.findByType('Mesh')\n    expect(foundNestedMesh).toBeDefined()\n  })\n\n  it('root instance and refs return the same value', async () => {\n    let refInst = null\n    const renderer = await ReactThreeTestRenderer.create(<mesh ref={(ref) => (refInst = ref)} />)\n    const root = renderer.getInstance() // this will be Mesh\n    expect(root).toEqual(refInst)\n  })\n\n  it('handles primitive objects and their children correctly in toGraph', async () => {\n    // Create a component with both regular objects and primitives with children\n    const PrimitiveTestComponent = () => {\n      // Create a THREE.js group with mesh children\n      const group = new THREE.Group()\n      group.name = 'PrimitiveGroup'\n\n      const childMesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 'red' }))\n      childMesh.name = 'PrimitiveChildMesh'\n      group.add(childMesh)\n\n      // Add a nested group to test deeper hierarchies\n      const nestedGroup = new THREE.Group()\n      nestedGroup.name = 'NestedGroup'\n      const nestedMesh = new THREE.Mesh(new THREE.SphereGeometry(0.5), new THREE.MeshBasicMaterial({ color: 'blue' }))\n      nestedMesh.name = 'NestedMesh'\n      nestedGroup.add(nestedMesh)\n      group.add(nestedGroup)\n\n      return (\n        <>\n          <mesh name=\"RegularMesh\">\n            <boxGeometry args={[2, 2]} />\n            <meshBasicMaterial />\n          </mesh>\n\n          <primitive object={group} />\n        </>\n      )\n    }\n\n    const renderer = await ReactThreeTestRenderer.create(<PrimitiveTestComponent />)\n\n    expect(renderer.toGraph()).toMatchSnapshot()\n  })\n})\n"
  },
  {
    "path": "packages/test-renderer/src/__tests__/RTTR.events.test.tsx",
    "content": "import * as React from 'react'\n\nimport ReactThreeTestRenderer from '../index'\nimport type { ReactThreeTest } from '../index'\n\ndescribe('ReactThreeTestRenderer Events', () => {\n  it('should fire an event', async () => {\n    const handlePointerDown = jest.fn().mockImplementationOnce((event: ReactThreeTest.MockSyntheticEvent) => {\n      expect(() => event.stopPropagation()).not.toThrow()\n      expect(event.offsetX).toEqual(640)\n      expect(event.offsetY).toEqual(400)\n    })\n\n    const Component = () => {\n      return (\n        <mesh onPointerDown={handlePointerDown}>\n          <boxGeometry args={[2, 2]} />\n          <meshBasicMaterial />\n        </mesh>\n      )\n    }\n\n    const { scene, fireEvent } = await ReactThreeTestRenderer.create(<Component />)\n\n    const eventData = {\n      offsetX: 640,\n      offsetY: 400,\n    }\n\n    await fireEvent(scene.children[0], 'onPointerDown', eventData)\n\n    expect(handlePointerDown).toHaveBeenCalledTimes(1)\n\n    await fireEvent(scene.children[0], 'pointerDown')\n\n    expect(handlePointerDown).toHaveBeenCalledTimes(2)\n  })\n\n  it('should not throw if the handle name is incorrect', async () => {\n    const handlePointerDown = jest.fn()\n\n    const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementationOnce(jest.fn())\n\n    const Component = () => {\n      return (\n        <mesh onPointerDown={handlePointerDown}>\n          <boxGeometry args={[2, 2]} />\n          <meshBasicMaterial />\n        </mesh>\n      )\n    }\n\n    const { scene, fireEvent } = await ReactThreeTestRenderer.create(<Component />)\n\n    expect(async () => await fireEvent(scene.children[0], 'onPointerUp')).not.toThrow()\n\n    expect(handlePointerDown).not.toHaveBeenCalled()\n\n    expect(consoleWarnSpy).toHaveBeenCalledTimes(1)\n    expect(consoleWarnSpy).toHaveBeenCalledWith(\n      'Handler for onPointerUp was not found. You must pass event names in camelCase or name of the handler https://github.com/pmndrs/react-three-fiber/blob/master/packages/test-renderer/markdown/rttr.md#create-fireevent',\n    )\n  })\n})\n"
  },
  {
    "path": "packages/test-renderer/src/__tests__/RTTR.hooks.test.tsx",
    "content": "import * as React from 'react'\nimport * as THREE from 'three'\nimport { useFrame, useLoader, useThree } from '@react-three/fiber'\n\nimport ReactThreeTestRenderer from '../index'\n\ndescribe('ReactThreeTestRenderer Hooks', () => {\n  it('can handle useThree hook', async () => {\n    let result = {} as {\n      camera: THREE.Camera\n      scene: THREE.Scene\n      raycaster: THREE.Raycaster\n      size: { width: number; height: number }\n    }\n\n    const Component = () => {\n      const res = useThree((state) => ({\n        camera: state.camera,\n        scene: state.scene,\n        size: state.size,\n        raycaster: state.raycaster,\n      }))\n\n      result = res\n\n      return <group />\n    }\n\n    await ReactThreeTestRenderer.create(<Component />, { width: 1280, height: 800 })\n\n    expect(result.camera instanceof THREE.Camera).toBeTruthy()\n    expect(result.scene instanceof THREE.Scene).toBeTruthy()\n    expect(result.raycaster instanceof THREE.Raycaster).toBeTruthy()\n    expect(result.size).toEqual({ height: 800, width: 1280, top: 0, left: 0 })\n  })\n\n  it('can handle useLoader hook', async () => {\n    const MockMesh = new THREE.Mesh()\n    class Loader extends THREE.Loader<THREE.Mesh, string> {\n      load(url: string, onLoad: (mesh: THREE.Mesh) => void): void {\n        onLoad(MockMesh)\n      }\n    }\n\n    const Component = () => {\n      const model = useLoader(Loader, '/suzanne.glb')\n\n      return <primitive object={model} />\n    }\n\n    const renderer = await ReactThreeTestRenderer.create(<Component />)\n    expect(renderer.scene.children[0].instance).toBe(MockMesh)\n  })\n\n  it('can handle useFrame hook using test renderers advanceFrames function', async () => {\n    const Component = () => {\n      const meshRef = React.useRef<THREE.Mesh>(null!)\n      useFrame((_, delta) => {\n        meshRef.current.rotation.x += delta\n      })\n\n      return (\n        <mesh ref={meshRef}>\n          <boxGeometry args={[2, 2]} />\n          <meshBasicMaterial />\n        </mesh>\n      )\n    }\n\n    const renderer = await ReactThreeTestRenderer.create(<Component />)\n\n    expect(renderer.scene.children[0].instance.rotation.x).toEqual(0)\n\n    await ReactThreeTestRenderer.act(async () => {\n      await renderer.advanceFrames(2, 1)\n    })\n\n    expect(renderer.scene.children[0].instance.rotation.x).toEqual(2)\n  })\n})\n"
  },
  {
    "path": "packages/test-renderer/src/__tests__/RTTR.methods.test.tsx",
    "content": "import * as React from 'react'\n\nimport ReactThreeTestRenderer from '../index'\n\ndescribe('ReactThreeTestRenderer instance methods', () => {\n  const ExampleComponent = () => {\n    return (\n      <group>\n        <mesh name=\"mesh_01\">\n          <boxGeometry args={[2, 2]} />\n          <meshStandardMaterial color={0x0000ff} />\n        </mesh>\n        <mesh name=\"mesh_02\">\n          <boxGeometry args={[2, 2]} />\n          <meshBasicMaterial color={0x0000ff} />\n        </mesh>\n      </group>\n    )\n  }\n\n  it('should pass the parent', async () => {\n    const { scene } = await ReactThreeTestRenderer.create(<ExampleComponent />)\n\n    expect(scene.parent).toBeNull()\n\n    expect(scene.children[0].parent).toBeDefined()\n    expect(scene.children[0].parent!.type).toEqual('Scene')\n  })\n\n  it('searches via .find() / .findAll()', async () => {\n    const { scene } = await ReactThreeTestRenderer.create(<ExampleComponent />)\n\n    const foundByName = scene.find((node) => node.instance.name === 'mesh_01')\n\n    expect(foundByName.type).toEqual('Mesh')\n\n    const foundAllByColor = scene.findAll((node) => node.props.color === 0x0000ff)\n\n    expect(foundAllByColor).toHaveLength(2)\n    expect(foundAllByColor[0].type).toEqual('MeshStandardMaterial')\n    expect(foundAllByColor[1].type).toEqual('MeshBasicMaterial')\n\n    const foundAllByType = scene.findAll((node) => node.type === 'InstancedMesh')\n\n    expect(foundAllByType).toHaveLength(0)\n    expect(foundAllByType).toEqual([])\n\n    expect(() => scene.find((node) => node.props.color === 0x0000ff)).toThrow()\n  })\n\n  it('searches via .findByType() / findAllByType()', async () => {\n    const { scene } = await ReactThreeTestRenderer.create(<ExampleComponent />)\n\n    const foundByStandardMaterial = scene.findByType('MeshStandardMaterial')\n\n    expect(foundByStandardMaterial).toBeDefined()\n\n    const foundAllByMesh = scene.findAllByType('Mesh')\n\n    expect(foundAllByMesh).toHaveLength(2)\n    expect(foundAllByMesh[0].instance.name).toEqual('mesh_01')\n    expect(foundAllByMesh[1].instance.name).toEqual('mesh_02')\n\n    const foundAllByBoxBufferGeometry = scene.findAllByType('BoxBufferGeometry')\n\n    expect(foundAllByBoxBufferGeometry).toHaveLength(0)\n    expect(foundAllByBoxBufferGeometry).toEqual([])\n\n    expect(() => scene.findByType('BufferGeometry')).toThrow()\n  })\n\n  it('searches via .findByProps() / .findAllByProps()', async () => {\n    const { scene } = await ReactThreeTestRenderer.create(<ExampleComponent />)\n\n    const foundByName = scene.findByProps({\n      name: 'mesh_01',\n    })\n\n    expect(foundByName.type).toEqual('Mesh')\n\n    const foundAllByColor = scene.findAllByProps({\n      color: 0x0000ff,\n    })\n\n    expect(foundAllByColor).toHaveLength(2)\n    expect(foundAllByColor[0].type).toEqual('MeshStandardMaterial')\n    expect(foundAllByColor[1].type).toEqual('MeshBasicMaterial')\n\n    const foundAllByColorAndName = scene.findAllByProps({\n      color: 0x0000ff,\n      name: 'mesh_01',\n    })\n\n    expect(foundAllByColorAndName).toHaveLength(0)\n    expect(foundAllByColorAndName).toEqual([])\n\n    expect(() => scene.findByProps({ color: 0x0000ff })).toThrow()\n  })\n\n  it('searches RegExp via .findByProps() / .findAllByProps()', async () => {\n    const { scene } = await ReactThreeTestRenderer.create(<ExampleComponent />)\n\n    const single = scene.findByProps({\n      name: /^mesh_01$/,\n    })\n\n    expect(single.type).toEqual('Mesh')\n\n    const multiple = scene.findAllByProps({\n      name: /^mesh_\\d+$/,\n    })\n\n    expect(multiple.length).toEqual(2)\n  })\n})\n"
  },
  {
    "path": "packages/test-renderer/src/__tests__/__snapshots__/RTTR.core.test.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`ReactThreeTestRenderer Core can render a composite component & correctly build simple graph 1`] = `\nArray [\n  Object {\n    \"children\": Array [\n      Object {\n        \"children\": Array [],\n        \"name\": \"\",\n        \"type\": undefined,\n      },\n      Object {\n        \"children\": Array [\n          Object {\n            \"children\": Array [],\n            \"name\": \"\",\n            \"type\": \"BoxGeometry\",\n          },\n          Object {\n            \"children\": Array [],\n            \"name\": \"\",\n            \"type\": \"MeshBasicMaterial\",\n          },\n        ],\n        \"name\": \"\",\n        \"type\": \"Mesh\",\n      },\n    ],\n    \"name\": \"\",\n    \"type\": \"Group\",\n  },\n]\n`;\n\nexports[`ReactThreeTestRenderer Core correctly builds a tree 1`] = `\nArray [\n  Object {\n    \"children\": Array [\n      Object {\n        \"children\": Array [],\n        \"props\": Object {\n          \"args\": Array [\n            0,\n            0,\n            255,\n          ],\n          \"attach\": \"background\",\n        },\n        \"type\": \"color\",\n      },\n      Object {\n        \"children\": Array [\n          Object {\n            \"children\": Array [\n              Object {\n                \"children\": Array [],\n                \"props\": Object {\n                  \"args\": Array [\n                    Float32Array [\n                      -1,\n                      -1,\n                      1,\n                      1,\n                      -1,\n                      1,\n                      1,\n                      1,\n                      1,\n                      1,\n                      1,\n                      1,\n                      -1,\n                      1,\n                      1,\n                      -1,\n                      -1,\n                      1,\n                    ],\n                    3,\n                  ],\n                  \"attach\": \"attributes-position\",\n                },\n                \"type\": \"bufferAttribute\",\n              },\n            ],\n            \"props\": Object {\n              \"attach\": \"geometry\",\n            },\n            \"type\": \"bufferGeometry\",\n          },\n          Object {\n            \"children\": Array [],\n            \"props\": Object {\n              \"attach\": \"material\",\n              \"color\": \"hotpink\",\n            },\n            \"type\": \"meshBasicMaterial\",\n          },\n        ],\n        \"props\": Object {},\n        \"type\": \"mesh\",\n      },\n    ],\n    \"props\": Object {\n      \"position\": Array [\n        1,\n        2,\n        3,\n      ],\n    },\n    \"type\": \"group\",\n  },\n]\n`;\n\nexports[`ReactThreeTestRenderer Core exposes the instance 1`] = `\nArray [\n  Object {\n    \"children\": Array [\n      Object {\n        \"children\": Array [],\n        \"props\": Object {\n          \"args\": Array [\n            2,\n            2,\n          ],\n          \"attach\": \"geometry\",\n        },\n        \"type\": \"boxGeometry\",\n      },\n      Object {\n        \"children\": Array [],\n        \"props\": Object {\n          \"attach\": \"material\",\n        },\n        \"type\": \"meshBasicMaterial\",\n      },\n    ],\n    \"props\": Object {},\n    \"type\": \"mesh\",\n  },\n]\n`;\n\nexports[`ReactThreeTestRenderer Core exposes the instance 2`] = `\nArray [\n  Object {\n    \"children\": Array [\n      Object {\n        \"children\": Array [],\n        \"props\": Object {\n          \"args\": Array [\n            2,\n            2,\n          ],\n          \"attach\": \"geometry\",\n        },\n        \"type\": \"boxGeometry\",\n      },\n      Object {\n        \"children\": Array [],\n        \"props\": Object {\n          \"attach\": \"material\",\n        },\n        \"type\": \"meshStandardMaterial\",\n      },\n    ],\n    \"props\": Object {},\n    \"type\": \"mesh\",\n  },\n]\n`;\n\nexports[`ReactThreeTestRenderer Core handles primitive objects and their children correctly in toGraph 1`] = `\nArray [\n  Object {\n    \"children\": Array [\n      Object {\n        \"children\": Array [],\n        \"name\": \"\",\n        \"type\": \"BoxGeometry\",\n      },\n      Object {\n        \"children\": Array [],\n        \"name\": \"\",\n        \"type\": \"MeshBasicMaterial\",\n      },\n    ],\n    \"name\": \"RegularMesh\",\n    \"type\": \"Mesh\",\n  },\n  Object {\n    \"children\": Array [\n      Object {\n        \"children\": Array [],\n        \"name\": \"PrimitiveChildMesh\",\n        \"type\": \"Mesh\",\n      },\n      Object {\n        \"children\": Array [\n          Object {\n            \"children\": Array [],\n            \"name\": \"NestedMesh\",\n            \"type\": \"Mesh\",\n          },\n        ],\n        \"name\": \"NestedGroup\",\n        \"type\": \"Group\",\n      },\n    ],\n    \"name\": \"PrimitiveGroup\",\n    \"type\": \"Group\",\n  },\n]\n`;\n\nexports[`ReactThreeTestRenderer Core toTree() handles complicated tree of fragments 1`] = `\nArray [\n  Object {\n    \"children\": Array [\n      Object {\n        \"children\": Array [],\n        \"props\": Object {\n          \"args\": Array [\n            0,\n            0,\n            0,\n          ],\n          \"attach\": \"background\",\n        },\n        \"type\": \"color\",\n      },\n    ],\n    \"props\": Object {},\n    \"type\": \"group\",\n  },\n  Object {\n    \"children\": Array [\n      Object {\n        \"children\": Array [],\n        \"props\": Object {\n          \"args\": Array [\n            0,\n            0,\n            255,\n          ],\n          \"attach\": \"background\",\n        },\n        \"type\": \"color\",\n      },\n    ],\n    \"props\": Object {},\n    \"type\": \"group\",\n  },\n  Object {\n    \"children\": Array [\n      Object {\n        \"children\": Array [],\n        \"props\": Object {\n          \"args\": Array [\n            255,\n            0,\n            0,\n          ],\n          \"attach\": \"background\",\n        },\n        \"type\": \"color\",\n      },\n    ],\n    \"props\": Object {},\n    \"type\": \"group\",\n  },\n]\n`;\n\nexports[`ReactThreeTestRenderer Core toTree() handles nested Fragments 1`] = `\nArray [\n  Object {\n    \"children\": Array [],\n    \"props\": Object {},\n    \"type\": \"group\",\n  },\n]\n`;\n\nexports[`ReactThreeTestRenderer Core updates children 1`] = `\nArray [\n  Object {\n    \"children\": Array [\n      Object {\n        \"children\": Array [\n          Object {\n            \"children\": Array [],\n            \"props\": Object {\n              \"args\": Array [\n                2,\n                2,\n              ],\n              \"attach\": \"geometry\",\n            },\n            \"type\": \"boxGeometry\",\n          },\n          Object {\n            \"children\": Array [],\n            \"props\": Object {\n              \"attach\": \"material\",\n            },\n            \"type\": \"meshBasicMaterial\",\n          },\n        ],\n        \"props\": Object {\n          \"position-z\": 12,\n        },\n        \"type\": \"mesh\",\n      },\n      Object {\n        \"children\": Array [\n          Object {\n            \"children\": Array [],\n            \"props\": Object {\n              \"args\": Array [\n                4,\n                4,\n              ],\n              \"attach\": \"geometry\",\n            },\n            \"type\": \"boxGeometry\",\n          },\n          Object {\n            \"children\": Array [],\n            \"props\": Object {\n              \"attach\": \"material\",\n            },\n            \"type\": \"meshBasicMaterial\",\n          },\n        ],\n        \"props\": Object {\n          \"position-y\": 12,\n        },\n        \"type\": \"mesh\",\n      },\n      Object {\n        \"children\": Array [\n          Object {\n            \"children\": Array [],\n            \"props\": Object {\n              \"args\": Array [\n                6,\n                6,\n              ],\n              \"attach\": \"geometry\",\n            },\n            \"type\": \"boxGeometry\",\n          },\n          Object {\n            \"children\": Array [],\n            \"props\": Object {\n              \"attach\": \"material\",\n            },\n            \"type\": \"meshBasicMaterial\",\n          },\n        ],\n        \"props\": Object {\n          \"position-x\": 12,\n        },\n        \"type\": \"mesh\",\n      },\n    ],\n    \"props\": Object {},\n    \"type\": \"group\",\n  },\n]\n`;\n\nexports[`ReactThreeTestRenderer Core updates children 2`] = `\nArray [\n  Object {\n    \"children\": Array [\n      Object {\n        \"children\": Array [\n          Object {\n            \"children\": Array [],\n            \"props\": Object {\n              \"args\": Array [\n                6,\n                6,\n              ],\n              \"attach\": \"geometry\",\n            },\n            \"type\": \"boxGeometry\",\n          },\n          Object {\n            \"children\": Array [],\n            \"props\": Object {\n              \"attach\": \"material\",\n            },\n            \"type\": \"meshBasicMaterial\",\n          },\n        ],\n        \"props\": Object {\n          \"rotation-x\": 1,\n        },\n        \"type\": \"mesh\",\n      },\n      Object {\n        \"children\": Array [\n          Object {\n            \"children\": Array [],\n            \"props\": Object {\n              \"args\": Array [\n                4,\n                4,\n              ],\n              \"attach\": \"geometry\",\n            },\n            \"type\": \"boxGeometry\",\n          },\n          Object {\n            \"children\": Array [],\n            \"props\": Object {\n              \"attach\": \"material\",\n            },\n            \"type\": \"meshBasicMaterial\",\n          },\n        ],\n        \"props\": Object {\n          \"position-y\": 12,\n        },\n        \"type\": \"mesh\",\n      },\n      Object {\n        \"children\": Array [\n          Object {\n            \"children\": Array [],\n            \"props\": Object {\n              \"args\": Array [\n                2,\n                2,\n              ],\n              \"attach\": \"geometry\",\n            },\n            \"type\": \"boxGeometry\",\n          },\n          Object {\n            \"children\": Array [],\n            \"props\": Object {\n              \"attach\": \"material\",\n            },\n            \"type\": \"meshBasicMaterial\",\n          },\n        ],\n        \"props\": Object {\n          \"position-x\": 12,\n        },\n        \"type\": \"mesh\",\n      },\n    ],\n    \"props\": Object {},\n    \"type\": \"group\",\n  },\n]\n`;\n"
  },
  {
    "path": "packages/test-renderer/src/createTestCanvas.ts",
    "content": "import { WebGL2RenderingContext } from './WebGL2RenderingContext'\nimport type { CreateCanvasParameters } from './types/internal'\n\nexport const createCanvas = ({ beforeReturn, width = 1280, height = 800 }: CreateCanvasParameters = {}) => {\n  let canvas: HTMLCanvasElement\n\n  if (typeof document !== 'undefined' && typeof document.createElement === 'function') {\n    canvas = document.createElement('canvas')\n  } else {\n    canvas = {\n      style: {},\n      addEventListener: (() => {}) as any,\n      removeEventListener: (() => {}) as any,\n      clientWidth: width,\n      clientHeight: height,\n      getContext: (() => new WebGL2RenderingContext(canvas)) as any,\n    } as HTMLCanvasElement\n  }\n  canvas.width = width\n  canvas.height = height\n\n  if (globalThis.HTMLCanvasElement) {\n    const getContext = HTMLCanvasElement.prototype.getContext\n    HTMLCanvasElement.prototype.getContext = function (id: string) {\n      if (id.startsWith('webgl')) return new WebGL2RenderingContext(this)\n      return getContext.apply(this, arguments as any)\n    } as any\n  }\n\n  beforeReturn?.(canvas)\n\n  class WebGLRenderingContext extends WebGL2RenderingContext {}\n  // @ts-expect-error\n  globalThis.WebGLRenderingContext ??= WebGLRenderingContext\n  // @ts-expect-error\n  globalThis.WebGL2RenderingContext ??= WebGL2RenderingContext\n\n  return canvas\n}\n"
  },
  {
    "path": "packages/test-renderer/src/createTestInstance.ts",
    "content": "import type * as THREE from 'three'\nimport type { Instance } from '@react-three/fiber'\n\nimport type { Obj, TestInstanceChildOpts } from './types/internal'\n\nimport { expectOne, matchProps, findAll } from './helpers/testInstance'\n\n// Helper to create a minimal wrapper for THREE.Object3D children of primitives\nconst createVirtualInstance = (object: THREE.Object3D, parent: Instance<any>): Instance<THREE.Object3D> => {\n  // Create the virtual instance for this object\n  // we can't import the prepare method from packages/fiber/src/core/utils.tsx so we do what we can\n  const instance: Instance<THREE.Object3D> = {\n    root: parent.root,\n    type: object.type.toLowerCase(), // Convert to lowercase to match R3F convention\n    parent,\n    children: [],\n    props: { object },\n    object,\n    eventCount: 0,\n    handlers: {},\n    isHidden: false,\n  }\n\n  // Recursively process children if they exist\n  if (object.children && object.children.length > 0) {\n    const objectChildren = object.children as THREE.Object3D[]\n    instance.children = Array.from(objectChildren).map((child) => createVirtualInstance(child, instance))\n  }\n\n  return instance\n}\n\nexport class ReactThreeTestInstance<TObject extends THREE.Object3D = THREE.Object3D> {\n  _fiber: Instance<TObject>\n\n  constructor(fiber: Instance<TObject>) {\n    this._fiber = fiber\n  }\n\n  public get fiber(): Instance<TObject> {\n    return this._fiber\n  }\n\n  public get instance(): TObject {\n    return this._fiber.object\n  }\n\n  public get type(): string {\n    return this._fiber.object.type\n  }\n\n  public get props(): Obj {\n    return this._fiber.props\n  }\n\n  public get parent(): ReactThreeTestInstance | null {\n    const parent = this._fiber.parent\n    if (parent !== null) {\n      return wrapFiber(parent)\n    }\n    return parent\n  }\n\n  public get children(): ReactThreeTestInstance[] {\n    return this.getChildren(this._fiber)\n  }\n\n  public get allChildren(): ReactThreeTestInstance[] {\n    return this.getChildren(this._fiber, { exhaustive: true })\n  }\n\n  private getChildren = (\n    fiber: Instance,\n    opts: TestInstanceChildOpts = { exhaustive: false },\n  ): ReactThreeTestInstance[] => {\n    // Get standard R3F children\n    const r3fChildren = fiber.children\n      .filter((child) => !child.props.attach || opts.exhaustive)\n      .map((fib) => wrapFiber(fib as Instance))\n\n    // For primitives, also add THREE.js object children\n    if (fiber.type === 'primitive' && fiber.object.children?.length) {\n      const threeChildren = fiber.object.children.map((child: THREE.Object3D) => {\n        // Create a virtual instance that wraps the THREE.js child\n        const virtualInstance = createVirtualInstance(child, fiber)\n        return wrapFiber(virtualInstance)\n      })\n\n      r3fChildren.push(...threeChildren)\n\n      return r3fChildren\n    }\n\n    return r3fChildren\n  }\n\n  public findAll = (decider: (node: ReactThreeTestInstance) => boolean): ReactThreeTestInstance[] =>\n    findAll(this as unknown as ReactThreeTestInstance, decider, { includeRoot: false })\n\n  public find = (decider: (node: ReactThreeTestInstance) => boolean): ReactThreeTestInstance =>\n    expectOne(this.findAll(decider), `matching custom checker: ${decider.toString()}`)\n\n  public findByType = (type: string): ReactThreeTestInstance =>\n    expectOne(\n      this.findAll((node) => Boolean(node.type && node.type === type)),\n      `with node type: \"${type || 'Unknown'}\"`,\n    )\n\n  public findAllByType = (type: string): ReactThreeTestInstance[] =>\n    this.findAll((node) => Boolean(node.type && node.type === type))\n\n  public findByProps = (props: Obj): ReactThreeTestInstance =>\n    expectOne(this.findAllByProps(props), `with props: ${JSON.stringify(props)}`)\n\n  public findAllByProps = (props: Obj): ReactThreeTestInstance[] =>\n    this.findAll((node: ReactThreeTestInstance) => Boolean(node.props && matchProps(node.props, props)))\n}\n\nconst fiberToWrapper = new WeakMap<Instance>()\nexport const wrapFiber = (fiber: Instance): ReactThreeTestInstance => {\n  let wrapper = fiberToWrapper.get(fiber)\n  if (wrapper === undefined) {\n    wrapper = new ReactThreeTestInstance(fiber)\n    fiberToWrapper.set(fiber, wrapper)\n  }\n  return wrapper\n}\n"
  },
  {
    "path": "packages/test-renderer/src/fireEvent.ts",
    "content": "import type { RootStore } from '@react-three/fiber'\n\nimport { toEventHandlerName } from './helpers/strings'\n\nimport { ReactThreeTestInstance } from './createTestInstance'\n\nimport type { Act, MockSyntheticEvent } from './types/public'\nimport type { MockEventData } from './types/internal'\n\nexport const createEventFirer = (act: Act, store: RootStore) => {\n  const findEventHandler = (\n    element: ReactThreeTestInstance,\n    eventName: string,\n  ): ((event: MockSyntheticEvent) => any) | null => {\n    const eventHandlerName = toEventHandlerName(eventName)\n\n    const props = element.props\n\n    if (typeof props[eventHandlerName] === 'function') {\n      return props[eventHandlerName]\n    }\n\n    if (typeof props[eventName] === 'function') {\n      return props[eventName]\n    }\n\n    console.warn(\n      `Handler for ${eventName} was not found. You must pass event names in camelCase or name of the handler https://github.com/pmndrs/react-three-fiber/blob/master/packages/test-renderer/markdown/rttr.md#create-fireevent`,\n    )\n\n    return null\n  }\n\n  const createSyntheticEvent = (element: ReactThreeTestInstance, data: MockEventData): MockSyntheticEvent => {\n    const raycastEvent = {\n      camera: store.getState().camera,\n      stopPropagation: () => {},\n      target: element,\n      currentTarget: element,\n      sourceEvent: data,\n      ...data,\n    }\n    return raycastEvent\n  }\n\n  const invokeEvent = async (element: ReactThreeTestInstance, eventName: string, data: MockEventData): Promise<any> => {\n    const handler = findEventHandler(element, eventName)\n\n    if (!handler) {\n      return\n    }\n\n    let returnValue: any\n\n    await act(async () => {\n      returnValue = handler(createSyntheticEvent(element, data))\n    })\n\n    return returnValue\n  }\n\n  const fireEvent = async (\n    element: ReactThreeTestInstance,\n    eventName: string,\n    data: MockEventData = {},\n  ): Promise<any> => await invokeEvent(element, eventName, data)\n\n  return fireEvent\n}\n"
  },
  {
    "path": "packages/test-renderer/src/helpers/events.ts",
    "content": "import type { MockEventData } from '../types/internal'\n\nexport const calculateDistance = (event: MockEventData) => {\n  if (event.offsetX && event.offsetY && event.initialClick.x && event.initialClick.y) {\n    const dx = event.offsetX - event.initialClick.x\n    const dy = event.offsetY - event.initialClick.y\n    return Math.round(Math.sqrt(dx * dx + dy * dy))\n  }\n  return 0\n}\n"
  },
  {
    "path": "packages/test-renderer/src/helpers/graph.ts",
    "content": "import type { Instance } from '@react-three/fiber'\nimport type { SceneGraphItem } from '../types/public'\nimport type * as THREE from 'three'\n\nconst graphObjectFactory = (\n  type: SceneGraphItem['type'],\n  name: SceneGraphItem['name'],\n  children: SceneGraphItem['children'],\n): SceneGraphItem => ({\n  type,\n  name,\n  children,\n})\n\n// Helper function to process raw THREE.js children objects\nfunction processThreeChildren(children: THREE.Object3D[]): SceneGraphItem[] {\n  return children.map((object) =>\n    graphObjectFactory(\n      object.type,\n      object.name || '',\n      object.children && object.children.length > 0 ? processThreeChildren(object.children) : [],\n    ),\n  )\n}\n\nexport const toGraph = (object: Instance): SceneGraphItem[] => {\n  return object.children.map((child) => {\n    // Process standard R3F children\n    const children = toGraph(child)\n\n    // For primitives, also include THREE.js object children\n    if (child.type === 'primitive' && child.object.children?.length) {\n      const threeChildren = processThreeChildren(child.object.children)\n      children.push(...threeChildren)\n    }\n\n    return graphObjectFactory(child.object.type, child.object.name ?? '', children)\n  })\n}\n"
  },
  {
    "path": "packages/test-renderer/src/helpers/strings.ts",
    "content": "export const lowerCaseFirstLetter = (str: string) => `${str.charAt(0).toLowerCase()}${str.slice(1)}`\n\nexport const toEventHandlerName = (eventName: string) => `on${eventName.charAt(0).toUpperCase()}${eventName.slice(1)}`\n"
  },
  {
    "path": "packages/test-renderer/src/helpers/testInstance.ts",
    "content": "import type { ReactThreeTestInstance } from '../createTestInstance'\nimport type { Obj } from '../types/internal'\n\nexport const expectOne = <TItem>(items: TItem[], msg: string) => {\n  if (items.length === 1) {\n    return items[0]\n  }\n\n  const prefix =\n    items.length === 0 ? 'RTTR: No instances found' : `RTTR: Expected 1 but found ${items.length} instances`\n\n  throw new Error(`${prefix} ${msg}`)\n}\n\nexport const matchProps = (props: Obj, filter: Obj) => {\n  for (const key in filter) {\n    // Check for matches if filter contains regex matchers\n    const isRegex = filter[key] instanceof RegExp\n    const shouldMatch = isRegex && typeof props[key] === 'string'\n    const match = shouldMatch && filter[key].test(props[key])\n\n    // Bail if props aren't identical and filters found no match\n    if (props[key] !== filter[key] && !match) {\n      return false\n    }\n  }\n\n  return true\n}\n\ninterface FindAllOptions {\n  /**\n   * Whether to include the root node in search results.\n   * When false, only searches within children.\n   * @default true\n   */\n  includeRoot?: boolean\n}\n\nexport const findAll = (\n  root: ReactThreeTestInstance,\n  decider: (node: ReactThreeTestInstance) => boolean,\n  options: FindAllOptions = { includeRoot: true },\n) => {\n  const results = []\n\n  // Only include the root node if the option is enabled\n  if (options.includeRoot !== false && decider(root)) {\n    results.push(root)\n  }\n\n  // Always search through children\n  root.allChildren.forEach((child) => {\n    // When recursively searching children, we always want to include their roots\n    results.push(...findAll(child, decider, { includeRoot: true }))\n  })\n\n  return results\n}\n"
  },
  {
    "path": "packages/test-renderer/src/helpers/tree.ts",
    "content": "import * as THREE from 'three'\nimport type { Instance } from '@react-three/fiber'\nimport type { TreeNode, Tree } from '../types/public'\nimport { lowerCaseFirstLetter } from './strings'\n\nconst treeObjectFactory = (\n  type: TreeNode['type'],\n  props: TreeNode['props'],\n  children: TreeNode['children'],\n): TreeNode => ({\n  type,\n  props,\n  children,\n})\n\nconst toTreeBranch = (children: Instance[]): TreeNode[] =>\n  children.map((child) => {\n    return treeObjectFactory(\n      lowerCaseFirstLetter(child.object.type || child.object.constructor.name),\n      child.props,\n      toTreeBranch(child.children),\n    )\n  })\n\nexport const toTree = (root: Instance<THREE.Scene>): Tree => toTreeBranch(root.children)\n"
  },
  {
    "path": "packages/test-renderer/src/helpers/waitFor.ts",
    "content": "import { act } from '@react-three/fiber'\n\nexport interface WaitOptions {\n  interval?: number\n  timeout?: number\n}\n\nexport async function waitFor(\n  callback: () => boolean | void,\n  { interval = 50, timeout = 5000 }: WaitOptions = {},\n): Promise<void> {\n  await act(async () => {\n    const start = performance.now()\n\n    while (true) {\n      const result = callback()\n      if (result || result == null) break\n      if (interval) await new Promise((resolve) => setTimeout(resolve, interval))\n      if (timeout && performance.now() - start >= timeout) throw new Error(`Timed out after ${timeout}ms.`)\n    }\n  })\n}\n"
  },
  {
    "path": "packages/test-renderer/src/index.tsx",
    "content": "import * as React from 'react'\nimport * as THREE from 'three'\n\nimport { extend, _roots as mockRoots, createRoot, reconciler, act, Instance } from '@react-three/fiber'\n\nimport { toTree } from './helpers/tree'\nimport { toGraph } from './helpers/graph'\n\nimport { createCanvas } from './createTestCanvas'\nimport { createEventFirer } from './fireEvent'\n\nimport type { CreateOptions, Renderer } from './types/public'\nimport { wrapFiber } from './createTestInstance'\nimport { waitFor, WaitOptions } from './helpers/waitFor'\n\n// Extend catalogue for render API in tests.\nextend(THREE as any)\n\nconst create = async (element: React.ReactNode, options?: Partial<CreateOptions>): Promise<Renderer> => {\n  const canvas = createCanvas(options)\n\n  const _root = createRoot(canvas)\n  await _root.configure({\n    frameloop: 'never',\n    // TODO: remove and use default behavior\n    size: {\n      width: options?.width ?? 1280,\n      height: options?.height ?? 800,\n      top: 0,\n      left: 0,\n    },\n    ...options,\n    events: undefined,\n  })\n\n  const _store = mockRoots.get(canvas)!.store\n\n  await act(async () => _root.render(element))\n  const _scene = (_store.getState().scene as Instance<THREE.Scene>['object']).__r3f!\n\n  return {\n    scene: wrapFiber(_scene),\n    async unmount() {\n      await act(async () => {\n        _root.unmount()\n      })\n    },\n    getInstance() {\n      // Bail if canvas is unmounted\n      if (!mockRoots.has(canvas)) return null\n\n      // Traverse fiber nodes for R3F root\n      const root = { current: mockRoots.get(canvas)!.fiber.current }\n      while (!root.current.child?.stateNode) root.current = root.current.child\n\n      // Return R3F instance from root\n      return reconciler.getPublicRootInstance(root)\n    },\n    async update(newElement: React.ReactNode) {\n      if (!mockRoots.has(canvas)) return console.warn('RTTR: attempted to update an unmounted root!')\n\n      await act(async () => {\n        _root.render(newElement)\n      })\n    },\n    toTree() {\n      return toTree(_scene)\n    },\n    toGraph() {\n      return toGraph(_scene)\n    },\n    fireEvent: createEventFirer(act, _store),\n    async advanceFrames(frames: number, delta: number | number[] = 1) {\n      const state = _store.getState()\n      const storeSubscribers = state.internal.subscribers\n\n      const promises: Promise<void>[] = []\n\n      storeSubscribers.forEach((subscriber) => {\n        for (let i = 0; i < frames; i++) {\n          if (Array.isArray(delta)) {\n            promises.push(\n              new Promise(() => subscriber.ref.current(state, (delta as number[])[i] || (delta as number[])[-1])),\n            )\n          } else {\n            promises.push(new Promise(() => subscriber.ref.current(state, delta as number)))\n          }\n        }\n      })\n\n      Promise.all(promises)\n    },\n  }\n}\n\nexport { create, act, waitFor }\nexport type { WaitOptions }\n\nexport * as ReactThreeTest from './types'\nexport default { create, act, waitFor }\n"
  },
  {
    "path": "packages/test-renderer/src/types/index.ts",
    "content": "export * from './public'\n"
  },
  {
    "path": "packages/test-renderer/src/types/internal.ts",
    "content": "export type CreateCanvasParameters = {\n  beforeReturn?: (canvas: HTMLCanvasElement) => void\n  width?: number\n  height?: number\n}\n\nexport interface Obj {\n  [key: string]: any\n}\n\n/**\n * this is an empty object of any,\n * the data is passed to a new event\n * and subsequently passed to the\n * event handler you're calling\n */\nexport type MockEventData = {\n  [key: string]: any\n}\n\nexport interface TestInstanceChildOpts {\n  exhaustive: boolean\n}\n"
  },
  {
    "path": "packages/test-renderer/src/types/public.ts",
    "content": "import type { Camera, RenderProps } from '@react-three/fiber'\n\nimport { ReactThreeTestInstance } from '../createTestInstance'\n\nimport type { MockEventData, CreateCanvasParameters } from './internal'\n\nexport { ReactThreeTestInstance }\n\nexport type MockSyntheticEvent = {\n  camera: Camera\n  stopPropagation: () => void\n  target: ReactThreeTestInstance\n  currentTarget: ReactThreeTestInstance\n  sourceEvent: MockEventData\n  [key: string]: any\n}\n\nexport type CreateOptions = CreateCanvasParameters & RenderProps<HTMLCanvasElement>\n\nexport type Renderer = {\n  scene: ReactThreeTestInstance\n  unmount: () => Promise<void>\n  getInstance: () => null | unknown\n  update: (el: React.ReactNode) => Promise<void>\n  toTree: () => Tree | undefined\n  toGraph: () => SceneGraph | undefined\n  fireEvent: (element: ReactThreeTestInstance, handler: string, data?: MockEventData) => Promise<any>\n  advanceFrames: (frames: number, delta: number | number[]) => Promise<void>\n}\n\nexport interface SceneGraphItem {\n  type: string\n  name: string\n  children: SceneGraphItem[] | null\n}\n\nexport type SceneGraph = SceneGraphItem[]\n\nexport interface TreeNode {\n  type: string\n  props: {\n    [key: string]: any\n  }\n  children: TreeNode[]\n}\n\nexport type Tree = TreeNode[]\n\nexport type { Act } from '@react-three/fiber'\n"
  },
  {
    "path": "readme.md",
    "content": "<h1>@react-three/fiber</h1>\n\n[![Version](https://img.shields.io/npm/v/@react-three/fiber?style=flat&colorA=000000&colorB=000000)](https://npmjs.com/package/@react-three/fiber)\n[![Downloads](https://img.shields.io/npm/dt/@react-three/fiber.svg?style=flat&colorA=000000&colorB=000000)](https://npmjs.com/package/@react-three/fiber)\n[![Twitter](https://img.shields.io/twitter/follow/pmndrs?label=%40pmndrs&style=flat&colorA=000000&colorB=000000&logo=twitter&logoColor=000000)](https://twitter.com/pmndrs)\n[![Discord](https://img.shields.io/discord/740090768164651008?style=flat&colorA=000000&colorB=000000&label=discord&logo=discord&logoColor=000000)](https://discord.gg/ZZjjNvJ)\n[![Open Collective](https://img.shields.io/opencollective/all/react-three-fiber?style=flat&colorA=000000&colorB=000000)](https://opencollective.com/react-three-fiber)\n[![ETH](https://img.shields.io/badge/ETH-f5f5f5?style=flat&colorA=000000&colorB=000000)](https://blockchain.com/eth/address/0x6E3f79Ea1d0dcedeb33D3fC6c34d2B1f156F2682)\n[![BTC](https://img.shields.io/badge/BTC-f5f5f5?style=flat&colorA=000000&colorB=000000)](https://blockchain.com/btc/address/36fuguTPxGCNnYZSRdgdh6Ea94brCAjMbH)\n\n<a href=\"https://docs.pmnd.rs/react-three-fiber/getting-started/examples\"><img src=\"docs/banner-r3f.jpg\" /></a>\n\nreact-three-fiber is a <a href=\"https://reactjs.org/docs/codebase-overview.html#renderers\">React renderer</a> for threejs.\n\nBuild your scene declaratively with re-usable, self-contained components that react to state, are readily interactive and can participate in React's ecosystem.\n\n```bash\nnpm install three @types/three @react-three/fiber\n```\n\n> [!WARNING]  \n> Three-fiber is a React renderer, it must pair with a major version of React, just like react-dom, react-native, etc. @react-three/fiber@8 pairs with react@18, @react-three/fiber@9 pairs with react@19.\n\n---\n\n#### Does it have limitations?\n\nNone. Everything that works in Threejs will work here without exception.\n\n#### Is it slower than plain Threejs?\n\nNo. There is no overhead. Components render outside of React. It outperforms Threejs in scale due to React's scheduling abilities.\n\n#### Can it keep up with frequent feature updates to Threejs?\n\nYes. It merely expresses Threejs in JSX, `<mesh />` dynamically turns into `new THREE.Mesh()`. If a new Threejs version adds, removes or changes features, it will be available to you instantly without depending on updates to this library.\n\n### What does it look like?\n\n<table>\n  <tbody>\n    <tr>\n      <td>Let's make a re-usable component that has its own state, reacts to user-input and participates in the render-loop. (<a href=\"https://codesandbox.io/s/rrppl0y8l4?file=/src/App.js\">live demo</a>).</td>\n      <td>\n        <a href=\"https://codesandbox.io/s/rrppl0y8l4\">\n          <img src=\"/docs/basic-app.gif\" />\n        </a>\n      </td>\n    </tr>\n  </tbody>\n</table>\n\n```jsx\nimport { createRoot } from 'react-dom/client'\nimport React, { useRef, useState } from 'react'\nimport { Canvas, useFrame } from '@react-three/fiber'\n\nfunction Box(props) {\n  // This reference gives us direct access to the THREE.Mesh object\n  const ref = useRef()\n  // Hold state for hovered and clicked events\n  const [hovered, hover] = useState(false)\n  const [clicked, click] = useState(false)\n  // Subscribe this component to the render-loop, rotate the mesh every frame\n  useFrame((state, delta) => (ref.current.rotation.x += delta))\n  // Return the view, these are regular Threejs elements expressed in JSX\n  return (\n    <mesh\n      {...props}\n      ref={ref}\n      scale={clicked ? 1.5 : 1}\n      onClick={(event) => click(!clicked)}\n      onPointerOver={(event) => hover(true)}\n      onPointerOut={(event) => hover(false)}>\n      <boxGeometry args={[1, 1, 1]} />\n      <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />\n    </mesh>\n  )\n}\n\ncreateRoot(document.getElementById('root')).render(\n  <Canvas>\n    <ambientLight intensity={Math.PI / 2} />\n    <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />\n    <pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />\n    <Box position={[-1.2, 0, 0]} />\n    <Box position={[1.2, 0, 0]} />\n  </Canvas>,\n)\n```\n\n<details>\n  <summary>Show TypeScript example</summary>\n\n```bash\nnpm install @types/three\n```\n\n```tsx\nimport * as THREE from 'three'\nimport { createRoot } from 'react-dom/client'\nimport React, { useRef, useState } from 'react'\nimport { Canvas, useFrame, ThreeElements } from '@react-three/fiber'\n\nfunction Box(props: ThreeElements['mesh']) {\n  const ref = useRef<THREE.Mesh>(null!)\n  const [hovered, hover] = useState(false)\n  const [clicked, click] = useState(false)\n  useFrame((state, delta) => (ref.current.rotation.x += delta))\n  return (\n    <mesh\n      {...props}\n      ref={ref}\n      scale={clicked ? 1.5 : 1}\n      onClick={(event) => click(!clicked)}\n      onPointerOver={(event) => hover(true)}\n      onPointerOut={(event) => hover(false)}>\n      <boxGeometry args={[1, 1, 1]} />\n      <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />\n    </mesh>\n  )\n}\n\ncreateRoot(document.getElementById('root') as HTMLElement).render(\n  <Canvas>\n    <ambientLight intensity={Math.PI / 2} />\n    <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />\n    <pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />\n    <Box position={[-1.2, 0, 0]} />\n    <Box position={[1.2, 0, 0]} />\n  </Canvas>,\n)\n```\n\nLive demo: https://codesandbox.io/s/icy-tree-brnsm?file=/src/App.tsx\n\n</details>\n\n<details>\n  <summary>Show React Native example</summary>\n\nThis example relies on react 18 and uses `expo-cli`, but you can create a bare project with their template or with the `react-native` CLI.\n\n```bash\n# Install expo-cli, this will create our app\nnpm install expo-cli -g\n# Create app and cd into it\nexpo init my-app\ncd my-app\n# Install dependencies\nnpm install three @react-three/fiber@beta react@rc\n# Start\nexpo start\n```\n\nSome configuration may be required to tell the Metro bundler about your assets if you use `useLoader` or Drei abstractions like `useGLTF` and `useTexture`:\n\n```js\n// metro.config.js\nmodule.exports = {\n  resolver: {\n    sourceExts: ['js', 'jsx', 'json', 'ts', 'tsx', 'cjs'],\n    assetExts: ['glb', 'png', 'jpg'],\n  },\n}\n```\n\n```tsx\nimport React, { useRef, useState } from 'react'\nimport { Canvas, useFrame } from '@react-three/fiber/native'\nfunction Box(props) {\n  const mesh = useRef(null)\n  const [hovered, setHover] = useState(false)\n  const [active, setActive] = useState(false)\n  useFrame((state, delta) => (mesh.current.rotation.x += delta))\n  return (\n    <mesh\n      {...props}\n      ref={mesh}\n      scale={active ? 1.5 : 1}\n      onClick={(event) => setActive(!active)}\n      onPointerOver={(event) => setHover(true)}\n      onPointerOut={(event) => setHover(false)}>\n      <boxGeometry args={[1, 1, 1]} />\n      <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />\n    </mesh>\n  )\n}\nexport default function App() {\n  return (\n    <Canvas>\n      <ambientLight intensity={Math.PI / 2} />\n      <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />\n      <pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />\n      <Box position={[-1.2, 0, 0]} />\n      <Box position={[1.2, 0, 0]} />\n    </Canvas>\n  )\n}\n```\n\n</details>\n\n---\n\n# Documentation, tutorials, examples\n\nVisit [docs.pmnd.rs](https://docs.pmnd.rs/react-three-fiber)\n\n# First steps\n\nYou need to be versed in both React and Threejs before rushing into this. If you are unsure about React consult the official [React docs](https://react.dev/learn), especially [the section about hooks](https://react.dev/reference/react). As for Threejs, make sure you at least glance over the following links:\n\n1. Make sure you have a [basic grasp of Threejs](https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene). Keep that site open.\n2. When you know what a scene is, a camera, mesh, geometry, material, fork the [demo above](https://github.com/pmndrs/react-three-fiber#what-does-it-look-like).\n3. [Look up](https://threejs.org/docs/index.html#api/en/objects/Mesh) the JSX elements that you see (mesh, ambientLight, etc), _all_ threejs exports are native to three-fiber.\n4. Try changing some values, scroll through our [API](https://docs.pmnd.rs/react-three-fiber) to see what the various settings and hooks do.\n\nSome helpful material:\n\n- [Threejs-docs](https://threejs.org/docs) and [examples](https://threejs.org/examples)\n- [Discover Threejs](https://discoverthreejs.com), especially the [Tips and Tricks](https://discoverthreejs.com/tips-and-tricks) chapter for best practices\n- [Bruno Simons Threejs Journey](https://threejs-journey.com), arguably the best learning resource, now includes a full [R3F chapter](https://threejs-journey.com/lessons/what-are-react-and-react-three-fiber)\n\n<a href=\"https://threejs-journey.com\"><img src=\"docs/banner-journey.jpg\" /></a>\n\n# Ecosystem\n\nThere is a vibrant and extensive eco system around three-fiber, full of libraries, helpers and abstractions.\n\n- [`@react-three/drei`](https://github.com/pmndrs/drei) &ndash; useful helpers, this is an eco system in itself\n- [`@react-three/gltfjsx`](https://github.com/pmndrs/gltfjsx) &ndash; turns GLTFs into JSX components\n- [`@react-three/postprocessing`](https://github.com/pmndrs/react-postprocessing) &ndash; post-processing effects\n- [`@react-three/uikit`](https://github.com/pmndrs/uikit) &ndash; WebGL rendered UI components for three-fiber\n- [`@react-three/test-renderer`](https://github.com/pmndrs/react-three-fiber/tree/master/packages/test-renderer) &ndash; for unit tests in node\n- [`@react-three/offscreen`](https://github.com/pmndrs/react-three-offscreen) &ndash; offscreen/worker canvas for react-three-fiber\n- [`@react-three/flex`](https://github.com/pmndrs/react-three-flex) &ndash; flexbox for react-three-fiber\n- [`@react-three/xr`](https://github.com/pmndrs/react-xr) &ndash; VR/AR controllers and events\n- [`@react-three/csg`](https://github.com/pmndrs/react-three-csg) &ndash; constructive solid geometry\n- [`@react-three/rapier`](https://github.com/pmndrs/react-three-rapier) &ndash; 3D physics using Rapier\n- [`@react-three/cannon`](https://github.com/pmndrs/use-cannon) &ndash; 3D physics using Cannon\n- [`@react-three/p2`](https://github.com/pmndrs/use-p2) &ndash; 2D physics using P2\n- [`@react-three/a11y`](https://github.com/pmndrs/react-three-a11y) &ndash; real a11y for your scene\n- [`@react-three/gpu-pathtracer`](https://github.com/pmndrs/react-three-gpu-pathtracer) &ndash; realistic path tracing\n- [`create-r3f-app next`](https://github.com/pmndrs/react-three-next) &ndash; nextjs starter\n- [`lamina`](https://github.com/pmndrs/lamina) &ndash; layer based shader materials\n- [`zustand`](https://github.com/pmndrs/zustand) &ndash; flux based state management\n- [`jotai`](https://github.com/pmndrs/jotai) &ndash; atoms based state management\n- [`valtio`](https://github.com/pmndrs/valtio) &ndash; proxy based state management\n- [`react-spring`](https://github.com/pmndrs/react-spring) &ndash; a spring-physics-based animation library\n- [`framer-motion-3d`](https://www.framer.com/docs/three-introduction/) &ndash; framer motion, a popular animation library\n- [`use-gesture`](https://github.com/pmndrs/react-use-gesture) &ndash; mouse/touch gestures\n- [`leva`](https://github.com/pmndrs/leva) &ndash; create GUI controls in seconds\n- [`maath`](https://github.com/pmndrs/maath) &ndash; a kitchen sink for math helpers\n- [`miniplex`](https://github.com/hmans/miniplex) &ndash; ECS (entity management system)\n- [`composer-suite`](https://github.com/hmans/composer-suite) &ndash; composing shaders, particles, effects and game mechanics\n- [`triplex`](https://triplex.dev/) &ndash; visual editor for react-three-fiber\n- [`koestlich`](https://github.com/coconut-xr/koestlich) &ndash; UI component library for react-three-fiber\n\n[Usage Trend of the @react-three Family](https://npm-compare.com/@react-three/a11y,@react-three/cannon,@react-three/csg,@react-three/drei,@react-three/flex,@react-three/gltfjsx,@react-three/gpu-pathtracer,@react-three/offscreen,@react-three/p2,@react-three/postprocessing,@react-three/rapier,@react-three/test-renderer,@react-three/uikit,@react-three/xr)\n\n# Who is using Three-fiber\n\nA small selection of companies and projects relying on three-fiber.\n\n- [`vercel`](https://www.vercel.com) (design agency)\n- [`basement`](https://basement.studio) (design agency)\n- [`studio freight`](https://studiofreight.com) (design agency)\n- [`14 islands`](https://www.14islands.com) (design agency)\n- [`ueno`](https://dribbble.com/ueno) (design agency) — [video](https://twitter.com/0xca0a/status/1204373807408013312)\n- [`flux.ai`](https://www.flux.ai) (PCB builder)\n- [`colorful.app`](https://www.colorful.app) (modeller)\n- [`bezi`](https://www.bezi.com) (modeller)\n- [`readyplayer.me`](https://readyplayer.me) (avatar configurator)\n- [`zillow`](https://www.zillow.com) (real estate)\n- [`lumalabs.ai/genie`](https://lumalabs.ai/genie) (AI models)\n- [`skybox.blockadelabs`](https://skybox.blockadelabs.com) (AI envmaps)\n- [`3dconfig`](https://3dconfig.com) (floor planer)\n- [`buerli.io`](https://buerli.io) (CAD)\n- [`getencube`](https://www.getencube.com) (CAD)\n- [`glowbuzzer`](https://www.glowbuzzer.com) (CAD) — [video](https://twitter.com/glowbuzzer/status/1678396014644940800)\n- [`triplex`](https://triplex.dev) (editor) — [video](https://twitter.com/_douges/status/1708859381369221539)\n- [`theatrejs`](https://www.theatrejs.com) (editor) — [video](https://twitter.com/0xca0a/status/1566838823170068480)\n\n# How to contribute\n\nIf you like this project, please consider helping out. All contributions are welcome as well as donations to [Opencollective](https://opencollective.com/react-three-fiber), or in crypto `BTC: 36fuguTPxGCNnYZSRdgdh6Ea94brCAjMbH`, `ETH: 0x6E3f79Ea1d0dcedeb33D3fC6c34d2B1f156F2682`.\n\n#### Backers\n\nThank you to all our backers! 🙏\n\n<a href=\"https://opencollective.com/react-three-fiber#backers\" target=\"_blank\">\n  <img src=\"https://opencollective.com/react-three-fiber/backers.svg?width=890\"/>\n</a>\n\n#### Contributors\n\nThis project exists thanks to all the people who contribute.\n\n<a href=\"https://github.com/pmndrs/react-three-fiber/graphs/contributors\">\n  <img src=\"https://opencollective.com/react-three-fiber/contributors.svg?width=890\" />\n</a>\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es6\",\n    \"lib\": [\"es2019\", \"dom\"],\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"esModuleInterop\": true,\n    \"jsx\": \"react-jsx\",\n    \"pretty\": true,\n    \"strict\": true,\n    \"skipLibCheck\": true,\n    \"declaration\": true,\n    \"removeComments\": false,\n    \"emitDeclarationOnly\": true,\n    \"resolveJsonModule\": true,\n    \"noImplicitThis\": false,\n    \"baseUrl\": \"./\",\n    \"types\": [\"jest\", \"node\", \"offscreencanvas\"]\n  },\n  \"include\": [\"packages/**/*\"],\n  \"exclude\": [\"node_modules\", \"dist\"]\n}\n"
  },
  {
    "path": "vite.config.ts",
    "content": "import { defineConfig, transformWithEsbuild } from 'vite'\nimport * as path from 'node:path'\nimport * as fs from 'node:fs'\n\nexport default defineConfig({\n  build: {\n    outDir: 'packages/fiber/react-reconciler',\n    target: 'esnext',\n    lib: {\n      formats: ['es'],\n      entry: {\n        index: 'packages/fiber/node_modules/react-reconciler/index.js',\n        constants: 'packages/fiber/node_modules/react-reconciler/cjs/react-reconciler-constants.production.js',\n      },\n      fileName: '[name]',\n    },\n    rollupOptions: {\n      treeshake: false,\n      external: (id: string) => !id.startsWith('.') && !path.isAbsolute(id),\n      output: {\n        entryFileNames: '[name].js',\n        chunkFileNames: '[name].js',\n      },\n    },\n  },\n  plugins: [\n    {\n      name: 'vite-ts',\n      enforce: 'pre',\n      transform(code, id) {\n        // Vite does not preserve static exports which breaks ESM transpilation\n        // Instead, we manually transpile constants from CJS -> ESM\n        if (id.includes('react-reconciler-constants')) {\n          return (\n            code\n              // Export top-level constants\n              .replace(/exports\\.(\\w+)\\s*=\\s*(.*?);/g, 'export const $1 = $2;')\n              // Remove \"use strict\"\n              .replace(/\"use strict\";\\s*/g, '')\n          )\n        }\n      },\n      generateBundle(_options, bundle) {\n        for (const key in bundle) {\n          if (!('isEntry' in bundle[key]) || !bundle[key].isEntry) continue\n\n          const name = bundle[key].name\n          const source = fs.readFileSync(`packages/fiber/node_modules/@types/react-reconciler/${name}.d.ts`, 'utf-8')\n          this.emitFile({ type: 'asset', fileName: `${name}.d.ts`, source })\n        }\n      },\n    },\n    {\n      name: 'vite-minify',\n      renderChunk: {\n        order: 'post',\n        handler(code, { fileName }) {\n          return transformWithEsbuild(code, fileName, { minify: true })\n        },\n      },\n    },\n  ],\n})\n"
  }
]