diff --git a/packages/cookbook-v2/package.json b/packages/cookbook-v2/package.json index 8d4d1767..efefa53c 100644 --- a/packages/cookbook-v2/package.json +++ b/packages/cookbook-v2/package.json @@ -12,7 +12,7 @@ "lint:fix": "eslint . --ext ts,tsx --fix" }, "dependencies": { - "@heroicons/react": "^2.1.1", + "@heroicons/react": "^2.2.0", "gl-react": "^5.2.0", "gl-react-dom": "^5.2.1", "gl-shader": "^4.2.1", @@ -20,20 +20,20 @@ "gl-transitions": "^1.43.0", "hoist-non-react-statics": "^3.3.2", "invariant": "^2.2.4", - "lodash": "^4.17.21", + "lodash": "^4.17.23", "ndarray": "^1.0.19", "ndarray-ops": "^1.2.2", "prism-theme-one-dark": "^1.0.0", - "prismjs": "^1.29.0", + "prismjs": "^1.30.0", "prop-types": "^15.8.1", - "query-string": "^9.1.0", + "query-string": "^9.3.1", "raf": "^3.4.1", "react": "^19.0.0", "react-color": "^2.19.3", "react-dom": "^19.0.0", "react-gl-transition": "^1.19.2", "react-json2d": "^0.3.0", - "react-motion": "^0.5.0", + "react-motion": "^0.5.2", "react-router-dom": "^6.28.0" }, "devDependencies": { @@ -41,20 +41,20 @@ "@babel/preset-react": "^7.28.5", "@playwright/test": "^1.58.2", "@types/babel__core": "^7", - "@types/lodash": "^4.17.0", - "@types/prismjs": "^1.26.3", + "@types/lodash": "^4.17.24", + "@types/prismjs": "^1.26.6", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", - "@typescript-eslint/eslint-plugin": "^8.15.0", - "@typescript-eslint/parser": "^8.15.0", - "@vitejs/plugin-react": "^4.3.3", - "autoprefixer": "^10.4.20", + "@typescript-eslint/eslint-plugin": "^8.57.1", + "@typescript-eslint/parser": "^8.57.1", + "@vitejs/plugin-react": "^4.7.0", + "autoprefixer": "^10.4.27", "eslint": "^9.15.0", "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-refresh": "^0.4.14", - "postcss": "^8.4.49", + "postcss": "^8.5.8", "tailwindcss": "^3.4.15", - "typescript": "^5.6.3", - "vite": "^6.0.1" + "typescript": "^5.9.3", + "vite": "^6.4.1" } } diff --git a/packages/cookbook-v2/src/components/Breadcrumb.tsx b/packages/cookbook-v2/src/components/Breadcrumb.tsx new file mode 100644 index 00000000..6f1c8a2d --- /dev/null +++ b/packages/cookbook-v2/src/components/Breadcrumb.tsx @@ -0,0 +1,47 @@ +import { Link, useNavigate } from "react-router-dom"; +import { ChevronRightIcon } from "@heroicons/react/24/solid"; +import { examples } from "../examples"; + +const categories = [...new Set(examples.map((e) => e.category))]; +const grouped = categories.map((cat) => ({ + label: cat, + items: examples.filter((e) => e.category === cat), +})); + +export function Breadcrumb({ exampleId }: { exampleId?: string }) { + const navigate = useNavigate(); + const current = exampleId + ? examples.find((e) => e.id === exampleId) + : undefined; + + return ( + + ); +} diff --git a/packages/cookbook-v2/src/components/GLInspector.css b/packages/cookbook-v2/src/components/GLInspector.css new file mode 100644 index 00000000..dac3475c --- /dev/null +++ b/packages/cookbook-v2/src/components/GLInspector.css @@ -0,0 +1,307 @@ +.gl-inspector { + display: flex; + flex-direction: column; + height: 100%; + font-size: 12px; + background: #fff; + color: #000; + overflow: hidden; +} +.gl-inspector h2 { + font-weight: normal; +} + +.gl-inspector .body .nodes { + position: relative; + width: 100%; + height: 100%; + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AscDA0JcudXmQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAABIElEQVR42u3cMQqAQAxE0YkK3v/Ago3gEULIe43lwvLZzqn0Ob/v4/wWd5IcYTUBCAABIAAEgAAQAAJAAAgAASAABIAAEAACQAAIAAEgAASAABAAAkAACAABIACmsQ+wl30A7ANsPt8+AAIQgCsQAAJAAAgAASAABIAAEAACQAAIAAEgAASAABAAAkAACAABIAAEgAAQAAJgGvsAe9kHwD7A5vPtAyAAAbgCASAABIAAEAACQAAIAAEgAASAABAAAkAACAABIAAEgAAQAAJAAAgAASAApql8f4niBWChF2e+DTF+hz42AAAAAElFTkSuQmCC"); + background-size: 64px 64px; +} + +.gl-inspector .body .hook-drawer { + position: absolute; + top: 0; + left: 0; + overflow: visible; + pointer-events: none; + user-select: none; +} + +.gl-inspector .box { + position: absolute; + display: flex; + flex-direction: column; + width: 180px; + border-radius: 2px; + box-shadow: 0px 0px 3px rgba(0, 0, 0, 0); + border: 1px solid #eee; + background: #fff; + transition: 0.2s border-color; +} +.gl-inspector .box.recent-draw { + border-color: #999; + transition: 0.1s border-color; +} + +.gl-inspector .box header { + display: flex; + flex-direction: row; + font-size: 12px; + padding: 0px 4px; + margin-bottom: 4px; + line-height: 24px; + cursor: move; + border-bottom: 1px solid #eee; +} + +.gl-inspector .box header .name { + flex: 1; + font-weight: bold; + word-wrap: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.gl-inspector .box header .redraw { + padding: 0px 6px; + min-width: 30px; + line-height: 24px; + opacity: 0.5; + cursor: pointer; + text-align: right; +} +.gl-inspector .box header .redraw:hover { + opacity: 1; +} +.gl-inspector .box header .drawCount { + font-size: 10px; + min-width: 20px; + text-align: right; +} + +.gl-inspector .box .content-html { + max-height: 80px; + overflow: auto; + color: #aaa; + padding: 2px 8px; + font-size: 9px; + white-space: normal; + word-wrap: break-word; +} + +.gl-inspector .box.minimized { + width: 80px; +} +.gl-inspector .box.minimized header { + border-bottom: none; + margin-bottom: -2px; +} +.gl-inspector .box.minimized footer { + border-top: none; + margin-top: 0; +} +.gl-inspector .box.minimized .redraw, +.gl-inspector .box.minimized .content-html { + display: none; +} +.gl-inspector .box.minimized .uniform { + line-height: 12px; +} +.gl-inspector .box.minimized .uniform .anchor-hook { + height: 12px; +} +.gl-inspector + .box.minimized + .uniform:not(.type-sampler2D):not(.type-array-sampler2D) { + display: none; +} + +.gl-inspector .box footer { + font-size: 10px; + display: flex; + flex-direction: row; + align-items: baseline; + justify-content: space-between; + padding: 2px 4px; + margin-top: 4px; + border-top: 1px solid #eee; + cursor: pointer; + user-select: none; + position: relative; +} + +.gl-inspector .box footer .minimize { + opacity: 0; + position: absolute; + left: -12px; + padding: 1px 4px; +} +.gl-inspector .box:hover footer .minimize { + opacity: 0.2; +} +.gl-inspector .box footer:hover .minimize { + opacity: 1; +} +.gl-inspector .box footer .dim { + color: #888; + font-size: 8px; +} +.gl-inspector .box footer .mode { + padding-left: 4px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.gl-inspector .box .anchor-hook { + position: absolute; + left: -2px; + height: 16px; +} + +.gl-inspector .box .hook { + position: absolute; + right: 0px; + height: 20px; +} + +.gl-inspector .uniforms { + display: flex; + flex-direction: column; + overflow: hidden; +} + +.gl-inspector .uniforms .uniform { + line-height: 18px; + font-size: 11px; + display: flex; + flex-direction: row; + justify-content: space-between; +} +.gl-inspector .uniforms .name { + color: #000; + display: inline-block; + padding: 0 4px; +} +.gl-inspector .uniforms .value-container { + word-break: break-all; + padding-right: 6px; + overflow: hidden; +} +.gl-inspector .uniforms .val { + color: #850; + background: #ffc; + padding: 1px 3px; + border: 1px solid #ed9; +} +.gl-inspector .uniforms .value.type-sampler2D { + text-align: right; +} +.gl-inspector .uniforms .value.type-sampler2D .val { + display: none; +} + +.gl-inspector .uniforms .value-array .value-array, +.gl-inspector .uniforms .value.type-sampler2D { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.gl-inspector .uniforms .value-array > :first-child:before { + content: "["; + color: #aaa; +} +.gl-inspector .uniforms .value-array > :after { + content: ","; + color: #aaa; +} +.gl-inspector .uniforms .value-array > :last-child:after { + content: "]"; + color: #aaa; +} + +.gl-inspector .uniforms .meta-info { + color: #ec6; + padding: 0 2px; +} + +.gl-inspector .preview { + background: #fff; + position: relative; + flex: 1; + display: flex; + justify-content: center; + align-items: center; + padding: 10px; +} + +.gl-inspector .connection circle { + fill: #666; + r: 1; +} +.gl-inspector .connection-dot { + fill: #333; +} + +.gl-inspector circle.hook, +.gl-inspector circle.anchor { + r: 2; + fill: #666; +} +.gl-inspector circle.standalone-output { + r: 2; + fill: #000; + opacity: 1; +} + +.gl-inspector path.connection-line { + opacity: 0.1; + stroke: #000; + fill: none; + stroke-width: 1; +} + +.gl-inspector .box.grabbed { + border-color: #000; + transition: none; +} +.gl-inspector .anchor-group.grabbed circle.hook, +.gl-inspector .anchor-group.grabbed circle.anchor { + fill: #000; +} +.gl-inspector .anchor-group.grabbed path.connection-line { + stroke: #000; + opacity: 0.3; +} + +.gl-inspector .preview canvas { + background: #f3f3f3 + url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAARklEQVRYw+3XwQkAIAwEwYvYf8uxBcE8fMwWEAbuleruDDd6cOXzAAEBAQEBAQEBAQFf2tM/RJIyMSAgICAgICAgICDgZQeYxgVOKu5KXQAAAABJRU5ErkJggg==) + repeat; + background-size: 20px 20px; + border: 1px solid #666; + max-width: 100%; + min-height: 64px; + box-sizing: border-box; + image-rendering: pixelated; +} + +.gl-inspector .btn { + cursor: pointer; + text-decoration: underline; + user-select: none; + padding: 0 0.4em; +} + +.gl-inspector .no-surface { + padding: 20px; +} +.gl-inspector .no-surface ul { + font-size: 16px; +} +.gl-inspector .no-surface li span { + text-decoration: underline; + cursor: pointer; +} diff --git a/packages/cookbook-v2/src/components/GLInspector.tsx b/packages/cookbook-v2/src/components/GLInspector.tsx new file mode 100644 index 00000000..aa915d0c --- /dev/null +++ b/packages/cookbook-v2/src/components/GLInspector.tsx @@ -0,0 +1,1688 @@ +import React, { + createContext, + useContext, + useEffect, + useRef, + useState, + useCallback, + useReducer, + memo, +} from "react"; +import { + CameraIcon, + PlayIcon, + PauseIcon, + ArrowsPointingInIcon, + ArrowsPointingOutIcon, + BoltIcon, + BoltSlashIcon, + CommandLineIcon, + TrashIcon, + ArrowPathIcon, +} from "@heroicons/react/24/solid"; +import { + Visitors, + VisitorLogger, + Node, + Bus, + Uniform, + listSurfaces, + Visitor, +} from "gl-react"; +import type { Surface } from "gl-react"; +import createTexture from "gl-texture2d"; +import createShader from "gl-shader"; +import throttle from "lodash/throttle"; +import "./GLInspector.css"; + +// ─── Constants ─────────────────────────────────────────────────────────────── + +const drawTimeWindow = 1000; +const anchorYOff = 30; + +// ─── Utility Functions ─────────────────────────────────────────────────────── + +const formatNumber = (n: number): string => { + const str = String(n); + const i = str.indexOf("."); + if (i === -1) return str; + return str.slice(0, i + 1 + Math.max(1, 5 - i)); +}; + +const formatObject = (o: unknown): string => { + if (typeof o === "object") { + const name = + o && (o as { constructor?: { name?: string } }).constructor?.name; + if (name) { + return "[object " + String(name) + "]"; + } + } + return String(o); +}; + +const primitiveTypeAlias: Record = { + vec2: Array(2).fill("float"), + vec3: Array(3).fill("float"), + vec4: Array(4).fill("float"), + ivec2: Array(2).fill("int"), + ivec3: Array(3).fill("int"), + ivec4: Array(4).fill("int"), + bvec2: Array(2).fill("bool"), + bvec3: Array(3).fill("bool"), + bvec4: Array(4).fill("bool"), + mat2: Array(4).fill("float"), + mat3: Array(9).fill("float"), + mat4: Array(16).fill("float"), +}; + +const classType = (type: unknown): string => { + if (Array.isArray(type)) return "type-array-" + type[0]; + return "type-" + type; +}; + +const inferSize = (obj: unknown): [number, number] => { + if (obj) { + const o = obj as Record; + if (o.shape) { + return o.shape as [number, number]; + } + if (o.videoWidth && o.videoHeight) { + return [o.videoWidth as number, o.videoHeight as number]; + } + if (o.width && o.height) { + return [o.width as number, o.height as number]; + } + } + return [0, 0]; +}; + +const formatType = (t: unknown): string => { + if (Array.isArray(t)) return t[0] + "[]"; + return String(t); +}; + +// ─── PreviewRenderer ───────────────────────────────────────────────────────── + +class PreviewRenderer { + canvas: HTMLCanvasElement; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + copyShader: any; + gl: WebGLRenderingContext | null = null; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + texture: any; + + constructor() { + const canvas = document.createElement("canvas"); + canvas.width = canvas.height = 2; + const opts = { preserveDrawingBuffer: true }; + const gl = canvas.getContext("webgl", opts) as WebGLRenderingContext | null; + this.canvas = canvas; + if (!gl) return; + this.gl = gl; + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); + const texture = (createTexture as Function)(gl, [2, 2]); + const buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData( + gl.ARRAY_BUFFER, + new Float32Array([ + -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, + ]), + gl.STATIC_DRAW, + ); + const copyShader = (createShader as Function)( + gl, + ` +attribute vec2 _p; +varying vec2 uv; +void main() { + gl_Position = vec4(_p,0.0,1.0); + uv = vec2(0.5, 0.5) * (_p+vec2(1.0, 1.0)); +} +`, + ` +precision highp float; +varying vec2 uv; +uniform sampler2D t; +void main () { + vec4 c = texture2D(t, uv); + gl_FragColor = c; +}`, + ); + copyShader.bind(); + copyShader.attributes._p.pointer(); + this.copyShader = copyShader; + this.texture = texture; + } + + setSize(newWidth: number, newHeight: number) { + const { gl, canvas, texture } = this; + if (!gl) return; + if ( + gl.drawingBufferWidth === newWidth && + gl.drawingBufferHeight === newHeight + ) + return; + texture.shape = [newWidth, newHeight]; + canvas.width = newWidth; + canvas.height = newHeight; + gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + draw(obj: any) { + const { texture, gl, copyShader } = this; + if (!gl) return; + const [w, h] = inferSize(obj); + if (!w || !h) return; + this.setSize(w, h); + texture.setPixels(obj); + copyShader.uniforms.t = texture.bind(); + gl.drawArrays(gl.TRIANGLES, 0, 6); + } + + copyToCanvas2D(ctx: CanvasRenderingContext2D) { + const { canvas } = this; + ctx.canvas.width = canvas.width; + ctx.canvas.height = canvas.height; + ctx.drawImage(canvas, 0, 0); + } +} + +const sharedRenderer = new PreviewRenderer(); + +// ─── Inspector Manager (non-React class) ───────────────────────────────────── + +/** + * AnchorRef holds a ref to the anchor DOM element and draw history data. + * In the old code, Anchor was a React class component with instance methods. + * Here we separate the data model from the rendering. + */ +interface AnchorRef { + el: HTMLSpanElement | null; + drawHistoryDates: number[]; + getDrawCountsForTimeWindow: (w: number) => number[]; + getXY: () => [number, number]; +} + +interface AnchorHookRef { + el: HTMLSpanElement | null; + id: string; + nodeId: number; + anchorId: number; + getXY: () => [number, number]; +} + +interface BoxRef { + el: HTMLDivElement | null; + getSize: () => [number, number]; +} + +interface GrabbingState { + id: number; + initialPos: [number, number]; + initialEventPos: [number, number]; +} + +/** + * InspectorManager holds all mutable state that the inspector uses. + * It extends Visitor to hook into the gl-react draw lifecycle. + * Components subscribe to changes via a forceUpdate trigger. + */ +class InspectorManager extends Visitor { + surface: Surface | null = null; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + preparedUniformsMap: WeakMap = new WeakMap(); + nodeDrawCounts: WeakMap = new WeakMap(); + busDrawCounts: WeakMap = new WeakMap(); + anchorsById: Map = new Map(); + anchorHooksByAnchorId: Map = new Map(); + boxesById: Map = new Map(); + + anchorPositions: Map = new Map(); + anchorHookPositions: Map = new Map(); + + boxMinimized: Map = new Map(); + boxPos: Map = new Map(); + boxVel: Map = new Map(); + boxAcc: Map = new Map(); + boxSizes: Map = new Map(); + grabbing: GrabbingState | null = null; + bounds: DOMRect | null = null; + + _triggerUpdate: (() => void) | null = null; + _minimizeAll = false; + + requestUpdate() { + this._triggerUpdate?.(); + } + + setSurface(surface: Surface | null) { + if (surface === this.surface) return; + this.preparedUniformsMap = new WeakMap(); + this.surface = surface; + if (surface) surface.rebootForDebug(); + this.requestUpdate(); + } + + detectSurface() { + const surface = listSurfaces()[0]; + this.setSurface(surface ?? null); + } + + // ─── Visitor methods ───────────────────────────────────────────────── + + onSurfaceMount() { + if (!this.surface) { + this.detectSurface(); + } + } + + onSurfaceUnmount(surface: Surface) { + if (surface === this.surface) { + this.setSurface(null); + } + } + + onSurfaceGLContextChange() {} + onSurfaceDrawSkipped() {} + onSurfaceDrawStart() {} + onSurfaceDrawError() { + return false; + } + + onSurfaceDrawEnd(surface: Surface) { + if (surface === this.surface) { + this.requestUpdate(); + } + } + + onNodeDrawSkipped() {} + onNodeDrawStart() {} + onNodeSyncDeps() {} + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + onNodeDraw(node: Node, preparedUniforms: any) { + this.preparedUniformsMap.set(node, preparedUniforms); + } + + onNodeDrawEnd(node: Node) { + this.nodeDrawCounts.set(node, (this.nodeDrawCounts.get(node) || 0) + 1); + node.dependencies.forEach((obj) => { + if (obj instanceof Bus) { + this.busDrawCounts.set(obj, (this.busDrawCounts.get(obj) || 0) + 1); + } + }); + } + + // ─── Anchor management ─────────────────────────────────────────────── + + addAnchor(id: number, anchor: AnchorRef) { + this.anchorsById.set(id, anchor); + this.requestUpdate(); + } + + removeAnchor(id: number) { + this.anchorsById.delete(id); + this.requestUpdate(); + } + + addAnchorHook(id: number, hook: AnchorHookRef) { + let hooks = this.anchorHooksByAnchorId.get(id); + if (!hooks) { + this.anchorHooksByAnchorId.set(id, [hook]); + } else { + hooks.push(hook); + } + this.requestUpdate(); + } + + removeAnchorHook(id: number, hook: AnchorHookRef) { + const hooks = this.anchorHooksByAnchorId.get(id); + if (hooks) { + const i = hooks.indexOf(hook); + if (i !== -1) hooks.splice(i, 1); + if (hooks.length === 0) { + this.anchorHooksByAnchorId.delete(id); + } + this.requestUpdate(); + } + } + + // ─── Box management ────────────────────────────────────────────────── + + addBox(id: number, box: BoxRef) { + this.boxesById.set(id, box); + const i = this.boxPos.size; + const pos: [number, number] = [ + 60 + 240 * ((i + 1) % 2), + 40 + 200 * Math.floor(i / 2), + ]; + this.boxPos.set(id, pos); + this.boxMinimized.set(id, this._minimizeAll); + } + + removeBox(id: number) { + this.boxesById.delete(id); + this.boxPos.delete(id); + this.boxSizes.delete(id); + this.boxMinimized.delete(id); + this.boxVel.delete(id); + this.boxAcc.delete(id); + } + + // ─── DOM sync ──────────────────────────────────────────────────────── + + syncFromDom( + nodesEl: HTMLDivElement | null, + bodyEl: HTMLDivElement | null, + ): boolean { + let hasChanged = false; + if (nodesEl && bodyEl) { + this.bounds = bodyEl.getBoundingClientRect(); + const { top: offY, left: offX } = nodesEl.getBoundingClientRect(); + + this.anchorsById.forEach((anchor, id) => { + const [x0, y0] = anchor.getXY(); + const x = Math.round(x0 - offX); + const y = Math.round(y0 - offY); + const old = this.anchorPositions.get(id); + if (!old || x !== old[0] || y !== old[1]) { + hasChanged = true; + this.anchorPositions.set(id, [x, y]); + } + }); + + this.anchorHooksByAnchorId.forEach((anchorHooks) => { + anchorHooks.forEach((anchorHook) => { + const [x0, y0] = anchorHook.getXY(); + const x = Math.round(x0 - offX); + const y = Math.round(y0 - offY); + const old = this.anchorHookPositions.get(anchorHook); + if (!old || x !== old[0] || y !== old[1]) { + hasChanged = true; + this.anchorHookPositions.set(anchorHook, [x, y]); + } + }); + }); + + this.boxesById.forEach((box, id) => { + const size = box.getSize(); + const old = this.boxSizes.get(id); + if (!old || size[0] !== old[0] || size[1] !== old[1]) { + hasChanged = true; + this.boxSizes.set(id, size); + } + }); + } + return hasChanged; + } + + // ─── Physics ───────────────────────────────────────────────────────── + + applyForce(id: number, force: [number, number]) { + const mass = 1; + const acc = this.boxAcc.get(id) || [0, 0]; + this.boxAcc.set(id, [ + acc[0] + force[0] / mass, + acc[1] + force[1] / mass, + ]); + } + + spring() { + this.anchorsById.forEach((anchor, anchorId) => { + const hooks = this.anchorHooksByAnchorId.get(anchorId) || []; + const anchorPos = this.anchorPositions.get(anchorId); + if (!anchorPos) return; + hooks.forEach((hook) => { + const hookNodeId = hook.nodeId; + if (anchorId !== hookNodeId) { + const hookPos = this.anchorHookPositions.get(hook); + if (!hookPos) return; + const dx = anchorPos[0] - hookPos[0]; + const dy = anchorPos[1] - hookPos[1]; + const magn = Math.sqrt(dx * dx + dy * dy); + const dist = magn + 0.1; + const dir: [number, number] = [dx / magn, dy / magn]; + const m = 8 / (dist * dist); + this.applyForce(anchorId, [m * dir[0], m * dir[1]]); + this.applyForce(hookNodeId, [-m * dir[0], -m * dir[1]]); + const length = 1.0; + const disp = length - magn; + const n = 0.5 * 0.00001 * disp; + this.applyForce(hookNodeId, [-n * dir[0], -n * dir[1]]); + this.applyForce(anchorId, [n * dir[0], n * dir[1]]); + } + }); + }); + + const boxIds = [...this.boxPos.keys()]; + for (let i = 0; i < boxIds.length; i++) { + const box1id = boxIds[i]; + const box1pos = this.boxPos.get(box1id); + const box1size = this.boxSizes.get(box1id); + if (!box1size) continue; + for (let j = i + 1; j < boxIds.length; j++) { + const box2id = boxIds[j]; + const box2pos = this.boxPos.get(box2id); + const box2size = this.boxSizes.get(box2id); + if (!box2size || !box2pos || !box1pos) continue; + const dx = + box1pos[0] + + box1size[0] / 2 - + (box2pos[0] + box2size[0]); + const dy = + box1pos[1] + + box1size[1] / 2 - + (box2pos[1] + box2size[1]); + const magn = Math.sqrt(dx * dx + dy * dy); + const dist = magn + 0.1; + const dir: [number, number] = [dx / magn, dy / magn]; + const m = 6 / (dist * dist); + this.applyForce(box1id, [m * dir[0], m * dir[1]]); + this.applyForce(box2id, [-m * dir[0], -m * dir[1]]); + } + } + } + + updateVelocityPosition(timestep: number) { + const damping = 0.5; + const grabId = this.grabbing?.id; + this.boxPos.forEach((p, id) => { + if (id === grabId) return; + const a = this.boxAcc.get(id) || [0, 0]; + let v = this.boxVel.get(id) || [0, 0]; + const size = this.boxSizes.get(id) || [0, 0]; + v = [ + (v[0] + a[0] * timestep) * damping, + (v[1] + a[1] * timestep) * damping, + ]; + const newP: [number, number] = [ + p[0] + v[0] * timestep, + p[1] + v[1] * timestep, + ]; + if (this.bounds) { + const pad = 20; + newP[0] = Math.max( + pad, + Math.min(newP[0], this.bounds.width - size[0] - pad), + ); + newP[1] = Math.max( + pad, + Math.min(newP[1], this.bounds.height - size[1] - pad), + ); + } + this.boxVel.set(id, v); + this.boxAcc.set(id, [0, 0]); + this.boxPos.set(id, newP); + }); + } + + physicsStep(timestep: number): boolean { + this.spring(); + this.updateVelocityPosition(timestep); + let energy = 0.0; + this.boxVel.forEach((v) => { + energy += v[0] * v[0] + v[1] * v[1]; + }); + return energy > 0.00001; + } +} + +// ─── React Context ─────────────────────────────────────────────────────────── + +const InspectorContext = createContext(null); + +function useInspector(): InspectorManager { + const ctx = useContext(InspectorContext); + if (!ctx) throw new Error("useInspector must be used within GLInspector"); + return ctx; +} + +// ─── Small Components ──────────────────────────────────────────────────────── + +function Btn({ + onClick, + children, +}: { + onClick?: (() => void) | null; + children?: React.ReactNode; +}) { + return ( + + {children} + + ); +} + +// ─── Anchor ────────────────────────────────────────────────────────────────── + +function Anchor({ + id, + drawCount, +}: { + id: number; + drawCount: number; +}) { + const mgr = useInspector(); + const elRef = useRef(null); + const anchorRef = useRef(null); + const prevDrawCountRef = useRef(drawCount); + + // Create and register the anchor ref object once + useEffect(() => { + const anchor: AnchorRef = { + el: null, + drawHistoryDates: [], + getDrawCountsForTimeWindow(w: number) { + const now = Date.now(); + const values: number[] = []; + for (let i = anchor.drawHistoryDates.length - 1; i > 0; i--) { + const t = (now - anchor.drawHistoryDates[i]) / w; + if (t > 1) break; + values.push(t); + } + return values; + }, + getXY() { + if (!anchor.el) return [0, 0]; + const { top, left, height } = anchor.el.getBoundingClientRect(); + return [left, top + height / 2]; + }, + }; + anchorRef.current = anchor; + mgr.addAnchor(id, anchor); + return () => { + mgr.removeAnchor(id); + }; + }, [id, mgr]); + + // Sync the DOM element ref + useEffect(() => { + if (anchorRef.current) { + anchorRef.current.el = elRef.current; + } + }); + + // Track draw count changes + useEffect(() => { + const anchor = anchorRef.current; + if (!anchor) return; + for (let i = prevDrawCountRef.current; i < drawCount; i++) { + anchor.drawHistoryDates.push(Date.now()); + if (anchor.drawHistoryDates.length > 200) { + anchor.drawHistoryDates.shift(); + } + } + prevDrawCountRef.current = drawCount; + }, [drawCount]); + + return ; +} + +// ─── AnchorHook ────────────────────────────────────────────────────────────── + +function AnchorHook({ + id, + nodeId, + anchorId, +}: { + id: string; + nodeId: number; + anchorId: number; +}) { + const mgr = useInspector(); + const elRef = useRef(null); + const hookRef = useRef(null); + + useEffect(() => { + const hook: AnchorHookRef = { + el: null, + id, + nodeId, + anchorId, + getXY() { + if (!hook.el) return [0, 0]; + const { top, left, height } = hook.el.getBoundingClientRect(); + return [left, top + height / 2]; + }, + }; + hookRef.current = hook; + mgr.addAnchorHook(anchorId, hook); + return () => { + mgr.removeAnchorHook(anchorId, hook); + }; + }, [id, nodeId, anchorId, mgr]); + + // Sync the DOM element ref + useEffect(() => { + if (hookRef.current) { + hookRef.current.el = elRef.current; + } + }); + + return ; +} + +// ─── MetaInfo ──────────────────────────────────────────────────────────────── + +interface MetaInfoData { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + obj?: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + initialObj?: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + dependency?: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + textureOptions?: any; +} + +function MetaInfo({ + id, + node, + info, +}: { + id: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + node: any; + info: MetaInfoData; +}) { + const { dependency, obj } = info; + const isBackbuffer = obj === Uniform.Backbuffer; + const isBackbufferFrom = + obj && typeof obj === "object" && obj.type === "BackbufferFrom"; + return ( + + {dependency ? ( + + ) : isBackbuffer ? ( + + ) : isBackbufferFrom ? ( + + ) : null} + {dependency ? ( + dependency.getGLShortName() + ) : isBackbuffer ? ( + "Backbuffer" + ) : isBackbufferFrom ? ( + "BackbufferFrom" + ) : typeof obj === "string" ? ( + + {obj} + + ) : ( + formatObject(obj) + )} + + ); +} + +// ─── UniformValue ──────────────────────────────────────────────────────────── + +function UniformValue({ + id, + node, + value, + type, + info, +}: { + id: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + node: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + value: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + type: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + info: any; +}) { + let resolvedType = type; + if (typeof resolvedType === "string" && resolvedType in primitiveTypeAlias) { + resolvedType = primitiveTypeAlias[resolvedType]; + } + if (Array.isArray(resolvedType)) { + return ( + + {resolvedType.map((t: string, i: number) => ( + + ))} + + ); + } + return ( + + + {typeof value === "number" ? formatNumber(value) : String(value)} + + {info ? : null} + + ); +} + +// ─── Uniforms ──────────────────────────────────────────────────────────────── + +const Uniforms = memo(function Uniforms({ + node, + preparedUniforms, +}: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + node: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + preparedUniforms: any[] | null | undefined; +}) { + return ( +
+ {preparedUniforms && + // eslint-disable-next-line @typescript-eslint/no-explicit-any + preparedUniforms.map((u: any) => ( +
+ + {u.key} + + + + +
+ ))} +
+ ); +}); + +// ─── Preview ───────────────────────────────────────────────────────────────── + +function Preview({ capture }: { capture: () => unknown }) { + const ctxRef = useRef(null); + + const doCapture = useRef( + throttle(() => { + const ctx = ctxRef.current; + if (!ctx) return; + const snap = capture(); + if (snap) { + sharedRenderer.draw(snap); + sharedRenderer.copyToCanvas2D(ctx); + } + }, 100), + ).current; + + // Re-capture whenever the component renders (props changed) + useEffect(() => { + doCapture(); + }); + + useEffect(() => { + return () => { + doCapture.cancel(); + }; + }, [doCapture]); + + const onCanvasRef = useCallback( + (canvas: HTMLCanvasElement | null) => { + if (!canvas || ctxRef.current) return; + const ctx = canvas.getContext("2d"); + if (!ctx) return; + ctx.imageSmoothingEnabled = false; + ctxRef.current = ctx; + doCapture(); + }, + [doCapture], + ); + + return ( +
+ +
+ ); +} + +function PreviewNode({ node }: { node: Node }) { + const capture = useCallback(() => { + try { + return node.capture(); + } catch (_e) { + return undefined; + } + }, [node]); + return ; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function PreviewContent({ content }: { content: any }) { + const capture = useCallback(() => content, [content]); + return ; +} + +// ─── DrawCount ─────────────────────────────────────────────────────────────── + +const DrawCount = memo(function DrawCount({ + drawCount, +}: { + drawCount: number; +}) { + return {drawCount}; +}); + +// ─── InspectorBox ──────────────────────────────────────────────────────────── + +function InspectorBox({ + animated: _animated, + drawCount, + cls, + pos, + glObject, + mode, + width, + height, + children, + grabbed, + minimized, + onGrabStart, + onMinimizeChange, +}: { + animated: boolean; + drawCount: number; + cls: string; + pos: [number, number]; + glObject: Bus | Node; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + mode?: any; + width: number; + height: number; + children?: React.ReactNode; + grabbed?: boolean; + minimized: boolean; + onGrabStart: (id: number, e: React.MouseEvent) => void; + onMinimizeChange: (id: number, minimized: boolean) => void; +}) { + const mgr = useInspector(); + const boxElRef = useRef(null); + const [recentDraw, setRecentDraw] = useState(false); + const timeoutRef = useRef>(); + const prevDrawCountRef = useRef(drawCount); + + // Register box + useEffect(() => { + const boxRef: BoxRef = { + el: null, + getSize() { + if (!boxElRef.current) return [0, 0]; + const { width: w, height: h } = + boxElRef.current.getBoundingClientRect(); + return [w, h]; + }, + }; + mgr.addBox(glObject.id, boxRef); + return () => { + mgr.removeBox(glObject.id); + }; + }, [glObject.id, mgr]); + + // Sync the DOM ref into the boxRef + useEffect(() => { + const box = mgr.boxesById.get(glObject.id); + if (box) { + box.el = boxElRef.current; + } + }); + + // Handle new draws + useEffect(() => { + if (drawCount !== prevDrawCountRef.current) { + prevDrawCountRef.current = drawCount; + setRecentDraw(true); + clearTimeout(timeoutRef.current); + timeoutRef.current = setTimeout(() => setRecentDraw(false), 20); + } + }, [drawCount]); + + useEffect(() => { + return () => clearTimeout(timeoutRef.current); + }, []); + + const onMouseDown = useCallback( + (e: React.MouseEvent) => { + e.preventDefault(); + onGrabStart(glObject.id, e); + }, + [glObject.id, onGrabStart], + ); + + const onClickMinimize = useCallback( + (e: React.MouseEvent) => { + e.preventDefault(); + onMinimizeChange(glObject.id, !minimized); + }, + [glObject.id, minimized, onMinimizeChange], + ); + + const onRedraw = useCallback(() => { + (glObject as Node).redraw?.(); + }, [glObject]); + + const className = [ + "box", + cls, + recentDraw && "recent-draw", + grabbed && "grabbed", + minimized && "minimized", + ] + .filter(Boolean) + .join(" "); + + return ( +
+
+ + {glObject.getGLShortName()} + + + +
+ {children} +
+ {"\u2195"} + + {width} + {"\u2A09"} + {height} + + {mode} +
+
+ ); +} + +// ─── SVG Connection Components ─────────────────────────────────────────────── + +// Cubic bezier evaluation at parameter t +function cubicBezier( + t: number, + p0: [number, number], + p1: [number, number], + p2: [number, number], + p3: [number, number], +): [number, number] { + const u = 1 - t; + const uu = u * u; + const tt = t * t; + return [ + uu * u * p0[0] + 3 * uu * t * p1[0] + 3 * u * tt * p2[0] + tt * t * p3[0], + uu * u * p0[1] + 3 * uu * t * p1[1] + 3 * u * tt * p2[1] + tt * t * p3[1], + ]; +} + +function lerp(t: number, a: [number, number], b: [number, number]): [number, number] { + return [a[0] + t * (b[0] - a[0]), a[1] + t * (b[1] - a[1])]; +} + +// Build path segments as evaluable functions + SVG d string +function buildPath( + anchorX: number, + anchorY: number, + hookX: number, + hookY: number, + recursive?: boolean, + reversedHook?: boolean, +) { + const dx = hookX - anchorX; + const dy = hookY - anchorY; + const factor = Math.abs(dx) + Math.abs(dy); + const ct = Math.round((recursive ? 0.1 : 0.3) * factor); + const s = Math.round((recursive ? 0.02 : 0.05) * factor); + const rh = reversedHook ? -1 : 1; + + const d = + `M${anchorX},${anchorY} ` + + `L${anchorX + s},${anchorY} ` + + `C${anchorX + ct},${anchorY} ` + + (recursive + ? `${anchorX + ct},${anchorY - anchorYOff} ` + + `${anchorX + s},${anchorY - anchorYOff} ` + + `L${hookX - s},${anchorY - anchorYOff} ` + + `C${hookX - ct},${anchorY - anchorYOff} ` + : "") + + `${hookX - ct * rh},${hookY} ` + + `${hookX - s * rh},${hookY} ` + + `L${hookX},${hookY}`; + + // Build segments for parametric evaluation + type Seg = { len: number; eval: (t: number) => [number, number] }; + const segs: Seg[] = []; + + const p0: [number, number] = [anchorX, anchorY]; + const p1: [number, number] = [anchorX + s, anchorY]; + segs.push({ len: Math.abs(s), eval: (t) => lerp(t, p0, p1) }); + + if (recursive) { + const c1s: [number, number] = [anchorX + s, anchorY]; + const c1c1: [number, number] = [anchorX + ct, anchorY]; + const c1c2: [number, number] = [anchorX + ct, anchorY - anchorYOff]; + const c1e: [number, number] = [anchorX + s, anchorY - anchorYOff]; + segs.push({ len: factor * 0.4, eval: (t) => cubicBezier(t, c1s, c1c1, c1c2, c1e) }); + + const l2s: [number, number] = [anchorX + s, anchorY - anchorYOff]; + const l2e: [number, number] = [hookX - s, anchorY - anchorYOff]; + segs.push({ len: Math.abs(hookX - s - anchorX - s), eval: (t) => lerp(t, l2s, l2e) }); + + const c2s: [number, number] = [hookX - s, anchorY - anchorYOff]; + const c2c1: [number, number] = [hookX - ct, anchorY - anchorYOff]; + const c2c2: [number, number] = [hookX - ct * rh, hookY]; + const c2e: [number, number] = [hookX - s * rh, hookY]; + segs.push({ len: factor * 0.4, eval: (t) => cubicBezier(t, c2s, c2c1, c2c2, c2e) }); + } else { + const cs: [number, number] = [anchorX + s, anchorY]; + const cc1: [number, number] = [anchorX + ct, anchorY]; + const cc2: [number, number] = [hookX - ct * rh, hookY]; + const ce: [number, number] = [hookX - s * rh, hookY]; + segs.push({ len: factor * 0.6, eval: (t) => cubicBezier(t, cs, cc1, cc2, ce) }); + } + + const pEnd: [number, number] = [hookX - s * rh, hookY]; + const pFinal: [number, number] = [hookX, hookY]; + segs.push({ len: Math.abs(s), eval: (t) => lerp(t, pEnd, pFinal) }); + + const totalLen = segs.reduce((a, s) => a + Math.max(s.len, 1), 0); + + function evalAt(u: number): [number, number] { + let remaining = u * totalLen; + for (const seg of segs) { + const sl = Math.max(seg.len, 1); + if (remaining <= sl) return seg.eval(remaining / sl); + remaining -= sl; + } + return pFinal; + } + + return { d, evalAt }; +} + +function SVGConnection({ + anchor, + hookX, + hookY, + anchorX, + anchorY, + recursive, + reversedHook, + animated, +}: { + anchor: AnchorRef; + hookX: number; + hookY: number; + anchorX: number; + anchorY: number; + recursive?: boolean; + reversedHook?: boolean; + animated?: boolean; +}) { + const [dots, setDots] = useState<[number, number][]>([]); + const rafRef = useRef(0); + const path = buildPath(anchorX, anchorY, hookX, hookY, recursive, reversedHook); + + useEffect(() => { + if (!animated) { + setDots([]); + return; + } + const loop = () => { + rafRef.current = requestAnimationFrame(loop); + const values = anchor.getDrawCountsForTimeWindow(drawTimeWindow); + if (values.length === 0) { + setDots((prev) => (prev.length ? [] : prev)); + return; + } + setDots(values.map((v) => path.evalAt(v))); + }; + rafRef.current = requestAnimationFrame(loop); + return () => cancelAnimationFrame(rafRef.current); + }, [anchor, animated, path.d]); + + return ( + + + {dots.map(([x, y], i) => ( + + ))} + + ); +} + +function SVGStandaloneConnection({ + animated, + anchor, + anchorX, + anchorY, + hookX, + hookY, +}: { + animated: boolean; + anchor: AnchorRef; + anchorX: number; + anchorY: number; + hookX: number; + hookY: number; +}) { + return ( + + + + + ); +} + +// ─── HookDrawer ────────────────────────────────────────────────────────────── + +function HookDrawer({ + animated, + anchorHooksByAnchorId, + anchorsById, + anchorPositions, + anchorHookPositions, + boxSizes, + grabbing, +}: { + animated: boolean; + anchorHooksByAnchorId: Map; + anchorsById: Map; + anchorPositions: Map; + anchorHookPositions: Map; + boxSizes: Map; + grabbing: GrabbingState | null; +}) { + return ( + + {[...anchorsById].map(([anchorId, anchor]) => { + const hooks = anchorHooksByAnchorId.get(anchorId) || []; + const anchorPosition = anchorPositions.get(anchorId); + const size = boxSizes.get(anchorId); + const anchorIsGrabbed = grabbing?.id === anchorId; + if (!anchorPosition || !size) return null; + return ( + + {hooks.length === 0 ? ( + + ) : ( + hooks.map((hook) => { + const hookId = hook.id; + const hookNodeId = hook.nodeId; + const hookIsGrabbed = grabbing?.id === hookNodeId; + const hookPosition = anchorHookPositions.get(hook); + if (!hookPosition) return null; + return ( + + + + + ); + }) + )} + + + ); + })} + + ); +} + +// ─── GLInspector (main component) ──────────────────────────────────────────── + +const inspectorVisitorLogger = new VisitorLogger(); + +export default function GLInspector() { + const [, forceUpdate] = useReducer((x: number) => x + 1, 0); + const [capture, setCapture] = useState(false); + const [animated, setAnimated] = useState(true); + const [physics, setPhysics] = useState(true); + const [minimizeAll, setMinimizeAll] = useState(false); + + const mgrRef = useRef(null); + if (!mgrRef.current) { + mgrRef.current = new InspectorManager(); + } + const mgr = mgrRef.current; + + const nodesRef = useRef(null); + const bodyRef = useRef(null); + const loseContextExtRef = useRef(null); + + // Wire up the forceUpdate trigger + useEffect(() => { + mgr._triggerUpdate = forceUpdate; + return () => { + mgr._triggerUpdate = null; + }; + }, [mgr]); + + // Register as visitor and start animation loop + useEffect(() => { + Visitors.add(mgr); + const startupTimeout = setTimeout(() => mgr.detectSurface(), 0); + + let lastT: number | undefined; + let rafId: number; + const loop = (t: number) => { + rafId = requestAnimationFrame(loop); + if (!lastT) lastT = t; + const delta = Math.min(100, t - lastT); + lastT = t; + const syncChanged = mgr.syncFromDom(nodesRef.current, bodyRef.current); + const physicsChanged = physics && mgr.physicsStep(delta); + if (syncChanged || physicsChanged) { + forceUpdate(); + } + }; + rafId = requestAnimationFrame(loop); + + return () => { + cancelAnimationFrame(rafId); + clearTimeout(startupTimeout); + Visitors.remove(mgr); + }; + // physics is intentionally captured by this effect so we get the latest + // value. The effect re-runs when physics changes which is fine. + }, [mgr, physics]); + + // ─── Event handlers ──────────────────────────────────────────────── + + const onGrabStart = useCallback( + (id: number, e: React.MouseEvent) => { + const boxPos = mgr.boxPos.get(id) || [0, 0]; + mgr.grabbing = { + id, + initialPos: boxPos, + initialEventPos: [e.clientX, e.clientY], + }; + forceUpdate(); + }, + [mgr], + ); + + const onMouseMove = useCallback( + (e: React.MouseEvent) => { + if (mgr.grabbing) { + const { id, initialPos, initialEventPos } = mgr.grabbing; + const dx = e.clientX - initialEventPos[0]; + const dy = e.clientY - initialEventPos[1]; + const pos: [number, number] = [initialPos[0] + dx, initialPos[1] + dy]; + mgr.boxPos.set(id, pos); + forceUpdate(); + } + }, + [mgr], + ); + + const onMouseLeave = useCallback(() => { + if (mgr.grabbing) { + mgr.grabbing = null; + forceUpdate(); + } + }, [mgr]); + + const onMouseUp = useCallback(() => { + if (mgr.grabbing) { + mgr.grabbing = null; + forceUpdate(); + } + }, [mgr]); + + const onMinimizeChange = useCallback( + (id: number, min: boolean) => { + mgr.boxMinimized.set(id, min); + forceUpdate(); + }, + [mgr], + ); + + const onSelectChange = useCallback( + (e: React.ChangeEvent) => { + const { selectedIndex } = e.target; + mgr.setSurface( + selectedIndex === 0 ? null : listSurfaces()[selectedIndex - 1] ?? null, + ); + }, + [mgr], + ); + + const loseContext = useCallback(() => { + const { surface } = mgr; + if (surface) { + const { gl } = surface; + if (gl) { + loseContextExtRef.current = gl.getExtension("WEBGL_lose_context"); + loseContextExtRef.current?.loseContext(); + } + } + forceUpdate(); + }, [mgr]); + + const restoreContext = useCallback(() => { + loseContextExtRef.current?.restoreContext(); + loseContextExtRef.current = null; + forceUpdate(); + }, []); + + const [consoleLogsOn, setConsoleLogsOn] = useState(false); + const onVisitorLoggerChange = useCallback(() => { + setConsoleLogsOn((prev) => { + const next = !prev; + Visitors.remove(inspectorVisitorLogger); + if (next) Visitors.add(inspectorVisitorLogger); + return next; + }); + }, []); + + // ─── Render ──────────────────────────────────────────────────────── + + const { surface, boxPos, grabbing } = mgr; + const key = surface?.id; + + let headerBody: React.ReactNode = null; + let body: React.ReactNode = null; + + if (surface) { + const root = surface.root; + const nodeElements: React.ReactNode[] = []; + + if (root) { + let stack: Array = [root]; + const explored: Record = {}; + while (stack.length > 0) { + const n = stack.pop()!; + const deps = n instanceof Node ? n.dependencies : []; + stack = deps.concat(stack); + const { id } = n; + if (explored[id]) continue; + explored[id] = true; + const pos = boxPos.get(id) || ([0, 0] as [number, number]); + const boxMinimized = mgr.boxMinimized.get(id) || false; + const sharedProps = { + animated, + pos, + glObject: n, + grabbed: grabbing ? grabbing.id === id : false, + minimized: boxMinimized, + onMinimizeChange, + onGrabStart, + }; + if (n instanceof Node) { + const [w, h] = n.getGLSize(); + const drawCount = mgr.nodeDrawCounts.get(n) ?? 0; + nodeElements.push( + + + {capture && !boxMinimized ? ( + + ) : null} + , + ); + } else { + const content = (n as Bus).getGLRenderableContent(); + const [w, h] = inferSize(content); + const drawCount = mgr.busDrawCounts.get(n as Bus) ?? 0; + nodeElements.push( + +
+ {(content && content.outerHTML) || null} +
+ {capture && !boxMinimized ? ( + + ) : null} +
, + ); + } + } + + const lost = !surface.gl || loseContextExtRef.current; + headerBody = ( + <> + + + + +
+ {lost ? ( + + ) : ( + + )} + + ); + + body = ( +
+
+ {nodeElements} +
+ +
+ ); + } + } else { + body = ( +
+
+

No GL Surface detected. Navigate to an example to inspect it.

+
    + {listSurfaces().map((s) => ( +
  • + mgr.setSurface(s)}> + {s.getGLName()} + +
  • + ))} +
+
+
+ ); + } + + return ( + +
+
+ + +
+
+ {headerBody} +
+
+ {body} +
+
+ ); +} diff --git a/packages/cookbook-v2/src/components/Layout.tsx b/packages/cookbook-v2/src/components/Layout.tsx index a2635c29..880e1664 100644 --- a/packages/cookbook-v2/src/components/Layout.tsx +++ b/packages/cookbook-v2/src/components/Layout.tsx @@ -1,122 +1,59 @@ -import React, { useState } from 'react' -import { Link, useLocation } from 'react-router-dom' -import { - Bars3Icon, - XMarkIcon, - CodeBracketIcon, - BeakerIcon, - BookOpenIcon, - HomeIcon -} from '@heroicons/react/24/outline' - -interface LayoutProps { - children: React.ReactNode -} - -const navigation = [ - { name: 'Home', href: '/', icon: HomeIcon }, - { name: 'Examples', href: '/examples', icon: BeakerIcon }, -] - -export function Layout({ children }: LayoutProps) { - const [sidebarOpen, setSidebarOpen] = useState(false) - const location = useLocation() +import { useState, useEffect, useRef, type ReactNode } from 'react' +import { XMarkIcon, EyeIcon } from '@heroicons/react/24/outline' +import GLInspector from './GLInspector' + +const DEFAULT_HEIGHT = 500 + +export function Layout({ children }: { children: ReactNode }) { + const [open, setOpen] = useState(false) + const [height, setHeight] = useState(DEFAULT_HEIGHT) + const [hoveredCanvas, setHoveredCanvas] = useState(null) + const overlayRef = useRef(null) + + const openInspector = () => { setHeight(DEFAULT_HEIGHT); setOpen(true) } + + const onResizeStart = (e: React.MouseEvent) => { + e.preventDefault() + const startY = e.clientY, startH = height + const onMove = (e: MouseEvent) => { + const h = startH + startY - e.clientY + if (h < 100) { setOpen(false); cleanup() } + else setHeight(Math.min(window.innerHeight - 100, h)) + } + const onUp = () => cleanup() + const cleanup = () => { document.removeEventListener('mousemove', onMove); document.removeEventListener('mouseup', onUp) } + document.addEventListener('mousemove', onMove) + document.addEventListener('mouseup', onUp) + } + + useEffect(() => { + const onOver = (e: MouseEvent) => setHoveredCanvas((e.target as HTMLElement).closest('canvas') as HTMLCanvasElement | null) + const onOut = (e: MouseEvent) => { if (!(e.relatedTarget as HTMLElement)?.closest('canvas') && e.relatedTarget !== overlayRef.current) setHoveredCanvas(null) } + document.addEventListener('mouseover', onOver) + document.addEventListener('mouseout', onOut) + return () => { document.removeEventListener('mouseover', onOver); document.removeEventListener('mouseout', onOut) } + }, []) return ( -
- {/* Mobile sidebar */} -
-
setSidebarOpen(false)} /> -
-
- - - GL React - - -
- -
-
- - {/* Desktop sidebar */} -
-
-
- - - GL React - -
- +
+
+
{children}
+
+ + {!open && hoveredCanvas && (() => { + const r = hoveredCanvas.getBoundingClientRect() + return + })()} + + {!open && } + + {open && ( +
+
+
+
-
- - {/* Main content */} -
- {/* Top bar */} -
- -
- - {/* Page content */} -
-
-
- {children} -
-
-
-
+ )}
) } - - diff --git a/packages/cookbook-v2/src/examples/animated.tsx b/packages/cookbook-v2/src/examples/animated.tsx index 98ce5259..9db3266b 100644 --- a/packages/cookbook-v2/src/examples/animated.tsx +++ b/packages/cookbook-v2/src/examples/animated.tsx @@ -16,7 +16,7 @@ void main() { }, }); -// Simple spring physics for smooth cursor following +// Spring physics for smooth cursor following function useSpring(target: { x: number; y: number }) { const [pos, setPos] = useState(target); const velRef = useRef({ x: 0, y: 0 }); @@ -34,7 +34,6 @@ function useSpring(target: { x: number; y: number }) { const t = targetRef.current; const v = velRef.current; - // Spring force const ax = stiffness * (t.x - pos.x) - damping * v.x; const ay = stiffness * (t.y - pos.y) - damping * v.y; @@ -57,6 +56,7 @@ export default function Animated() { const [target, setTarget] = useState({ x: 0.5, y: 0.5 }); const pos = useSpring(target); + // Convert DOM mouse position to UV coordinates (Y-flipped for GL) const onMouseMove = useCallback((e: React.MouseEvent) => { const canvas = (e.currentTarget as HTMLElement).querySelector("canvas"); if (!canvas) return; diff --git a/packages/cookbook-v2/src/examples/behindasteroids.tsx b/packages/cookbook-v2/src/examples/behindasteroids.tsx index ffc22c58..79aa2049 100644 --- a/packages/cookbook-v2/src/examples/behindasteroids.tsx +++ b/packages/cookbook-v2/src/examples/behindasteroids.tsx @@ -3,12 +3,6 @@ import { Shaders, Node, GLSL, Bus, Uniform } from "gl-react"; import { Surface } from "gl-react-dom"; import { useTimeLoop } from "../hooks/useTimeLoop"; -/** - * This example reproduces the after effects made in a js13k game: - * https://github.com/gre/behind-asteroids - * Simplified to just the shader composition without the game logic. - */ - const shaders = Shaders.create({ blur1d: { frag: GLSL` @@ -34,13 +28,8 @@ void main() { frag: GLSL` precision highp float; varying vec2 uv; -uniform sampler2D G; -uniform sampler2D R; -uniform sampler2D B; -uniform sampler2D L; -uniform sampler2D E; -uniform float s; -uniform float F; +uniform sampler2D G, R, B, L, E; +uniform float s, F; uniform vec2 k; uniform float S; float squircleDist (vec2 a, vec2 b) { @@ -209,7 +198,6 @@ void main() { } `, }, - // Simple demo scene that generates game-like content demoScene: { frag: GLSL` precision highp float; @@ -218,12 +206,10 @@ uniform float time; void main() { float t = time; vec3 c = vec3(0.0); - // Fake laser beams float beam1 = smoothstep(0.01, 0.0, abs(uv.y - 0.5 - 0.3*sin(t*2.0+uv.x*10.0))); float beam2 = smoothstep(0.01, 0.0, abs(uv.y - 0.5 + 0.2*cos(t*3.0+uv.x*8.0))); c += vec3(0.0, 0.5, 1.0) * beam1; c += vec3(1.0, 0.3, 0.0) * beam2; - // Stars float star = step(0.99, fract(sin(dot(floor(uv*40.0), vec2(12.9898,78.233)))*43758.5453)); c += vec3(star) * 0.5; gl_FragColor = vec4(c, 1.0); @@ -255,7 +241,6 @@ export default function BehindAsteroids() { const H = 400; const dim: [number, number] = [W, H]; - // Simulated game params that animate over time const t = time / 1000; const pt = t; const pl = 0; diff --git a/packages/cookbook-v2/src/examples/blurfeedback.tsx b/packages/cookbook-v2/src/examples/blurfeedback.tsx index 71ded5ac..0b1564c9 100644 --- a/packages/cookbook-v2/src/examples/blurfeedback.tsx +++ b/packages/cookbook-v2/src/examples/blurfeedback.tsx @@ -54,7 +54,7 @@ export default function BlurFeedback({ if (!initialized) setInitialized(true); }; - void tick; + void tick; // tick triggers re-renders for the feedback loop return ( @@ -68,6 +68,7 @@ export default function BlurFeedback({ onDraw={onDraw} /> ) : ( + // Each frame reads its own previous output via Backbuffer ); +// Renders text to a 2D canvas, wrapped in LinearCopy for use as texture function Title({ children, width, diff --git a/packages/cookbook-v2/src/examples/blurmap.tsx b/packages/cookbook-v2/src/examples/blurmap.tsx index 03cddeef..e745d934 100644 --- a/packages/cookbook-v2/src/examples/blurmap.tsx +++ b/packages/cookbook-v2/src/examples/blurmap.tsx @@ -4,6 +4,7 @@ import { Surface } from "gl-react-dom"; const NORM = Math.sqrt(2) / 2; +// Cycles through H, V, and diagonal directions with increasing radius const directionForPass = ( p: number, factor: number, @@ -41,6 +42,7 @@ vec4 blur9(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { return color; } void main() { + // Direction scaled by map value makes blur strength vary per-pixel gl_FragColor = blur9(t, uv, resolution, direction * texture2D(map, uv).rg); }`, }, @@ -67,6 +69,7 @@ export const BlurV1D = connectSize( ) ); +// Recursively nests BlurV1D passes for multi-directional blur export const BlurV = connectSize( ({ children, diff --git a/packages/cookbook-v2/src/examples/blurmulti.tsx b/packages/cookbook-v2/src/examples/blurmulti.tsx index 0d6df4be..c1aceb6d 100644 --- a/packages/cookbook-v2/src/examples/blurmulti.tsx +++ b/packages/cookbook-v2/src/examples/blurmulti.tsx @@ -3,6 +3,7 @@ import { connectSize } from "gl-react"; import { Surface } from "gl-react-dom"; import { Blur1D } from "./blurxy"; +// 4 directional blur passes for a smoother result export const BlurV = connectSize(({ children, factor }: any) => { const s = factor; return ( diff --git a/packages/cookbook-v2/src/examples/blurvideo.tsx b/packages/cookbook-v2/src/examples/blurvideo.tsx index aa3fc4b2..cf2c7f31 100644 --- a/packages/cookbook-v2/src/examples/blurvideo.tsx +++ b/packages/cookbook-v2/src/examples/blurvideo.tsx @@ -13,7 +13,6 @@ const blurMapImages = [ "https://i.imgur.com/wh0On3P.png", ]; -// Inner class component for Bus ref stability class BlurVideoInner extends Component<{ video: HTMLVideoElement; factor: number; diff --git a/packages/cookbook-v2/src/examples/blurxy.tsx b/packages/cookbook-v2/src/examples/blurxy.tsx index 4cebe69f..5451f370 100644 --- a/packages/cookbook-v2/src/examples/blurxy.tsx +++ b/packages/cookbook-v2/src/examples/blurxy.tsx @@ -26,6 +26,7 @@ void main() { }, }); +// connectSize injects width/height for correct texel offset calculation export const Blur1D = connectSize( ({ children: t, direction, width, height }: any) => ( ( {children} diff --git a/packages/cookbook-v2/src/examples/blurxydownscale.tsx b/packages/cookbook-v2/src/examples/blurxydownscale.tsx index dd0a82b1..03c62ba4 100644 --- a/packages/cookbook-v2/src/examples/blurxydownscale.tsx +++ b/packages/cookbook-v2/src/examples/blurxydownscale.tsx @@ -5,6 +5,7 @@ import { BlurXY } from "./blurxy"; export default function BlurXYDownscale({ factor = 2 }: { factor?: number }) { return ( + {/* Blur runs at 100x75 then upscales to 400x300 for performance */} {"https://i.imgur.com/iPKTONG.jpg"} diff --git a/packages/cookbook-v2/src/examples/colorscale.tsx b/packages/cookbook-v2/src/examples/colorscale.tsx index 0658f026..5caf3958 100644 --- a/packages/cookbook-v2/src/examples/colorscale.tsx +++ b/packages/cookbook-v2/src/examples/colorscale.tsx @@ -12,6 +12,7 @@ float greyscale (vec3 c) { return 0.2125 * c.r + 0.7154 * c.g + 0.0721 * c.b; } void main() { vec4 original = texture2D(children, uv); float grey = greyscale(original.rgb); + // Grayscale value indexes into the color scale gradient (1D LUT) gl_FragColor = vec4(texture2D(colorScale, vec2(grey, 0.5)).rgb, 1.0); }`, }, diff --git a/packages/cookbook-v2/src/examples/demodesertcrt.tsx b/packages/cookbook-v2/src/examples/demodesertcrt.tsx index 0dbe51cb..686a6e7b 100644 --- a/packages/cookbook-v2/src/examples/demodesertcrt.tsx +++ b/packages/cookbook-v2/src/examples/demodesertcrt.tsx @@ -192,9 +192,9 @@ void main() { }, }); +// CRT effect shader adapted from http://bit.ly/2eR1iKi const crtShaders = Shaders.create({ crt: { - // adapted from http://bit.ly/2eR1iKi frag: GLSL` precision highp float; varying vec2 uv; diff --git a/packages/cookbook-v2/src/examples/demotunnel.tsx b/packages/cookbook-v2/src/examples/demotunnel.tsx index 8d9810f7..7a8b4612 100644 --- a/packages/cookbook-v2/src/examples/demotunnel.tsx +++ b/packages/cookbook-v2/src/examples/demotunnel.tsx @@ -12,6 +12,7 @@ uniform float iGlobalTime; void main() { vec2 p = 2.0 * uv - vec2(1.0); float a = atan(p.y,p.x); + // Superellipse distance (Lp norm p=8) creates a square tunnel shape float r = pow( pow(p.x*p.x,4.0) + pow(p.y*p.y,4.0), 1.0/8.0 ); vec2 uv = vec2( 1.0/r + 0.2*iGlobalTime, a ); float f = cos(12.0*uv.x)*cos(6.0*uv.y); diff --git a/packages/cookbook-v2/src/examples/diamondcrop.tsx b/packages/cookbook-v2/src/examples/diamondcrop.tsx index ec9731a2..93b17a5a 100644 --- a/packages/cookbook-v2/src/examples/diamondcrop.tsx +++ b/packages/cookbook-v2/src/examples/diamondcrop.tsx @@ -9,6 +9,7 @@ precision highp float; varying vec2 uv; uniform sampler2D t; void main() { + // Manhattan distance from center defines the diamond shape gl_FragColor = mix( texture2D(t, uv), vec4(0.0), @@ -18,6 +19,7 @@ void main() { }, }); +// gl-react maps children to the t uniform automatically export const DiamondCrop = ({ children: t }: { children: any }) => ( ); diff --git a/packages/cookbook-v2/src/examples/diamondhello.tsx b/packages/cookbook-v2/src/examples/diamondhello.tsx index 6a8ebd33..cda4a5b3 100644 --- a/packages/cookbook-v2/src/examples/diamondhello.tsx +++ b/packages/cookbook-v2/src/examples/diamondhello.tsx @@ -6,6 +6,7 @@ import { HelloBlue } from "./helloblue"; export default function DiamondHello() { return ( + {/* Nesting Nodes: HelloBlue renders to a framebuffer, DiamondCrop samples it */} diff --git a/packages/cookbook-v2/src/examples/glsledit.tsx b/packages/cookbook-v2/src/examples/glsledit.tsx index b7741fd0..c39f610b 100644 --- a/packages/cookbook-v2/src/examples/glsledit.tsx +++ b/packages/cookbook-v2/src/examples/glsledit.tsx @@ -28,6 +28,7 @@ function Preview({ frag, visitor }: { frag: string; visitor: Visitor }) { const { time } = useTimeLoop(); return ( + {/* Passing { frag } directly triggers dynamic shader compilation */} ); @@ -36,11 +37,12 @@ function Preview({ frag, visitor }: { frag: string; visitor: Visitor }) { export default function GLSLEdit() { const [frag, setFrag] = useState(defaultFrag); const [error, setError] = useState(null); + // Visitor catches shader compilation errors and reports success const [visitor] = useState(() => { const v = new Visitor(); v.onSurfaceDrawError = (err: Error) => { setError(err.message); - return true; + return true; // suppress error, keep last valid frame }; v.onSurfaceDrawEnd = () => setError(null); return v; diff --git a/packages/cookbook-v2/src/examples/gol.tsx b/packages/cookbook-v2/src/examples/gol.tsx index b204eb74..ecc835b5 100644 --- a/packages/cookbook-v2/src/examples/gol.tsx +++ b/packages/cookbook-v2/src/examples/gol.tsx @@ -24,6 +24,7 @@ uniform sampler2D t; void main() { float prev = step(0.5, texture2D(t, uv).r); float c = 1.0 / size; + // Count the 8 neighbors (Moore neighborhood) float sum = step(0.5, texture2D(t, uv + vec2(-1.0, -1.0)*c).r) + step(0.5, texture2D(t, uv + vec2(-1.0, 0.0)*c).r) + @@ -41,6 +42,7 @@ void main() { const refreshEveryTicks = 20; +// Re-initializes with a larger grid every refreshEveryTicks ticks export const GameOfLife = ({ tick }: { tick: number }) => { const size = 16 * (1 + Math.floor(tick / refreshEveryTicks)); return tick % refreshEveryTicks === 0 ? ( diff --git a/packages/cookbook-v2/src/examples/golglider.tsx b/packages/cookbook-v2/src/examples/golglider.tsx index 82bf652b..3049c28c 100644 --- a/packages/cookbook-v2/src/examples/golglider.tsx +++ b/packages/cookbook-v2/src/examples/golglider.tsx @@ -4,8 +4,7 @@ import { Surface } from "gl-react-dom"; import { golShaders } from "./gol"; import { useTimeLoop } from "../hooks/useTimeLoop"; -// Gosper glider gun 64x64 pattern encoded as a data URL -// We generate it on-the-fly via a canvas +// Gosper glider gun 64x64 pattern drawn on a canvas function createGliderGunTexture(): HTMLCanvasElement { const size = 64; const canvas = document.createElement("canvas"); @@ -16,7 +15,6 @@ function createGliderGunTexture(): HTMLCanvasElement { ctx.fillRect(0, 0, size, size); ctx.fillStyle = "#fff"; - // Gosper glider gun cells (x, y) - standard pattern const cells = [ [1, 5], [1, 6], [2, 5], [2, 6], [11, 5], [11, 6], [11, 7], @@ -58,6 +56,7 @@ function GameOfLifeGlider({ tick, size }: { tick: number; size: number }) { backbuffering sync uniforms={{ + // tick 0: initialize from canvas, then read from backbuffer t: tick === 0 ? getGliderGunTexture() : Uniform.Backbuffer, size, }} diff --git a/packages/cookbook-v2/src/examples/golrotscu.tsx b/packages/cookbook-v2/src/examples/golrotscu.tsx index a9c5ce3a..9ce1b8a2 100644 --- a/packages/cookbook-v2/src/examples/golrotscu.tsx +++ b/packages/cookbook-v2/src/examples/golrotscu.tsx @@ -4,6 +4,7 @@ import { useTimeLoop } from "../hooks/useTimeLoop"; import { GameOfLife } from "./gol"; import { Rotating } from "./golrot"; +// useMemo prevents GameOfLife from re-rendering on every animation frame function RotatingGameOfLife({ time }: { time: number }) { const golTick = useMemo(() => Math.floor(time / 200), [Math.floor(time / 200)]); return ( diff --git a/packages/cookbook-v2/src/examples/heart.tsx b/packages/cookbook-v2/src/examples/heart.tsx index e20ed4d0..ef1ece91 100644 --- a/packages/cookbook-v2/src/examples/heart.tsx +++ b/packages/cookbook-v2/src/examples/heart.tsx @@ -26,6 +26,7 @@ void main() { }, }); +// Spring animation hook for smooth hover/click transitions function useSpringValue(target: number, stiffness = 150, damping = 15) { const ref = useRef({ value: target, velocity: 0 }); const [value, setValue] = useState(target); diff --git a/packages/cookbook-v2/src/examples/helloblue.tsx b/packages/cookbook-v2/src/examples/helloblue.tsx index ef12ba30..8513861e 100644 --- a/packages/cookbook-v2/src/examples/helloblue.tsx +++ b/packages/cookbook-v2/src/examples/helloblue.tsx @@ -8,13 +8,14 @@ const shaders = Shaders.create({ precision highp float; in vec2 uv; out vec4 color; -uniform float blue; +uniform float blue; // a uniform: a value sent from JS (React props) to the GPU void main() { - color = vec4(uv.x, uv.y, blue, 1.0); + color = vec4(uv.x, uv.y, blue, 1.0); // uv.x->red, uv.y->green, blue uniform->blue }`, }, }); +// uniforms maps JS values to GLSL uniform variables by name export const HelloBlue = ({ blue }: { blue: number }) => ( ); diff --git a/packages/cookbook-v2/src/examples/hellogl.tsx b/packages/cookbook-v2/src/examples/hellogl.tsx index fc675457..053d7eaf 100644 --- a/packages/cookbook-v2/src/examples/hellogl.tsx +++ b/packages/cookbook-v2/src/examples/hellogl.tsx @@ -2,20 +2,23 @@ import React from "react"; import { Shaders, Node, GLSL } from "gl-react"; import { Surface } from "gl-react-dom"; +// Shaders must be statically created (compiled once on GPU) const shaders = Shaders.create({ helloGL: { + // fragment shader in GLSL — runs on GPU for EACH pixel frag: GLSL`#version 300 es precision highp float; -in vec2 uv; +in vec2 uv; // uv.x and uv.y vary from 0.0 to 1.0 out vec4 color; void main() { - color = vec4(uv.x, uv.y, 0.5, 1.0); + color = vec4(uv.x, uv.y, 0.5, 1.0); // output pixel color as vec4(r,g,b,a) }`, }, }); export default function HelloGL() { return ( + // Surface creates the WebGL canvas. Node runs the shader. diff --git a/packages/cookbook-v2/src/examples/ibex.tsx b/packages/cookbook-v2/src/examples/ibex.tsx index bd0f3dc6..a1e38978 100644 --- a/packages/cookbook-v2/src/examples/ibex.tsx +++ b/packages/cookbook-v2/src/examples/ibex.tsx @@ -3,12 +3,6 @@ import { Shaders, Node, GLSL, Uniform } from "gl-react"; import { Surface } from "gl-react-dom"; import { useTimeLoop } from "../hooks/useTimeLoop"; -/** - * IBEX cellular automaton - extracted from a js13k game (2014) - * https://github.com/gre/ibex - * Simplified version: uses a random initial state instead of the full generation algorithm. - */ - const shaders = Shaders.create({ IBEXRender: { frag: GLSL` @@ -335,15 +329,15 @@ void main () { }); const colors = [ - [0.11, 0.16, 0.23], // 0: air - [0.74, 0.66, 0.51], // 1: earth - [0.84, 0.17, 0.08], // 2: fire - [0.4, 0.75, 0.9], // 3: water - [0.6, 0.0, 0.0], // 4: volcano - [0.3, 0.6, 0.7], // 5: source - [0.15, 0.2, 0.27], // 6: wind left - [0.07, 0.12, 0.19], // 7: wind right - [0.2, 0.6, 0.2], // 8: grass + [0.11, 0.16, 0.23], + [0.74, 0.66, 0.51], + [0.84, 0.17, 0.08], + [0.4, 0.75, 0.9], + [0.6, 0.0, 0.0], + [0.3, 0.6, 0.7], + [0.15, 0.2, 0.27], + [0.07, 0.12, 0.19], + [0.2, 0.6, 0.2], ]; const worldSize: [number, number] = [200, 200]; @@ -357,22 +351,19 @@ function generateInitialState(): HTMLCanvasElement { const imageData = ctx.createImageData(w, h); const data = imageData.data; - // Simple terrain generation for (let x = 0; x < w; x++) { const groundLevel = Math.floor( h * 0.45 + 15 * Math.sin(x * 0.05) + 5 * Math.cos(x * 0.12) ); for (let y = 0; y < h; y++) { - const flippedY = h - 1 - y; // flip Y + const flippedY = h - 1 - y; const idx = (flippedY * w + x) * 4; - let element = 0; // air + let element = 0; if (y < groundLevel) { - element = 1; // earth - // Occasional water source + element = 1; if (Math.random() < 0.001 && y > groundLevel - 10) { element = 5; } - // Occasional volcano if (Math.random() < 0.0005 && y < 10) { element = 4; } diff --git a/packages/cookbook-v2/src/examples/paint.tsx b/packages/cookbook-v2/src/examples/paint.tsx index 919e8780..fb8bd2d1 100644 --- a/packages/cookbook-v2/src/examples/paint.tsx +++ b/packages/cookbook-v2/src/examples/paint.tsx @@ -26,7 +26,7 @@ void main() { gl_FragColor = color; } else { - discard; + discard; // preserve existing framebuffer content } } else { diff --git a/packages/cookbook-v2/src/examples/pixeleditor.tsx b/packages/cookbook-v2/src/examples/pixeleditor.tsx index 3b226fa1..d0df80c0 100644 --- a/packages/cookbook-v2/src/examples/pixeleditor.tsx +++ b/packages/cookbook-v2/src/examples/pixeleditor.tsx @@ -98,7 +98,6 @@ function rgbaToCSS(c: number[]) { return `rgba(${Math.round(c[0] * 255)},${Math.round(c[1] * 255)},${Math.round(c[2] * 255)},${c[3]})`; } -// Preload mario as dataURL so it's available synchronously for the init texture function useMarioDataURL() { const [dataURL, setDataURL] = useState(null); useEffect(() => { diff --git a/packages/cookbook-v2/src/examples/saturation.tsx b/packages/cookbook-v2/src/examples/saturation.tsx index acf356e0..2bce65b3 100644 --- a/packages/cookbook-v2/src/examples/saturation.tsx +++ b/packages/cookbook-v2/src/examples/saturation.tsx @@ -9,7 +9,7 @@ precision highp float; varying vec2 uv; uniform sampler2D t; uniform float contrast, saturation, brightness; -const vec3 L = vec3(0.2125, 0.7154, 0.0721); +const vec3 L = vec3(0.2125, 0.7154, 0.0721); // BT.709 luminance weights void main() { vec4 c = texture2D(t, uv); vec3 brt = c.rgb * brightness; diff --git a/packages/cookbook-v2/src/examples/transitions.tsx b/packages/cookbook-v2/src/examples/transitions.tsx index 0c79985b..85a94a59 100644 --- a/packages/cookbook-v2/src/examples/transitions.tsx +++ b/packages/cookbook-v2/src/examples/transitions.tsx @@ -3,9 +3,6 @@ import { Shaders, Node, GLSL, LinearCopy } from "gl-react"; import { Surface } from "gl-react-dom"; import { useTimeLoop } from "../hooks/useTimeLoop"; -// A set of built-in GL transition shaders -// We inline a few classic transitions rather than depending on gl-transitions package - const transitionShaders = Shaders.create({ fade: { frag: GLSL` diff --git a/packages/cookbook-v2/src/examples/video.tsx b/packages/cookbook-v2/src/examples/video.tsx index 05c0aa71..7fa50caa 100644 --- a/packages/cookbook-v2/src/examples/video.tsx +++ b/packages/cookbook-v2/src/examples/video.tsx @@ -2,10 +2,9 @@ import React, { useRef, useEffect, useState, createContext } from "react"; import { Shaders, GLSL, Node } from "gl-react"; import { Surface } from "gl-react-dom"; -// VideoContext for webcam examples that need to attach a stream to a video element export const VideoContext = createContext | null>(null); -// Legacy Video component for webcam examples (renders a hidden video element in the DOM) +// Renders a hidden video element, calls onFrame on each new video frame export const Video = ({ onFrame, ...rest }: { onFrame: (t: number) => void; [key: string]: any }) => { const videoRef = useRef(null); @@ -32,6 +31,7 @@ export const Video = ({ onFrame, ...rest }: { onFrame: (t: number) => void; [key ); }; +// Creates a video element and re-renders on new frames export function useVideo(src: string) { const videoRef = useRef(null); const [, setTick] = useState(0); diff --git a/packages/cookbook-v2/src/main.tsx b/packages/cookbook-v2/src/main.tsx index e6ad8b57..88a47541 100644 --- a/packages/cookbook-v2/src/main.tsx +++ b/packages/cookbook-v2/src/main.tsx @@ -6,7 +6,7 @@ import './index.css' ReactDOM.createRoot(document.getElementById('root')!).render( - + , diff --git a/packages/cookbook-v2/src/pages/ApiPage.tsx b/packages/cookbook-v2/src/pages/ApiPage.tsx index 2ed09e76..043b0b0c 100644 --- a/packages/cookbook-v2/src/pages/ApiPage.tsx +++ b/packages/cookbook-v2/src/pages/ApiPage.tsx @@ -40,7 +40,7 @@ export function ApiPage() {

