Skip to content

Commit 3da3c4a

Browse files
committed
feat: Initial version of @typegpu/react
Adapt examples environment to allow for React examples Implemented stuff feat(@typegpu/react): Implement useUniformValue hook (#1694) Use callable schemas for initial value Move example to appropriate place Change 'kernel' to 'use gpu'
1 parent ac065f2 commit 3da3c4a

20 files changed

Lines changed: 443 additions & 21 deletions

File tree

apps/typegpu-docs/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"@typegpu/concurrent-scan": "workspace:*",
3232
"@typegpu/geometry": "workspace:*",
3333
"@typegpu/noise": "workspace:*",
34+
"@typegpu/react": "workspace:*",
3435
"@typegpu/sdf": "workspace:*",
3536
"@typegpu/three": "workspace:*",
3637
"@types/react": "^19.1.8",

apps/typegpu-docs/src/components/ExampleView.tsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,20 +66,21 @@ function useExample(
6666
export function ExampleView({ example, common }: Props) {
6767
const { tsFiles: srcFiles, tsImport, htmlFile } = example;
6868

69-
const [snackbarText, setSnackbarText] = useAtom(currentSnackbarAtom);
70-
const [currentFilePath, setCurrentFilePath] = useState<string>('index.ts');
71-
72-
const codeEditorShown = useAtomValue(codeEditorShownAtom);
73-
const exampleHtmlRef = useRef<HTMLDivElement>(null);
74-
7569
const tsFiles = filterRelevantTsFiles(srcFiles, common);
7670
const filePaths = tsFiles.map((file) => file.path);
71+
const entryFile = filePaths.find((path) => path.startsWith('index.ts')) as string;
7772
const editorTabsList = [
78-
'index.ts',
79-
...filePaths.filter((name) => name !== 'index.ts'),
73+
entryFile,
74+
...filePaths.filter((name) => name !== entryFile),
8075
'index.html',
8176
];
8277

78+
const [snackbarText, setSnackbarText] = useAtom(currentSnackbarAtom);
79+
const [currentFilePath, setCurrentFilePath] = useState(entryFile);
80+
81+
const codeEditorShown = useAtomValue(codeEditorShownAtom);
82+
const exampleHtmlRef = useRef<HTMLDivElement>(null);
83+
8384
useEffect(() => {
8485
if (!exampleHtmlRef.current) {
8586
return;

apps/typegpu-docs/src/components/stackblitz/openInStackBlitz.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import typegpuColorPackageJson from '@typegpu/color/package.json' with { type: '
55
import typegpuNoisePackageJson from '@typegpu/noise/package.json' with { type: 'json' };
66
import typegpuSdfPackageJson from '@typegpu/sdf/package.json' with { type: 'json' };
77
import typegpuThreePackageJson from '@typegpu/three/package.json' with { type: 'json' };
8+
import typegpuReactPackageJson from '@typegpu/react/package.json' with { type: 'json' };
89
import typegpuPackageJson from 'typegpu/package.json' with { type: 'json' };
910
import unpluginPackageJson from 'unplugin-typegpu/package.json' with { type: 'json' };
1011
import pnpmWorkspace from '../../../../../pnpm-workspace.yaml?raw';
@@ -121,6 +122,7 @@ ${example.htmlFile.content}
121122
"@typegpu/color": "${typegpuColorPackageJson.version}",
122123
"@typegpu/sdf": "${typegpuSdfPackageJson.version}",
123124
"@typegpu/three": "${typegpuThreePackageJson.version}"
125+
"@typegpu/react": "${typegpuReactPackageJson.version}"
124126
}
125127
}`,
126128
'vite.config.js': `\

apps/typegpu-docs/src/examples/exampleContent.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,16 @@ const metaFiles = R.pipe(
5959
);
6060

6161
// Files that were generated by stripping away use of overloaded operators in .ts files
62-
// './<category>/<example>/<file>.tsnotover.ts'
63-
const exampleTsnotoverFiles = import.meta.glob('./*/*/*.tsnotover.ts', {
62+
// './<category>/<example>/<file>.tsnotover.tsx?'
63+
const exampleTsnotoverFiles = import.meta.glob(['./*/*/*.tsnotover.ts', './*/*/*.tsnotover.tsx'], {
6464
query: 'raw',
6565
eager: true,
6666
import: 'default',
6767
}) as Record<string, string>;
6868

6969
const exampleTsFiles = R.pipe(
70-
// './<category>/<example>/<file>.ts'
71-
import.meta.glob('./*/*/*.ts', {
70+
// './<category>/<example>/<file>.tsx?'
71+
import.meta.glob(['./**/*.ts', './**/*.tsx'], {
7272
query: 'raw',
7373
eager: true,
7474
import: 'default',
@@ -87,7 +87,7 @@ const exampleTsFiles = R.pipe(
8787
);
8888

8989
const tsFilesImportFunctions = R.pipe(
90-
import.meta.glob('./**/index.ts') as Record<string, () => Promise<unknown>>,
90+
import.meta.glob(['./**/index.ts', './**/index.tsx']) as Record<string, () => Promise<unknown>>,
9191
R.mapKeys(pathToExampleKey),
9292
);
9393

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div id="example-app"></div>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import * as d from 'typegpu/data';
2+
import { useFrame, useRender, useUniformValue } from '@typegpu/react';
3+
import { hsvToRgb } from '@typegpu/color';
4+
5+
function App() {
6+
const time = useUniformValue(d.f32, 0);
7+
8+
useFrame(() => {
9+
time.value = performance.now() / 1000;
10+
});
11+
12+
const { ref } = useRender({
13+
fragment: () => {
14+
'use gpu';
15+
const t = time.$;
16+
const rgb = hsvToRgb(d.vec3f(t * 0.5, 1, 1));
17+
return d.vec4f(rgb, 1);
18+
},
19+
});
20+
21+
return (
22+
<main>
23+
<canvas ref={ref} width="256" height="256" />
24+
</main>
25+
);
26+
}
27+
28+
// #region Example controls and cleanup
29+
30+
import { createRoot } from 'react-dom/client';
31+
const reactRoot = createRoot(document.getElementById('example-app') as HTMLDivElement);
32+
reactRoot.render(<App />);
33+
34+
export function onCleanup() {
35+
setTimeout(() => reactRoot.unmount(), 0);
36+
}
37+
38+
// #endregion
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"title": "React: Spinning Triangle",
3+
"category": "react",
4+
"tags": ["experimental"]
5+
}
181 KB
Loading

apps/typegpu-docs/src/utils/examples/sandboxModules.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@ function dtsFileToModule(
4646
] as const;
4747
}
4848

49+
const reactModules = pipe(
50+
entries(
51+
import.meta.glob('../../../node_modules/@types/react/**/*.d.ts', {
52+
query: 'raw',
53+
eager: true,
54+
import: 'default',
55+
}) as Record<string, string>,
56+
),
57+
map((dtsFile) => dtsFileToModule(dtsFile, '../../../node_modules/')),
58+
fromEntries(),
59+
);
60+
4961
const allPackagesSrcFiles = pipe(
5062
entries(
5163
import.meta.glob(
@@ -93,7 +105,11 @@ export const SANDBOX_MODULES: Record<string, SandboxModuleDefinition> = {
93105
...allPackagesSrcFiles,
94106
...threeModules,
95107
...mediacaptureModules,
108+
...reactModules,
96109

110+
react: {
111+
typeDef: { reroute: ['@types/react/index.d.ts'] },
112+
},
97113
'@webgpu/types': {
98114
typeDef: { content: dtsWebGPU },
99115
},
@@ -144,4 +160,10 @@ export const SANDBOX_MODULES: Record<string, SandboxModuleDefinition> = {
144160
'@typegpu/three': {
145161
typeDef: { reroute: 'typegpu-three/src/index.ts' },
146162
},
163+
'@typegpu/sdf': {
164+
typeDef: { reroute: ['typegpu-sdf/src/index.ts'] },
165+
},
166+
'@typegpu/react': {
167+
typeDef: { reroute: ['typegpu-react/src/index.ts'] },
168+
},
147169
};

apps/typegpu-docs/src/utils/liveEditor/embeddedTypeScript.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ export const tsCompilerOptions: languages.typescript.CompilerOptions = {
1010
skipLibCheck: true,
1111
exactOptionalPropertyTypes: true,
1212
baseUrl: '.',
13+
jsx: languages.typescript.JsxEmit.React,
1314
lib: ['dom', 'es2021'],
1415
};

0 commit comments

Comments
 (0)