diff --git a/.eslintignore b/.eslintignore
index 9a0ecdac..63902168 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,3 +1,2 @@
build/
-lib/wgputoy
types/supabase.ts
diff --git a/.github/workflows/cf-pages.yml b/.github/workflows/cf-pages.yml
index 44804da0..5ee70f93 100644
--- a/.github/workflows/cf-pages.yml
+++ b/.github/workflows/cf-pages.yml
@@ -14,7 +14,7 @@ jobs:
pull-requests: write
deployments: write
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: echo Deploying ${{ github.event.pull_request.head.sha }}
- name: Await CF Pages
id: cf-pages
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index ae39396f..81361ae5 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -35,6 +35,7 @@ jobs:
cache: 'yarn'
- run: yarn
- run: yarn build
+ - run: npm install -g vercel
- name: Deploy to Vercel Action
uses: BetaHuhn/deploy-to-vercel-action@v1
with:
diff --git a/.gitmodules b/.gitmodules
index 0cd43930..e69de29b 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +0,0 @@
-[submodule "wgpu-compute-toy"]
- path = wgpu-compute-toy
- url = https://github.com/compute-toys/wgpu-compute-toy.git
diff --git a/components/banner.tsx b/components/banner.tsx
deleted file mode 100644
index 82b97a0a..00000000
--- a/components/banner.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import Alert from '@mui/material/Alert';
-import AlertTitle from '@mui/material/AlertTitle';
-import Typography from '@mui/material/Typography';
-import Logo from 'components/global/logo';
-import { theme } from 'theme/theme';
-
-export default function Banner() {
- return (
-
-
-
- is an experimental editor for{' '}
-
- WebGPU compute shaders
-
- .
-
- At this time, only Chrome (
- v113+ ) is supported,
- as{' '}
-
- WebGPU is not yet fully supported by other browsers
-
- .
-
-
- );
-}
diff --git a/components/editor/explainer.tsx b/components/editor/explainer.tsx
index 9e98f31c..6b44ecb8 100644
--- a/components/editor/explainer.tsx
+++ b/components/editor/explainer.tsx
@@ -48,9 +48,11 @@ const ExplainerBody = () => {
Timing information is in the time struct:
- time.frame: u32
-
time.elapsed: f32
+
+ time.delta: f32
+
+ time.frame: u32
Custom uniforms are in the custom struct:
@@ -109,9 +111,17 @@ const ExplainerBody = () => {
#dispatch_count ENTRYPOINT N for dispatching an entrypoint
multiple times in a row
+
+ #dispatch_once ENTRYPOINT for initialization purposes, ensuring
+ the entrypoint is dispatched only once
+
#storage NAME TYPE for declaring a storage buffer
+
+ SCREEN_WIDTH and SCREEN_HEIGHT are predefined
+ variables for accessing the canvas dimensions
+
Storage
Read-write storage buffers can be declared using the #storage {' '}
@@ -130,31 +140,53 @@ const ExplainerBody = () => {
storage texture, which displays the result in the canvas on this page.
+ {/*
Debugging assertions are supported with an assert helper function:
assert(0, isfinite(col.x))
assert(1, isfinite(col.y))
+ */}
Examples
+
+ Preprocessor #include
+
+
+
+
+ Storage usage
+
+
+
+
+ Texture usage
+
+
+
- Simple single pass shader
+ Texture pass
-
- Preprocessor #include
+
+ Custom uniforms
-
- Terminal overlay
+
+ Preprocessor #workgroup_count
-
- Storage usage
+
+ Preprocessor #dispatch_once
+
+
+
+
+ Preprocessor #dispatch_count
@@ -163,18 +195,28 @@ const ExplainerBody = () => {
-
- Preprocessor #dispatch_count
+
+ Threads execution order
-
- Preprocessor #workgroup_count
+
+ Enable WGSL extension
+
+
+
+
+ Terminal text overlay
+
+
+
+
+ Simple raymarcher
-
- Assert
+
+ Simple rasterizer
diff --git a/components/wgputoy.tsx b/components/wgputoy.tsx
index bfbd9c35..b5df6b50 100644
--- a/components/wgputoy.tsx
+++ b/components/wgputoy.tsx
@@ -7,7 +7,6 @@ import { canvasElAtom, wgpuAvailabilityAtom } from 'lib/atoms/wgputoyatoms';
import dynamic from 'next/dynamic';
import { Suspense, useCallback, useState } from 'react';
import { theme } from 'theme/theme';
-import Logo from './global/logo';
export const WgpuToyWrapper = props => {
const setCanvasEl = useSetAtom(canvasElAtom);
@@ -68,8 +67,13 @@ export const WgpuToyWrapper = props => {
WebGPU support was not detected in your browser.
- For information on how to set up your browser to run WebGPU code, please see
- the instructions linked on the homepage.
+
+ Click here
+ {' '}
+ for further information about supported browsers.
)}
diff --git a/components/wgputoycontroller.tsx b/components/wgputoycontroller.tsx
index 4c269e5b..ba7acd7d 100644
--- a/components/wgputoycontroller.tsx
+++ b/components/wgputoycontroller.tsx
@@ -26,13 +26,8 @@ import {
titleAtom,
widthAtom
} from 'lib/atoms/atoms';
-import {
- canvasElAtom,
- canvasParentElAtom,
- isSafeContext,
- wgputoyAtom,
- wgputoyPreludeAtom
-} from 'lib/atoms/wgputoyatoms';
+import { canvasElAtom, canvasParentElAtom, wgputoyPreludeAtom } from 'lib/atoms/wgputoyatoms';
+import { ComputeEngine } from 'lib/engine';
import { useCallback, useEffect } from 'react';
import { theme } from 'theme/theme';
import { getDimensions } from 'types/canvasdimensions';
@@ -45,6 +40,7 @@ declare global {
}
const needsInitialResetAtom = atom
(false);
+const performingInitialResetAtom = atom(false);
/*
Controller component. Returns null because we expect to be notified
@@ -65,6 +61,9 @@ const WgpuToyController = props => {
const [sliderUpdateSignal, setSliderUpdateSignal] = useTransientAtom(sliderUpdateSignalAtom);
const [manualReload, setManualReload] = useTransientAtom(manualReloadAtom);
const [needsInitialReset, setNeedsInitialReset] = useTransientAtom(needsInitialResetAtom);
+ const [performingInitialReset, setPerformingInitialReset] = useTransientAtom(
+ performingInitialResetAtom
+ );
const [isPlaying, setIsPlaying] = useTransientAtom(isPlayingAtom);
const [codeHot] = useTransientAtom(codeAtom);
const [dbLoaded] = useTransientAtom(dbLoadedAtom);
@@ -76,19 +75,18 @@ const WgpuToyController = props => {
// "hot" access and effect hook access for code
const code = useAtomValue(codeAtom);
- const [parseError, setParseError] = useTransientAtom(parseErrorAtom);
+ const [, setParseError] = useTransientAtom(parseErrorAtom);
const loadedTextures = useAtomValue(loadedTexturesAtom);
const setEntryPoints = useSetAtom(entryPointsAtom);
const setSaveColorTransitionSignal = useSetAtom(saveColorTransitionSignalAtom);
- const wgputoy = useAtomValue(wgputoyAtom);
const canvas = useAtomValue(canvasElAtom);
const [, setPrelude] = useAtom(wgputoyPreludeAtom);
const parentRef = useAtomValue(canvasParentElAtom);
const [width, setWidth] = useTransientAtom(widthAtom);
- const [, setHeight] = useTransientAtom(heightAtom);
+ const [height, setHeight] = useTransientAtom(heightAtom);
const [scale, setScale] = useTransientAtom(scaleAtom);
const [requestFullscreenSignal, setRequestFullscreenSignal] = useAtom(requestFullscreenAtom);
@@ -96,50 +94,30 @@ const WgpuToyController = props => {
const halfResolution = useAtomValue(halfResolutionAtom);
const updateUniforms = useCallback(async () => {
- if (isSafeContext(wgputoy)) {
- const names: string[] = [];
- const values: number[] = [];
- [...sliderRefMap().keys()].map(uuid => {
- names.push(sliderRefMap().get(uuid).getUniform());
- values.push(sliderRefMap().get(uuid).getVal());
- }, this);
- if (names.length > 0) {
- await wgputoy.set_custom_floats(names, Float32Array.from(values));
- }
- setSliderUpdateSignal(false);
+ const names: string[] = [];
+ const values: number[] = [];
+ [...sliderRefMap().keys()].map(uuid => {
+ names.push(sliderRefMap().get(uuid).getUniform());
+ values.push(sliderRefMap().get(uuid).getVal());
+ }, this);
+ if (names.length > 0) {
+ // console.log(`Setting uniforms: ${names} with values: ${values}`);
+ await ComputeEngine.getInstance().setCustomFloats(names, Float32Array.from(values));
}
+ setSliderUpdateSignal(false);
}, []);
- const reloadCallback = useCallback(() => {
- updateUniforms().then(() => {
- if (isSafeContext(wgputoy)) {
- wgputoy.preprocess(codeHot()).then(s => {
- if (s) {
- wgputoy.compile(s);
- setPrelude(wgputoy.prelude());
- wgputoy.render();
- }
- });
- setManualReload(false);
- }
- });
- }, []);
-
- const awaitableReloadCallback = async () => {
- return updateUniforms().then(() => {
- if (isSafeContext(wgputoy)) {
- wgputoy.preprocess(codeHot()).then(s => {
- if (s) {
- wgputoy.compile(s);
- setPrelude(wgputoy.prelude());
- wgputoy.render();
- }
- });
- return true;
- } else {
- return false;
- }
- });
+ const recompile = async () => {
+ await updateUniforms();
+ console.log('Recompiling shader...');
+ const s = await ComputeEngine.getInstance().preprocess(codeHot());
+ if (s) {
+ await ComputeEngine.getInstance().compile(s);
+ setPrelude(ComputeEngine.getInstance().getPrelude());
+ ComputeEngine.getInstance().render();
+ } else {
+ console.error('Recompilation failed');
+ }
};
/*
@@ -147,42 +125,65 @@ const WgpuToyController = props => {
where manualReload gets set before the controller is loaded, which
results in the effect hook for manualReload never getting called.
*/
- const liveReloadCallback = useCallback(() => {
- if (needsInitialReset() && dbLoaded()) {
- awaitableReloadCallback().then(ready => {
- // we don't want to reset in general except on load
- if (ready && parseError().success) {
- resetCallback();
- setNeedsInitialReset(false);
- }
- });
- } else if (dbLoaded() && manualReload()) {
- reloadCallback();
+ useAnimationFrame(async e => {
+ if (sliderUpdateSignal() && !needsInitialReset()) {
+ await updateUniforms();
}
- }, []);
-
- useAnimationFrame(e => {
- if (isSafeContext(wgputoy)) {
- if (sliderUpdateSignal()) {
- updateUniforms().then(() => {
- liveReloadCallback();
- });
- } else {
- liveReloadCallback();
+ if (performingInitialReset()) {
+ // wait for initial reset to complete
+ } else if (needsInitialReset() && dbLoaded()) {
+ console.log('Initialising engine...');
+ setPerformingInitialReset(true);
+ await ComputeEngine.create();
+ const engine = ComputeEngine.getInstance();
+ if (!canvas) {
+ console.error('Canvas not found');
+ return;
}
- if (sliderUpdateSignal() && !isPlaying()) {
- wgputoy.set_time_delta(e.delta);
- wgputoy.render();
- } else if (isPlaying() || manualReload()) {
- let t = timer();
- if (!manualReload()) {
- t += e.delta;
- }
- setTimer(t);
- wgputoy.set_time_elapsed(t);
- wgputoy.set_time_delta(e.delta);
- wgputoy.render();
+ engine.setSurface(canvas);
+ engine.onSuccess(handleSuccess);
+ engine.onError(handleError);
+ setTimer(0);
+ engine.setPassF32(float32Enabled);
+ updateResolution();
+ engine.resize(width(), height(), scale());
+ engine.reset();
+ loadTexture(0, loadedTextures[0].img);
+ loadTexture(1, loadedTextures[1].img);
+ await updateUniforms();
+ console.log('Compiling shader...');
+ const s = await engine.preprocess(codeHot());
+ if (!s) {
+ console.error('Initialisation aborted: shader compilation failed');
+ return;
+ }
+ await engine.compile(s);
+ setPrelude(engine.getPrelude());
+ engine.render();
+ setManualReload(false);
+ setNeedsInitialReset(false);
+ setPerformingInitialReset(false);
+ console.log('Initialisation complete');
+ } else if (dbLoaded() && manualReload()) {
+ console.log('Manual reload triggered');
+ await recompile();
+ setManualReload(false);
+ }
+ if (needsInitialReset()) {
+ return;
+ }
+ if (sliderUpdateSignal() && !isPlaying()) {
+ ComputeEngine.getInstance().setTimeDelta(e.delta);
+ ComputeEngine.getInstance().render();
+ } else if (isPlaying() || manualReload()) {
+ let t = timer();
+ if (!manualReload()) {
+ t += e.delta;
}
+ setTimer(t);
+ ComputeEngine.getInstance().setTimeElapsed(t);
+ ComputeEngine.getInstance().setTimeDelta(e.delta);
+ ComputeEngine.getInstance().render();
}
});
@@ -195,10 +196,11 @@ const WgpuToyController = props => {
}, []);
const resetCallback = useCallback(() => {
- if (isSafeContext(wgputoy)) {
+ if (!needsInitialReset()) {
+ console.log('Resetting engine...');
setTimer(0);
- wgputoy.reset();
- reloadCallback();
+ ComputeEngine.getInstance().reset();
+ recompile();
}
}, []);
@@ -221,34 +223,31 @@ const WgpuToyController = props => {
if (!hotReloadHot()) setSaveColorTransitionSignal(theme.palette.dracula.orange);
}, []);
- if (window) window['wgsl_error_handler'] = handleError;
+ // if (window) window['wgsl_error_handler'] = handleError;
const loadTexture = useCallback((index: number, uri: string) => {
- if (isSafeContext(wgputoy)) {
- fetch(uri)
- .then(response => {
- if (!response.ok) {
- throw new Error('Failed to load image');
- }
- return response.blob();
- })
- .then(b => b.arrayBuffer())
- .then(data => {
- if (uri.match(/\.hdr/i)) {
- wgputoy.load_channel_hdr(index, new Uint8Array(data));
- } else {
- wgputoy.load_channel(index, new Uint8Array(data));
- }
- })
- .catch(error => console.error(error));
- }
+ console.log(`Loading texture ${index} from ${uri}`);
+ fetch(uri)
+ .then(response => {
+ if (!response.ok) {
+ throw new Error('Failed to load image');
+ }
+ return response.blob();
+ })
+ .then(b => b.arrayBuffer())
+ .then(data => {
+ if (uri.match(/\.hdr/i)) {
+ ComputeEngine.getInstance().loadChannelHDR(index, new Uint8Array(data));
+ } else {
+ ComputeEngine.getInstance().loadChannel(index, new Uint8Array(data));
+ }
+ })
+ .catch(error => console.error(error));
}, []);
const requestFullscreen = useCallback(() => {
- if (isSafeContext(wgputoy) && canvas !== false) {
- if (!document.fullscreenElement) {
- canvas.requestFullscreen({ navigationUI: 'hide' });
- }
+ if (canvas && !document.fullscreenElement) {
+ canvas.requestFullscreen({ navigationUI: 'hide' });
}
}, []);
@@ -257,9 +256,9 @@ const WgpuToyController = props => {
useEffect(() => {
const handleKeyDown = e => {
- if (isSafeContext(wgputoy)) {
- if (typeof e.keyCode === 'number') wgputoy.set_keydown(e.keyCode, true);
- }
+ // console.log(`Key down: ${e.keyCode}`);
+ if (typeof e.keyCode === 'number')
+ ComputeEngine.getInstance().setKeydown(e.keyCode, true);
};
if (canvas) {
canvas.addEventListener('keydown', handleKeyDown);
@@ -270,9 +269,9 @@ const WgpuToyController = props => {
useEffect(() => {
const handleKeyUp = e => {
- if (isSafeContext(wgputoy)) {
- if (typeof e.keyCode === 'number') wgputoy.set_keydown(e.keyCode, false);
- }
+ // console.log(`Key up: ${e.keyCode}`);
+ if (typeof e.keyCode === 'number')
+ ComputeEngine.getInstance().setKeydown(e.keyCode, false);
};
if (canvas) {
canvas.addEventListener('keyup', handleKeyUp);
@@ -399,30 +398,27 @@ const WgpuToyController = props => {
useEffect(() => {
if (canvas !== false) {
const handleMouseMove = (e: MouseEvent) => {
- if (isSafeContext(wgputoy)) {
- wgputoy.set_mouse_pos(
- e.offsetX / canvas.clientWidth,
- e.offsetY / canvas.clientHeight
- );
- if (!isPlaying()) {
- wgputoy.render();
- }
+ // console.log(`Mouse move: ${e.offsetX}, ${e.offsetY}`);
+ ComputeEngine.getInstance().setMousePos(
+ e.offsetX / canvas.clientWidth,
+ e.offsetY / canvas.clientHeight
+ );
+ if (!isPlaying()) {
+ ComputeEngine.getInstance().render();
}
};
const handleMouseUp = () => {
- if (isSafeContext(wgputoy)) {
- wgputoy.set_mouse_click(false);
- canvas.onmousemove = null;
- }
+ // console.log('Mouse up');
+ ComputeEngine.getInstance().setMouseClick(false);
+ canvas.onmousemove = null;
};
const handleMouseDown = (e: MouseEvent) => {
- if (isSafeContext(wgputoy)) {
- wgputoy.set_mouse_click(true);
- handleMouseMove(e);
- canvas.onmousemove = handleMouseMove;
- }
+ // console.log('Mouse down');
+ ComputeEngine.getInstance().setMouseClick(true);
+ handleMouseMove(e);
+ canvas.onmousemove = handleMouseMove;
};
canvas.onmousedown = handleMouseDown;
@@ -431,12 +427,6 @@ const WgpuToyController = props => {
}
}, []);
- useEffect(() => {
- if (isSafeContext(wgputoy)) {
- wgputoy.on_success(handleSuccess);
- }
- }, []);
-
useEffect(() => {
if (!isPlaying()) {
setPlay(true);
@@ -462,56 +452,64 @@ const WgpuToyController = props => {
special case where we're paused and a reload is called
*/
if (hotReload || (!isPlaying() && manualReload())) {
- reloadCallback();
+ console.log('Hot reload triggered...');
+ recompile().then(() => setManualReload(false));
}
}, [code, hotReload, manualReload()]);
const updateResolution = () => {
- if (isSafeContext(wgputoy)) {
- let dimensions = { x: 0, y: 0 }; // dimensions in device (physical) pixels
- if (document.fullscreenElement) {
- // calculate actual screen resolution, accounting for both zoom and hidpi
- // https://stackoverflow.com/a/55839671/78204
- dimensions.x =
- Math.round(
- (window.screen.width * window.devicePixelRatio) /
- (window.outerWidth / window.innerWidth) /
- 80
- ) * 80;
- dimensions.y =
- Math.round(
- (window.screen.height * window.devicePixelRatio) /
- (window.outerWidth / window.innerWidth) /
- 60
- ) * 60;
- } else if (props.embed) {
- dimensions = getDimensions(window.innerWidth * window.devicePixelRatio);
- } else {
- const padding = 16;
- dimensions = getDimensions(
- (parentRef.offsetWidth - padding) * window.devicePixelRatio
- );
- }
- const newScale = halfResolution ? 0.5 : 1;
- if (dimensions.x !== width() || newScale !== scale()) {
- setWidth(dimensions.x);
- setHeight(dimensions.y);
- setScale(newScale);
- wgputoy.resize(dimensions.x, dimensions.y, newScale);
- reloadCallback();
- }
- if (canvas) {
- canvas.width = dimensions.x;
- canvas.height = dimensions.y;
- canvas.style.width = `${dimensions.x / window.devicePixelRatio}px`;
- canvas.style.height = `${dimensions.y / window.devicePixelRatio}px`;
- }
+ let dimensions = { x: 0, y: 0 }; // dimensions in device (physical) pixels
+ if (document.fullscreenElement) {
+ // calculate actual screen resolution, accounting for both zoom and hidpi
+ // https://stackoverflow.com/a/55839671/78204
+ dimensions.x =
+ Math.round(
+ (window.screen.width * window.devicePixelRatio) /
+ (window.outerWidth / window.innerWidth) /
+ 80
+ ) * 80;
+ dimensions.y =
+ Math.round(
+ (window.screen.height * window.devicePixelRatio) /
+ (window.outerWidth / window.innerWidth) /
+ 60
+ ) * 60;
+ } else if (props.embed) {
+ dimensions = getDimensions(window.innerWidth * window.devicePixelRatio);
+ } else {
+ const padding = 16;
+ dimensions = getDimensions((parentRef.offsetWidth - padding) * window.devicePixelRatio);
+ }
+ if (canvas) {
+ canvas.width = dimensions.x;
+ canvas.height = dimensions.y;
+ canvas.style.width = `${dimensions.x / window.devicePixelRatio}px`;
+ canvas.style.height = `${dimensions.y / window.devicePixelRatio}px`;
}
+ const newScale = halfResolution ? 0.5 : 1;
+ if (dimensions.x !== width() || newScale !== scale()) {
+ console.log(`Resizing to ${dimensions.x}x${dimensions.y} @ ${newScale}x`);
+ setWidth(dimensions.x);
+ setHeight(dimensions.y);
+ setScale(newScale);
+ return true;
+ }
+ return false;
};
- useResizeObserver(parentRef, updateResolution);
+ useResizeObserver(parentRef, () => {
+ if (!needsInitialReset() && updateResolution()) {
+ ComputeEngine.getInstance().resize(width(), height(), scale());
+ resetCallback();
+ }
+ });
- useEffect(updateResolution, [halfResolution]);
+ useEffect(() => {
+ if (!needsInitialReset() && updateResolution()) {
+ ComputeEngine.getInstance().resize(width(), height(), scale());
+ resetCallback();
+ }
+ }, [halfResolution]);
useEffect(() => {
if (reset) {
@@ -521,11 +519,15 @@ const WgpuToyController = props => {
}, [reset]);
useEffect(() => {
- loadTexture(0, loadedTextures[0].img);
+ if (!needsInitialReset()) {
+ loadTexture(0, loadedTextures[0].img);
+ }
}, [loadedTextures[0]]);
useEffect(() => {
- loadTexture(1, loadedTextures[1].img);
+ if (!needsInitialReset()) {
+ loadTexture(1, loadedTextures[1].img);
+ }
}, [loadedTextures[1]]);
useEffect(() => {
@@ -536,10 +538,12 @@ const WgpuToyController = props => {
}, [requestFullscreenSignal]);
useEffect(() => {
- if (isSafeContext(wgputoy)) {
- wgputoy.set_pass_f32(float32Enabled);
+ if (!needsInitialReset()) {
+ console.log(`Setting passF32 to ${float32Enabled}`);
+ ComputeEngine.getInstance().setPassF32(float32Enabled);
+ ComputeEngine.getInstance().reset();
if (dbLoaded()) {
- awaitableReloadCallback().then(() => {
+ recompile().then(() => {
resetCallback();
});
}
diff --git a/lib/atoms/wgputoyatoms.tsx b/lib/atoms/wgputoyatoms.tsx
index 75772fe6..d22f523c 100644
--- a/lib/atoms/wgputoyatoms.tsx
+++ b/lib/atoms/wgputoyatoms.tsx
@@ -1,16 +1,5 @@
'use client';
import { atom } from 'jotai';
-import { create_renderer, WgpuToyRenderer } from 'lib/wgputoy';
-import { getDimensions } from '../../types/canvasdimensions';
-
-// just to check if the object has already been freed (ptr=0)
-declare module 'lib/wgputoy' {
- interface WgpuToyRenderer {
- __wbg_ptr: number;
- }
-}
-
-const isSSR = typeof window === 'undefined';
// Using 'false' here to satisfy type checker for Jotai's function overloads
export const canvasElAtom = atom(false);
@@ -28,18 +17,4 @@ export const canvasParentElAtom = atom('unknown');
-export const wgputoyAtom = atom>(async get => {
- if (!isSSR && get(canvasElAtom) !== false && get(canvasParentElAtom)) {
- const parentEl = get(canvasParentElAtom);
- const dim = getDimensions(parentEl.offsetWidth * window.devicePixelRatio);
- return create_renderer(dim.x, dim.y, (get(canvasElAtom) as HTMLCanvasElement).id);
- } else {
- return false;
- }
-});
-
export const wgputoyPreludeAtom = atom('');
-
-// type predicate
-export const isSafeContext = (context: WgpuToyRenderer | false): context is WgpuToyRenderer =>
- context !== false && context.__wbg_ptr !== 0;
diff --git a/lib/engine/bind.ts b/lib/engine/bind.ts
new file mode 100644
index 00000000..e459f041
--- /dev/null
+++ b/lib/engine/bind.ts
@@ -0,0 +1,628 @@
+// Constants
+const NUM_KEYCODES = 256;
+const MAX_CUSTOM_PARAMS = 32;
+// export const NUM_ASSERT_COUNTERS = 10;
+// const USER_DATA_BYTES = 4096;
+const OFFSET_ALIGNMENT = 256;
+
+// Core data structures
+class Time {
+ frame: number;
+ elapsed: number;
+ delta: number;
+
+ constructor(frame: number = 0, elapsed: number = 0, delta: number = 0) {
+ this.frame = frame;
+ this.elapsed = elapsed;
+ this.delta = delta;
+ }
+
+ toBuffer(): Uint8Array {
+ const buffer = new Uint8Array(12);
+ const view = new DataView(buffer.buffer);
+
+ view.setUint32(0, this.frame, true); // true for little-endian
+ view.setFloat32(4, this.elapsed, true);
+ view.setFloat32(8, this.delta, true);
+
+ return buffer;
+ }
+}
+
+class Mouse {
+ pos: [number, number];
+ click: number;
+
+ constructor(x: number, y: number, click: number) {
+ this.pos = [x, y];
+ this.click = click;
+ }
+
+ toBuffer(): Uint8Array {
+ const buffer = new Uint8Array(12);
+ const view = new DataView(buffer.buffer);
+
+ view.setInt32(0, this.pos[0], true);
+ view.setInt32(4, this.pos[1], true);
+ view.setInt32(8, this.click, true);
+
+ return buffer;
+ }
+}
+
+class BitArray {
+ private bits: Uint8Array;
+
+ constructor(size: number) {
+ this.bits = new Uint8Array(Math.ceil(size / 8));
+ }
+
+ toBuffer(): Uint8Array {
+ return this.bits;
+ }
+
+ get(index: number): boolean {
+ const byteIndex = Math.floor(index / 8);
+ const bitIndex = index % 8;
+ return (this.bits[byteIndex] & (1 << bitIndex)) !== 0;
+ }
+
+ set(index: number, value: boolean): void {
+ const byteIndex = Math.floor(index / 8);
+ const bitIndex = index % 8;
+ if (value) {
+ this.bits[byteIndex] |= 1 << bitIndex;
+ } else {
+ this.bits[byteIndex] &= ~(1 << bitIndex);
+ }
+ }
+}
+
+interface Binding {
+ getLayoutEntry(binding: number): GPUBindGroupLayoutEntry;
+ binding(): GPUBindingResource;
+ toWGSL(): string;
+}
+
+class BufferBinding implements Binding {
+ host: H;
+ device: GPUBuffer;
+ layout: GPUBufferBindingLayout;
+ bindingSize?: GPUSize64;
+ decl: string;
+
+ constructor(params: {
+ host: H;
+ device: GPUBuffer;
+ layout: GPUBufferBindingLayout;
+ bindingSize?: GPUSize64;
+ decl: string;
+ }) {
+ this.host = params.host;
+ this.device = params.device;
+ this.layout = params.layout;
+ this.bindingSize = params.bindingSize;
+ this.decl = params.decl;
+ }
+
+ getLayoutEntry(binding: number): GPUBindGroupLayoutEntry {
+ return {
+ binding,
+ visibility: GPUShaderStage.COMPUTE,
+ buffer: this.layout
+ };
+ }
+
+ binding(): GPUBufferBinding {
+ return { buffer: this.device, offset: 0, size: this.bindingSize };
+ }
+
+ toWGSL(): string {
+ return this.decl;
+ }
+}
+
+class TextureBinding implements Binding {
+ device: GPUTexture;
+ view: GPUTextureView;
+ layout: GPUTextureBindingLayout;
+ decl: string;
+
+ constructor(params: {
+ device: GPUTexture;
+ view: GPUTextureView;
+ layout: GPUTextureBindingLayout;
+ decl: string;
+ }) {
+ this.device = params.device;
+ this.view = params.view;
+ this.layout = params.layout;
+ this.decl = params.decl;
+ }
+
+ getLayoutEntry(binding: number): GPUBindGroupLayoutEntry {
+ return {
+ binding,
+ visibility: GPUShaderStage.COMPUTE,
+ texture: this.layout
+ };
+ }
+
+ binding(): GPUTextureView {
+ return this.view;
+ }
+
+ toWGSL(): string {
+ return this.decl;
+ }
+
+ texture(): GPUTexture {
+ return this.device;
+ }
+
+ setTexture(texture: GPUTexture): void {
+ this.device = texture;
+ this.view = texture.createView();
+ }
+}
+
+class StorageTextureBinding implements Binding {
+ device: GPUTexture;
+ view: GPUTextureView;
+ layout: GPUStorageTextureBindingLayout;
+ decl: string;
+
+ constructor(params: {
+ device: GPUTexture;
+ view: GPUTextureView;
+ layout: GPUStorageTextureBindingLayout;
+ decl: string;
+ }) {
+ this.device = params.device;
+ this.view = params.view;
+ this.layout = params.layout;
+ this.decl = params.decl;
+ }
+
+ getLayoutEntry(binding: number): GPUBindGroupLayoutEntry {
+ return {
+ binding,
+ visibility: GPUShaderStage.COMPUTE,
+ storageTexture: this.layout
+ };
+ }
+
+ binding(): GPUTextureView {
+ return this.view;
+ }
+
+ toWGSL(): string {
+ return this.decl;
+ }
+
+ texture(): GPUTexture {
+ return this.device;
+ }
+
+ setTexture(texture: GPUTexture): void {
+ this.device = texture;
+ this.view = texture.createView();
+ }
+}
+
+class SamplerBinding implements Binding {
+ layout: GPUSamplerBindingLayout;
+ bind: GPUSampler;
+ decl: string;
+
+ constructor(params: { layout: GPUSamplerBindingLayout; bind: GPUSampler; decl: string }) {
+ this.layout = params.layout;
+ this.bind = params.bind;
+ this.decl = params.decl;
+ }
+
+ getLayoutEntry(binding: number): GPUBindGroupLayoutEntry {
+ return {
+ binding,
+ visibility: GPUShaderStage.COMPUTE,
+ sampler: this.layout
+ };
+ }
+
+ binding(): GPUSampler {
+ return this.bind;
+ }
+
+ toWGSL(): string {
+ return this.decl;
+ }
+}
+
+// Main bindings class
+export class Bindings {
+ time: BufferBinding;
+ mouse: BufferBinding;
+ keys: BufferBinding;
+ custom: BufferBinding<[string[], Float32Array]>;
+ // userData: BufferBinding>;
+
+ storage1: BufferBinding;
+ storage2: BufferBinding;
+ // debugBuffer: BufferBinding;
+ dispatchInfo: BufferBinding;
+
+ texScreen: StorageTextureBinding;
+ texRead: TextureBinding;
+ texWrite: StorageTextureBinding;
+ channels: TextureBinding[];
+
+ nearest: SamplerBinding;
+ bilinear: SamplerBinding;
+ trilinear: SamplerBinding;
+ nearestRepeat: SamplerBinding;
+ bilinearRepeat: SamplerBinding;
+ trilinearRepeat: SamplerBinding;
+
+ constructor(device: GPUDevice, width: number, height: number, passF32: boolean) {
+ const uniformBuffer: GPUBufferBindingLayout = {
+ type: 'uniform'
+ };
+
+ const storageBuffer: GPUBufferBindingLayout = {
+ type: 'storage'
+ };
+
+ const passFormat = passF32 ? 'rgba32float' : 'rgba16float';
+
+ const blank: GPUTextureDescriptor = {
+ size: {
+ width: 1,
+ height: 1,
+ depthOrArrayLayers: 1
+ },
+ format: 'rgba8unorm-srgb',
+ usage: GPUTextureUsage.TEXTURE_BINDING,
+ dimension: '2d',
+ mipLevelCount: 1,
+ sampleCount: 1
+ };
+
+ const channelLayout: GPUTextureBindingLayout = {
+ sampleType: 'float',
+ viewDimension: '2d',
+ multisampled: false
+ };
+
+ const repeat: GPUSamplerDescriptor = {
+ addressModeU: 'repeat',
+ addressModeV: 'repeat',
+ addressModeW: 'repeat'
+ };
+
+ // Create textures
+ const texScreen = device.createTexture({
+ size: {
+ width,
+ height,
+ depthOrArrayLayers: 1
+ },
+ format: 'rgba16float',
+ usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING,
+ dimension: '2d',
+ mipLevelCount: 1,
+ sampleCount: 1
+ });
+
+ const texRead = device.createTexture({
+ size: {
+ width,
+ height,
+ depthOrArrayLayers: 4
+ },
+ format: passF32 ? 'rgba32float' : 'rgba16float',
+ usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING,
+ dimension: '2d',
+ mipLevelCount: 1,
+ sampleCount: 1
+ });
+
+ const texWrite = device.createTexture({
+ size: {
+ width,
+ height,
+ depthOrArrayLayers: 4
+ },
+ format: passF32 ? 'rgba32float' : 'rgba16float',
+ usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.STORAGE_BINDING,
+ dimension: '2d',
+ mipLevelCount: 1,
+ sampleCount: 1
+ });
+
+ const channel0 = device.createTexture(blank);
+ const channel1 = device.createTexture(blank);
+
+ // Initialize time binding
+ this.time = new BufferBinding({
+ host: new Time(),
+ device: device.createBuffer({
+ size: 16, // Aligned to 16 bytes
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
+ }),
+ layout: uniformBuffer,
+ decl: 'var time: Time'
+ });
+
+ // Initialize mouse binding
+ this.mouse = new BufferBinding({
+ host: new Mouse(width / 2, height / 2, 0),
+ device: device.createBuffer({
+ size: 16, // Aligned to 16 bytes
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
+ }),
+ layout: uniformBuffer,
+ decl: 'var mouse: Mouse'
+ });
+
+ // Initialize keyboard binding
+ this.keys = new BufferBinding({
+ host: new BitArray(NUM_KEYCODES),
+ device: device.createBuffer({
+ size: 32, // Aligned to 16 bytes
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
+ }),
+ layout: uniformBuffer,
+ decl: 'var _keyboard: array,2>'
+ });
+
+ // Initialize custom binding
+ this.custom = new BufferBinding<[string[], Float32Array]>({
+ host: [['_dummy'], new Float32Array([0])],
+ device: device.createBuffer({
+ size: MAX_CUSTOM_PARAMS * 4,
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
+ }),
+ layout: uniformBuffer,
+ decl: 'var custom: Custom'
+ });
+
+ // Initialize user data binding
+ // this.userData = new BufferBinding>({
+ // host: new Map([['_dummy', new Uint32Array([0])]]),
+ // device: device.createBuffer({
+ // size: USER_DATA_BYTES,
+ // usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
+ // }),
+ // layout: { ...storageBuffer, type: 'read-only-storage' },
+ // decl: 'var data: Data'
+ // });
+
+ // Initialize storage buffers
+ const storageSize = 128 * 1024 * 1024; // 128MB
+ this.storage1 = new BufferBinding({
+ host: undefined,
+ device: device.createBuffer({
+ size: storageSize,
+ usage: GPUBufferUsage.STORAGE
+ }),
+ layout: storageBuffer,
+ decl: ''
+ });
+
+ this.storage2 = new BufferBinding({
+ host: undefined,
+ device: device.createBuffer({
+ size: storageSize,
+ usage: GPUBufferUsage.STORAGE
+ }),
+ layout: storageBuffer,
+ decl: ''
+ });
+
+ // Initialize debug buffer
+ // this.debugBuffer = new BufferBinding({
+ // host: undefined,
+ // device: device.createBuffer({
+ // size: 16 * NUM_ASSERT_COUNTERS, // Aligned to 16 bytes
+ // usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST
+ // }),
+ // layout: storageBuffer,
+ // decl: 'var _assert_counts: array>'
+ // });
+
+ // Initialize dispatch info buffer
+ this.dispatchInfo = new BufferBinding({
+ host: undefined,
+ device: device.createBuffer({
+ size: 256 * OFFSET_ALIGNMENT,
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
+ }),
+ layout: { ...uniformBuffer, hasDynamicOffset: true, type: 'uniform' },
+ bindingSize: 4,
+ decl: 'var dispatch: DispatchInfo'
+ });
+
+ // Initialize texture bindings
+ this.texScreen = new StorageTextureBinding({
+ device: texScreen,
+ view: texScreen.createView(),
+ layout: {
+ access: 'write-only',
+ format: 'rgba16float',
+ viewDimension: '2d'
+ },
+ decl: 'var screen: texture_storage_2d'
+ });
+
+ this.texRead = new TextureBinding({
+ device: texRead,
+ view: texRead.createView({
+ dimension: '2d-array'
+ }),
+ layout: {
+ sampleType: passF32 ? 'unfilterable-float' : 'float',
+ viewDimension: '2d-array',
+ multisampled: false
+ },
+ decl: 'var pass_in: texture_2d_array'
+ });
+
+ this.texWrite = new StorageTextureBinding({
+ device: texWrite,
+ view: texWrite.createView({
+ dimension: '2d-array'
+ }),
+ layout: {
+ access: 'write-only',
+ format: passF32 ? 'rgba32float' : 'rgba16float',
+ viewDimension: '2d-array'
+ },
+ decl: `var pass_out: texture_storage_2d_array<${passFormat},write>`
+ });
+
+ this.channels = [
+ new TextureBinding({
+ device: channel0,
+ view: channel0.createView(),
+ layout: channelLayout,
+ decl: 'var channel0: texture_2d'
+ }),
+ new TextureBinding({
+ device: channel1,
+ view: channel1.createView(),
+ layout: channelLayout,
+ decl: 'var channel1: texture_2d'
+ })
+ ];
+
+ // Initialize sampler bindings
+ this.nearest = new SamplerBinding({
+ layout: {
+ type: 'non-filtering'
+ },
+ bind: device.createSampler(),
+ decl: 'var nearest: sampler'
+ });
+
+ this.bilinear = new SamplerBinding({
+ layout: {
+ type: 'filtering'
+ },
+ bind: device.createSampler({
+ magFilter: 'linear',
+ minFilter: 'linear'
+ }),
+ decl: 'var bilinear: sampler'
+ });
+
+ this.trilinear = new SamplerBinding({
+ layout: {
+ type: 'filtering'
+ },
+ bind: device.createSampler({
+ magFilter: 'linear',
+ minFilter: 'linear',
+ mipmapFilter: 'linear'
+ }),
+ decl: 'var trilinear: sampler'
+ });
+
+ this.nearestRepeat = new SamplerBinding({
+ layout: {
+ type: 'non-filtering'
+ },
+ bind: device.createSampler(repeat),
+ decl: 'var nearest_repeat: sampler'
+ });
+
+ this.bilinearRepeat = new SamplerBinding({
+ layout: {
+ type: 'filtering'
+ },
+ bind: device.createSampler({
+ ...repeat,
+ magFilter: 'linear',
+ minFilter: 'linear'
+ }),
+ decl: 'var bilinear_repeat: sampler'
+ });
+
+ this.trilinearRepeat = new SamplerBinding({
+ layout: {
+ type: 'filtering'
+ },
+ bind: device.createSampler({
+ ...repeat,
+ magFilter: 'linear',
+ minFilter: 'linear',
+ mipmapFilter: 'linear'
+ }),
+ decl: 'var trilinear_repeat: sampler'
+ });
+ }
+
+ private getAllBindings(): Binding[] {
+ return [
+ this.storage1,
+ this.storage2,
+ this.time,
+ this.mouse,
+ this.keys,
+ this.custom,
+ // this.userData,
+ // this.debugBuffer,
+ this.dispatchInfo,
+ this.texScreen,
+ this.texRead,
+ this.texWrite,
+ ...this.channels,
+ this.nearest,
+ this.bilinear,
+ this.trilinear,
+ this.nearestRepeat,
+ this.bilinearRepeat,
+ this.trilinearRepeat
+ ];
+ }
+
+ createBindGroupLayout(device: GPUDevice): GPUBindGroupLayout {
+ return device.createBindGroupLayout({
+ entries: this.getAllBindings().map((binding, index) => binding.getLayoutEntry(index))
+ });
+ }
+
+ createPipelineLayout(device: GPUDevice, layout: GPUBindGroupLayout): GPUPipelineLayout {
+ return device.createPipelineLayout({
+ bindGroupLayouts: [layout]
+ });
+ }
+
+ createBindGroup(device: GPUDevice, layout: GPUBindGroupLayout): GPUBindGroup {
+ return device.createBindGroup({
+ layout,
+ entries: this.getAllBindings().map((binding, index) => ({
+ binding: index,
+ resource: binding.binding()
+ }))
+ });
+ }
+
+ toWGSL(): string {
+ return this.getAllBindings()
+ .map((binding, index) => {
+ const decl = binding.toWGSL();
+ if (!decl) return '';
+ return `@group(0) @binding(${index}) ${decl};`;
+ })
+ .filter(s => s)
+ .join('\n');
+ }
+
+ stage(queue: GPUQueue): void {
+ queue.writeBuffer(this.custom.device, 0, this.custom.host[1].buffer);
+ // queue.writeBuffer(this.userData.device, 0, this.userData.host.toBuffer());
+ queue.writeBuffer(this.time.device, 0, this.time.host.toBuffer());
+ queue.writeBuffer(this.mouse.device, 0, this.mouse.host.toBuffer());
+ queue.writeBuffer(this.keys.device, 0, this.keys.host.toBuffer());
+ }
+}
diff --git a/lib/engine/blit.ts b/lib/engine/blit.ts
new file mode 100644
index 00000000..4f4ca359
--- /dev/null
+++ b/lib/engine/blit.ts
@@ -0,0 +1,261 @@
+// WGSL shader for blit operations
+const BLIT_SHADER = `
+struct VertexOutput {
+ @builtin(position) position: vec4,
+ @location(0) tex_coords: vec2
+};
+
+@vertex
+fn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {
+ var out: VertexOutput;
+ let x = i32(vertex_index) / 2;
+ let y = i32(vertex_index) & 1;
+ let tc = vec2(
+ f32(x) * 2.0,
+ f32(y) * 2.0
+ );
+ out.position = vec4(
+ tc.x * 2.0 - 1.0,
+ 1.0 - tc.y * 2.0,
+ 0.0, 1.0
+ );
+ out.tex_coords = tc;
+ return out;
+}
+
+@group(0) @binding(0) var r_color: texture_2d;
+@group(0) @binding(1) var r_sampler: sampler;
+
+fn srgb_to_linear(rgb: vec3) -> vec3 {
+ return select(
+ pow((rgb + 0.055) * (1.0 / 1.055), vec3(2.4)),
+ rgb * (1.0/12.92),
+ rgb <= vec3(0.04045));
+}
+
+fn linear_to_srgb(rgb: vec3) -> vec3 {
+ return select(
+ 1.055 * pow(rgb, vec3(1.0 / 2.4)) - 0.055,
+ rgb * 12.92,
+ rgb <= vec3(0.0031308));
+}
+
+@fragment
+fn fs_main(in: VertexOutput) -> @location(0) vec4 {
+ return textureSample(r_color, r_sampler, in.tex_coords);
+}
+
+@fragment
+fn fs_main_linear_to_srgb(in: VertexOutput) -> @location(0) vec4 {
+ let rgba = textureSample(r_color, r_sampler, in.tex_coords);
+ return vec4(linear_to_srgb(rgba.rgb), rgba.a);
+}
+
+@fragment
+fn fs_main_rgbe_to_linear(in: VertexOutput) -> @location(0) vec4 {
+ let rgbe = textureSample(r_color, r_sampler, in.tex_coords);
+ return vec4(rgbe.rgb * exp2(rgbe.a * 255. - 128.), 1.);
+}`;
+
+/**
+ * Represents different color space conversions
+ */
+export enum ColorSpace {
+ Linear = 'linear',
+ Rgbe = 'rgbe'
+}
+
+/**
+ * Handles blitting operations between textures with color space conversion
+ */
+export class Blitter {
+ private renderPipeline: GPURenderPipeline;
+ private renderBindGroup: GPUBindGroup;
+ private destFormat: GPUTextureFormat;
+
+ constructor(
+ device: GPUDevice,
+ src: GPUTextureView,
+ srcSpace: ColorSpace,
+ destFormat: GPUTextureFormat,
+ filter: GPUFilterMode
+ ) {
+ this.destFormat = destFormat;
+
+ // Create shader module
+ const shaderModule = device.createShaderModule({
+ label: 'Blit Shader',
+ code: BLIT_SHADER
+ });
+
+ // Create bind group layout
+ const bindGroupLayout = device.createBindGroupLayout({
+ label: 'Blit Bind Group Layout',
+ entries: [
+ {
+ binding: 0,
+ visibility: GPUShaderStage.FRAGMENT,
+ texture: {
+ sampleType: 'float',
+ viewDimension: '2d',
+ multisampled: false
+ }
+ },
+ {
+ binding: 1,
+ visibility: GPUShaderStage.FRAGMENT,
+ sampler: {
+ type: filter === 'linear' ? 'filtering' : 'non-filtering'
+ }
+ }
+ ]
+ });
+
+ // Create pipeline layout
+ const pipelineLayout = device.createPipelineLayout({
+ label: 'Blit Pipeline Layout',
+ bindGroupLayouts: [bindGroupLayout]
+ });
+
+ // Create sampler
+ const sampler = device.createSampler({
+ minFilter: filter,
+ magFilter: filter
+ });
+
+ // Create bind group
+ this.renderBindGroup = device.createBindGroup({
+ layout: bindGroupLayout,
+ entries: [
+ {
+ binding: 0,
+ resource: src
+ },
+ {
+ binding: 1,
+ resource: sampler
+ }
+ ]
+ });
+
+ // Determine fragment shader entry point based on color space conversion
+ const fragmentEntry = this.getFragmentEntry(srcSpace, destFormat);
+
+ // Create render pipeline
+ this.renderPipeline = device.createRenderPipeline({
+ layout: pipelineLayout,
+ vertex: {
+ module: shaderModule,
+ entryPoint: 'vs_main'
+ },
+ fragment: {
+ module: shaderModule,
+ entryPoint: fragmentEntry,
+ targets: [
+ {
+ format: destFormat
+ }
+ ]
+ },
+ primitive: {
+ topology: 'triangle-list'
+ }
+ });
+ }
+
+ /**
+ * Determine the appropriate fragment shader entry point based on color space conversion
+ */
+ private getFragmentEntry(srcSpace: ColorSpace, destFormat: GPUTextureFormat): string {
+ if (srcSpace === ColorSpace.Linear) {
+ // Handle sRGB conversion cases
+ if (destFormat === 'bgra8unorm' || destFormat === 'rgba8unorm') {
+ return 'fs_main_linear_to_srgb';
+ }
+ if (
+ destFormat === 'bgra8unorm-srgb' ||
+ destFormat === 'rgba8unorm-srgb' ||
+ destFormat === 'rgba16float'
+ ) {
+ return 'fs_main';
+ }
+ }
+ if (srcSpace === ColorSpace.Rgbe && destFormat === 'rgba16float') {
+ return 'fs_main_rgbe_to_linear';
+ }
+ throw new Error(`Unsupported color space conversion: ${srcSpace} to ${destFormat}`);
+ }
+
+ /**
+ * Perform the blit operation
+ */
+ blit(encoder: GPUCommandEncoder, view: GPUTextureView) {
+ const renderPass = encoder.beginRenderPass({
+ colorAttachments: [
+ {
+ view: view,
+ clearValue: { r: 0, g: 1, b: 0, a: 1 },
+ loadOp: 'clear',
+ storeOp: 'store'
+ }
+ ]
+ });
+
+ renderPass.setPipeline(this.renderPipeline);
+ renderPass.setBindGroup(0, this.renderBindGroup);
+ renderPass.draw(3, 1, 0, 0);
+ renderPass.end();
+ }
+
+ /**
+ * Create a new texture with mipmaps
+ */
+ createTexture(
+ device: GPUDevice,
+ queue: GPUQueue,
+ width: number,
+ height: number,
+ mipLevelCount: number
+ ): GPUTexture {
+ const texture = device.createTexture({
+ size: {
+ width,
+ height,
+ depthOrArrayLayers: 1
+ },
+ mipLevelCount,
+ sampleCount: 1,
+ dimension: '2d',
+ format: this.destFormat,
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
+ });
+
+ const encoder = device.createCommandEncoder();
+
+ // Generate mipmap chain
+ const views: GPUTextureView[] = Array.from({ length: mipLevelCount }, (_, i) =>
+ texture.createView({
+ baseMipLevel: i,
+ mipLevelCount: 1
+ })
+ );
+
+ // Blit to first mip level
+ this.blit(encoder, views[0]);
+
+ // Generate remaining mip levels
+ for (let targetMip = 1; targetMip < mipLevelCount; targetMip++) {
+ const prevLevelBlitter = new Blitter(
+ device,
+ views[targetMip - 1],
+ ColorSpace.Linear,
+ this.destFormat,
+ 'linear'
+ );
+ prevLevelBlitter.blit(encoder, views[targetMip]);
+ }
+
+ queue.submit([encoder.finish()]);
+ return texture;
+ }
+}
diff --git a/lib/engine/hdr.ts b/lib/engine/hdr.ts
new file mode 100644
index 00000000..128abd02
--- /dev/null
+++ b/lib/engine/hdr.ts
@@ -0,0 +1,96 @@
+// HDR format loading from https://enkimute.github.io/hdrpng.js/
+
+export function loadHDR(data: Uint8Array): {
+ rgbe: Uint8Array;
+ width: number;
+ height: number;
+} {
+ let header = '';
+ let pos = 0;
+
+ // Read header
+ while (!header.match(/\n\n[^\n]+\n/g)) {
+ header += String.fromCharCode(data[pos++]);
+ }
+
+ // Check format
+ const format = header.match(/FORMAT=(.*)$/m)![1];
+ if (format !== '32-bit_rle_rgbe') {
+ console.warn('unknown format : ' + format);
+ }
+
+ // Parse resolution
+ const rez = header.split(/\n/).reverse()[1].split(' ');
+ const width = parseInt(rez[3]);
+ const height = parseInt(rez[1]);
+
+ // Create image
+ const img = new Uint8Array(width * height * 4);
+ let ipos = 0;
+
+ // Read all scanlines
+ for (let j = 0; j < height; j++) {
+ const rgbe = data.slice(pos, (pos += 4));
+ const scanline: number[] = [];
+
+ if (rgbe[0] !== 2 || rgbe[1] !== 2 || rgbe[2] & 0x80) {
+ let len = width;
+ let rs = 0;
+ pos -= 4;
+
+ while (len > 0) {
+ img.set(data.slice(pos, (pos += 4)), ipos);
+ if (img[ipos] === 1 && img[ipos + 1] === 1 && img[ipos + 2] === 1) {
+ let i = img[ipos + 3] << rs;
+ while (i > 0) {
+ img.set(img.slice(ipos - 4, ipos), ipos);
+ ipos += 4;
+ len--;
+ i--;
+ }
+ rs += 8;
+ } else {
+ len--;
+ ipos += 4;
+ rs = 0;
+ }
+ }
+ } else {
+ if ((rgbe[2] << 8) + rgbe[3] !== width) {
+ console.warn('HDR line mismatch ..');
+ }
+
+ for (let i = 0; i < 4; i++) {
+ let ptr = i * width;
+ const ptr_end = (i + 1) * width;
+ let buf: Uint8Array;
+ let count: number;
+
+ while (ptr < ptr_end) {
+ buf = data.slice(pos, (pos += 2));
+ if (buf[0] > 128) {
+ count = buf[0] - 128;
+ while (count-- > 0) {
+ scanline[ptr++] = buf[1];
+ }
+ } else {
+ count = buf[0] - 1;
+ scanline[ptr++] = buf[1];
+ while (count-- > 0) {
+ scanline[ptr++] = data[pos++];
+ }
+ }
+ }
+ }
+
+ for (let i = 0; i < width; i++) {
+ img[ipos++] = scanline[i];
+ img[ipos++] = scanline[i + width];
+ img[ipos++] = scanline[i + 2 * width];
+ img[ipos++] = scanline[i + 3 * width];
+ }
+ }
+ }
+
+ return { rgbe: img, width, height };
+}
diff --git a/lib/engine/index.ts b/lib/engine/index.ts
new file mode 100644
index 00000000..761fe79e
--- /dev/null
+++ b/lib/engine/index.ts
@@ -0,0 +1,662 @@
+/**
+ * WGPU Compute Toy Library
+ * TypeScript port of the Rust compute-toys project
+ */
+
+import { Mutex } from 'async-mutex';
+import { Bindings } from './bind';
+import { Blitter, ColorSpace } from './blit';
+import { loadHDR } from './hdr';
+import { Preprocessor, SourceMap } from './preprocessor';
+import { countNewlines } from './utils';
+
+// Regular expression for parsing compute shader entry points
+const RE_ENTRY_POINT = /@compute[^@]*?@workgroup_size\((.*?)\)[^@]*?fn\s+(\w+)/g;
+
+/**
+ * Information about a compute pipeline
+ */
+interface ComputePipeline {
+ name: string;
+ workgroupSize: [number, number, number];
+ workgroupCount?: [number, number, number];
+ dispatchOnce: boolean;
+ dispatchCount: number;
+ pipeline: GPUComputePipeline;
+}
+
+/**
+ * Core renderer class for compute toy
+ */
+export class ComputeEngine {
+ private static instance: ComputeEngine | null = null;
+
+ private device: GPUDevice;
+
+ private surface?: GPUCanvasContext;
+ private screenWidth: number;
+ private screenHeight: number;
+
+ private bindings?: Bindings;
+ private computePipelineLayout: GPUPipelineLayout;
+ private lastComputePipelines?: ComputePipeline[];
+ private computePipelines: ComputePipeline[] = [];
+ private computeBindGroup: GPUBindGroup;
+ private computeBindGroupLayout: GPUBindGroupLayout;
+ private onSuccessCb?: (entryPoints: string[]) => void;
+ private onErrorCb?: (summary: string, row: number, col: number) => void;
+ private passF32: boolean = false;
+ private screenBlitter: Blitter;
+ // private querySet?: GPUQuerySet;
+ private lastStats: number = performance.now();
+ // private source: SourceMap;
+
+ private compileMutex = new Mutex();
+
+ // static readonly STATS_PERIOD = 100;
+ // static readonly ASSERTS_SIZE = 40; // NUM_ASSERT_COUNTERS * 4
+
+ private static shaderError = false;
+
+ /**
+ * Create a new renderer instance
+ */
+ private constructor(device: GPUDevice) {
+ this.device = device;
+ }
+
+ /**
+ * Factory method to create a new renderer
+ */
+ public static async create(): Promise {
+ // Initialize WebGPU adapter and device
+ const adapter = await navigator.gpu.requestAdapter({
+ powerPreference: 'high-performance'
+ });
+ if (!adapter) {
+ throw new Error('No appropriate GPUAdapter found');
+ }
+
+ // Log adapter capabilities
+ const features = [...adapter.features] as GPUFeatureName[];
+ console.log('Adapter features:', features);
+ console.log('Adapter limits:', adapter.limits);
+
+ const device = await adapter.requestDevice({
+ label: `compute.toys device created at ${new Date().toLocaleTimeString()}`,
+ requiredFeatures: features
+ });
+
+ if (ComputeEngine.instance) {
+ console.log('Destroying existing engine');
+ ComputeEngine.instance.device.destroy();
+ }
+ ComputeEngine.instance = new ComputeEngine(device);
+ console.log('WebGPU engine created');
+ }
+
+ /**
+ * Get the current renderer instance
+ */
+ public static getInstance(): ComputeEngine {
+ if (!ComputeEngine.instance) {
+ throw new Error('WebGPU engine not initialised');
+ }
+ return ComputeEngine.instance;
+ }
+
+ public setSurface(canvas: HTMLCanvasElement) {
+ this.surface = canvas.getContext('webgpu');
+ if (!this.surface) {
+ throw new Error('WebGPU not supported');
+ }
+ const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
+ this.surface.configure({
+ device: this.device,
+ format: presentationFormat,
+ alphaMode: 'opaque',
+ usage: GPUTextureUsage.RENDER_ATTACHMENT,
+ viewFormats: [presentationFormat]
+ });
+ }
+
+ /**
+ * Get the prelude code that's added to all shaders
+ */
+ public getPrelude(): string {
+ let prelude = '';
+
+ // Add type aliases
+ for (const [alias, type] of [
+ ['int', 'i32'],
+ ['uint', 'u32'],
+ ['float', 'f32']
+ ]) {
+ prelude += `alias ${alias} = ${type};\n`;
+ }
+
+ // Vector type aliases
+ for (const [alias, type] of [
+ ['int', 'i32'],
+ ['uint', 'u32'],
+ ['float', 'f32'],
+ ['bool', 'bool']
+ ]) {
+ for (let n = 2; n < 5; n++) {
+ prelude += `alias ${alias}${n} = vec${n}<${type}>;\n`;
+ }
+ }
+
+ // Matrix type aliases
+ for (let n = 2; n < 5; n++) {
+ for (let m = 2; m < 5; m++) {
+ prelude += `alias float${n}x${m} = mat${n}x${m};\n`;
+ }
+ }
+
+ // Add struct definitions
+ prelude += `
+struct Time { frame: uint, elapsed: float, delta: float }
+struct Mouse { pos: uint2, click: int }
+struct DispatchInfo { id: uint }
+`;
+
+ // Add custom uniforms struct
+ prelude += 'struct Custom {\n';
+ const [customNames] = this.bindings.custom.host;
+ for (const name of customNames) {
+ prelude += ` ${name}: float,\n`;
+ }
+ prelude += '};\n';
+
+ // Add user data struct
+ // prelude += 'struct Data {\n';
+ // for (const [key, value] of this.bindings.userData.host) {
+ // prelude += ` ${key}: array,\n`;
+ // }
+ // prelude += '};\n';
+
+ // Add bindings
+ prelude += this.bindings.toWGSL();
+
+ // Add utility functions
+ prelude += `
+fn keyDown(keycode: uint) -> bool {
+ return ((_keyboard[keycode / 128u][(keycode % 128u) / 32u] >> (keycode % 32u)) & 1u) == 1u;
+}
+
+fn assert(index: int, success: bool) {
+ if (!success) {
+ // atomicAdd(&_assert_counts[index], 1u);
+ }
+}
+
+fn passStore(pass_index: int, coord: int2, value: float4) {
+ textureStore(pass_out, coord, pass_index, value);
+}
+
+fn passLoad(pass_index: int, coord: int2, lod: int) -> float4 {
+ return textureLoad(pass_in, coord, pass_index, lod);
+}
+`;
+
+ // Add pass sampling function
+ prelude += `
+fn passSampleLevelBilinearRepeat(pass_index: int, uv: float2, lod: float) -> float4 {`;
+
+ if (this.passF32) {
+ // Manual bilinear filtering for f32 textures
+ prelude += `
+ let res = float2(textureDimensions(pass_in));
+ let st = uv * res - 0.5;
+ let iuv = floor(st);
+ let fuv = fract(st);
+ let a = textureSampleLevel(pass_in, nearest, fract((iuv + float2(0.5,0.5)) / res), pass_index, lod);
+ let b = textureSampleLevel(pass_in, nearest, fract((iuv + float2(1.5,0.5)) / res), pass_index, lod);
+ let c = textureSampleLevel(pass_in, nearest, fract((iuv + float2(0.5,1.5)) / res), pass_index, lod);
+ let d = textureSampleLevel(pass_in, nearest, fract((iuv + float2(1.5,1.5)) / res), pass_index, lod);
+ return mix(mix(a, b, fuv.x), mix(c, d, fuv.x), fuv.y);`;
+ } else {
+ // Hardware filtering for f16 textures
+ prelude += `
+ return textureSampleLevel(pass_in, bilinear, fract(uv), pass_index, lod);`;
+ }
+ prelude += '\n}';
+
+ return prelude;
+ }
+
+ /**
+ * Preprocess shader source code
+ */
+ async preprocess(shader: string): Promise {
+ const defines = new Map([
+ ['SCREEN_WIDTH', this.screenWidth.toString()],
+ ['SCREEN_HEIGHT', this.screenHeight.toString()]
+ ]);
+
+ return new Preprocessor(defines).preprocess(shader);
+ }
+
+ /**
+ * Compile preprocessed shader code
+ */
+ async compile(source: SourceMap): Promise {
+ const release = await this.compileMutex.acquire();
+ const start = performance.now();
+ const prelude = source.extensions + this.getPrelude();
+ const preludeLines = countNewlines(prelude);
+ const wgsl = prelude + source.source;
+
+ // Parse entry points
+ const entryPoints: Array<[string, [number, number, number]]> = [];
+ const entryPointCode = Preprocessor.stripComments(wgsl);
+ let match;
+
+ while ((match = RE_ENTRY_POINT.exec(entryPointCode)) !== null) {
+ const [, sizeStr, name] = match;
+ const sizes = sizeStr.split(',').map(s => parseInt(s.trim(), 10));
+ entryPoints.push([name, [sizes[0] || 1, sizes[1] || 1, sizes[2] || 1]]);
+ }
+
+ // Notify success callback
+ const entryPointNames = entryPoints.map(([name]) => name);
+ this.onSuccessCb?.(entryPointNames);
+
+ // Create shader module
+ const shaderModule = this.device.createShaderModule({
+ label: 'Compute shader',
+ code: wgsl
+ });
+
+ const compilationInfo = await shaderModule.getCompilationInfo();
+ for (const message of compilationInfo.messages) {
+ let row = message.lineNum;
+ if (row >= preludeLines) {
+ row -= preludeLines;
+ }
+ if (row < source.map.length) {
+ row = source.map[row];
+ }
+ if (message.type === 'error') {
+ this.onErrorCb?.(message.message, row, message.linePos);
+ } else if (message.type === 'warning') {
+ console.warn(message.message);
+ } else {
+ console.log(message.message);
+ }
+ }
+
+ // Create compute pipelines
+ if (this.lastComputePipelines) {
+ this.computePipelines = this.lastComputePipelines;
+ }
+ this.lastComputePipelines = this.computePipelines;
+
+ this.computePipelines = entryPoints.map(([name, workgroupSize]) => ({
+ name,
+ workgroupSize,
+ workgroupCount: source.workgroupCount.get(name),
+ dispatchOnce: source.dispatchOnce.get(name) ?? false,
+ dispatchCount: source.dispatchCount.get(name) ?? 1,
+ pipeline: this.device.createComputePipeline({
+ label: `Pipeline ${name}`,
+ layout: this.computePipelineLayout,
+ compute: {
+ module: shaderModule,
+ entryPoint: name
+ }
+ })
+ }));
+
+ // Update bindings
+ // this.bindings.userData.host = source.userData;
+
+ console.log(`Shader compiled in ${(performance.now() - start).toFixed(2)}ms`);
+ // this.source = source;
+ release();
+ }
+
+ /**
+ * Main render function
+ */
+ async render(): Promise {
+ if (this.compileMutex.isLocked()) {
+ return;
+ }
+ try {
+ const textureView = this.surface.getCurrentTexture().createView();
+
+ const encoder = this.device.createCommandEncoder();
+
+ // Update bindings
+ this.bindings.stage(this.device.queue);
+
+ // Clear debug buffer periodically
+ // if (this.bindings.time.host.frame % WgpuToyRenderer.STATS_PERIOD === 0) {
+ // this.device.queue.writeBuffer(
+ // this.bindings.debugBuffer.device,
+ // 0,
+ // new Uint32Array(40) // NUM_ASSERT_COUNTERS * 4
+ // );
+
+ // if (this.bindings.time.host.frame > 0) {
+ // const mean =
+ // (performance.now() - this.lastStats) / WgpuToyRenderer.STATS_PERIOD;
+ // this.lastStats = performance.now();
+ // console.log(`${(1000 / mean).toFixed(1)} fps (${mean.toFixed(1)} ms)`);
+ // }
+ // }
+
+ // Handle shader errors
+ if (ComputeEngine.shaderError) {
+ ComputeEngine.shaderError = false;
+ if (this.lastComputePipelines) {
+ this.computePipelines = this.lastComputePipelines;
+ }
+ }
+
+ // Dispatch compute passes
+ let dispatchCounter = 0;
+ for (const pipeline of this.computePipelines) {
+ if (pipeline.dispatchOnce) {
+ if (this.bindings.time.host.frame === 0) {
+ console.log(`Dispatching ${pipeline.name} once`);
+ } else {
+ continue;
+ }
+ }
+ for (let i = 0; i < pipeline.dispatchCount; i++) {
+ const pass = encoder.beginComputePass();
+
+ const workgroupCount = pipeline.workgroupCount ?? [
+ Math.ceil(this.screenWidth / pipeline.workgroupSize[0]),
+ Math.ceil(this.screenHeight / pipeline.workgroupSize[1]),
+ 1
+ ];
+
+ // Update dispatch info
+ this.device.queue.writeBuffer(
+ this.bindings.dispatchInfo.device,
+ dispatchCounter * 256,
+ new Uint32Array([i])
+ );
+
+ pass.setPipeline(pipeline.pipeline);
+ pass.setBindGroup(0, this.computeBindGroup, [dispatchCounter * 256]);
+ pass.dispatchWorkgroups(...workgroupCount);
+ pass.end();
+
+ // Copy write texture to read texture
+ encoder.copyTextureToTexture(
+ { texture: this.bindings.texWrite.texture() },
+ { texture: this.bindings.texRead.texture() },
+ {
+ width: this.screenWidth,
+ height: this.screenHeight,
+ depthOrArrayLayers: 4
+ }
+ );
+
+ dispatchCounter++;
+ }
+ }
+
+ // Blit to screen
+ this.screenBlitter.blit(encoder, textureView);
+
+ // Submit command buffer
+ this.device.queue.submit([encoder.finish()]);
+
+ // Update frame counter
+ this.bindings.time.host.frame += 1;
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ /**
+ * Set success callback for shader compilation
+ */
+ onSuccess(callback: (entryPoints: string[]) => void): void {
+ this.onSuccessCb = callback;
+ }
+
+ onError(callback: (summary: string, row: number, col: number) => void): void {
+ this.onErrorCb = callback;
+ }
+
+ /**
+ * Update time information
+ */
+ setTimeElapsed(time: number): void {
+ this.bindings.time.host.elapsed = time;
+ }
+
+ setTimeDelta(delta: number): void {
+ this.bindings.time.host.delta = delta;
+ }
+
+ /**
+ * Update mouse state
+ */
+ setMousePos(x: number, y: number): void {
+ const mouse = this.bindings.mouse.host;
+ if (mouse.click === 1) {
+ mouse.pos = [Math.floor(x * this.screenWidth), Math.floor(y * this.screenHeight)];
+ this.bindings.mouse.host = mouse;
+ }
+ }
+
+ setMouseClick(click: boolean): void {
+ const mouse = this.bindings.mouse.host;
+ mouse.click = click ? 1 : 0;
+ this.bindings.mouse.host = mouse;
+ }
+
+ /**
+ * Update keyboard state
+ */
+ setKeydown(keycode: number, keydown: boolean): void {
+ this.bindings.keys.host.set(keycode, keydown);
+ }
+
+ /**
+ * Set custom float parameters
+ */
+ setCustomFloats(names: string[], values: Float32Array): void {
+ this.bindings.custom.host = [names, new Float32Array(values)];
+ }
+
+ /**
+ * Set pass texture format
+ */
+ setPassF32(passF32: boolean): void {
+ this.passF32 = passF32;
+ // this.reset();
+ }
+
+ /**
+ * Handle window resize
+ */
+ resize(width: number, height: number, scale: number): void {
+ this.screenWidth = Math.floor(width * scale);
+ this.screenHeight = Math.floor(height * scale);
+
+ // this.surface.configure(this.surfaceConfig);
+
+ // this.reset();
+ }
+
+ /**
+ * Reset renderer state
+ */
+ reset(): void {
+ // Create new bindings with current settings
+ const newBindings = new Bindings(
+ this.device,
+ this.screenWidth,
+ this.screenHeight,
+ this.passF32
+ );
+
+ if (this.bindings) {
+ // Copy over dynamic state
+ newBindings.custom = this.bindings.custom;
+ // newBindings.userData = this.bindings.userData;
+ newBindings.channels = this.bindings.channels;
+ }
+
+ // Clean up old bindings
+ // this.bindings.destroy();
+ this.bindings = newBindings;
+
+ // Recreate pipeline and binding group layouts
+ const layout = this.bindings.createBindGroupLayout(this.device);
+ this.computePipelineLayout = this.bindings.createPipelineLayout(this.device, layout);
+ this.computeBindGroup = this.bindings.createBindGroup(this.device, layout);
+ this.computeBindGroupLayout = layout;
+
+ // Recreate screen blitter
+ const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
+ this.screenBlitter = new Blitter(
+ this.device,
+ this.bindings.texScreen.view,
+ ColorSpace.Linear,
+ presentationFormat,
+ 'linear'
+ );
+ }
+
+ /**
+ * Load texture into channel
+ */
+ async loadChannel(index: number, data: Uint8Array): Promise {
+ const start = performance.now();
+
+ // Create ImageBitmap from data
+ const imageBitmap = await createImageBitmap(new Blob([data]), {
+ premultiplyAlpha: 'none',
+ colorSpaceConversion: 'none'
+ });
+
+ // Create texture
+ let texture = this.device.createTexture({
+ size: {
+ width: imageBitmap.width,
+ height: imageBitmap.height,
+ depthOrArrayLayers: 1
+ },
+ format: 'rgba8unorm-srgb',
+ usage:
+ GPUTextureUsage.TEXTURE_BINDING |
+ GPUTextureUsage.COPY_DST |
+ GPUTextureUsage.RENDER_ATTACHMENT
+ });
+
+ // Copy image data to texture
+ this.device.queue.copyExternalImageToTexture(
+ { source: imageBitmap },
+ { texture },
+ {
+ width: imageBitmap.width,
+ height: imageBitmap.height,
+ depthOrArrayLayers: 1
+ }
+ );
+
+ // Generate mipmap chain
+ const blitter = new Blitter(
+ this.device,
+ texture.createView(),
+ ColorSpace.Linear,
+ 'rgba8unorm-srgb',
+ 'linear'
+ );
+ texture = blitter.createTexture(
+ this.device,
+ this.device.queue,
+ imageBitmap.width,
+ imageBitmap.height,
+ 1 + Math.floor(Math.log2(Math.max(imageBitmap.width, imageBitmap.height)))
+ );
+
+ // Update channel texture
+ this.bindings.channels[index].setTexture(texture);
+
+ // Recreate bind group since we changed a texture
+ this.computeBindGroup = this.bindings.createBindGroup(
+ this.device,
+ this.computeBindGroupLayout
+ );
+
+ console.log(`Channel ${index} loaded in ${(performance.now() - start).toFixed(2)}ms`);
+ }
+
+ /**
+ * Load HDR texture into channel
+ */
+ async loadChannelHDR(index: number, data: Uint8Array): Promise {
+ const start = performance.now();
+
+ // Load HDR data
+ const { rgbe, width, height } = loadHDR(data);
+
+ // Create RGBE texture
+ const rgbeTexture = this.device.createTexture({
+ size: {
+ width,
+ height,
+ depthOrArrayLayers: 1
+ },
+ mipLevelCount: 1,
+ sampleCount: 1,
+ dimension: '2d',
+ format: 'rgba8unorm',
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
+ });
+
+ // Copy RGBE data to texture
+ this.device.queue.writeTexture(
+ { texture: rgbeTexture },
+ rgbe,
+ {
+ offset: 0,
+ bytesPerRow: 4 * width,
+ rowsPerImage: height
+ },
+ {
+ width,
+ height,
+ depthOrArrayLayers: 1
+ }
+ );
+
+ // Convert RGBE to float texture and generate mipmap chain
+ const blitter = new Blitter(
+ this.device,
+ rgbeTexture.createView(),
+ ColorSpace.Rgbe,
+ 'rgba16float',
+ 'linear'
+ );
+ const texture = blitter.createTexture(
+ this.device,
+ this.device.queue,
+ width,
+ height,
+ 1 + Math.floor(Math.log2(Math.max(width, height)))
+ );
+
+ // Update channel texture
+ this.bindings.channels[index].setTexture(texture);
+
+ // Recreate bind group since we changed a texture
+ this.computeBindGroup = this.bindings.createBindGroup(
+ this.device,
+ this.computeBindGroupLayout
+ );
+
+ console.log(`Channel ${index} loaded in ${(performance.now() - start).toFixed(2)}ms`);
+ }
+}
diff --git a/lib/engine/preprocessor.ts b/lib/engine/preprocessor.ts
new file mode 100644
index 00000000..9c775ec0
--- /dev/null
+++ b/lib/engine/preprocessor.ts
@@ -0,0 +1,313 @@
+/**
+ * WGSL shader preprocessor implementation
+ */
+import { fetchInclude, parseUint32, WGSLError } from './utils';
+
+// Regular expressions for preprocessing
+const RE_COMMENT = /(\/\/.*|\/\*[\s\S]*?\*\/)/g;
+const RE_WORD = /[a-zA-Z_][a-zA-Z0-9_]*/g;
+
+const STRING_MAX_LEN = 20;
+
+/**
+ * Maps the processed shader source to original line numbers
+ */
+export class SourceMap {
+ extensions: string = '';
+ source: string = '';
+ map: number[] = [0];
+ workgroupCount = new Map();
+ dispatchOnce = new Map();
+ dispatchCount = new Map();
+ // assertMap: number[] = [];
+ // userData = new Map([['_dummy', new Uint32Array([0])]]);
+
+ /**
+ * Add a line to the source map
+ */
+ pushLine(line: string, lineNumber: number) {
+ this.source += line + '\n';
+ this.map.push(lineNumber);
+ }
+}
+
+/**
+ * Handles WGSL preprocessing including includes, defines, etc.
+ */
+export class Preprocessor {
+ private defines: Map;
+ private source: SourceMap;
+ private storageCount: number;
+ // private assertCount: number;
+ private specialStrings: boolean;
+
+ constructor(defines: Map) {
+ this.defines = new Map(defines);
+ this.defines.set('STRING_MAX_LEN', STRING_MAX_LEN.toString());
+ this.source = new SourceMap();
+ this.storageCount = 0;
+ // this.assertCount = 0;
+ this.specialStrings = false;
+ }
+
+ /**
+ * Strip comments from WGSL source
+ */
+ static stripComments(source: string): string {
+ return source.replace(RE_COMMENT, '');
+ }
+
+ /**
+ * Substitute defines in source text
+ */
+ private substDefines(source: string): string {
+ return source.replace(RE_WORD, match => {
+ return this.defines.get(match) ?? match;
+ });
+ }
+
+ /**
+ * Process a single line of shader source
+ */
+ private async processLine(lineOrig: string, lineNum: number): Promise {
+ let line = this.substDefines(lineOrig);
+
+ // Handle enable directives
+ if (line.trimStart().startsWith('enable')) {
+ line = line.replace(RE_COMMENT, '');
+ this.source.extensions += line + '\n';
+ return;
+ }
+
+ // Handle preprocessor directives
+ if (line.trimStart().startsWith('#')) {
+ line = line.replace(RE_COMMENT, '');
+ const tokens = line.trim().split(' ');
+ const directive = tokens[0];
+
+ switch (directive) {
+ case '#include':
+ await this.handleInclude(tokens, lineNum);
+ break;
+
+ case '#workgroup_count':
+ this.handleWorkgroupCount(tokens, lineNum);
+ break;
+
+ case '#dispatch_once':
+ this.handleDispatchOnce(tokens);
+ break;
+
+ case '#dispatch_count':
+ this.handleDispatchCount(tokens, lineNum);
+ break;
+
+ case '#define':
+ this.handleDefine(lineOrig, tokens, lineNum);
+ break;
+
+ case '#storage':
+ this.handleStorage(tokens, lineNum);
+ break;
+
+ // case '#assert':
+ // this.handleAssert(tokens, lineNum);
+ // break;
+
+ // case '#data':
+ // this.handleData(tokens, lineNum);
+ // break;
+
+ default:
+ throw new WGSLError('Unrecognized preprocessor directive', lineNum);
+ }
+ return;
+ }
+
+ // Handle string literals if enabled
+ if (this.specialStrings) {
+ let error: WGSLError | null = null;
+ line = line.replace(/"((?:[^\\"]|\\.)*)"/g, match => {
+ try {
+ const unescaped = JSON.parse(match) as string;
+ const chars = Array.from(unescaped).map(c => c.charCodeAt(0));
+
+ if (chars.length > STRING_MAX_LEN) {
+ error = new WGSLError(
+ `String literals cannot be longer than ${STRING_MAX_LEN} characters`,
+ lineNum
+ );
+ return match;
+ }
+
+ // Pad array to fixed length
+ const len = chars.length;
+ while (chars.length < STRING_MAX_LEN) {
+ chars.push(0);
+ }
+
+ return `String(${len}, array(${chars
+ .map(c => `0x${c.toString(16).padStart(4, '0')}`)
+ .join(', ')}))`;
+ } catch (e) {
+ console.error(e);
+ return match;
+ }
+ });
+
+ if (error) {
+ throw error;
+ }
+ }
+
+ this.source.pushLine(line, lineNum);
+ }
+
+ /**
+ * Handle #include directive
+ */
+ private async handleInclude(tokens: string[], lineNum: number): Promise {
+ if (tokens.length !== 2) {
+ throw new WGSLError('Invalid #include syntax', lineNum);
+ }
+
+ const nameMatcher = tokens[1].match(/"(.*)"/) || tokens[1].match(/<(.*)>/);
+ if (!nameMatcher) {
+ throw new WGSLError('Path must be enclosed in quotes or chevrons', lineNum);
+ }
+
+ const name = nameMatcher[1];
+ if (/<.*>/.test(tokens[1]) && name === 'string') {
+ this.specialStrings = true;
+ }
+
+ const includePath = /<.*>/.test(tokens[1]) ? `std/${name}` : name;
+ const includeContent = await fetchInclude(includePath);
+
+ if (!includeContent) {
+ throw new WGSLError(`Cannot find include ${tokens[1]}`, lineNum);
+ }
+
+ for (const includeLine of includeContent.split('\n')) {
+ await this.processLine(includeLine, lineNum);
+ }
+ }
+
+ /**
+ * Handle #workgroup_count directive
+ */
+ private handleWorkgroupCount(tokens: string[], lineNum: number): void {
+ if (tokens.length !== 5) {
+ throw new WGSLError('Invalid #workgroup_count syntax', lineNum);
+ }
+
+ const [, name, x, y, z] = tokens;
+ this.source.workgroupCount.set(name, [
+ parseUint32(x, lineNum),
+ parseUint32(y, lineNum),
+ parseUint32(z, lineNum)
+ ]);
+ }
+
+ /**
+ * Handle #dispatch_once directive
+ */
+ private handleDispatchOnce(tokens: string[]): void {
+ const [, name] = tokens;
+ this.source.dispatchOnce.set(name, true);
+ }
+
+ /**
+ * Handle #dispatch_count directive
+ */
+ private handleDispatchCount(tokens: string[], lineNum: number): void {
+ if (tokens.length !== 3) {
+ throw new WGSLError('Invalid #dispatch_count syntax', lineNum);
+ }
+
+ const [, name, count] = tokens;
+ this.source.dispatchCount.set(name, parseUint32(count, lineNum));
+ }
+
+ /**
+ * Handle #define directive
+ */
+ private handleDefine(lineOrig: string, tokens: string[], lineNum: number): void {
+ const name = lineOrig.trim().split(' ')[1];
+ if (!name) {
+ throw new WGSLError('Invalid #define syntax', lineNum);
+ }
+
+ const value = tokens.slice(2).join(' ');
+ if (this.defines.has(name)) {
+ throw new WGSLError(`Cannot redefine ${name}`, lineNum);
+ }
+
+ this.defines.set(name, value);
+ }
+
+ /**
+ * Handle #storage directive
+ */
+ private handleStorage(tokens: string[], lineNum: number): void {
+ if (this.storageCount >= 2) {
+ throw new WGSLError('Only two storage buffers are currently supported', lineNum);
+ }
+
+ const [, name, ...types] = tokens;
+ const type = types.join(' ');
+ this.source.pushLine(
+ `@group(0) @binding(${this.storageCount}) var ${name}: ${type};`,
+ lineNum
+ );
+ this.storageCount++;
+ }
+
+ /*
+ private handleAssert(tokens: string[], lineNum: number): void {
+ if (this.assertCount >= NUM_ASSERT_COUNTERS) {
+ throw new WGSLError(
+ `A maximum of ${NUM_ASSERT_COUNTERS} assertions are currently supported`,
+ lineNum
+ );
+ }
+
+ const predicate = tokens.slice(1).join(' ');
+ this.source.pushLine(`assert(${this.assertCount}, ${predicate});`, lineNum);
+ this.source.assertMap.push(lineNum);
+ this.assertCount++;
+ }
+
+ private handleData(tokens: string[], lineNum: number): void {
+ if (tokens.length < 4 || tokens[2] !== 'u32') {
+ throw new WGSLError('Invalid #data syntax', lineNum);
+ }
+
+ const name = tokens[1];
+ const dataStr = tokens.slice(3).join('');
+ const data = new Uint32Array(dataStr.split(',').map(s => parseUint32(s, lineNum)));
+
+ const existing = this.source.userData.get(name);
+ if (existing) {
+ // Append to existing data
+ const combined = new Uint32Array(existing.length + data.length);
+ combined.set(existing);
+ combined.set(data, existing.length);
+ this.source.userData.set(name, combined);
+ } else {
+ this.source.userData.set(name, data);
+ }
+ }
+ */
+
+ /**
+ * Process complete shader source
+ */
+ async preprocess(shader: string): Promise {
+ const lines = shader.split('\n');
+ for (let i = 0; i < lines.length; i++) {
+ await this.processLine(lines[i], i + 1);
+ }
+ return this.source;
+ }
+}
diff --git a/lib/engine/utils.ts b/lib/engine/utils.ts
new file mode 100644
index 00000000..b581c7be
--- /dev/null
+++ b/lib/engine/utils.ts
@@ -0,0 +1,84 @@
+/**
+ * Utility functions and types for the compute toy library
+ */
+
+/**
+ * Custom error for WGSL shader compilation and runtime errors
+ */
+export class WGSLError extends Error {
+ line: number;
+ column: number;
+
+ constructor(message: string, line: number = 0, column: number = 0) {
+ super(message);
+ this.name = 'WGSLError';
+ this.line = line;
+ this.column = column;
+ }
+
+ /**
+ * Format error message with line and column information
+ */
+ toString(): string {
+ return `${this.name} at line ${this.line}, column ${this.column}: ${this.message}`;
+ }
+}
+
+/**
+ * Parse a string into a uint32, supporting both decimal and hex formats
+ */
+export function parseUint32(value: string, line: number): number {
+ try {
+ const trimmed = value.trim().replace(/u$/, ''); // Remove trailing 'u' if present
+
+ if (trimmed.startsWith('0x')) {
+ return parseInt(trimmed.slice(2), 16);
+ } else {
+ return parseInt(trimmed, 10);
+ }
+ } catch (e) {
+ console.error(e);
+ throw new WGSLError(`Cannot parse '${value}' as u32`, line);
+ }
+}
+
+// Cache for fetched includes
+const includeCache = new Map>();
+
+/**
+ * Fetch and cache shader include files
+ */
+export async function fetchInclude(name: string): Promise {
+ const cached = includeCache.get(name);
+ if (cached) {
+ return cached;
+ }
+
+ const fetchPromise = (async () => {
+ try {
+ const url = `https://compute-toys.github.io/include/${name}.wgsl`;
+ const response = await fetch(url);
+
+ if (!response.ok) {
+ console.error(`Failed to fetch include ${name}: ${response.statusText}`);
+ return null;
+ }
+
+ return await response.text();
+ } catch (error) {
+ console.error(`Error fetching include ${name}:`, error);
+ return null;
+ }
+ })();
+
+ // Store in cache even if it fails - we don't want to retry failed fetches
+ includeCache.set(name, fetchPromise);
+ return fetchPromise;
+}
+
+/**
+ * Helper to ensure shader error line numbers are correct
+ */
+export function countNewlines(text: string): number {
+ return (text.match(/\n/g) || []).length;
+}
diff --git a/package.json b/package.json
index 5e5d763a..66041c7f 100644
--- a/package.json
+++ b/package.json
@@ -5,14 +5,12 @@
},
"packageManager": "yarn@1.22.22",
"scripts": {
- "predev": "wasm-pack build --dev wgpu-compute-toy --out-dir ../lib/wgputoy",
"dev": "next dev",
- "prebuild": "wasm-pack build --release wgpu-compute-toy --out-dir ../lib/wgputoy",
"build": "next build",
"start": "next start",
"lint": "next lint",
"fix": "next lint --fix",
- "pages:setup": "curl https://sh.rustup.rs -sSf | sh -s -- -y && . $HOME/.cargo/env && curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh && npx @cloudflare/next-on-pages",
+ "pages:setup": "npx @cloudflare/next-on-pages",
"pages:build": "npx @cloudflare/next-on-pages",
"pages:preview": "wrangler pages dev"
},
@@ -25,44 +23,45 @@
"@fortawesome/react-fontawesome": "^0.2.2",
"@giscus/react": "^3.0.0",
"@monaco-editor/react": "^4.4.5",
- "@mui/icons-material": "^5.16.7",
- "@mui/lab": "^5.0.0-alpha.173",
+ "@mui/icons-material": "^5.16.13",
+ "@mui/lab": "^5.0.0-alpha.175",
"@mui/material": "^5.16.7",
"@mui/utils": "^6.1.7",
"@react-hook/resize-observer": "^2.0.2",
"@supabase/gotrue-js": "^1.22.13",
"@supabase/supabase-js": "^1.35.2",
+ "async-mutex": "^0.5.0",
"firacode": "^6.2.0",
"jotai": "^2.10.4",
"jotai-game": "^0.2.0",
"monaco-editor": "^0.52.2",
- "next": "^15.1.0",
+ "next": "^15.1.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-draggable": "^4.4.5",
"sharp": "^0.33.5",
"use-animation-frame": "^0.2.1",
- "uuid": "^10.0.0"
+ "uuid": "^11.0.3"
},
"devDependencies": {
"@cloudflare/next-on-pages": "^1.13.7",
- "@next/bundle-analyzer": "^15.1.0",
- "@types/node": "^22.10.2",
+ "@next/bundle-analyzer": "^15.1.3",
+ "@types/node": "^22.10.4",
"@types/react": "^18.3.12",
- "@webgpu/types": "^0.1.51",
+ "@webgpu/types": "^0.1.52",
"copy-webpack-plugin": "^12.0.2",
"depcheck": "^1.4.7",
"eslint": "^8.57.1",
- "eslint-config-next": "^15.1.0",
+ "eslint-config-next": "^15.1.3",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.31.0",
- "eslint-plugin-n": "^17.15.0",
+ "eslint-plugin-n": "^17.15.1",
"eslint-plugin-prettier": "^5.2.1",
"prettier": "^3.4.2",
"prettier-plugin-organize-imports": "^4.1.0",
"supabase": "^1.219.2",
"typescript": "^5.7.2",
- "vercel": "^39.2.2",
- "wrangler": "^3.96.0"
+ "vercel": "^39.2.4",
+ "wrangler": "^3.99.0"
}
}
diff --git a/pages/list/[page].tsx b/pages/list/[page].tsx
index 90a9d302..669b0853 100644
--- a/pages/list/[page].tsx
+++ b/pages/list/[page].tsx
@@ -4,7 +4,6 @@ import ImageListItem, { imageListItemClasses } from '@mui/material/ImageListItem
import ImageListItemBar from '@mui/material/ImageListItemBar';
import Pagination from '@mui/material/Pagination';
import PaginationItem from '@mui/material/PaginationItem';
-import Banner from 'components/banner';
import Avatar from 'components/global/avatar';
import { supabase, SUPABASE_SHADERTHUMB_BUCKET_NAME } from 'lib/db/supabaseclient';
import { getFullyQualifiedSupabaseBucketURL } from 'lib/util/urlutils';
@@ -180,7 +179,6 @@ export default function ShaderList(props) {
width: '100%'
}}
>
-
-
-
-
-
-
+
+
+
+
+
+
+
+
);
}
diff --git a/wgpu-compute-toy b/wgpu-compute-toy
deleted file mode 160000
index 64eda6e1..00000000
--- a/wgpu-compute-toy
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 64eda6e19e02c3f798ec9ae204c407c001bb974d
diff --git a/yarn.lock b/yarn.lock
index 419d8e6d..9aaee6ea 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -144,30 +144,30 @@
pcre-to-regexp "^1.1.0"
semver "^7.5.2"
-"@cloudflare/workerd-darwin-64@1.20241205.0":
- version "1.20241205.0"
- resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20241205.0.tgz#44fac2be0c2088150ee08a8b063345d80b5063d1"
- integrity sha512-TArEZkSZkHJyEwnlWWkSpCI99cF6lJ14OVeEoI9Um/+cD9CKZLM9vCmsLeKglKheJ0KcdCnkA+DbeD15t3VaWg==
-
-"@cloudflare/workerd-darwin-arm64@1.20241205.0":
- version "1.20241205.0"
- resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20241205.0.tgz#388323c215d341fee84da3f1dfa07a877609a88c"
- integrity sha512-u5eqKa9QRdA8MugfgCoD+ADDjY6EpKbv3hSYJETmmUh17l7WXjWBzv4pUvOKIX67C0UzMUy4jZYwC53MymhX3w==
-
-"@cloudflare/workerd-linux-64@1.20241205.0":
- version "1.20241205.0"
- resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20241205.0.tgz#5d716a4cba8252746e91db1d4c6f71bd40689a89"
- integrity sha512-OYA7S5zpumMamWEW+IhhBU6YojIEocyE5X/YFPiTOCrDE3dsfr9t6oqNE7hxGm1VAAu+Irtl+a/5LwmBOU681w==
-
-"@cloudflare/workerd-linux-arm64@1.20241205.0":
- version "1.20241205.0"
- resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20241205.0.tgz#0242a5a13d7b8c9020680c90de85ac72e209beef"
- integrity sha512-qAzecONjFJGIAVJZKExQ5dlbic0f3d4A+GdKa+H6SoUJtPaWiE3K6WuePo4JOT7W3/Zfh25McmX+MmpMUUcM5Q==
-
-"@cloudflare/workerd-windows-64@1.20241205.0":
- version "1.20241205.0"
- resolved "https://registry.yarnpkg.com/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20241205.0.tgz#a2a15eef4d154076527ccb9049d216b4c80717fe"
- integrity sha512-BEab+HiUgCdl6GXAT7EI2yaRtDPiRJlB94XLvRvXi1ZcmQqsrq6awGo6apctFo4WUL29V7c09LxmN4HQ3X2Tvg==
+"@cloudflare/workerd-darwin-64@1.20241218.0":
+ version "1.20241218.0"
+ resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20241218.0.tgz#cdcdeb2d0c60c6c5de538370570544b32213c5fe"
+ integrity sha512-8rveQoxtUvlmORKqTWgjv2ycM8uqWox0u9evn3zd2iWKdou5sncFwH517ZRLI3rq9P31ZLmCQBZ0gloFsTeY6w==
+
+"@cloudflare/workerd-darwin-arm64@1.20241218.0":
+ version "1.20241218.0"
+ resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20241218.0.tgz#25ae222fe89bdc40e8d1b276866fa1ec517d27a5"
+ integrity sha512-be59Ad9nmM9lCkhHqmTs/uZ3JVZt8NJ9Z0PY+B0xnc5z6WwmV2lj0RVLtq7xJhQsQJA189zt5rXqDP6J+2mu7Q==
+
+"@cloudflare/workerd-linux-64@1.20241218.0":
+ version "1.20241218.0"
+ resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20241218.0.tgz#87302ea1e1b7a04b8486958f659f75ab8a142084"
+ integrity sha512-MzpSBcfZXRxrYWxQ4pVDYDrUbkQuM62ssl4ZtHH8J35OAeGsWFAYji6MkS2SpVwVcvacPwJXIF4JSzp4xKImKw==
+
+"@cloudflare/workerd-linux-arm64@1.20241218.0":
+ version "1.20241218.0"
+ resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20241218.0.tgz#560f20883e1afb684199aab03eaeb45804c03dde"
+ integrity sha512-RIuJjPxpNqvwIs52vQsXeRMttvhIjgg9NLjjFa3jK8Ijnj8c3ZDru9Wqi48lJP07yDFIRr4uDMMqh/y29YQi2A==
+
+"@cloudflare/workerd-windows-64@1.20241218.0":
+ version "1.20241218.0"
+ resolved "https://registry.yarnpkg.com/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20241218.0.tgz#f62b6cdb7a970375721c22ef70a9c238c4bb5f9b"
+ integrity sha512-tO1VjlvK3F6Yb2d1jgEy/QBYl//9Pyv3K0j+lq8Eu7qdfm0IgKwSRgDWLept84/qmNsQfausZ4JdNGxTf9xsxQ==
"@cspotcode/source-map-support@0.8.1", "@cspotcode/source-map-support@^0.8.0":
version "0.8.1"
@@ -232,7 +232,7 @@
source-map "^0.5.7"
stylis "4.2.0"
-"@emotion/cache@^11.11.0", "@emotion/cache@^11.14.0":
+"@emotion/cache@^11.13.5", "@emotion/cache@^11.14.0":
version "11.14.0"
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.14.0.tgz#ee44b26986eeb93c8be82bb92f1f7a9b21b2ed76"
integrity sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==
@@ -794,15 +794,15 @@
dependencies:
"@monaco-editor/loader" "^1.4.0"
-"@mui/base@5.0.0-beta.40":
- version "5.0.0-beta.40"
- resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.40.tgz#1f8a782f1fbf3f84a961e954c8176b187de3dae2"
- integrity sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==
+"@mui/base@5.0.0-beta.40-0":
+ version "5.0.0-beta.40-0"
+ resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.40-0.tgz#4e211724d3feefa3dd1546952502b87491137e89"
+ integrity sha512-hG3atoDUxlvEy+0mqdMpWd04wca8HKr2IHjW/fAjlkCHQolSLazhZM46vnHjOf15M4ESu25mV/3PgjczyjVM4w==
dependencies:
"@babel/runtime" "^7.23.9"
"@floating-ui/react-dom" "^2.0.8"
- "@mui/types" "^7.2.14"
- "@mui/utils" "^5.15.14"
+ "@mui/types" "^7.2.15"
+ "@mui/utils" "^5.16.12"
"@popperjs/core" "^2.11.8"
clsx "^2.1.0"
prop-types "^15.8.1"
@@ -812,23 +812,23 @@
resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.7.tgz#182a325a520f7ebd75de051fceabfc0314cfd004"
integrity sha512-RtsCt4Geed2/v74sbihWzzRs+HsIQCfclHeORh5Ynu2fS4icIKozcSubwuG7vtzq2uW3fOR1zITSP84TNt2GoQ==
-"@mui/icons-material@^5.16.7":
- version "5.16.7"
- resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.16.7.tgz#e27f901af792065efc9f3d75d74a66af7529a10a"
- integrity sha512-UrGwDJCXEszbDI7yV047BYU5A28eGJ79keTCP4cc74WyncuVrnurlmIRxaHL8YK+LI1Kzq+/JM52IAkNnv4u+Q==
+"@mui/icons-material@^5.16.13":
+ version "5.16.13"
+ resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.16.13.tgz#c89043915826a60dfbbe776ae6192480a8aa7844"
+ integrity sha512-aWyOgGDEqj37m3K4F6qUfn7JrEccwiDynJtGQMFbxp94EqyGwO13TKcZ4O8aHdwW3tG63hpbION8KyUoBWI4JQ==
dependencies:
"@babel/runtime" "^7.23.9"
-"@mui/lab@^5.0.0-alpha.173":
- version "5.0.0-alpha.173"
- resolved "https://registry.yarnpkg.com/@mui/lab/-/lab-5.0.0-alpha.173.tgz#a0f9696d93a765b48d69a7da5aaca0affa510ae8"
- integrity sha512-Gt5zopIWwxDgGy/MXcp6GueD84xFFugFai4hYiXY0zowJpTVnIrTQCQXV004Q7rejJ7aaCntX9hpPJqCrioshA==
+"@mui/lab@^5.0.0-alpha.175":
+ version "5.0.0-alpha.175"
+ resolved "https://registry.yarnpkg.com/@mui/lab/-/lab-5.0.0-alpha.175.tgz#eb3bc607b96d164455c4beafe8d7f718bb1971fe"
+ integrity sha512-AvM0Nvnnj7vHc9+pkkQkoE1i+dEbr6gsMdnSfy7X4w3Ljgcj1yrjZhIt3jGTCLzyKVLa6uve5eLluOcGkvMqUA==
dependencies:
"@babel/runtime" "^7.23.9"
- "@mui/base" "5.0.0-beta.40"
- "@mui/system" "^5.16.5"
+ "@mui/base" "5.0.0-beta.40-0"
+ "@mui/system" "^5.16.12"
"@mui/types" "^7.2.15"
- "@mui/utils" "^5.16.5"
+ "@mui/utils" "^5.16.12"
clsx "^2.1.0"
prop-types "^15.8.1"
@@ -850,55 +850,55 @@
react-is "^18.3.1"
react-transition-group "^4.4.5"
-"@mui/private-theming@^5.16.6":
- version "5.16.6"
- resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.16.6.tgz#547671e7ae3f86b68d1289a0b90af04dfcc1c8c9"
- integrity sha512-rAk+Rh8Clg7Cd7shZhyt2HGTTE5wYKNSJ5sspf28Fqm/PZ69Er9o6KX25g03/FG2dfpg5GCwZh/xOojiTfm3hw==
+"@mui/private-theming@^5.16.13":
+ version "5.16.13"
+ resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.16.13.tgz#0ea25eb5272c2d16f555803bf804b31867d8eed9"
+ integrity sha512-+s0FklvDvO7j0yBZn19DIIT3rLfub2fWvXGtMX49rG/xHfDFcP7fbWbZKHZMMP/2/IoTRDrZCbY1iP0xZlmuJA==
dependencies:
"@babel/runtime" "^7.23.9"
- "@mui/utils" "^5.16.6"
+ "@mui/utils" "^5.16.13"
prop-types "^15.8.1"
-"@mui/styled-engine@^5.16.6":
- version "5.16.6"
- resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.16.6.tgz#60110c106dd482dfdb7e2aa94fd6490a0a3f8852"
- integrity sha512-zaThmS67ZmtHSWToTiHslbI8jwrmITcN93LQaR2lKArbvS7Z3iLkwRoiikNWutx9MBs8Q6okKvbZq1RQYB3v7g==
+"@mui/styled-engine@^5.16.13":
+ version "5.16.13"
+ resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.16.13.tgz#b87205cd36b7a6b104f1151e8745bf7a19346987"
+ integrity sha512-2XNHEG8/o1ucSLhTA9J+HIIXjzlnEc0OV7kneeUQ5JukErPYT2zc6KYBDLjlKWrzQyvnQzbiffjjspgHUColZg==
dependencies:
"@babel/runtime" "^7.23.9"
- "@emotion/cache" "^11.11.0"
+ "@emotion/cache" "^11.13.5"
csstype "^3.1.3"
prop-types "^15.8.1"
-"@mui/system@^5.16.5", "@mui/system@^5.16.7":
- version "5.16.7"
- resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.16.7.tgz#4583ca5bf3b38942e02c15a1e622ba869ac51393"
- integrity sha512-Jncvs/r/d/itkxh7O7opOunTqbbSSzMTHzZkNLM+FjAOg+cYAZHrPDlYe1ZGKUYORwwb2XexlWnpZp0kZ4AHuA==
+"@mui/system@^5.16.12", "@mui/system@^5.16.7":
+ version "5.16.13"
+ resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.16.13.tgz#2ebbda08e962e378ecec9cf5a3e50ac9b098472d"
+ integrity sha512-JnO3VH3yNoAmgyr44/2jiS1tcNwshwAqAaG5fTEEjHQbkuZT/mvPYj2GC1cON0zEQ5V03xrCNl/D+gU9AXibpw==
dependencies:
"@babel/runtime" "^7.23.9"
- "@mui/private-theming" "^5.16.6"
- "@mui/styled-engine" "^5.16.6"
+ "@mui/private-theming" "^5.16.13"
+ "@mui/styled-engine" "^5.16.13"
"@mui/types" "^7.2.15"
- "@mui/utils" "^5.16.6"
+ "@mui/utils" "^5.16.13"
clsx "^2.1.0"
csstype "^3.1.3"
prop-types "^15.8.1"
-"@mui/types@^7.2.14", "@mui/types@^7.2.15", "@mui/types@^7.2.19":
+"@mui/types@^7.2.15", "@mui/types@^7.2.19":
version "7.2.19"
resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.19.tgz#c941954dd24393fdce5f07830d44440cf4ab6c80"
integrity sha512-6XpZEM/Q3epK9RN8ENoXuygnqUQxE+siN/6rGRi2iwJPgBUR25mphYQ9ZI87plGh58YoZ5pp40bFvKYOCDJ3tA==
-"@mui/utils@^5.15.14", "@mui/utils@^5.16.5", "@mui/utils@^5.16.6":
- version "5.16.6"
- resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.16.6.tgz#905875bbc58d3dcc24531c3314a6807aba22a711"
- integrity sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==
+"@mui/utils@^5.16.12", "@mui/utils@^5.16.13", "@mui/utils@^5.16.6":
+ version "5.16.13"
+ resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.16.13.tgz#6276eafd15e2c6778cdb6aeb07017c996d3fda52"
+ integrity sha512-35kLiShnDPByk57Mz4PP66fQUodCFiOD92HfpW6dK9lc7kjhZsKHRKeYPgWuwEHeXwYsCSFtBCW4RZh/8WT+TQ==
dependencies:
"@babel/runtime" "^7.23.9"
"@mui/types" "^7.2.15"
"@types/prop-types" "^15.7.12"
clsx "^2.1.1"
prop-types "^15.8.1"
- react-is "^18.3.1"
+ react-is "^19.0.0"
"@mui/utils@^6.1.7":
version "6.1.7"
@@ -912,64 +912,64 @@
prop-types "^15.8.1"
react-is "^18.3.1"
-"@next/bundle-analyzer@^15.1.0":
- version "15.1.0"
- resolved "https://registry.yarnpkg.com/@next/bundle-analyzer/-/bundle-analyzer-15.1.0.tgz#444549cac0d1fb41172dd4b2f4a65e81ebe5af4b"
- integrity sha512-uEyuNZZgAbSWgiUbtihTA8y6QgEzc4b8Fpslmc4SXAjj67Ax5mlcv1HLlez+5dIGwO+vJ9PgCoI8ngWtBh/g1Q==
+"@next/bundle-analyzer@^15.1.3":
+ version "15.1.3"
+ resolved "https://registry.yarnpkg.com/@next/bundle-analyzer/-/bundle-analyzer-15.1.3.tgz#901a6efa3fcf7676062d0dcff180f87a20ea1d49"
+ integrity sha512-dh5i2KBONWVhQzJnL10sv9+ImsKgGtOHHeA1dWp/H3MXphWBt1uGjXCwPCcitwimvNncHBmxaOyTm2FwfOLRSA==
dependencies:
webpack-bundle-analyzer "4.10.1"
-"@next/env@15.1.0":
- version "15.1.0"
- resolved "https://registry.yarnpkg.com/@next/env/-/env-15.1.0.tgz#35b00a5f60ff10dc275182928c325d25c29379ae"
- integrity sha512-UcCO481cROsqJuszPPXJnb7GGuLq617ve4xuAyyNG4VSSocJNtMU5Fsx+Lp6mlN8c7W58aZLc5y6D/2xNmaK+w==
+"@next/env@15.1.3":
+ version "15.1.3"
+ resolved "https://registry.yarnpkg.com/@next/env/-/env-15.1.3.tgz#bc747e041cd105170d4cae07cc802e20b4a0c153"
+ integrity sha512-Q1tXwQCGWyA3ehMph3VO+E6xFPHDKdHFYosadt0F78EObYxPio0S09H9UGYznDe6Wc8eLKLG89GqcFJJDiK5xw==
-"@next/eslint-plugin-next@15.1.0":
- version "15.1.0"
- resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-15.1.0.tgz#421b47ad0772e11b2d67416054675cd32f95b8b7"
- integrity sha512-+jPT0h+nelBT6HC9ZCHGc7DgGVy04cv4shYdAe6tKlEbjQUtwU3LzQhzbDHQyY2m6g39m6B0kOFVuLGBrxxbGg==
+"@next/eslint-plugin-next@15.1.3":
+ version "15.1.3"
+ resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-15.1.3.tgz#32777736af151577df52d83f25c0c22bc9f3cb5e"
+ integrity sha512-oeP1vnc5Cq9UoOb8SYHAEPbCXMzOgG70l+Zfd+Ie00R25FOm+CCVNrcIubJvB1tvBgakXE37MmqSycksXVPRqg==
dependencies:
fast-glob "3.3.1"
-"@next/swc-darwin-arm64@15.1.0":
- version "15.1.0"
- resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.1.0.tgz#30cb89220e719244c9fa7391641e515a078ade46"
- integrity sha512-ZU8d7xxpX14uIaFC3nsr4L++5ZS/AkWDm1PzPO6gD9xWhFkOj2hzSbSIxoncsnlJXB1CbLOfGVN4Zk9tg83PUw==
-
-"@next/swc-darwin-x64@15.1.0":
- version "15.1.0"
- resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.1.0.tgz#c24c4f5d1016dd161da32049305b0ddddfc80951"
- integrity sha512-DQ3RiUoW2XC9FcSM4ffpfndq1EsLV0fj0/UY33i7eklW5akPUCo6OX2qkcLXZ3jyPdo4sf2flwAED3AAq3Om2Q==
-
-"@next/swc-linux-arm64-gnu@15.1.0":
- version "15.1.0"
- resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.1.0.tgz#08ed540ecdac74426a624cc7d736dc709244b004"
- integrity sha512-M+vhTovRS2F//LMx9KtxbkWk627l5Q7AqXWWWrfIzNIaUFiz2/NkOFkxCFyNyGACi5YbA8aekzCLtbDyfF/v5Q==
-
-"@next/swc-linux-arm64-musl@15.1.0":
- version "15.1.0"
- resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.1.0.tgz#dfddbd40087d018266aa92515ec5b3e251efa6dd"
- integrity sha512-Qn6vOuwaTCx3pNwygpSGtdIu0TfS1KiaYLYXLH5zq1scoTXdwYfdZtwvJTpB1WrLgiQE2Ne2kt8MZok3HlFqmg==
-
-"@next/swc-linux-x64-gnu@15.1.0":
- version "15.1.0"
- resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.1.0.tgz#a7b5373a1b28c0acecbc826a3790139fc0d899e5"
- integrity sha512-yeNh9ofMqzOZ5yTOk+2rwncBzucc6a1lyqtg8xZv0rH5znyjxHOWsoUtSq4cUTeeBIiXXX51QOOe+VoCjdXJRw==
-
-"@next/swc-linux-x64-musl@15.1.0":
- version "15.1.0"
- resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.1.0.tgz#b82a29903ee2f12d8b64163ddf208ac519869550"
- integrity sha512-t9IfNkHQs/uKgPoyEtU912MG6a1j7Had37cSUyLTKx9MnUpjj+ZDKw9OyqTI9OwIIv0wmkr1pkZy+3T5pxhJPg==
-
-"@next/swc-win32-arm64-msvc@15.1.0":
- version "15.1.0"
- resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.1.0.tgz#98deae6cb1fccfb6a600e9faa6aa714402a9ab9a"
- integrity sha512-WEAoHyG14t5sTavZa1c6BnOIEukll9iqFRTavqRVPfYmfegOAd5MaZfXgOGG6kGo1RduyGdTHD4+YZQSdsNZXg==
-
-"@next/swc-win32-x64-msvc@15.1.0":
- version "15.1.0"
- resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.1.0.tgz#4b04a6a667c41fecdc63db57dd71ca7e84d0946b"
- integrity sha512-J1YdKuJv9xcixzXR24Dv+4SaDKc2jj31IVUEMdO5xJivMTXuE6MAdIi4qPjSymHuFG8O5wbfWKnhJUcHHpj5CA==
+"@next/swc-darwin-arm64@15.1.3":
+ version "15.1.3"
+ resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.1.3.tgz#cac0dc4a1086a33767cc5fb8d11c97391216ca7c"
+ integrity sha512-aZtmIh8jU89DZahXQt1La0f2EMPt/i7W+rG1sLtYJERsP7GRnNFghsciFpQcKHcGh4dUiyTB5C1X3Dde/Gw8gg==
+
+"@next/swc-darwin-x64@15.1.3":
+ version "15.1.3"
+ resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.1.3.tgz#7ebfe9abd7db5abbd28e699d234517f63259ff53"
+ integrity sha512-aw8901rjkVBK5mbq5oV32IqkJg+CQa6aULNlN8zyCWSsePzEG3kpDkAFkkTOh3eJ0p95KbkLyWBzslQKamXsLA==
+
+"@next/swc-linux-arm64-gnu@15.1.3":
+ version "15.1.3"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.1.3.tgz#a6773ea8df2838e8aea1d638176f40849af1ed06"
+ integrity sha512-YbdaYjyHa4fPK4GR4k2XgXV0p8vbU1SZh7vv6El4bl9N+ZSiMfbmqCuCuNU1Z4ebJMumafaz6UCC2zaJCsdzjw==
+
+"@next/swc-linux-arm64-musl@15.1.3":
+ version "15.1.3"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.1.3.tgz#309f60d4218511d152876e7f6cc624b7e6a44f6c"
+ integrity sha512-qgH/aRj2xcr4BouwKG3XdqNu33SDadqbkqB6KaZZkozar857upxKakbRllpqZgWl/NDeSCBYPmUAZPBHZpbA0w==
+
+"@next/swc-linux-x64-gnu@15.1.3":
+ version "15.1.3"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.1.3.tgz#c9ac936eee265da738cde4758d95af4562bbd38b"
+ integrity sha512-uzafnTFwZCPN499fNVnS2xFME8WLC9y7PLRs/yqz5lz1X/ySoxfaK2Hbz74zYUdEg+iDZPd8KlsWaw9HKkLEVw==
+
+"@next/swc-linux-x64-musl@15.1.3":
+ version "15.1.3"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.1.3.tgz#2fe0c262b379f3c5b39cab2fa0ba3d9108c8e440"
+ integrity sha512-el6GUFi4SiDYnMTTlJJFMU+GHvw0UIFnffP1qhurrN1qJV3BqaSRUjkDUgVV44T6zpw1Lc6u+yn0puDKHs+Sbw==
+
+"@next/swc-win32-arm64-msvc@15.1.3":
+ version "15.1.3"
+ resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.1.3.tgz#67252289babfa38712497af646744ffc758aa3dc"
+ integrity sha512-6RxKjvnvVMM89giYGI1qye9ODsBQpHSHVo8vqA8xGhmRPZHDQUE4jcDbhBwK0GnFMqBnu+XMg3nYukNkmLOLWw==
+
+"@next/swc-win32-x64-msvc@15.1.3":
+ version "15.1.3"
+ resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.1.3.tgz#99f8e4cc2b6c469155e75c4e8fdeb5d08254b27c"
+ integrity sha512-VId/f5blObG7IodwC5Grf+aYP0O8Saz1/aeU3YcWqNdIUAmFQY3VEPKPaIzfv32F/clvanOb2K2BR5DtDs6XyQ==
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
@@ -1174,10 +1174,10 @@
dependencies:
"@types/node" "*"
-"@types/node@*", "@types/node@^22.10.2":
- version "22.10.2"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.2.tgz#a485426e6d1fdafc7b0d4c7b24e2c78182ddabb9"
- integrity sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==
+"@types/node@*", "@types/node@^22.10.4":
+ version "22.10.4"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.4.tgz#da36bebcc4b124f3d62bfde1cd1dafd7763949c1"
+ integrity sha512-99l6wv4HEzBQhvaU/UGoeBoCK61SCROQaCCGyQSgX2tEQ3rKkNZ2S7CEWnS/4s1LV+8ODdK21UeyR1fHP2mXug==
dependencies:
undici-types "~6.20.0"
@@ -1307,24 +1307,24 @@
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
-"@vercel/build-utils@8.8.0":
- version "8.8.0"
- resolved "https://registry.yarnpkg.com/@vercel/build-utils/-/build-utils-8.8.0.tgz#e1b8f55b0addc3604e98fd3e0c3a070d874c7dcc"
- integrity sha512-4jkeJ/Xr0epojgfiyQufB8vC0ubE9SCfA9I2BGcOIKcf65C25juSvuYwaLixnjZXHUTO4Y9W7fdgiLUk55MgbA==
+"@vercel/build-utils@9.0.0":
+ version "9.0.0"
+ resolved "https://registry.yarnpkg.com/@vercel/build-utils/-/build-utils-9.0.0.tgz#49d953762b0e1b20c001f0e2ee6cdc4a0cefd78c"
+ integrity sha512-uOUC25R+KdwCpfwgHZlL7GTp1RD2AFkmS9lfEEGeNv3OUrBo7XPX2EnI1jsoWs/VFjJ9TGZLx177qBH8IGCoMw==
"@vercel/error-utils@2.0.3":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@vercel/error-utils/-/error-utils-2.0.3.tgz#72575bf3a3ff3e67f474364e24e753d113c5b79a"
integrity sha512-CqC01WZxbLUxoiVdh9B/poPbNpY9U+tO1N9oWHwTl5YAZxcqXmmWJ8KNMFItJCUUWdY3J3xv8LvAuQv2KZ5YdQ==
-"@vercel/fun@1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@vercel/fun/-/fun-1.1.0.tgz#5bac83585a736b6bfe4616017fe5f0a46562c3ba"
- integrity sha512-SpuPAo+MlAYMtcMcC0plx7Tv4Mp7SQhJJj1iIENlOnABL24kxHpL09XLQMGzZIzIW7upR8c3edwgfpRtp+dhVw==
+"@vercel/fun@1.1.2":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@vercel/fun/-/fun-1.1.2.tgz#437cb320d161deac37621e7c118e26a27247d8e8"
+ integrity sha512-n13RO1BUy8u6+kzDQ2++BRj4Y5EAiQPt+aV+Tb2HNTmToNr4Mu3dE1kFlaTVTxQzAT3hvIRlVEU/OMvF8LCFJw==
dependencies:
"@tootallnate/once" "2.0.0"
async-listen "1.2.0"
- debug "4.1.1"
+ debug "4.3.4"
execa "3.2.0"
fs-extra "8.1.0"
generic-pool "3.4.2"
@@ -1333,7 +1333,7 @@
node-fetch "2.6.7"
path-match "1.2.4"
promisepipe "3.0.0"
- semver "7.3.5"
+ semver "7.5.4"
stat-mode "0.3.0"
stream-to-promise "2.2.0"
tar "4.4.18"
@@ -1350,13 +1350,13 @@
dependencies:
web-vitals "0.2.4"
-"@vercel/gatsby-plugin-vercel-builder@2.0.61":
- version "2.0.61"
- resolved "https://registry.yarnpkg.com/@vercel/gatsby-plugin-vercel-builder/-/gatsby-plugin-vercel-builder-2.0.61.tgz#5c5c0c47e92c4010469fb3079a937986f3fe26c5"
- integrity sha512-OchbRapri4fGCefvjOG9m0yP2JICvCwrrQ+jS3lumcvr6NimmdDu3yxnzFYlSZ06D8cqD3hXx7a+hJKY5zdk3Q==
+"@vercel/gatsby-plugin-vercel-builder@2.0.62":
+ version "2.0.62"
+ resolved "https://registry.yarnpkg.com/@vercel/gatsby-plugin-vercel-builder/-/gatsby-plugin-vercel-builder-2.0.62.tgz#5de6a8af311f1385e1d0c762e275d715d90066d5"
+ integrity sha512-tXL8fY1JTzH/u90/FSdY7tih8RLWbtdX7vcQI2VZY001ae1kTfLHXNcO+eq/hPM20o/SpbuzYtNo9wHNoyP06g==
dependencies:
"@sinclair/typebox" "0.25.24"
- "@vercel/build-utils" "8.8.0"
+ "@vercel/build-utils" "9.0.0"
"@vercel/routing-utils" "5.0.0"
esbuild "0.14.47"
etag "1.8.1"
@@ -1367,18 +1367,18 @@
resolved "https://registry.yarnpkg.com/@vercel/go/-/go-3.2.1.tgz#f98b3312237163cfe9b079493caeb26fb28d64b9"
integrity sha512-ezjmuUvLigH9V4egEaX0SZ+phILx8lb+Zkp1iTqKI+yl/ibPAtVo5o+dLSRAXU9U01LBmaLu3O8Oxd/JpWYCOw==
-"@vercel/hydrogen@1.0.9":
- version "1.0.9"
- resolved "https://registry.yarnpkg.com/@vercel/hydrogen/-/hydrogen-1.0.9.tgz#fc1cbe7ef8032feb48276fc4de745c65d698f746"
- integrity sha512-IPAVaALuGAzt2apvTtBs5tB+8zZRzn/yG3AGp8dFyCsw/v5YOuk0Q5s8Z3fayLvJbFpjrKtqRNDZzVJBBU3MrQ==
+"@vercel/hydrogen@1.0.10":
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/@vercel/hydrogen/-/hydrogen-1.0.10.tgz#f5abcea1ba23adbd48a86a52c13f2a387e1b4be0"
+ integrity sha512-CMuF1TaERm3M65vCR2qoN49KdoMnZ3tWemBb2lcULrq5ZZegkSN18v0iOzO7eM+lIYH6lN9giyrmBH08XWsrKg==
dependencies:
"@vercel/static-config" "3.0.0"
ts-morph "12.0.0"
-"@vercel/next@4.4.0":
- version "4.4.0"
- resolved "https://registry.yarnpkg.com/@vercel/next/-/next-4.4.0.tgz#31b3a30427e3c609a1ce012a2a05d43b7fe4d5ae"
- integrity sha512-oqeAY+f6qMeS3/zEUHeJqa47Sa4ULB33a1DuXcRgvACwewGFUApp2XYm+pgci3EU/19ij8c2EHIASp7B6Y+XVg==
+"@vercel/next@4.4.1":
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/@vercel/next/-/next-4.4.1.tgz#452b265f1a0fb40b297696da40fb973d72b179ee"
+ integrity sha512-b7szq2venuPQsZhezV+LiiFQVMBrFoGSuikJSHjhc1jYSee33AB6dEryvZIcQcRYjjUy9h5jjfFV8EBplwinig==
dependencies:
"@vercel/nft" "0.27.3"
@@ -1400,16 +1400,16 @@
node-gyp-build "^4.2.2"
resolve-from "^5.0.0"
-"@vercel/node@5.0.0":
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/@vercel/node/-/node-5.0.0.tgz#c69c683c6519c9c00f40e46a28f9f835eca3f74c"
- integrity sha512-l917aGyDkaOhqfDrYSqy9sjd+Pv6K8mCsVyxzGv4kwmbhERpi8rS6aBmfIf4fDevEPYiOasftPHASbdnyHOe/g==
+"@vercel/node@5.0.1":
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/@vercel/node/-/node-5.0.1.tgz#40558fe09ae5a93bff4e7cab1a6f261bc16370af"
+ integrity sha512-Udl2TGDdAMH2Od54ARJWyKwFFHy9z1GaS8B2T3UKH1V8gz32SAOe47x9PzP+hWKNOj5xjrdwPAtm3lE7bU6TcQ==
dependencies:
"@edge-runtime/node-utils" "2.3.0"
"@edge-runtime/primitives" "4.1.0"
"@edge-runtime/vm" "3.2.0"
"@types/node" "16.18.11"
- "@vercel/build-utils" "8.8.0"
+ "@vercel/build-utils" "9.0.0"
"@vercel/error-utils" "2.0.3"
"@vercel/nft" "0.27.3"
"@vercel/static-config" "3.0.0"
@@ -1426,15 +1426,15 @@
typescript "4.9.5"
undici "5.28.4"
-"@vercel/python@4.5.1":
- version "4.5.1"
- resolved "https://registry.yarnpkg.com/@vercel/python/-/python-4.5.1.tgz#4a3a36fc345c874908349f68398c7aa64adfea84"
- integrity sha512-nZX1oezs5E+Un5Pw21P7cEXV9WBohRSq8gDAqipu7KHFfdAQ7ubfBclRmDTGaHOiYvdLsJPiE599vsUfKKob/w==
+"@vercel/python@4.6.0":
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/@vercel/python/-/python-4.6.0.tgz#35b53793f8e2f3d3ef32a04bdc5cf5721f553079"
+ integrity sha512-43bpxcas3d3ep+F5zdziva6gW1LBc71YGQiG/XmajGlDv4cCNO312Vf8BE5SYafb5ft6rwl69+WOoFwGkI26qA==
-"@vercel/redwood@2.1.10":
- version "2.1.10"
- resolved "https://registry.yarnpkg.com/@vercel/redwood/-/redwood-2.1.10.tgz#34bb0c9b3df758bb05cbfea4b53057f5610bf1a9"
- integrity sha512-JscXZGNuZY1IHq5CbGBHt7BvHEHh35ZIgorJ5RAEjvuqaox/EE4bA0oyI8y/5aWoZfMvJifS+UATKwI8HrP97w==
+"@vercel/redwood@2.1.11":
+ version "2.1.11"
+ resolved "https://registry.yarnpkg.com/@vercel/redwood/-/redwood-2.1.11.tgz#8d015a6aa32afae784fad840398500d5681921d4"
+ integrity sha512-fQyI/4Hvb8y0QfqwR0qz46Mhs0GL4JXSL78/jjotTLwMscrIJH8lCAqVYq7GGHtqdWcPpo3vUgEgbCwkmFZYmg==
dependencies:
"@vercel/nft" "0.27.3"
"@vercel/routing-utils" "5.0.0"
@@ -1442,10 +1442,10 @@
semver "6.3.1"
ts-morph "12.0.0"
-"@vercel/remix-builder@4.0.0":
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/@vercel/remix-builder/-/remix-builder-4.0.0.tgz#5c14cfdfd4d0b2b956ee5a42004f1a7195280cc2"
- integrity sha512-82O08BggyQgRR86jbkx6pTWaTVhlMA9vU0nvql8af76r3k0iBrkoe6tJiEnwut2YaePUNWr2Kr+mdy6OrJHmVw==
+"@vercel/remix-builder@5.0.1":
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/@vercel/remix-builder/-/remix-builder-5.0.1.tgz#9ec1392dd5eda008814817e0b0abe2878d56407b"
+ integrity sha512-fHJ5aa+jtA8DyVneJyOq4dB0WXBbJuqEqrCcKqzaDk9O+g7PPBbuG2XGwe1j3HR6WfTMGODoCXfOv6zp4b0ltg==
dependencies:
"@vercel/error-utils" "2.0.3"
"@vercel/nft" "0.27.3"
@@ -1466,13 +1466,13 @@
resolved "https://registry.yarnpkg.com/@vercel/ruby/-/ruby-2.1.0.tgz#431400276bcb131ef0b9f6cad2e4242557b921a4"
integrity sha512-UZYwlSEEfVnfzTmgkD+kxex9/gkZGt7unOWNyWFN7V/ZnZSsGBUgv6hXLnwejdRi3EztgRQEBd1kUKlXdIeC0Q==
-"@vercel/static-build@2.5.39":
- version "2.5.39"
- resolved "https://registry.yarnpkg.com/@vercel/static-build/-/static-build-2.5.39.tgz#21b69f30d06d59d480dc211c32751c856ffc453c"
- integrity sha512-gnurv1NWzyE5STFF0BlgWXzIMODv9HeVezRwiEyhndjeugwkzp3xyoRXXR178LZ87lZ7gcgOp6HWEpNmaHDSyw==
+"@vercel/static-build@2.5.40":
+ version "2.5.40"
+ resolved "https://registry.yarnpkg.com/@vercel/static-build/-/static-build-2.5.40.tgz#cf4ba7f7fe4c9bf72160ce882df350d41a06845b"
+ integrity sha512-jaL2dQ/ED3wE2hK8AwvwyKLBbxrPKAxoz/lEaxzcj4t9tz/CfjJxuaDE2MwJvdigYFrIHwQqffu4zQTC+oMLsA==
dependencies:
"@vercel/gatsby-plugin-vercel-analytics" "1.0.11"
- "@vercel/gatsby-plugin-vercel-builder" "2.0.61"
+ "@vercel/gatsby-plugin-vercel-builder" "2.0.62"
"@vercel/static-config" "3.0.0"
ts-morph "12.0.0"
@@ -1543,10 +1543,10 @@
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.3.8.tgz#f044942142e1d3a395f24132e6203a784838542d"
integrity sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==
-"@webgpu/types@^0.1.51":
- version "0.1.51"
- resolved "https://registry.yarnpkg.com/@webgpu/types/-/types-0.1.51.tgz#a4c5a12d0554032bb3cdfad632123e736b9de943"
- integrity sha512-ktR3u64NPjwIViNCck+z9QeyN0iPkQCUOQ07ZCV1RzlkfP+olLTeEZ95O1QHS+v4w9vJeY9xj/uJuSphsHy5rQ==
+"@webgpu/types@^0.1.52":
+ version "0.1.52"
+ resolved "https://registry.yarnpkg.com/@webgpu/types/-/types-0.1.52.tgz#239995418d86de927269aca54cbadfbee04eb03a"
+ integrity sha512-eI883Nlag2hGIkhXxAnq8s4APpqXWuPL3Gbn2ghiU12UjLvfCbVqHK4XfXl3eLRTatqcMmeK7jws7IwWsGfbzw==
abbrev@1:
version "1.1.1"
@@ -1879,6 +1879,13 @@ async-listen@3.0.1:
resolved "https://registry.yarnpkg.com/async-listen/-/async-listen-3.0.1.tgz#cbe4edeace2b93ebf5cf8092899ee139457978b7"
integrity sha512-cWMaNwUJnf37C/S5TfCkk/15MwbPRwVYALA2jtjkbHjCmAPiDXyNJy2q3p1KAZzDLHAWyarUWSujUoHR4pEgrA==
+async-mutex@^0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.5.0.tgz#353c69a0b9e75250971a64ac203b0ebfddd75482"
+ integrity sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==
+ dependencies:
+ tslib "^2.4.0"
+
async-sema@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/async-sema/-/async-sema-3.1.1.tgz#e527c08758a0f8f6f9f15f799a173ff3c40ea808"
@@ -2348,12 +2355,12 @@ debug@4, debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
dependencies:
ms "^2.1.3"
-debug@4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
- integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
+debug@4.3.4:
+ version "4.3.4"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+ integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
- ms "^2.1.1"
+ ms "2.1.2"
debug@^2.2.0:
version "2.6.9"
@@ -3110,12 +3117,12 @@ eslint-compat-utils@^0.5.1:
dependencies:
semver "^7.5.4"
-eslint-config-next@^15.1.0:
- version "15.1.0"
- resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-15.1.0.tgz#25a9a076b059905fd0cf3f6f832771724dfcbbdf"
- integrity sha512-gADO+nKVseGso3DtOrYX9H7TxB/MuX7AUYhMlvQMqLYvUWu4HrOQuU7cC1HW74tHIqkAvXdwgAz3TCbczzSEXw==
+eslint-config-next@^15.1.3:
+ version "15.1.3"
+ resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-15.1.3.tgz#7656b47591745bcdbd60d396282924d89f82eea6"
+ integrity sha512-wGYlNuWnh4ujuKtZvH+7B2Z2vy9nONZE6ztd+DKF7hAsIabkrxmD4TzYHzASHENo42lmz2tnT2B+zN2sOHvpJg==
dependencies:
- "@next/eslint-plugin-next" "15.1.0"
+ "@next/eslint-plugin-next" "15.1.3"
"@rushstack/eslint-patch" "^1.10.3"
"@typescript-eslint/eslint-plugin" "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0"
"@typescript-eslint/parser" "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0"
@@ -3215,10 +3222,10 @@ eslint-plugin-jsx-a11y@^6.10.0:
safe-regex-test "^1.0.3"
string.prototype.includes "^2.0.1"
-eslint-plugin-n@^17.15.0:
- version "17.15.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-17.15.0.tgz#c6ab639a7d7761085cff05313753413d898b087e"
- integrity sha512-xF3zJkOfLlFOm5TvmqmsnA9/fO+/z2pYs0dkuKXKN/ymS6UB1yEcaoIkqxLKQ9Dw/WmLX/Tdh6/5ZS5azVixFQ==
+eslint-plugin-n@^17.15.1:
+ version "17.15.1"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-17.15.1.tgz#2129bbc7b11466c3bfec57876a15aadfad3a83f2"
+ integrity sha512-KFw7x02hZZkBdbZEFQduRGH4VkIH4MW97ClsbAM4Y4E6KguBJWGfWG1P4HEIpZk2bkoWf0bojpnjNAhYQP8beA==
dependencies:
"@eslint-community/eslint-utils" "^4.4.1"
enhanced-resolve "^5.17.1"
@@ -4608,10 +4615,10 @@ mimic-fn@^2.1.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
-miniflare@3.20241205.0, miniflare@^3.20231218.1:
- version "3.20241205.0"
- resolved "https://registry.yarnpkg.com/miniflare/-/miniflare-3.20241205.0.tgz#602546d4bdcc22f2f3efdd0dc2c44cbd895e4430"
- integrity sha512-Z0cTtIf6ZrcAJ3SrOI9EUM3s4dkGhNeU6Ubl8sroYhsPVD+rtz3m5+p6McHFWCkcMff1o60X5XEKVTmkz0gbpA==
+miniflare@3.20241218.0, miniflare@^3.20231218.1:
+ version "3.20241218.0"
+ resolved "https://registry.yarnpkg.com/miniflare/-/miniflare-3.20241218.0.tgz#c9d56b4236a6552cb3749cf7e452b7736f902090"
+ integrity sha512-spYFDArH0wd+wJSTrzBrWrXJrbyJhRMJa35mat947y1jYhVV8I5V8vnD3LwjfpLr0SaEilojz1OIW7ekmnRe+w==
dependencies:
"@cspotcode/source-map-support" "0.8.1"
acorn "^8.8.0"
@@ -4621,7 +4628,7 @@ miniflare@3.20241205.0, miniflare@^3.20231218.1:
glob-to-regexp "^0.4.1"
stoppable "^1.1.0"
undici "^5.28.4"
- workerd "1.20241205.0"
+ workerd "1.20241218.0"
ws "^8.18.0"
youch "^3.2.2"
zod "^3.22.3"
@@ -4742,6 +4749,11 @@ ms@2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
+ms@2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+ integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
ms@^2.1.1, ms@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
@@ -4778,12 +4790,12 @@ next-tick@^1.1.0:
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==
-next@^15.1.0:
- version "15.1.0"
- resolved "https://registry.yarnpkg.com/next/-/next-15.1.0.tgz#be847cf67ac94ae23b57f3ea6d10642f3fc1ad69"
- integrity sha512-QKhzt6Y8rgLNlj30izdMbxAwjHMFANnLwDwZ+WQh5sMhyt4lEBqDK9QpvWHtIM4rINKPoJ8aiRZKg5ULSybVHw==
+next@^15.1.3:
+ version "15.1.3"
+ resolved "https://registry.yarnpkg.com/next/-/next-15.1.3.tgz#49c45f884660dfaf07f53e70585d109a3283100e"
+ integrity sha512-5igmb8N8AEhWDYzogcJvtcRDU6n4cMGtBklxKD4biYv4LXN8+awc/bbQ2IM2NQHdVPgJ6XumYXfo3hBtErg1DA==
dependencies:
- "@next/env" "15.1.0"
+ "@next/env" "15.1.3"
"@swc/counter" "0.1.3"
"@swc/helpers" "0.5.15"
busboy "1.6.0"
@@ -4791,14 +4803,14 @@ next@^15.1.0:
postcss "8.4.31"
styled-jsx "5.1.6"
optionalDependencies:
- "@next/swc-darwin-arm64" "15.1.0"
- "@next/swc-darwin-x64" "15.1.0"
- "@next/swc-linux-arm64-gnu" "15.1.0"
- "@next/swc-linux-arm64-musl" "15.1.0"
- "@next/swc-linux-x64-gnu" "15.1.0"
- "@next/swc-linux-x64-musl" "15.1.0"
- "@next/swc-win32-arm64-msvc" "15.1.0"
- "@next/swc-win32-x64-msvc" "15.1.0"
+ "@next/swc-darwin-arm64" "15.1.3"
+ "@next/swc-darwin-x64" "15.1.3"
+ "@next/swc-linux-arm64-gnu" "15.1.3"
+ "@next/swc-linux-arm64-musl" "15.1.3"
+ "@next/swc-linux-x64-gnu" "15.1.3"
+ "@next/swc-linux-x64-musl" "15.1.3"
+ "@next/swc-win32-arm64-msvc" "15.1.3"
+ "@next/swc-win32-x64-msvc" "15.1.3"
sharp "^0.33.5"
node-domexception@^1.0.0:
@@ -5302,6 +5314,11 @@ react-is@^18.3.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e"
integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
+react-is@^19.0.0:
+ version "19.0.0"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.0.0.tgz#d6669fd389ff022a9684f708cf6fa4962d1fea7a"
+ integrity sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==
+
react-transition-group@^4.4.5:
version "4.4.5"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
@@ -5584,10 +5601,10 @@ semver@6.3.1, semver@^6.0.0, semver@^6.3.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
-semver@7.3.5:
- version "7.3.5"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
- integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
+semver@7.5.4:
+ version "7.5.4"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
+ integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
dependencies:
lru-cache "^6.0.0"
@@ -6397,32 +6414,32 @@ uuid@3.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
-uuid@^10.0.0:
- version "10.0.0"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294"
- integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==
+uuid@^11.0.3:
+ version "11.0.3"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.0.3.tgz#248451cac9d1a4a4128033e765d137e2b2c49a3d"
+ integrity sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==
v8-compile-cache-lib@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
-vercel@^39.2.2:
- version "39.2.2"
- resolved "https://registry.yarnpkg.com/vercel/-/vercel-39.2.2.tgz#a1e4a9a7592ec51607a9046411825be0c0ac45ae"
- integrity sha512-FTt0r++eORfQ3TLshYFdq5WiC7xVfbKMLE5YsBzex41yeDSCo5a5KXD6nDyMm+IIlu++XsHEVJRnfOA/JjL/mw==
+vercel@^39.2.4:
+ version "39.2.4"
+ resolved "https://registry.yarnpkg.com/vercel/-/vercel-39.2.4.tgz#9cdaedd230454449d64002453bdb101e4b6d84a2"
+ integrity sha512-VWmnSH2qgrugOvc6vRm0F/trUjAX1/Ew/5bQY+JzVC6nvRPmOot2zjTs7c38Ye8pr1RdMMqxKY1+L5FzWI8Tdw==
dependencies:
- "@vercel/build-utils" "8.8.0"
- "@vercel/fun" "1.1.0"
+ "@vercel/build-utils" "9.0.0"
+ "@vercel/fun" "1.1.2"
"@vercel/go" "3.2.1"
- "@vercel/hydrogen" "1.0.9"
- "@vercel/next" "4.4.0"
- "@vercel/node" "5.0.0"
- "@vercel/python" "4.5.1"
- "@vercel/redwood" "2.1.10"
- "@vercel/remix-builder" "4.0.0"
+ "@vercel/hydrogen" "1.0.10"
+ "@vercel/next" "4.4.1"
+ "@vercel/node" "5.0.1"
+ "@vercel/python" "4.6.0"
+ "@vercel/redwood" "2.1.11"
+ "@vercel/remix-builder" "5.0.1"
"@vercel/ruby" "2.1.0"
- "@vercel/static-build" "2.5.39"
+ "@vercel/static-build" "2.5.40"
chokidar "4.0.0"
web-streams-polyfill@^3.0.3:
@@ -6566,21 +6583,21 @@ word-wrap@^1.2.5:
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
-workerd@1.20241205.0:
- version "1.20241205.0"
- resolved "https://registry.yarnpkg.com/workerd/-/workerd-1.20241205.0.tgz#db66f5ef2c0b7c5ddd993b22e4c8cc9b446d72b6"
- integrity sha512-vso/2n0c5SdBDWiD+Sx5gM7unA6SiZXRVUHDqH1euoP/9mFVHZF8icoYsNLB87b/TX8zNgpae+I5N/xFpd9v0g==
+workerd@1.20241218.0:
+ version "1.20241218.0"
+ resolved "https://registry.yarnpkg.com/workerd/-/workerd-1.20241218.0.tgz#b288f928b833b65e484726aaf630f13ebdd7d0fb"
+ integrity sha512-7Z3D4vOVChMz9mWDffE299oQxUWm/pbkeAWx1btVamPcAK/2IuoNBhwflWo3jyuKuxvYuFAdIucgYxc8ICqXiA==
optionalDependencies:
- "@cloudflare/workerd-darwin-64" "1.20241205.0"
- "@cloudflare/workerd-darwin-arm64" "1.20241205.0"
- "@cloudflare/workerd-linux-64" "1.20241205.0"
- "@cloudflare/workerd-linux-arm64" "1.20241205.0"
- "@cloudflare/workerd-windows-64" "1.20241205.0"
+ "@cloudflare/workerd-darwin-64" "1.20241218.0"
+ "@cloudflare/workerd-darwin-arm64" "1.20241218.0"
+ "@cloudflare/workerd-linux-64" "1.20241218.0"
+ "@cloudflare/workerd-linux-arm64" "1.20241218.0"
+ "@cloudflare/workerd-windows-64" "1.20241218.0"
-wrangler@^3.96.0:
- version "3.96.0"
- resolved "https://registry.yarnpkg.com/wrangler/-/wrangler-3.96.0.tgz#d62b11bd2968f44a1705706dc3ceac27137915c6"
- integrity sha512-KjbHTUnwTa5eKl3hzv2h6nHBfAsbUkdurL7f6Y288/Bdn6tcEis13jLVR/nw/eWa3tNCBG1xOMZJboUyzWcC1g==
+wrangler@^3.99.0:
+ version "3.99.0"
+ resolved "https://registry.yarnpkg.com/wrangler/-/wrangler-3.99.0.tgz#2c442fde399d45ce23262b88056bb6794377f6f6"
+ integrity sha512-k0x4rT3G/QCbxcoZY7CHRVlAIS8WMmKdga6lf4d2c3gXFqssh44vwlTDuARA9QANBxKJTcA7JPTJRfUDhd9QBA==
dependencies:
"@cloudflare/kv-asset-handler" "0.3.4"
"@esbuild-plugins/node-globals-polyfill" "^0.2.3"
@@ -6590,14 +6607,14 @@ wrangler@^3.96.0:
date-fns "^4.1.0"
esbuild "0.17.19"
itty-time "^1.0.6"
- miniflare "3.20241205.0"
+ miniflare "3.20241218.0"
nanoid "^3.3.3"
path-to-regexp "^6.3.0"
resolve "^1.22.8"
selfsigned "^2.0.1"
source-map "^0.6.1"
unenv "npm:unenv-nightly@2.0.0-20241204-140205-a5d5190"
- workerd "1.20241205.0"
+ workerd "1.20241218.0"
xxhash-wasm "^1.0.1"
optionalDependencies:
fsevents "~2.3.2"