├── examples
├── react
│ ├── public
│ │ └── paddo.jpg
│ ├── vite.config.js
│ ├── src
│ │ ├── main.tsx
│ │ ├── vite-env.d.ts
│ │ ├── App.tsx
│ │ ├── App.css
│ │ └── shaders
│ │ │ └── glitch.glsl
│ ├── tsconfig.node.json
│ ├── .gitignore
│ ├── index.html
│ ├── package.json
│ ├── tsconfig.json
│ ├── README.md
│ └── package-lock.json
└── vanilla
│ ├── vite.config.js
│ ├── public
│ └── static
│ │ ├── paddo.jpg
│ │ └── growMask.png
│ ├── src
│ ├── shader
│ │ ├── flow_image.glsl
│ │ ├── fluid_image.glsl
│ │ ├── flow_buffer.glsl
│ │ ├── fluid_paint.glsl
│ │ ├── mask_2.glsl
│ │ ├── mask_1.glsl
│ │ ├── fluid_dynamics.glsl
│ │ ├── fluid_like.glsl
│ │ ├── stargate.glsl
│ │ └── glitch.glsl
│ ├── examples
│ │ ├── Stargate.js
│ │ ├── FluidLike.js
│ │ ├── Glitch.js
│ │ ├── Mask1.js
│ │ ├── Mask2.js
│ │ ├── Flow.js
│ │ ├── CreateDestructTest.js
│ │ └── FluidDynamics.js
│ ├── utils
│ │ └── ImageLoader.js
│ └── index.js
│ ├── package.json
│ ├── .gitignore
│ ├── index.html
│ └── package-lock.json
├── src
├── lib
│ ├── Texture.js.ts
│ ├── Uniform.ts
│ ├── MouseListener.ts
│ ├── RendererBuffer.ts
│ ├── FrameBuffer.ts
│ ├── ImageEffectRenderer.ts
│ ├── WebGLInstance.ts
│ ├── Program.ts
│ ├── Renderer.ts
│ └── RendererInstance.ts
├── index.ts
└── react
│ ├── index.ts
│ ├── ImageEffectRendererComponent.tsx
│ └── useImageEffectRenderer.ts
├── .editorconfig
├── .github
├── dependabot.yml
└── workflows
│ ├── deploy-to-pages.yml
│ ├── build.yml
│ ├── publish-dev-release.yml
│ └── bump-version-and-publish.yml
├── .gitignore
├── tsconfig.json
├── vite.config.js
├── scripts
└── preparePublish.ts
├── package.json
└── README.md
/examples/react/public/paddo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mediamonks/image-effect-renderer/HEAD/examples/react/public/paddo.jpg
--------------------------------------------------------------------------------
/examples/vanilla/vite.config.js:
--------------------------------------------------------------------------------
1 | import {defineConfig} from 'vite'
2 |
3 | export default defineConfig({
4 | base: './',
5 | })
6 |
--------------------------------------------------------------------------------
/examples/vanilla/public/static/paddo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mediamonks/image-effect-renderer/HEAD/examples/vanilla/public/static/paddo.jpg
--------------------------------------------------------------------------------
/examples/vanilla/public/static/growMask.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mediamonks/image-effect-renderer/HEAD/examples/vanilla/public/static/growMask.png
--------------------------------------------------------------------------------
/examples/react/vite.config.js:
--------------------------------------------------------------------------------
1 | import {defineConfig} from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/src/lib/Texture.js.ts:
--------------------------------------------------------------------------------
1 | import type {RendererBuffer} from "./RendererBuffer.js";
2 |
3 | export type Texture = {
4 | texture: WebGLTexture | undefined;
5 | buffer: RendererBuffer | undefined;
6 | cached: boolean;
7 | }
8 |
--------------------------------------------------------------------------------
/examples/vanilla/src/shader/flow_image.glsl:
--------------------------------------------------------------------------------
1 | void mainImage(out vec4 fragColor, in vec2 fragCoord) {
2 | vec2 uv = fragCoord.xy / iResolution.xy;
3 | uv += .1 * (texture(iChannel1, uv).xy);
4 | fragColor = texture(iChannel0, uv);
5 | }
6 |
--------------------------------------------------------------------------------
/examples/react/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import App from './App';
4 |
5 | ReactDOM.createRoot(document.getElementById('root')!).render(
6 |
7 |
8 |
9 | );
10 |
--------------------------------------------------------------------------------
/examples/react/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module '*.glsl' {
4 | const content: string;
5 | export default content;
6 | }
7 |
8 | declare module '*.glsl?raw' {
9 | const content: string;
10 | export default content;
11 | }
12 |
--------------------------------------------------------------------------------
/examples/vanilla/src/shader/fluid_image.glsl:
--------------------------------------------------------------------------------
1 | void mainImage(out vec4 fragColor, in vec2 fragCoord) {
2 | vec2 uv = fragCoord.xy / iResolution.xy;
3 |
4 | vec3 col = 1.-exp(-texture(iChannel0, uv).rgb);
5 | col = smoothstep(vec3(0), vec3(1), col);
6 |
7 | fragColor = vec4(col, 1);
8 | }
9 |
--------------------------------------------------------------------------------
/examples/react/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "include": [
10 | "vite.config.js"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | end_of_line = lf
8 | indent_style = space
9 | indent_size = 2
10 | insert_final_newline = true
11 | max_line_length = 100
12 | quote_type = single
13 | trim_trailing_whitespace = true
14 |
15 | [*.{md,markdown}]
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/examples/vanilla/src/examples/Stargate.js:
--------------------------------------------------------------------------------
1 | import {ImageEffectRenderer} from '../../../../src/index';
2 | import shader from '../shader/stargate.glsl?raw';
3 |
4 | export default class Stargate {
5 | constructor(wrapper, options = {}) {
6 | this.renderer = ImageEffectRenderer.createTemporary(wrapper, shader, {loop: true, ...options});
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/examples/vanilla/src/examples/FluidLike.js:
--------------------------------------------------------------------------------
1 | import {ImageEffectRenderer} from '../../../../src/index';
2 | import shader from '../shader/fluid_like.glsl?raw';
3 |
4 | export default class FluidLike {
5 | constructor(wrapper, options = {}) {
6 | this.renderer = ImageEffectRenderer.createTemporary(wrapper, shader, {loop: true, ...options});
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/examples/react/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Dependencies
7 | node_modules/
8 |
9 | # Build output
10 | dist/
11 | dist-ssr/
12 |
13 | # Environment
14 | .env
15 | .env.local
16 | .env.*.local
17 |
18 | # Editor
19 | .vscode/*
20 | !.vscode/extensions.json
21 | .idea
22 | *.suo
23 | *.ntvs*
24 | *.njsproj
25 | *.sln
26 | *.sw?
27 |
--------------------------------------------------------------------------------
/examples/react/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Image Effect Renderer - React Example
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/examples/vanilla/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "image-effect-renderer examples",
3 | "version": "1.0.0",
4 | "description": "",
5 | "author": "Reinder Nijhoff ",
6 | "main": "index.js",
7 | "scripts": {
8 | "dev": "vite",
9 | "build": "vite build",
10 | "preview": "vite preview"
11 | },
12 | "keywords": [],
13 | "license": "MIT",
14 | "devDependencies": {
15 | "vite": "^5.2.14"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/examples/vanilla/src/utils/ImageLoader.js:
--------------------------------------------------------------------------------
1 | export default class ImageLoader {
2 | static loadImages(images) {
3 | return Promise.all(
4 | images.map(fileName => ImageLoader.loadImage(fileName)),
5 | );
6 | }
7 |
8 | static loadImage(fileName) {
9 | return new Promise((resolve) => {
10 | const img = new Image;
11 | // img.onload = () => resolve(img);
12 | resolve(img);
13 | img.src = `./static/${fileName}`;
14 | });
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "npm"
9 | directory: "/"
10 | schedule:
11 | interval: "weekly"
12 | day: "sunday"
13 |
--------------------------------------------------------------------------------
/examples/vanilla/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # cypress
15 | /cypress/screenshots
16 | /cypress/videos
17 |
18 | # misc
19 | .DS_Store
20 | .env.local
21 | .env.development.local
22 | .env.test.local
23 | .env.production.local
24 | .idea
25 | .vscode
26 |
27 | npm-debug.log*
28 | yarn-debug.log*
29 | yarn-error.log*
30 |
31 | .eslintcache
32 |
--------------------------------------------------------------------------------
/examples/vanilla/src/examples/Glitch.js:
--------------------------------------------------------------------------------
1 | import {ImageEffectRenderer} from '../../../../src/index';
2 | import shader from '../shader/glitch.glsl?raw';
3 | import ImageLoader from "../utils/ImageLoader";
4 |
5 | export default class Glitch {
6 | constructor(wrapper, options = {}) {
7 | this.renderer = ImageEffectRenderer.createTemporary(wrapper, shader, options);
8 |
9 | ImageLoader.loadImages(['./paddo.jpg']).then(([mask]) => {
10 | this.renderer.setImage(0, mask, {flipY: true});
11 | this.renderer.play();
12 | });
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/examples/vanilla/src/shader/flow_buffer.glsl:
--------------------------------------------------------------------------------
1 | vec3 mouseInput(vec2 uv) {
2 | vec2 d = uv - iMouse.xy;
3 | d.x *= iResolution.x / iResolution.y;
4 | return vec3((iMouse.zw-iMouse.xy) * 20. * smoothstep(.2, 0., length(d)), 0);
5 | }
6 |
7 | void mainImage(out vec4 fragColor, in vec2 fragCoord) {
8 | vec2 uv = fragCoord.xy / iResolution.xy;
9 |
10 | vec3 oldColor = iFrame <= 1 ? vec3(0) : texture(iChannel0, uv).rgb * 250./255.;
11 | vec3 newColor = oldColor + mouseInput(uv);
12 |
13 | // newColor -= sign(newColor) * 1./127.;
14 |
15 | fragColor = vec4(newColor, 1);
16 | }
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # cypress
15 | /cypress/screenshots
16 | /cypress/videos
17 |
18 | # misc
19 | .DS_Store
20 | .env.local
21 | .env.development.local
22 | .env.test.local
23 | .env.production.local
24 | .idea
25 | .vscode
26 |
27 | npm-debug.log*
28 | yarn-debug.log*
29 | yarn-error.log*
30 |
31 | .eslintcache
32 |
33 | # npm publish artifects
34 | /dist
35 | /example/dist
36 |
--------------------------------------------------------------------------------
/examples/vanilla/src/examples/Mask1.js:
--------------------------------------------------------------------------------
1 | import {ImageEffectRenderer} from '../../../../src/index';
2 | import shader from '../shader/mask_1.glsl?raw';
3 | import ImageLoader from "../utils/ImageLoader";
4 |
5 | export default class Mask1 {
6 | constructor(wrapper, options = {}) {
7 | this.renderer = ImageEffectRenderer.createTemporary(wrapper, shader, options);
8 |
9 | ImageLoader.loadImages(['./growMask.png']).then(([mask]) => {
10 | this.renderer.setImage(0, mask);
11 | this.renderer.setUniformFloat('iFrames', 30);
12 |
13 | this.renderer.play();
14 | });
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/ImageEffectRenderer.js';
2 | export type {
3 | RendererInstance, RendererData, BufferData, ImagesData, BufferIndex
4 | } from './lib/RendererInstance.js';
5 | export {
6 | BUFFER_0, BUFFER_1, BUFFER_2, BUFFER_3, BUFFER_4, BUFFER_5, BUFFER_6, BUFFER_7
7 | } from './lib/RendererInstance.js';
8 | export type {RendererBuffer} from './lib/RendererBuffer.js';
9 | export type {ImageOptions, ImageSource} from './lib/Renderer.js';
10 | export type {ImageEffectRendererOptions} from './lib/ImageEffectRenderer.js';
11 | export type {BufferOptions} from './lib/RendererBuffer.js';
12 |
--------------------------------------------------------------------------------
/examples/react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "image-effect-renderer-react-example",
3 | "version": "1.0.0",
4 | "description": "React example for image-effect-renderer",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "@mediamonks/image-effect-renderer": "file:../..",
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.3.12",
18 | "@types/react-dom": "^18.3.1",
19 | "@vitejs/plugin-react": "^4.3.4",
20 | "typescript": "^5.7.2",
21 | "vite": "^6.0.1"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/vanilla/src/examples/Mask2.js:
--------------------------------------------------------------------------------
1 | import {ImageEffectRenderer} from '../../../../src/index';
2 | import shader from '../shader/mask_2.glsl?raw';
3 | import ImageLoader from "../utils/ImageLoader";
4 |
5 | export default class Mask2 {
6 | constructor(wrapper, options = {}) {
7 | this.renderer = ImageEffectRenderer.createTemporary(wrapper, shader, options);
8 |
9 | ImageLoader.loadImages(['./growMask.png', './paddo.jpg']).then(([mask, paddo]) => {
10 | this.renderer.setImage(0, mask);
11 | this.renderer.setImage(1, paddo, {useMips: true});
12 |
13 | this.renderer.setUniformFloat('iFrames', 30);
14 | this.renderer.play();
15 | });
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "NodeNext",
5 | "lib": [
6 | "ESNext",
7 | "DOM"
8 | ],
9 | "jsx": "react-jsx",
10 | "strict": true,
11 | "noUncheckedIndexedAccess": true,
12 | "exactOptionalPropertyTypes": true,
13 | "noPropertyAccessFromIndexSignature": true,
14 | "noImplicitOverride": true,
15 | "moduleResolution": "NodeNext",
16 | "allowSyntheticDefaultImports": true,
17 | "esModuleInterop": true,
18 | "verbatimModuleSyntax": true,
19 | "incremental": false,
20 | "outDir": "./dist",
21 | "declaration": true,
22 | "allowJs": true,
23 | "skipLibCheck": true
24 | },
25 | "include": [
26 | "./src/**/*"
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/examples/react/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": [
6 | "ES2020",
7 | "DOM",
8 | "DOM.Iterable"
9 | ],
10 | "module": "ESNext",
11 | "skipLibCheck": true,
12 | /* Bundler mode */
13 | "moduleResolution": "bundler",
14 | "allowImportingTsExtensions": true,
15 | "resolveJsonModule": true,
16 | "isolatedModules": true,
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 | /* Linting */
20 | "strict": true,
21 | "noUnusedLocals": true,
22 | "noUnusedParameters": true,
23 | "noFallthroughCasesInSwitch": true
24 | },
25 | "include": [
26 | "src"
27 | ],
28 | "references": [
29 | {
30 | "path": "./tsconfig.node.json"
31 | }
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/src/lib/Uniform.ts:
--------------------------------------------------------------------------------
1 | export const UNIFORM_INT = 0;
2 | export const UNIFORM_FLOAT = 1;
3 | export const UNIFORM_VEC2 = 2;
4 | export const UNIFORM_VEC3 = 3;
5 | export const UNIFORM_VEC4 = 4;
6 | export const UNIFORM_MATRIX = 5;
7 |
8 | export type UniformType =
9 | typeof UNIFORM_INT
10 | | typeof UNIFORM_FLOAT
11 | | typeof UNIFORM_VEC2
12 | | typeof UNIFORM_VEC3
13 | | typeof UNIFORM_VEC4
14 | | typeof UNIFORM_MATRIX;
15 |
16 | export default class Uniform {
17 | public type: UniformType;
18 | public name: string;
19 | public x: number = 0;
20 | public y: number = 0;
21 | public z: number = 0;
22 | public w: number = 0;
23 | public matrix: Float32Array | undefined;
24 |
25 | constructor(type: UniformType, name: string) {
26 | this.type = type;
27 | this.name = name;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/examples/vanilla/src/shader/fluid_paint.glsl:
--------------------------------------------------------------------------------
1 | // The MIT License
2 | // Copyright © 2015 Inigo Quilez
3 | // https://www.shadertoy.com/view/ll2GD3
4 |
5 | vec3 pal(in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d) {
6 | return a + b*cos(6.28318*(c*t+d));
7 | }
8 |
9 | void mainImage(out vec4 fragColor, in vec2 fragCoord) {
10 | const float dt = 0.15;
11 |
12 | vec2 uv = fragCoord.xy / iResolution.xy;
13 |
14 | vec2 velocity = texture(iChannel0, uv).xy;
15 | vec3 col = texture(iChannel1, uv - (dt*3.)*velocity/iResolution.xy).rgb;
16 |
17 | vec3 newCol = pal(iTime, vec3(0.5, 0.5, 0.5), vec3(0.5, 0.5, 0.5), vec3(1.0, 1.0, 1.0), vec3(0.0, 0.10, 0.20));
18 |
19 | col += newCol * 0.01*distance(iMouse.xy, iMouse.zw)/(dot(uv - iMouse.xy, uv - iMouse.xy)+0.002);
20 |
21 | col = clamp(0.998 * col - 0.00005, 0., 5.);
22 | fragColor = vec4(col, 1.);
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/.github/workflows/deploy-to-pages.yml:
--------------------------------------------------------------------------------
1 | name: Build and Deploy
2 | on:
3 | push:
4 | branches:
5 | - main
6 |
7 | jobs:
8 | build-and-deploy:
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v4
14 |
15 | - name: Set up Node.js
16 | uses: actions/setup-node@v4
17 | with:
18 | node-version: '20'
19 | cache: 'npm'
20 |
21 | - name: Install npm packages
22 | run: npm ci
23 | working-directory: ./examples/vanilla
24 |
25 | - name: Build
26 | run: npm run build
27 | working-directory: ./examples/vanilla
28 |
29 | - name: Deploy to GitHub Pages
30 | uses: peaceiris/actions-gh-pages@v4
31 | with:
32 | github_token: ${{ secrets.GITHUB_TOKEN }}
33 | publish_dir: ./examples/vanilla/dist
34 |
--------------------------------------------------------------------------------
/examples/vanilla/src/examples/Flow.js:
--------------------------------------------------------------------------------
1 | import {ImageEffectRenderer} from '../../../../src/index';
2 | import flow_image from '../shader/flow_image.glsl?raw';
3 | import flow_buffer from '../shader/flow_buffer.glsl?raw';
4 | import ImageLoader from "../utils/ImageLoader";
5 |
6 | export default class Flow {
7 | constructor(wrapper, options = {}) {
8 | this.wrapper = wrapper;
9 |
10 | this.renderer = ImageEffectRenderer.createTemporary(this.wrapper, flow_image, options);
11 |
12 | this.renderer.createBuffer(0, flow_buffer);
13 | this.renderer.buffers[0].setImage(0, this.renderer.buffers[0], {type: WebGLRenderingContext.FLOAT});
14 | this.renderer.setImage(1, this.renderer.buffers[0]);
15 |
16 | ImageLoader.loadImages(['./paddo.jpg']).then(([mask]) => {
17 | this.renderer.setImage(0, mask, {flipY: true});
18 | this.renderer.play();
19 | });
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/lib/MouseListener.ts:
--------------------------------------------------------------------------------
1 | let mouseX: number = -0;
2 | let mouseY: number = -0;
3 | let mouseBinded: boolean = false;
4 |
5 | export function bindMouseListener(container: HTMLElement) {
6 | if (mouseBinded) {
7 | return;
8 | }
9 | mouseBinded = true;
10 | container.addEventListener('mousemove', (event) => {
11 | mouseX = event.clientX;
12 | mouseY = event.clientY;
13 | }, {passive: true});
14 | }
15 |
16 | export function getMousePosition(): [number, number] {
17 | return [mouseX, mouseY];
18 | }
19 |
20 | export type Rect = {
21 | left: number,
22 | top: number,
23 | width: number,
24 | height: number,
25 | }
26 |
27 | export function getNormalizedMousePosition(container: Rect, mouse: [number, number]): [number, number] {
28 | const x = (mouse[0] - container.left) / container.width;
29 | const y = 1 - (mouse[1] - container.top) / container.height;
30 | return [x, y];
31 | }
--------------------------------------------------------------------------------
/examples/vanilla/src/shader/mask_2.glsl:
--------------------------------------------------------------------------------
1 | const float iFrameStepSize = 1.;// Based on alpha during additive blending
2 | const float smoothWidth = 4./255.;// 0 - 1 (1./255. = no smoothing, 10./255. = 10 frames smoothing)
3 |
4 | uniform float iFrames;
5 |
6 | float getDelta() {
7 | // normally, you will pass delta as an uniform
8 | return clamp(mod(iTime * 24., iFrames + 4.) - 2., 0., iFrames) / 255.;
9 | }
10 |
11 | void mainImage(out vec4 fragColor, in vec2 fragCoord)
12 | {
13 | vec2 uv = fragCoord/iResolution.xy;
14 | uv.y = 1.0 - uv.y;
15 |
16 | float maskGrad = iFrames/255. - texture(iChannel0, uv).r * (1. / iFrameStepSize);
17 |
18 | // normally, you will pass delta as an uniform
19 | float delta = getDelta();
20 |
21 | vec3 col = texture(iChannel1, uv).rgb;
22 |
23 | float alpha = smoothstep(maskGrad, maskGrad + smoothWidth, delta * (1.0 + smoothWidth * 255./iFrames));
24 |
25 | fragColor = vec4(col * alpha, alpha);
26 | }
27 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import {defineConfig} from 'vite'
2 | import {resolve} from 'path'
3 | import dts from "vite-plugin-dts";
4 |
5 | export default defineConfig({
6 | target: 'esnext',
7 | plugins: [
8 | dts({
9 | include: ['src/**/*'],
10 | exclude: ['**/*.spec.ts', '**/*.test.ts'],
11 | })
12 | ],
13 | build: {
14 | assetsInlineLimit: 409600,
15 | target: 'esnext',
16 | lib: {
17 | assetsInlineLimit: 409600,
18 | entry: {
19 | 'image-effect-renderer': resolve(__dirname, 'src/index.ts'),
20 | 'image-effect-renderer-react': resolve(__dirname, 'src/react/index.ts'),
21 | },
22 | name: "ImageEffectRenderer",
23 | formats: ['es'],
24 | },
25 | rollupOptions: {
26 | external: ['react', 'react-dom'],
27 | output: {
28 | globals: {
29 | react: 'React',
30 | 'react-dom': 'ReactDOM',
31 | },
32 | },
33 | },
34 | }
35 | })
36 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
3 |
4 | name: Build
5 |
6 | on:
7 | push:
8 | branches: [ 'main' ]
9 | pull_request:
10 | branches: [ 'main' ]
11 |
12 | jobs:
13 | build:
14 | runs-on: ubuntu-latest
15 |
16 | strategy:
17 | matrix:
18 | node-version: [ 20.x ]
19 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
20 |
21 | steps:
22 | - uses: actions/checkout@v4
23 | - name: Use Node.js ${{ matrix.node-version }}
24 | uses: actions/setup-node@v4
25 | with:
26 | node-version: ${{ matrix.node-version }}
27 | cache: 'npm'
28 | - run: npm ci
29 | - run: npm run build
30 |
--------------------------------------------------------------------------------
/examples/vanilla/src/shader/mask_1.glsl:
--------------------------------------------------------------------------------
1 | const float iFrameStepSize = 1.;// Based on alpha during additive blending
2 | const float smoothWidth = 4./255.;// 0 - 1 (1./255. = no smoothing, 10./255. = 10 frames smoothing)
3 |
4 | uniform float iFrames;
5 |
6 | float getDelta() {
7 | // normally, you will pass delta as an uniform
8 | return clamp(mod(iTime * 24., iFrames + 4.) - 2., 0., iFrames) / 255.;
9 | }
10 |
11 | void mainImage(out vec4 fragColor, in vec2 fragCoord)
12 | {
13 | vec2 uv = fragCoord/iResolution.xy;
14 | uv.y = 1.0 - uv.y;
15 |
16 | float maskGrad = iFrames/255. - texture(iChannel0, uv).r * (1. / iFrameStepSize);
17 |
18 | // normally, you will pass delta as an uniform
19 | float delta = getDelta();
20 |
21 | // temp color, you probably want to use texture
22 | vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0, 2, 4));
23 |
24 | float alpha = smoothstep(maskGrad, maskGrad + smoothWidth, delta * (1.0 + smoothWidth * 255./iFrames));
25 |
26 | fragColor = vec4(col * alpha, 1.0);
27 | }
28 |
--------------------------------------------------------------------------------
/examples/vanilla/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ImageEffectRenderer Examples
6 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/examples/vanilla/src/examples/CreateDestructTest.js:
--------------------------------------------------------------------------------
1 | import {ImageEffectRenderer} from '../../../../src/index';
2 | import Stargate from "./Stargate";
3 | import Flow from "./Flow";
4 | import FluidDynamics from "./FluidDynamics";
5 | import Glitch from "./Glitch";
6 | import FluidLike from "./FluidLike";
7 | import Mask1 from "./Mask1";
8 | import Mask2 from "./Mask2";
9 |
10 | export default class CreateDestructTest {
11 | constructor(wrapper, options = {}) {
12 | this.wrapper = wrapper;
13 | this.options = options;
14 |
15 | this.index = 0;
16 | this.classes = [Stargate, Flow, FluidDynamics, Glitch, Mask1, Mask2, FluidLike];
17 | // this.classes = [CornellBox, Glitch, Mask1, Mask2, MetaBalls];
18 |
19 | window.setInterval(() => {
20 | if (this.renderer) {
21 | ImageEffectRenderer.releaseTemporary(this.renderer);
22 | }
23 |
24 | this.index = (this.index + 1) % this.classes.length;
25 |
26 | this.renderer = (new this.classes[this.index](this.wrapper, {...this.options})).renderer;
27 | }, 500);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/examples/vanilla/src/index.js:
--------------------------------------------------------------------------------
1 | import Stargate from "./examples/Stargate";
2 | import Glitch from "./examples/Glitch";
3 | import FluidDynamics from "./examples/FluidDynamics";
4 | import FluidLike from "./examples/FluidLike";
5 | import Mask1 from "./examples/Mask1";
6 | import Mask2 from "./examples/Mask2";
7 | import Flow from "./examples/Flow";
8 | import CreateDestructTest from "./examples/CreateDestructTest";
9 |
10 | new Mask1(document.getElementsByClassName('grid-item')[0]);
11 | new Stargate(document.getElementsByClassName('grid-item')[1]);
12 | new Glitch(document.getElementsByClassName('grid-item')[2]);
13 | new FluidLike(document.getElementsByClassName('grid-item')[3]);
14 | new FluidDynamics(document.getElementsByClassName('grid-item')[4]);
15 | new Mask2(document.getElementsByClassName('grid-item')[5]);
16 | new Flow(document.getElementsByClassName('grid-item')[6]);
17 | new CreateDestructTest(document.getElementsByClassName('grid-item')[7], {useSharedContext: true});
18 | new Mask1(document.getElementsByClassName('grid-item')[8], {useSharedContext: false});
19 |
20 |
--------------------------------------------------------------------------------
/src/react/index.ts:
--------------------------------------------------------------------------------
1 | export type {
2 | ImageEffectRendererOptions,
3 | } from '../lib/ImageEffectRenderer.js';
4 | export {ImageEffectRenderer} from '../lib/ImageEffectRenderer.js';
5 | export type {
6 | RendererInstance, RendererData, BufferData, ImagesData, BufferIndex
7 | } from '../lib/RendererInstance.js';
8 | export {
9 | BUFFER_0, BUFFER_1, BUFFER_2, BUFFER_3, BUFFER_4, BUFFER_5, BUFFER_6, BUFFER_7
10 | } from '../lib/RendererInstance.js';
11 | export type {RendererBuffer} from '../lib/RendererBuffer.js';
12 | export type {ImageOptions, ImageSource} from '../lib/Renderer.js';
13 | export type {BufferOptions} from '../lib/RendererBuffer.js';
14 |
15 | export {useImageEffectRenderer} from './useImageEffectRenderer.js';
16 | export type {
17 | UseImageEffectRendererOptions, UseImageEffectRendererReturn
18 | } from './useImageEffectRenderer.js';
19 |
20 | export {ImageEffectRendererComponent} from './ImageEffectRendererComponent.js';
21 | export type {
22 | ImageEffectRendererComponentProps, ImageEffectRendererComponentRef
23 | } from './ImageEffectRendererComponent.js';
24 |
--------------------------------------------------------------------------------
/.github/workflows/publish-dev-release.yml:
--------------------------------------------------------------------------------
1 | name: Publish dev release
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 |
7 | jobs:
8 | bump-version:
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - name: Check out source
13 | uses: actions/checkout@v3
14 |
15 | - name: Setup Node.js
16 | uses: actions/setup-node@v3
17 | with:
18 | node-version: '20'
19 | cache: 'npm'
20 |
21 | - name: Install npm packages
22 | run: npm ci
23 |
24 | - name: Setup Git
25 | run: |
26 | git config user.name github-actions
27 | git config user.email github-actions@github.com
28 |
29 | - name: Bump Version
30 | run: |
31 | echo "$(npm pkg get version | cut -d '"' -f 2 | cut -d '-' -f 1)-dev.$(git rev-parse --short HEAD)" | xargs npm version --no-git-tag-version
32 |
33 | - name: Build & Publish
34 | run: npm run build
35 |
36 | - name: Publish
37 | run: |
38 | npm run prepare-publish
39 | cd ./dist
40 | npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN}
41 | npm publish --tag next
42 | env:
43 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
44 |
--------------------------------------------------------------------------------
/scripts/preparePublish.ts:
--------------------------------------------------------------------------------
1 | import {join} from 'node:path';
2 | import shell from 'shelljs';
3 |
4 | /**
5 | * This script is run before publishing the package to npm.
6 | * It copies over the package.json and README.md files to the dist folder.
7 | * It also removes the 'dist/' from the 'exports' field in the package.json.
8 | *
9 | * This is needed because the remapping of paths in the 'exports' field
10 | * is not properly supported by editors like VSCode and WebStorm when
11 | * using auto-import or auto-complete.
12 | */
13 | // eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle
14 | import {fileURLToPath} from 'url'
15 |
16 | const __dirname = fileURLToPath(new URL('.', import.meta.url))
17 | const projectFolder = join(__dirname, '..');
18 |
19 | // eslint-disable-next-line unicorn/prevent-abbreviations
20 | const distFolder = join(projectFolder, 'dist');
21 |
22 | // include all paths (files or folders) that should be copied over to the dist folder on publish
23 | // this should likely be in sync with the 'files' field in the package.json
24 | const filesToPublish = ['package.json', 'README.md'];
25 |
26 | for (const file of filesToPublish) {
27 | shell.cp('-R', join(projectFolder, file), join(distFolder, file));
28 | }
29 |
30 | // remove the 'dist' folder from all fields in the package.json
31 | // it might replace it in areas other than the 'exports' field, but that's fine since it only uses those fields
32 | shell.sed('-i', 'dist/', '', join(distFolder, 'package.json'));
33 |
--------------------------------------------------------------------------------
/examples/vanilla/src/examples/FluidDynamics.js:
--------------------------------------------------------------------------------
1 | import {ImageEffectRenderer} from '../../../../src/index';
2 | import fluid_dynamics from '../shader/fluid_dynamics.glsl?raw';
3 | import fluid_paint from '../shader/fluid_paint.glsl?raw';
4 | import fluid_image from '../shader/fluid_image.glsl?raw';
5 |
6 | export default class FluidDynamics {
7 | constructor(wrapper, options = {}) {
8 | this.wrapper = wrapper;
9 |
10 | this.renderer = ImageEffectRenderer.createTemporary(this.wrapper, fluid_image, {loop: true, ...options});
11 |
12 | const bufferOptions = {
13 | type: WebGLRenderingContext.FLOAT,
14 | clampX: false,
15 | clampY: false
16 | };
17 |
18 | this.renderer.setData({
19 | buffers: [
20 | // Fluid dynamics buffers with circular dependencies
21 | {
22 | index: 0,
23 | shader: fluid_dynamics,
24 | options: bufferOptions,
25 | images: [{slotIndex: 0, image: {bufferIndex: 2}}]
26 | },
27 | {
28 | index: 1,
29 | shader: fluid_dynamics,
30 | options: bufferOptions,
31 | images: [{slotIndex: 0, image: {bufferIndex: 0}}]
32 | },
33 | {
34 | index: 2,
35 | shader: fluid_dynamics,
36 | options: bufferOptions,
37 | images: [{slotIndex: 0, image: {bufferIndex: 1}}]
38 | },
39 | // Fluid paint buffer
40 | {
41 | index: 3,
42 | shader: fluid_paint,
43 | options: bufferOptions,
44 | images: [
45 | {slotIndex: 0, image: {bufferIndex: 2}},
46 | {slotIndex: 1, image: {bufferIndex: 3}}
47 | ]
48 | }
49 | ],
50 | // Main renderer uses buffer 3
51 | images: [{slotIndex: 0, image: {bufferIndex: 3}}]
52 | });
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/examples/vanilla/src/shader/fluid_dynamics.glsl:
--------------------------------------------------------------------------------
1 | uniform float uMouseDown;
2 |
3 | void mainImage(out vec4 fragColor, in vec2 fragCoord) {
4 | vec2 uv = fragCoord / iResolution.xy;
5 | const float dt = 0.15;
6 |
7 | // Simple and Fast Fluids
8 | // https://hal.inria.fr/inria-00596050/document
9 |
10 | vec4 me = texture(iChannel0, uv);// x,y velocity, z density, w curl
11 | vec4 tr = texture(iChannel0, uv + vec2(1./iResolution.x, 0));
12 | vec4 tl = texture(iChannel0, uv - vec2(1./iResolution.x, 0));
13 | vec4 tu = texture(iChannel0, uv + vec2(0, 1./iResolution.y));
14 | vec4 td = texture(iChannel0, uv - vec2(0, 1./iResolution.y));
15 |
16 | vec3 dx = (tr.xyz - tl.xyz)*0.5;
17 | vec3 dy = (tu.xyz - td.xyz)*0.5;
18 | vec2 DdX = vec2(dx.z, dy.z);
19 |
20 | // Solve for density
21 | me.z -= dt*dot(vec3(DdX, dx.x + dy.y), me.xyz);
22 |
23 | // Solve for velocity
24 | vec2 viscosityForce = 0.55*(tu.xy + td.xy + tr.xy + tl.xy - 4.0*me.xy);
25 | me.xyw = texture(iChannel0, uv - me.xy*(dt/iResolution.xy)).xyw;
26 |
27 | vec2 externalForces = clamp(vec2(iMouse.xy - iMouse.zw) * (.4 / max(dot(uv - iMouse.xy, uv - iMouse.xy), .05)), -1., 1.);
28 |
29 | // Semi−lagrangian advection.
30 | me.xy += dt*(viscosityForce.xy + externalForces) - 0.2*DdX;
31 |
32 | // Vorticity refinement, copied from "Chimera's Breath" by nimitz 2018 (twitter: @stormoid)
33 | // https://www.shadertoy.com/view/4tGfDW
34 | // License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
35 | me.w = (tr.y - tl.y - tu.x + td.x);
36 | vec2 vort = vec2(abs(tu.w) - abs(td.w), abs(tl.w) - abs(tr.w));
37 | vort *= 0.11/length(vort + 1e-9)*me.w;
38 | me.xy += vort;
39 | // end of vorticy refinement
40 |
41 | // stability
42 | fragColor = clamp(me, vec4(-10, -10, 0.5, -10.), vec4(10, 10, 3.0, 10.));
43 | }
44 |
--------------------------------------------------------------------------------
/src/react/ImageEffectRendererComponent.tsx:
--------------------------------------------------------------------------------
1 | import React, {forwardRef, useEffect, useImperativeHandle, useRef} from 'react';
2 | import {
3 | useImageEffectRenderer,
4 | type UseImageEffectRendererOptions
5 | } from './useImageEffectRenderer.js';
6 | import type {RendererInstance} from '../lib/RendererInstance.js';
7 |
8 | export interface ImageEffectRendererComponentProps extends UseImageEffectRendererOptions {
9 | className?: string;
10 | style?: React.CSSProperties;
11 | onReady?: (renderer: RendererInstance) => void;
12 | }
13 |
14 | export interface ImageEffectRendererComponentRef {
15 | renderer: RendererInstance | null;
16 | container: HTMLDivElement | null;
17 | }
18 |
19 | export const ImageEffectRendererComponent = forwardRef<
20 | ImageEffectRendererComponentRef,
21 | ImageEffectRendererComponentProps
22 | >(function ImageEffectRendererComponent(props, ref) {
23 | const {className, style, onReady, ...rendererOptions} = props;
24 |
25 | const {ref: containerRef, renderer, isReady} = useImageEffectRenderer(rendererOptions);
26 | const onReadyCalledForRenderer = useRef(null);
27 |
28 | useImperativeHandle(ref, () => ({
29 | renderer,
30 | container: containerRef.current,
31 | }), [renderer]);
32 |
33 | useEffect(() => {
34 | // Only call onReady once per renderer instance
35 | if (isReady && renderer && onReady && onReadyCalledForRenderer.current !== renderer) {
36 | onReadyCalledForRenderer.current = renderer;
37 | onReady(renderer);
38 | }
39 | }, [isReady, renderer, onReady]);
40 |
41 | return (
42 |
52 | );
53 | });
54 |
--------------------------------------------------------------------------------
/.github/workflows/bump-version-and-publish.yml:
--------------------------------------------------------------------------------
1 | name: Bump version, Tag & Publish
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | version:
7 | description: 'Semver type of new version'
8 | required: true
9 | type: choice
10 | options:
11 | - major
12 | - minor
13 | - patch
14 | - prerelease
15 | prereleaseId:
16 | description: 'Prerelease id'
17 | required: false
18 | type: choice
19 | options:
20 | - alpha
21 | - beta
22 | - rc
23 |
24 | jobs:
25 | bump-version:
26 | runs-on: ubuntu-latest
27 |
28 | steps:
29 | - name: Check out source
30 | uses: actions/checkout@v4
31 |
32 | - name: Setup Node.js
33 | uses: actions/setup-node@v4
34 | with:
35 | node-version: '20'
36 | cache: 'npm'
37 |
38 | - name: Install npm packages
39 | run: npm ci
40 |
41 | - name: Setup Git
42 | run: |
43 | git config user.name github-actions
44 | git config user.email github-actions@github.com
45 |
46 | - name: Bump Version
47 | run: |
48 | npm version ${{ github.event.inputs.version }} --no-git-tag-version --preid ${{ github.event.inputs.prereleaseId }}
49 | git add .
50 | git commit -m "v$(npm pkg get version | tr -d '"')" --no-verify
51 | git tag $(npm pkg get version | tr -d '"') -m "v$(npm pkg get version | tr -d '"')"
52 |
53 | - name: Build & Publish
54 | run: npm run build
55 |
56 | - name: Publish to NPM
57 | run: |
58 | npm run prepare-publish
59 | cd ./dist
60 | npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN}
61 | npm publish
62 | env:
63 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
64 |
65 | - name: Push latest version
66 | run: git push origin main --follow-tags
67 |
--------------------------------------------------------------------------------
/src/lib/RendererBuffer.ts:
--------------------------------------------------------------------------------
1 | import {defaultFrameBufferOptions, FrameBuffer, type FrameBufferOptions} from "./FrameBuffer.js";
2 | import {WebGLInstance} from "./WebGLInstance.js";
3 | import {defaultImageOptions, type ImageOptions, Renderer} from "./Renderer.js";
4 |
5 | export type BufferOptions = ImageOptions & FrameBufferOptions;
6 |
7 | const defaultBufferOptions: BufferOptions = {
8 | ...defaultImageOptions,
9 | ...defaultFrameBufferOptions,
10 | useMipmap: false,
11 | useCache: false,
12 | };
13 |
14 | export class RendererBuffer extends Renderer {
15 | public options: BufferOptions;
16 | // buffers
17 | private readonly frameBuffer0: FrameBuffer;
18 | private readonly frameBuffer1: FrameBuffer;
19 |
20 | constructor(glInstance: WebGLInstance, options: Partial = {}) {
21 | super(glInstance);
22 |
23 | this.options = {...defaultBufferOptions, ...options};
24 |
25 | this.frameBuffer0 = new FrameBuffer(glInstance, this.options);
26 | this.frameBuffer1 = new FrameBuffer(glInstance, this.options);
27 | }
28 |
29 | public get src(): FrameBuffer {
30 | return (this.frame % 2 === 0
31 | ? this.frameBuffer0
32 | : this.frameBuffer1);
33 | }
34 |
35 | public get dest(): FrameBuffer {
36 | return (this.frame % 2 === 1
37 | ? this.frameBuffer0
38 | : this.frameBuffer1);
39 | }
40 |
41 | public override draw(time: number = 0, width: number, height: number): void {
42 | if (width <= 0 || height <= 0) {
43 | return;
44 | }
45 |
46 | const context = this.gl.context;
47 |
48 | const fb = this.dest;
49 |
50 | fb.resize(width, height);
51 | context.bindFramebuffer(context.FRAMEBUFFER, fb.frameBuffer);
52 | context.clear(context.COLOR_BUFFER_BIT);
53 |
54 | super.draw(time, width, height);
55 |
56 | context.bindFramebuffer(context.FRAMEBUFFER, null);
57 | }
58 |
59 | public override destruct() {
60 | super.destruct();
61 |
62 | this.frameBuffer0.destruct();
63 | this.frameBuffer1.destruct();
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/examples/react/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, {useCallback, useRef, useState} from 'react';
2 | import {
3 | ImageEffectRendererComponent,
4 | type ImageEffectRendererComponentRef
5 | } from '../../../src/react';
6 | import glitchShader from './shaders/glitch.glsl?raw';
7 | import './App.css';
8 |
9 | // Load and cache the image once at module level
10 | const paddoImage = new Image();
11 | paddoImage.crossOrigin = 'anonymous';
12 | paddoImage.src = '/paddo.jpg';
13 |
14 | export default function App() {
15 | const rendererRef = useRef(null);
16 | const [isPlaying, setIsPlaying] = useState(true);
17 |
18 | const handleTogglePlay = useCallback(() => {
19 | const renderer = rendererRef.current?.renderer;
20 | if (!renderer) {
21 | return;
22 | }
23 |
24 | if (isPlaying) {
25 | renderer.stop();
26 | setIsPlaying(false);
27 | } else {
28 | renderer.play();
29 | setIsPlaying(true);
30 | }
31 | }, [isPlaying]);
32 |
33 | return (
34 |
35 |
41 |
42 |
43 |
44 |
45 |
54 |
55 |
56 |
57 |
63 |
64 |
65 |
66 |
67 | );
68 | }
69 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@mediamonks/image-effect-renderer",
3 | "version": "3.0.0",
4 | "description": "The image-effect-renderer is a lightweight package for running fragment shaders on websites using WebGL. Apply effects to HTML image or video sources. Zero dependencies.",
5 | "keywords": [
6 | "fragment shader",
7 | "webgl",
8 | "image",
9 | "video",
10 | "effect",
11 | "shadertoy",
12 | "glsl",
13 | "shader",
14 | "oneshader",
15 | "react"
16 | ],
17 | "repository": "git@github.com:mediamonks/image-effect-renderer.git",
18 | "author": "Reinder Nijhoff ",
19 | "license": "MIT",
20 | "type": "module",
21 | "main": "./dist/image-effect-renderer.umd.cjs",
22 | "module": "./dist/image-effect-renderer.js",
23 | "types": "./index.d.ts",
24 | "exports": {
25 | ".": {
26 | "types": "./index.d.ts",
27 | "import": "./dist/image-effect-renderer.js"
28 | },
29 | "./react": {
30 | "types": "./dist/react/index.d.ts",
31 | "import": "./dist/image-effect-renderer-react.js"
32 | }
33 | },
34 | "files": [
35 | "dist/**/*",
36 | "README.md"
37 | ],
38 | "publishConfig": {
39 | "access": "public"
40 | },
41 | "scripts": {
42 | "dev": "vite",
43 | "build": "npm run clean && npm run build:ts",
44 | "build:ts": "vite build",
45 | "clean": "shx rm -rf dist node_modules/.cache/.tsbuildinfo",
46 | "typecheck": "tsc --project tsconfig.json --noEmit --noUnusedLocals",
47 | "prepare-publish": "tsx ./scripts/preparePublish.ts"
48 | },
49 | "devDependencies": {
50 | "@types/react": "^18.3.12",
51 | "@types/shelljs": "^0.8.14",
52 | "react": "^18.3.1",
53 | "shelljs": "^0.8.5",
54 | "shx": "^0.3.4",
55 | "tsx": "^4.6.2",
56 | "typescript": "^5.0.4",
57 | "vite": "^6.0.1",
58 | "vite-plugin-dts": "^4.2.1"
59 | },
60 | "peerDependencies": {
61 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
62 | },
63 | "peerDependenciesMeta": {
64 | "react": {
65 | "optional": true
66 | }
67 | },
68 | "engines": {
69 | "node": ">=16.0.0"
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/examples/vanilla/src/shader/fluid_like.glsl:
--------------------------------------------------------------------------------
1 | #version 300 es
2 | precision highp float;
3 |
4 | #define PI2 6.2831853
5 |
6 | uniform float iTime;
7 | uniform vec2 iResolution;
8 | const float Detail = 2.5;// value= 2.5, min=1., max=5., step=0.1
9 |
10 | in vec2 vScreen;
11 | out vec4 fragColor;
12 |
13 | #define _CameraDist 3.
14 |
15 | #define _Saturation 0.37
16 | #define _Color0 vec3(180./255., 205./255., 245./255.)
17 | #define _Color1 vec3(173./255., 215./255., 252./255.)
18 | #define _Color2 vec3(202./255., 204./255., 235./255.)
19 | #define _NormalStrength 0.75
20 | #define _Frequency 3.5
21 |
22 | #define TemporalFrequency 0.125
23 | #define Falloff 0.525
24 | #define Frequency PI2
25 |
26 | #define m3 mat3(-0.737, 0.456, 0.498, 0, -0.737, 0.675, 0.675, 0.498, 0.544)
27 |
28 | vec3 twistedSineNoise33(vec3 q) {
29 | q.xy *= vec2(1.8, 1.);
30 | float a = 1.;
31 | vec3 sum = vec3(0);
32 | for (int i = 0; i <4; i++){
33 | q = m3 * q;
34 | vec3 s = sin(q.zxy * (1./ a)) * a;
35 | q += s;
36 | sum += s;
37 | a *= Falloff;
38 | }
39 | return sum;
40 | }
41 |
42 | vec3 getBgCol(vec3 p) {
43 | vec3 mn = twistedSineNoise33(p);
44 | vec3 col = mix(mix(mix(_Color0 * _Color0, _Color1 * _Color1, mn.x), _Color2 * _Color2, mn.z), vec3(1), .5 * mn.y);
45 | return max(vec3(0), col);
46 | }
47 |
48 | void main() {
49 | vec2 uv = vScreen;
50 |
51 | vec3 huv = 7. + vec3(uv, iTime * (_Frequency * TemporalFrequency / Detail));
52 | vec3 hduv = vec3(1./iResolution.x, 1./iResolution.y, 0.);
53 |
54 | float hdx = (twistedSineNoise33(huv + hduv.xzz).x - twistedSineNoise33(huv - hduv.xzz).x) * (iResolution.x * .5);
55 | float hdy = (twistedSineNoise33(huv + hduv.zyz).x - twistedSineNoise33(huv - hduv.zyz).x) * (iResolution.x * .5);
56 |
57 | vec3 normal = normalize(vec3(hdx, hdy, _NormalStrength));
58 |
59 | vec3 pos = vec3(uv, _CameraDist);
60 | vec3 rd = normalize(pos);
61 |
62 | vec3 rf = reflect(-rd, normal);
63 | vec3 col = getBgCol(rf - vec3(uv * Detail, iTime * TemporalFrequency));
64 |
65 | vec3 rfr = refract(-rd, normal, 1./1.4);
66 | col += getBgCol(rfr + vec3(uv * Detail, iTime * TemporalFrequency));
67 |
68 | col = sqrt(col * .6);
69 |
70 | fragColor = vec4(col, 1.0);
71 | }
72 |
--------------------------------------------------------------------------------
/src/react/useImageEffectRenderer.ts:
--------------------------------------------------------------------------------
1 | import {useEffect, useRef, useState} from 'react';
2 | import {ImageEffectRenderer, type ImageEffectRendererOptions} from '../lib/ImageEffectRenderer.js';
3 | import type {BufferData, ImagesData, RendererInstance} from '../lib/RendererInstance.js';
4 |
5 | export interface UseImageEffectRendererOptions extends Partial {
6 | shader: string;
7 | autoInit?: boolean;
8 | buffers?: BufferData[];
9 | images?: ImagesData;
10 | }
11 |
12 | export interface UseImageEffectRendererReturn {
13 | ref: React.RefObject;
14 | renderer: RendererInstance | null;
15 | isReady: boolean;
16 | }
17 |
18 | export function useImageEffectRenderer(
19 | options: UseImageEffectRendererOptions
20 | ): UseImageEffectRendererReturn {
21 | const containerRef = useRef(null);
22 | const rendererRef = useRef(null);
23 | const [isReady, setIsReady] = useState(false);
24 |
25 | const {shader, autoInit = true, buffers, images, ...rendererOptions} = options;
26 |
27 | useEffect(() => {
28 | if (!containerRef.current || !autoInit || !shader) {
29 | return;
30 | }
31 |
32 | const renderer = ImageEffectRenderer.createTemporary(
33 | containerRef.current,
34 | shader,
35 | rendererOptions
36 | );
37 |
38 | rendererRef.current = renderer;
39 |
40 | renderer.ready(() => {
41 | setIsReady(true);
42 | });
43 |
44 | return () => {
45 | if (rendererRef.current) {
46 | ImageEffectRenderer.releaseTemporary(rendererRef.current);
47 | rendererRef.current = null;
48 | }
49 | setIsReady(false);
50 | };
51 | }, [shader, autoInit]);
52 |
53 | // Set buffers first (before images, as images may reference buffers)
54 | useEffect(() => {
55 | if (rendererRef.current && buffers) {
56 | rendererRef.current.setBuffersData(buffers);
57 | }
58 | }, [rendererRef.current, buffers]);
59 |
60 | // Set images after buffers are ready
61 | useEffect(() => {
62 | if (rendererRef.current && images) {
63 | rendererRef.current.setImagesData(images);
64 | }
65 | }, [rendererRef.current, images]);
66 |
67 | return {
68 | ref: containerRef,
69 | renderer: rendererRef.current,
70 | isReady,
71 | };
72 | }
73 |
--------------------------------------------------------------------------------
/src/lib/FrameBuffer.ts:
--------------------------------------------------------------------------------
1 | import {WebGLInstance} from "./WebGLInstance.js";
2 |
3 | export type FrameBufferOptions = {
4 | type: number,
5 | pixelRatio: number,
6 | msaa: boolean,
7 | }
8 |
9 | export const defaultFrameBufferOptions: FrameBufferOptions = {
10 | type: 5121, // WebGLRenderingContext.UNSIGNED_BYTE,
11 | pixelRatio: 1,
12 | msaa: false,
13 | };
14 |
15 | export class FrameBuffer {
16 | public width: number = 0;
17 | public height: number = 0;
18 |
19 | public texture: WebGLTexture;
20 | public frameBuffer: WebGLFramebuffer;
21 | public options: FrameBufferOptions;
22 |
23 | private gl: WebGLInstance;
24 | private format: number = WebGLRenderingContext.RGBA;
25 | private internalFormat: number = WebGLRenderingContext.RGBA;
26 |
27 | constructor(gl: WebGLInstance, options: Partial = {}) {
28 | this.gl = gl;
29 | this.options = {
30 | ...defaultFrameBufferOptions,
31 | ...options,
32 | };
33 |
34 | switch (this.options.type) {
35 | case WebGLRenderingContext.UNSIGNED_BYTE:
36 | this.internalFormat = WebGL2RenderingContext.RGBA8;
37 | break;
38 | case WebGLRenderingContext.FLOAT:
39 | this.internalFormat = WebGL2RenderingContext.RGBA32F;
40 | break;
41 | }
42 |
43 | const context = gl.context;
44 |
45 | this.texture = context.createTexture();
46 | this.resize(16, 16);
47 |
48 | this.frameBuffer = context.createFramebuffer();
49 | context.bindFramebuffer(context.FRAMEBUFFER, this.frameBuffer);
50 | context.framebufferTexture2D(context.FRAMEBUFFER, context.COLOR_ATTACHMENT0, context.TEXTURE_2D, this.texture, 0);
51 | context.bindFramebuffer(context.FRAMEBUFFER, null);
52 | }
53 |
54 | public resize(width: number, height: number) {
55 | if (this.width === (width | 0) && this.height === (height | 0)) {
56 | return;
57 | }
58 | this.width = width | 0;
59 | this.height = height | 0;
60 |
61 | const context = this.gl.context;
62 |
63 | // this.gl.activeTexture(GL.TEXTURE0);
64 | context.bindTexture(context.TEXTURE_2D, this.texture);
65 | context.pixelStorei(context.UNPACK_FLIP_Y_WEBGL, 0);
66 |
67 | context.texImage2D(context.TEXTURE_2D, 0, this.internalFormat, this.width, this.height, 0, this.format, this.options.type, null);
68 | }
69 |
70 | public destruct() {
71 | const context = this.gl.context;
72 |
73 | if (this.frameBuffer) {
74 | context.deleteFramebuffer(this.frameBuffer);
75 | }
76 | if (this.texture) {
77 | context.deleteTexture(this.texture);
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/examples/react/README.md:
--------------------------------------------------------------------------------
1 | # Image Effect Renderer - React Example
2 |
3 | This is a TypeScript React example demonstrating how to use the `@mediamonks/image-effect-renderer`
4 | package
5 | with React components and hooks.
6 |
7 | ## Features
8 |
9 | - TypeScript support for type safety
10 | - Demonstrates the ImageEffectRendererComponent usage
11 | - Shows a glitch effect using WebGL fragment shaders
12 | - Declarative image configuration
13 | - Modern React with hooks and functional components
14 |
15 | ## Getting Started
16 |
17 | 1. Install dependencies:
18 |
19 | ```bash
20 | npm install
21 | ```
22 |
23 | 2. Run the development server:
24 |
25 | ```bash
26 | npm run dev
27 | ```
28 |
29 | 3. Open your browser and navigate to the URL shown in the terminal (usually `http://localhost:5173`)
30 |
31 | ## Project Structure
32 |
33 | - `src/App.tsx` - Main application component
34 | - `src/main.tsx` - Application entry point
35 | - `src/shaders/glitch.glsl` - WebGL fragment shader for the glitch effect
36 | - `public/paddo.jpg` - Sample image
37 |
38 | ## Usage
39 |
40 | The example demonstrates how to use the `ImageEffectRendererComponent` with play/pause controls:
41 |
42 | ```tsx
43 | import { useCallback, useRef, useState } from 'react';
44 | import {
45 | ImageEffectRendererComponent,
46 | type ImageEffectRendererComponentRef
47 | } from '@mediamonks/image-effect-renderer/react';
48 | import glitchShader from './shaders/glitch.glsl?raw';
49 |
50 | // Load image at module level
51 | const myImage = new Image();
52 | myImage.crossOrigin = 'anonymous';
53 | myImage.src = '/my-image.jpg';
54 |
55 | function App() {
56 | const rendererRef = useRef(null);
57 | const [isPlaying, setIsPlaying] = useState(true);
58 |
59 | const handleTogglePlay = useCallback(() => {
60 | const renderer = rendererRef.current?.renderer;
61 | if (!renderer) return;
62 |
63 | if (isPlaying) {
64 | renderer.stop();
65 | setIsPlaying(false);
66 | } else {
67 | renderer.play();
68 | setIsPlaying(true);
69 | }
70 | }, [isPlaying]);
71 |
72 | return (
73 | <>
74 |
83 |
86 | >
87 | );
88 | }
89 | ```
90 |
91 | ## Learn More
92 |
93 | - [Image Effect Renderer Documentation](https://github.com/mediamonks/image-effect-renderer)
94 | - [Vite Documentation](https://vitejs.dev/)
95 | - [React Documentation](https://react.dev/)
96 |
--------------------------------------------------------------------------------
/examples/react/src/App.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | }
6 |
7 | html {
8 | background: #0a0a0a;
9 | }
10 |
11 | body {
12 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
13 | background: #0a0a0a;
14 | color: #fff;
15 | overflow-x: hidden;
16 | min-height: 100vh;
17 | }
18 |
19 | .app {
20 | min-height: 100vh;
21 | display: flex;
22 | flex-direction: column;
23 | }
24 |
25 | .header {
26 | padding: 2rem 2rem 1rem;
27 | text-align: center;
28 | background: linear-gradient(180deg, #0a0a0a 0%, transparent 100%);
29 | }
30 |
31 | .header h1 {
32 | font-size: 2.5rem;
33 | font-weight: 700;
34 | margin-bottom: 0.5rem;
35 | background: linear-gradient(135deg, #fff 0%, #888 100%);
36 | -webkit-background-clip: text;
37 | -webkit-text-fill-color: transparent;
38 | background-clip: text;
39 | }
40 |
41 | .header p {
42 | font-size: 1rem;
43 | color: #999;
44 | max-width: 600px;
45 | margin: 0 auto;
46 | }
47 |
48 | .content {
49 | flex: 1;
50 | display: flex;
51 | align-items: center;
52 | justify-content: center;
53 | padding: 2rem;
54 | }
55 |
56 | .renderer-wrapper {
57 | width: 100%;
58 | max-width: 800px;
59 | position: relative;
60 | }
61 |
62 | .renderer-container {
63 | width: 100%;
64 | aspect-ratio: 16 / 9;
65 | max-height: 60vh;
66 | border-radius: 16px;
67 | overflow: hidden;
68 | box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
69 | position: relative;
70 | background: #000;
71 | }
72 |
73 | .renderer-container > * {
74 | width: 100%;
75 | height: 100%;
76 | display: block;
77 | }
78 |
79 | .controls {
80 | margin-top: 1.5rem;
81 | text-align: center;
82 | }
83 |
84 | .btn {
85 | padding: 12px 32px;
86 | font-size: 16px;
87 | font-weight: 600;
88 | border: none;
89 | border-radius: 8px;
90 | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
91 | color: white;
92 | cursor: pointer;
93 | transition: all 0.3s ease;
94 | box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
95 | }
96 |
97 | .btn:hover {
98 | transform: translateY(-2px);
99 | box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
100 | }
101 |
102 | .btn:active {
103 | transform: translateY(0);
104 | }
105 |
106 | .btn:disabled {
107 | opacity: 0.5;
108 | cursor: not-allowed;
109 | transform: none;
110 | }
111 |
112 | @media (max-width: 768px) {
113 | .header h1 {
114 | font-size: 2rem;
115 | }
116 |
117 | .renderer-container {
118 | max-height: 50vh;
119 | }
120 |
121 | .content {
122 | padding: 1rem;
123 | }
124 |
125 | .header {
126 | padding: 1.5rem 1rem 0.5rem;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/examples/vanilla/src/shader/stargate.glsl:
--------------------------------------------------------------------------------
1 | #version 300 es
2 | precision highp float;
3 |
4 | uniform float iTime;
5 | uniform vec2 iResolution;
6 |
7 | in vec2 vScreen;
8 |
9 | out vec4 fragColor;
10 |
11 |
12 | const float _Temporal = 0.25;//value=.25, min=0, max=1, step=0.01
13 | const float _FrequencyY = 2.;//value=2., min=0.1, max=4, step=0.01
14 | const float _SpeedZ = 2.;//value=2., min=0., max=32, step=0.01
15 | const float _RandomSpeed = 6.;//value=6., min=0., max=8, step=0.01
16 | const float _FrequencyZ = 0.01;//value=.01, min=0.0001, max=0.1, step=0.0001
17 |
18 | const float PI2 = 6.2831853;
19 |
20 |
21 | vec3 hash31(float p)
22 | {
23 | vec3 p3 = fract(p * vec3(.1031, .1030, .0973));
24 | p3 += dot(p3, p3.yzx+19.19);
25 | return fract((p3.xxy+p3.yzz)*p3.zyx);
26 | }
27 |
28 | vec2 hash21(float p)
29 | {
30 | vec3 p3 = fract(vec3(p) * vec3(.1031, .1030, .0973));
31 | p3 += dot(p3, p3.yzx + 33.33);
32 | return fract((p3.xx+p3.yz)*p3.zy);
33 |
34 | }
35 |
36 |
37 | vec3 spectrum(in float d)
38 | {
39 | return smoothstep(0.25, 0., abs(d + vec3(0.125, 0., -0.125)));
40 | //return smoothstep(0.25, 0., abs(d + vec3(0.125,0.,-0.125)));
41 | //return sin((vec3(0, 1. ,2) / 3. + d) * PI2) * 0.5 + 0.5;
42 | }
43 |
44 | float aa(float x)
45 | {
46 | float dx = fwidth(x);
47 | return smoothstep(dx, 0., x);
48 | }
49 |
50 | float aaa(float x)
51 | {
52 | float dx = fwidth(x);
53 | return smoothstep(-dx, 0., x) * smoothstep(dx, 0., x);
54 | }
55 |
56 | void main() {
57 | vec3 color = vec3(0.);
58 | float z = iTime * _SpeedZ;
59 | float zOffset = z * _RandomSpeed;
60 | vec2 uv = vScreen * .5;
61 | uv.x = abs(uv.x);
62 | uv.y = abs(uv.y);
63 |
64 | vec3 ray = normalize(vec3(uv, 1.5));
65 | float l = length(ray.xy);
66 | bool isX = abs(ray.x) > abs(ray.y);
67 | vec3 dir = ray / max(abs(ray.x), abs(ray.y));
68 | float r = 0.5;
69 | float offset = 0.;
70 | float hue = sin(iTime) * 0.5 + 0.5;
71 |
72 |
73 | for (int i = 0; i <4; i++){
74 | vec3 hit = dir * r++;
75 | vec3 p = hit;
76 | p.z += z;
77 |
78 | float phase = isX? hit.y : hit.x;
79 | offset += 2.4;
80 | phase += sin(iTime * _Temporal + offset);
81 | phase += sin(phase * 6.) * 0.5;
82 | phase *= _FrequencyY;
83 |
84 | vec3 rand = hash31(floor(phase));
85 |
86 | //z animation
87 | p.z += rand.x * zOffset;
88 | p.z += float(i) * 10.;
89 | //frequency in z
90 | p.z *= _FrequencyZ / (rand.y + 0.05);
91 | p.z += sin(p.z * 10.);
92 | // p.z += cos( p.z * 17.) * 0.5;
93 | vec2 cell = fract(vec2(phase, p.z)) - 0.5;
94 |
95 | float cellID = floor(p.z);
96 | vec2 cellRand = hash21(cellID);
97 | float ax = abs(cell.x);
98 | float ay = abs(cell.y);
99 | float fx = fwidth(ax) * 1.;
100 | float fy = fwidth(ay) * 1.;
101 |
102 | //float d = 0.45;
103 | float d = 0.1 + cellRand.y * 0.3;
104 | float outer = smoothstep(fx, 0., ax - d + fx) * smoothstep(fy, 0., ay - d + fy);
105 | float inner = smoothstep(fx, 0., ax -d + fx * 2.) * smoothstep(fy, 0., ay - d + fy * 2.);
106 |
107 | float outerGlow = smoothstep(0.1, 0., ax - d);
108 | outerGlow *= smoothstep(0.5, 0., ay);
109 | float b = outer - inner;
110 | b += outerGlow * 0.25;
111 | b *= smoothstep(50., 20., hit.z);
112 | b *= 0.5 + rand.z;
113 | color += spectrum((cellRand.x - hue) * 0.25) * b;
114 | }
115 | //color *= 2.;
116 | color = sqrt(color);
117 | color *= 1. - dot(uv, uv);
118 | fragColor = vec4(color, 1.0);
119 | }
120 |
--------------------------------------------------------------------------------
/examples/react/src/shaders/glitch.glsl:
--------------------------------------------------------------------------------
1 | //
2 | // Description : Array and textureless GLSL 2D simplex noise function.
3 | // Author : Ian McEwan, Ashima Arts.
4 | // Maintainer : stegu
5 | // Lastmod : 20110822 (ijm)
6 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved.
7 | // Distributed under the MIT License. See LICENSE file.
8 | // https://github.com/ashima/webgl-noise
9 | // https://github.com/stegu/webgl-noise
10 | //
11 |
12 | vec3 mod289(vec3 x) {
13 | return x - floor(x * (1.0 / 289.0)) * 289.0;
14 | }
15 |
16 | vec2 mod289(vec2 x) {
17 | return x - floor(x * (1.0 / 289.0)) * 289.0;
18 | }
19 |
20 | vec3 permute(vec3 x) {
21 | return mod289(((x*34.0)+1.0)*x);
22 | }
23 |
24 | float snoise(vec2 v)
25 | {
26 | const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
27 | 0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
28 | -0.577350269189626, // -1.0 + 2.0 * C.x
29 | 0.024390243902439);// 1.0 / 41.0
30 | // First corner
31 | vec2 i = floor(v + dot(v, C.yy));
32 | vec2 x0 = v - i + dot(i, C.xx);
33 |
34 | // Other corners
35 | vec2 i1;
36 | //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
37 | //i1.y = 1.0 - i1.x;
38 | i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
39 | // x0 = x0 - 0.0 + 0.0 * C.xx ;
40 | // x1 = x0 - i1 + 1.0 * C.xx ;
41 | // x2 = x0 - 1.0 + 2.0 * C.xx ;
42 | vec4 x12 = x0.xyxy + C.xxzz;
43 | x12.xy -= i1;
44 |
45 | // Permutations
46 | i = mod289(i);// Avoid truncation effects in permutation
47 | vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0))
48 | + i.x + vec3(0.0, i1.x, 1.0));
49 |
50 | vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0);
51 | m = m*m;
52 | m = m*m;
53 |
54 | // Gradients: 41 points uniformly over a line, mapped onto a diamond.
55 | // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
56 |
57 | vec3 x = 2.0 * fract(p * C.www) - 1.0;
58 | vec3 h = abs(x) - 0.5;
59 | vec3 ox = floor(x + 0.5);
60 | vec3 a0 = x - ox;
61 |
62 | // Normalise gradients implicitly by scaling m
63 | // Approximation of: m *= inversesqrt( a0*a0 + h*h );
64 | m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);
65 |
66 | // Compute final noise value at P
67 | vec3 g;
68 | g.x = a0.x * x0.x + h.x * x0.y;
69 | g.yz = a0.yz * x12.xz + h.yz * x12.yw;
70 | return 130.0 * dot(m, g);
71 | }
72 |
73 | float rand(vec2 co)
74 | {
75 | return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);
76 | }
77 |
78 |
79 | void mainImage(out vec4 fragColor, in vec2 fragCoord)
80 | {
81 | vec2 uv = fragCoord.xy / iResolution.xy;
82 | float time = iTime * 2.0;
83 |
84 | // Create large, incidental noise waves
85 | float noise = max(0.0, snoise(vec2(time, uv.y * 0.3)) - 0.3) * (1.0 / 0.7);
86 |
87 | // Offset by smaller, constant noise waves
88 | noise = noise + (snoise(vec2(time*10.0, uv.y * 2.4)) - 0.5) * 0.15;
89 |
90 | // Apply the noise as x displacement for every line
91 | float xpos = uv.x - noise * noise * 0.25;
92 | fragColor = texture(iChannel0, vec2(xpos, uv.y));
93 |
94 | // Mix in some random interference for lines
95 | fragColor.rgb = mix(fragColor.rgb, vec3(rand(vec2(uv.y * time))), noise * 0.3).rgb;
96 |
97 | // Apply a line pattern every 4 pixels
98 | if (floor(mod(fragCoord.y * 0.25, 2.0)) == 0.0)
99 | {
100 | fragColor.rgb *= 1.0 - (0.15 * noise);
101 | }
102 |
103 | // Shift green/blue channels (using the red channel)
104 | fragColor.g = mix(fragColor.r, texture(iChannel0, vec2(xpos + noise * 0.05, uv.y)).g, 0.25);
105 | fragColor.b = mix(fragColor.r, texture(iChannel0, vec2(xpos - noise * 0.05, uv.y)).b, 0.25);
106 | }
107 |
--------------------------------------------------------------------------------
/examples/vanilla/src/shader/glitch.glsl:
--------------------------------------------------------------------------------
1 | //
2 | // Description : Array and textureless GLSL 2D simplex noise function.
3 | // Author : Ian McEwan, Ashima Arts.
4 | // Maintainer : stegu
5 | // Lastmod : 20110822 (ijm)
6 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved.
7 | // Distributed under the MIT License. See LICENSE file.
8 | // https://github.com/ashima/webgl-noise
9 | // https://github.com/stegu/webgl-noise
10 | //
11 |
12 | vec3 mod289(vec3 x) {
13 | return x - floor(x * (1.0 / 289.0)) * 289.0;
14 | }
15 |
16 | vec2 mod289(vec2 x) {
17 | return x - floor(x * (1.0 / 289.0)) * 289.0;
18 | }
19 |
20 | vec3 permute(vec3 x) {
21 | return mod289(((x*34.0)+1.0)*x);
22 | }
23 |
24 | float snoise(vec2 v)
25 | {
26 | const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
27 | 0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
28 | -0.577350269189626, // -1.0 + 2.0 * C.x
29 | 0.024390243902439);// 1.0 / 41.0
30 | // First corner
31 | vec2 i = floor(v + dot(v, C.yy));
32 | vec2 x0 = v - i + dot(i, C.xx);
33 |
34 | // Other corners
35 | vec2 i1;
36 | //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
37 | //i1.y = 1.0 - i1.x;
38 | i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
39 | // x0 = x0 - 0.0 + 0.0 * C.xx ;
40 | // x1 = x0 - i1 + 1.0 * C.xx ;
41 | // x2 = x0 - 1.0 + 2.0 * C.xx ;
42 | vec4 x12 = x0.xyxy + C.xxzz;
43 | x12.xy -= i1;
44 |
45 | // Permutations
46 | i = mod289(i);// Avoid truncation effects in permutation
47 | vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0))
48 | + i.x + vec3(0.0, i1.x, 1.0));
49 |
50 | vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0);
51 | m = m*m;
52 | m = m*m;
53 |
54 | // Gradients: 41 points uniformly over a line, mapped onto a diamond.
55 | // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
56 |
57 | vec3 x = 2.0 * fract(p * C.www) - 1.0;
58 | vec3 h = abs(x) - 0.5;
59 | vec3 ox = floor(x + 0.5);
60 | vec3 a0 = x - ox;
61 |
62 | // Normalise gradients implicitly by scaling m
63 | // Approximation of: m *= inversesqrt( a0*a0 + h*h );
64 | m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);
65 |
66 | // Compute final noise value at P
67 | vec3 g;
68 | g.x = a0.x * x0.x + h.x * x0.y;
69 | g.yz = a0.yz * x12.xz + h.yz * x12.yw;
70 | return 130.0 * dot(m, g);
71 | }
72 |
73 | float rand(vec2 co)
74 | {
75 | return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);
76 | }
77 |
78 |
79 | void mainImage(out vec4 fragColor, in vec2 fragCoord)
80 | {
81 | vec2 uv = fragCoord.xy / iResolution.xy;
82 | float time = iTime * 2.0;
83 |
84 | // Create large, incidental noise waves
85 | float noise = max(0.0, snoise(vec2(time, uv.y * 0.3)) - 0.3) * (1.0 / 0.7);
86 |
87 | // Offset by smaller, constant noise waves
88 | noise = noise + (snoise(vec2(time*10.0, uv.y * 2.4)) - 0.5) * 0.15;
89 |
90 | // Apply the noise as x displacement for every line
91 | float xpos = uv.x - noise * noise * 0.25;
92 | fragColor = texture(iChannel0, vec2(xpos, uv.y));
93 |
94 | // Mix in some random interference for lines
95 | fragColor.rgb = mix(fragColor.rgb, vec3(rand(vec2(uv.y * time))), noise * 0.3).rgb;
96 |
97 | // Apply a line pattern every 4 pixels
98 | if (floor(mod(fragCoord.y * 0.25, 2.0)) == 0.0)
99 | {
100 | fragColor.rgb *= 1.0 - (0.15 * noise);
101 | }
102 |
103 | // Shift green/blue channels (using the red channel)
104 | fragColor.g = mix(fragColor.r, texture(iChannel0, vec2(xpos + noise * 0.05, uv.y)).g, 0.25);
105 | fragColor.b = mix(fragColor.r, texture(iChannel0, vec2(xpos - noise * 0.05, uv.y)).b, 0.25);
106 | }
107 |
--------------------------------------------------------------------------------
/src/lib/ImageEffectRenderer.ts:
--------------------------------------------------------------------------------
1 | import {WebGLInstance} from './WebGLInstance.js';
2 | import {RendererInstance} from "./RendererInstance.js";
3 |
4 | /**
5 | * @typedef {Object} ImageEffectRendererOptions
6 | * @property {boolean} loop - Determines if the renderer should loop. Defaults to false.
7 | * @property {boolean} autoResize - Determines if the renderer should automatically resize. Defaults to true.
8 | * @property {number} pixelRatio - The pixel ratio of the renderer. Defaults to window.devicePixelRatio.
9 | * @property {boolean} useSharedContext - Determines if the renderer should use a shared WebGL context. Defaults to false.
10 | * @property {boolean} asyncCompile - Determines if the renderer should compile shaders asynchronously. Defaults to true.
11 | */
12 | export type ImageEffectRendererOptions = {
13 | loop: boolean;
14 | autoResize: boolean;
15 | pixelRatio: number;
16 | useSharedContext: boolean;
17 | asyncCompile: boolean;
18 | }
19 |
20 | const defaultOptions: ImageEffectRendererOptions = {
21 | loop: false,
22 | autoResize: true,
23 | pixelRatio: typeof window !== 'undefined' ? window.devicePixelRatio : 1,
24 | useSharedContext: false,
25 | asyncCompile: true,
26 | };
27 |
28 | const poolInUse: RendererInstance[] = [];
29 | const poolWebGLInstance: WebGLInstance[] = [];
30 | let sharedInstance: WebGLInstance;
31 | let sharedTime: number = -1;
32 |
33 | export class ImageEffectRenderer {
34 | constructor() {
35 | throw new Error('Use ImageEffectRenderer.createTemporary to create an ImageEffectRenderer');
36 | }
37 |
38 | /**
39 | * Create a temporary ImageEffectRenderer instance for use.
40 | *
41 | * @param container - The HTML element to contain the WebGL canvas.
42 | * @param shader - The shader used for rendering.
43 | * @param options - Custom configuration for renderer creation.
44 | * @returns RendererInstance - The created Renderer instance
45 | */
46 | public static createTemporary(container: HTMLElement, shader: string, options: Partial = {}): RendererInstance {
47 | const instOptions = {...defaultOptions, ...options};
48 |
49 | if (instOptions.useSharedContext) {
50 | if (!sharedInstance) {
51 | sharedInstance = new WebGLInstance();
52 | this.drawInstances(0);
53 | }
54 | const instance = new RendererInstance(sharedInstance, container, shader, instOptions);
55 | poolInUse.push(instance);
56 | return instance;
57 | } else {
58 | const gl = poolWebGLInstance.pop() || new WebGLInstance();
59 | return new RendererInstance(gl, container, shader, instOptions);
60 | }
61 | }
62 |
63 | /**
64 | * Clean up a temporary ImageEffectRenderer instance.
65 | *
66 | * @param ier - RendererInstance to be cleaned up and released.
67 | */
68 | public static releaseTemporary(ier: RendererInstance): void {
69 | if (!ier.options.useSharedContext) {
70 | poolWebGLInstance.push(ier.gl);
71 | }
72 |
73 | ier.stop();
74 | ier.destruct();
75 |
76 | const index = poolInUse.indexOf(ier);
77 | if (index > -1) {
78 | poolInUse.splice(index, 1);
79 | }
80 | }
81 |
82 | private static drawInstances(time: number = 0): void {
83 | window.requestAnimationFrame(time => this.drawInstances(time));
84 |
85 | time /= 1000;
86 |
87 | const dt = sharedTime < 0 ? 1 / 60 : time - sharedTime;
88 | sharedTime = time;
89 |
90 | const canvas = sharedInstance.canvas;
91 | const gl = sharedInstance.context;
92 | const pool = poolInUse;
93 |
94 | let maxWidth = 0, maxHeight = 0;
95 |
96 | pool.forEach(ier => {
97 | ier.update(dt);
98 | });
99 |
100 | pool.forEach(ier => {
101 | if (ier.drawThisFrame) {
102 | maxWidth = Math.max(maxWidth, ier.width);
103 | maxHeight = Math.max(maxHeight, ier.height);
104 | }
105 | });
106 |
107 | if (maxWidth > canvas.width || maxHeight > canvas.height) {
108 | canvas.width = maxWidth;
109 | canvas.height = maxHeight;
110 | }
111 |
112 | gl.clear(gl.COLOR_BUFFER_BIT);
113 |
114 | pool.forEach(ier => {
115 | if (ier.drawThisFrame) {
116 | ier.drawInstance(dt);
117 | ier.copyCanvas();
118 | }
119 | });
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/lib/WebGLInstance.ts:
--------------------------------------------------------------------------------
1 | import type {ImageOptions} from "./Renderer.js";
2 | import Program from "./Program.js";
3 | import Uniform, {
4 | UNIFORM_FLOAT,
5 | UNIFORM_INT,
6 | UNIFORM_MATRIX,
7 | UNIFORM_VEC2,
8 | UNIFORM_VEC3,
9 | UNIFORM_VEC4
10 | } from "./Uniform.js";
11 | import type {Texture} from "./Texture.js.js";
12 |
13 | export class WebGLInstance {
14 | public context: WebGLRenderingContext;
15 | public canvas: HTMLCanvasElement;
16 | public sharedPrograms: { [k: string]: Program } = {};
17 | public sharedTextures: { [k: string]: WebGLTexture } = {};
18 |
19 | private quadVBO: WebGLBuffer;
20 | private lastQuadVBO: WebGLBuffer | undefined = undefined;
21 |
22 | constructor(canvas: HTMLCanvasElement | undefined = undefined) {
23 | this.canvas = canvas || document.createElement('canvas');
24 |
25 | const options = {
26 | premultipliedAlpha: true,
27 | alpha: true,
28 | preserveDrawingBuffer: false,
29 | antialias: false,
30 | depth: false,
31 | stencil: false,
32 | };
33 |
34 | this.context = this.canvas.getContext('webgl2', options);
35 | if (!this.context) {
36 | throw new Error('Unable to create WebGL2 context.');
37 | }
38 |
39 | this.context.getExtension('WEBGL_color_buffer_float');
40 | this.context.getExtension('EXT_color_buffer_float');
41 |
42 | this.context.getExtension('OES_texture_float');
43 | this.context.getExtension('OES_texture_float_linear');
44 |
45 | this.context.getExtension("KHR_parallel_shader_compile");
46 |
47 | this.context.clearColor(0, 0, 0, 0);
48 | this.context.clear(this.context.COLOR_BUFFER_BIT);
49 | this.context.enable(this.context.BLEND);
50 | this.context.blendFunc(this.context.ONE, this.context.ONE_MINUS_SRC_ALPHA);
51 |
52 | this.quadVBO = this.generateQuad();
53 | }
54 |
55 | public drawQuad(posAttributeIndex: number, uvAttributeIndex: number): void {
56 | const gl = this.context;
57 |
58 | if (this.lastQuadVBO !== this.quadVBO) {
59 | this.lastQuadVBO = this.quadVBO;
60 |
61 | // render NDC quad
62 | gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVBO);
63 | gl.enableVertexAttribArray(posAttributeIndex);
64 |
65 | // 4 32-bit values = 4 4-byte values
66 | gl.vertexAttribPointer(posAttributeIndex, 2, gl.FLOAT, false, 4 * 4, 0);
67 |
68 | gl.enableVertexAttribArray(uvAttributeIndex);
69 | gl.vertexAttribPointer(uvAttributeIndex, 2, gl.FLOAT, false, 4 * 4, 2 * 4);
70 | }
71 | gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
72 | }
73 |
74 | public getCachedTexture(src: string, options: ImageOptions): WebGLTexture {
75 | const key = `${src}_${options.clampX}_${options.clampY}_${options.useMipmap}`;
76 | if (this.sharedTextures[src]) {
77 | return this.sharedTextures[key];
78 | }
79 | return this.sharedTextures[key] = this.context.createTexture();
80 | }
81 |
82 | public compileShader(fsSource: string): Program {
83 | if (this.sharedPrograms[fsSource]) {
84 | return this.sharedPrograms[fsSource];
85 | }
86 |
87 | return this.sharedPrograms[fsSource] = new Program(this, fsSource);
88 | }
89 |
90 | public setTextureParameter(texture: WebGLTexture, options: ImageOptions) {
91 | const gl = this.context;
92 |
93 | gl.bindTexture(gl.TEXTURE_2D, texture);
94 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, options.clampX ? gl.CLAMP_TO_EDGE : gl.REPEAT);
95 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, options.clampY ? gl.CLAMP_TO_EDGE : gl.REPEAT);
96 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, options.magFilterLinear ? gl.LINEAR : gl.NEAREST);
97 | if (options.useMipmap) {
98 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
99 | gl.generateMipmap(gl.TEXTURE_2D);
100 | } else {
101 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, options.minFilterLinear ? gl.LINEAR : gl.NEAREST);
102 | }
103 | }
104 |
105 | public bindTextures(textures: Texture[]) {
106 | const context = this.context;
107 | // texture/channel uniforms
108 | for (let slotIndex: number = 0; slotIndex < 8; slotIndex++) {
109 | context.activeTexture(context.TEXTURE0 + slotIndex);
110 | const t = textures[slotIndex];
111 | if (t && t.buffer) {
112 | context.bindTexture(context.TEXTURE_2D, t.buffer.src.texture);
113 | } else if (t && t.texture) {
114 | context.bindTexture(context.TEXTURE_2D, t.texture);
115 | } else {
116 | context.bindTexture(context.TEXTURE_2D, null);
117 | }
118 | }
119 | }
120 |
121 | public setUniforms(uniforms: { [k: string]: Uniform }, program: Program) {
122 | const context = this.context;
123 | Object.values(uniforms).forEach((u) => {
124 | const location = program.getUniformLocation(u.name);
125 | if (location !== null) {
126 | switch (u.type) {
127 | case UNIFORM_INT:
128 | context.uniform1i(location, u.x);
129 | break;
130 | case UNIFORM_FLOAT:
131 | context.uniform1f(location, u.x);
132 | break;
133 | case UNIFORM_VEC2:
134 | context.uniform2f(location, u.x, u.y);
135 | break;
136 | case UNIFORM_VEC3:
137 | context.uniform3f(location, u.x, u.y, u.z);
138 | break;
139 | case UNIFORM_VEC4:
140 | context.uniform4f(location, u.x, u.y, u.z, u.w);
141 | break;
142 | case UNIFORM_MATRIX:
143 | context.uniformMatrix4fv(location, false, u.matrix);
144 | break;
145 | }
146 | }
147 | });
148 | }
149 |
150 | private generateQuad(): WebGLBuffer {
151 | const gl = this.context;
152 | const vertices: Float32Array = new Float32Array([-1, 1, 0, 1, -1, -1, 0, 0, 1, 1, 1, 1, 1, -1, 1, 0]);
153 | const quadVBO = gl.createBuffer();
154 | gl.bindBuffer(gl.ARRAY_BUFFER, quadVBO);
155 | gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
156 | return quadVBO;
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/src/lib/Program.ts:
--------------------------------------------------------------------------------
1 | import type {WebGLInstance} from "./WebGLInstance.js";
2 |
3 | export const PROGRAM_SHADERTOY = 0;
4 | export const PROGRAM_ONESHADER = 2;
5 | export const PROGRAM_ONESHADER_ES300 = 3;
6 |
7 | export type ProgramType =
8 | typeof PROGRAM_SHADERTOY
9 | | typeof PROGRAM_ONESHADER
10 | | typeof PROGRAM_ONESHADER_ES300;
11 |
12 | export default class Program {
13 | private gl: WebGLInstance;
14 | private _program: WebGLProgram;
15 | private vs: WebGLShader;
16 | private fs: WebGLShader;
17 | private initialized: boolean = false;
18 | private ext: KHR_parallel_shader_compile | null;
19 |
20 | private type: ProgramType = PROGRAM_SHADERTOY;
21 |
22 | private vsSource: string = '';
23 | private fsSource: string = '';
24 |
25 | private uniformLocations: { [k: string]: WebGLUniformLocation | null } = {};
26 | private attributeLocations: { [k: string]: number } = {};
27 | private _compiled: boolean = false;
28 |
29 | constructor(gl: WebGLInstance, fsSource: string) {
30 | this.gl = gl;
31 | const context = gl.context;
32 | this.ext = context.getExtension("KHR_parallel_shader_compile");
33 |
34 | this._program = context.createProgram();
35 |
36 | this.vs = context.createShader(context.VERTEX_SHADER);
37 | this.fs = context.createShader(context.FRAGMENT_SHADER);
38 |
39 | this.type = this.detectType(fsSource);
40 |
41 | // vertex shader
42 | this.vsSource = this.getVertexShader(this.type);
43 | context.shaderSource(this.vs, this.vsSource);
44 | context.compileShader(this.vs);
45 |
46 | this.fsSource = `${this.getFragmentShader(this.type)}${fsSource}`;
47 | context.shaderSource(this.fs, this.fsSource);
48 | context.compileShader(this.fs);
49 |
50 | // link shaders
51 | context.attachShader(this._program, this.vs);
52 | context.attachShader(this._program, this.fs);
53 | context.linkProgram(this._program);
54 | }
55 |
56 | public get program(): WebGLProgram | null {
57 | if (this.initialized) {
58 | return this._program;
59 | }
60 |
61 | this.initialized = true;
62 |
63 | const context = this.gl.context;
64 |
65 | let success = context.getShaderParameter(this.vs, context.COMPILE_STATUS);
66 | if (!success) {
67 | console.table(this.vsSource.split('\n'));
68 | throw new Error(`ImageEffectRenderer: Vertex shader compilation failed: ${context.getShaderInfoLog(this.vs)}`);
69 | }
70 |
71 | success = context.getShaderParameter(this.fs, context.COMPILE_STATUS);
72 | if (!success) {
73 | console.table(this.fsSource.split('\n'));
74 | throw new Error(`ImageEffectRenderer: Shader compilation failed: ${context.getShaderInfoLog(this.fs)}`);
75 | }
76 |
77 | success = context.getProgramParameter(this._program, context.LINK_STATUS);
78 | if (!success) {
79 | throw new Error(`ImageEffectRenderer: Program linking failed: ${context.getProgramInfoLog(this._program)}`);
80 | }
81 | return this._program;
82 | }
83 |
84 | public get shaderCompiled(): boolean {
85 | this._compiled = this._compiled || (!this.ext || this.gl.context.getProgramParameter(this._program, this.ext.COMPLETION_STATUS_KHR));
86 | return this._compiled;
87 | }
88 |
89 | public use() {
90 | this.gl.context.useProgram(this.program);
91 | }
92 |
93 | public getUniformLocation(name: string): WebGLUniformLocation | null {
94 | if (this.uniformLocations[name] !== undefined) {
95 | return this.uniformLocations[name] as (WebGLUniformLocation | null);
96 | }
97 | return this.uniformLocations[name] = this.gl.context.getUniformLocation(this._program, name);
98 | }
99 |
100 | public getAttributeLocation(name: string): number {
101 | if (this.attributeLocations[name] !== undefined) {
102 | return this.attributeLocations[name] as number;
103 | }
104 | this.gl.context.useProgram(this.program);
105 | return this.attributeLocations[name] = this.gl.context.getAttribLocation(this._program, name);
106 | }
107 |
108 | private detectType(src: string) {
109 | const res = /mainImage/gmi;
110 | const re2 = /^#version[\s]+300[\s]+es[\s]+/gmi;
111 |
112 | if (res.exec(src)) {
113 | return PROGRAM_SHADERTOY;
114 | } else if (re2.exec(src)) {
115 | return PROGRAM_ONESHADER_ES300;
116 | } else {
117 | return PROGRAM_ONESHADER;
118 | }
119 | }
120 |
121 | private getFragmentShader(type: ProgramType) {
122 | switch (type) {
123 | case PROGRAM_SHADERTOY:
124 | return `#version 300 es
125 | precision highp float;
126 |
127 | ${this.getUniformShader()}
128 |
129 | in vec2 vUV0;
130 | out vec4 outFragColor;
131 |
132 | void mainImage(out vec4, vec2);
133 |
134 | vec4 texture2D(sampler2D tex, vec2 uv) {
135 | return texture(tex, uv);
136 | }
137 |
138 | void main(void) {
139 | outFragColor = vec4(0.0, 0.0, 0.0, 1.0);
140 | mainImage(outFragColor, vUV0 * iResolution.xy);
141 | }
142 | `;
143 | default:
144 | return '';
145 | }
146 | }
147 |
148 | private getVertexShader(type: ProgramType) {
149 | switch (type) {
150 | case PROGRAM_SHADERTOY:
151 | return `#version 300 es
152 | in vec2 aPos;
153 | in vec2 aUV;
154 |
155 | out vec2 vUV0;
156 |
157 | void main(void) {
158 | vUV0 = aUV;
159 | gl_Position = vec4(aPos, 0.0, 1.0);
160 | }
161 | `;
162 | case PROGRAM_ONESHADER:
163 | return `attribute vec3 aPos;
164 | attribute vec2 aUV;
165 |
166 | uniform float iAspect;
167 |
168 | varying vec2 vScreen;
169 | varying vec2 vUV0;
170 |
171 | void main(void) {
172 | vUV0 = aUV;
173 | vScreen = aPos.xy;
174 | vScreen.x *= iAspect;
175 | gl_Position = vec4(aPos, 1.0);
176 | }`;
177 | case PROGRAM_ONESHADER_ES300:
178 | default:
179 | return `#version 300 es
180 | in vec3 aPos;
181 | in vec2 aUV;
182 |
183 | uniform float iAspect;
184 |
185 | out vec2 vScreen;
186 | out vec2 vUV0;
187 |
188 | void main(void) {
189 | vUV0 = aUV;
190 | vScreen = aPos.xy;
191 | vScreen.x *= iAspect;
192 | gl_Position = vec4(aPos, 1.0);
193 | }`;
194 |
195 | }
196 | }
197 |
198 | private getUniformShader(): string {
199 | return `
200 | uniform vec2 iResolution;
201 | uniform float iTime;
202 | uniform float iGlobalTime;
203 | uniform float iAspect;
204 | uniform int iFrame;
205 | uniform vec4 iMouse;
206 |
207 | uniform highp sampler2D iChannel0;
208 | uniform highp sampler2D iChannel1;
209 | uniform highp sampler2D iChannel2;
210 | uniform highp sampler2D iChannel3;
211 | uniform highp sampler2D iChannel4;
212 | uniform highp sampler2D iChannel5;
213 | uniform highp sampler2D iChannel6;
214 | uniform highp sampler2D iChannel7;
215 |
216 | uniform vec2 iChannelResolution0;
217 | uniform vec2 iChannelResolution1;
218 | uniform vec2 iChannelResolution2;
219 | uniform vec2 iChannelResolution3;
220 | uniform vec2 iChannelResolution4;
221 | uniform vec2 iChannelResolution5;
222 | uniform vec2 iChannelResolution6;
223 | uniform vec2 iChannelResolution7;
224 | `;
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/src/lib/Renderer.ts:
--------------------------------------------------------------------------------
1 | import {WebGLInstance} from './WebGLInstance.js';
2 | import Uniform, {
3 | UNIFORM_FLOAT,
4 | UNIFORM_INT,
5 | UNIFORM_MATRIX,
6 | UNIFORM_VEC2,
7 | UNIFORM_VEC3,
8 | UNIFORM_VEC4,
9 | type UniformType
10 | } from "./Uniform.js";
11 | import type Program from "./Program.js";
12 | import type {RendererBuffer} from "./RendererBuffer.js";
13 | import type {Texture} from "./Texture.js.js";
14 | import type {RendererInstance} from "./RendererInstance.js";
15 |
16 | /**
17 | * @typedef {Object} ImageOptions
18 | * @property {boolean} clampX - Determines if the texture's horizontal dimension will be clamped. Defaults to true.
19 | * @property {boolean} clampY - Determines if the texture's vertical dimension will be clamped. Defaults to true.
20 | * @property {boolean} flipY - Inverts the image texture in the y-axis. Defaults to false.
21 | * @property {boolean} useMipmap - Specifies whether to use mipmaps for texture sampling. Defaults to true.
22 | * @property {boolean} useCache - Indicates if the texture should be cached. Defaults to true.
23 | * @property {boolean} minFilterLinear - Determines if the texture's min filter will be linear. Defaults to true.
24 | * @property {boolean} magFilterLinear - Determines if the texture's mag filter will be linear. Defaults to true.
25 | */
26 | export type ImageOptions = {
27 | clampX: boolean,
28 | clampY: boolean,
29 | flipY: boolean,
30 | useMipmap: boolean,
31 | useCache: boolean,
32 | minFilterLinear: boolean,
33 | magFilterLinear: boolean,
34 | }
35 |
36 | export const defaultImageOptions: ImageOptions = {
37 | clampX: true,
38 | clampY: true,
39 | flipY: false,
40 | useMipmap: true,
41 | useCache: true,
42 | minFilterLinear: true,
43 | magFilterLinear: true,
44 | };
45 |
46 | export type ImageSource = TexImageSource | RendererBuffer;
47 |
48 | export class Renderer {
49 | public width: number = 0;
50 | public height: number = 0;
51 | public program!: Program;
52 | public main!: RendererInstance;
53 |
54 | public gl: WebGLInstance;
55 | protected frame: number = 0;
56 | protected mouse: [number, number, number, number] = [0, 0, 0, 0];
57 | private uniforms: { [k: string]: Uniform } = {};
58 | private textures: Texture[] = [];
59 |
60 | constructor(glInstance: WebGLInstance) {
61 | this.gl = glInstance;
62 | }
63 |
64 | public get shaderCompiled(): boolean {
65 | return this.program.shaderCompiled;
66 | }
67 |
68 | public get iMouseUsed(): boolean {
69 | return this.program.getUniformLocation('iMouse') !== null;
70 | }
71 |
72 | /**
73 | * Set an image to a slot for rendering.
74 | * Possible images can be image elements, video elements, canvas elements, or buffers.
75 | *
76 | * @param slotIndex - Index of the slot where to set the image.
77 | * @param image - The image data that you want to use in the shader.
78 | * @param options - Custom configuration for image handling.
79 | */
80 | public setImage(
81 | slotIndex: number,
82 | image: ImageSource,
83 | options: Partial = {},
84 | ): void {
85 | if (slotIndex >= 8) {
86 | throw new Error(
87 | 'ImageEffectRenderer: A maximum of 8 slots is available, slotIndex is out of bounds.',
88 | );
89 | }
90 |
91 | // Check if image/video is ready
92 | if (image instanceof HTMLImageElement) {
93 | if (!image.complete || image.naturalWidth === 0) {
94 | image.addEventListener('load', () => {
95 | this.setImage(slotIndex, image, options);
96 | }, {once: true});
97 | return;
98 | }
99 | } else if (image instanceof HTMLVideoElement) {
100 | if (image.readyState < HTMLMediaElement.HAVE_CURRENT_DATA) {
101 | image.addEventListener('loadeddata', () => {
102 | this.setImage(slotIndex, image, options);
103 | }, {once: true});
104 | return;
105 | }
106 | }
107 |
108 | this.setUniformInt(`iChannel${slotIndex}`, slotIndex);
109 | // get image width and height
110 | let width, height;
111 | if (typeof VideoFrame !== 'undefined' && image instanceof VideoFrame) {
112 | width = image.displayWidth;
113 | height = image.displayHeight;
114 | } else {
115 | width = (image as any).width;
116 | height = (image as any).height;
117 | }
118 |
119 | this.setUniformVec2(`iChannelResolution${slotIndex}`, width, height);
120 |
121 | const context = this.gl.context;
122 | const currentTexture = this.textures[slotIndex];
123 |
124 | if (image instanceof Renderer) {
125 | if (currentTexture && currentTexture.texture && !currentTexture.cached) {
126 | context.deleteTexture(currentTexture.texture);
127 | }
128 | const bufferOptions = {...image.options, ...options};
129 |
130 | this.textures[slotIndex] = {
131 | texture: undefined,
132 | buffer: image,
133 | cached: false,
134 | };
135 |
136 | this.gl.setTextureParameter(image.src.texture, bufferOptions);
137 | this.gl.setTextureParameter(image.dest.texture, bufferOptions);
138 | } else {
139 | const imageOptions = {...defaultImageOptions, ...options};
140 | imageOptions.useCache = imageOptions.useCache && image instanceof HTMLImageElement;
141 |
142 | if (imageOptions.useCache && currentTexture && currentTexture.texture && !currentTexture.cached) {
143 | context.deleteTexture(currentTexture.texture);
144 | currentTexture.texture = undefined;
145 | }
146 | let texture = currentTexture && currentTexture.texture;
147 | if (imageOptions.useCache && image instanceof HTMLImageElement) {
148 | texture = this.gl.getCachedTexture(image.src, imageOptions);
149 | }
150 | if (!texture) {
151 | texture = context.createTexture();
152 | }
153 | this.textures[slotIndex] = {
154 | texture: texture,
155 | buffer: undefined,
156 | cached: imageOptions.useCache,
157 | };
158 | context.bindTexture(context.TEXTURE_2D, texture);
159 | context.pixelStorei(context.UNPACK_FLIP_Y_WEBGL, options.flipY ? 1 : 0);
160 | context.texImage2D(context.TEXTURE_2D, 0, context.RGBA, context.RGBA, context.UNSIGNED_BYTE, image);
161 |
162 | this.gl.setTextureParameter(texture, imageOptions);
163 | }
164 | }
165 |
166 | /**
167 | * Set a float uniform in the shader program.
168 | * @param name - Name of the uniform.
169 | * @param value - Float value.
170 | */
171 | public setUniformFloat(name: string, value: number): void {
172 | this.setUniform(name, UNIFORM_FLOAT, value, 0, 0, 0, undefined);
173 | }
174 |
175 | /**
176 | * Set an integer uniform in the shader program.
177 | * @param name - Name of the uniform.
178 | * @param value - Integer value.
179 | */
180 | public setUniformInt(name: string, value: number): void {
181 | this.setUniform(name, UNIFORM_INT, value, 0, 0, 0, undefined);
182 | }
183 |
184 | /**
185 | * Set a vec2 uniform in the shader program.
186 | * @param name - Name of the uniform.
187 | * @param x - X value.
188 | * @param y - Y value.
189 | */
190 | public setUniformVec2(name: string, x: number, y: number): void {
191 | this.setUniform(name, UNIFORM_VEC2, x, y, 0, 0, undefined);
192 | }
193 |
194 | /**
195 | * Set a vec3 uniform in the shader program.
196 | * @param name - Name of the uniform.
197 | * @param x - X value.
198 | * @param y - Y value.
199 | * @param z - Z value.
200 | */
201 | public setUniformVec3(name: string, x: number, y: number, z: number): void {
202 | this.setUniform(name, UNIFORM_VEC3, x, y, z, 0, undefined);
203 | }
204 |
205 | /**
206 | * Set a vec4 uniform in the shader program.
207 | * @param name - Name of the uniform.
208 | * @param x - X value.
209 | * @param y - Y value.
210 | * @param z - Z value.
211 | * @param w - W value.
212 | */
213 | public setUniformVec4(name: string, x: number, y: number, z: number, w: number): void {
214 | this.setUniform(name, UNIFORM_VEC4, x, y, z, w, undefined);
215 | }
216 |
217 | /**
218 | * Set a matrix uniform in the shader program.
219 | * @param name - Name of the uniform.
220 | * @param matrix - 4X4 matrix.
221 | */
222 | public setUniformMatrix(name: string, matrix: Float32Array): void {
223 | this.setUniform(name, UNIFORM_MATRIX, 0, 0, 0, 0, matrix);
224 | }
225 |
226 | public destruct() {
227 | this.textures.forEach(t => t.texture && !t.cached && this.gl.context.deleteTexture(t.texture));
228 | this.textures = [];
229 | this.uniforms = {};
230 | }
231 |
232 | protected draw(time: number = 0, width: number, height: number): void {
233 | this.width = width | 0;
234 | this.height = height | 0;
235 |
236 | this.program.use();
237 |
238 | this.setUniformFloat('iGlobalTime', time);
239 | this.setUniformFloat('iTime', time);
240 | this.setUniformInt('iFrame', this.frame);
241 | this.setUniformFloat('iAspect', width / height);
242 | this.setUniformVec2('iResolution', width, height);
243 |
244 | const mouse = this.main.mouse;
245 | this.setUniformVec4('iMouse', mouse[0], mouse[1], mouse[2], mouse[3]);
246 |
247 | this.gl.setUniforms(this.uniforms, this.program);
248 | this.gl.bindTextures(this.textures);
249 | this.gl.drawQuad(this.program.getAttributeLocation('aPos'), this.program.getAttributeLocation('aUV'));
250 |
251 | this.frame++;
252 | }
253 |
254 | private setUniform(name: string, type: UniformType, x: number, y: number, z: number, w: number, matrix: Float32Array | undefined) {
255 | let uniform = this.uniforms[name];
256 | if (!uniform) {
257 | uniform = this.uniforms[name] = new Uniform(type, name);
258 | }
259 | uniform.x = x;
260 | uniform.y = y;
261 | uniform.z = z;
262 | uniform.w = w;
263 | uniform.matrix = matrix;
264 | }
265 | }
266 |
--------------------------------------------------------------------------------
/src/lib/RendererInstance.ts:
--------------------------------------------------------------------------------
1 | import {WebGLInstance} from "./WebGLInstance.js";
2 | import type {ImageEffectRendererOptions} from "./ImageEffectRenderer.js";
3 | import {type ImageOptions, type ImageSource, Renderer} from "./Renderer.js";
4 | import {type BufferOptions, RendererBuffer} from "./RendererBuffer.js";
5 | import Program from "./Program.js";
6 | import {bindMouseListener, getMousePosition, getNormalizedMousePosition} from "./MouseListener.js";
7 |
8 | export const BUFFER_0 = 0;
9 | export const BUFFER_1 = 1;
10 | export const BUFFER_2 = 2;
11 | export const BUFFER_3 = 3;
12 | export const BUFFER_4 = 4;
13 | export const BUFFER_5 = 5;
14 | export const BUFFER_6 = 6;
15 | export const BUFFER_7 = 7;
16 |
17 | export type BufferIndex =
18 | typeof BUFFER_0
19 | | typeof BUFFER_1
20 | | typeof BUFFER_2
21 | | typeof BUFFER_3
22 | | typeof BUFFER_4
23 | | typeof BUFFER_5
24 | | typeof BUFFER_6
25 | | typeof BUFFER_7;
26 |
27 | export type ImagesData = {
28 | slotIndex: number,
29 | image: ImageSource & { bufferIndex?: BufferIndex },
30 | options?: Partial,
31 | }[];
32 |
33 | export type BufferData = {
34 | index: BufferIndex,
35 | shader: string,
36 | options?: Partial,
37 | images?: ImagesData,
38 | }
39 |
40 | export type RendererData = {
41 | shader: string,
42 | options?: Partial,
43 | images?: ImagesData,
44 | buffers?: BufferData[],
45 | }
46 |
47 | export class RendererInstance extends Renderer {
48 | public canvas: HTMLCanvasElement;
49 | public buffers: RendererBuffer[] = [];
50 | public options: ImageEffectRendererOptions;
51 | public time: number = 0;
52 |
53 | private tickFuncs: ((dt: number) => void) [] = [];
54 | private readyFuncs: (() => void) [] = [];
55 |
56 | private startTime: number = -1;
57 | private drawOneFrame: boolean = false;
58 | private container: HTMLElement;
59 |
60 | private animationRequestId: number = 0;
61 | private resizeObserver: ResizeObserver;
62 | private _ready: boolean = false;
63 |
64 | constructor(glInstance: WebGLInstance, container: HTMLElement, shader: string, options: ImageEffectRendererOptions) {
65 | super(glInstance);
66 |
67 | this.options = {...options};
68 | this.container = container;
69 | this.main = this;
70 |
71 | if (this.options.useSharedContext) {
72 | this.canvas = document.createElement('canvas');
73 | const context = this.canvas.getContext('2d');
74 | context.fillStyle = '#00000000';
75 | context.clearRect(0, 0, this.canvas.width, this.canvas.height);
76 | } else {
77 | this.canvas = this.gl.canvas;
78 | }
79 | Object.assign(this.canvas.style, {
80 | inset: '0',
81 | width: '100%',
82 | height: '100%',
83 | margin: '0',
84 | display: 'block',
85 | });
86 |
87 | this.container.appendChild(this.canvas);
88 |
89 | this.program = new Program(this.gl, shader);
90 |
91 | this.resizeObserver = new ResizeObserver(() => {
92 | if (this.options.autoResize) {
93 | this.updateSize();
94 | }
95 | });
96 | this.resizeObserver.observe(container);
97 |
98 | if (!this.options.useSharedContext) {
99 | this.drawingLoop(0);
100 | }
101 | }
102 |
103 | public get drawThisFrame(): boolean {
104 | return (this.options.loop || this.drawOneFrame) && this.width > 0 && this.height > 0 && (!this.options.asyncCompile || this.allShadersCompiled);
105 | }
106 |
107 | public override get iMouseUsed(): boolean {
108 | return super.iMouseUsed || this.buffers.some(buffer => buffer && buffer.iMouseUsed);
109 | }
110 |
111 | private get allShadersCompiled(): boolean {
112 | return this.shaderCompiled && this.buffers.every(buffer => buffer && buffer.shaderCompiled);
113 | }
114 |
115 | /**
116 | * Commence or resume the rendering loop.
117 | */
118 | public play(): void {
119 | this.options.loop = true;
120 | }
121 |
122 | /**
123 | * Pause the rendering loop.
124 | */
125 | public stop(): void {
126 | this.options.loop = false;
127 | }
128 |
129 | /**
130 | * Create a new render buffer, replace existing buffer if index is the same.
131 | *
132 | * @param i - The index of the buffer to create/replace.
133 | * @param shader - The shader used for the buffer rendering.
134 | * @param options - Custom configuration for buffer creation.
135 | * @returns Renderer - the newly created or replaced buffer object.
136 | */
137 | public createBuffer(i: number, shader: string, options: Partial = {}): Renderer {
138 | const oldBuffer = this.buffers[i];
139 | if (oldBuffer) {
140 | oldBuffer.destruct();
141 | }
142 | const newBuffer = new RendererBuffer(this.gl, options);
143 | newBuffer.program = this.gl.compileShader(shader);
144 | newBuffer.main = this;
145 | return this.buffers[i] = newBuffer;
146 | }
147 |
148 | /**
149 | * Register a tick function to be called on every frame update.
150 | *
151 | * @param tick - The function to be called.
152 | */
153 | public tick(tick: (dt: number) => void) {
154 | this.tickFuncs.push(tick);
155 | }
156 |
157 | /**
158 | * Register a ready function to be called when the renderer instance is ready.
159 | *
160 | * @param ready - The function to be called.
161 | */
162 | public ready(ready: () => void) {
163 | this.readyFuncs.push(ready);
164 | }
165 |
166 | /**
167 | * Draw a frame manually.
168 | *
169 | * @param time - Time of the frame to draw. Defaults to 0 if not specified.
170 | */
171 | public drawFrame(time: number = 0): void {
172 | this.time = time / 1000;
173 | this.drawOneFrame = true;
174 | }
175 |
176 | /**
177 | * Apply data to the renderer instance, including buffers and images.
178 | * Buffers are created first, then images are set for both the main renderer and buffers.
179 | *
180 | * @param data - Data object containing buffers and images setup.
181 | */
182 | public setData(data: RendererData): void {
183 | // first create buffers
184 | data.buffers && this.setBuffersData(data.buffers);
185 | // then set images
186 | data.images && this.setImagesData(data.images);
187 | }
188 |
189 | /**
190 | * Set multiple images to slots for rendering.
191 | * Possible images can be image elements, video elements, canvas elements, or buffers.
192 | * Images can reference buffers using the bufferIndex property.
193 | *
194 | * @param images - Array of image configurations to set.
195 | * @param target - The renderer to set images on (defaults to this renderer instance).
196 | */
197 | public setImagesData(images: ImagesData, target: Renderer | undefined = this): void {
198 | images.forEach(image => {
199 | if (image.image.bufferIndex !== undefined) {
200 | target?.setImage(image.slotIndex, this.buffers[image.image.bufferIndex] as RendererBuffer, image.options);
201 | } else {
202 | target?.setImage(image.slotIndex, image.image, image.options);
203 | }
204 | });
205 | }
206 |
207 | /**
208 | * Create multiple buffers with their respective shaders and images from buffer data.
209 | * Buffers are created in two passes: first all buffers are initialized,
210 | * then images are assigned to ensure buffer dependencies are available.
211 | *
212 | * @param buffers - Array of buffer data configurations to create.
213 | */
214 | public setBuffersData(buffers: BufferData[]): void {
215 | buffers.forEach(buffer => {
216 | this.createBuffer(buffer.index, buffer.shader, buffer.options);
217 | });
218 | // set images for buffers
219 | buffers.forEach(buffer => {
220 | buffer.images && this.setImagesData(buffer.images, this.buffers[buffer.index]);
221 | });
222 | }
223 |
224 | public drawInstance(dt: number): void {
225 | const context = this.gl.context;
226 |
227 | if (!this.drawOneFrame) {
228 | this.time += dt;
229 | }
230 |
231 | this.tickFuncs.forEach(func => func(dt));
232 |
233 | if (this.iMouseUsed) {
234 | const xprev = this.mouse[0], yprev = this.mouse[1];
235 | const [x, y] = getNormalizedMousePosition(this.container.getBoundingClientRect(), getMousePosition());
236 | this.mouse = [x, y, xprev, yprev];
237 | }
238 |
239 | // update buffers
240 | this.buffers.forEach(buffer => {
241 | if (buffer) {
242 | context.viewport(0, 0, this.width, this.height);
243 | buffer.draw(this.time, this.canvas.width, this.canvas.height);
244 | }
245 | });
246 |
247 | context.viewport(0, 0, this.width, this.height);
248 | context.clear(context.COLOR_BUFFER_BIT);
249 | this.draw(this.time, this.canvas.width, this.canvas.height);
250 |
251 | this.drawOneFrame = false;
252 | }
253 |
254 | public update(dt: number) {
255 | if (this.allShadersCompiled) {
256 | if (!this._ready) {
257 | this._ready = true;
258 | this.readyFuncs.forEach(func => func());
259 | this.readyFuncs = [];
260 |
261 | if (this.iMouseUsed) {
262 | bindMouseListener(document.body);
263 | }
264 | }
265 | }
266 | }
267 |
268 | public override destruct() {
269 | cancelAnimationFrame(this.animationRequestId);
270 |
271 | super.destruct();
272 |
273 | this.resizeObserver.disconnect();
274 | this.container.removeChild(this.canvas);
275 | this.canvas.replaceWith(this.canvas.cloneNode(true));
276 |
277 | this.buffers.forEach(buffer => {
278 | buffer.destruct();
279 | });
280 | this.buffers = [];
281 | this.tickFuncs = [];
282 | }
283 |
284 | public copyCanvas() {
285 | const srcCanvas = this.gl.canvas;
286 | const dstCanvas = this.canvas;
287 | const context = dstCanvas.getContext('2d');
288 | context.clearRect(0, 0, this.width, this.height);
289 | context.drawImage(srcCanvas, 0, srcCanvas.height - this.height, this.width, this.height, 0, 0, this.width, this.height);
290 | }
291 |
292 | private updateSize(): void {
293 | this.width = (this.container.offsetWidth * this.options.pixelRatio) | 0;
294 | this.height = (this.container.offsetHeight * this.options.pixelRatio) | 0;
295 |
296 | if (this.width !== this.canvas.width || this.height !== this.canvas.height) {
297 | this.canvas.width = this.width;
298 | this.canvas.height = this.height;
299 | this.drawOneFrame = true;
300 | }
301 | }
302 |
303 | private drawingLoop(time: number = 0) {
304 | this.animationRequestId = window.requestAnimationFrame(time => this.drawingLoop(time));
305 |
306 | time /= 1000;
307 |
308 | const dt = this.startTime < 0 ? 1 / 60 : time - this.startTime;
309 | this.startTime = time > 0 ? time : -1;
310 |
311 | this.update(dt);
312 |
313 | if (this.drawThisFrame) {
314 | this.drawInstance(dt);
315 | }
316 | }
317 | }
318 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Image Effect Renderer
2 |
3 | The image-effect-renderer is a lightweight package that allows you to run fragment shaders in your
4 | website using WebGL. It can be used to apply effects to HTML images or video sources. Zero
5 | dependencies.
6 |
7 | The ImageEffectRenderer supports the most common variables used
8 | in [Shadertoy](https://www.shadertoy.com/) and the syntax of fragments shaders from Shadertoy
9 | and [OneShader](https://www.oneshader.net/). This makes it easy to prototype different effects using
10 | Shadertoy or OneShader.
11 |
12 | ## Demo
13 |
14 | - [Stress test with multiple demos](https://mediamonks.github.io/image-effect-renderer/).
15 |
16 | This is a build from the repository's example/ directory.
17 |
18 | ## Getting started
19 |
20 | ### Installing
21 |
22 | Add `@mediamonks/image-effect-renderer` to your project:
23 |
24 | ```sh
25 | npm i @mediamonks/image-effect-renderer
26 | ```
27 |
28 | ## Basic usage
29 |
30 | Simple shader rendering on canvas.
31 |
32 | ```ts
33 | import {ImageEffectRenderer} from '@mediamonks/image-effect-renderer';
34 | import shader from './shader.glsl';
35 |
36 | const options = {loop: true};
37 |
38 | // Creating an RendererInstance
39 | const renderer = ImageEffectRenderer.createTemporary(wrapperElement, shader, options);
40 |
41 | ...
42 |
43 | // Clean up the renderer when it's no longer needed
44 | ImageEffectRenderer.releaseTemporary(renderer);
45 | ```
46 |
47 | ### ImageEffectRendererOptions
48 |
49 | You can set the following options when creating a renderer:
50 |
51 | - **loop** _(boolean)_ - The renderer will play automatically if set to true. The default value is
52 | `false`.
53 | - **autoResize** _(boolean)_ - If set to true, the renderer will automatically resize to fit the
54 | wrapperElement. The default value is `true`.
55 | - **pixelRatio** _(number)_ - This sets the pixel ratio of the renderer. The default value is
56 | `window.devicePixelRatio`.
57 | - **useSharedContext** _(boolean)_ - If set to true, the renderer will use a shared WebGL context.
58 | The default value is `false`.
59 | - **asyncCompile** _(boolean)_ - If set to true, the renderer will compile shaders asynchronously.
60 | The default value is `true`.
61 |
62 | ImageEffectRenderers can share one WebGLContext (see `useSharedContext`). This is needed if multiple
63 | ImageEffectRenderers are active at a time.
64 | If you have only one ImageEffectRenderer on a page or if you create a large ImageEffectRenderer (
65 | i.e. fullscreen), the ImageEffectRenderer will run faster if you create it having its own
66 | WebGLContext:
67 |
68 | ## React Usage
69 |
70 | The package includes optional React components and hooks. React is tree-shakeable and only included
71 | when you import from `@mediamonks/image-effect-renderer/react`.
72 |
73 | ### Using the React Component
74 |
75 | ```tsx
76 | import { useRef } from 'react';
77 | import { ImageEffectRendererComponent } from '@mediamonks/image-effect-renderer/react';
78 | import glitchShader from './glitch.glsl?raw';
79 |
80 | function App() {
81 | const rendererRef = useRef(null);
82 |
83 | const handlePlay = () => {
84 | rendererRef.current?.renderer?.play();
85 | };
86 |
87 | return (
88 | <>
89 |
98 |
99 | >
100 | );
101 | }
102 | ```
103 |
104 | ### Using the React Hook
105 |
106 | For more control, you can use the `useImageEffectRenderer` hook:
107 |
108 | ```tsx
109 | import { useEffect } from 'react';
110 | import { useImageEffectRenderer } from '@mediamonks/image-effect-renderer/react';
111 | import shader from './shader.glsl?raw';
112 |
113 | function App() {
114 | const { ref, renderer, isReady } = useImageEffectRenderer({
115 | shader,
116 | loop: true,
117 | images: [{ slotIndex: 0, image: myImage }]
118 | });
119 |
120 | useEffect(() => {
121 | if (renderer && isReady) {
122 | renderer.play();
123 | }
124 | }, [renderer, isReady]);
125 |
126 | return ;
127 | }
128 | ```
129 |
130 | ### React Component Props
131 |
132 | The `ImageEffectRendererComponent` accepts all `ImageEffectRendererOptions` plus:
133 |
134 | - **shader** _(string)_ - The fragment shader code (required)
135 | - **buffers** _(BufferData[])_ - Array of buffer configurations
136 | - **images** _(ImagesData)_ - Array of image configurations
137 | - **className** _(string)_ - CSS class name for the container
138 | - **style** _(React.CSSProperties)_ - Inline styles for the container
139 | - **onReady** _(renderer: RendererInstance) => void_ - Callback when renderer is ready
140 |
141 | ### React Hook Return Value
142 |
143 | The `useImageEffectRenderer` hook returns:
144 |
145 | - **ref** _React.RefObject\_ - Ref to attach to your container element
146 | - **renderer** _RendererInstance | null_ - The RendererInstance
147 | - **isReady** _boolean_ - Whether the renderer is initialized and ready
148 |
149 | ## Methods
150 |
151 | ### setImage(slotIndex, image, options)
152 |
153 | This library allows adding images into up to eight slots, which can be utilized in the shader (as
154 | sampler2D iChannel0 to iChannel7). Ensure images are fully loaded before adding them.
155 |
156 | - `slotIndex` _(number)_ - Index of the slot where the image will be set.
157 | - `image` _(HTMLImageElement | HTMLCanvasElement | HTMLVideoElement | RendererBuffer)_ - The image
158 | data you want to use in the shader.
159 | - `options` _(Partial)_ - Custom configuration for image handling. This is optional.
160 |
161 | ```ts
162 | import {ImageEffectRenderer} from '@mediamonks/image-effect-renderer';
163 | import shader from './shader.glsl';
164 |
165 | const renderer = ImageEffectRenderer.createTemporary(wrapperElement, shader, {loop: false});
166 |
167 | const imageOptions = {};
168 | renderer.setImage(0, image, imageOptions);
169 | renderer.play();
170 | ```
171 |
172 | When setting an image, you can pass an object with the following options:
173 |
174 | - **clampX** _(boolean)_ - If set to true, the texture's horizontal dimension will be clamped. The
175 | default value is `true`.
176 | - **clampY** _(boolean)_ - If set to true, the texture's vertical dimension will be clamped. The
177 | default value is `true`.
178 | - **flipY** _(boolean)_ - If set to true, the image texture will be inverted on the y-axis. The
179 | default value is `false`.
180 | - **useMipmap** _(boolean)_ - If set to true, mipmaps will be used for texture sampling. The default
181 | value is `true`.
182 | - **useCache** _(boolean)_ - If set to true, the texture will be cached. The default value is
183 | `true`.
184 | - **minFilterLinear** _(boolean)_ - If set to true, the texture's min filter will be linear. The
185 | default value is `true`.
186 | - **magFilterLinear** _(boolean)_ - If set to true, the texture's mag filter will be linear. The
187 | default value is `true`.
188 |
189 | ### play()
190 |
191 | Commences or resumes the rendering loop.
192 |
193 | ```ts
194 | renderer.play();
195 | ```
196 |
197 | ### stop()
198 |
199 | Pauses the rendering loop.
200 |
201 | ```ts
202 | renderer.stop();
203 | ```
204 |
205 | ### createBuffer(i, shader, options = {})
206 |
207 | Creates or replaces a render buffer at a specified index.
208 |
209 | - `i` _(number)_ - The index of the buffer to create/replace.
210 | - `shader` _(string)_ - The shader used for the buffer rendering.
211 | - `options` _(Partial)_ - Custom configuration for buffer creation. This is optional.
212 |
213 | #### Returns
214 |
215 | - `Renderer` - The newly created or replaced buffer object.
216 |
217 | ```ts
218 | let newBuffer = renderer.createBuffer(index, shader, options);
219 | ```
220 |
221 | ### tick(tick: (dt) => void)
222 |
223 | Registers a tick function to be called on every frame update.
224 |
225 | - `tick` _(Function)_ - The function to be called. It accepts a single parameter `dt` representing
226 | the delta time.
227 |
228 | ```ts
229 | renderer.tick(dt => {
230 | // Operations to be performed every tick
231 | });
232 | ```
233 |
234 | ### ready(ready: () => void)
235 |
236 | Registers a ready function to be called when the renderer instance is ready.
237 |
238 | - `ready` _(Function)_ - The function to be called.
239 |
240 | ```ts
241 | renderer.ready(() => {
242 | // Operations to be performed when renderer is ready
243 | });
244 | ```
245 |
246 | ### drawFrame(time = 0)
247 |
248 | Draws a frame manually.
249 |
250 | - `time` _(number)_ - Time of the frame to draw. Defaults to 0 if not specified.
251 |
252 | ```ts
253 | renderer.drawFrame(time);
254 | ```
255 |
256 | ### Setting uniforms
257 |
258 | You can set uniforms for each RendererInstance (created by calling
259 | `ImageEffectRenderer.createTemporary` or by creating a buffer using `renderer.createBuffer`).
260 |
261 | #### setUniformFloat(name: string, value: number)
262 |
263 | Sets a float uniform in the shader program.
264 |
265 | - `name` _(string)_ - Name of the uniform.
266 | - `value` _(number)_ - Float value.
267 |
268 | ```ts
269 | renderer.setUniformFloat('uniformName', 0.5);
270 | ```
271 |
272 | #### setUniformInt(name: string, value: number)
273 |
274 | Sets an integer uniform in the shader program.
275 |
276 | - `name` _(string)_ - Name of the uniform.
277 | - `value` _(number)_ - Integer value.
278 |
279 | ```ts
280 | renderer.setUniformInt('uniformName', 4);
281 | ```
282 |
283 | #### setUniformVec2(name: string, x: number, y: number)
284 |
285 | Sets a vec2 uniform in the shader program.
286 |
287 | - `name` _(string)_ - Name of the uniform.
288 | - `x` _(number)_ - X value.
289 | - `y` _(number)_ - Y value.
290 |
291 | ```ts
292 | renderer.setUniformVec2('uniformName', 0.5, 1.5);
293 | ```
294 |
295 | #### setUniformVec3(name: string, x: number, y: number, z: number)
296 |
297 | Sets a vec3 uniform in the shader program.
298 |
299 | - `name` _(string)_ - Name of the uniform.
300 | - `x` _(number)_ - X value.
301 | - `y` _(number)_ - Y value.
302 | - `z` _(number)_ - Z value.
303 |
304 | ```ts
305 | renderer.setUniformVec3('uniformName', 0.5, 1.5, 2.5);
306 | ```
307 |
308 | #### setUniformVec4(name: string, x: number, y: number, z: number, w: number)
309 |
310 | Sets a vec4 uniform in the shader program.
311 |
312 | - `name` _(string)_ - Name of the uniform.
313 | - `x` _(number)_ - X value.
314 | - `y` _(number)_ - Y value.
315 | - `z` _(number)_ - Z value.
316 | - `w` _(number)_ - W value.
317 |
318 | ```ts
319 | renderer.setUniformVec4('uniformName', 0.5, 1.5, 2.5, 3.5);
320 | ```
321 |
322 | #### setUniformMatrix(name: string, matrix: Float32Array)
323 |
324 | Sets a matrix uniform in the shader program.
325 |
326 | - `name` _(string)_ - Name of the uniform.
327 | - `matrix` _(Float32Array)_ - 4X4 matrix.
328 |
329 | ```ts
330 | let matrix = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
331 | renderer.setUniformMatrix('uniformName', matrix);
332 | ```
333 |
334 | ## Multiple buffers
335 |
336 | It is possible to create multiple ping-pong buffers, each functioning with its shader, which
337 | functions analogously to adding extra buffer tabs in Shadertoy.
338 |
339 | ```
340 | renderer.createBuffer(0, shader);
341 | ```
342 |
343 | You can assign a buffer to an image slot:
344 |
345 | ```
346 | renderer.buffers[0].setImage(0, this.renderer.buffers[0]); // ping-pong
347 | // and
348 | renderer.setImage(0, renderer.buffers[0]);
349 | ```
350 |
351 | ### setData(data)
352 |
353 | Sets buffers and images using a declarative configuration object. This is a convenience method that
354 | calls `setBuffersData` and `setImagesData` in the correct order.
355 |
356 | - `data` _(RendererData)_ - Configuration object containing buffers and/or images.
357 |
358 | ```ts
359 | renderer.setData({
360 | buffers: [
361 | {
362 | index: 0,
363 | shader: bufferShader,
364 | options: { clampX: false, clampY: false },
365 | images: [{ slotIndex: 0, image: { bufferIndex: 1 } }]
366 | }
367 | ],
368 | images: [
369 | { slotIndex: 0, image: myImage, options: { flipY: true } }
370 | ]
371 | });
372 | ```
373 |
374 | ### setBuffersData(buffers)
375 |
376 | Creates multiple buffers from an array of buffer configurations. Buffers are created in two passes:
377 | first all buffers are initialized, then images are assigned to ensure buffer dependencies are
378 | available.
379 |
380 | - `buffers` _(BufferData[])_ - Array of buffer configurations.
381 |
382 | ```ts
383 | renderer.setBuffersData([
384 | {
385 | index: 0,
386 | shader: shader0,
387 | options: { clampX: false },
388 | images: [{ slotIndex: 0, image: { bufferIndex: 1 } }]
389 | },
390 | {
391 | index: 1,
392 | shader: shader1,
393 | options: { clampY: false }
394 | }
395 | ]);
396 | ```
397 |
398 | ### setImagesData(images)
399 |
400 | Sets multiple images to slots using an array configuration. Images can reference buffers using the
401 | `bufferIndex` property.
402 |
403 | - `images` _(ImagesData)_ - Array of image configurations.
404 |
405 | ```ts
406 | renderer.setImagesData([
407 | { slotIndex: 0, image: myImage, options: { flipY: true } },
408 | { slotIndex: 1, image: { bufferIndex: 0 } } // Reference buffer 0
409 | ]);
410 | ```
411 |
412 | A buffer will render in the exact resolution as the output canvas. Please take a look at the
413 | examples directory for more examples.
414 |
415 | ## Building
416 |
417 | To build image-effect-renderer, ensure that you have [Git](http://git-scm.com/downloads)
418 | and [Node.js](http://nodejs.org/) installed.
419 |
420 | Clone a copy of the repo:
421 |
422 | ```sh
423 | git clone https://github.com/mediamonks/image-effect-renderer.git
424 | ```
425 |
426 | Change to the image-effect-renderer directory:
427 |
428 | ```sh
429 | cd image-effect-renderer
430 | ```
431 |
432 | Install dev dependencies:
433 |
434 | ```sh
435 | npm i
436 | ```
437 |
438 | Build package:
439 |
440 | ```sh
441 | npm run build
442 | ```
443 |
--------------------------------------------------------------------------------
/examples/vanilla/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "image-effect-renderer examples",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "image-effect-renderer examples",
9 | "version": "1.0.0",
10 | "license": "MIT",
11 | "devDependencies": {
12 | "vite": "^5.2.14"
13 | }
14 | },
15 | "node_modules/@esbuild/aix-ppc64": {
16 | "version": "0.20.2",
17 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
18 | "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
19 | "cpu": [
20 | "ppc64"
21 | ],
22 | "dev": true,
23 | "optional": true,
24 | "os": [
25 | "aix"
26 | ],
27 | "engines": {
28 | "node": ">=12"
29 | }
30 | },
31 | "node_modules/@esbuild/android-arm": {
32 | "version": "0.20.2",
33 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
34 | "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
35 | "cpu": [
36 | "arm"
37 | ],
38 | "dev": true,
39 | "optional": true,
40 | "os": [
41 | "android"
42 | ],
43 | "engines": {
44 | "node": ">=12"
45 | }
46 | },
47 | "node_modules/@esbuild/android-arm64": {
48 | "version": "0.20.2",
49 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
50 | "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
51 | "cpu": [
52 | "arm64"
53 | ],
54 | "dev": true,
55 | "optional": true,
56 | "os": [
57 | "android"
58 | ],
59 | "engines": {
60 | "node": ">=12"
61 | }
62 | },
63 | "node_modules/@esbuild/android-x64": {
64 | "version": "0.20.2",
65 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
66 | "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
67 | "cpu": [
68 | "x64"
69 | ],
70 | "dev": true,
71 | "optional": true,
72 | "os": [
73 | "android"
74 | ],
75 | "engines": {
76 | "node": ">=12"
77 | }
78 | },
79 | "node_modules/@esbuild/darwin-arm64": {
80 | "version": "0.20.2",
81 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
82 | "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
83 | "cpu": [
84 | "arm64"
85 | ],
86 | "dev": true,
87 | "optional": true,
88 | "os": [
89 | "darwin"
90 | ],
91 | "engines": {
92 | "node": ">=12"
93 | }
94 | },
95 | "node_modules/@esbuild/darwin-x64": {
96 | "version": "0.20.2",
97 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
98 | "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
99 | "cpu": [
100 | "x64"
101 | ],
102 | "dev": true,
103 | "optional": true,
104 | "os": [
105 | "darwin"
106 | ],
107 | "engines": {
108 | "node": ">=12"
109 | }
110 | },
111 | "node_modules/@esbuild/freebsd-arm64": {
112 | "version": "0.20.2",
113 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
114 | "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
115 | "cpu": [
116 | "arm64"
117 | ],
118 | "dev": true,
119 | "optional": true,
120 | "os": [
121 | "freebsd"
122 | ],
123 | "engines": {
124 | "node": ">=12"
125 | }
126 | },
127 | "node_modules/@esbuild/freebsd-x64": {
128 | "version": "0.20.2",
129 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
130 | "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
131 | "cpu": [
132 | "x64"
133 | ],
134 | "dev": true,
135 | "optional": true,
136 | "os": [
137 | "freebsd"
138 | ],
139 | "engines": {
140 | "node": ">=12"
141 | }
142 | },
143 | "node_modules/@esbuild/linux-arm": {
144 | "version": "0.20.2",
145 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
146 | "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
147 | "cpu": [
148 | "arm"
149 | ],
150 | "dev": true,
151 | "optional": true,
152 | "os": [
153 | "linux"
154 | ],
155 | "engines": {
156 | "node": ">=12"
157 | }
158 | },
159 | "node_modules/@esbuild/linux-arm64": {
160 | "version": "0.20.2",
161 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
162 | "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
163 | "cpu": [
164 | "arm64"
165 | ],
166 | "dev": true,
167 | "optional": true,
168 | "os": [
169 | "linux"
170 | ],
171 | "engines": {
172 | "node": ">=12"
173 | }
174 | },
175 | "node_modules/@esbuild/linux-ia32": {
176 | "version": "0.20.2",
177 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
178 | "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
179 | "cpu": [
180 | "ia32"
181 | ],
182 | "dev": true,
183 | "optional": true,
184 | "os": [
185 | "linux"
186 | ],
187 | "engines": {
188 | "node": ">=12"
189 | }
190 | },
191 | "node_modules/@esbuild/linux-loong64": {
192 | "version": "0.20.2",
193 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
194 | "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
195 | "cpu": [
196 | "loong64"
197 | ],
198 | "dev": true,
199 | "optional": true,
200 | "os": [
201 | "linux"
202 | ],
203 | "engines": {
204 | "node": ">=12"
205 | }
206 | },
207 | "node_modules/@esbuild/linux-mips64el": {
208 | "version": "0.20.2",
209 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
210 | "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
211 | "cpu": [
212 | "mips64el"
213 | ],
214 | "dev": true,
215 | "optional": true,
216 | "os": [
217 | "linux"
218 | ],
219 | "engines": {
220 | "node": ">=12"
221 | }
222 | },
223 | "node_modules/@esbuild/linux-ppc64": {
224 | "version": "0.20.2",
225 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
226 | "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
227 | "cpu": [
228 | "ppc64"
229 | ],
230 | "dev": true,
231 | "optional": true,
232 | "os": [
233 | "linux"
234 | ],
235 | "engines": {
236 | "node": ">=12"
237 | }
238 | },
239 | "node_modules/@esbuild/linux-riscv64": {
240 | "version": "0.20.2",
241 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
242 | "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
243 | "cpu": [
244 | "riscv64"
245 | ],
246 | "dev": true,
247 | "optional": true,
248 | "os": [
249 | "linux"
250 | ],
251 | "engines": {
252 | "node": ">=12"
253 | }
254 | },
255 | "node_modules/@esbuild/linux-s390x": {
256 | "version": "0.20.2",
257 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
258 | "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
259 | "cpu": [
260 | "s390x"
261 | ],
262 | "dev": true,
263 | "optional": true,
264 | "os": [
265 | "linux"
266 | ],
267 | "engines": {
268 | "node": ">=12"
269 | }
270 | },
271 | "node_modules/@esbuild/linux-x64": {
272 | "version": "0.20.2",
273 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
274 | "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
275 | "cpu": [
276 | "x64"
277 | ],
278 | "dev": true,
279 | "optional": true,
280 | "os": [
281 | "linux"
282 | ],
283 | "engines": {
284 | "node": ">=12"
285 | }
286 | },
287 | "node_modules/@esbuild/netbsd-x64": {
288 | "version": "0.20.2",
289 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
290 | "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
291 | "cpu": [
292 | "x64"
293 | ],
294 | "dev": true,
295 | "optional": true,
296 | "os": [
297 | "netbsd"
298 | ],
299 | "engines": {
300 | "node": ">=12"
301 | }
302 | },
303 | "node_modules/@esbuild/openbsd-x64": {
304 | "version": "0.20.2",
305 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
306 | "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
307 | "cpu": [
308 | "x64"
309 | ],
310 | "dev": true,
311 | "optional": true,
312 | "os": [
313 | "openbsd"
314 | ],
315 | "engines": {
316 | "node": ">=12"
317 | }
318 | },
319 | "node_modules/@esbuild/sunos-x64": {
320 | "version": "0.20.2",
321 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
322 | "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
323 | "cpu": [
324 | "x64"
325 | ],
326 | "dev": true,
327 | "optional": true,
328 | "os": [
329 | "sunos"
330 | ],
331 | "engines": {
332 | "node": ">=12"
333 | }
334 | },
335 | "node_modules/@esbuild/win32-arm64": {
336 | "version": "0.20.2",
337 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
338 | "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
339 | "cpu": [
340 | "arm64"
341 | ],
342 | "dev": true,
343 | "optional": true,
344 | "os": [
345 | "win32"
346 | ],
347 | "engines": {
348 | "node": ">=12"
349 | }
350 | },
351 | "node_modules/@esbuild/win32-ia32": {
352 | "version": "0.20.2",
353 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
354 | "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
355 | "cpu": [
356 | "ia32"
357 | ],
358 | "dev": true,
359 | "optional": true,
360 | "os": [
361 | "win32"
362 | ],
363 | "engines": {
364 | "node": ">=12"
365 | }
366 | },
367 | "node_modules/@esbuild/win32-x64": {
368 | "version": "0.20.2",
369 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
370 | "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
371 | "cpu": [
372 | "x64"
373 | ],
374 | "dev": true,
375 | "optional": true,
376 | "os": [
377 | "win32"
378 | ],
379 | "engines": {
380 | "node": ">=12"
381 | }
382 | },
383 | "node_modules/@jridgewell/gen-mapping": {
384 | "version": "0.3.3",
385 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
386 | "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
387 | "dev": true,
388 | "optional": true,
389 | "peer": true,
390 | "dependencies": {
391 | "@jridgewell/set-array": "^1.0.1",
392 | "@jridgewell/sourcemap-codec": "^1.4.10",
393 | "@jridgewell/trace-mapping": "^0.3.9"
394 | },
395 | "engines": {
396 | "node": ">=6.0.0"
397 | }
398 | },
399 | "node_modules/@jridgewell/resolve-uri": {
400 | "version": "3.1.1",
401 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
402 | "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
403 | "dev": true,
404 | "optional": true,
405 | "peer": true,
406 | "engines": {
407 | "node": ">=6.0.0"
408 | }
409 | },
410 | "node_modules/@jridgewell/set-array": {
411 | "version": "1.1.2",
412 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
413 | "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
414 | "dev": true,
415 | "optional": true,
416 | "peer": true,
417 | "engines": {
418 | "node": ">=6.0.0"
419 | }
420 | },
421 | "node_modules/@jridgewell/source-map": {
422 | "version": "0.3.5",
423 | "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz",
424 | "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==",
425 | "dev": true,
426 | "optional": true,
427 | "peer": true,
428 | "dependencies": {
429 | "@jridgewell/gen-mapping": "^0.3.0",
430 | "@jridgewell/trace-mapping": "^0.3.9"
431 | }
432 | },
433 | "node_modules/@jridgewell/sourcemap-codec": {
434 | "version": "1.4.15",
435 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
436 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
437 | "dev": true,
438 | "optional": true,
439 | "peer": true
440 | },
441 | "node_modules/@jridgewell/trace-mapping": {
442 | "version": "0.3.20",
443 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
444 | "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
445 | "dev": true,
446 | "optional": true,
447 | "peer": true,
448 | "dependencies": {
449 | "@jridgewell/resolve-uri": "^3.1.0",
450 | "@jridgewell/sourcemap-codec": "^1.4.14"
451 | }
452 | },
453 | "node_modules/@rollup/rollup-android-arm-eabi": {
454 | "version": "4.24.3",
455 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.3.tgz",
456 | "integrity": "sha512-ufb2CH2KfBWPJok95frEZZ82LtDl0A6QKTa8MoM+cWwDZvVGl5/jNb79pIhRvAalUu+7LD91VYR0nwRD799HkQ==",
457 | "cpu": [
458 | "arm"
459 | ],
460 | "dev": true,
461 | "optional": true,
462 | "os": [
463 | "android"
464 | ]
465 | },
466 | "node_modules/@rollup/rollup-android-arm64": {
467 | "version": "4.24.3",
468 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.3.tgz",
469 | "integrity": "sha512-iAHpft/eQk9vkWIV5t22V77d90CRofgR2006UiCjHcHJFVI1E0oBkQIAbz+pLtthFw3hWEmVB4ilxGyBf48i2Q==",
470 | "cpu": [
471 | "arm64"
472 | ],
473 | "dev": true,
474 | "optional": true,
475 | "os": [
476 | "android"
477 | ]
478 | },
479 | "node_modules/@rollup/rollup-darwin-arm64": {
480 | "version": "4.24.3",
481 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.3.tgz",
482 | "integrity": "sha512-QPW2YmkWLlvqmOa2OwrfqLJqkHm7kJCIMq9kOz40Zo9Ipi40kf9ONG5Sz76zszrmIZZ4hgRIkez69YnTHgEz1w==",
483 | "cpu": [
484 | "arm64"
485 | ],
486 | "dev": true,
487 | "optional": true,
488 | "os": [
489 | "darwin"
490 | ]
491 | },
492 | "node_modules/@rollup/rollup-darwin-x64": {
493 | "version": "4.24.3",
494 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.3.tgz",
495 | "integrity": "sha512-KO0pN5x3+uZm1ZXeIfDqwcvnQ9UEGN8JX5ufhmgH5Lz4ujjZMAnxQygZAVGemFWn+ZZC0FQopruV4lqmGMshow==",
496 | "cpu": [
497 | "x64"
498 | ],
499 | "dev": true,
500 | "optional": true,
501 | "os": [
502 | "darwin"
503 | ]
504 | },
505 | "node_modules/@rollup/rollup-freebsd-arm64": {
506 | "version": "4.24.3",
507 | "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.3.tgz",
508 | "integrity": "sha512-CsC+ZdIiZCZbBI+aRlWpYJMSWvVssPuWqrDy/zi9YfnatKKSLFCe6fjna1grHuo/nVaHG+kiglpRhyBQYRTK4A==",
509 | "cpu": [
510 | "arm64"
511 | ],
512 | "dev": true,
513 | "optional": true,
514 | "os": [
515 | "freebsd"
516 | ]
517 | },
518 | "node_modules/@rollup/rollup-freebsd-x64": {
519 | "version": "4.24.3",
520 | "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.3.tgz",
521 | "integrity": "sha512-F0nqiLThcfKvRQhZEzMIXOQG4EeX61im61VYL1jo4eBxv4aZRmpin6crnBJQ/nWnCsjH5F6J3W6Stdm0mBNqBg==",
522 | "cpu": [
523 | "x64"
524 | ],
525 | "dev": true,
526 | "optional": true,
527 | "os": [
528 | "freebsd"
529 | ]
530 | },
531 | "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
532 | "version": "4.24.3",
533 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.3.tgz",
534 | "integrity": "sha512-KRSFHyE/RdxQ1CSeOIBVIAxStFC/hnBgVcaiCkQaVC+EYDtTe4X7z5tBkFyRoBgUGtB6Xg6t9t2kulnX6wJc6A==",
535 | "cpu": [
536 | "arm"
537 | ],
538 | "dev": true,
539 | "optional": true,
540 | "os": [
541 | "linux"
542 | ]
543 | },
544 | "node_modules/@rollup/rollup-linux-arm-musleabihf": {
545 | "version": "4.24.3",
546 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.3.tgz",
547 | "integrity": "sha512-h6Q8MT+e05zP5BxEKz0vi0DhthLdrNEnspdLzkoFqGwnmOzakEHSlXfVyA4HJ322QtFy7biUAVFPvIDEDQa6rw==",
548 | "cpu": [
549 | "arm"
550 | ],
551 | "dev": true,
552 | "optional": true,
553 | "os": [
554 | "linux"
555 | ]
556 | },
557 | "node_modules/@rollup/rollup-linux-arm64-gnu": {
558 | "version": "4.24.3",
559 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.3.tgz",
560 | "integrity": "sha512-fKElSyXhXIJ9pqiYRqisfirIo2Z5pTTve5K438URf08fsypXrEkVmShkSfM8GJ1aUyvjakT+fn2W7Czlpd/0FQ==",
561 | "cpu": [
562 | "arm64"
563 | ],
564 | "dev": true,
565 | "optional": true,
566 | "os": [
567 | "linux"
568 | ]
569 | },
570 | "node_modules/@rollup/rollup-linux-arm64-musl": {
571 | "version": "4.24.3",
572 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.3.tgz",
573 | "integrity": "sha512-YlddZSUk8G0px9/+V9PVilVDC6ydMz7WquxozToozSnfFK6wa6ne1ATUjUvjin09jp34p84milxlY5ikueoenw==",
574 | "cpu": [
575 | "arm64"
576 | ],
577 | "dev": true,
578 | "optional": true,
579 | "os": [
580 | "linux"
581 | ]
582 | },
583 | "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
584 | "version": "4.24.3",
585 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.3.tgz",
586 | "integrity": "sha512-yNaWw+GAO8JjVx3s3cMeG5Esz1cKVzz8PkTJSfYzE5u7A+NvGmbVFEHP+BikTIyYWuz0+DX9kaA3pH9Sqxp69g==",
587 | "cpu": [
588 | "ppc64"
589 | ],
590 | "dev": true,
591 | "optional": true,
592 | "os": [
593 | "linux"
594 | ]
595 | },
596 | "node_modules/@rollup/rollup-linux-riscv64-gnu": {
597 | "version": "4.24.3",
598 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.3.tgz",
599 | "integrity": "sha512-lWKNQfsbpv14ZCtM/HkjCTm4oWTKTfxPmr7iPfp3AHSqyoTz5AgLemYkWLwOBWc+XxBbrU9SCokZP0WlBZM9lA==",
600 | "cpu": [
601 | "riscv64"
602 | ],
603 | "dev": true,
604 | "optional": true,
605 | "os": [
606 | "linux"
607 | ]
608 | },
609 | "node_modules/@rollup/rollup-linux-s390x-gnu": {
610 | "version": "4.24.3",
611 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.3.tgz",
612 | "integrity": "sha512-HoojGXTC2CgCcq0Woc/dn12wQUlkNyfH0I1ABK4Ni9YXyFQa86Fkt2Q0nqgLfbhkyfQ6003i3qQk9pLh/SpAYw==",
613 | "cpu": [
614 | "s390x"
615 | ],
616 | "dev": true,
617 | "optional": true,
618 | "os": [
619 | "linux"
620 | ]
621 | },
622 | "node_modules/@rollup/rollup-linux-x64-gnu": {
623 | "version": "4.24.3",
624 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.3.tgz",
625 | "integrity": "sha512-mnEOh4iE4USSccBOtcrjF5nj+5/zm6NcNhbSEfR3Ot0pxBwvEn5QVUXcuOwwPkapDtGZ6pT02xLoPaNv06w7KQ==",
626 | "cpu": [
627 | "x64"
628 | ],
629 | "dev": true,
630 | "optional": true,
631 | "os": [
632 | "linux"
633 | ]
634 | },
635 | "node_modules/@rollup/rollup-linux-x64-musl": {
636 | "version": "4.24.3",
637 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.3.tgz",
638 | "integrity": "sha512-rMTzawBPimBQkG9NKpNHvquIUTQPzrnPxPbCY1Xt+mFkW7pshvyIS5kYgcf74goxXOQk0CP3EoOC1zcEezKXhw==",
639 | "cpu": [
640 | "x64"
641 | ],
642 | "dev": true,
643 | "optional": true,
644 | "os": [
645 | "linux"
646 | ]
647 | },
648 | "node_modules/@rollup/rollup-win32-arm64-msvc": {
649 | "version": "4.24.3",
650 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.3.tgz",
651 | "integrity": "sha512-2lg1CE305xNvnH3SyiKwPVsTVLCg4TmNCF1z7PSHX2uZY2VbUpdkgAllVoISD7JO7zu+YynpWNSKAtOrX3AiuA==",
652 | "cpu": [
653 | "arm64"
654 | ],
655 | "dev": true,
656 | "optional": true,
657 | "os": [
658 | "win32"
659 | ]
660 | },
661 | "node_modules/@rollup/rollup-win32-ia32-msvc": {
662 | "version": "4.24.3",
663 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.3.tgz",
664 | "integrity": "sha512-9SjYp1sPyxJsPWuhOCX6F4jUMXGbVVd5obVpoVEi8ClZqo52ViZewA6eFz85y8ezuOA+uJMP5A5zo6Oz4S5rVQ==",
665 | "cpu": [
666 | "ia32"
667 | ],
668 | "dev": true,
669 | "optional": true,
670 | "os": [
671 | "win32"
672 | ]
673 | },
674 | "node_modules/@rollup/rollup-win32-x64-msvc": {
675 | "version": "4.24.3",
676 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.3.tgz",
677 | "integrity": "sha512-HGZgRFFYrMrP3TJlq58nR1xy8zHKId25vhmm5S9jETEfDf6xybPxsavFTJaufe2zgOGYJBskGlj49CwtEuFhWQ==",
678 | "cpu": [
679 | "x64"
680 | ],
681 | "dev": true,
682 | "optional": true,
683 | "os": [
684 | "win32"
685 | ]
686 | },
687 | "node_modules/@types/estree": {
688 | "version": "1.0.6",
689 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
690 | "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
691 | "dev": true
692 | },
693 | "node_modules/@types/node": {
694 | "version": "20.8.9",
695 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.9.tgz",
696 | "integrity": "sha512-UzykFsT3FhHb1h7yD4CA4YhBHq545JC0YnEz41xkipN88eKQtL6rSgocL5tbAP6Ola9Izm/Aw4Ora8He4x0BHg==",
697 | "dev": true,
698 | "optional": true,
699 | "peer": true,
700 | "dependencies": {
701 | "undici-types": "~5.26.4"
702 | }
703 | },
704 | "node_modules/acorn": {
705 | "version": "8.10.0",
706 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
707 | "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
708 | "dev": true,
709 | "optional": true,
710 | "peer": true,
711 | "bin": {
712 | "acorn": "bin/acorn"
713 | },
714 | "engines": {
715 | "node": ">=0.4.0"
716 | }
717 | },
718 | "node_modules/buffer-from": {
719 | "version": "1.1.2",
720 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
721 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
722 | "dev": true,
723 | "optional": true,
724 | "peer": true
725 | },
726 | "node_modules/commander": {
727 | "version": "2.20.3",
728 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
729 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
730 | "dev": true,
731 | "optional": true,
732 | "peer": true
733 | },
734 | "node_modules/esbuild": {
735 | "version": "0.20.2",
736 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
737 | "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
738 | "dev": true,
739 | "hasInstallScript": true,
740 | "bin": {
741 | "esbuild": "bin/esbuild"
742 | },
743 | "engines": {
744 | "node": ">=12"
745 | },
746 | "optionalDependencies": {
747 | "@esbuild/aix-ppc64": "0.20.2",
748 | "@esbuild/android-arm": "0.20.2",
749 | "@esbuild/android-arm64": "0.20.2",
750 | "@esbuild/android-x64": "0.20.2",
751 | "@esbuild/darwin-arm64": "0.20.2",
752 | "@esbuild/darwin-x64": "0.20.2",
753 | "@esbuild/freebsd-arm64": "0.20.2",
754 | "@esbuild/freebsd-x64": "0.20.2",
755 | "@esbuild/linux-arm": "0.20.2",
756 | "@esbuild/linux-arm64": "0.20.2",
757 | "@esbuild/linux-ia32": "0.20.2",
758 | "@esbuild/linux-loong64": "0.20.2",
759 | "@esbuild/linux-mips64el": "0.20.2",
760 | "@esbuild/linux-ppc64": "0.20.2",
761 | "@esbuild/linux-riscv64": "0.20.2",
762 | "@esbuild/linux-s390x": "0.20.2",
763 | "@esbuild/linux-x64": "0.20.2",
764 | "@esbuild/netbsd-x64": "0.20.2",
765 | "@esbuild/openbsd-x64": "0.20.2",
766 | "@esbuild/sunos-x64": "0.20.2",
767 | "@esbuild/win32-arm64": "0.20.2",
768 | "@esbuild/win32-ia32": "0.20.2",
769 | "@esbuild/win32-x64": "0.20.2"
770 | }
771 | },
772 | "node_modules/fsevents": {
773 | "version": "2.3.3",
774 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
775 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
776 | "dev": true,
777 | "hasInstallScript": true,
778 | "optional": true,
779 | "os": [
780 | "darwin"
781 | ],
782 | "engines": {
783 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
784 | }
785 | },
786 | "node_modules/nanoid": {
787 | "version": "3.3.7",
788 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
789 | "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
790 | "dev": true,
791 | "funding": [
792 | {
793 | "type": "github",
794 | "url": "https://github.com/sponsors/ai"
795 | }
796 | ],
797 | "bin": {
798 | "nanoid": "bin/nanoid.cjs"
799 | },
800 | "engines": {
801 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
802 | }
803 | },
804 | "node_modules/picocolors": {
805 | "version": "1.0.0",
806 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
807 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
808 | "dev": true
809 | },
810 | "node_modules/postcss": {
811 | "version": "8.4.38",
812 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
813 | "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
814 | "dev": true,
815 | "funding": [
816 | {
817 | "type": "opencollective",
818 | "url": "https://opencollective.com/postcss/"
819 | },
820 | {
821 | "type": "tidelift",
822 | "url": "https://tidelift.com/funding/github/npm/postcss"
823 | },
824 | {
825 | "type": "github",
826 | "url": "https://github.com/sponsors/ai"
827 | }
828 | ],
829 | "dependencies": {
830 | "nanoid": "^3.3.7",
831 | "picocolors": "^1.0.0",
832 | "source-map-js": "^1.2.0"
833 | },
834 | "engines": {
835 | "node": "^10 || ^12 || >=14"
836 | }
837 | },
838 | "node_modules/rollup": {
839 | "version": "4.24.3",
840 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.3.tgz",
841 | "integrity": "sha512-HBW896xR5HGmoksbi3JBDtmVzWiPAYqp7wip50hjQ67JbDz61nyoMPdqu1DvVW9asYb2M65Z20ZHsyJCMqMyDg==",
842 | "dev": true,
843 | "dependencies": {
844 | "@types/estree": "1.0.6"
845 | },
846 | "bin": {
847 | "rollup": "dist/bin/rollup"
848 | },
849 | "engines": {
850 | "node": ">=18.0.0",
851 | "npm": ">=8.0.0"
852 | },
853 | "optionalDependencies": {
854 | "@rollup/rollup-android-arm-eabi": "4.24.3",
855 | "@rollup/rollup-android-arm64": "4.24.3",
856 | "@rollup/rollup-darwin-arm64": "4.24.3",
857 | "@rollup/rollup-darwin-x64": "4.24.3",
858 | "@rollup/rollup-freebsd-arm64": "4.24.3",
859 | "@rollup/rollup-freebsd-x64": "4.24.3",
860 | "@rollup/rollup-linux-arm-gnueabihf": "4.24.3",
861 | "@rollup/rollup-linux-arm-musleabihf": "4.24.3",
862 | "@rollup/rollup-linux-arm64-gnu": "4.24.3",
863 | "@rollup/rollup-linux-arm64-musl": "4.24.3",
864 | "@rollup/rollup-linux-powerpc64le-gnu": "4.24.3",
865 | "@rollup/rollup-linux-riscv64-gnu": "4.24.3",
866 | "@rollup/rollup-linux-s390x-gnu": "4.24.3",
867 | "@rollup/rollup-linux-x64-gnu": "4.24.3",
868 | "@rollup/rollup-linux-x64-musl": "4.24.3",
869 | "@rollup/rollup-win32-arm64-msvc": "4.24.3",
870 | "@rollup/rollup-win32-ia32-msvc": "4.24.3",
871 | "@rollup/rollup-win32-x64-msvc": "4.24.3",
872 | "fsevents": "~2.3.2"
873 | }
874 | },
875 | "node_modules/source-map": {
876 | "version": "0.6.1",
877 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
878 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
879 | "dev": true,
880 | "optional": true,
881 | "peer": true,
882 | "engines": {
883 | "node": ">=0.10.0"
884 | }
885 | },
886 | "node_modules/source-map-js": {
887 | "version": "1.2.0",
888 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
889 | "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
890 | "dev": true,
891 | "engines": {
892 | "node": ">=0.10.0"
893 | }
894 | },
895 | "node_modules/source-map-support": {
896 | "version": "0.5.21",
897 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
898 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
899 | "dev": true,
900 | "optional": true,
901 | "peer": true,
902 | "dependencies": {
903 | "buffer-from": "^1.0.0",
904 | "source-map": "^0.6.0"
905 | }
906 | },
907 | "node_modules/terser": {
908 | "version": "5.22.0",
909 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.22.0.tgz",
910 | "integrity": "sha512-hHZVLgRA2z4NWcN6aS5rQDc+7Dcy58HOf2zbYwmFcQ+ua3h6eEFf5lIDKTzbWwlazPyOZsFQO8V80/IjVNExEw==",
911 | "dev": true,
912 | "optional": true,
913 | "peer": true,
914 | "dependencies": {
915 | "@jridgewell/source-map": "^0.3.3",
916 | "acorn": "^8.8.2",
917 | "commander": "^2.20.0",
918 | "source-map-support": "~0.5.20"
919 | },
920 | "bin": {
921 | "terser": "bin/terser"
922 | },
923 | "engines": {
924 | "node": ">=10"
925 | }
926 | },
927 | "node_modules/undici-types": {
928 | "version": "5.26.5",
929 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
930 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
931 | "dev": true,
932 | "optional": true,
933 | "peer": true
934 | },
935 | "node_modules/vite": {
936 | "version": "5.2.14",
937 | "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.14.tgz",
938 | "integrity": "sha512-TFQLuwWLPms+NBNlh0D9LZQ+HXW471COABxw/9TEUBrjuHMo9BrYBPrN/SYAwIuVL+rLerycxiLT41t4f5MZpA==",
939 | "dev": true,
940 | "dependencies": {
941 | "esbuild": "^0.20.1",
942 | "postcss": "^8.4.38",
943 | "rollup": "^4.13.0"
944 | },
945 | "bin": {
946 | "vite": "bin/vite.js"
947 | },
948 | "engines": {
949 | "node": "^18.0.0 || >=20.0.0"
950 | },
951 | "funding": {
952 | "url": "https://github.com/vitejs/vite?sponsor=1"
953 | },
954 | "optionalDependencies": {
955 | "fsevents": "~2.3.3"
956 | },
957 | "peerDependencies": {
958 | "@types/node": "^18.0.0 || >=20.0.0",
959 | "less": "*",
960 | "lightningcss": "^1.21.0",
961 | "sass": "*",
962 | "stylus": "*",
963 | "sugarss": "*",
964 | "terser": "^5.4.0"
965 | },
966 | "peerDependenciesMeta": {
967 | "@types/node": {
968 | "optional": true
969 | },
970 | "less": {
971 | "optional": true
972 | },
973 | "lightningcss": {
974 | "optional": true
975 | },
976 | "sass": {
977 | "optional": true
978 | },
979 | "stylus": {
980 | "optional": true
981 | },
982 | "sugarss": {
983 | "optional": true
984 | },
985 | "terser": {
986 | "optional": true
987 | }
988 | }
989 | }
990 | }
991 | }
992 |
--------------------------------------------------------------------------------
/examples/react/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "image-effect-renderer-react-example",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "image-effect-renderer-react-example",
9 | "version": "1.0.0",
10 | "dependencies": {
11 | "@mediamonks/image-effect-renderer": "file:../..",
12 | "react": "^18.3.1",
13 | "react-dom": "^18.3.1"
14 | },
15 | "devDependencies": {
16 | "@types/react": "^18.3.12",
17 | "@types/react-dom": "^18.3.1",
18 | "@vitejs/plugin-react": "^4.3.4",
19 | "typescript": "^5.7.2",
20 | "vite": "^6.0.1"
21 | }
22 | },
23 | "../..": {
24 | "name": "@mediamonks/image-effect-renderer",
25 | "version": "2.4.1",
26 | "license": "MIT",
27 | "devDependencies": {
28 | "@types/react": "^18.3.12",
29 | "@types/shelljs": "^0.8.14",
30 | "react": "^18.3.1",
31 | "shelljs": "^0.8.5",
32 | "shx": "^0.3.4",
33 | "tsx": "^4.6.2",
34 | "typescript": "^5.0.4",
35 | "vite": "^6.0.1",
36 | "vite-plugin-dts": "^4.2.1"
37 | },
38 | "engines": {
39 | "node": ">=16.0.0"
40 | },
41 | "peerDependencies": {
42 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
43 | },
44 | "peerDependenciesMeta": {
45 | "react": {
46 | "optional": true
47 | }
48 | }
49 | },
50 | "node_modules/@babel/code-frame": {
51 | "version": "7.27.1",
52 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
53 | "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
54 | "dev": true,
55 | "license": "MIT",
56 | "dependencies": {
57 | "@babel/helper-validator-identifier": "^7.27.1",
58 | "js-tokens": "^4.0.0",
59 | "picocolors": "^1.1.1"
60 | },
61 | "engines": {
62 | "node": ">=6.9.0"
63 | }
64 | },
65 | "node_modules/@babel/compat-data": {
66 | "version": "7.28.5",
67 | "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
68 | "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
69 | "dev": true,
70 | "license": "MIT",
71 | "engines": {
72 | "node": ">=6.9.0"
73 | }
74 | },
75 | "node_modules/@babel/core": {
76 | "version": "7.28.5",
77 | "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
78 | "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
79 | "dev": true,
80 | "license": "MIT",
81 | "dependencies": {
82 | "@babel/code-frame": "^7.27.1",
83 | "@babel/generator": "^7.28.5",
84 | "@babel/helper-compilation-targets": "^7.27.2",
85 | "@babel/helper-module-transforms": "^7.28.3",
86 | "@babel/helpers": "^7.28.4",
87 | "@babel/parser": "^7.28.5",
88 | "@babel/template": "^7.27.2",
89 | "@babel/traverse": "^7.28.5",
90 | "@babel/types": "^7.28.5",
91 | "@jridgewell/remapping": "^2.3.5",
92 | "convert-source-map": "^2.0.0",
93 | "debug": "^4.1.0",
94 | "gensync": "^1.0.0-beta.2",
95 | "json5": "^2.2.3",
96 | "semver": "^6.3.1"
97 | },
98 | "engines": {
99 | "node": ">=6.9.0"
100 | },
101 | "funding": {
102 | "type": "opencollective",
103 | "url": "https://opencollective.com/babel"
104 | }
105 | },
106 | "node_modules/@babel/generator": {
107 | "version": "7.28.5",
108 | "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
109 | "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
110 | "dev": true,
111 | "license": "MIT",
112 | "dependencies": {
113 | "@babel/parser": "^7.28.5",
114 | "@babel/types": "^7.28.5",
115 | "@jridgewell/gen-mapping": "^0.3.12",
116 | "@jridgewell/trace-mapping": "^0.3.28",
117 | "jsesc": "^3.0.2"
118 | },
119 | "engines": {
120 | "node": ">=6.9.0"
121 | }
122 | },
123 | "node_modules/@babel/helper-compilation-targets": {
124 | "version": "7.27.2",
125 | "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
126 | "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
127 | "dev": true,
128 | "license": "MIT",
129 | "dependencies": {
130 | "@babel/compat-data": "^7.27.2",
131 | "@babel/helper-validator-option": "^7.27.1",
132 | "browserslist": "^4.24.0",
133 | "lru-cache": "^5.1.1",
134 | "semver": "^6.3.1"
135 | },
136 | "engines": {
137 | "node": ">=6.9.0"
138 | }
139 | },
140 | "node_modules/@babel/helper-globals": {
141 | "version": "7.28.0",
142 | "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
143 | "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
144 | "dev": true,
145 | "license": "MIT",
146 | "engines": {
147 | "node": ">=6.9.0"
148 | }
149 | },
150 | "node_modules/@babel/helper-module-imports": {
151 | "version": "7.27.1",
152 | "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
153 | "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
154 | "dev": true,
155 | "license": "MIT",
156 | "dependencies": {
157 | "@babel/traverse": "^7.27.1",
158 | "@babel/types": "^7.27.1"
159 | },
160 | "engines": {
161 | "node": ">=6.9.0"
162 | }
163 | },
164 | "node_modules/@babel/helper-module-transforms": {
165 | "version": "7.28.3",
166 | "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
167 | "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
168 | "dev": true,
169 | "license": "MIT",
170 | "dependencies": {
171 | "@babel/helper-module-imports": "^7.27.1",
172 | "@babel/helper-validator-identifier": "^7.27.1",
173 | "@babel/traverse": "^7.28.3"
174 | },
175 | "engines": {
176 | "node": ">=6.9.0"
177 | },
178 | "peerDependencies": {
179 | "@babel/core": "^7.0.0"
180 | }
181 | },
182 | "node_modules/@babel/helper-plugin-utils": {
183 | "version": "7.27.1",
184 | "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
185 | "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
186 | "dev": true,
187 | "license": "MIT",
188 | "engines": {
189 | "node": ">=6.9.0"
190 | }
191 | },
192 | "node_modules/@babel/helper-string-parser": {
193 | "version": "7.27.1",
194 | "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
195 | "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
196 | "dev": true,
197 | "license": "MIT",
198 | "engines": {
199 | "node": ">=6.9.0"
200 | }
201 | },
202 | "node_modules/@babel/helper-validator-identifier": {
203 | "version": "7.28.5",
204 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
205 | "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
206 | "dev": true,
207 | "license": "MIT",
208 | "engines": {
209 | "node": ">=6.9.0"
210 | }
211 | },
212 | "node_modules/@babel/helper-validator-option": {
213 | "version": "7.27.1",
214 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
215 | "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
216 | "dev": true,
217 | "license": "MIT",
218 | "engines": {
219 | "node": ">=6.9.0"
220 | }
221 | },
222 | "node_modules/@babel/helpers": {
223 | "version": "7.28.4",
224 | "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
225 | "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
226 | "dev": true,
227 | "license": "MIT",
228 | "dependencies": {
229 | "@babel/template": "^7.27.2",
230 | "@babel/types": "^7.28.4"
231 | },
232 | "engines": {
233 | "node": ">=6.9.0"
234 | }
235 | },
236 | "node_modules/@babel/parser": {
237 | "version": "7.28.5",
238 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
239 | "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
240 | "dev": true,
241 | "license": "MIT",
242 | "dependencies": {
243 | "@babel/types": "^7.28.5"
244 | },
245 | "bin": {
246 | "parser": "bin/babel-parser.js"
247 | },
248 | "engines": {
249 | "node": ">=6.0.0"
250 | }
251 | },
252 | "node_modules/@babel/plugin-transform-react-jsx-self": {
253 | "version": "7.27.1",
254 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
255 | "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
256 | "dev": true,
257 | "license": "MIT",
258 | "dependencies": {
259 | "@babel/helper-plugin-utils": "^7.27.1"
260 | },
261 | "engines": {
262 | "node": ">=6.9.0"
263 | },
264 | "peerDependencies": {
265 | "@babel/core": "^7.0.0-0"
266 | }
267 | },
268 | "node_modules/@babel/plugin-transform-react-jsx-source": {
269 | "version": "7.27.1",
270 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
271 | "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
272 | "dev": true,
273 | "license": "MIT",
274 | "dependencies": {
275 | "@babel/helper-plugin-utils": "^7.27.1"
276 | },
277 | "engines": {
278 | "node": ">=6.9.0"
279 | },
280 | "peerDependencies": {
281 | "@babel/core": "^7.0.0-0"
282 | }
283 | },
284 | "node_modules/@babel/template": {
285 | "version": "7.27.2",
286 | "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
287 | "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
288 | "dev": true,
289 | "license": "MIT",
290 | "dependencies": {
291 | "@babel/code-frame": "^7.27.1",
292 | "@babel/parser": "^7.27.2",
293 | "@babel/types": "^7.27.1"
294 | },
295 | "engines": {
296 | "node": ">=6.9.0"
297 | }
298 | },
299 | "node_modules/@babel/traverse": {
300 | "version": "7.28.5",
301 | "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
302 | "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
303 | "dev": true,
304 | "license": "MIT",
305 | "dependencies": {
306 | "@babel/code-frame": "^7.27.1",
307 | "@babel/generator": "^7.28.5",
308 | "@babel/helper-globals": "^7.28.0",
309 | "@babel/parser": "^7.28.5",
310 | "@babel/template": "^7.27.2",
311 | "@babel/types": "^7.28.5",
312 | "debug": "^4.3.1"
313 | },
314 | "engines": {
315 | "node": ">=6.9.0"
316 | }
317 | },
318 | "node_modules/@babel/types": {
319 | "version": "7.28.5",
320 | "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
321 | "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
322 | "dev": true,
323 | "license": "MIT",
324 | "dependencies": {
325 | "@babel/helper-string-parser": "^7.27.1",
326 | "@babel/helper-validator-identifier": "^7.28.5"
327 | },
328 | "engines": {
329 | "node": ">=6.9.0"
330 | }
331 | },
332 | "node_modules/@esbuild/aix-ppc64": {
333 | "version": "0.25.12",
334 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
335 | "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
336 | "cpu": [
337 | "ppc64"
338 | ],
339 | "dev": true,
340 | "license": "MIT",
341 | "optional": true,
342 | "os": [
343 | "aix"
344 | ],
345 | "engines": {
346 | "node": ">=18"
347 | }
348 | },
349 | "node_modules/@esbuild/android-arm": {
350 | "version": "0.25.12",
351 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
352 | "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
353 | "cpu": [
354 | "arm"
355 | ],
356 | "dev": true,
357 | "license": "MIT",
358 | "optional": true,
359 | "os": [
360 | "android"
361 | ],
362 | "engines": {
363 | "node": ">=18"
364 | }
365 | },
366 | "node_modules/@esbuild/android-arm64": {
367 | "version": "0.25.12",
368 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
369 | "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
370 | "cpu": [
371 | "arm64"
372 | ],
373 | "dev": true,
374 | "license": "MIT",
375 | "optional": true,
376 | "os": [
377 | "android"
378 | ],
379 | "engines": {
380 | "node": ">=18"
381 | }
382 | },
383 | "node_modules/@esbuild/android-x64": {
384 | "version": "0.25.12",
385 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
386 | "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
387 | "cpu": [
388 | "x64"
389 | ],
390 | "dev": true,
391 | "license": "MIT",
392 | "optional": true,
393 | "os": [
394 | "android"
395 | ],
396 | "engines": {
397 | "node": ">=18"
398 | }
399 | },
400 | "node_modules/@esbuild/darwin-arm64": {
401 | "version": "0.25.12",
402 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
403 | "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
404 | "cpu": [
405 | "arm64"
406 | ],
407 | "dev": true,
408 | "license": "MIT",
409 | "optional": true,
410 | "os": [
411 | "darwin"
412 | ],
413 | "engines": {
414 | "node": ">=18"
415 | }
416 | },
417 | "node_modules/@esbuild/darwin-x64": {
418 | "version": "0.25.12",
419 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
420 | "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
421 | "cpu": [
422 | "x64"
423 | ],
424 | "dev": true,
425 | "license": "MIT",
426 | "optional": true,
427 | "os": [
428 | "darwin"
429 | ],
430 | "engines": {
431 | "node": ">=18"
432 | }
433 | },
434 | "node_modules/@esbuild/freebsd-arm64": {
435 | "version": "0.25.12",
436 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
437 | "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
438 | "cpu": [
439 | "arm64"
440 | ],
441 | "dev": true,
442 | "license": "MIT",
443 | "optional": true,
444 | "os": [
445 | "freebsd"
446 | ],
447 | "engines": {
448 | "node": ">=18"
449 | }
450 | },
451 | "node_modules/@esbuild/freebsd-x64": {
452 | "version": "0.25.12",
453 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
454 | "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
455 | "cpu": [
456 | "x64"
457 | ],
458 | "dev": true,
459 | "license": "MIT",
460 | "optional": true,
461 | "os": [
462 | "freebsd"
463 | ],
464 | "engines": {
465 | "node": ">=18"
466 | }
467 | },
468 | "node_modules/@esbuild/linux-arm": {
469 | "version": "0.25.12",
470 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
471 | "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
472 | "cpu": [
473 | "arm"
474 | ],
475 | "dev": true,
476 | "license": "MIT",
477 | "optional": true,
478 | "os": [
479 | "linux"
480 | ],
481 | "engines": {
482 | "node": ">=18"
483 | }
484 | },
485 | "node_modules/@esbuild/linux-arm64": {
486 | "version": "0.25.12",
487 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
488 | "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
489 | "cpu": [
490 | "arm64"
491 | ],
492 | "dev": true,
493 | "license": "MIT",
494 | "optional": true,
495 | "os": [
496 | "linux"
497 | ],
498 | "engines": {
499 | "node": ">=18"
500 | }
501 | },
502 | "node_modules/@esbuild/linux-ia32": {
503 | "version": "0.25.12",
504 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
505 | "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
506 | "cpu": [
507 | "ia32"
508 | ],
509 | "dev": true,
510 | "license": "MIT",
511 | "optional": true,
512 | "os": [
513 | "linux"
514 | ],
515 | "engines": {
516 | "node": ">=18"
517 | }
518 | },
519 | "node_modules/@esbuild/linux-loong64": {
520 | "version": "0.25.12",
521 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
522 | "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
523 | "cpu": [
524 | "loong64"
525 | ],
526 | "dev": true,
527 | "license": "MIT",
528 | "optional": true,
529 | "os": [
530 | "linux"
531 | ],
532 | "engines": {
533 | "node": ">=18"
534 | }
535 | },
536 | "node_modules/@esbuild/linux-mips64el": {
537 | "version": "0.25.12",
538 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
539 | "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
540 | "cpu": [
541 | "mips64el"
542 | ],
543 | "dev": true,
544 | "license": "MIT",
545 | "optional": true,
546 | "os": [
547 | "linux"
548 | ],
549 | "engines": {
550 | "node": ">=18"
551 | }
552 | },
553 | "node_modules/@esbuild/linux-ppc64": {
554 | "version": "0.25.12",
555 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
556 | "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
557 | "cpu": [
558 | "ppc64"
559 | ],
560 | "dev": true,
561 | "license": "MIT",
562 | "optional": true,
563 | "os": [
564 | "linux"
565 | ],
566 | "engines": {
567 | "node": ">=18"
568 | }
569 | },
570 | "node_modules/@esbuild/linux-riscv64": {
571 | "version": "0.25.12",
572 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
573 | "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
574 | "cpu": [
575 | "riscv64"
576 | ],
577 | "dev": true,
578 | "license": "MIT",
579 | "optional": true,
580 | "os": [
581 | "linux"
582 | ],
583 | "engines": {
584 | "node": ">=18"
585 | }
586 | },
587 | "node_modules/@esbuild/linux-s390x": {
588 | "version": "0.25.12",
589 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
590 | "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
591 | "cpu": [
592 | "s390x"
593 | ],
594 | "dev": true,
595 | "license": "MIT",
596 | "optional": true,
597 | "os": [
598 | "linux"
599 | ],
600 | "engines": {
601 | "node": ">=18"
602 | }
603 | },
604 | "node_modules/@esbuild/linux-x64": {
605 | "version": "0.25.12",
606 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
607 | "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
608 | "cpu": [
609 | "x64"
610 | ],
611 | "dev": true,
612 | "license": "MIT",
613 | "optional": true,
614 | "os": [
615 | "linux"
616 | ],
617 | "engines": {
618 | "node": ">=18"
619 | }
620 | },
621 | "node_modules/@esbuild/netbsd-arm64": {
622 | "version": "0.25.12",
623 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
624 | "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
625 | "cpu": [
626 | "arm64"
627 | ],
628 | "dev": true,
629 | "license": "MIT",
630 | "optional": true,
631 | "os": [
632 | "netbsd"
633 | ],
634 | "engines": {
635 | "node": ">=18"
636 | }
637 | },
638 | "node_modules/@esbuild/netbsd-x64": {
639 | "version": "0.25.12",
640 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
641 | "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
642 | "cpu": [
643 | "x64"
644 | ],
645 | "dev": true,
646 | "license": "MIT",
647 | "optional": true,
648 | "os": [
649 | "netbsd"
650 | ],
651 | "engines": {
652 | "node": ">=18"
653 | }
654 | },
655 | "node_modules/@esbuild/openbsd-arm64": {
656 | "version": "0.25.12",
657 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
658 | "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
659 | "cpu": [
660 | "arm64"
661 | ],
662 | "dev": true,
663 | "license": "MIT",
664 | "optional": true,
665 | "os": [
666 | "openbsd"
667 | ],
668 | "engines": {
669 | "node": ">=18"
670 | }
671 | },
672 | "node_modules/@esbuild/openbsd-x64": {
673 | "version": "0.25.12",
674 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
675 | "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
676 | "cpu": [
677 | "x64"
678 | ],
679 | "dev": true,
680 | "license": "MIT",
681 | "optional": true,
682 | "os": [
683 | "openbsd"
684 | ],
685 | "engines": {
686 | "node": ">=18"
687 | }
688 | },
689 | "node_modules/@esbuild/openharmony-arm64": {
690 | "version": "0.25.12",
691 | "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
692 | "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
693 | "cpu": [
694 | "arm64"
695 | ],
696 | "dev": true,
697 | "license": "MIT",
698 | "optional": true,
699 | "os": [
700 | "openharmony"
701 | ],
702 | "engines": {
703 | "node": ">=18"
704 | }
705 | },
706 | "node_modules/@esbuild/sunos-x64": {
707 | "version": "0.25.12",
708 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
709 | "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
710 | "cpu": [
711 | "x64"
712 | ],
713 | "dev": true,
714 | "license": "MIT",
715 | "optional": true,
716 | "os": [
717 | "sunos"
718 | ],
719 | "engines": {
720 | "node": ">=18"
721 | }
722 | },
723 | "node_modules/@esbuild/win32-arm64": {
724 | "version": "0.25.12",
725 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
726 | "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
727 | "cpu": [
728 | "arm64"
729 | ],
730 | "dev": true,
731 | "license": "MIT",
732 | "optional": true,
733 | "os": [
734 | "win32"
735 | ],
736 | "engines": {
737 | "node": ">=18"
738 | }
739 | },
740 | "node_modules/@esbuild/win32-ia32": {
741 | "version": "0.25.12",
742 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
743 | "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
744 | "cpu": [
745 | "ia32"
746 | ],
747 | "dev": true,
748 | "license": "MIT",
749 | "optional": true,
750 | "os": [
751 | "win32"
752 | ],
753 | "engines": {
754 | "node": ">=18"
755 | }
756 | },
757 | "node_modules/@esbuild/win32-x64": {
758 | "version": "0.25.12",
759 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
760 | "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
761 | "cpu": [
762 | "x64"
763 | ],
764 | "dev": true,
765 | "license": "MIT",
766 | "optional": true,
767 | "os": [
768 | "win32"
769 | ],
770 | "engines": {
771 | "node": ">=18"
772 | }
773 | },
774 | "node_modules/@jridgewell/gen-mapping": {
775 | "version": "0.3.13",
776 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
777 | "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
778 | "dev": true,
779 | "license": "MIT",
780 | "dependencies": {
781 | "@jridgewell/sourcemap-codec": "^1.5.0",
782 | "@jridgewell/trace-mapping": "^0.3.24"
783 | }
784 | },
785 | "node_modules/@jridgewell/remapping": {
786 | "version": "2.3.5",
787 | "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
788 | "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
789 | "dev": true,
790 | "license": "MIT",
791 | "dependencies": {
792 | "@jridgewell/gen-mapping": "^0.3.5",
793 | "@jridgewell/trace-mapping": "^0.3.24"
794 | }
795 | },
796 | "node_modules/@jridgewell/resolve-uri": {
797 | "version": "3.1.2",
798 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
799 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
800 | "dev": true,
801 | "license": "MIT",
802 | "engines": {
803 | "node": ">=6.0.0"
804 | }
805 | },
806 | "node_modules/@jridgewell/sourcemap-codec": {
807 | "version": "1.5.5",
808 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
809 | "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
810 | "dev": true,
811 | "license": "MIT"
812 | },
813 | "node_modules/@jridgewell/trace-mapping": {
814 | "version": "0.3.31",
815 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
816 | "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
817 | "dev": true,
818 | "license": "MIT",
819 | "dependencies": {
820 | "@jridgewell/resolve-uri": "^3.1.0",
821 | "@jridgewell/sourcemap-codec": "^1.4.14"
822 | }
823 | },
824 | "node_modules/@mediamonks/image-effect-renderer": {
825 | "resolved": "../..",
826 | "link": true
827 | },
828 | "node_modules/@rolldown/pluginutils": {
829 | "version": "1.0.0-beta.27",
830 | "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
831 | "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
832 | "dev": true,
833 | "license": "MIT"
834 | },
835 | "node_modules/@rollup/rollup-android-arm-eabi": {
836 | "version": "4.53.2",
837 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.2.tgz",
838 | "integrity": "sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==",
839 | "cpu": [
840 | "arm"
841 | ],
842 | "dev": true,
843 | "license": "MIT",
844 | "optional": true,
845 | "os": [
846 | "android"
847 | ]
848 | },
849 | "node_modules/@rollup/rollup-android-arm64": {
850 | "version": "4.53.2",
851 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.2.tgz",
852 | "integrity": "sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==",
853 | "cpu": [
854 | "arm64"
855 | ],
856 | "dev": true,
857 | "license": "MIT",
858 | "optional": true,
859 | "os": [
860 | "android"
861 | ]
862 | },
863 | "node_modules/@rollup/rollup-darwin-arm64": {
864 | "version": "4.53.2",
865 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.2.tgz",
866 | "integrity": "sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==",
867 | "cpu": [
868 | "arm64"
869 | ],
870 | "dev": true,
871 | "license": "MIT",
872 | "optional": true,
873 | "os": [
874 | "darwin"
875 | ]
876 | },
877 | "node_modules/@rollup/rollup-darwin-x64": {
878 | "version": "4.53.2",
879 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.2.tgz",
880 | "integrity": "sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==",
881 | "cpu": [
882 | "x64"
883 | ],
884 | "dev": true,
885 | "license": "MIT",
886 | "optional": true,
887 | "os": [
888 | "darwin"
889 | ]
890 | },
891 | "node_modules/@rollup/rollup-freebsd-arm64": {
892 | "version": "4.53.2",
893 | "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.2.tgz",
894 | "integrity": "sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==",
895 | "cpu": [
896 | "arm64"
897 | ],
898 | "dev": true,
899 | "license": "MIT",
900 | "optional": true,
901 | "os": [
902 | "freebsd"
903 | ]
904 | },
905 | "node_modules/@rollup/rollup-freebsd-x64": {
906 | "version": "4.53.2",
907 | "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.2.tgz",
908 | "integrity": "sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==",
909 | "cpu": [
910 | "x64"
911 | ],
912 | "dev": true,
913 | "license": "MIT",
914 | "optional": true,
915 | "os": [
916 | "freebsd"
917 | ]
918 | },
919 | "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
920 | "version": "4.53.2",
921 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.2.tgz",
922 | "integrity": "sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==",
923 | "cpu": [
924 | "arm"
925 | ],
926 | "dev": true,
927 | "license": "MIT",
928 | "optional": true,
929 | "os": [
930 | "linux"
931 | ]
932 | },
933 | "node_modules/@rollup/rollup-linux-arm-musleabihf": {
934 | "version": "4.53.2",
935 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.2.tgz",
936 | "integrity": "sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==",
937 | "cpu": [
938 | "arm"
939 | ],
940 | "dev": true,
941 | "license": "MIT",
942 | "optional": true,
943 | "os": [
944 | "linux"
945 | ]
946 | },
947 | "node_modules/@rollup/rollup-linux-arm64-gnu": {
948 | "version": "4.53.2",
949 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.2.tgz",
950 | "integrity": "sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==",
951 | "cpu": [
952 | "arm64"
953 | ],
954 | "dev": true,
955 | "license": "MIT",
956 | "optional": true,
957 | "os": [
958 | "linux"
959 | ]
960 | },
961 | "node_modules/@rollup/rollup-linux-arm64-musl": {
962 | "version": "4.53.2",
963 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.2.tgz",
964 | "integrity": "sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==",
965 | "cpu": [
966 | "arm64"
967 | ],
968 | "dev": true,
969 | "license": "MIT",
970 | "optional": true,
971 | "os": [
972 | "linux"
973 | ]
974 | },
975 | "node_modules/@rollup/rollup-linux-loong64-gnu": {
976 | "version": "4.53.2",
977 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.2.tgz",
978 | "integrity": "sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==",
979 | "cpu": [
980 | "loong64"
981 | ],
982 | "dev": true,
983 | "license": "MIT",
984 | "optional": true,
985 | "os": [
986 | "linux"
987 | ]
988 | },
989 | "node_modules/@rollup/rollup-linux-ppc64-gnu": {
990 | "version": "4.53.2",
991 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.2.tgz",
992 | "integrity": "sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==",
993 | "cpu": [
994 | "ppc64"
995 | ],
996 | "dev": true,
997 | "license": "MIT",
998 | "optional": true,
999 | "os": [
1000 | "linux"
1001 | ]
1002 | },
1003 | "node_modules/@rollup/rollup-linux-riscv64-gnu": {
1004 | "version": "4.53.2",
1005 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.2.tgz",
1006 | "integrity": "sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==",
1007 | "cpu": [
1008 | "riscv64"
1009 | ],
1010 | "dev": true,
1011 | "license": "MIT",
1012 | "optional": true,
1013 | "os": [
1014 | "linux"
1015 | ]
1016 | },
1017 | "node_modules/@rollup/rollup-linux-riscv64-musl": {
1018 | "version": "4.53.2",
1019 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.2.tgz",
1020 | "integrity": "sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==",
1021 | "cpu": [
1022 | "riscv64"
1023 | ],
1024 | "dev": true,
1025 | "license": "MIT",
1026 | "optional": true,
1027 | "os": [
1028 | "linux"
1029 | ]
1030 | },
1031 | "node_modules/@rollup/rollup-linux-s390x-gnu": {
1032 | "version": "4.53.2",
1033 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.2.tgz",
1034 | "integrity": "sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==",
1035 | "cpu": [
1036 | "s390x"
1037 | ],
1038 | "dev": true,
1039 | "license": "MIT",
1040 | "optional": true,
1041 | "os": [
1042 | "linux"
1043 | ]
1044 | },
1045 | "node_modules/@rollup/rollup-linux-x64-gnu": {
1046 | "version": "4.53.2",
1047 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.2.tgz",
1048 | "integrity": "sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==",
1049 | "cpu": [
1050 | "x64"
1051 | ],
1052 | "dev": true,
1053 | "license": "MIT",
1054 | "optional": true,
1055 | "os": [
1056 | "linux"
1057 | ]
1058 | },
1059 | "node_modules/@rollup/rollup-linux-x64-musl": {
1060 | "version": "4.53.2",
1061 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.2.tgz",
1062 | "integrity": "sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==",
1063 | "cpu": [
1064 | "x64"
1065 | ],
1066 | "dev": true,
1067 | "license": "MIT",
1068 | "optional": true,
1069 | "os": [
1070 | "linux"
1071 | ]
1072 | },
1073 | "node_modules/@rollup/rollup-openharmony-arm64": {
1074 | "version": "4.53.2",
1075 | "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.2.tgz",
1076 | "integrity": "sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==",
1077 | "cpu": [
1078 | "arm64"
1079 | ],
1080 | "dev": true,
1081 | "license": "MIT",
1082 | "optional": true,
1083 | "os": [
1084 | "openharmony"
1085 | ]
1086 | },
1087 | "node_modules/@rollup/rollup-win32-arm64-msvc": {
1088 | "version": "4.53.2",
1089 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.2.tgz",
1090 | "integrity": "sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==",
1091 | "cpu": [
1092 | "arm64"
1093 | ],
1094 | "dev": true,
1095 | "license": "MIT",
1096 | "optional": true,
1097 | "os": [
1098 | "win32"
1099 | ]
1100 | },
1101 | "node_modules/@rollup/rollup-win32-ia32-msvc": {
1102 | "version": "4.53.2",
1103 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.2.tgz",
1104 | "integrity": "sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==",
1105 | "cpu": [
1106 | "ia32"
1107 | ],
1108 | "dev": true,
1109 | "license": "MIT",
1110 | "optional": true,
1111 | "os": [
1112 | "win32"
1113 | ]
1114 | },
1115 | "node_modules/@rollup/rollup-win32-x64-gnu": {
1116 | "version": "4.53.2",
1117 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.2.tgz",
1118 | "integrity": "sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==",
1119 | "cpu": [
1120 | "x64"
1121 | ],
1122 | "dev": true,
1123 | "license": "MIT",
1124 | "optional": true,
1125 | "os": [
1126 | "win32"
1127 | ]
1128 | },
1129 | "node_modules/@rollup/rollup-win32-x64-msvc": {
1130 | "version": "4.53.2",
1131 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.2.tgz",
1132 | "integrity": "sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==",
1133 | "cpu": [
1134 | "x64"
1135 | ],
1136 | "dev": true,
1137 | "license": "MIT",
1138 | "optional": true,
1139 | "os": [
1140 | "win32"
1141 | ]
1142 | },
1143 | "node_modules/@types/babel__core": {
1144 | "version": "7.20.5",
1145 | "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
1146 | "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
1147 | "dev": true,
1148 | "license": "MIT",
1149 | "dependencies": {
1150 | "@babel/parser": "^7.20.7",
1151 | "@babel/types": "^7.20.7",
1152 | "@types/babel__generator": "*",
1153 | "@types/babel__template": "*",
1154 | "@types/babel__traverse": "*"
1155 | }
1156 | },
1157 | "node_modules/@types/babel__generator": {
1158 | "version": "7.27.0",
1159 | "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
1160 | "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
1161 | "dev": true,
1162 | "license": "MIT",
1163 | "dependencies": {
1164 | "@babel/types": "^7.0.0"
1165 | }
1166 | },
1167 | "node_modules/@types/babel__template": {
1168 | "version": "7.4.4",
1169 | "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
1170 | "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
1171 | "dev": true,
1172 | "license": "MIT",
1173 | "dependencies": {
1174 | "@babel/parser": "^7.1.0",
1175 | "@babel/types": "^7.0.0"
1176 | }
1177 | },
1178 | "node_modules/@types/babel__traverse": {
1179 | "version": "7.28.0",
1180 | "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
1181 | "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
1182 | "dev": true,
1183 | "license": "MIT",
1184 | "dependencies": {
1185 | "@babel/types": "^7.28.2"
1186 | }
1187 | },
1188 | "node_modules/@types/estree": {
1189 | "version": "1.0.8",
1190 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
1191 | "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
1192 | "dev": true,
1193 | "license": "MIT"
1194 | },
1195 | "node_modules/@types/prop-types": {
1196 | "version": "15.7.15",
1197 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
1198 | "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
1199 | "dev": true,
1200 | "license": "MIT"
1201 | },
1202 | "node_modules/@types/react": {
1203 | "version": "18.3.26",
1204 | "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.26.tgz",
1205 | "integrity": "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==",
1206 | "dev": true,
1207 | "license": "MIT",
1208 | "dependencies": {
1209 | "@types/prop-types": "*",
1210 | "csstype": "^3.0.2"
1211 | }
1212 | },
1213 | "node_modules/@types/react-dom": {
1214 | "version": "18.3.7",
1215 | "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
1216 | "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
1217 | "dev": true,
1218 | "license": "MIT",
1219 | "peerDependencies": {
1220 | "@types/react": "^18.0.0"
1221 | }
1222 | },
1223 | "node_modules/@vitejs/plugin-react": {
1224 | "version": "4.7.0",
1225 | "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
1226 | "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
1227 | "dev": true,
1228 | "license": "MIT",
1229 | "dependencies": {
1230 | "@babel/core": "^7.28.0",
1231 | "@babel/plugin-transform-react-jsx-self": "^7.27.1",
1232 | "@babel/plugin-transform-react-jsx-source": "^7.27.1",
1233 | "@rolldown/pluginutils": "1.0.0-beta.27",
1234 | "@types/babel__core": "^7.20.5",
1235 | "react-refresh": "^0.17.0"
1236 | },
1237 | "engines": {
1238 | "node": "^14.18.0 || >=16.0.0"
1239 | },
1240 | "peerDependencies": {
1241 | "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
1242 | }
1243 | },
1244 | "node_modules/baseline-browser-mapping": {
1245 | "version": "2.8.28",
1246 | "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.28.tgz",
1247 | "integrity": "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ==",
1248 | "dev": true,
1249 | "license": "Apache-2.0",
1250 | "bin": {
1251 | "baseline-browser-mapping": "dist/cli.js"
1252 | }
1253 | },
1254 | "node_modules/browserslist": {
1255 | "version": "4.28.0",
1256 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz",
1257 | "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==",
1258 | "dev": true,
1259 | "funding": [
1260 | {
1261 | "type": "opencollective",
1262 | "url": "https://opencollective.com/browserslist"
1263 | },
1264 | {
1265 | "type": "tidelift",
1266 | "url": "https://tidelift.com/funding/github/npm/browserslist"
1267 | },
1268 | {
1269 | "type": "github",
1270 | "url": "https://github.com/sponsors/ai"
1271 | }
1272 | ],
1273 | "license": "MIT",
1274 | "dependencies": {
1275 | "baseline-browser-mapping": "^2.8.25",
1276 | "caniuse-lite": "^1.0.30001754",
1277 | "electron-to-chromium": "^1.5.249",
1278 | "node-releases": "^2.0.27",
1279 | "update-browserslist-db": "^1.1.4"
1280 | },
1281 | "bin": {
1282 | "browserslist": "cli.js"
1283 | },
1284 | "engines": {
1285 | "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
1286 | }
1287 | },
1288 | "node_modules/caniuse-lite": {
1289 | "version": "1.0.30001754",
1290 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001754.tgz",
1291 | "integrity": "sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==",
1292 | "dev": true,
1293 | "funding": [
1294 | {
1295 | "type": "opencollective",
1296 | "url": "https://opencollective.com/browserslist"
1297 | },
1298 | {
1299 | "type": "tidelift",
1300 | "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
1301 | },
1302 | {
1303 | "type": "github",
1304 | "url": "https://github.com/sponsors/ai"
1305 | }
1306 | ],
1307 | "license": "CC-BY-4.0"
1308 | },
1309 | "node_modules/convert-source-map": {
1310 | "version": "2.0.0",
1311 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
1312 | "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
1313 | "dev": true,
1314 | "license": "MIT"
1315 | },
1316 | "node_modules/csstype": {
1317 | "version": "3.2.0",
1318 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.0.tgz",
1319 | "integrity": "sha512-si++xzRAY9iPp60roQiFta7OFbhrgvcthrhlNAGeQptSY25uJjkfUV8OArC3KLocB8JT8ohz+qgxWCmz8RhjIg==",
1320 | "dev": true,
1321 | "license": "MIT"
1322 | },
1323 | "node_modules/debug": {
1324 | "version": "4.4.3",
1325 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
1326 | "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
1327 | "dev": true,
1328 | "license": "MIT",
1329 | "dependencies": {
1330 | "ms": "^2.1.3"
1331 | },
1332 | "engines": {
1333 | "node": ">=6.0"
1334 | },
1335 | "peerDependenciesMeta": {
1336 | "supports-color": {
1337 | "optional": true
1338 | }
1339 | }
1340 | },
1341 | "node_modules/electron-to-chromium": {
1342 | "version": "1.5.252",
1343 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.252.tgz",
1344 | "integrity": "sha512-53uTpjtRgS7gjIxZ4qCgFdNO2q+wJt/Z8+xAvxbCqXPJrY6h7ighUkadQmNMXH96crtpa6gPFNP7BF4UBGDuaA==",
1345 | "dev": true,
1346 | "license": "ISC"
1347 | },
1348 | "node_modules/esbuild": {
1349 | "version": "0.25.12",
1350 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
1351 | "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
1352 | "dev": true,
1353 | "hasInstallScript": true,
1354 | "license": "MIT",
1355 | "bin": {
1356 | "esbuild": "bin/esbuild"
1357 | },
1358 | "engines": {
1359 | "node": ">=18"
1360 | },
1361 | "optionalDependencies": {
1362 | "@esbuild/aix-ppc64": "0.25.12",
1363 | "@esbuild/android-arm": "0.25.12",
1364 | "@esbuild/android-arm64": "0.25.12",
1365 | "@esbuild/android-x64": "0.25.12",
1366 | "@esbuild/darwin-arm64": "0.25.12",
1367 | "@esbuild/darwin-x64": "0.25.12",
1368 | "@esbuild/freebsd-arm64": "0.25.12",
1369 | "@esbuild/freebsd-x64": "0.25.12",
1370 | "@esbuild/linux-arm": "0.25.12",
1371 | "@esbuild/linux-arm64": "0.25.12",
1372 | "@esbuild/linux-ia32": "0.25.12",
1373 | "@esbuild/linux-loong64": "0.25.12",
1374 | "@esbuild/linux-mips64el": "0.25.12",
1375 | "@esbuild/linux-ppc64": "0.25.12",
1376 | "@esbuild/linux-riscv64": "0.25.12",
1377 | "@esbuild/linux-s390x": "0.25.12",
1378 | "@esbuild/linux-x64": "0.25.12",
1379 | "@esbuild/netbsd-arm64": "0.25.12",
1380 | "@esbuild/netbsd-x64": "0.25.12",
1381 | "@esbuild/openbsd-arm64": "0.25.12",
1382 | "@esbuild/openbsd-x64": "0.25.12",
1383 | "@esbuild/openharmony-arm64": "0.25.12",
1384 | "@esbuild/sunos-x64": "0.25.12",
1385 | "@esbuild/win32-arm64": "0.25.12",
1386 | "@esbuild/win32-ia32": "0.25.12",
1387 | "@esbuild/win32-x64": "0.25.12"
1388 | }
1389 | },
1390 | "node_modules/escalade": {
1391 | "version": "3.2.0",
1392 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
1393 | "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
1394 | "dev": true,
1395 | "license": "MIT",
1396 | "engines": {
1397 | "node": ">=6"
1398 | }
1399 | },
1400 | "node_modules/fdir": {
1401 | "version": "6.5.0",
1402 | "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
1403 | "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
1404 | "dev": true,
1405 | "license": "MIT",
1406 | "engines": {
1407 | "node": ">=12.0.0"
1408 | },
1409 | "peerDependencies": {
1410 | "picomatch": "^3 || ^4"
1411 | },
1412 | "peerDependenciesMeta": {
1413 | "picomatch": {
1414 | "optional": true
1415 | }
1416 | }
1417 | },
1418 | "node_modules/fsevents": {
1419 | "version": "2.3.3",
1420 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
1421 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
1422 | "dev": true,
1423 | "hasInstallScript": true,
1424 | "license": "MIT",
1425 | "optional": true,
1426 | "os": [
1427 | "darwin"
1428 | ],
1429 | "engines": {
1430 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
1431 | }
1432 | },
1433 | "node_modules/gensync": {
1434 | "version": "1.0.0-beta.2",
1435 | "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
1436 | "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
1437 | "dev": true,
1438 | "license": "MIT",
1439 | "engines": {
1440 | "node": ">=6.9.0"
1441 | }
1442 | },
1443 | "node_modules/js-tokens": {
1444 | "version": "4.0.0",
1445 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
1446 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
1447 | "license": "MIT"
1448 | },
1449 | "node_modules/jsesc": {
1450 | "version": "3.1.0",
1451 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
1452 | "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
1453 | "dev": true,
1454 | "license": "MIT",
1455 | "bin": {
1456 | "jsesc": "bin/jsesc"
1457 | },
1458 | "engines": {
1459 | "node": ">=6"
1460 | }
1461 | },
1462 | "node_modules/json5": {
1463 | "version": "2.2.3",
1464 | "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
1465 | "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
1466 | "dev": true,
1467 | "license": "MIT",
1468 | "bin": {
1469 | "json5": "lib/cli.js"
1470 | },
1471 | "engines": {
1472 | "node": ">=6"
1473 | }
1474 | },
1475 | "node_modules/loose-envify": {
1476 | "version": "1.4.0",
1477 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
1478 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
1479 | "license": "MIT",
1480 | "dependencies": {
1481 | "js-tokens": "^3.0.0 || ^4.0.0"
1482 | },
1483 | "bin": {
1484 | "loose-envify": "cli.js"
1485 | }
1486 | },
1487 | "node_modules/lru-cache": {
1488 | "version": "5.1.1",
1489 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
1490 | "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
1491 | "dev": true,
1492 | "license": "ISC",
1493 | "dependencies": {
1494 | "yallist": "^3.0.2"
1495 | }
1496 | },
1497 | "node_modules/ms": {
1498 | "version": "2.1.3",
1499 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1500 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1501 | "dev": true,
1502 | "license": "MIT"
1503 | },
1504 | "node_modules/nanoid": {
1505 | "version": "3.3.11",
1506 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
1507 | "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
1508 | "dev": true,
1509 | "funding": [
1510 | {
1511 | "type": "github",
1512 | "url": "https://github.com/sponsors/ai"
1513 | }
1514 | ],
1515 | "license": "MIT",
1516 | "bin": {
1517 | "nanoid": "bin/nanoid.cjs"
1518 | },
1519 | "engines": {
1520 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1521 | }
1522 | },
1523 | "node_modules/node-releases": {
1524 | "version": "2.0.27",
1525 | "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
1526 | "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
1527 | "dev": true,
1528 | "license": "MIT"
1529 | },
1530 | "node_modules/picocolors": {
1531 | "version": "1.1.1",
1532 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
1533 | "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
1534 | "dev": true,
1535 | "license": "ISC"
1536 | },
1537 | "node_modules/picomatch": {
1538 | "version": "4.0.3",
1539 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
1540 | "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
1541 | "dev": true,
1542 | "license": "MIT",
1543 | "engines": {
1544 | "node": ">=12"
1545 | },
1546 | "funding": {
1547 | "url": "https://github.com/sponsors/jonschlinkert"
1548 | }
1549 | },
1550 | "node_modules/postcss": {
1551 | "version": "8.5.6",
1552 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
1553 | "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
1554 | "dev": true,
1555 | "funding": [
1556 | {
1557 | "type": "opencollective",
1558 | "url": "https://opencollective.com/postcss/"
1559 | },
1560 | {
1561 | "type": "tidelift",
1562 | "url": "https://tidelift.com/funding/github/npm/postcss"
1563 | },
1564 | {
1565 | "type": "github",
1566 | "url": "https://github.com/sponsors/ai"
1567 | }
1568 | ],
1569 | "license": "MIT",
1570 | "dependencies": {
1571 | "nanoid": "^3.3.11",
1572 | "picocolors": "^1.1.1",
1573 | "source-map-js": "^1.2.1"
1574 | },
1575 | "engines": {
1576 | "node": "^10 || ^12 || >=14"
1577 | }
1578 | },
1579 | "node_modules/react": {
1580 | "version": "18.3.1",
1581 | "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
1582 | "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
1583 | "license": "MIT",
1584 | "dependencies": {
1585 | "loose-envify": "^1.1.0"
1586 | },
1587 | "engines": {
1588 | "node": ">=0.10.0"
1589 | }
1590 | },
1591 | "node_modules/react-dom": {
1592 | "version": "18.3.1",
1593 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
1594 | "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
1595 | "license": "MIT",
1596 | "dependencies": {
1597 | "loose-envify": "^1.1.0",
1598 | "scheduler": "^0.23.2"
1599 | },
1600 | "peerDependencies": {
1601 | "react": "^18.3.1"
1602 | }
1603 | },
1604 | "node_modules/react-refresh": {
1605 | "version": "0.17.0",
1606 | "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
1607 | "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
1608 | "dev": true,
1609 | "license": "MIT",
1610 | "engines": {
1611 | "node": ">=0.10.0"
1612 | }
1613 | },
1614 | "node_modules/rollup": {
1615 | "version": "4.53.2",
1616 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.2.tgz",
1617 | "integrity": "sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==",
1618 | "dev": true,
1619 | "license": "MIT",
1620 | "dependencies": {
1621 | "@types/estree": "1.0.8"
1622 | },
1623 | "bin": {
1624 | "rollup": "dist/bin/rollup"
1625 | },
1626 | "engines": {
1627 | "node": ">=18.0.0",
1628 | "npm": ">=8.0.0"
1629 | },
1630 | "optionalDependencies": {
1631 | "@rollup/rollup-android-arm-eabi": "4.53.2",
1632 | "@rollup/rollup-android-arm64": "4.53.2",
1633 | "@rollup/rollup-darwin-arm64": "4.53.2",
1634 | "@rollup/rollup-darwin-x64": "4.53.2",
1635 | "@rollup/rollup-freebsd-arm64": "4.53.2",
1636 | "@rollup/rollup-freebsd-x64": "4.53.2",
1637 | "@rollup/rollup-linux-arm-gnueabihf": "4.53.2",
1638 | "@rollup/rollup-linux-arm-musleabihf": "4.53.2",
1639 | "@rollup/rollup-linux-arm64-gnu": "4.53.2",
1640 | "@rollup/rollup-linux-arm64-musl": "4.53.2",
1641 | "@rollup/rollup-linux-loong64-gnu": "4.53.2",
1642 | "@rollup/rollup-linux-ppc64-gnu": "4.53.2",
1643 | "@rollup/rollup-linux-riscv64-gnu": "4.53.2",
1644 | "@rollup/rollup-linux-riscv64-musl": "4.53.2",
1645 | "@rollup/rollup-linux-s390x-gnu": "4.53.2",
1646 | "@rollup/rollup-linux-x64-gnu": "4.53.2",
1647 | "@rollup/rollup-linux-x64-musl": "4.53.2",
1648 | "@rollup/rollup-openharmony-arm64": "4.53.2",
1649 | "@rollup/rollup-win32-arm64-msvc": "4.53.2",
1650 | "@rollup/rollup-win32-ia32-msvc": "4.53.2",
1651 | "@rollup/rollup-win32-x64-gnu": "4.53.2",
1652 | "@rollup/rollup-win32-x64-msvc": "4.53.2",
1653 | "fsevents": "~2.3.2"
1654 | }
1655 | },
1656 | "node_modules/scheduler": {
1657 | "version": "0.23.2",
1658 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
1659 | "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
1660 | "license": "MIT",
1661 | "dependencies": {
1662 | "loose-envify": "^1.1.0"
1663 | }
1664 | },
1665 | "node_modules/semver": {
1666 | "version": "6.3.1",
1667 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
1668 | "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
1669 | "dev": true,
1670 | "license": "ISC",
1671 | "bin": {
1672 | "semver": "bin/semver.js"
1673 | }
1674 | },
1675 | "node_modules/source-map-js": {
1676 | "version": "1.2.1",
1677 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
1678 | "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
1679 | "dev": true,
1680 | "license": "BSD-3-Clause",
1681 | "engines": {
1682 | "node": ">=0.10.0"
1683 | }
1684 | },
1685 | "node_modules/tinyglobby": {
1686 | "version": "0.2.15",
1687 | "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
1688 | "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
1689 | "dev": true,
1690 | "license": "MIT",
1691 | "dependencies": {
1692 | "fdir": "^6.5.0",
1693 | "picomatch": "^4.0.3"
1694 | },
1695 | "engines": {
1696 | "node": ">=12.0.0"
1697 | },
1698 | "funding": {
1699 | "url": "https://github.com/sponsors/SuperchupuDev"
1700 | }
1701 | },
1702 | "node_modules/typescript": {
1703 | "version": "5.9.3",
1704 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
1705 | "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
1706 | "dev": true,
1707 | "license": "Apache-2.0",
1708 | "bin": {
1709 | "tsc": "bin/tsc",
1710 | "tsserver": "bin/tsserver"
1711 | },
1712 | "engines": {
1713 | "node": ">=14.17"
1714 | }
1715 | },
1716 | "node_modules/update-browserslist-db": {
1717 | "version": "1.1.4",
1718 | "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz",
1719 | "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==",
1720 | "dev": true,
1721 | "funding": [
1722 | {
1723 | "type": "opencollective",
1724 | "url": "https://opencollective.com/browserslist"
1725 | },
1726 | {
1727 | "type": "tidelift",
1728 | "url": "https://tidelift.com/funding/github/npm/browserslist"
1729 | },
1730 | {
1731 | "type": "github",
1732 | "url": "https://github.com/sponsors/ai"
1733 | }
1734 | ],
1735 | "license": "MIT",
1736 | "dependencies": {
1737 | "escalade": "^3.2.0",
1738 | "picocolors": "^1.1.1"
1739 | },
1740 | "bin": {
1741 | "update-browserslist-db": "cli.js"
1742 | },
1743 | "peerDependencies": {
1744 | "browserslist": ">= 4.21.0"
1745 | }
1746 | },
1747 | "node_modules/vite": {
1748 | "version": "6.4.1",
1749 | "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
1750 | "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
1751 | "dev": true,
1752 | "license": "MIT",
1753 | "dependencies": {
1754 | "esbuild": "^0.25.0",
1755 | "fdir": "^6.4.4",
1756 | "picomatch": "^4.0.2",
1757 | "postcss": "^8.5.3",
1758 | "rollup": "^4.34.9",
1759 | "tinyglobby": "^0.2.13"
1760 | },
1761 | "bin": {
1762 | "vite": "bin/vite.js"
1763 | },
1764 | "engines": {
1765 | "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
1766 | },
1767 | "funding": {
1768 | "url": "https://github.com/vitejs/vite?sponsor=1"
1769 | },
1770 | "optionalDependencies": {
1771 | "fsevents": "~2.3.3"
1772 | },
1773 | "peerDependencies": {
1774 | "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
1775 | "jiti": ">=1.21.0",
1776 | "less": "*",
1777 | "lightningcss": "^1.21.0",
1778 | "sass": "*",
1779 | "sass-embedded": "*",
1780 | "stylus": "*",
1781 | "sugarss": "*",
1782 | "terser": "^5.16.0",
1783 | "tsx": "^4.8.1",
1784 | "yaml": "^2.4.2"
1785 | },
1786 | "peerDependenciesMeta": {
1787 | "@types/node": {
1788 | "optional": true
1789 | },
1790 | "jiti": {
1791 | "optional": true
1792 | },
1793 | "less": {
1794 | "optional": true
1795 | },
1796 | "lightningcss": {
1797 | "optional": true
1798 | },
1799 | "sass": {
1800 | "optional": true
1801 | },
1802 | "sass-embedded": {
1803 | "optional": true
1804 | },
1805 | "stylus": {
1806 | "optional": true
1807 | },
1808 | "sugarss": {
1809 | "optional": true
1810 | },
1811 | "terser": {
1812 | "optional": true
1813 | },
1814 | "tsx": {
1815 | "optional": true
1816 | },
1817 | "yaml": {
1818 | "optional": true
1819 | }
1820 | }
1821 | },
1822 | "node_modules/yallist": {
1823 | "version": "3.1.1",
1824 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
1825 | "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
1826 | "dev": true,
1827 | "license": "ISC"
1828 | }
1829 | }
1830 | }
1831 |
--------------------------------------------------------------------------------