├── 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 |
36 |

ImageEffectRenderer

37 |

38 | React example with WebGL fragment shaders 39 |

40 |
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 | --------------------------------------------------------------------------------