From 968307e36784e0ce0c476ee5e301e094effb8620 Mon Sep 17 00:00:00 2001 From: Raanan Weber Date: Wed, 6 May 2026 16:33:53 +0200 Subject: [PATCH 01/12] Add compiled demos pipeline --- .prettierignore | 4 + .prettierrc.json | 5 + azure-pipelines.compiled-demos.yml | 60 + docs/compiled-demos.md | 94 + eslint.config.mjs | 38 + package-lock.json | 4798 +++++++++++++++++++++-- package.json | 26 +- scripts/demos/check-render.mjs | 239 ++ scripts/demos/generate-source-pages.mjs | 139 + src/compiledDemos/Boom/index.html | 105 + src/compiledDemos/Boom/main.ts | 6 + src/compiledDemos/Boom/scene.ts | 258 ++ src/compiledDemos/manifest.json | 21 + src/compiledDemos/shared/demoRunner.ts | 106 + tsconfig.demos.json | 17 + vite.demos.config.mjs | 32 + 16 files changed, 5629 insertions(+), 319 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc.json create mode 100644 azure-pipelines.compiled-demos.yml create mode 100644 docs/compiled-demos.md create mode 100644 eslint.config.mjs create mode 100644 scripts/demos/check-render.mjs create mode 100644 scripts/demos/generate-source-pages.mjs create mode 100644 src/compiledDemos/Boom/index.html create mode 100644 src/compiledDemos/Boom/main.ts create mode 100644 src/compiledDemos/Boom/scene.ts create mode 100644 src/compiledDemos/manifest.json create mode 100644 src/compiledDemos/shared/demoRunner.ts create mode 100644 tsconfig.demos.json create mode 100644 vite.demos.config.mjs diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..af289c005 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +build/ +node_modules/ +src/build/ +static/ \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 000000000..949de408b --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,5 @@ +{ + "printWidth": 120, + "tabWidth": 4, + "trailingComma": "es5" +} \ No newline at end of file diff --git a/azure-pipelines.compiled-demos.yml b/azure-pipelines.compiled-demos.yml new file mode 100644 index 000000000..c4d54f374 --- /dev/null +++ b/azure-pipelines.compiled-demos.yml @@ -0,0 +1,60 @@ +trigger: + branches: + include: + - master + paths: + include: + - azure-pipelines.compiled-demos.yml + - .prettierignore + - .prettierrc.json + - docs/compiled-demos.md + - eslint.config.mjs + - package.json + - package-lock.json + - scripts/demos/** + - src/compiledDemos/** + - static/Demos/** + - static/Scenes/** + - tsconfig.demos.json + - vite.demos.config.mjs + +pr: + branches: + include: + - master + paths: + include: + - azure-pipelines.compiled-demos.yml + - .prettierignore + - .prettierrc.json + - docs/compiled-demos.md + - eslint.config.mjs + - package.json + - package-lock.json + - scripts/demos/** + - src/compiledDemos/** + - static/Demos/** + - static/Scenes/** + - tsconfig.demos.json + - vite.demos.config.mjs + +pool: + vmImage: ubuntu-latest + +steps: + - task: NodeTool@0 + displayName: Use Node.js 20 + inputs: + versionSpec: 20.x + + - script: npm ci + displayName: Install dependencies + + - script: npx playwright install --with-deps chromium + displayName: Install Playwright Chromium + + - script: npm run build + displayName: Build site and compiled demos + + - script: npm run demos:ci + displayName: Lint, typecheck, and render-check compiled demos diff --git a/docs/compiled-demos.md b/docs/compiled-demos.md new file mode 100644 index 000000000..ac306e672 --- /dev/null +++ b/docs/compiled-demos.md @@ -0,0 +1,94 @@ +# Compiled Demos Architecture + +The legacy demos under `static/Demos/` are copied directly into `build/` and load Babylon from the UMD CDN. Compiled demos live under `src/compiledDemos/`, import `@babylonjs/core` and sibling packages as ES modules, and are bundled with Vite. + +## Goals + +- Keep existing demo URLs stable, for example `/Demos/Boom/`. +- Migrate one demo at a time without disturbing demos that still live in `static/Demos/`. +- Make new demos easy to add with a small source folder and one manifest entry. +- Check that compiled demos typecheck, bundle, load in a browser, create a scene, and render a nonblank canvas. + +## Build Flow + +1. `npm run site:build` runs the existing Eleventy build and copies `static/` to `build/`. +2. `npm run demos:build` runs Vite with `vite.demos.config.mjs` and generates source pages. +3. `npm run build` runs both steps in order, producing the full deployable site output. +4. Vite writes compiled demo pages into `build/Demos//`, overlaying only the migrated demos listed in `src/compiledDemos/manifest.json`. +5. Unmigrated demos continue to come from `static/Demos/`. + +This keeps the site and demo build steps available separately while making the default `build` script produce the final combined output. + +For local preview, run `npm run demos:serve`. This builds the site, overlays the compiled demos, and serves the final `build/` output with Vite preview. + +## Source Layout + +```text +src/compiledDemos/ + manifest.json + shared/ + demoRunner.ts + Boom/ + index.html + main.ts + scene.ts +``` + +Each demo folder owns its HTML shell and TypeScript entry. Shared browser bootstrapping lives in `shared/demoRunner.ts`, which creates the engine, starts the render loop, wires common controls, and exposes `window.__babylonDemoReady` for CI. + +Each demo page should include a small source link at the bottom of the viewport: + +```html +Source +``` + +`npm run demos:build` generates `/Demos//source/` from the files listed in `manifest.json`, so the site can show the exact TypeScript source for each compiled demo. + +## Adding A Demo + +1. Create `src/compiledDemos//index.html`, `main.ts`, and `scene.ts`. +2. Export a scene factory from `scene.ts`: + + ```ts + export function createScene(engine: Engine, canvas: HTMLCanvasElement): Scene { + // create and return a Babylon scene + } + ``` + +3. Call the shared runner from `main.ts`: + + ```ts + import { runDemo } from "../shared/demoRunner"; + import { createScene } from "./scene"; + + runDemo({ createScene }); + ``` + +4. Add the demo to `src/compiledDemos/manifest.json`. +5. Add `sourceFiles` for the files that should appear on the source page. +6. Run `npm run demos:format:write` if needed, then `npm run build && npm run demos:ci`. + +## Render Checks + +`npm run demos:check` serves the `build/` directory locally, opens each compiled demo with Playwright Chromium, waits for `window.__babylonDemoReady`, screenshots the canvas, samples pixels with Sharp, and fails if the canvas is blank or browser errors were reported. + +The initial check is intentionally a health check. It proves the demo compiles and renders. Individual demos can add `renderCheck.interaction` entries for important first-screen behavior; Boom uses this to click the canvas and verify the rendered output changes after the sign explodes. Screenshot baselines can be added later once the migration has enough coverage to justify the extra maintenance. + +## Linting And Formatting + +Compiled demo source is linted with ESLint and formatted with Prettier. The checks are intentionally scoped to the new compiled demo pipeline, not the legacy `static/` demo tree. + +- `npm run demos:lint` checks TypeScript demo source and demo build scripts. +- `npm run demos:format` verifies formatting for compiled demo source, config, and docs. +- `npm run demos:format:write` applies the configured formatting. + +## CI + +`azure-pipelines.compiled-demos.yml` is intentionally separate from the existing build CI. It runs `npm run build` first, which produces the site with compiled demos, and then runs `npm run demos:ci` to lint, format-check, typecheck, and render-check the compiled demos. If the main build pipeline can publish a build artifact later, this pipeline can be changed to consume that artifact instead of rebuilding the site. + +## Migration Notes + +- Custom scene-factory demos usually map cleanly from `BABYLON.*` globals to named ES module imports from `@babylonjs/core`. +- Demos that currently load `.babylon`, `.glb`, GUI, materials, loaders, or inspector packages should import the matching side-effect modules in their compiled entry. +- Keep asset URLs rooted at existing public paths such as `/Scenes/...` when assets are already served from `static/`. +- Once a compiled demo is stable, the old `static/Demos//` sources can be removed in a separate cleanup PR. diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 000000000..86204decb --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,38 @@ +import js from "@eslint/js"; +import globals from "globals"; +import tseslint from "typescript-eslint"; + +export default [ + { + ignores: ["build/**", "node_modules/**", "src/build/**", "static/**"], + }, + js.configs.recommended, + ...tseslint.configs.recommended, + { + files: ["scripts/demos/**/*.mjs", "vite.demos.config.mjs", "eslint.config.mjs"], + languageOptions: { + ecmaVersion: "latest", + sourceType: "module", + globals: { ...globals.node, ...globals.browser }, + }, + }, + { + files: ["src/compiledDemos/**/*.ts"], + languageOptions: { + ecmaVersion: "latest", + sourceType: "module", + globals: globals.browser, + }, + rules: { + "no-undef": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + }, + ], + }, + }, +]; diff --git a/package-lock.json b/package-lock.json index a3cb1984d..dbf97ac22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,21 @@ "dependencies": { "@11ty/eleventy": "^3.1.5", "sharp": "^0.33.5" + }, + "devDependencies": { + "@babylonjs/core": "^9.5.2", + "@babylonjs/gui": "^9.5.2", + "@babylonjs/inspector": "^9.5.2", + "@babylonjs/loaders": "^9.5.2", + "@babylonjs/materials": "^9.5.2", + "@eslint/js": "^10.0.1", + "eslint": "^10.3.0", + "globals": "^17.6.0", + "playwright": "^1.59.1", + "prettier": "^3.8.3", + "typescript": "^6.0.3", + "typescript-eslint": "^8.59.2", + "vite": "^8.0.10" } }, "node_modules/@11ty/dependency-tree": { @@ -189,406 +204,3226 @@ "node": ">=18" } }, - "node_modules/@emnapi/runtime": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", - "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "dev": true, "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "peer": true, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", - "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", - "cpu": [ - "arm64" - ], + "node_modules/@babylonjs/addons": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/@babylonjs/addons/-/addons-9.5.2.tgz", + "integrity": "sha512-vIk4EZqSfSiJHTDz1KXF/oqzFNmAYhbfALgQ4P8WBiwojvO66L54A5VISNuP6JSOn5cTqtRli/VLTXxCn5hK5Q==", + "dev": true, "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.4" + "peer": true, + "peerDependencies": { + "@babylonjs/core": "^9.0.0" } }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", - "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", - "cpu": [ - "x64" - ], + "node_modules/@babylonjs/core": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-9.5.2.tgz", + "integrity": "sha512-rh/ahhlEc8QQqmXx/ZZjuz36zoRbYvd0pUw3O2NFGtnamKCI4P/RuitSypn9KivvzfqMasNDwUJQrx5te3LXWg==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@babylonjs/gui": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-9.5.2.tgz", + "integrity": "sha512-LP0eb+VZSaK0PUOtyoQkX5B7OprQU/UodwSLkoNCuSFcuUQvPVW6+K7NgDQWF/6RgINujFFCI2nBYUrLpLJ+DA==", + "dev": true, "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.4" + "peerDependencies": { + "@babylonjs/core": "^9.0.0" } }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", - "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "node_modules/@babylonjs/gui-editor": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/@babylonjs/gui-editor/-/gui-editor-9.5.2.tgz", + "integrity": "sha512-D78CO6aDEjbzTYyu14gcGTP4CBpDDMu0DJ9yen1lPCsjOo9o5M3r6ilUbZmFGqzH4LitT0ZbY6LQD/BHxrZeGQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "peerDependencies": { + "@babylonjs/core": "^9.0.0", + "@babylonjs/gui": "^9.0.0", + "@types/react": ">=16.7.3", + "@types/react-dom": ">=16.0.9" } }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", - "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "node_modules/@babylonjs/inspector": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/@babylonjs/inspector/-/inspector-9.5.2.tgz", + "integrity": "sha512-dfLoetibow78e6dFA3LfsnmXNQXWa3XPwz1UEZvBm0jnruDHIPhgF8Ko8U0eau2MZ01jpGHwCdyAD6ztNuWVcQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "babylon-inspector": "bin/inspector-cli.mjs" + }, + "peerDependencies": { + "@babylonjs/addons": "^9.0.0", + "@babylonjs/core": "^9.0.0", + "@babylonjs/gui": "^9.0.0", + "@babylonjs/gui-editor": "^9.0.0", + "@babylonjs/loaders": "^9.0.0", + "@babylonjs/materials": "^9.0.0", + "@babylonjs/serializers": "^9.0.0", + "@fluentui-contrib/react-resize-handle": "^0.8.4", + "@fluentui-contrib/react-virtualizer": "^1.0.0", + "@fluentui/react-components": "^9.70.0", + "@fluentui/react-icons": "^2.0.310", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0", + "usehooks-ts": "^3.1.1" + } + }, + "node_modules/@babylonjs/loaders": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-9.5.2.tgz", + "integrity": "sha512-JAewBFzYfTUnSPyTboIz+NjQq4r+ki1ASdFrcj8a10N3+8Vn1xhAzUOr8HjVjyrWDYk7iIVGcbr2qtAu+nQ6/w==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "@babylonjs/core": "^9.0.0", + "babylonjs-gltf2interface": "^9.0.0" } }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", - "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "node_modules/@babylonjs/materials": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-9.5.2.tgz", + "integrity": "sha512-ufWYFfNlbrLjZF7e9pmSWgKfqT8Gp5WwQDBweGE9bAjrU0oY+z95aWL/fV2eZeGG9rIYxFf07LcCWsrAF+R/1Q==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "@babylonjs/core": "^9.0.0" } }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", - "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "node_modules/@babylonjs/serializers": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-9.5.2.tgz", + "integrity": "sha512-jU6JMPVSgTC0Q/GaukhtoBmij78yDNqRFC5DVxVlxYLO4MNwvC29IPMxu2KbMpUE1vyxlNxUce5e9dWziwWdCA==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "peerDependencies": { + "@babylonjs/core": "^9.0.0", + "babylonjs-gltf2interface": "^9.0.0" } }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", - "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" } }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", - "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" } }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", - "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "license": "MIT", "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "dependencies": { + "tslib": "^2.4.0" } }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", - "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "dependencies": { + "tslib": "^2.4.0" } }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", - "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/libvips" + "url": "https://opencollective.com/eslint" }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.5" + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", - "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.4" + "url": "https://opencollective.com/eslint" } }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", - "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.0.4" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", - "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" }, - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-array/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", + "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/devtools": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@floating-ui/devtools/-/devtools-0.2.3.tgz", + "integrity": "sha512-ZTcxTvgo9CRlP7vJV62yCxdqmahHTGpSTi5QaTDgGoyQq0OyjaVZhUhXv/qdkQFOI3Sxlfmz0XGG4HaZMsDf8Q==", + "dev": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "@floating-ui/dom": "^1.0.0" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@fluentui-contrib/react-resize-handle": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/@fluentui-contrib/react-resize-handle/-/react-resize-handle-0.8.4.tgz", + "integrity": "sha512-g3cJ3q+nn32BB5b5mEtuSh6sGNYOGcKvRxQljvfg+UAhStceCPZYRM8gF+HswPPhQ0LUR6h780WMe072ndR6Lw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-utilities": "^9.16.0", + "@swc/helpers": "~0.5.11" + }, + "peerDependencies": { + "@fluentui/react-components": ">=9.70.0 <10.0.0", + "@types/react": ">=16.8.0 <20.0.0", + "@types/react-dom": ">=16.8.0 <20.0.0", + "react": ">=16.8.0 <20.0.0", + "react-dom": ">=16.8.0 <20.0.0" + } + }, + "node_modules/@fluentui-contrib/react-virtualizer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@fluentui-contrib/react-virtualizer/-/react-virtualizer-1.0.0.tgz", + "integrity": "sha512-z1KE+OyCTICkOeyCmFI0nlNRTPAhCv2ZhCIDjebnlkNQMIcMtTrI/ogy4Fu8UgbFP+WReE50JeduLtM+Sst+1A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-jsx-runtime": "^9.0.29", + "@fluentui/react-utilities": "^9.16.0", + "@griffel/react": "^1.5.14", + "@swc/helpers": "~0.5.11" + }, + "peerDependencies": { + "@fluentui/react-shared-contexts": ">=9.7.2 <10.0.0", + "@types/react": ">=16.8.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.8.0 <20.0.0", + "react-dom": ">=16.8.0 <20.0.0" + } + }, + "node_modules/@fluentui/keyboard-keys": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@fluentui/keyboard-keys/-/keyboard-keys-9.0.8.tgz", + "integrity": "sha512-iUSJUUHAyTosnXK8O2Ilbfxma+ZyZPMua5vB028Ys96z80v+LFwntoehlFsdH3rMuPsA8GaC1RE7LMezwPBPdw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@swc/helpers": "^0.5.1" + } + }, + "node_modules/@fluentui/priority-overflow": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@fluentui/priority-overflow/-/priority-overflow-9.3.0.tgz", + "integrity": "sha512-yaBC0R4e+4ZlCWDulB5S+xBrlnLwfzdg68GaarCqQO8OHjLg7Ah05xTj7PsAYcoHeEg/9vYeBwGXBpRO8+Tjqw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@swc/helpers": "^0.5.1" + } + }, + "node_modules/@fluentui/react-accordion": { + "version": "9.11.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-accordion/-/react-accordion-9.11.0.tgz", + "integrity": "sha512-mEy73hbJM53tMj3MWqm3ajbBxj48uubnJjumVKI8Z/eXHS8L3GzUy5rf/gUH26xSR2Tl+edpFhYB8PFbJDIKKw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-motion-components-preview": "^0.15.4", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-alert": { + "version": "9.0.0-beta.139", + "resolved": "https://registry.npmjs.org/@fluentui/react-alert/-/react-alert-9.0.0-beta.139.tgz", + "integrity": "sha512-R9r4dwwpWpgFmB8wVeWqipjUh/e6lyacnerX39HtVdgcG/PE+kpdHjKGiy8MAD+BGYCzrUxKNhXTQDlpXasJ1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-avatar": "^9.11.1", + "@fluentui/react-button": "^9.9.1", + "@fluentui/react-icons": "^2.0.239", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-aria": { + "version": "9.17.11", + "resolved": "https://registry.npmjs.org/@fluentui/react-aria/-/react-aria-9.17.11.tgz", + "integrity": "sha512-K9nz+Wn5JliCpG6bIYYPXvKmpOql+w9uyzmYNYkYQ6QHgoCpph7XUFx1HCtsJm2PPNi8WO8g0ZV9jojdGKl1Tg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.8", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-utilities": "^9.26.3", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-avatar": { + "version": "9.11.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-avatar/-/react-avatar-9.11.1.tgz", + "integrity": "sha512-y1T67rVQQ/D4FAod8F4crXo9funaptscRIiW81LAsbN82fFVexMPQ9GmXooQQvn6ILvjJtf9IyvSJ195qDsyag==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-badge": "^9.5.2", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-popover": "^9.14.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-tooltip": "^9.10.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-badge": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-badge/-/react-badge-9.5.2.tgz", + "integrity": "sha512-+UPAK9dCD6Gx+LWr6vqKMIbYOPf7oXX+GXRtCJ5fekCTHD0VgIWuIMuEtxVrHpJQdb2VNaZadY8/dMomk2JaXw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-breadcrumb": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-breadcrumb/-/react-breadcrumb-9.4.1.tgz", + "integrity": "sha512-XgUB1yv04GdcL/6kUo6kh+BaN4df1A/Ds/fL1QxNrm5E26Vmvvlc0LN0WV/qb5qhKx0NwhtIXgOZHjfzyt7iCA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-button": "^9.9.1", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-link": "^9.8.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-button": { + "version": "9.9.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-button/-/react-button-9.9.1.tgz", + "integrity": "sha512-WNzpseiVbqEKKevTkAnyHNoK/8ktYPE6rvf31gGvSDnBBclqfrn4PSYG2ppi+Z7abmClnaNFxpp1OHuOoVQ8Bg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.8", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-card": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-card/-/react-card-9.6.1.tgz", + "integrity": "sha512-KBijjAxi0mBDSgnA1OCglqAVWc+Q0L7A2wCokszX/53oqfJPSvWxWFma7esz9b5MF/kdRrAR0vmy7MiosepNLQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.8", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-text": "^9.6.16", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-carousel": { + "version": "9.9.7", + "resolved": "https://registry.npmjs.org/@fluentui/react-carousel/-/react-carousel-9.9.7.tgz", + "integrity": "sha512-lummYk+tASL/rM/SXWruoqhUAyJjTiOMgiCz55ncE3q2pSZe/EbsV5WfRw5B3y7pHX8xLusN831TBgUthj/sUw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-button": "^9.9.1", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-tooltip": "^9.10.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1", + "embla-carousel": "^8.5.1", + "embla-carousel-autoplay": "^8.5.1", + "embla-carousel-fade": "^8.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-checkbox": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-checkbox/-/react-checkbox-9.6.1.tgz", + "integrity": "sha512-Rsf3TmcNrzLuHan9lyUFUmMZnNyvS7DV8C4Vc9lZnZTFRBo94GRMGzu0BcWKFbr3cCDT/r5RmIyQYz0kc7Jd2w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-label": "^9.4.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-color-picker": { + "version": "9.2.16", + "resolved": "https://registry.npmjs.org/@fluentui/react-color-picker/-/react-color-picker-9.2.16.tgz", + "integrity": "sha512-+H8Ea8dwoSeUCTLRpUiGLrRsNvBnlHplnwJPU0isp8jdAfrIM/savZTLj6o4rqNFpNHQqAXxGwNuUV9YfHoJuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@ctrl/tinycolor": "^3.3.4", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-combobox": { + "version": "9.17.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-combobox/-/react-combobox-9.17.1.tgz", + "integrity": "sha512-ezgt6tfOKd3wlG6IHvWl0TPNPpfHRtnEwC2kuqHYH/r1nMNp9edFi8Ya3+1eM7oxai19XW0swt69GPwRu51FVQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.8", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-portal": "^9.8.12", + "@fluentui/react-positioning": "^9.22.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-components": { + "version": "9.73.8", + "resolved": "https://registry.npmjs.org/@fluentui/react-components/-/react-components-9.73.8.tgz", + "integrity": "sha512-JG4KQjEvRRfPlh4yt6Rv1/k87ydM2y49r5XPNCnuYHahA7kEM+dY8JdOI7n7FW8bdcvZ7qt4smDrQ2XcPfmxlA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-accordion": "^9.11.0", + "@fluentui/react-alert": "9.0.0-beta.139", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-avatar": "^9.11.1", + "@fluentui/react-badge": "^9.5.2", + "@fluentui/react-breadcrumb": "^9.4.1", + "@fluentui/react-button": "^9.9.1", + "@fluentui/react-card": "^9.6.1", + "@fluentui/react-carousel": "^9.9.7", + "@fluentui/react-checkbox": "^9.6.1", + "@fluentui/react-color-picker": "^9.2.16", + "@fluentui/react-combobox": "^9.17.1", + "@fluentui/react-dialog": "^9.18.0", + "@fluentui/react-divider": "^9.7.1", + "@fluentui/react-drawer": "^9.12.0", + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-image": "^9.4.1", + "@fluentui/react-infobutton": "9.0.0-beta.115", + "@fluentui/react-infolabel": "^9.4.20", + "@fluentui/react-input": "^9.8.2", + "@fluentui/react-label": "^9.4.1", + "@fluentui/react-link": "^9.8.1", + "@fluentui/react-list": "^9.6.14", + "@fluentui/react-menu": "^9.24.1", + "@fluentui/react-message-bar": "^9.7.0", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-nav": "^9.3.24", + "@fluentui/react-overflow": "^9.7.2", + "@fluentui/react-persona": "^9.7.3", + "@fluentui/react-popover": "^9.14.2", + "@fluentui/react-portal": "^9.8.12", + "@fluentui/react-positioning": "^9.22.1", + "@fluentui/react-progress": "^9.5.1", + "@fluentui/react-provider": "^9.22.16", + "@fluentui/react-radio": "^9.6.2", + "@fluentui/react-rating": "^9.4.1", + "@fluentui/react-search": "^9.4.2", + "@fluentui/react-select": "^9.5.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-skeleton": "^9.7.2", + "@fluentui/react-slider": "^9.6.2", + "@fluentui/react-spinbutton": "^9.6.2", + "@fluentui/react-spinner": "^9.8.2", + "@fluentui/react-swatch-picker": "^9.5.2", + "@fluentui/react-switch": "^9.7.2", + "@fluentui/react-table": "^9.19.15", + "@fluentui/react-tabs": "^9.12.1", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-tag-picker": "^9.8.6", + "@fluentui/react-tags": "^9.8.1", + "@fluentui/react-teaching-popover": "^9.6.21", + "@fluentui/react-text": "^9.6.16", + "@fluentui/react-textarea": "^9.7.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-toast": "^9.7.17", + "@fluentui/react-toolbar": "^9.8.0", + "@fluentui/react-tooltip": "^9.10.1", + "@fluentui/react-tree": "^9.16.0", + "@fluentui/react-utilities": "^9.26.3", + "@fluentui/react-virtualizer": "9.0.0-alpha.112", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-context-selector": { + "version": "9.2.16", + "resolved": "https://registry.npmjs.org/@fluentui/react-context-selector/-/react-context-selector-9.2.16.tgz", + "integrity": "sha512-D+/X2liT+eZe0rzXbwddPH333ml2SXz71biR13aeyGJQr8+W+icMAIsYhpwk0CC3KtJ3f1/CLTm7vcIrvqsJ4g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-utilities": "^9.26.3", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0", + "scheduler": ">=0.19.0" + } + }, + "node_modules/@fluentui/react-dialog": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-dialog/-/react-dialog-9.18.0.tgz", + "integrity": "sha512-i+V2o0NJ1itjVADJFov5AR/JetpD2hCMiLye0vfi3/XsFMgEPZnGzILVxPCO/ovULTiCyThcL1UvY0d/PYrZfA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.8", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-motion-components-preview": "^0.15.4", + "@fluentui/react-portal": "^9.8.12", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-divider": { + "version": "9.7.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-divider/-/react-divider-9.7.1.tgz", + "integrity": "sha512-ptymE6iADb/ugezulaMeoAfGxKSwOjHEHBh8N1ydOR3AoOxsSUPkvoPC0mReO/yV5Nas7pz5s5VuJTspmFz0hA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-drawer": { + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-drawer/-/react-drawer-9.12.0.tgz", + "integrity": "sha512-PUXeXUH6JqwpjqYphHesHl75UAFSvxQJQqrevMFHE78ZF0Cqn59Xpa+8hGwRSuoRcYa90jjfHzJOOjN0iNM2iA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-dialog": "^9.18.0", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-motion-components-preview": "^0.15.4", + "@fluentui/react-portal": "^9.8.12", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-field": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-field/-/react-field-9.5.1.tgz", + "integrity": "sha512-u8J2d3AWb4yZXvy/mQd95y2lTon890RfybBTCbeBUzApGMI/77WqT5pRJ+zTM3lOMToPHVKylchNFusMpJaX9w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-label": "^9.4.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-icons": { + "version": "2.0.325", + "resolved": "https://registry.npmjs.org/@fluentui/react-icons/-/react-icons-2.0.325.tgz", + "integrity": "sha512-dbmpGMwyCFfkFdMSTEFnbNMXKDomPpqbzXVKRKM0EEQrcwOoQvm08d+SV+WWH11kni26SrgEtB9ycldD+fr92w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@griffel/react": "^1.6.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "react": ">=16.8.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-image": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-image/-/react-image-9.4.1.tgz", + "integrity": "sha512-yNd2Wq2xq952UUEVBkWeEmM7bTKdWx6BnsHPYRf0kdTADox2PquApYXsI1xw2pnAh3GSjARrGi9Eto0qxouLqA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-infobutton": { + "version": "9.0.0-beta.115", + "resolved": "https://registry.npmjs.org/@fluentui/react-infobutton/-/react-infobutton-9.0.0-beta.115.tgz", + "integrity": "sha512-b+4B0ODzPEb4jNaW9HdT6VVt3CL5FgPL2yuKzALBsYVl3udJdFpyxHsZEPf3JrVTBL/rgF2fRI1iAioX6Fl7DA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-icons": "^2.0.237", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-label": "^9.4.1", + "@fluentui/react-popover": "^9.14.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-infolabel": { + "version": "9.4.20", + "resolved": "https://registry.npmjs.org/@fluentui/react-infolabel/-/react-infolabel-9.4.20.tgz", + "integrity": "sha512-w4FOnNP+CtbVdKBEO6wXAcmOuPZWvmB/BJj+7J/8cLAQm7+4kQgitFHncU6rtFhPdGbikVoBf707/0R1mA4aIg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-label": "^9.4.1", + "@fluentui/react-popover": "^9.14.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.8.0 <20.0.0", + "@types/react-dom": ">=16.8.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.8.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-input": { + "version": "9.8.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-input/-/react-input-9.8.2.tgz", + "integrity": "sha512-t9zmqZR4bqeRjpWuCGfI4yrtPoCXFiK2XO4BoV5nNwAesglgz4+Vtso4YXst9QYEAazHtKI73YFJf1mn55hCuA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-jsx-runtime": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-jsx-runtime/-/react-jsx-runtime-9.4.2.tgz", + "integrity": "sha512-y3o0PBg2qzSdvgxDm7rH9BWq7E1h/eUWS+IhjQhd9dRpme6Py01+OLOglHojM5Tc9QjIp2Rjy2mFWBHXOR+8mw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-utilities": "^9.26.3", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "react": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-label": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-label/-/react-label-9.4.1.tgz", + "integrity": "sha512-4O3cPX6dSJVBKlIEbznjJ08utEc98lKbZz/6MZTTQfFgYl0TxAhxEDsIIIyNjj0Xy9eJpqubJsaswucWXTG/qg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-link": { + "version": "9.8.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-link/-/react-link-9.8.1.tgz", + "integrity": "sha512-ZxrCeX4pMWHujdmYV8b0QW0ztLtu0rHHvRNx67Y3WqSijVyij8QtNNiZ/nab+UDNlz9t8QIXKdWQgYj1uKDpMg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.8", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-list": { + "version": "9.6.14", + "resolved": "https://registry.npmjs.org/@fluentui/react-list/-/react-list-9.6.14.tgz", + "integrity": "sha512-B1mUQFvJOUlZysSduVnATNZggrGpgEWnW9ZSJAZ17LM0+9nWEQRi40jpUGI/d3PGKHt5O2df78s+1nEPAk0L6A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.8", + "@fluentui/react-checkbox": "^9.6.1", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.8.0 <20.0.0", + "@types/react-dom": ">=16.8.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.8.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-menu": { + "version": "9.24.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-menu/-/react-menu-9.24.1.tgz", + "integrity": "sha512-NLB5EhzKFiwax3O5JTRTtsqdEFDGEXzEuP/suyxNAaaQsIuXygo//Rmdq6dSn7GybTpEOZHKxYDyyG7dj+a4YA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.8", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-motion-components-preview": "^0.15.4", + "@fluentui/react-portal": "^9.8.12", + "@fluentui/react-positioning": "^9.22.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-message-bar": { + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-message-bar/-/react-message-bar-9.7.0.tgz", + "integrity": "sha512-ICFDxZ62r5OG97/FcfK1EfJPxGlyDNyFixLD/a3gOREvEcT/hyZgnlUM9Y30u92HjxChx2SwGWnv3iaQPsvToQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-button": "^9.9.1", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-link": "^9.8.1", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-motion-components-preview": "^0.15.4", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.8.0 <20.0.0", + "@types/react-dom": ">=16.8.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.8.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-motion": { + "version": "9.15.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-motion/-/react-motion-9.15.0.tgz", + "integrity": "sha512-ZNQHYzE6MRbLQFT08/mrcqQ9k7F5niktRP93X1v/kmwKfPjvdDofySfbhQXQs3zQw600690C9rfJTKUd3h+zlg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-utilities": "^9.26.3", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.8.0 <20.0.0", + "@types/react-dom": ">=16.8.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.8.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-motion-components-preview": { + "version": "0.15.4", + "resolved": "https://registry.npmjs.org/@fluentui/react-motion-components-preview/-/react-motion-components-preview-0.15.4.tgz", + "integrity": "sha512-gAHPlyEYylZzUSGwc68VaB+vO8CTX6tgIA3d2+jFrpcwvXZjsdCpF1w1zK1+hTuiipmEaZLZyBz0e0CKH2+3XQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-motion": "*", + "@fluentui/react-utilities": "*", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-nav": { + "version": "9.3.24", + "resolved": "https://registry.npmjs.org/@fluentui/react-nav/-/react-nav-9.3.24.tgz", + "integrity": "sha512-OlB5k5Zev5VNjSRfJvJLO09Hjcv2UHAjLpSVa6gKHx+1NqqSJWZeDLSF7r+/nyZ4CWP5jWZYq7whEu3WvzdVZw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-button": "^9.9.1", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-divider": "^9.7.1", + "@fluentui/react-drawer": "^9.12.0", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-motion-components-preview": "^0.15.4", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-tooltip": "^9.10.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-overflow": { + "version": "9.7.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-overflow/-/react-overflow-9.7.2.tgz", + "integrity": "sha512-5PA67LgnVmbbOzBN2H5gH3OvSVy1373VJfsHq2+6TLCfm+LXAkWBoFwvBuFI7HsMYae9A0FVlgX7gTsKVfMddw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/priority-overflow": "^9.3.0", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-persona": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@fluentui/react-persona/-/react-persona-9.7.3.tgz", + "integrity": "sha512-OY3xpSD6l4NDdeKihriC+H0q6P1CA2xyZ+pe/WwfKPnatxs2BALoRFtDQduMO7AK/j0w7UAxnaZrvEeftLen2g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-avatar": "^9.11.1", + "@fluentui/react-badge": "^9.5.2", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-popover": { + "version": "9.14.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-popover/-/react-popover-9.14.2.tgz", + "integrity": "sha512-EDvzLkT98/vcCSGrcZWUACGsvLjrHin0Xf9eowMQKiiHFWbu8HNRmr7W2XB9Eja1W5HSIK6+mV8ro9zrLibG4w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.8", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-motion-components-preview": "^0.15.4", + "@fluentui/react-portal": "^9.8.12", + "@fluentui/react-positioning": "^9.22.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-portal": { + "version": "9.8.12", + "resolved": "https://registry.npmjs.org/@fluentui/react-portal/-/react-portal-9.8.12.tgz", + "integrity": "sha512-+WH0wH/5lsodGP6Mam1alHXpkMCYA5uMcnF98RVOs7/GR69KiFcza1mCnvPJUaJ55AfwLuz/xLxuWdWgQnUdMQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-positioning": { + "version": "9.22.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-positioning/-/react-positioning-9.22.1.tgz", + "integrity": "sha512-/r1BHQKr/WCjEM8UGloiq7bWWBSYB/Uqt7D1sAF9EHd968VH07cAN3RMVKmWWjeJO31rstOZHdgcz0WHhFF+2Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@floating-ui/devtools": "^0.2.3", + "@floating-ui/dom": "^1.6.12", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1", + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-progress": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-progress/-/react-progress-9.5.1.tgz", + "integrity": "sha512-EXJ/Bp67d5+bXPNpPabxdtXUgCMTtvYrBoKtIS6wE5KeUzaek/rgQ3v5wnGfbuLnJ4J/kj+n7XQEc9fhoFPy9w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-provider": { + "version": "9.22.16", + "resolved": "https://registry.npmjs.org/@fluentui/react-provider/-/react-provider-9.22.16.tgz", + "integrity": "sha512-S77n5ASUWE/V1I6lX09CrHm4TAKSGENhIrKz9qMKDv2Vrq44/j3eGBLz12k8IW4TJVu9nwGwst9kBpCT+3WHpA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/core": "^1.16.0", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-radio": { + "version": "9.6.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-radio/-/react-radio-9.6.2.tgz", + "integrity": "sha512-Sp2us4eWRopUKOMCQw5/iks7euPKY6FeesBCCUIVGBg5VKZf2/CfEtbCa9hMjn4D4PCHGivnUTf23t238mvvnw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-label": "^9.4.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-rating": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-rating/-/react-rating-9.4.1.tgz", + "integrity": "sha512-DfWipzrT44j+yaShtfHz96/vHEa5ut5IR1kobrO0bSqAcpetOn327gFeY+sG/W6xzork/STcy/T836yK8A2+DQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.8.0 <20.0.0", + "@types/react-dom": ">=16.8.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.8.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-search": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-search/-/react-search-9.4.2.tgz", + "integrity": "sha512-PIb50euHoMsKWLqFymf8wo/+z1jrx1MB7uNuhjNT5DvwTP4VYAy5EtRCSwVRyxWNSaWSL6iy6dDy517EQE96mA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-input": "^9.8.2", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-select": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-select/-/react-select-9.5.1.tgz", + "integrity": "sha512-8GocQKiUHEUlAks6zA0HbGGSF2lpjuSZuxPzIBqTyuWof8vFiK6eFAcSXb0hTYIVH3RsTihhfc6G3NRnHoBrzg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-shared-contexts": { + "version": "9.26.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-shared-contexts/-/react-shared-contexts-9.26.2.tgz", + "integrity": "sha512-upKXkwlIp5oIhELr4clAZXQkuCd4GDXM6GZEz8BOmRO+PnxyqmycCXvxDxsmi6XN+0vkGM4joiIgkB14o/FctQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-theme": "^9.2.1", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "react": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-skeleton": { + "version": "9.7.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-skeleton/-/react-skeleton-9.7.2.tgz", + "integrity": "sha512-PrUgdSGDAZw9FIP5NyvPoPfHDe2N9VxMyBfyTwWfZVg03dzRfnE3vEqr7N5xyfv4JsRs6u1xSqVn/0jdS0IEMQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-slider": { + "version": "9.6.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-slider/-/react-slider-9.6.2.tgz", + "integrity": "sha512-lVavtTg8eqovfRokeYDk4popwCi8nuicacJ/HZdF3ni5e3y/2WT/bVP0eErS/GvC7+90ACQQ8uxdr4sjjY/HWA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-spinbutton": { + "version": "9.6.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-spinbutton/-/react-spinbutton-9.6.2.tgz", + "integrity": "sha512-P4vvJH7P5yHPFAv6aSo3dZxtErN62DiRJN+nEKS+/XBoRGsOGQdqyyx5Q/PQKOmyQrtwuZdXNHUjcyv8b50T2w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.8", + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-spinner": { + "version": "9.8.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-spinner/-/react-spinner-9.8.2.tgz", + "integrity": "sha512-0LxykLJGUD/I3XEeIXAWznwdg9XRe0piaByR0nLFOOV3UPwkVc2w5UdPhy2Y0NZDvtPHbNaMCuQAq82+bxg/0w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-label": "^9.4.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-swatch-picker": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-swatch-picker/-/react-swatch-picker-9.5.2.tgz", + "integrity": "sha512-DK6UU9OJY9XaGBPU2ROx+B5/7XdwVtHBdVthOAptyKSsYGOdQt5AQqg3ZOXH6r5WYbMRQDuP2OZ2iKtwidFCVQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.8.0 <20.0.0", + "@types/react-dom": ">=16.8.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.8.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-switch": { + "version": "9.7.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-switch/-/react-switch-9.7.2.tgz", + "integrity": "sha512-j3e5se+3d+befV9MytkxxvJ9nHZOeZ7thKDTF4YVSYf6kcNx9eOlLvPgDjhGO08gzngO4B7aaprhDN7DJc3W1g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-label": "^9.4.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-table": { + "version": "9.19.15", + "resolved": "https://registry.npmjs.org/@fluentui/react-table/-/react-table-9.19.15.tgz", + "integrity": "sha512-OdQ2Nwx2nAlPMlJeyAFrKa3Zy5Ya/H87OU8MvtFJhabM/FkHiZoli/DO1mavVI+jqavOlJuQWmJ55D6jjuGa7g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.8", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-avatar": "^9.11.1", + "@fluentui/react-checkbox": "^9.6.1", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-radio": "^9.6.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-tabs": { + "version": "9.12.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-tabs/-/react-tabs-9.12.1.tgz", + "integrity": "sha512-WvzOtpC6C/7Mo5X+xmE+3stpCbx2iH9BqrEN5KuGrsHJ78DjMDeabYeL90vlrHBdP4VlTpwdORBui/jtWkxnmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-tabster": { + "version": "9.26.14", + "resolved": "https://registry.npmjs.org/@fluentui/react-tabster/-/react-tabster-9.26.14.tgz", + "integrity": "sha512-WibgoF67hl6BXfmsY6RSIWSHadeMP/6EDG9gAacfHlwKvK0+FiHp5ernwuXTPAmu2kiHicn2qUZ8EteCFiFryg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1", + "keyborg": "^2.6.0", + "tabster": "^8.5.5" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-tag-picker": { + "version": "9.8.6", + "resolved": "https://registry.npmjs.org/@fluentui/react-tag-picker/-/react-tag-picker-9.8.6.tgz", + "integrity": "sha512-sOZ+wBA3hgGhKrOP7wbjB2yRvAxjcRXtcj1jDTrtSkaDPXb3K0nGmjiqp2mve995ps3wvCGnNKK4EurX842ZbA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.8", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-combobox": "^9.17.1", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-portal": "^9.8.12", + "@fluentui/react-positioning": "^9.22.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-tags": "^9.8.1", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-tags": { + "version": "9.8.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-tags/-/react-tags-9.8.1.tgz", + "integrity": "sha512-6ZTW78fu5eWByKHIM3i+raDrX3hwfZ67ONfZ8wEUXfZHowskxqpMHI8Gw7IAMWkC1scgLqEnht8TnHvZgjo7Ug==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.8", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-avatar": "^9.11.1", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-teaching-popover": { + "version": "9.6.21", + "resolved": "https://registry.npmjs.org/@fluentui/react-teaching-popover/-/react-teaching-popover-9.6.21.tgz", + "integrity": "sha512-V86zLB1B8xu3U/02FvvMdsJP+ZC9l3vT9bQ2Gr7hZHxJ4/0NLpVcrYSBFHpON9e/WK3p+A5b5V96p86b5Pavlg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-button": "^9.9.1", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-popover": "^9.14.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1", + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "@types/react": ">=16.8.0 <20.0.0", + "@types/react-dom": ">=16.8.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.8.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-text": { + "version": "9.6.16", + "resolved": "https://registry.npmjs.org/@fluentui/react-text/-/react-text-9.6.16.tgz", + "integrity": "sha512-ZzCSJWQ6LrVuPqA6sqNEZaXbLvhi2NxBOtlMudWlqYzidLQp038d7mMGSzNnhyeblg+gj+bOVE2eOgWFuVHGYw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-textarea": { + "version": "9.7.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-textarea/-/react-textarea-9.7.2.tgz", + "integrity": "sha512-awlkZoW81WaOqSoXTT9rZs3mTAzCCHnC9eAm6J8ZxI5+ASX07BTolBfZ82it5wxOHI5GMDfbFOl+xIy8uAMdzA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-theme": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-theme/-/react-theme-9.2.1.tgz", + "integrity": "sha512-lJxfz7LmmglFz+c9C41qmMqaRRZZUPtPPl9DWQ79vH+JwZd4dkN7eA78OTRwcGCOTPEKoLTX72R+EFaWEDlX+w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/tokens": "1.0.0-alpha.23", + "@swc/helpers": "^0.5.1" + } + }, + "node_modules/@fluentui/react-toast": { + "version": "9.7.17", + "resolved": "https://registry.npmjs.org/@fluentui/react-toast/-/react-toast-9.7.17.tgz", + "integrity": "sha512-DWA5EARWSo1k19iWAulLpKrcUHT+Dq/Bw9zfdpoQEWWybrAZwyN7WiYFkBjKCQRxSp10OjLASSQK91CXfb1wJA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.8", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-motion-components-preview": "^0.15.4", + "@fluentui/react-portal": "^9.8.12", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-toolbar": { + "version": "9.8.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-toolbar/-/react-toolbar-9.8.0.tgz", + "integrity": "sha512-EIe+QWOaFR1pZzENefsFTmjxGa2yJb4A/by3kGuGqSjx7isqPUllPq0/kFQzfoUYgPDJbJtQ+KyuRDekTL0QpQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-button": "^9.9.1", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-divider": "^9.7.1", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-radio": "^9.6.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-tooltip": { + "version": "9.10.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-tooltip/-/react-tooltip-9.10.1.tgz", + "integrity": "sha512-IPHBFjqGhaaMDhLt5NSNOE9LEpDOpT7qgEqNz+Mlflo0A4qI2LW/EnkNop7IRmX/bC88A+wUtEONTjjR87dNBw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.8", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-portal": "^9.8.12", + "@fluentui/react-positioning": "^9.22.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-tree": { + "version": "9.16.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-tree/-/react-tree-9.16.0.tgz", + "integrity": "sha512-c+Q4AVaYk9U69aGDgmJVNne+CtWKS75YIfGoxs6+9+wE2Wqz4T0E+gE1ng7ARCQQgI7E2NEJlot6DuI6nYYrRw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.8", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-avatar": "^9.11.1", + "@fluentui/react-button": "^9.9.1", + "@fluentui/react-checkbox": "^9.6.1", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-icons": "^2.0.245", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-motion-components-preview": "^0.15.4", + "@fluentui/react-radio": "^9.6.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-utilities": { + "version": "9.26.3", + "resolved": "https://registry.npmjs.org/@fluentui/react-utilities/-/react-utilities-9.26.3.tgz", + "integrity": "sha512-bXB3jMm/RroT8c5eGZkijkPbLd4MqMI6biBHjavo0e7OkZHv9IPfH2nDkGhSn5Sh8e6kRcX0IjYhbM10WUK2iQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.8", + "@fluentui/react-shared-contexts": "^9.26.2", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "react": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/react-virtualizer": { + "version": "9.0.0-alpha.112", + "resolved": "https://registry.npmjs.org/@fluentui/react-virtualizer/-/react-virtualizer-9.0.0-alpha.112.tgz", + "integrity": "sha512-dao/mQssaPFxCXMx7K+G/DrRoZg28kXcE1NGbJ1RPtbkVCzJgwrEEeDhM5/wyOXO/Z5EZ31FIerDDVOyr6FAaw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-utilities": "^9.26.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <20.0.0", + "@types/react-dom": ">=16.9.0 <20.0.0", + "react": ">=16.14.0 <20.0.0", + "react-dom": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@fluentui/tokens": { + "version": "1.0.0-alpha.23", + "resolved": "https://registry.npmjs.org/@fluentui/tokens/-/tokens-1.0.0-alpha.23.tgz", + "integrity": "sha512-uxrzF9Z+J10naP0pGS7zPmzSkspSS+3OJDmYIK3o1nkntQrgBXq3dBob4xSlTDm5aOQ0kw6EvB9wQgtlyy4eKQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@swc/helpers": "^0.5.1" + } + }, + "node_modules/@griffel/core": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@griffel/core/-/core-1.21.0.tgz", + "integrity": "sha512-QqMoewiNTT0DmLM7OY607c7yhg18SuKfzovTO3hPXGQvtdu/StnjvuBAs+1B1kYSVGReAo6s/dJVeLnPuHjE7Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@emotion/hash": "^0.9.0", + "@griffel/style-types": "^1.4.0", + "csstype": "^3.1.3", + "rtl-css-js": "^1.16.1", + "stylis": "^4.4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@griffel/react": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@griffel/react/-/react-1.7.2.tgz", + "integrity": "sha512-/+N+81e9ibNsh2wNlhGf2PcimEFrx8VbtWiLCmI6lsrTV5eBcQrIIDVQq737KPIJFtD4v6Txu9n0PXbN3hedNg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@griffel/core": "^1.21.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "react": ">=16.14.0 <20.0.0" + } + }, + "node_modules/@griffel/style-types": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@griffel/style-types/-/style-types-1.4.0.tgz", + "integrity": "sha512-vNDfOGV7RN/XkA7vxgf7Z5HgW8eiBm5cHT9wQPhsKB4pxWom5u6eQ9CkYE5mCCTSPl9H6Nd1NBai04d4P6BD7Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.1.3" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" }, "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" } }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", - "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.127.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.127.0.tgz", + "integrity": "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.17.tgz", + "integrity": "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.17.tgz", + "integrity": "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.17.tgz", + "integrity": "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==", "cpu": [ "arm64" ], - "license": "Apache-2.0", + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", - "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.17.tgz", + "integrity": "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==", "cpu": [ "x64" ], - "license": "Apache-2.0", + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.17.tgz", + "integrity": "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.17.tgz", + "integrity": "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz", + "integrity": "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.17.tgz", + "integrity": "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.17.tgz", + "integrity": "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sindresorhus/slugify": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/slugify/-/slugify-2.2.1.tgz", + "integrity": "sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw==", + "license": "MIT", + "dependencies": { + "@sindresorhus/transliterate": "^1.0.0", + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@sindresorhus/transliterate": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/transliterate/-/transliterate-1.6.0.tgz", + "integrity": "sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.21.tgz", + "integrity": "sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.2.tgz", + "integrity": "sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/type-utils": "8.59.2", + "@typescript-eslint/utils": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.59.2", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.2.tgz", + "integrity": "sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.2.tgz", + "integrity": "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.59.2", + "@typescript-eslint/types": "^8.59.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz", + "integrity": "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz", + "integrity": "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.2.tgz", + "integrity": "sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/utils": "8.59.2", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/libvips" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@img/sharp-wasm32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", - "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, + "node_modules/@typescript-eslint/types": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.2.tgz", + "integrity": "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz", + "integrity": "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==", + "dev": true, + "license": "MIT", "dependencies": { - "@emnapi/runtime": "^1.2.0" + "@typescript-eslint/project-service": "8.59.2", + "@typescript-eslint/tsconfig-utils": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/libvips" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", - "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" }, - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": "18 || 20 || >=22" } }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", - "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "18 || 20 || >=22" }, "funding": { - "url": "https://opencollective.com/libvips" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@sindresorhus/slugify": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/slugify/-/slugify-2.2.1.tgz", - "integrity": "sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw==", + "node_modules/@typescript-eslint/utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.2.tgz", + "integrity": "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==", + "dev": true, "license": "MIT", "dependencies": { - "@sindresorhus/transliterate": "^1.0.0", - "escape-string-regexp": "^5.0.0" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2" }, "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@sindresorhus/transliterate": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/transliterate/-/transliterate-1.6.0.tgz", - "integrity": "sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz", + "integrity": "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==", + "dev": true, "license": "MIT", "dependencies": { - "escape-string-regexp": "^5.0.0" + "@typescript-eslint/types": "8.59.2", + "eslint-visitor-keys": "^5.0.0" }, "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/a-sync-waterfall": { @@ -609,6 +3444,16 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/acorn-walk": { "version": "8.3.5", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", @@ -621,6 +3466,23 @@ "node": ">=0.4.0" } }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -658,6 +3520,14 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "license": "MIT" }, + "node_modules/babylonjs-gltf2interface": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-9.5.2.tgz", + "integrity": "sha512-ICFeKhRDruVXezoQVdjQCjjnxYWNAiRFcLRvw+e6FrHyuhjB621EgAzTijDqE7y8E3OZ53vaHKR9T4ADZ9lEnQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -817,6 +3687,29 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "license": "MIT" }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -834,6 +3727,13 @@ } } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -931,6 +3831,36 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/embla-carousel": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", + "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/embla-carousel-autoplay": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-autoplay/-/embla-carousel-autoplay-8.6.0.tgz", + "integrity": "sha512-OBu5G3nwaSXkZCo1A6LTaFMZ8EpkYbwIaH+bPqdBnDGQ2fh4+NbzjXjs2SktoPNKCtflfVMc75njaDHOYXcrsA==", + "dev": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "embla-carousel": "8.6.0" + } + }, + "node_modules/embla-carousel-fade": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-fade/-/embla-carousel-fade-8.6.0.tgz", + "integrity": "sha512-qaYsx5mwCz72ZrjlsXgs1nKejSrW+UhkbOMwLgfRT7w2LtdEB03nPRI06GHuHv5ac2USvbEiX2/nAHctcDwvpg==", + "dev": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "embla-carousel": "8.6.0" + } + }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -982,6 +3912,159 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz", + "integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.5.5", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/esm-import-transformer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/esm-import-transformer/-/esm-import-transformer-3.0.5.tgz", @@ -991,6 +4074,24 @@ "acorn": "^8.15.0" } }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -1004,6 +4105,52 @@ "node": ">=4" } }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -1034,6 +4181,27 @@ "node": ">=0.10.0" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -1051,6 +4219,19 @@ } } }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/filesize": { "version": "10.1.6", "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.1.6.tgz", @@ -1086,24 +4267,62 @@ "statuses": "~2.0.2", "unpipe": "~1.0.0" }, - "engines": { - "node": ">= 0.8" + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, "license": "MIT", "dependencies": { - "ms": "2.0.0" + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" } }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" }, "node_modules/fresh": { "version": "2.0.0", @@ -1140,6 +4359,19 @@ "node": ">= 6" } }, + "node_modules/globals": { + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.6.0.tgz", + "integrity": "sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gray-matter": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", @@ -1237,6 +4469,26 @@ "url": "https://opencollective.com/express" } }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -1340,6 +4592,13 @@ "node": ">=0.12.0" } }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, "node_modules/iso-639-1": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/iso-639-1/-/iso-639-1-3.1.5.tgz", @@ -1361,6 +4620,27 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/junk": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", @@ -1370,6 +4650,24 @@ "node": ">=8" } }, + "node_modules/keyborg": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/keyborg/-/keyborg-2.14.1.tgz", + "integrity": "sha512-/WmmVBa6Me3hIKAOIyIq1sql+6oydQZzGMBDLNfOcJ8710byMsq3KSLS8GQhBJHOMtvnXnUBrDAIbABcZVipcg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -1388,6 +4686,281 @@ "node": ">=6" } }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/linkify-it": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", @@ -1423,6 +4996,30 @@ "integrity": "sha512-+dAZZ2mM+/m+vY9ezfoueVvrgnHIGi5FvgSymbIgJOFwiznWyA59mav95L+Mc6xPtL3s9gm5eNTlNtxJLbNM1g==", "license": "MIT" }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/luxon": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", @@ -1552,6 +5149,32 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, "node_modules/node-retrieve-globals": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/node-retrieve-globals/-/node-retrieve-globals-6.0.1.tgz", @@ -1603,19 +5226,69 @@ "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "license": "MIT", "engines": { - "node": ">= 6" + "node": ">= 6" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, "license": "MIT", "dependencies": { - "ee-first": "1.1.1" + "p-limit": "^3.0.2" }, "engines": { - "node": ">= 0.8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/parse-srcset": { @@ -1633,6 +5306,33 @@ "node": ">= 0.8" } }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/picomatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", @@ -1645,6 +5345,53 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/playwright": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/please-upgrade-node": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", @@ -1654,6 +5401,35 @@ "semver-compare": "^1.0.0" } }, + "node_modules/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/posthtml": { "version": "0.16.7", "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.7.tgz", @@ -1703,12 +5479,48 @@ "node": ">=12" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", "license": "MIT" }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/punycode.js": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", @@ -1727,6 +5539,31 @@ "node": ">= 0.6" } }, + "node_modules/react": { + "version": "19.2.5", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", + "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.5", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", + "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.5" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -1751,6 +5588,59 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/rolldown": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.17.tgz", + "integrity": "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.127.0", + "@rolldown/pluginutils": "1.0.0-rc.17" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.17", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.17", + "@rolldown/binding-darwin-x64": "1.0.0-rc.17", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.17", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.17", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.17", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.17", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17" + } + }, + "node_modules/rtl-css-js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz", + "integrity": "sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.1.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/section-matter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", @@ -1853,6 +5743,29 @@ "@img/sharp-win32-x64": "0.33.5" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/simple-swizzle": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", @@ -1880,6 +5793,16 @@ "node": ">=8.0.0" } }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -1916,6 +5839,26 @@ "node": ">=0.10.0" } }, + "node_modules/stylis": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz", + "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/tabster": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/tabster/-/tabster-8.8.0.tgz", + "integrity": "sha512-eGFXgtvKOQP5BywDI9Ngs+Atm6TRj45epAAqWKyVoi+HmOmdamEB//1H/FttLdNly/+Cz+GJ4RN8TnXTw0KwfA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "keyborg": "^2.14.0", + "tslib": "^2.8.1" + } + }, "node_modules/tinyglobby": { "version": "0.2.16", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", @@ -1953,12 +5896,76 @@ "node": ">=0.6" } }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "optional": true + "devOptional": true, + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.2.tgz", + "integrity": "sha512-pJw051uomb3ZeCzGTpRb8RbEqB5Y4WWet8gl/GcTlU35BSx0PVdZ86/bqkQCyKKuraVQEK7r6kBHQXF+fBhkoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.59.2", + "@typescript-eslint/parser": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/utils": "8.59.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } }, "node_modules/uc.micro": { "version": "2.1.0", @@ -1975,12 +5982,154 @@ "node": ">= 0.8" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/urlpattern-polyfill": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.1.0.tgz", "integrity": "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==", "license": "MIT" }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "dev": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/usehooks-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-3.1.1.tgz", + "integrity": "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash.debounce": "^4.0.8" + }, + "engines": { + "node": ">=16.15.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/vite": { + "version": "8.0.10", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz", + "integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.10", + "rolldown": "1.0.0-rc.17", + "tinyglobby": "^0.2.16" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ws": { "version": "8.20.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", @@ -2001,6 +6150,19 @@ "optional": true } } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 3205e2cc4..9c70328ea 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,16 @@ "main": "eleventy.config.js", "scripts": { "start": "npx @11ty/eleventy --serve", - "build": "npx @11ty/eleventy", + "site:build": "npx @11ty/eleventy", + "build": "npm run site:build && npm run demos:build", + "demos:lint": "eslint \"vite.demos.config.mjs\" \"scripts/demos/**/*.mjs\" \"src/compiledDemos/**/*.ts\"", + "demos:format": "prettier --check \"azure-pipelines.compiled-demos.yml\" \"docs/compiled-demos.md\" \"eslint.config.mjs\" \"package.json\" \"scripts/demos/**/*.mjs\" \"src/compiledDemos/**/*.{html,json,ts}\" \"tsconfig.demos.json\" \"vite.demos.config.mjs\"", + "demos:format:write": "prettier --write \"azure-pipelines.compiled-demos.yml\" \"docs/compiled-demos.md\" \"eslint.config.mjs\" \"package.json\" \"scripts/demos/**/*.mjs\" \"src/compiledDemos/**/*.{html,json,ts}\" \"tsconfig.demos.json\" \"vite.demos.config.mjs\"", + "demos:build": "vite build --config vite.demos.config.mjs --logLevel silent && node scripts/demos/generate-source-pages.mjs", + "demos:serve": "npm run build && vite preview --host 127.0.0.1 --port 4173 --outDir build", + "demos:typecheck": "tsc -p tsconfig.demos.json --noEmit", + "demos:check": "node scripts/demos/check-render.mjs", + "demos:ci": "npm run demos:lint && npm run demos:format && npm run demos:typecheck && npm run demos:check", "process-images": "node .github/scripts/process-images.js", "update-image": "node .github/scripts/update-image.js", "test": "echo \"Error: no test specified\" && exit 1" @@ -15,5 +24,20 @@ "dependencies": { "@11ty/eleventy": "^3.1.5", "sharp": "^0.33.5" + }, + "devDependencies": { + "@babylonjs/core": "^9.5.2", + "@babylonjs/gui": "^9.5.2", + "@babylonjs/inspector": "^9.5.2", + "@babylonjs/loaders": "^9.5.2", + "@babylonjs/materials": "^9.5.2", + "@eslint/js": "^10.0.1", + "eslint": "^10.3.0", + "globals": "^17.6.0", + "playwright": "^1.59.1", + "prettier": "^3.8.3", + "typescript": "^6.0.3", + "typescript-eslint": "^8.59.2", + "vite": "^8.0.10" } } diff --git a/scripts/demos/check-render.mjs b/scripts/demos/check-render.mjs new file mode 100644 index 000000000..1cc07ed34 --- /dev/null +++ b/scripts/demos/check-render.mjs @@ -0,0 +1,239 @@ +import fs from "node:fs/promises"; +import http from "node:http"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { chromium } from "playwright"; +import sharp from "sharp"; + +const repoRoot = path.dirname(path.dirname(path.dirname(fileURLToPath(import.meta.url)))); +const buildRoot = path.join(repoRoot, "build"); +const manifestPath = path.join(repoRoot, "src/compiledDemos/manifest.json"); +const manifest = JSON.parse(await fs.readFile(manifestPath, "utf8")); +const selectedDemos = new Set( + (process.env.DEMOS || "") + .split(",") + .map((value) => value.trim()) + .filter(Boolean) +); + +const contentTypes = new Map([ + [".html", "text/html; charset=utf-8"], + [".js", "text/javascript; charset=utf-8"], + [".css", "text/css; charset=utf-8"], + [".json", "application/json; charset=utf-8"], + [".png", "image/png"], + [".jpg", "image/jpeg"], + [".jpeg", "image/jpeg"], + [".webp", "image/webp"], + [".wasm", "application/wasm"], +]); + +const demos = manifest.demos.filter( + (demo) => !demo.renderCheck?.disabled && (selectedDemos.size === 0 || selectedDemos.has(demo.slug)) +); + +if (demos.length === 0) { + console.log("No compiled demos selected for render checks."); + process.exit(0); +} + +const server = http.createServer(async (request, response) => { + try { + const requestUrl = new URL(request.url || "/", "http://127.0.0.1"); + let relativePath = decodeURIComponent(requestUrl.pathname).replace(/^\/+/, ""); + if (!relativePath || relativePath.endsWith("/")) { + relativePath = path.join(relativePath, "index.html"); + } + + const filePath = path.normalize(path.join(buildRoot, relativePath)); + if (!filePath.startsWith(buildRoot)) { + response.writeHead(403); + response.end("Forbidden"); + return; + } + + const body = await fs.readFile(filePath); + response.writeHead(200, { + "content-type": contentTypes.get(path.extname(filePath).toLowerCase()) || "application/octet-stream", + }); + response.end(body); + } catch { + response.writeHead(404); + response.end("Not found"); + } +}); + +await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); +const address = server.address(); +const baseUrl = `http://127.0.0.1:${address.port}`; + +const browser = await chromium.launch({ + args: ["--ignore-gpu-blocklist", "--use-gl=swiftshader"], +}); + +const failures = []; + +async function sampleCanvas(canvas, timeoutMs) { + const screenshot = await canvas.screenshot({ timeout: timeoutMs }); + const image = sharp(screenshot).ensureAlpha(); + const metadata = await image.metadata(); + const pixels = await image.raw().toBuffer(); + let coloredSamples = 0; + let transparentSamples = 0; + const step = 12; + + for (let y = 0; y < metadata.height; y += step) { + for (let x = 0; x < metadata.width; x += step) { + const index = (y * metadata.width + x) * 4; + const red = pixels[index]; + const green = pixels[index + 1]; + const blue = pixels[index + 2]; + const alpha = pixels[index + 3]; + + if (alpha === 0) { + transparentSamples++; + } + + if (alpha > 0 && (red > 8 || green > 8 || blue > 8)) { + coloredSamples++; + } + } + } + + return { + stats: { + exists: true, + readable: true, + width: metadata.width, + height: metadata.height, + coloredSamples, + transparentSamples, + }, + pixels, + }; +} + +function countChangedSamples(before, after, width, height) { + let changedSamples = 0; + const step = 12; + const threshold = 18; + + for (let y = 0; y < height; y += step) { + for (let x = 0; x < width; x += step) { + const index = (y * width + x) * 4; + const delta = + Math.abs(before[index] - after[index]) + + Math.abs(before[index + 1] - after[index + 1]) + + Math.abs(before[index + 2] - after[index + 2]) + + Math.abs(before[index + 3] - after[index + 3]); + + if (delta > threshold) { + changedSamples++; + } + } + } + + return changedSamples; +} + +async function waitForAnimationFramePair(page) { + await page.evaluate( + () => + new Promise((resolve) => { + requestAnimationFrame(() => requestAnimationFrame(resolve)); + }) + ); +} + +try { + for (const demo of demos) { + const page = await browser.newPage({ viewport: { width: 960, height: 540 } }); + const consoleErrors = []; + const pageErrors = []; + page.on("console", (message) => { + if (message.type() === "error") { + consoleErrors.push(message.text()); + } + }); + page.on("pageerror", (error) => pageErrors.push(error.message)); + + const timeoutMs = demo.renderCheck?.timeoutMs || 15000; + const url = `${baseUrl}/Demos/${encodeURIComponent(demo.slug)}/`; + + try { + await page.goto(url, { waitUntil: "domcontentloaded", timeout: timeoutMs }); + await page.waitForFunction(() => Boolean(window.__babylonDemoReady), null, { timeout: timeoutMs }); + await page.evaluate(() => window.__babylonDemoReady); + await waitForAnimationFramePair(page); + + const canvas = page.locator("canvas").first(); + const canvasCount = await canvas.count(); + let canvasStats = { exists: false }; + let canvasPixels = null; + + if (canvasCount > 0) { + const sample = await sampleCanvas(canvas, timeoutMs); + canvasStats = sample.stats; + canvasPixels = sample.pixels; + } + + const minimumColoredSamples = demo.renderCheck?.minimumColoredSamples || 50; + if (!canvasStats.exists || !canvasStats.readable || canvasStats.coloredSamples < minimumColoredSamples) { + failures.push( + `${demo.slug}: canvas check failed (${JSON.stringify(canvasStats)}, expected at least ${minimumColoredSamples} colored samples)` + ); + } + + const interaction = demo.renderCheck?.interaction; + if (interaction?.type === "clickCanvas" && canvasStats.readable) { + const box = await canvas.boundingBox(); + if (!box) { + failures.push(`${demo.slug}: interaction check failed (canvas has no bounding box)`); + } else { + await canvas.click({ + position: { + x: box.width * (interaction.x ?? 0.5), + y: box.height * (interaction.y ?? 0.5), + }, + timeout: timeoutMs, + }); + await page.waitForTimeout(interaction.waitMs || 750); + await waitForAnimationFramePair(page); + + const afterInteraction = await sampleCanvas(canvas, timeoutMs); + const changedSamples = countChangedSamples( + canvasPixels, + afterInteraction.pixels, + canvasStats.width, + canvasStats.height + ); + const minimumChangedSamples = interaction.minimumChangedSamples || 25; + + if (changedSamples < minimumChangedSamples) { + failures.push( + `${demo.slug}: interaction check failed (${changedSamples} changed samples, expected at least ${minimumChangedSamples})` + ); + } + } + } + + if (consoleErrors.length > 0 || pageErrors.length > 0) { + failures.push(`${demo.slug}: browser errors: ${[...consoleErrors, ...pageErrors].join(" | ")}`); + } + } catch (error) { + failures.push(`${demo.slug}: ${error.message}`); + } finally { + await page.close(); + } + } +} finally { + await browser.close(); + server.close(); +} + +if (failures.length > 0) { + console.error(failures.join("\n")); + process.exit(1); +} + +console.log(`Checked ${demos.length} compiled demo${demos.length === 1 ? "" : "s"}.`); diff --git a/scripts/demos/generate-source-pages.mjs b/scripts/demos/generate-source-pages.mjs new file mode 100644 index 000000000..00f24b757 --- /dev/null +++ b/scripts/demos/generate-source-pages.mjs @@ -0,0 +1,139 @@ +import fs from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const repoRoot = path.dirname(path.dirname(path.dirname(fileURLToPath(import.meta.url)))); +const sourceRoot = path.join(repoRoot, "src/compiledDemos"); +const buildRoot = path.join(repoRoot, "build/Demos"); +const manifest = JSON.parse(await fs.readFile(path.join(sourceRoot, "manifest.json"), "utf8")); + +const escapeHtml = (value) => + value + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """) + .replaceAll("'", "'"); + +const renderSourcePage = (demo, files) => ` + + + + + ${escapeHtml(demo.title)} source + + + +
+

${escapeHtml(demo.title)} source

+ Back to demo +
+ ${files + .map( + (file) => `
+

${escapeHtml(file.path)}

+
${escapeHtml(file.contents)}
+
` + ) + .join("\n")} + + +`; + +for (const demo of manifest.demos) { + const sourceFiles = demo.sourceFiles || ["main.ts", "scene.ts"]; + const files = []; + + for (const sourceFile of sourceFiles) { + const filePath = path.normalize(path.join(sourceRoot, demo.slug, sourceFile)); + const demoRoot = path.join(sourceRoot, demo.slug); + + if (!filePath.startsWith(demoRoot)) { + throw new Error(`Source file ${sourceFile} escapes ${demo.slug}.`); + } + + files.push({ + path: sourceFile, + contents: await fs.readFile(filePath, "utf8"), + }); + } + + const sourceOutDir = path.join(buildRoot, demo.slug, "source"); + await fs.mkdir(sourceOutDir, { recursive: true }); + await fs.writeFile(path.join(sourceOutDir, "index.html"), renderSourcePage(demo, files)); +} + +console.log( + `Generated source pages for ${manifest.demos.length} compiled demo${manifest.demos.length === 1 ? "" : "s"}.` +); diff --git a/src/compiledDemos/Boom/index.html b/src/compiledDemos/Boom/index.html new file mode 100644 index 000000000..911ee325b --- /dev/null +++ b/src/compiledDemos/Boom/index.html @@ -0,0 +1,105 @@ + + + + + + Babylon.js - Boom demo + + + + +
+ + +
+ Source +
+
+ + + + diff --git a/src/compiledDemos/Boom/main.ts b/src/compiledDemos/Boom/main.ts new file mode 100644 index 000000000..d93bfd623 --- /dev/null +++ b/src/compiledDemos/Boom/main.ts @@ -0,0 +1,6 @@ +import { runDemo } from "../shared/demoRunner"; +import { createBoomScene } from "./scene"; + +runDemo({ + createScene: createBoomScene, +}); diff --git a/src/compiledDemos/Boom/scene.ts b/src/compiledDemos/Boom/scene.ts new file mode 100644 index 000000000..ff229c507 --- /dev/null +++ b/src/compiledDemos/Boom/scene.ts @@ -0,0 +1,258 @@ +import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera"; +import { DirectionalLight } from "@babylonjs/core/Lights/directionalLight"; +import { HemisphericLight } from "@babylonjs/core/Lights/hemisphericLight"; +import { PointLight } from "@babylonjs/core/Lights/pointLight"; +import { ShadowGenerator } from "@babylonjs/core/Lights/Shadows/shadowGenerator"; +import { Color3 } from "@babylonjs/core/Maths/math.color"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial"; +import { DynamicTexture } from "@babylonjs/core/Materials/Textures/dynamicTexture"; +import { Texture } from "@babylonjs/core/Materials/Textures/texture"; +import { CreateBox } from "@babylonjs/core/Meshes/Builders/boxBuilder"; +import { CreateDisc } from "@babylonjs/core/Meshes/Builders/discBuilder"; +import { CreateGroundFromHeightMap } from "@babylonjs/core/Meshes/Builders/groundBuilder"; +import { SolidParticleSystem } from "@babylonjs/core/Particles/solidParticleSystem"; +import { Engine } from "@babylonjs/core/Engines/engine"; +import { Scene } from "@babylonjs/core/scene"; +import "@babylonjs/core/Culling/ray"; +import "@babylonjs/core/Lights/Shadows/shadowGeneratorSceneComponent"; + +export function createBoomScene(engine: Engine, canvas: HTMLCanvasElement): Scene { + const size = 10; + const widthCount = 30; + const heightCount = 20; + const gravity = -0.07; + const restitution = 0.9; + const friction = 0.995; + const radius = (size * heightCount) / 12; + const speed = radius * 1.2; + + const subdivisions = 50; + const width = 1000; + const height = 1000; + const groundHeight = width / 6; + + const scene = new Scene(engine); + scene.clearColor = new Color3(0.4, 0.6, 0.8).toColor4(1); + + const camera = new ArcRotateCamera("camera1", 0, 0, 0, Vector3.Zero(), scene); + camera.setPosition(new Vector3(0, 0, -800)); + camera.attachControl(canvas, true); + + const hemisphericLight = new HemisphericLight("light1", new Vector3(0, 1, 0), scene); + hemisphericLight.groundColor = new Color3(0.5, 0.5, 0.5); + hemisphericLight.intensity = 0.2; + + const lightDirection = new Vector3(0, -1, 1); + const directionalLight = new DirectionalLight("dl", lightDirection, scene); + directionalLight.position = new Vector3(0, 200, -1000); + directionalLight.diffuse = Color3.White(); + directionalLight.intensity = 0.8; + directionalLight.specular = new Color3(0.5, 0.5, 0.2); + + const pointLight = new PointLight("pl", Vector3.Zero(), scene); + pointLight.diffuse = Color3.White(); + pointLight.specular = Color3.Black(); + pointLight.intensity = 0.4; + + const texture = new DynamicTexture("dt", { width: 500, height: 65 }, scene); + texture.hasAlpha = true; + texture.drawText("BabylonJS Roxxx", null, 45, "bold 56px Arial", "blue", "red", true, false); + texture.drawText("CLICK = BOOM", null, 60, "bold 20px Arial", "yellow", null, true, true); + + const particleMaterial = new StandardMaterial("mat1", scene); + particleMaterial.diffuseTexture = texture; + particleMaterial.freeze(); + + const ground = CreateGroundFromHeightMap( + "ground", + "/Scenes/Customs/heightMap.png", + { + width, + height, + subdivisions, + minHeight: 0, + maxHeight: groundHeight, + onReady: (mesh) => { + mesh.getHeightAtCoordinates(mesh.position.x, mesh.position.z); + }, + }, + scene + ); + + const groundMaterial = new StandardMaterial("ground", scene); + const groundTexture = new Texture("/Scenes/Customs/Ground.jpg", scene); + groundTexture.uScale = 6; + groundTexture.vScale = 6; + groundMaterial.diffuseTexture = groundTexture; + groundMaterial.specularColor = Color3.Black(); + ground.material = groundMaterial; + ground.isPickable = false; + ground.position.y = -heightCount * size; + ground.position.z = heightCount * size; + groundMaterial.freeze(); + ground.freezeWorldMatrix(); + + const disc = CreateDisc("d", { radius: width * 4 }, scene); + disc.position.copyFrom(ground.position); + disc.position.y -= 0.1; + disc.rotation.x = Math.PI / 2; + + const discMaterial = new StandardMaterial("groundDisc", scene); + const discTexture = new Texture("/Scenes/Customs/Ground.jpg", scene); + discTexture.uScale = 50; + discTexture.vScale = 50; + discMaterial.diffuseTexture = discTexture; + discMaterial.specularColor = Color3.Black(); + discMaterial.zOffset = 1; + discMaterial.freeze(); + disc.material = discMaterial; + disc.isPickable = false; + disc.freezeWorldMatrix(); + + const model = CreateBox("m", { size }, scene); + const solidParticles = new SolidParticleSystem("sps", scene, { isPickable: true }); + solidParticles.addShape(model, widthCount * heightCount); + model.dispose(); + + const particleMesh = solidParticles.buildMesh(); + particleMesh.material = particleMaterial; + particleMesh.freezeWorldMatrix(); + + const particleVars = solidParticles.vars as Record; + particleVars.target = Vector3.Zero(); + particleVars.temp = Vector3.Zero(); + particleVars.totalWidth = size * widthCount; + particleVars.totalHeight = size * heightCount; + particleVars.shiftX = -particleVars.totalWidth / 2; + particleVars.shiftY = -particleVars.totalHeight / 2; + particleVars.radius = radius; + particleVars.minY = 0; + particleVars.normal = Vector3.Zero(); + particleVars.symmetry = 0; + particleVars.loss = 0; + particleVars.justClicked = false; + + const shadowGenerator = new ShadowGenerator(1024, directionalLight); + shadowGenerator.getShadowMap()?.renderList?.push(particleMesh); + shadowGenerator.setDarkness(0.2); + shadowGenerator.usePoissonSampling = true; + ground.receiveShadows = true; + disc.receiveShadows = true; + + solidParticles.initParticles = () => { + let particleIndex = 0; + for (let heightIndex = 0; heightIndex < heightCount; heightIndex++) { + for (let widthIndex = 0; widthIndex < widthCount; widthIndex++) { + const particle = solidParticles.particles[particleIndex] as any; + particle.position.x = widthIndex * size + particleVars.shiftX; + particle.position.y = heightIndex * size + particleVars.shiftY; + particle.position.z = 0; + particle.uvs.x = (widthIndex * size) / particleVars.totalWidth; + particle.uvs.y = (heightIndex * size) / particleVars.totalHeight; + particle.uvs.z = ((widthIndex + 1) * size) / particleVars.totalWidth; + particle.uvs.w = ((heightIndex + 1) * size) / particleVars.totalHeight; + particle.randomFactor = 1 / (1 + Math.random()) / 10; + particleIndex++; + } + } + }; + + let exploded = false; + + solidParticles.updateParticle = (particle) => { + const activeParticle = particle as any; + + if (particleVars.justClicked) { + activeParticle.position.subtractToRef(particleVars.target, particleVars.temp); + const distance = particleVars.temp.length(); + const scale = distance < 0.001 ? 1 : particleVars.radius / distance; + particleVars.temp.normalize(); + activeParticle.velocity.x += particleVars.temp.x * scale * speed * (1 + Math.random() * 0.3); + activeParticle.velocity.y += particleVars.temp.y * scale * speed * (1 + Math.random() * 0.3); + activeParticle.velocity.z += particleVars.temp.z * scale * speed * (1 + Math.random() * 0.3); + if (activeParticle.idx === solidParticles.nbParticles - 1) { + particleVars.justClicked = false; + } + } + + if (exploded && !particleVars.justClicked) { + particleVars.minY = + ground.getHeightAtCoordinates(activeParticle.position.x, activeParticle.position.z) + size; + particleVars.loss = -restitution * activeParticle.randomFactor * 10; + + if (activeParticle.position.y < particleVars.minY) { + ground.getNormalAtCoordinatesToRef( + activeParticle.position.x, + activeParticle.position.z, + particleVars.normal + ); + particleVars.symmetry = + (2 * + (particleVars.normal.x * activeParticle.velocity.x + + particleVars.normal.y * activeParticle.velocity.y + + particleVars.normal.z * activeParticle.velocity.z)) / + particleVars.normal.lengthSquared(); + + activeParticle.velocity.x = particleVars.symmetry * particleVars.normal.x - activeParticle.velocity.x; + activeParticle.velocity.z = particleVars.symmetry * particleVars.normal.z - activeParticle.velocity.z; + activeParticle.velocity.y = particleVars.symmetry * particleVars.normal.y - activeParticle.velocity.y; + activeParticle.velocity.x *= particleVars.loss; + activeParticle.velocity.y *= particleVars.loss; + activeParticle.velocity.z *= particleVars.loss; + } + + activeParticle.velocity.y += gravity; + activeParticle.position.x += activeParticle.velocity.x; + activeParticle.position.y += activeParticle.velocity.y; + activeParticle.position.z += activeParticle.velocity.z; + activeParticle.rotation.x += activeParticle.velocity.z * activeParticle.randomFactor; + activeParticle.rotation.y += activeParticle.velocity.x * activeParticle.randomFactor; + activeParticle.rotation.z += activeParticle.velocity.y * activeParticle.randomFactor; + + if (activeParticle.position.y < particleVars.minY && Math.abs(activeParticle.velocity.y) < 0.1 - gravity) { + activeParticle.velocity.x *= friction; + activeParticle.velocity.z *= friction; + activeParticle.position.y = particleVars.minY; + activeParticle.velocity.y = 0; + } + } + return particle; + }; + + solidParticles.afterUpdateParticles = function () { + this.refreshVisibleSize(); + }; + + solidParticles.initParticles(); + solidParticles.setParticles(); + solidParticles.computeParticleColor = false; + solidParticles.computeParticleTexture = false; + + scene.onPointerDown = (_event, pickResult) => { + const faceId = pickResult.faceId; + if (faceId === -1 || faceId === undefined) { + return; + } + + const pickedParticle = solidParticles.pickedParticles[faceId]; + if (!pickedParticle) { + return; + } + + const particle = solidParticles.particles[pickedParticle.idx] as any; + exploded = true; + camera.position.subtractToRef(particle.position, particleVars.target); + particleVars.target.normalize(); + particleVars.target.scaleInPlace(radius); + particleVars.target.addInPlace(particle.position); + particleVars.justClicked = true; + }; + + scene.registerBeforeRender(() => { + solidParticles.setParticles(); + pointLight.position.copyFrom(camera.position); + }); + + return scene; +} diff --git a/src/compiledDemos/manifest.json b/src/compiledDemos/manifest.json new file mode 100644 index 000000000..85838b9de --- /dev/null +++ b/src/compiledDemos/manifest.json @@ -0,0 +1,21 @@ +{ + "demos": [ + { + "slug": "Boom", + "title": "Babylon.js - Boom demo", + "legacyPath": "static/Demos/Boom", + "sourceFiles": ["main.ts", "scene.ts"], + "renderCheck": { + "timeoutMs": 20000, + "minimumColoredSamples": 200, + "interaction": { + "type": "clickCanvas", + "x": 0.5, + "y": 0.5, + "waitMs": 1000, + "minimumChangedSamples": 400 + } + } + } + ] +} diff --git a/src/compiledDemos/shared/demoRunner.ts b/src/compiledDemos/shared/demoRunner.ts new file mode 100644 index 000000000..267dce6dd --- /dev/null +++ b/src/compiledDemos/shared/demoRunner.ts @@ -0,0 +1,106 @@ +import { Engine } from "@babylonjs/core/Engines/engine"; +import type { Scene } from "@babylonjs/core/scene"; +import "@babylonjs/core/Engines/Extensions/engine.dynamicTexture"; +import "@babylonjs/core/Engines/Extensions/engine.readTexture"; +import "@babylonjs/core/Materials/standardMaterial"; +import "@babylonjs/core/Rendering/depthRendererSceneComponent"; +import "@babylonjs/core/Rendering/outlineRenderer"; + +export interface DemoDefinition { + createScene: (engine: Engine, canvas: HTMLCanvasElement) => Scene | Promise; + onReady?: (scene: Scene, engine: Engine) => void | Promise; +} + +declare global { + interface Window { + __babylonDemoReady?: Promise; + __babylonDemo?: { + engine: Engine; + scene?: Scene; + }; + } +} + +export function runDemo(definition: DemoDefinition): void { + const canvas = document.getElementById("renderCanvas") as HTMLCanvasElement | null; + const fps = document.getElementById("fps"); + const status = document.getElementById("status"); + const debugButton = document.getElementById("enableDebug"); + const fullscreenButton = document.getElementById("fullscreen"); + const notSupported = document.getElementById("notSupported"); + + if (!canvas) { + throw new Error("Compiled demo requires a canvas with id renderCanvas."); + } + + if (!Engine.isSupported()) { + notSupported?.classList.remove("hidden"); + return; + } + + const engine = new Engine(canvas, true, { + preserveDrawingBuffer: true, + stencil: true, + }); + + let scene: Scene | undefined; + window.__babylonDemo = { engine }; + + const ready = Promise.resolve(definition.createScene(engine, canvas)).then( + (createdScene) => + new Promise((resolve) => { + scene = createdScene; + window.__babylonDemo = { engine, scene }; + + if (status) { + status.textContent = "Loading..."; + } + + scene.executeWhenReady(async () => { + if (scene?.activeCamera) { + scene.activeCamera.attachControl(canvas, true); + } + + await definition.onReady?.(scene, engine); + + canvas.style.opacity = "1"; + if (status) { + status.textContent = ""; + } + resolve(); + }); + }) + ); + + window.__babylonDemoReady = ready; + + engine.runRenderLoop(() => { + if (fps) { + fps.textContent = `${engine.getFps().toFixed()} fps`; + } + + scene?.render(); + }); + + window.addEventListener("resize", () => { + engine.resize(); + }); + + fullscreenButton?.addEventListener("click", () => { + engine.switchFullscreen(true); + }); + + debugButton?.addEventListener("click", async () => { + if (!scene) { + return; + } + + await import("@babylonjs/inspector"); + + if (scene.debugLayer.isVisible()) { + scene.debugLayer.hide(); + } else { + scene.debugLayer.show(); + } + }); +} diff --git a/tsconfig.demos.json b/tsconfig.demos.json new file mode 100644 index 000000000..d0336672d --- /dev/null +++ b/tsconfig.demos.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": false, + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "types": [], + "allowJs": false, + "checkJs": false, + "strict": false, + "isolatedModules": true, + "noEmit": true, + "skipLibCheck": true + }, + "include": ["src/compiledDemos/**/*.ts"] +} diff --git a/vite.demos.config.mjs b/vite.demos.config.mjs new file mode 100644 index 000000000..939ac93b4 --- /dev/null +++ b/vite.demos.config.mjs @@ -0,0 +1,32 @@ +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { defineConfig } from "vite"; + +const repoRoot = path.dirname(fileURLToPath(import.meta.url)); +const demosRoot = path.join(repoRoot, "src/compiledDemos"); +const manifestPath = path.join(demosRoot, "manifest.json"); +const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8")); + +const input = Object.fromEntries( + manifest.demos.map((demo) => [`${demo.slug}/index`, path.join(demosRoot, demo.slug, "index.html")]) +); + +export default defineConfig({ + root: demosRoot, + base: "/Demos/", + publicDir: false, + build: { + outDir: path.join(repoRoot, "build/Demos"), + emptyOutDir: false, + sourcemap: true, + rollupOptions: { + input, + output: { + entryFileNames: "_compiled/[name]-[hash].js", + chunkFileNames: "_compiled/[name]-[hash].js", + assetFileNames: "_compiled/[name]-[hash][extname]", + }, + }, + }, +}); From db1a50b35fba2c213d4e397a007ed6164860ba42 Mon Sep 17 00:00:00 2001 From: Raanan Weber Date: Wed, 6 May 2026 16:35:25 +0200 Subject: [PATCH 02/12] Port Lines demo to compiled demos --- docs/compiled-demos.md | 4 ++ src/compiledDemos/Lines/index.html | 105 +++++++++++++++++++++++++++++ src/compiledDemos/Lines/main.ts | 6 ++ src/compiledDemos/Lines/scene.ts | 50 ++++++++++++++ src/compiledDemos/manifest.json | 10 +++ 5 files changed, 175 insertions(+) create mode 100644 src/compiledDemos/Lines/index.html create mode 100644 src/compiledDemos/Lines/main.ts create mode 100644 src/compiledDemos/Lines/scene.ts diff --git a/docs/compiled-demos.md b/docs/compiled-demos.md index ac306e672..971364258 100644 --- a/docs/compiled-demos.md +++ b/docs/compiled-demos.md @@ -32,6 +32,10 @@ src/compiledDemos/ index.html main.ts scene.ts + Lines/ + index.html + main.ts + scene.ts ``` Each demo folder owns its HTML shell and TypeScript entry. Shared browser bootstrapping lives in `shared/demoRunner.ts`, which creates the engine, starts the render loop, wires common controls, and exposes `window.__babylonDemoReady` for CI. diff --git a/src/compiledDemos/Lines/index.html b/src/compiledDemos/Lines/index.html new file mode 100644 index 000000000..0c7e0366c --- /dev/null +++ b/src/compiledDemos/Lines/index.html @@ -0,0 +1,105 @@ + + + + + + Babylon.js - Lines demo + + + + +
+ + +
+ Source +
+
+ + + + diff --git a/src/compiledDemos/Lines/main.ts b/src/compiledDemos/Lines/main.ts new file mode 100644 index 000000000..0afda6640 --- /dev/null +++ b/src/compiledDemos/Lines/main.ts @@ -0,0 +1,6 @@ +import { runDemo } from "../shared/demoRunner"; +import { createLinesScene } from "./scene"; + +runDemo({ + createScene: createLinesScene, +}); diff --git a/src/compiledDemos/Lines/scene.ts b/src/compiledDemos/Lines/scene.ts new file mode 100644 index 000000000..cfce88e4d --- /dev/null +++ b/src/compiledDemos/Lines/scene.ts @@ -0,0 +1,50 @@ +import { VertexBuffer } from "@babylonjs/core/Buffers/buffer"; +import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera"; +import { Engine } from "@babylonjs/core/Engines/engine"; +import { Color3 } from "@babylonjs/core/Maths/math.color"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { CreateLines } from "@babylonjs/core/Meshes/Builders/linesBuilder"; +import { Scene } from "@babylonjs/core/scene"; + +export function createLinesScene(engine: Engine, canvas: HTMLCanvasElement): Scene { + const scene = new Scene(engine); + const camera = new ArcRotateCamera("Camera", 0, 0, 10, Vector3.Zero(), scene); + camera.setPosition(new Vector3(20, 200, 400)); + camera.maxZ = 20000; + camera.lowerRadiusLimit = 150; + camera.attachControl(canvas, true); + + scene.clearColor = Color3.Black().toColor4(1); + + const points: Vector3[] = []; + let radius = 0.5; + let angle = 0; + + for (let index = 0; index < 1000; index++) { + points.push(new Vector3(radius * Math.cos(angle), 0, radius * Math.sin(angle))); + radius += 0.3; + angle += 0.1; + } + + const whirlpool = CreateLines("whirlpool", { points, updatable: true }, scene); + whirlpool.color = Color3.White(); + + const positionData = whirlpool.getVerticesData(VertexBuffer.PositionKind); + if (!positionData) { + throw new Error("Lines demo could not read whirlpool vertex positions."); + } + + const heightRange = 10; + let alpha = 0; + + scene.registerBeforeRender(() => { + for (let index = 0; index < 1000; index++) { + positionData[index * 3 + 1] = heightRange * Math.sin(alpha + index * 0.1); + } + + whirlpool.updateVerticesData(VertexBuffer.PositionKind, positionData); + alpha += 0.05 * scene.getAnimationRatio(); + }); + + return scene; +} diff --git a/src/compiledDemos/manifest.json b/src/compiledDemos/manifest.json index 85838b9de..0cfa49822 100644 --- a/src/compiledDemos/manifest.json +++ b/src/compiledDemos/manifest.json @@ -16,6 +16,16 @@ "minimumChangedSamples": 400 } } + }, + { + "slug": "Lines", + "title": "Babylon.js - Lines demo", + "legacyPath": "static/Demos/Lines", + "sourceFiles": ["main.ts", "scene.ts"], + "renderCheck": { + "timeoutMs": 15000, + "minimumColoredSamples": 10 + } } ] } From 508b8459682d8d5936bfe7fad4b891d11e393a3d Mon Sep 17 00:00:00 2001 From: Raanan Weber Date: Wed, 6 May 2026 16:37:02 +0200 Subject: [PATCH 03/12] Port Heightmap demo to compiled demos --- docs/compiled-demos.md | 4 + src/compiledDemos/Heightmap/index.html | 105 +++++++++++++++++++++++++ src/compiledDemos/Heightmap/main.ts | 6 ++ src/compiledDemos/Heightmap/scene.ts | 72 +++++++++++++++++ src/compiledDemos/manifest.json | 10 +++ 5 files changed, 197 insertions(+) create mode 100644 src/compiledDemos/Heightmap/index.html create mode 100644 src/compiledDemos/Heightmap/main.ts create mode 100644 src/compiledDemos/Heightmap/scene.ts diff --git a/docs/compiled-demos.md b/docs/compiled-demos.md index 971364258..019a26f33 100644 --- a/docs/compiled-demos.md +++ b/docs/compiled-demos.md @@ -36,6 +36,10 @@ src/compiledDemos/ index.html main.ts scene.ts + Heightmap/ + index.html + main.ts + scene.ts ``` Each demo folder owns its HTML shell and TypeScript entry. Shared browser bootstrapping lives in `shared/demoRunner.ts`, which creates the engine, starts the render loop, wires common controls, and exposes `window.__babylonDemoReady` for CI. diff --git a/src/compiledDemos/Heightmap/index.html b/src/compiledDemos/Heightmap/index.html new file mode 100644 index 000000000..d00e71963 --- /dev/null +++ b/src/compiledDemos/Heightmap/index.html @@ -0,0 +1,105 @@ + + + + + + Babylon.js - Heightmap demo + + + + +
+ + +
+ Source +
+
+ + + + diff --git a/src/compiledDemos/Heightmap/main.ts b/src/compiledDemos/Heightmap/main.ts new file mode 100644 index 000000000..128386a5f --- /dev/null +++ b/src/compiledDemos/Heightmap/main.ts @@ -0,0 +1,6 @@ +import { runDemo } from "../shared/demoRunner"; +import { createHeightmapScene } from "./scene"; + +runDemo({ + createScene: createHeightmapScene, +}); diff --git a/src/compiledDemos/Heightmap/scene.ts b/src/compiledDemos/Heightmap/scene.ts new file mode 100644 index 000000000..e13e614f6 --- /dev/null +++ b/src/compiledDemos/Heightmap/scene.ts @@ -0,0 +1,72 @@ +import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera"; +import { Engine } from "@babylonjs/core/Engines/engine"; +import { PointLight } from "@babylonjs/core/Lights/pointLight"; +import { Color3 } from "@babylonjs/core/Maths/math.color"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial"; +import { CubeTexture } from "@babylonjs/core/Materials/Textures/cubeTexture"; +import { Texture } from "@babylonjs/core/Materials/Textures/texture"; +import { CreateBox } from "@babylonjs/core/Meshes/Builders/boxBuilder"; +import { CreateGroundFromHeightMap } from "@babylonjs/core/Meshes/Builders/groundBuilder"; +import { Scene } from "@babylonjs/core/scene"; + +export function createHeightmapScene(engine: Engine, canvas: HTMLCanvasElement): Scene { + const scene = new Scene(engine); + const camera = new ArcRotateCamera("Camera", 0, 0, 10, Vector3.Zero(), scene); + new PointLight("Omni0", new Vector3(60, 100, 10), scene); + + camera.setPosition(new Vector3(-20, 20, 0)); + camera.attachControl(canvas, true); + + const skybox = CreateBox("skyBox", { size: 100 }, scene); + const skyboxMaterial = new StandardMaterial("skyBox", scene); + const skyboxTexture = new CubeTexture("/Scenes/Customs/skybox/skybox", scene); + skyboxTexture.coordinatesMode = Texture.SKYBOX_MODE; + skyboxMaterial.backFaceCulling = false; + skyboxMaterial.reflectionTexture = skyboxTexture; + skyboxMaterial.diffuseColor = Color3.Black(); + skyboxMaterial.specularColor = Color3.Black(); + skyboxMaterial.disableLighting = true; + skybox.material = skyboxMaterial; + + const ground = CreateGroundFromHeightMap( + "ground", + "./heightMap.png", + { + width: 100, + height: 100, + subdivisions: 100, + minHeight: 0, + maxHeight: 10, + updatable: false, + }, + scene + ); + + const groundMaterial = new StandardMaterial("ground", scene); + const groundTexture = new Texture("./Ground.jpg", scene); + groundTexture.uScale = 6; + groundTexture.vScale = 6; + groundMaterial.diffuseTexture = groundTexture; + groundMaterial.specularColor = Color3.Black(); + ground.position.y = -2.05; + ground.material = groundMaterial; + + scene.registerBeforeRender(() => { + if (camera.beta < 0.1) { + camera.beta = 0.1; + } else if (camera.beta > (Math.PI / 2) * 0.9) { + camera.beta = (Math.PI / 2) * 0.9; + } + + if (camera.radius > 50) { + camera.radius = 50; + } + + if (camera.radius < 5) { + camera.radius = 5; + } + }); + + return scene; +} diff --git a/src/compiledDemos/manifest.json b/src/compiledDemos/manifest.json index 0cfa49822..c2b0cfe88 100644 --- a/src/compiledDemos/manifest.json +++ b/src/compiledDemos/manifest.json @@ -26,6 +26,16 @@ "timeoutMs": 15000, "minimumColoredSamples": 10 } + }, + { + "slug": "Heightmap", + "title": "Babylon.js - Heightmap demo", + "legacyPath": "static/Demos/Heightmap", + "sourceFiles": ["main.ts", "scene.ts"], + "renderCheck": { + "timeoutMs": 20000, + "minimumColoredSamples": 200 + } } ] } From de38f7af111baa1ab0ce35fbc25087a7a4efd963 Mon Sep 17 00:00:00 2001 From: Raanan Weber Date: Wed, 6 May 2026 16:52:22 +0200 Subject: [PATCH 04/12] Port Offscreen demo to compiled demos --- docs/compiled-demos.md | 7 + scripts/demos/check-render.mjs | 35 +++-- src/compiledDemos/Offscreen/index.html | 187 +++++++++++++++++++++++++ src/compiledDemos/Offscreen/main.ts | 137 ++++++++++++++++++ src/compiledDemos/Offscreen/scene.ts | 24 ++++ src/compiledDemos/Offscreen/worker.ts | 69 +++++++++ src/compiledDemos/manifest.json | 12 ++ 7 files changed, 463 insertions(+), 8 deletions(-) create mode 100644 src/compiledDemos/Offscreen/index.html create mode 100644 src/compiledDemos/Offscreen/main.ts create mode 100644 src/compiledDemos/Offscreen/scene.ts create mode 100644 src/compiledDemos/Offscreen/worker.ts diff --git a/docs/compiled-demos.md b/docs/compiled-demos.md index 019a26f33..4af32ddb7 100644 --- a/docs/compiled-demos.md +++ b/docs/compiled-demos.md @@ -40,6 +40,11 @@ src/compiledDemos/ index.html main.ts scene.ts + Offscreen/ + index.html + main.ts + scene.ts + worker.ts ``` Each demo folder owns its HTML shell and TypeScript entry. Shared browser bootstrapping lives in `shared/demoRunner.ts`, which creates the engine, starts the render loop, wires common controls, and exposes `window.__babylonDemoReady` for CI. @@ -82,6 +87,8 @@ Each demo page should include a small source link at the bottom of the viewport: The initial check is intentionally a health check. It proves the demo compiles and renders. Individual demos can add `renderCheck.interaction` entries for important first-screen behavior; Boom uses this to click the canvas and verify the rendered output changes after the sign explodes. Screenshot baselines can be added later once the migration has enough coverage to justify the extra maintenance. +Demos with more than one canvas can set `renderCheck.minimumCanvasCount` and `renderCheck.canvasSelector`. The Offscreen demo uses this to verify both the main-thread canvas and worker OffscreenCanvas render nonblank frames. + ## Linting And Formatting Compiled demo source is linted with ESLint and formatted with Prettier. The checks are intentionally scoped to the new compiled demo pipeline, not the legacy `static/` demo tree. diff --git a/scripts/demos/check-render.mjs b/scripts/demos/check-render.mjs index 1cc07ed34..40b68fb69 100644 --- a/scripts/demos/check-render.mjs +++ b/scripts/demos/check-render.mjs @@ -145,6 +145,19 @@ async function waitForAnimationFramePair(page) { ); } +async function checkCanvas(locator, demo, timeoutMs, label) { + const sample = await sampleCanvas(locator, timeoutMs); + const minimumColoredSamples = demo.renderCheck?.minimumColoredSamples || 50; + + if (sample.stats.coloredSamples < minimumColoredSamples) { + failures.push( + `${demo.slug}: ${label} canvas check failed (${JSON.stringify(sample.stats)}, expected at least ${minimumColoredSamples} colored samples)` + ); + } + + return sample; +} + try { for (const demo of demos) { const page = await browser.newPage({ viewport: { width: 960, height: 540 } }); @@ -166,22 +179,28 @@ try { await page.evaluate(() => window.__babylonDemoReady); await waitForAnimationFramePair(page); - const canvas = page.locator("canvas").first(); - const canvasCount = await canvas.count(); + const canvasSelector = demo.renderCheck?.canvasSelector || "canvas"; + const canvases = page.locator(canvasSelector); + const canvasCount = await canvases.count(); + const minimumCanvasCount = demo.renderCheck?.minimumCanvasCount || 1; + const canvas = canvases.first(); let canvasStats = { exists: false }; let canvasPixels = null; + if (canvasCount < minimumCanvasCount) { + failures.push( + `${demo.slug}: expected at least ${minimumCanvasCount} canvas element${minimumCanvasCount === 1 ? "" : "s"}, found ${canvasCount}` + ); + } + if (canvasCount > 0) { - const sample = await sampleCanvas(canvas, timeoutMs); + const sample = await checkCanvas(canvas, demo, timeoutMs, "primary"); canvasStats = sample.stats; canvasPixels = sample.pixels; } - const minimumColoredSamples = demo.renderCheck?.minimumColoredSamples || 50; - if (!canvasStats.exists || !canvasStats.readable || canvasStats.coloredSamples < minimumColoredSamples) { - failures.push( - `${demo.slug}: canvas check failed (${JSON.stringify(canvasStats)}, expected at least ${minimumColoredSamples} colored samples)` - ); + for (let canvasIndex = 1; canvasIndex < Math.min(canvasCount, minimumCanvasCount); canvasIndex++) { + await checkCanvas(canvases.nth(canvasIndex), demo, timeoutMs, `canvas ${canvasIndex + 1}`); } const interaction = demo.renderCheck?.interaction; diff --git a/src/compiledDemos/Offscreen/index.html b/src/compiledDemos/Offscreen/index.html new file mode 100644 index 000000000..77083b056 --- /dev/null +++ b/src/compiledDemos/Offscreen/index.html @@ -0,0 +1,187 @@ + + + + + + Babylon.js - Worker mode + + + +
+

Babylon.js Offscreen Canvas demo

+
+ This page demonstrates Babylon.js rendering in a worker thread. The left canvas renders on the main + thread. The right canvas renders with an OffscreenCanvas in a worker thread. The button simulates heavy + main-thread work to make the difference visible. +
+ Click + here + for more documentation. +
+ + + +
UI thread
+
Worker thread
+
Please wait loading...
+ +
+ Source + + + diff --git a/src/compiledDemos/Offscreen/main.ts b/src/compiledDemos/Offscreen/main.ts new file mode 100644 index 000000000..3db3dbc1d --- /dev/null +++ b/src/compiledDemos/Offscreen/main.ts @@ -0,0 +1,137 @@ +import { Engine } from "@babylonjs/core/Engines/engine"; +import type { Scene } from "@babylonjs/core/scene"; +import { createOffscreenScene } from "./scene"; + +declare global { + interface Window { + __babylonDemoReady?: Promise; + __babylonDemo?: { + engine: Engine; + scene?: Scene; + }; + } +} + +type WorkerMessage = + | { + type: "ready"; + } + | { + type: "error"; + message: string; + }; + +const mainCanvas = document.getElementById("renderCanvas") as HTMLCanvasElement | null; +const workerCanvas = document.getElementById("workerRenderCanvas") as HTMLCanvasElement | null; +const loading = document.getElementById("loading"); +const notSupported = document.getElementById("notSupported"); +const labelWorker = document.getElementById("labelWorker"); +const slowButton = document.getElementById("slowButton"); + +if (!mainCanvas || !workerCanvas) { + throw new Error("Offscreen demo requires renderCanvas and workerRenderCanvas elements."); +} + +function fitCanvasToDisplaySize(canvas: HTMLCanvasElement): void { + canvas.width = Math.max(1, Math.floor(canvas.clientWidth)); + canvas.height = Math.max(1, Math.floor(canvas.clientHeight)); +} + +function slowDownMainThread(): void { + let count = 0; + window.setInterval(() => { + for (let index = 0; index < 10_000_000; index++) { + count += Math.cos(Math.sin(Math.random())); + } + + if (count > Number.MAX_SAFE_INTEGER) { + count = 0; + } + }, 1); +} + +async function waitForSceneReady(scene: Scene): Promise { + await new Promise((resolve) => { + scene.executeWhenReady(resolve); + }); +} + +function startMainThreadDemo(): Promise { + const engine = new Engine(mainCanvas, true, { + preserveDrawingBuffer: true, + stencil: true, + }); + window.__babylonDemo = { engine }; + + return createOffscreenScene(engine, true).then(async (scene) => { + window.__babylonDemo = { engine, scene }; + await waitForSceneReady(scene); + mainCanvas.style.opacity = "1"; + + engine.runRenderLoop(() => { + engine.resize(); + if (scene.activeCamera) { + scene.render(); + } + }); + + window.addEventListener("resize", () => { + engine.resize(); + }); + }); +} + +function startWorkerDemo(): Promise { + if (!("OffscreenCanvas" in window) || typeof workerCanvas.transferControlToOffscreen !== "function") { + notSupported?.classList.remove("hidden"); + if (labelWorker) { + labelWorker.textContent = "OffscreenCanvas is not supported"; + } + return Promise.reject(new Error("OffscreenCanvas is not supported by this browser.")); + } + + fitCanvasToDisplaySize(workerCanvas); + const offscreenCanvas = workerCanvas.transferControlToOffscreen(); + const worker = new Worker(new URL("./worker.ts", import.meta.url), { type: "module" }); + + const ready = new Promise((resolve, reject) => { + worker.addEventListener("message", (event: MessageEvent) => { + if (event.data.type === "ready") { + workerCanvas.style.opacity = "1"; + resolve(); + } else if (event.data.type === "error") { + reject(new Error(event.data.message)); + } + }); + + worker.addEventListener("error", (event) => { + reject(new Error(event.message)); + }); + }); + + worker.postMessage( + { + type: "init", + canvas: offscreenCanvas, + width: workerCanvas.width, + height: workerCanvas.height, + }, + [offscreenCanvas] + ); + + window.addEventListener("resize", () => { + worker.postMessage({ + type: "resize", + width: Math.max(1, Math.floor(workerCanvas.clientWidth)), + height: Math.max(1, Math.floor(workerCanvas.clientHeight)), + }); + }); + + return ready; +} + +slowButton?.addEventListener("click", slowDownMainThread); + +window.__babylonDemoReady = Promise.all([startMainThreadDemo(), startWorkerDemo()]).then(() => { + loading?.classList.add("hidden"); +}); diff --git a/src/compiledDemos/Offscreen/scene.ts b/src/compiledDemos/Offscreen/scene.ts new file mode 100644 index 000000000..8e8323f15 --- /dev/null +++ b/src/compiledDemos/Offscreen/scene.ts @@ -0,0 +1,24 @@ +import { Animation } from "@babylonjs/core/Animations/animation"; +import type { Engine } from "@babylonjs/core/Engines/engine"; +import { ImportMeshAsync } from "@babylonjs/core/Loading/sceneLoader"; +import { Scene } from "@babylonjs/core/scene"; +import "@babylonjs/core/Helpers/sceneHelpers"; +import "@babylonjs/loaders/glTF"; + +export async function createOffscreenScene(engine: Engine, attachCameraControls: boolean): Promise { + const scene = new Scene(engine); + const result = await ImportMeshAsync("flightHelmet.glb", scene, { + rootUrl: "https://models.babylonjs.com/", + }); + + scene.createDefaultCameraOrLight(true, true, attachCameraControls); + scene.createDefaultEnvironment(); + + const rootMesh = result.meshes[0]; + if (rootMesh) { + rootMesh.rotationQuaternion = null; + Animation.CreateAndStartAnimation("turnTable", rootMesh, "rotation.y", 60, 480, 0, Math.PI * 2); + } + + return scene; +} diff --git a/src/compiledDemos/Offscreen/worker.ts b/src/compiledDemos/Offscreen/worker.ts new file mode 100644 index 000000000..6c0064af5 --- /dev/null +++ b/src/compiledDemos/Offscreen/worker.ts @@ -0,0 +1,69 @@ +import { Engine } from "@babylonjs/core/Engines/engine"; +import { createOffscreenScene } from "./scene"; + +type InitMessage = { + type: "init"; + canvas: OffscreenCanvas; + width: number; + height: number; +}; + +type ResizeMessage = { + type: "resize"; + width: number; + height: number; +}; + +type WorkerMessage = InitMessage | ResizeMessage; + +let canvas: OffscreenCanvas | undefined; +let engine: Engine | undefined; + +async function waitForSceneReady(scene: Awaited>): Promise { + await new Promise((resolve) => { + scene.executeWhenReady(resolve); + }); +} + +async function startWorkerRender(message: InitMessage): Promise { + canvas = message.canvas; + canvas.width = message.width; + canvas.height = message.height; + + engine = new Engine(canvas, true, { + preserveDrawingBuffer: true, + stencil: true, + }); + + const scene = await createOffscreenScene(engine, false); + await waitForSceneReady(scene); + + engine.runRenderLoop(() => { + engine?.resize(); + if (scene.activeCamera) { + scene.render(); + } + }); + + self.postMessage({ type: "ready" }); +} + +self.onmessage = (event: MessageEvent) => { + const message = event.data; + + if (message.type === "init") { + startWorkerRender(message).catch((error: unknown) => { + self.postMessage({ + type: "error", + message: error instanceof Error ? error.message : String(error), + }); + }); + return; + } + + if (canvas) { + canvas.width = message.width; + canvas.height = message.height; + engine?.resize(); + } +}; diff --git a/src/compiledDemos/manifest.json b/src/compiledDemos/manifest.json index c2b0cfe88..7a919237f 100644 --- a/src/compiledDemos/manifest.json +++ b/src/compiledDemos/manifest.json @@ -36,6 +36,18 @@ "timeoutMs": 20000, "minimumColoredSamples": 200 } + }, + { + "slug": "Offscreen", + "title": "Babylon.js - Worker mode", + "legacyPath": "static/Demos/Offscreen", + "sourceFiles": ["main.ts", "scene.ts", "worker.ts"], + "renderCheck": { + "timeoutMs": 45000, + "canvasSelector": "canvas", + "minimumCanvasCount": 2, + "minimumColoredSamples": 150 + } } ] } From 200305394ef9674f1b76ed7b8ea88da5347ae07c Mon Sep 17 00:00:00 2001 From: Raanan Weber Date: Wed, 6 May 2026 17:16:23 +0200 Subject: [PATCH 05/12] Add compiled demo migration inventory --- docs/compiled-demo-migration.md | 46 +++++++++++++++++++++++++++++++++ docs/compiled-demos.md | 2 ++ 2 files changed, 48 insertions(+) create mode 100644 docs/compiled-demo-migration.md diff --git a/docs/compiled-demo-migration.md b/docs/compiled-demo-migration.md new file mode 100644 index 000000000..8943c7a2f --- /dev/null +++ b/docs/compiled-demo-migration.md @@ -0,0 +1,46 @@ +# Compiled Demo Migration Inventory + +This tracks the move from legacy static demos under `static/Demos/` to compiled demos under `src/compiledDemos/`. Each migration should keep the public URL stable at `/Demos//`, expose source with `/Demos//source/`, and pass the compiled demo CI checks. + +## Status Key + +- `done`: Ported to `src/compiledDemos`, registered in `manifest.json`, and covered by render checks. +- `wave-1`: Simple single-canvas demo selected for the current migration wave. +- `candidate`: Not migrated yet, likely suitable for a later wave after inspection. +- `special`: Needs extra handling such as workers, vendored bundles, service workers, generated app bundles, WebGPU, physics, GUI, or complex external assets. + +## Current Status + +| Demo | Status | Notes | +| --- | --- | --- | +| Boom | done | First compiled demo; includes click interaction render check. | +| Lines | done | Thin animated line rendering; custom low colored-sample threshold. | +| Heightmap | done | Texture and heightmap asset loading. | +| Offscreen | done | Custom worker bootstrap; render check validates both canvases. | +| Fog | wave-1 | Animated simple geometry and exponential fog. | +| Bump | wave-1 | Simple texture material using an existing normal map asset. | +| Lights | wave-1 | Multiple dynamic lights and skybox. | +| Particles | wave-1 | CPU particle systems plus mirror render target texture. | + +## Next Waves + +| Wave | Demos | Main Risk To Prove | +| --- | --- | --- | +| Wave 1 follow-up | Fresnel, Ribbons, Polygon, Shadows, PointLightShadowMap, Refraction | Lens flares, procedural textures, polygon triangulation, shadows, and reflection/refraction scene components. | +| Asset and loader demos | AssetsManager, FlightHelmet, GLTF, GLTF1CesiumMan, GLTFMeshPrimitiveAttributeTest, GLTFNormals, Mansion, Sponza, TheCar, Yeti | Loader side effects, remote/local asset paths, large model load times. | +| Materials and render pipeline demos | Bump, CellShading, FireMaterial, Fur, GlowLayer, PBR, PBRGlossy, PBRGlossyBloom, PBRRough, WaterMaterial, DOF, DefaultRenderingPipeline, MotionBlur, PPBloom, PPConvolution, PPRef, SSAO, SSAO2, StandardRenderingPipeline, VolumetricLightScattering | Extra Babylon packages, shader/material side effects, post-process scene components. | +| Interaction and tooling demos | ActionBuilder, Actions, Charting, CustomShader, Decals, DragNDrop, Facets, Highlights, Lens, LookAt, Octree, Procedural, Simplification, VertexData, Views | Picking, pointer events, custom shaders, scene tools, and user interaction checks. | +| Animation, skeleton, and morph demos | Bones, Dancers, Dancing CSG, HillValley, HillValleyVR, InstancedBones, Instances, Instances2, LOD, MorphTargets, V8 | Skeletons, animation loops, instancing, LOD behavior, and render-check stability. | +| Physics and advanced systems | AdvancedShadows, CSG, Cloth, CustomRenderTarget, DisplacementMap, ExtrudePolygon, Multimaterial, Particles2, Physics, Planet, Ribbons, Ruins, SelfShadowing, SoftShadows, SPS, SPSCollisions, Starfield, Tunnel, Viper | Physics engines, generated geometry, custom render targets, particles, and advanced scene components. | +| Special app-style demos | Amp360Video, AnimatedGif, AudioAnalyser, AudioAnalyzer, ChibiRex, Distraction, Espilit, FatObjects, Flat2009, GlowingEspilit, Heart, Ink, MansionVR, ProductPage, RefProbe, Retail, SpaceDeK, Spaceship, SponzaDynamicShadows, Train, UTD, VideoProcessing, WCafe, WebGPU | Vendored bundles, multi-page apps, workers/service workers, audio/video, VR, WebGPU, or nonstandard build/runtime flows. | + +## Per-Demo Checklist + +1. Inspect the legacy `index.html`, `demo.js`, scene file, and local assets. +2. Create `src/compiledDemos//index.html`, `main.ts`, and `scene.ts` unless the demo needs a custom bootstrap. +3. Prefer `shared/demoRunner.ts` for single-canvas demos. +4. Replace `BABYLON.*` globals with named ESM imports and add side-effect imports for prototype-augmented APIs. +5. Keep asset URLs rooted at stable public paths such as `/Scenes/...` when possible. +6. Register the demo in `src/compiledDemos/manifest.json` with source files and render-check thresholds. +7. Run `npm run demos:format:write`, `npm run build`, and `npm run demos:ci`. +8. Commit one demo or small coherent batch at a time. \ No newline at end of file diff --git a/docs/compiled-demos.md b/docs/compiled-demos.md index 4af32ddb7..ac442f63d 100644 --- a/docs/compiled-demos.md +++ b/docs/compiled-demos.md @@ -2,6 +2,8 @@ The legacy demos under `static/Demos/` are copied directly into `build/` and load Babylon from the UMD CDN. Compiled demos live under `src/compiledDemos/`, import `@babylonjs/core` and sibling packages as ES modules, and are bundled with Vite. +The migration tracker lives in `docs/compiled-demo-migration.md`. + ## Goals - Keep existing demo URLs stable, for example `/Demos/Boom/`. From b2bcf450252597fbc0cc62bc31aa625c87c6dfa6 Mon Sep 17 00:00:00 2001 From: Raanan Weber Date: Wed, 6 May 2026 17:23:43 +0200 Subject: [PATCH 06/12] Port Wave 1 demos to compiled demos --- docs/compiled-demo-migration.md | 12 +-- docs/compiled-demos.md | 16 ++++ src/compiledDemos/Bump/index.html | 105 +++++++++++++++++++++++++ src/compiledDemos/Bump/main.ts | 6 ++ src/compiledDemos/Bump/scene.ts | 32 ++++++++ src/compiledDemos/Fog/index.html | 105 +++++++++++++++++++++++++ src/compiledDemos/Fog/main.ts | 6 ++ src/compiledDemos/Fog/scene.ts | 48 +++++++++++ src/compiledDemos/Lights/index.html | 105 +++++++++++++++++++++++++ src/compiledDemos/Lights/main.ts | 6 ++ src/compiledDemos/Lights/scene.ts | 82 +++++++++++++++++++ src/compiledDemos/Particles/index.html | 105 +++++++++++++++++++++++++ src/compiledDemos/Particles/main.ts | 6 ++ src/compiledDemos/Particles/scene.ts | 92 ++++++++++++++++++++++ src/compiledDemos/manifest.json | 40 ++++++++++ 15 files changed, 760 insertions(+), 6 deletions(-) create mode 100644 src/compiledDemos/Bump/index.html create mode 100644 src/compiledDemos/Bump/main.ts create mode 100644 src/compiledDemos/Bump/scene.ts create mode 100644 src/compiledDemos/Fog/index.html create mode 100644 src/compiledDemos/Fog/main.ts create mode 100644 src/compiledDemos/Fog/scene.ts create mode 100644 src/compiledDemos/Lights/index.html create mode 100644 src/compiledDemos/Lights/main.ts create mode 100644 src/compiledDemos/Lights/scene.ts create mode 100644 src/compiledDemos/Particles/index.html create mode 100644 src/compiledDemos/Particles/main.ts create mode 100644 src/compiledDemos/Particles/scene.ts diff --git a/docs/compiled-demo-migration.md b/docs/compiled-demo-migration.md index 8943c7a2f..f6ce03f80 100644 --- a/docs/compiled-demo-migration.md +++ b/docs/compiled-demo-migration.md @@ -17,18 +17,18 @@ This tracks the move from legacy static demos under `static/Demos/` to compiled | Lines | done | Thin animated line rendering; custom low colored-sample threshold. | | Heightmap | done | Texture and heightmap asset loading. | | Offscreen | done | Custom worker bootstrap; render check validates both canvases. | -| Fog | wave-1 | Animated simple geometry and exponential fog. | -| Bump | wave-1 | Simple texture material using an existing normal map asset. | -| Lights | wave-1 | Multiple dynamic lights and skybox. | -| Particles | wave-1 | CPU particle systems plus mirror render target texture. | +| Fog | done | Animated simple geometry and exponential fog. | +| Bump | done | Simple texture material using an existing shared normal map asset. | +| Lights | done | Multiple dynamic lights and shared skybox assets. | +| Particles | done | CPU particle systems plus mirror render target texture. | ## Next Waves | Wave | Demos | Main Risk To Prove | | --- | --- | --- | -| Wave 1 follow-up | Fresnel, Ribbons, Polygon, Shadows, PointLightShadowMap, Refraction | Lens flares, procedural textures, polygon triangulation, shadows, and reflection/refraction scene components. | +| Wave 1 follow-up | Fresnel, Ribbons, Polygon, Shadows, PointLightShadowMap, Refraction | Lens flares, procedural textures, polygon triangulation, shadows, and reflection/refraction scene components. Polygon needs an explicit `earcut` dependency decision before porting. | | Asset and loader demos | AssetsManager, FlightHelmet, GLTF, GLTF1CesiumMan, GLTFMeshPrimitiveAttributeTest, GLTFNormals, Mansion, Sponza, TheCar, Yeti | Loader side effects, remote/local asset paths, large model load times. | -| Materials and render pipeline demos | Bump, CellShading, FireMaterial, Fur, GlowLayer, PBR, PBRGlossy, PBRGlossyBloom, PBRRough, WaterMaterial, DOF, DefaultRenderingPipeline, MotionBlur, PPBloom, PPConvolution, PPRef, SSAO, SSAO2, StandardRenderingPipeline, VolumetricLightScattering | Extra Babylon packages, shader/material side effects, post-process scene components. | +| Materials and render pipeline demos | CellShading, FireMaterial, Fur, GlowLayer, PBR, PBRGlossy, PBRGlossyBloom, PBRRough, WaterMaterial, DOF, DefaultRenderingPipeline, MotionBlur, PPBloom, PPConvolution, PPRef, SSAO, SSAO2, StandardRenderingPipeline, VolumetricLightScattering | Extra Babylon packages, shader/material side effects, post-process scene components. | | Interaction and tooling demos | ActionBuilder, Actions, Charting, CustomShader, Decals, DragNDrop, Facets, Highlights, Lens, LookAt, Octree, Procedural, Simplification, VertexData, Views | Picking, pointer events, custom shaders, scene tools, and user interaction checks. | | Animation, skeleton, and morph demos | Bones, Dancers, Dancing CSG, HillValley, HillValleyVR, InstancedBones, Instances, Instances2, LOD, MorphTargets, V8 | Skeletons, animation loops, instancing, LOD behavior, and render-check stability. | | Physics and advanced systems | AdvancedShadows, CSG, Cloth, CustomRenderTarget, DisplacementMap, ExtrudePolygon, Multimaterial, Particles2, Physics, Planet, Ribbons, Ruins, SelfShadowing, SoftShadows, SPS, SPSCollisions, Starfield, Tunnel, Viper | Physics engines, generated geometry, custom render targets, particles, and advanced scene components. | diff --git a/docs/compiled-demos.md b/docs/compiled-demos.md index ac442f63d..edf8b0bea 100644 --- a/docs/compiled-demos.md +++ b/docs/compiled-demos.md @@ -34,10 +34,22 @@ src/compiledDemos/ index.html main.ts scene.ts + Bump/ + index.html + main.ts + scene.ts + Fog/ + index.html + main.ts + scene.ts Lines/ index.html main.ts scene.ts + Lights/ + index.html + main.ts + scene.ts Heightmap/ index.html main.ts @@ -47,6 +59,10 @@ src/compiledDemos/ main.ts scene.ts worker.ts + Particles/ + index.html + main.ts + scene.ts ``` Each demo folder owns its HTML shell and TypeScript entry. Shared browser bootstrapping lives in `shared/demoRunner.ts`, which creates the engine, starts the render loop, wires common controls, and exposes `window.__babylonDemoReady` for CI. diff --git a/src/compiledDemos/Bump/index.html b/src/compiledDemos/Bump/index.html new file mode 100644 index 000000000..388b8514e --- /dev/null +++ b/src/compiledDemos/Bump/index.html @@ -0,0 +1,105 @@ + + + + + + Babylon.js - Bump demo + + + + +
+ + +
+ Source +
+
+ + + + diff --git a/src/compiledDemos/Bump/main.ts b/src/compiledDemos/Bump/main.ts new file mode 100644 index 000000000..3758165a5 --- /dev/null +++ b/src/compiledDemos/Bump/main.ts @@ -0,0 +1,6 @@ +import { runDemo } from "../shared/demoRunner"; +import { createBumpScene } from "./scene"; + +runDemo({ + createScene: createBumpScene, +}); diff --git a/src/compiledDemos/Bump/scene.ts b/src/compiledDemos/Bump/scene.ts new file mode 100644 index 000000000..1e019c17c --- /dev/null +++ b/src/compiledDemos/Bump/scene.ts @@ -0,0 +1,32 @@ +import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera"; +import { Engine } from "@babylonjs/core/Engines/engine"; +import { PointLight } from "@babylonjs/core/Lights/pointLight"; +import { Color3 } from "@babylonjs/core/Maths/math.color"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial"; +import { Texture } from "@babylonjs/core/Materials/Textures/texture"; +import { CreateSphere } from "@babylonjs/core/Meshes/Builders/sphereBuilder"; +import { Scene } from "@babylonjs/core/scene"; + +export function createBumpScene(engine: Engine, canvas: HTMLCanvasElement): Scene { + const scene = new Scene(engine); + const camera = new ArcRotateCamera("Camera", 0, 0, 10, Vector3.Zero(), scene); + new PointLight("Omni", new Vector3(20, 100, 2), scene); + + const sphere = CreateSphere("Sphere", { segments: 16, diameter: 3 }, scene); + const material = new StandardMaterial("kosh", scene); + const bumpTexture = new Texture("/Scenes/Customs/normalMap.jpg", scene); + bumpTexture.level = 1; + material.bumpTexture = bumpTexture; + material.diffuseColor = new Color3(1, 0, 0); + sphere.material = material; + + camera.setPosition(new Vector3(-5, 5, 0)); + camera.attachControl(canvas, true); + + scene.registerBeforeRender(() => { + sphere.rotation.y += 0.02; + }); + + return scene; +} diff --git a/src/compiledDemos/Fog/index.html b/src/compiledDemos/Fog/index.html new file mode 100644 index 000000000..590fa8127 --- /dev/null +++ b/src/compiledDemos/Fog/index.html @@ -0,0 +1,105 @@ + + + + + + Babylon.js - Fog demo + + + + +
+ + +
+ Source +
+
+ + + + diff --git a/src/compiledDemos/Fog/main.ts b/src/compiledDemos/Fog/main.ts new file mode 100644 index 000000000..fde490891 --- /dev/null +++ b/src/compiledDemos/Fog/main.ts @@ -0,0 +1,6 @@ +import { runDemo } from "../shared/demoRunner"; +import { createFogScene } from "./scene"; + +runDemo({ + createScene: createFogScene, +}); diff --git a/src/compiledDemos/Fog/scene.ts b/src/compiledDemos/Fog/scene.ts new file mode 100644 index 000000000..8e5b39aea --- /dev/null +++ b/src/compiledDemos/Fog/scene.ts @@ -0,0 +1,48 @@ +import { FreeCamera } from "@babylonjs/core/Cameras/freeCamera"; +import { Engine } from "@babylonjs/core/Engines/engine"; +import { PointLight } from "@babylonjs/core/Lights/pointLight"; +import { Color3 } from "@babylonjs/core/Maths/math.color"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial"; +import { CreateSphere } from "@babylonjs/core/Meshes/Builders/sphereBuilder"; +import { Scene } from "@babylonjs/core/scene"; + +export function createFogScene(engine: Engine): Scene { + const scene = new Scene(engine); + const camera = new FreeCamera("Camera", new Vector3(0, 0, -20), scene); + new PointLight("Omni", new Vector3(20, 100, 2), scene); + + const sphere0 = CreateSphere("Sphere0", { segments: 16, diameter: 3 }, scene); + const sphere1 = CreateSphere("Sphere1", { segments: 16, diameter: 3 }, scene); + const sphere2 = CreateSphere("Sphere2", { segments: 16, diameter: 3 }, scene); + + const material0 = new StandardMaterial("mat0", scene); + material0.diffuseColor = new Color3(1, 0, 0); + sphere0.material = material0; + sphere0.position.x = -10; + + const material1 = new StandardMaterial("mat1", scene); + material1.diffuseColor = new Color3(1, 1, 0); + sphere1.material = material1; + + const material2 = new StandardMaterial("mat2", scene); + material2.diffuseColor = new Color3(1, 0, 1); + sphere2.material = material2; + sphere2.position.x = 10; + + sphere1.convertToFlatShadedMesh(); + camera.setTarget(Vector3.Zero()); + + scene.fogMode = Scene.FOGMODE_EXP; + scene.fogDensity = 0.1; + + let alpha = 0; + scene.registerBeforeRender(() => { + sphere0.position.z = 4 * Math.cos(alpha); + sphere1.position.z = 4 * Math.sin(alpha); + sphere2.position.z = sphere0.position.z; + alpha += 0.1; + }); + + return scene; +} diff --git a/src/compiledDemos/Lights/index.html b/src/compiledDemos/Lights/index.html new file mode 100644 index 000000000..1b478eb23 --- /dev/null +++ b/src/compiledDemos/Lights/index.html @@ -0,0 +1,105 @@ + + + + + + Babylon.js - Lights demo + + + + +
+ + +
+ Source +
+
+ + + + diff --git a/src/compiledDemos/Lights/main.ts b/src/compiledDemos/Lights/main.ts new file mode 100644 index 000000000..1dafa7975 --- /dev/null +++ b/src/compiledDemos/Lights/main.ts @@ -0,0 +1,6 @@ +import { runDemo } from "../shared/demoRunner"; +import { createLightsScene } from "./scene"; + +runDemo({ + createScene: createLightsScene, +}); diff --git a/src/compiledDemos/Lights/scene.ts b/src/compiledDemos/Lights/scene.ts new file mode 100644 index 000000000..be6db11ea --- /dev/null +++ b/src/compiledDemos/Lights/scene.ts @@ -0,0 +1,82 @@ +import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera"; +import { Engine } from "@babylonjs/core/Engines/engine"; +import { DirectionalLight } from "@babylonjs/core/Lights/directionalLight"; +import { PointLight } from "@babylonjs/core/Lights/pointLight"; +import { Color3 } from "@babylonjs/core/Maths/math.color"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial"; +import { CubeTexture } from "@babylonjs/core/Materials/Textures/cubeTexture"; +import { Texture } from "@babylonjs/core/Materials/Textures/texture"; +import { CreateBox } from "@babylonjs/core/Meshes/Builders/boxBuilder"; +import { CreateSphere } from "@babylonjs/core/Meshes/Builders/sphereBuilder"; +import { Scene } from "@babylonjs/core/scene"; + +export function createLightsScene(engine: Engine, canvas: HTMLCanvasElement): Scene { + const scene = new Scene(engine); + const camera = new ArcRotateCamera("Camera", 0, 0, 10, Vector3.Zero(), scene); + const light0 = new PointLight("Omni0", new Vector3(0, 10, 0), scene); + const light1 = new PointLight("Omni1", new Vector3(0, -10, 0), scene); + const light2 = new PointLight("Omni2", new Vector3(10, 0, 0), scene); + const light3 = new DirectionalLight("Dir0", new Vector3(1, -1, 0), scene); + const material = new StandardMaterial("kosh", scene); + const sphere = CreateSphere("Sphere", { segments: 16, diameter: 3 }, scene); + + camera.setPosition(new Vector3(-10, 10, 0)); + camera.attachControl(canvas, true); + light3.parent = camera; + + const lightSphere0 = CreateSphere("Sphere0", { segments: 16, diameter: 0.5 }, scene); + const lightSphere1 = CreateSphere("Sphere1", { segments: 16, diameter: 0.5 }, scene); + const lightSphere2 = CreateSphere("Sphere2", { segments: 16, diameter: 0.5 }, scene); + + lightSphere0.material = createEmissiveMaterial("red", new Color3(1, 0, 0), scene); + lightSphere1.material = createEmissiveMaterial("green", new Color3(0, 1, 0), scene); + lightSphere2.material = createEmissiveMaterial("blue", new Color3(0, 0, 1), scene); + + material.diffuseColor = Color3.White(); + sphere.material = material; + + light0.diffuse = new Color3(1, 0, 0); + light0.specular = new Color3(1, 0, 0); + light1.diffuse = new Color3(0, 1, 0); + light1.specular = new Color3(0, 1, 0); + light2.diffuse = new Color3(0, 0, 1); + light2.specular = new Color3(0, 0, 1); + light3.diffuse = Color3.White(); + light3.specular = Color3.White(); + + const skybox = CreateBox("skyBox", { size: 100 }, scene); + const skyboxMaterial = new StandardMaterial("skyBox", scene); + const skyboxTexture = new CubeTexture("/Scenes/Customs/skybox/skybox", scene); + skyboxTexture.coordinatesMode = Texture.SKYBOX_MODE; + skyboxMaterial.backFaceCulling = false; + skyboxMaterial.reflectionTexture = skyboxTexture; + skyboxMaterial.diffuseColor = Color3.Black(); + skyboxMaterial.specularColor = Color3.Black(); + skyboxMaterial.disableLighting = true; + skybox.material = skyboxMaterial; + skybox.infiniteDistance = true; + + let alpha = 0; + scene.registerBeforeRender(() => { + light0.position.set(10 * Math.sin(alpha), 0, 10 * Math.cos(alpha)); + light1.position.set(10 * Math.sin(alpha), 0, -10 * Math.cos(alpha)); + light2.position.set(10 * Math.cos(alpha), 0, 10 * Math.sin(alpha)); + + lightSphere0.position.copyFrom(light0.position); + lightSphere1.position.copyFrom(light1.position); + lightSphere2.position.copyFrom(light2.position); + + alpha += 0.01; + }); + + return scene; +} + +function createEmissiveMaterial(name: string, color: Color3, scene: Scene): StandardMaterial { + const material = new StandardMaterial(name, scene); + material.diffuseColor = Color3.Black(); + material.specularColor = Color3.Black(); + material.emissiveColor = color; + return material; +} diff --git a/src/compiledDemos/Particles/index.html b/src/compiledDemos/Particles/index.html new file mode 100644 index 000000000..c8f662911 --- /dev/null +++ b/src/compiledDemos/Particles/index.html @@ -0,0 +1,105 @@ + + + + + + Babylon.js - Particles demo + + + + +
+ + +
+ Source +
+
+ + + + diff --git a/src/compiledDemos/Particles/main.ts b/src/compiledDemos/Particles/main.ts new file mode 100644 index 000000000..eb80c392a --- /dev/null +++ b/src/compiledDemos/Particles/main.ts @@ -0,0 +1,6 @@ +import { runDemo } from "../shared/demoRunner"; +import { createParticlesScene } from "./scene"; + +runDemo({ + createScene: createParticlesScene, +}); diff --git a/src/compiledDemos/Particles/scene.ts b/src/compiledDemos/Particles/scene.ts new file mode 100644 index 000000000..df2d6253d --- /dev/null +++ b/src/compiledDemos/Particles/scene.ts @@ -0,0 +1,92 @@ +import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera"; +import { Engine } from "@babylonjs/core/Engines/engine"; +import { Color3, Color4 } from "@babylonjs/core/Maths/math.color"; +import { Plane } from "@babylonjs/core/Maths/math.plane"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial"; +import { MirrorTexture } from "@babylonjs/core/Materials/Textures/mirrorTexture"; +import { Texture } from "@babylonjs/core/Materials/Textures/texture"; +import { CreateBox } from "@babylonjs/core/Meshes/Builders/boxBuilder"; +import { ParticleSystem } from "@babylonjs/core/Particles/particleSystem"; +import { Scene } from "@babylonjs/core/scene"; + +const particleTextureUrl = "/Scenes/WorldMonger/Assets/Flare.png"; + +export function createParticlesScene(engine: Engine, canvas: HTMLCanvasElement): Scene { + const scene = new Scene(engine); + const camera = new ArcRotateCamera("Camera", 0, 0, 10, Vector3.Zero(), scene); + camera.setPosition(new Vector3(-5, 5, 0)); + camera.lowerBetaLimit = 0.1; + camera.upperBetaLimit = (Math.PI / 2) * 0.99; + camera.lowerRadiusLimit = 5; + camera.attachControl(canvas, true); + + const mirror = CreateBox("Mirror", { size: 1 }, scene); + mirror.scaling = new Vector3(100, 0.01, 100); + mirror.position.set(0, 0, 0); + + const mirrorMaterial = new StandardMaterial("mirror", scene); + const mirrorTexture = new MirrorTexture("mirror", 512, scene, true); + mirrorTexture.mirrorPlane = new Plane(0, -1, 0, 0); + mirrorTexture.level = 0.2; + mirrorMaterial.diffuseColor = new Color3(0.4, 0.4, 0.4); + mirrorMaterial.specularColor = Color3.Black(); + mirrorMaterial.reflectionTexture = mirrorTexture; + mirror.material = mirrorMaterial; + + const emitter0 = CreateBox("emitter0", { size: 0.1 }, scene); + emitter0.isVisible = false; + const emitter1 = CreateBox("emitter1", { size: 0.1 }, scene); + emitter1.isVisible = false; + mirrorTexture.renderList = [emitter0, emitter1]; + + const particleSystem = new ParticleSystem("particles", 4000, scene); + particleSystem.particleTexture = new Texture(particleTextureUrl, scene); + particleSystem.minAngularSpeed = -0.5; + particleSystem.maxAngularSpeed = 0.5; + particleSystem.minSize = 0.1; + particleSystem.maxSize = 0.5; + particleSystem.minLifeTime = 0.5; + particleSystem.maxLifeTime = 2; + particleSystem.minEmitPower = 0.5; + particleSystem.maxEmitPower = 4; + particleSystem.emitter = emitter0; + particleSystem.emitRate = 400; + particleSystem.blendMode = ParticleSystem.BLENDMODE_ONEONE; + particleSystem.minEmitBox = new Vector3(-0.5, 0, -0.5); + particleSystem.maxEmitBox = new Vector3(0.5, 0, 0.5); + particleSystem.direction1 = new Vector3(-1, 1, -1); + particleSystem.direction2 = new Vector3(1, 1, 1); + particleSystem.color1 = new Color4(1, 0, 0, 1); + particleSystem.color2 = new Color4(0, 1, 1, 1); + particleSystem.gravity = new Vector3(0, -2, 0); + particleSystem.start(); + + const particleSystem2 = new ParticleSystem("particles2", 4000, scene); + particleSystem2.particleTexture = new Texture(particleTextureUrl, scene); + particleSystem2.minSize = 0.1; + particleSystem2.maxSize = 0.3; + particleSystem2.minEmitPower = 1; + particleSystem2.maxEmitPower = 2; + particleSystem2.minLifeTime = 0.5; + particleSystem2.maxLifeTime = 1; + particleSystem2.emitter = emitter1; + particleSystem2.emitRate = 500; + particleSystem2.blendMode = ParticleSystem.BLENDMODE_ONEONE; + particleSystem2.minEmitBox = Vector3.Zero(); + particleSystem2.maxEmitBox = Vector3.Zero(); + particleSystem2.gravity = new Vector3(0, -0.5, 0); + particleSystem2.direction1 = Vector3.Zero(); + particleSystem2.direction2 = Vector3.Zero(); + particleSystem2.start(); + + let alpha = 0; + scene.registerBeforeRender(() => { + emitter1.position.x = 3 * Math.cos(alpha); + emitter1.position.y = 1; + emitter1.position.z = 3 * Math.sin(alpha); + alpha += 0.05 * scene.getAnimationRatio(); + }); + + return scene; +} diff --git a/src/compiledDemos/manifest.json b/src/compiledDemos/manifest.json index 7a919237f..d2e0719bb 100644 --- a/src/compiledDemos/manifest.json +++ b/src/compiledDemos/manifest.json @@ -17,6 +17,26 @@ } } }, + { + "slug": "Bump", + "title": "Babylon.js - Bump demo", + "legacyPath": "static/Demos/Bump", + "sourceFiles": ["main.ts", "scene.ts"], + "renderCheck": { + "timeoutMs": 15000, + "minimumColoredSamples": 120 + } + }, + { + "slug": "Fog", + "title": "Babylon.js - Fog demo", + "legacyPath": "static/Demos/Fog", + "sourceFiles": ["main.ts", "scene.ts"], + "renderCheck": { + "timeoutMs": 15000, + "minimumColoredSamples": 120 + } + }, { "slug": "Lines", "title": "Babylon.js - Lines demo", @@ -27,6 +47,16 @@ "minimumColoredSamples": 10 } }, + { + "slug": "Lights", + "title": "Babylon.js - Lights demo", + "legacyPath": "static/Demos/Lights", + "sourceFiles": ["main.ts", "scene.ts"], + "renderCheck": { + "timeoutMs": 20000, + "minimumColoredSamples": 120 + } + }, { "slug": "Heightmap", "title": "Babylon.js - Heightmap demo", @@ -48,6 +78,16 @@ "minimumCanvasCount": 2, "minimumColoredSamples": 150 } + }, + { + "slug": "Particles", + "title": "Babylon.js - Particles demo", + "legacyPath": "static/Demos/Particles", + "sourceFiles": ["main.ts", "scene.ts"], + "renderCheck": { + "timeoutMs": 20000, + "minimumColoredSamples": 80 + } } ] } From 05324f636387e3d1f9dd46623b2a483b670e4094 Mon Sep 17 00:00:00 2001 From: Raanan Weber Date: Wed, 6 May 2026 17:44:18 +0200 Subject: [PATCH 07/12] Port Wave 2 demos to compiled demos --- docs/compiled-demo-migration.md | 6 +- docs/compiled-demos.md | 16 ++ src/compiledDemos/Fresnel/index.html | 105 ++++++++++++ src/compiledDemos/Fresnel/main.ts | 6 + src/compiledDemos/Fresnel/scene.ts | 153 ++++++++++++++++++ .../PointLightShadowMap/index.html | 96 +++++++++++ src/compiledDemos/PointLightShadowMap/main.ts | 6 + .../PointLightShadowMap/scene.ts | 74 +++++++++ src/compiledDemos/Refraction/index.html | 126 +++++++++++++++ src/compiledDemos/Refraction/main.ts | 6 + src/compiledDemos/Refraction/scene.ts | 108 +++++++++++++ src/compiledDemos/Shadows/index.html | 96 +++++++++++ src/compiledDemos/Shadows/main.ts | 6 + src/compiledDemos/Shadows/scene.ts | 82 ++++++++++ src/compiledDemos/manifest.json | 40 +++++ 15 files changed, 925 insertions(+), 1 deletion(-) create mode 100644 src/compiledDemos/Fresnel/index.html create mode 100644 src/compiledDemos/Fresnel/main.ts create mode 100644 src/compiledDemos/Fresnel/scene.ts create mode 100644 src/compiledDemos/PointLightShadowMap/index.html create mode 100644 src/compiledDemos/PointLightShadowMap/main.ts create mode 100644 src/compiledDemos/PointLightShadowMap/scene.ts create mode 100644 src/compiledDemos/Refraction/index.html create mode 100644 src/compiledDemos/Refraction/main.ts create mode 100644 src/compiledDemos/Refraction/scene.ts create mode 100644 src/compiledDemos/Shadows/index.html create mode 100644 src/compiledDemos/Shadows/main.ts create mode 100644 src/compiledDemos/Shadows/scene.ts diff --git a/docs/compiled-demo-migration.md b/docs/compiled-demo-migration.md index f6ce03f80..06f4ef94c 100644 --- a/docs/compiled-demo-migration.md +++ b/docs/compiled-demo-migration.md @@ -21,12 +21,16 @@ This tracks the move from legacy static demos under `static/Demos/` to compiled | Bump | done | Simple texture material using an existing shared normal map asset. | | Lights | done | Multiple dynamic lights and shared skybox assets. | | Particles | done | CPU particle systems plus mirror render target texture. | +| Fresnel | done | Fresnel material parameters, shared skybox assets, and lens flares. | +| Shadows | done | Directional lights, shadow generators, and legacy grass texture. | +| PointLightShadowMap | done | Point light shadow generator with torus knot scene. | +| Refraction | done | Reflection probe refraction with built-in bump/fresnel/IOR controls. | ## Next Waves | Wave | Demos | Main Risk To Prove | | --- | --- | --- | -| Wave 1 follow-up | Fresnel, Ribbons, Polygon, Shadows, PointLightShadowMap, Refraction | Lens flares, procedural textures, polygon triangulation, shadows, and reflection/refraction scene components. Polygon needs an explicit `earcut` dependency decision before porting. | +| Wave 2 follow-up | Ribbons, Polygon | Ribbons needs procedural texture and volumetric-light-scattering post-process coverage. Polygon needs an explicit `earcut` dependency decision before porting. | | Asset and loader demos | AssetsManager, FlightHelmet, GLTF, GLTF1CesiumMan, GLTFMeshPrimitiveAttributeTest, GLTFNormals, Mansion, Sponza, TheCar, Yeti | Loader side effects, remote/local asset paths, large model load times. | | Materials and render pipeline demos | CellShading, FireMaterial, Fur, GlowLayer, PBR, PBRGlossy, PBRGlossyBloom, PBRRough, WaterMaterial, DOF, DefaultRenderingPipeline, MotionBlur, PPBloom, PPConvolution, PPRef, SSAO, SSAO2, StandardRenderingPipeline, VolumetricLightScattering | Extra Babylon packages, shader/material side effects, post-process scene components. | | Interaction and tooling demos | ActionBuilder, Actions, Charting, CustomShader, Decals, DragNDrop, Facets, Highlights, Lens, LookAt, Octree, Procedural, Simplification, VertexData, Views | Picking, pointer events, custom shaders, scene tools, and user interaction checks. | diff --git a/docs/compiled-demos.md b/docs/compiled-demos.md index edf8b0bea..6aa2f4544 100644 --- a/docs/compiled-demos.md +++ b/docs/compiled-demos.md @@ -42,6 +42,10 @@ src/compiledDemos/ index.html main.ts scene.ts + Fresnel/ + index.html + main.ts + scene.ts Lines/ index.html main.ts @@ -59,10 +63,22 @@ src/compiledDemos/ main.ts scene.ts worker.ts + PointLightShadowMap/ + index.html + main.ts + scene.ts Particles/ index.html main.ts scene.ts + Refraction/ + index.html + main.ts + scene.ts + Shadows/ + index.html + main.ts + scene.ts ``` Each demo folder owns its HTML shell and TypeScript entry. Shared browser bootstrapping lives in `shared/demoRunner.ts`, which creates the engine, starts the render loop, wires common controls, and exposes `window.__babylonDemoReady` for CI. diff --git a/src/compiledDemos/Fresnel/index.html b/src/compiledDemos/Fresnel/index.html new file mode 100644 index 000000000..bd5bfbbed --- /dev/null +++ b/src/compiledDemos/Fresnel/index.html @@ -0,0 +1,105 @@ + + + + + + Babylon.js - Fresnel demo + + + + +
+ + +
+ Source +
+
+ + + + diff --git a/src/compiledDemos/Fresnel/main.ts b/src/compiledDemos/Fresnel/main.ts new file mode 100644 index 000000000..611320c6b --- /dev/null +++ b/src/compiledDemos/Fresnel/main.ts @@ -0,0 +1,6 @@ +import { runDemo } from "../shared/demoRunner"; +import { createFresnelScene } from "./scene"; + +runDemo({ + createScene: createFresnelScene, +}); diff --git a/src/compiledDemos/Fresnel/scene.ts b/src/compiledDemos/Fresnel/scene.ts new file mode 100644 index 000000000..1a70a73eb --- /dev/null +++ b/src/compiledDemos/Fresnel/scene.ts @@ -0,0 +1,153 @@ +import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera"; +import { Engine } from "@babylonjs/core/Engines/engine"; +import { LensFlare } from "@babylonjs/core/LensFlares/lensFlare"; +import { LensFlareSystem } from "@babylonjs/core/LensFlares/lensFlareSystem"; +import { PointLight } from "@babylonjs/core/Lights/pointLight"; +import { Color3 } from "@babylonjs/core/Maths/math.color"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { FresnelParameters } from "@babylonjs/core/Materials/fresnelParameters"; +import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial"; +import { CubeTexture } from "@babylonjs/core/Materials/Textures/cubeTexture"; +import { Texture } from "@babylonjs/core/Materials/Textures/texture"; +import { CreateBox } from "@babylonjs/core/Meshes/Builders/boxBuilder"; +import { CreateSphere } from "@babylonjs/core/Meshes/Builders/sphereBuilder"; +import { Mesh } from "@babylonjs/core/Meshes/mesh"; +import { Scene } from "@babylonjs/core/scene"; +import "@babylonjs/core/LensFlares/lensFlareSystemSceneComponent"; + +const skyboxTextureUrl = "/Scenes/Customs/skybox/TropicalSunnyDay"; +const flareTextureUrl = "/Scenes/WorldMonger/Assets/Flare.png"; + +export function createFresnelScene(engine: Engine): Scene { + const scene = new Scene(engine); + const camera = new ArcRotateCamera("Camera", 0, 0, 10, Vector3.Zero(), scene); + const light = new PointLight("Omni0", new Vector3(-17.6, 18.8, -49.9), scene); + + camera.setPosition(new Vector3(-15, 3, 0)); + + const sphere1 = CreateSphere("Sphere1", { segments: 32, diameter: 3 }, scene); + const sphere2 = CreateSphere("Sphere2", { segments: 32, diameter: 3 }, scene); + const sphere3 = CreateSphere("Sphere3", { segments: 32, diameter: 3 }, scene); + const sphere4 = CreateSphere("Sphere4", { segments: 32, diameter: 3 }, scene); + const sphere5 = CreateSphere("Sphere5", { segments: 32, diameter: 3 }, scene); + + sphere2.position.z -= 5; + sphere3.position.z += 5; + sphere4.position.x += 5; + sphere5.position.x -= 5; + + sphere1.material = createReflectiveMaterial("kosh", scene); + sphere2.material = createBrightReflectiveMaterial(scene); + sphere3.material = createTransparentWhiteMaterial("kosh3", 0.2, scene); + sphere4.material = createOpaqueWhiteMaterial(scene); + sphere5.material = createDarkReflectiveMaterial(scene); + + markAsLensFlareBlocker(sphere2); + markAsLensFlareBlocker(sphere3); + markAsLensFlareBlocker(sphere4); + markAsLensFlareBlocker(sphere5); + + const skybox = CreateBox("skyBox", { size: 100 }, scene); + const skyboxMaterial = new StandardMaterial("skyBox", scene); + const skyboxTexture = new CubeTexture(skyboxTextureUrl, scene); + skyboxTexture.coordinatesMode = Texture.SKYBOX_MODE; + skyboxMaterial.backFaceCulling = false; + skyboxMaterial.reflectionTexture = skyboxTexture; + skyboxMaterial.diffuseColor = Color3.Black(); + skyboxMaterial.specularColor = Color3.Black(); + skyboxMaterial.disableLighting = true; + skybox.material = skyboxMaterial; + + const lensFlareSystem = new LensFlareSystem("lensFlareSystem", light, scene); + new LensFlare(0.2, 0, Color3.White(), flareTextureUrl, lensFlareSystem); + new LensFlare(0.5, 0.2, new Color3(0.5, 0.5, 1), flareTextureUrl, lensFlareSystem); + new LensFlare(0.2, 1, Color3.White(), flareTextureUrl, lensFlareSystem); + new LensFlare(0.4, 0.4, new Color3(1, 0.5, 1), flareTextureUrl, lensFlareSystem); + new LensFlare(0.1, 0.6, Color3.White(), flareTextureUrl, lensFlareSystem); + new LensFlare(0.3, 0.8, Color3.White(), flareTextureUrl, lensFlareSystem); + + scene.registerBeforeRender(() => { + camera.alpha += 0.01 * scene.getAnimationRatio(); + }); + + return scene; +} + +function createReflectiveMaterial(name: string, scene: Scene): StandardMaterial { + const material = new StandardMaterial(name, scene); + material.reflectionTexture = new CubeTexture(skyboxTextureUrl, scene); + material.diffuseColor = Color3.Black(); + material.emissiveColor = new Color3(0.5, 0.5, 0.5); + material.alpha = 0.2; + material.specularPower = 16; + material.reflectionFresnelParameters = new FresnelParameters(); + material.reflectionFresnelParameters.bias = 0.1; + material.emissiveFresnelParameters = createEmissiveFresnel(0.6, 4, Color3.White(), Color3.Black()); + material.opacityFresnelParameters = createOpacityFresnel(1, Color3.White(), Color3.Black()); + return material; +} + +function createBrightReflectiveMaterial(scene: Scene): StandardMaterial { + const material = new StandardMaterial("kosh2", scene); + material.reflectionTexture = new CubeTexture(skyboxTextureUrl, scene); + material.diffuseColor = Color3.Black(); + material.emissiveColor = new Color3(0.5, 0.5, 0.5); + material.specularPower = 32; + material.reflectionFresnelParameters = new FresnelParameters(); + material.reflectionFresnelParameters.bias = 0.1; + material.emissiveFresnelParameters = createEmissiveFresnel(0.5, 4, Color3.White(), Color3.Black()); + return material; +} + +function createTransparentWhiteMaterial(name: string, alpha: number, scene: Scene): StandardMaterial { + const material = new StandardMaterial(name, scene); + material.diffuseColor = Color3.Black(); + material.emissiveColor = Color3.White(); + material.specularPower = 64; + material.alpha = alpha; + material.emissiveFresnelParameters = createEmissiveFresnel(0.2, 1, Color3.White(), Color3.Black()); + material.opacityFresnelParameters = createOpacityFresnel(4, Color3.White(), Color3.Black()); + return material; +} + +function createOpaqueWhiteMaterial(scene: Scene): StandardMaterial { + const material = new StandardMaterial("kosh4", scene); + material.diffuseColor = Color3.Black(); + material.emissiveColor = Color3.White(); + material.specularPower = 64; + material.emissiveFresnelParameters = createEmissiveFresnel(0.5, 4, Color3.White(), Color3.Black()); + return material; +} + +function createDarkReflectiveMaterial(scene: Scene): StandardMaterial { + const material = new StandardMaterial("kosh5", scene); + const reflectionTexture = new CubeTexture(skyboxTextureUrl, scene); + reflectionTexture.level = 0.5; + material.diffuseColor = Color3.Black(); + material.reflectionTexture = reflectionTexture; + material.specularPower = 64; + material.emissiveColor = new Color3(0.2, 0.2, 0.2); + material.emissiveFresnelParameters = createEmissiveFresnel(0.4, 2, Color3.Black(), Color3.White()); + return material; +} + +function createEmissiveFresnel(bias: number, power: number, leftColor: Color3, rightColor: Color3): FresnelParameters { + const fresnel = new FresnelParameters(); + fresnel.bias = bias; + fresnel.power = power; + fresnel.leftColor = leftColor; + fresnel.rightColor = rightColor; + return fresnel; +} + +function createOpacityFresnel(power: number, leftColor: Color3, rightColor: Color3): FresnelParameters { + const fresnel = new FresnelParameters(); + fresnel.power = power; + fresnel.leftColor = leftColor; + fresnel.rightColor = rightColor; + return fresnel; +} + +function markAsLensFlareBlocker(mesh: Mesh & { isBlocker?: boolean }): void { + mesh.isBlocker = true; +} diff --git a/src/compiledDemos/PointLightShadowMap/index.html b/src/compiledDemos/PointLightShadowMap/index.html new file mode 100644 index 000000000..3134bfc05 --- /dev/null +++ b/src/compiledDemos/PointLightShadowMap/index.html @@ -0,0 +1,96 @@ + + + + + + Babylon.js - Point light shadow map demo + + + + +
+ + +
+ Source +
+
+ + + + diff --git a/src/compiledDemos/PointLightShadowMap/main.ts b/src/compiledDemos/PointLightShadowMap/main.ts new file mode 100644 index 000000000..98d8a8366 --- /dev/null +++ b/src/compiledDemos/PointLightShadowMap/main.ts @@ -0,0 +1,6 @@ +import { runDemo } from "../shared/demoRunner"; +import { createPointLightShadowMapScene } from "./scene"; + +runDemo({ + createScene: createPointLightShadowMapScene, +}); diff --git a/src/compiledDemos/PointLightShadowMap/scene.ts b/src/compiledDemos/PointLightShadowMap/scene.ts new file mode 100644 index 000000000..1ea2d8608 --- /dev/null +++ b/src/compiledDemos/PointLightShadowMap/scene.ts @@ -0,0 +1,74 @@ +import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera"; +import { Engine } from "@babylonjs/core/Engines/engine"; +import { PointLight } from "@babylonjs/core/Lights/pointLight"; +import { ShadowGenerator } from "@babylonjs/core/Lights/Shadows/shadowGenerator"; +import { Color3 } from "@babylonjs/core/Maths/math.color"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial"; +import { Texture } from "@babylonjs/core/Materials/Textures/texture"; +import { CreateSphere } from "@babylonjs/core/Meshes/Builders/sphereBuilder"; +import { CreateTorus } from "@babylonjs/core/Meshes/Builders/torusBuilder"; +import { CreateTorusKnot } from "@babylonjs/core/Meshes/Builders/torusKnotBuilder"; +import { Mesh } from "@babylonjs/core/Meshes/mesh"; +import { Scene } from "@babylonjs/core/scene"; +import "@babylonjs/core/Lights/Shadows/shadowGeneratorSceneComponent"; + +export function createPointLightShadowMapScene(engine: Engine, canvas: HTMLCanvasElement): Scene { + const scene = new Scene(engine); + const camera = new ArcRotateCamera("Camera", (3 * Math.PI) / 2, Math.PI / 8, 30, Vector3.Zero(), scene); + camera.lowerRadiusLimit = 5; + camera.upperRadiusLimit = 40; + camera.minZ = 0; + camera.attachControl(canvas, true); + + const light = new PointLight("light1", Vector3.Zero(), scene); + light.intensity = 0.7; + + const lightImpostor = CreateSphere("sphere1", { segments: 16, diameter: 1 }, scene); + const lightImpostorMaterial = new StandardMaterial("lightImpostor", scene); + lightImpostorMaterial.emissiveColor = Color3.Yellow(); + lightImpostorMaterial.linkEmissiveWithDiffuse = true; + lightImpostor.material = lightImpostorMaterial; + lightImpostor.parent = light; + + const knot = CreateTorusKnot( + "knot", + { radius: 2, tube: 0.2, radialSegments: 128, tubularSegments: 64, p: 4, q: 1 }, + scene + ); + const torus = CreateTorus("torus", { diameter: 8, thickness: 1, tessellation: 32 }, scene); + + const torusMaterial = new StandardMaterial("torus", scene); + torusMaterial.diffuseColor = Color3.Red(); + torus.material = torusMaterial; + + const knotMaterial = new StandardMaterial("knot", scene); + knotMaterial.diffuseColor = Color3.White(); + knot.material = knotMaterial; + + const container = CreateSphere("sphere2", { segments: 16, diameter: 50, sideOrientation: Mesh.BACKSIDE }, scene); + const containerMaterial = new StandardMaterial("container", scene); + const containerTexture = new Texture("/Scenes/Customs/grass.jpg", scene); + containerTexture.uScale = 10; + containerTexture.vScale = 10; + containerMaterial.diffuseTexture = containerTexture; + container.material = containerMaterial; + + const shadowGenerator = new ShadowGenerator(1024, light); + shadowGenerator.getShadowMap()?.renderList?.push(knot, torus); + shadowGenerator.setDarkness(0.5); + shadowGenerator.usePoissonSampling = true; + shadowGenerator.bias = 0; + + container.receiveShadows = true; + torus.receiveShadows = true; + + scene.registerBeforeRender(() => { + knot.rotation.y += 0.01; + knot.rotation.x += 0.01; + torus.rotation.y += 0.05; + torus.rotation.z += 0.03; + }); + + return scene; +} diff --git a/src/compiledDemos/Refraction/index.html b/src/compiledDemos/Refraction/index.html new file mode 100644 index 000000000..6a8ab7b71 --- /dev/null +++ b/src/compiledDemos/Refraction/index.html @@ -0,0 +1,126 @@ + + + + + + Babylon.js - Refraction demo + + + + +
+ + +
+
+ + + +
+ Source +
+
+ + + + diff --git a/src/compiledDemos/Refraction/main.ts b/src/compiledDemos/Refraction/main.ts new file mode 100644 index 000000000..488e28bab --- /dev/null +++ b/src/compiledDemos/Refraction/main.ts @@ -0,0 +1,6 @@ +import { runDemo } from "../shared/demoRunner"; +import { createRefractionScene } from "./scene"; + +runDemo({ + createScene: createRefractionScene, +}); diff --git a/src/compiledDemos/Refraction/scene.ts b/src/compiledDemos/Refraction/scene.ts new file mode 100644 index 000000000..ed7b5b577 --- /dev/null +++ b/src/compiledDemos/Refraction/scene.ts @@ -0,0 +1,108 @@ +import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera"; +import { Engine } from "@babylonjs/core/Engines/engine"; +import { HemisphericLight } from "@babylonjs/core/Lights/hemisphericLight"; +import { Color3 } from "@babylonjs/core/Maths/math.color"; +import { Matrix, Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { FresnelParameters } from "@babylonjs/core/Materials/fresnelParameters"; +import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial"; +import { Texture } from "@babylonjs/core/Materials/Textures/texture"; +import { CreateBox } from "@babylonjs/core/Meshes/Builders/boxBuilder"; +import { CreateSphere } from "@babylonjs/core/Meshes/Builders/sphereBuilder"; +import { CreateTorusKnot } from "@babylonjs/core/Meshes/Builders/torusKnotBuilder"; +import { ReflectionProbe } from "@babylonjs/core/Probes/reflectionProbe"; +import { Scene } from "@babylonjs/core/scene"; + +export function createRefractionScene(engine: Engine, canvas: HTMLCanvasElement): Scene { + const scene = new Scene(engine); + const camera = new ArcRotateCamera("camera1", 0, 0, 10, Vector3.Zero(), scene); + camera.setPosition(new Vector3(0, 5, -10)); + camera.attachControl(canvas, true); + camera.upperBetaLimit = Math.PI / 2; + camera.lowerRadiusLimit = 4; + + const light = new HemisphericLight("light1", new Vector3(0, 1, 0), scene); + light.intensity = 0.7; + + const knot = CreateTorusKnot( + "knot", + { radius: 1, tube: 0.4, radialSegments: 128, tubularSegments: 64, p: 2, q: 3 }, + scene + ); + const yellowSphere = CreateSphere("yellowSphere", { segments: 16, diameter: 1.5 }, scene); + const greenSphere = CreateSphere("greenSphere", { segments: 16, diameter: 1.5 }, scene); + + yellowSphere.setPivotMatrix(Matrix.Translation(3, 0, 0)); + greenSphere.setPivotMatrix(Matrix.Translation(0, 0, 3)); + yellowSphere.material = createDiffuseMaterial("yellowMaterial", Color3.Yellow(), scene); + greenSphere.material = createDiffuseMaterial("greenMaterial", Color3.Green(), scene); + + const ground = CreateBox("Mirror", { size: 1 }, scene); + const groundMaterial = new StandardMaterial("ground", scene); + ground.scaling = new Vector3(100, 0.01, 100); + const groundTexture = new Texture("./refraction.jpg", scene); + groundTexture.uScale = 10; + groundTexture.vScale = 10; + groundMaterial.diffuseTexture = groundTexture; + ground.position = new Vector3(0, -2, 0); + ground.material = groundMaterial; + + const mainMaterial = new StandardMaterial("main", scene); + const probe = new ReflectionProbe("main", 512, scene); + probe.renderList?.push(yellowSphere, greenSphere, ground); + mainMaterial.diffuseColor = new Color3(1, 0.5, 0.5); + mainMaterial.refractionTexture = probe.cubeTexture; + mainMaterial.refractionFresnelParameters = new FresnelParameters(); + mainMaterial.refractionFresnelParameters.bias = 0.5; + mainMaterial.refractionFresnelParameters.power = 16; + mainMaterial.refractionFresnelParameters.leftColor = Color3.Black(); + mainMaterial.refractionFresnelParameters.rightColor = Color3.White(); + mainMaterial.refractionFresnelParameters.isEnabled = false; + mainMaterial.indexOfRefraction = 1.05; + knot.material = mainMaterial; + + scene.fogMode = Scene.FOGMODE_LINEAR; + scene.fogColor = new Color3(scene.clearColor.r, scene.clearColor.g, scene.clearColor.b); + scene.fogStart = 20; + scene.fogEnd = 50; + + wireControls(mainMaterial, scene); + + scene.registerBeforeRender(() => { + yellowSphere.rotation.y += 0.01; + greenSphere.rotation.y += 0.01; + }); + + return scene; +} + +function createDiffuseMaterial(name: string, color: Color3, scene: Scene): StandardMaterial { + const material = new StandardMaterial(name, scene); + material.diffuseColor = color; + return material; +} + +function wireControls(mainMaterial: StandardMaterial, scene: Scene): void { + const bumpToggle = document.getElementById("bumpToggle") as HTMLInputElement | null; + const fresnelToggle = document.getElementById("fresnelToggle") as HTMLInputElement | null; + const indexOfRefraction = document.getElementById("indexOfRefraction") as HTMLInputElement | null; + + bumpToggle?.addEventListener("change", () => { + if (!bumpToggle.checked) { + mainMaterial.bumpTexture?.dispose(); + mainMaterial.bumpTexture = null; + return; + } + + mainMaterial.bumpTexture = new Texture("/Scenes/Customs/normalMap.jpg", scene); + }); + + fresnelToggle?.addEventListener("change", () => { + if (mainMaterial.refractionFresnelParameters) { + mainMaterial.refractionFresnelParameters.isEnabled = fresnelToggle.checked; + } + }); + + indexOfRefraction?.addEventListener("input", () => { + mainMaterial.indexOfRefraction = Number(indexOfRefraction.value); + }); +} diff --git a/src/compiledDemos/Shadows/index.html b/src/compiledDemos/Shadows/index.html new file mode 100644 index 000000000..db902c93d --- /dev/null +++ b/src/compiledDemos/Shadows/index.html @@ -0,0 +1,96 @@ + + + + + + Babylon.js - Shadows demo + + + + +
+ + +
+ Source +
+
+ + + + diff --git a/src/compiledDemos/Shadows/main.ts b/src/compiledDemos/Shadows/main.ts new file mode 100644 index 000000000..d7b44c04e --- /dev/null +++ b/src/compiledDemos/Shadows/main.ts @@ -0,0 +1,6 @@ +import { runDemo } from "../shared/demoRunner"; +import { createShadowsScene } from "./scene"; + +runDemo({ + createScene: createShadowsScene, +}); diff --git a/src/compiledDemos/Shadows/scene.ts b/src/compiledDemos/Shadows/scene.ts new file mode 100644 index 000000000..fe995f471 --- /dev/null +++ b/src/compiledDemos/Shadows/scene.ts @@ -0,0 +1,82 @@ +import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera"; +import { Engine } from "@babylonjs/core/Engines/engine"; +import { DirectionalLight } from "@babylonjs/core/Lights/directionalLight"; +import { ShadowGenerator } from "@babylonjs/core/Lights/Shadows/shadowGenerator"; +import { Color3 } from "@babylonjs/core/Maths/math.color"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial"; +import { CubeTexture } from "@babylonjs/core/Materials/Textures/cubeTexture"; +import { Texture } from "@babylonjs/core/Materials/Textures/texture"; +import { CreateBox } from "@babylonjs/core/Meshes/Builders/boxBuilder"; +import { CreateGround } from "@babylonjs/core/Meshes/Builders/groundBuilder"; +import { CreateTorus } from "@babylonjs/core/Meshes/Builders/torusBuilder"; +import { Scene } from "@babylonjs/core/scene"; +import "@babylonjs/core/Lights/Shadows/shadowGeneratorSceneComponent"; + +export function createShadowsScene(engine: Engine, canvas: HTMLCanvasElement): Scene { + const scene = new Scene(engine); + const camera = new ArcRotateCamera("Camera", 0, 0, 10, Vector3.Zero(), scene); + const light = new DirectionalLight("dir01", new Vector3(0, -1, -0.2), scene); + const light2 = new DirectionalLight("dir02", new Vector3(-1, -2, -1), scene); + + light.position = new Vector3(0, 30, 0); + light2.position = new Vector3(10, 20, 10); + light.intensity = 0.6; + light2.intensity = 0.6; + + camera.setPosition(new Vector3(-20, 20, 0)); + camera.attachControl(canvas, true); + + const skybox = CreateBox("skyBox", { size: 1000 }, scene); + const skyboxMaterial = new StandardMaterial("skyBox", scene); + const skyboxTexture = new CubeTexture("/Scenes/Customs/skybox/night", scene); + skyboxTexture.coordinatesMode = Texture.SKYBOX_MODE; + skyboxMaterial.backFaceCulling = false; + skyboxMaterial.reflectionTexture = skyboxTexture; + skyboxMaterial.diffuseColor = Color3.Black(); + skyboxMaterial.specularColor = Color3.Black(); + skyboxMaterial.disableLighting = true; + skybox.material = skyboxMaterial; + + const ground = CreateGround("ground", { width: 1000, height: 1000, subdivisions: 1 }, scene); + const groundMaterial = new StandardMaterial("ground", scene); + const grassTexture = new Texture("./grass.jpg", scene); + grassTexture.uScale = 60; + grassTexture.vScale = 60; + groundMaterial.diffuseTexture = grassTexture; + groundMaterial.specularColor = Color3.Black(); + ground.position.y = -2.05; + ground.material = groundMaterial; + + const torus = CreateTorus("torus", { diameter: 8, thickness: 2, tessellation: 32 }, scene); + torus.position.y = 6; + const torus2 = CreateTorus("torus2", { diameter: 4, thickness: 1, tessellation: 32 }, scene); + torus2.position.y = 6; + + const torusMaterial = new StandardMaterial("torus", scene); + torusMaterial.diffuseColor = new Color3(0.5, 0.5, 0.5); + torusMaterial.specularColor = new Color3(0.5, 0.5, 0.5); + torus.material = torusMaterial; + torus2.material = torusMaterial; + + const shadowGenerator = new ShadowGenerator(512, light); + shadowGenerator.getShadowMap()?.renderList?.push(torus, torus2); + shadowGenerator.useExponentialShadowMap = true; + + const shadowGenerator2 = new ShadowGenerator(512, light2); + shadowGenerator2.getShadowMap()?.renderList?.push(torus, torus2); + shadowGenerator2.useExponentialShadowMap = true; + + ground.receiveShadows = true; + + scene.registerBeforeRender(() => { + camera.beta = Math.min(Math.max(camera.beta, 0.1), (Math.PI / 2) * 0.99); + camera.radius = Math.min(Math.max(camera.radius, 5), 150); + torus.rotation.x += 0.01; + torus.rotation.z += 0.02; + torus2.rotation.x += 0.02; + torus2.rotation.y += 0.01; + }); + + return scene; +} diff --git a/src/compiledDemos/manifest.json b/src/compiledDemos/manifest.json index d2e0719bb..857f73417 100644 --- a/src/compiledDemos/manifest.json +++ b/src/compiledDemos/manifest.json @@ -37,6 +37,16 @@ "minimumColoredSamples": 120 } }, + { + "slug": "Fresnel", + "title": "Babylon.js - Fresnel demo", + "legacyPath": "static/Demos/Fresnel", + "sourceFiles": ["main.ts", "scene.ts"], + "renderCheck": { + "timeoutMs": 20000, + "minimumColoredSamples": 160 + } + }, { "slug": "Lines", "title": "Babylon.js - Lines demo", @@ -79,6 +89,16 @@ "minimumColoredSamples": 150 } }, + { + "slug": "PointLightShadowMap", + "title": "Babylon.js - Point light shadow map demo", + "legacyPath": "static/Demos/PointLightShadowMap", + "sourceFiles": ["main.ts", "scene.ts"], + "renderCheck": { + "timeoutMs": 20000, + "minimumColoredSamples": 120 + } + }, { "slug": "Particles", "title": "Babylon.js - Particles demo", @@ -88,6 +108,26 @@ "timeoutMs": 20000, "minimumColoredSamples": 80 } + }, + { + "slug": "Refraction", + "title": "Babylon.js - Refraction demo", + "legacyPath": "static/Demos/Refraction", + "sourceFiles": ["main.ts", "scene.ts"], + "renderCheck": { + "timeoutMs": 20000, + "minimumColoredSamples": 120 + } + }, + { + "slug": "Shadows", + "title": "Babylon.js - Shadows demo", + "legacyPath": "static/Demos/Shadows", + "sourceFiles": ["main.ts", "scene.ts"], + "renderCheck": { + "timeoutMs": 20000, + "minimumColoredSamples": 120 + } } ] } From 902b03f63063d3540edfbbf99a57553f8428dd24 Mon Sep 17 00:00:00 2001 From: Raanan Weber Date: Wed, 6 May 2026 18:14:49 +0200 Subject: [PATCH 08/12] Port asset loading demos to compiled demos --- docs/compiled-demo-migration.md | 8 +- docs/compiled-demos.md | 16 +++ src/compiledDemos/AssetsManager/index.html | 112 ++++++++++++++++++ src/compiledDemos/AssetsManager/main.ts | 6 + src/compiledDemos/AssetsManager/scene.ts | 48 ++++++++ src/compiledDemos/GLTF/index.html | 105 ++++++++++++++++ src/compiledDemos/GLTF/main.ts | 6 + src/compiledDemos/GLTF/scene.ts | 34 ++++++ .../GLTFMeshPrimitiveAttributeTest/index.html | 105 ++++++++++++++++ .../GLTFMeshPrimitiveAttributeTest/main.ts | 6 + .../GLTFMeshPrimitiveAttributeTest/scene.ts | 75 ++++++++++++ src/compiledDemos/GLTFNormals/index.html | 105 ++++++++++++++++ src/compiledDemos/GLTFNormals/main.ts | 6 + src/compiledDemos/GLTFNormals/scene.ts | 74 ++++++++++++ src/compiledDemos/manifest.json | 40 +++++++ 15 files changed, 744 insertions(+), 2 deletions(-) create mode 100644 src/compiledDemos/AssetsManager/index.html create mode 100644 src/compiledDemos/AssetsManager/main.ts create mode 100644 src/compiledDemos/AssetsManager/scene.ts create mode 100644 src/compiledDemos/GLTF/index.html create mode 100644 src/compiledDemos/GLTF/main.ts create mode 100644 src/compiledDemos/GLTF/scene.ts create mode 100644 src/compiledDemos/GLTFMeshPrimitiveAttributeTest/index.html create mode 100644 src/compiledDemos/GLTFMeshPrimitiveAttributeTest/main.ts create mode 100644 src/compiledDemos/GLTFMeshPrimitiveAttributeTest/scene.ts create mode 100644 src/compiledDemos/GLTFNormals/index.html create mode 100644 src/compiledDemos/GLTFNormals/main.ts create mode 100644 src/compiledDemos/GLTFNormals/scene.ts diff --git a/docs/compiled-demo-migration.md b/docs/compiled-demo-migration.md index 06f4ef94c..b95c45c2f 100644 --- a/docs/compiled-demo-migration.md +++ b/docs/compiled-demo-migration.md @@ -13,12 +13,16 @@ This tracks the move from legacy static demos under `static/Demos/` to compiled | Demo | Status | Notes | | --- | --- | --- | +| AssetsManager | done | AssetsManager mesh, text, and binary tasks using local legacy demo assets. | | Boom | done | First compiled demo; includes click interaction render check. | | Lines | done | Thin animated line rendering; custom low colored-sample threshold. | | Heightmap | done | Texture and heightmap asset loading. | | Offscreen | done | Custom worker bootstrap; render check validates both canvases. | | Fog | done | Animated simple geometry and exponential fog. | | Bump | done | Simple texture material using an existing shared normal map asset. | +| GLTF | done | glTF binary asset loading from the deployed legacy `/assets/` path. | +| GLTFMeshPrimitiveAttributeTest | done | Multiple remote glTF assets plus generated normal attribute coverage. | +| GLTFNormals | done | Multiple remote glTF assets covering normals/tangents variants. | | Lights | done | Multiple dynamic lights and shared skybox assets. | | Particles | done | CPU particle systems plus mirror render target texture. | | Fresnel | done | Fresnel material parameters, shared skybox assets, and lens flares. | @@ -30,8 +34,8 @@ This tracks the move from legacy static demos under `static/Demos/` to compiled | Wave | Demos | Main Risk To Prove | | --- | --- | --- | -| Wave 2 follow-up | Ribbons, Polygon | Ribbons needs procedural texture and volumetric-light-scattering post-process coverage. Polygon needs an explicit `earcut` dependency decision before porting. | -| Asset and loader demos | AssetsManager, FlightHelmet, GLTF, GLTF1CesiumMan, GLTFMeshPrimitiveAttributeTest, GLTFNormals, Mansion, Sponza, TheCar, Yeti | Loader side effects, remote/local asset paths, large model load times. | +| Asset and loader follow-up | FlightHelmet, GLTF1CesiumMan, Mansion, Sponza, TheCar, Yeti | Larger model load times, glTF v1 compatibility, environment assets, shadows, and optimizer behavior. | +| Render/effects follow-up | Ribbons, Polygon | Ribbons needs procedural texture and volumetric-light-scattering post-process coverage. Polygon needs an explicit `earcut` dependency decision before porting. | | Materials and render pipeline demos | CellShading, FireMaterial, Fur, GlowLayer, PBR, PBRGlossy, PBRGlossyBloom, PBRRough, WaterMaterial, DOF, DefaultRenderingPipeline, MotionBlur, PPBloom, PPConvolution, PPRef, SSAO, SSAO2, StandardRenderingPipeline, VolumetricLightScattering | Extra Babylon packages, shader/material side effects, post-process scene components. | | Interaction and tooling demos | ActionBuilder, Actions, Charting, CustomShader, Decals, DragNDrop, Facets, Highlights, Lens, LookAt, Octree, Procedural, Simplification, VertexData, Views | Picking, pointer events, custom shaders, scene tools, and user interaction checks. | | Animation, skeleton, and morph demos | Bones, Dancers, Dancing CSG, HillValley, HillValleyVR, InstancedBones, Instances, Instances2, LOD, MorphTargets, V8 | Skeletons, animation loops, instancing, LOD behavior, and render-check stability. | diff --git a/docs/compiled-demos.md b/docs/compiled-demos.md index 6aa2f4544..9e64402f6 100644 --- a/docs/compiled-demos.md +++ b/docs/compiled-demos.md @@ -30,6 +30,10 @@ src/compiledDemos/ manifest.json shared/ demoRunner.ts + AssetsManager/ + index.html + main.ts + scene.ts Boom/ index.html main.ts @@ -46,6 +50,18 @@ src/compiledDemos/ index.html main.ts scene.ts + GLTF/ + index.html + main.ts + scene.ts + GLTFMeshPrimitiveAttributeTest/ + index.html + main.ts + scene.ts + GLTFNormals/ + index.html + main.ts + scene.ts Lines/ index.html main.ts diff --git a/src/compiledDemos/AssetsManager/index.html b/src/compiledDemos/AssetsManager/index.html new file mode 100644 index 000000000..84e1510d1 --- /dev/null +++ b/src/compiledDemos/AssetsManager/index.html @@ -0,0 +1,112 @@ + + + + + + Babylon.js - Assets manager demo + + + + +
+ + +
+ Source +
+
+
+ + + + diff --git a/src/compiledDemos/AssetsManager/main.ts b/src/compiledDemos/AssetsManager/main.ts new file mode 100644 index 000000000..e6a7c67ba --- /dev/null +++ b/src/compiledDemos/AssetsManager/main.ts @@ -0,0 +1,6 @@ +import { runDemo } from "../shared/demoRunner"; +import { createAssetsManagerScene } from "./scene"; + +runDemo({ + createScene: createAssetsManagerScene, +}); diff --git a/src/compiledDemos/AssetsManager/scene.ts b/src/compiledDemos/AssetsManager/scene.ts new file mode 100644 index 000000000..d09430644 --- /dev/null +++ b/src/compiledDemos/AssetsManager/scene.ts @@ -0,0 +1,48 @@ +import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera"; +import type { Engine } from "@babylonjs/core/Engines/engine"; +import { HemisphericLight } from "@babylonjs/core/Lights/hemisphericLight"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { AssetsManager } from "@babylonjs/core/Misc/assetsManager"; +import { Scene } from "@babylonjs/core/scene"; +import "@babylonjs/core/Loading/Plugins/babylonFileLoader"; + +export async function createAssetsManagerScene(engine: Engine, canvas: HTMLCanvasElement): Promise { + const scene = new Scene(engine); + const status = document.getElementById("assetStatus"); + const camera = new ArcRotateCamera("Camera", 0, Math.PI / 2, 80, Vector3.Zero(), scene); + new HemisphericLight("light", new Vector3(0, 1, 0), scene); + + camera.attachControl(canvas, true); + + const assetsManager = new AssetsManager(scene); + assetsManager.useDefaultLoadingScreen = false; + + const meshTask = assetsManager.addMeshTask("skull task", "", "./", "skull.babylon"); + meshTask.onSuccess = (task) => { + const rootMesh = task.loadedMeshes[0]; + if (rootMesh) { + rootMesh.position = Vector3.Zero(); + } + }; + + const textTask = assetsManager.addTextFileTask("text task", "msg.txt"); + textTask.onSuccess = (task) => { + if (status) { + status.textContent = task.text.trim(); + } + }; + + assetsManager.addBinaryFileTask("binary task", "grass.jpg"); + + await new Promise((resolve, reject) => { + assetsManager.onTaskError = (task) => { + reject(new Error(`Unable to complete asset task: ${task.name}`)); + }; + assetsManager.onFinish = () => { + resolve(); + }; + assetsManager.load(); + }); + + return scene; +} diff --git a/src/compiledDemos/GLTF/index.html b/src/compiledDemos/GLTF/index.html new file mode 100644 index 000000000..ec3f9b1f5 --- /dev/null +++ b/src/compiledDemos/GLTF/index.html @@ -0,0 +1,105 @@ + + + + + + Babylon.js - GLTF demo + + + + +
+ + +
+ Source +
+
+ + + + diff --git a/src/compiledDemos/GLTF/main.ts b/src/compiledDemos/GLTF/main.ts new file mode 100644 index 000000000..e40c9f9e2 --- /dev/null +++ b/src/compiledDemos/GLTF/main.ts @@ -0,0 +1,6 @@ +import { runDemo } from "../shared/demoRunner"; +import { createGltfScene } from "./scene"; + +runDemo({ + createScene: createGltfScene, +}); diff --git a/src/compiledDemos/GLTF/scene.ts b/src/compiledDemos/GLTF/scene.ts new file mode 100644 index 000000000..340736c2a --- /dev/null +++ b/src/compiledDemos/GLTF/scene.ts @@ -0,0 +1,34 @@ +import type { Engine } from "@babylonjs/core/Engines/engine"; +import { HemisphericLight } from "@babylonjs/core/Lights/hemisphericLight"; +import { AppendSceneAsync } from "@babylonjs/core/Loading/sceneLoader"; +import { Color3 } from "@babylonjs/core/Maths/math.color"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { Scene } from "@babylonjs/core/scene"; +import "@babylonjs/core/Helpers/sceneHelpers"; +import "@babylonjs/core/Loading/loadingScreen"; +import "@babylonjs/loaders/glTF"; + +export async function createGltfScene(engine: Engine): Promise { + const scene = new Scene(engine); + scene.clearColor.set(0, 0, 0, 1); + + const light = new HemisphericLight("light", new Vector3(0, 1, 0), scene); + light.diffuse = Color3.White(); + + await AppendSceneAsync("Alien.glb", scene, { + rootUrl: "https://www.babylonjs.com/assets/", + }); + + scene.activeCamera = null; + scene.createDefaultCameraOrLight(true, true, true); + + const rootMesh = scene.meshes[0]; + if (rootMesh) { + rootMesh.rotationQuaternion = null; + scene.registerBeforeRender(() => { + rootMesh.rotation.y += 0.003 * scene.getAnimationRatio(); + }); + } + + return scene; +} diff --git a/src/compiledDemos/GLTFMeshPrimitiveAttributeTest/index.html b/src/compiledDemos/GLTFMeshPrimitiveAttributeTest/index.html new file mode 100644 index 000000000..7671e45d7 --- /dev/null +++ b/src/compiledDemos/GLTFMeshPrimitiveAttributeTest/index.html @@ -0,0 +1,105 @@ + + + + + + Babylon.js - Mesh primitive attribute test + + + + +
+ + +
+ Source +
+
+ + + + diff --git a/src/compiledDemos/GLTFMeshPrimitiveAttributeTest/main.ts b/src/compiledDemos/GLTFMeshPrimitiveAttributeTest/main.ts new file mode 100644 index 000000000..49c90bcb4 --- /dev/null +++ b/src/compiledDemos/GLTFMeshPrimitiveAttributeTest/main.ts @@ -0,0 +1,6 @@ +import { runDemo } from "../shared/demoRunner"; +import { createGltfMeshPrimitiveAttributeTestScene } from "./scene"; + +runDemo({ + createScene: createGltfMeshPrimitiveAttributeTestScene, +}); diff --git a/src/compiledDemos/GLTFMeshPrimitiveAttributeTest/scene.ts b/src/compiledDemos/GLTFMeshPrimitiveAttributeTest/scene.ts new file mode 100644 index 000000000..dfb6c73e1 --- /dev/null +++ b/src/compiledDemos/GLTFMeshPrimitiveAttributeTest/scene.ts @@ -0,0 +1,75 @@ +import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera"; +import type { Engine } from "@babylonjs/core/Engines/engine"; +import { ImportMeshAsync } from "@babylonjs/core/Loading/sceneLoader"; +import { Color3 } from "@babylonjs/core/Maths/math.color"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial"; +import { CubeTexture } from "@babylonjs/core/Materials/Textures/cubeTexture"; +import { DynamicTexture } from "@babylonjs/core/Materials/Textures/dynamicTexture"; +import { CreatePlane } from "@babylonjs/core/Meshes/Builders/planeBuilder"; +import { Mesh } from "@babylonjs/core/Meshes/mesh"; +import { Scene } from "@babylonjs/core/scene"; +import "@babylonjs/core/Helpers/sceneHelpers"; +import "@babylonjs/core/Loading/loadingScreen"; +import "@babylonjs/loaders/glTF"; + +const rootUrl = "https://www.babylonjs.com/Assets/glTFMeshPrimitiveAttributeTest/"; + +export async function createGltfMeshPrimitiveAttributeTestScene( + engine: Engine, + canvas: HTMLCanvasElement +): Promise { + const scene = new Scene(engine); + const hdrTexture = CubeTexture.CreateFromPrefilteredData("https://www.babylonjs.com/Assets/environment.dds", scene); + hdrTexture.gammaSpace = false; + scene.createDefaultSkybox(hdrTexture, true, 100, 0.3); + + const camera = new ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2, 5, Vector3.Zero(), scene); + camera.wheelPrecision = 100; + camera.attachControl(canvas, true); + + const title = createLabel(scene, "Generated Normal Attribute"); + title.position = new Vector3(0, 1.7, 0); + + await Promise.all([ + loadModel(scene, "NoNormalsBottom.gltf", new Vector3(-1.5, 0.7, 0), "Bottom Primitive"), + loadModel(scene, "NoNormalsMiddle.gltf", new Vector3(0, 0.7, 0), "Middle Primitive"), + loadModel(scene, "NoNormalsTop.gltf", new Vector3(1.5, 0.7, 0), "Top Primitive"), + ]); + + return scene; +} + +async function loadModel(scene: Scene, name: string, center: Vector3, caption: string): Promise { + const result = await ImportMeshAsync(name, scene, { rootUrl }); + const root = new Mesh(`${name} root`, scene); + + result.meshes.forEach((mesh) => { + if (!mesh.parent) { + mesh.setParent(root); + } + }); + + root.position = center; + root.rotation = new Vector3(0, Math.PI, 0); + + const label = createLabel(scene, caption); + label.position = center.clone(); + label.position.y -= 2; +} + +function createLabel(scene: Scene, text: string): Mesh { + const dynamicTexture = new DynamicTexture("DynamicTexture", 512, scene, true); + dynamicTexture.hasAlpha = true; + dynamicTexture.drawText(text, null, null, "36px Arial", "white", "transparent"); + + const plane = CreatePlane("TextPlane", { size: 2 }, scene); + const material = new StandardMaterial("TextPlaneMaterial", scene); + material.backFaceCulling = false; + material.specularColor = Color3.Black(); + material.diffuseTexture = dynamicTexture; + material.useAlphaFromDiffuseTexture = true; + plane.material = material; + + return plane; +} diff --git a/src/compiledDemos/GLTFNormals/index.html b/src/compiledDemos/GLTFNormals/index.html new file mode 100644 index 000000000..4a177171d --- /dev/null +++ b/src/compiledDemos/GLTFNormals/index.html @@ -0,0 +1,105 @@ + + + + + + Babylon.js - Normals Test + + + + +
+ + +
+ Source +
+
+ + + + diff --git a/src/compiledDemos/GLTFNormals/main.ts b/src/compiledDemos/GLTFNormals/main.ts new file mode 100644 index 000000000..8c81176e0 --- /dev/null +++ b/src/compiledDemos/GLTFNormals/main.ts @@ -0,0 +1,6 @@ +import { runDemo } from "../shared/demoRunner"; +import { createGltfNormalsScene } from "./scene"; + +runDemo({ + createScene: createGltfNormalsScene, +}); diff --git a/src/compiledDemos/GLTFNormals/scene.ts b/src/compiledDemos/GLTFNormals/scene.ts new file mode 100644 index 000000000..11acd58e0 --- /dev/null +++ b/src/compiledDemos/GLTFNormals/scene.ts @@ -0,0 +1,74 @@ +import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera"; +import type { Engine } from "@babylonjs/core/Engines/engine"; +import { DirectionalLight } from "@babylonjs/core/Lights/directionalLight"; +import { ImportMeshAsync } from "@babylonjs/core/Loading/sceneLoader"; +import { Color3 } from "@babylonjs/core/Maths/math.color"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { PBRMaterial } from "@babylonjs/core/Materials/PBR/pbrMaterial"; +import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial"; +import { DynamicTexture } from "@babylonjs/core/Materials/Textures/dynamicTexture"; +import { CreatePlane } from "@babylonjs/core/Meshes/Builders/planeBuilder"; +import { CreateSphere } from "@babylonjs/core/Meshes/Builders/sphereBuilder"; +import { Mesh } from "@babylonjs/core/Meshes/mesh"; +import { Scene } from "@babylonjs/core/scene"; +import "@babylonjs/core/Loading/loadingScreen"; +import "@babylonjs/loaders/glTF"; + +const rootUrl = "https://www.babylonjs.com/Assets/TestCube/"; + +export async function createGltfNormalsScene(engine: Engine, canvas: HTMLCanvasElement): Promise { + const scene = new Scene(engine); + new DirectionalLight("light", new Vector3(1, -1, 1), scene); + + const sphere = CreateSphere("sphere", { segments: 16, diameter: 0.5 }, scene); + const sphereMaterial = new PBRMaterial("sphereMaterial", scene); + sphere.position.y = 1; + sphereMaterial.metallic = 0; + sphere.material = sphereMaterial; + + const camera = new ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2, 6, Vector3.Zero(), scene); + camera.wheelPrecision = 100; + camera.attachControl(canvas, true); + + await Promise.all([ + loadModel(scene, "TestCube1.gltf", new Vector3(-2, -0.5, 0), "Normals + Tangents"), + loadModel(scene, "TestCube2.gltf", new Vector3(0, -0.5, 0), "Normals Only"), + loadModel(scene, "TestCube3.gltf", new Vector3(2, -0.5, 0), "No Normals/Tangents"), + ]); + + return scene; +} + +async function loadModel(scene: Scene, name: string, center: Vector3, caption: string): Promise { + const result = await ImportMeshAsync(name, scene, { rootUrl }); + const root = new Mesh(`${name} root`, scene); + + result.meshes.forEach((mesh) => { + if (!mesh.parent) { + mesh.setParent(root); + } + }); + + root.position = center; + root.rotation = new Vector3(Math.PI / 4, Math.PI / 4, 0); + + const label = createLabel(scene, caption); + label.position = center.clone(); + label.position.y -= 1; +} + +function createLabel(scene: Scene, text: string): Mesh { + const dynamicTexture = new DynamicTexture("DynamicTexture", 512, scene, true); + dynamicTexture.hasAlpha = true; + dynamicTexture.drawText(text, null, null, "36px Arial", "white", "transparent"); + + const plane = CreatePlane("TextPlane", { size: 2 }, scene); + const material = new StandardMaterial("TextPlaneMaterial", scene); + material.backFaceCulling = false; + material.specularColor = Color3.Black(); + material.diffuseTexture = dynamicTexture; + material.useAlphaFromDiffuseTexture = true; + plane.material = material; + + return plane; +} diff --git a/src/compiledDemos/manifest.json b/src/compiledDemos/manifest.json index 857f73417..2801bd4ca 100644 --- a/src/compiledDemos/manifest.json +++ b/src/compiledDemos/manifest.json @@ -1,5 +1,15 @@ { "demos": [ + { + "slug": "AssetsManager", + "title": "Babylon.js - Assets manager demo", + "legacyPath": "static/Demos/AssetsManager", + "sourceFiles": ["main.ts", "scene.ts"], + "renderCheck": { + "timeoutMs": 20000, + "minimumColoredSamples": 80 + } + }, { "slug": "Boom", "title": "Babylon.js - Boom demo", @@ -67,6 +77,36 @@ "minimumColoredSamples": 120 } }, + { + "slug": "GLTF", + "title": "Babylon.js - GLTF demo", + "legacyPath": "static/Demos/GLTF", + "sourceFiles": ["main.ts", "scene.ts"], + "renderCheck": { + "timeoutMs": 60000, + "minimumColoredSamples": 160 + } + }, + { + "slug": "GLTFMeshPrimitiveAttributeTest", + "title": "Babylon.js - Mesh primitive attribute test", + "legacyPath": "static/Demos/GLTFMeshPrimitiveAttributeTest", + "sourceFiles": ["main.ts", "scene.ts"], + "renderCheck": { + "timeoutMs": 30000, + "minimumColoredSamples": 100 + } + }, + { + "slug": "GLTFNormals", + "title": "Babylon.js - Normals Test", + "legacyPath": "static/Demos/GLTFNormals", + "sourceFiles": ["main.ts", "scene.ts"], + "renderCheck": { + "timeoutMs": 30000, + "minimumColoredSamples": 100 + } + }, { "slug": "Heightmap", "title": "Babylon.js - Heightmap demo", From 9bcd3d87684d4fa1f34e2b0048e28cf41e7b79a0 Mon Sep 17 00:00:00 2001 From: Raanan Weber Date: Wed, 6 May 2026 18:29:24 +0200 Subject: [PATCH 09/12] Port FlightHelmet and glTF1 CesiumMan demos --- docs/compiled-demo-migration.md | 4 +- docs/compiled-demos.md | 8 ++ src/compiledDemos/FlightHelmet/index.html | 113 ++++++++++++++++++++ src/compiledDemos/FlightHelmet/main.ts | 6 ++ src/compiledDemos/FlightHelmet/scene.ts | 69 ++++++++++++ src/compiledDemos/GLTF1CesiumMan/index.html | 105 ++++++++++++++++++ src/compiledDemos/GLTF1CesiumMan/main.ts | 6 ++ src/compiledDemos/GLTF1CesiumMan/scene.ts | 37 +++++++ src/compiledDemos/manifest.json | 20 ++++ 9 files changed, 367 insertions(+), 1 deletion(-) create mode 100644 src/compiledDemos/FlightHelmet/index.html create mode 100644 src/compiledDemos/FlightHelmet/main.ts create mode 100644 src/compiledDemos/FlightHelmet/scene.ts create mode 100644 src/compiledDemos/GLTF1CesiumMan/index.html create mode 100644 src/compiledDemos/GLTF1CesiumMan/main.ts create mode 100644 src/compiledDemos/GLTF1CesiumMan/scene.ts diff --git a/docs/compiled-demo-migration.md b/docs/compiled-demo-migration.md index b95c45c2f..651e6ceb7 100644 --- a/docs/compiled-demo-migration.md +++ b/docs/compiled-demo-migration.md @@ -20,7 +20,9 @@ This tracks the move from legacy static demos under `static/Demos/` to compiled | Offscreen | done | Custom worker bootstrap; render check validates both canvases. | | Fog | done | Animated simple geometry and exponential fog. | | Bump | done | Simple texture material using an existing shared normal map asset. | +| FlightHelmet | done | Remote glTF asset, default environment, shadows, and scene optimizer coverage. | | GLTF | done | glTF binary asset loading from the deployed legacy `/assets/` path. | +| GLTF1CesiumMan | done | Remote glTF 1.0 asset loading with shared prefiltered DDS environment texture. | | GLTFMeshPrimitiveAttributeTest | done | Multiple remote glTF assets plus generated normal attribute coverage. | | GLTFNormals | done | Multiple remote glTF assets covering normals/tangents variants. | | Lights | done | Multiple dynamic lights and shared skybox assets. | @@ -34,7 +36,7 @@ This tracks the move from legacy static demos under `static/Demos/` to compiled | Wave | Demos | Main Risk To Prove | | --- | --- | --- | -| Asset and loader follow-up | FlightHelmet, GLTF1CesiumMan, Mansion, Sponza, TheCar, Yeti | Larger model load times, glTF v1 compatibility, environment assets, shadows, and optimizer behavior. | +| Asset and loader follow-up | Mansion, Sponza, TheCar, Yeti | Larger model load times, local scene payloads, animation/UI dependencies, service workers, shadows, and optimizer behavior. | | Render/effects follow-up | Ribbons, Polygon | Ribbons needs procedural texture and volumetric-light-scattering post-process coverage. Polygon needs an explicit `earcut` dependency decision before porting. | | Materials and render pipeline demos | CellShading, FireMaterial, Fur, GlowLayer, PBR, PBRGlossy, PBRGlossyBloom, PBRRough, WaterMaterial, DOF, DefaultRenderingPipeline, MotionBlur, PPBloom, PPConvolution, PPRef, SSAO, SSAO2, StandardRenderingPipeline, VolumetricLightScattering | Extra Babylon packages, shader/material side effects, post-process scene components. | | Interaction and tooling demos | ActionBuilder, Actions, Charting, CustomShader, Decals, DragNDrop, Facets, Highlights, Lens, LookAt, Octree, Procedural, Simplification, VertexData, Views | Picking, pointer events, custom shaders, scene tools, and user interaction checks. | diff --git a/docs/compiled-demos.md b/docs/compiled-demos.md index 9e64402f6..505de2289 100644 --- a/docs/compiled-demos.md +++ b/docs/compiled-demos.md @@ -46,6 +46,10 @@ src/compiledDemos/ index.html main.ts scene.ts + FlightHelmet/ + index.html + main.ts + scene.ts Fresnel/ index.html main.ts @@ -54,6 +58,10 @@ src/compiledDemos/ index.html main.ts scene.ts + GLTF1CesiumMan/ + index.html + main.ts + scene.ts GLTFMeshPrimitiveAttributeTest/ index.html main.ts diff --git a/src/compiledDemos/FlightHelmet/index.html b/src/compiledDemos/FlightHelmet/index.html new file mode 100644 index 000000000..cbce408af --- /dev/null +++ b/src/compiledDemos/FlightHelmet/index.html @@ -0,0 +1,113 @@ + + + + + + Babylon.js - Flight Helmet demo + + + + +
+ + +
+ Source +
+
Flight Helmet by Patrick Ryan
+
+ + + + diff --git a/src/compiledDemos/FlightHelmet/main.ts b/src/compiledDemos/FlightHelmet/main.ts new file mode 100644 index 000000000..8aac899e9 --- /dev/null +++ b/src/compiledDemos/FlightHelmet/main.ts @@ -0,0 +1,6 @@ +import { runDemo } from "../shared/demoRunner"; +import { createFlightHelmetScene } from "./scene"; + +runDemo({ + createScene: createFlightHelmetScene, +}); diff --git a/src/compiledDemos/FlightHelmet/scene.ts b/src/compiledDemos/FlightHelmet/scene.ts new file mode 100644 index 000000000..f979c5c9f --- /dev/null +++ b/src/compiledDemos/FlightHelmet/scene.ts @@ -0,0 +1,69 @@ +import type { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera"; +import type { Engine } from "@babylonjs/core/Engines/engine"; +import { DirectionalLight } from "@babylonjs/core/Lights/directionalLight"; +import { ShadowGenerator } from "@babylonjs/core/Lights/Shadows/shadowGenerator"; +import { AppendSceneAsync } from "@babylonjs/core/Loading/sceneLoader"; +import { Color3, Color4 } from "@babylonjs/core/Maths/math.color"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { RenderTargetTexture } from "@babylonjs/core/Materials/Textures/renderTargetTexture"; +import { + HardwareScalingOptimization, + SceneOptimizer, + SceneOptimizerOptions, +} from "@babylonjs/core/Misc/sceneOptimizer"; +import { Scene } from "@babylonjs/core/scene"; +import "@babylonjs/core/Helpers/sceneHelpers"; +import "@babylonjs/core/Lights/Shadows/shadowGeneratorSceneComponent"; +import "@babylonjs/core/Loading/loadingScreen"; +import "@babylonjs/loaders/glTF"; + +export async function createFlightHelmetScene(engine: Engine): Promise { + const scene = new Scene(engine); + scene.clearColor = new Color4(0.02, 0.02, 0.02, 1); + scene.imageProcessingConfiguration.contrast = 1.6; + scene.imageProcessingConfiguration.exposure = 0.6; + scene.imageProcessingConfiguration.toneMappingEnabled = true; + engine.setHardwareScalingLevel(0.5); + + await AppendSceneAsync("FlightHelmet_Materials.gltf", scene, { + rootUrl: "https://www.babylonjs.com/Assets/FlightHelmet/glTF/", + }); + + scene.activeCamera = null; + scene.createDefaultCameraOrLight(true, true, true); + + const camera = scene.activeCamera as ArcRotateCamera | null; + if (camera) { + camera.lowerRadiusLimit = 20; + camera.upperRadiusLimit = 80; + camera.alpha = 2.5; + camera.beta = 1.5; + camera.useAutoRotationBehavior = true; + } + + const light = new DirectionalLight("light1", new Vector3(2, -3, -1), scene); + light.position = new Vector3(-20, 20, 6); + + const generator = new ShadowGenerator(512, light); + generator.useBlurExponentialShadowMap = true; + generator.blurKernel = 32; + const shadowMap = generator.getShadowMap(); + if (shadowMap) { + shadowMap.refreshRate = RenderTargetTexture.REFRESHRATE_RENDER_ONCE; + } + + scene.meshes.forEach((mesh) => { + generator.addShadowCaster(mesh); + }); + + const helper = scene.createDefaultEnvironment({ + groundShadowLevel: 0.6, + }); + helper?.setMainColor(new Color3(0.42, 0.41, 0.33)); + + const options = new SceneOptimizerOptions(50, 2000); + options.addOptimization(new HardwareScalingOptimization(0, 1)); + new SceneOptimizer(scene, options).start(); + + return scene; +} diff --git a/src/compiledDemos/GLTF1CesiumMan/index.html b/src/compiledDemos/GLTF1CesiumMan/index.html new file mode 100644 index 000000000..3f8728d5c --- /dev/null +++ b/src/compiledDemos/GLTF1CesiumMan/index.html @@ -0,0 +1,105 @@ + + + + + + Babylon.js - glTF 1.0 Cesium Man demo + + + + +
+ + +
+ Source +
+
+ + + + diff --git a/src/compiledDemos/GLTF1CesiumMan/main.ts b/src/compiledDemos/GLTF1CesiumMan/main.ts new file mode 100644 index 000000000..dd7d55507 --- /dev/null +++ b/src/compiledDemos/GLTF1CesiumMan/main.ts @@ -0,0 +1,6 @@ +import { runDemo } from "../shared/demoRunner"; +import { createGltf1CesiumManScene } from "./scene"; + +runDemo({ + createScene: createGltf1CesiumManScene, +}); diff --git a/src/compiledDemos/GLTF1CesiumMan/scene.ts b/src/compiledDemos/GLTF1CesiumMan/scene.ts new file mode 100644 index 000000000..4733c1a2f --- /dev/null +++ b/src/compiledDemos/GLTF1CesiumMan/scene.ts @@ -0,0 +1,37 @@ +import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera"; +import type { Engine } from "@babylonjs/core/Engines/engine"; +import { ImportMeshAsync } from "@babylonjs/core/Loading/sceneLoader"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { CubeTexture } from "@babylonjs/core/Materials/Textures/cubeTexture"; +import { Mesh } from "@babylonjs/core/Meshes/mesh"; +import { Scene } from "@babylonjs/core/scene"; +import "@babylonjs/core/Helpers/sceneHelpers"; +import "@babylonjs/core/Loading/loadingScreen"; +import "@babylonjs/loaders/glTF"; + +export async function createGltf1CesiumManScene(engine: Engine, canvas: HTMLCanvasElement): Promise { + const scene = new Scene(engine); + scene.useRightHandedSystem = true; + + const hdrTexture = CubeTexture.CreateFromPrefilteredData("https://www.babylonjs.com/Assets/environment.dds", scene); + hdrTexture.gammaSpace = false; + scene.createDefaultSkybox(hdrTexture, true, 100, 0.3); + + const camera = new ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2, 3, new Vector3(0, 0.7, 0.8), scene); + camera.wheelPrecision = 100; + camera.attachControl(canvas, true); + + const result = await ImportMeshAsync("CesiumMan.gltf", scene, { + rootUrl: "https://www.babylonjs.com/Assets/glTF1CesiumMan/", + }); + const root = new Mesh("root", scene); + + result.meshes.forEach((mesh) => { + if (!mesh.parent) { + mesh.setParent(root); + } + }); + root.rotation = new Vector3(0, Math.PI, 0); + + return scene; +} diff --git a/src/compiledDemos/manifest.json b/src/compiledDemos/manifest.json index 2801bd4ca..e436d0b10 100644 --- a/src/compiledDemos/manifest.json +++ b/src/compiledDemos/manifest.json @@ -47,6 +47,16 @@ "minimumColoredSamples": 120 } }, + { + "slug": "FlightHelmet", + "title": "Babylon.js - Flight Helmet demo", + "legacyPath": "static/Demos/FlightHelmet", + "sourceFiles": ["main.ts", "scene.ts"], + "renderCheck": { + "timeoutMs": 60000, + "minimumColoredSamples": 160 + } + }, { "slug": "Fresnel", "title": "Babylon.js - Fresnel demo", @@ -87,6 +97,16 @@ "minimumColoredSamples": 160 } }, + { + "slug": "GLTF1CesiumMan", + "title": "Babylon.js - glTF 1.0 Cesium Man demo", + "legacyPath": "static/Demos/GLTF1CesiumMan", + "sourceFiles": ["main.ts", "scene.ts"], + "renderCheck": { + "timeoutMs": 45000, + "minimumColoredSamples": 100 + } + }, { "slug": "GLTFMeshPrimitiveAttributeTest", "title": "Babylon.js - Mesh primitive attribute test", From 541e04dc72496b892201d0164132e1d9332afc75 Mon Sep 17 00:00:00 2001 From: Raanan Weber Date: Wed, 6 May 2026 18:39:08 +0200 Subject: [PATCH 10/12] Replace unusable glTF1 demo with Yeti --- docs/compiled-demo-migration.md | 6 +- docs/compiled-demos.md | 8 +- src/compiledDemos/GLTF1CesiumMan/main.ts | 6 -- src/compiledDemos/GLTF1CesiumMan/scene.ts | 37 -------- .../{GLTF1CesiumMan => Yeti}/index.html | 12 ++- src/compiledDemos/Yeti/main.ts | 6 ++ src/compiledDemos/Yeti/scene.ts | 90 +++++++++++++++++++ src/compiledDemos/manifest.json | 20 ++--- 8 files changed, 124 insertions(+), 61 deletions(-) delete mode 100644 src/compiledDemos/GLTF1CesiumMan/main.ts delete mode 100644 src/compiledDemos/GLTF1CesiumMan/scene.ts rename src/compiledDemos/{GLTF1CesiumMan => Yeti}/index.html (90%) create mode 100644 src/compiledDemos/Yeti/main.ts create mode 100644 src/compiledDemos/Yeti/scene.ts diff --git a/docs/compiled-demo-migration.md b/docs/compiled-demo-migration.md index 651e6ceb7..3b3c6320d 100644 --- a/docs/compiled-demo-migration.md +++ b/docs/compiled-demo-migration.md @@ -5,6 +5,7 @@ This tracks the move from legacy static demos under `static/Demos/` to compiled ## Status Key - `done`: Ported to `src/compiledDemos`, registered in `manifest.json`, and covered by render checks. +- `unusable`: The legacy/online demo no longer renders correctly and should not be ported until the source demo is repaired or replaced. - `wave-1`: Simple single-canvas demo selected for the current migration wave. - `candidate`: Not migrated yet, likely suitable for a later wave after inspection. - `special`: Needs extra handling such as workers, vendored bundles, service workers, generated app bundles, WebGPU, physics, GUI, or complex external assets. @@ -22,7 +23,7 @@ This tracks the move from legacy static demos under `static/Demos/` to compiled | Bump | done | Simple texture material using an existing shared normal map asset. | | FlightHelmet | done | Remote glTF asset, default environment, shadows, and scene optimizer coverage. | | GLTF | done | glTF binary asset loading from the deployed legacy `/assets/` path. | -| GLTF1CesiumMan | done | Remote glTF 1.0 asset loading with shared prefiltered DDS environment texture. | +| GLTF1CesiumMan | unusable | Legacy and online demo do not render; excluded from the compiled registry until the source demo is repaired or replaced. | | GLTFMeshPrimitiveAttributeTest | done | Multiple remote glTF assets plus generated normal attribute coverage. | | GLTFNormals | done | Multiple remote glTF assets covering normals/tangents variants. | | Lights | done | Multiple dynamic lights and shared skybox assets. | @@ -31,12 +32,13 @@ This tracks the move from legacy static demos under `static/Demos/` to compiled | Shadows | done | Directional lights, shadow generators, and legacy grass texture. | | PointLightShadowMap | done | Point light shadow generator with torus knot scene. | | Refraction | done | Reflection probe refraction with built-in bump/fresnel/IOR controls. | +| Yeti | done | Remote animated glTF asset with loader animation options, default environment, and textured snow particles. | ## Next Waves | Wave | Demos | Main Risk To Prove | | --- | --- | --- | -| Asset and loader follow-up | Mansion, Sponza, TheCar, Yeti | Larger model load times, local scene payloads, animation/UI dependencies, service workers, shadows, and optimizer behavior. | +| Asset and loader follow-up | Mansion, Sponza, TheCar | Larger model load times, local scene payloads, animation/UI dependencies, service workers, shadows, and optimizer behavior. | | Render/effects follow-up | Ribbons, Polygon | Ribbons needs procedural texture and volumetric-light-scattering post-process coverage. Polygon needs an explicit `earcut` dependency decision before porting. | | Materials and render pipeline demos | CellShading, FireMaterial, Fur, GlowLayer, PBR, PBRGlossy, PBRGlossyBloom, PBRRough, WaterMaterial, DOF, DefaultRenderingPipeline, MotionBlur, PPBloom, PPConvolution, PPRef, SSAO, SSAO2, StandardRenderingPipeline, VolumetricLightScattering | Extra Babylon packages, shader/material side effects, post-process scene components. | | Interaction and tooling demos | ActionBuilder, Actions, Charting, CustomShader, Decals, DragNDrop, Facets, Highlights, Lens, LookAt, Octree, Procedural, Simplification, VertexData, Views | Picking, pointer events, custom shaders, scene tools, and user interaction checks. | diff --git a/docs/compiled-demos.md b/docs/compiled-demos.md index 505de2289..84f6fc772 100644 --- a/docs/compiled-demos.md +++ b/docs/compiled-demos.md @@ -58,10 +58,6 @@ src/compiledDemos/ index.html main.ts scene.ts - GLTF1CesiumMan/ - index.html - main.ts - scene.ts GLTFMeshPrimitiveAttributeTest/ index.html main.ts @@ -78,6 +74,10 @@ src/compiledDemos/ index.html main.ts scene.ts + Yeti/ + index.html + main.ts + scene.ts Heightmap/ index.html main.ts diff --git a/src/compiledDemos/GLTF1CesiumMan/main.ts b/src/compiledDemos/GLTF1CesiumMan/main.ts deleted file mode 100644 index dd7d55507..000000000 --- a/src/compiledDemos/GLTF1CesiumMan/main.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { runDemo } from "../shared/demoRunner"; -import { createGltf1CesiumManScene } from "./scene"; - -runDemo({ - createScene: createGltf1CesiumManScene, -}); diff --git a/src/compiledDemos/GLTF1CesiumMan/scene.ts b/src/compiledDemos/GLTF1CesiumMan/scene.ts deleted file mode 100644 index 4733c1a2f..000000000 --- a/src/compiledDemos/GLTF1CesiumMan/scene.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera"; -import type { Engine } from "@babylonjs/core/Engines/engine"; -import { ImportMeshAsync } from "@babylonjs/core/Loading/sceneLoader"; -import { Vector3 } from "@babylonjs/core/Maths/math.vector"; -import { CubeTexture } from "@babylonjs/core/Materials/Textures/cubeTexture"; -import { Mesh } from "@babylonjs/core/Meshes/mesh"; -import { Scene } from "@babylonjs/core/scene"; -import "@babylonjs/core/Helpers/sceneHelpers"; -import "@babylonjs/core/Loading/loadingScreen"; -import "@babylonjs/loaders/glTF"; - -export async function createGltf1CesiumManScene(engine: Engine, canvas: HTMLCanvasElement): Promise { - const scene = new Scene(engine); - scene.useRightHandedSystem = true; - - const hdrTexture = CubeTexture.CreateFromPrefilteredData("https://www.babylonjs.com/Assets/environment.dds", scene); - hdrTexture.gammaSpace = false; - scene.createDefaultSkybox(hdrTexture, true, 100, 0.3); - - const camera = new ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2, 3, new Vector3(0, 0.7, 0.8), scene); - camera.wheelPrecision = 100; - camera.attachControl(canvas, true); - - const result = await ImportMeshAsync("CesiumMan.gltf", scene, { - rootUrl: "https://www.babylonjs.com/Assets/glTF1CesiumMan/", - }); - const root = new Mesh("root", scene); - - result.meshes.forEach((mesh) => { - if (!mesh.parent) { - mesh.setParent(root); - } - }); - root.rotation = new Vector3(0, Math.PI, 0); - - return scene; -} diff --git a/src/compiledDemos/GLTF1CesiumMan/index.html b/src/compiledDemos/Yeti/index.html similarity index 90% rename from src/compiledDemos/GLTF1CesiumMan/index.html rename to src/compiledDemos/Yeti/index.html index 3f8728d5c..eba8a71e5 100644 --- a/src/compiledDemos/GLTF1CesiumMan/index.html +++ b/src/compiledDemos/Yeti/index.html @@ -3,7 +3,7 @@ - Babylon.js - glTF 1.0 Cesium Man demo + Babylon.js - Yeti demo + + + +
+ + +
+ Source +
+
+ + + + diff --git a/src/compiledDemos/VolumetricLightScattering/main.ts b/src/compiledDemos/VolumetricLightScattering/main.ts new file mode 100644 index 000000000..d188c147d --- /dev/null +++ b/src/compiledDemos/VolumetricLightScattering/main.ts @@ -0,0 +1,6 @@ +import { runDemo } from "../shared/demoRunner"; +import { createVolumetricLightScatteringScene } from "./scene"; + +runDemo({ + createScene: createVolumetricLightScatteringScene, +}); diff --git a/src/compiledDemos/VolumetricLightScattering/scene.ts b/src/compiledDemos/VolumetricLightScattering/scene.ts new file mode 100644 index 000000000..d71586f59 --- /dev/null +++ b/src/compiledDemos/VolumetricLightScattering/scene.ts @@ -0,0 +1,62 @@ +import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera"; +import type { Engine } from "@babylonjs/core/Engines/engine"; +import { PointLight } from "@babylonjs/core/Lights/pointLight"; +import { ImportMeshAsync } from "@babylonjs/core/Loading/sceneLoader"; +import { Color3 } from "@babylonjs/core/Maths/math.color"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial"; +import { Texture } from "@babylonjs/core/Materials/Textures/texture"; +import { VolumetricLightScatteringPostProcess } from "@babylonjs/core/PostProcesses/volumetricLightScatteringPostProcess"; +import { Scene } from "@babylonjs/core/scene"; +import "@babylonjs/core/Loading/Plugins/babylonFileLoader"; + +export async function createVolumetricLightScatteringScene(engine: Engine, canvas: HTMLCanvasElement): Promise { + const scene = new Scene(engine); + scene.clearColor.set(0.02, 0.02, 0.08, 1); + + const light = new PointLight("Omni", new Vector3(20, 20, 100), scene); + const camera = new ArcRotateCamera("Camera", -0.5, 2.2, 100, Vector3.Zero(), scene); + camera.attachControl(canvas, true); + + const result = await ImportMeshAsync("skull.babylon", scene, { + rootUrl: "/Scenes/Assets/", + meshNames: "", + }); + + const skull = result.meshes[0]; + if (skull) { + camera.target = skull.position; + const material = new StandardMaterial("skull", scene); + material.emissiveColor = new Color3(0.2, 0.2, 0.2); + skull.material = material; + } + + const godRays = new VolumetricLightScatteringPostProcess( + "godrays", + 1, + camera, + null, + 100, + Texture.BILINEAR_SAMPLINGMODE, + engine, + false + ); + + if (godRays.mesh.material instanceof StandardMaterial) { + const sunTexture = new Texture("/Scenes/Assets/sun.png", scene, true, false, Texture.BILINEAR_SAMPLINGMODE); + sunTexture.hasAlpha = true; + godRays.mesh.material.diffuseTexture = sunTexture; + } + + godRays.mesh.position = new Vector3(-150, 150, 150); + godRays.mesh.scaling = new Vector3(350, 350, 350); + light.position = godRays.mesh.position; + + scene.registerBeforeRender(() => { + if (skull) { + skull.rotation.y += 0.002 * scene.getAnimationRatio(); + } + }); + + return scene; +} diff --git a/src/compiledDemos/manifest.json b/src/compiledDemos/manifest.json index 4e48dd0f2..afb931a3a 100644 --- a/src/compiledDemos/manifest.json +++ b/src/compiledDemos/manifest.json @@ -179,6 +179,16 @@ "minimumColoredSamples": 120 } }, + { + "slug": "VolumetricLightScattering", + "title": "Babylon.js - Volumetric Light Scattering demo", + "legacyPath": "static/Demos/VolumetricLightScattering", + "sourceFiles": ["main.ts", "scene.ts"], + "renderCheck": { + "timeoutMs": 20000, + "minimumColoredSamples": 120 + } + }, { "slug": "Yeti", "title": "Babylon.js - Yeti demo",