API Documentation

- Complete reference for GL React components and APIs + Complete reference for gl-react components and APIs

diff --git a/packages/cookbook-v2/src/pages/ExampleDetailPage.tsx b/packages/cookbook-v2/src/pages/ExampleDetailPage.tsx index 70235e22..b2cc967e 100644 --- a/packages/cookbook-v2/src/pages/ExampleDetailPage.tsx +++ b/packages/cookbook-v2/src/pages/ExampleDetailPage.tsx @@ -1,6 +1,6 @@ import React, { Suspense, useState, useCallback, useMemo } from "react"; -import { useParams, Link } from "react-router-dom"; -import { ArrowLeftIcon, ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/24/outline"; +import { useParams, Link, useNavigate } from "react-router-dom"; +import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/24/solid"; import Prism from "prismjs"; import "prismjs/components/prism-javascript"; import "prismjs/components/prism-typescript"; @@ -9,6 +9,7 @@ import "prismjs/components/prism-tsx"; import "prismjs/themes/prism-tomorrow.css"; import { examples } from "../examples"; import { ControlsPanel, getDefaults } from "../controls"; +import { Breadcrumb } from "../components/Breadcrumb"; const sources = import.meta.glob("../examples/*.tsx", { query: "?raw", import: "default", eager: true }) as Record; @@ -19,6 +20,7 @@ function getSource(id: string): string | null { export function ExampleDetailPage() { const { id } = useParams<{ id: string }>(); + const navigate = useNavigate(); const exampleIndex = examples.findIndex((e) => e.id === id); const example = exampleIndex >= 0 ? examples[exampleIndex] : undefined; const prev = exampleIndex > 0 ? examples[exampleIndex - 1] : null; @@ -51,55 +53,30 @@ export function ExampleDetailPage() { ); return ( -
- {/* Nav */} -
- - - Back - -
- {prev ? ( - - - prev - - ) : ( - - - prev - - )} - {next ? ( - - next - - - ) : ( - - next - - - )} -
-
+
+ - {/* Title */} -
-

{example.title}

-

{example.description}

-
+

{example.description}

+ + {/* Prev/Next arrows */} + {prev && ( + + )} + {next && ( + + )} {/* Main layout: side-by-side on wide screens */}
diff --git a/packages/cookbook-v2/src/pages/ExamplesPage.tsx b/packages/cookbook-v2/src/pages/ExamplesPage.tsx index f22205f8..49290a72 100644 --- a/packages/cookbook-v2/src/pages/ExamplesPage.tsx +++ b/packages/cookbook-v2/src/pages/ExamplesPage.tsx @@ -2,15 +2,14 @@ import React from "react"; import { Link } from "react-router-dom"; import { BeakerIcon } from "@heroicons/react/24/outline"; import { examples } from "../examples"; +import { Breadcrumb } from "../components/Breadcrumb"; const categories = [...new Set(examples.map((e) => e.category))]; export function ExamplesPage() { return (
-
-

Examples

-
+ {categories.map((category) => (
diff --git a/packages/cookbook-v2/src/pages/HomePage.tsx b/packages/cookbook-v2/src/pages/HomePage.tsx index 686aab5a..4691a77c 100644 --- a/packages/cookbook-v2/src/pages/HomePage.tsx +++ b/packages/cookbook-v2/src/pages/HomePage.tsx @@ -1,32 +1,184 @@ -import React from 'react' -import { Link } from 'react-router-dom' -import { AnimatedWebGLExample } from '../components/WebGLExample' +import { Link } from "react-router-dom"; +import { CodeBracketIcon } from "@heroicons/react/24/outline"; +import { Surface } from "gl-react-dom"; +import { Node, Shaders, GLSL, Uniform, LinearCopy } from "gl-react"; +import Prism from "prismjs"; +import "prismjs/components/prism-jsx"; +import { useTimeLoop } from "../hooks/useTimeLoop"; -export function HomePage() { - return ( -
-
-

- GL React Cookbook -

-
- - Explore Examples - -
-
- -
-
-
-
- -
-
-
+const shaders = Shaders.create({ + MotionBlur: { + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform sampler2D children, backbuffer; +uniform float persistence; +void main () { + gl_FragColor = vec4(mix( + texture2D(children, uv), + texture2D(backbuffer, uv), + persistence + ).rgb, 1.0); +}`, + }, + HelloGL: { + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform float red; +void main() { + gl_FragColor = vec4(red, uv.x, uv.y, 1.0); +}`, + }, + Rotate: { + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform float angle, scale; +uniform sampler2D children; +void main() { + mat2 rotation = mat2(cos(angle), -sin(angle), sin(angle), cos(angle)); + vec2 p = (uv - vec2(0.5)) * rotation / scale + vec2(0.5); + gl_FragColor = + p.x < 0.0 || p.x > 1.0 || p.y < 0.0 || p.y > 1.0 + ? vec4(0.067, 0.094, 0.153, 1.0) + : texture2D(children, p); +}`, + }, +}); + +function MotionBlur({ + children, + persistence, +}: { + children: any; + persistence: number; +}) { + return ( + + ); +} + +function HelloGL({ red }: { red: number }) { + return ; +} + +function Rotate({ + children, + scale, + angle, +}: { + children: any; + scale: number; + angle: number; +}) { + return ( + + ); +} + +function LiveCode({ + persistence, + scale, + angle, + red, +}: { + persistence: number; + scale: number; + angle: number; + red: number; +}) { + const code = ` + + + + + +`; + + const html = Prism.highlight(code, Prism.languages.jsx, "jsx"); + + return ( +
+      
+    
+ ); +} + +function HeroDemo() { + const { time } = useTimeLoop(); + const t = time / 1000; + const persistence = 0.75 - 0.2 * Math.cos(0.5 * t); + const red = 0.6 + 0.4 * Math.cos(4 * t); + const scale = 0.7 + 0.4 * Math.cos(t); + const angle = 2 * Math.PI * (0.5 + 0.5 * Math.cos(t)); + + return ( +
+
+
+
+ + + + + + + + +
- ) +
+
+ +
+
+ ); +} + +export function HomePage() { + return ( +
+
+

+ + gl-react +

+

+ A{" "} + + React + {" "} + library to write and compose WebGL shaders. +

+
+ +
+ + Explore Examples + + + GitHub + +
+ + +
+ ); } diff --git a/packages/cookbook-v2/src/shims/buffer.ts b/packages/cookbook-v2/src/shims/buffer.ts new file mode 100644 index 00000000..249755b5 --- /dev/null +++ b/packages/cookbook-v2/src/shims/buffer.ts @@ -0,0 +1,7 @@ +// Minimal browser shim for Node's `buffer` module. +// Only used by typedarray-pool (dep of gl-texture2d). +export const Buffer = { + isBuffer() { + return false; + }, +}; diff --git a/packages/cookbook-v2/vite.config.ts b/packages/cookbook-v2/vite.config.ts index 2076bb53..ea443377 100644 --- a/packages/cookbook-v2/vite.config.ts +++ b/packages/cookbook-v2/vite.config.ts @@ -23,6 +23,7 @@ export default defineConfig({ '@': resolve(__dirname, './src'), 'gl-react': resolve(__dirname, '../gl-react/src'), 'gl-react-dom': resolve(__dirname, '../gl-react-dom/src'), + buffer: resolve(__dirname, './src/shims/buffer.ts'), }, }, server: { diff --git a/yarn.lock b/yarn.lock index ad450b66..856fb6f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -117,7 +117,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.1.0, @babel/core@npm:^7.12.3, @babel/core@npm:^7.16.0, @babel/core@npm:^7.28.0, @babel/core@npm:^7.7.5, @babel/core@npm:^7.8.4, @babel/core@npm:^7.9.0": +"@babel/core@npm:^7.1.0, @babel/core@npm:^7.12.3, @babel/core@npm:^7.16.0, @babel/core@npm:^7.7.5, @babel/core@npm:^7.8.4, @babel/core@npm:^7.9.0": version: 7.28.4 resolution: "@babel/core@npm:7.28.4" dependencies: @@ -140,7 +140,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.11.6, @babel/core@npm:^7.23.9, @babel/core@npm:^7.26.0, @babel/core@npm:^7.29.0": +"@babel/core@npm:^7.11.6, @babel/core@npm:^7.23.9, @babel/core@npm:^7.26.0, @babel/core@npm:^7.28.0, @babel/core@npm:^7.29.0": version: 7.29.0 resolution: "@babel/core@npm:7.29.0" dependencies: @@ -2625,189 +2625,189 @@ __metadata: languageName: node linkType: hard -"@esbuild/aix-ppc64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/aix-ppc64@npm:0.25.10" +"@esbuild/aix-ppc64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/aix-ppc64@npm:0.25.12" conditions: os=aix & cpu=ppc64 languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/android-arm64@npm:0.25.10" +"@esbuild/android-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-arm64@npm:0.25.12" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@esbuild/android-arm@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/android-arm@npm:0.25.10" +"@esbuild/android-arm@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-arm@npm:0.25.12" conditions: os=android & cpu=arm languageName: node linkType: hard -"@esbuild/android-x64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/android-x64@npm:0.25.10" +"@esbuild/android-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-x64@npm:0.25.12" conditions: os=android & cpu=x64 languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/darwin-arm64@npm:0.25.10" +"@esbuild/darwin-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/darwin-arm64@npm:0.25.12" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/darwin-x64@npm:0.25.10" +"@esbuild/darwin-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/darwin-x64@npm:0.25.12" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/freebsd-arm64@npm:0.25.10" +"@esbuild/freebsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/freebsd-arm64@npm:0.25.12" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/freebsd-x64@npm:0.25.10" +"@esbuild/freebsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/freebsd-x64@npm:0.25.12" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/linux-arm64@npm:0.25.10" +"@esbuild/linux-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-arm64@npm:0.25.12" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/linux-arm@npm:0.25.10" +"@esbuild/linux-arm@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-arm@npm:0.25.12" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/linux-ia32@npm:0.25.10" +"@esbuild/linux-ia32@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-ia32@npm:0.25.12" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/linux-loong64@npm:0.25.10" +"@esbuild/linux-loong64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-loong64@npm:0.25.12" conditions: os=linux & cpu=loong64 languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/linux-mips64el@npm:0.25.10" +"@esbuild/linux-mips64el@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-mips64el@npm:0.25.12" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/linux-ppc64@npm:0.25.10" +"@esbuild/linux-ppc64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-ppc64@npm:0.25.12" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/linux-riscv64@npm:0.25.10" +"@esbuild/linux-riscv64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-riscv64@npm:0.25.12" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/linux-s390x@npm:0.25.10" +"@esbuild/linux-s390x@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-s390x@npm:0.25.12" conditions: os=linux & cpu=s390x languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/linux-x64@npm:0.25.10" +"@esbuild/linux-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-x64@npm:0.25.12" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@esbuild/netbsd-arm64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/netbsd-arm64@npm:0.25.10" +"@esbuild/netbsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/netbsd-arm64@npm:0.25.12" conditions: os=netbsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/netbsd-x64@npm:0.25.10" +"@esbuild/netbsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/netbsd-x64@npm:0.25.12" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openbsd-arm64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/openbsd-arm64@npm:0.25.10" +"@esbuild/openbsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openbsd-arm64@npm:0.25.12" conditions: os=openbsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/openbsd-x64@npm:0.25.10" +"@esbuild/openbsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openbsd-x64@npm:0.25.12" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openharmony-arm64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/openharmony-arm64@npm:0.25.10" +"@esbuild/openharmony-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openharmony-arm64@npm:0.25.12" conditions: os=openharmony & cpu=arm64 languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/sunos-x64@npm:0.25.10" +"@esbuild/sunos-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/sunos-x64@npm:0.25.12" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/win32-arm64@npm:0.25.10" +"@esbuild/win32-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-arm64@npm:0.25.12" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/win32-ia32@npm:0.25.10" +"@esbuild/win32-ia32@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-ia32@npm:0.25.12" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.25.10": - version: 0.25.10 - resolution: "@esbuild/win32-x64@npm:0.25.10" +"@esbuild/win32-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-x64@npm:0.25.12" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@eslint-community/eslint-utils@npm:^4.7.0, @eslint-community/eslint-utils@npm:^4.8.0": +"@eslint-community/eslint-utils@npm:^4.8.0": version: 4.9.0 resolution: "@eslint-community/eslint-utils@npm:4.9.0" dependencies: @@ -2818,13 +2818,31 @@ __metadata: languageName: node linkType: hard -"@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.12.1": +"@eslint-community/eslint-utils@npm:^4.9.1": + version: 4.9.1 + resolution: "@eslint-community/eslint-utils@npm:4.9.1" + dependencies: + eslint-visitor-keys: "npm:^3.4.3" + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + checksum: 10c0/dc4ab5e3e364ef27e33666b11f4b86e1a6c1d7cbf16f0c6ff87b1619b3562335e9201a3d6ce806221887ff780ec9d828962a290bb910759fd40a674686503f02 + languageName: node + linkType: hard + +"@eslint-community/regexpp@npm:^4.12.1": version: 4.12.1 resolution: "@eslint-community/regexpp@npm:4.12.1" checksum: 10c0/a03d98c246bcb9109aec2c08e4d10c8d010256538dcb3f56610191607214523d4fb1b00aa81df830b6dffb74c5fa0be03642513a289c567949d3e550ca11cdf6 languageName: node linkType: hard +"@eslint-community/regexpp@npm:^4.12.2": + version: 4.12.2 + resolution: "@eslint-community/regexpp@npm:4.12.2" + checksum: 10c0/fddcbc66851b308478d04e302a4d771d6917a0b3740dc351513c0da9ca2eab8a1adf99f5e0aa7ab8b13fa0df005c81adeee7e63a92f3effd7d367a163b721c2d + languageName: node + linkType: hard + "@eslint/config-array@npm:^0.21.0": version: 0.21.0 resolution: "@eslint/config-array@npm:0.21.0" @@ -2968,7 +2986,7 @@ __metadata: languageName: node linkType: hard -"@heroicons/react@npm:^2.1.1": +"@heroicons/react@npm:^2.2.0": version: 2.2.0 resolution: "@heroicons/react@npm:2.2.0" peerDependencies: @@ -3786,156 +3804,177 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-android-arm-eabi@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-android-arm-eabi@npm:4.52.4" +"@rollup/rollup-android-arm-eabi@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.59.0" conditions: os=android & cpu=arm languageName: node linkType: hard -"@rollup/rollup-android-arm64@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-android-arm64@npm:4.52.4" +"@rollup/rollup-android-arm64@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-android-arm64@npm:4.59.0" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-arm64@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-darwin-arm64@npm:4.52.4" +"@rollup/rollup-darwin-arm64@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-darwin-arm64@npm:4.59.0" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-x64@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-darwin-x64@npm:4.52.4" +"@rollup/rollup-darwin-x64@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-darwin-x64@npm:4.59.0" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-freebsd-arm64@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-freebsd-arm64@npm:4.52.4" +"@rollup/rollup-freebsd-arm64@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.59.0" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-freebsd-x64@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-freebsd-x64@npm:4.52.4" +"@rollup/rollup-freebsd-x64@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-freebsd-x64@npm:4.59.0" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-linux-arm-gnueabihf@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.52.4" +"@rollup/rollup-linux-arm-gnueabihf@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.59.0" conditions: os=linux & cpu=arm & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm-musleabihf@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.52.4" +"@rollup/rollup-linux-arm-musleabihf@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.59.0" conditions: os=linux & cpu=arm & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-arm64-gnu@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.52.4" +"@rollup/rollup-linux-arm64-gnu@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.59.0" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm64-musl@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-linux-arm64-musl@npm:4.52.4" +"@rollup/rollup-linux-arm64-musl@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.59.0" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-loong64-gnu@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.52.4" +"@rollup/rollup-linux-loong64-gnu@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.59.0" conditions: os=linux & cpu=loong64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-ppc64-gnu@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.52.4" +"@rollup/rollup-linux-loong64-musl@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-linux-loong64-musl@npm:4.59.0" + conditions: os=linux & cpu=loong64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-ppc64-gnu@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.59.0" conditions: os=linux & cpu=ppc64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-riscv64-gnu@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.52.4" +"@rollup/rollup-linux-ppc64-musl@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-linux-ppc64-musl@npm:4.59.0" + conditions: os=linux & cpu=ppc64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-gnu@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.59.0" conditions: os=linux & cpu=riscv64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-riscv64-musl@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.52.4" +"@rollup/rollup-linux-riscv64-musl@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.59.0" conditions: os=linux & cpu=riscv64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-s390x-gnu@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.52.4" +"@rollup/rollup-linux-s390x-gnu@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.59.0" conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-gnu@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-linux-x64-gnu@npm:4.52.4" +"@rollup/rollup-linux-x64-gnu@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.59.0" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-musl@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-linux-x64-musl@npm:4.52.4" +"@rollup/rollup-linux-x64-musl@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.59.0" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-openharmony-arm64@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-openharmony-arm64@npm:4.52.4" +"@rollup/rollup-openbsd-x64@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-openbsd-x64@npm:4.59.0" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-openharmony-arm64@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-openharmony-arm64@npm:4.59.0" conditions: os=openharmony & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-win32-arm64-msvc@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.52.4" +"@rollup/rollup-win32-arm64-msvc@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.59.0" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-win32-ia32-msvc@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.52.4" +"@rollup/rollup-win32-ia32-msvc@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.59.0" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rollup/rollup-win32-x64-gnu@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-win32-x64-gnu@npm:4.52.4" +"@rollup/rollup-win32-x64-gnu@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-win32-x64-gnu@npm:4.59.0" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-win32-x64-msvc@npm:4.52.4": - version: 4.52.4 - resolution: "@rollup/rollup-win32-x64-msvc@npm:4.52.4" +"@rollup/rollup-win32-x64-msvc@npm:4.59.0": + version: 4.59.0 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.59.0" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -4286,10 +4325,10 @@ __metadata: languageName: node linkType: hard -"@types/lodash@npm:^4.17.0": - version: 4.17.20 - resolution: "@types/lodash@npm:4.17.20" - checksum: 10c0/98cdd0faae22cbb8079a01a3bb65aa8f8c41143367486c1cbf5adc83f16c9272a2a5d2c1f541f61d0d73da543c16ee1d21cf2ef86cb93cd0cc0ac3bced6dd88f +"@types/lodash@npm:^4.17.24": + version: 4.17.24 + resolution: "@types/lodash@npm:4.17.24" + checksum: 10c0/b72f60d4daacdad1fa643edb3faba204c02a01eb1ac00a83ff73496a6d236fc55e459c06106e8ced42277dba932d087d8fc090f8de4ef590d3f91e6d6f7ce85a languageName: node linkType: hard @@ -4346,10 +4385,10 @@ __metadata: languageName: node linkType: hard -"@types/prismjs@npm:^1.26.3": - version: 1.26.5 - resolution: "@types/prismjs@npm:1.26.5" - checksum: 10c0/5619cb449e0d8df098c8759d6f47bf8fdd510abf5dbdfa999e55c6a2545efbd1e209cc85a33d8d9f4ff2898089a1a6d9a70737c9baffaae635c46852c40d384a +"@types/prismjs@npm:^1.26.6": + version: 1.26.6 + resolution: "@types/prismjs@npm:1.26.6" + checksum: 10c0/152a27500cb32b114edfb77f9d0dccd03bebc84828d1e92abacaf212b22d3ccdde041ce421dd58b6ec8461bbec7cd76ed5ee773cae4be7ca36a6dd4ddcf0f9e7 languageName: node linkType: hard @@ -4503,24 +4542,23 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^8.15.0": - version: 8.46.0 - resolution: "@typescript-eslint/eslint-plugin@npm:8.46.0" +"@typescript-eslint/eslint-plugin@npm:^8.57.1": + version: 8.57.1 + resolution: "@typescript-eslint/eslint-plugin@npm:8.57.1" dependencies: - "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:8.46.0" - "@typescript-eslint/type-utils": "npm:8.46.0" - "@typescript-eslint/utils": "npm:8.46.0" - "@typescript-eslint/visitor-keys": "npm:8.46.0" - graphemer: "npm:^1.4.0" - ignore: "npm:^7.0.0" + "@eslint-community/regexpp": "npm:^4.12.2" + "@typescript-eslint/scope-manager": "npm:8.57.1" + "@typescript-eslint/type-utils": "npm:8.57.1" + "@typescript-eslint/utils": "npm:8.57.1" + "@typescript-eslint/visitor-keys": "npm:8.57.1" + ignore: "npm:^7.0.5" natural-compare: "npm:^1.4.0" - ts-api-utils: "npm:^2.1.0" + ts-api-utils: "npm:^2.4.0" peerDependencies: - "@typescript-eslint/parser": ^8.46.0 - eslint: ^8.57.0 || ^9.0.0 + "@typescript-eslint/parser": ^8.57.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/9de2b2127b977b0d73733042602a744e5b69bfe906c6dac424123ff9726816dcc4bb3d4ba470bc1fc5c741421f53274a3a896c09fbb50e298352d4a72011b2c2 + checksum: 10c0/5bf9227f5d608d4313c9f898da3a2f6737eca985aa925df9e90b73499b9d552221781d3d09245543c6d09995ab262ea0d6773d2dae4b8bdf319765d46b22d0e1 languageName: node linkType: hard @@ -4572,32 +4610,32 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:^8.15.0": - version: 8.46.0 - resolution: "@typescript-eslint/parser@npm:8.46.0" +"@typescript-eslint/parser@npm:^8.57.1": + version: 8.57.1 + resolution: "@typescript-eslint/parser@npm:8.57.1" dependencies: - "@typescript-eslint/scope-manager": "npm:8.46.0" - "@typescript-eslint/types": "npm:8.46.0" - "@typescript-eslint/typescript-estree": "npm:8.46.0" - "@typescript-eslint/visitor-keys": "npm:8.46.0" - debug: "npm:^4.3.4" + "@typescript-eslint/scope-manager": "npm:8.57.1" + "@typescript-eslint/types": "npm:8.57.1" + "@typescript-eslint/typescript-estree": "npm:8.57.1" + "@typescript-eslint/visitor-keys": "npm:8.57.1" + debug: "npm:^4.4.3" peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/2e8c75b72c7cf170aca554014dbe30e85478d96799a2eb782c4fb61423c0c9e4416e98d6e7903601b1738ea1d0936417dbf61ac0293a0500f56e0eaeefbb2ecd + checksum: 10c0/ab624f5ad6f3585ee690d11be36597135779a373e7f07810ed921163de2e879000f6d3213db67413ee630bcf25d5cfaa24b089ee49596cd11b0456372bc17163 languageName: node linkType: hard -"@typescript-eslint/project-service@npm:8.46.0": - version: 8.46.0 - resolution: "@typescript-eslint/project-service@npm:8.46.0" +"@typescript-eslint/project-service@npm:8.57.1": + version: 8.57.1 + resolution: "@typescript-eslint/project-service@npm:8.57.1" dependencies: - "@typescript-eslint/tsconfig-utils": "npm:^8.46.0" - "@typescript-eslint/types": "npm:^8.46.0" - debug: "npm:^4.3.4" + "@typescript-eslint/tsconfig-utils": "npm:^8.57.1" + "@typescript-eslint/types": "npm:^8.57.1" + debug: "npm:^4.4.3" peerDependencies: typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/c3164c795570edfa141917f3099724eca70383b016be1b08f656a491b459d68cf8e2547ac416d75048d3511ca5feaea0586aabad339e3dfe2ae6fddb650d7bc8 + checksum: 10c0/7830f61e35364ba77799f4badeaca8bd8914bbcda6afe37b788821f94f4b88b9c49817c50f4bdba497e8e542a705e9d921d36f5e67960ebf33f4f3d3111cdfee languageName: node linkType: hard @@ -4611,38 +4649,38 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.46.0": - version: 8.46.0 - resolution: "@typescript-eslint/scope-manager@npm:8.46.0" +"@typescript-eslint/scope-manager@npm:8.57.1": + version: 8.57.1 + resolution: "@typescript-eslint/scope-manager@npm:8.57.1" dependencies: - "@typescript-eslint/types": "npm:8.46.0" - "@typescript-eslint/visitor-keys": "npm:8.46.0" - checksum: 10c0/9c242d1edd51247559f99dd8986bdb571db0a2a583a2d02ee8f5f346d265e956f413b442c27e1b02d55ce3944609f6593050ec657be672d9b24b7ed0a359a6ad + "@typescript-eslint/types": "npm:8.57.1" + "@typescript-eslint/visitor-keys": "npm:8.57.1" + checksum: 10c0/42b0b54981318bf21be6b107df82910718497b7b7b2b60df635aa06d78e313759e4b675830c0e542b6d87104d35b49df41b9fb7739b8ae326eaba2d6f7116166 languageName: node linkType: hard -"@typescript-eslint/tsconfig-utils@npm:8.46.0, @typescript-eslint/tsconfig-utils@npm:^8.46.0": - version: 8.46.0 - resolution: "@typescript-eslint/tsconfig-utils@npm:8.46.0" +"@typescript-eslint/tsconfig-utils@npm:8.57.1, @typescript-eslint/tsconfig-utils@npm:^8.57.1": + version: 8.57.1 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.57.1" peerDependencies: typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/306b27c741709f2435dd1c7eabdf552775dff1b3ced01d791c5b9755394ceb3f37c9bcceec92adb6fe60c622523f9d47d9b0d9e515071f47d50527705a4706f7 + checksum: 10c0/3d3c8d80621507d31e4656c693534f28a1c04dfb047538cb79b0b6da874ef41875f5df5e814fa3a38812451cff6d5a7ae38d0bf77eb7fec7867f9c80af361b00 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.46.0": - version: 8.46.0 - resolution: "@typescript-eslint/type-utils@npm:8.46.0" +"@typescript-eslint/type-utils@npm:8.57.1": + version: 8.57.1 + resolution: "@typescript-eslint/type-utils@npm:8.57.1" dependencies: - "@typescript-eslint/types": "npm:8.46.0" - "@typescript-eslint/typescript-estree": "npm:8.46.0" - "@typescript-eslint/utils": "npm:8.46.0" - debug: "npm:^4.3.4" - ts-api-utils: "npm:^2.1.0" + "@typescript-eslint/types": "npm:8.57.1" + "@typescript-eslint/typescript-estree": "npm:8.57.1" + "@typescript-eslint/utils": "npm:8.57.1" + debug: "npm:^4.4.3" + ts-api-utils: "npm:^2.4.0" peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/a0fa4617a998094bc217be1989b76a3e45c058117cda027a723ff6f98f15e5237abfa123284afbdea7f320b4da65e1053ed47c8a211dd012591908a9daa46f02 + checksum: 10c0/e8eae4e3b9ca71ad065c307fd3cdefdcc6abc31bda2ef74f0e54b5c9ac0ee6bc0e2d69ec9097899f4d7a99d4a8a72391503b47f4317b3b6b9ba41cea24e6b9e9 languageName: node linkType: hard @@ -4660,10 +4698,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:8.46.0, @typescript-eslint/types@npm:^8.46.0": - version: 8.46.0 - resolution: "@typescript-eslint/types@npm:8.46.0" - checksum: 10c0/2f986852139bcbe940b4aafe79bbd28dcca7176e95ba4e3880984ef58c81ad077ca9d9191aad56d2b1df6d16060f5744a96ab3118ddbc9766e5035ed470445c1 +"@typescript-eslint/types@npm:8.57.1, @typescript-eslint/types@npm:^8.57.1": + version: 8.57.1 + resolution: "@typescript-eslint/types@npm:8.57.1" + checksum: 10c0/f447015276a31871440b07e328c2bbcee8337d72dca90ae00ac91e87d09e28a8a9c2fe44726a5226fcaa7db9d5347aafa650d59f7577a074dc65ea1414d24da1 languageName: node linkType: hard @@ -4704,38 +4742,37 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:8.46.0": - version: 8.46.0 - resolution: "@typescript-eslint/typescript-estree@npm:8.46.0" +"@typescript-eslint/typescript-estree@npm:8.57.1": + version: 8.57.1 + resolution: "@typescript-eslint/typescript-estree@npm:8.57.1" dependencies: - "@typescript-eslint/project-service": "npm:8.46.0" - "@typescript-eslint/tsconfig-utils": "npm:8.46.0" - "@typescript-eslint/types": "npm:8.46.0" - "@typescript-eslint/visitor-keys": "npm:8.46.0" - debug: "npm:^4.3.4" - fast-glob: "npm:^3.3.2" - is-glob: "npm:^4.0.3" - minimatch: "npm:^9.0.4" - semver: "npm:^7.6.0" - ts-api-utils: "npm:^2.1.0" + "@typescript-eslint/project-service": "npm:8.57.1" + "@typescript-eslint/tsconfig-utils": "npm:8.57.1" + "@typescript-eslint/types": "npm:8.57.1" + "@typescript-eslint/visitor-keys": "npm:8.57.1" + debug: "npm:^4.4.3" + minimatch: "npm:^10.2.2" + semver: "npm:^7.7.3" + tinyglobby: "npm:^0.2.15" + ts-api-utils: "npm:^2.4.0" peerDependencies: typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/39aed033dc23c3356e39891c9eba6dde0dc618406f0e13e9adc5967fb81790ec199b1d6eb1144e35ad13a0daaf72157f5f3fc7ac1b7c58d152ade68fe27ad221 + checksum: 10c0/a87e1d920a8fd2231b6a98b279dc7680d10ceac072001e85a72cd43adce288ed471afcaf8f171378f5a3221c500b3cf0ffc10a75fd521fb69fbd8b26d4626677 languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.46.0": - version: 8.46.0 - resolution: "@typescript-eslint/utils@npm:8.46.0" +"@typescript-eslint/utils@npm:8.57.1": + version: 8.57.1 + resolution: "@typescript-eslint/utils@npm:8.57.1" dependencies: - "@eslint-community/eslint-utils": "npm:^4.7.0" - "@typescript-eslint/scope-manager": "npm:8.46.0" - "@typescript-eslint/types": "npm:8.46.0" - "@typescript-eslint/typescript-estree": "npm:8.46.0" + "@eslint-community/eslint-utils": "npm:^4.9.1" + "@typescript-eslint/scope-manager": "npm:8.57.1" + "@typescript-eslint/types": "npm:8.57.1" + "@typescript-eslint/typescript-estree": "npm:8.57.1" peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/77cc7dff9132d9f02e8766d128edbeb7c2f2b56f9ebdac7308e75a04924e2369857da27b23f0054476c9640609a9707b8dd8ca8b1c59a067e45f65bf5ef4cc1b + checksum: 10c0/c85d6e7c618dbf902fda98cc795883388bc512bc2c34c7ac0481ea43acb6dd3cd38d60bdb571b586f392419a17998c89330fd7b0b9a344161f4a595637dd3f55 languageName: node linkType: hard @@ -4758,17 +4795,17 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.46.0": - version: 8.46.0 - resolution: "@typescript-eslint/visitor-keys@npm:8.46.0" +"@typescript-eslint/visitor-keys@npm:8.57.1": + version: 8.57.1 + resolution: "@typescript-eslint/visitor-keys@npm:8.57.1" dependencies: - "@typescript-eslint/types": "npm:8.46.0" - eslint-visitor-keys: "npm:^4.2.1" - checksum: 10c0/473dd4861b81238c1df10008b3b6d4684b2fa5ec4f3a8eeb544ea1278a5e2119f839447d16653ea3070164d7e742e3516fe9b0faf16e12a457fa26d5e14a7498 + "@typescript-eslint/types": "npm:8.57.1" + eslint-visitor-keys: "npm:^5.0.0" + checksum: 10c0/088a545c4aec6d9cabb266e1e40634f5fafa06cb05ef172526555957b0d99ac08822733fb788a09227071fdd6bd8b63f054393a0ecf9d4599c54b57918aa0e57 languageName: node linkType: hard -"@vitejs/plugin-react@npm:^4.3.3": +"@vitejs/plugin-react@npm:^4.7.0": version: 4.7.0 resolution: "@vitejs/plugin-react@npm:4.7.0" dependencies: @@ -5771,21 +5808,20 @@ __metadata: languageName: node linkType: hard -"autoprefixer@npm:^10.4.20": - version: 10.4.21 - resolution: "autoprefixer@npm:10.4.21" +"autoprefixer@npm:^10.4.27": + version: 10.4.27 + resolution: "autoprefixer@npm:10.4.27" dependencies: - browserslist: "npm:^4.24.4" - caniuse-lite: "npm:^1.0.30001702" - fraction.js: "npm:^4.3.7" - normalize-range: "npm:^0.1.2" + browserslist: "npm:^4.28.1" + caniuse-lite: "npm:^1.0.30001774" + fraction.js: "npm:^5.3.4" picocolors: "npm:^1.1.1" postcss-value-parser: "npm:^4.2.0" peerDependencies: postcss: ^8.1.0 bin: autoprefixer: bin/autoprefixer - checksum: 10c0/de5b71d26d0baff4bbfb3d59f7cf7114a6030c9eeb66167acf49a32c5b61c68e308f1e0f869d92334436a221035d08b51cd1b2f2c4689b8d955149423c16d4d4 + checksum: 10c0/698ad9e23436635af1806d4a8b80393020135174d1d4a97eb81887cdddac2f297f198d1d717932db8503b325f9f2dc3accb6e290b2d3ce1a7ddeb947100e5b25 languageName: node linkType: hard @@ -6634,7 +6670,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.0.0, browserslist@npm:^4.12.0, browserslist@npm:^4.24.0, browserslist@npm:^4.24.4, browserslist@npm:^4.25.3, browserslist@npm:^4.6.2, browserslist@npm:^4.6.4": +"browserslist@npm:^4.0.0, browserslist@npm:^4.12.0, browserslist@npm:^4.24.0, browserslist@npm:^4.25.3, browserslist@npm:^4.6.2, browserslist@npm:^4.6.4": version: 4.26.3 resolution: "browserslist@npm:4.26.3" dependencies: @@ -6996,14 +7032,14 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30000981, caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001125, caniuse-lite@npm:^1.0.30001702, caniuse-lite@npm:^1.0.30001746": +"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30000981, caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001125, caniuse-lite@npm:^1.0.30001746": version: 1.0.30001749 resolution: "caniuse-lite@npm:1.0.30001749" checksum: 10c0/f5100599361be03e93cae1f38380fdb78b477b573cb27f8aaf8ed8461ac0233ea84c7e127f441ca0a82ff23f4c37a8855b7c6a02e79d70769a786550bc1be69e languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001759": +"caniuse-lite@npm:^1.0.30001759, caniuse-lite@npm:^1.0.30001774": version: 1.0.30001780 resolution: "caniuse-lite@npm:1.0.30001780" checksum: 10c0/8a88f39758a228852d6f3ac92362ecb7694b1b2b022f194d8dfe59123ad40a5de6202bf2dff0fe316bb3d5ca9caf316c22056e0da693459c3be2771cde4f4bf9 @@ -9645,35 +9681,35 @@ __metadata: linkType: hard "esbuild@npm:^0.25.0": - version: 0.25.10 - resolution: "esbuild@npm:0.25.10" - dependencies: - "@esbuild/aix-ppc64": "npm:0.25.10" - "@esbuild/android-arm": "npm:0.25.10" - "@esbuild/android-arm64": "npm:0.25.10" - "@esbuild/android-x64": "npm:0.25.10" - "@esbuild/darwin-arm64": "npm:0.25.10" - "@esbuild/darwin-x64": "npm:0.25.10" - "@esbuild/freebsd-arm64": "npm:0.25.10" - "@esbuild/freebsd-x64": "npm:0.25.10" - "@esbuild/linux-arm": "npm:0.25.10" - "@esbuild/linux-arm64": "npm:0.25.10" - "@esbuild/linux-ia32": "npm:0.25.10" - "@esbuild/linux-loong64": "npm:0.25.10" - "@esbuild/linux-mips64el": "npm:0.25.10" - "@esbuild/linux-ppc64": "npm:0.25.10" - "@esbuild/linux-riscv64": "npm:0.25.10" - "@esbuild/linux-s390x": "npm:0.25.10" - "@esbuild/linux-x64": "npm:0.25.10" - "@esbuild/netbsd-arm64": "npm:0.25.10" - "@esbuild/netbsd-x64": "npm:0.25.10" - "@esbuild/openbsd-arm64": "npm:0.25.10" - "@esbuild/openbsd-x64": "npm:0.25.10" - "@esbuild/openharmony-arm64": "npm:0.25.10" - "@esbuild/sunos-x64": "npm:0.25.10" - "@esbuild/win32-arm64": "npm:0.25.10" - "@esbuild/win32-ia32": "npm:0.25.10" - "@esbuild/win32-x64": "npm:0.25.10" + version: 0.25.12 + resolution: "esbuild@npm:0.25.12" + dependencies: + "@esbuild/aix-ppc64": "npm:0.25.12" + "@esbuild/android-arm": "npm:0.25.12" + "@esbuild/android-arm64": "npm:0.25.12" + "@esbuild/android-x64": "npm:0.25.12" + "@esbuild/darwin-arm64": "npm:0.25.12" + "@esbuild/darwin-x64": "npm:0.25.12" + "@esbuild/freebsd-arm64": "npm:0.25.12" + "@esbuild/freebsd-x64": "npm:0.25.12" + "@esbuild/linux-arm": "npm:0.25.12" + "@esbuild/linux-arm64": "npm:0.25.12" + "@esbuild/linux-ia32": "npm:0.25.12" + "@esbuild/linux-loong64": "npm:0.25.12" + "@esbuild/linux-mips64el": "npm:0.25.12" + "@esbuild/linux-ppc64": "npm:0.25.12" + "@esbuild/linux-riscv64": "npm:0.25.12" + "@esbuild/linux-s390x": "npm:0.25.12" + "@esbuild/linux-x64": "npm:0.25.12" + "@esbuild/netbsd-arm64": "npm:0.25.12" + "@esbuild/netbsd-x64": "npm:0.25.12" + "@esbuild/openbsd-arm64": "npm:0.25.12" + "@esbuild/openbsd-x64": "npm:0.25.12" + "@esbuild/openharmony-arm64": "npm:0.25.12" + "@esbuild/sunos-x64": "npm:0.25.12" + "@esbuild/win32-arm64": "npm:0.25.12" + "@esbuild/win32-ia32": "npm:0.25.12" + "@esbuild/win32-x64": "npm:0.25.12" dependenciesMeta: "@esbuild/aix-ppc64": optional: true @@ -9729,7 +9765,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 10c0/8ee5fdd43ed0d4092ce7f41577c63147f54049d5617763f0549c638bbe939e8adaa8f1a2728adb63417eb11df51956b7b0d8eb88ee08c27ad1d42960256158fa + checksum: 10c0/c205357531423220a9de8e1e6c6514242bc9b1666e762cd67ccdf8fdfdc3f1d0bd76f8d9383958b97ad4c953efdb7b6e8c1f9ca5951cd2b7c5235e8755b34a6b languageName: node linkType: hard @@ -10097,6 +10133,13 @@ __metadata: languageName: node linkType: hard +"eslint-visitor-keys@npm:^5.0.0": + version: 5.0.1 + resolution: "eslint-visitor-keys@npm:5.0.1" + checksum: 10c0/16190bdf2cbae40a1109384c94450c526a79b0b9c3cb21e544256ed85ac48a4b84db66b74a6561d20fe6ab77447f150d711c2ad5ad74df4fcc133736bce99678 + languageName: node + linkType: hard + "eslint-webpack-plugin@npm:^2.1.0": version: 2.7.0 resolution: "eslint-webpack-plugin@npm:2.7.0" @@ -11062,10 +11105,10 @@ __metadata: languageName: node linkType: hard -"fraction.js@npm:^4.3.7": - version: 4.3.7 - resolution: "fraction.js@npm:4.3.7" - checksum: 10c0/df291391beea9ab4c263487ffd9d17fed162dbb736982dee1379b2a8cc94e4e24e46ed508c6d278aded9080ba51872f1bc5f3a5fd8d7c74e5f105b508ac28711 +"fraction.js@npm:^5.3.4": + version: 5.3.4 + resolution: "fraction.js@npm:5.3.4" + checksum: 10c0/f90079fe9bfc665e0a07079938e8ff71115bce9462f17b32fc283f163b0540ec34dc33df8ed41bb56f028316b04361b9a9995b9ee9258617f8338e0b05c5f95a languageName: node linkType: hard @@ -11534,17 +11577,17 @@ __metadata: dependencies: "@babel/core": "npm:^7.29.0" "@babel/preset-react": "npm:^7.28.5" - "@heroicons/react": "npm:^2.1.1" + "@heroicons/react": "npm:^2.2.0" "@playwright/test": "npm:^1.58.2" "@types/babel__core": "npm:^7" - "@types/lodash": "npm:^4.17.0" - "@types/prismjs": "npm:^1.26.3" + "@types/lodash": "npm:^4.17.24" + "@types/prismjs": "npm:^1.26.6" "@types/react": "npm:^19.0.0" "@types/react-dom": "npm:^19.0.0" - "@typescript-eslint/eslint-plugin": "npm:^8.15.0" - "@typescript-eslint/parser": "npm:^8.15.0" - "@vitejs/plugin-react": "npm:^4.3.3" - autoprefixer: "npm:^10.4.20" + "@typescript-eslint/eslint-plugin": "npm:^8.57.1" + "@typescript-eslint/parser": "npm:^8.57.1" + "@vitejs/plugin-react": "npm:^4.7.0" + autoprefixer: "npm:^10.4.27" eslint: "npm:^9.15.0" eslint-plugin-react-hooks: "npm:^5.0.0" eslint-plugin-react-refresh: "npm:^0.4.14" @@ -11555,25 +11598,25 @@ __metadata: gl-transitions: "npm:^1.43.0" hoist-non-react-statics: "npm:^3.3.2" invariant: "npm:^2.2.4" - lodash: "npm:^4.17.21" + lodash: "npm:^4.17.23" ndarray: "npm:^1.0.19" ndarray-ops: "npm:^1.2.2" - postcss: "npm:^8.4.49" + postcss: "npm:^8.5.8" prism-theme-one-dark: "npm:^1.0.0" - prismjs: "npm:^1.29.0" + prismjs: "npm:^1.30.0" prop-types: "npm:^15.8.1" - query-string: "npm:^9.1.0" + query-string: "npm:^9.3.1" raf: "npm:^3.4.1" react: "npm:^19.0.0" react-color: "npm:^2.19.3" react-dom: "npm:^19.0.0" react-gl-transition: "npm:^1.19.2" react-json2d: "npm:^0.3.0" - react-motion: "npm:^0.5.0" + react-motion: "npm:^0.5.2" react-router-dom: "npm:^6.28.0" tailwindcss: "npm:^3.4.15" - typescript: "npm:^5.6.3" - vite: "npm:^6.0.1" + typescript: "npm:^5.9.3" + vite: "npm:^6.4.1" languageName: unknown linkType: soft @@ -12013,13 +12056,6 @@ __metadata: languageName: node linkType: hard -"graphemer@npm:^1.4.0": - version: 1.4.0 - resolution: "graphemer@npm:1.4.0" - checksum: 10c0/e951259d8cd2e0d196c72ec711add7115d42eb9a8146c8eeda5b8d3ac91e5dd816b9cd68920726d9fd4490368e7ed86e9c423f40db87e2d8dfafa00fa17c3a31 - languageName: node - linkType: hard - "growly@npm:^1.3.0": version: 1.3.0 resolution: "growly@npm:1.3.0" @@ -12706,7 +12742,7 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^7.0.0": +"ignore@npm:^7.0.5": version: 7.0.5 resolution: "ignore@npm:7.0.5" checksum: 10c0/ae00db89fe873064a093b8999fe4cc284b13ef2a178636211842cceb650b9c3e390d3339191acb145d81ed5379d2074840cf0c33a20bdbd6f32821f79eb4ad5d @@ -15402,6 +15438,13 @@ __metadata: languageName: node linkType: hard +"lodash@npm:^4.17.23": + version: 4.17.23 + resolution: "lodash@npm:4.17.23" + checksum: 10c0/1264a90469f5bb95d4739c43eb6277d15b6d9e186df4ac68c3620443160fc669e2f14c11e7d8b2ccf078b81d06147c01a8ccced9aab9f9f63d50dcf8cace6bf6 + languageName: node + linkType: hard + "loglevel@npm:^1.6.8": version: 1.9.2 resolution: "loglevel@npm:1.9.2" @@ -18541,7 +18584,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.1.0, postcss@npm:^8.4.47, postcss@npm:^8.4.49, postcss@npm:^8.5.3": +"postcss@npm:^8.1.0, postcss@npm:^8.4.47": version: 8.5.6 resolution: "postcss@npm:8.5.6" dependencies: @@ -18552,6 +18595,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.5.3, postcss@npm:^8.5.8": + version: 8.5.8 + resolution: "postcss@npm:8.5.8" + dependencies: + nanoid: "npm:^3.3.11" + picocolors: "npm:^1.1.1" + source-map-js: "npm:^1.2.1" + checksum: 10c0/dd918f7127ee7c60a0295bae2e72b3787892296e1d1c3c564d7a2a00c68d8df83cadc3178491259daa19ccc54804fb71ed8c937c6787e08d8bd4bedf8d17044c + languageName: node + linkType: hard + "prebuild-install@npm:^7.1.3": version: 7.1.3 resolution: "prebuild-install@npm:7.1.3" @@ -18663,7 +18717,7 @@ __metadata: languageName: node linkType: hard -"prismjs@npm:^1.29.0": +"prismjs@npm:^1.30.0": version: 1.30.0 resolution: "prismjs@npm:1.30.0" checksum: 10c0/f56205bfd58ef71ccfcbcb691fd0eb84adc96c6ff21b0b69fc6fdcf02be42d6ef972ba4aed60466310de3d67733f6a746f89f2fb79c00bf217406d465b3e8f23 @@ -18959,7 +19013,7 @@ __metadata: languageName: node linkType: hard -"query-string@npm:^9.1.0": +"query-string@npm:^9.3.1": version: 9.3.1 resolution: "query-string@npm:9.3.1" dependencies: @@ -19224,7 +19278,7 @@ __metadata: languageName: node linkType: hard -"react-motion@npm:^0.5.0": +"react-motion@npm:^0.5.0, react-motion@npm:^0.5.2": version: 0.5.2 resolution: "react-motion@npm:0.5.2" dependencies: @@ -20389,31 +20443,34 @@ __metadata: linkType: hard "rollup@npm:^4.34.9": - version: 4.52.4 - resolution: "rollup@npm:4.52.4" - dependencies: - "@rollup/rollup-android-arm-eabi": "npm:4.52.4" - "@rollup/rollup-android-arm64": "npm:4.52.4" - "@rollup/rollup-darwin-arm64": "npm:4.52.4" - "@rollup/rollup-darwin-x64": "npm:4.52.4" - "@rollup/rollup-freebsd-arm64": "npm:4.52.4" - "@rollup/rollup-freebsd-x64": "npm:4.52.4" - "@rollup/rollup-linux-arm-gnueabihf": "npm:4.52.4" - "@rollup/rollup-linux-arm-musleabihf": "npm:4.52.4" - "@rollup/rollup-linux-arm64-gnu": "npm:4.52.4" - "@rollup/rollup-linux-arm64-musl": "npm:4.52.4" - "@rollup/rollup-linux-loong64-gnu": "npm:4.52.4" - "@rollup/rollup-linux-ppc64-gnu": "npm:4.52.4" - "@rollup/rollup-linux-riscv64-gnu": "npm:4.52.4" - "@rollup/rollup-linux-riscv64-musl": "npm:4.52.4" - "@rollup/rollup-linux-s390x-gnu": "npm:4.52.4" - "@rollup/rollup-linux-x64-gnu": "npm:4.52.4" - "@rollup/rollup-linux-x64-musl": "npm:4.52.4" - "@rollup/rollup-openharmony-arm64": "npm:4.52.4" - "@rollup/rollup-win32-arm64-msvc": "npm:4.52.4" - "@rollup/rollup-win32-ia32-msvc": "npm:4.52.4" - "@rollup/rollup-win32-x64-gnu": "npm:4.52.4" - "@rollup/rollup-win32-x64-msvc": "npm:4.52.4" + version: 4.59.0 + resolution: "rollup@npm:4.59.0" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.59.0" + "@rollup/rollup-android-arm64": "npm:4.59.0" + "@rollup/rollup-darwin-arm64": "npm:4.59.0" + "@rollup/rollup-darwin-x64": "npm:4.59.0" + "@rollup/rollup-freebsd-arm64": "npm:4.59.0" + "@rollup/rollup-freebsd-x64": "npm:4.59.0" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.59.0" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.59.0" + "@rollup/rollup-linux-arm64-gnu": "npm:4.59.0" + "@rollup/rollup-linux-arm64-musl": "npm:4.59.0" + "@rollup/rollup-linux-loong64-gnu": "npm:4.59.0" + "@rollup/rollup-linux-loong64-musl": "npm:4.59.0" + "@rollup/rollup-linux-ppc64-gnu": "npm:4.59.0" + "@rollup/rollup-linux-ppc64-musl": "npm:4.59.0" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.59.0" + "@rollup/rollup-linux-riscv64-musl": "npm:4.59.0" + "@rollup/rollup-linux-s390x-gnu": "npm:4.59.0" + "@rollup/rollup-linux-x64-gnu": "npm:4.59.0" + "@rollup/rollup-linux-x64-musl": "npm:4.59.0" + "@rollup/rollup-openbsd-x64": "npm:4.59.0" + "@rollup/rollup-openharmony-arm64": "npm:4.59.0" + "@rollup/rollup-win32-arm64-msvc": "npm:4.59.0" + "@rollup/rollup-win32-ia32-msvc": "npm:4.59.0" + "@rollup/rollup-win32-x64-gnu": "npm:4.59.0" + "@rollup/rollup-win32-x64-msvc": "npm:4.59.0" "@types/estree": "npm:1.0.8" fsevents: "npm:~2.3.2" dependenciesMeta: @@ -20439,8 +20496,12 @@ __metadata: optional: true "@rollup/rollup-linux-loong64-gnu": optional: true + "@rollup/rollup-linux-loong64-musl": + optional: true "@rollup/rollup-linux-ppc64-gnu": optional: true + "@rollup/rollup-linux-ppc64-musl": + optional: true "@rollup/rollup-linux-riscv64-gnu": optional: true "@rollup/rollup-linux-riscv64-musl": @@ -20451,6 +20512,8 @@ __metadata: optional: true "@rollup/rollup-linux-x64-musl": optional: true + "@rollup/rollup-openbsd-x64": + optional: true "@rollup/rollup-openharmony-arm64": optional: true "@rollup/rollup-win32-arm64-msvc": @@ -20465,7 +20528,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: 10c0/aaec0f57e887d4fb37d152f93cf7133954eec79d11643e95de768ec9a377f08793b1745c648ca65a0dcc6c795c4d9ca398724d013e5745de270e88a543782aea + checksum: 10c0/f38742da34cfee5e899302615fa157aa77cb6a2a1495e5e3ce4cc9c540d3262e235bbe60caa31562bbfe492b01fdb3e7a8c43c39d842d3293bcf843123b766fc languageName: node linkType: hard @@ -20740,7 +20803,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.1.1, semver@npm:^7.2.1, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.6.0": +"semver@npm:^7.1.1, semver@npm:^7.2.1, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.5.3": version: 7.7.3 resolution: "semver@npm:7.7.3" bin: @@ -20749,7 +20812,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.5.4": +"semver@npm:^7.5.4, semver@npm:^7.7.3": version: 7.7.4 resolution: "semver@npm:7.7.4" bin: @@ -22525,7 +22588,7 @@ __metadata: languageName: node linkType: hard -"tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.13": +"tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.13, tinyglobby@npm:^0.2.15": version: 0.2.15 resolution: "tinyglobby@npm:0.2.15" dependencies: @@ -22707,12 +22770,12 @@ __metadata: languageName: node linkType: hard -"ts-api-utils@npm:^2.1.0": - version: 2.1.0 - resolution: "ts-api-utils@npm:2.1.0" +"ts-api-utils@npm:^2.4.0": + version: 2.5.0 + resolution: "ts-api-utils@npm:2.5.0" peerDependencies: typescript: ">=4.8.4" - checksum: 10c0/9806a38adea2db0f6aa217ccc6bc9c391ddba338a9fe3080676d0d50ed806d305bb90e8cef0276e793d28c8a929f400abb184ddd7ff83a416959c0f4d2ce754f + checksum: 10c0/767849383c114e7f1971fa976b20e73ac28fd0c70d8d65c0004790bf4d8f89888c7e4cf6d5949f9c1beae9bc3c64835bef77bbe27fddf45a3c7b60cebcf85c8c languageName: node linkType: hard @@ -22963,7 +23026,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.6.3, typescript@npm:^5.7.0": +"typescript@npm:^5.7.0, typescript@npm:^5.9.3": version: 5.9.3 resolution: "typescript@npm:5.9.3" bin: @@ -22973,7 +23036,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.6.3#optional!builtin, typescript@patch:typescript@npm%3A^5.7.0#optional!builtin": +"typescript@patch:typescript@npm%3A^5.7.0#optional!builtin, typescript@patch:typescript@npm%3A^5.9.3#optional!builtin": version: 5.9.3 resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" bin: @@ -23716,9 +23779,9 @@ __metadata: languageName: node linkType: hard -"vite@npm:^6.0.1": - version: 6.3.6 - resolution: "vite@npm:6.3.6" +"vite@npm:^6.4.1": + version: 6.4.1 + resolution: "vite@npm:6.4.1" dependencies: esbuild: "npm:^0.25.0" fdir: "npm:^6.4.4" @@ -23767,7 +23830,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10c0/add701f1e72596c002275782e38d0389ab400c1be330c93a3009804d62db68097a936ca1c53c3301df3aaacfe5e328eab547060f31ef9c49a277ae50df6ad4fb + checksum: 10c0/77bb4c5b10f2a185e7859cc9a81c789021bc18009b02900347d1583b453b58e4b19ff07a5e5a5b522b68fc88728460bb45a63b104d969e8c6a6152aea3b849f7 languageName: node linkType: hard