diff --git a/.yarn/cache/@dnd-kit-accessibility-npm-3.0.1-78bf91a820-0afc2c0fce.zip b/.yarn/cache/@dnd-kit-accessibility-npm-3.0.1-78bf91a820-0afc2c0fce.zip
new file mode 100644
index 0000000..363191a
Binary files /dev/null and b/.yarn/cache/@dnd-kit-accessibility-npm-3.0.1-78bf91a820-0afc2c0fce.zip differ
diff --git a/.yarn/cache/@dnd-kit-core-npm-6.0.8-66053fe203-abe48ff739.zip b/.yarn/cache/@dnd-kit-core-npm-6.0.8-66053fe203-abe48ff739.zip
new file mode 100644
index 0000000..00f614e
Binary files /dev/null and b/.yarn/cache/@dnd-kit-core-npm-6.0.8-66053fe203-abe48ff739.zip differ
diff --git a/.yarn/cache/@dnd-kit-sortable-npm-7.0.2-8871eace61-4ce705aceb.zip b/.yarn/cache/@dnd-kit-sortable-npm-7.0.2-8871eace61-4ce705aceb.zip
new file mode 100644
index 0000000..5ba559a
Binary files /dev/null and b/.yarn/cache/@dnd-kit-sortable-npm-7.0.2-8871eace61-4ce705aceb.zip differ
diff --git a/.yarn/cache/@dnd-kit-utilities-npm-3.2.1-e4b7ea044f-038fd5cc13.zip b/.yarn/cache/@dnd-kit-utilities-npm-3.2.1-e4b7ea044f-038fd5cc13.zip
new file mode 100644
index 0000000..b862f3e
Binary files /dev/null and b/.yarn/cache/@dnd-kit-utilities-npm-3.2.1-e4b7ea044f-038fd5cc13.zip differ
diff --git a/.yarn/cache/@radix-ui-popper-npm-0.1.0-d7a438eda7-f362a9fb8e.zip b/.yarn/cache/@radix-ui-popper-npm-0.1.0-d7a438eda7-f362a9fb8e.zip
deleted file mode 100644
index 5fd286e..0000000
Binary files a/.yarn/cache/@radix-ui-popper-npm-0.1.0-d7a438eda7-f362a9fb8e.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-primitive-npm-0.1.0-0a49584f8e-5f721bcfeb.zip b/.yarn/cache/@radix-ui-primitive-npm-0.1.0-0a49584f8e-5f721bcfeb.zip
deleted file mode 100644
index 2a95810..0000000
Binary files a/.yarn/cache/@radix-ui-primitive-npm-0.1.0-0a49584f8e-5f721bcfeb.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-arrow-npm-0.1.3-081c30972a-d8dcb85845.zip b/.yarn/cache/@radix-ui-react-arrow-npm-0.1.3-081c30972a-d8dcb85845.zip
deleted file mode 100644
index 35e326f..0000000
Binary files a/.yarn/cache/@radix-ui-react-arrow-npm-0.1.3-081c30972a-d8dcb85845.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-compose-refs-npm-0.1.0-a16c93a4d0-d1455577b2.zip b/.yarn/cache/@radix-ui-react-compose-refs-npm-0.1.0-a16c93a4d0-d1455577b2.zip
deleted file mode 100644
index 08f7246..0000000
Binary files a/.yarn/cache/@radix-ui-react-compose-refs-npm-0.1.0-a16c93a4d0-d1455577b2.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-context-npm-0.1.1-f6f528ee12-85ed35b6e3.zip b/.yarn/cache/@radix-ui-react-context-npm-0.1.1-f6f528ee12-85ed35b6e3.zip
deleted file mode 100644
index 4a38b10..0000000
Binary files a/.yarn/cache/@radix-ui-react-context-npm-0.1.1-f6f528ee12-85ed35b6e3.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-id-npm-0.1.4-b4fa5bb4b3-09ed338c10.zip b/.yarn/cache/@radix-ui-react-id-npm-0.1.4-b4fa5bb4b3-09ed338c10.zip
deleted file mode 100644
index 6e20c37..0000000
Binary files a/.yarn/cache/@radix-ui-react-id-npm-0.1.4-b4fa5bb4b3-09ed338c10.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-popper-npm-0.1.3-f38ea29c70-f057a1c583.zip b/.yarn/cache/@radix-ui-react-popper-npm-0.1.3-f38ea29c70-f057a1c583.zip
deleted file mode 100644
index 380e82d..0000000
Binary files a/.yarn/cache/@radix-ui-react-popper-npm-0.1.3-f38ea29c70-f057a1c583.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-portal-npm-0.1.3-4c76506063-56836fdff1.zip b/.yarn/cache/@radix-ui-react-portal-npm-0.1.3-4c76506063-56836fdff1.zip
deleted file mode 100644
index cc6dff3..0000000
Binary files a/.yarn/cache/@radix-ui-react-portal-npm-0.1.3-4c76506063-56836fdff1.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-portal-npm-0.1.4-5d15e94604-fb047d56c4.zip b/.yarn/cache/@radix-ui-react-portal-npm-0.1.4-5d15e94604-fb047d56c4.zip
deleted file mode 100644
index b51c277..0000000
Binary files a/.yarn/cache/@radix-ui-react-portal-npm-0.1.4-5d15e94604-fb047d56c4.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-presence-npm-0.1.1-93a3a72005-b8911eb908.zip b/.yarn/cache/@radix-ui-react-presence-npm-0.1.1-93a3a72005-b8911eb908.zip
deleted file mode 100644
index 59afb23..0000000
Binary files a/.yarn/cache/@radix-ui-react-presence-npm-0.1.1-93a3a72005-b8911eb908.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-primitive-npm-0.1.3-f5e2e8eea1-2cce826e9e.zip b/.yarn/cache/@radix-ui-react-primitive-npm-0.1.3-f5e2e8eea1-2cce826e9e.zip
deleted file mode 100644
index ac19f9d..0000000
Binary files a/.yarn/cache/@radix-ui-react-primitive-npm-0.1.3-f5e2e8eea1-2cce826e9e.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-primitive-npm-0.1.4-115fd5c8b4-e7b83dc515.zip b/.yarn/cache/@radix-ui-react-primitive-npm-0.1.4-115fd5c8b4-e7b83dc515.zip
deleted file mode 100644
index 463cb41..0000000
Binary files a/.yarn/cache/@radix-ui-react-primitive-npm-0.1.4-115fd5c8b4-e7b83dc515.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-slot-npm-0.1.2-b662327955-216927b9b1.zip b/.yarn/cache/@radix-ui-react-slot-npm-0.1.2-b662327955-216927b9b1.zip
deleted file mode 100644
index 5c80f39..0000000
Binary files a/.yarn/cache/@radix-ui-react-slot-npm-0.1.2-b662327955-216927b9b1.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-tooltip-npm-0.1.6-6bfafc0cd9-fa409aec05.zip b/.yarn/cache/@radix-ui-react-tooltip-npm-0.1.6-6bfafc0cd9-fa409aec05.zip
deleted file mode 100644
index eefedfd..0000000
Binary files a/.yarn/cache/@radix-ui-react-tooltip-npm-0.1.6-6bfafc0cd9-fa409aec05.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-use-callback-ref-npm-0.1.0-838ec38d13-5356971123.zip b/.yarn/cache/@radix-ui-react-use-callback-ref-npm-0.1.0-838ec38d13-5356971123.zip
deleted file mode 100644
index 832aee8..0000000
Binary files a/.yarn/cache/@radix-ui-react-use-callback-ref-npm-0.1.0-838ec38d13-5356971123.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-use-controllable-state-npm-0.1.0-75d1e06cac-2ddd05854a.zip b/.yarn/cache/@radix-ui-react-use-controllable-state-npm-0.1.0-75d1e06cac-2ddd05854a.zip
deleted file mode 100644
index 31be25e..0000000
Binary files a/.yarn/cache/@radix-ui-react-use-controllable-state-npm-0.1.0-75d1e06cac-2ddd05854a.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-use-escape-keydown-npm-0.1.0-af4910b082-b92769ecf4.zip b/.yarn/cache/@radix-ui-react-use-escape-keydown-npm-0.1.0-af4910b082-b92769ecf4.zip
deleted file mode 100644
index 6ab32bf..0000000
Binary files a/.yarn/cache/@radix-ui-react-use-escape-keydown-npm-0.1.0-af4910b082-b92769ecf4.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-use-layout-effect-npm-0.1.0-d80d7efedb-d8be1f9770.zip b/.yarn/cache/@radix-ui-react-use-layout-effect-npm-0.1.0-d80d7efedb-d8be1f9770.zip
deleted file mode 100644
index 2f2b7b6..0000000
Binary files a/.yarn/cache/@radix-ui-react-use-layout-effect-npm-0.1.0-d80d7efedb-d8be1f9770.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-use-previous-npm-0.1.0-4fbb87052c-3189d18c5c.zip b/.yarn/cache/@radix-ui-react-use-previous-npm-0.1.0-4fbb87052c-3189d18c5c.zip
deleted file mode 100644
index a7e0b45..0000000
Binary files a/.yarn/cache/@radix-ui-react-use-previous-npm-0.1.0-4fbb87052c-3189d18c5c.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-use-rect-npm-0.1.1-4803859eb8-aacf810744.zip b/.yarn/cache/@radix-ui-react-use-rect-npm-0.1.1-4803859eb8-aacf810744.zip
deleted file mode 100644
index 6334fd0..0000000
Binary files a/.yarn/cache/@radix-ui-react-use-rect-npm-0.1.1-4803859eb8-aacf810744.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-use-size-npm-0.1.0-bf02f18986-29134b0caf.zip b/.yarn/cache/@radix-ui-react-use-size-npm-0.1.0-bf02f18986-29134b0caf.zip
deleted file mode 100644
index aa11af4..0000000
Binary files a/.yarn/cache/@radix-ui-react-use-size-npm-0.1.0-bf02f18986-29134b0caf.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-react-visually-hidden-npm-0.1.3-48d22d0c00-4434bc9524.zip b/.yarn/cache/@radix-ui-react-visually-hidden-npm-0.1.3-48d22d0c00-4434bc9524.zip
deleted file mode 100644
index 81d6650..0000000
Binary files a/.yarn/cache/@radix-ui-react-visually-hidden-npm-0.1.3-48d22d0c00-4434bc9524.zip and /dev/null differ
diff --git a/.yarn/cache/@radix-ui-rect-npm-0.1.1-1e3be8d132-6f781fe3f6.zip b/.yarn/cache/@radix-ui-rect-npm-0.1.1-1e3be8d132-6f781fe3f6.zip
deleted file mode 100644
index d5dec09..0000000
Binary files a/.yarn/cache/@radix-ui-rect-npm-0.1.1-1e3be8d132-6f781fe3f6.zip and /dev/null differ
diff --git a/.yarn/cache/@tweakpane-core-npm-2.0.0-dc03f230e8-c92cb9d1d1.zip b/.yarn/cache/@tweakpane-core-npm-2.0.0-dc03f230e8-c92cb9d1d1.zip
new file mode 100644
index 0000000..477177a
Binary files /dev/null and b/.yarn/cache/@tweakpane-core-npm-2.0.0-dc03f230e8-c92cb9d1d1.zip differ
diff --git a/.yarn/cache/@use-gesture-core-npm-10.2.19-d139237291-2ae4d0f3e6.zip b/.yarn/cache/@use-gesture-core-npm-10.2.19-d139237291-2ae4d0f3e6.zip
deleted file mode 100644
index ec59c3b..0000000
Binary files a/.yarn/cache/@use-gesture-core-npm-10.2.19-d139237291-2ae4d0f3e6.zip and /dev/null differ
diff --git a/.yarn/cache/@use-gesture-react-npm-10.2.19-fc5a25abfc-25835f8843.zip b/.yarn/cache/@use-gesture-react-npm-10.2.19-fc5a25abfc-25835f8843.zip
deleted file mode 100644
index 5414550..0000000
Binary files a/.yarn/cache/@use-gesture-react-npm-10.2.19-fc5a25abfc-25835f8843.zip and /dev/null differ
diff --git a/.yarn/cache/@welldone-software-why-did-you-render-npm-6.2.3-3f37eda05f-6c012f0a67.zip b/.yarn/cache/@welldone-software-why-did-you-render-npm-6.2.3-3f37eda05f-6c012f0a67.zip
deleted file mode 100644
index 9a24034..0000000
Binary files a/.yarn/cache/@welldone-software-why-did-you-render-npm-6.2.3-3f37eda05f-6c012f0a67.zip and /dev/null differ
diff --git a/.yarn/cache/assign-symbols-npm-1.0.0-fd803ccdf1-c0eb895911.zip b/.yarn/cache/assign-symbols-npm-1.0.0-fd803ccdf1-c0eb895911.zip
deleted file mode 100644
index 6e72b81..0000000
Binary files a/.yarn/cache/assign-symbols-npm-1.0.0-fd803ccdf1-c0eb895911.zip and /dev/null differ
diff --git a/.yarn/cache/attr-accept-npm-2.2.2-b9cd0d8eac-496f724935.zip b/.yarn/cache/attr-accept-npm-2.2.2-b9cd0d8eac-496f724935.zip
deleted file mode 100644
index 5aabbe2..0000000
Binary files a/.yarn/cache/attr-accept-npm-2.2.2-b9cd0d8eac-496f724935.zip and /dev/null differ
diff --git a/.yarn/cache/colord-npm-2.9.3-5c35c27898-95d909bfbc.zip b/.yarn/cache/colord-npm-2.9.3-5c35c27898-95d909bfbc.zip
deleted file mode 100644
index 9a082ce..0000000
Binary files a/.yarn/cache/colord-npm-2.9.3-5c35c27898-95d909bfbc.zip and /dev/null differ
diff --git a/.yarn/cache/core-util-is-npm-1.0.3-ca74b76c90-9de8597363.zip b/.yarn/cache/core-util-is-npm-1.0.3-ca74b76c90-9de8597363.zip
deleted file mode 100644
index 2c844fe..0000000
Binary files a/.yarn/cache/core-util-is-npm-1.0.3-ca74b76c90-9de8597363.zip and /dev/null differ
diff --git a/.yarn/cache/dequal-npm-2.0.3-53a630c60e-8679b850e1.zip b/.yarn/cache/dequal-npm-2.0.3-53a630c60e-8679b850e1.zip
deleted file mode 100644
index 7721391..0000000
Binary files a/.yarn/cache/dequal-npm-2.0.3-53a630c60e-8679b850e1.zip and /dev/null differ
diff --git a/.yarn/cache/extend-shallow-npm-2.0.1-e6ef52b29c-8fb58d9d7a.zip b/.yarn/cache/extend-shallow-npm-2.0.1-e6ef52b29c-8fb58d9d7a.zip
deleted file mode 100644
index ba82137..0000000
Binary files a/.yarn/cache/extend-shallow-npm-2.0.1-e6ef52b29c-8fb58d9d7a.zip and /dev/null differ
diff --git a/.yarn/cache/extend-shallow-npm-3.0.2-77bbe1bbf5-a920b0cd58.zip b/.yarn/cache/extend-shallow-npm-3.0.2-77bbe1bbf5-a920b0cd58.zip
deleted file mode 100644
index ad15ea9..0000000
Binary files a/.yarn/cache/extend-shallow-npm-3.0.2-77bbe1bbf5-a920b0cd58.zip and /dev/null differ
diff --git a/.yarn/cache/file-selector-npm-0.5.0-c731a8ed22-f95a069381.zip b/.yarn/cache/file-selector-npm-0.5.0-c731a8ed22-f95a069381.zip
deleted file mode 100644
index f9392ea..0000000
Binary files a/.yarn/cache/file-selector-npm-0.5.0-c731a8ed22-f95a069381.zip and /dev/null differ
diff --git a/.yarn/cache/for-in-npm-1.0.2-37e3d7aae5-09f4ae93ce.zip b/.yarn/cache/for-in-npm-1.0.2-37e3d7aae5-09f4ae93ce.zip
deleted file mode 100644
index 51aeea2..0000000
Binary files a/.yarn/cache/for-in-npm-1.0.2-37e3d7aae5-09f4ae93ce.zip and /dev/null differ
diff --git a/.yarn/cache/get-value-npm-2.0.6-03cd422e0a-5c3b99cb53.zip b/.yarn/cache/get-value-npm-2.0.6-03cd422e0a-5c3b99cb53.zip
deleted file mode 100644
index 101e5bb..0000000
Binary files a/.yarn/cache/get-value-npm-2.0.6-03cd422e0a-5c3b99cb53.zip and /dev/null differ
diff --git a/.yarn/cache/glsl-token-assignments-npm-2.0.2-d9ee3ee3f3-efd6051cfd.zip b/.yarn/cache/glsl-token-assignments-npm-2.0.2-d9ee3ee3f3-efd6051cfd.zip
deleted file mode 100644
index 1fe3c07..0000000
Binary files a/.yarn/cache/glsl-token-assignments-npm-2.0.2-d9ee3ee3f3-efd6051cfd.zip and /dev/null differ
diff --git a/.yarn/cache/glsl-token-depth-npm-1.1.2-2c8fa14257-97fff701ee.zip b/.yarn/cache/glsl-token-depth-npm-1.1.2-2c8fa14257-97fff701ee.zip
deleted file mode 100644
index 9fa5b97..0000000
Binary files a/.yarn/cache/glsl-token-depth-npm-1.1.2-2c8fa14257-97fff701ee.zip and /dev/null differ
diff --git a/.yarn/cache/glsl-token-descope-npm-1.0.2-658390c61c-a0d578d5e7.zip b/.yarn/cache/glsl-token-descope-npm-1.0.2-658390c61c-a0d578d5e7.zip
deleted file mode 100644
index cd10b0e..0000000
Binary files a/.yarn/cache/glsl-token-descope-npm-1.0.2-658390c61c-a0d578d5e7.zip and /dev/null differ
diff --git a/.yarn/cache/glsl-token-functions-npm-1.0.1-970f7fe287-65801ee698.zip b/.yarn/cache/glsl-token-functions-npm-1.0.1-970f7fe287-65801ee698.zip
deleted file mode 100644
index 190c203..0000000
Binary files a/.yarn/cache/glsl-token-functions-npm-1.0.1-970f7fe287-65801ee698.zip and /dev/null differ
diff --git a/.yarn/cache/glsl-token-properties-npm-1.0.1-c5dfeda50c-9b4d1caf02.zip b/.yarn/cache/glsl-token-properties-npm-1.0.1-c5dfeda50c-9b4d1caf02.zip
deleted file mode 100644
index c9b7efe..0000000
Binary files a/.yarn/cache/glsl-token-properties-npm-1.0.1-c5dfeda50c-9b4d1caf02.zip and /dev/null differ
diff --git a/.yarn/cache/glsl-token-scope-npm-1.1.2-e6c5871b08-d62812c81a.zip b/.yarn/cache/glsl-token-scope-npm-1.1.2-e6c5871b08-d62812c81a.zip
deleted file mode 100644
index ffea413..0000000
Binary files a/.yarn/cache/glsl-token-scope-npm-1.1.2-e6c5871b08-d62812c81a.zip and /dev/null differ
diff --git a/.yarn/cache/glsl-token-string-npm-1.0.1-b01fad3c92-3260c1486b.zip b/.yarn/cache/glsl-token-string-npm-1.0.1-b01fad3c92-3260c1486b.zip
deleted file mode 100644
index 31d39ee..0000000
Binary files a/.yarn/cache/glsl-token-string-npm-1.0.1-b01fad3c92-3260c1486b.zip and /dev/null differ
diff --git a/.yarn/cache/glsl-tokenizer-npm-2.1.5-41724a73eb-daf70e91c6.zip b/.yarn/cache/glsl-tokenizer-npm-2.1.5-41724a73eb-daf70e91c6.zip
deleted file mode 100644
index 20d90bb..0000000
Binary files a/.yarn/cache/glsl-tokenizer-npm-2.1.5-41724a73eb-daf70e91c6.zip and /dev/null differ
diff --git a/.yarn/cache/immer-npm-9.0.15-6c734225db-92e3d63e81.zip b/.yarn/cache/immer-npm-9.0.15-6c734225db-92e3d63e81.zip
deleted file mode 100644
index ba6ea19..0000000
Binary files a/.yarn/cache/immer-npm-9.0.15-6c734225db-92e3d63e81.zip and /dev/null differ
diff --git a/.yarn/cache/is-extendable-npm-0.1.1-322b4649ec-3875571d20.zip b/.yarn/cache/is-extendable-npm-0.1.1-322b4649ec-3875571d20.zip
deleted file mode 100644
index e3eead3..0000000
Binary files a/.yarn/cache/is-extendable-npm-0.1.1-322b4649ec-3875571d20.zip and /dev/null differ
diff --git a/.yarn/cache/is-extendable-npm-1.0.1-7095ad8b16-db07bc1e9d.zip b/.yarn/cache/is-extendable-npm-1.0.1-7095ad8b16-db07bc1e9d.zip
deleted file mode 100644
index a2db00a..0000000
Binary files a/.yarn/cache/is-extendable-npm-1.0.1-7095ad8b16-db07bc1e9d.zip and /dev/null differ
diff --git a/.yarn/cache/is-plain-object-npm-2.0.4-da3265d804-2a401140cf.zip b/.yarn/cache/is-plain-object-npm-2.0.4-da3265d804-2a401140cf.zip
deleted file mode 100644
index 8b68965..0000000
Binary files a/.yarn/cache/is-plain-object-npm-2.0.4-da3265d804-2a401140cf.zip and /dev/null differ
diff --git a/.yarn/cache/isarray-npm-0.0.1-92e37e0a70-49191f1425.zip b/.yarn/cache/isarray-npm-0.0.1-92e37e0a70-49191f1425.zip
deleted file mode 100644
index 4c3f427..0000000
Binary files a/.yarn/cache/isarray-npm-0.0.1-92e37e0a70-49191f1425.zip and /dev/null differ
diff --git a/.yarn/cache/isobject-npm-3.0.1-8145901fd2-db85c4c970.zip b/.yarn/cache/isobject-npm-3.0.1-8145901fd2-db85c4c970.zip
deleted file mode 100644
index 214104c..0000000
Binary files a/.yarn/cache/isobject-npm-3.0.1-8145901fd2-db85c4c970.zip and /dev/null differ
diff --git a/.yarn/cache/jotai-npm-2.3.0-9e47e9bf85-21df579532.zip b/.yarn/cache/jotai-npm-2.3.0-9e47e9bf85-21df579532.zip
new file mode 100644
index 0000000..d1ba796
Binary files /dev/null and b/.yarn/cache/jotai-npm-2.3.0-9e47e9bf85-21df579532.zip differ
diff --git a/.yarn/cache/lamina-npm-1.1.23-ee95085731-2b9b0da0ec.zip b/.yarn/cache/lamina-npm-1.1.23-ee95085731-2b9b0da0ec.zip
deleted file mode 100644
index 3a839e8..0000000
Binary files a/.yarn/cache/lamina-npm-1.1.23-ee95085731-2b9b0da0ec.zip and /dev/null differ
diff --git a/.yarn/cache/leva-npm-0.9.31-165c9663cf-c3caff4249.zip b/.yarn/cache/leva-npm-0.9.31-165c9663cf-c3caff4249.zip
deleted file mode 100644
index 22485af..0000000
Binary files a/.yarn/cache/leva-npm-0.9.31-165c9663cf-c3caff4249.zip and /dev/null differ
diff --git a/.yarn/cache/leva-npm-0.9.34-d78329d111-72461068ac.zip b/.yarn/cache/leva-npm-0.9.34-d78329d111-72461068ac.zip
deleted file mode 100644
index ea732d0..0000000
Binary files a/.yarn/cache/leva-npm-0.9.34-d78329d111-72461068ac.zip and /dev/null differ
diff --git a/.yarn/cache/merge-value-npm-1.0.0-30d67b896c-32c0ecaac8.zip b/.yarn/cache/merge-value-npm-1.0.0-30d67b896c-32c0ecaac8.zip
deleted file mode 100644
index 20e5fc4..0000000
Binary files a/.yarn/cache/merge-value-npm-1.0.0-30d67b896c-32c0ecaac8.zip and /dev/null differ
diff --git a/.yarn/cache/mixin-deep-npm-1.3.2-29b528e571-820d5a51fc.zip b/.yarn/cache/mixin-deep-npm-1.3.2-29b528e571-820d5a51fc.zip
deleted file mode 100644
index 543d9a7..0000000
Binary files a/.yarn/cache/mixin-deep-npm-1.3.2-29b528e571-820d5a51fc.zip and /dev/null differ
diff --git a/.yarn/cache/react-colorful-npm-5.6.1-ba0c706357-e432b7cb0d.zip b/.yarn/cache/react-colorful-npm-5.6.1-ba0c706357-e432b7cb0d.zip
deleted file mode 100644
index 4ee5417..0000000
Binary files a/.yarn/cache/react-colorful-npm-5.6.1-ba0c706357-e432b7cb0d.zip and /dev/null differ
diff --git a/.yarn/cache/react-dropzone-npm-12.1.0-56c67f77aa-1be37433cf.zip b/.yarn/cache/react-dropzone-npm-12.1.0-56c67f77aa-1be37433cf.zip
deleted file mode 100644
index b96a24b..0000000
Binary files a/.yarn/cache/react-dropzone-npm-12.1.0-56c67f77aa-1be37433cf.zip and /dev/null differ
diff --git a/.yarn/cache/readable-stream-npm-1.0.34-db63158f3f-85042c537e.zip b/.yarn/cache/readable-stream-npm-1.0.34-db63158f3f-85042c537e.zip
deleted file mode 100644
index eb4518d..0000000
Binary files a/.yarn/cache/readable-stream-npm-1.0.34-db63158f3f-85042c537e.zip and /dev/null differ
diff --git a/.yarn/cache/set-value-npm-2.0.1-35da5f8180-09a4bc72c9.zip b/.yarn/cache/set-value-npm-2.0.1-35da5f8180-09a4bc72c9.zip
deleted file mode 100644
index 6647983..0000000
Binary files a/.yarn/cache/set-value-npm-2.0.1-35da5f8180-09a4bc72c9.zip and /dev/null differ
diff --git a/.yarn/cache/split-string-npm-3.1.0-df5d83450e-ae5af5c91b.zip b/.yarn/cache/split-string-npm-3.1.0-df5d83450e-ae5af5c91b.zip
deleted file mode 100644
index 4777e83..0000000
Binary files a/.yarn/cache/split-string-npm-3.1.0-df5d83450e-ae5af5c91b.zip and /dev/null differ
diff --git a/.yarn/cache/string_decoder-npm-0.10.31-851f3f7302-fe00f8e303.zip b/.yarn/cache/string_decoder-npm-0.10.31-851f3f7302-fe00f8e303.zip
deleted file mode 100644
index 52b4bfd..0000000
Binary files a/.yarn/cache/string_decoder-npm-0.10.31-851f3f7302-fe00f8e303.zip and /dev/null differ
diff --git a/.yarn/cache/three-custom-shader-material-npm-4.0.0-f3c6c1ba02-4803fde843.zip b/.yarn/cache/three-custom-shader-material-npm-4.0.0-f3c6c1ba02-4803fde843.zip
deleted file mode 100644
index 7eddee2..0000000
Binary files a/.yarn/cache/three-custom-shader-material-npm-4.0.0-f3c6c1ba02-4803fde843.zip and /dev/null differ
diff --git a/.yarn/cache/through2-npm-0.6.5-562fbaa3f1-dfea228e31.zip b/.yarn/cache/through2-npm-0.6.5-562fbaa3f1-dfea228e31.zip
deleted file mode 100644
index f4efc85..0000000
Binary files a/.yarn/cache/through2-npm-0.6.5-562fbaa3f1-dfea228e31.zip and /dev/null differ
diff --git a/.yarn/cache/tunnel-rat-npm-0.1.2-69bf8f367e-9d5975d589.zip b/.yarn/cache/tunnel-rat-npm-0.1.2-69bf8f367e-9d5975d589.zip
new file mode 100644
index 0000000..486a1a7
Binary files /dev/null and b/.yarn/cache/tunnel-rat-npm-0.1.2-69bf8f367e-9d5975d589.zip differ
diff --git a/.yarn/cache/tweakpane-npm-4.0.0-0250290069-a7f0839556.zip b/.yarn/cache/tweakpane-npm-4.0.0-0250290069-a7f0839556.zip
new file mode 100644
index 0000000..27226b2
Binary files /dev/null and b/.yarn/cache/tweakpane-npm-4.0.0-0250290069-a7f0839556.zip differ
diff --git a/.yarn/cache/v8n-npm-1.5.1-670231b6b7-96c8dff914.zip b/.yarn/cache/v8n-npm-1.5.1-670231b6b7-96c8dff914.zip
deleted file mode 100644
index 9c776a0..0000000
Binary files a/.yarn/cache/v8n-npm-1.5.1-670231b6b7-96c8dff914.zip and /dev/null differ
diff --git a/.yarn/cache/zustand-npm-4.1.1-60b4581ecd-03eefb193e.zip b/.yarn/cache/zustand-npm-4.1.1-60b4581ecd-03eefb193e.zip
deleted file mode 100644
index abafd9a..0000000
Binary files a/.yarn/cache/zustand-npm-4.1.1-60b4581ecd-03eefb193e.zip and /dev/null differ
diff --git a/.yarn/cache/zustand-npm-4.4.0-382083347f-37e69eec1b.zip b/.yarn/cache/zustand-npm-4.4.0-382083347f-37e69eec1b.zip
new file mode 100644
index 0000000..3a4b378
Binary files /dev/null and b/.yarn/cache/zustand-npm-4.4.0-382083347f-37e69eec1b.zip differ
diff --git a/package.json b/package.json
index ae2ab69..ec6e740 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,8 @@
},
"dependencies": {
"@derschmale/io-rgbe": "^0.1.1",
+ "@dnd-kit/core": "^6.0.8",
+ "@dnd-kit/sortable": "^7.0.2",
"@heroicons/react": "^2.0.18",
"@radix-ui/react-context-menu": "^1.0.0",
"@radix-ui/react-toolbar": "^1.0.2",
@@ -22,9 +24,7 @@
"@react-three/postprocessing": "^2.14.9",
"clsx": "^1.2.1",
"cmdk": "^0.2.0",
- "immer": "^9.0.15",
- "lamina": "^1.1.23",
- "leva": "^0.9.34",
+ "jotai": "^2.3.0",
"prettier": "^2.8.4",
"prism-react-renderer": "^1.3.5",
"r3f-perf": "^7.1.2",
@@ -34,9 +34,11 @@
"sonner": "^0.4.0",
"three": "^0.153.0",
"three-stdlib": "^2.23.9",
- "zustand": "^4.1.1"
+ "tunnel-rat": "^0.1.2",
+ "tweakpane": "^4.0.0"
},
"devDependencies": {
+ "@tweakpane/core": "^2.0.0",
"@types/prettier": "^2.7.2",
"@types/react": "^18.0.19",
"@types/react-dom": "^18.0.6",
diff --git a/public/textures/checkerboard.png b/public/textures/checkerboard.png
deleted file mode 100644
index bc2945c..0000000
Binary files a/public/textures/checkerboard.png and /dev/null differ
diff --git a/public/textures/flash-head.exr b/public/textures/flash-head.exr
new file mode 100644
index 0000000..5634dd3
Binary files /dev/null and b/public/textures/flash-head.exr differ
diff --git a/public/textures/flash-head.png b/public/textures/flash-head.png
new file mode 100644
index 0000000..306e849
Binary files /dev/null and b/public/textures/flash-head.png differ
diff --git a/public/textures/scrim.png b/public/textures/scrim.png
new file mode 100644
index 0000000..3203916
Binary files /dev/null and b/public/textures/scrim.png differ
diff --git a/public/textures/softbox-octagon.exr b/public/textures/softbox-octagon.exr
new file mode 100644
index 0000000..fba3bc7
Binary files /dev/null and b/public/textures/softbox-octagon.exr differ
diff --git a/public/textures/softbox-octagon.png b/public/textures/softbox-octagon.png
new file mode 100644
index 0000000..59c742d
Binary files /dev/null and b/public/textures/softbox-octagon.png differ
diff --git a/public/textures/umbrella.exr b/public/textures/umbrella.exr
new file mode 100644
index 0000000..352c035
Binary files /dev/null and b/public/textures/umbrella.exr differ
diff --git a/public/textures/umbrella.png b/public/textures/umbrella.png
new file mode 100644
index 0000000..1286820
Binary files /dev/null and b/public/textures/umbrella.png differ
diff --git a/src/App.tsx b/src/App.tsx
index 2007237..2414ed2 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,104 +1,18 @@
-import { Bars2Icon } from "@heroicons/react/24/outline";
-import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
import { Toaster } from "sonner";
-
+import { AppContent } from "./components/AppContent";
+import { AppLayout } from "./components/AppLayout";
import { AppToolbar } from "./components/AppToolbar";
import { CommandPalette } from "./components/CommandPalette";
-import { Outliner } from "./components/Outliner/Outliner";
-import { Properties } from "./components/Properties";
-import { HDRIPreview } from "./components/HDRIPreview";
-import { ScenePreview } from "./components/ScenePreview";
-import { useStore } from "./hooks/useStore";
-import { Code } from "./components/Code";
export default function App() {
- const mode = useStore((state) => state.mode);
return (
-
-
-
+ <>
+
-
-
-
-
- {/* Left */}
-
-
-
-
-
-
-
-
- {/* Middle */}
-
-
-
-
-
-
- {mode.hdri && (
- <>
-
-
-
-
-
-
-
- >
- )}
-
- {mode.code && (
- <>
-
-
-
-
-
-
-
- >
- )}
-
-
-
-
-
-
-
- {/* Right */}
-
-
-
-
-
+
+
+
+
+ >
);
}
diff --git a/src/components/AppContent/index.tsx b/src/components/AppContent/index.tsx
new file mode 100644
index 0000000..077492d
--- /dev/null
+++ b/src/components/AppContent/index.tsx
@@ -0,0 +1,95 @@
+import { Bars2Icon } from "@heroicons/react/24/outline";
+import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
+
+import { Outliner } from "../Outliner/Outliner";
+import { Properties } from "../Properties";
+import { HDRIPreview } from "../HDRIPreview";
+import { ScenePreview } from "../ScenePreview";
+import { Code } from "../Code";
+import { useAtomValue } from "jotai";
+import { modeAtom } from "../../store";
+
+export function AppContent() {
+ const mode = useAtomValue(modeAtom);
+
+ return (
+
+ {/* Left */}
+
+
+
+
+
+
+
+
+ {/* Middle */}
+
+
+
+
+
+
+ {mode.hdri && (
+ <>
+
+
+
+
+
+
+
+ >
+ )}
+
+ {mode.code && (
+ <>
+
+
+
+
+
+
+
+ >
+ )}
+
+
+
+
+
+
+
+ {/* Right */}
+
+
+
+
+ );
+}
diff --git a/src/components/AppLayout/index.tsx b/src/components/AppLayout/index.tsx
new file mode 100644
index 0000000..e4563c7
--- /dev/null
+++ b/src/components/AppLayout/index.tsx
@@ -0,0 +1,7 @@
+export function AppLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/AppToolbar/index.tsx b/src/components/AppToolbar/index.tsx
index 70f2ba8..c5b7589 100644
--- a/src/components/AppToolbar/index.tsx
+++ b/src/components/AppToolbar/index.tsx
@@ -5,12 +5,13 @@ import {
PhotoIcon,
} from "@heroicons/react/24/solid";
import * as Toolbar from "@radix-ui/react-toolbar";
-import { useStore } from "../../hooks/useStore";
+import { activeModesAtom, modeAtom } from "../../store";
import { Logo } from "./Logo";
+import { useAtomValue, useSetAtom } from "jotai";
export function AppToolbar() {
- const mode = useStore((state) => state.mode);
- const setMode = useStore((state) => state.setMode);
+ const setMode = useSetAtom(modeAtom);
+ const activeModes = useAtomValue(activeModesAtom);
return (
mode[key as keyof typeof mode]
- )}
+ value={activeModes}
onValueChange={(modes) =>
setMode(
modes.reduce((acc, mode) => ({ ...acc, [mode]: true }), {
diff --git a/src/components/Code/index.tsx b/src/components/Code/index.tsx
index 1c20a37..2cc06f7 100644
--- a/src/components/Code/index.tsx
+++ b/src/components/Code/index.tsx
@@ -1,11 +1,13 @@
-import { useStore } from "../../hooks/useStore";
import { format } from "prettier";
import parserBabel from "prettier/parser-babel";
import theme from "prism-react-renderer/themes/vsDark";
import Highlight, { defaultProps } from "prism-react-renderer";
+import { useAtomValue } from "jotai";
+import { lightsAtom } from "../../store";
+import { latlonToPhiTheta } from "../../utils/coordinates";
export function Code() {
- const lights = useStore((state) => state.lights);
+ const lights = useAtomValue(lightsAtom);
const code = `
import React from "react";
@@ -21,6 +23,7 @@ export function Env() {
{/* Lights */}
${lights
.map((light) => {
+ const { phi, theta } = latlonToPhiTheta(light.latlon);
return `
{/* ${light.name} */}
{
const down = (e: KeyboardEvent) => {
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
e.preventDefault();
- setOpen((open) => !open);
+ setOpen(true);
}
};
@@ -40,7 +42,7 @@ export function CommandPalette() {
@@ -75,15 +77,31 @@ export function CommandPalette() {
subtitle="Deflect light off umbrella"
colorTheme="orange"
>
-
+
+
+ -
+
+
+ -
+
-
-
+
@@ -104,7 +122,7 @@ export function CommandPalette() {
-
@@ -120,10 +138,12 @@ export function CommandPalette() {
)}
{value === "softbox" && }
+ {value === "procedural_scrim" && }
{value === "umbrella" && }
- {value === "flood" && }
+ {value === "procedural_umbrella" && }
+ {value === "flash_head" && }
{value === "sky" && }
- {value === "gradient" && }
+ {value === "sky_gradient" && }
@@ -144,10 +164,82 @@ function Item({
subtitle: string;
colorTheme?: "orange" | "blue" | "green" | "red" | "purple";
}) {
+ const setOpen = useSetAtom(isCommandPaletteOpenAtom);
+ const [lights, setLights] = useAtom(lightsAtom);
+ const addLight = (light: Light) => setLights((lights) => [...lights, light]);
+
+ function handleSelect(value: string) {
+ const commonProps = {
+ name: `${value} ${String.fromCharCode(lights.length + 65)}`,
+ id: THREE.MathUtils.generateUUID(),
+ ts: Date.now(),
+ shape: "rect" as const,
+ latlon: { x: 0, y: 0 },
+ intensity: 1,
+ rotation: 0,
+ scale: 1,
+ scaleX: 1,
+ scaleY: 1,
+ target: { x: 0, y: 0, z: 0 },
+ visible: true,
+ solo: false,
+ selected: false,
+ opacity: 1,
+ animate: false,
+ };
+
+ if (value === "softbox") {
+ addLight({
+ ...commonProps,
+ type: "texture",
+ color: "#ffffff",
+ map: "/textures/softbox-octagon.exr",
+ });
+ } else if (value === "procedural_scrim") {
+ addLight({
+ ...commonProps,
+ type: "procedural_scrim",
+ color: "#ffffff",
+ lightDistance: 0.3,
+ lightPosition: { x: 0, y: 0 },
+ });
+ } else if (value === "umbrella") {
+ addLight({
+ ...commonProps,
+ type: "texture",
+ color: "#ffffff",
+ map: "/textures/umbrella.exr",
+ });
+ } else if (value === "flash_head") {
+ addLight({
+ ...commonProps,
+ type: "texture",
+ color: "#ffffff",
+ map: "/textures/flash-head.exr",
+ });
+ } else if (value === "procedural_umbrella") {
+ addLight({
+ ...commonProps,
+ type: "procedural_umbrella",
+ color: "#ffffff",
+ lightSides: 3,
+ });
+ } else if (value === "sky_gradient") {
+ addLight({
+ ...commonProps,
+ type: "sky_gradient",
+ color: "#ff0000",
+ color2: "#0000ff",
+ });
+ }
+
+ setOpen(false);
+ }
+
return (
{}}
+ onSelect={handleSelect}
className="group cursor-pointer flex items-center rounded-lg text-sm gap-3 text-neutral-100 p-2 mr-2 font-medium transition-all transition-none data-[selected='true']:bg-blue-500 data-[selected='true']:text-white"
style={{ contentVisibility: "auto" }}
>
@@ -176,7 +268,7 @@ function Item({
function Softbox() {
return (
);
}
-function Flood() {
+function FlashHead() {
return (
+ );
+}
+
+function Umbrella() {
+ return (
+
diff --git a/src/components/Effects/index.tsx b/src/components/Effects/index.tsx
index e29b269..b0e483b 100644
--- a/src/components/Effects/index.tsx
+++ b/src/components/Effects/index.tsx
@@ -17,17 +17,17 @@ export function Effects() {
{enabled ? (
<>
-
-
>
) : (
<>>
)}
+
+
);
}
diff --git a/src/components/Env/LightRenderer.tsx b/src/components/Env/LightRenderer.tsx
new file mode 100644
index 0000000..f0e6273
--- /dev/null
+++ b/src/components/Env/LightRenderer.tsx
@@ -0,0 +1,89 @@
+import { Sphere } from "@react-three/drei";
+import { useFrame } from "@react-three/fiber";
+import { PrimitiveAtom, useAtomValue } from "jotai";
+import { useRef } from "react";
+import * as THREE from "three";
+import {
+ Light,
+ ProceduralScrimLight,
+ ProceduralUmbrellaLight,
+ SkyGradientLight,
+ TextureLight,
+} from "../../store";
+import { ProceduralScrimLightMaterial } from "./ProceduralScrimLightMaterial";
+import { ProceduralUmbrellaLightMaterial } from "./ProceduralUmbrellaLightMaterial";
+import { SkyGradientLightMaterial } from "./SkyGradientLightMaterial";
+import { TextureLightMaterial } from "./TextureLightMaterial";
+import { latlonToPhiTheta } from "../../utils/coordinates";
+
+export function LightRenderer({
+ index,
+ lightAtom,
+}: {
+ index: number;
+ lightAtom: PrimitiveAtom;
+}) {
+ const meshRef = useRef(null);
+ const light = useAtomValue(lightAtom);
+
+ useFrame(() => {
+ if (!meshRef.current) {
+ return;
+ }
+
+ const { phi, theta } = latlonToPhiTheta(light.latlon);
+
+ meshRef.current.position.setFromSphericalCoords(3, phi, theta);
+
+ meshRef.current.scale.setX(light.scale * light.scaleX);
+ meshRef.current.scale.setY(light.scale * light.scaleY);
+ meshRef.current.scale.setZ(light.scale);
+
+ meshRef.current.lookAt(light.target.x, light.target.y, light.target.z);
+ meshRef.current.rotateZ(light.rotation);
+ meshRef.current.updateMatrix();
+ });
+
+ if (light.type === "sky_gradient") {
+ return (
+
+ }
+ />
+
+ );
+ }
+
+ return (
+
+
+ {light.type === "procedural_scrim" && (
+ }
+ />
+ )}
+ {light.type === "texture" && (
+ }
+ />
+ )}
+ {light.type === "procedural_umbrella" && (
+ }
+ />
+ )}
+
+ );
+}
diff --git a/src/components/Env/LightformerLayers.tsx b/src/components/Env/LightformerLayers.tsx
deleted file mode 100644
index e8f7b02..0000000
--- a/src/components/Env/LightformerLayers.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import * as THREE from "three";
-import { Gradient, Noise, Color, Texture } from "lamina";
-import { Light } from "../../hooks/useStore";
-
-export function LightformerLayers({ light }: { light: Light }) {
- if (light.type === "solid") {
- const color = new THREE.Color(light.color);
- color.multiplyScalar(light.intensity);
- return ;
- } else if (light.type === "gradient") {
- const colorA = new THREE.Color(light.colorA);
- const colorB = new THREE.Color(light.colorB);
- colorA.multiplyScalar(light.intensity);
- colorB.multiplyScalar(light.intensity);
- return (
-
- );
- } else if (light.type === "noise") {
- const colorA = new THREE.Color(light.colorA);
- const colorB = new THREE.Color(light.colorB);
- const colorC = new THREE.Color(light.colorC);
- const colorD = new THREE.Color(light.colorD);
- colorA.multiplyScalar(light.intensity);
- colorB.multiplyScalar(light.intensity);
- colorC.multiplyScalar(light.intensity);
- colorD.multiplyScalar(light.intensity);
- return (
-
- );
- } else if (light.type === "texture") {
- return ;
- } else {
- throw new Error("Unknown light type");
- }
-}
diff --git a/src/components/Env/ProceduralScrimLightMaterial.tsx b/src/components/Env/ProceduralScrimLightMaterial.tsx
new file mode 100644
index 0000000..b9d95d2
--- /dev/null
+++ b/src/components/Env/ProceduralScrimLightMaterial.tsx
@@ -0,0 +1,161 @@
+import { shaderMaterial } from "@react-three/drei";
+import {
+ MaterialNode,
+ ThreeElements,
+ extend,
+ useFrame,
+} from "@react-three/fiber";
+import { useRef, useState } from "react";
+import * as THREE from "three";
+import { Light, ProceduralScrimLight } from "../../store";
+import { PrimitiveAtom, useAtomValue } from "jotai";
+
+const vertexShader = /* glsl */ `
+ varying vec2 vUv;
+
+ void main() {
+ vUv = uv;
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
+ }
+`;
+
+const fragmentShader = /* glsl */ `
+ #define PI 3.14159265359
+ #define TWO_PI 6.28318530718
+
+ uniform float uOpacity;
+ uniform vec3 uColor;
+ uniform float uIntensity;
+ uniform vec2 uLightPosition;
+ uniform float uLightDistance;
+
+ varying vec2 vUv;
+
+ // SOURCE: https://articles.hyperknot.com/area_lights_in_shaders/
+
+ float point_light(vec2 uv, float h, float i) {
+ // h - light's height over the ground
+ // i - light's intensity
+ return i * h * pow(dot(uv, uv) + h * h, -1.5);
+ }
+
+ float disc_light(vec2 uv, float h, float i) {
+ // h - light's height over the ground
+ // i - light's intensity
+ if (uv.x > 0.) return 0.;
+ return i * h * -uv.x * pow(dot(uv,uv) + h*h, -2.);
+ }
+
+ float rod_light_antideriv(vec2 uv, float i, float h) {
+ return i * uv.x / (dot(uv,uv) + h*h);
+ }
+
+ float rod_light(vec2 uv, float i, float h_top, float h_bottom) {
+ // h_top and h_bottom - the light's top and bottom above the ground
+ // i - light's intensity
+ return rod_light_antideriv(uv, i, h_top) - rod_light_antideriv(uv, i, h_bottom);
+ }
+
+ float area_light_antideriv(vec2 uv, float i, float h, float t) {
+ float lxh = length(vec2(uv.x, h));
+ return -i * uv.x * atan((t-uv.y) / lxh) / lxh;
+ }
+
+ float area_light(vec2 uv, float i, float h_bottom, float h_top, float t_start, float t_end) {
+ // i - light's intensity
+ // h_top and h_bottom - the light's top and bottom above the ground
+ // t_start and t_end - the light's start and end on the y-axis
+ float v =
+ + area_light_antideriv(uv, i, h_top, t_end)
+ + area_light_antideriv(uv, i, h_bottom, t_start)
+ - area_light_antideriv(uv, i, h_bottom, t_end)
+ - area_light_antideriv(uv, i, h_top, t_start);
+ return max(0.0, v);
+ }
+
+ void main() {
+ vec2 uv = vUv;
+ uv = (2.0 * uv - 1.0);
+ uv -= uLightPosition;
+
+ float l = 0.0;
+ l = point_light(uv, uLightDistance, uIntensity);
+ // l = disc_light(uv, uLightDistance, uIntensity);
+ // l = rod_light(uv, uIntensity, uLightDistance, uLightDistance + 0.5);
+ // l = area_light(uv, uIntensity, uLightDistance, uLightDistance + 0.5, -0.25, 0.25);
+
+ // Clamp to 0
+ l = max(0.0, l);
+
+ vec3 color = vec3(l);
+
+ // Apply intensity and color
+ color *= uColor;
+
+ gl_FragColor = vec4(color, uOpacity);
+ }
+`;
+
+const ProceduralScrimLightShaderMaterial = shaderMaterial(
+ {
+ uOpacity: 1,
+ uColor: new THREE.Color(0xffffff),
+ uIntensity: 1,
+ uLightPosition: new THREE.Vector2(0, 0),
+ uLightDistance: 0.5,
+ },
+ vertexShader,
+ fragmentShader
+);
+
+extend({ ProceduralScrimLightShaderMaterial });
+
+declare module "@react-three/fiber" {
+ interface ThreeElements {
+ proceduralScrimLightShaderMaterial: MaterialNode<
+ any,
+ typeof ProceduralScrimLightShaderMaterial
+ >;
+ }
+}
+
+export function ProceduralScrimLightMaterial({
+ lightAtom,
+}: {
+ lightAtom: PrimitiveAtom;
+}) {
+ const ref = useRef(
+ null!
+ );
+
+ const light = useAtomValue(lightAtom);
+
+ const [color] = useState(() => new THREE.Color(0xffffff));
+
+ useFrame(() => {
+ ref.current.uniforms.uColor.value = color.set(light.color);
+ ref.current.uniforms.uIntensity.value = light.intensity;
+ ref.current.uniforms.uOpacity.value = light.opacity;
+ ref.current.uniforms.uLightPosition.value = new THREE.Vector2(
+ light.lightPosition.x,
+ light.lightPosition.y
+ );
+ ref.current.uniforms.uLightDistance.value = light.lightDistance;
+ });
+
+ return (
+
+ );
+}
+
+// Reload on HMR
+if (import.meta.hot) {
+ import.meta.hot.accept();
+ import.meta.hot.dispose(() => {
+ window.location.reload();
+ });
+}
diff --git a/src/components/Env/ProceduralUmbrellaLightMaterial.tsx b/src/components/Env/ProceduralUmbrellaLightMaterial.tsx
new file mode 100644
index 0000000..aa89b98
--- /dev/null
+++ b/src/components/Env/ProceduralUmbrellaLightMaterial.tsx
@@ -0,0 +1,107 @@
+import { shaderMaterial } from "@react-three/drei";
+import {
+ MaterialNode,
+ ThreeElements,
+ extend,
+ useFrame,
+} from "@react-three/fiber";
+import { PrimitiveAtom, useAtomValue } from "jotai";
+import { useRef } from "react";
+import * as THREE from "three";
+import { ProceduralUmbrellaLight } from "../../store";
+
+const vertexShader = /* glsl */ `
+ varying vec2 vUv;
+
+ void main() {
+ vUv = uv;
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
+ }
+`;
+
+const fragmentShader = /* glsl */ `
+ #define PI 3.14159265359
+ #define TWO_PI 6.28318530718
+
+ uniform float uOpacity;
+ uniform vec3 uColor;
+ uniform float uIntensity;
+ uniform float uLightSides;
+
+ varying vec2 vUv;
+
+ void main() {
+ vec2 uv = vUv;
+ uv = 2.0 * uv - 1.0;
+
+ vec2 p = uv;
+ float at = atan(p.y, p.x);
+ float angle = sin(fract(at / PI * uLightSides) * PI) * PI * 0.5;
+ float radius = length(p);
+ float intensity = 1.0 - smoothstep(0.8, 1.0, radius * radius);
+
+ vec3 color = vec3(angle * intensity);
+
+ // Apply intensity and color
+ color *= uColor * uIntensity;
+ float alpha = smoothstep(0.0, 0.5, intensity) * uOpacity;
+
+ gl_FragColor = vec4(color, alpha);
+ }
+`;
+
+const ProceduralUmbrellaLightShaderMaterial = shaderMaterial(
+ {
+ uOpacity: 1,
+ uColor: new THREE.Color(0xffffff),
+ uIntensity: 1,
+ uLightSides: 4,
+ },
+ vertexShader,
+ fragmentShader
+);
+
+extend({ ProceduralUmbrellaLightShaderMaterial });
+
+declare module "@react-three/fiber" {
+ interface ThreeElements {
+ proceduralUmbrellaLightShaderMaterial: MaterialNode<
+ any,
+ typeof ProceduralUmbrellaLightShaderMaterial
+ >;
+ }
+}
+
+export function ProceduralUmbrellaLightMaterial({
+ lightAtom,
+}: {
+ lightAtom: PrimitiveAtom;
+}) {
+ const light = useAtomValue(lightAtom);
+ const ref = useRef(
+ null!
+ );
+
+ useFrame(() => {
+ ref.current.uniforms.uOpacity.value = light.opacity;
+ ref.current.uniforms.uIntensity.value = light.intensity;
+ ref.current.uniforms.uColor.value = new THREE.Color(light.color);
+ ref.current.uniforms.uLightSides.value = light.lightSides;
+ });
+
+ return (
+
+ );
+}
+
+// Reload on HMR
+if (import.meta.hot) {
+ import.meta.hot.accept();
+ import.meta.hot.dispose(() => {
+ window.location.reload();
+ });
+}
diff --git a/src/components/Env/SkyGradientLightMaterial.tsx b/src/components/Env/SkyGradientLightMaterial.tsx
new file mode 100644
index 0000000..7b8cfe1
--- /dev/null
+++ b/src/components/Env/SkyGradientLightMaterial.tsx
@@ -0,0 +1,93 @@
+import { shaderMaterial } from "@react-three/drei";
+import {
+ MaterialNode,
+ ThreeElements,
+ extend,
+ useFrame,
+} from "@react-three/fiber";
+import { PrimitiveAtom, useAtomValue } from "jotai";
+import { useRef } from "react";
+import * as THREE from "three";
+import { SkyGradientLight } from "../../store";
+
+const vertexShader = /* glsl */ `
+ varying vec2 vUv;
+
+ void main() {
+ vUv = uv;
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
+ }
+`;
+
+const fragmentShader = /* glsl */ `
+ #define PI 3.14159265359
+ #define TWO_PI 6.28318530718
+
+ uniform float uOpacity;
+ uniform vec3 uColor;
+ uniform vec3 uColor2;
+ uniform float uIntensity;
+
+ varying vec2 vUv;
+
+ void main() {
+ vec2 uv = vUv;
+ vec3 color = mix(uColor, uColor2, uv.y);
+ gl_FragColor = vec4(color * uIntensity, uOpacity);
+ }
+`;
+
+const SkyGradientLightShaderMaterial = shaderMaterial(
+ {
+ uOpacity: 1,
+ uColor: new THREE.Color(0xffffff),
+ uColor2: new THREE.Color(0xffffff),
+ uIntensity: 1,
+ },
+ vertexShader,
+ fragmentShader
+);
+
+extend({ SkyGradientLightShaderMaterial });
+
+declare module "@react-three/fiber" {
+ interface ThreeElements {
+ skyGradientLightShaderMaterial: MaterialNode<
+ any,
+ typeof SkyGradientLightShaderMaterial
+ >;
+ }
+}
+
+export function SkyGradientLightMaterial({
+ lightAtom,
+}: {
+ lightAtom: PrimitiveAtom;
+}) {
+ const light = useAtomValue(lightAtom);
+ const ref = useRef(null!);
+
+ useFrame(() => {
+ ref.current.uniforms.uOpacity.value = light.opacity;
+ ref.current.uniforms.uIntensity.value = light.intensity;
+ ref.current.uniforms.uColor.value = new THREE.Color(light.color);
+ ref.current.uniforms.uColor2.value = new THREE.Color(light.color2);
+ });
+
+ return (
+
+ );
+}
+
+// Reload on HMR
+if (import.meta.hot) {
+ import.meta.hot.accept();
+ import.meta.hot.dispose(() => {
+ window.location.reload();
+ });
+}
diff --git a/src/components/Env/TextureLightMaterial.tsx b/src/components/Env/TextureLightMaterial.tsx
new file mode 100644
index 0000000..7a13014
--- /dev/null
+++ b/src/components/Env/TextureLightMaterial.tsx
@@ -0,0 +1,97 @@
+import { shaderMaterial } from "@react-three/drei";
+import {
+ MaterialNode,
+ ThreeElements,
+ extend,
+ useFrame,
+ useLoader,
+} from "@react-three/fiber";
+import { PrimitiveAtom, useAtomValue } from "jotai";
+import { useRef } from "react";
+import * as THREE from "three";
+import { EXRLoader } from "three-stdlib";
+import { TextureLight } from "../../store";
+
+const vertexShader = /* glsl */ `
+ varying vec2 vUv;
+
+ void main() {
+ vUv = uv;
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
+ }
+`;
+
+const fragmentShader = /* glsl */ `
+ #define PI 3.14159265359
+ #define TWO_PI 6.28318530718
+
+ uniform float uOpacity;
+ uniform vec3 uColor;
+ uniform float uIntensity;
+ uniform sampler2D uTexture;
+
+ varying vec2 vUv;
+
+ void main() {
+ vec2 uv = vUv;
+
+ vec4 tex = texture2D(uTexture, uv);
+
+ gl_FragColor = vec4(tex.rgb * uColor * uIntensity, tex.a * uOpacity);
+ }
+`;
+
+const TextureLightShaderMaterial = shaderMaterial(
+ {
+ uOpacity: 1,
+ uColor: new THREE.Color(0xffffff),
+ uIntensity: 1,
+ uTexture: new THREE.DataTexture(),
+ },
+ vertexShader,
+ fragmentShader
+);
+
+extend({ TextureLightShaderMaterial });
+
+declare module "@react-three/fiber" {
+ interface ThreeElements {
+ textureLightShaderMaterial: MaterialNode<
+ any,
+ typeof TextureLightShaderMaterial
+ >;
+ }
+}
+
+export function TextureLightMaterial({
+ lightAtom,
+}: {
+ lightAtom: PrimitiveAtom;
+}) {
+ const light = useAtomValue(lightAtom);
+ const ref = useRef(null!);
+ const texture = useLoader(EXRLoader, light.map);
+
+ useFrame(() => {
+ ref.current.uniforms.uOpacity.value = light.opacity;
+ ref.current.uniforms.uIntensity.value = light.intensity;
+ ref.current.uniforms.uColor.value = new THREE.Color(light.color);
+ });
+
+ return (
+
+ );
+}
+
+// Reload on HMR
+if (import.meta.hot) {
+ import.meta.hot.accept();
+ import.meta.hot.dispose(() => {
+ window.location.reload();
+ });
+}
diff --git a/src/components/Env/index.tsx b/src/components/Env/index.tsx
index 6a02fa3..73a5c69 100644
--- a/src/components/Env/index.tsx
+++ b/src/components/Env/index.tsx
@@ -1,133 +1,20 @@
-import * as THREE from "three";
-import { Environment, Float, Lightformer } from "@react-three/drei";
-import { LayerMaterial } from "lamina";
-import { useControls, folder, LevaInputs } from "leva";
-import { useStore } from "../../hooks/useStore";
-import { LightformerLayers } from "./LightformerLayers";
-import { useState } from "react";
+import { lightAtomsAtom } from "../../store";
+import { useAtomValue } from "jotai";
+import { LightRenderer } from "./LightRenderer";
export function Env() {
- const mode = useStore((state) => state.mode);
- const lights = useStore((state) => state.lights);
- const selectedLightId = useStore((state) => state.selectedLightId);
-
- const [{ background, backgroundColor, preset, blur }] = useControls(() => {
- return {
- Background: folder(
- {
- background: {
- label: "Show BG",
- value: true,
- render: () => selectedLightId === null && mode.scene,
- },
- preset: {
- type: LevaInputs.SELECT,
- label: "Preset",
- value: "",
- options: [
- "",
- "sunset",
- "dawn",
- "night",
- "warehouse",
- "forest",
- "apartment",
- "studio",
- "city",
- "park",
- "lobby",
- ] as const,
- },
- backgroundColor: {
- label: "BG Color",
- value: "#000000",
- render: () => selectedLightId === null && mode.scene,
- },
- blur: {
- label: "Blur",
- value: 0,
- min: 0,
- max: 1,
- },
- },
- {
- order: 0,
- color: "cyan",
- collapsed: true,
- }
- ),
- };
- }, [selectedLightId]);
-
- const [resolution, setResolution] = useState(2048);
+ const lightAtoms = useAtomValue(lightAtomsAtom);
return (
-
-
- {lights.map((light) => {
- const {
- id,
- distance,
- phi,
- theta,
- intensity,
- shape,
- scale,
- scaleX,
- scaleY,
- visible,
- rotation,
- opacity,
- animate,
- animationSpeed,
- animationFloatIntensity,
- animationRotationIntensity,
- animationFloatingRange,
- } = light;
-
- return (
-
-
-
-
-
-
-
- );
- })}
-
+ <>
+
+ {lightAtoms.map((lightAtom, i) => (
+
+ ))}
+ >
);
}
diff --git a/src/components/HDRIPreview/DownloadHDRI.tsx b/src/components/HDRIPreview/DownloadHDRI.tsx
index 93b8570..0aa390f 100644
--- a/src/components/HDRIPreview/DownloadHDRI.tsx
+++ b/src/components/HDRIPreview/DownloadHDRI.tsx
@@ -1,135 +1,132 @@
import { useThree } from "@react-three/fiber";
import * as THREE from "three";
import convertCubemapToEquirectangular from "./convertCubemapToEquirectangular";
-import { useStore } from "../../hooks/useStore";
-import { button, folder, LevaInputs, useControls } from "leva";
import { encodeRGBE, HDRImageData } from "@derschmale/io-rgbe";
export function DownloadHDRI({ texture }: { texture: THREE.CubeTexture }) {
const renderer = useThree((state) => state.gl);
- const selectedLightId = useStore((state) => state.selectedLightId);
- useControls(
- () => ({
- "Export Settings": folder(
- {
- resolution: {
- label: "Resolution",
- type: LevaInputs.SELECT,
- options: {
- "1k": [1024, 512],
- "2k": [2048, 1024],
- "4k": [4096, 2048],
- },
- },
- format: {
- label: "Format",
- type: LevaInputs.SELECT,
- options: {
- HDR: "image/vnd.radiance",
- PNG: "image/png",
- JPEG: "image/jpg",
- WEBP: "image/webp",
- },
- },
- "Export HDRI": button((get) => {
- const [width, height] = get("Export Settings.resolution");
- const format = get("Export Settings.format");
- const filename =
- format === "image/vnd.radiance"
- ? "envmap.hdr"
- : format === "image/png"
- ? "envmap.png"
- : format === "image/jpg"
- ? "envmap.jpg"
- : format === "image/webp"
- ? "envmap.webp"
- : "envmap";
+ // useControls(
+ // () => ({
+ // "Export Settings": folder(
+ // {
+ // resolution: {
+ // label: "Resolution",
+ // type: LevaInputs.SELECT,
+ // options: {
+ // "1k": [1024, 512],
+ // "2k": [2048, 1024],
+ // "4k": [4096, 2048],
+ // },
+ // },
+ // format: {
+ // label: "Format",
+ // type: LevaInputs.SELECT,
+ // options: {
+ // HDR: "image/vnd.radiance",
+ // PNG: "image/png",
+ // JPEG: "image/jpg",
+ // WEBP: "image/webp",
+ // },
+ // },
+ // "Export HDRI": button((get) => {
+ // const [width, height] = get("Export Settings.resolution");
+ // const format = get("Export Settings.format");
+ // const filename =
+ // format === "image/vnd.radiance"
+ // ? "envmap.hdr"
+ // : format === "image/png"
+ // ? "envmap.png"
+ // : format === "image/jpg"
+ // ? "envmap.jpg"
+ // : format === "image/webp"
+ // ? "envmap.webp"
+ // : "envmap";
- if (format === "image/vnd.radiance") {
- const fbo = convertCubemapToEquirectangular(
- texture,
- renderer,
- width,
- height,
- THREE.SRGBColorSpace,
- THREE.FloatType
- );
+ // if (format === "image/vnd.radiance") {
+ // const fbo = convertCubemapToEquirectangular(
+ // texture,
+ // renderer,
+ // width,
+ // height,
+ // THREE.SRGBColorSpace,
+ // THREE.FloatType
+ // );
- const pixels = new Float32Array(width * height * 4);
- renderer.readRenderTargetPixels(fbo, 0, 0, width, height, pixels);
+ // const pixels = new Float32Array(width * height * 4);
+ // renderer.readRenderTargetPixels(fbo, 0, 0, width, height, pixels);
- // Convert RBGA buffer to RGB
- const rgbPixels = new Float32Array(width * height * 3);
- for (let i = 0; i < width * height; i++) {
- rgbPixels[i * 3] = pixels[i * 4];
- rgbPixels[i * 3 + 1] = pixels[i * 4 + 1];
- rgbPixels[i * 3 + 2] = pixels[i * 4 + 2];
- }
+ // // Convert RBGA buffer to RGB
+ // const rgbPixels = new Float32Array(width * height * 3);
+ // for (let i = 0; i < width * height; i++) {
+ // rgbPixels[i * 3] = pixels[i * 4];
+ // rgbPixels[i * 3 + 1] = pixels[i * 4 + 1];
+ // rgbPixels[i * 3 + 2] = pixels[i * 4 + 2];
+ // }
- const imgData = new HDRImageData();
- imgData.width = width;
- imgData.height = height;
- imgData.exposure = 1;
- imgData.gamma = 1;
- imgData.data = rgbPixels;
+ // const imgData = new HDRImageData();
+ // imgData.width = width;
+ // imgData.height = height;
+ // imgData.exposure = 1;
+ // imgData.gamma = 1;
+ // imgData.data = rgbPixels;
- const blob = new Blob([encodeRGBE(imgData)], {
- type: "application/octet-stream",
- });
- const url = URL.createObjectURL(blob);
+ // const blob = new Blob([encodeRGBE(imgData)], {
+ // type: "application/octet-stream",
+ // });
+ // const url = URL.createObjectURL(blob);
- const link = document.createElement("a");
- link.download = filename;
- link.href = url;
- link.click();
+ // const link = document.createElement("a");
+ // link.download = filename;
+ // link.href = url;
+ // link.click();
- URL.revokeObjectURL(url);
- } else {
- const fbo = convertCubemapToEquirectangular(
- texture,
- renderer,
- width,
- height
- );
+ // URL.revokeObjectURL(url);
+ // } else {
+ // const fbo = convertCubemapToEquirectangular(
+ // texture,
+ // renderer,
+ // width,
+ // height
+ // );
- const canvas = document.createElement("canvas");
- canvas.width = width;
- canvas.height = height;
+ // const canvas = document.createElement("canvas");
+ // canvas.width = width;
+ // canvas.height = height;
- const ctx = canvas.getContext("2d");
- if (ctx) {
- const pixels = new Uint8Array(width * height * 4);
- renderer.readRenderTargetPixels(
- fbo,
- 0,
- 0,
- width,
- height,
- pixels
- );
- const imageData = new ImageData(
- new Uint8ClampedArray(pixels),
- width,
- height
- );
- ctx.putImageData(imageData, 0, 0);
- const link = document.createElement("a");
- link.download = filename;
- link.href = canvas.toDataURL(format, 1);
- link.click();
- }
- }
- }),
- },
- {
- order: 3,
- color: "magenta",
- }
- ),
- }),
- [selectedLightId, texture]
- );
+ // const ctx = canvas.getContext("2d");
+ // if (ctx) {
+ // const pixels = new Uint8Array(width * height * 4);
+ // renderer.readRenderTargetPixels(
+ // fbo,
+ // 0,
+ // 0,
+ // width,
+ // height,
+ // pixels
+ // );
+ // const imageData = new ImageData(
+ // new Uint8ClampedArray(pixels),
+ // width,
+ // height
+ // );
+ // ctx.putImageData(imageData, 0, 0);
+ // const link = document.createElement("a");
+ // link.download = filename;
+ // link.href = canvas.toDataURL(format, 1);
+ // link.click();
+ // }
+ // }
+ // }),
+ // },
+ // {
+ // order: 3,
+ // color: "magenta",
+ // }
+ // ),
+ // }),
+ // [selectedLightId, texture]
+ // );
return null;
}
diff --git a/src/components/HDRIPreview/EnvMapPlane.tsx b/src/components/HDRIPreview/EnvMapPlane.tsx
index 6b72647..f058282 100644
--- a/src/components/HDRIPreview/EnvMapPlane.tsx
+++ b/src/components/HDRIPreview/EnvMapPlane.tsx
@@ -5,6 +5,7 @@ import { CubeMaterial } from "./CubeMaterial";
import { Env } from "../Env";
import { DownloadHDRI } from "./DownloadHDRI";
import { SaveBackgroundTexture } from "./SaveBackgroundTexture";
+import { Environment } from "@react-three/drei";
export function EnvMapPlane() {
const [texture, setTexture] = useState(() => new THREE.CubeTexture());
@@ -16,7 +17,15 @@ export function EnvMapPlane() {
<>
-
+
+
+
>,
scene
)}
diff --git a/src/components/HDRIPreview/convertCubemapToEquirectangular.ts b/src/components/HDRIPreview/convertCubemapToEquirectangular.ts
index dc87634..6eb67cb 100644
--- a/src/components/HDRIPreview/convertCubemapToEquirectangular.ts
+++ b/src/components/HDRIPreview/convertCubemapToEquirectangular.ts
@@ -34,7 +34,7 @@ void main() {
normalize( dir );
gl_FragColor = textureCube( map, dir );
- // #include
+ #include
#include
}
`;
diff --git a/src/components/Model/index.tsx b/src/components/Model/index.tsx
index 528970d..15f3aad 100644
--- a/src/components/Model/index.tsx
+++ b/src/components/Model/index.tsx
@@ -1,10 +1,12 @@
import { Resize, useGLTF } from "@react-three/drei";
+import { useAtomValue } from "jotai";
import { useMemo } from "react";
import * as THREE from "three";
-import { useStore } from "../../hooks/useStore";
+import { modelUrlAtom } from "../../store";
export function Model({ debugMaterial, ...props }: any) {
- const modelUrl = useStore((state) => state.modelUrl);
+ const modelUrl = useAtomValue(modelUrlAtom);
+
const {
scene,
// @ts-ignore
@@ -34,8 +36,10 @@ export function Model({ debugMaterial, ...props }: any) {
}, [nodes, materials, debugMaterial]);
return (
-
-
-
+
+
+
+
+
);
}
diff --git a/src/components/Outliner/CameraListItem.tsx b/src/components/Outliner/CameraListItem.tsx
index 38bc0ca..b64f9b4 100644
--- a/src/components/Outliner/CameraListItem.tsx
+++ b/src/components/Outliner/CameraListItem.tsx
@@ -1,46 +1,55 @@
import { CameraIcon } from "@heroicons/react/24/outline";
import clsx from "clsx";
-import { useStore, Camera } from "../../hooks/useStore";
+import {
+ Camera,
+ selectCameraAtom,
+ toggleCameraSelectionAtom,
+} from "../../store";
import { useKeyPress } from "../../hooks/useKeyPress";
+import { PrimitiveAtom, useAtom, useAtomValue, useSetAtom } from "jotai";
-export function CameraListItem({ index, camera }: { index: number; camera: Camera; }) {
- const { id, name } = camera;
- const selectedCameraId = useStore((state) => state.selectedCameraId);
- const setSelectedCameraId = useStore((state) => state.setSelectedCameraId);
+export function CameraListItem({
+ index,
+ cameraAtom,
+}: {
+ index: number;
+ cameraAtom: PrimitiveAtom;
+}) {
+ const camera = useAtomValue(cameraAtom);
- const isSelected = selectedCameraId === id;
+ const toggleCameraSelection = useSetAtom(toggleCameraSelectionAtom);
+ const selectCamera = useSetAtom(selectCameraAtom);
const key = String(index + 1);
- useKeyPress(key, () => setSelectedCameraId(id));
+ useKeyPress(key, () => selectCamera(camera.id));
return (
{
- if (!isSelected) {
- setSelectedCameraId(id);
- }
- }}
+ onClick={() => toggleCameraSelection(camera.id)}
>
+ checked={camera.selected}
+ className="peer"
+ />
- {name}
+
+ {camera.name}
+
{key}
diff --git a/src/components/Outliner/DragHandleIcon.tsx b/src/components/Outliner/DragHandleIcon.tsx
new file mode 100644
index 0000000..5f2b0c1
--- /dev/null
+++ b/src/components/Outliner/DragHandleIcon.tsx
@@ -0,0 +1,12 @@
+export function DragHandleIcon(props: React.SVGProps) {
+ return (
+
+ );
+}
diff --git a/src/components/Outliner/LightListItem.tsx b/src/components/Outliner/LightListItem.tsx
index 414ca2a..ecb3169 100644
--- a/src/components/Outliner/LightListItem.tsx
+++ b/src/components/Outliner/LightListItem.tsx
@@ -1,4 +1,6 @@
import {
+ Bars2Icon,
+ ChevronUpDownIcon,
EyeSlashIcon,
FlagIcon,
LightBulbIcon,
@@ -9,382 +11,158 @@ import {
} from "@heroicons/react/24/solid";
import * as ContextMenu from "@radix-ui/react-context-menu";
import clsx from "clsx";
-import { folder, useControls } from "leva";
-import { useStore, Light } from "../../hooks/useStore";
-
-export function LightListItem({ light }: { light: Light }) {
- const {
- id,
- type,
- name,
- visible,
- solo,
- shape,
- intensity,
- distance,
- phi,
- theta,
- scale,
- scaleX,
- scaleY,
- opacity,
- animate,
- } = light;
-
- const selectedLightId = useStore((state) => state.selectedLightId);
- const toggleLightVisibilityById = useStore(
- (state) => state.toggleLightVisibilityById
- );
- const setSelectedLightId = useStore((state) => state.setSelectedLightId);
- const clearSelectedLight = useStore((state) => state.clearSelectedLight);
- const updateLight = useStore((state) => state.updateLight);
- const duplicateLightById = useStore((state) => state.duplicateLightById);
- const removeLightById = useStore((state) => state.removeLightById);
- const toggleSoloLightById = useStore((state) => state.toggleSoloLightById);
- const isSolo = useStore((state) => state.isSolo);
- const textureMaps = useStore((state) => state.textureMaps);
+import {
+ Light,
+ isSoloAtom,
+ toggleSoloAtom,
+ toggleLightSelectionAtom,
+ deleteLightAtom,
+ duplicateLightAtom,
+} from "../../store";
+import { PropertiesPanelTunnel } from "../Properties";
+import { PrimitiveAtom, useAtom, useAtomValue, useSetAtom } from "jotai";
+import { LightProperties } from "./LightProperties";
+import { useSortable } from "@dnd-kit/sortable";
+import { CSS } from "@dnd-kit/utilities";
+import { DragHandleIcon } from "./DragHandleIcon";
- useControls(() => {
- if (selectedLightId !== id) {
- return {
- Light: folder(
- {},
- {
- color: "yellow",
- order: 2,
- }
- ),
- };
- } else {
- return {
- Light: folder(
- {
- [`name ~${id}`]: {
- label: "Name",
- value: name ?? "Light",
- onChange: (v) => updateLight({ id, name: v }),
- },
- [`shape ~${id}`]: {
- label: "Shape",
- value: shape ?? "rect",
- options: ["rect", "ring", "circle"],
- onChange: (v) => updateLight({ id, shape: v }),
- },
- [`intensity ~${id}`]: {
- label: "Intensity",
- value: intensity,
- step: 0.1,
- min: 0,
- onChange: (v) => updateLight({ id, intensity: v }),
- },
- [`opacity ~${id}`]: {
- label: "Opacity",
- value: opacity ?? 1,
- step: 0.1,
- min: 0,
- max: 1,
- onChange: (v) => updateLight({ id, opacity: v }),
- },
- [`scaleMultiplier ~${id}`]: {
- label: "Scale Multiplier",
- value: scale ?? 1,
- step: 0.1,
- min: 0,
- max: 10,
- onChange: (v) => updateLight({ id, scale: v }),
- },
- [`scale ~${id}`]: {
- label: "Scale",
- value: [scaleX, scaleY] ?? [1, 1],
- step: 0.1,
- min: 0,
- joystick: false,
- onChange: (v) => updateLight({ id, scaleX: v[0], scaleY: v[1] }),
- },
- [`distance ~${id}`]: {
- label: "Distance",
- value: distance ?? 1,
- step: 0.1,
- min: 0,
- onChange: (v) => updateLight({ id, distance: v }),
- },
- [`phi ~${id}`]: {
- label: "Phi",
- value: phi ?? 1,
- step: 0.1,
- min: 0,
- max: Math.PI,
- onChange: (v) => updateLight({ id, phi: v }),
- },
- [`theta ~${id}`]: {
- label: "Theta",
- value: theta ?? 1,
- step: 0.1,
- min: 0,
- max: Math.PI * 2,
- onChange: (v) => updateLight({ id, theta: v }),
- },
+export function LightListItem({
+ lightAtom,
+}: {
+ lightAtom: PrimitiveAtom;
+}) {
+ const [light, setLight] = useAtom(lightAtom);
+ const isSolo = useAtomValue(isSoloAtom);
+ const toggleSolo = useSetAtom(toggleSoloAtom);
+ const toggleSelection = useSetAtom(toggleLightSelectionAtom);
+ const duplicateLight = useSetAtom(duplicateLightAtom);
+ const deleteLight = useSetAtom(deleteLightAtom);
- [`type ~${id}`]: {
- label: "Type",
- value: type ?? "#fff",
- options: ["solid", "gradient", "noise", "texture"],
- onChange: (v) => updateLight({ id, type: v }),
- },
+ const { id, name, visible, solo, selected } = light;
- ...(() => {
- if (light.type === "solid") {
- return {
- [`color ~${id}`]: {
- label: "Color",
- value: light.color ?? "#fff",
- onChange: (v) => updateLight({ id, color: v }),
- },
- };
- } else if (light.type === "gradient") {
- return {
- [`colorA ~${id}`]: {
- label: "Color A",
- value: light.colorA ?? "#f5c664",
- onChange: (v) => updateLight({ id, colorA: v }),
- },
- [`colorB ~${id}`]: {
- label: "Color B",
- value: light.colorB ?? "#ff0000",
- onChange: (v) => updateLight({ id, colorB: v }),
- },
- [`contrast ~${id}`]: {
- label: "Contrast",
- value: light.contrast ?? 1,
- onChange: (v) => updateLight({ id, contrast: v }),
- },
- [`axes ~${id}`]: {
- label: "Axes",
- value: light.axes ?? "x",
- options: ["x", "y"],
- onChange: (v) => updateLight({ id, axes: v }),
- },
- };
- } else if (light.type === "noise") {
- return {
- [`colorA ~${id}`]: {
- label: "Color A",
- value: light.colorA ?? "#f5c664",
- onChange: (v) => updateLight({ id, colorA: v }),
- },
- [`colorB ~${id}`]: {
- label: "Color B",
- value: light.colorB ?? "#ff0000",
- onChange: (v) => updateLight({ id, colorB: v }),
- },
- [`colorC ~${id}`]: {
- label: "Color C",
- value: light.colorB ?? "#00ff00",
- onChange: (v) => updateLight({ id, colorC: v }),
- },
- [`colorD ~${id}`]: {
- label: "Color D",
- value: light.colorD ?? "#0000ff",
- onChange: (v) => updateLight({ id, colorD: v }),
- },
- [`noiseScale ~${id}`]: {
- label: "Noise Scale",
- value: light.noiseScale ?? 1,
- min: 0,
- onChange: (v) => updateLight({ id, noiseScale: v }),
- },
- [`noiseType ~${id}`]: {
- label: "Noise Type",
- value: light.noiseType ?? "perlin",
- options: ["perlin", "simplex", "cell", "curl"],
- onChange: (v) => updateLight({ id, noiseType: v }),
- },
- };
- } else if (light.type === "texture") {
- return {
- [`map ~${id}`]: {
- label: "Map",
- value:
- textureMaps.find((value) => light.map === value)?.name ??
- "none",
- options: [
- "none",
- ...textureMaps.map((value) => value.name),
- ],
- onChange: (v) => {
- updateLight({
- id,
- map: textureMaps.find((map) => map.name === v),
- });
- },
- },
- };
- } else {
- return {};
- }
- })(),
+ const toggleVisibility = () =>
+ setLight((old) => ({ ...old, visible: !old.visible }));
- [`animate ~${id}`]: {
- label: "Animate",
- value: light.animate ?? false,
- onChange: (v) => updateLight({ id, animate: v }),
- },
+ const updateLight = (light: Partial) =>
+ setLight((old) => ({ ...old, ...light } as any));
- ...(() => {
- if (!light.animate) {
- return {};
- }
+ const {
+ attributes,
+ listeners,
+ setNodeRef,
+ setActivatorNodeRef,
+ transform,
+ transition,
+ } = useSortable({ id: light.id, transition: null });
- return {
- [`animationSpeed ~${id}`]: {
- label: "Animation Speed",
- value: light.animationSpeed ?? 1,
- min: 0,
- onChange: (v) => updateLight({ id, animationSpeed: v }),
- },
- [`animationRotationIntensity ~${id}`]: {
- label: "Rotation Intensity",
- value: light.animationRotationIntensity ?? 1,
- min: 0,
- onChange: (v) =>
- updateLight({ id, animationRotationIntensity: v }),
- },
- [`animationFloatIntensity ~${id}`]: {
- label: "Float Intensity",
- value: light.animationFloatIntensity ?? 1,
- min: 0,
- onChange: (v) =>
- updateLight({ id, animationFloatIntensity: v }),
- },
- [`animationFloatingRange ~${id}`]: {
- label: "Floating Range",
- value: light.animationFloatingRange ?? [0, 2],
- min: 0,
- max: 2,
- onChange: (v) =>
- updateLight({ id, animationFloatingRange: v }),
- },
- };
- })(),
- },
- {
- color: "yellow",
- order: 2,
- }
- ),
- };
- }
- }, [
- selectedLightId,
- id,
- name,
- shape,
- intensity,
- type,
- distance,
- phi,
- theta,
- scale,
- scaleX,
- scaleY,
- animate,
- ]);
+ const style = {
+ transform: CSS.Transform.toString(transform),
+ transition,
+ };
return (
-
-
- {
- if (selectedLightId === id) {
- clearSelectedLight();
- } else {
- setSelectedLightId(id);
- }
- }}
- >
-
-
-
-
- {name}
-
-
+ <>
+
+
+
-
-
-
+
-
-
- duplicateLightById(id)}
- >
- Duplicate
-
- removeLightById(id)}
- >
- Delete
-
-
-
-
+
+
+ duplicateLight(light.id)}
+ >
+ Duplicate
+
+ deleteLight(light.id)}
+ >
+ Delete
+
+
+
+
+
+ {selected && (
+
+
+
+ )}
+ >
);
}
diff --git a/src/components/Outliner/LightProperties.tsx b/src/components/Outliner/LightProperties.tsx
new file mode 100644
index 0000000..08520d6
--- /dev/null
+++ b/src/components/Outliner/LightProperties.tsx
@@ -0,0 +1,108 @@
+import { Light } from "../../store";
+import { PrimitiveAtom, useAtom } from "jotai";
+import { useCallback, useEffect, useRef } from "react";
+import { Pane } from "tweakpane";
+
+export function LightProperties({
+ lightAtom,
+}: {
+ lightAtom: PrimitiveAtom;
+}) {
+ const [light, setLight] = useAtom(lightAtom);
+ const ref = useRef(null!);
+ const pane = useRef(null!);
+
+ const handleChange = useCallback(
+ (e: any) => {
+ setLight((old) => ({
+ ...old,
+ [e.target.key]: structuredClone(e.value),
+ ts: Date.now(),
+ }));
+ },
+ [light.id]
+ );
+
+ useEffect(() => {
+ pane.current?.refresh();
+ }, [light.ts]);
+
+ useEffect(() => {
+ if (!ref.current) {
+ return;
+ }
+
+ pane.current = new Pane({ container: ref.current, expanded: true });
+
+ pane.current.addBinding(light, "name").on("change", handleChange);
+
+ pane.current.addBlade({ view: "separator" });
+
+ pane.current
+ .addBinding(light, "scale", { min: 0, step: 0.1 })
+ .on("change", handleChange);
+ pane.current
+ .addBinding(light, "scaleX", { label: "width", min: 0, step: 0.1 })
+ .on("change", handleChange);
+ pane.current
+ .addBinding(light, "scaleY", { label: "height", min: 0, step: 0.1 })
+ .on("change", handleChange);
+ pane.current
+ .addBinding(light, "rotation", { step: 0.1 })
+ .on("change", handleChange);
+ pane.current
+ .addBinding(light, "latlon", {
+ x: { min: -1, max: 1, step: 0.01 },
+ y: { inverted: true, min: -1, max: 1, step: 0.01 },
+ })
+ .on("change", handleChange);
+ pane.current.addBinding(light, "target").on("change", handleChange);
+
+ pane.current.addBlade({ view: "separator" });
+
+ pane.current.addBinding(light, "color").on("change", handleChange);
+ pane.current
+ .addBinding(light, "intensity", { min: 0, step: 0.1 })
+ .on("change", handleChange);
+ pane.current
+ .addBinding(light, "opacity", { min: 0, max: 1 })
+ .on("change", handleChange);
+
+ pane.current.addBlade({ view: "separator" });
+
+ pane.current.addBinding(light, "type", { readonly: true });
+
+ if (light.type === "procedural_umbrella") {
+ pane.current
+ .addBinding(light, "lightSides", { min: 3, max: 20 })
+ .on("change", handleChange);
+ }
+
+ if (light.type === "procedural_scrim") {
+ pane.current
+ .addBinding(light, "lightPosition", {
+ label: "scrim xy",
+ x: { min: -1, max: 1 },
+ y: { inverted: true, min: -1, max: 1 },
+ })
+ .on("change", handleChange);
+ pane.current
+ .addBinding(light, "lightDistance", {
+ min: 0.01,
+ max: 1,
+ label: "spread",
+ })
+ .on("change", handleChange);
+ }
+
+ if (light.type === "sky_gradient") {
+ pane.current.addBinding(light, "color2").on("change", handleChange);
+ }
+
+ return () => {
+ pane.current.dispose();
+ };
+ }, [light.id]);
+
+ return ;
+}
diff --git a/src/components/Outliner/Outliner.tsx b/src/components/Outliner/Outliner.tsx
index 97af5ee..b2f6d5d 100644
--- a/src/components/Outliner/Outliner.tsx
+++ b/src/components/Outliner/Outliner.tsx
@@ -1,21 +1,68 @@
import { PlusIcon } from "@heroicons/react/24/outline";
import * as THREE from "three";
-
-import { useStore } from "../../hooks/useStore";
+import {
+ DndContext,
+ closestCenter,
+ KeyboardSensor,
+ PointerSensor,
+ useSensor,
+ useSensors,
+ DragEndEvent,
+} from "@dnd-kit/core";
+import {
+ arrayMove,
+ SortableContext,
+ sortableKeyboardCoordinates,
+ verticalListSortingStrategy,
+} from "@dnd-kit/sortable";
+import {
+ Camera,
+ Light,
+ cameraAtomsAtom,
+ camerasAtom,
+ isCommandPaletteOpenAtom,
+ lightAtomsAtom,
+ lightIdsAtom,
+ lightsAtom,
+ selectedCameraAtom,
+} from "../../store";
import { LightListItem } from "./LightListItem";
import { CameraListItem } from "./CameraListItem";
+import { useAtomValue, useSetAtom } from "jotai";
export function Outliner() {
- const lights = useStore((state) => state.lights);
- const cameras = useStore((state) => state.cameras);
- const addLight = useStore((state) => state.addLight);
- const addCamera = useStore((state) => state.addCamera);
+ const lightIds = useAtomValue(lightIdsAtom);
+ const setLights = useSetAtom(lightsAtom);
+ const setIsCommandPaletteOpen = useSetAtom(isCommandPaletteOpenAtom);
+ const lightAtoms = useAtomValue(lightAtomsAtom);
+ const cameraAtoms = useAtomValue(cameraAtomsAtom);
+ const setCameras = useSetAtom(camerasAtom);
+ const currentCamera = useAtomValue(selectedCameraAtom);
+ const addCamera = (camera: Camera) =>
+ setCameras((cameras) => [...cameras, camera]);
+
+ const sensors = useSensors(
+ useSensor(PointerSensor),
+ useSensor(KeyboardSensor, {
+ coordinateGetter: sortableKeyboardCoordinates,
+ })
+ );
+
+ function handleDragEnd(event: DragEndEvent) {
+ const { active, over } = event;
+
+ if (active && over && active.id !== over.id) {
+ setLights((lights) => {
+ const oldIndex = lights.findIndex((light) => light.id === active.id);
+ const newIndex = lights.findIndex((light) => light.id === over.id);
- const selectedCameraId = useStore((state) => state.selectedCameraId);
- const currentCamera = cameras.find((c) => c.id === selectedCameraId);
+ return arrayMove(lights, oldIndex, newIndex);
+ });
+ }
+ }
return (
-
+
Cameras
@@ -24,10 +71,9 @@ export function Outliner() {
className="rounded p-1 -m-1 hover:bg-white/20 transition-colors"
onClick={() => {
addCamera({
- rotation: [0, 0, 0],
- position: [0, 0, 5],
...currentCamera,
- name: `Camera ${String.fromCharCode(cameras.length + 65)}`,
+ selected: false,
+ name: `Camera ${String.fromCharCode(cameraAtoms.length + 65)}`,
id: THREE.MathUtils.generateUUID(),
});
}}
@@ -37,8 +83,12 @@ export function Outliner() {
- {cameras.map((camera, index) => (
-
+ {cameraAtoms.map((cameraAtom, index) => (
+
))}
@@ -49,36 +99,28 @@ export function Outliner() {
{
- addLight({
- name: `Light ${String.fromCharCode(lights.length + 65)}`,
- id: THREE.MathUtils.generateUUID(),
- shape: "rect",
- type: "solid",
- color: "#fff",
- distance: 4,
- phi: Math.PI / 2,
- theta: 0,
- intensity: 1,
- rotation: 0,
- scale: 2,
- scaleX: 1,
- scaleY: 1,
- target: [0, 0, 0],
- visible: true,
- solo: false,
- opacity: 1,
- animate: false,
- });
+ setIsCommandPaletteOpen(true);
}}
>
-
- {lights.map((light) => (
-
- ))}
+
+
+
+ {lightAtoms.map((lightAtom) => (
+
+ ))}
+
+
);
diff --git a/src/components/Properties/index.tsx b/src/components/Properties/index.tsx
index ff55f95..c3150b5 100644
--- a/src/components/Properties/index.tsx
+++ b/src/components/Properties/index.tsx
@@ -1,4 +1,6 @@
-import { Leva } from "leva";
+import tunnel from "tunnel-rat";
+
+export const PropertiesPanelTunnel = tunnel();
export function Properties() {
return (
@@ -6,23 +8,9 @@ export function Properties() {
Properties
+
);
diff --git a/src/components/ScenePreview/Cameras.tsx b/src/components/ScenePreview/Cameras.tsx
index 81810a9..2993191 100644
--- a/src/components/ScenePreview/Cameras.tsx
+++ b/src/components/ScenePreview/Cameras.tsx
@@ -1,21 +1,21 @@
import { PerspectiveCamera } from "@react-three/drei";
-import React from "react";
-import { useStore } from "../../hooks/useStore";
+import { camerasAtom } from "../../store";
+import { useAtomValue } from "jotai";
export function Cameras() {
- const cameras = useStore((state) => state.cameras);
- const selectedCameraId = useStore((state) => state.selectedCameraId);
+ const cameras = useAtomValue(camerasAtom);
return (
<>
{cameras.map((camera) => (
+ far={100}
+ />
))}
>
);
diff --git a/src/components/ScenePreview/Controls.tsx b/src/components/ScenePreview/Controls.tsx
index afcfdef..2a90b27 100644
--- a/src/components/ScenePreview/Controls.tsx
+++ b/src/components/ScenePreview/Controls.tsx
@@ -1,6 +1,7 @@
import { useRef } from "react";
import { OrbitControls } from "@react-three/drei";
-import { useStore } from "../../hooks/useStore";
+import { selectedCameraAtom } from "../../store";
+import { useSetAtom } from "jotai";
export type ControlsProps = {
autoRotate: boolean;
@@ -8,7 +9,7 @@ export type ControlsProps = {
export const Controls = ({ autoRotate }: ControlsProps) => {
const controlsRef = useRef>(null);
- const updateSelectedCamera = useStore((state) => state.updateSelectedCamera);
+ const setCamera = useSetAtom(selectedCameraAtom);
return (
{
enableDamping={false}
onEnd={(e) => {
if (controlsRef.current) {
- updateSelectedCamera({
+ setCamera({
position: controlsRef.current.object.position.toArray(),
rotation: controlsRef.current.object.rotation.toArray() as [
number,
diff --git a/src/components/ScenePreview/Debug.tsx b/src/components/ScenePreview/Debug.tsx
index a7ba25d..75f8cb6 100644
--- a/src/components/ScenePreview/Debug.tsx
+++ b/src/components/ScenePreview/Debug.tsx
@@ -1,7 +1,64 @@
+import { useFrame } from "@react-three/fiber";
+import { useAtom } from "jotai";
+import { useAtomCallback } from "jotai/utils";
import { Perf } from "r3f-perf";
+import { useRef } from "react";
+import * as THREE from "three";
+import { useKeyPress } from "../../hooks/useKeyPress";
+import { debugAtom, pointerAtom } from "../../store";
export function Debug() {
+ const [debug, setDebug] = useAtom(debugAtom);
+
+ useKeyPress("]", () => {
+ setDebug((old) => !old);
+ });
+
+ const arrowRef = useRef(null);
+ const polarGridRef = useRef(null);
+
+ const readPointer = useAtomCallback((get) => {
+ const pointer = get(pointerAtom);
+ return pointer;
+ });
+
+ useFrame(() => {
+ if (!debug) {
+ return;
+ }
+
+ const { point, normal } = readPointer();
+
+ if (arrowRef.current) {
+ arrowRef.current.position.copy(point);
+ arrowRef.current.setDirection(normal);
+ }
+
+ if (polarGridRef.current) {
+ polarGridRef.current.position.copy(point);
+ polarGridRef.current.lookAt(
+ point.clone().add(normal.clone().multiplyScalar(1))
+ );
+ polarGridRef.current.rotateX(Math.PI / 2);
+ polarGridRef.current.updateMatrixWorld();
+ }
+ });
+
+ if (!debug) {
+ return null;
+ }
+
return (
-
+ <>
+
+
+
+
+
+ >
);
}
diff --git a/src/components/ScenePreview/LoadTextureMaps.tsx b/src/components/ScenePreview/LoadTextureMaps.tsx
deleted file mode 100644
index dcef35f..0000000
--- a/src/components/ScenePreview/LoadTextureMaps.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { useTexture } from "@react-three/drei";
-import * as THREE from "three";
-import { useStore } from "../../hooks/useStore";
-
-export function LoadTextureMaps() {
- const setTextureMaps = useStore((state) => state.setTextureMaps);
-
- useTexture({ checkerboard: "/textures/checkerboard.png" }, (textures) => {
- if (Array.isArray(textures)) {
- textures.forEach((texture) => {
- texture.wrapS = THREE.RepeatWrapping;
- texture.wrapT = THREE.RepeatWrapping;
- const url = new URL(texture.source.data.currentSrc);
- texture.name = url.pathname.split("/").pop() as string;
- texture.needsUpdate = true;
- });
- setTextureMaps(textures);
- } else {
- setTextureMaps([textures]);
- }
- });
-
- return null;
-}
diff --git a/src/components/ScenePreview/index.tsx b/src/components/ScenePreview/index.tsx
index b45dde8..bb6854c 100644
--- a/src/components/ScenePreview/index.tsx
+++ b/src/components/ScenePreview/index.tsx
@@ -1,81 +1,73 @@
-import { Suspense } from "react";
-import { Bvh, PerformanceMonitor, useGLTF } from "@react-three/drei";
-import { Canvas } from "@react-three/fiber";
-import { button, folder, useControls } from "leva";
-import { useStore } from "../../hooks/useStore";
-import { Effects } from "../Effects";
+import { BoltIcon } from "@heroicons/react/24/solid";
+import { Bvh, Environment, PerformanceMonitor } from "@react-three/drei";
+import { Canvas, ThreeEvent } from "@react-three/fiber";
+import { useSetAtom } from "jotai";
+import { PointerEvent, Suspense, useCallback } from "react";
+import { toast } from "sonner";
+import * as THREE from "three";
+import { lightsAtom, pointerAtom } from "../../store";
import { Env } from "../Env";
import { Model } from "../Model";
import { Cameras } from "./Cameras";
import { Controls } from "./Controls";
import { Debug } from "./Debug";
-import { LoadTextureMaps } from "./LoadTextureMaps";
import { Lights } from "./Lights";
-import { toast } from "sonner";
-import { BoltIcon } from "@heroicons/react/24/solid";
export function ScenePreview() {
- const mode = useStore((state) => state.mode);
-
- const [{ ambientLightIntensity, debugMaterial, autoRotate }] = useControls(
- () => ({
- Preview: folder(
- {
- ambientLightIntensity: {
- label: "Ambient Intensity",
- value: 0.5,
- min: 0,
- max: 3,
- render: () => mode.scene,
- },
- debugMaterial: {
- label: "Debug Material",
- value: false,
- render: () => mode.scene,
- },
- autoRotate: {
- label: "Auto Rotate",
- value: false,
- render: () => mode.scene,
- },
- Screenshot: button(() => {
- const canvas = document.querySelector("canvas");
- if (canvas) {
- const link = document.createElement("a");
- link.download = "screenshot.png";
- link.href = canvas.toDataURL("image/png", 1);
- link.click();
- }
- }),
- "Upload Model": button(() => {
- const input = document.createElement("input");
- input.type = "file";
- input.accept = ".glb";
- input.onchange = (e) => {
- const file = (e.target as HTMLInputElement).files?.[0];
- if (file) {
- const reader = new FileReader();
- reader.onload = (e) => {
- const data = e.target?.result;
- if (typeof data === "string") {
- const modelUrl = data;
- useGLTF.preload(modelUrl);
- useStore.setState({ modelUrl });
- }
- };
- reader.readAsDataURL(file);
- }
- };
- input.click();
- }),
- },
- {
- order: 1,
- color: "limegreen",
- collapsed: true,
- }
- ),
- })
+ const setLights = useSetAtom(lightsAtom);
+
+ const handleModelClick = useCallback(
+ (e: ThreeEvent) => {
+ e.stopPropagation();
+
+ const cameraPosition = e.camera.position.clone();
+ const point = e.point.clone();
+ const normal =
+ e.face?.normal?.clone()?.transformDirection(e.object.matrixWorld) ??
+ new THREE.Vector3(0, 0, 1);
+
+ // Reflect the camera position across the normal so that the
+ // light is visible in the reflection.
+ const cameraToPoint = point.clone().sub(cameraPosition).normalize();
+ const reflected = cameraToPoint.reflect(normal);
+
+ const spherical = new THREE.Spherical().setFromVector3(reflected);
+
+ const lat = THREE.MathUtils.mapLinear(spherical.phi, 0, Math.PI, 1, -1);
+ const lon = THREE.MathUtils.mapLinear(
+ spherical.theta,
+ 0.5 * Math.PI,
+ -1.5 * Math.PI,
+ -1,
+ 1
+ );
+
+ const { x, y, z } = point;
+ setLights((lights) =>
+ lights.map((l) => ({
+ ...l,
+ target: l.selected ? { x, y, z } : l.target,
+ latlon: l.selected ? { x: lon, y: lat } : l.latlon,
+ ts: Date.now(),
+ }))
+ );
+ },
+ [setLights]
+ );
+
+ const setPointer = useSetAtom(pointerAtom);
+ const handleModelPointerMove = useCallback(
+ (e: ThreeEvent) => {
+ e.stopPropagation();
+
+ const point = e.point.clone();
+ const normal =
+ e.face?.normal?.clone()?.transformDirection(e.object.matrixWorld) ??
+ new THREE.Vector3(0, 0, 1);
+
+ setPointer({ point, normal });
+ },
+ [setPointer]
);
return (
@@ -87,11 +79,12 @@ export function ScenePreview() {
logarithmicDepthBuffer: true,
antialias: true,
}}
+ style={{ touchAction: "none" }}
>
{
toast("Switching to low performance mode", {
description:
@@ -101,26 +94,35 @@ export function ScenePreview() {
}}
>
+
-
+
-
-
-
+
+
+
{/* */}
-
-
-
+
);
diff --git a/src/hooks/useStore.tsx b/src/hooks/useStore.tsx
deleted file mode 100644
index c25fdf3..0000000
--- a/src/hooks/useStore.tsx
+++ /dev/null
@@ -1,253 +0,0 @@
-import * as THREE from "three";
-import create from "zustand";
-import { persist } from "zustand/middleware";
-import { immer } from "zustand/middleware/immer";
-
-export type Camera = {
- id: string;
- name: string;
- position: [number, number, number];
- rotation: [number, number, number];
-};
-
-type BaseLight = {
- id: string;
- name: string;
- shape: "rect" | "circle" | "ring";
- intensity: number;
- scale: number;
- scaleX: number;
- scaleY: number;
- rotation: number;
- distance: number;
- phi: number;
- theta: number;
- target: [number, number, number];
- visible: boolean;
- solo: boolean;
- opacity: number;
- animate: boolean;
- animationSpeed?: number;
- animationRotationIntensity?: number;
- animationFloatIntensity?: number;
- animationFloatingRange?: [number, number];
-};
-
-type SolidLight = BaseLight & {
- type: "solid";
- color: string;
-};
-
-type GradientLight = BaseLight & {
- type: "gradient";
- colorA: string;
- colorB: string;
- contrast: number;
- axes: "x" | "y" | "z";
-};
-
-type NoiseLight = BaseLight & {
- type: "noise";
- algorithm: "perlin" | "simplex" | "cell" | "curl";
- colorA: string;
- colorB: string;
- colorC: string;
- colorD: string;
- noiseScale: number;
- noiseType: "perlin" | "simplex" | "cell" | "curl";
-};
-
-type TextureLight = BaseLight & {
- type: "texture";
- map: THREE.Texture;
-};
-
-export type Light = SolidLight | GradientLight | NoiseLight | TextureLight;
-
-type State = {
- mode: Record<"scene" | "code" | "hdri", boolean>;
- setMode: (mode: State["mode"]) => void;
- modelUrl: string;
- isSolo: boolean;
- textureMaps: THREE.Texture[];
- cameras: Camera[];
- selectedCameraId: string;
- lights: Light[];
- selectedLightId: string | null;
- setTextureMaps: (maps: THREE.Texture[]) => void;
- setSelectedCameraId: (id: string) => void;
- resetSelectedCamera: () => void;
- updateSelectedCamera: (camera: Partial) => void;
- addCamera: (camera: Camera) => void;
- setSelectedLightId: (id: string) => void;
- clearSelectedLight: () => void;
- addLight: (light: Light) => void;
- updateLight: (light: Partial) => void;
- setLightVisibleById: (id: string, visible: boolean) => void;
- toggleLightVisibilityById: (id: string) => void;
- duplicateLightById: (id: string) => void;
- removeLightById: (id: string) => void;
- removeSelectedLight: () => void;
- duplicateSelectedLight: () => void;
- toggleSoloLightById: (id: string) => void;
-};
-
-export const useStore = create()(
- persist(
- immer(
- (set, get) =>
- ({
- mode: { scene: true, hdri: true, code: false },
- setMode: (mode) => set({ mode }),
- modelUrl: "/911-transformed.glb",
- isSolo: false,
- textureMaps: [],
- setTextureMaps: (maps: THREE.Texture[]) =>
- set((state) => void (state.textureMaps = maps)),
- cameras: [
- {
- id: "default",
- name: "Default",
- position: [0, 0, 5],
- rotation: [0, 0, 0],
- },
- ],
- selectedCameraId: "default",
- setSelectedCameraId: (id: string) => set({ selectedCameraId: id }),
- resetSelectedCamera: () => {
- set({ selectedCameraId: "default" });
- },
- addCamera: (camera: Camera) =>
- set((state) => void state.cameras.push(camera)),
-
- updateSelectedCamera: (camera: Partial) =>
- set((state) => ({
- cameras: state.cameras.map((c: Camera) =>
- c.id === get().selectedCameraId ? { ...c, ...camera } : c
- ),
- })),
- lights: [
- {
- name: `Light A`,
- id: THREE.MathUtils.generateUUID(),
- shape: "rect",
- type: "solid",
- color: "#fff",
- distance: 4,
- phi: Math.PI / 2,
- theta: 0,
- intensity: 1,
- rotation: 0,
- scale: 2,
- scaleX: 1,
- scaleY: 1,
- target: [0, 0, 0],
- visible: true,
- solo: false,
- opacity: 1,
- animate: false,
- },
- ],
- selectedLightId: null,
- setSelectedLightId: (id: string) => set({ selectedLightId: id }),
- clearSelectedLight: () => {
- set({ selectedLightId: null });
- },
- addLight: (light: Light) =>
- set((state) => ({
- lights: [...state.lights, light],
- })),
- updateLight: (light: Partial) =>
- set((state) => {
- const targetLight = state.lights.find(
- (l: Light) => l.id === light.id
- );
-
- if (!targetLight) {
- return;
- }
-
- Object.assign(targetLight, light);
- }),
- setLightVisibleById: (id: string, visible: boolean) => {
- const light = get().lights.find((l) => l.id === id);
- if (light) {
- set((state) => {
- const light = state.lights.find((l: Light) => l.id === id);
- if (light) {
- light.visible = visible;
- }
- });
- }
- },
- toggleLightVisibilityById: (id: string) => {
- const state = get();
- const light = state.lights.find((l) => l.id === id);
- if (light) {
- state.setLightVisibleById(id, !light.visible);
- }
- },
- duplicateLightById: (id: string) => {
- const state = get();
- const light = state.lights.find((l) => l.id === id);
- if (light) {
- const newLight = {
- ...light,
- id: THREE.MathUtils.generateUUID(),
- name: `${light.name} (copy)`,
- };
- state.addLight(newLight);
- }
- },
- removeLightById: (id: string) => {
- const state = get();
- const light = state.lights.find((l) => l.id === id);
- if (light) {
- set((state) => ({
- lights: state.lights.filter((l) => l.id !== id),
- selectedLightId:
- state.selectedLightId === id ? null : state.selectedLightId,
- }));
- }
- },
- removeSelectedLight: () => {
- const state = get();
- if (state.selectedLightId) {
- state.removeLightById(state.selectedLightId);
- }
- },
- duplicateSelectedLight: () => {
- const state = get();
- if (state.selectedLightId) {
- state.duplicateLightById(state.selectedLightId);
- }
- },
- toggleSoloLightById: (id: string) => {
- set((state) => {
- const light = state.lights.find((l) => l.id === id);
- if (light) {
- light.solo = !light.solo;
- }
-
- // Check if any lights are soloed
- const soloed = state.lights.some((l) => l.solo);
- if (soloed) {
- // If so, make all lights invisible except the soloed ones
- state.lights.forEach((l) => (l.visible = l.solo));
- state.isSolo = true;
- } else {
- // If not, make all lights visible
- state.lights.forEach((l) => (l.visible = true));
- state.isSolo = false;
- }
- });
- },
- } as State)
- ),
- {
- name: "env-storage",
- version: 4,
- getStorage: () => localStorage,
- }
- )
-);
diff --git a/src/index.css b/src/index.css
index 964a243..56a04e0 100644
--- a/src/index.css
+++ b/src/index.css
@@ -8,4 +8,35 @@ body,
height: 100%;
width: 100%;
margin: 0;
+ overscroll-behavior: none;
+}
+
+:root {
+ --tp-base-background-color: theme("colors.neutral.900");
+ --tp-base-shadow-color: transparent;
+
+ --tp-button-background-color: theme("colors.blue.500");
+ --tp-button-background-color-active: theme("colors.blue.600");
+ --tp-button-background-color-focus: theme("colors.blue.400");
+ --tp-button-background-color-hover: theme("colors.blue.400");
+ --tp-button-foreground-color: theme("colors.white");
+
+ --tp-container-background-color: hsla(0, 0%, 0%, 0.3);
+ --tp-container-background-color-active: hsla(0, 0%, 0%, 0.6);
+ --tp-container-background-color-focus: hsla(0, 0%, 0%, 0.5);
+ --tp-container-background-color-hover: hsla(0, 0%, 0%, 0.4);
+ --tp-container-foreground-color: hsla(0, 0%, 100%, 0.5);
+
+ --tp-groove-foreground-color: theme("colors.neutral.800");
+
+ --tp-input-background-color: theme("colors.neutral.800");
+ --tp-input-background-color-active: theme("colors.neutral.900");
+ --tp-input-background-color-focus: theme("colors.neutral.700");
+ --tp-input-background-color-hover: theme("colors.neutral.700");
+ --tp-input-foreground-color: theme("colors.neutral.400");
+
+ --tp-label-foreground-color: theme("colors.neutral.400");
+
+ --tp-monitor-background-color: theme("colors.neutral.800");
+ --tp-monitor-foreground-color: theme("colors.neutral.400");
}
diff --git a/src/store.tsx b/src/store.tsx
new file mode 100644
index 0000000..e3faf10
--- /dev/null
+++ b/src/store.tsx
@@ -0,0 +1,274 @@
+import * as THREE from "three";
+import { atom } from "jotai";
+import { splitAtom, atomWithStorage } from "jotai/utils";
+
+export type Camera = {
+ id: string;
+ name: string;
+ selected: boolean;
+ position: [number, number, number];
+ rotation: [number, number, number];
+};
+
+type BaseLight = {
+ id: string;
+ ts: number;
+ name: string;
+
+ shape: "rect" | "circle" | "ring";
+ intensity: number;
+ opacity: number;
+
+ scale: number;
+ scaleX: number;
+ scaleY: number;
+ rotation: number;
+
+ latlon: { x: number; y: number };
+ target: { x: number; y: number; z: number };
+
+ selected: boolean;
+ visible: boolean;
+ solo: boolean;
+
+ animate: boolean;
+ animationSpeed?: number;
+ animationRotationIntensity?: number;
+ animationFloatIntensity?: number;
+ animationFloatingRange?: [number, number];
+};
+
+export type TextureLight = BaseLight & {
+ type: "texture";
+ color: string;
+ map: string;
+};
+
+export type ProceduralScrimLight = BaseLight & {
+ type: "procedural_scrim";
+ color: string;
+ lightPosition: { x: number; y: number };
+ lightDistance: number;
+};
+
+export type ProceduralUmbrellaLight = BaseLight & {
+ type: "procedural_umbrella";
+ color: string;
+ lightSides: number;
+};
+
+export type SkyGradientLight = BaseLight & {
+ type: "sky_gradient";
+ color: string;
+ color2: string;
+};
+
+export type Light =
+ | TextureLight
+ | ProceduralScrimLight
+ | ProceduralUmbrellaLight
+ | SkyGradientLight;
+
+export const debugAtom = atom(false);
+
+export const modeAtom = atomWithStorage("mode", {
+ scene: true,
+ hdri: true,
+ code: false,
+});
+
+export const activeModesAtom = atom((get) => {
+ const mode = get(modeAtom);
+ return Object.keys(mode).filter((key) => mode[key as keyof typeof mode]);
+});
+
+export const modelUrlAtom = atom("/911-transformed.glb");
+
+export const isCommandPaletteOpenAtom = atom(false);
+
+export const pointerAtom = atom({
+ point: new THREE.Vector3(),
+ normal: new THREE.Vector3(),
+});
+
+export const lightsAtom = atomWithStorage("lights", [
+ {
+ name: `Light A`,
+ id: THREE.MathUtils.generateUUID(),
+ ts: Date.now(),
+ shape: "rect",
+ type: "procedural_scrim",
+ color: "#fff",
+ latlon: { x: 0, y: 0 },
+ intensity: 1,
+ rotation: 0,
+ scale: 2,
+ scaleX: 1,
+ scaleY: 1,
+ target: { x: 0, y: 0, z: 0 },
+ selected: false,
+ visible: true,
+ solo: false,
+ opacity: 1,
+ animate: false,
+ lightDistance: 0.3,
+ lightPosition: { x: 0, y: 0 },
+ },
+]);
+
+export const lightIdsAtom = atom((get) => get(lightsAtom).map((l) => l.id));
+
+export const lightAtomsAtom = splitAtom(lightsAtom);
+
+export const isSoloAtom = atom((get) => {
+ const lights = get(lightsAtom);
+ return lights.length > 0 && lights.some((l) => l.solo);
+});
+
+export const isLightSelectedAtom = atom((get) => {
+ const lights = get(lightsAtom);
+ return lights.length > 0 && lights.some((l) => l.selected);
+});
+
+export const selectLightAtom = atom(null, (get, set, lightId: Light["id"]) => {
+ set(lightsAtom, (lights) =>
+ lights.map((l) => ({
+ ...l,
+ selected: l.id === lightId,
+ }))
+ );
+});
+
+export const toggleSoloAtom = atom(null, (get, set, lightId: Light["id"]) => {
+ const lights = get(lightsAtom);
+ const light = lights.find((l) => l.id === lightId)!;
+ const isSolo = get(isSoloAtom);
+
+ if (isSolo && light.solo) {
+ set(
+ lightsAtom,
+ lights.map((l) => ({
+ ...l,
+ solo: false,
+ visible: true,
+ }))
+ );
+ } else {
+ set(
+ lightsAtom,
+ lights.map((l) => ({
+ ...l,
+ solo: l.id === lightId,
+ visible: l.id === lightId,
+ selected: l.id === lightId,
+ }))
+ );
+ }
+});
+
+export const toggleLightSelectionAtom = atom(
+ null,
+ (get, set, lightId: Light["id"]) => {
+ set(lightsAtom, (lights) =>
+ lights.map((l) => ({
+ ...l,
+ selected: l.id === lightId ? !l.selected : false,
+ }))
+ );
+ }
+);
+
+export const duplicateLightAtom = atom(
+ null,
+ (get, set, lightId: Light["id"]) => {
+ const lights = get(lightsAtom);
+ const light = lights.find((l) => l.id === lightId)!;
+ const isSolo = get(isSoloAtom);
+ const newLight = {
+ ...structuredClone(light),
+ visible: isSolo ? false : light.visible,
+ solo: false,
+ selected: false,
+ id: THREE.MathUtils.generateUUID(),
+ name: `${light.name} (copy)`,
+ };
+ set(lightsAtom, [...lights, newLight]);
+ }
+);
+
+export const deleteLightAtom = atom(null, (get, set, lightId: Light["id"]) => {
+ const lights = get(lightsAtom);
+ const light = lights.find((l) => l.id === lightId)!;
+ const isSolo = get(isSoloAtom);
+
+ const newLights = lights.filter((l) => l.id !== lightId);
+
+ if (isSolo && light.solo) {
+ set(
+ lightsAtom,
+ newLights.map((l) => ({
+ ...l,
+ solo: false,
+ visible: true,
+ }))
+ );
+ } else {
+ set(lightsAtom, newLights);
+ }
+});
+
+export const camerasAtom = atomWithStorage("cameras", [
+ {
+ id: "default",
+ name: "Default",
+ selected: true,
+ position: [0, 0, 5],
+ rotation: [0, 0, 0],
+ },
+]);
+
+export const cameraAtomsAtom = splitAtom(camerasAtom);
+
+export const selectedCameraAtom = atom(
+ (get) => {
+ const cameras = get(camerasAtom);
+ return cameras.find((c) => c.selected)!;
+ },
+ (get, set, value: Partial) => {
+ const cameras = get(camerasAtom);
+ const selectedCamera = cameras.find((c) => c.selected)!;
+ set(
+ camerasAtom,
+ cameras.map((c) => (c.id === selectedCamera.id ? { ...c, ...value } : c))
+ );
+ }
+);
+
+export const isCameraSelectedAtom = atom((get) => {
+ const cameras = get(camerasAtom);
+ return cameras.length > 0 && cameras.some((c) => c.selected);
+});
+
+export const toggleCameraSelectionAtom = atom(
+ null,
+ (get, set, cameraId: Camera["id"]) => {
+ set(camerasAtom, (cameras) =>
+ cameras.map((c) => ({
+ ...c,
+ selected: c.id === cameraId ? !c.selected : false,
+ }))
+ );
+ }
+);
+
+export const selectCameraAtom = atom(
+ null,
+ (get, set, cameraId: Camera["id"]) => {
+ set(camerasAtom, (cameras) =>
+ cameras.map((c) => ({
+ ...c,
+ selected: c.id === cameraId,
+ }))
+ );
+ }
+);
diff --git a/src/utils/coordinates.ts b/src/utils/coordinates.ts
new file mode 100644
index 0000000..bb2acbb
--- /dev/null
+++ b/src/utils/coordinates.ts
@@ -0,0 +1,16 @@
+import * as THREE from "three";
+
+export function latlonToPhiTheta(latlon: { x: number; y: number }): {
+ phi: number;
+ theta: number;
+} {
+ const phi = THREE.MathUtils.mapLinear(latlon.y, -1, 1, Math.PI, 0);
+ const theta = THREE.MathUtils.mapLinear(
+ latlon.x,
+ -1,
+ 1,
+ 0.5 * Math.PI,
+ -1.5 * Math.PI
+ );
+ return { phi, theta };
+}
diff --git a/tailwind.config.cjs b/tailwind.config.cjs
index 2c527f2..9d973e9 100644
--- a/tailwind.config.cjs
+++ b/tailwind.config.cjs
@@ -4,7 +4,50 @@ const plugin = require("tailwindcss/plugin");
module.exports = {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
- extend: {},
+ extend: {
+ gridColumn: {
+ "span-13": "span 13 / span 13",
+ "span-14": "span 14 / span 14",
+ "span-15": "span 15 / span 15",
+ "span-16": "span 16 / span 16",
+ "span-17": "span 17 / span 17",
+ "span-18": "span 18 / span 18",
+ "span-19": "span 19 / span 19",
+ "span-20": "span 20 / span 20",
+ "span-21": "span 21 / span 21",
+ "span-22": "span 22 / span 22",
+ "span-23": "span 23 / span 23",
+ "span-24": "span 24 / span 24",
+ },
+ gridColumnStart: {
+ 13: "13",
+ 14: "14",
+ 15: "15",
+ 16: "16",
+ 17: "17",
+ 18: "18",
+ 19: "19",
+ 20: "20",
+ 21: "21",
+ 22: "22",
+ 23: "23",
+ 24: "24",
+ },
+ gridColumnEnd: {
+ 13: "13",
+ 14: "14",
+ 15: "15",
+ 16: "16",
+ 17: "17",
+ 18: "18",
+ 19: "19",
+ 20: "20",
+ 21: "21",
+ 22: "22",
+ 23: "23",
+ 24: "24",
+ },
+ },
},
plugins: [
plugin(({ addVariant }) => {
diff --git a/vite.config.ts b/vite.config.ts
index b1b5f91..c2f13ca 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,7 +1,11 @@
-import { defineConfig } from 'vite'
-import react from '@vitejs/plugin-react'
+import { defineConfig } from "vite";
+import react from "@vitejs/plugin-react";
+import jotaiDebugLabel from "jotai/babel/plugin-debug-label";
+import jotaiReactRefresh from "jotai/babel/plugin-react-refresh";
// https://vitejs.dev/config/
export default defineConfig({
- plugins: [react()]
-})
+ plugins: [
+ react({ babel: { plugins: [jotaiDebugLabel, jotaiReactRefresh] } }),
+ ],
+});
diff --git a/yarn.lock b/yarn.lock
index 21e7c09..6dddcde 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -347,6 +347,55 @@ __metadata:
languageName: node
linkType: hard
+"@dnd-kit/accessibility@npm:^3.0.0":
+ version: 3.0.1
+ resolution: "@dnd-kit/accessibility@npm:3.0.1"
+ dependencies:
+ tslib: ^2.0.0
+ peerDependencies:
+ react: ">=16.8.0"
+ checksum: 0afc2c0fce9a1c107453620ca0da1778f182d340e74ffbc6e369ef0ac8943cafb929d3a6c0891d9b915aa23b2b92137ff4fad958f43118466586d8129a3359d5
+ languageName: node
+ linkType: hard
+
+"@dnd-kit/core@npm:^6.0.8":
+ version: 6.0.8
+ resolution: "@dnd-kit/core@npm:6.0.8"
+ dependencies:
+ "@dnd-kit/accessibility": ^3.0.0
+ "@dnd-kit/utilities": ^3.2.1
+ tslib: ^2.0.0
+ peerDependencies:
+ react: ">=16.8.0"
+ react-dom: ">=16.8.0"
+ checksum: abe48ff7395f84fd8c15e6c8b13da4df153dc1f1076096d783acd0c25539516c77e4854ea59be6621dde55739cb0df1d62924ad069df3267fe05ad90ef729b2f
+ languageName: node
+ linkType: hard
+
+"@dnd-kit/sortable@npm:^7.0.2":
+ version: 7.0.2
+ resolution: "@dnd-kit/sortable@npm:7.0.2"
+ dependencies:
+ "@dnd-kit/utilities": ^3.2.0
+ tslib: ^2.0.0
+ peerDependencies:
+ "@dnd-kit/core": ^6.0.7
+ react: ">=16.8.0"
+ checksum: 4ce705aceb15766a0deefe25a9d95a87e9413c3fb9088ea3eb0962e57f844895000117fcec7c0944a0d4ae4e1e889cfa69e3d3778164d4d23115fb1edb218283
+ languageName: node
+ linkType: hard
+
+"@dnd-kit/utilities@npm:^3.2.0, @dnd-kit/utilities@npm:^3.2.1":
+ version: 3.2.1
+ resolution: "@dnd-kit/utilities@npm:3.2.1"
+ dependencies:
+ tslib: ^2.0.0
+ peerDependencies:
+ react: ">=16.8.0"
+ checksum: 038fd5cc1328bf4c9dca17cd48046e5a687bbf9d904c7197f851aab869ab52d9dee2734b2e255256fd6158245acd00063a23deed962c7673c0fadfbf061f04ca
+ languageName: node
+ linkType: hard
+
"@esbuild/android-arm64@npm:0.17.19":
version: 0.17.19
resolution: "@esbuild/android-arm64@npm:0.17.19"
@@ -663,25 +712,6 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/popper@npm:0.1.0":
- version: 0.1.0
- resolution: "@radix-ui/popper@npm:0.1.0"
- dependencies:
- "@babel/runtime": ^7.13.10
- csstype: ^3.0.4
- checksum: f362a9fb8ed8db7ae9d697c9847d7b709d77e8630964c4bf6de458cac14822bab9c0e35aa3f2ea600a556cae49d025941a8bcee689b8a5fa18f8a0084576640c
- languageName: node
- linkType: hard
-
-"@radix-ui/primitive@npm:0.1.0":
- version: 0.1.0
- resolution: "@radix-ui/primitive@npm:0.1.0"
- dependencies:
- "@babel/runtime": ^7.13.10
- checksum: 5f721bcfebb2482fc2d034d2782219f4b1035977d3a1bd854719ff07c82fb545083ff1247a987ea0218109c5801375724f60910b0c71f7bb78ea0ab21b2bcb26
- languageName: node
- linkType: hard
-
"@radix-ui/primitive@npm:1.0.0":
version: 1.0.0
resolution: "@radix-ui/primitive@npm:1.0.0"
@@ -691,18 +721,6 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/react-arrow@npm:0.1.3":
- version: 0.1.3
- resolution: "@radix-ui/react-arrow@npm:0.1.3"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-primitive": 0.1.3
- peerDependencies:
- react: ^16.8 || ^17.0
- checksum: d8dcb858454653f99f63e41993b9073bbc7b617e4eff6e271a722fc3f19f063e7e90ef549b132fd8dd4f0c4e8b91669f602863e9850799e71fb3be1f2fd9835d
- languageName: node
- linkType: hard
-
"@radix-ui/react-arrow@npm:1.0.0":
version: 1.0.0
resolution: "@radix-ui/react-arrow@npm:1.0.0"
@@ -748,17 +766,6 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/react-compose-refs@npm:0.1.0":
- version: 0.1.0
- resolution: "@radix-ui/react-compose-refs@npm:0.1.0"
- dependencies:
- "@babel/runtime": ^7.13.10
- peerDependencies:
- react: ^16.8 || ^17.0
- checksum: d1455577b2afee141998e847890e8f5ba5cb17aa58ba699f9abe21c7948e2435bbda28f7f7efe825ca200c66bcaf095ff4b93553778d599cba3f611c97cd222e
- languageName: node
- linkType: hard
-
"@radix-ui/react-compose-refs@npm:1.0.0":
version: 1.0.0
resolution: "@radix-ui/react-compose-refs@npm:1.0.0"
@@ -788,17 +795,6 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/react-context@npm:0.1.1":
- version: 0.1.1
- resolution: "@radix-ui/react-context@npm:0.1.1"
- dependencies:
- "@babel/runtime": ^7.13.10
- peerDependencies:
- react: ^16.8 || ^17.0
- checksum: 85ed35b6e386706bc3a8d21ff7e2a55d0f452fd8ab89f6c9a6c2e271e390c8788800517589d5606a3bfbcca08741fbcb4b6c695c466a284ae35957d92620c467
- languageName: node
- linkType: hard
-
"@radix-ui/react-context@npm:1.0.0":
version: 1.0.0
resolution: "@radix-ui/react-context@npm:1.0.0"
@@ -899,18 +895,6 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/react-id@npm:0.1.4":
- version: 0.1.4
- resolution: "@radix-ui/react-id@npm:0.1.4"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-use-layout-effect": 0.1.0
- peerDependencies:
- react: ^16.8 || ^17.0
- checksum: 09ed338c1039e5ce6cb1c7d5c288643aa4e0dbf0a3066e9da6cd468893061907de7346a97590b63a9aab42707bf960be2c7314c425269af33209a636297c6184
- languageName: node
- linkType: hard
-
"@radix-ui/react-id@npm:1.0.0":
version: 1.0.0
resolution: "@radix-ui/react-id@npm:1.0.0"
@@ -953,25 +937,6 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/react-popper@npm:0.1.3":
- version: 0.1.3
- resolution: "@radix-ui/react-popper@npm:0.1.3"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/popper": 0.1.0
- "@radix-ui/react-arrow": 0.1.3
- "@radix-ui/react-compose-refs": 0.1.0
- "@radix-ui/react-context": 0.1.1
- "@radix-ui/react-primitive": 0.1.3
- "@radix-ui/react-use-rect": 0.1.1
- "@radix-ui/react-use-size": 0.1.0
- "@radix-ui/rect": 0.1.1
- peerDependencies:
- react: ^16.8 || ^17.0
- checksum: f057a1c58372ca9c1877facb2284cc135b48467ef7a835ca9ce02d3ecd732316a1cfd90aa28db75684a7b6be015316d8af435daf793c76d778041a2efcd4a556
- languageName: node
- linkType: hard
-
"@radix-ui/react-popper@npm:1.0.0":
version: 1.0.0
resolution: "@radix-ui/react-popper@npm:1.0.0"
@@ -993,20 +958,6 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/react-portal@npm:0.1.3":
- version: 0.1.3
- resolution: "@radix-ui/react-portal@npm:0.1.3"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-primitive": 0.1.3
- "@radix-ui/react-use-layout-effect": 0.1.0
- peerDependencies:
- react: ^16.8 || ^17.0
- react-dom: ^16.8 || ^17.0
- checksum: 56836fdff1011c6e68baa948c89db2a9f3de2eaa54e9dff5887a1c78769f3ec66bc37c5b2f231f832ce76e82cb3528588b1e321f1bb9bc30ce4e9f7fd30acff0
- languageName: node
- linkType: hard
-
"@radix-ui/react-portal@npm:1.0.0":
version: 1.0.0
resolution: "@radix-ui/react-portal@npm:1.0.0"
@@ -1020,33 +971,6 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/react-portal@npm:^0.1.3":
- version: 0.1.4
- resolution: "@radix-ui/react-portal@npm:0.1.4"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-primitive": 0.1.4
- "@radix-ui/react-use-layout-effect": 0.1.0
- peerDependencies:
- react: ^16.8 || ^17.0
- react-dom: ^16.8 || ^17.0
- checksum: fb047d56c46711ed1d5146b21f9b77614556d578e55d8c3a39c5014e2883796091a3baeb3d3f8642209a0ff062bf6fd175a666f6f35dd154492ce06761824df3
- languageName: node
- linkType: hard
-
-"@radix-ui/react-presence@npm:0.1.1":
- version: 0.1.1
- resolution: "@radix-ui/react-presence@npm:0.1.1"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-compose-refs": 0.1.0
- "@radix-ui/react-use-layout-effect": 0.1.0
- peerDependencies:
- react: ">=16.8"
- checksum: b8911eb908111135b585fc09500b9582039a53166bf1e59103df083534da5c8d7277c080bd45c270ae70510d41046a15c9ac357094b8588d26eacf9102de1dc6
- languageName: node
- linkType: hard
-
"@radix-ui/react-presence@npm:1.0.0":
version: 1.0.0
resolution: "@radix-ui/react-presence@npm:1.0.0"
@@ -1061,30 +985,6 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/react-primitive@npm:0.1.3":
- version: 0.1.3
- resolution: "@radix-ui/react-primitive@npm:0.1.3"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-slot": 0.1.2
- peerDependencies:
- react: ^16.8 || ^17.0
- checksum: 2cce826e9ec5afbb0251d4ef0f053b87a4cb64694766a07ac4aebc747e734caf4443bc3b7faaea9a71d475cba0d5699a56b658a8a09c0dd89b2c70174c155028
- languageName: node
- linkType: hard
-
-"@radix-ui/react-primitive@npm:0.1.4":
- version: 0.1.4
- resolution: "@radix-ui/react-primitive@npm:0.1.4"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-slot": 0.1.2
- peerDependencies:
- react: ^16.8 || ^17.0
- checksum: e7b83dc51565a7a54dfd16296e2aa1639dafe32655e3a3974d29d28497f0e9ec9cdf0ee59bc54a88b2a51eeb307781f01f6fcacb4d6dc84a8e10631ddb6142e5
- languageName: node
- linkType: hard
-
"@radix-ui/react-primitive@npm:1.0.0":
version: 1.0.0
resolution: "@radix-ui/react-primitive@npm:1.0.0"
@@ -1166,18 +1066,6 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/react-slot@npm:0.1.2":
- version: 0.1.2
- resolution: "@radix-ui/react-slot@npm:0.1.2"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-compose-refs": 0.1.0
- peerDependencies:
- react: ^16.8 || ^17.0
- checksum: 216927b9b1dae28328d630f6b2c91f1a424c0b00fb4efcebb7a109fdfc5bceda5cf878dfac5baa8aa441150d4c5263f5a914f2962bbce8375972ae076e4d3b65
- languageName: node
- linkType: hard
-
"@radix-ui/react-slot@npm:1.0.0":
version: 1.0.0
resolution: "@radix-ui/react-slot@npm:1.0.0"
@@ -1255,43 +1143,6 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/react-tooltip@npm:0.1.6":
- version: 0.1.6
- resolution: "@radix-ui/react-tooltip@npm:0.1.6"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/primitive": 0.1.0
- "@radix-ui/react-compose-refs": 0.1.0
- "@radix-ui/react-context": 0.1.1
- "@radix-ui/react-id": 0.1.4
- "@radix-ui/react-popper": 0.1.3
- "@radix-ui/react-portal": 0.1.3
- "@radix-ui/react-presence": 0.1.1
- "@radix-ui/react-primitive": 0.1.3
- "@radix-ui/react-slot": 0.1.2
- "@radix-ui/react-use-controllable-state": 0.1.0
- "@radix-ui/react-use-escape-keydown": 0.1.0
- "@radix-ui/react-use-previous": 0.1.0
- "@radix-ui/react-use-rect": 0.1.1
- "@radix-ui/react-visually-hidden": 0.1.3
- peerDependencies:
- react: ^16.8 || ^17.0
- react-dom: ^16.8 || ^17.0
- checksum: fa409aec05043906305b176019a8fc5d538af0490b623877c47e9e1fc159adf178306a42212566d009837729a276164fcd5e126a4e2320cea573ba6b3e26c4de
- languageName: node
- linkType: hard
-
-"@radix-ui/react-use-callback-ref@npm:0.1.0":
- version: 0.1.0
- resolution: "@radix-ui/react-use-callback-ref@npm:0.1.0"
- dependencies:
- "@babel/runtime": ^7.13.10
- peerDependencies:
- react: ^16.8 || ^17.0
- checksum: 5356971123d7bbc66a208eca4709483190d0927a6817089f885d4538cd701a174d76830ba36cfaa6336b340415aaefaddc606a575246b0cbcb4b1f2897075203
- languageName: node
- linkType: hard
-
"@radix-ui/react-use-callback-ref@npm:1.0.0":
version: 1.0.0
resolution: "@radix-ui/react-use-callback-ref@npm:1.0.0"
@@ -1303,18 +1154,6 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/react-use-controllable-state@npm:0.1.0":
- version: 0.1.0
- resolution: "@radix-ui/react-use-controllable-state@npm:0.1.0"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-use-callback-ref": 0.1.0
- peerDependencies:
- react: ^16.8 || ^17.0
- checksum: 2ddd05854af227b74e8b4a76d0d3c49e83b481b059fad68b769f76b46faa4db8eeb68e1ccf15cf6c4c54a89e6debc6440ee492ccac64570bdf12173e49b2fddc
- languageName: node
- linkType: hard
-
"@radix-ui/react-use-controllable-state@npm:1.0.0":
version: 1.0.0
resolution: "@radix-ui/react-use-controllable-state@npm:1.0.0"
@@ -1327,18 +1166,6 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/react-use-escape-keydown@npm:0.1.0":
- version: 0.1.0
- resolution: "@radix-ui/react-use-escape-keydown@npm:0.1.0"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-use-callback-ref": 0.1.0
- peerDependencies:
- react: ^16.8 || ^17.0
- checksum: b92769ecf49eac072c95898e230f9066385b6623d5af8d8f6a322a84bac4bcfab149eb3321fc363dad1d8b1b9706dcdde461a5423bc77f4afffba346b2f11ea3
- languageName: node
- linkType: hard
-
"@radix-ui/react-use-escape-keydown@npm:1.0.0":
version: 1.0.0
resolution: "@radix-ui/react-use-escape-keydown@npm:1.0.0"
@@ -1351,17 +1178,6 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/react-use-layout-effect@npm:0.1.0":
- version: 0.1.0
- resolution: "@radix-ui/react-use-layout-effect@npm:0.1.0"
- dependencies:
- "@babel/runtime": ^7.13.10
- peerDependencies:
- react: ^16.8 || ^17.0
- checksum: d8be1f97706dec2dcdf98284ad04a115898338dd34f68d61cf9bfda87d88c694019576313a235202b05be3a56ab6453fcee44d651f6b8a502a0cd2dbde153f49
- languageName: node
- linkType: hard
-
"@radix-ui/react-use-layout-effect@npm:1.0.0":
version: 1.0.0
resolution: "@radix-ui/react-use-layout-effect@npm:1.0.0"
@@ -1373,29 +1189,6 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/react-use-previous@npm:0.1.0":
- version: 0.1.0
- resolution: "@radix-ui/react-use-previous@npm:0.1.0"
- dependencies:
- "@babel/runtime": ^7.13.10
- peerDependencies:
- react: ^16.8 || ^17.0
- checksum: 3189d18c5c37bb94c576bb71008c07a2ee935b157c1f94ba7ded0e85df5926a06deb966bc39dfeea1d3f96a80d9504ade36d2e34dbca382767e07a4eea09cfed
- languageName: node
- linkType: hard
-
-"@radix-ui/react-use-rect@npm:0.1.1":
- version: 0.1.1
- resolution: "@radix-ui/react-use-rect@npm:0.1.1"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/rect": 0.1.1
- peerDependencies:
- react: ^16.8 || ^17.0
- checksum: aacf81074482e71661a61bbc14e2bc4a227903e0461465a25dd4b36be0a2eebc6b326ad2f1cd90240d74f6e795cfd72fed0433d94b61f8c275fd75626405946a
- languageName: node
- linkType: hard
-
"@radix-ui/react-use-rect@npm:1.0.0":
version: 1.0.0
resolution: "@radix-ui/react-use-rect@npm:1.0.0"
@@ -1408,17 +1201,6 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/react-use-size@npm:0.1.0":
- version: 0.1.0
- resolution: "@radix-ui/react-use-size@npm:0.1.0"
- dependencies:
- "@babel/runtime": ^7.13.10
- peerDependencies:
- react: ^16.8 || ^17.0
- checksum: 29134b0caf3109e4c8c5f6b9b6d6c1dd0da4900c899ac4f7e7efecd903f06b8ffd6cf9e42bef464e3d7788bbdaa569b1ea0ff4920d2e93d843d53ce6b3815628
- languageName: node
- linkType: hard
-
"@radix-ui/react-use-size@npm:1.0.0":
version: 1.0.0
resolution: "@radix-ui/react-use-size@npm:1.0.0"
@@ -1431,27 +1213,6 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/react-visually-hidden@npm:0.1.3":
- version: 0.1.3
- resolution: "@radix-ui/react-visually-hidden@npm:0.1.3"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-primitive": 0.1.3
- peerDependencies:
- react: ^16.8 || ^17.0
- checksum: 4434bc95244c8224af5eb4e37f6278da96df1bce05e85ea7c4fecef1ed9c0ed3252e1269c7f979b9c115a9c75ad057a028078abc42ebebddcde0b97a0e06f92d
- languageName: node
- linkType: hard
-
-"@radix-ui/rect@npm:0.1.1":
- version: 0.1.1
- resolution: "@radix-ui/rect@npm:0.1.1"
- dependencies:
- "@babel/runtime": ^7.13.10
- checksum: 6f781fe3f6546930a69de7f3763593c1fbaffd17f03ec613c78946344769c157ebf4d8a5e4eb36e31c7ff51fbff6f7e9b27a3e9f1f411fc6a4528f439b2fba96
- languageName: node
- linkType: hard
-
"@radix-ui/rect@npm:1.0.0":
version: 1.0.0
resolution: "@radix-ui/rect@npm:1.0.0"
@@ -1618,7 +1379,7 @@ __metadata:
languageName: node
linkType: hard
-"@stitches/react@npm:1.2.8, @stitches/react@npm:^1.2.8":
+"@stitches/react@npm:^1.2.8":
version: 1.2.8
resolution: "@stitches/react@npm:1.2.8"
peerDependencies:
@@ -1634,6 +1395,13 @@ __metadata:
languageName: node
linkType: hard
+"@tweakpane/core@npm:^2.0.0":
+ version: 2.0.0
+ resolution: "@tweakpane/core@npm:2.0.0"
+ checksum: c92cb9d1d12717e5d7eea1e8440b63ef1edeff3cd0304fd1a23229ef79348951c9a2bea13a15d612330d22dca690534cd342d7823135ab8acee90222523ccc3d
+ languageName: node
+ linkType: hard
+
"@tweenjs/tween.js@npm:~18.6.4":
version: 18.6.4
resolution: "@tweenjs/tween.js@npm:18.6.4"
@@ -1748,13 +1516,6 @@ __metadata:
languageName: node
linkType: hard
-"@use-gesture/core@npm:10.2.19":
- version: 10.2.19
- resolution: "@use-gesture/core@npm:10.2.19"
- checksum: 2ae4d0f3e63e5f679129ff9c3b29fc80fee42e63080063c299fc841deb7ac3608b699e429887da0d6ca212fb3ad1043f93d4dc8d40b30824fa70b35795ecea39
- languageName: node
- linkType: hard
-
"@use-gesture/core@npm:10.2.27":
version: 10.2.27
resolution: "@use-gesture/core@npm:10.2.27"
@@ -1773,17 +1534,6 @@ __metadata:
languageName: node
linkType: hard
-"@use-gesture/react@npm:^10.2.5":
- version: 10.2.19
- resolution: "@use-gesture/react@npm:10.2.19"
- dependencies:
- "@use-gesture/core": 10.2.19
- peerDependencies:
- react: ">= 16.8.0"
- checksum: 25835f884382d331d08bb2397e19d14d6319c4969570151317fca7708bec26bd00d0b227e6951913653e324fda353252d854abdcb31b4580172ff8dc608fe31a
- languageName: node
- linkType: hard
-
"@utsubo/events@npm:^0.1.7":
version: 0.1.7
resolution: "@utsubo/events@npm:0.1.7"
@@ -1812,17 +1562,6 @@ __metadata:
languageName: node
linkType: hard
-"@welldone-software/why-did-you-render@npm:^6.2.3":
- version: 6.2.3
- resolution: "@welldone-software/why-did-you-render@npm:6.2.3"
- dependencies:
- lodash: ^4
- peerDependencies:
- react: ^16 || ^17
- checksum: 6c012f0a67c94c19cd2e8221438d547990a26b0593b0d1e8fd57084253d7cd305f4904e5bda984fd38416390b35b95fb0c3631fa485bd41b0b317568f080376c
- languageName: node
- linkType: hard
-
"abbrev@npm:1":
version: 1.1.1
resolution: "abbrev@npm:1.1.1"
@@ -1952,20 +1691,6 @@ __metadata:
languageName: node
linkType: hard
-"assign-symbols@npm:^1.0.0":
- version: 1.0.0
- resolution: "assign-symbols@npm:1.0.0"
- checksum: c0eb895911d05b6b2d245154f70461c5e42c107457972e5ebba38d48967870dee53bcdf6c7047990586daa80fab8dab3cc6300800fbd47b454247fdedd859a2c
- languageName: node
- linkType: hard
-
-"attr-accept@npm:^2.2.2":
- version: 2.2.2
- resolution: "attr-accept@npm:2.2.2"
- checksum: 496f7249354ab53e522510c1dc8f67a1887382187adde4dc205507d2f014836a247073b05e9d9ea51e2e9c7f71b0d2aa21730af80efa9af2d68303e5f0565c4d
- languageName: node
- linkType: hard
-
"autoprefixer@npm:^10.4.13":
version: 10.4.13
resolution: "autoprefixer@npm:10.4.13"
@@ -2229,13 +1954,6 @@ __metadata:
languageName: node
linkType: hard
-"colord@npm:^2.9.2":
- version: 2.9.3
- resolution: "colord@npm:2.9.3"
- checksum: 95d909bfbcfd8d5605cbb5af56f2d1ce2b323990258fd7c0d2eb0e6d3bb177254d7fb8213758db56bb4ede708964f78c6b992b326615f81a18a6aaf11d64c650
- languageName: node
- linkType: hard
-
"command-score@npm:0.1.2":
version: 0.1.2
resolution: "command-score@npm:0.1.2"
@@ -2266,13 +1984,6 @@ __metadata:
languageName: node
linkType: hard
-"core-util-is@npm:~1.0.0":
- version: 1.0.3
- resolution: "core-util-is@npm:1.0.3"
- checksum: 9de8597363a8e9b9952491ebe18167e3b36e7707569eed0ebf14f8bba773611376466ae34575bca8cfe3c767890c859c74056084738f09d4e4a6f902b2ad7d99
- languageName: node
- linkType: hard
-
"cssesc@npm:^3.0.0":
version: 3.0.0
resolution: "cssesc@npm:3.0.0"
@@ -2282,7 +1993,7 @@ __metadata:
languageName: node
linkType: hard
-"csstype@npm:^3.0.2, csstype@npm:^3.0.4":
+"csstype@npm:^3.0.2":
version: 3.1.0
resolution: "csstype@npm:3.1.0"
checksum: 644e986cefab86525f0b674a06889cfdbb1f117e5b7d1ce0fc55b0423ecc58807a1ea42ecc75c4f18999d14fc42d1d255f84662a45003a52bb5840e977eb2ffd
@@ -2329,13 +2040,6 @@ __metadata:
languageName: node
linkType: hard
-"dequal@npm:^2.0.2":
- version: 2.0.3
- resolution: "dequal@npm:2.0.3"
- checksum: 8679b850e1a3d0ebbc46ee780d5df7b478c23f335887464023a631d1b9af051ad4a6595a44220f9ff8ff95a8ddccf019b5ad778a976fd7bbf77383d36f412f90
- languageName: node
- linkType: hard
-
"detect-gpu@npm:^5.0.14":
version: 5.0.28
resolution: "detect-gpu@npm:5.0.28"
@@ -2528,25 +2232,6 @@ __metadata:
languageName: node
linkType: hard
-"extend-shallow@npm:^2.0.1":
- version: 2.0.1
- resolution: "extend-shallow@npm:2.0.1"
- dependencies:
- is-extendable: ^0.1.0
- checksum: 8fb58d9d7a511f4baf78d383e637bd7d2e80843bd9cd0853649108ea835208fb614da502a553acc30208e1325240bb7cc4a68473021612496bb89725483656d8
- languageName: node
- linkType: hard
-
-"extend-shallow@npm:^3.0.0":
- version: 3.0.2
- resolution: "extend-shallow@npm:3.0.2"
- dependencies:
- assign-symbols: ^1.0.0
- is-extendable: ^1.0.1
- checksum: a920b0cd5838a9995ace31dfd11ab5e79bf6e295aa566910ce53dff19f4b1c0fda2ef21f26b28586c7a2450ca2b42d97bd8c0f5cec9351a819222bf861e02461
- languageName: node
- linkType: hard
-
"fast-glob@npm:^3.2.12":
version: 3.2.12
resolution: "fast-glob@npm:3.2.12"
@@ -2576,15 +2261,6 @@ __metadata:
languageName: node
linkType: hard
-"file-selector@npm:^0.5.0":
- version: 0.5.0
- resolution: "file-selector@npm:0.5.0"
- dependencies:
- tslib: ^2.0.3
- checksum: f95a06938123a2b765d136a4430cc8b19165f06a53e7ae1dcca4947716d61e9181453fcfeb2358c2660cbcecf96d7334f0528ba60071fed81f8bd358ea08454a
- languageName: node
- linkType: hard
-
"fill-range@npm:^7.0.1":
version: 7.0.1
resolution: "fill-range@npm:7.0.1"
@@ -2594,13 +2270,6 @@ __metadata:
languageName: node
linkType: hard
-"for-in@npm:^1.0.2":
- version: 1.0.2
- resolution: "for-in@npm:1.0.2"
- checksum: 09f4ae93ce785d253ac963d94c7f3432d89398bf25ac7a24ed034ca393bf74380bdeccc40e0f2d721a895e54211b07c8fad7132e8157827f6f7f059b70b4043d
- languageName: node
- linkType: hard
-
"fraction.js@npm:^4.2.0":
version: 4.2.0
resolution: "fraction.js@npm:4.2.0"
@@ -2680,13 +2349,6 @@ __metadata:
languageName: node
linkType: hard
-"get-value@npm:^2.0.6":
- version: 2.0.6
- resolution: "get-value@npm:2.0.6"
- checksum: 5c3b99cb5398ea8016bf46ff17afc5d1d286874d2ad38ca5edb6e87d75c0965b0094cb9a9dddef2c59c23d250702323539a7fbdd870620db38c7e7d7ec87c1eb
- languageName: node
- linkType: hard
-
"glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2":
version: 5.1.2
resolution: "glob-parent@npm:5.1.2"
@@ -2746,69 +2408,6 @@ __metadata:
languageName: node
linkType: hard
-"glsl-token-assignments@npm:^2.0.0":
- version: 2.0.2
- resolution: "glsl-token-assignments@npm:2.0.2"
- checksum: efd6051cfd0e5dc4749cc05530e79c42b2396685345695d1232ab3904011e65f117110a2ef7e92a06bc687abf6182f4e90b6b51cc4ab20147aafcc57f724ecb5
- languageName: node
- linkType: hard
-
-"glsl-token-depth@npm:^1.1.0":
- version: 1.1.2
- resolution: "glsl-token-depth@npm:1.1.2"
- checksum: 97fff701eef20c2ef4552885f060dbf05b307f59b9f1637ddd73c3d5e7d3cc5b4851123706be9f2590042566132f5175ae86a82576bdcfa1edd4625c58d6843c
- languageName: node
- linkType: hard
-
-"glsl-token-descope@npm:^1.0.2":
- version: 1.0.2
- resolution: "glsl-token-descope@npm:1.0.2"
- dependencies:
- glsl-token-assignments: ^2.0.0
- glsl-token-depth: ^1.1.0
- glsl-token-properties: ^1.0.0
- glsl-token-scope: ^1.1.0
- checksum: a0d578d5e71178cd5679504a94a60e0811980f46fe6b3cb018bb165530faa75ffcb61b62a1984052223cf2455e36c08f9aa72cf3fdce419aac5d8844ec84cf5a
- languageName: node
- linkType: hard
-
-"glsl-token-functions@npm:^1.0.1":
- version: 1.0.1
- resolution: "glsl-token-functions@npm:1.0.1"
- checksum: 65801ee69830e429f28484181b0ad081c79f2f5c4913073bf0f7c50339de1da2604a82c3b5d29509638cf5a1b302f9425ce91d8e87c56ec1331c5c22f4b2a73c
- languageName: node
- linkType: hard
-
-"glsl-token-properties@npm:^1.0.0":
- version: 1.0.1
- resolution: "glsl-token-properties@npm:1.0.1"
- checksum: 9b4d1caf02d52f6407479bcd3e780133d6952ba6ae0d85ccd4f3de9ead061a173da0820b0238a0e721ae75370b645152d468bc24eb6f1fd37b5000c500d97cd4
- languageName: node
- linkType: hard
-
-"glsl-token-scope@npm:^1.1.0":
- version: 1.1.2
- resolution: "glsl-token-scope@npm:1.1.2"
- checksum: d62812c81a399d7bdd001ce4414293e508dbd78d480b1984190c8d3243c14817c34109893a71503a50ef09de28e4b0c0124be1979292aba5df3f0207eace1b70
- languageName: node
- linkType: hard
-
-"glsl-token-string@npm:^1.0.1":
- version: 1.0.1
- resolution: "glsl-token-string@npm:1.0.1"
- checksum: 3260c1486b620277396ecb92b13434764eddcd59330ffb7a25d0e5fc2750fbd4330899e2acb5ab36408ea7451f3e103418ca0430b4c6a225a7e5f318b5028fda
- languageName: node
- linkType: hard
-
-"glsl-tokenizer@npm:^2.1.5":
- version: 2.1.5
- resolution: "glsl-tokenizer@npm:2.1.5"
- dependencies:
- through2: ^0.6.3
- checksum: daf70e91c66a3143fe0b22be18a0f8cc965d7b81f73a58b14d55d08593bdcc3f996996549bda78b4cc822d7fe8c216aaeaab71f2695d802fb79fc9e89fb507d3
- languageName: node
- linkType: hard
-
"graceful-fs@npm:^4.2.6":
version: 4.2.10
resolution: "graceful-fs@npm:4.2.10"
@@ -2844,12 +2443,15 @@ __metadata:
resolution: "hdri-editor@workspace:."
dependencies:
"@derschmale/io-rgbe": ^0.1.1
+ "@dnd-kit/core": ^6.0.8
+ "@dnd-kit/sortable": ^7.0.2
"@heroicons/react": ^2.0.18
"@radix-ui/react-context-menu": ^1.0.0
"@radix-ui/react-toolbar": ^1.0.2
"@react-three/drei": ^9.74.8
"@react-three/fiber": ^8.13.1
"@react-three/postprocessing": ^2.14.9
+ "@tweakpane/core": ^2.0.0
"@types/prettier": ^2.7.2
"@types/react": ^18.0.19
"@types/react-dom": ^18.0.6
@@ -2858,9 +2460,7 @@ __metadata:
autoprefixer: ^10.4.13
clsx: ^1.2.1
cmdk: ^0.2.0
- immer: ^9.0.15
- lamina: ^1.1.23
- leva: ^0.9.34
+ jotai: ^2.3.0
postcss: ^8.4.21
prettier: ^2.8.4
prism-react-renderer: ^1.3.5
@@ -2872,9 +2472,10 @@ __metadata:
tailwindcss: ^3.2.6
three: ^0.153.0
three-stdlib: ^2.23.9
+ tunnel-rat: ^0.1.2
+ tweakpane: ^4.0.0
typescript: ^5.1.3
vite: ^4.3.9
- zustand: ^4.1.1
languageName: unknown
linkType: soft
@@ -2924,13 +2525,6 @@ __metadata:
languageName: node
linkType: hard
-"immer@npm:^9.0.15":
- version: 9.0.15
- resolution: "immer@npm:9.0.15"
- checksum: 92e3d63e810e3c3c2bb61b70c45443e37ef983ad12924e3edaf03725ae5979618f5b473439bb3bb4a8c4769f25132f18dec10ea15c40f0b20da5691ff96ff611
- languageName: node
- linkType: hard
-
"imurmurhash@npm:^0.1.4":
version: 0.1.4
resolution: "imurmurhash@npm:0.1.4"
@@ -2962,7 +2556,7 @@ __metadata:
languageName: node
linkType: hard
-"inherits@npm:2, inherits@npm:^2.0.3, inherits@npm:~2.0.1":
+"inherits@npm:2, inherits@npm:^2.0.3":
version: 2.0.4
resolution: "inherits@npm:2.0.4"
checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1
@@ -3003,22 +2597,6 @@ __metadata:
languageName: node
linkType: hard
-"is-extendable@npm:^0.1.0, is-extendable@npm:^0.1.1":
- version: 0.1.1
- resolution: "is-extendable@npm:0.1.1"
- checksum: 3875571d20a7563772ecc7a5f36cb03167e9be31ad259041b4a8f73f33f885441f778cee1f1fe0085eb4bc71679b9d8c923690003a36a6a5fdf8023e6e3f0672
- languageName: node
- linkType: hard
-
-"is-extendable@npm:^1.0.0, is-extendable@npm:^1.0.1":
- version: 1.0.1
- resolution: "is-extendable@npm:1.0.1"
- dependencies:
- is-plain-object: ^2.0.4
- checksum: db07bc1e9de6170de70eff7001943691f05b9d1547730b11be01c0ebfe67362912ba743cf4be6fd20a5e03b4180c685dad80b7c509fe717037e3eee30ad8e84f
- languageName: node
- linkType: hard
-
"is-extglob@npm:^2.1.1":
version: 2.1.1
resolution: "is-extglob@npm:2.1.1"
@@ -3056,22 +2634,6 @@ __metadata:
languageName: node
linkType: hard
-"is-plain-object@npm:^2.0.3, is-plain-object@npm:^2.0.4":
- version: 2.0.4
- resolution: "is-plain-object@npm:2.0.4"
- dependencies:
- isobject: ^3.0.1
- checksum: 2a401140cfd86cabe25214956ae2cfee6fbd8186809555cd0e84574f88de7b17abacb2e477a6a658fa54c6083ecbda1e6ae404c7720244cd198903848fca70ca
- languageName: node
- linkType: hard
-
-"isarray@npm:0.0.1":
- version: 0.0.1
- resolution: "isarray@npm:0.0.1"
- checksum: 49191f1425681df4a18c2f0f93db3adb85573bcdd6a4482539d98eac9e705d8961317b01175627e860516a2fc45f8f9302db26e5a380a97a520e272e2a40a8d4
- languageName: node
- linkType: hard
-
"isexe@npm:^2.0.0":
version: 2.0.0
resolution: "isexe@npm:2.0.0"
@@ -3079,13 +2641,6 @@ __metadata:
languageName: node
linkType: hard
-"isobject@npm:^3.0.1":
- version: 3.0.1
- resolution: "isobject@npm:3.0.1"
- checksum: db85c4c970ce30693676487cca0e61da2ca34e8d4967c2e1309143ff910c207133a969f9e4ddb2dc6aba670aabce4e0e307146c310350b298e74a31f7d464703
- languageName: node
- linkType: hard
-
"its-fine@npm:^1.0.6":
version: 1.0.8
resolution: "its-fine@npm:1.0.8"
@@ -3097,6 +2652,21 @@ __metadata:
languageName: node
linkType: hard
+"jotai@npm:^2.3.0":
+ version: 2.3.0
+ resolution: "jotai@npm:2.3.0"
+ peerDependencies:
+ "@types/react": ">=17.0.0"
+ react: ">=17.0.0"
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ react:
+ optional: true
+ checksum: 21df579532a61e9b745584a17252560ca7267af363e4d9ea3283a1f1290aded33b33d96c5748b09f3e7c23fceae02055a8093647d841b57eb87e566995b89e4a
+ languageName: node
+ linkType: hard
+
"js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0":
version: 4.0.0
resolution: "js-tokens@npm:4.0.0"
@@ -3129,78 +2699,6 @@ __metadata:
languageName: node
linkType: hard
-"lamina@npm:^1.1.22, lamina@npm:^1.1.23":
- version: 1.1.23
- resolution: "lamina@npm:1.1.23"
- dependencies:
- glsl-token-descope: ^1.0.2
- glsl-token-functions: ^1.0.1
- glsl-token-string: ^1.0.1
- glsl-tokenizer: ^2.1.5
- lamina: ^1.1.22
- leva: ^0.9.20
- three-custom-shader-material: ^4.0.0
- peerDependencies:
- "@react-three/fiber": ">=8.0"
- react: ">=18.0"
- react-dom: ">=18.0"
- three: ">=0.138"
- peerDependenciesMeta:
- "@react-three/fiber":
- optional: true
- react:
- optional: true
- react-dom:
- optional: true
- checksum: 2b9b0da0ec25435172ce0dbd0daefbf2262bf34b865c8125def75e32aff5df27a2b408099ba7c955e9d181c6bb7a7f6419c9022c507ebe7ba1fb099748278c31
- languageName: node
- linkType: hard
-
-"leva@npm:^0.9.20":
- version: 0.9.31
- resolution: "leva@npm:0.9.31"
- dependencies:
- "@radix-ui/react-portal": ^0.1.3
- "@radix-ui/react-tooltip": 0.1.6
- "@stitches/react": 1.2.8
- "@use-gesture/react": ^10.2.5
- "@welldone-software/why-did-you-render": ^6.2.3
- colord: ^2.9.2
- dequal: ^2.0.2
- merge-value: ^1.0.0
- react-colorful: ^5.5.1
- react-dropzone: ^12.0.0
- v8n: ^1.3.3
- zustand: ^3.6.9
- peerDependencies:
- react: ">=16.8.0"
- react-dom: ">=16.8.0"
- checksum: c3caff4249226c093315ad40fcaa5cdaa2aedca4fe580de526ecdf1260f169c23f6fdcc74665fa43c3c28d83619d1092b02378f265ac1a8c438b24782e9f7b66
- languageName: node
- linkType: hard
-
-"leva@npm:^0.9.34":
- version: 0.9.34
- resolution: "leva@npm:0.9.34"
- dependencies:
- "@radix-ui/react-portal": ^0.1.3
- "@radix-ui/react-tooltip": 0.1.6
- "@stitches/react": 1.2.8
- "@use-gesture/react": ^10.2.5
- colord: ^2.9.2
- dequal: ^2.0.2
- merge-value: ^1.0.0
- react-colorful: ^5.5.1
- react-dropzone: ^12.0.0
- v8n: ^1.3.3
- zustand: ^3.6.9
- peerDependencies:
- react: ">=16.8.0"
- react-dom: ">=16.8.0"
- checksum: 72461068ac01f174cff0eb4ea3dc4e50736c6e3cf9494bcb777662345dff6af1df512e7884dba803d7f567494cb04618d7104605fe7cf9a92e5b691f32f1f2b5
- languageName: node
- linkType: hard
-
"lil-gui@npm:~0.17.0":
version: 0.17.0
resolution: "lil-gui@npm:0.17.0"
@@ -3236,7 +2734,7 @@ __metadata:
languageName: node
linkType: hard
-"lodash@npm:4.17.21, lodash@npm:^4":
+"lodash@npm:4.17.21":
version: 4.17.21
resolution: "lodash@npm:4.17.21"
checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7
@@ -3313,18 +2811,6 @@ __metadata:
languageName: node
linkType: hard
-"merge-value@npm:^1.0.0":
- version: 1.0.0
- resolution: "merge-value@npm:1.0.0"
- dependencies:
- get-value: ^2.0.6
- is-extendable: ^1.0.0
- mixin-deep: ^1.2.0
- set-value: ^2.0.0
- checksum: 32c0ecaac8513d43389e979fa3f4bc73f6599ac7440f25721714bc4afc3c78bc55f251817e9ee22e94116791e493a789bf308dfe9e65601e5c8c267be47758f5
- languageName: node
- linkType: hard
-
"merge2@npm:^1.3.0":
version: 1.4.1
resolution: "merge2@npm:1.4.1"
@@ -3446,16 +2932,6 @@ __metadata:
languageName: node
linkType: hard
-"mixin-deep@npm:^1.2.0":
- version: 1.3.2
- resolution: "mixin-deep@npm:1.3.2"
- dependencies:
- for-in: ^1.0.2
- is-extendable: ^1.0.1
- checksum: 820d5a51fcb7479f2926b97f2c3bb223546bc915e6b3a3eb5d906dda871bba569863595424a76682f2b15718252954644f3891437cb7e3f220949bed54b1750d
- languageName: node
- linkType: hard
-
"mkdirp@npm:^1.0.3, mkdirp@npm:^1.0.4":
version: 1.0.4
resolution: "mkdirp@npm:1.0.4"
@@ -3824,7 +3300,7 @@ __metadata:
languageName: node
linkType: hard
-"prop-types@npm:^15.6.0, prop-types@npm:^15.8.1":
+"prop-types@npm:^15.6.0":
version: 15.8.1
resolution: "prop-types@npm:15.8.1"
dependencies:
@@ -3874,16 +3350,6 @@ __metadata:
languageName: node
linkType: hard
-"react-colorful@npm:^5.5.1":
- version: 5.6.1
- resolution: "react-colorful@npm:5.6.1"
- peerDependencies:
- react: ">=16.8.0"
- react-dom: ">=16.8.0"
- checksum: e432b7cb0df57e8f0bcdc3b012d2e93fcbcb6092c9e0f85654788d5ebfc4442536d8cc35b2418061ba3c4afb8b7788cc101c606d86a1732407921de7a9244c8d
- languageName: node
- linkType: hard
-
"react-composer@npm:^5.0.3":
version: 5.0.3
resolution: "react-composer@npm:5.0.3"
@@ -3907,19 +3373,6 @@ __metadata:
languageName: node
linkType: hard
-"react-dropzone@npm:^12.0.0":
- version: 12.1.0
- resolution: "react-dropzone@npm:12.1.0"
- dependencies:
- attr-accept: ^2.2.2
- file-selector: ^0.5.0
- prop-types: ^15.8.1
- peerDependencies:
- react: ">= 16.8"
- checksum: 1be37433cf42b8a9f98c8f59678e30fffc1e9b8e3fdb20f3a376557948f727156123ca0a7e45cd3882606184d945ea1139f17da0e1e5ba0b646a23be0ed65fb3
- languageName: node
- linkType: hard
-
"react-is@npm:^16.13.1":
version: 16.13.1
resolution: "react-is@npm:16.13.1"
@@ -4045,18 +3498,6 @@ __metadata:
languageName: node
linkType: hard
-"readable-stream@npm:>=1.0.33-1 <1.1.0-0":
- version: 1.0.34
- resolution: "readable-stream@npm:1.0.34"
- dependencies:
- core-util-is: ~1.0.0
- inherits: ~2.0.1
- isarray: 0.0.1
- string_decoder: ~0.10.x
- checksum: 85042c537e4f067daa1448a7e257a201070bfec3dd2706abdbd8ebc7f3418eb4d3ed4b8e5af63e2544d69f88ab09c28d5da3c0b77dc76185fddd189a59863b60
- languageName: node
- linkType: hard
-
"readable-stream@npm:^3.6.0":
version: 3.6.0
resolution: "readable-stream@npm:3.6.0"
@@ -4248,18 +3689,6 @@ __metadata:
languageName: node
linkType: hard
-"set-value@npm:^2.0.0":
- version: 2.0.1
- resolution: "set-value@npm:2.0.1"
- dependencies:
- extend-shallow: ^2.0.1
- is-extendable: ^0.1.1
- is-plain-object: ^2.0.3
- split-string: ^3.0.1
- checksum: 09a4bc72c94641aeae950eb60dc2755943b863780fcc32e441eda964b64df5e3f50603d5ebdd33394ede722528bd55ed43aae26e9df469b4d32e2292b427b601
- languageName: node
- linkType: hard
-
"signal-exit@npm:^3.0.7":
version: 3.0.7
resolution: "signal-exit@npm:3.0.7"
@@ -4312,15 +3741,6 @@ __metadata:
languageName: node
linkType: hard
-"split-string@npm:^3.0.1":
- version: 3.1.0
- resolution: "split-string@npm:3.1.0"
- dependencies:
- extend-shallow: ^3.0.0
- checksum: ae5af5c91bdc3633628821bde92fdf9492fa0e8a63cf6a0376ed6afde93c701422a1610916f59be61972717070119e848d10dfbbd5024b7729d6a71972d2a84c
- languageName: node
- linkType: hard
-
"ssri@npm:^9.0.0":
version: 9.0.1
resolution: "ssri@npm:9.0.1"
@@ -4364,13 +3784,6 @@ __metadata:
languageName: node
linkType: hard
-"string_decoder@npm:~0.10.x":
- version: 0.10.31
- resolution: "string_decoder@npm:0.10.31"
- checksum: fe00f8e303647e5db919948ccb5ce0da7dea209ab54702894dd0c664edd98e5d4df4b80d6fabf7b9e92b237359d21136c95bf068b2f7760b772ca974ba970202
- languageName: node
- linkType: hard
-
"strip-ansi@npm:^6.0.1":
version: 6.0.1
resolution: "strip-ansi@npm:6.0.1"
@@ -4455,27 +3868,6 @@ __metadata:
languageName: node
linkType: hard
-"three-custom-shader-material@npm:^4.0.0":
- version: 4.0.0
- resolution: "three-custom-shader-material@npm:4.0.0"
- dependencies:
- glsl-token-functions: ^1.0.1
- glsl-token-string: ^1.0.1
- glsl-tokenizer: ^2.1.5
- object-hash: ^3.0.0
- peerDependencies:
- "@react-three/fiber": ">=8.0"
- react: ">=18.0"
- three: ">=0.140"
- peerDependenciesMeta:
- "@react-three/fiber":
- optional: true
- react:
- optional: true
- checksum: 4803fde8433218c330284491dc548c216b6970da9ffd437d02bb9c711892629bd5b53e4baa3214196a5daf5465e3632112e024adbf1a81a0c8bede3a152dd5d0
- languageName: node
- linkType: hard
-
"three-mesh-bvh@npm:^0.5.23":
version: 0.5.24
resolution: "three-mesh-bvh@npm:0.5.24"
@@ -4513,16 +3905,6 @@ __metadata:
languageName: node
linkType: hard
-"through2@npm:^0.6.3":
- version: 0.6.5
- resolution: "through2@npm:0.6.5"
- dependencies:
- readable-stream: ">=1.0.33-1 <1.1.0-0"
- xtend: ">=4.0.0 <4.1.0-0"
- checksum: dfea228e3134a33219a588448847250897a9994a687807dab52f850fac8b4eb1dc18e3b2c1d3d60dd0d78eb492d2032fdf814ac6576ba5b8d5ba0dade29a3544
- languageName: node
- linkType: hard
-
"tiny-inflate@npm:^1.0.3":
version: 1.0.3
resolution: "tiny-inflate@npm:1.0.3"
@@ -4576,13 +3958,29 @@ __metadata:
languageName: node
linkType: hard
-"tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0":
+"tslib@npm:^2.0.0, tslib@npm:^2.1.0":
version: 2.4.0
resolution: "tslib@npm:2.4.0"
checksum: 8c4aa6a3c5a754bf76aefc38026134180c053b7bd2f81338cb5e5ebf96fefa0f417bff221592bf801077f5bf990562f6264fecbc42cd3309b33872cb6fc3b113
languageName: node
linkType: hard
+"tunnel-rat@npm:^0.1.2":
+ version: 0.1.2
+ resolution: "tunnel-rat@npm:0.1.2"
+ dependencies:
+ zustand: ^4.3.2
+ checksum: 9d5975d589db705e7707dcfd2bdb9f1773b014179a7e56a9cd5aaa1824ee28143efcfea30e826f3fccc0a9cb7a8631b4fd490a5849a20893e37e93e122bd9430
+ languageName: node
+ linkType: hard
+
+"tweakpane@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "tweakpane@npm:4.0.0"
+ checksum: a7f0839556b7b7f40528cded3c29163f45e319b16f74b4bedd38dda13e161756b1e33f5cd595a2e93bb3f98b069db27e574a1cc58388a53b14e47c3a525a5877
+ languageName: node
+ linkType: hard
+
"typescript@npm:^5.1.3":
version: 5.1.3
resolution: "typescript@npm:5.1.3"
@@ -4715,13 +4113,6 @@ __metadata:
languageName: node
linkType: hard
-"v8n@npm:^1.3.3":
- version: 1.5.1
- resolution: "v8n@npm:1.5.1"
- checksum: 96c8dff9144001da46152f37b9323e2bf9a1f915c6a3f6f5e8683f7a540a6551a18e937267e257f8753da594f33a0b1724770cd50f73e6ea7dc3ceb0510ca72f
- languageName: node
- linkType: hard
-
"vite@npm:^4.3.9":
version: 4.3.9
resolution: "vite@npm:4.3.9"
@@ -4800,7 +4191,7 @@ __metadata:
languageName: node
linkType: hard
-"xtend@npm:>=4.0.0 <4.1.0-0, xtend@npm:^4.0.2":
+"xtend@npm:^4.0.2":
version: 4.0.2
resolution: "xtend@npm:4.0.2"
checksum: ac5dfa738b21f6e7f0dd6e65e1b3155036d68104e67e5d5d1bde74892e327d7e5636a076f625599dc394330a731861e87343ff184b0047fef1360a7ec0a5a36a
@@ -4835,7 +4226,7 @@ __metadata:
languageName: node
linkType: hard
-"zustand@npm:^3.5.13, zustand@npm:^3.6.9, zustand@npm:^3.7.1":
+"zustand@npm:^3.5.13, zustand@npm:^3.7.1":
version: 3.7.2
resolution: "zustand@npm:3.7.2"
peerDependencies:
@@ -4847,20 +4238,23 @@ __metadata:
languageName: node
linkType: hard
-"zustand@npm:^4.1.1":
- version: 4.1.1
- resolution: "zustand@npm:4.1.1"
+"zustand@npm:^4.3.2":
+ version: 4.4.0
+ resolution: "zustand@npm:4.4.0"
dependencies:
use-sync-external-store: 1.2.0
peerDependencies:
+ "@types/react": ">=16.8"
immer: ">=9.0"
react: ">=16.8"
peerDependenciesMeta:
+ "@types/react":
+ optional: true
immer:
optional: true
react:
optional: true
- checksum: 03eefb193e2ecb43a761c81cb60f517c2780289dab0f55f2cbdb91400924c6291abb5a007b32ee19787b8f72b6769f891a38b64fd296660c170d632c3182f6e1
+ checksum: 37e69eec1b56677a93712e5aa6d0048b55997379919dc0f78f61181f8a58994a6cae064f816f8101f5b1039008d3c1c9d136432a62e0edeb796807cc84cf45ef
languageName: node
linkType: hard