diff --git a/apps/example/getWebMetroConfig.js b/apps/example/getWebMetroConfig.js
new file mode 100644
index 000000000..d16cdb946
--- /dev/null
+++ b/apps/example/getWebMetroConfig.js
@@ -0,0 +1,32 @@
+const path = require("path");
+const root = path.resolve(__dirname, "../..");
+const rnwPath = path.resolve(root, "node_modules/react-native-web");
+const assetRegistryPath = path.resolve(
+ root,
+ "node_modules/react-native-web/dist/modules/AssetRegistry/index",
+);
+
+module.exports = function (metroConfig) {
+ metroConfig.resolver.platforms = ["ios", "android", "web"];
+ const origResolveRequest = metroConfig.resolver.resolveRequest;
+ metroConfig.resolver.resolveRequest = (contextRaw, moduleName, platform) => {
+ const context = {
+ ...contextRaw,
+ preferNativePlatform: false,
+ };
+
+ if (moduleName === "react-native") {
+ return {
+ filePath: path.resolve(rnwPath, "dist/index.js"),
+ type: "sourceFile",
+ };
+ }
+
+ // Let default config handle other modules
+ return origResolveRequest(context, moduleName, platform);
+ };
+
+ metroConfig.transformer.assetRegistryPath = assetRegistryPath;
+
+ return metroConfig;
+};
diff --git a/apps/example/index.html b/apps/example/index.html
new file mode 100644
index 000000000..456a055c3
--- /dev/null
+++ b/apps/example/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+ Example
+
+
+
+
+
+
+
+
+
diff --git a/apps/example/index.web.js b/apps/example/index.web.js
new file mode 100644
index 000000000..405131a75
--- /dev/null
+++ b/apps/example/index.web.js
@@ -0,0 +1,22 @@
+import { AppRegistry } from "react-native";
+
+import App from "./src/App";
+import { name as appName } from "./app.json";
+
+AppRegistry.registerComponent(appName, () => App);
+
+const rootTag = document.getElementById("root");
+if (process.env.NODE_ENV !== "production") {
+ if (!rootTag) {
+ throw new Error(
+ 'Required HTML element with id "root" was not found in the document HTML.',
+ );
+ }
+}
+
+CanvasKitInit({
+ locateFile: (file) => `https://unpkg.com/canvaskit-wasm/bin/full/${file}`,
+}).then((CanvasKit) => {
+ window.CanvasKit = global.CanvasKit = CanvasKit;
+ AppRegistry.runApplication(appName, { rootTag });
+});
diff --git a/apps/example/ios/Podfile.lock b/apps/example/ios/Podfile.lock
index fa69eaf26..5b2e58d55 100644
--- a/apps/example/ios/Podfile.lock
+++ b/apps/example/ios/Podfile.lock
@@ -1865,7 +1865,7 @@ PODS:
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- - react-native-wgpu (0.3.2):
+ - react-native-wgpu (0.4.0):
- boost
- DoubleConversion
- fast_float
@@ -2903,7 +2903,7 @@ SPEC CHECKSUMS:
React-microtasksnativemodule: 75b6604b667d297292345302cc5bfb6b6aeccc1b
react-native-safe-area-context: c6e2edd1c1da07bdce287fa9d9e60c5f7b514616
react-native-skia: 5bf2b2107cd7f2d806fd364f5e16b1c7554ed3cd
- react-native-wgpu: 15ebc049194b0d06082ab00fb2e5d21ec871c2f4
+ react-native-wgpu: 5abf760f89d1517255e48de1b5208d2a307109a0
React-NativeModulesApple: 879fbdc5dcff7136abceb7880fe8a2022a1bd7c3
React-oscompat: 93b5535ea7f7dff46aaee4f78309a70979bdde9d
React-perflogger: 5536d2df3d18fe0920263466f7b46a56351c0510
diff --git a/apps/example/metro.config.js b/apps/example/metro.config.js
index cc4d67857..28b1ad125 100644
--- a/apps/example/metro.config.js
+++ b/apps/example/metro.config.js
@@ -1,9 +1,11 @@
const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config");
const path = require('path');
+const getWebMetroConfig = require('./getWebMetroConfig');
const root = path.resolve(__dirname, '../..');
const threePackagePath = path.resolve(root, 'node_modules/three');
+const r3fPath = path.resolve(root, "node_modules/@react-three/fiber");
const defaultConfig = getDefaultConfig(__dirname);
const customConfig = {
@@ -32,6 +34,14 @@ const customConfig = {
type: 'sourceFile',
};
}
+
+ if (moduleName === "@react-three/fiber") {
+ //Just use the vanilla web build of react three fiber, not the stale "native" code path which has not been kept up to date.
+ return {
+ filePath: path.resolve(r3fPath, "dist/react-three-fiber.esm.js"),
+ type: "sourceFile",
+ };
+ }
// Let Metro handle other modules
return context.resolveRequest(context, moduleName, platform);
},
@@ -51,5 +61,4 @@ const customConfig = {
const metroConfig = mergeConfig(defaultConfig, customConfig);
-
-module.exports = metroConfig;
+module.exports = !!process.env.IS_WEB_BUILD ? getWebMetroConfig(metroConfig) : metroConfig;
diff --git a/apps/example/package.json b/apps/example/package.json
index 1b8223271..95e9bc9a1 100644
--- a/apps/example/package.json
+++ b/apps/example/package.json
@@ -7,6 +7,7 @@
"tsc": "tsc --noEmit",
"android": "react-native run-android",
"ios": "react-native run-ios",
+ "web": "IS_WEB_BUILD=true react-native start",
"start": "react-native start",
"pod:install:ios": "pod install --project-directory=ios",
"pod:install:macos": "pod install --project-directory=macos",
@@ -28,11 +29,13 @@
"async-mutex": "^0.5.0",
"fast-text-encoding": "^1.0.6",
"react": "19.1.0",
+ "react-dom": "19.1.0",
"react-native": "0.81.4",
"react-native-gesture-handler": "^2.28.0",
"react-native-macos": "^0.79.0",
"react-native-reanimated": "3.19.1",
"react-native-safe-area-context": "^5.4.0",
+ "react-native-web": "^0.21.2",
"react-native-wgpu": "*",
"teapot": "^1.0.0",
"three": "0.172.0",
@@ -50,6 +53,7 @@
"@rnx-kit/metro-config": "^2.0.0",
"@types/node": "^20.14.7",
"@types/react": "^18.2.6",
+ "@types/react-dom": "^19.2.2",
"@types/react-test-renderer": "^18.0.0",
"@types/three": "0.172.0",
"@webgpu/types": "0.1.65",
diff --git a/apps/example/src/App.tsx b/apps/example/src/App.tsx
index 21637b42c..c1f2037d7 100644
--- a/apps/example/src/App.tsx
+++ b/apps/example/src/App.tsx
@@ -1,3 +1,5 @@
+import "./resolveAssetSourcePolyfill";
+
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import { GestureHandlerRootView } from "react-native-gesture-handler";
@@ -48,7 +50,10 @@ function App() {
return (
-
+
;
}
+// Use a CORS proxy for web to bypass CORS restrictions
+const getCorsProxyUrl = (url: string) => {
+ return Platform.OS === "web"
+ ? `https://corsproxy.io/?${encodeURIComponent(url)}`
+ : url;
+};
+
export const useComputeToy = (toyId: number) => {
const [props, setProps] = useState(null);
useEffect(() => {
(async () => {
- const shaderURL = `https://compute.toys/view/${toyId}/wgsl`;
- const uniformsURL = `https://compute.toys/view/${toyId}/json`;
+ const shaderURL = getCorsProxyUrl(
+ `https://compute.toys/view/${toyId}/wgsl`,
+ );
+ const uniformsURL = getCorsProxyUrl(
+ `https://compute.toys/view/${toyId}/json`,
+ );
// Execute both fetch requests in parallel
const [shaderResponse, uniformsResponse] = await Promise.all([
diff --git a/apps/example/src/Home.tsx b/apps/example/src/Home.tsx
index 0894a25f9..711978821 100644
--- a/apps/example/src/Home.tsx
+++ b/apps/example/src/Home.tsx
@@ -126,9 +126,11 @@ export const examples = [
];
const styles = StyleSheet.create({
- container: {},
+ container: {
+ flex: 1,
+ },
content: {
- paddingBottom: 32,
+ marginBottom: 32,
},
thumbnail: {
backgroundColor: "white",
diff --git a/apps/example/src/MNISTInference/MNISTInference.web.tsx b/apps/example/src/MNISTInference/MNISTInference.web.tsx
new file mode 100644
index 000000000..128b1b096
--- /dev/null
+++ b/apps/example/src/MNISTInference/MNISTInference.web.tsx
@@ -0,0 +1,3 @@
+export const MNISTInference = () => {
+ return null;
+};
diff --git a/apps/example/src/assets/helvetica.ttf b/apps/example/src/assets/helvetica.ttf
new file mode 100644
index 000000000..ecc926416
Binary files /dev/null and b/apps/example/src/assets/helvetica.ttf differ
diff --git a/apps/example/src/resolveAssetSourcePolyfill.js b/apps/example/src/resolveAssetSourcePolyfill.js
new file mode 100644
index 000000000..315749983
--- /dev/null
+++ b/apps/example/src/resolveAssetSourcePolyfill.js
@@ -0,0 +1,57 @@
+import { Image, PixelRatio, Platform } from "react-native";
+import { getAssetByID } from "react-native-web/dist/modules/AssetRegistry";
+
+// react-native-web does not support resolveAssetSource out of the box
+// https://github.com/necolas/react-native-web/issues/1666
+if (Platform.OS == "web") {
+ function resolveAssetUri(source) {
+ let uri = null;
+ if (typeof source === "number") {
+ // get the URI from the packager
+ const asset = getAssetByID(source);
+ if (asset == null) {
+ throw new Error(
+ `Image: asset with ID "${source}" could not be found. Please check the image source or packager.`,
+ );
+ }
+ // eslint-disable-next-line prefer-destructuring
+ let scale = asset.scales[0];
+ if (asset.scales.length > 1) {
+ const preferredScale = PixelRatio.get();
+ // Get the scale which is closest to the preferred scale
+ scale = asset.scales.reduce((prev, curr) =>
+ Math.abs(curr - preferredScale) < Math.abs(prev - preferredScale)
+ ? curr
+ : prev,
+ );
+ }
+ const scaleSuffix = scale !== 1 ? `@${scale}x` : "";
+ uri = asset
+ ? `${asset.httpServerLocation}/${asset.name}${scaleSuffix}.${asset.type}`
+ : "";
+ } else if (typeof source === "string") {
+ uri = source;
+ } else if (source && typeof source.uri === "string") {
+ // eslint-disable-next-line prefer-destructuring
+ uri = source.uri;
+ }
+
+ if (uri) {
+ const svgDataUriPattern = /^(data:image\/svg\+xml;utf8,)(.*)/;
+ const match = uri.match(svgDataUriPattern);
+ // inline SVG markup may contain characters (e.g., #, ") that need to be escaped
+ if (match) {
+ const [, prefix, svg] = match;
+ const encodedSvg = encodeURIComponent(svg);
+ return `${prefix}${encodedSvg}`;
+ }
+ }
+
+ return uri;
+ }
+
+ Image.resolveAssetSource = (source) => {
+ const uri = resolveAssetUri(source) || "";
+ return { uri };
+ };
+}
diff --git a/package.json b/package.json
index c837e2c27..d66820d35 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"pod:install": "turbo run pod:install"
},
"devDependencies": {
+ "@types/react-native-web": "^0.19.2",
"turbo": "^2.1.0"
}
}
diff --git a/packages/webgpu/README.md b/packages/webgpu/README.md
index 5ed3e459a..ea2eb0ccc 100644
--- a/packages/webgpu/README.md
+++ b/packages/webgpu/README.md
@@ -1,7 +1,7 @@
# React Native WebGPU
-React Native implementation of WebGPU using [Dawn](https://dawn.googlesource.com/dawn).
-This is currently a technical preview for early adopters.
+React Native implementation of WebGPU using [Dawn](https://dawn.googlesource.com/dawn).
+This is currently a technical preview for early adopters.
React Native WebGPU requires React Native 0.81 or newer and doesn't run on legacy architecture.
@@ -13,6 +13,8 @@ Please note that the package name is `react-native-wgpu`.
npm install react-native-wgpu
```
+Note that if you use pnpm, you MUST use a `node-linker = hoisted` so that the external reference to the Dawn webgpu library can successfully link it.
+
Below are some examples from the [example app](/apps/example/).
https://github.com/user-attachments/assets/116a41b2-2cf8-49f1-9f16-a5c83637c198
@@ -156,8 +158,8 @@ From there you will be able to run the example app properly.
## Similarities and Differences with the Web
-The API has been designed to be completely symmetric with the Web.
-For instance, you can access the WebGPU context synchronously, as well as the canvas size.
+The API has been designed to be completely symmetric with the Web.
+For instance, you can access the WebGPU context synchronously, as well as the canvas size.
Pixel density and canvas resizing are handled exactly like on the Web as well.
```tsx
@@ -171,7 +173,7 @@ ctx.canvas.height = ctx.canvas.clientHeight * PixelRatio.get();
### Frame Scheduling
-In React Native, we want to keep frame presentation as a manual operation as we plan to provide more advanced rendering options that are React Native specific.
+In React Native, we want to keep frame presentation as a manual operation as we plan to provide more advanced rendering options that are React Native specific.
This means that when you are ready to present a frame, you need to call `present` on the context.
```tsx
diff --git a/packages/webgpu/package.json b/packages/webgpu/package.json
index 3a688afe2..801e206a4 100644
--- a/packages/webgpu/package.json
+++ b/packages/webgpu/package.json
@@ -1,6 +1,6 @@
{
"name": "react-native-wgpu",
- "version": "0.3.2",
+ "version": "0.4.0",
"description": "React Native WebGPU",
"main": "lib/commonjs/index",
"module": "lib/module/index",
@@ -81,6 +81,7 @@
"react": "19.1.0",
"react-native": "0.81.4",
"react-native-builder-bob": "^0.23.2",
+ "react-native-web": "^0.21.2",
"rimraf": "^5.0.7",
"seedrandom": "^3.0.5",
"teapot": "^1.0.0",
diff --git a/packages/webgpu/src/Canvas.tsx b/packages/webgpu/src/Canvas.tsx
index e311f0b48..5ea1bf1c9 100644
--- a/packages/webgpu/src/Canvas.tsx
+++ b/packages/webgpu/src/Canvas.tsx
@@ -1,5 +1,5 @@
import type { ViewProps } from "react-native";
-import { View } from "react-native";
+import { Platform, View } from "react-native";
import { forwardRef, useImperativeHandle, useRef, useState } from "react";
import WebGPUNativeView from "./WebGPUViewNativeComponent";
@@ -62,9 +62,16 @@ export const Canvas = forwardRef<
if (!viewRef.current) {
throw new Error("[WebGPU] Cannot get context before mount");
}
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-expect-error
- const size = viewRef.current.unstable_getBoundingClientRect();
+ let size;
+ if (Platform.OS === "web") {
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-expect-error
+ size = viewRef.current.getBoundingClientRect();
+ } else {
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-expect-error
+ size = viewRef.current.unstable_getBoundingClientRect();
+ }
return RNWebGPU.MakeWebGPUCanvasContext(
contextId,
size.width,
@@ -72,6 +79,7 @@ export const Canvas = forwardRef<
);
},
}));
+
return (
void>(
+ func: T,
+ wait: number,
+ immediate = false,
+) {
+ let timeout: ReturnType | undefined;
+ return function debounced(
+ this: ThisParameterType,
+ ...args: Parameters
+ ) {
+ const context = this;
+ const callNow = immediate && !timeout;
+ if (timeout) {
+ clearTimeout(timeout);
+ }
+ timeout = setTimeout(() => {
+ timeout = undefined;
+ if (!immediate) {
+ func.apply(context, args);
+ }
+ }, wait);
+ if (callNow) {
+ func.apply(context, args);
+ }
+ };
+}
+
+function resizeCanvas(canvas?: HTMLCanvasElement) {
+ if (!canvas) {
+ return;
+ }
+
+ const dpr = window.devicePixelRatio || 1;
+
+ const { height, width } = canvas.getBoundingClientRect();
+ canvas.setAttribute("height", (height * dpr).toString());
+ canvas.setAttribute("width", (width * dpr).toString());
+}
+
+// eslint-disable-next-line import/no-default-export
+export default function WebGPUViewNativeComponent(props: NativeProps) {
+ const { contextId, style, transparent, ...rest } = props;
+
+ const canvasElm = useRef();
+
+ useEffect(() => {
+ const onResize = debounce(() => resizeCanvas(canvasElm.current), 100);
+ window.addEventListener("resize", onResize);
+ return () => {
+ window.removeEventListener("resize", onResize);
+ };
+ }, []);
+
+ return unstableCreateElement("canvas", {
+ ...rest,
+ style: [
+ styles.view,
+ styles.flex1,
+ transparent === false && { backgroundColor: "white" }, // Canvas elements are transparent by default on the web
+ style,
+ ],
+ id: contextIdToId(contextId),
+ ref: (ref: HTMLCanvasElement) => {
+ canvasElm.current = ref;
+ if (ref) {
+ resizeCanvas(ref);
+ }
+ },
+ });
+}
+
+const styles = StyleSheet.create({
+ flex1: {
+ flex: 1,
+ },
+ view: {
+ alignItems: "stretch",
+ backgroundColor: "transparent",
+ // @ts-expect-error - not a valid RN style, but it's valid for web
+ border: "0 solid black",
+ boxSizing: "border-box",
+ display: "flex",
+ flexBasis: "auto",
+ flexDirection: "column",
+ flexShrink: 0,
+ listStyle: "none",
+ margin: 0,
+ minHeight: 0,
+ minWidth: 0,
+ padding: 0,
+ position: "relative",
+ zIndex: 0,
+ },
+});
diff --git a/packages/webgpu/src/WebPolyfillGPUModule.ts b/packages/webgpu/src/WebPolyfillGPUModule.ts
new file mode 100644
index 000000000..9dcc1f1c5
--- /dev/null
+++ b/packages/webgpu/src/WebPolyfillGPUModule.ts
@@ -0,0 +1,53 @@
+import { contextIdToId } from "./utils";
+
+const fabric = true;
+
+function getNativeSurface(contextId: number) {
+ const canvas = document.getElementById(
+ contextIdToId(contextId),
+ ) as HTMLCanvasElement;
+
+ const { height, width } = canvas.getBoundingClientRect()!;
+
+ return {
+ surface: BigInt(contextId),
+ height,
+ width,
+ clientHeight: height,
+ clientWidth: width,
+ };
+}
+
+function makeWebGPUCanvasContext(
+ contextId: number,
+ width: number,
+ height: number,
+) {
+ const canvas = document.getElementById(
+ contextIdToId(contextId),
+ ) as HTMLCanvasElement;
+
+ const dpr = window.devicePixelRatio || 1;
+ const pixelWidth = (width * dpr).toString();
+ const pixelHeight = (height * dpr).toString();
+
+ if (
+ canvas.getAttribute("width") !== pixelWidth ||
+ canvas.getAttribute("height") !== pixelHeight
+ ) {
+ canvas.setAttribute("width", pixelWidth);
+ canvas.setAttribute("height", pixelHeight);
+ }
+
+ const context = canvas.getContext("webgpu")!;
+ return Object.assign(context, {
+ present: () => {},
+ });
+}
+
+// @ts-expect-error - polyfill for RNWebGPU native module
+window.RNWebGPU = {
+ getNativeSurface,
+ MakeWebGPUCanvasContext: makeWebGPUCanvasContext,
+ fabric,
+};
diff --git a/packages/webgpu/src/index.tsx b/packages/webgpu/src/index.tsx
index 88d6e991e..46875c256 100644
--- a/packages/webgpu/src/index.tsx
+++ b/packages/webgpu/src/index.tsx
@@ -1,197 +1,26 @@
-/* eslint-disable @typescript-eslint/no-explicit-any */
-import WebGPUNativeModule from "./NativeWebGPUModule";
-
-export * from "./Canvas";
-export * from "./Offscreen";
-export * from "./WebGPUViewNativeComponent";
-export * from "./hooks";
-export { default as WebGPUModule } from "./NativeWebGPUModule";
-
-const GPU: any = {};
-GPU[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPU";
-};
-
-const GPUAdapter: any = {};
-GPUAdapter[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUAdapter";
-};
-
-const GPUAdapterInfo: any = {};
-GPUAdapterInfo[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUAdapterInfo";
-};
-
-const GPUBindGroup: any = {};
-GPUBindGroup[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUBindGroup";
-};
-
-const GPUBindGroupLayout: any = {};
-GPUBindGroupLayout[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUBindGroupLayout";
-};
-
-const GPUBuffer: any = {};
-GPUBuffer[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUBuffer";
-};
-
-const GPUCanvasContext: any = {};
-GPUCanvasContext[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUCanvasContext";
-};
-
-const GPUCommandBuffer: any = {};
-GPUCommandBuffer[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUCommandBuffer";
-};
-
-const GPUCommandEncoder: any = {};
-GPUCommandEncoder[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUCommandEncoder";
-};
-
-const GPUCompilationInfo: any = {};
-GPUCompilationInfo[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUCompilationInfo";
-};
-
-const GPUCompilationMessage: any = {};
-GPUCompilationMessage[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUCompilationMessage";
-};
-
-const GPUComputePassEncoder: any = {};
-GPUComputePassEncoder[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUComputePassEncoder";
-};
-
-const GPUComputePipeline: any = {};
-GPUComputePipeline[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUComputePipeline";
-};
-
-const GPUDevice: any = {};
-GPUDevice[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUDevice";
-};
-
-const GPUDeviceLostInfo: any = {};
-GPUDeviceLostInfo[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUDeviceLostInfo";
-};
-
-const GPUError: any = {};
-GPUError[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUError";
-};
-
-const GPUExternalTexture: any = {};
-GPUExternalTexture[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUExternalTexture";
-};
-
-const GPUPipelineLayout: any = {};
-GPUPipelineLayout[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUPipelineLayout";
-};
-
-const GPUQuerySet: any = {};
-GPUQuerySet[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUQuerySet";
-};
-
-const GPUQueue: any = {};
-GPUQueue[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUQueue";
-};
-
-const GPURenderBundle: any = {};
-GPURenderBundle[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPURenderBundle";
-};
-
-const GPURenderBundleEncoder: any = {};
-GPURenderBundleEncoder[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPURenderBundleEncoder";
-};
-
-const GPURenderPassEncoder: any = {};
-GPURenderPassEncoder[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPURenderPassEncoder";
-};
-
-const GPURenderPipeline: any = {};
-GPURenderPipeline[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPURenderPipeline";
-};
-
-const GPUSampler: any = {};
-GPUSampler[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUSampler";
-};
-
-const GPUShaderModule: any = {};
-GPUShaderModule[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUShaderModule";
-};
-
-const GPUTexture: any = {};
-GPUTexture[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUTexture";
-};
-
-const GPUTextureView: any = {};
-GPUTextureView[Symbol.hasInstance] = function (instance: object) {
- return "__brand" in instance && instance.__brand === "GPUTextureView";
-};
-
-global.GPU = GPU;
-global.GPUAdapter = GPUAdapter;
-global.GPUAdapterInfo = GPUAdapterInfo;
-global.GPUBindGroup = GPUBindGroup;
-global.GPUBindGroupLayout = GPUBindGroupLayout;
-global.GPUBuffer = GPUBuffer;
-global.GPUCanvasContext = GPUCanvasContext;
-global.GPUCommandBuffer = GPUCommandBuffer;
-global.GPUCommandEncoder = GPUCommandEncoder;
-global.GPUCompilationInfo = GPUCompilationInfo;
-global.GPUCompilationMessage = GPUCompilationMessage;
-global.GPUComputePassEncoder = GPUComputePassEncoder;
-global.GPUComputePipeline = GPUComputePipeline;
-global.GPUDevice = GPUDevice;
-global.GPUDeviceLostInfo = GPUDeviceLostInfo;
-global.GPUError = GPUError;
-global.GPUExternalTexture = GPUExternalTexture;
-global.GPUPipelineLayout = GPUPipelineLayout;
-global.GPUQuerySet = GPUQuerySet;
-global.GPUQueue = GPUQueue;
-global.GPURenderBundle = GPURenderBundle;
-global.GPURenderBundleEncoder = GPURenderBundleEncoder;
-global.GPURenderPassEncoder = GPURenderPassEncoder;
-global.GPURenderPipeline = GPURenderPipeline;
-global.GPUSampler = GPUSampler;
-global.GPUShaderModule = GPUShaderModule;
-global.GPUTexture = GPUTexture;
-global.GPUTextureView = GPUTextureView;
-
-WebGPUNativeModule.install();
-
-if (!navigator) {
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-expect-error
- navigator = {};
+///
+
+import type { NativeCanvas, RNCanvasContext } from "./types";
+
+export * from "./main";
+
+declare global {
+ interface Navigator {
+ gpu: GPU;
+ }
+
+ var navigator: Navigator;
+
+ var RNWebGPU: {
+ gpu: GPU;
+ fabric: boolean;
+ getNativeSurface: (contextId: number) => NativeCanvas;
+ MakeWebGPUCanvasContext: (
+ contextId: number,
+ width: number,
+ height: number,
+ ) => RNCanvasContext;
+ DecodeToUTF8: (buffer: NodeJS.ArrayBufferView | ArrayBuffer) => string;
+ createImageBitmap: typeof createImageBitmap;
+ };
}
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-// @ts-expect-error
-navigator.gpu = RNWebGPU.gpu;
-
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-// @ts-ignore
-navigator.userAgent = "react-native";
-
-global.createImageBitmap =
- global.createImageBitmap ??
- ((...params: Parameters) =>
- new Promise((resolve) => resolve(RNWebGPU.createImageBitmap(...params))));
diff --git a/packages/webgpu/src/main/index.tsx b/packages/webgpu/src/main/index.tsx
new file mode 100644
index 000000000..3bcbe6c22
--- /dev/null
+++ b/packages/webgpu/src/main/index.tsx
@@ -0,0 +1,204 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import WebGPUModule from "../NativeWebGPUModule";
+
+export * from "../Canvas";
+export * from "../Offscreen";
+export * from "../WebGPUViewNativeComponent";
+export * from "../hooks";
+
+export { default as WebGPUModule } from "../NativeWebGPUModule";
+
+const GPU: any = {};
+GPU[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPU";
+};
+
+const GPUAdapter: any = {};
+GPUAdapter[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUAdapter";
+};
+
+const GPUAdapterInfo: any = {};
+GPUAdapterInfo[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUAdapterInfo";
+};
+
+const GPUBindGroup: any = {};
+GPUBindGroup[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUBindGroup";
+};
+
+const GPUBindGroupLayout: any = {};
+GPUBindGroupLayout[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUBindGroupLayout";
+};
+
+const GPUBuffer: any = {};
+GPUBuffer[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUBuffer";
+};
+
+const GPUCanvasContext: any = {};
+GPUCanvasContext[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUCanvasContext";
+};
+
+const GPUCommandBuffer: any = {};
+GPUCommandBuffer[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUCommandBuffer";
+};
+
+const GPUCommandEncoder: any = {};
+GPUCommandEncoder[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUCommandEncoder";
+};
+
+const GPUCompilationInfo: any = {};
+GPUCompilationInfo[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUCompilationInfo";
+};
+
+const GPUCompilationMessage: any = {};
+GPUCompilationMessage[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUCompilationMessage";
+};
+
+const GPUComputePassEncoder: any = {};
+GPUComputePassEncoder[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUComputePassEncoder";
+};
+
+const GPUComputePipeline: any = {};
+GPUComputePipeline[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUComputePipeline";
+};
+
+const GPUDevice: any = {};
+GPUDevice[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUDevice";
+};
+
+const GPUDeviceLostInfo: any = {};
+GPUDeviceLostInfo[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUDeviceLostInfo";
+};
+
+const GPUError: any = {};
+GPUError[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUError";
+};
+
+const GPUExternalTexture: any = {};
+GPUExternalTexture[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUExternalTexture";
+};
+
+const GPUPipelineLayout: any = {};
+GPUPipelineLayout[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUPipelineLayout";
+};
+
+const GPUQuerySet: any = {};
+GPUQuerySet[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUQuerySet";
+};
+
+const GPUQueue: any = {};
+GPUQueue[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUQueue";
+};
+
+const GPURenderBundle: any = {};
+GPURenderBundle[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPURenderBundle";
+};
+
+const GPURenderBundleEncoder: any = {};
+GPURenderBundleEncoder[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPURenderBundleEncoder";
+};
+
+const GPURenderPassEncoder: any = {};
+GPURenderPassEncoder[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPURenderPassEncoder";
+};
+
+const GPURenderPipeline: any = {};
+GPURenderPipeline[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPURenderPipeline";
+};
+
+const GPUSampler: any = {};
+GPUSampler[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUSampler";
+};
+
+const GPUShaderModule: any = {};
+GPUShaderModule[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUShaderModule";
+};
+
+const GPUTexture: any = {};
+GPUTexture[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUTexture";
+};
+
+const GPUTextureView: any = {};
+GPUTextureView[Symbol.hasInstance] = function (instance: object) {
+ return "__brand" in instance && instance.__brand === "GPUTextureView";
+};
+
+global.GPU = GPU;
+global.GPUAdapter = GPUAdapter;
+global.GPUAdapterInfo = GPUAdapterInfo;
+global.GPUBindGroup = GPUBindGroup;
+global.GPUBindGroupLayout = GPUBindGroupLayout;
+global.GPUBuffer = GPUBuffer;
+global.GPUCanvasContext = GPUCanvasContext;
+global.GPUCommandBuffer = GPUCommandBuffer;
+global.GPUCommandEncoder = GPUCommandEncoder;
+global.GPUCompilationInfo = GPUCompilationInfo;
+global.GPUCompilationMessage = GPUCompilationMessage;
+global.GPUComputePassEncoder = GPUComputePassEncoder;
+global.GPUComputePipeline = GPUComputePipeline;
+global.GPUDevice = GPUDevice;
+global.GPUDeviceLostInfo = GPUDeviceLostInfo;
+global.GPUError = GPUError;
+global.GPUExternalTexture = GPUExternalTexture;
+global.GPUPipelineLayout = GPUPipelineLayout;
+global.GPUQuerySet = GPUQuerySet;
+global.GPUQueue = GPUQueue;
+global.GPURenderBundle = GPURenderBundle;
+global.GPURenderBundleEncoder = GPURenderBundleEncoder;
+global.GPURenderPassEncoder = GPURenderPassEncoder;
+global.GPURenderPipeline = GPURenderPipeline;
+global.GPUSampler = GPUSampler;
+global.GPUShaderModule = GPUShaderModule;
+global.GPUTexture = GPUTexture;
+global.GPUTextureView = GPUTextureView;
+
+WebGPUModule.install();
+
+if (!navigator) {
+ // @ts-expect-error Navigation object is more complex than this, setting it to an empty object to add gpu property
+ navigator = {
+ gpu: RNWebGPU.gpu,
+ userAgent: "react-native",
+ };
+} else {
+ navigator.gpu = RNWebGPU.gpu;
+ if (typeof navigator.userAgent !== "string") {
+ try {
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore - Hermes navigator may not include a userAgent, align with the polyfill if needed
+ navigator.userAgent = "react-native";
+ } catch {
+ // navigator.userAgent can be read-only; ignore if assignment fails
+ }
+ }
+}
+
+global.createImageBitmap =
+ global.createImageBitmap ??
+ ((...params: Parameters) =>
+ new Promise((resolve) => resolve(RNWebGPU.createImageBitmap(...params))));
diff --git a/packages/webgpu/src/main/index.web.tsx b/packages/webgpu/src/main/index.web.tsx
new file mode 100644
index 000000000..b848bbc69
--- /dev/null
+++ b/packages/webgpu/src/main/index.web.tsx
@@ -0,0 +1,8 @@
+import "../WebPolyfillGPUModule";
+
+export * from "../Canvas";
+export * from "../Offscreen";
+export * from "../WebGPUViewNativeComponent";
+export * from "../hooks";
+
+// We don't need to set all global properties on web, webgpu is already available globally
diff --git a/packages/webgpu/src/types.ts b/packages/webgpu/src/types.ts
new file mode 100644
index 000000000..af4684cfa
--- /dev/null
+++ b/packages/webgpu/src/types.ts
@@ -0,0 +1,20 @@
+type SurfacePointer = bigint;
+
+export interface NativeCanvas {
+ surface: SurfacePointer;
+ width: number;
+ height: number;
+ clientWidth: number;
+ clientHeight: number;
+}
+
+export type RNCanvasContext = GPUCanvasContext & {
+ present: () => void;
+};
+
+export interface CanvasRef {
+ getContextId: () => number;
+ getContext(contextName: "webgpu"): RNCanvasContext | null;
+ getNativeSurface: () => NativeCanvas;
+ whenReady: (callback: () => void) => void;
+}
diff --git a/packages/webgpu/src/utils.ts b/packages/webgpu/src/utils.ts
new file mode 100644
index 000000000..39fa1b8df
--- /dev/null
+++ b/packages/webgpu/src/utils.ts
@@ -0,0 +1,4 @@
+// Only used on the web
+export function contextIdToId(contextId: number) {
+ return "rnwgpu-canvas-" + contextId;
+}
diff --git a/yarn.lock b/yarn.lock
index 09d499738..766610bc0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1585,7 +1585,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.25.0":
+"@babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.25.0":
version: 7.28.4
resolution: "@babel/runtime@npm:7.28.4"
checksum: 934b0a0460f7d06637d93fcd1a44ac49adc33518d17253b5a0b55ff4cb90a45d8fe78bf034b448911dbec7aff2a90b918697559f78d21c99ff8dbadae9565b55
@@ -2799,6 +2799,13 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/assets-registry@npm:0.82.1":
+ version: 0.82.1
+ resolution: "@react-native/assets-registry@npm:0.82.1"
+ checksum: f511a248f455c7fe2bae330ed15929294d5d25e5d8aa27e148588a554bde31a1ddf54d08ebc21128cabe9978be4604b2e8ad4343566c9840fc49e2b0d119cbc6
+ languageName: node
+ linkType: hard
+
"@react-native/babel-plugin-codegen@npm:0.74.84":
version: 0.74.84
resolution: "@react-native/babel-plugin-codegen@npm:0.74.84"
@@ -2992,6 +2999,23 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/codegen@npm:0.82.1":
+ version: 0.82.1
+ resolution: "@react-native/codegen@npm:0.82.1"
+ dependencies:
+ "@babel/core": ^7.25.2
+ "@babel/parser": ^7.25.3
+ glob: ^7.1.1
+ hermes-parser: 0.32.0
+ invariant: ^2.2.4
+ nullthrows: ^1.1.1
+ yargs: ^17.6.2
+ peerDependencies:
+ "@babel/core": "*"
+ checksum: d965d9f387a62e27a75d80546a6c825b21a42b56738eedf294b59221c117345148daa0362af5b793a2d5ce3e089e3682dd4bdd9548235ab040543f0aa21d46f0
+ languageName: node
+ linkType: hard
+
"@react-native/community-cli-plugin@npm:0.74.84":
version: 0.74.84
resolution: "@react-native/community-cli-plugin@npm:0.74.84"
@@ -3056,6 +3080,29 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/community-cli-plugin@npm:0.82.1":
+ version: 0.82.1
+ resolution: "@react-native/community-cli-plugin@npm:0.82.1"
+ dependencies:
+ "@react-native/dev-middleware": 0.82.1
+ debug: ^4.4.0
+ invariant: ^2.2.4
+ metro: ^0.83.1
+ metro-config: ^0.83.1
+ metro-core: ^0.83.1
+ semver: ^7.1.3
+ peerDependencies:
+ "@react-native-community/cli": "*"
+ "@react-native/metro-config": "*"
+ peerDependenciesMeta:
+ "@react-native-community/cli":
+ optional: true
+ "@react-native/metro-config":
+ optional: true
+ checksum: 680aef3270c56a73467ba40f7de416d32fef8b7e484421fccd437f940692eee76382e82eddd0a4749675b855d642fd6c83b147136bdcf03ee84f36ed38f7e0de
+ languageName: node
+ linkType: hard
+
"@react-native/debugger-frontend@npm:0.74.84":
version: 0.74.84
resolution: "@react-native/debugger-frontend@npm:0.74.84"
@@ -3077,6 +3124,23 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/debugger-frontend@npm:0.82.1":
+ version: 0.82.1
+ resolution: "@react-native/debugger-frontend@npm:0.82.1"
+ checksum: b767c7586c782a130d3579a1d8c137a8c55361d579028e44a31b220c566ab793a83b256b39eb114a759e07031574cd142cae1bdc1ec80dc02e7a6a191409548e
+ languageName: node
+ linkType: hard
+
+"@react-native/debugger-shell@npm:0.82.1":
+ version: 0.82.1
+ resolution: "@react-native/debugger-shell@npm:0.82.1"
+ dependencies:
+ cross-spawn: ^7.0.6
+ fb-dotslash: 0.5.8
+ checksum: 9b4ec7f413d5e776a7361f1a5e8ecc7dbc8b56c7fc119fec1895f4821071a7b95b2a3db0cef016614a1eb50288df0282c7f7cf5944e3934bc8694529556f44e8
+ languageName: node
+ linkType: hard
+
"@react-native/dev-middleware@npm:0.74.84":
version: 0.74.84
resolution: "@react-native/dev-middleware@npm:0.74.84"
@@ -3136,6 +3200,26 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/dev-middleware@npm:0.82.1":
+ version: 0.82.1
+ resolution: "@react-native/dev-middleware@npm:0.82.1"
+ dependencies:
+ "@isaacs/ttlcache": ^1.4.1
+ "@react-native/debugger-frontend": 0.82.1
+ "@react-native/debugger-shell": 0.82.1
+ chrome-launcher: ^0.15.2
+ chromium-edge-launcher: ^0.2.0
+ connect: ^3.6.5
+ debug: ^4.4.0
+ invariant: ^2.2.4
+ nullthrows: ^1.1.1
+ open: ^7.0.3
+ serve-static: ^1.16.2
+ ws: ^6.2.3
+ checksum: 0fed27cb7d7bd9e2e3b9cd20776000ec730ea6672779ccd971e831c67a4b25adcda9d82e0042d3f37e1311736add0d4bb51519c463ea81a11565a2bac1cee68c
+ languageName: node
+ linkType: hard
+
"@react-native/eslint-config@npm:0.81.0":
version: 0.81.0
resolution: "@react-native/eslint-config@npm:0.81.0"
@@ -3187,6 +3271,13 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/gradle-plugin@npm:0.82.1":
+ version: 0.82.1
+ resolution: "@react-native/gradle-plugin@npm:0.82.1"
+ checksum: 7e7e2d768a8ff599dba5ef7b0a417e1d14a032a3344cc1e57852d4ebee1587dc877f83ae9dd4beae3b27fe2389d235227df12bd8aaa9be8b6ef1c7784419e0de
+ languageName: node
+ linkType: hard
+
"@react-native/js-polyfills@npm:0.74.84":
version: 0.74.84
resolution: "@react-native/js-polyfills@npm:0.74.84"
@@ -3215,6 +3306,13 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/js-polyfills@npm:0.82.1":
+ version: 0.82.1
+ resolution: "@react-native/js-polyfills@npm:0.82.1"
+ checksum: 271d5bcff95d3867237ae4ec4745247c7048ea950912b3c31c8bbffd801c714509294b04d176c8121389788a192680f482b21e99ab24c9b2dcbce37acfdeaa5f
+ languageName: node
+ linkType: hard
+
"@react-native/metro-babel-transformer@npm:0.74.84":
version: 0.74.84
resolution: "@react-native/metro-babel-transformer@npm:0.74.84"
@@ -3276,6 +3374,20 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/normalize-colors@npm:0.82.1":
+ version: 0.82.1
+ resolution: "@react-native/normalize-colors@npm:0.82.1"
+ checksum: d180cc6591989a3d490ad4454d63a19ec9be796314632adb29051515eb31e98fbdd12903c00750d4ce023306e159a2498867bf6f25bcf11a8ed48e5486482947
+ languageName: node
+ linkType: hard
+
+"@react-native/normalize-colors@npm:^0.74.1":
+ version: 0.74.89
+ resolution: "@react-native/normalize-colors@npm:0.74.89"
+ checksum: df62772f029dd132d3061a8ee7f90b6aaf5c525bb7e33c22908249daffa42995cddd91adc790ec9ce701636c14eeafc1809a9d0d879e3f8c71c9f340145abfce
+ languageName: node
+ linkType: hard
+
"@react-native/typescript-config@npm:0.81.0":
version: 0.81.0
resolution: "@react-native/typescript-config@npm:0.81.0"
@@ -3334,6 +3446,23 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/virtualized-lists@npm:0.82.1":
+ version: 0.82.1
+ resolution: "@react-native/virtualized-lists@npm:0.82.1"
+ dependencies:
+ invariant: ^2.2.4
+ nullthrows: ^1.1.1
+ peerDependencies:
+ "@types/react": ^19.1.1
+ react: "*"
+ react-native: "*"
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 4961af57d477f16c1b7e0b584e9f54cef876bbb738b3e628b5dfc8ddc157f83bbe1b3e9d8a0c426f41018cdec5d1073f2ffe23b830e288c7d8b730753700608a
+ languageName: node
+ linkType: hard
+
"@react-navigation/core@npm:^6.4.17":
version: 6.4.17
resolution: "@react-navigation/core@npm:6.4.17"
@@ -4038,6 +4167,25 @@ __metadata:
languageName: node
linkType: hard
+"@types/react-dom@npm:^19.2.2":
+ version: 19.2.2
+ resolution: "@types/react-dom@npm:19.2.2"
+ peerDependencies:
+ "@types/react": ^19.2.0
+ checksum: a9e16d59f89b2794a3b062766de2eedf98cf66e59de7560de5beb95fb8742161b2dc4751530380c38d51320bc99b8a1d66fa113cee9b5d0f138ef6fb49fb4ce9
+ languageName: node
+ linkType: hard
+
+"@types/react-native-web@npm:^0.19.2":
+ version: 0.19.2
+ resolution: "@types/react-native-web@npm:0.19.2"
+ dependencies:
+ "@types/react": "*"
+ react-native: "*"
+ checksum: d6c6bbaa4610dbc1588205b2a936d88938f3d24e6b6a79d238192ff1cf1dc1865864c9c5e00d500c108f0324b6464c3903e5b5aba9e69fc1ab5638ec81b0f163
+ languageName: node
+ linkType: hard
+
"@types/react-reconciler@npm:^0.28.9":
version: 0.28.9
resolution: "@types/react-reconciler@npm:0.28.9"
@@ -4714,6 +4862,7 @@ __metadata:
"@tensorflow/tfjs-vis": ^1.5.1
"@types/node": ^20.14.7
"@types/react": ^18.2.6
+ "@types/react-dom": ^19.2.2
"@types/react-test-renderer": ^18.0.0
"@types/three": 0.172.0
"@webgpu/types": 0.1.65
@@ -4726,12 +4875,14 @@ __metadata:
jest: ^29.6.3
prettier: 2.8.8
react: 19.1.0
+ react-dom: 19.1.0
react-native: 0.81.4
react-native-gesture-handler: ^2.28.0
react-native-macos: ^0.79.0
react-native-reanimated: 3.19.1
react-native-safe-area-context: ^5.4.0
react-native-test-app: 4.4.10
+ react-native-web: ^0.21.2
react-native-wgpu: "*"
react-test-renderer: 18.2.0
teapot: ^1.0.0
@@ -5280,6 +5431,15 @@ __metadata:
languageName: node
linkType: hard
+"babel-plugin-syntax-hermes-parser@npm:0.32.0":
+ version: 0.32.0
+ resolution: "babel-plugin-syntax-hermes-parser@npm:0.32.0"
+ dependencies:
+ hermes-parser: 0.32.0
+ checksum: ec76abeefabf940e2d571db3b47d022a9be7602286133291e8e047d4855af6a8afc079e4631bc9a56209d751fad54b5199932a55753b1e2b56a719d20e2d5065
+ languageName: node
+ linkType: hard
+
"babel-plugin-transform-flow-enums@npm:^0.0.2":
version: 0.0.2
resolution: "babel-plugin-transform-flow-enums@npm:0.0.2"
@@ -6125,6 +6285,15 @@ __metadata:
languageName: node
linkType: hard
+"cross-fetch@npm:^3.1.5":
+ version: 3.2.0
+ resolution: "cross-fetch@npm:3.2.0"
+ dependencies:
+ node-fetch: ^2.7.0
+ checksum: 8ded5ea35f705e81e569e7db244a3f96e05e95996ff51877c89b0c1ec1163c76bb5dad77d0f8fba6bb35a0abacb36403d7271dc586d8b1f636110ee7a8d959fd
+ languageName: node
+ linkType: hard
+
"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.6":
version: 7.0.6
resolution: "cross-spawn@npm:7.0.6"
@@ -6146,6 +6315,15 @@ __metadata:
languageName: node
linkType: hard
+"css-in-js-utils@npm:^3.1.0":
+ version: 3.1.0
+ resolution: "css-in-js-utils@npm:3.1.0"
+ dependencies:
+ hyphenate-style-name: ^1.0.3
+ checksum: 066318e918c04a5e5bce46b38fe81052ea6ac051bcc6d3c369a1d59ceb1546cb2b6086901ab5d22be084122ee3732169996a3dfb04d3406eaee205af77aec61b
+ languageName: node
+ linkType: hard
+
"csstype@npm:^3.0.2":
version: 3.1.3
resolution: "csstype@npm:3.1.3"
@@ -7843,6 +8021,15 @@ __metadata:
languageName: node
linkType: hard
+"fb-dotslash@npm:0.5.8":
+ version: 0.5.8
+ resolution: "fb-dotslash@npm:0.5.8"
+ bin:
+ dotslash: bin/dotslash
+ checksum: 5678efe96898294e41c983cb8ea28952539566df5f8bfd2913e8e146425d7d9999d2c458bb4f3e0b07b36b5bcd23cada0868d94509c8b2d4b17de8bf0641775a
+ languageName: node
+ linkType: hard
+
"fb-watchman@npm:^2.0.0":
version: 2.0.2
resolution: "fb-watchman@npm:2.0.2"
@@ -7852,6 +8039,13 @@ __metadata:
languageName: node
linkType: hard
+"fbjs-css-vars@npm:^1.0.0":
+ version: 1.0.2
+ resolution: "fbjs-css-vars@npm:1.0.2"
+ checksum: 72baf6d22c45b75109118b4daecb6c8016d4c83c8c0f23f683f22e9d7c21f32fff6201d288df46eb561e3c7d4bb4489b8ad140b7f56444c453ba407e8bd28511
+ languageName: node
+ linkType: hard
+
"fbjs@npm:^0.8.12":
version: 0.8.18
resolution: "fbjs@npm:0.8.18"
@@ -7867,6 +8061,21 @@ __metadata:
languageName: node
linkType: hard
+"fbjs@npm:^3.0.4":
+ version: 3.0.5
+ resolution: "fbjs@npm:3.0.5"
+ dependencies:
+ cross-fetch: ^3.1.5
+ fbjs-css-vars: ^1.0.0
+ loose-envify: ^1.0.0
+ object-assign: ^4.1.0
+ promise: ^7.1.1
+ setimmediate: ^1.0.5
+ ua-parser-js: ^1.0.35
+ checksum: e609b5b64686bc96495a5c67728ed9b2710b9b3d695c5759c5f5e47c9483d1c323543ac777a86459e3694efc5712c6ce7212e944feb19752867d699568bb0e54
+ languageName: node
+ linkType: hard
+
"fd-slicer@npm:~1.1.0":
version: 1.1.0
resolution: "fd-slicer@npm:1.1.0"
@@ -8439,6 +8648,13 @@ __metadata:
languageName: node
linkType: hard
+"hermes-compiler@npm:0.0.0":
+ version: 0.0.0
+ resolution: "hermes-compiler@npm:0.0.0"
+ checksum: 8b6fc8a64c2fa18c9aa6ddb8831c92253b6a2f10adf7d5d8f361b574f07e91b64f0c44b1370665075c33c17dd71c02fd19422124a3d2aa1717c37006ab12a1f0
+ languageName: node
+ linkType: hard
+
"hermes-estree@npm:0.19.1":
version: 0.19.1
resolution: "hermes-estree@npm:0.19.1"
@@ -8607,7 +8823,7 @@ __metadata:
languageName: node
linkType: hard
-"hyphenate-style-name@npm:^1.0.2":
+"hyphenate-style-name@npm:^1.0.2, hyphenate-style-name@npm:^1.0.3":
version: 1.1.0
resolution: "hyphenate-style-name@npm:1.1.0"
checksum: b9ed74e29181d96bd58a2d0e62fc4a19879db591dba268275829ff0ae595fcdf11faafaeaa63330a45c3004664d7db1f0fc7cdb372af8ee4615ed8260302c207
@@ -8744,6 +8960,15 @@ __metadata:
languageName: node
linkType: hard
+"inline-style-prefixer@npm:^7.0.1":
+ version: 7.0.1
+ resolution: "inline-style-prefixer@npm:7.0.1"
+ dependencies:
+ css-in-js-utils: ^3.1.0
+ checksum: 07a72573dfdac5e08fa18f5ce71d922861716955e230175ac415db227d9ed49443c764356cb407a92f4c85b30ebf39604165260b4dfbf3196b7736d7332c5c06
+ languageName: node
+ linkType: hard
+
"internal-slot@npm:^1.1.0":
version: 1.1.0
resolution: "internal-slot@npm:1.1.0"
@@ -10293,6 +10518,13 @@ __metadata:
languageName: node
linkType: hard
+"memoize-one@npm:^6.0.0":
+ version: 6.0.0
+ resolution: "memoize-one@npm:6.0.0"
+ checksum: f185ea69f7cceae5d1cb596266dcffccf545e8e7b4106ec6aa93b71ab9d16460dd118ac8b12982c55f6d6322fcc1485de139df07eacffaae94888b9b3ad7675f
+ languageName: node
+ linkType: hard
+
"meow@npm:^10.1.3":
version: 10.1.5
resolution: "meow@npm:10.1.5"
@@ -11345,7 +11577,7 @@ __metadata:
languageName: node
linkType: hard
-"node-fetch@npm:^2.2.0, node-fetch@npm:^2.6.0, node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7":
+"node-fetch@npm:^2.2.0, node-fetch@npm:^2.6.0, node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7, node-fetch@npm:^2.7.0":
version: 2.7.0
resolution: "node-fetch@npm:2.7.0"
dependencies:
@@ -11979,6 +12211,13 @@ __metadata:
languageName: node
linkType: hard
+"postcss-value-parser@npm:^4.2.0":
+ version: 4.2.0
+ resolution: "postcss-value-parser@npm:4.2.0"
+ checksum: 819ffab0c9d51cf0acbabf8996dffbfafbafa57afc0e4c98db88b67f2094cb44488758f06e5da95d7036f19556a4a732525e84289a425f4f6fd8e412a9d7442f
+ languageName: node
+ linkType: hard
+
"preact@npm:~8.2.9":
version: 8.2.9
resolution: "preact@npm:8.2.9"
@@ -12260,6 +12499,17 @@ __metadata:
languageName: node
linkType: hard
+"react-dom@npm:19.1.0":
+ version: 19.1.0
+ resolution: "react-dom@npm:19.1.0"
+ dependencies:
+ scheduler: ^0.26.0
+ peerDependencies:
+ react: ^19.1.0
+ checksum: 1d154b6543467095ac269e61ca59db546f34ef76bcdeb90f2dad41d682cd210aae492e70c85010ed5d0a2caea225e9a55139ebc1a615ee85bf197d7f99678cdf
+ languageName: node
+ linkType: hard
+
"react-is@npm:^16.12.0 || ^17.0.0 || ^18.0.0, react-is@npm:^18.0.0, react-is@npm:^18.2.0":
version: 18.3.1
resolution: "react-is@npm:18.3.1"
@@ -12460,10 +12710,30 @@ __metadata:
languageName: node
linkType: hard
+"react-native-web@npm:^0.21.2":
+ version: 0.21.2
+ resolution: "react-native-web@npm:0.21.2"
+ dependencies:
+ "@babel/runtime": ^7.18.6
+ "@react-native/normalize-colors": ^0.74.1
+ fbjs: ^3.0.4
+ inline-style-prefixer: ^7.0.1
+ memoize-one: ^6.0.0
+ nullthrows: ^1.1.1
+ postcss-value-parser: ^4.2.0
+ styleq: ^0.1.3
+ peerDependencies:
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ checksum: 3d8be3ee2bae2790949683d8002973882538a49d5182bdda2a38739d44f0a5918bf082427ad062c98b71d3585ab9664c406685ceafe2432bb99877188dc9782d
+ languageName: node
+ linkType: hard
+
"react-native-webgpu@workspace:.":
version: 0.0.0-use.local
resolution: "react-native-webgpu@workspace:."
dependencies:
+ "@types/react-native-web": ^0.19.2
turbo: ^2.1.0
languageName: unknown
linkType: soft
@@ -12497,6 +12767,7 @@ __metadata:
react: 19.1.0
react-native: 0.81.4
react-native-builder-bob: ^0.23.2
+ react-native-web: ^0.21.2
rimraf: ^5.0.7
seedrandom: ^3.0.5
teapot: ^1.0.0
@@ -12512,6 +12783,57 @@ __metadata:
languageName: unknown
linkType: soft
+"react-native@npm:*":
+ version: 0.82.1
+ resolution: "react-native@npm:0.82.1"
+ dependencies:
+ "@jest/create-cache-key-function": ^29.7.0
+ "@react-native/assets-registry": 0.82.1
+ "@react-native/codegen": 0.82.1
+ "@react-native/community-cli-plugin": 0.82.1
+ "@react-native/gradle-plugin": 0.82.1
+ "@react-native/js-polyfills": 0.82.1
+ "@react-native/normalize-colors": 0.82.1
+ "@react-native/virtualized-lists": 0.82.1
+ abort-controller: ^3.0.0
+ anser: ^1.4.9
+ ansi-regex: ^5.0.0
+ babel-jest: ^29.7.0
+ babel-plugin-syntax-hermes-parser: 0.32.0
+ base64-js: ^1.5.1
+ commander: ^12.0.0
+ flow-enums-runtime: ^0.0.6
+ glob: ^7.1.1
+ hermes-compiler: 0.0.0
+ invariant: ^2.2.4
+ jest-environment-node: ^29.7.0
+ memoize-one: ^5.0.0
+ metro-runtime: ^0.83.1
+ metro-source-map: ^0.83.1
+ nullthrows: ^1.1.1
+ pretty-format: ^29.7.0
+ promise: ^8.3.0
+ react-devtools-core: ^6.1.5
+ react-refresh: ^0.14.0
+ regenerator-runtime: ^0.13.2
+ scheduler: 0.26.0
+ semver: ^7.1.3
+ stacktrace-parser: ^0.1.10
+ whatwg-fetch: ^3.0.0
+ ws: ^6.2.3
+ yargs: ^17.6.2
+ peerDependencies:
+ "@types/react": ^19.1.1
+ react: ^19.1.1
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ bin:
+ react-native: cli.js
+ checksum: 16c5945aae14bd8d7113d2751ad558d91a0bc8cfbffd8ad588722635aac404b512be5ef0f5953ce62b9808ae7ddcbefac909575bcf7da013d06146ab0d48f29a
+ languageName: node
+ linkType: hard
+
"react-native@npm:0.81.4":
version: 0.81.4
resolution: "react-native@npm:0.81.4"
@@ -13059,7 +13381,7 @@ __metadata:
languageName: node
linkType: hard
-"scheduler@npm:0.26.0":
+"scheduler@npm:0.26.0, scheduler@npm:^0.26.0":
version: 0.26.0
resolution: "scheduler@npm:0.26.0"
checksum: c63a9f1c0e5089b537231cff6c11f75455b5c8625ae09535c1d7cd0a1b0c77ceecdd9f1074e5e063da5d8dc11e73e8033dcac3361791088be08a6e60c0283ed9
@@ -13747,6 +14069,13 @@ __metadata:
languageName: node
linkType: hard
+"styleq@npm:^0.1.3":
+ version: 0.1.3
+ resolution: "styleq@npm:0.1.3"
+ checksum: 14a8d23abd914166a9b4bd04ed753bd91363f0e029ee4a94ec2c7dc37d3213fe01fceee22dc655288da3ae89f5dc01cec42d5e2b58478b0dea33bf5bdf509be1
+ languageName: node
+ linkType: hard
+
"sudo-prompt@npm:^9.0.0":
version: 9.2.1
resolution: "sudo-prompt@npm:9.2.1"
@@ -14315,6 +14644,15 @@ __metadata:
languageName: node
linkType: hard
+"ua-parser-js@npm:^1.0.35":
+ version: 1.0.41
+ resolution: "ua-parser-js@npm:1.0.41"
+ bin:
+ ua-parser-js: script/cli.js
+ checksum: a57c258ea3a242ade7601460ddf9a7e990d8d8bffc15df2ca87057a81993ca19f5045432c744d07bf2d9f280665d84aebb08630c5af5bea3922fdbe8f6fe6cb0
+ languageName: node
+ linkType: hard
+
"unbox-primitive@npm:^1.1.0":
version: 1.1.0
resolution: "unbox-primitive@npm:1.1.0"