diff --git a/apps/typegpu-docs/package.json b/apps/typegpu-docs/package.json
index 6642eccb9..a47ac1826 100644
--- a/apps/typegpu-docs/package.json
+++ b/apps/typegpu-docs/package.json
@@ -27,8 +27,10 @@
"classnames": "^2.5.1",
"jotai": "^2.8.4",
"jotai-location": "^0.5.5",
+ "lucide-react": "^0.474.0",
"lz-string": "^1.5.0",
"monaco-editor": "^0.50.0",
+ "motion": "^12.0.5",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"remeda": "^2.3.0",
@@ -37,6 +39,7 @@
"starlight-blog": "^0.12.0",
"starlight-typedoc": "^0.17.0",
"tailwindcss": "^3.4.6",
+ "tinybench": "^3.1.0",
"typed-binary": "^4.0.0",
"typedoc": "^0.27.1",
"typedoc-plugin-markdown": "^4.3.0",
@@ -49,6 +52,7 @@
"@types/babel__template": "^7.4.4",
"@types/babel__traverse": "^7.20.6",
"@webgpu/types": "^0.1.43",
- "astro-vtbot": "^1.8.2"
+ "astro-vtbot": "^1.8.2",
+ "tailwindcss-motion": "^1.0.1"
}
}
diff --git a/apps/typegpu-docs/src/components/design/DeleteIcon.tsx b/apps/typegpu-docs/src/components/design/DeleteIcon.tsx
new file mode 100644
index 000000000..ddb4486fe
--- /dev/null
+++ b/apps/typegpu-docs/src/components/design/DeleteIcon.tsx
@@ -0,0 +1,84 @@
+'use client';
+
+import type { Variants } from 'motion/react';
+import { motion, useAnimation } from 'motion/react';
+
+const lidVariants: Variants = {
+ normal: { y: 0 },
+ animate: { y: -1.1 },
+};
+
+const springTransition = {
+ type: 'spring',
+ stiffness: 500,
+ damping: 30,
+};
+
+const DeleteIcon = () => {
+ const controls = useAnimation();
+
+ return (
+
controls.start('animate')}
+ onMouseLeave={() => controls.start('normal')}
+ >
+
+
+ );
+};
+
+export { DeleteIcon };
diff --git a/apps/typegpu-docs/src/layouts/PageLayout.astro b/apps/typegpu-docs/src/layouts/PageLayout.astro
index e3c6785e9..00c672b5a 100644
--- a/apps/typegpu-docs/src/layouts/PageLayout.astro
+++ b/apps/typegpu-docs/src/layouts/PageLayout.astro
@@ -1,10 +1,10 @@
---
import '../tailwind.css';
import '../fonts/font-face.css';
-const { title } = Astro.props;
+const { title, theme = 'light' } = Astro.props;
---
-
+
{title ? `${title} |` : ''} TypeGPU
@@ -49,6 +49,14 @@ const { title } = Astro.props;
margin: 0;
}
+ [data-theme='dark'] body {
+ background-color: #171724;
+ color: white;
+ }
+
+ [data-theme='light'] body {
+ background-color: white;
+ }
diff --git a/apps/typegpu-docs/src/pages/benchmark/atom-with-url.ts b/apps/typegpu-docs/src/pages/benchmark/atom-with-url.ts
new file mode 100644
index 000000000..776dd40de
--- /dev/null
+++ b/apps/typegpu-docs/src/pages/benchmark/atom-with-url.ts
@@ -0,0 +1,67 @@
+import { atom } from 'jotai';
+import { atomWithLocation } from 'jotai-location';
+
+const locationAtom = atomWithLocation();
+
+export const stringParam = {
+ encode: (val: string) => val,
+ decode: (val: string) => val,
+};
+
+export const boolParam = {
+ encode: (val: boolean) => (val ? '1' : '0'),
+ decode: (val: string) => val === '1',
+};
+
+export const numberParam = {
+ encode: (val: number) => String(val),
+ decode: (val: string) => Number.parseFloat(val),
+};
+
+export const objParam = {
+ encode: JSON.stringify,
+ decode: JSON.parse,
+};
+
+export const typeToParam = {
+ string: stringParam,
+ boolean: boolParam,
+ number: numberParam,
+ object: objParam,
+};
+
+export const atomWithUrl = (
+ key: string,
+ defaultValue: T,
+ options?: { encode: (val: T) => string; decode: (val: string) => unknown },
+) => {
+ const optionsOrInferred =
+ options ??
+ typeToParam[typeof defaultValue as keyof typeof typeToParam] ??
+ objParam;
+
+ const { encode, decode } = optionsOrInferred;
+
+ return atom(
+ (get) => {
+ const location = get(locationAtom);
+ return location.searchParams?.has(key)
+ ? (decode(location.searchParams.get(key) ?? '') as T)
+ : defaultValue;
+ },
+ (get, set, newValue: T) => {
+ const prev = get(locationAtom);
+ const searchParams = new URLSearchParams(prev.searchParams);
+ searchParams.set(key, encode(newValue));
+
+ set(
+ locationAtom,
+ {
+ ...prev,
+ searchParams: searchParams,
+ },
+ { replace: true },
+ );
+ },
+ );
+};
diff --git a/apps/typegpu-docs/src/pages/benchmark/benchmark-app.tsx b/apps/typegpu-docs/src/pages/benchmark/benchmark-app.tsx
new file mode 100644
index 000000000..fd71a53de
--- /dev/null
+++ b/apps/typegpu-docs/src/pages/benchmark/benchmark-app.tsx
@@ -0,0 +1,223 @@
+import { useAtomValue, useSetAtom } from 'jotai/react';
+import { atom } from 'jotai/vanilla';
+import { CirclePlus } from 'lucide-react';
+import { Suspense, useMemo } from 'react';
+import { Bench } from 'tinybench';
+import { importTypeGPU, importTypeGPUData } from './modules.js';
+import { ParameterSetRow } from './parameter-set-row.js';
+import {
+ type BenchParameterSet,
+ createParameterSetAtom,
+ parameterSetAtomsAtom,
+ parameterSetsAtom,
+ stringifyLocator,
+} from './parameter-set.js';
+
+interface BenchResults {
+ parameterSet: BenchParameterSet;
+ bench: Bench;
+}
+
+async function runBench(params: BenchParameterSet): Promise {
+ const { tgpu } = await importTypeGPU(params.typegpu);
+ const d = await importTypeGPUData(params.typegpu);
+
+ const bench = new Bench({
+ name: stringifyLocator('typegpu', params.typegpu),
+ time: 1000,
+ });
+
+ const amountOfBoids = 10000;
+
+ bench
+ .add('mass boid transfer', async () => {
+ const root = await tgpu.init();
+
+ const Boid = d.struct({
+ pos: d.vec3f,
+ vel: d.vec3f,
+ });
+
+ const BoidArray = d.arrayOf(Boid, amountOfBoids);
+
+ const buffer = root.createBuffer(BoidArray);
+
+ buffer.write(
+ Array.from({ length: amountOfBoids }).map(() => ({
+ pos: d.vec3f(1, 2, 3),
+ vel: d.vec3f(4, 5, 6),
+ })),
+ );
+
+ root.destroy();
+ })
+ .add('mass boid transfer (manual reference)', async () => {
+ const root = await tgpu.init();
+
+ const Boid = d.struct({
+ pos: d.vec3f,
+ vel: d.vec3f,
+ });
+
+ const BoidArray = d.arrayOf(Boid, amountOfBoids);
+
+ const buffer = root.createBuffer(BoidArray);
+
+ const data = new ArrayBuffer(d.sizeOf(BoidArray));
+ const fView = new Float32Array(data);
+
+ for (let i = 0; i < amountOfBoids; ++i) {
+ fView[i * 8 + 0] = 1;
+ fView[i * 8 + 1] = 2;
+ fView[i * 8 + 2] = 3;
+
+ fView[i * 8 + 4] = 4;
+ fView[i * 8 + 5] = 5;
+ fView[i * 8 + 6] = 6;
+ }
+
+ root.device.queue.writeBuffer(root.unwrap(buffer), 0, data);
+
+ root.destroy();
+ });
+
+ await bench.run();
+
+ return { parameterSet: params, bench };
+}
+
+const benchResultsAtom = atom | null>(null);
+
+const runBenchmarksAtom = atom(null, async (get, set) => {
+ const parameterSets = get(parameterSetsAtom);
+
+ set(
+ benchResultsAtom,
+ (async () => {
+ const results: BenchResults[] = [];
+
+ // Running each benchmark in sequence
+ for (const params of parameterSets) {
+ results.push(await runBench(params));
+ }
+
+ return results;
+ })(),
+ );
+});
+
+function SingleBenchResults(props: { results: BenchResults }) {
+ const { bench } = props.results;
+ const results = useMemo(() => bench.table(), [bench]);
+ const columns = Object.keys(results?.[0] ?? {});
+
+ return (
+
+ {bench.name}
+
+
+ {columns.map((columnKey) => (
+
+ {columnKey}
+ |
+ ))}
+
+
+
+ {results.map((task) => (
+
+ {columns.map((columnKey) => (
+
+ {task?.[columnKey]}
+ |
+ ))}
+
+ ))}
+
+
+ );
+}
+
+function BenchmarkResults() {
+ const benchResults = useAtomValue(benchResultsAtom);
+
+ return benchResults?.map((results) => (
+
+ ));
+}
+
+function BenchmarkFallback() {
+ return (
+
+ );
+}
+
+export default function BenchmarkApp() {
+ const parameterSetAtoms = useAtomValue(parameterSetAtomsAtom);
+ const runBenchmarks = useSetAtom(runBenchmarksAtom);
+ const createParameterSet = useSetAtom(createParameterSetAtom);
+
+ return (
+
+
+
Versions to compare:
+
+ {parameterSetAtoms.map((paramsAtom, index) => (
+ -
+ key={`${index}`}
+ className="w-full"
+ >
+
+
+ ))}
+
+
+
+
+
+
+
}>
+
+
+
+ );
+}
diff --git a/apps/typegpu-docs/src/pages/benchmark/index.astro b/apps/typegpu-docs/src/pages/benchmark/index.astro
new file mode 100644
index 000000000..ae2082dd7
--- /dev/null
+++ b/apps/typegpu-docs/src/pages/benchmark/index.astro
@@ -0,0 +1,19 @@
+---
+import { Image } from 'astro:assets';
+import PageLayout from '../../layouts/PageLayout.astro';
+import BenchmarkApp from './benchmark-app.tsx';
+import TypeGPULogoDark from '../../assets/typegpu-logo-dark.svg';
+---
+
+
+
+
+
— benchmark
+
+
+
+
diff --git a/apps/typegpu-docs/src/pages/benchmark/modules.ts b/apps/typegpu-docs/src/pages/benchmark/modules.ts
new file mode 100644
index 000000000..32e84cccf
--- /dev/null
+++ b/apps/typegpu-docs/src/pages/benchmark/modules.ts
@@ -0,0 +1,35 @@
+import type { PackageLocator } from './parameter-set.js';
+
+export type TypeGPUModule = typeof import('typegpu');
+export type TypeGPUDataModule = typeof import('typegpu/data');
+export type TypeGPUStdModule = typeof import('typegpu/std');
+
+export function importTypeGPU(locator: PackageLocator): Promise {
+ if (locator.type === 'local') {
+ return import('typegpu');
+ }
+
+ if (locator.type === 'npm') {
+ return import(
+ /* @vite-ignore */ `https://esm.sh/typegpu@${locator.version}/`
+ );
+ }
+
+ throw new Error('Unsupported import of `typegpu`');
+}
+
+export function importTypeGPUData(
+ locator: PackageLocator,
+): Promise {
+ if (locator.type === 'local') {
+ return import('typegpu/data');
+ }
+
+ if (locator.type === 'npm') {
+ return import(
+ /* @vite-ignore */ `https://esm.sh/typegpu@${locator.version}/data`
+ );
+ }
+
+ throw new Error('Unsupported import of `typegpu/data`');
+}
diff --git a/apps/typegpu-docs/src/pages/benchmark/parameter-set-row.tsx b/apps/typegpu-docs/src/pages/benchmark/parameter-set-row.tsx
new file mode 100644
index 000000000..172d60956
--- /dev/null
+++ b/apps/typegpu-docs/src/pages/benchmark/parameter-set-row.tsx
@@ -0,0 +1,85 @@
+import { type PrimitiveAtom, useAtom, useSetAtom } from 'jotai';
+import { useCallback } from 'react';
+import { DeleteIcon } from '../../components/design/DeleteIcon.js';
+import {
+ type BenchParameterSet,
+ deleteParameterSetAtom,
+} from './parameter-set.js';
+
+function NpmParameters(props: {
+ parameterSetAtom: PrimitiveAtom;
+}) {
+ const [parameterSet, setParameterSet] = useAtom(props.parameterSetAtom);
+
+ const version =
+ parameterSet.typegpu.type === 'npm' ? parameterSet.typegpu.version : '';
+
+ const setVersion = useCallback(
+ (version: string) => {
+ setParameterSet((prev) => ({
+ ...prev,
+ typegpu: { ...prev.typegpu, version },
+ }));
+ },
+ [setParameterSet],
+ );
+
+ return (
+ <>
+ typegpu@
+ setVersion(e.target.value)}
+ placeholder="0.0.0"
+ />
+ >
+ );
+}
+
+export function ParameterSetRow(props: {
+ parameterSetAtom: PrimitiveAtom;
+}) {
+ const [parameterSet, setParameterSet] = useAtom(props.parameterSetAtom);
+ const deleteParameterSet = useSetAtom(deleteParameterSetAtom);
+
+ const typeValue = parameterSet.typegpu.type;
+
+ const setType = useCallback(
+ (type: 'local' | 'npm') => {
+ setParameterSet((prev) => ({
+ ...prev,
+ typegpu: { type },
+ }));
+ },
+ [setParameterSet],
+ );
+
+ return (
+
+
+
+
+ {typeValue === 'local' &&
typegpu
}
+ {typeValue === 'npm' && (
+
+ )}
+
+
+ );
+}
diff --git a/apps/typegpu-docs/src/pages/benchmark/parameter-set.ts b/apps/typegpu-docs/src/pages/benchmark/parameter-set.ts
new file mode 100644
index 000000000..7d624e3c1
--- /dev/null
+++ b/apps/typegpu-docs/src/pages/benchmark/parameter-set.ts
@@ -0,0 +1,64 @@
+import { type Getter, atom } from 'jotai';
+import { splitAtom } from 'jotai/utils';
+import { atomWithUrl } from './atom-with-url.js';
+
+export type PackageLocator =
+ | {
+ type: 'npm';
+ version?: string;
+ }
+ | {
+ type: 'local';
+ };
+
+export interface BenchParameterSet {
+ key: number;
+ typegpu: PackageLocator;
+}
+
+export function stringifyLocator(
+ name: string,
+ locator: PackageLocator,
+): string {
+ if (locator.type === 'npm') {
+ return `${name}@${locator.version}`;
+ }
+
+ if (locator.type === 'local') {
+ return `${name}:local`;
+ }
+
+ return name;
+}
+
+function getFreeKey(get: Getter): number {
+ return Math.max(...get(parameterSetsAtom).map((params) => params.key)) + 1;
+}
+
+export const parameterSetsAtom = atomWithUrl('p', [
+ { key: 1, typegpu: { type: 'local' } },
+ { key: 2, typegpu: { type: 'npm', version: 'latest' } },
+]);
+
+export const parameterSetAtomsAtom = splitAtom(
+ parameterSetsAtom,
+ (params) => params.key,
+);
+
+export const createParameterSetAtom = atom(null, (get, set) => {
+ const prev = get(parameterSetsAtom);
+ const key = getFreeKey(get);
+
+ set(parameterSetsAtom, [
+ ...prev,
+ { key, typegpu: { type: 'npm', version: '' } },
+ ]);
+});
+
+export const deleteParameterSetAtom = atom(null, (get, set, key: number) => {
+ const prev = get(parameterSetsAtom);
+ set(
+ parameterSetsAtom,
+ prev.filter((params) => params.key !== key),
+ );
+});
diff --git a/apps/typegpu-docs/tailwind.config.mjs b/apps/typegpu-docs/tailwind.config.mjs
index 03cbb7216..4d57be12d 100644
--- a/apps/typegpu-docs/tailwind.config.mjs
+++ b/apps/typegpu-docs/tailwind.config.mjs
@@ -1,4 +1,7 @@
import starlightPlugin from '@astrojs/starlight-tailwind';
+import tailwindcssMotion from 'tailwindcss-motion';
+// @ts-check
+import plugin from 'tailwindcss/plugin';
const accent = {
200: '#c3c4f1',
@@ -17,6 +20,7 @@ const gray = {
900: '#171724',
};
+/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: {
@@ -70,5 +74,11 @@ export default {
lg: '1441px',
},
},
- plugins: [starlightPlugin()],
+ plugins: [
+ starlightPlugin(),
+ tailwindcssMotion,
+ plugin(({ addVariant }) => {
+ addVariant('starting', '@starting-style');
+ }),
+ ],
};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 327d09471..386c49476 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -83,12 +83,18 @@ importers:
jotai-location:
specifier: ^0.5.5
version: 0.5.5(jotai@2.8.4)
+ lucide-react:
+ specifier: ^0.474.0
+ version: 0.474.0(react@18.3.1)
lz-string:
specifier: ^1.5.0
version: 1.5.0
monaco-editor:
specifier: ^0.50.0
version: 0.50.0
+ motion:
+ specifier: ^12.0.5
+ version: 12.0.5(react-dom@18.3.1)(react@18.3.1)
react:
specifier: ^18.3.1
version: 18.3.1
@@ -113,6 +119,9 @@ importers:
tailwindcss:
specifier: ^3.4.6
version: 3.4.6
+ tinybench:
+ specifier: ^3.1.0
+ version: 3.1.0
typed-binary:
specifier: ^4.0.0
version: 4.0.0
@@ -147,6 +156,9 @@ importers:
astro-vtbot:
specifier: ^1.8.2
version: 1.10.7
+ tailwindcss-motion:
+ specifier: ^1.0.1
+ version: 1.0.1(tailwindcss@3.4.6)
packages/rollup-plugin:
dependencies:
@@ -336,7 +348,6 @@ packages:
/@alloc/quick-lru@5.2.0:
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
engines: {node: '>=10'}
- dev: false
/@ampproject/remapping@2.2.1:
resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==}
@@ -2856,7 +2867,6 @@ packages:
/arg@5.0.2:
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
- dev: false
/argparse@1.0.10:
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
@@ -3174,7 +3184,6 @@ packages:
/camelcase-css@2.0.1:
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
engines: {node: '>= 6'}
- dev: false
/camelcase@7.0.1:
resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==}
@@ -3397,7 +3406,6 @@ packages:
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
engines: {node: '>=4'}
hasBin: true
- dev: false
/csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
@@ -3487,7 +3495,6 @@ packages:
/didyoumean@1.2.2:
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
- dev: false
/diff@5.2.0:
resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==}
@@ -3512,7 +3519,6 @@ packages:
/dlv@1.1.3:
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
- dev: false
/dpdm@3.14.0:
resolution: {integrity: sha512-YJzsFSyEtj88q5eTELg3UWU7TVZkG1dpbF4JDQ3t1b07xuzXmdoGeSz9TKOke1mUuOpWlk4q+pBh+aHzD6GBTg==}
@@ -3826,6 +3832,27 @@ packages:
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
dev: false
+ /framer-motion@12.0.5(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-OgfUHfL+u80uCf6VzkV7j3+H2W9zPMdV+40bug4ftB0C2+0FpfNca2rbH2Fouq2R7//bjw6tJhOwXsrsM4+LKw==}
+ peerDependencies:
+ '@emotion/is-prop-valid': '*'
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@emotion/is-prop-valid':
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+ dependencies:
+ motion-dom: 12.0.0
+ motion-utils: 12.0.0
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ tslib: 2.6.3
+ dev: false
+
/fs-constants@1.0.0:
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
dev: false
@@ -3848,7 +3875,6 @@ packages:
/function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
- dev: false
/gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
@@ -3904,7 +3930,6 @@ packages:
engines: {node: '>=10.13.0'}
dependencies:
is-glob: 4.0.3
- dev: false
/glob@10.3.10:
resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
@@ -3975,7 +4000,6 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
function-bind: 1.1.2
- dev: false
/hast-util-embedded@3.0.0:
resolution: {integrity: sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==}
@@ -4280,7 +4304,6 @@ packages:
resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
dependencies:
hasown: 2.0.1
- dev: false
/is-decimal@2.0.1:
resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==}
@@ -4401,7 +4424,6 @@ packages:
/jiti@1.21.6:
resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==}
hasBin: true
- dev: false
/jotai-location@0.5.5(jotai@2.8.4):
resolution: {integrity: sha512-6QW/7W9IJHjhbn7gRgAw4sC30k0/G6JiC4uPlKi8ZPZGYk7R7r9PyMD2eVhL4XSxxag89JxS1iSyr6BIXsB4Sw==}
@@ -4493,7 +4515,6 @@ packages:
/lilconfig@2.1.0:
resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
engines: {node: '>=10'}
- dev: false
/lilconfig@3.1.0:
resolution: {integrity: sha512-p3cz0JV5vw/XeouBU3Ldnp+ZkBjE+n8ydJ4mcwBrOiXXPqNlrzGBqWs9X4MWF7f+iKUBu794Y8Hh8yawiJbCjw==}
@@ -4581,6 +4602,14 @@ packages:
yallist: 3.1.1
dev: false
+ /lucide-react@0.474.0(react@18.3.1):
+ resolution: {integrity: sha512-CmghgHkh0OJNmxGKWc0qfPJCYHASPMVSyGY8fj3xgk4v84ItqDg64JNKFZn5hC6E0vHi6gxnbCgwhyVB09wQtA==}
+ peerDependencies:
+ react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ dependencies:
+ react: 18.3.1
+ dev: false
+
/lunr@2.3.9:
resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==}
dev: false
@@ -5300,6 +5329,36 @@ packages:
/moo@0.5.2:
resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==}
+ /motion-dom@12.0.0:
+ resolution: {integrity: sha512-CvYd15OeIR6kHgMdonCc1ihsaUG4MYh/wrkz8gZ3hBX/uamyZCXN9S9qJoYF03GqfTt7thTV/dxnHYX4+55vDg==}
+ dependencies:
+ motion-utils: 12.0.0
+ dev: false
+
+ /motion-utils@12.0.0:
+ resolution: {integrity: sha512-MNFiBKbbqnmvOjkPyOKgHUp3Q6oiokLkI1bEwm5QA28cxMZrv0CbbBGDNmhF6DIXsi1pCQBSs0dX8xjeER1tmA==}
+ dev: false
+
+ /motion@12.0.5(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-AFCMJWX7xtd2V4PEuL+FnVscw++SaMZuFXrm3kjN1sIqrNDJdJrFbZ2B+pfq6CKlcTor/vVJcmrc2pJqF3EByw==}
+ peerDependencies:
+ '@emotion/is-prop-valid': '*'
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@emotion/is-prop-valid':
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+ dependencies:
+ framer-motion: 12.0.5(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ tslib: 2.6.3
+ dev: false
+
/mrmime@2.0.0:
resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==}
engines: {node: '>=10'}
@@ -5402,7 +5461,6 @@ packages:
/object-hash@3.0.0:
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
engines: {node: '>= 6'}
- dev: false
/once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
@@ -5557,7 +5615,6 @@ packages:
/path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
- dev: false
/path-scurry@1.10.1:
resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
@@ -5614,7 +5671,6 @@ packages:
/pify@2.3.0:
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
engines: {node: '>=0.10.0'}
- dev: false
/pify@4.0.1:
resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
@@ -5642,7 +5698,6 @@ packages:
postcss-value-parser: 4.2.0
read-cache: 1.0.0
resolve: 1.22.8
- dev: false
/postcss-js@4.0.1(postcss@8.4.39):
resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
@@ -5652,7 +5707,6 @@ packages:
dependencies:
camelcase-css: 2.0.1
postcss: 8.4.39
- dev: false
/postcss-load-config@4.0.2(postcss@8.4.39):
resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
@@ -5678,7 +5732,6 @@ packages:
dependencies:
postcss: 8.4.39
postcss-selector-parser: 6.1.0
- dev: false
/postcss-selector-parser@6.1.0:
resolution: {integrity: sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==}
@@ -5686,11 +5739,9 @@ packages:
dependencies:
cssesc: 3.0.0
util-deprecate: 1.0.2
- dev: false
/postcss-value-parser@4.2.0:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
- dev: false
/postcss@8.4.39:
resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==}
@@ -5869,7 +5920,6 @@ packages:
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
dependencies:
pify: 2.3.0
- dev: false
/readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
@@ -6054,7 +6104,6 @@ packages:
is-core-module: 2.13.1
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
- dev: false
/restore-cursor@3.1.0:
resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
@@ -6522,7 +6571,14 @@ packages:
/supports-preserve-symlinks-flag@1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
- dev: false
+
+ /tailwindcss-motion@1.0.1(tailwindcss@3.4.6):
+ resolution: {integrity: sha512-ZqarvI3XSclT46Dq1wOo9qG9Yx0TIyEtkMfTo8l0JuVKvO1WiSbvAECuTqdYHpC9Ml7abuJIk7u907SBHYprwQ==}
+ peerDependencies:
+ tailwindcss: '>=3.0.0 || insiders'
+ dependencies:
+ tailwindcss: 3.4.6
+ dev: true
/tailwindcss@3.4.6:
resolution: {integrity: sha512-1uRHzPB+Vzu57ocybfZ4jh5Q3SdlH7XW23J5sQoM9LhE9eIOlzxer/3XPSsycvih3rboRsvt0QCmzSrqyOYUIA==}
@@ -6553,7 +6609,6 @@ packages:
sucrase: 3.35.0
transitivePeerDependencies:
- ts-node
- dev: false
/tar-fs@2.1.1:
resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==}
@@ -6614,6 +6669,11 @@ packages:
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
dev: true
+ /tinybench@3.1.0:
+ resolution: {integrity: sha512-Km+oMh2xqNCxuyoUsqbRmHgFSd8sATh7v7xreP+kHN6x67w28Pawr83WmBxcaORvxkc0Ex6zgqK951yBnTFaaQ==}
+ engines: {node: '>=18.0.0'}
+ dev: false
+
/tinyexec@0.3.1:
resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==}
dev: true