diff --git a/video/.gitignore b/video/.gitignore new file mode 100644 index 00000000..20873b10 --- /dev/null +++ b/video/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +out/ +.tmp-narration/ +.piper-voices/ +*.log +.DS_Store diff --git a/video/README.md b/video/README.md new file mode 100644 index 00000000..9c0ce288 --- /dev/null +++ b/video/README.md @@ -0,0 +1,191 @@ +# SimpleModule Showcase Videos + +Remotion videos that showcase the SimpleModule framework. + +- **`SimpleModule`** — ~39 s master reel covering compile-time module + discovery, `IEndpoint` auto-registration, `MapCrud<>`, Inertia, and the + event bus. Cross-fade + slide transitions connect the scenes. +- **`Module-`** — 14 s per-module spotlights, one for each core + module (Users, Permissions, Settings, Admin, Dashboard, AuditLogs, + FileStorage, Email, BackgroundJobs, FeatureFlags). Each one shows the + module's tagline, three features, and a `SimpleModule.` callout. + +All videos ship with a Scott Buckley background track and Piper-generated +narration (see **Audio** below). + +## Prerequisites + +- Node.js 20+ +- Linux/macOS/Windows with the system libs Chromium Headless Shell needs + (on Ubuntu/Debian these are: `libnss3 libgbm1 libasound2t64 + libatk-bridge2.0-0t64 libatk1.0-0t64 libdrm2 libxkbcommon0 libxcomposite1 + libxdamage1 libxfixes3 libxrandr2`). The `@remotion/renderer` package + bundles its own ffmpeg, so the system `ffmpeg` binary is not required. + +## Install + +```bash +cd video +npm install +``` + +## Audio + +Every video ships with two audio layers: + +- **Background music** at `public/background.mp3` — a 42 s clip of + "I Walk With Ghosts" by **Scott Buckley** (CC-BY 4.0), trimmed from + 0:30. Attribution required if you redistribute the video — + . +- **Narration** — generated by **Piper** (a local neural TTS) using the + `en_US-ryan-high` voice. `public/narration/s1.mp3 … s9.mp3` drives the + main showcase; `public/narration/modules/.mp3` drives each module + spotlight. + +### Regenerating narration + +Narration lines live next to the data they describe: + +- Main reel copy — in `scripts/generate-narration.mjs` (top of the file). +- Module copy — in `src/data/moduleShowcases.ts` (the `narration` field). + +After editing either, run: + +```bash +npm run narrate +``` + +The script speaks each line with Piper, re-encodes to MP3, and writes a +`manifest.json` with durations so you can sanity-check that lines still +fit in their scenes. + +### Getting a Piper voice + +`npm run narrate` expects `.piper-voices/en_US-ryan-high.onnx` (+ `.json`). +Download both from +: + +```bash +mkdir -p .piper-voices +cd .piper-voices +curl -LO "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/ryan/high/en_US-ryan-high.onnx" +curl -LO "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/ryan/high/en_US-ryan-high.onnx.json" +``` + +The voices folder is `.gitignore`d. Swap to any Piper voice by updating +the path in `scripts/generate-narration.mjs`. Recommended alternatives: +`en_US-lessac-medium` (clear female), `en_GB-alan-medium` (British male), +`en_US-libritts_r-medium` (neutral narrator). + +### Swapping the background track + +Drop your own ≥40 s MP3 at `public/background.mp3`. The music is ducked +to 0.20–0.22 so narration stays intelligible; the fade-in/out envelope +lives in `src/Video.tsx` and `src/compositions/ModuleShowcase.tsx`. + +As a pure-synthesis fallback, `scripts/generate-audio.mjs` writes an +ambient Cmaj9 pad with no licensing concerns. + +### Silent renders + +```bash +REMOTION_DISABLE_AUDIO=1 npm run render # main only +REMOTION_DISABLE_AUDIO=1 npm run render:all # every video +``` + +### Loudness normalization + +Remotion just mixes audio sources verbatim — it doesn't compress or limit +— so a hot TTS track can push the mix over -5 LUFS (ear-splitting). Every +render pipeline that produces a deliverable ends with a loudnorm pass: + +```bash +npm run render:all # renders, then normalizes every mp4 to -14 LUFS +node scripts/normalize-loudness.mjs # normalize existing files in-place +``` + +Target: -14 LUFS integrated, -1.5 dBTP ceiling (matches Spotify/YouTube). + +## Develop + +```bash +npm run start +# opens the Remotion studio at http://localhost:3000 +# pick the "SimpleModule" composition +``` + +## Render + +First render downloads the Chrome Headless Shell (~170 MB) to +`~/.cache/remotion/`. On hosts running as root you must disable the +Chromium sandbox — the snippets below already set the flag: + +```bash +# pre-warm the Chrome download (recommended) +npx remotion browser ensure + +# render the main showcase +REMOTION_CHROME_FLAGS="--no-sandbox --disable-setuid-sandbox" npm run render +# output: out/simplemodule.mp4 + +# render the main showcase + every per-module spotlight +REMOTION_CHROME_FLAGS="--no-sandbox --disable-setuid-sandbox" npm run render:all +# outputs: out/simplemodule.mp4 + out/modules/.mp4 +``` + +If your host sits behind an SSL-intercepting proxy (e.g. some corporate +networks), Google Fonts may fail to load. The `remotion.config.ts` in +this project already sets `setChromiumIgnoreCertificateErrors(true)` so +the render still succeeds — fonts simply fall back to the system stack. + +## Structure + +``` +video/ +├── public/ # logos, background.mp3, narration/** +├── scripts/ # generate-audio.mjs, generate-narration.mjs, render-all.mjs +└── src/ + ├── Root.tsx # 1 main + 10 per-module s + ├── Video.tsx # main: TransitionSeries + audio layers + ├── compositions/ + │ └── ModuleShowcase.tsx # reusable 14-second per-module template + ├── scenes/ # storyboard scenes for the main reel + ├── components/ # Logo, CodeBlock, SplitCodePair, ModuleChip, ... + └── data/ + ├── modules.ts # core-module chip list + ├── techStack.ts # badges + stats + ├── codeSnippets.ts # code shown in main reel + └── moduleShowcases.ts # per-module data driving Module- comps +``` + +## Storyboards + +### Main reel (`SimpleModule`) + +| # | Scene | Duration | +|---|---|---| +| 1 | Opening logo + tagline | 3.2s | +| 2 | Problem → Solution | 4.7s | +| 3 | `[Module]` attribute + Roslyn generator | 5.3s | +| 4 | `IEndpoint` auto-discovery | 5.0s | +| 5 | `MapCrud<>` helper | 6.2s | +| 6 | Inertia.Render + IEventBus | 5.3s | +| 7 | 10 core module grid | 5.0s | +| 8 | Tech stack + stats | 5.8s | +| 9 | Call to action | 3.3s | + +Scenes are stitched with `@remotion/transitions` — alternating cross-fade +and slide (18 frames each), plus a bottom-slide into the module grid. + +### Per-module spotlight (`Module-`) + +Same 14 s template for every core module, driven by +`src/data/moduleShowcases.ts`: + +| Phase | Frames | What's on screen | +|---|---|---| +| Intro | 0–110 | Accent halo, gradient module name, tagline | +| Features | 120–300 | Three bullets slide in (60 frames apart) | +| Outro | 320–420 | Logo + `SimpleModule.` callout | + +Each module gets its own accent colour and narration line. diff --git a/video/package-lock.json b/video/package-lock.json new file mode 100644 index 00000000..52367221 --- /dev/null +++ b/video/package-lock.json @@ -0,0 +1,2693 @@ +{ + "name": "simplemodule-video", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "simplemodule-video", + "version": "1.0.0", + "dependencies": { + "@remotion/bundler": "4.0.380", + "@remotion/cli": "4.0.380", + "@remotion/google-fonts": "4.0.380", + "@remotion/renderer": "4.0.380", + "@remotion/shapes": "4.0.380", + "@remotion/transitions": "^4.0.380", + "react": "19.0.0", + "react-dom": "19.0.0", + "remotion": "4.0.380" + }, + "devDependencies": { + "@types/node": "22.10.0", + "@types/react": "19.0.0", + "typescript": "5.6.3" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", + "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@remotion/bundler": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/bundler/-/bundler-4.0.380.tgz", + "integrity": "sha512-AQFgNDnA+Ej8/7KZyERHW84TRtBCfDT11GZgA5hHQhb9fEF3MnbQu6hmM0Z5haCiCB0FF33oVYWLeadjtdkgRA==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@remotion/media-parser": "4.0.380", + "@remotion/studio": "4.0.380", + "@remotion/studio-shared": "4.0.380", + "css-loader": "5.2.7", + "esbuild": "0.25.0", + "react-refresh": "0.9.0", + "remotion": "4.0.380", + "source-map": "0.7.3", + "style-loader": "4.0.0", + "webpack": "5.96.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/cli": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/cli/-/cli-4.0.380.tgz", + "integrity": "sha512-A4PxaEyN5Bu0uQuBgoHJ6Cx4kkNYJsizIyUEk4/8oxLcFXbULh2hef9qM/+o1QwIHd3wBeRstuf8iayHj+LdIA==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@remotion/bundler": "4.0.380", + "@remotion/media-utils": "4.0.380", + "@remotion/player": "4.0.380", + "@remotion/renderer": "4.0.380", + "@remotion/studio": "4.0.380", + "@remotion/studio-server": "4.0.380", + "@remotion/studio-shared": "4.0.380", + "dotenv": "9.0.2", + "minimist": "1.2.6", + "prompts": "2.4.2", + "remotion": "4.0.380" + }, + "bin": { + "remotion": "remotion-cli.js", + "remotionb": "remotionb-cli.js", + "remotiond": "remotiond-cli.js" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/compositor-darwin-arm64": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/compositor-darwin-arm64/-/compositor-darwin-arm64-4.0.380.tgz", + "integrity": "sha512-+R9ji58jAkEvJX7tOjw1IZ9UKjLx98IUyO7XaGcYN4YgPXXO6uo1BqAMn/Ql1mJTHJSmjWcvyJWyNQfVv2XsSQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@remotion/compositor-darwin-x64": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/compositor-darwin-x64/-/compositor-darwin-x64-4.0.380.tgz", + "integrity": "sha512-xyR1QhT5vebgbVE3gfkgxYcJYLfC2D8fm6yPaBFDEwZ0iVq5FTlra9oLgRAo+m3LFVDePLAFD/mqbWoO52i8xw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@remotion/compositor-linux-arm64-gnu": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/compositor-linux-arm64-gnu/-/compositor-linux-arm64-gnu-4.0.380.tgz", + "integrity": "sha512-nl7b5JVmtDTurBF9R9gbBqAn0V5RtMe2bsCtmdJHbYRMQzJ+7W1UCj78xOzwFq0NiH7lRNC5Hem3xCLz8lve+Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@remotion/compositor-linux-arm64-musl": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/compositor-linux-arm64-musl/-/compositor-linux-arm64-musl-4.0.380.tgz", + "integrity": "sha512-hlW29ZngfEudGLGQnzXSFmTU7dMV52E7oDiPFYwGz4ncxkz/qdHDpTSuXZfwl8RldxWq3gL4iEulxUl22SXGaA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@remotion/compositor-linux-x64-gnu": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/compositor-linux-x64-gnu/-/compositor-linux-x64-gnu-4.0.380.tgz", + "integrity": "sha512-2uNdI0iDgKAQz81LRlqHTypm0hVxvfiu2jU64i9Zc34g2iog386RYdqCv6q9VzQvEJa8tRfVRwFoqtmXcS7lCQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@remotion/compositor-linux-x64-musl": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/compositor-linux-x64-musl/-/compositor-linux-x64-musl-4.0.380.tgz", + "integrity": "sha512-e69jYIfAkp2NjJKnE2wGPdj3DDWbT5nQdiEHUn1mzcbex61pz26G9jFmdFPKOPNht6s8I9FZVmDKFUXCBDPpfA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@remotion/compositor-win32-x64-msvc": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/compositor-win32-x64-msvc/-/compositor-win32-x64-msvc-4.0.380.tgz", + "integrity": "sha512-3A2Gg2LC1oput/YDtBSW5uS4lZDycBq30LqEknzB+lnMRb5F8iSlNhuyUyDlfuk8EIb84HGYMFl57qiA6NqfCg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@remotion/google-fonts": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/google-fonts/-/google-fonts-4.0.380.tgz", + "integrity": "sha512-R4AElf9ICucomgGIBJMikF6HzQT31HhZyYayBtkBYwOPJ7dD8BUuUQyOlj3lgq6LaqbeYKqHbewVyuc7e1WtQg==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "remotion": "4.0.380" + } + }, + "node_modules/@remotion/licensing": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/licensing/-/licensing-4.0.380.tgz", + "integrity": "sha512-q3vhQcf3yZBmLuciUK8Imz5ycKeUoT240Az7vYIzB44wpmsYKtTO47WKxJ/xejJsyeaLa8YYE3d5EFbvx+X2+A==", + "license": "MIT" + }, + "node_modules/@remotion/media-parser": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/media-parser/-/media-parser-4.0.380.tgz", + "integrity": "sha512-gIp4bAFns9GRvEaz3n1zhPZkSWyHq2sh0edfj0ryk3HiSdpfX58XAdLbYy3CmiSUN4zvDSMLLTsGIG6ksamXHQ==", + "license": "Remotion License https://remotion.dev/license" + }, + "node_modules/@remotion/media-utils": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/media-utils/-/media-utils-4.0.380.tgz", + "integrity": "sha512-r0zJ8M8Ubw6KSVXdcvaa90F7e14UqohRRtnlWtrOXYpFJh+iFK1uLfX1TkoXdAZ9vGEXgyPIt/6X5paOq/JUFg==", + "license": "MIT", + "dependencies": { + "remotion": "4.0.380" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/paths": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/paths/-/paths-4.0.380.tgz", + "integrity": "sha512-kiSl/MPcbNr+3Uev/hJEFDZ8+6eyBTEJxE5zHE7OWX3u5DnduihxsDUpppJxcIk+hRmsblVUbPSIum0FCol16g==", + "license": "MIT" + }, + "node_modules/@remotion/player": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/player/-/player-4.0.380.tgz", + "integrity": "sha512-LWDQeHkU29qlGKu88CFn9RMXrLEQrduuH1094t7x/UpVw2M/ZW7pryagvbXhm3uKhkbFhgyNoFTcJ85mZN2Jfg==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "remotion": "4.0.380" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/renderer": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/renderer/-/renderer-4.0.380.tgz", + "integrity": "sha512-G0wdAahBPh7+eXux3/Uv+MUw6baeJSZiWrxu2JheYrhSnb3FmijpsC2z+3/xRWJ1NjtnBDVj/raFp8sRIhYX4Q==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@remotion/licensing": "4.0.380", + "@remotion/streaming": "4.0.380", + "execa": "5.1.1", + "extract-zip": "2.0.1", + "remotion": "4.0.380", + "source-map": "^0.8.0-beta.0", + "ws": "8.17.1" + }, + "optionalDependencies": { + "@remotion/compositor-darwin-arm64": "4.0.380", + "@remotion/compositor-darwin-x64": "4.0.380", + "@remotion/compositor-linux-arm64-gnu": "4.0.380", + "@remotion/compositor-linux-arm64-musl": "4.0.380", + "@remotion/compositor-linux-x64-gnu": "4.0.380", + "@remotion/compositor-linux-x64-musl": "4.0.380", + "@remotion/compositor-win32-x64-msvc": "4.0.380" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/renderer/node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "deprecated": "The work that was done in this beta branch won't be included in future versions", + "license": "BSD-3-Clause", + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@remotion/shapes": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/shapes/-/shapes-4.0.380.tgz", + "integrity": "sha512-V3aRWWZIdLyzl+JaGQHD+t04SnWniQ0W6dYQGJOosA6YxY0TPDApb3tikA+ZCQ8I+hw/LJBsaVuBZx3+DYNW9g==", + "license": "MIT", + "dependencies": { + "@remotion/paths": "4.0.380" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/streaming": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/streaming/-/streaming-4.0.380.tgz", + "integrity": "sha512-5Zd95Bv3LqHiVm7QfGTFznWJAEHwD5j+bPyhRJUgUfbCTuNemqeQXGQQt4TWwvXx5AS1+SlJxJPwkQ1nBMh68g==", + "license": "MIT" + }, + "node_modules/@remotion/studio": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/studio/-/studio-4.0.380.tgz", + "integrity": "sha512-BnxXu5vCIRpf17+wvQh6tsv50yoep2MpysonRnD/fqtd+y8D7kyxg5zBgTRcu+h01QtUKvge2ZfQS6EX1bbZCg==", + "license": "MIT", + "dependencies": { + "@remotion/media-utils": "4.0.380", + "@remotion/player": "4.0.380", + "@remotion/renderer": "4.0.380", + "@remotion/studio-shared": "4.0.380", + "@remotion/web-renderer": "4.0.380", + "@remotion/zod-types": "4.0.380", + "mediabunny": "1.24.5", + "memfs": "3.4.3", + "open": "^8.4.2", + "remotion": "4.0.380", + "semver": "7.5.3", + "source-map": "0.7.3", + "zod": "3.22.3" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/studio-server": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/studio-server/-/studio-server-4.0.380.tgz", + "integrity": "sha512-qODw46xCp2Mfr2ogzG1Ol1WxXLZFcdXlRMoOfvIMKen5EIdPFZ2igKWyVYaDkJSY9gDHPpEqqqaDUpt/ixh83A==", + "license": "MIT", + "dependencies": { + "@babel/parser": "7.24.1", + "@remotion/bundler": "4.0.380", + "@remotion/renderer": "4.0.380", + "@remotion/studio-shared": "4.0.380", + "memfs": "3.4.3", + "open": "^8.4.2", + "recast": "0.23.11", + "remotion": "4.0.380", + "semver": "7.5.3", + "source-map": "0.7.3" + } + }, + "node_modules/@remotion/studio-shared": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/studio-shared/-/studio-shared-4.0.380.tgz", + "integrity": "sha512-3IHtrQk25aP/uBm5W231+I/qqFJgxAKe02+BQOrTIZrBB+ssW4aAe8vicXvtuivmP3A04WEJR26Mr5Ow+GukpQ==", + "license": "MIT", + "dependencies": { + "remotion": "4.0.380" + } + }, + "node_modules/@remotion/transitions": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/transitions/-/transitions-4.0.380.tgz", + "integrity": "sha512-NR1Lhsh6m83eqrcpgvO1wKZ/Y+zExR3vnktUktwOsHqjNJ2yt6AfDm6v3fxIXwUsBlngmxho5XdWOZhlRNp4UQ==", + "license": "UNLICENSED", + "dependencies": { + "@remotion/paths": "4.0.380", + "@remotion/shapes": "4.0.380", + "remotion": "4.0.380" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/web-renderer": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/web-renderer/-/web-renderer-4.0.380.tgz", + "integrity": "sha512-WuWH4VA2ubiw58FNGh7gmnGlzXDkxiLGYp+47uATzFe/GtCf5dVKc5360N3HP4s2nnPgBh//uKt7CovALJlkhw==", + "license": "UNLICENSED", + "dependencies": { + "mediabunny": "1.24.5", + "remotion": "4.0.380" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@remotion/zod-types": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/@remotion/zod-types/-/zod-types-4.0.380.tgz", + "integrity": "sha512-VvgUEI50sSAiRIem0l17Yf5mQY90A00mryr+1NcPADxOhYrTUtHW6BejU/nWv4lAHw31/W/grXmfrLUi3HVWjw==", + "license": "MIT", + "dependencies": { + "remotion": "4.0.380" + }, + "peerDependencies": { + "zod": "3.22.3" + } + }, + "node_modules/@types/dom-mediacapture-transform": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@types/dom-mediacapture-transform/-/dom-mediacapture-transform-0.1.11.tgz", + "integrity": "sha512-Y2p+nGf1bF2XMttBnsVPHUWzRRZzqUoJAKmiP10b5umnO6DDrWI0BrGDJy1pOHoOULVmGSfFNkQrAlC5dcj6nQ==", + "license": "MIT", + "dependencies": { + "@types/dom-webcodecs": "*" + } + }, + "node_modules/@types/dom-webcodecs": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@types/dom-webcodecs/-/dom-webcodecs-0.1.13.tgz", + "integrity": "sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ==", + "license": "MIT" + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "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==", + "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==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/react": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.0.tgz", + "integrity": "sha512-MY3oPudxvMYyesqs/kW1Bh8y9VqSmf+tzqw3ae8a9DZW68pUe3zAdHeI1jc6iAysuRdACnVknHP8AhwD4/dxtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "license": "Apache-2.0" + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "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/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ast-types": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.21", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.21.tgz", + "integrity": "sha512-Q+rUQ7Uz8AHM7DEaNdwvfFCTq7a43lNTzuS94eiWqwyxfV/wJv+oUivef51T91mmRY4d4A1u9rcSvkeufCVXlA==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001790", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001790.tgz", + "integrity": "sha512-bOoxfJPyYo+ds6W0YfptaCWbFnJYjh2Y1Eow5lRv+vI2u8ganPZqNm1JwNh0t2ELQCqIWg4B3dWEusgAmsoyOw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "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==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-loader": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz", + "integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==", + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.15", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.27.0 || ^5.0.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "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" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz", + "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=10" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.344", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.344.tgz", + "integrity": "sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg==", + "license": "ISC" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "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==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "license": "Unlicense" + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause" + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "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==", + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/loader-runner": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", + "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", + "license": "MIT", + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mediabunny": { + "version": "1.24.5", + "resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.24.5.tgz", + "integrity": "sha512-4Sd5x8WXQiuDKZcJDAiQjyD0Lg/cg9p+dBShCM02F1pMvKdB9R6+WHZ9NFCFSqViVPX2fYSu95ZIpBUg9+TwTQ==", + "license": "MPL-2.0", + "workspaces": [ + "packages/*" + ], + "dependencies": { + "@types/dom-mediacapture-transform": "^0.1.11", + "@types/dom-webcodecs": "0.1.13" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/Vanilagy" + } + }, + "node_modules/memfs": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.3.tgz", + "integrity": "sha512-eivjfi7Ahr6eQTn44nvTnR60e4a1Fs1Via2kCR5lHo/kyNoiMWaXCNJ/GpSd0ilXas2JSOl9B5FTIhflXu0hlg==", + "license": "Unlicense", + "dependencies": { + "fs-monkey": "1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "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/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", + "license": "MIT" + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "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/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", + "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.25.0" + }, + "peerDependencies": { + "react": "^19.0.0" + } + }, + "node_modules/react-refresh": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz", + "integrity": "sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/recast": { + "version": "0.23.11", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz", + "integrity": "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==", + "license": "MIT", + "dependencies": { + "ast-types": "^0.16.1", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tiny-invariant": "^1.3.3", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/recast/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/remotion": { + "version": "4.0.380", + "resolved": "https://registry.npmjs.org/remotion/-/remotion-4.0.380.tgz", + "integrity": "sha512-mVrq8dmKjHpIRcvXGo4iUx/n+rea5YQqYouTN8afg8ZGUvA181Q+BiiSHGP+ow7Wx7posPH6OFpqtYn0swbobw==", + "license": "SEE LICENSE IN LICENSE.md", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/scheduler": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", + "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", + "license": "MIT" + }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/style-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-4.0.0.tgz", + "integrity": "sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==", + "license": "MIT", + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.27.0" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser": { + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz", + "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==", + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz", + "integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "license": "MIT", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "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" + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/watchpack": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "license": "BSD-2-Clause" + }, + "node_modules/webpack": { + "version": "5.96.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", + "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz", + "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "license": "MIT", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/zod": { + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz", + "integrity": "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/video/package.json b/video/package.json new file mode 100644 index 00000000..1f72365e --- /dev/null +++ b/video/package.json @@ -0,0 +1,31 @@ +{ + "name": "simplemodule-video", + "version": "1.0.0", + "private": true, + "description": "Remotion showcase video for the SimpleModule framework", + "scripts": { + "start": "remotion studio", + "build": "remotion render SimpleModule out/simplemodule.mp4 --codec=h264", + "render": "remotion render SimpleModule out/simplemodule.mp4 --codec=h264", + "render:all": "node scripts/render-all.mjs", + "narrate": "node scripts/generate-narration.mjs", + "verify": "node scripts/verify-narration.mjs", + "upgrade": "remotion upgrade" + }, + "dependencies": { + "@remotion/bundler": "4.0.380", + "@remotion/cli": "4.0.380", + "@remotion/google-fonts": "4.0.380", + "@remotion/renderer": "4.0.380", + "@remotion/shapes": "4.0.380", + "@remotion/transitions": "^4.0.380", + "react": "19.0.0", + "react-dom": "19.0.0", + "remotion": "4.0.380" + }, + "devDependencies": { + "@types/node": "22.10.0", + "@types/react": "19.0.0", + "typescript": "5.6.3" + } +} diff --git a/video/public/background.mp3 b/video/public/background.mp3 new file mode 100644 index 00000000..bb640075 Binary files /dev/null and b/video/public/background.mp3 differ diff --git a/video/public/logo-wordmark.svg b/video/public/logo-wordmark.svg new file mode 100644 index 00000000..26b200b8 --- /dev/null +++ b/video/public/logo-wordmark.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SimpleModule + + diff --git a/video/public/logo.svg b/video/public/logo.svg new file mode 100644 index 00000000..cf9de0a4 --- /dev/null +++ b/video/public/logo.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/video/public/narration/manifest.json b/video/public/narration/manifest.json new file mode 100644 index 00000000..db7a1251 --- /dev/null +++ b/video/public/narration/manifest.json @@ -0,0 +1,47 @@ +[ + { + "id": "s1", + "durationS": 3.160816, + "text": "SimpleModule — the modular monolith for .NET." + }, + { + "id": "s2", + "durationS": 3.866122, + "text": "Monoliths tangle. Microservices complicate. There's a better way." + }, + { + "id": "s3", + "durationS": 4.362449, + "text": "Mark a class with Module, and the Roslyn generator wires it up at compile time." + }, + { + "id": "s4", + "durationS": 3.866122, + "text": "Write one IEndpoint class. Routes and permissions register themselves." + }, + { + "id": "s5", + "durationS": 3.239184, + "text": "MapCrud turns a single line into five REST endpoints." + }, + { + "id": "s6", + "durationS": 4.257959, + "text": "Render React pages with Inertia, and broadcast events across modules." + }, + { + "id": "s7", + "durationS": 3.056327, + "text": "Ten production-ready core modules ship in the box." + }, + { + "id": "s8", + "durationS": 5.067755, + "text": "Built on .NET 10 and React 19, with fifty-five compile-time safety checks." + }, + { + "id": "s9", + "durationS": 2.76898, + "text": "Start building today. You'll find us on GitHub." + } +] diff --git a/video/public/narration/modules/Admin.mp3 b/video/public/narration/modules/Admin.mp3 new file mode 100644 index 00000000..61a1168a Binary files /dev/null and b/video/public/narration/modules/Admin.mp3 differ diff --git a/video/public/narration/modules/AuditLogs.mp3 b/video/public/narration/modules/AuditLogs.mp3 new file mode 100644 index 00000000..b300d385 Binary files /dev/null and b/video/public/narration/modules/AuditLogs.mp3 differ diff --git a/video/public/narration/modules/BackgroundJobs.mp3 b/video/public/narration/modules/BackgroundJobs.mp3 new file mode 100644 index 00000000..b3ef4038 Binary files /dev/null and b/video/public/narration/modules/BackgroundJobs.mp3 differ diff --git a/video/public/narration/modules/Dashboard.mp3 b/video/public/narration/modules/Dashboard.mp3 new file mode 100644 index 00000000..77948040 Binary files /dev/null and b/video/public/narration/modules/Dashboard.mp3 differ diff --git a/video/public/narration/modules/Email.mp3 b/video/public/narration/modules/Email.mp3 new file mode 100644 index 00000000..ada27497 Binary files /dev/null and b/video/public/narration/modules/Email.mp3 differ diff --git a/video/public/narration/modules/FeatureFlags.mp3 b/video/public/narration/modules/FeatureFlags.mp3 new file mode 100644 index 00000000..b85b4973 Binary files /dev/null and b/video/public/narration/modules/FeatureFlags.mp3 differ diff --git a/video/public/narration/modules/FileStorage.mp3 b/video/public/narration/modules/FileStorage.mp3 new file mode 100644 index 00000000..4932df72 Binary files /dev/null and b/video/public/narration/modules/FileStorage.mp3 differ diff --git a/video/public/narration/modules/Permissions.mp3 b/video/public/narration/modules/Permissions.mp3 new file mode 100644 index 00000000..b53f88b3 Binary files /dev/null and b/video/public/narration/modules/Permissions.mp3 differ diff --git a/video/public/narration/modules/Settings.mp3 b/video/public/narration/modules/Settings.mp3 new file mode 100644 index 00000000..cec35fb3 Binary files /dev/null and b/video/public/narration/modules/Settings.mp3 differ diff --git a/video/public/narration/modules/Users.mp3 b/video/public/narration/modules/Users.mp3 new file mode 100644 index 00000000..5c5bcc2e Binary files /dev/null and b/video/public/narration/modules/Users.mp3 differ diff --git a/video/public/narration/modules/manifest.json b/video/public/narration/modules/manifest.json new file mode 100644 index 00000000..14bb6b79 --- /dev/null +++ b/video/public/narration/modules/manifest.json @@ -0,0 +1,52 @@ +[ + { + "id": "Users", + "durationS": 9.16898, + "text": "The Users module handles identity — registration, login, profiles, and roles. Backed by OpenIddict, so OAuth and OpenID Connect work out of the box." + }, + { + "id": "Permissions", + "durationS": 7.288163, + "text": "Permissions is the authorization layer. Define rules once, and every endpoint enforces them — checked at compile time by the source generator." + }, + { + "id": "Settings", + "durationS": 8.097959, + "text": "Settings gives every module typed, cached configuration that you can edit at runtime. No redeploy needed to flip a flag or tune a limit." + }, + { + "id": "Admin", + "durationS": 7.836735, + "text": "Admin is the built-in control panel. Manage users, roles, and every module from one React dashboard — no extra setup required." + }, + { + "id": "Dashboard", + "durationS": 7.47102, + "text": "Dashboard is your home screen. It shows health, metrics, and every module active in your application, updated in real time." + }, + { + "id": "AuditLogs", + "durationS": 6.896327, + "text": "AuditLogs captures every significant change — who did what, when, to which entity. Fully queryable and ready for compliance." + }, + { + "id": "FileStorage", + "durationS": 8.228571, + "text": "FileStorage abstracts local disk, Azure Blob, and S3 behind one interface. Swap providers with a single line of configuration." + }, + { + "id": "Email", + "durationS": 7.288163, + "text": "Email ships with templates, queuing, and retries. Send from any module through one contract — delivery details stay out of your way." + }, + { + "id": "BackgroundJobs", + "durationS": 7.340408, + "text": "BackgroundJobs runs work off the request path. Scheduled jobs, durable queues, and automatic retries — powered by TickerQ." + }, + { + "id": "FeatureFlags", + "durationS": 8.646531, + "text": "FeatureFlags gates code behind runtime toggles. Percentage rollouts, staff-only previews, or classic A/B — all flippable without a deploy." + } +] diff --git a/video/public/narration/s1.mp3 b/video/public/narration/s1.mp3 new file mode 100644 index 00000000..c2c53620 Binary files /dev/null and b/video/public/narration/s1.mp3 differ diff --git a/video/public/narration/s2.mp3 b/video/public/narration/s2.mp3 new file mode 100644 index 00000000..79084973 Binary files /dev/null and b/video/public/narration/s2.mp3 differ diff --git a/video/public/narration/s3.mp3 b/video/public/narration/s3.mp3 new file mode 100644 index 00000000..244d4849 Binary files /dev/null and b/video/public/narration/s3.mp3 differ diff --git a/video/public/narration/s4.mp3 b/video/public/narration/s4.mp3 new file mode 100644 index 00000000..5cb6003c Binary files /dev/null and b/video/public/narration/s4.mp3 differ diff --git a/video/public/narration/s5.mp3 b/video/public/narration/s5.mp3 new file mode 100644 index 00000000..bd169ee7 Binary files /dev/null and b/video/public/narration/s5.mp3 differ diff --git a/video/public/narration/s6.mp3 b/video/public/narration/s6.mp3 new file mode 100644 index 00000000..265ecf1d Binary files /dev/null and b/video/public/narration/s6.mp3 differ diff --git a/video/public/narration/s7.mp3 b/video/public/narration/s7.mp3 new file mode 100644 index 00000000..04c67343 Binary files /dev/null and b/video/public/narration/s7.mp3 differ diff --git a/video/public/narration/s8.mp3 b/video/public/narration/s8.mp3 new file mode 100644 index 00000000..cc4bda82 Binary files /dev/null and b/video/public/narration/s8.mp3 differ diff --git a/video/public/narration/s9.mp3 b/video/public/narration/s9.mp3 new file mode 100644 index 00000000..3faf5c6f Binary files /dev/null and b/video/public/narration/s9.mp3 differ diff --git a/video/remotion.config.ts b/video/remotion.config.ts new file mode 100644 index 00000000..89d468b6 --- /dev/null +++ b/video/remotion.config.ts @@ -0,0 +1,6 @@ +import { Config } from '@remotion/cli/config'; + +Config.setVideoImageFormat('jpeg'); +Config.setConcurrency(1); +Config.setChromiumOpenGlRenderer('angle'); +Config.setChromiumIgnoreCertificateErrors(true); diff --git a/video/scripts/generate-audio.mjs b/video/scripts/generate-audio.mjs new file mode 100644 index 00000000..31477407 --- /dev/null +++ b/video/scripts/generate-audio.mjs @@ -0,0 +1,122 @@ +#!/usr/bin/env node +// Synthesize a gentle ambient pad as a WAV file. +// Layered sine chord (Cmaj9 voicing) + slow LFO tremolo + simple 1-pole lowpass. + +import { writeFileSync } from 'node:fs'; +import { resolve, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const SAMPLE_RATE = 44100; +const DURATION_S = 42; +const CHANNELS = 2; +const TOTAL_SAMPLES = SAMPLE_RATE * DURATION_S; + +// Cmaj9 voicing (C3, E3, G3, C4, D4, E4) with amplitudes tuned so the low +// partials dominate and the upper ones only add sparkle. +const partials = [ + { freq: 130.81, amp: 0.55, pan: 0.0 }, // C3 + { freq: 164.81, amp: 0.32, pan: -0.3 }, // E3 + { freq: 196.0, amp: 0.38, pan: 0.3 }, // G3 + { freq: 261.63, amp: 0.22, pan: -0.15 }, // C4 + { freq: 293.66, amp: 0.12, pan: 0.15 }, // D4 + { freq: 329.63, amp: 0.14, pan: 0.0 }, // E4 +]; + +// Tremolo LFO on overall amplitude — slow and shallow. +const tremoloHz = 0.28; +const tremoloDepth = 0.18; + +// Tiny 1-pole lowpass to soften the highs. +const cutoffHz = 1400; +const rc = 1 / (2 * Math.PI * cutoffHz); +const dt = 1 / SAMPLE_RATE; +const alpha = dt / (rc + dt); + +// Fade envelopes at the edges so the loop point is clean. +const fadeInSamples = Math.floor(SAMPLE_RATE * 1.5); +const fadeOutSamples = Math.floor(SAMPLE_RATE * 2.5); + +function envelope(i) { + if (i < fadeInSamples) return i / fadeInSamples; + if (i > TOTAL_SAMPLES - fadeOutSamples) { + return (TOTAL_SAMPLES - i) / fadeOutSamples; + } + return 1; +} + +function pan(amp, p) { + // equal-power pan: p in [-1,1] → (left, right) gains + const angle = ((p + 1) / 2) * (Math.PI / 2); + return { l: amp * Math.cos(angle), r: amp * Math.sin(angle) }; +} + +// Pre-compute per-partial pan gains. +const partialGains = partials.map((p) => ({ ...p, gains: pan(1, p.pan) })); + +let lastL = 0; +let lastR = 0; + +// PCM16 stereo interleaved +const pcm = new Int16Array(TOTAL_SAMPLES * CHANNELS); + +for (let i = 0; i < TOTAL_SAMPLES; i++) { + const t = i / SAMPLE_RATE; + const env = envelope(i); + const trem = 1 - tremoloDepth + tremoloDepth * Math.sin(2 * Math.PI * tremoloHz * t); + + let l = 0; + let r = 0; + for (const p of partialGains) { + const s = Math.sin(2 * Math.PI * p.freq * t) * p.amp; + l += s * p.gains.l; + r += s * p.gains.r; + } + + // apply tremolo + envelope + l *= trem * env; + r *= trem * env; + + // 1-pole lowpass per-channel + lastL = lastL + alpha * (l - lastL); + lastR = lastR + alpha * (r - lastR); + + // master gain to keep headroom + const master = 0.85; + let outL = lastL * master; + let outR = lastR * master; + + // clamp + if (outL > 1) outL = 1; + else if (outL < -1) outL = -1; + if (outR > 1) outR = 1; + else if (outR < -1) outR = -1; + + pcm[i * 2] = Math.round(outL * 32767); + pcm[i * 2 + 1] = Math.round(outR * 32767); +} + +// Build a WAV container +const byteRate = SAMPLE_RATE * CHANNELS * 2; +const blockAlign = CHANNELS * 2; +const dataSize = pcm.byteLength; +const header = Buffer.alloc(44); +header.write('RIFF', 0); +header.writeUInt32LE(36 + dataSize, 4); +header.write('WAVE', 8); +header.write('fmt ', 12); +header.writeUInt32LE(16, 16); // PCM chunk size +header.writeUInt16LE(1, 20); // PCM format +header.writeUInt16LE(CHANNELS, 22); +header.writeUInt32LE(SAMPLE_RATE, 24); +header.writeUInt32LE(byteRate, 28); +header.writeUInt16LE(blockAlign, 32); +header.writeUInt16LE(16, 34); // bits per sample +header.write('data', 36); +header.writeUInt32LE(dataSize, 40); + +const wavPath = resolve(__dirname, '..', 'public', 'background.wav'); +writeFileSync(wavPath, Buffer.concat([header, Buffer.from(pcm.buffer)])); +console.log('wrote', wavPath, dataSize, 'bytes of PCM'); diff --git a/video/scripts/generate-narration.mjs b/video/scripts/generate-narration.mjs new file mode 100644 index 00000000..a1cb7c8b --- /dev/null +++ b/video/scripts/generate-narration.mjs @@ -0,0 +1,161 @@ +#!/usr/bin/env node +// Generate scene narration with Piper (a local neural TTS) for a more natural +// voice than macOS `say`. Runs serially and writes per-scene MP3s plus a +// manifest so the Remotion composition can line them up by frame offset. + +import { execFileSync } from 'node:child_process'; +import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { resolve, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const videoRoot = resolve(__dirname, '..'); +const publicDir = resolve(videoRoot, 'public'); +const narrationDir = resolve(publicDir, 'narration'); +const moduleNarrationDir = resolve(narrationDir, 'modules'); +const tmpDir = resolve(videoRoot, '.tmp-narration'); + +const voiceModel = resolve(videoRoot, '.piper-voices', 'en_US-ryan-high.onnx'); +if (!existsSync(voiceModel)) { + console.error(`Missing Piper voice model: ${voiceModel}`); + console.error('Download from https://huggingface.co/rhasspy/piper-voices (see README).'); + process.exit(1); +} + +rmSync(narrationDir, { recursive: true, force: true }); +mkdirSync(narrationDir, { recursive: true }); +mkdirSync(moduleNarrationDir, { recursive: true }); +rmSync(tmpDir, { recursive: true, force: true }); +mkdirSync(tmpDir, { recursive: true }); + +const compositor = resolve( + videoRoot, + 'node_modules', + '@remotion', + 'compositor-darwin-arm64', +); +const ffmpeg = resolve(compositor, 'ffmpeg'); +const ffprobe = resolve(compositor, 'ffprobe'); + +// `length_scale` > 1 slows speech, < 1 speeds it up. 0.95 is a touch quicker +// than default and keeps each line inside its scene budget. +const LENGTH_SCALE = '0.95'; +const SENTENCE_SILENCE = '0.25'; + +// Copy polished for a more human cadence: contractions, shorter sentences, +// fewer acronym-as-word hacks. Piper handles ".NET" and "IEndpoint" better +// than `say` did, so we can drop the "dot NET" / "I Endpoint" spellings. +const lines = [ + { id: 's1', text: 'SimpleModule — the modular monolith for .NET.' }, + { id: 's2', text: "Monoliths tangle. Microservices complicate. There's a better way." }, + { id: 's3', text: 'Mark a class with Module, and the Roslyn generator wires it up at compile time.' }, + { id: 's4', text: 'Write one IEndpoint class. Routes and permissions register themselves.' }, + { id: 's5', text: 'MapCrud turns a single line into five REST endpoints.' }, + { id: 's6', text: 'Render React pages with Inertia, and broadcast events across modules.' }, + { id: 's7', text: 'Ten production-ready core modules ship in the box.' }, + { id: 's8', text: 'Built on .NET 10 and React 19, with fifty-five compile-time safety checks.' }, + { id: 's9', text: "Start building today. You'll find us on GitHub." }, +]; + +function run(cmd, args, env = {}) { + return execFileSync(cmd, args, { + env: { ...process.env, ...env }, + stdio: ['pipe', 'pipe', 'inherit'], + }).toString(); +} + +function speak(text, wavPath) { + execFileSync( + 'piper', + [ + '-m', voiceModel, + '-f', wavPath, + '--length-scale', LENGTH_SCALE, + '--sentence-silence', SENTENCE_SILENCE, + ], + { + input: text, + stdio: ['pipe', 'inherit', 'pipe'], + }, + ); +} + +function wavToMp3(wavPath, mp3Path) { + run( + ffmpeg, + [ + '-y', + '-i', wavPath, + '-acodec', 'libmp3lame', + '-b:a', '160k', + '-ar', '44100', + '-ac', '1', + mp3Path, + ], + { DYLD_LIBRARY_PATH: compositor }, + ); +} + +function probeDuration(mp3Path) { + const out = run( + ffprobe, + [ + '-v', 'error', + '-show_entries', 'format=duration', + '-of', 'default=noprint_wrappers=1:nokey=1', + mp3Path, + ], + { DYLD_LIBRARY_PATH: compositor }, + ); + return parseFloat(out.trim()); +} + +function generate(id, text, outDir) { + const wav = resolve(tmpDir, `${id}.wav`); + const mp3 = resolve(outDir, `${id}.mp3`); + speak(text, wav); + wavToMp3(wav, mp3); + return probeDuration(mp3); +} + +// --- Main showcase narration --------------------------------------------- +const manifest = []; +for (const line of lines) { + const durationS = generate(line.id, line.text, narrationDir); + manifest.push({ id: line.id, durationS, text: line.text }); + console.log(`[main] ${line.id}: ${durationS.toFixed(2)}s — ${line.text}`); +} +writeFileSync( + resolve(narrationDir, 'manifest.json'), + JSON.stringify(manifest, null, 2) + '\n', +); + +// --- Per-module showcase narration --------------------------------------- +// Extract narration strings from src/data/moduleShowcases.ts so we can stay +// in lockstep with the composition data without a transpile step. +const moduleSource = readFileSync( + resolve(videoRoot, 'src', 'data', 'moduleShowcases.ts'), + 'utf8', +); +const moduleEntries = [...moduleSource.matchAll( + /id:\s*'([^']+)',[\s\S]*?narration:\s*\n?\s*'([^']+)'/g, +)].map(([, id, narration]) => ({ id, narration })); + +if (moduleEntries.length === 0) { + throw new Error('Failed to parse moduleShowcases.ts — narration regex missed.'); +} + +const moduleManifest = []; +for (const mod of moduleEntries) { + const durationS = generate(mod.id, mod.narration, moduleNarrationDir); + moduleManifest.push({ id: mod.id, durationS, text: mod.narration }); + console.log(`[module:${mod.id}] ${durationS.toFixed(2)}s`); +} +writeFileSync( + resolve(moduleNarrationDir, 'manifest.json'), + JSON.stringify(moduleManifest, null, 2) + '\n', +); + +rmSync(tmpDir, { recursive: true, force: true }); diff --git a/video/scripts/normalize-loudness.mjs b/video/scripts/normalize-loudness.mjs new file mode 100644 index 00000000..8ed21309 --- /dev/null +++ b/video/scripts/normalize-loudness.mjs @@ -0,0 +1,73 @@ +#!/usr/bin/env node +// Post-process rendered MP4s so their audio hits -14 LUFS (streaming +// standard). Remotion just mixes sources verbatim, so a hot TTS track can +// push the integrated loudness well over -10 LUFS without this step. + +import { execFileSync } from 'node:child_process'; +import { readdirSync, renameSync, rmSync, statSync } from 'node:fs'; +import { resolve, dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const videoRoot = resolve(__dirname, '..'); +const outDir = resolve(videoRoot, 'out'); +const compositor = resolve( + videoRoot, + 'node_modules', + '@remotion', + 'compositor-darwin-arm64', +); +const ffmpeg = resolve(compositor, 'ffmpeg'); + +function findMp4s(dir, acc = []) { + for (const entry of readdirSync(dir)) { + const full = join(dir, entry); + if (statSync(full).isDirectory()) { + findMp4s(full, acc); + } else if (entry.endsWith('.mp4')) { + acc.push(full); + } + } + return acc; +} + +function normalize(mp4Path) { + const tmp = mp4Path.replace(/\.mp4$/, '.norm.mp4'); + // Copy video, re-encode audio through loudnorm. Target -14 LUFS with + // -1.5 dBTP ceiling, matches Spotify/YouTube delivery specs. + execFileSync( + ffmpeg, + [ + '-y', + '-i', mp4Path, + '-c:v', 'copy', + '-af', 'loudnorm=I=-14:TP=-1.5:LRA=11', + '-c:a', 'aac', + '-b:a', '192k', + '-ar', '48000', + '-movflags', '+faststart', + tmp, + ], + { + env: { ...process.env, DYLD_LIBRARY_PATH: compositor }, + stdio: ['ignore', 'ignore', 'inherit'], + }, + ); + rmSync(mp4Path); + renameSync(tmp, mp4Path); +} + +const targets = findMp4s(outDir); +if (targets.length === 0) { + console.error(`No mp4s found under ${outDir}. Run "npm run render:all" first.`); + process.exit(1); +} + +for (const t of targets) { + process.stdout.write(`normalizing ${t.replace(outDir + '/', '')} … `); + normalize(t); + console.log('ok'); +} +console.log(`\nnormalized ${targets.length} videos to -14 LUFS`); diff --git a/video/scripts/render-all.mjs b/video/scripts/render-all.mjs new file mode 100644 index 00000000..f9dd0d35 --- /dev/null +++ b/video/scripts/render-all.mjs @@ -0,0 +1,75 @@ +#!/usr/bin/env node +// Render the main SimpleModule showcase + one video per core module. +// Output files land in out/simplemodule.mp4 and out/modules/.mp4. + +import { execFileSync } from 'node:child_process'; +import { existsSync, mkdirSync, readFileSync } from 'node:fs'; +import { resolve, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const videoRoot = resolve(__dirname, '..'); +const outDir = resolve(videoRoot, 'out'); +const modulesOutDir = resolve(outDir, 'modules'); +mkdirSync(modulesOutDir, { recursive: true }); + +const moduleSource = readFileSync( + resolve(videoRoot, 'src', 'data', 'moduleShowcases.ts'), + 'utf8', +); +const moduleIds = [...moduleSource.matchAll(/id:\s*'([^']+)'/g)].map((m) => m[1]); + +const targets = [ + { id: 'SimpleModule', out: resolve(outDir, 'simplemodule.mp4') }, + ...moduleIds.map((id) => ({ + id: `Module-${id}`, + out: resolve(modulesOutDir, `${id}.mp4`), + })), +]; + +// Fail fast if any referenced narration clip is missing — otherwise Remotion +// silently renders video with no voice-over for that clip. +const narrationDir = resolve(videoRoot, 'public', 'narration'); +const missing = []; +const mainClips = ['s1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9']; +for (const c of mainClips) { + const p = resolve(narrationDir, `${c}.mp3`); + if (!existsSync(p)) missing.push(p); +} +for (const id of moduleIds) { + const p = resolve(narrationDir, 'modules', `${id}.mp3`); + if (!existsSync(p)) missing.push(p); +} +if (missing.length) { + console.error('Missing narration files — run `npm run narrate` first:'); + for (const m of missing) console.error(' ' + m); + process.exit(1); +} + +const remotion = resolve(videoRoot, 'node_modules', '.bin', 'remotion'); + +for (const t of targets) { + console.log(`\n>>> rendering ${t.id} -> ${t.out}`); + execFileSync(remotion, ['render', t.id, t.out, '--codec=h264', '--log=info'], { + cwd: videoRoot, + stdio: 'inherit', + }); +} + +console.log(`\n[done] ${targets.length} videos written under ${outDir}`); + +// Normalize every video to -14 LUFS so narration + music don't overshoot. +console.log('\n>>> normalizing loudness'); +execFileSync('node', [resolve(__dirname, 'normalize-loudness.mjs')], { + cwd: videoRoot, + stdio: 'inherit', +}); + +// Prove each per-module video carries its own narration, not a neighbour's. +console.log('\n>>> verifying narration-to-module mapping'); +execFileSync('node', [resolve(__dirname, 'verify-narration.mjs')], { + cwd: videoRoot, + stdio: 'inherit', +}); diff --git a/video/scripts/verify-narration.mjs b/video/scripts/verify-narration.mjs new file mode 100644 index 00000000..87c0d37f --- /dev/null +++ b/video/scripts/verify-narration.mjs @@ -0,0 +1,143 @@ +#!/usr/bin/env node +// Verify each rendered per-module .mp4 actually carries its own module's +// narration. Builds a speech envelope for every narration source MP3, a +// narration-band envelope for every video, and checks that each video's +// shape matches its expected source better than any other module's. + +import { execFileSync } from 'node:child_process'; +import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from 'node:fs'; +import { resolve, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { tmpdir } from 'node:os'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const videoRoot = resolve(__dirname, '..'); +const compositor = resolve( + videoRoot, + 'node_modules', + '@remotion', + 'compositor-darwin-arm64', +); +const ffmpeg = resolve(compositor, 'ffmpeg'); + +// Narration in each Module-.mp4 starts at frame 18 (0.6s) per +// ModuleShowcase's . +const NARRATION_OFFSET_S = 18 / 30; +const WINDOW_S = 0.1; // 100 ms RMS window +const SAMPLE_RATE = 16000; +const WINDOW_SAMPLES = Math.round(SAMPLE_RATE * WINDOW_S); + +const workDir = resolve(tmpdir(), `simplemodule-verify-${process.pid}`); +mkdirSync(workDir, { recursive: true }); +process.on('exit', () => rmSync(workDir, { recursive: true, force: true })); + +function decodeToPcm(mp4OrMp3, startS = 0, durS = 8) { + const wav = resolve(workDir, `dec-${Math.random().toString(36).slice(2)}.wav`); + execFileSync( + ffmpeg, + [ + '-y', + '-v', 'error', + '-ss', String(startS), + '-t', String(durS), + '-i', mp4OrMp3, + '-vn', + '-acodec', 'pcm_s16le', + '-ar', String(SAMPLE_RATE), + '-ac', '1', + wav, + ], + { + env: { ...process.env, DYLD_LIBRARY_PATH: compositor }, + stdio: ['ignore', 'ignore', 'inherit'], + }, + ); + const buf = readFileSync(wav); + rmSync(wav); + // Strip 44-byte WAV header; everything after is little-endian int16 PCM. + return new Int16Array(buf.buffer, buf.byteOffset + 44, (buf.byteLength - 44) / 2); +} + +function envelope(samples) { + const out = new Float32Array(Math.floor(samples.length / WINDOW_SAMPLES)); + for (let w = 0; w < out.length; w++) { + let sum = 0; + for (let i = 0; i < WINDOW_SAMPLES; i++) { + const s = samples[w * WINDOW_SAMPLES + i] / 32768; + sum += s * s; + } + out[w] = Math.sqrt(sum / WINDOW_SAMPLES); + } + return out; +} + +function correlate(a, b) { + const n = Math.min(a.length, b.length); + let meanA = 0; + let meanB = 0; + for (let i = 0; i < n; i++) { + meanA += a[i]; + meanB += b[i]; + } + meanA /= n; + meanB /= n; + let num = 0; + let denA = 0; + let denB = 0; + for (let i = 0; i < n; i++) { + const da = a[i] - meanA; + const db = b[i] - meanB; + num += da * db; + denA += da * da; + denB += db * db; + } + const den = Math.sqrt(denA * denB); + return den === 0 ? 0 : num / den; +} + +const narrationDir = resolve(videoRoot, 'public', 'narration', 'modules'); +const modulesOutDir = resolve(videoRoot, 'out', 'modules'); +const ids = readdirSync(narrationDir) + .filter((f) => f.endsWith('.mp3')) + .map((f) => f.replace(/\.mp3$/, '')); + +const sources = {}; +for (const id of ids) { + const src = resolve(narrationDir, `${id}.mp3`); + sources[id] = envelope(decodeToPcm(src, 0, 8)); +} + +let ok = 0; +let fail = 0; +for (const id of ids) { + const vid = resolve(modulesOutDir, `${id}.mp4`); + if (!existsSync(vid)) { + console.log(`skip ${id}: ${vid} missing`); + continue; + } + const vidEnv = envelope(decodeToPcm(vid, NARRATION_OFFSET_S, 8)); + + // Score each source against this video's narration-band envelope. + let bestId = null; + let bestCorr = -Infinity; + const scores = {}; + for (const sid of ids) { + const c = correlate(vidEnv, sources[sid]); + scores[sid] = c; + if (c > bestCorr) { + bestCorr = c; + bestId = sid; + } + } + const ownCorr = scores[id]; + const status = bestId === id ? 'ok' : 'MISMATCH'; + if (bestId === id) ok++; + else fail++; + console.log( + `${status.padEnd(9)} ${id.padEnd(16)} own=${ownCorr.toFixed(3)} best=${bestId}(${bestCorr.toFixed(3)})`, + ); +} +console.log(`\n${ok} ok, ${fail} mismatch`); +process.exit(fail === 0 ? 0 : 1); diff --git a/video/src/Root.tsx b/video/src/Root.tsx new file mode 100644 index 00000000..838ea1af --- /dev/null +++ b/video/src/Root.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { Composition } from 'remotion'; +import { Video } from './Video'; +import { ModuleShowcase } from './compositions/ModuleShowcase'; +import { moduleShowcases } from './data/moduleShowcases'; + +export const Root: React.FC = () => { + return ( + <> + + + {moduleShowcases.map((mod) => ( + + ))} + + ); +}; diff --git a/video/src/Video.tsx b/video/src/Video.tsx new file mode 100644 index 00000000..064f5719 --- /dev/null +++ b/video/src/Video.tsx @@ -0,0 +1,146 @@ +import React from 'react'; +import { AbsoluteFill, Audio, Sequence, staticFile, interpolate } from 'remotion'; +import { TransitionSeries, linearTiming } from '@remotion/transitions'; +import { fade } from '@remotion/transitions/fade'; +import { slide } from '@remotion/transitions/slide'; +import { SceneOpening } from './scenes/SceneOpening'; +import { SceneProblemSolution } from './scenes/SceneProblemSolution'; +import { SceneModuleAttribute } from './scenes/SceneModuleAttribute'; +import { SceneEndpoint } from './scenes/SceneEndpoint'; +import { SceneCrudEndpoints } from './scenes/SceneCrudEndpoints'; +import { SceneInertiaEvents } from './scenes/SceneInertiaEvents'; +import { SceneModules } from './scenes/SceneModules'; +import { SceneTechStats } from './scenes/SceneTechStats'; +import { SceneCTA } from './scenes/SceneCTA'; +import './fonts'; + +// Audio toggles: both background music and narration play by default. +// Set REMOTION_DISABLE_AUDIO=1 to render a silent clip. +const audioDisabled = process.env.REMOTION_DISABLE_AUDIO === '1'; + +const TRANSITION_FRAMES = 18; + +// Narration clips with their start frames. Scene start frames (with 18-frame +// transition overlap): S1=0, S2=77, S3=199, S4=341, S5=473, S6=640, S7=782, +// S8=914, S9=1071. Each voice clip is offset ~10–15 frames into its scene +// to let visuals settle before speech begins. +const narration = [ + { src: 'narration/s1.mp3', from: 5 }, + { src: 'narration/s2.mp3', from: 90 }, + { src: 'narration/s3.mp3', from: 215 }, + { src: 'narration/s4.mp3', from: 355 }, + { src: 'narration/s5.mp3', from: 488 }, + { src: 'narration/s6.mp3', from: 655 }, + { src: 'narration/s7.mp3', from: 795 }, + { src: 'narration/s8.mp3', from: 925 }, + { src: 'narration/s9.mp3', from: 1080 }, +]; + +export const Video: React.FC = () => { + // Total = sum of sequence lengths (1315) - 8 × TRANSITION_FRAMES overlap = 1171. + const totalFrames = 1315 - 8 * TRANSITION_FRAMES; + const musicFadeOutStart = totalFrames - 45; + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {audioDisabled ? null : ( + <> + + ); +}; diff --git a/video/src/components/ArrowFlow.tsx b/video/src/components/ArrowFlow.tsx new file mode 100644 index 00000000..dce2e21a --- /dev/null +++ b/video/src/components/ArrowFlow.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { useCurrentFrame, interpolate } from 'remotion'; +import { colors } from '../theme'; + +type Props = { + startFrame?: number; + duration?: number; +}; + +export const ArrowFlow: React.FC = ({ startFrame = 0, duration = 20 }) => { + const frame = useCurrentFrame(); + const progress = interpolate( + frame, + [startFrame, startFrame + duration], + [0, 1], + { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }, + ); + + const dash = 120; + const offset = dash * (1 - progress); + + return ( + + + + + + + + + + + ); +}; diff --git a/video/src/components/BigNumber.tsx b/video/src/components/BigNumber.tsx new file mode 100644 index 00000000..77b053d6 --- /dev/null +++ b/video/src/components/BigNumber.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { useCurrentFrame, interpolate, Easing } from 'remotion'; +import { colors, fonts } from '../theme'; + +type Props = { + value: number; + label: string; + suffix?: string; + delay?: number; + duration?: number; +}; + +export const BigNumber: React.FC = ({ + value, + label, + suffix = '', + delay = 0, + duration = 45, +}) => { + const frame = useCurrentFrame(); + + const current = Math.round( + interpolate(frame, [delay, delay + duration], [0, value], { + extrapolateLeft: 'clamp', + extrapolateRight: 'clamp', + easing: Easing.out(Easing.cubic), + }), + ); + + const reveal = interpolate(frame, [delay, delay + 10], [0, 1], { + extrapolateLeft: 'clamp', + extrapolateRight: 'clamp', + }); + + return ( +
+
+ {current} + {suffix} +
+
+ {label} +
+
+ ); +}; diff --git a/video/src/components/CodeBlock.tsx b/video/src/components/CodeBlock.tsx new file mode 100644 index 00000000..3b0aa4a7 --- /dev/null +++ b/video/src/components/CodeBlock.tsx @@ -0,0 +1,142 @@ +import React from 'react'; +import { useCurrentFrame, interpolate } from 'remotion'; +import { colors, fonts } from '../theme'; + +type Props = { + code: string; + language?: 'csharp' | 'http' | 'typescript'; + fontSize?: number; + revealStart?: number; + revealDuration?: number; + label?: string; +}; + +const csharpKeywords = new Set([ + 'public', 'class', 'static', 'void', 'async', 'await', 'var', 'return', + 'new', 'this', 'null', 'using', 'record', 'struct', 'interface', + 'string', 'int', 'bool', 'decimal', 'Guid', +]); + +const csharpTypes = new Set([ + 'IEndpoint', 'IEndpointRouteBuilder', 'IModule', 'IServiceCollection', + 'IProductsContracts', 'CancellationToken', 'ProductsPermissions', + 'ProductsModule', 'UsersModule', 'OrdersModule', 'Product', + 'Module', 'Dto', 'ListProducts', 'ProductEndpoints', 'MapGet', + 'MapPost', 'MapPut', 'MapDelete', 'MapCrud', 'Inertia', 'OrderPlaced', + 'RequirePermission', 'AddModule', 'AddModules', 'PublishAsync', + 'GetAllAsync', 'Render', +]); + +const tokenise = (line: string): Array<{ t: string; type: string }> => { + const tokens: Array<{ t: string; type: string }> = []; + const re = + /(\/\/[^\n]*)|("(?:[^"\\]|\\.)*")|(\b\d+\b)|(\[[A-Za-z]+(?:\([^\)]*\))?\])|([A-Za-z_][A-Za-z0-9_]*)|([\s]+)|([^\sA-Za-z0-9_]+)/g; + let m: RegExpExecArray | null; + while ((m = re.exec(line)) !== null) { + if (m[1]) tokens.push({ t: m[1], type: 'comment' }); + else if (m[2]) tokens.push({ t: m[2], type: 'string' }); + else if (m[3]) tokens.push({ t: m[3], type: 'number' }); + else if (m[4]) tokens.push({ t: m[4], type: 'attr' }); + else if (m[5]) { + const w = m[5]; + if (csharpKeywords.has(w)) tokens.push({ t: w, type: 'keyword' }); + else if (csharpTypes.has(w)) tokens.push({ t: w, type: 'type' }); + else tokens.push({ t: w, type: 'ident' }); + } else if (m[6]) tokens.push({ t: m[6], type: 'space' }); + else if (m[7]) tokens.push({ t: m[7], type: 'punct' }); + } + return tokens; +}; + +const colorFor = (type: string) => { + switch (type) { + case 'keyword': return colors.code.keyword; + case 'type': return colors.code.type; + case 'string': return colors.code.string; + case 'comment': return colors.code.comment; + case 'number': return colors.code.number; + case 'attr': return colors.code.attr; + case 'punct': return '#67e8f9'; + default: return colors.code.text; + } +}; + +export const CodeBlock: React.FC = ({ + code, + fontSize = 26, + revealStart = 0, + revealDuration = 40, + label, +}) => { + const frame = useCurrentFrame(); + const lines = code.split('\n'); + const perLine = Math.max(2, revealDuration / lines.length); + + return ( +
+ {label ? ( +
+ {label} +
+ ) : null} + {lines.map((line, i) => { + const start = revealStart + i * perLine; + const opacity = interpolate( + frame, + [start, start + perLine * 0.9], + [0, 1], + { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }, + ); + const translate = interpolate( + frame, + [start, start + perLine], + [8, 0], + { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }, + ); + const tokens = tokenise(line || ' '); + return ( +
+ {tokens.map((tok, j) => ( + + {tok.t} + + ))} +
+ ); + })} +
+ ); +}; diff --git a/video/src/components/GradientBackground.tsx b/video/src/components/GradientBackground.tsx new file mode 100644 index 00000000..8bc28ef1 --- /dev/null +++ b/video/src/components/GradientBackground.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { AbsoluteFill, useCurrentFrame } from 'remotion'; +import { colors } from '../theme'; + +type Props = { + variant?: 'dark' | 'hero' | 'panel'; + // Optional module accent that tints the radial glow; lets each per-module + // composition have its own signature background even with one template. + accent?: string; +}; + +export const GradientBackground: React.FC = ({ variant = 'dark', accent }) => { + const frame = useCurrentFrame(); + const drift = (frame / 6) % 360; + + const glowA = accent ?? colors.accent; + const glowB = accent ?? colors.primary; + + const base = + variant === 'hero' + ? `radial-gradient(circle at 30% 20%, ${glowA}55 0%, transparent 55%), + radial-gradient(circle at 75% 80%, ${glowB}55 0%, transparent 55%), + linear-gradient(135deg, ${colors.bgDeep} 0%, #031a14 100%)` + : variant === 'panel' + ? `linear-gradient(135deg, #04241c 0%, ${colors.bgDeep} 100%)` + : `linear-gradient(135deg, ${colors.bgDeep} 0%, #03110c 100%)`; + + return ( + + + + ); +}; diff --git a/video/src/components/Logo.tsx b/video/src/components/Logo.tsx new file mode 100644 index 00000000..2556884a --- /dev/null +++ b/video/src/components/Logo.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import { useCurrentFrame, useVideoConfig, spring, interpolate } from 'remotion'; +import { colors } from '../theme'; + +type Props = { + size?: number; + delay?: number; +}; + +export const Logo: React.FC = ({ size = 360, delay = 0 }) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const blockProgress = (i: number) => + spring({ + frame: frame - delay - i * 4, + fps, + config: { damping: 14, mass: 0.6, stiffness: 180 }, + }); + + const lineOpacity = interpolate( + frame - delay - 30, + [0, 20], + [0, 1], + { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }, + ); + + const blocks = [ + { x: 96, y: 96, fill: 'url(#grad1)' }, + { x: 276, y: 96, fill: 'url(#grad2)' }, + { x: 96, y: 276, fill: 'url(#grad2)' }, + { x: 276, y: 276, fill: 'url(#grad3)' }, + ]; + + return ( + + + + + + + + + + + + + + + + + {blocks.map((b, i) => { + const p = blockProgress(i); + const cx = b.x + 70; + const cy = b.y + 70; + return ( + + + + ); + })} + + + + + + + + + + + + + ); +}; diff --git a/video/src/components/ModuleChip.tsx b/video/src/components/ModuleChip.tsx new file mode 100644 index 00000000..e8b0e2df --- /dev/null +++ b/video/src/components/ModuleChip.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { useCurrentFrame, useVideoConfig, spring } from 'remotion'; +import { colors, fonts } from '../theme'; + +type Props = { + name: string; + delay: number; +}; + +export const ModuleChip: React.FC = ({ name, delay }) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const p = spring({ + frame: frame - delay, + fps, + config: { damping: 13, mass: 0.6, stiffness: 160 }, + }); + + return ( +
+ {name} +
+ ); +}; diff --git a/video/src/components/SplitCodePair.tsx b/video/src/components/SplitCodePair.tsx new file mode 100644 index 00000000..008e2016 --- /dev/null +++ b/video/src/components/SplitCodePair.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import { colors, fonts } from '../theme'; +import { ArrowFlow } from './ArrowFlow'; + +type Props = { + left: React.ReactNode; + right: React.ReactNode; + leftLabel?: string; + rightLabel?: string; + arrowFrame?: number; +}; + +export const SplitCodePair: React.FC = ({ + left, + right, + leftLabel, + rightLabel, + arrowFrame = 30, +}) => { + return ( +
+
+ {leftLabel ? : null} + {left} +
+ +
+ +
+ +
+ {rightLabel ? : null} + {right} +
+
+ ); +}; + +const SectionLabel: React.FC<{ text: string }> = ({ text }) => ( +
+ {text} +
+); diff --git a/video/src/components/TechBadge.tsx b/video/src/components/TechBadge.tsx new file mode 100644 index 00000000..fee1d1e7 --- /dev/null +++ b/video/src/components/TechBadge.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { useCurrentFrame, useVideoConfig, spring } from 'remotion'; +import { colors, fonts } from '../theme'; + +type Props = { + name: string; + tagline: string; + delay: number; +}; + +export const TechBadge: React.FC = ({ name, tagline, delay }) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const p = spring({ + frame: frame - delay, + fps, + config: { damping: 14, mass: 0.7, stiffness: 150 }, + }); + + return ( +
+
+ {name} +
+
+ {tagline} +
+
+ ); +}; diff --git a/video/src/compositions/ModuleShowcase.tsx b/video/src/compositions/ModuleShowcase.tsx new file mode 100644 index 00000000..0a5cff22 --- /dev/null +++ b/video/src/compositions/ModuleShowcase.tsx @@ -0,0 +1,235 @@ +import React from 'react'; +import { + AbsoluteFill, + Audio, + Sequence, + interpolate, + spring, + staticFile, + useCurrentFrame, + useVideoConfig, +} from 'remotion'; +import { GradientBackground } from '../components/GradientBackground'; +import { Logo } from '../components/Logo'; +import type { ModuleShowcase as ModuleShowcaseData } from '../data/moduleShowcases'; +import { colors, fonts } from '../theme'; + +type Props = { + module: ModuleShowcaseData; + narrationFrom?: number; +}; + +// Single-composition template, 420 frames (14s @ 30fps): +// 0–40f background reveal +// 20–80f module name springs in +// 70–110f tagline fades in +// 120–300f three feature bullets stagger in (60 frames each) +// 320–420f outro CTA fades in +export const ModuleShowcase: React.FC = ({ module, narrationFrom = 18 }) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + const audioDisabled = process.env.REMOTION_DISABLE_AUDIO === '1'; + + const nameSpring = spring({ + frame: frame - 20, + fps, + config: { damping: 14, mass: 0.85, stiffness: 140 }, + }); + + const taglineOpacity = interpolate(frame, [70, 110], [0, 1], { + extrapolateLeft: 'clamp', + extrapolateRight: 'clamp', + }); + + const outroSpring = spring({ + frame: frame - 320, + fps, + config: { damping: 14, mass: 0.8, stiffness: 140 }, + }); + + const musicFadeOutStart = 420 - 35; + + return ( + + + + {/* accent halo behind name */} + +
+ + + +
+ SimpleModule Core +
+ +
+ {module.name} +
+ +
+ {module.tagline} +
+ +
+ {module.features.map((feat, i) => { + const p = spring({ + frame: frame - (120 + i * 55), + fps, + config: { damping: 14, mass: 0.6, stiffness: 160 }, + }); + return ( +
+ + {String(i + 1).padStart(2, '0')} + + {feat} +
+ ); + })} +
+ +
+ +
+ SimpleModule.{module.name} +
+
+
+ + {audioDisabled ? null : ( + <> +