From 68b3f42c48fa34f62f73d1619868a48886988999 Mon Sep 17 00:00:00 2001 From: Kris Baumgarter Date: Wed, 8 Apr 2026 00:19:35 -0400 Subject: [PATCH 1/3] Migrate to pnpm catalogs (#104) * Migrate to pnpm catalogs * Switch from catalogs to syncpack * Attemp to fix CI script * Another CI fix --- .github/workflows/ci.yml | 14 +- .syncpackrc.json | 59 + README.md | 31 +- apps/website/app/demos/[demoname]/Dev.tsx | 2 +- apps/website/app/demos/[demoname]/Social.tsx | 2 +- apps/website/components/Image.tsx | 10 +- apps/website/components/Nav.tsx | 2 +- apps/website/package.json | 320 +- bin/depcheck.mjs | 2 +- demos/arkanoid/package.json | 2 +- demos/dbismut-furniture/package.json | 2 +- demos/floating-laptop/package.json | 2 +- .../components/ForceShield/consts/index.ts | 76 +- .../src/components/ForceShield/index.tsx | 240 +- .../components/ForceShield/shaderMaterial.ts | 78 +- .../ForceShield/useShieldControls.ts | 88 +- .../src/components/overlay/LoadingOverlay.tsx | 10 +- .../src/components/overlay/OverlayButtons.tsx | 80 +- .../src/components/overlay/OverlayHeader.tsx | 58 +- .../src/components/overlay/UIOverlay.tsx | 10 +- .../src/components/playground/DemoSphere.tsx | 33 +- .../src/components/playground/Droideka.tsx | 475 +- .../src/components/playground/GlbModel.tsx | 103 +- .../src/components/playground/GridFloor.tsx | 101 +- .../playground/PlaygroundCanvas.tsx | 87 +- .../components/playground/PostProcessing.tsx | 97 +- .../src/components/playground/SceneCamera.tsx | 113 +- .../components/playground/SceneContent.tsx | 38 +- .../playground/SceneEnvironment.tsx | 34 +- .../components/playground/SceneLighting.tsx | 107 +- .../flow-shield/src/components/theme/theme.ts | 94 +- .../src/react-three-fiber-jsx.d.ts | 6 +- demos/flow-shield/vite.config.ts | 6 +- demos/gltf-animations-re-used/package.json | 2 +- demos/image-gallery/package.json | 2 +- demos/infinite-scroll/src/ScrollControls.tsx | 6 +- .../package.json | 2 +- demos/landing-page/package.json | 2 +- demos/minecraft/package.json | 2 +- demos/mount-transitions/package.json | 2 +- demos/racing-game/src/App.tsx | 3 +- demos/racing-game/src/store.ts | 6 +- demos/racing-game/src/ui/Editor.ts | 11 +- demos/racing-game/src/useToggle.tsx | 10 +- demos/raycast-cycling/package.json | 2 +- demos/react-spring-animations/package.json | 2 +- .../package.json | 2 +- demos/space-game/package.json | 2 +- demos/spline-glass-shapes/package.json | 2 +- demos/springy-boxes/package.json | 2 +- demos/t-shirt-configurator/package.json | 2 +- demos/the-three-graces/package.json | 2 +- demos/wobbling-sphere/package.json | 2 +- package-lock.json | 18258 ---------------- package.json | 24 +- packages/e2e/bin/test.mjs | 2 +- pnpm-lock.yaml | 16965 ++++++++++++++ pnpm-workspace.yaml | 10 + 58 files changed, 18056 insertions(+), 19651 deletions(-) create mode 100644 .syncpackrc.json delete mode 100644 package-lock.json create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16043604..c43b0ea6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,24 +33,30 @@ jobs: - uses: actions/checkout@v4 - id: configurepages uses: actions/configure-pages@v5 + - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: node-version-file: ".nvmrc" - cache: "npm" - - run: npm ci + cache: "pnpm" + - run: pnpm install --frozen-lockfile - uses: rharkor/caching-for-turbo@v1.5 + # + # Lint + # + - run: pnpm lint:versions + # # Test # - # - run: npm test + # - run: pnpm test # env: # BASE_PATH: ${{ steps.configurepages.outputs.base_path }} # # Build (only for pushes on main) # - - run: npm run build + - run: pnpm build if: github.event_name != 'pull_request' env: BASE_PATH: ${{ steps.configurepages.outputs.base_path }} diff --git a/.syncpackrc.json b/.syncpackrc.json new file mode 100644 index 00000000..5ddec8a8 --- /dev/null +++ b/.syncpackrc.json @@ -0,0 +1,59 @@ +{ + "versionGroups": [ + { + "label": "Demos must use identical versions of shared dependencies", + "packages": ["@demo/**"], + "dependencies": [ + "react", + "react-dom", + "three", + "@react-three/fiber", + "@react-three/drei", + "@react-three/postprocessing", + "@react-three/rapier", + "@react-three/cannon", + "@react-three/csg", + "@pmndrs/branding", + "@pmndrs/assets", + "@react-spring/three", + "@react-spring/web", + "framer-motion", + "zustand", + "valtio", + "leva", + "maath", + "postprocessing", + "r3f-perf", + "lamina", + "three-stdlib", + "meshline", + "styled-components", + "react-colorful", + "react-icons", + "antd", + "@headlessui/react", + "@heroicons/react", + "wouter", + "@splinetool/loader", + "@splinetool/r3f-spline", + "nice-color-palettes", + "lodash-es", + "@babel/runtime", + "babel-plugin-glsl", + "@types/react", + "@types/react-dom", + "@types/three", + "@vitejs/plugin-react", + "typescript", + "vite" + ], + "policy": "sameRange" + }, + { + "label": "Workspace deps use workspace protocol", + "dependencies": ["@demo/**"], + "dependencyTypes": ["prod"], + "policy": "sameRange" + } + ] +} diff --git a/README.md b/README.md index b4eea065..1671577c 100644 --- a/README.md +++ b/README.md @@ -23,32 +23,23 @@ Prerequisites: nb: if you want this node version to be your default nvm's one: `nvm alias default node` -- Install [PNPM](https://pnpm.io/installation#using-corepack) - Package Manager, - with: - - ```sh - $ corepack enable - $ corepack prepare --activate # it reads "packageManager" - $ npm -v # make sure your version satisfies package.json#engines.npm - ``` - ``` -$ npm ci +$ pnpm install ``` # dev ```sh -$ npm run dev +$ pnpm dev ``` # build ```sh -$ npm run build +$ pnpm build ``` -NB: `npm run build -- --force` to ignore turbo cache +NB: `pnpm build --force` to ignore turbo cache Then `npx serve out`. @@ -73,7 +64,7 @@ This will: > debug purposes(to be 1:1 with GitHub pages) you can: > > ```sh -> $ BASE_PATH=/examples BASE_URL=http://localhost:4000 npm run build +> $ BASE_PATH=/examples BASE_URL=http://localhost:4000 pnpm build > $ npx serve out -p 4000 > ``` @@ -82,17 +73,17 @@ This will: # test ```sh -$ npm test +$ pnpm test ``` -To update the snapshots: `npm test -- -- --update-snapshots` +To update the snapshots: `pnpm test -- -- --update-snapshots`
You can also: ```sh -$ BASE_PATH=/examples npm test +$ BASE_PATH=/examples pnpm test ```
@@ -110,8 +101,8 @@ $ docker run -it --rm \ # # echo "Hey, I am acting like the CI" # -# npm ci -# npm test +# pnpm install +# pnpm test ``` or in one command to update snapshots: @@ -119,7 +110,7 @@ or in one command to update snapshots: ```sh docker run --rm \ -w /app -v "$(pwd)":/app -v /app/node_modules \ - mcr.microsoft.com/playwright:v1.45.3-jammy /bin/sh -c "npm ci && npm test -- -- --update-snapshots" + mcr.microsoft.com/playwright:v1.45.3-jammy /bin/sh -c "pnpm install && pnpm test -- -- --update-snapshots" ``` # Colophon diff --git a/apps/website/app/demos/[demoname]/Dev.tsx b/apps/website/app/demos/[demoname]/Dev.tsx index 13011d84..0583f4f8 100644 --- a/apps/website/app/demos/[demoname]/Dev.tsx +++ b/apps/website/app/demos/[demoname]/Dev.tsx @@ -6,7 +6,7 @@ import { useRouter } from "next/navigation"; export function Dev({ demoname }: { demoname: string }) { const { refresh } = useRouter(); - const cmd = `npm run -w demos/${demoname} dev3`; + const cmd = `pnpm --filter @demo/${demoname} dev3`; return (
diff --git a/apps/website/app/demos/[demoname]/Social.tsx b/apps/website/app/demos/[demoname]/Social.tsx index fef02ed9..e625539d 100644 --- a/apps/website/app/demos/[demoname]/Social.tsx +++ b/apps/website/app/demos/[demoname]/Social.tsx @@ -17,7 +17,7 @@ export function Social({ const handleClick = async () => { await navigator.clipboard.writeText( - `cd $(mktemp -d ${demoname}.XXX) && npx -y degit pmndrs/examples/demos/${demoname} . && npm i && npm run dev` + `cd $(mktemp -d ${demoname}.XXX) && npx -y degit pmndrs/examples/demos/${demoname} . && npm i && npm run dev`, ); setCopied(true); }; diff --git a/apps/website/components/Image.tsx b/apps/website/components/Image.tsx index fbf5c2c5..4f1cc721 100644 --- a/apps/website/components/Image.tsx +++ b/apps/website/components/Image.tsx @@ -1,9 +1,9 @@ -import NextImage, { ImageProps } from 'next/image' +import NextImage, { ImageProps } from "next/image"; -const basePath = process.env.BASE_PATH +const basePath = process.env.BASE_PATH; const Image = ({ src, ...rest }: ImageProps) => ( - -) + +); -export default Image \ No newline at end of file +export default Image; diff --git a/apps/website/components/Nav.tsx b/apps/website/components/Nav.tsx index 94773370..94b91bba 100644 --- a/apps/website/components/Nav.tsx +++ b/apps/website/components/Nav.tsx @@ -21,7 +21,7 @@ export default function Nav({ }: { demos: ReturnType } & ComponentProps<"nav">) { const ulRef = useRef>(null); const lisRef = useRef( - Array.from({ length: demos.length }).map(() => createRef()) + Array.from({ length: demos.length }).map(() => createRef()), ); const { demoname } = useParams(); diff --git a/apps/website/package.json b/apps/website/package.json index e75b4620..1c4cb645 100644 --- a/apps/website/package.json +++ b/apps/website/package.json @@ -4,171 +4,171 @@ "private": true, "scripts": { "dev": "next dev", - "dev2": "npm run dev", + "dev2": "pnpm run dev", "build": "NODE_ENV=production next build", - "build3": "npm run build", + "build3": "pnpm run build", "start": "next start", "lint": "next lint" }, "dependencies": { - "@demo/aquarium": "^1.0.0", - "@demo/arkanoid": "^1.0.0", - "@demo/arkanoid-under-60-loc": "^1.0.0", - "@demo/audio-analyser": "^1.0.0", - "@demo/backdrop-and-cables": "^1.0.0", - "@demo/baking-soft-shadows": "^1.0.0", - "@demo/basic-ballpit": "^1.0.0", - "@demo/basic-demo": "^1.0.0", - "@demo/bestservedbold-christmas-baubles": "^1.0.0", - "@demo/bezier-curves-and-nodes": "^1.0.0", - "@demo/bloom-hdr-workflow-gltf": "^1.0.0", - "@demo/bouncy-watch": "^1.0.0", - "@demo/bounds-and-makedefault": "^1.0.0", - "@demo/bruno-simons-20k-challenge": "^1.0.0", - "@demo/building-dynamic-envmaps": "^1.0.0", - "@demo/building-live-envmaps": "^1.0.0", - "@demo/bvh": "^1.0.0", - "@demo/camera-scroll": "^1.0.0", - "@demo/camera-shake": "^1.0.0", - "@demo/canvas-text": "^1.0.0", - "@demo/cards": "^1.0.0", - "@demo/cards-with-border-radius": "^1.0.0", - "@demo/caustics": "^1.0.0", - "@demo/cell-fracture": "^1.0.0", - "@demo/clones": "^1.0.0", - "@demo/clouds": "^0.1.0", - "@demo/color-grading": "^1.0.0", - "@demo/confetti": "^1.0.0", - "@demo/csg-bunny-usegroups": "^1.0.0", - "@demo/csg-house": "^1.0.0", - "@demo/csg-operations-rapier-physics": "^1.0.0", - "@demo/dbismut-furniture": "^1.0.0", - "@demo/diamond-refraction": "^1.0.0", - "@demo/diamond-ring": "^1.0.0", - "@demo/drei-rendertexture": "^1.0.0", - "@demo/ecctrl-fisheye": "^1.0.0", - "@demo/edgesgeometry": "^1.0.0", - "@demo/enter-portals": "^1.0.0", - "@demo/environment-blur-and-transitions": "^1.0.0", - "@demo/envmap-ground-projection": "^1.0.0", - "@demo/faucets-select-highlight": "^1.0.0", - "@demo/flexbox-yoga-in-webgl": "^0.1.0", - "@demo/floating-diamonds": "^1.0.0", - "@demo/floating-instanced-shoes": "^1.0.0", - "@demo/flow-shield": "^1.0.0", - "@demo/floating-laptop": "^1.0.0", - "@demo/flying-bananas": "^1.0.0", - "@demo/frosted-glass": "^1.0.0", - "@demo/gatsby-stars": "^1.0.0", - "@demo/glass-flower": "^1.0.0", - "@demo/gltf-animations": "^1.0.0", - "@demo/gltf-animations-re-used": "^1.0.0", - "@demo/gltf-animations-tied-to-scroll": "^1.0.0", - "@demo/gltfjsx-400kb-drone": "^1.0.0", - "@demo/gpgpu-curl-noise-dof": "^1.0.0", - "@demo/grass-shader": "^1.0.0", - "@demo/ground-projected-envmaps-lamina": "^1.0.0", - "@demo/ground-reflections-and-video-textures": "^1.0.0", - "@demo/hi-key-bubbles": "^1.0.0", - "@demo/horizontal-tiles": "^1.0.0", - "@demo/html-annotations": "^1.0.0", - "@demo/html-input-fields": "^1.0.0", - "@demo/html-markers": "^1.0.0", - "@demo/image-gallery": "^1.0.0", - "@demo/infinite-scroll": "^1.0.0", - "@demo/instanced-particles-effects": "^1.0.0", - "@demo/instanced-vertex-colors": "^1.0.0", - "@demo/instances": "^1.0.0", - "@demo/inter-epoxy-resin": "^1.0.0", - "@demo/interactive-spline-scene-live-html": "^1.0.0", - "@demo/inverted-stencil-buffer": "^1.0.0", - "@demo/iridescent-decals": "^1.0.0", - "@demo/lamina-1x": "^1.0.0", - "@demo/landing-page": "^1.0.0", - "@demo/learn-with-jason": "^0.1.0", - "@demo/lulaby-city": "^0.1.0", - "@demo/lusion-connectors": "^1.0.0", - "@demo/magic-box": "^1.0.0", - "@demo/merged-instance": "^1.0.0", - "@demo/minecraft": "^0.1.0", - "@demo/mixing-controls": "^1.0.0", - "@demo/mixing-html-and-webgl": "^1.0.0", - "@demo/mixing-html-and-webgl-w-occlusion": "^1.0.0", - "@demo/moksha": "^1.0.0", - "@demo/monitors": "^1.0.0", - "@demo/motionpathcontrols": "^1.0.0", - "@demo/mount-transitions": "^1.0.0", - "@demo/multiple-views-with-uniform-controls": "^1.0.0", - "@demo/nextjs-prism": "^1.0.0", - "@demo/night-train": "^1.0.0", - "@demo/object-clump": "^1.0.0", - "@demo/pairing-threejs-to-ui": "^1.0.0", - "@demo/pass-through-portals": "^1.0.0", - "@demo/physics-with-convex-polyhedrons": "^1.0.0", - "@demo/pinball-in-70-lines": "^1.0.0", - "@demo/pmndrs-vercel": "^1.0.0", - "@demo/portal-shapes": "^1.0.0", - "@demo/portals": "^1.0.0", - "@demo/progressive-loading-states-with-suspense": "^1.0.0", - "@demo/racing-game": "^1.0.0", - "@demo/ragdoll-physics": "^1.0.0", - "@demo/rapier-physics": "^1.0.0", - "@demo/rapier-ping-pong": "^1.0.0", - "@demo/raycast-cycling": "^1.0.0", - "@demo/re-using-geometry-and-level-of-detail": "^1.0.0", - "@demo/re-using-gltfs": "^1.0.0", - "@demo/react-ellipsecurve": "^1.0.0", - "@demo/react-pp-outlines": "^1.0.0", - "@demo/react-spring-animations": "^1.0.0", - "@demo/room-with-soft-shadows": "^1.0.0", - "@demo/router-transitions": "^1.0.0", - "@demo/scrollcontrols-and-lens-refraction": "^1.0.0", - "@demo/scrollcontrols-gltf": "^1.0.0", - "@demo/scrollcontrols-with-minimap": "^1.0.0", - "@demo/selective-outlines": "^1.0.0", - "@demo/shader-fire": "^1.0.0", - "@demo/shader-hmr": "^1.0.0", - "@demo/shadermaterials": "^1.0.0", - "@demo/shoe-configurator": "^1.0.0", - "@demo/shopping": "^1.0.0", - "@demo/simple-audio-analyser": "^1.0.0", - "@demo/simple-physics-demo": "^1.0.0", - "@demo/simple-physics-demo-with-debug-bounds": "^1.0.0", - "@demo/sky-dome-with-annotations": "^1.0.0", - "@demo/soft-shadows": "^1.0.0", - "@demo/space-game": "^1.0.0", - "@demo/sparks-and-effects": "^1.0.0", - "@demo/spline-glass-shapes": "^1.0.0", - "@demo/sport-hall": "^1.0.0", - "@demo/springy-boxes": "^1.0.0", - "@demo/ssgi-spheres-with-rapier-physics": "^1.0.0", - "@demo/ssr-test": "^1.0.0", - "@demo/stage-presets-gltfjsx": "^1.0.0", - "@demo/staging-and-camerashake": "^1.0.0", - "@demo/starwars": "^1.0.0", - "@demo/stencil-mask": "^1.0.0", - "@demo/svg-maps-with-html-annotations": "^1.0.0", - "@demo/svg-renderer": "^1.0.0", - "@demo/t-shirt-configurator": "^1.0.0", - "@demo/the-three-graces": "^0.1.0", - "@demo/threejs-journey-lv-1-fisheye": "^1.0.0", - "@demo/threejs-journey-portal": "^1.0.0", - "@demo/thunder-clouds": "^0.1.0", - "@demo/transformcontrols-and-makedefault": "^1.0.0", - "@demo/transparent-aesop-bottles": "^0.1.0", - "@demo/trigger-meshes": "^1.0.0", - "@demo/tying-canvas-to-scroll-offset": "^1.0.0", - "@demo/useintersect-and-scrollcontrols": "^1.0.0", - "@demo/video-cookies": "^1.0.0", - "@demo/video-textures": "^1.0.0", - "@demo/view-tracking": "^1.0.0", - "@demo/viewcube": "^1.0.0", - "@demo/viking-ship": "^0.1.0", - "@demo/volumetric-light-godray": "^1.0.0", - "@demo/volumetric-spotlight": "^1.0.0", - "@demo/water-shader": "^1.0.0", - "@demo/wobbling-sphere": "^1.0.0", - "@demo/zustand-site": "^0.1.0", + "@demo/aquarium": "workspace:*", + "@demo/arkanoid": "workspace:*", + "@demo/arkanoid-under-60-loc": "workspace:*", + "@demo/audio-analyser": "workspace:*", + "@demo/backdrop-and-cables": "workspace:*", + "@demo/baking-soft-shadows": "workspace:*", + "@demo/basic-ballpit": "workspace:*", + "@demo/basic-demo": "workspace:*", + "@demo/bestservedbold-christmas-baubles": "workspace:*", + "@demo/bezier-curves-and-nodes": "workspace:*", + "@demo/bloom-hdr-workflow-gltf": "workspace:*", + "@demo/bouncy-watch": "workspace:*", + "@demo/bounds-and-makedefault": "workspace:*", + "@demo/bruno-simons-20k-challenge": "workspace:*", + "@demo/building-dynamic-envmaps": "workspace:*", + "@demo/building-live-envmaps": "workspace:*", + "@demo/bvh": "workspace:*", + "@demo/camera-scroll": "workspace:*", + "@demo/camera-shake": "workspace:*", + "@demo/canvas-text": "workspace:*", + "@demo/cards": "workspace:*", + "@demo/cards-with-border-radius": "workspace:*", + "@demo/caustics": "workspace:*", + "@demo/cell-fracture": "workspace:*", + "@demo/clones": "workspace:*", + "@demo/clouds": "workspace:*", + "@demo/color-grading": "workspace:*", + "@demo/confetti": "workspace:*", + "@demo/csg-bunny-usegroups": "workspace:*", + "@demo/csg-house": "workspace:*", + "@demo/csg-operations-rapier-physics": "workspace:*", + "@demo/dbismut-furniture": "workspace:*", + "@demo/diamond-refraction": "workspace:*", + "@demo/diamond-ring": "workspace:*", + "@demo/drei-rendertexture": "workspace:*", + "@demo/ecctrl-fisheye": "workspace:*", + "@demo/edgesgeometry": "workspace:*", + "@demo/enter-portals": "workspace:*", + "@demo/environment-blur-and-transitions": "workspace:*", + "@demo/envmap-ground-projection": "workspace:*", + "@demo/faucets-select-highlight": "workspace:*", + "@demo/flexbox-yoga-in-webgl": "workspace:*", + "@demo/floating-diamonds": "workspace:*", + "@demo/floating-instanced-shoes": "workspace:*", + "@demo/flow-shield": "workspace:*", + "@demo/floating-laptop": "workspace:*", + "@demo/flying-bananas": "workspace:*", + "@demo/frosted-glass": "workspace:*", + "@demo/gatsby-stars": "workspace:*", + "@demo/glass-flower": "workspace:*", + "@demo/gltf-animations": "workspace:*", + "@demo/gltf-animations-re-used": "workspace:*", + "@demo/gltf-animations-tied-to-scroll": "workspace:*", + "@demo/gltfjsx-400kb-drone": "workspace:*", + "@demo/gpgpu-curl-noise-dof": "workspace:*", + "@demo/grass-shader": "workspace:*", + "@demo/ground-projected-envmaps-lamina": "workspace:*", + "@demo/ground-reflections-and-video-textures": "workspace:*", + "@demo/hi-key-bubbles": "workspace:*", + "@demo/horizontal-tiles": "workspace:*", + "@demo/html-annotations": "workspace:*", + "@demo/html-input-fields": "workspace:*", + "@demo/html-markers": "workspace:*", + "@demo/image-gallery": "workspace:*", + "@demo/infinite-scroll": "workspace:*", + "@demo/instanced-particles-effects": "workspace:*", + "@demo/instanced-vertex-colors": "workspace:*", + "@demo/instances": "workspace:*", + "@demo/inter-epoxy-resin": "workspace:*", + "@demo/interactive-spline-scene-live-html": "workspace:*", + "@demo/inverted-stencil-buffer": "workspace:*", + "@demo/iridescent-decals": "workspace:*", + "@demo/lamina-1x": "workspace:*", + "@demo/landing-page": "workspace:*", + "@demo/learn-with-jason": "workspace:*", + "@demo/lulaby-city": "workspace:*", + "@demo/lusion-connectors": "workspace:*", + "@demo/magic-box": "workspace:*", + "@demo/merged-instance": "workspace:*", + "@demo/minecraft": "workspace:*", + "@demo/mixing-controls": "workspace:*", + "@demo/mixing-html-and-webgl": "workspace:*", + "@demo/mixing-html-and-webgl-w-occlusion": "workspace:*", + "@demo/moksha": "workspace:*", + "@demo/monitors": "workspace:*", + "@demo/motionpathcontrols": "workspace:*", + "@demo/mount-transitions": "workspace:*", + "@demo/multiple-views-with-uniform-controls": "workspace:*", + "@demo/nextjs-prism": "workspace:*", + "@demo/night-train": "workspace:*", + "@demo/object-clump": "workspace:*", + "@demo/pairing-threejs-to-ui": "workspace:*", + "@demo/pass-through-portals": "workspace:*", + "@demo/physics-with-convex-polyhedrons": "workspace:*", + "@demo/pinball-in-70-lines": "workspace:*", + "@demo/pmndrs-vercel": "workspace:*", + "@demo/portal-shapes": "workspace:*", + "@demo/portals": "workspace:*", + "@demo/progressive-loading-states-with-suspense": "workspace:*", + "@demo/racing-game": "workspace:*", + "@demo/ragdoll-physics": "workspace:*", + "@demo/rapier-physics": "workspace:*", + "@demo/rapier-ping-pong": "workspace:*", + "@demo/raycast-cycling": "workspace:*", + "@demo/re-using-geometry-and-level-of-detail": "workspace:*", + "@demo/re-using-gltfs": "workspace:*", + "@demo/react-ellipsecurve": "workspace:*", + "@demo/react-pp-outlines": "workspace:*", + "@demo/react-spring-animations": "workspace:*", + "@demo/room-with-soft-shadows": "workspace:*", + "@demo/router-transitions": "workspace:*", + "@demo/scrollcontrols-and-lens-refraction": "workspace:*", + "@demo/scrollcontrols-gltf": "workspace:*", + "@demo/scrollcontrols-with-minimap": "workspace:*", + "@demo/selective-outlines": "workspace:*", + "@demo/shader-fire": "workspace:*", + "@demo/shader-hmr": "workspace:*", + "@demo/shadermaterials": "workspace:*", + "@demo/shoe-configurator": "workspace:*", + "@demo/shopping": "workspace:*", + "@demo/simple-audio-analyser": "workspace:*", + "@demo/simple-physics-demo": "workspace:*", + "@demo/simple-physics-demo-with-debug-bounds": "workspace:*", + "@demo/sky-dome-with-annotations": "workspace:*", + "@demo/soft-shadows": "workspace:*", + "@demo/space-game": "workspace:*", + "@demo/sparks-and-effects": "workspace:*", + "@demo/spline-glass-shapes": "workspace:*", + "@demo/sport-hall": "workspace:*", + "@demo/springy-boxes": "workspace:*", + "@demo/ssgi-spheres-with-rapier-physics": "workspace:*", + "@demo/ssr-test": "workspace:*", + "@demo/stage-presets-gltfjsx": "workspace:*", + "@demo/staging-and-camerashake": "workspace:*", + "@demo/starwars": "workspace:*", + "@demo/stencil-mask": "workspace:*", + "@demo/svg-maps-with-html-annotations": "workspace:*", + "@demo/svg-renderer": "workspace:*", + "@demo/t-shirt-configurator": "workspace:*", + "@demo/the-three-graces": "workspace:*", + "@demo/threejs-journey-lv-1-fisheye": "workspace:*", + "@demo/threejs-journey-portal": "workspace:*", + "@demo/thunder-clouds": "workspace:*", + "@demo/transformcontrols-and-makedefault": "workspace:*", + "@demo/transparent-aesop-bottles": "workspace:*", + "@demo/trigger-meshes": "workspace:*", + "@demo/tying-canvas-to-scroll-offset": "workspace:*", + "@demo/useintersect-and-scrollcontrols": "workspace:*", + "@demo/video-cookies": "workspace:*", + "@demo/video-textures": "workspace:*", + "@demo/view-tracking": "workspace:*", + "@demo/viewcube": "workspace:*", + "@demo/viking-ship": "workspace:*", + "@demo/volumetric-light-godray": "workspace:*", + "@demo/volumetric-spotlight": "workspace:*", + "@demo/water-shader": "workspace:*", + "@demo/wobbling-sphere": "workspace:*", + "@demo/zustand-site": "workspace:*", "clsx": "^2.1.1", "next": "14.2.5", "open-props": "^1.7.5", diff --git a/bin/depcheck.mjs b/bin/depcheck.mjs index 703b75c7..90e635f4 100644 --- a/bin/depcheck.mjs +++ b/bin/depcheck.mjs @@ -65,7 +65,7 @@ async function checkAllWorkspaces() { for (const [workspace, deps] of Object.entries(workspaceUnusedDeps)) { if (deps.length > 0) { hasUnusedDeps = true; - const uninstallCommand = `npm uninstall ${deps.join(" ")} -w demos/${workspace}`; + const uninstallCommand = `pnpm remove ${deps.join(" ")} --filter @demo/${workspace}`; console.log(`\n$ ${uninstallCommand}`); } } diff --git a/demos/arkanoid/package.json b/demos/arkanoid/package.json index b9583805..a478dc51 100644 --- a/demos/arkanoid/package.json +++ b/demos/arkanoid/package.json @@ -10,7 +10,7 @@ "audio" ], "dependencies": { - "@react-spring/three": "^9.6.1", + "@react-spring/three": "^9.7.3", "@react-three/cannon": "^6.6.0", "@react-three/drei": "^9.109.5", "@react-three/fiber": "^8.17.5", diff --git a/demos/dbismut-furniture/package.json b/demos/dbismut-furniture/package.json index 0841cc21..9a040167 100644 --- a/demos/dbismut-furniture/package.json +++ b/demos/dbismut-furniture/package.json @@ -5,7 +5,7 @@ "homepage": "https://codesandbox.io/s/62o18n", "keywords": [], "dependencies": { - "@react-spring/three": "^9.7.1", + "@react-spring/three": "^9.7.3", "@react-three/drei": "^9.109.5", "@react-three/fiber": "^8.17.5", "react": "^18.3.1", diff --git a/demos/floating-laptop/package.json b/demos/floating-laptop/package.json index 7effe807..035d0cfa 100644 --- a/demos/floating-laptop/package.json +++ b/demos/floating-laptop/package.json @@ -9,7 +9,7 @@ "react-spring" ], "dependencies": { - "@react-spring/three": "^9.7.2", + "@react-spring/three": "^9.7.3", "@react-spring/web": "^9.7.4", "@react-three/drei": "^9.109.5", "@react-three/fiber": "^8.17.5", diff --git a/demos/flow-shield/src/components/ForceShield/consts/index.ts b/demos/flow-shield/src/components/ForceShield/consts/index.ts index 1c17b61a..0bf53c32 100644 --- a/demos/flow-shield/src/components/ForceShield/consts/index.ts +++ b/demos/flow-shield/src/components/ForceShield/consts/index.ts @@ -1,54 +1,54 @@ -import { Preset } from "../../overlay/OverlayButtons"; +import { Preset } from '../../overlay/OverlayButtons' -export const MAX_HITS = 6; +export const MAX_HITS = 6 export const SHIELD_PRESETS: Record> = { default: { - color: "#26aeff", - opacity: 0.76, - showHex: true, - hexScale: 3.0, - hexOpacity: 0.13, - edgeWidth: 0.06, - fresnelPower: 1.8, - fresnelStrength: 1.75, - flashSpeed: 0.6, - flashIntensity: 0.11, - noiseScale: 1.3, - noiseEdgeColor: "#26aeff", - noiseEdgeWidth: 0.02, + color: '#26aeff', + opacity: 0.76, + showHex: true, + hexScale: 3.0, + hexOpacity: 0.13, + edgeWidth: 0.06, + fresnelPower: 1.8, + fresnelStrength: 1.75, + flashSpeed: 0.6, + flashIntensity: 0.11, + noiseScale: 1.3, + noiseEdgeColor: '#26aeff', + noiseEdgeWidth: 0.02, noiseEdgeIntensity: 10.0, noiseEdgeSmoothness: 0.5, - flowScale: 2.4, - flowSpeed: 1.13, - flowIntensity: 4, - hitRingSpeed: 1.75, - hitRingWidth: 0.12, - hitMaxRadius: 0.85, - fadeStart: -1, + flowScale: 2.4, + flowSpeed: 1.13, + flowIntensity: 4, + hitRingSpeed: 1.75, + hitRingWidth: 0.12, + hitMaxRadius: 0.85, + fadeStart: -1 }, droideka: { - hexScale: 3, - hexOpacity: 0.27, + hexScale: 3, + hexOpacity: 0.27, showHex: false, - edgeWidth: 0.2, + edgeWidth: 0.2, fresnelPower: 1.8, - fadeStart:1, + fadeStart: 1, fresnelStrength: 1.75, - flashSpeed: 0.6, - color:"#5992f7", - noiseEdgeColor:"#7faaf5", - noiseEdgeWidth: 0.1, - noiseEdgeIntensity: 0.6, + flashSpeed: 0.6, + color: '#5992f7', + noiseEdgeColor: '#7faaf5', + noiseEdgeWidth: 0.1, + noiseEdgeIntensity: 0.6, noiseEdgeSmoothness: 0.5, - noiseScale: 1, - opacity:0.29, + noiseScale: 1, + opacity: 0.29, flashIntensity: 0.11, - flowScale: 6.2, - flowSpeed: 1.08, + flowScale: 6.2, + flowSpeed: 1.08, flowIntensity: 4, hitRingSpeed: 0.8, hitRingWidth: 0.12, - hitMaxRadius: 2.1, - }, -}; \ No newline at end of file + hitMaxRadius: 2.1 + } +} diff --git a/demos/flow-shield/src/components/ForceShield/index.tsx b/demos/flow-shield/src/components/ForceShield/index.tsx index 1cc0d0a5..f4bf364e 100644 --- a/demos/flow-shield/src/components/ForceShield/index.tsx +++ b/demos/flow-shield/src/components/ForceShield/index.tsx @@ -1,140 +1,180 @@ -"use client"; +'use client' -import { useRef, useMemo, useEffect, useCallback } from "react"; -import { useFrame } from "@react-three/fiber"; -import type { ThreeEvent } from "@react-three/fiber"; -import * as THREE from "three"; -import type { Preset } from "../overlay/OverlayButtons"; -import { MAX_HITS, SHIELD_PRESETS } from "./consts"; -import { useShieldControls } from "./useShieldControls"; -import { createShieldMaterial } from "./shaderMaterial"; +import { useRef, useMemo, useEffect, useCallback } from 'react' +import { useFrame } from '@react-three/fiber' +import type { ThreeEvent } from '@react-three/fiber' +import * as THREE from 'three' +import type { Preset } from '../overlay/OverlayButtons' +import { MAX_HITS, SHIELD_PRESETS } from './consts' +import { useShieldControls } from './useShieldControls' +import { createShieldMaterial } from './shaderMaterial' interface ShieldProps { - isActive?: boolean; - posYOverride?: number; - preset?: Preset; + isActive?: boolean + posYOverride?: number + preset?: Preset } function Shield({ isActive = false, posYOverride, preset }: ShieldProps) { - const materialRef = useRef(null!); - const groupRef = useRef(null!); - const revealRef = useRef(1); - const timeRef = useRef(0); - const hitIdxRef = useRef(0); - const lifeRef = useRef(1.0); - const hitDamageRef = useRef(10); - - const [controls, setShield] = useShieldControls(lifeRef); + const materialRef = useRef(null!) + const groupRef = useRef(null!) + const revealRef = useRef(1) + const timeRef = useRef(0) + const hitIdxRef = useRef(0) + const lifeRef = useRef(1.0) + const hitDamageRef = useRef(10) + + const [controls, setShield] = useShieldControls(lifeRef) const { - debugAlwaysOn, manualReveal, revealProgress, - posX, posY, posZ, scale, color, - hexScale, hexOpacity, showHex, edgeWidth, - fresnelPower, fresnelStrength, opacity, fadeStart, revealSpeed, - flashSpeed, flashIntensity, - noiseScale, noiseEdgeColor, noiseEdgeWidth, noiseEdgeIntensity, noiseEdgeSmoothness, - flowScale, flowSpeed, flowIntensity, - hitRingSpeed, hitRingWidth, hitDuration, hitIntensity, hitImpactRadius, hitMaxRadius, - hitDamage, - } = controls; - - const visible = isActive || debugAlwaysOn; + debugAlwaysOn, + manualReveal, + revealProgress, + posX, + posY, + posZ, + scale, + color, + hexScale, + hexOpacity, + showHex, + edgeWidth, + fresnelPower, + fresnelStrength, + opacity, + fadeStart, + revealSpeed, + flashSpeed, + flashIntensity, + noiseScale, + noiseEdgeColor, + noiseEdgeWidth, + noiseEdgeIntensity, + noiseEdgeSmoothness, + flowScale, + flowSpeed, + flowIntensity, + hitRingSpeed, + hitRingWidth, + hitDuration, + hitIntensity, + hitImpactRadius, + hitMaxRadius, + hitDamage + } = controls + + const visible = isActive || debugAlwaysOn // Keep hitDamageRef in sync so the click handler always sees latest value - hitDamageRef.current = hitDamage; + hitDamageRef.current = hitDamage // Apply preset values to Leva controls when preset changes useEffect(() => { - if (preset) setShield(SHIELD_PRESETS[preset]); - }, [preset]); + if (preset) setShield(SHIELD_PRESETS[preset]) + }, [preset]) // ── Shader material ─────────────────────────────────────────────────────── - const shieldMaterial = useMemo(() => createShieldMaterial(), []); + const shieldMaterial = useMemo(() => createShieldMaterial(), []) if (shieldMaterial && materialRef.current !== shieldMaterial) { - materialRef.current = shieldMaterial; + materialRef.current = shieldMaterial } // ── Sync Leva → uniforms ────────────────────────────────────────────────── useEffect(() => { - if (!materialRef.current) return; - const u = materialRef.current.uniforms; - u.uColor.value.set(color); - u.uHexScale.value = hexScale; - u.uHexOpacity.value = hexOpacity; - u.uShowHex.value = showHex ? 1.0 : 0.0; - u.uEdgeWidth.value = edgeWidth; - u.uFresnelPower.value = fresnelPower; - u.uFresnelStrength.value = fresnelStrength; - u.uOpacity.value = opacity; - u.uFadeStart.value = fadeStart; - u.uFlashSpeed.value = flashSpeed; - u.uFlashIntensity.value = flashIntensity; - u.uNoiseScale.value = noiseScale; - u.uNoiseEdgeColor.value.set(noiseEdgeColor); - u.uNoiseEdgeWidth.value = noiseEdgeWidth; - u.uNoiseEdgeIntensity.value = noiseEdgeIntensity; - u.uNoiseEdgeSmoothness.value = noiseEdgeSmoothness; - u.uFlowScale.value = flowScale; - u.uFlowSpeed.value = flowSpeed; - u.uFlowIntensity.value = flowIntensity; - u.uHitRingSpeed.value = hitRingSpeed; - u.uHitRingWidth.value = hitRingWidth; - u.uHitMaxRadius.value = hitMaxRadius; - u.uHitDuration.value = hitDuration; - u.uHitIntensity.value = hitIntensity; - u.uHitImpactRadius.value = hitImpactRadius; + if (!materialRef.current) return + const u = materialRef.current.uniforms + u.uColor.value.set(color) + u.uHexScale.value = hexScale + u.uHexOpacity.value = hexOpacity + u.uShowHex.value = showHex ? 1.0 : 0.0 + u.uEdgeWidth.value = edgeWidth + u.uFresnelPower.value = fresnelPower + u.uFresnelStrength.value = fresnelStrength + u.uOpacity.value = opacity + u.uFadeStart.value = fadeStart + u.uFlashSpeed.value = flashSpeed + u.uFlashIntensity.value = flashIntensity + u.uNoiseScale.value = noiseScale + u.uNoiseEdgeColor.value.set(noiseEdgeColor) + u.uNoiseEdgeWidth.value = noiseEdgeWidth + u.uNoiseEdgeIntensity.value = noiseEdgeIntensity + u.uNoiseEdgeSmoothness.value = noiseEdgeSmoothness + u.uFlowScale.value = flowScale + u.uFlowSpeed.value = flowSpeed + u.uFlowIntensity.value = flowIntensity + u.uHitRingSpeed.value = hitRingSpeed + u.uHitRingWidth.value = hitRingWidth + u.uHitMaxRadius.value = hitMaxRadius + u.uHitDuration.value = hitDuration + u.uHitIntensity.value = hitIntensity + u.uHitImpactRadius.value = hitImpactRadius }, [ - color, hexScale, hexOpacity, showHex, edgeWidth, - fresnelPower, fresnelStrength, opacity, fadeStart, - flashSpeed, flashIntensity, - noiseScale, noiseEdgeColor, noiseEdgeWidth, noiseEdgeIntensity, noiseEdgeSmoothness, - flowScale, flowSpeed, flowIntensity, - hitRingSpeed, hitRingWidth, hitMaxRadius, hitDuration, hitIntensity, hitImpactRadius, - ]); + color, + hexScale, + hexOpacity, + showHex, + edgeWidth, + fresnelPower, + fresnelStrength, + opacity, + fadeStart, + flashSpeed, + flashIntensity, + noiseScale, + noiseEdgeColor, + noiseEdgeWidth, + noiseEdgeIntensity, + noiseEdgeSmoothness, + flowScale, + flowSpeed, + flowIntensity, + hitRingSpeed, + hitRingWidth, + hitMaxRadius, + hitDuration, + hitIntensity, + hitImpactRadius + ]) // ── Click → spawn hit in ring buffer ───────────────────────────────────── const handleClick = useCallback((e: ThreeEvent) => { - e.stopPropagation(); - if (!materialRef.current) return; + e.stopPropagation() + if (!materialRef.current) return // e.point is world-space; worldToLocal gives object space, // matching vObjPos (position attribute) in the vertex shader. - const localPoint = e.object.worldToLocal(e.point.clone()); + const localPoint = e.object.worldToLocal(e.point.clone()) - const idx = hitIdxRef.current % MAX_HITS; - hitIdxRef.current++; + const idx = hitIdxRef.current % MAX_HITS + hitIdxRef.current++ - const u = materialRef.current.uniforms; - u.uHitPos.value[idx].copy(localPoint); - u.uHitTime.value[idx] = timeRef.current; + const u = materialRef.current.uniforms + u.uHitPos.value[idx].copy(localPoint) + u.uHitTime.value[idx] = timeRef.current - lifeRef.current = Math.max(0, lifeRef.current - hitDamageRef.current / 100); - }, []); + lifeRef.current = Math.max(0, lifeRef.current - hitDamageRef.current / 100) + }, []) // ── Per-frame ───────────────────────────────────────────────────────────── useFrame((state, delta) => { - if (!materialRef.current) return; + if (!materialRef.current) return - timeRef.current = state.clock.elapsedTime; - materialRef.current.uniforms.uTime.value = timeRef.current; - materialRef.current.uniforms.uLife.value = lifeRef.current; + timeRef.current = state.clock.elapsedTime + materialRef.current.uniforms.uTime.value = timeRef.current + materialRef.current.uniforms.uLife.value = lifeRef.current if (manualReveal) { - revealRef.current = revealProgress; + revealRef.current = revealProgress } else { - const target = visible ? 0 : 1; - revealRef.current = THREE.MathUtils.lerp( - revealRef.current, - target, - 1 - Math.exp(-revealSpeed * delta) - ); - if ( visible && revealRef.current < 0.005) revealRef.current = 0; - if (!visible && revealRef.current > 0.995) revealRef.current = 1; + const target = visible ? 0 : 1 + revealRef.current = THREE.MathUtils.lerp(revealRef.current, target, 1 - Math.exp(-revealSpeed * delta)) + if (visible && revealRef.current < 0.005) revealRef.current = 0 + if (!visible && revealRef.current > 0.995) revealRef.current = 1 } - materialRef.current.uniforms.uReveal.value = revealRef.current; - materialRef.current.visible = revealRef.current < 1; - }); + materialRef.current.uniforms.uReveal.value = revealRef.current + materialRef.current.visible = revealRef.current < 1 + }) return ( @@ -143,7 +183,7 @@ function Shield({ isActive = false, posYOverride, preset }: ShieldProps) { - ); + ) } -export default Shield; +export default Shield diff --git a/demos/flow-shield/src/components/ForceShield/shaderMaterial.ts b/demos/flow-shield/src/components/ForceShield/shaderMaterial.ts index 4c15be91..3ed1ded1 100644 --- a/demos/flow-shield/src/components/ForceShield/shaderMaterial.ts +++ b/demos/flow-shield/src/components/ForceShield/shaderMaterial.ts @@ -1,5 +1,5 @@ -import * as THREE from "three"; -import { MAX_HITS } from "./consts"; +import * as THREE from 'three' +import { MAX_HITS } from './consts' // ── Vertex shader ───────────────────────────────────────────────────────────── export const vertexShader = /* glsl */ ` @@ -14,7 +14,7 @@ export const vertexShader = /* glsl */ ` vViewDir = normalize(-viewPosition.xyz); gl_Position = projectionMatrix * viewPosition; } -`; +` // ── Fragment shader ─────────────────────────────────────────────────────────── export const fragmentShader = /* glsl */ ` @@ -237,51 +237,51 @@ export const fragmentShader = /* glsl */ ` gl_FragColor = vec4(shieldColor + edgeGlow, alpha); } -`; +` // ── Material factory ────────────────────────────────────────────────────────── export function createShieldMaterial(): THREE.ShaderMaterial { - const hitPositions = Array.from({ length: MAX_HITS }, () => new THREE.Vector3(0, 1.8, 0)); - const hitTimes = new Array(MAX_HITS).fill(-999); + const hitPositions = Array.from({ length: MAX_HITS }, () => new THREE.Vector3(0, 1.8, 0)) + const hitTimes = new Array(MAX_HITS).fill(-999) return new THREE.ShaderMaterial({ uniforms: { - uTime: { value: 0 }, - uColor: { value: new THREE.Color("#26aeff") }, - uLife: { value: 1.0 }, - uHexScale: { value: 3.0 }, - uEdgeWidth: { value: 0.06 }, - uFresnelPower: { value: 1.8 }, - uFresnelStrength: { value: 1.75 }, - uOpacity: { value: 0.76 }, - uReveal: { value: 1 }, - uFlashSpeed: { value: 0.6 }, - uFlashIntensity: { value: 0.11 }, - uNoiseScale: { value: 1.3 }, - uNoiseEdgeColor: { value: new THREE.Color("#26aeff") }, - uNoiseEdgeWidth: { value: 0.02 }, - uNoiseEdgeIntensity: { value: 10.0 }, + uTime: { value: 0 }, + uColor: { value: new THREE.Color('#26aeff') }, + uLife: { value: 1.0 }, + uHexScale: { value: 3.0 }, + uEdgeWidth: { value: 0.06 }, + uFresnelPower: { value: 1.8 }, + uFresnelStrength: { value: 1.75 }, + uOpacity: { value: 0.76 }, + uReveal: { value: 1 }, + uFlashSpeed: { value: 0.6 }, + uFlashIntensity: { value: 0.11 }, + uNoiseScale: { value: 1.3 }, + uNoiseEdgeColor: { value: new THREE.Color('#26aeff') }, + uNoiseEdgeWidth: { value: 0.02 }, + uNoiseEdgeIntensity: { value: 10.0 }, uNoiseEdgeSmoothness: { value: 0.5 }, - uHexOpacity: { value: 0.13 }, - uShowHex: { value: 1.0 }, - uFlowScale: { value: 2.4 }, - uFlowSpeed: { value: 1.13 }, - uFlowIntensity: { value: 4 }, - uHitPos: { value: hitPositions }, - uHitTime: { value: hitTimes }, - uHitRingSpeed: { value: 1.75 }, - uHitRingWidth: { value: 0.12 }, - uHitMaxRadius: { value: 0.85 }, - uHitDuration: { value: 1.8 }, - uHitIntensity: { value: 4.1 }, - uHitImpactRadius: { value: 0.30 }, - uFadeStart: { value: 0.0 }, + uHexOpacity: { value: 0.13 }, + uShowHex: { value: 1.0 }, + uFlowScale: { value: 2.4 }, + uFlowSpeed: { value: 1.13 }, + uFlowIntensity: { value: 4 }, + uHitPos: { value: hitPositions }, + uHitTime: { value: hitTimes }, + uHitRingSpeed: { value: 1.75 }, + uHitRingWidth: { value: 0.12 }, + uHitMaxRadius: { value: 0.85 }, + uHitDuration: { value: 1.8 }, + uHitIntensity: { value: 4.1 }, + uHitImpactRadius: { value: 0.3 }, + uFadeStart: { value: 0.0 } }, vertexShader, fragmentShader, transparent: true, - depthWrite: false, - side: THREE.FrontSide, - blending: THREE.AdditiveBlending, - }); + depthWrite: false, + side: THREE.FrontSide, + blending: THREE.AdditiveBlending + }) } diff --git a/demos/flow-shield/src/components/ForceShield/useShieldControls.ts b/demos/flow-shield/src/components/ForceShield/useShieldControls.ts index bf40e647..dc37a4e6 100644 --- a/demos/flow-shield/src/components/ForceShield/useShieldControls.ts +++ b/demos/flow-shield/src/components/ForceShield/useShieldControls.ts @@ -1,63 +1,65 @@ -"use client"; +'use client' -import type { MutableRefObject } from "react"; -import { useControls, folder, button } from "leva"; +import type { MutableRefObject } from 'react' +import { useControls, folder, button } from 'leva' export function useShieldControls(lifeRef: MutableRefObject) { return useControls( - "Force Shield", + 'Force Shield', () => ({ - debugAlwaysOn: { value: true, label: "Show Shield" }, - manualReveal: { value: false, label: "Manual Reveal" }, - revealProgress: { value: 0.0, min: 0, max: 1, step: 0.01, label: "↳ Reveal Progress" }, + debugAlwaysOn: { value: true, label: 'Show Shield' }, + manualReveal: { value: false, label: 'Manual Reveal' }, + revealProgress: { value: 0.0, min: 0, max: 1, step: 0.01, label: '↳ Reveal Progress' }, Transform: folder( { - posX: { value: 0, min: -10, max: 10, step: 0.1, label: "Pos X" }, - posY: { value: 2, min: -10, max: 10, step: 0.1, label: "Pos Y" }, - posZ: { value: 0.2, min: -10, max: 10, step: 0.1, label: "Pos Z" }, - scale: { value: 1, min: 0.1, max: 5, step: 0.05, label: "Scale" }, + posX: { value: 0, min: -10, max: 10, step: 0.1, label: 'Pos X' }, + posY: { value: 2, min: -10, max: 10, step: 0.1, label: 'Pos Y' }, + posZ: { value: 0.2, min: -10, max: 10, step: 0.1, label: 'Pos Z' }, + scale: { value: 1, min: 0.1, max: 5, step: 0.05, label: 'Scale' } }, { collapsed: true } ), - color: "#26aeff", - hexScale: { value: 3.0, min: 1, max: 20, step: 0.5 }, - hexOpacity: { value: 0.13, min: 0, max: 1, step: 0.01, label: "Hex Opacity" }, - showHex: { value: true, label: "Show Hex" }, - edgeWidth: { value: 0.06, min: 0.01, max: 0.2, step: 0.005 }, - fresnelPower: { value: 1.8, min: 0.5, max: 5, step: 0.1, label: "Fresnel Power" }, - fresnelStrength: { value: 1.75, min: 0, max: 3, step: 0.05, label: "Fresnel Strength" }, - opacity: { value: 0.76, min: 0, max: 1, step: 0.01 }, - fadeStart: { value: 0.0, min: -1, max: 1, step: 0.01, label: "Fade Start" }, - revealSpeed: { value: 3.5, min: 0.5, max: 5, step: 0.1 }, - flashSpeed: { value: 0.6, min: 0.5, max: 8, step: 0.1 }, - flashIntensity: { value: 0.11, min: 0, max: 1, step: 0.01 }, - noiseScale: { value: 1.3, min: 0.5, max: 10, step: 0.1 }, - noiseEdgeColor: { value: "#26aeff", label: "Noise Edge Color" }, - noiseEdgeWidth: { value: 0.02, min: 0.01, max: 0.5, step: 0.01, label: "Noise Edge Width" }, - noiseEdgeIntensity: { value: 10.0, min: 0, max: 10, step: 0.1, label: "Noise Edge Intensity" }, - noiseEdgeSmoothness: { value: 0.5, min: 0, max: 1, step: 0.01, label: "Noise Edge Smoothness" }, - "Flow Noise": folder( + color: '#26aeff', + hexScale: { value: 3.0, min: 1, max: 20, step: 0.5 }, + hexOpacity: { value: 0.13, min: 0, max: 1, step: 0.01, label: 'Hex Opacity' }, + showHex: { value: true, label: 'Show Hex' }, + edgeWidth: { value: 0.06, min: 0.01, max: 0.2, step: 0.005 }, + fresnelPower: { value: 1.8, min: 0.5, max: 5, step: 0.1, label: 'Fresnel Power' }, + fresnelStrength: { value: 1.75, min: 0, max: 3, step: 0.05, label: 'Fresnel Strength' }, + opacity: { value: 0.76, min: 0, max: 1, step: 0.01 }, + fadeStart: { value: 0.0, min: -1, max: 1, step: 0.01, label: 'Fade Start' }, + revealSpeed: { value: 3.5, min: 0.5, max: 5, step: 0.1 }, + flashSpeed: { value: 0.6, min: 0.5, max: 8, step: 0.1 }, + flashIntensity: { value: 0.11, min: 0, max: 1, step: 0.01 }, + noiseScale: { value: 1.3, min: 0.5, max: 10, step: 0.1 }, + noiseEdgeColor: { value: '#26aeff', label: 'Noise Edge Color' }, + noiseEdgeWidth: { value: 0.02, min: 0.01, max: 0.5, step: 0.01, label: 'Noise Edge Width' }, + noiseEdgeIntensity: { value: 10.0, min: 0, max: 10, step: 0.1, label: 'Noise Edge Intensity' }, + noiseEdgeSmoothness: { value: 0.5, min: 0, max: 1, step: 0.01, label: 'Noise Edge Smoothness' }, + 'Flow Noise': folder( { - flowScale: { value: 2.4, min: 0.1, max: 8, step: 0.1, label: "Scale" }, - flowSpeed: { value: 1.13, min: 0, max: 2, step: 0.01, label: "Speed" }, - flowIntensity: { value: 4, min: 0, max: 4, step: 0.05, label: "Intensity" }, + flowScale: { value: 2.4, min: 0.1, max: 8, step: 0.1, label: 'Scale' }, + flowSpeed: { value: 1.13, min: 0, max: 2, step: 0.01, label: 'Speed' }, + flowIntensity: { value: 4, min: 0, max: 4, step: 0.05, label: 'Intensity' } }, { collapsed: false } ), - "Hit Effect": folder( + 'Hit Effect': folder( { - hitRingSpeed: { value: 1.75, min: 0.1, max: 6, step: 0.05, label: "Ring Speed" }, - hitRingWidth: { value: 0.12, min: 0.01, max: 0.5, step: 0.01, label: "Ring Width" }, - hitMaxRadius: { value: 0.85, min: 0.2, max: 3.14, step: 0.05, label: "Max Radius" }, - hitDuration: { value: 1.8, min: 0.5, max: 6, step: 0.1, label: "Duration" }, - hitIntensity: { value: 4.1, min: 0, max: 8, step: 0.1, label: "Intensity" }, - hitImpactRadius: { value: 0.30, min: 0.1, max: 1.5, step: 0.05, label: "Impact Radius" }, - hitDamage: { value: 5, min: 1, max: 100, step: 1, label: "Damage %" }, - "Reset Life": button(() => { lifeRef.current = 1.0; }), + hitRingSpeed: { value: 1.75, min: 0.1, max: 6, step: 0.05, label: 'Ring Speed' }, + hitRingWidth: { value: 0.12, min: 0.01, max: 0.5, step: 0.01, label: 'Ring Width' }, + hitMaxRadius: { value: 0.85, min: 0.2, max: 3.14, step: 0.05, label: 'Max Radius' }, + hitDuration: { value: 1.8, min: 0.5, max: 6, step: 0.1, label: 'Duration' }, + hitIntensity: { value: 4.1, min: 0, max: 8, step: 0.1, label: 'Intensity' }, + hitImpactRadius: { value: 0.3, min: 0.1, max: 1.5, step: 0.05, label: 'Impact Radius' }, + hitDamage: { value: 5, min: 1, max: 100, step: 1, label: 'Damage %' }, + 'Reset Life': button(() => { + lifeRef.current = 1.0 + }) }, { collapsed: false } - ), + ) }), { collapsed: true } - ); + ) } diff --git a/demos/flow-shield/src/components/overlay/LoadingOverlay.tsx b/demos/flow-shield/src/components/overlay/LoadingOverlay.tsx index 19aca53d..8f63e6dd 100644 --- a/demos/flow-shield/src/components/overlay/LoadingOverlay.tsx +++ b/demos/flow-shield/src/components/overlay/LoadingOverlay.tsx @@ -1,13 +1,13 @@ -"use client"; +'use client' -import styles from "./LoadingOverlay.module.css"; +import styles from './LoadingOverlay.module.css' interface LoadingOverlayProps { - visible: boolean; + visible: boolean } export default function LoadingOverlay({ visible }: LoadingOverlayProps) { - if (!visible) return null; + if (!visible) return null return (
@@ -16,5 +16,5 @@ export default function LoadingOverlay({ visible }: LoadingOverlayProps) { Loading Model...
- ); + ) } diff --git a/demos/flow-shield/src/components/overlay/OverlayButtons.tsx b/demos/flow-shield/src/components/overlay/OverlayButtons.tsx index 293757d3..2bc34d97 100644 --- a/demos/flow-shield/src/components/overlay/OverlayButtons.tsx +++ b/demos/flow-shield/src/components/overlay/OverlayButtons.tsx @@ -1,21 +1,21 @@ -"use client"; +'use client' -import { useRef } from "react"; -import { COLORS } from "../theme/theme"; -import styles from "./OverlayButtons.module.css"; +import { useRef } from 'react' +import { COLORS } from '../theme/theme' +import styles from './OverlayButtons.module.css' -export type Preset = "default" | "droideka"; +export type Preset = 'default' | 'droideka' interface OverlayButtonsProps { - showGrid: boolean; - onToggleGrid: () => void; - hideLeva: boolean; - onToggleLeva: () => void; - hasGlb: boolean; - onLoadGlb: (file: File) => void; - onClearGlb: () => void; - preset: Preset; - onSetPreset: (p: Preset) => void; + showGrid: boolean + onToggleGrid: () => void + hideLeva: boolean + onToggleLeva: () => void + hasGlb: boolean + onLoadGlb: (file: File) => void + onClearGlb: () => void + preset: Preset + onSetPreset: (p: Preset) => void } export default function OverlayButtons({ @@ -27,41 +27,41 @@ export default function OverlayButtons({ onLoadGlb, onClearGlb, preset, - onSetPreset, + onSetPreset }: OverlayButtonsProps) { - const fileInputRef = useRef(null); + const fileInputRef = useRef(null) const handleFileChange = (e: React.ChangeEvent) => { - const file = e.target.files?.[0]; - if (file) onLoadGlb(file); - e.target.value = ""; - }; + const file = e.target.files?.[0] + if (file) onLoadGlb(file) + e.target.value = '' + } return (
{/* Preset selector */}
- ); + ) } diff --git a/demos/flow-shield/src/components/overlay/OverlayHeader.tsx b/demos/flow-shield/src/components/overlay/OverlayHeader.tsx index 433305cc..70efc33b 100644 --- a/demos/flow-shield/src/components/overlay/OverlayHeader.tsx +++ b/demos/flow-shield/src/components/overlay/OverlayHeader.tsx @@ -1,24 +1,24 @@ -"use client"; +'use client' export default function OverlayHeader() { - const now = new Date(); - const dateStr = now.toLocaleDateString("en-US", { - year: "numeric", - month: "short", - day: "2-digit", - }); + const now = new Date() + const dateStr = now.toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: '2-digit' + }) return ( -
+
{/* Classification label */}
SHIELD VFX WITH HIT DETECTION @@ -28,21 +28,21 @@ export default function OverlayHeader() {
{/* Title */}

SHIELD VFX @@ -51,13 +51,13 @@ export default function OverlayHeader() { {/* Designation subtitle */}
3D PLAYGROUND ENVIRONMENT @@ -66,16 +66,16 @@ export default function OverlayHeader() { {/* Rev / Date metadata */}
V 1.01 — {dateStr.toUpperCase()} — R3F / DREI / LEVA
- ); + ) } diff --git a/demos/flow-shield/src/components/overlay/UIOverlay.tsx b/demos/flow-shield/src/components/overlay/UIOverlay.tsx index b1ba2bdd..a9493f5b 100644 --- a/demos/flow-shield/src/components/overlay/UIOverlay.tsx +++ b/demos/flow-shield/src/components/overlay/UIOverlay.tsx @@ -1,18 +1,18 @@ -"use client"; +'use client' -import OverlayHeader from "./OverlayHeader"; +import OverlayHeader from './OverlayHeader' export default function UIOverlay() { return (
- ); + ) } diff --git a/demos/flow-shield/src/components/playground/DemoSphere.tsx b/demos/flow-shield/src/components/playground/DemoSphere.tsx index b7f97260..8f856dd8 100644 --- a/demos/flow-shield/src/components/playground/DemoSphere.tsx +++ b/demos/flow-shield/src/components/playground/DemoSphere.tsx @@ -1,35 +1,26 @@ -"use client"; +'use client' -import { useRef } from "react"; -import { useFrame } from "@react-three/fiber"; -import type { Mesh } from "three"; -import type { SceneMode } from "./SceneContent"; +import { useRef } from 'react' +import { useFrame } from '@react-three/fiber' +import type { Mesh } from 'three' +import type { SceneMode } from './SceneContent' export default function DemoSphere({ mode }: { mode: SceneMode }) { - const meshRef = useRef(null!); - const wireframe = mode === "Frame"; + const meshRef = useRef(null!) + const wireframe = mode === 'Frame' useFrame(() => { - meshRef.current.position.y = 1 + Math.sin(Date.now() * 0.001) * 0.2; - }); + meshRef.current.position.y = 1 + Math.sin(Date.now() * 0.001) * 0.2 + }) return ( {wireframe ? ( - - + ) : ( - + )} - - ); + ) } diff --git a/demos/flow-shield/src/components/playground/Droideka.tsx b/demos/flow-shield/src/components/playground/Droideka.tsx index f82e9a37..96862517 100644 --- a/demos/flow-shield/src/components/playground/Droideka.tsx +++ b/demos/flow-shield/src/components/playground/Droideka.tsx @@ -14,44 +14,20 @@ const DROIDEKA_URL = `${import.meta.env.BASE_URL.replace(/\/?$/, '/')}droideka.g export function Droideka() { const { nodes, materials } = useGLTF(DROIDEKA_URL) - + return ( - - + + - - + + - + - + - + - + - + - + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - - + + - - + + - + - + - - + + - - + + - - + + - - + + - + - - + + @@ -736,4 +403,4 @@ export function Droideka() { ) } -useGLTF.preload(DROIDEKA_URL) \ No newline at end of file +useGLTF.preload(DROIDEKA_URL) diff --git a/demos/flow-shield/src/components/playground/GlbModel.tsx b/demos/flow-shield/src/components/playground/GlbModel.tsx index 672bc7e5..a59827fa 100644 --- a/demos/flow-shield/src/components/playground/GlbModel.tsx +++ b/demos/flow-shield/src/components/playground/GlbModel.tsx @@ -1,92 +1,87 @@ -"use client"; +'use client' -import { useMemo, useEffect } from "react"; -import { useGLTF } from "@react-three/drei"; -import { useControls, folder } from "leva"; -import { Box3, Vector3, MathUtils } from "three"; -import type { Mesh, MeshStandardMaterial } from "three"; -import type { SceneMode } from "./SceneContent"; +import { useMemo, useEffect } from 'react' +import { useGLTF } from '@react-three/drei' +import { useControls, folder } from 'leva' +import { Box3, Vector3, MathUtils } from 'three' +import type { Mesh, MeshStandardMaterial } from 'three' +import type { SceneMode } from './SceneContent' interface GlbModelProps { - url: string; - onLoaded?: () => void; - mode: SceneMode; + url: string + onLoaded?: () => void + mode: SceneMode } export default function GlbModel({ url, onLoaded, mode }: GlbModelProps) { - const { scene } = useGLTF(url); - const wireframe = mode === "Frame"; + const { scene } = useGLTF(url) + const wireframe = mode === 'Frame' useEffect(() => { - onLoaded?.(); - }, [scene, onLoaded]); + onLoaded?.() + }, [scene, onLoaded]) const autoScale = useMemo(() => { - const box = new Box3().setFromObject(scene as any); - const center = new Vector3(); - box.getCenter(center); - scene.position.sub(center); + const box = new Box3().setFromObject(scene as any) + const center = new Vector3() + box.getCenter(center) + scene.position.sub(center) - const size = new Vector3(); - box.getSize(size); - const maxDim = Math.max(size.x, size.y, size.z); - const s = maxDim > 0 ? 3 / maxDim : 1; + const size = new Vector3() + box.getSize(size) + const maxDim = Math.max(size.x, size.y, size.z) + const s = maxDim > 0 ? 3 / maxDim : 1 - scene.scale.setScalar(s); + scene.scale.setScalar(s) scene.traverse((child) => { - if ("isMesh" in child && child.isMesh) { - (child as any).castShadow = true; - (child as any).receiveShadow = true; + if ('isMesh' in child && child.isMesh) { + ;(child as any).castShadow = true + ;(child as any).receiveShadow = true } - }); + }) - return s; - }, [scene]); + return s + }, [scene]) useEffect(() => { scene.traverse((child) => { - if ("isMesh" in child && child.isMesh) { - const mat = (child as any).material; + if ('isMesh' in child && child.isMesh) { + const mat = (child as any).material if (Array.isArray(mat)) { - mat.forEach((m) => { (m as any as MeshStandardMaterial).wireframe = wireframe; }); + mat.forEach((m) => { + ;(m as any as MeshStandardMaterial).wireframe = wireframe + }) } else { - (mat as any as MeshStandardMaterial).wireframe = wireframe; + ;(mat as any as MeshStandardMaterial).wireframe = wireframe } } - }); - }, [scene, wireframe]); + }) + }, [scene, wireframe]) - const { scale, posX, posY, posZ, rotX, rotY, rotZ } = useControls("Model", { + const { scale, posX, posY, posZ, rotX, rotY, rotZ } = useControls('Model', { Transform: folder( { - scale: { value: autoScale, min: 0.01, max: 20, step: 0.01, label: "Scale" }, - posX: { value: 0, min: -10, max: 10, step: 0.1, label: "Pos X" }, - posY: { value: 1, min: -10, max: 10, step: 0.1, label: "Pos Y" }, - posZ: { value: 0, min: -10, max: 10, step: 0.1, label: "Pos Z" }, + scale: { value: autoScale, min: 0.01, max: 20, step: 0.01, label: 'Scale' }, + posX: { value: 0, min: -10, max: 10, step: 0.1, label: 'Pos X' }, + posY: { value: 1, min: -10, max: 10, step: 0.1, label: 'Pos Y' }, + posZ: { value: 0, min: -10, max: 10, step: 0.1, label: 'Pos Z' } }, { collapsed: false } ), Rotation: folder( { - rotX: { value: 0, min: -180, max: 180, step: 1, label: "Rot X" }, - rotY: { value: 0, min: -180, max: 180, step: 1, label: "Rot Y" }, - rotZ: { value: 0, min: -180, max: 180, step: 1, label: "Rot Z" }, + rotX: { value: 0, min: -180, max: 180, step: 1, label: 'Rot X' }, + rotY: { value: 0, min: -180, max: 180, step: 1, label: 'Rot Y' }, + rotZ: { value: 0, min: -180, max: 180, step: 1, label: 'Rot Z' } }, { collapsed: true } - ), - }); + ) + }) return ( - + - ); + ) } diff --git a/demos/flow-shield/src/components/playground/GridFloor.tsx b/demos/flow-shield/src/components/playground/GridFloor.tsx index a1700d2d..2307a72e 100644 --- a/demos/flow-shield/src/components/playground/GridFloor.tsx +++ b/demos/flow-shield/src/components/playground/GridFloor.tsx @@ -1,69 +1,78 @@ -"use client"; +'use client' -import { useMemo } from "react"; -import * as THREE from "three"; -import { Grid, MeshReflectorMaterial } from "@react-three/drei"; -import { useControls, folder } from "leva"; -import { COLORS } from "../theme/theme"; -import type { SceneMode } from "./SceneContent"; +import { useMemo } from 'react' +import * as THREE from 'three' +import { Grid, MeshReflectorMaterial } from '@react-three/drei' +import { useControls, folder } from 'leva' +import { COLORS } from '../theme/theme' +import type { SceneMode } from './SceneContent' function useRadialAlphaMap(size: number, innerStop: number, outerStop: number) { return useMemo(() => { - const canvas = document.createElement("canvas"); - canvas.width = size; - canvas.height = size; - const ctx = canvas.getContext("2d")!; + const canvas = document.createElement('canvas') + canvas.width = size + canvas.height = size + const ctx = canvas.getContext('2d')! - const half = size / 2; - const gradient = ctx.createRadialGradient(half, half, 0, half, half, half); - gradient.addColorStop(0, "#ffffff"); - gradient.addColorStop(Math.min(innerStop, 0.99), "#ffffff"); - gradient.addColorStop(Math.min(outerStop, 1), "#000000"); - gradient.addColorStop(1, "#000000"); + const half = size / 2 + const gradient = ctx.createRadialGradient(half, half, 0, half, half, half) + gradient.addColorStop(0, '#ffffff') + gradient.addColorStop(Math.min(innerStop, 0.99), '#ffffff') + gradient.addColorStop(Math.min(outerStop, 1), '#000000') + gradient.addColorStop(1, '#000000') - ctx.fillStyle = gradient; - ctx.fillRect(0, 0, size, size); + ctx.fillStyle = gradient + ctx.fillRect(0, 0, size, size) - const texture = new THREE.CanvasTexture(canvas); - texture.needsUpdate = true; - return texture; - }, [size, innerStop, outerStop]); + const texture = new THREE.CanvasTexture(canvas) + texture.needsUpdate = true + return texture + }, [size, innerStop, outerStop]) } export default function GridFloor({ mode }: { mode: SceneMode }) { const { - cellSize, cellThickness, cellColor, - sectionSize, sectionThickness, sectionColor, - fadeDistance, fadeStrength, infiniteGrid, followCamera, - resolution, fadeInner, fadeOuter, - } = useControls("Scene", { + cellSize, + cellThickness, + cellColor, + sectionSize, + sectionThickness, + sectionColor, + fadeDistance, + fadeStrength, + infiniteGrid, + followCamera, + resolution, + fadeInner, + fadeOuter + } = useControls('Scene', { Grid: folder( { - cellSize: { value: 3.0, min: 0.1, max: 5, step: 0.1, label: "Cell Size" }, - cellThickness: { value: 1.4, min: 0.1, max: 3, step: 0.1, label: "Cell Thickness" }, - cellColor: { value: COLORS.gridCell, label: "Cell Color" }, - sectionSize: { value: 1, min: 1, max: 10, step: 1, label: "Section Size" }, - sectionThickness: { value: 0.5, min: 0.1, max: 5, step: 0.1, label: "Section Thickness" }, - sectionColor: { value: COLORS.gridSection, label: "Section Color" }, - fadeDistance: { value: 67, min: 5, max: 100, step: 1, label: "Fade Distance" }, - fadeStrength: { value: 3.2, min: 0, max: 5, step: 0.1, label: "Fade Strength" }, - infiniteGrid: { value: true, label: "Infinite Grid" }, - followCamera: { value: false, label: "Follow Camera" }, + cellSize: { value: 3.0, min: 0.1, max: 5, step: 0.1, label: 'Cell Size' }, + cellThickness: { value: 1.4, min: 0.1, max: 3, step: 0.1, label: 'Cell Thickness' }, + cellColor: { value: COLORS.gridCell, label: 'Cell Color' }, + sectionSize: { value: 1, min: 1, max: 10, step: 1, label: 'Section Size' }, + sectionThickness: { value: 0.5, min: 0.1, max: 5, step: 0.1, label: 'Section Thickness' }, + sectionColor: { value: COLORS.gridSection, label: 'Section Color' }, + fadeDistance: { value: 67, min: 5, max: 100, step: 1, label: 'Fade Distance' }, + fadeStrength: { value: 3.2, min: 0, max: 5, step: 0.1, label: 'Fade Strength' }, + infiniteGrid: { value: true, label: 'Infinite Grid' }, + followCamera: { value: false, label: 'Follow Camera' } }, { collapsed: true } ), Floor: folder( { - resolution: { value: 512, options: [256, 512, 1024, 2048], label: "Resolution" }, - fadeInner: { value: 0.16, min: 0, max: 1, step: 0.01, label: "Fade Inner" }, - fadeOuter: { value: 0.66, min: 0, max: 1, step: 0.01, label: "Fade Outer" }, + resolution: { value: 512, options: [256, 512, 1024, 2048], label: 'Resolution' }, + fadeInner: { value: 0.16, min: 0, max: 1, step: 0.01, label: 'Fade Inner' }, + fadeOuter: { value: 0.66, min: 0, max: 1, step: 0.01, label: 'Fade Outer' } }, { collapsed: true } - ), - }); + ) + }) - const alphaMap = useRadialAlphaMap(512, fadeInner, fadeOuter); - const showFloor = mode === "Background"; + const alphaMap = useRadialAlphaMap(512, fadeInner, fadeOuter) + const showFloor = mode === 'Background' return ( @@ -103,5 +112,5 @@ export default function GridFloor({ mode }: { mode: SceneMode }) { followCamera={followCamera} /> - ); + ) } diff --git a/demos/flow-shield/src/components/playground/PlaygroundCanvas.tsx b/demos/flow-shield/src/components/playground/PlaygroundCanvas.tsx index c91acdf0..ac320d34 100644 --- a/demos/flow-shield/src/components/playground/PlaygroundCanvas.tsx +++ b/demos/flow-shield/src/components/playground/PlaygroundCanvas.tsx @@ -1,65 +1,62 @@ -"use client"; +'use client' -import { useState, useCallback, useRef } from "react"; -import { Canvas } from "@react-three/fiber"; -import { Leva, useControls } from "leva"; -import { LEVA_THEME } from "../theme/theme"; -import SceneContent from "./SceneContent"; -import type { SceneMode } from "./SceneContent"; -import UIOverlay from "../overlay/UIOverlay"; -import OverlayButtons, { type Preset } from "../overlay/OverlayButtons"; -import LoadingOverlay from "../overlay/LoadingOverlay"; +import { useState, useCallback, useRef } from 'react' +import { Canvas } from '@react-three/fiber' +import { Leva, useControls } from 'leva' +import { LEVA_THEME } from '../theme/theme' +import SceneContent from './SceneContent' +import type { SceneMode } from './SceneContent' +import UIOverlay from '../overlay/UIOverlay' +import OverlayButtons, { type Preset } from '../overlay/OverlayButtons' +import LoadingOverlay from '../overlay/LoadingOverlay' export default function PlaygroundCanvas() { - const [showGrid, setShowGrid] = useState(true); - const [hideLeva, setHideLeva] = useState(false); - const [glbUrl, setGlbUrl] = useState(null); - const [preset, setPreset] = useState("default"); - const [isLoadingModel, setIsLoadingModel] = useState(false); - const glbUrlRef = useRef(null); + const [showGrid, setShowGrid] = useState(true) + const [hideLeva, setHideLeva] = useState(false) + const [glbUrl, setGlbUrl] = useState(null) + const [preset, setPreset] = useState('default') + const [isLoadingModel, setIsLoadingModel] = useState(false) + const glbUrlRef = useRef(null) const handleLoadGlb = useCallback((file: File) => { - if (glbUrlRef.current) URL.revokeObjectURL(glbUrlRef.current); - const url = URL.createObjectURL(file); - glbUrlRef.current = url; - setIsLoadingModel(true); - setGlbUrl(url); - }, []); + if (glbUrlRef.current) URL.revokeObjectURL(glbUrlRef.current) + const url = URL.createObjectURL(file) + glbUrlRef.current = url + setIsLoadingModel(true) + setGlbUrl(url) + }, []) const handleModelLoaded = useCallback(() => { - setIsLoadingModel(false); - }, []); + setIsLoadingModel(false) + }, []) const handleClearGlb = useCallback(() => { - if (glbUrlRef.current) URL.revokeObjectURL(glbUrlRef.current); - glbUrlRef.current = null; - setGlbUrl(null); - }, []); + if (glbUrlRef.current) URL.revokeObjectURL(glbUrlRef.current) + glbUrlRef.current = null + setGlbUrl(null) + }, []) - const { mode } = useControls("Scene", { - mode: { - value: "Background" as SceneMode, - options: ["Background", "Frame"] as SceneMode[], - label: "Mode", + const { mode } = useControls( + 'Scene', + { + mode: { + value: 'Background' as SceneMode, + options: ['Background', 'Frame'] as SceneMode[], + label: 'Mode' + } }, - }, { collapsed: true }); + { collapsed: true } + ) return ( <> -