diff --git a/scripts/create_remotion_snapshot.py b/scripts/create_remotion_snapshot.py new file mode 100644 index 000000000..07e0ff767 --- /dev/null +++ b/scripts/create_remotion_snapshot.py @@ -0,0 +1,159 @@ +""" +One-time script to build and register the Remotion Daytona snapshot. + +Run from the repo root: + cd surfsense_backend + uv run python ../scripts/create_remotion_snapshot.py + +Prerequisites: + - DAYTONA_API_KEY set in surfsense_backend/.env (or exported in shell) + - DAYTONA_API_URL=https://app.daytona.io/api + - DAYTONA_TARGET=us (or eu) + +After this script succeeds, add to surfsense_backend/.env: + DAYTONA_REMOTION_SNAPSHOT_ID=remotion-surfsense +""" + +import os +import sys +from pathlib import Path + +from dotenv import load_dotenv + +# Load .env from surfsense_backend (works whether you run from repo root or backend dir) +_here = Path(__file__).parent +for candidate in [_here / "../surfsense_backend/.env", _here / ".env"]: + if candidate.exists(): + load_dotenv(candidate) + break + +from daytona import CreateSnapshotParams, Daytona, Image, Resources # noqa: E402 + +SNAPSHOT_NAME = "remotion-surfsense" + +# The Daytona sandbox runs as user 'daytona' with home /home/daytona. +# All project files and output must live under this path so they are +# accessible in the running sandbox (files placed outside /home/daytona +# during the Docker build are not visible to the daytona user session). +PROJECT_DIR = "/home/daytona/remotion-project" +OUT_DIR = "/home/daytona/out" +SKILLS_DIR = "/home/daytona/skills" +# Official @remotion/skills package — sparse-cloned during image build. +# Agent reads SKILL.md first (progressive disclosure), then individual rules files. +REMOTION_SKILLS_DIR = f"{SKILLS_DIR}/remotion-best-practices" +REMOTION_SKILLS_REPO = "https://github.com/remotion-dev/remotion.git" +REMOTION_SKILLS_REPO_PATH = "packages/skills/skills/remotion" + +# NVM-managed node/npm/npx paths inside daytonaio/sandbox:0.6.0. +# These are stable paths regardless of which Node version NVM has active. +NPM = "/usr/local/share/nvm/current/bin/npm" +NPX = "/usr/local/share/nvm/current/bin/npx" + +# Linux shared library dependencies required by Chrome Headless Shell. +# Source: https://remotion.dev/docs/miscellaneous/linux-dependencies +REMOTION_LINUX_DEPS = ( + "libnss3 libdbus-1-3 libatk1.0-0 libgbm-dev libasound2 " + "libxrandr2 libxkbcommon-dev libxfixes3 libxcomposite1 " + "libxdamage1 libatk-bridge2.0-0 libpango-1.0-0 libcairo2 libcups2" +) + + +def build_image() -> Image: + """ + Declaratively build the Remotion sandbox image. + + Base image: daytonaio/sandbox:0.6.0 + - The Daytona runtime base. Sandboxes always run as user 'daytona' + in /home/daytona — files placed outside that path during the Docker + build are not accessible to the user session. + - Already ships with git, NVM, Node.js, and Python. + + Layers (each cached independently by Daytona): + 1. Linux shared libraries required by Chrome Headless Shell + 2. Clone remotion-dev/template-helloworld into /home/daytona/remotion-project + 3. npm install (via NVM's npm) + 4. Pre-download Chrome Headless Shell (via NVM's npx) + 5. Sparse-clone official @remotion/skills into /home/daytona/skills/remotion-best-practices + 6. Pre-create output directory, set daytona ownership + """ + return ( + Image.base("daytonaio/sandbox:0.6.0") + .run_commands( + # daytonaio/sandbox runs as a non-root user, so apt-get needs sudo. + # Chrome Headless Shell needs these shared libs on Debian/Ubuntu. + # Do NOT install 'chromium' — Remotion manages its own pinned Chrome. + f"sudo apt-get update && sudo apt-get install -y {REMOTION_LINUX_DEPS}" + " && sudo rm -rf /var/lib/apt/lists/*", + ) + .env({"CI": "true"}) + .run_commands( + # Clone the Hello World template into the daytona user's home. + # We use Hello World (not blank) so the agent has working reference + # code to read before overwriting it with the generated video. + f"git clone --depth 1" + f" https://github.com/remotion-dev/template-helloworld.git {PROJECT_DIR}", + # Install npm dependencies using NVM's npm (already in the base image). + f"cd {PROJECT_DIR} && {NPM} install", + # Pre-download Remotion's pinned Chrome Headless Shell. + f"cd {PROJECT_DIR} && {NPX} remotion browser ensure", + # Sparse-clone just the @remotion/skills directory from the monorepo. + # --filter=blob:none + --sparse avoids downloading the entire ~400 MB repo. + f"mkdir -p {REMOTION_SKILLS_DIR} {OUT_DIR}" + f" && git clone --depth 1 --filter=blob:none --sparse" + f" {REMOTION_SKILLS_REPO} /tmp/remotion-skills" + f" && cd /tmp/remotion-skills" + f" && git sparse-checkout set {REMOTION_SKILLS_REPO_PATH}" + f" && cp -r {REMOTION_SKILLS_REPO_PATH}/. {REMOTION_SKILLS_DIR}/" + f" && rm -rf /tmp/remotion-skills", + # Overwrite the cloned SKILL.md description so the agent matches this skill + # for every Remotion video task (the upstream description is too generic). + f"python3 -c \"" + f"import re, pathlib; " + f"p = pathlib.Path('{REMOTION_SKILLS_DIR}/SKILL.md'); " + f"txt = p.read_text(); " + f"txt = re.sub(r'description:.*', " + f"'description: Use this skill for ALL Remotion video generation tasks. " + f"Contains critical patterns for Sequence/Series timing, spring-physics animations, " + f"chart and SVG layout, component composition, geometry blocks, and common visual bug fixes. " + f"Always read this before writing any Remotion code.', txt); " + f"p.write_text(txt)" + f"\"", + # Give the daytona user ownership of everything we just created. + f"chown -R daytona:daytona {PROJECT_DIR} {OUT_DIR} {SKILLS_DIR}", + ) + .cmd(["sleep", "infinity"]) + ) + + +def main() -> None: + api_key = os.environ.get("DAYTONA_API_KEY") + if not api_key: + print("ERROR: DAYTONA_API_KEY is not set.", file=sys.stderr) + print("Add it to surfsense_backend/.env or export it in your shell.", file=sys.stderr) + sys.exit(1) + + print(f"Building snapshot '{SNAPSHOT_NAME}' …") + print("This takes 5–10 minutes (Chrome Headless Shell download included). Logs stream below:\n") + + daytona = Daytona() + + daytona.snapshot.create( + CreateSnapshotParams( + name=SNAPSHOT_NAME, + image=build_image(), + resources=Resources( + cpu=2, # 2 vCPU — Remotion renders frames in parallel + memory=4, # 4 GiB — Chrome Headless Shell + Node can use 2 GiB under load + disk=8, # 8 GiB — image + node_modules (~800 MB with Chrome) + MP4 output + ), + ), + on_logs=lambda chunk: print(chunk, end="", flush=True), + ) + + print(f"\n\n✅ Snapshot '{SNAPSHOT_NAME}' is ready.") + print("\nAdd this to surfsense_backend/.env:") + print(f" DAYTONA_REMOTION_SNAPSHOT_ID={SNAPSHOT_NAME}") + + +if __name__ == "__main__": + main() diff --git a/scripts/create_remotion_snapshot_v2.py b/scripts/create_remotion_snapshot_v2.py new file mode 100644 index 000000000..ce34ca7f1 --- /dev/null +++ b/scripts/create_remotion_snapshot_v2.py @@ -0,0 +1,148 @@ +""" +One-time script to build and register the Remotion Daytona snapshot (V2). + +Extends V1 by installing additional npm packages required by prompts_v2: + remotion-bits, culori, lucide-react, + @remotion/paths, @remotion/shapes, + @remotion/layout-utils, @remotion/animation-utils + +Run from the repo root: + cd surfsense_backend + uv run python ../scripts/create_remotion_snapshot_v2.py + +Prerequisites: + - DAYTONA_API_KEY set in surfsense_backend/.env (or exported in shell) + - DAYTONA_API_URL=https://app.daytona.io/api + - DAYTONA_TARGET=us (or eu) + +After this script succeeds, add to surfsense_backend/.env: + DAYTONA_REMOTION_SNAPSHOT_ID=remotion-surfsense-v2 +""" + +import os +import sys +import time +from pathlib import Path + +from dotenv import load_dotenv + +_here = Path(__file__).parent +for candidate in [_here / "../surfsense_backend/.env", _here / ".env"]: + if candidate.exists(): + load_dotenv(candidate) + break + +from daytona import CreateSnapshotParams, Daytona, Image, Resources # noqa: E402 + +SNAPSHOT_NAME = "remotion-surfsense-v2" +OLD_SNAPSHOT_NAME = "remotion-surfsense" + +PROJECT_DIR = "/home/daytona/remotion-project" +OUT_DIR = "/home/daytona/out" + +NPM = "/usr/local/share/nvm/current/bin/npm" +NPX = "/usr/local/share/nvm/current/bin/npx" + +REMOTION_LINUX_DEPS = ( + "libnss3 libdbus-1-3 libatk1.0-0 libgbm-dev libasound2 " + "libxrandr2 libxkbcommon-dev libxfixes3 libxcomposite1 " + "libxdamage1 libatk-bridge2.0-0 libpango-1.0-0 libcairo2 libcups2" +) + +V2_PACKAGES = ( + "remotion-bits" + " culori" + " lucide-react" + " @remotion/paths" + " @remotion/shapes" + " @remotion/layout-utils" + " @remotion/animation-utils" + " @remotion/google-fonts" +) + + +def build_image() -> Image: + """ + Build the V2 Remotion sandbox image. + + Layers: + 1. Linux shared libraries required by Chrome Headless Shell + 2. Clone remotion template into /home/daytona/remotion-project + 3. npm install (base + V2 packages) + 4. Pre-download Chrome Headless Shell + 5. Create output directory, set ownership + + Skills are NOT included — they live in the backend repo at + surfsense_backend/app/agents/video/skills/ and are loaded by the + pipeline into the system prompt at runtime. + """ + return ( + Image.base("daytonaio/sandbox:0.6.0") + .run_commands( + f"sudo apt-get update && sudo apt-get install -y {REMOTION_LINUX_DEPS}" + " && sudo rm -rf /var/lib/apt/lists/*", + ) + .env({"CI": "true"}) + .run_commands( + f"git clone --depth 1" + f" https://github.com/remotion-dev/template-helloworld.git {PROJECT_DIR}", + f"cd {PROJECT_DIR} && {NPM} install", + f"cd {PROJECT_DIR} && {NPM} install {V2_PACKAGES}", + f"cd {PROJECT_DIR} && {NPX} remotion browser ensure", + f"mkdir -p {OUT_DIR}", + f"chown -R daytona:daytona {PROJECT_DIR} {OUT_DIR}", + ) + .cmd(["sleep", "infinity"]) + ) + + +def main() -> None: + api_key = os.environ.get("DAYTONA_API_KEY") + if not api_key: + print("ERROR: DAYTONA_API_KEY is not set.", file=sys.stderr) + print("Add it to surfsense_backend/.env or export it in your shell.", file=sys.stderr) + sys.exit(1) + + daytona = Daytona() + + for name in [OLD_SNAPSHOT_NAME, SNAPSHOT_NAME]: + try: + old = daytona.snapshot.get(name) + print(f"Deleting existing snapshot '{name}' …") + daytona.snapshot.delete(old) + print(f"Deleted '{name}'. Waiting for removal to propagate …") + for attempt in range(30): + time.sleep(2) + try: + daytona.snapshot.get(name) + except Exception: + print(f"Confirmed '{name}' is gone.\n") + break + else: + print(f"WARNING: '{name}' may still exist after 60s. Proceeding anyway.\n") + except Exception: + pass + + print(f"Building snapshot '{SNAPSHOT_NAME}' …") + print("This takes 5–10 minutes (Chrome Headless Shell download included). Logs stream below:\n") + + daytona.snapshot.create( + CreateSnapshotParams( + name=SNAPSHOT_NAME, + image=build_image(), + resources=Resources( + cpu=2, + memory=4, + disk=8, + ), + ), + on_logs=lambda chunk: print(chunk, end="", flush=True), + ) + + print(f"\n\n✅ Snapshot '{SNAPSHOT_NAME}' is ready.") + print("\nAdd this to surfsense_backend/.env:") + print(f" DAYTONA_REMOTION_SNAPSHOT_ID={SNAPSHOT_NAME}") + + +if __name__ == "__main__": + main() diff --git a/scripts/verify_remotion_snapshot.py b/scripts/verify_remotion_snapshot.py new file mode 100644 index 000000000..9df3e1a0f --- /dev/null +++ b/scripts/verify_remotion_snapshot.py @@ -0,0 +1,108 @@ +""" +Verify the Remotion snapshot works end-to-end before running the full agent. + +Run from repo root: + cd surfsense_backend + uv run python ../scripts/verify_remotion_snapshot.py + +Checks: + 1. tsc --noEmit passes with 0 errors + 2. npx remotion render produces a real MP4 file + 3. /skills directory exists +""" + +import os +import sys +from pathlib import Path + +from dotenv import load_dotenv + +_here = Path(__file__).parent +for candidate in [_here / "../surfsense_backend/.env", _here / ".env"]: + if candidate.exists(): + load_dotenv(candidate) + break + +from daytona import CreateSandboxFromSnapshotParams, Daytona # noqa: E402 + +SNAPSHOT_NAME = os.environ.get("DAYTONA_REMOTION_SNAPSHOT_ID", "remotion-surfsense") + + +def check(label: str, result) -> bool: + ok = result.exit_code == 0 + status = "✅" if ok else "❌" + print(f"\n{status} {label}") + if result.result: + for line in result.result.strip().splitlines()[-5:]: + print(f" {line}") + return ok + + +def main() -> None: + print(f"Creating test sandbox from snapshot '{SNAPSHOT_NAME}' …") + daytona = Daytona() + sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot=SNAPSHOT_NAME)) + print(f"Sandbox ready: {sandbox.id}\n") + + passed = True + + try: + PROJECT = "/home/daytona/remotion-project" + OUT = "/home/daytona/out" + NPX = "/usr/local/share/nvm/current/bin/npx" + + # 1 — TypeScript check + passed &= check( + "tsc --noEmit", + sandbox.process.exec(f"cd {PROJECT} && {NPX} tsc --noEmit 2>&1"), + ) + + # 2 — Render a test MP4 + passed &= check( + "npx remotion render HelloWorld", + sandbox.process.exec( + f"cd {PROJECT} && {NPX} remotion render HelloWorld {OUT}/test.mp4 2>&1", + timeout=120, + ), + ) + + # 3 — Confirm the MP4 file was produced + passed &= check( + "MP4 file exists", + sandbox.process.exec(f"ls -lh {OUT}/test.mp4"), + ) + + # 4 — Chrome Headless Shell is pre-downloaded + passed &= check( + "Chrome Headless Shell exists", + sandbox.process.exec( + f"ls {PROJECT}/node_modules/.remotion/chrome-headless-shell/" + ), + ) + + # 5 — Official @remotion/skills are present (SKILL.md + rules/) + passed &= check( + "remotion skills SKILL.md exists", + sandbox.process.exec("ls /home/daytona/skills/remotion-best-practices/SKILL.md"), + ) + passed &= check( + "remotion skills rules/ populated", + sandbox.process.exec("ls /home/daytona/skills/remotion-best-practices/rules/ | wc -l"), + ) + + finally: + print("\nCleaning up test sandbox …") + sandbox.delete() + + print() + if passed: + print("✅ All checks passed. The snapshot is ready for the video agent.") + print(f"\nMake sure surfsense_backend/.env contains:") + print(f" DAYTONA_REMOTION_SNAPSHOT_ID={SNAPSHOT_NAME}") + else: + print("❌ Some checks failed. Fix the issues and re-run create_remotion_snapshot.py.") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/scripts/verify_remotion_snapshot_v2.py b/scripts/verify_remotion_snapshot_v2.py new file mode 100644 index 000000000..33dd813ff --- /dev/null +++ b/scripts/verify_remotion_snapshot_v2.py @@ -0,0 +1,120 @@ +""" +Verify the V2 Remotion snapshot works end-to-end. + +Run from repo root: + cd surfsense_backend + uv run python ../scripts/verify_remotion_snapshot_v2.py + +Checks: + 1. tsc --noEmit passes + 2. npx remotion render produces a real MP4 + 3. Chrome Headless Shell pre-downloaded + 4. V2 packages installed (remotion-bits, lucide-react, @remotion/paths, etc.) +""" + +import os +import sys +from pathlib import Path + +from dotenv import load_dotenv + +_here = Path(__file__).parent +for candidate in [_here / "../surfsense_backend/.env", _here / ".env"]: + if candidate.exists(): + load_dotenv(candidate) + break + +from daytona import CreateSandboxFromSnapshotParams, Daytona # noqa: E402 + +SNAPSHOT_NAME = os.environ.get("DAYTONA_REMOTION_SNAPSHOT_ID", "remotion-surfsense-v2") + +V2_PACKAGES = [ + "remotion-bits", + "culori", + "lucide-react", + "@remotion/paths", + "@remotion/shapes", + "@remotion/layout-utils", + "@remotion/animation-utils", + "@remotion/google-fonts", +] + + +def check(label: str, result) -> bool: + ok = result.exit_code == 0 + status = "✅" if ok else "❌" + print(f"\n{status} {label}") + if result.result: + for line in result.result.strip().splitlines()[-5:]: + print(f" {line}") + return ok + + +def main() -> None: + print(f"Creating test sandbox from snapshot '{SNAPSHOT_NAME}' …") + daytona = Daytona() + sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot=SNAPSHOT_NAME)) + print(f"Sandbox ready: {sandbox.id}\n") + + passed = True + + try: + PROJECT = "/home/daytona/remotion-project" + OUT = "/home/daytona/out" + NPX = "/usr/local/share/nvm/current/bin/npx" + + # 1 — TypeScript check + passed &= check( + "tsc --noEmit", + sandbox.process.exec(f"cd {PROJECT} && {NPX} tsc --noEmit 2>&1"), + ) + + # 2 — Render a test MP4 + passed &= check( + "npx remotion render HelloWorld", + sandbox.process.exec( + f"cd {PROJECT} && {NPX} remotion render HelloWorld {OUT}/test.mp4 2>&1", + timeout=120, + ), + ) + + # 3 — Confirm the MP4 file was produced + passed &= check( + "MP4 file exists", + sandbox.process.exec(f"ls -lh {OUT}/test.mp4"), + ) + + # 4 — Chrome Headless Shell is pre-downloaded + passed &= check( + "Chrome Headless Shell exists", + sandbox.process.exec( + f"ls {PROJECT}/node_modules/.remotion/chrome-headless-shell/" + ), + ) + + # 5 — V2 packages installed + for pkg in V2_PACKAGES: + pkg_dir = pkg if not pkg.startswith("@") else pkg + passed &= check( + f"npm package: {pkg}", + sandbox.process.exec( + f"ls {PROJECT}/node_modules/{pkg_dir}/package.json" + ), + ) + + finally: + print("\nCleaning up test sandbox …") + sandbox.delete() + + print() + if passed: + print("✅ All checks passed. The V2 snapshot is ready for the video agent.") + print(f"\nMake sure surfsense_backend/.env contains:") + print(f" DAYTONA_REMOTION_SNAPSHOT_ID={SNAPSHOT_NAME}") + else: + print("❌ Some checks failed. Fix the issues and re-run create_remotion_snapshot_v2.py.") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/surfsense_backend/alembic/versions/102_add_video_llm_id_to_search_space.py b/surfsense_backend/alembic/versions/102_add_video_llm_id_to_search_space.py new file mode 100644 index 000000000..a5a55ca5f --- /dev/null +++ b/surfsense_backend/alembic/versions/102_add_video_llm_id_to_search_space.py @@ -0,0 +1,52 @@ +"""Add video_llm_id to searchspaces + +Revision ID: 102 +Revises: 101 +Create Date: 2026-02-27 +""" + +import sqlalchemy as sa + +from alembic import op + +revision: str = "102" +down_revision: str | None = "101" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + connection = op.get_bind() + result = connection.execute( + sa.text( + """ + SELECT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'searchspaces' + AND column_name = 'video_llm_id' + ) + """ + ) + ) + if not result.scalar(): + op.add_column( + "searchspaces", + sa.Column("video_llm_id", sa.Integer(), nullable=True), + ) + + +def downgrade() -> None: + connection = op.get_bind() + result = connection.execute( + sa.text( + """ + SELECT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'searchspaces' + AND column_name = 'video_llm_id' + ) + """ + ) + ) + if result.scalar(): + op.drop_column("searchspaces", "video_llm_id") diff --git a/surfsense_backend/app/agents/new_chat/chat_deepagent.py b/surfsense_backend/app/agents/new_chat/chat_deepagent.py index 5fcb8236d..e03f282b2 100644 --- a/surfsense_backend/app/agents/new_chat/chat_deepagent.py +++ b/surfsense_backend/app/agents/new_chat/chat_deepagent.py @@ -137,6 +137,7 @@ async def create_surfsense_deep_agent( The agent comes with built-in tools that can be configured: - search_knowledge_base: Search the user's personal knowledge base - generate_podcast: Generate audio podcasts from content + - generate_video: Generate animated video components from content - generate_image: Generate images from text descriptions using AI models - link_preview: Fetch rich previews for URLs - display_image: Display images in chat diff --git a/surfsense_backend/app/agents/new_chat/system_prompt.py b/surfsense_backend/app/agents/new_chat/system_prompt.py index 1ca036c40..c99bcae03 100644 --- a/surfsense_backend/app/agents/new_chat/system_prompt.py +++ b/surfsense_backend/app/agents/new_chat/system_prompt.py @@ -160,7 +160,27 @@ def _get_system_instructions( * NEVER call search_knowledge_base and then pass its results to generate_report. The tool handles KB search internally. - AFTER CALLING THIS TOOL: Do NOT repeat, summarize, or reproduce the report content in the chat. The report is already displayed as an interactive card that the user can open, read, copy, and export. Simply confirm that the report was generated (e.g., "I've generated your report on [topic]. You can view the Markdown report now, and export to PDF/DOCX from the card."). NEVER write out the report text in the chat. -4. link_preview: Fetch metadata for a URL to display a rich preview card. +4. generate_video: Generate an animated Remotion video component from conversation content. + - Use this when the user's request is about creating visual/animated content. + - Valid use cases: + * Animated text, titles, or typography + * Data visualizations (charts, graphs, progress bars) + * Explainer animations or animated summaries + * Social media content (stories, reels, posts) + * Kinetic typography or abstract motion graphics + * Countdown timers, loading animations + * Any visual/animated content request + - Invalid use cases (answer in chat instead): + * Questions (e.g., "What is...?", "How do I...?") + * Requests for text/written content (essays, summaries, explanations) + * Non-visual tasks (calculations, translations) + - Args: + - topic: Short title for the video (max ~8 words). + - source_content: Comprehensive summary of the content to animate. The richer this is, the better the animation quality. + - Returns: A live video player rendered directly in the chat — no download required for preview. + - After calling: confirm with one sentence (e.g., "Here's your animated video on [topic]."). Do NOT reproduce the content in chat. + +5. link_preview: Fetch metadata for a URL to display a rich preview card. - IMPORTANT: Use this tool WHENEVER the user shares or mentions a URL/link in their message. - This fetches the page's Open Graph metadata (title, description, thumbnail) to show a preview card. - NOTE: This tool only fetches metadata, NOT the full page content. It cannot read the article text. @@ -173,7 +193,7 @@ def _get_system_instructions( - Returns: A rich preview card with title, description, thumbnail, and domain - The preview card will automatically be displayed in the chat. -5. display_image: Display an image in the chat with metadata. +6. display_image: Display an image in the chat with metadata. - Use this tool ONLY when you have a valid public HTTP/HTTPS image URL to show. - This displays the image with an optional title, description, and source attribution. - Valid use cases: @@ -198,7 +218,7 @@ def _get_system_instructions( - Returns: An image card with the image, title, and description - The image will automatically be displayed in the chat. -6. generate_image: Generate images from text descriptions using AI image models. +7. generate_image: Generate images from text descriptions using AI image models. - Use this when the user asks you to create, generate, draw, design, or make an image. - Trigger phrases: "generate an image of", "create a picture of", "draw me", "make an image", "design a logo", "create artwork" - Args: @@ -212,7 +232,7 @@ def _get_system_instructions( expand and improve the prompt with specific details about style, lighting, composition, and mood. - If the user's request is vague (e.g., "make me an image of a cat"), enhance the prompt with artistic details. -7. scrape_webpage: Scrape and extract the main content from a webpage. +8. scrape_webpage: Scrape and extract the main content from a webpage. - Use this when the user wants you to READ and UNDERSTAND the actual content of a webpage. - IMPORTANT: This is different from link_preview: * link_preview: Only fetches metadata (title, description, thumbnail) for display diff --git a/surfsense_backend/app/agents/new_chat/tools/__init__.py b/surfsense_backend/app/agents/new_chat/tools/__init__.py index 0a11951f0..c24a2f993 100644 --- a/surfsense_backend/app/agents/new_chat/tools/__init__.py +++ b/surfsense_backend/app/agents/new_chat/tools/__init__.py @@ -8,6 +8,7 @@ - search_knowledge_base: Search the user's personal knowledge base - search_surfsense_docs: Search Surfsense documentation for usage help - generate_podcast: Generate audio podcasts from content +- generate_video: Generate animated Remotion video components from content - generate_image: Generate images from text descriptions using AI models - link_preview: Fetch rich previews for URLs - display_image: Display images in chat @@ -39,6 +40,7 @@ from .scrape_webpage import create_scrape_webpage_tool from .search_surfsense_docs import create_search_surfsense_docs_tool from .user_memory import create_recall_memory_tool, create_save_memory_tool +from .video import create_generate_video_tool __all__ = [ # Registry @@ -51,6 +53,7 @@ "create_display_image_tool", "create_generate_image_tool", "create_generate_podcast_tool", + "create_generate_video_tool", "create_link_preview_tool", "create_recall_memory_tool", "create_save_memory_tool", diff --git a/surfsense_backend/app/agents/new_chat/tools/registry.py b/surfsense_backend/app/agents/new_chat/tools/registry.py index dffed5e86..f282d3c20 100644 --- a/surfsense_backend/app/agents/new_chat/tools/registry.py +++ b/surfsense_backend/app/agents/new_chat/tools/registry.py @@ -73,6 +73,7 @@ async def my_tool(param: str) -> dict: create_save_shared_memory_tool, ) from .user_memory import create_recall_memory_tool, create_save_memory_tool +from .video import create_generate_video_tool # ============================================================================= # Tool Definition @@ -122,6 +123,17 @@ class ToolDefinition: requires=["search_space_id", "db_session", "connector_service"], # Note: available_connectors and available_document_types are optional ), + # Video generation tool + ToolDefinition( + name="generate_video", + description="Generate an animated Remotion video from conversation content", + factory=lambda deps: create_generate_video_tool( + search_space_id=deps["search_space_id"], + thread_id=deps["thread_id"], + db_session=deps["db_session"], + ), + requires=["search_space_id", "thread_id", "db_session"], + ), # Podcast generation tool ToolDefinition( name="generate_podcast", diff --git a/surfsense_backend/app/agents/new_chat/tools/report.py b/surfsense_backend/app/agents/new_chat/tools/report.py index 0896fea4b..50753cf4b 100644 --- a/surfsense_backend/app/agents/new_chat/tools/report.py +++ b/surfsense_backend/app/agents/new_chat/tools/report.py @@ -36,6 +36,7 @@ from app.db import Report, async_session_maker from app.services.connector_service import ConnectorService from app.services.llm_service import get_document_summary_llm +from app.utils.content_utils import strip_code_fences logger = logging.getLogger(__name__) @@ -191,26 +192,7 @@ # ─── Utility Functions ────────────────────────────────────────────────────── -def _strip_wrapping_code_fences(text: str) -> str: - """Remove wrapping code fences that LLMs often add around Markdown output. - - Handles patterns like: - ```markdown\\n...content...\\n``` - ````markdown\\n...content...\\n```` - ```md\\n...content...\\n``` - ```\\n...content...\\n``` - ```json\\n...content...\\n``` - Supports 3 or more backticks (LLMs escalate when content has triple-backtick blocks). - """ - stripped = text.strip() - # Match opening fence with 3+ backticks and optional language tag - m = re.match(r"^(`{3,})(?:markdown|md|json)?\s*\n", stripped) - if m: - fence = m.group(1) # e.g. "```" or "````" - if stripped.endswith(fence): - stripped = stripped[m.end() :] # remove opening fence - stripped = stripped[: -len(fence)].rstrip() # remove closing fence - return stripped +_strip_wrapping_code_fences = strip_code_fences def _extract_metadata(content: str) -> dict[str, Any]: diff --git a/surfsense_backend/app/agents/new_chat/tools/video/__init__.py b/surfsense_backend/app/agents/new_chat/tools/video/__init__.py new file mode 100644 index 000000000..556945341 --- /dev/null +++ b/surfsense_backend/app/agents/new_chat/tools/video/__init__.py @@ -0,0 +1,3 @@ +from .generate_video import create_generate_video_tool + +__all__ = ["create_generate_video_tool"] diff --git a/surfsense_backend/app/agents/new_chat/tools/video/generate_video.py b/surfsense_backend/app/agents/new_chat/tools/video/generate_video.py new file mode 100644 index 000000000..a7e925a1b --- /dev/null +++ b/surfsense_backend/app/agents/new_chat/tools/video/generate_video.py @@ -0,0 +1,61 @@ +"""Chat tool: prepare video generation from conversation content. + +When the chat agent decides to create a video, it calls this tool. +The tool returns the topic and source_content. The frontend then: + 1. Calls POST /api/v1/video/generate-script to get VideoInput JSON + 2. Renders the video via Remotion Lambda + 3. Displays the result +""" + +import logging +from typing import Any + +from langchain_core.tools import tool +from sqlalchemy.ext.asyncio import AsyncSession + +logger = logging.getLogger(__name__) + + +def create_generate_video_tool( + search_space_id: int, + thread_id: int, + db_session: AsyncSession, +): + @tool + async def generate_video( + topic: str, + source_content: str, + ) -> dict[str, Any]: + """Prepare video generation from conversation content. + + Use this when the user asks to create, generate, or make a video, + animate content, export as video, or make an animated summary. + + IMPORTANT — source_content must be structured for VISUAL animation, + NOT a text essay. Organize the content by themes with short phrases: + - Key statistics and numbers + - Hierarchies and taxonomies + - Processes and sequences + - Comparisons and contrasts + - Relationships between concepts + - Important definitions and quotes + + Keep labels SHORT (max 6-8 words) and descriptions concise (max 2 sentences). + + Args: + topic: Short title for the video (max ~8 words). + source_content: Structured content organized by themes for visual animation. + + Returns: + A dictionary with topic and source_content for the frontend to render. + """ + logger.info("[generate_video] Preparing video for '%s'", topic) + + return { + "status": "success", + "topic": topic, + "source_content": source_content, + "search_space_id": search_space_id, + } + + return generate_video diff --git a/surfsense_backend/app/agents/video/__init__.py b/surfsense_backend/app/agents/video/__init__.py new file mode 100644 index 000000000..111cc4d67 --- /dev/null +++ b/surfsense_backend/app/agents/video/__init__.py @@ -0,0 +1,9 @@ +"""Video generation package. + +Public API: + generate_video_script() — LLM structured output → VideoInput JSON +""" + +from app.agents.video.script_generator import generate_video_script + +__all__ = ["generate_video_script"] diff --git a/surfsense_backend/app/agents/video/script_generator.py b/surfsense_backend/app/agents/video/script_generator.py new file mode 100644 index 000000000..fe37b205c --- /dev/null +++ b/surfsense_backend/app/agents/video/script_generator.py @@ -0,0 +1,51 @@ +"""Video script generator. + +Flow: topic + source_content -> LLM (structured output) -> VideoInput JSON. +The frontend then renders the JSON via Remotion Lambda. +""" + +from __future__ import annotations + +import logging + +from langchain_core.messages import HumanMessage, SystemMessage +from sqlalchemy.ext.asyncio import AsyncSession + +from app.agents.video.script_prompt import SCRIPT_SYSTEM_PROMPT +from app.schemas.video import VideoInput +from app.services.llm_service import get_video_llm + +logger = logging.getLogger(__name__) + + +async def generate_video_script( + session: AsyncSession, + search_space_id: int, + topic: str, + source_content: str, +) -> VideoInput: + """Generate a VideoInput script from topic + source content using structured output.""" + llm = await get_video_llm(session, search_space_id) + if not llm: + raise ValueError("No LLM configured. Please configure a language model in Settings.") + + structured_llm = llm.with_structured_output(VideoInput) + + user_prompt = ( + f"Topic: {topic}\n\n" + f"Source Content:\n{source_content}" + ) + + logger.info("[video/script_generator] Generating video script for: '%s'", topic) + + result = await structured_llm.ainvoke([ + SystemMessage(content=SCRIPT_SYSTEM_PROMPT), + HumanMessage(content=user_prompt), + ]) + + logger.info( + "[video/script_generator] Generated %d scenes for '%s'", + len(result.scenes), + topic, + ) + return result diff --git a/surfsense_backend/app/agents/video/script_prompt.py b/surfsense_backend/app/agents/video/script_prompt.py new file mode 100644 index 000000000..fba9a7029 --- /dev/null +++ b/surfsense_backend/app/agents/video/script_prompt.py @@ -0,0 +1,60 @@ +"""System prompt for structured video script generation. + +Used by script_generator.generate_video_script() to guide the LLM +in producing a VideoInput JSON via structured output. +""" + +SCRIPT_SYSTEM_PROMPT = """\ +You are a video script architect. Given a topic and source content, produce a \ +structured VideoInput JSON that will be rendered as an animated infographic video. + +SCENE TYPES — use the right type for each piece of content: + + intro Opening title card. Use once at the start. + Fields: title (required), subtitle (optional). + + spotlight Full-screen card for a single standout data point. + Each item has a "category" discriminator: + stat — numeric statistic (title, value as string, desc?, color) + info — informational card (title, subtitle?, desc, tag?, color) + quote — quote with attribution (quote, author, role?, color) + profile — person card (name, role, desc?, tag?, color) + progress — progress metric (title, value as number, max?, desc?, color) + fact — single statement (statement, source?, color) + definition — term + definition (term, definition, example?, color) + 1-8 items per spotlight scene; 1 item = single full-screen card. + + hierarchy Tree / taxonomy / org chart. Recursive nodes with optional children. + Fields per node: label (required), color?, desc?, children? (nested). + Provide root-level nodes in "items" array. + + list Ranked items, feature lists, bullet points. + Fields per item: label (required), desc?, value? (string or number), color?. + + sequence Ordered process, timeline, step-by-step flow. + Fields per item: label (required), desc?, color?. + + chart Numeric data — rendered as bar, column, pie, or line chart. + Fields per item: label (required), value (number, required), color?. + Scene also supports: xTitle?, yTitle?. + + relation Network graph with nodes and directed edges. + Node fields: id (required, unique), label (required), desc?, color?. + Edge fields: from (node id), to (node id), label?. + + comparison Side-by-side comparison (A vs B, pros/cons). + At least 2 groups, each with: label, color?, items (label, desc?). + + outro Closing card. Use once at the end. + Fields: title? (defaults to "Thank you"), subtitle?. + +GUIDELINES: +- Start with an intro scene and end with an outro scene. +- Create as many content scenes as the source material warrants — no filler. +- Use diverse scene types — pick the type that best fits each piece of content. +- Avoid repeating the same scene type consecutively. +- Keep text SHORT — max 6-8 words per label, max 2 sentences per description. +- Use hex colors (e.g. "#3b82f6") — vary colors across scenes for visual diversity. +- Extract real data, facts, and structure from the source content. +- If the source content is thin, keep the video concise rather than padding. +""" diff --git a/surfsense_backend/app/config/model_list_fallback.json b/surfsense_backend/app/config/model_list_fallback.json index 42a3052f3..d3d60b5a5 100644 --- a/surfsense_backend/app/config/model_list_fallback.json +++ b/surfsense_backend/app/config/model_list_fallback.json @@ -342,7 +342,7 @@ "expiration_date": null }, { - "id": "anthropic/claude-opus-4.6", + "id": "anthropic/claude-opus-4-6", "canonical_slug": "anthropic/claude-4.6-opus-20260205", "hugging_face_id": "", "name": "Anthropic: Claude Opus 4.6", diff --git a/surfsense_backend/app/db.py b/surfsense_backend/app/db.py index 1c9181ed2..60c98fde9 100644 --- a/surfsense_backend/app/db.py +++ b/surfsense_backend/app/db.py @@ -1197,6 +1197,9 @@ class SearchSpace(BaseModel, TimestampMixin): image_generation_config_id = Column( Integer, nullable=True, default=0 ) # For image generation, defaults to Auto mode + video_llm_id = Column( + Integer, nullable=True, default=0 + ) # For video code generation, defaults to Auto mode user_id = Column( UUID(as_uuid=True), ForeignKey("user.id", ondelete="CASCADE"), nullable=False diff --git a/surfsense_backend/app/routes/__init__.py b/surfsense_backend/app/routes/__init__.py index 6114dd207..91b2a6077 100644 --- a/surfsense_backend/app/routes/__init__.py +++ b/surfsense_backend/app/routes/__init__.py @@ -42,6 +42,7 @@ from .slack_add_connector_route import router as slack_add_connector_router from .surfsense_docs_routes import router as surfsense_docs_router from .teams_add_connector_route import router as teams_add_connector_router +from .video_routes import router as video_router router = APIRouter() @@ -79,3 +80,4 @@ router.include_router(composio_router) # Composio OAuth and toolkit management router.include_router(public_chat_router) # Public chat sharing and cloning router.include_router(incentive_tasks_router) # Incentive tasks for earning free pages +router.include_router(video_router) # Video code generation via LLM diff --git a/surfsense_backend/app/routes/reports_routes.py b/surfsense_backend/app/routes/reports_routes.py index e32d7adcd..a4ce45c3c 100644 --- a/surfsense_backend/app/routes/reports_routes.py +++ b/surfsense_backend/app/routes/reports_routes.py @@ -37,6 +37,7 @@ from app.schemas import ReportContentRead, ReportContentUpdate, ReportRead from app.schemas.reports import ReportVersionInfo from app.users import current_active_user +from app.utils.content_utils import strip_code_fences from app.utils.rbac import check_search_space_access logger = logging.getLogger(__name__) @@ -55,16 +56,7 @@ class ExportFormat(str, Enum): # Helpers # --------------------------------------------------------------------------- -_CODE_FENCE_RE = re.compile(r"^```(?:markdown|md)?\s*\n", re.MULTILINE) - - -def _strip_wrapping_code_fences(text: str) -> str: - """Remove wrapping code fences (```markdown...```) that LLMs often add.""" - stripped = text.strip() - m = _CODE_FENCE_RE.match(stripped) - if m and stripped.endswith("```"): - stripped = stripped[m.end() : -3].rstrip() - return stripped +_strip_wrapping_code_fences = strip_code_fences def _normalize_latex_delimiters(text: str) -> str: diff --git a/surfsense_backend/app/routes/sandbox_routes.py b/surfsense_backend/app/routes/sandbox_routes.py index 2c12c3a1e..d573550e1 100644 --- a/surfsense_backend/app/routes/sandbox_routes.py +++ b/surfsense_backend/app/routes/sandbox_routes.py @@ -32,6 +32,8 @@ ".html": "text/html", ".md": "text/markdown", ".py": "text/x-python", + ".mp4": "video/mp4", + ".webm": "video/webm", ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ".zip": "application/zip", } diff --git a/surfsense_backend/app/routes/search_spaces_routes.py b/surfsense_backend/app/routes/search_spaces_routes.py index d115c31e2..fb608e283 100644 --- a/surfsense_backend/app/routes/search_spaces_routes.py +++ b/surfsense_backend/app/routes/search_spaces_routes.py @@ -490,14 +490,17 @@ async def get_llm_preferences( image_generation_config = await _get_image_gen_config_by_id( session, search_space.image_generation_config_id ) + video_llm = await _get_llm_config_by_id(session, search_space.video_llm_id) return LLMPreferencesRead( agent_llm_id=search_space.agent_llm_id, document_summary_llm_id=search_space.document_summary_llm_id, image_generation_config_id=search_space.image_generation_config_id, + video_llm_id=search_space.video_llm_id, agent_llm=agent_llm, document_summary_llm=document_summary_llm, image_generation_config=image_generation_config, + video_llm=video_llm, ) except HTTPException: @@ -557,14 +560,17 @@ async def update_llm_preferences( image_generation_config = await _get_image_gen_config_by_id( session, search_space.image_generation_config_id ) + video_llm = await _get_llm_config_by_id(session, search_space.video_llm_id) return LLMPreferencesRead( agent_llm_id=search_space.agent_llm_id, document_summary_llm_id=search_space.document_summary_llm_id, image_generation_config_id=search_space.image_generation_config_id, + video_llm_id=search_space.video_llm_id, agent_llm=agent_llm, document_summary_llm=document_summary_llm, image_generation_config=image_generation_config, + video_llm=video_llm, ) except HTTPException: diff --git a/surfsense_backend/app/routes/video_routes.py b/surfsense_backend/app/routes/video_routes.py new file mode 100644 index 000000000..7dd7a1812 --- /dev/null +++ b/surfsense_backend/app/routes/video_routes.py @@ -0,0 +1,38 @@ +"""Video routes: script generation.""" + +import logging + +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.ext.asyncio import AsyncSession + +from app.agents.video.script_generator import generate_video_script +from app.db import get_async_session +from app.schemas.video import GenerateScriptRequest, VideoInput + +logger = logging.getLogger(__name__) + +router = APIRouter() + + +@router.post("/video/generate-script", response_model=VideoInput) +async def generate_script( + request: GenerateScriptRequest, + search_space_id: int, + session: AsyncSession = Depends(get_async_session), +): + """Generate a VideoInput JSON from topic + source content. + + The LLM produces structured output matching the VideoInput schema. + The frontend then renders this JSON via Remotion Lambda. + """ + try: + result = await generate_video_script( + session=session, + search_space_id=search_space_id, + topic=request.topic, + source_content=request.source_content, + ) + return result + except Exception as exc: + logger.exception("[video/generate-script] Failed for topic '%s'", request.topic) + raise HTTPException(status_code=500, detail=str(exc)) from exc diff --git a/surfsense_backend/app/schemas/new_llm_config.py b/surfsense_backend/app/schemas/new_llm_config.py index 9863665b6..337068a01 100644 --- a/surfsense_backend/app/schemas/new_llm_config.py +++ b/surfsense_backend/app/schemas/new_llm_config.py @@ -182,6 +182,9 @@ class LLMPreferencesRead(BaseModel): image_generation_config_id: int | None = Field( None, description="ID of the image generation config to use" ) + video_llm_id: int | None = Field( + None, description="ID of the LLM config to use for video code generation" + ) agent_llm: dict[str, Any] | None = Field( None, description="Full config for agent LLM" ) @@ -191,6 +194,9 @@ class LLMPreferencesRead(BaseModel): image_generation_config: dict[str, Any] | None = Field( None, description="Full config for image generation" ) + video_llm: dict[str, Any] | None = Field( + None, description="Full config for video LLM" + ) model_config = ConfigDict(from_attributes=True) @@ -207,3 +213,6 @@ class LLMPreferencesUpdate(BaseModel): image_generation_config_id: int | None = Field( None, description="ID of the image generation config to use" ) + video_llm_id: int | None = Field( + None, description="ID of the LLM config to use for video code generation" + ) diff --git a/surfsense_backend/app/schemas/video.py b/surfsense_backend/app/schemas/video.py new file mode 100644 index 000000000..c492ac80b --- /dev/null +++ b/surfsense_backend/app/schemas/video.py @@ -0,0 +1,343 @@ +"""Pydantic models equivalent to the Remotion Zod VideoInput schema. + +Designed for LLM structured output — every field carries a description +so the LLM understands what to produce. Array-length constraints are +expressed in descriptions (not Field validators) for provider compatibility. +""" + +from __future__ import annotations + +from typing import Annotated, Literal, Optional, Union + +from pydantic import BaseModel, ConfigDict, Field + + +# ─── Intro ─────────────────────────────────────────────────────────────────── + + +class IntroSceneInput(BaseModel): + type: Literal["intro"] + title: str = Field(description="Main title displayed in the intro scene.") + subtitle: Optional[str] = Field( + None, description="Optional subtitle shown below the title." + ) + + +# ─── Spotlight card items (discriminated on "category") ────────────────────── + + +class StatItem(BaseModel): + """A stat/metric card showing a key value.""" + + category: Literal["stat"] + title: str = Field(description="Label for the statistic.") + value: str = Field(description="The stat value as a string (e.g. '42%', '$1.2M').") + desc: Optional[str] = Field(None, description="Optional short description.") + color: str = Field(description="Hex color for the card accent (e.g. '#3b82f6').") + + +class InfoItem(BaseModel): + """An information card with a title and description.""" + + category: Literal["info"] + title: str = Field(description="Card heading.") + subtitle: Optional[str] = Field(None, description="Optional card subtitle.") + desc: str = Field(description="Body text for the card.") + tag: Optional[str] = Field(None, description="Optional tag/badge label.") + color: str = Field(description="Hex accent color.") + + +class QuoteItem(BaseModel): + """A quote card.""" + + category: Literal["quote"] + quote: str = Field(description="The quote text.") + author: str = Field(description="Who said or wrote the quote.") + role: Optional[str] = Field(None, description="Author's role or title.") + color: str = Field(description="Hex accent color.") + + +class ProfileItem(BaseModel): + """A person/profile card.""" + + category: Literal["profile"] + name: str = Field(description="Person's name.") + role: str = Field(description="Person's role or title.") + desc: Optional[str] = Field(None, description="Optional short bio.") + tag: Optional[str] = Field(None, description="Optional tag/badge.") + color: str = Field(description="Hex accent color.") + + +class ProgressItem(BaseModel): + """A progress/metric card with a numeric value.""" + + category: Literal["progress"] + title: str = Field(description="Label for the progress metric.") + value: float = Field(description="Current progress value.") + max: Optional[float] = Field( + None, description="Maximum value for the progress bar. Defaults to 100 if omitted." + ) + desc: Optional[str] = Field(None, description="Optional description.") + color: str = Field(description="Hex accent color.") + + +class FactItem(BaseModel): + """A fact/statement card.""" + + category: Literal["fact"] + statement: str = Field(description="The fact or statement text.") + source: Optional[str] = Field(None, description="Optional source attribution.") + color: str = Field(description="Hex accent color.") + + +class DefinitionItem(BaseModel): + """A term + definition card.""" + + category: Literal["definition"] + term: str = Field(description="The term being defined.") + definition: str = Field(description="The definition text.") + example: Optional[str] = Field(None, description="Optional usage example.") + color: str = Field(description="Hex accent color.") + + +CardItem = Annotated[ + Union[ + StatItem, + InfoItem, + QuoteItem, + ProfileItem, + ProgressItem, + FactItem, + DefinitionItem, + ], + Field(discriminator="category"), +] + + +class SpotlightSceneInput(BaseModel): + """A spotlight scene showing 1–8 cards of mixed types.""" + + type: Literal["spotlight"] + items: list[CardItem] = Field( + description="Array of spotlight cards (1 to 8 items). Each card has a 'category' discriminator." + ) + + +# ─── Hierarchy (recursive tree) ───────────────────────────────────────────── + + +class HierarchyNode(BaseModel): + """A node in a hierarchy tree. Can contain nested children.""" + + label: str = Field(description="Display label for this node.") + color: Optional[str] = Field(None, description="Optional hex color.") + desc: Optional[str] = Field(None, description="Optional description.") + children: Optional[list[HierarchyNode]] = Field( + None, description="Optional child nodes forming the subtree." + ) + + +class HierarchySceneInput(BaseModel): + """A hierarchy/tree scene with at least one root node.""" + + type: Literal["hierarchy"] + title: Optional[str] = Field(None, description="Optional scene title.") + items: list[HierarchyNode] = Field( + description="Root-level hierarchy nodes (at least 1)." + ) + + +# ─── List ──────────────────────────────────────────────────────────────────── + + +class ListItem(BaseModel): + """A single list entry.""" + + label: str = Field(description="Primary label text.") + desc: Optional[str] = Field(None, description="Optional description.") + value: Optional[Union[str, float]] = Field( + None, description="Optional value — can be text or a number." + ) + color: Optional[str] = Field(None, description="Optional hex color.") + + +class ListSceneInput(BaseModel): + """A list scene showing items in order (at least 1 item).""" + + type: Literal["list"] + title: Optional[str] = Field(None, description="Optional scene title.") + subtitle: Optional[str] = Field(None, description="Optional scene subtitle.") + items: list[ListItem] = Field(description="The list entries (at least 1).") + + +# ─── Sequence ──────────────────────────────────────────────────────────────── + + +class SequenceItem(BaseModel): + """A single step in a sequence/flow.""" + + label: str = Field(description="Step label.") + desc: Optional[str] = Field(None, description="Optional step description.") + color: Optional[str] = Field(None, description="Optional hex color.") + + +class SequenceSceneInput(BaseModel): + """A sequence/flow scene showing ordered steps (at least 1).""" + + type: Literal["sequence"] + title: Optional[str] = Field(None, description="Optional scene title.") + subtitle: Optional[str] = Field(None, description="Optional scene subtitle.") + items: list[SequenceItem] = Field( + description="The sequence steps (at least 1)." + ) + + +# ─── Chart ─────────────────────────────────────────────────────────────────── + + +class ChartItem(BaseModel): + """A single data point in a chart.""" + + label: str = Field(description="Data point label (e.g. month name, category).") + value: float = Field(description="Numeric value for this data point.") + color: Optional[str] = Field(None, description="Optional hex color for this bar/segment.") + + +class ChartSceneInput(BaseModel): + """A chart scene (bar, line, or donut) with at least 1 data point.""" + + type: Literal["chart"] + title: Optional[str] = Field(None, description="Optional chart title.") + subtitle: Optional[str] = Field(None, description="Optional chart subtitle.") + xTitle: Optional[str] = Field(None, description="Optional X-axis label.") + yTitle: Optional[str] = Field(None, description="Optional Y-axis label.") + items: list[ChartItem] = Field( + description="Data points for the chart (at least 1)." + ) + + +# ─── Relation ──────────────────────────────────────────────────────────────── + + +class RelationNode(BaseModel): + """A node in a relation/network graph.""" + + id: str = Field(description="Unique identifier for this node (referenced by edges).") + label: str = Field(description="Display label.") + desc: Optional[str] = Field(None, description="Optional description.") + color: Optional[str] = Field(None, description="Optional hex color.") + + +class RelationEdge(BaseModel): + """A directed edge between two relation nodes.""" + + model_config = ConfigDict(populate_by_name=True) + + from_: str = Field( + alias="from", + description="ID of the source node.", + ) + to: str = Field(description="ID of the target node.") + label: Optional[str] = Field(None, description="Optional edge label.") + + +class RelationSceneInput(BaseModel): + """A relation/network graph scene with nodes and optional edges.""" + + type: Literal["relation"] + title: Optional[str] = Field(None, description="Optional scene title.") + subtitle: Optional[str] = Field(None, description="Optional scene subtitle.") + nodes: list[RelationNode] = Field( + description="Graph nodes (at least 1)." + ) + edges: list[RelationEdge] = Field( + default_factory=list, + description="Directed edges between nodes. Can be empty.", + ) + + +# ─── Comparison ────────────────────────────────────────────────────────────── + + +class CompareItem(BaseModel): + """A single item within a comparison group.""" + + label: str = Field(description="Item label.") + desc: Optional[str] = Field(None, description="Optional description.") + + +class CompareGroup(BaseModel): + """A group/column in a comparison (e.g. 'Pros' vs 'Cons').""" + + label: str = Field(description="Group heading.") + color: Optional[str] = Field(None, description="Optional hex accent color.") + items: list[CompareItem] = Field( + description="Items in this group (at least 1)." + ) + + +class ComparisonSceneInput(BaseModel): + """A comparison scene with at least 2 groups side by side.""" + + type: Literal["comparison"] + title: Optional[str] = Field(None, description="Optional scene title.") + subtitle: Optional[str] = Field(None, description="Optional scene subtitle.") + groups: list[CompareGroup] = Field( + description="The comparison groups (at least 2)." + ) + + +# ─── Outro ─────────────────────────────────────────────────────────────────── + + +class OutroSceneInput(BaseModel): + """Closing scene of the video.""" + + type: Literal["outro"] + title: Optional[str] = Field(None, description="Optional closing title.") + subtitle: Optional[str] = Field(None, description="Optional closing subtitle.") + + +# ─── Top-level (discriminated on "type") ───────────────────────────────────── + +SceneInput = Annotated[ + Union[ + IntroSceneInput, + SpotlightSceneInput, + HierarchySceneInput, + ListSceneInput, + SequenceSceneInput, + ChartSceneInput, + RelationSceneInput, + ComparisonSceneInput, + OutroSceneInput, + ], + Field(discriminator="type"), +] + + +class VideoInput(BaseModel): + """Complete video input — a sequence of scenes to render. + + The scenes array must contain at least one scene. Typically starts + with an 'intro' scene and ends with an 'outro' scene, with content + scenes in between. + """ + + scenes: list[SceneInput] = Field( + description=( + "Ordered list of scenes to render in the video (at least 1). " + "Each scene has a 'type' discriminator that determines its structure. " + "Available types: intro, spotlight, hierarchy, list, sequence, chart, relation, comparison, outro." + ), + ) + + +# -- Request / Response models for API endpoints --------------------------- + + +class GenerateScriptRequest(BaseModel): + """Request body for POST /video/generate-script.""" + + topic: str = Field(description="Short title for the video.") + source_content: str = Field(description="Structured content to turn into video scenes.") diff --git a/surfsense_backend/app/services/llm_service.py b/surfsense_backend/app/services/llm_service.py index 4833e62a6..d1e8e9d3d 100644 --- a/surfsense_backend/app/services/llm_service.py +++ b/surfsense_backend/app/services/llm_service.py @@ -24,6 +24,7 @@ class LLMRole: AGENT = "agent" # For agent/chat operations DOCUMENT_SUMMARY = "document_summary" # For document summarization + VIDEO = "video" # For Remotion video code generation def get_global_llm_config(llm_config_id: int) -> dict | None: @@ -200,6 +201,8 @@ async def get_search_space_llm_instance( llm_config_id = search_space.agent_llm_id elif role == LLMRole.DOCUMENT_SUMMARY: llm_config_id = search_space.document_summary_llm_id + elif role == LLMRole.VIDEO: + llm_config_id = search_space.video_llm_id or search_space.agent_llm_id else: logger.error(f"Invalid LLM role: {role}") return None @@ -382,6 +385,13 @@ async def get_agent_llm( return await get_search_space_llm_instance(session, search_space_id, LLMRole.AGENT) +async def get_video_llm( + session: AsyncSession, search_space_id: int +) -> ChatLiteLLM | ChatLiteLLMRouter | None: + """Get the search space's video LLM instance, falling back to the agent LLM if not set.""" + return await get_search_space_llm_instance(session, search_space_id, LLMRole.VIDEO) + + async def get_document_summary_llm( session: AsyncSession, search_space_id: int, disable_streaming: bool = False ) -> ChatLiteLLM | ChatLiteLLMRouter | None: diff --git a/surfsense_backend/app/services/model_list_service.py b/surfsense_backend/app/services/model_list_service.py index ebc0e0d7c..38a16977e 100644 --- a/surfsense_backend/app/services/model_list_service.py +++ b/surfsense_backend/app/services/model_list_service.py @@ -36,7 +36,9 @@ # OpenAI deployments (which require deployment names, not model ids). OPENROUTER_SLUG_TO_PROVIDER: dict[str, str] = { "openai": "OPENAI", - "anthropic": "ANTHROPIC", + # "anthropic" intentionally excluded: OpenRouter uses undated names like + # "claude-sonnet-4.5" (dots) which Anthropic's native API rejects. + # The hand-curated static list in llm-models.ts has the correct dated IDs. "google": "GOOGLE", "mistralai": "MISTRAL", "cohere": "COHERE", diff --git a/surfsense_backend/app/tasks/chat/stream_new_chat.py b/surfsense_backend/app/tasks/chat/stream_new_chat.py index a1d0b5cec..7a329b695 100644 --- a/surfsense_backend/app/tasks/chat/stream_new_chat.py +++ b/surfsense_backend/app/tasks/chat/stream_new_chat.py @@ -412,6 +412,23 @@ def complete_current_step() -> str | None: status="in_progress", items=last_active_step_items, ) + elif tool_name == "generate_video": + video_topic = ( + tool_input.get("topic", "Video") + if isinstance(tool_input, dict) + else "Video" + ) + last_active_step_title = "Generating video" + last_active_step_items = [ + f"Topic: {video_topic}", + "Composing visual content...", + ] + yield streaming_service.format_thinking_step( + step_id=tool_step_id, + title="Generating video", + status="in_progress", + items=last_active_step_items, + ) elif tool_name == "execute": cmd = ( tool_input.get("command", "") @@ -643,6 +660,42 @@ def complete_current_step() -> str | None: status="completed", items=completed_items, ) + elif tool_name == "generate_video": + video_status = ( + tool_output.get("status", "unknown") + if isinstance(tool_output, dict) + else "unknown" + ) + video_title = ( + tool_output.get("topic", "Video") + if isinstance(tool_output, dict) + else "Video" + ) + + if video_status == "success": + completed_items = [ + f"Topic: {video_title}", + "Video ready", + ] + elif video_status == "error": + error_msg = ( + tool_output.get("error", "Unknown error") + if isinstance(tool_output, dict) + else "Unknown error" + ) + completed_items = [ + f"Topic: {video_title}", + f"Failed: {error_msg}", + ] + else: + completed_items = last_active_step_items + + yield streaming_service.format_thinking_step( + step_id=original_step_id, + title="Generating video", + status="completed", + items=completed_items, + ) elif tool_name == "execute": raw_text = ( tool_output.get("result", "") @@ -846,7 +899,7 @@ def complete_current_step() -> str | None: f"Report generation failed: {error_msg}", "error", ) - elif tool_name in ( + elif tool_name == "generate_video" or tool_name in ( "create_notion_page", "update_notion_page", "delete_notion_page", diff --git a/surfsense_backend/app/utils/content_utils.py b/surfsense_backend/app/utils/content_utils.py index 5f2a4cd46..30f11a9b4 100644 --- a/surfsense_backend/app/utils/content_utils.py +++ b/surfsense_backend/app/utils/content_utils.py @@ -11,6 +11,7 @@ from __future__ import annotations +import re from typing import TYPE_CHECKING from langchain_core.messages import AIMessage, HumanMessage @@ -21,6 +22,28 @@ if TYPE_CHECKING: from app.db import ChatVisibility +_FENCE_RE = re.compile(r"^(`{3,})\w*\s*\n") + + +def strip_code_fences(text: str) -> str: + """Remove wrapping code fences that LLMs often add around their output. + + Handles patterns like: + ```tsx\\n...content...\\n``` + ```json\\n...content...\\n``` + ```markdown\\n...content...\\n``` + ```\\n...content...\\n``` + Supports 3 or more backticks (LLMs escalate when content itself has triple-backtick blocks). + """ + stripped = text.strip() + m = _FENCE_RE.match(stripped) + if m: + fence = m.group(1) + if stripped.endswith(fence): + stripped = stripped[m.end():] + stripped = stripped[: -len(fence)].rstrip() + return stripped + def extract_text_content(content: str | dict | list) -> str: """Extract plain text content from various message formats.""" diff --git a/surfsense_video/.env.example b/surfsense_video/.env.example new file mode 100644 index 000000000..23c2bac31 --- /dev/null +++ b/surfsense_video/.env.example @@ -0,0 +1,2 @@ +REMOTION_AWS_ACCESS_KEY_ID="" +REMOTION_AWS_SECRET_ACCESS_KEY="" diff --git a/surfsense_video/.gitignore b/surfsense_video/.gitignore new file mode 100644 index 000000000..cc9b35922 --- /dev/null +++ b/surfsense_video/.gitignore @@ -0,0 +1,38 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts +out/ +.env \ No newline at end of file diff --git a/surfsense_video/.npmrc b/surfsense_video/.npmrc new file mode 100644 index 000000000..3e775efb0 --- /dev/null +++ b/surfsense_video/.npmrc @@ -0,0 +1 @@ +auto-install-peers=true diff --git a/surfsense_video/.prettierrc b/surfsense_video/.prettierrc new file mode 100644 index 000000000..37d507174 --- /dev/null +++ b/surfsense_video/.prettierrc @@ -0,0 +1,5 @@ +{ + "useTabs": false, + "bracketSpacing": true, + "tabWidth": 2 +} diff --git a/surfsense_video/README.md b/surfsense_video/README.md new file mode 100644 index 000000000..2d68f954c --- /dev/null +++ b/surfsense_video/README.md @@ -0,0 +1,88 @@ + +
+
+ +This is a Next.js template for building programmatic video apps, with [`@remotion/player`](https://remotion.dev/player) and [`@remotion/lambda`](https://remotion.dev/lambda) built in. + +This template uses the Next.js App directory. There is a [Tailwind CSS version](https://github.com/remotion-dev/template-next-app-dir-tailwind) and a [Pages directory version](https://github.com/remotion-dev/template-next-pages-dir) of this template available. + + + +## Getting Started + +[Use this template](https://github.com/new?template_name=template-next-app-dir&template_owner=remotion-dev) to clone it into your GitHub account. Run + +``` +npm i +``` + +afterwards. Alternatively, use this command to scaffold a project: + +``` +npx create-video@latest --next +``` + +## Commands + +Start the Next.js dev server: + +``` +npm run dev +``` + +Open the Remotion Studio: + +``` +npm run remotion +``` + +Render a video locally: + +``` +npx remotion render +``` + +Upgrade Remotion: + +``` +npx remotion upgrade +``` + +The following script will set up your Remotion Bundle and Lambda function on AWS: + +``` +node deploy.mjs +``` + +You should run this script after: + +- changing the video template +- changing `config.mjs` +- upgrading Remotion to a newer version + +## Set up rendering on AWS Lambda + +This template supports rendering the videos via [Remotion Lambda](https://remotion.dev/lambda). + +1. Copy the `.env.example` file to `.env` and fill in the values. + Complete the [Lambda setup guide](https://www.remotion.dev/docs/lambda/setup) to get your AWS credentials. + +1. Edit the `config.mjs` file to your desired Lambda settings. + +1. Run `node deploy.mjs` to deploy your Lambda function and Remotion Bundle. + +## Docs + +Get started with Remotion by reading the [fundamentals page](https://www.remotion.dev/docs/the-fundamentals). + +## Help + +We provide help on our [Discord server](https://remotion.dev/discord). + +## Issues + +Found an issue with Remotion? [File an issue here](https://remotion.dev/issue). + +## License + +Note that for some entities a company license is needed. [Read the terms here](https://github.com/remotion-dev/remotion/blob/main/LICENSE.md). diff --git a/surfsense_video/config.mjs b/surfsense_video/config.mjs new file mode 100644 index 000000000..b18ed9515 --- /dev/null +++ b/surfsense_video/config.mjs @@ -0,0 +1,10 @@ +/** + * Use autocomplete to get a list of available regions. + * @type {import('@remotion/lambda').AwsRegion} + */ +export const REGION = "us-east-1"; + +export const SITE_NAME = "my-next-app"; +export const RAM = 3009; +export const DISK = 10240; +export const TIMEOUT = 240; diff --git a/surfsense_video/deploy.mjs b/surfsense_video/deploy.mjs new file mode 100644 index 000000000..311ab27e5 --- /dev/null +++ b/surfsense_video/deploy.mjs @@ -0,0 +1,77 @@ +import { + deployFunction, + deploySite, + getOrCreateBucket, +} from "@remotion/lambda"; +import dotenv from "dotenv"; +import path from "path"; +import { DISK, RAM, REGION, SITE_NAME, TIMEOUT } from "./config.mjs"; + +console.log("Selected region:", REGION); +dotenv.config({quiet: true}); + +if (!process.env.AWS_ACCESS_KEY_ID && !process.env.REMOTION_AWS_ACCESS_KEY_ID) { + console.log( + 'The environment variable "REMOTION_AWS_ACCESS_KEY_ID" is not set.', + ); + console.log("Lambda renders were not set up."); + console.log( + "Complete the Lambda setup: at https://www.remotion.dev/docs/lambda/setup", + ); + process.exit(0); +} +if ( + !process.env.AWS_SECRET_ACCESS_KEY && + !process.env.REMOTION_AWS_SECRET_ACCESS_KEY +) { + console.log( + 'The environment variable "REMOTION_REMOTION_AWS_SECRET_ACCESS_KEY" is not set.', + ); + console.log("Lambda renders were not set up."); + console.log( + "Complete the Lambda setup: at https://www.remotion.dev/docs/lambda/setup", + ); + process.exit(0); +} + +process.stdout.write("Deploying Lambda function... "); + +const { functionName, alreadyExisted: functionAlreadyExisted } = + await deployFunction({ + createCloudWatchLogGroup: true, + memorySizeInMb: RAM, + region: REGION, + timeoutInSeconds: TIMEOUT, + diskSizeInMb: DISK, + }); +console.log( + functionName, + functionAlreadyExisted ? "(already existed)" : "(created)", +); + +process.stdout.write("Ensuring bucket... "); +const { bucketName, alreadyExisted: bucketAlreadyExisted } = + await getOrCreateBucket({ + region: REGION, + }); +console.log( + bucketName, + bucketAlreadyExisted ? "(already existed)" : "(created)", +); + +process.stdout.write("Deploying site... "); +const { siteName } = await deploySite({ + bucketName, + entryPoint: path.join(process.cwd(), "src", "remotion", "index.ts"), + siteName: SITE_NAME, + region: REGION, +}); + +console.log(siteName); + +console.log(); +console.log("You now have everything you need to render videos!"); +console.log("Re-run this command when:"); +console.log(" 1) you changed the video template"); +console.log(" 2) you changed config.mjs"); +console.log(" 3) you upgraded Remotion to a newer version"); diff --git a/surfsense_video/eslint.config.mjs b/surfsense_video/eslint.config.mjs new file mode 100644 index 000000000..b37f42b20 --- /dev/null +++ b/surfsense_video/eslint.config.mjs @@ -0,0 +1,53 @@ +import js from "@eslint/js"; +import nextPlugin from "@next/eslint-plugin-next"; +import remotion from "@remotion/eslint-plugin"; +import tseslint from "typescript-eslint"; + +// Build Next.js recommended rules and an "off" map for overrides +const nextRecommended = nextPlugin.configs.recommended ?? { rules: {} }; +const nextRecommendedRules = nextRecommended.rules ?? {}; +const offNextRules = Object.fromEntries( + Object.keys(nextRecommendedRules).map((k) => [k, "off"]), +); + +export default [ + // Global ignores + { + ignores: [ + "node_modules/**", + ".next/**", + "out/**", + "build/**", + "next-env.d.ts", + "next.config.js", + "deploy.mjs", + ], + }, + // Base JS recommended + js.configs.recommended, + // TypeScript recommended (non type-checked for speed/simplicity) + ...tseslint.configs.recommended, + // Next.js recommended rules applied to app code + { + files: ["**/*.{js,jsx,ts,tsx}"], + plugins: { "@next/next": nextPlugin }, + rules: { + ...nextRecommendedRules, + }, + }, + // Remotion rules applied only to remotion files + { + files: ["src/remotion/**"], + ...remotion.flatPlugin, + rules: { + ...remotion.flatPlugin.rules, + }, + }, + // Disable all Next.js rules within remotion files + { + files: ["src/remotion/**"], + rules: { + ...offNextRules, + }, + }, +]; diff --git a/surfsense_video/next.config.js b/surfsense_video/next.config.js new file mode 100644 index 000000000..91ef62f0d --- /dev/null +++ b/surfsense_video/next.config.js @@ -0,0 +1,6 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, +}; + +module.exports = nextConfig; diff --git a/surfsense_video/package.json b/surfsense_video/package.json new file mode 100644 index 000000000..ab8f01ac9 --- /dev/null +++ b/surfsense_video/package.json @@ -0,0 +1,57 @@ +{ + "name": "surfsense_video", + "version": "0.1.0", + "private": true, + "dependencies": { + "@remotion/bundler": "4.0.434", + "@remotion/cli": "4.0.434", + "@remotion/google-fonts": "4.0.434", + "@remotion/lambda": "4.0.434", + "@remotion/paths": "4.0.434", + "@remotion/player": "4.0.434", + "@remotion/shapes": "4.0.434", + "@types/d3-force": "^3.0.10", + "@types/d3-hierarchy": "^3.1.7", + "@types/d3-scale": "^4.0.9", + "@types/d3-shape": "^3.1.8", + "culori": "^4.0.2", + "d3-force": "^3.0.0", + "d3-hierarchy": "^3.1.2", + "d3-scale": "^4.0.2", + "d3-shape": "^3.2.0", + "next": "16.1.5", + "react": "19.2.3", + "react-dom": "19.2.3", + "remotion": "4.0.434", + "remotion-bits": "^0.1.14", + "zod": "4.3.6" + }, + "devDependencies": { + "@eslint/eslintrc": "3.1.0", + "@eslint/js": "9.38.0", + "@next/eslint-plugin-next": "15.1.6", + "@remotion/eslint-config-flat": "4.0.434", + "@remotion/eslint-plugin": "4.0.434", + "@types/node": "20.12.14", + "@types/react": "19.2.7", + "@types/react-dom": "19.2.3", + "@types/web": "0.0.166", + "@typescript-eslint/eslint-plugin": "8.46.0", + "@typescript-eslint/parser": "8.46.0", + "dotenv": "17.3.1", + "eslint": "9.19.0", + "eslint-config-next": "15.1.6", + "prettier": "3.8.1", + "typescript": "5.9.3", + "typescript-eslint": "8.46.0" + }, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "eslint .", + "remotion": "remotion studio", + "render": "remotion render", + "deploy": "node deploy.mjs" + } +} \ No newline at end of file diff --git a/surfsense_video/pnpm-lock.yaml b/surfsense_video/pnpm-lock.yaml new file mode 100644 index 000000000..ca5f34316 --- /dev/null +++ b/surfsense_video/pnpm-lock.yaml @@ -0,0 +1,7582 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@remotion/bundler': + specifier: 4.0.434 + version: 4.0.434(@swc/helpers@0.5.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/cli': + specifier: 4.0.434 + version: 4.0.434(@swc/helpers@0.5.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/google-fonts': + specifier: 4.0.434 + version: 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/lambda': + specifier: 4.0.434 + version: 4.0.434(@remotion/bundler@4.0.434(@swc/helpers@0.5.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@swc/helpers@0.5.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/paths': + specifier: 4.0.434 + version: 4.0.434 + '@remotion/player': + specifier: 4.0.434 + version: 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/shapes': + specifier: 4.0.434 + version: 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@types/d3-force': + specifier: ^3.0.10 + version: 3.0.10 + '@types/d3-hierarchy': + specifier: ^3.1.7 + version: 3.1.7 + '@types/d3-scale': + specifier: ^4.0.9 + version: 4.0.9 + '@types/d3-shape': + specifier: ^3.1.8 + version: 3.1.8 + culori: + specifier: ^4.0.2 + version: 4.0.2 + d3-force: + specifier: ^3.0.0 + version: 3.0.0 + d3-hierarchy: + specifier: ^3.1.2 + version: 3.1.2 + d3-scale: + specifier: ^4.0.2 + version: 4.0.2 + d3-shape: + specifier: ^3.2.0 + version: 3.2.0 + next: + specifier: 16.1.5 + version: 16.1.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: + specifier: 19.2.3 + version: 19.2.3 + react-dom: + specifier: 19.2.3 + version: 19.2.3(react@19.2.3) + remotion: + specifier: 4.0.434 + version: 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + remotion-bits: + specifier: ^0.1.14 + version: 0.1.14(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(remotion@4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) + zod: + specifier: 4.3.6 + version: 4.3.6 + devDependencies: + '@eslint/eslintrc': + specifier: 3.1.0 + version: 3.1.0 + '@eslint/js': + specifier: 9.38.0 + version: 9.38.0 + '@next/eslint-plugin-next': + specifier: 15.1.6 + version: 15.1.6 + '@remotion/eslint-config-flat': + specifier: 4.0.434 + version: 4.0.434(eslint@9.19.0)(typescript@5.9.3) + '@remotion/eslint-plugin': + specifier: 4.0.434 + version: 4.0.434(eslint@9.19.0)(typescript@5.9.3) + '@types/node': + specifier: 20.12.14 + version: 20.12.14 + '@types/react': + specifier: 19.2.7 + version: 19.2.7 + '@types/react-dom': + specifier: 19.2.3 + version: 19.2.3(@types/react@19.2.7) + '@types/web': + specifier: 0.0.166 + version: 0.0.166 + '@typescript-eslint/eslint-plugin': + specifier: 8.46.0 + version: 8.46.0(@typescript-eslint/parser@8.46.0(eslint@9.19.0)(typescript@5.9.3))(eslint@9.19.0)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: 8.46.0 + version: 8.46.0(eslint@9.19.0)(typescript@5.9.3) + dotenv: + specifier: 17.3.1 + version: 17.3.1 + eslint: + specifier: 9.19.0 + version: 9.19.0 + eslint-config-next: + specifier: 15.1.6 + version: 15.1.6(eslint@9.19.0)(typescript@5.9.3) + prettier: + specifier: 3.8.1 + version: 3.8.1 + typescript: + specifier: 5.9.3 + version: 5.9.3 + typescript-eslint: + specifier: 8.46.0 + version: 8.46.0(eslint@9.19.0)(typescript@5.9.3) + +packages: + + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/crc32c@5.2.0': + resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} + + '@aws-crypto/sha1-browser@5.2.0': + resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/client-cloudwatch-logs@3.986.0': + resolution: {integrity: sha512-i1rpVE4gn7t1lakamh2EfePKJrG2sJM8y3o9xsEzm33oDpWWERUWO/ojB4gBAF1oCguMDqkL2l3cFJKtz0PivQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-iam@3.986.0': + resolution: {integrity: sha512-Xww27O4Zb+KSoBqCu7703o1IelX/oUEiIk40LS2l84RT8/YdYrfwlO9Q6U3qevcRTDPv05IEUU3tVj3WwgcaVg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-lambda@3.986.0': + resolution: {integrity: sha512-R0VrqSH622b0MmIULLCNbupyU9qqEn+vofIeKng+ALPJY6U7pq7MG0p+bbxLCGztLl0u2vmO237SPZYcFm3hCQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-s3@3.986.0': + resolution: {integrity: sha512-IcDJ8shVVvbxgMe8+dLWcv6uhSwmX65PHTVGX81BhWAElPnp3CL8w/5uzOPRo4n4/bqIk9eskGVEIicw2o+SrA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-service-quotas@3.986.0': + resolution: {integrity: sha512-Pa6CSHBID+ystd1s8iMoF8tYay9YavuLefo6RO6qH65tApbn7qSUIICZvXCjFU1hVPb/zXLHl+LYo1JOcrR12Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-sts@3.986.0': + resolution: {integrity: sha512-MKL3OSaUXJ4Xftl+sm7n7o8pJmX5FQgIFc/suWPoNvKEdMJOC+/+2DYjjpASw5X89LL4+9xL2BHhz0y32m5FYw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.973.17': + resolution: {integrity: sha512-VtgGP0TjbCeyp6DQpiBqJKbemTSIaN2bZc3UbeTDCani3lBCyxn75ouJYD6koSSp0bh7rKLEbUpiFsNCI7tr0w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/crc64-nvme@3.972.0': + resolution: {integrity: sha512-ThlLhTqX68jvoIVv+pryOdb5coP1cX1/MaTbB9xkGDCbWbsqQcLqzPxuSoW1DCnAAIacmXCWpzUNOB9pv+xXQw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.972.15': + resolution: {integrity: sha512-RhHQG1lhkWHL4tK1C/KDjaOeis+9U0tAMnWDiwiSVQZMC7CsST9Xin+sK89XywJ5g/tyABtb7TvFePJ4Te5XSQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-http@3.972.17': + resolution: {integrity: sha512-b/bDL76p51+yQ+0O9ZDH5nw/ioE0sRYkjwjOwFWAWZXo6it2kQZUOXhVpjohx3ldKyUxt/SwAivjUu1Nr/PWlQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-ini@3.972.15': + resolution: {integrity: sha512-qWnM+wB8MmU2kKY7f4KowKjOjkwRosaFxrtseEEIefwoXn1SjN+CbHzXBVdTAQxxkbBiqhPgJ/WHiPtES4grRQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-login@3.972.15': + resolution: {integrity: sha512-x92FJy34/95wgu+qOGD8SHcgh1hZ9Qx2uFtQEGn4m9Ljou8ICIv3Ybq5yxdB7A60S8ZGCQB0mIopmjJwiLbh5g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-node@3.972.16': + resolution: {integrity: sha512-7mlt14Ee4rPFAFUVgpWE7+0CBhetJJyzVFqfIsMp7sgyOSm9Y/+qHZOWAuK5I4JNc+Y5PltvJ9kssTzRo92iXQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-process@3.972.15': + resolution: {integrity: sha512-PrH3iTeD18y/8uJvQD2s/T87BTGhsdS/1KZU7ReWHXsplBwvCqi7AbnnNbML1pFlQwRWCE2RdSZFWDVId3CvkA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-sso@3.972.15': + resolution: {integrity: sha512-M/+LBHTPKZxxXckM6m4dnJeR+jlm9NynH9b2YDswN4Zj2St05SK/crdL3Wy3WfJTZootnnhm3oTh87Usl7PS7w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.972.15': + resolution: {integrity: sha512-QTH6k93v+UOfFam/ado8zc71tH+enTVyuvLy9uEWXX1x894dN5ovtf/MdBDgFwq3g6c9mbtgVJ4B+yBqDtXvdA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/lib-storage@3.986.0': + resolution: {integrity: sha512-tcP8NmpBidRHNhGqRQWEHG1vEsMTjOLd28aIS8dfhaFIAxFGMe5CH+fXunYE5ie52NZ5pLUrCvtAnwfBfSBbUw==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@aws-sdk/client-s3': ^3.986.0 + + '@aws-sdk/middleware-bucket-endpoint@3.972.6': + resolution: {integrity: sha512-3H2bhvb7Cb/S6WFsBy/Dy9q2aegC9JmGH1inO8Lb2sWirSqpLJlZmvQHPE29h2tIxzv6el/14X/tLCQ8BQU6ZQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-expect-continue@3.972.6': + resolution: {integrity: sha512-QMdffpU+GkSGC+bz6WdqlclqIeCsOfgX8JFZ5xvwDtX+UTj4mIXm3uXu7Ko6dBseRcJz1FA6T9OmlAAY6JgJUg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-flexible-checksums@3.972.5': + resolution: {integrity: sha512-SF/1MYWx67OyCrLA4icIpWUfCkdlOi8Y1KecQ9xYxkL10GMjVdPTGPnYhAg0dw5U43Y9PVUWhAV2ezOaG+0BLg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-host-header@3.972.6': + resolution: {integrity: sha512-5XHwjPH1lHB+1q4bfC7T8Z5zZrZXfaLcjSMwTd1HPSPrCmPFMbg3UQ5vgNWcVj0xoX4HWqTGkSf2byrjlnRg5w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-location-constraint@3.972.6': + resolution: {integrity: sha512-XdZ2TLwyj3Am6kvUc67vquQvs6+D8npXvXgyEUJAdkUDx5oMFJKOqpK+UpJhVDsEL068WAJl2NEGzbSik7dGJQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-logger@3.972.6': + resolution: {integrity: sha512-iFnaMFMQdljAPrvsCVKYltPt2j40LQqukAbXvW7v0aL5I+1GO7bZ/W8m12WxW3gwyK5p5u1WlHg8TSAizC5cZw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.972.6': + resolution: {integrity: sha512-dY4v3of5EEMvik6+UDwQ96KfUFDk8m1oZDdkSc5lwi4o7rFrjnv0A+yTV+gu230iybQZnKgDLg/rt2P3H+Vscw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.972.17': + resolution: {integrity: sha512-uSyOGoVFMP44pTt29MIMfsOjegqE/7lT0K3HG0GWPiH2lD4rqZC/TRi/kH4zrGiOQdsaLc+dkfd7Sb2q8vh+gA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-ssec@3.972.6': + resolution: {integrity: sha512-acvMUX9jF4I2Ew+Z/EA6gfaFaz9ehci5wxBmXCZeulLuv8m+iGf6pY9uKz8TPjg39bdAz3hxoE0eLP8Qz+IYlA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-user-agent@3.972.17': + resolution: {integrity: sha512-HHArkgWzomuwufXwheQqkddu763PWCpoNTq1dGjqXzJT/lojX3VlOqjNSR2Xvb6/T9ISfwYcMOcbFgUp4EWxXA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/nested-clients@3.996.5': + resolution: {integrity: sha512-zn0WApcULn7Rtl6T+KP2CQTZo/7wOa2YV1yHQnbijTQoi4YXQHM8s21JcJzt33/mqPh8AdvWX1f+83KvKuxlZw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/region-config-resolver@3.972.6': + resolution: {integrity: sha512-Aa5PusHLXAqLTX1UKDvI3pHQJtIsF7Q+3turCHqfz/1F61/zDMWfbTC8evjhrrYVAtz9Vsv3SJ/waSUeu7B6gw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/s3-request-presigner@3.986.0': + resolution: {integrity: sha512-+yopxtoXwRXZ2Ai9H4GzkN+T2D07sGrURYcm7Eh2OQe3p+Ys/3VrR6UrzILssaJGYtR2vQqVKnGJBHVYqaM1EQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.986.0': + resolution: {integrity: sha512-Upw+rw7wCH93E6QWxqpAqJLrUmJYVUAWrk4tCOBnkeuwzGERZvJFL5UQ6TAJFj9T18Ih+vNFaACh8J5aP4oTBw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1002.0': + resolution: {integrity: sha512-x972uKOydFn4Rb0PZJzLdNW59rH0KWC78Q2JbQzZpGlGt0DxjYdDRwBG6F42B1MyaEwHGqO/tkGc4r3/PRFfMw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.973.4': + resolution: {integrity: sha512-RW60aH26Bsc016Y9B98hC0Plx6fK5P2v/iQYwMzrSjiDh1qRMUCP6KrXHYEHe3uFvKiOC93Z9zk4BJsUi6Tj1Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-arn-parser@3.972.2': + resolution: {integrity: sha512-VkykWbqMjlSgBFDyrY3nOSqupMc6ivXuGmvci6Q3NnLq5kC+mKQe2QBZ4nrWRE/jqOxeFP2uYzLtwncYYcvQDg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-endpoints@3.986.0': + resolution: {integrity: sha512-Mqi79L38qi1gCG3adlVdbNrSxvcm1IPDLiJPA3OBypY5ewxUyWbaA3DD4goG+EwET6LSFgZJcRSIh6KBNpP5pA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-endpoints@3.996.3': + resolution: {integrity: sha512-yWIQSNiCjykLL+ezN5A+DfBb1gfXTytBxm57e64lYmwxDHNmInYHRJYYRAGWG1o77vKEiWaw4ui28e3yb1k5aQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-format-url@3.972.6': + resolution: {integrity: sha512-0YNVNgFyziCejXJx0rzxPiD2rkxTWco4c9wiMF6n37Tb9aQvIF8+t7GyEyIFCwQHZ0VMQaAl+nCZHOYz5I5EKw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-locate-window@3.965.4': + resolution: {integrity: sha512-H1onv5SkgPBK2P6JR2MjGgbOnttoNzSPIRoeZTNPZYyaplwGg50zS3amXvXqF0/qfXpWEC9rLWU564QTB9bSog==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-user-agent-browser@3.972.6': + resolution: {integrity: sha512-Fwr/llD6GOrFgQnKaI2glhohdGuBDfHfora6iG9qsBBBR8xv1SdCSwbtf5CWlUdCw5X7g76G/9Hf0Inh0EmoxA==} + + '@aws-sdk/util-user-agent-node@3.973.2': + resolution: {integrity: sha512-lpaIuekdkpw7VRiik0IZmd6TyvEUcuLgKZ5fKRGpCA3I4PjrD/XH15sSwW+OptxQjNU4DEzSxag70spC9SluvA==} + engines: {node: '>=20.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/xml-builder@3.972.9': + resolution: {integrity: sha512-ItnlMgSqkPrUfJs7EsvU/01zw5UeIb2tNPhD09LBLHbg+g+HDiKibSLwpkuz/ZIlz4F2IMn+5XgE4AK/pfPuog==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.2.3': + resolution: {integrity: sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==} + engines: {node: '>=18.0.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.24.1': + resolution: {integrity: sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@dimforge/rapier3d-compat@0.12.0': + resolution: {integrity: sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==} + + '@emnapi/core@1.8.1': + resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} + + '@emnapi/runtime@1.8.1': + resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + + '@esbuild/aix-ppc64@0.25.0': + resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.0': + resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.0': + resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.0': + resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.0': + resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.0': + resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.0': + resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.0': + resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.0': + resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.0': + resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.0': + resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.0': + resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.0': + resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.0': + resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.0': + resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.0': + resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.0': + resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.0': + resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.0': + resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.0': + resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.0': + resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.25.0': + resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.0': + resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.0': + resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.0': + resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.19.2': + resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.10.0': + resolution: {integrity: sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.13.0': + resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.1.0': + resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.4': + resolution: {integrity: sha512-4h4MVF8pmBsncB60r0wSJiIeUKTSD4m7FmTFThG8RHlsg9ajqckLm9OraguFGZE4vVdpiI1Q4+hFnisopmG6gQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.19.0': + resolution: {integrity: sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.38.0': + resolution: {integrity: sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.2.8': + resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@mediabunny/aac-encoder@1.37.0': + resolution: {integrity: sha512-mYnF1sObnPE+7+QWn9H7c0rbl5Dwu50JejUD/GJefRl8ozYp0sz3tt+zBLCeif5GXBkPhABIX3JVg1eGfqx6tg==} + peerDependencies: + mediabunny: ^1.0.0 + + '@mediabunny/flac-encoder@1.37.0': + resolution: {integrity: sha512-VwKIL5p1WZE4dSwZ1SVv/bd2ksul8a4run4S1eEbPRysnG87nmCXddO5ajD3b2k2478XWitKnVDXl/kxdIIWBw==} + peerDependencies: + mediabunny: ^1.0.0 + + '@mediabunny/mp3-encoder@1.37.0': + resolution: {integrity: sha512-6tXBO3iHDA55WiMhOoaOmeCCOQ51U38mdXRxYNS9/hUCpR0ScRo+NtWu2YUa/jp2q99JNPrA4yYjahE5xHDxpg==} + peerDependencies: + mediabunny: ^1.0.0 + + '@module-federation/error-codes@0.22.0': + resolution: {integrity: sha512-xF9SjnEy7vTdx+xekjPCV5cIHOGCkdn3pIxo9vU7gEZMIw0SvAEdsy6Uh17xaCpm8V0FWvR0SZoK9Ik6jGOaug==} + + '@module-federation/runtime-core@0.22.0': + resolution: {integrity: sha512-GR1TcD6/s7zqItfhC87zAp30PqzvceoeDGYTgF3Vx2TXvsfDrhP6Qw9T4vudDQL3uJRne6t7CzdT29YyVxlgIA==} + + '@module-federation/runtime-tools@0.22.0': + resolution: {integrity: sha512-4ScUJ/aUfEernb+4PbLdhM/c60VHl698Gn1gY21m9vyC1Ucn69fPCA1y2EwcCB7IItseRMoNhdcWQnzt/OPCNA==} + + '@module-federation/runtime@0.22.0': + resolution: {integrity: sha512-38g5iPju2tPC3KHMPxRKmy4k4onNp6ypFPS1eKGsNLUkXgHsPMBFqAjDw96iEcjri91BrahG4XcdyKi97xZzlA==} + + '@module-federation/sdk@0.22.0': + resolution: {integrity: sha512-x4aFNBKn2KVQRuNVC5A7SnrSCSqyfIWmm1DvubjbO9iKFe7ith5niw8dqSFBekYBg2Fwy+eMg4sEFNVvCAdo6g==} + + '@module-federation/webpack-bundler-runtime@0.22.0': + resolution: {integrity: sha512-aM8gCqXu+/4wBmJtVeMeeMN5guw3chf+2i6HajKtQv7SJfxV/f4IyNQJUeUQu9HfiAZHjqtMV5Lvq/Lvh8LdyA==} + + '@napi-rs/wasm-runtime@0.2.12': + resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + + '@napi-rs/wasm-runtime@1.0.7': + resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} + + '@next/env@16.1.5': + resolution: {integrity: sha512-CRSCPJiSZoi4Pn69RYBDI9R7YK2g59vLexPQFXY0eyw+ILevIenCywzg+DqmlBik9zszEnw2HLFOUlLAcJbL7g==} + + '@next/eslint-plugin-next@15.1.6': + resolution: {integrity: sha512-+slMxhTgILUntZDGNgsKEYHUvpn72WP1YTlkmEhS51vnVd7S9jEEy0n9YAMcI21vUG4akTw9voWH02lrClt/yw==} + + '@next/swc-darwin-arm64@16.1.5': + resolution: {integrity: sha512-eK7Wdm3Hjy/SCL7TevlH0C9chrpeOYWx2iR7guJDaz4zEQKWcS1IMVfMb9UKBFMg1XgzcPTYPIp1Vcpukkjg6Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@16.1.5': + resolution: {integrity: sha512-foQscSHD1dCuxBmGkbIr6ScAUF6pRoDZP6czajyvmXPAOFNnQUJu2Os1SGELODjKp/ULa4fulnBWoHV3XdPLfA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@16.1.5': + resolution: {integrity: sha512-qNIb42o3C02ccIeSeKjacF3HXotGsxh/FMk/rSRmCzOVMtoWH88odn2uZqF8RLsSUWHcAqTgYmPD3pZ03L9ZAA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-arm64-musl@16.1.5': + resolution: {integrity: sha512-U+kBxGUY1xMAzDTXmuVMfhaWUZQAwzRaHJ/I6ihtR5SbTVUEaDRiEU9YMjy1obBWpdOBuk1bcm+tsmifYSygfw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-x64-gnu@16.1.5': + resolution: {integrity: sha512-gq2UtoCpN7Ke/7tKaU7i/1L7eFLfhMbXjNghSv0MVGF1dmuoaPeEVDvkDuO/9LVa44h5gqpWeJ4mRRznjDv7LA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-linux-x64-musl@16.1.5': + resolution: {integrity: sha512-bQWSE729PbXT6mMklWLf8dotislPle2L70E9q6iwETYEOt092GDn0c+TTNj26AjmeceSsC4ndyGsK5nKqHYXjQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-win32-arm64-msvc@16.1.5': + resolution: {integrity: sha512-LZli0anutkIllMtTAWZlDqdfvjWX/ch8AFK5WgkNTvaqwlouiD1oHM+WW8RXMiL0+vAkAJyAGEzPPjO+hnrSNQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@16.1.5': + resolution: {integrity: sha512-7is37HJTNQGhjPpQbkKjKEboHYQnCgpVt/4rBrrln0D9nderNxZ8ZWs8w1fAtzUx7wEyYjQ+/13myFgFj6K2Ng==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@nolyfill/is-core-module@1.0.39': + resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} + engines: {node: '>=12.4.0'} + + '@remotion/bundler@4.0.434': + resolution: {integrity: sha512-S62GkQnMbS/svtNTxZwWvKE7oCSVGaeZllMMig07bFXsf+xLbgfZDBhe4iMqdOyk9+++OVmwgz0qNo+1q2r0FQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/cli@4.0.434': + resolution: {integrity: sha512-yuZYBQ1mXCJJaQHAf60Y1HupCWGqIM3hV+BYrGwx/TiXqv1GVMU4On5yuJSGVKoOc8ZTZkorOB0gNzB8fzC8qg==} + hasBin: true + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/compositor-darwin-arm64@4.0.434': + resolution: {integrity: sha512-9nJgIQcUrOYhr9EsbvCMNhC7g/HD/R+cDPsatFaD2FONHHNLKLZQp/wHGyPfcriblvoxy2hRjfqV8Z7nmkaWAQ==} + cpu: [arm64] + os: [darwin] + + '@remotion/compositor-darwin-x64@4.0.434': + resolution: {integrity: sha512-oIlEnStUCdVQbP5DB0GSXCavoWUaSpd+1z1VsSd7x9QfHiv4BsVjit3Say/HejoJB27YxuSzol47qhRDrruPEA==} + cpu: [x64] + os: [darwin] + + '@remotion/compositor-linux-arm64-gnu@4.0.434': + resolution: {integrity: sha512-928YpjfSKcVBIIJl1HI7KYiYYYQpsxdVldzvDjl3eWViqExeErzsS/R0lH/cDSaxtHttFDw+FgKr3RKsX9Yc8Q==} + cpu: [arm64] + os: [linux] + + '@remotion/compositor-linux-arm64-musl@4.0.434': + resolution: {integrity: sha512-8xEEhKk0E+dTtHnr/ESIZX1USBc6w/pbsxddzLFtaQLsfyiM+lfpvL/j/gYVdTZbqKe9idOkX14n3GlIu4N9rA==} + cpu: [arm64] + os: [linux] + + '@remotion/compositor-linux-x64-gnu@4.0.434': + resolution: {integrity: sha512-zvzLVeSK08j9U0z2FokwQ4DXAdbvL5oUboikR7G5l4e+ziYGx7dukCsTBDBb5Egd8eT+497viaWNSi1dK7hY0Q==} + cpu: [x64] + os: [linux] + + '@remotion/compositor-linux-x64-musl@4.0.434': + resolution: {integrity: sha512-Dze2Dsu+1oMmBrTQXWIfL+PwlA7XG2wBoaIE2W6/UAWAdKRb7vRybenDT7vIL9ErVREnVDxwHLF4NwpB2WeMFA==} + cpu: [x64] + os: [linux] + + '@remotion/compositor-win32-x64-msvc@4.0.434': + resolution: {integrity: sha512-3PmLo03LBYf123FvjlUNphbtnTJnvToHMDgBQYdrlQBGbAQpJGLsX7IViQwWSNFGZVzonwliNtC9568Nbts8lw==} + cpu: [x64] + os: [win32] + + '@remotion/eslint-config-flat@4.0.434': + resolution: {integrity: sha512-PsvZjXThFze/Pja49Zun1ejULGGnlWRC/dgJ28KGZ0aksBbYTiCzFwp4Cz68QKTi73N8aRDxIcrdAtT3BmxCww==} + peerDependencies: + eslint: '>=9' + + '@remotion/eslint-plugin@4.0.434': + resolution: {integrity: sha512-ODPvy4JBiq2G18vKAjIt7HoIGvNs2vzfhLuXWE6MHccLj6s7Bob8ZyKYHB8jaqA40ES34OgajpydmRzGKxHl2Q==} + peerDependencies: + eslint: '>=7.0.0' + + '@remotion/google-fonts@4.0.434': + resolution: {integrity: sha512-8FI5xPQ7kmdyWckMKRfHJuUbxGHaCSXdEgcqhwbWxRIwmGRuAQbyu2+Q6rh7pFzgl3eyXvqqF4SfE4eA3imXoA==} + + '@remotion/lambda-client@4.0.434': + resolution: {integrity: sha512-HGSQxejOYI0NC0RlDpMA+ZbdRa9aH6CQyv0rwUvD0Etal57L7DrhISVV0gkR711A48rtutfOpqXDZhc/BoiVfQ==} + + '@remotion/lambda@4.0.434': + resolution: {integrity: sha512-l4ru024+0biM7uEilweZGqvl5ijpw/jmy+wgfriBLEcOXSy3ep8MvjG/+KEj1fUN3mDkm9EJQcTB9OsbqsmtdQ==} + peerDependencies: + '@remotion/bundler': 4.0.434 + + '@remotion/licensing@4.0.434': + resolution: {integrity: sha512-Jp9pHUlQBOX7ZjDuG9OVIEDGU5JiN2eSjvHEDS8zwyIIGUaRwDhZRzvGULEiZpu1IJ6UMDpHqIylJ18Inl1PMg==} + + '@remotion/media-parser@4.0.434': + resolution: {integrity: sha512-+vuVOTd3v9BR2WQE3N4CUsCCUb4z8U1bvvxKrP6NGL9nqudu6IQpueXHNwxc/RzDTaxJ1aH6xOLzB3Iqc6EnIA==} + + '@remotion/media-utils@4.0.434': + resolution: {integrity: sha512-3wBtgZSBNXqwQFqQp6eLwsw3m32zlGhZHsD8bn1TeBJQkHflUXo3WhmGkLoUoZdU8nGutFOoFtckjVq1DEXzpg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/paths@4.0.434': + resolution: {integrity: sha512-6gd5NYtpWW4pdwM4P/+T6B6id9wzJnyJemeb2/7X3uGK9qVph+a8/+8uMHtRhOI9z0iQzpJEh0vB8I/oBL1Vwg==} + + '@remotion/player@4.0.434': + resolution: {integrity: sha512-H3SugDjf0QS7lU85A/hej+zUqJLRd0EHfW0jBsu4kerYVXZqi2vswqf7MRqEwQhMGtySVrhI+n9fGBqfcAbRpw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/renderer@4.0.434': + resolution: {integrity: sha512-I0MT0A6YHpO420ntQjF0KzERetpYcnsHlMNUWlNFDeAzFuEjrOgUcZ+6NcUV1lX9nOhixLANvfuMpCrdZazdYg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/serverless-client@4.0.434': + resolution: {integrity: sha512-GSB0Qe538dsVt4nfnm+fNIkvCfTpTym+jfrM7g6uZQ5BeqCa/Sg0ZP4uXTUY8S+/Uwk0FhN5jiWTBOkOSmzW+A==} + + '@remotion/serverless@4.0.434': + resolution: {integrity: sha512-18YKrx++q6UhA2LDPsQmtEPec3rIFdQYHAmPru36k3FC4G2BZv0qdNVFxF2g2prC1NCcA//eVO2YRmhuM9K1+Q==} + + '@remotion/shapes@4.0.434': + resolution: {integrity: sha512-sl/mvFRuYRfurGmhWXlr8e2QpF6QSHlId838i+GV0Rp5APRCFKgWEO2qemEqwhbtS8tjmo94ZUczvZ0T3yQZ2A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/streaming@4.0.434': + resolution: {integrity: sha512-Qlb7sSSXs3nkbs/C/62fLIn/ZxUiNdqtnim3AzAMmi9HdC1qcCMAJiQfugnpnKQ57xbS5Okp1HzSPQalpcgXgw==} + + '@remotion/studio-server@4.0.434': + resolution: {integrity: sha512-k1j7he3H6H19t0iiJ6qNRqzJiGqLzpSfFoi7Hb1NVAwFbcrBOYCsRh/sr+ecL5VMAcWZf2s+cgnaL+mZD6sCrQ==} + + '@remotion/studio-shared@4.0.434': + resolution: {integrity: sha512-5HL2ciT8BpGW8izhwOehMBVh2fR+/2X5EjoIUlWrXoDWpSgzSXmT8yI2nkKmBCxbZyYyyW/pQZKkvDj56jcB2Q==} + + '@remotion/studio@4.0.434': + resolution: {integrity: sha512-faNm11hVsjFyjNg9R7TQZcjsDflUfqkWGgbrq5jSubpVJD+eHEVBo1HIZwrDHIlvee8gTyAscR0tFrH27PLShQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/web-renderer@4.0.434': + resolution: {integrity: sha512-yYLQ/BbIlHd61pFH0B979UYWUnSMwubXvf1jeyu6w7/44Z2ZbI4h05fzzvIKEr9XZFZSVntBWeFUHuNCf/Tomg==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@remotion/webcodecs@4.0.434': + resolution: {integrity: sha512-Eq/3EB9w0L2F4Q2lgTXcc7maoGYn0nkfvoKclVkA4N6gsfgNand+SX/nDe8n8JMOD8NsEKOs5EEd3YWYY3oN5A==} + + '@remotion/zod-types@4.0.434': + resolution: {integrity: sha512-5urhO+1NYCWvddy7cPdqqfHH+VLHP1+5pREO8fqwemIU6jmJR9SFR0vDgMa9wyrQfGyQA7Q0sTMX73KInwszFg==} + peerDependencies: + zod: 4.3.6 + + '@rspack/binding-darwin-arm64@1.7.6': + resolution: {integrity: sha512-NZ9AWtB1COLUX1tA9HQQvWpTy07NSFfKBU8A6ylWd5KH8AePZztpNgLLAVPTuNO4CZXYpwcoclf8jG/luJcQdQ==} + cpu: [arm64] + os: [darwin] + + '@rspack/binding-darwin-x64@1.7.6': + resolution: {integrity: sha512-J2g6xk8ZS7uc024dNTGTHxoFzFovAZIRixUG7PiciLKTMP78svbSSWrmW6N8oAsAkzYfJWwQpVgWfFNRHvYxSw==} + cpu: [x64] + os: [darwin] + + '@rspack/binding-linux-arm64-gnu@1.7.6': + resolution: {integrity: sha512-eQfcsaxhFrv5FmtaA7+O1F9/2yFDNIoPZzV/ZvqvFz5bBXVc4FAm/1fVpBg8Po/kX1h0chBc7Xkpry3cabFW8w==} + cpu: [arm64] + os: [linux] + + '@rspack/binding-linux-arm64-musl@1.7.6': + resolution: {integrity: sha512-DfQXKiyPIl7i1yECHy4eAkSmlUzzsSAbOjgMuKn7pudsWf483jg0UUYutNgXSlBjc/QSUp7906Cg8oty9OfwPA==} + cpu: [arm64] + os: [linux] + + '@rspack/binding-linux-x64-gnu@1.7.6': + resolution: {integrity: sha512-NdA+2X3lk2GGrMMnTGyYTzM3pn+zNjaqXqlgKmFBXvjfZqzSsKq3pdD1KHZCd5QHN+Fwvoszj0JFsquEVhE1og==} + cpu: [x64] + os: [linux] + + '@rspack/binding-linux-x64-musl@1.7.6': + resolution: {integrity: sha512-rEy6MHKob02t/77YNgr6dREyJ0e0tv1X6Xsg8Z5E7rPXead06zefUbfazj4RELYySWnM38ovZyJAkPx/gOn3VA==} + cpu: [x64] + os: [linux] + + '@rspack/binding-wasm32-wasi@1.7.6': + resolution: {integrity: sha512-YupOrz0daSG+YBbCIgpDgzfMM38YpChv+afZpaxx5Ml7xPeAZIIdgWmLHnQ2rts73N2M1NspAiBwV00Xx0N4Vg==} + cpu: [wasm32] + + '@rspack/binding-win32-arm64-msvc@1.7.6': + resolution: {integrity: sha512-INj7aVXjBvlZ84kEhSK4kJ484ub0i+BzgnjDWOWM1K+eFYDZjLdAsQSS3fGGXwVc3qKbPIssFfnftATDMTEJHQ==} + cpu: [arm64] + os: [win32] + + '@rspack/binding-win32-ia32-msvc@1.7.6': + resolution: {integrity: sha512-lXGvC+z67UMcw58In12h8zCa9IyYRmuptUBMItQJzu+M278aMuD1nETyGLL7e4+OZ2lvrnnBIcjXN1hfw2yRzw==} + cpu: [ia32] + os: [win32] + + '@rspack/binding-win32-x64-msvc@1.7.6': + resolution: {integrity: sha512-zeUxEc0ZaPpmaYlCeWcjSJUPuRRySiSHN23oJ2Xyw0jsQ01Qm4OScPdr0RhEOFuK/UE+ANyRtDo4zJsY52Hadw==} + cpu: [x64] + os: [win32] + + '@rspack/binding@1.7.6': + resolution: {integrity: sha512-/NrEcfo8Gx22hLGysanrV6gHMuqZSxToSci/3M4kzEQtF5cPjfOv5pqeLK/+B6cr56ul/OmE96cCdWcXeVnFjQ==} + + '@rspack/core@1.7.6': + resolution: {integrity: sha512-Iax6UhrfZqJajA778c1d5DBFbSIqPOSrI34kpNIiNpWd8Jq7mFIa+Z60SQb5ZQDZuUxcCZikjz5BxinFjTkg7Q==} + engines: {node: '>=18.12.0'} + peerDependencies: + '@swc/helpers': '>=0.5.1' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@rspack/lite-tapable@1.1.0': + resolution: {integrity: sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==} + + '@rspack/plugin-react-refresh@1.6.1': + resolution: {integrity: sha512-eqqW5645VG3CzGzFgNg5HqNdHVXY+567PGjtDhhrM8t67caxmsSzRmT5qfoEIfBcGgFkH9vEg7kzXwmCYQdQDw==} + peerDependencies: + react-refresh: '>=0.10.0 <1.0.0' + webpack-hot-middleware: 2.x + peerDependenciesMeta: + webpack-hot-middleware: + optional: true + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@rushstack/eslint-patch@1.16.1': + resolution: {integrity: sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==} + + '@smithy/abort-controller@4.0.1': + resolution: {integrity: sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g==} + engines: {node: '>=18.0.0'} + + '@smithy/abort-controller@4.2.11': + resolution: {integrity: sha512-Hj4WoYWMJnSpM6/kchsm4bUNTL9XiSyhvoMb2KIq4VJzyDt7JpGHUZHkVNPZVC7YE1tf8tPeVauxpFBKGW4/KQ==} + engines: {node: '>=18.0.0'} + + '@smithy/chunked-blob-reader-native@4.2.3': + resolution: {integrity: sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw==} + engines: {node: '>=18.0.0'} + + '@smithy/chunked-blob-reader@5.2.2': + resolution: {integrity: sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw==} + engines: {node: '>=18.0.0'} + + '@smithy/config-resolver@4.4.10': + resolution: {integrity: sha512-IRTkd6ps0ru+lTWnfnsbXzW80A8Od8p3pYiZnW98K2Hb20rqfsX7VTlfUwhrcOeSSy68Gn9WBofwPuw3e5CCsg==} + engines: {node: '>=18.0.0'} + + '@smithy/core@3.23.8': + resolution: {integrity: sha512-f7uPeBi7ehmLT4YF2u9j3qx6lSnurG1DLXOsTtJrIRNDF7VXio4BGHQ+SQteN/BrUVudbkuL4v7oOsRCzq4BqA==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.2.11': + resolution: {integrity: sha512-lBXrS6ku0kTj3xLmsJW0WwqWbGQ6ueooYyp/1L9lkyT0M02C+DWwYwc5aTyXFbRaK38ojALxNixg+LxKSHZc0g==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-codec@4.2.11': + resolution: {integrity: sha512-Sf39Ml0iVX+ba/bgMPxaXWAAFmHqYLTmbjAPfLPLY8CrYkRDEqZdUsKC1OwVMCdJXfAt0v4j49GIJ8DoSYAe6w==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-browser@4.2.11': + resolution: {integrity: sha512-3rEpo3G6f/nRS7fQDsZmxw/ius6rnlIpz4UX6FlALEzz8JoSxFmdBt0SZnthis+km7sQo6q5/3e+UJcuQivoXA==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-config-resolver@4.3.11': + resolution: {integrity: sha512-XeNIA8tcP/GDWnnKkO7qEm/bg0B/bP9lvIXZBXcGZwZ+VYM8h8k9wuDvUODtdQ2Wcp2RcBkPTCSMmaniVHrMlA==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-node@4.2.11': + resolution: {integrity: sha512-fzbCh18rscBDTQSCrsp1fGcclLNF//nJyhjldsEl/5wCYmgpHblv5JSppQAyQI24lClsFT0wV06N1Porn0IsEw==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-universal@4.2.11': + resolution: {integrity: sha512-MJ7HcI+jEkqoWT5vp+uoVaAjBrmxBtKhZTeynDRG/seEjJfqyg3SiqMMqyPnAMzmIfLaeJ/uiuSDP/l9AnMy/Q==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.3.13': + resolution: {integrity: sha512-U2Hcfl2s3XaYjikN9cT4mPu8ybDbImV3baXR0PkVlC0TTx808bRP3FaPGAzPtB8OByI+JqJ1kyS+7GEgae7+qQ==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-blob-browser@4.2.12': + resolution: {integrity: sha512-1wQE33DsxkM/waftAhCH9VtJbUGyt1PJ9YRDpOu+q9FUi73LLFUZ2fD8A61g2mT1UY9k7b99+V1xZ41Rz4SHRQ==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-node@4.2.11': + resolution: {integrity: sha512-T+p1pNynRkydpdL015ruIoyPSRw9e/SQOWmSAMmmprfswMrd5Ow5igOWNVlvyVFZlxXqGmyH3NQwfwy8r5Jx0A==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-stream-node@4.2.11': + resolution: {integrity: sha512-hQsTjwPCRY8w9GK07w1RqJi3e+myh0UaOWBBhZ1UMSDgofH/Q1fEYzU1teaX6HkpX/eWDdm7tAGR0jBPlz9QEQ==} + engines: {node: '>=18.0.0'} + + '@smithy/invalid-dependency@4.2.11': + resolution: {integrity: sha512-cGNMrgykRmddrNhYy1yBdrp5GwIgEkniS7k9O1VLB38yxQtlvrxpZtUVvo6T4cKpeZsriukBuuxfJcdZQc/f/g==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@4.2.2': + resolution: {integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==} + engines: {node: '>=18.0.0'} + + '@smithy/md5-js@4.2.11': + resolution: {integrity: sha512-350X4kGIrty0Snx2OWv7rPM6p6vM7RzryvFs6B/56Cux3w3sChOb3bymo5oidXJlPcP9fIRxGUCk7GqpiSOtng==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-content-length@4.2.11': + resolution: {integrity: sha512-UvIfKYAKhCzr4p6jFevPlKhQwyQwlJ6IeKLDhmV1PlYfcW3RL4ROjNEDtSik4NYMi9kDkH7eSwyTP3vNJ/u/Dw==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-endpoint@4.4.22': + resolution: {integrity: sha512-sc81w1o4Jy+/MAQlY3sQ8C7CmSpcvIi3TAzXblUv2hjG11BBSJi/Cw8vDx5BxMxapuH2I+Gc+45vWsgU07WZRQ==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-retry@4.4.39': + resolution: {integrity: sha512-MCVCxaCzuZgiHtHGV2Ke44nh6t4+8/tO+rTYOzrr2+G4nMLU/qbzNCWKBX54lyEaVcGQrfOJiG2f8imtiw+nIQ==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-serde@4.2.12': + resolution: {integrity: sha512-W9g1bOLui7Xn5FABRVS0o3rXL0gfN37d/8I/W7i0N7oxjx9QecUmXEMSUMADTODwdtka9cN43t5BI2CodLJpng==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-stack@4.2.11': + resolution: {integrity: sha512-s+eenEPW6RgliDk2IhjD2hWOxIx1NKrOHxEwNUaUXxYBxIyCcDfNULZ2Mu15E3kwcJWBedTET/kEASPV1A1Akg==} + engines: {node: '>=18.0.0'} + + '@smithy/node-config-provider@4.3.11': + resolution: {integrity: sha512-xD17eE7kaLgBBGf5CZQ58hh2YmwK1Z0O8YhffwB/De2jsL0U3JklmhVYJ9Uf37OtUDLF2gsW40Xwwag9U869Gg==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.4.14': + resolution: {integrity: sha512-DamSqaU8nuk0xTJDrYnRzZndHwwRnyj/n/+RqGGCcBKB4qrQem0mSDiWdupaNWdwxzyMU91qxDmHOCazfhtO3A==} + engines: {node: '>=18.0.0'} + + '@smithy/property-provider@4.2.11': + resolution: {integrity: sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg==} + engines: {node: '>=18.0.0'} + + '@smithy/protocol-http@5.3.11': + resolution: {integrity: sha512-hI+barOVDJBkNt4y0L2mu3Ugc0w7+BpJ2CZuLwXtSltGAAwCb3IvnalGlbDV/UCS6a9ZuT3+exd1WxNdLb5IlQ==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-builder@4.2.11': + resolution: {integrity: sha512-7spdikrYiljpket6u0up2Ck2mxhy7dZ0+TDd+S53Dg2DHd6wg+YNJrTCHiLdgZmEXZKI7LJZcwL3721ZRDFiqA==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-parser@4.2.11': + resolution: {integrity: sha512-nE3IRNjDltvGcoThD2abTozI1dkSy8aX+a2N1Rs55en5UsdyyIXgGEmevUL3okZFoJC77JgRGe99xYohhsjivQ==} + engines: {node: '>=18.0.0'} + + '@smithy/service-error-classification@4.2.11': + resolution: {integrity: sha512-HkMFJZJUhzU3HvND1+Yw/kYWXp4RPDLBWLcK1n+Vqw8xn4y2YiBhdww8IxhkQjP/QlZun5bwm3vcHc8AqIU3zw==} + engines: {node: '>=18.0.0'} + + '@smithy/shared-ini-file-loader@4.4.6': + resolution: {integrity: sha512-IB/M5I8G0EeXZTHsAxpx51tMQ5R719F3aq+fjEB6VtNcCHDc0ajFDIGDZw+FW9GxtEkgTduiPpjveJdA/CX7sw==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.3.11': + resolution: {integrity: sha512-V1L6N9aKOBAN4wEHLyqjLBnAz13mtILU0SeDrjOaIZEeN6IFa6DxwRt1NNpOdmSpQUfkBj0qeD3m6P77uzMhgQ==} + engines: {node: '>=18.0.0'} + + '@smithy/smithy-client@4.12.2': + resolution: {integrity: sha512-HezY3UuG0k4T+4xhFKctLXCA5N2oN+Rtv+mmL8Gt7YmsUY2yhmcLyW75qrSzldfj75IsCW/4UhY3s20KcFnZqA==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.13.0': + resolution: {integrity: sha512-COuLsZILbbQsdrwKQpkkpyep7lCsByxwj7m0Mg5v66/ZTyenlfBc40/QFQ5chO0YN/PNEH1Bi3fGtfXPnYNeDw==} + engines: {node: '>=18.0.0'} + + '@smithy/url-parser@4.2.11': + resolution: {integrity: sha512-oTAGGHo8ZYc5VZsBREzuf5lf2pAurJQsccMusVZ85wDkX66ojEc/XauiGjzCj50A61ObFTPe6d7Pyt6UBYaing==} + engines: {node: '>=18.0.0'} + + '@smithy/util-base64@4.3.2': + resolution: {integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-browser@4.2.2': + resolution: {integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-node@4.2.3': + resolution: {integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@4.2.2': + resolution: {integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==} + engines: {node: '>=18.0.0'} + + '@smithy/util-config-provider@4.2.2': + resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-browser@4.3.38': + resolution: {integrity: sha512-c8P1mFLNxcsdAMabB8/VUQUbWzFmgujWi4bAXSggcqLYPc8V4U5abqFqOyn+dK4YT+q8UyCVkTO8807t4t2syA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-node@4.2.41': + resolution: {integrity: sha512-/UG+9MT3UZAR0fLzOtMJMfWGcjjHvgggq924x/CRy8vRbL+yFf3Z6vETlvq8vDH92+31P/1gSOFoo7303wN8WQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-endpoints@3.3.2': + resolution: {integrity: sha512-+4HFLpE5u29AbFlTdlKIT7jfOzZ8PDYZKTb3e+AgLz986OYwqTourQ5H+jg79/66DB69Un1+qKecLnkZdAsYcA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-hex-encoding@4.2.2': + resolution: {integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-middleware@4.2.11': + resolution: {integrity: sha512-r3dtF9F+TpSZUxpOVVtPfk09Rlo4lT6ORBqEvX3IBT6SkQAdDSVKR5GcfmZbtl7WKhKnmb3wbDTQ6ibR2XHClw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-retry@4.2.11': + resolution: {integrity: sha512-XSZULmL5x6aCTTii59wJqKsY1l3eMIAomRAccW7Tzh9r8s7T/7rdo03oektuH5jeYRlJMPcNP92EuRDvk9aXbw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-stream@4.5.17': + resolution: {integrity: sha512-793BYZ4h2JAQkNHcEnyFxDTcZbm9bVybD0UV/LEWmZ5bkTms7JqjfrLMi2Qy0E5WFcCzLwCAPgcvcvxoeALbAQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-uri-escape@4.2.2': + resolution: {integrity: sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@4.2.2': + resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-waiter@4.2.11': + resolution: {integrity: sha512-x7Rh2azQPs3XxbvCzcttRErKKvLnbZfqRf/gOjw2pb+ZscX88e5UkRPCB67bVnsFHxayvMvmePfKTqsRb+is1A==} + engines: {node: '>=18.0.0'} + + '@smithy/uuid@1.1.2': + resolution: {integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==} + engines: {node: '>=18.0.0'} + + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + + '@tweenjs/tween.js@23.1.3': + resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==} + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/d3-force@3.0.10': + resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} + + '@types/d3-hierarchy@3.1.7': + resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-shape@3.1.8': + resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/dom-mediacapture-transform@0.1.11': + resolution: {integrity: sha512-Y2p+nGf1bF2XMttBnsVPHUWzRRZzqUoJAKmiP10b5umnO6DDrWI0BrGDJy1pOHoOULVmGSfFNkQrAlC5dcj6nQ==} + + '@types/dom-webcodecs@0.1.13': + resolution: {integrity: sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ==} + + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/node@20.12.14': + resolution: {integrity: sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==} + + '@types/prismjs@1.26.6': + resolution: {integrity: sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.7': + resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} + + '@types/stats.js@0.17.4': + resolution: {integrity: sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==} + + '@types/three@0.182.0': + resolution: {integrity: sha512-WByN9V3Sbwbe2OkWuSGyoqQO8Du6yhYaXtXLoA5FkKTUJorZ+yOHBZ35zUUPQXlAKABZmbYp5oAqpA4RBjtJ/Q==} + + '@types/web@0.0.166': + resolution: {integrity: sha512-qvY/TzK1WuxfeACL3Zzw+gMivGiIynRKH98nLET7ACzTRTX8CWMA6LQJ9WayIHvTBU1JeFCBRIBjsxhGz4TfHQ==} + + '@types/webxr@0.5.24': + resolution: {integrity: sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==} + + '@types/yauzl@2.10.3': + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + + '@typescript-eslint/eslint-plugin@8.21.0': + resolution: {integrity: sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/eslint-plugin@8.46.0': + resolution: {integrity: sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.46.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.21.0': + resolution: {integrity: sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/parser@8.46.0': + resolution: {integrity: sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.46.0': + resolution: {integrity: sha512-OEhec0mH+U5Je2NZOeK1AbVCdm0ChyapAyTeXVIYTPXDJ3F07+cu87PPXcGoYqZ7M9YJVvFnfpGg1UmCIqM+QQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@5.19.0': + resolution: {integrity: sha512-Fz+VrjLmwq5fbQn5W7cIJZ066HxLMKvDEmf4eu1tZ8O956aoX45jAuBB76miAECMTODyUxH61AQM7q4/GOMQ5g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/scope-manager@8.21.0': + resolution: {integrity: sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/scope-manager@8.46.0': + resolution: {integrity: sha512-lWETPa9XGcBes4jqAMYD9fW0j4n6hrPtTJwWDmtqgFO/4HF4jmdH/Q6wggTw5qIT5TXjKzbt7GsZUBnWoO3dqw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.46.0': + resolution: {integrity: sha512-WrYXKGAHY836/N7zoK/kzi6p8tXFhasHh8ocFL9VZSAkvH956gfeRfcnhs3xzRy8qQ/dq3q44v1jvQieMFg2cw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.21.0': + resolution: {integrity: sha512-95OsL6J2BtzoBxHicoXHxgk3z+9P3BEcQTpBKriqiYzLKnM2DeSqs+sndMKdamU8FosiadQFT3D+BSL9EKnAJQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/type-utils@8.46.0': + resolution: {integrity: sha512-hy+lvYV1lZpVs2jRaEYvgCblZxUoJiPyCemwbQZ+NGulWkQRy0HRPYAoef/CNSzaLt+MLvMptZsHXHlkEilaeg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@5.19.0': + resolution: {integrity: sha512-zR1ithF4Iyq1wLwkDcT+qFnhs8L5VUtjgac212ftiOP/ZZUOCuuF2DeGiZZGQXGoHA50OreZqLH5NjDcDqn34w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/types@8.21.0': + resolution: {integrity: sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/types@8.46.0': + resolution: {integrity: sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@5.19.0': + resolution: {integrity: sha512-dRPuD4ocXdaE1BM/dNR21elSEUPKaWgowCA0bqJ6YbYkvtrPVEvZ+zqcX5a8ECYn3q5iBSSUcBBD42ubaOp0Hw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/typescript-estree@8.21.0': + resolution: {integrity: sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/typescript-estree@8.46.0': + resolution: {integrity: sha512-ekDCUfVpAKWJbRfm8T1YRrCot1KFxZn21oV76v5Fj4tr7ELyk84OS+ouvYdcDAwZL89WpEkEj2DKQ+qg//+ucg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@5.19.0': + resolution: {integrity: sha512-ZuEckdupXpXamKvFz/Ql8YnePh2ZWcwz7APICzJL985Rp5C2AYcHO62oJzIqNhAMtMK6XvrlBTZeNG8n7gS3lQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@typescript-eslint/utils@8.21.0': + resolution: {integrity: sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/utils@8.46.0': + resolution: {integrity: sha512-nD6yGWPj1xiOm4Gk0k6hLSZz2XkNXhuYmyIrOWcHoPuAhjT9i5bAG+xbWPgFeNR8HPHHtpNKdYUXJl/D3x7f5g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@5.19.0': + resolution: {integrity: sha512-Ym7zZoMDZcAKWsULi2s7UMLREdVQdScPQ/fKWMYefarCztWlHPFVJo8racf8R0Gc8FAEJ2eD4of8As1oFtnQlQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/visitor-keys@8.21.0': + resolution: {integrity: sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/visitor-keys@8.46.0': + resolution: {integrity: sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.11.1': + resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.11.1': + resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} + cpu: [ppc64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} + cpu: [s390x] + os: [linux] + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==} + cpu: [x64] + os: [win32] + + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} + + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} + + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} + + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} + + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + + '@webgpu/types@0.1.69': + resolution: {integrity: sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==} + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + + acorn-import-phases@1.0.4: + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + engines: {node: '>=10.13.0'} + peerDependencies: + acorn: ^8.14.0 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-keywords@3.5.2: + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + + ajv@6.14.0: + resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + ast-types-flow@0.0.8: + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + + ast-types@0.16.1: + resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} + engines: {node: '>=4'} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axe-core@4.11.1: + resolution: {integrity: sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==} + engines: {node: '>=4'} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.10.0: + resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} + engines: {node: '>=6.0.0'} + hasBin: true + + big.js@5.2.2: + resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer@5.6.0: + resolution: {integrity: sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001776: + resolution: {integrity: sha512-sg01JDPzZ9jGshqKSckOQthXnYwOEP50jeVFhaSFbZcOy05TiuuaffDOfcwtCisJ9kNQuLBFibYywv2Bgm9osw==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-loader@5.2.7: + resolution: {integrity: sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.27.0 || ^5.0.0 + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + culori@4.0.2: + resolution: {integrity: sha512-1+BhOB8ahCn4O0cep0Sh2l9KCOfOdY+BXJnKMHFFzDEouSr/el18QwXEMRlOj9UY5nCeA8UN3a/82rUWRBeyBw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-force@3.0.0: + resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} + engines: {node: '>=12'} + + d3-format@3.1.2: + resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} + engines: {node: '>=12'} + + d3-hierarchy@3.1.2: + resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-quadtree@3.0.1: + resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + damerau-levenshtein@1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + dotenv@17.3.1: + resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + electron-to-chromium@1.5.307: + resolution: {integrity: sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + emojis-list@3.0.0: + resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} + engines: {node: '>= 4'} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + enhanced-resolve@5.20.0: + resolution: {integrity: sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==} + engines: {node: '>=10.13.0'} + + error-stack-parser@2.1.4: + resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} + + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.2.2: + resolution: {integrity: sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==} + engines: {node: '>= 0.4'} + + es-module-lexer@2.0.0: + resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + esbuild@0.25.0: + resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-next@15.1.6: + resolution: {integrity: sha512-Wd1uy6y7nBbXUSg9QAuQ+xYEKli5CgUhLjz1QHW11jLDis5vK5XB3PemL6jEmy7HrdhaRFDz+GTZ/3FoH+EUjg==} + peerDependencies: + eslint: ^7.23.0 || ^8.0.0 || ^9.0.0 + typescript: '>=3.3.1' + peerDependenciesMeta: + typescript: + optional: true + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-import-resolver-typescript@3.10.1: + resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + + eslint-module-utils@2.12.1: + resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsx-a11y@6.10.2: + resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-utils@3.0.0: + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + + eslint-visitor-keys@2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.19.0: + resolution: {integrity: sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + extract-zip@2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + + fast-xml-builder@1.0.0: + resolution: {integrity: sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ==} + + fast-xml-parser@5.4.1: + resolution: {integrity: sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==} + hasBin: true + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.4: + resolution: {integrity: sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + fs-monkey@1.0.3: + resolution: {integrity: sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.13.6: + resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + html-entities@2.6.0: + resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + icss-utils@5.1.0: + resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-bun-module@2.0.0: + resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + language-subtag-registry@0.3.23: + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} + + language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + loader-runner@4.3.1: + resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} + engines: {node: '>=6.11.5'} + + loader-utils@2.0.4: + resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} + engines: {node: '>=8.9.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mediabunny@1.37.0: + resolution: {integrity: sha512-eV7M9IJ29pr/8RNL1sYtIxNbdMfDMN1hMwMaOFfNLhwuKKGSC+eKwiJFpdVjEJ3zrMA4LGerF4Hps0SENFSAlg==} + + memfs@3.4.3: + resolution: {integrity: sha512-eivjfi7Ahr6eQTn44nvTnR60e4a1Fs1Via2kCR5lHo/kyNoiMWaXCNJ/GpSd0ilXas2JSOl9B5FTIhflXu0hlg==} + engines: {node: '>= 4.0.0'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + meshoptimizer@0.22.0: + resolution: {integrity: sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.6: + resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + napi-postinstall@0.3.4: + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + next@16.1.5: + resolution: {integrity: sha512-f+wE+NSbiQgh3DSAlTaw2FwY5yGdVViAtp8TotNQj4kk4Q8Bh1sC/aL9aH+Rg1YAVn18OYXsRDT7U/079jgP7w==} + engines: {node: '>=20.9.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + + node-exports-info@1.6.0: + resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} + engines: {node: '>= 0.4'} + + node-releases@2.0.36: + resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss-modules-extract-imports@3.1.0: + resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-local-by-default@4.2.0: + resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-scope@3.2.1: + resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-values@4.0.0: + resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-selector-parser@7.1.1: + resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.8: + resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@3.8.1: + resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} + engines: {node: '>=14'} + hasBin: true + + prism-react-renderer@2.4.1: + resolution: {integrity: sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==} + peerDependencies: + react: '>=16.0.0' + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react-dom@19.2.3: + resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} + peerDependencies: + react: ^19.2.3 + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-refresh@0.18.0: + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} + engines: {node: '>=0.10.0'} + + react@19.2.3: + resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} + engines: {node: '>=0.10.0'} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + recast@0.23.11: + resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} + engines: {node: '>= 4'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + remotion-bits@0.1.14: + resolution: {integrity: sha512-Uo1uuGzW7z7lh0CFwYcUUhyGAjHgWL5fmbZd+OD91BxigdvkSxU51RJzULpg/+iCLm/B0LdoP7ZmAU0HtAZ7hg==} + engines: {node: '>=18'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + remotion: '>=4' + + remotion@4.0.434: + resolution: {integrity: sha512-r5SRjrB9lFeZPkNGTcFG0qJJOhV7m/W/xandMvReCZyvV8D3ScmfpJWqRoq/zGzBt6t2F6TW/3sUQ0QTU110ZQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + resolve@2.0.0-next.6: + resolution: {integrity: sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==} + engines: {node: '>= 0.4'} + hasBin: true + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + schema-utils@3.3.0: + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} + engines: {node: '>= 10.13.0'} + + schema-utils@4.3.3: + resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} + engines: {node: '>= 10.13.0'} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.5.3: + resolution: {integrity: sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==} + engines: {node: '>=10'} + hasBin: true + + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.3: + resolution: {integrity: sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==} + engines: {node: '>= 8'} + + source-map@0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + deprecated: The work that was done in this beta branch won't be included in future versions + + stable-hash@0.0.5: + resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} + + stackframe@1.3.4: + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + stream-browserify@3.0.0: + resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + + string.prototype.includes@2.0.1: + resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} + engines: {node: '>= 0.4'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strnum@2.2.0: + resolution: {integrity: sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==} + + style-loader@4.0.0: + resolution: {integrity: sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==} + engines: {node: '>= 18.12.0'} + peerDependencies: + webpack: ^5.27.0 + + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + + terser-webpack-plugin@5.3.17: + resolution: {integrity: sha512-YR7PtUp6GMU91BgSJmlaX/rS2lGDbAF7D+Wtq7hRO+MiljNmodYvqslzCFiYVAgW+Qoaaia/QUIP4lGXufjdZw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + + terser@5.46.0: + resolution: {integrity: sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==} + engines: {node: '>=10'} + hasBin: true + + three@0.182.0: + resolution: {integrity: sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==} + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsutils@3.21.0: + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typescript-eslint@8.21.0: + resolution: {integrity: sha512-txEKYY4XMKwPXxNkN8+AxAdX6iIJAPiJbHE/FpQccs/sxw8Lf26kqwC3cn0xkHlW8kEbLhkhCsjWuMveaY9Rxw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + typescript-eslint@8.46.0: + resolution: {integrity: sha512-6+ZrB6y2bT2DX3K+Qd9vn7OFOJR+xSLDj+Aw/N3zBwUt27uTw2sw2TE2+UcY1RiyBZkaGbTkVg9SSdPNUG6aUw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + unrs-resolver@1.11.1: + resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + watchpack@2.5.1: + resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==} + engines: {node: '>=10.13.0'} + + webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + + webpack-sources@3.3.4: + resolution: {integrity: sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==} + engines: {node: '>=10.13.0'} + + webpack@5.105.0: + resolution: {integrity: sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + + whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + 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 + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + +snapshots: + + '@aws-crypto/crc32@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.4 + tslib: 2.8.1 + + '@aws-crypto/crc32c@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.4 + tslib: 2.8.1 + + '@aws-crypto/sha1-browser@5.2.0': + dependencies: + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-locate-window': 3.965.4 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-locate-window': 3.965.4 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.4 + tslib: 2.8.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.973.4 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-sdk/client-cloudwatch-logs@3.986.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.17 + '@aws-sdk/credential-provider-node': 3.972.16 + '@aws-sdk/middleware-host-header': 3.972.6 + '@aws-sdk/middleware-logger': 3.972.6 + '@aws-sdk/middleware-recursion-detection': 3.972.6 + '@aws-sdk/middleware-user-agent': 3.972.17 + '@aws-sdk/region-config-resolver': 3.972.6 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-endpoints': 3.986.0 + '@aws-sdk/util-user-agent-browser': 3.972.6 + '@aws-sdk/util-user-agent-node': 3.973.2 + '@smithy/config-resolver': 4.4.10 + '@smithy/core': 3.23.8 + '@smithy/eventstream-serde-browser': 4.2.11 + '@smithy/eventstream-serde-config-resolver': 4.3.11 + '@smithy/eventstream-serde-node': 4.2.11 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/hash-node': 4.2.11 + '@smithy/invalid-dependency': 4.2.11 + '@smithy/middleware-content-length': 4.2.11 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/middleware-retry': 4.4.39 + '@smithy/middleware-serde': 4.2.12 + '@smithy/middleware-stack': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/node-http-handler': 4.4.14 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.38 + '@smithy/util-defaults-mode-node': 4.2.41 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-iam@3.986.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.17 + '@aws-sdk/credential-provider-node': 3.972.16 + '@aws-sdk/middleware-host-header': 3.972.6 + '@aws-sdk/middleware-logger': 3.972.6 + '@aws-sdk/middleware-recursion-detection': 3.972.6 + '@aws-sdk/middleware-user-agent': 3.972.17 + '@aws-sdk/region-config-resolver': 3.972.6 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-endpoints': 3.986.0 + '@aws-sdk/util-user-agent-browser': 3.972.6 + '@aws-sdk/util-user-agent-node': 3.973.2 + '@smithy/config-resolver': 4.4.10 + '@smithy/core': 3.23.8 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/hash-node': 4.2.11 + '@smithy/invalid-dependency': 4.2.11 + '@smithy/middleware-content-length': 4.2.11 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/middleware-retry': 4.4.39 + '@smithy/middleware-serde': 4.2.12 + '@smithy/middleware-stack': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/node-http-handler': 4.4.14 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.38 + '@smithy/util-defaults-mode-node': 4.2.41 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/util-utf8': 4.2.2 + '@smithy/util-waiter': 4.2.11 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-lambda@3.986.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.17 + '@aws-sdk/credential-provider-node': 3.972.16 + '@aws-sdk/middleware-host-header': 3.972.6 + '@aws-sdk/middleware-logger': 3.972.6 + '@aws-sdk/middleware-recursion-detection': 3.972.6 + '@aws-sdk/middleware-user-agent': 3.972.17 + '@aws-sdk/region-config-resolver': 3.972.6 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-endpoints': 3.986.0 + '@aws-sdk/util-user-agent-browser': 3.972.6 + '@aws-sdk/util-user-agent-node': 3.973.2 + '@smithy/config-resolver': 4.4.10 + '@smithy/core': 3.23.8 + '@smithy/eventstream-serde-browser': 4.2.11 + '@smithy/eventstream-serde-config-resolver': 4.3.11 + '@smithy/eventstream-serde-node': 4.2.11 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/hash-node': 4.2.11 + '@smithy/invalid-dependency': 4.2.11 + '@smithy/middleware-content-length': 4.2.11 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/middleware-retry': 4.4.39 + '@smithy/middleware-serde': 4.2.12 + '@smithy/middleware-stack': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/node-http-handler': 4.4.14 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.38 + '@smithy/util-defaults-mode-node': 4.2.41 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/util-stream': 4.5.17 + '@smithy/util-utf8': 4.2.2 + '@smithy/util-waiter': 4.2.11 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-s3@3.986.0': + dependencies: + '@aws-crypto/sha1-browser': 5.2.0 + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.17 + '@aws-sdk/credential-provider-node': 3.972.16 + '@aws-sdk/middleware-bucket-endpoint': 3.972.6 + '@aws-sdk/middleware-expect-continue': 3.972.6 + '@aws-sdk/middleware-flexible-checksums': 3.972.5 + '@aws-sdk/middleware-host-header': 3.972.6 + '@aws-sdk/middleware-location-constraint': 3.972.6 + '@aws-sdk/middleware-logger': 3.972.6 + '@aws-sdk/middleware-recursion-detection': 3.972.6 + '@aws-sdk/middleware-sdk-s3': 3.972.17 + '@aws-sdk/middleware-ssec': 3.972.6 + '@aws-sdk/middleware-user-agent': 3.972.17 + '@aws-sdk/region-config-resolver': 3.972.6 + '@aws-sdk/signature-v4-multi-region': 3.986.0 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-endpoints': 3.986.0 + '@aws-sdk/util-user-agent-browser': 3.972.6 + '@aws-sdk/util-user-agent-node': 3.973.2 + '@smithy/config-resolver': 4.4.10 + '@smithy/core': 3.23.8 + '@smithy/eventstream-serde-browser': 4.2.11 + '@smithy/eventstream-serde-config-resolver': 4.3.11 + '@smithy/eventstream-serde-node': 4.2.11 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/hash-blob-browser': 4.2.12 + '@smithy/hash-node': 4.2.11 + '@smithy/hash-stream-node': 4.2.11 + '@smithy/invalid-dependency': 4.2.11 + '@smithy/md5-js': 4.2.11 + '@smithy/middleware-content-length': 4.2.11 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/middleware-retry': 4.4.39 + '@smithy/middleware-serde': 4.2.12 + '@smithy/middleware-stack': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/node-http-handler': 4.4.14 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.38 + '@smithy/util-defaults-mode-node': 4.2.41 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/util-stream': 4.5.17 + '@smithy/util-utf8': 4.2.2 + '@smithy/util-waiter': 4.2.11 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-service-quotas@3.986.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.17 + '@aws-sdk/credential-provider-node': 3.972.16 + '@aws-sdk/middleware-host-header': 3.972.6 + '@aws-sdk/middleware-logger': 3.972.6 + '@aws-sdk/middleware-recursion-detection': 3.972.6 + '@aws-sdk/middleware-user-agent': 3.972.17 + '@aws-sdk/region-config-resolver': 3.972.6 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-endpoints': 3.986.0 + '@aws-sdk/util-user-agent-browser': 3.972.6 + '@aws-sdk/util-user-agent-node': 3.973.2 + '@smithy/config-resolver': 4.4.10 + '@smithy/core': 3.23.8 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/hash-node': 4.2.11 + '@smithy/invalid-dependency': 4.2.11 + '@smithy/middleware-content-length': 4.2.11 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/middleware-retry': 4.4.39 + '@smithy/middleware-serde': 4.2.12 + '@smithy/middleware-stack': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/node-http-handler': 4.4.14 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.38 + '@smithy/util-defaults-mode-node': 4.2.41 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sts@3.986.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.17 + '@aws-sdk/credential-provider-node': 3.972.16 + '@aws-sdk/middleware-host-header': 3.972.6 + '@aws-sdk/middleware-logger': 3.972.6 + '@aws-sdk/middleware-recursion-detection': 3.972.6 + '@aws-sdk/middleware-user-agent': 3.972.17 + '@aws-sdk/region-config-resolver': 3.972.6 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-endpoints': 3.986.0 + '@aws-sdk/util-user-agent-browser': 3.972.6 + '@aws-sdk/util-user-agent-node': 3.973.2 + '@smithy/config-resolver': 4.4.10 + '@smithy/core': 3.23.8 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/hash-node': 4.2.11 + '@smithy/invalid-dependency': 4.2.11 + '@smithy/middleware-content-length': 4.2.11 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/middleware-retry': 4.4.39 + '@smithy/middleware-serde': 4.2.12 + '@smithy/middleware-stack': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/node-http-handler': 4.4.14 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.38 + '@smithy/util-defaults-mode-node': 4.2.41 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/core@3.973.17': + dependencies: + '@aws-sdk/types': 3.973.4 + '@aws-sdk/xml-builder': 3.972.9 + '@smithy/core': 3.23.8 + '@smithy/node-config-provider': 4.3.11 + '@smithy/property-provider': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/signature-v4': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/crc64-nvme@3.972.0': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.972.15': + dependencies: + '@aws-sdk/core': 3.973.17 + '@aws-sdk/types': 3.973.4 + '@smithy/property-provider': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.972.17': + dependencies: + '@aws-sdk/core': 3.973.17 + '@aws-sdk/types': 3.973.4 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/node-http-handler': 4.4.14 + '@smithy/property-provider': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/util-stream': 4.5.17 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.972.15': + dependencies: + '@aws-sdk/core': 3.973.17 + '@aws-sdk/credential-provider-env': 3.972.15 + '@aws-sdk/credential-provider-http': 3.972.17 + '@aws-sdk/credential-provider-login': 3.972.15 + '@aws-sdk/credential-provider-process': 3.972.15 + '@aws-sdk/credential-provider-sso': 3.972.15 + '@aws-sdk/credential-provider-web-identity': 3.972.15 + '@aws-sdk/nested-clients': 3.996.5 + '@aws-sdk/types': 3.973.4 + '@smithy/credential-provider-imds': 4.2.11 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-login@3.972.15': + dependencies: + '@aws-sdk/core': 3.973.17 + '@aws-sdk/nested-clients': 3.996.5 + '@aws-sdk/types': 3.973.4 + '@smithy/property-provider': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-node@3.972.16': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.15 + '@aws-sdk/credential-provider-http': 3.972.17 + '@aws-sdk/credential-provider-ini': 3.972.15 + '@aws-sdk/credential-provider-process': 3.972.15 + '@aws-sdk/credential-provider-sso': 3.972.15 + '@aws-sdk/credential-provider-web-identity': 3.972.15 + '@aws-sdk/types': 3.973.4 + '@smithy/credential-provider-imds': 4.2.11 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-process@3.972.15': + dependencies: + '@aws-sdk/core': 3.973.17 + '@aws-sdk/types': 3.973.4 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.972.15': + dependencies: + '@aws-sdk/core': 3.973.17 + '@aws-sdk/nested-clients': 3.996.5 + '@aws-sdk/token-providers': 3.1002.0 + '@aws-sdk/types': 3.973.4 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-web-identity@3.972.15': + dependencies: + '@aws-sdk/core': 3.973.17 + '@aws-sdk/nested-clients': 3.996.5 + '@aws-sdk/types': 3.973.4 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/lib-storage@3.986.0(@aws-sdk/client-s3@3.986.0)': + dependencies: + '@aws-sdk/client-s3': 3.986.0 + '@smithy/abort-controller': 4.2.11 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/smithy-client': 4.12.2 + buffer: 5.6.0 + events: 3.3.0 + stream-browserify: 3.0.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-bucket-endpoint@3.972.6': + dependencies: + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-arn-parser': 3.972.2 + '@smithy/node-config-provider': 4.3.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-config-provider': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-expect-continue@3.972.6': + dependencies: + '@aws-sdk/types': 3.973.4 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-flexible-checksums@3.972.5': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@aws-crypto/crc32c': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/core': 3.973.17 + '@aws-sdk/crc64-nvme': 3.972.0 + '@aws-sdk/types': 3.973.4 + '@smithy/is-array-buffer': 4.2.2 + '@smithy/node-config-provider': 4.3.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-stream': 4.5.17 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-host-header@3.972.6': + dependencies: + '@aws-sdk/types': 3.973.4 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-location-constraint@3.972.6': + dependencies: + '@aws-sdk/types': 3.973.4 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-logger@3.972.6': + dependencies: + '@aws-sdk/types': 3.973.4 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-recursion-detection@3.972.6': + dependencies: + '@aws-sdk/types': 3.973.4 + '@aws/lambda-invoke-store': 0.2.3 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-sdk-s3@3.972.17': + dependencies: + '@aws-sdk/core': 3.973.17 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-arn-parser': 3.972.2 + '@smithy/core': 3.23.8 + '@smithy/node-config-provider': 4.3.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/signature-v4': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-stream': 4.5.17 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-ssec@3.972.6': + dependencies: + '@aws-sdk/types': 3.973.4 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-user-agent@3.972.17': + dependencies: + '@aws-sdk/core': 3.973.17 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-endpoints': 3.996.3 + '@smithy/core': 3.23.8 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.996.5': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.17 + '@aws-sdk/middleware-host-header': 3.972.6 + '@aws-sdk/middleware-logger': 3.972.6 + '@aws-sdk/middleware-recursion-detection': 3.972.6 + '@aws-sdk/middleware-user-agent': 3.972.17 + '@aws-sdk/region-config-resolver': 3.972.6 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-endpoints': 3.996.3 + '@aws-sdk/util-user-agent-browser': 3.972.6 + '@aws-sdk/util-user-agent-node': 3.973.2 + '@smithy/config-resolver': 4.4.10 + '@smithy/core': 3.23.8 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/hash-node': 4.2.11 + '@smithy/invalid-dependency': 4.2.11 + '@smithy/middleware-content-length': 4.2.11 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/middleware-retry': 4.4.39 + '@smithy/middleware-serde': 4.2.12 + '@smithy/middleware-stack': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/node-http-handler': 4.4.14 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.38 + '@smithy/util-defaults-mode-node': 4.2.41 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/region-config-resolver@3.972.6': + dependencies: + '@aws-sdk/types': 3.973.4 + '@smithy/config-resolver': 4.4.10 + '@smithy/node-config-provider': 4.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/s3-request-presigner@3.986.0': + dependencies: + '@aws-sdk/signature-v4-multi-region': 3.986.0 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-format-url': 3.972.6 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.986.0': + dependencies: + '@aws-sdk/middleware-sdk-s3': 3.972.17 + '@aws-sdk/types': 3.973.4 + '@smithy/protocol-http': 5.3.11 + '@smithy/signature-v4': 5.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.1002.0': + dependencies: + '@aws-sdk/core': 3.973.17 + '@aws-sdk/nested-clients': 3.996.5 + '@aws-sdk/types': 3.973.4 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/types@3.973.4': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/util-arn-parser@3.972.2': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-endpoints@3.986.0': + dependencies: + '@aws-sdk/types': 3.973.4 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-endpoints': 3.3.2 + tslib: 2.8.1 + + '@aws-sdk/util-endpoints@3.996.3': + dependencies: + '@aws-sdk/types': 3.973.4 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-endpoints': 3.3.2 + tslib: 2.8.1 + + '@aws-sdk/util-format-url@3.972.6': + dependencies: + '@aws-sdk/types': 3.973.4 + '@smithy/querystring-builder': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.965.4': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-browser@3.972.6': + dependencies: + '@aws-sdk/types': 3.973.4 + '@smithy/types': 4.13.0 + bowser: 2.14.1 + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-node@3.973.2': + dependencies: + '@aws-sdk/middleware-user-agent': 3.972.17 + '@aws-sdk/types': 3.973.4 + '@smithy/node-config-provider': 4.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.972.9': + dependencies: + '@smithy/types': 4.13.0 + fast-xml-parser: 5.4.1 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.2.3': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/parser@7.24.1': + dependencies: + '@babel/types': 7.29.0 + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@dimforge/rapier3d-compat@0.12.0': {} + + '@emnapi/core@1.8.1': + dependencies: + '@emnapi/wasi-threads': 1.1.0 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.8.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.1.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.25.0': + optional: true + + '@esbuild/android-arm64@0.25.0': + optional: true + + '@esbuild/android-arm@0.25.0': + optional: true + + '@esbuild/android-x64@0.25.0': + optional: true + + '@esbuild/darwin-arm64@0.25.0': + optional: true + + '@esbuild/darwin-x64@0.25.0': + optional: true + + '@esbuild/freebsd-arm64@0.25.0': + optional: true + + '@esbuild/freebsd-x64@0.25.0': + optional: true + + '@esbuild/linux-arm64@0.25.0': + optional: true + + '@esbuild/linux-arm@0.25.0': + optional: true + + '@esbuild/linux-ia32@0.25.0': + optional: true + + '@esbuild/linux-loong64@0.25.0': + optional: true + + '@esbuild/linux-mips64el@0.25.0': + optional: true + + '@esbuild/linux-ppc64@0.25.0': + optional: true + + '@esbuild/linux-riscv64@0.25.0': + optional: true + + '@esbuild/linux-s390x@0.25.0': + optional: true + + '@esbuild/linux-x64@0.25.0': + optional: true + + '@esbuild/netbsd-arm64@0.25.0': + optional: true + + '@esbuild/netbsd-x64@0.25.0': + optional: true + + '@esbuild/openbsd-arm64@0.25.0': + optional: true + + '@esbuild/openbsd-x64@0.25.0': + optional: true + + '@esbuild/sunos-x64@0.25.0': + optional: true + + '@esbuild/win32-arm64@0.25.0': + optional: true + + '@esbuild/win32-ia32@0.25.0': + optional: true + + '@esbuild/win32-x64@0.25.0': + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.19.0)': + dependencies: + eslint: 9.19.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.19.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/core@0.10.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.13.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.1.0': + dependencies: + ajv: 6.14.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/eslintrc@3.3.4': + dependencies: + ajv: 6.14.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.19.0': {} + + '@eslint/js@9.38.0': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.2.8': + dependencies: + '@eslint/core': 0.13.0 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@img/colour@1.1.0': + optional: true + + '@img/sharp-darwin-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + optional: true + + '@img/sharp-darwin-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + optional: true + + '@img/sharp-linux-arm@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + optional: true + + '@img/sharp-linux-ppc64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.4 + optional: true + + '@img/sharp-linux-riscv64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.2.4 + optional: true + + '@img/sharp-linux-s390x@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.4 + optional: true + + '@img/sharp-linux-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': + dependencies: + '@emnapi/runtime': 1.8.1 + optional: true + + '@img/sharp-win32-arm64@0.34.5': + optional: true + + '@img/sharp-win32-ia32@0.34.5': + optional: true + + '@img/sharp-win32-x64@0.34.5': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@mediabunny/aac-encoder@1.37.0(mediabunny@1.37.0)': + dependencies: + mediabunny: 1.37.0 + + '@mediabunny/flac-encoder@1.37.0(mediabunny@1.37.0)': + dependencies: + mediabunny: 1.37.0 + + '@mediabunny/mp3-encoder@1.37.0(mediabunny@1.37.0)': + dependencies: + mediabunny: 1.37.0 + + '@module-federation/error-codes@0.22.0': {} + + '@module-federation/runtime-core@0.22.0': + dependencies: + '@module-federation/error-codes': 0.22.0 + '@module-federation/sdk': 0.22.0 + + '@module-federation/runtime-tools@0.22.0': + dependencies: + '@module-federation/runtime': 0.22.0 + '@module-federation/webpack-bundler-runtime': 0.22.0 + + '@module-federation/runtime@0.22.0': + dependencies: + '@module-federation/error-codes': 0.22.0 + '@module-federation/runtime-core': 0.22.0 + '@module-federation/sdk': 0.22.0 + + '@module-federation/sdk@0.22.0': {} + + '@module-federation/webpack-bundler-runtime@0.22.0': + dependencies: + '@module-federation/runtime': 0.22.0 + '@module-federation/sdk': 0.22.0 + + '@napi-rs/wasm-runtime@0.2.12': + dependencies: + '@emnapi/core': 1.8.1 + '@emnapi/runtime': 1.8.1 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@napi-rs/wasm-runtime@1.0.7': + dependencies: + '@emnapi/core': 1.8.1 + '@emnapi/runtime': 1.8.1 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@next/env@16.1.5': {} + + '@next/eslint-plugin-next@15.1.6': + dependencies: + fast-glob: 3.3.1 + + '@next/swc-darwin-arm64@16.1.5': + optional: true + + '@next/swc-darwin-x64@16.1.5': + optional: true + + '@next/swc-linux-arm64-gnu@16.1.5': + optional: true + + '@next/swc-linux-arm64-musl@16.1.5': + optional: true + + '@next/swc-linux-x64-gnu@16.1.5': + optional: true + + '@next/swc-linux-x64-musl@16.1.5': + optional: true + + '@next/swc-win32-arm64-msvc@16.1.5': + optional: true + + '@next/swc-win32-x64-msvc@16.1.5': + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@nolyfill/is-core-module@1.0.39': {} + + '@remotion/bundler@4.0.434(@swc/helpers@0.5.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@remotion/media-parser': 4.0.434 + '@remotion/studio': 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/studio-shared': 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rspack/core': 1.7.6(@swc/helpers@0.5.15) + '@rspack/plugin-react-refresh': 1.6.1(react-refresh@0.18.0) + css-loader: 5.2.7(webpack@5.105.0(esbuild@0.25.0)) + esbuild: 0.25.0 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-refresh: 0.18.0 + remotion: 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + source-map: 0.7.3 + style-loader: 4.0.0(webpack@5.105.0(esbuild@0.25.0)) + webpack: 5.105.0(esbuild@0.25.0) + transitivePeerDependencies: + - '@swc/core' + - '@swc/helpers' + - bufferutil + - supports-color + - uglify-js + - utf-8-validate + - webpack-cli + - webpack-hot-middleware + + '@remotion/cli@4.0.434(@swc/helpers@0.5.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@remotion/bundler': 4.0.434(@swc/helpers@0.5.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/media-utils': 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/player': 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/renderer': 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/studio': 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/studio-server': 4.0.434(@swc/helpers@0.5.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/studio-shared': 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + dotenv: 17.3.1 + minimist: 1.2.6 + prompts: 2.4.2 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + remotion: 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + transitivePeerDependencies: + - '@swc/core' + - '@swc/helpers' + - bufferutil + - supports-color + - uglify-js + - utf-8-validate + - webpack-cli + - webpack-hot-middleware + + '@remotion/compositor-darwin-arm64@4.0.434': + optional: true + + '@remotion/compositor-darwin-x64@4.0.434': + optional: true + + '@remotion/compositor-linux-arm64-gnu@4.0.434': + optional: true + + '@remotion/compositor-linux-arm64-musl@4.0.434': + optional: true + + '@remotion/compositor-linux-x64-gnu@4.0.434': + optional: true + + '@remotion/compositor-linux-x64-musl@4.0.434': + optional: true + + '@remotion/compositor-win32-x64-msvc@4.0.434': + optional: true + + '@remotion/eslint-config-flat@4.0.434(eslint@9.19.0)(typescript@5.9.3)': + dependencies: + eslint: 9.19.0 + typescript-eslint: 8.21.0(eslint@9.19.0)(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + - typescript + + '@remotion/eslint-plugin@4.0.434(eslint@9.19.0)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/utils': 5.19.0(eslint@9.19.0)(typescript@5.9.3) + eslint: 9.19.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@remotion/google-fonts@4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + remotion: 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + transitivePeerDependencies: + - react + - react-dom + + '@remotion/lambda-client@4.0.434': {} + + '@remotion/lambda@4.0.434(@remotion/bundler@4.0.434(@swc/helpers@0.5.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@swc/helpers@0.5.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@aws-sdk/client-cloudwatch-logs': 3.986.0 + '@aws-sdk/client-iam': 3.986.0 + '@aws-sdk/client-lambda': 3.986.0 + '@aws-sdk/client-s3': 3.986.0 + '@aws-sdk/client-service-quotas': 3.986.0 + '@aws-sdk/client-sts': 3.986.0 + '@aws-sdk/lib-storage': 3.986.0(@aws-sdk/client-s3@3.986.0) + '@aws-sdk/middleware-flexible-checksums': 3.972.5 + '@aws-sdk/s3-request-presigner': 3.986.0 + '@remotion/bundler': 4.0.434(@swc/helpers@0.5.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/cli': 4.0.434(@swc/helpers@0.5.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/lambda-client': 4.0.434 + '@remotion/renderer': 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/serverless': 4.0.434(@swc/helpers@0.5.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/streaming': 4.0.434 + '@smithy/abort-controller': 4.0.1 + remotion: 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + zod: 4.3.6 + transitivePeerDependencies: + - '@swc/core' + - '@swc/helpers' + - aws-crt + - bufferutil + - react + - react-dom + - supports-color + - uglify-js + - utf-8-validate + - webpack-cli + - webpack-hot-middleware + + '@remotion/licensing@4.0.434': {} + + '@remotion/media-parser@4.0.434': {} + + '@remotion/media-utils@4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@remotion/media-parser': 4.0.434 + '@remotion/webcodecs': 4.0.434 + mediabunny: 1.37.0 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + remotion: 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + + '@remotion/paths@4.0.434': {} + + '@remotion/player@4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + remotion: 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + + '@remotion/renderer@4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@remotion/licensing': 4.0.434 + '@remotion/streaming': 4.0.434 + execa: 5.1.1 + extract-zip: 2.0.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + remotion: 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + source-map: 0.8.0-beta.0 + ws: 8.17.1 + optionalDependencies: + '@remotion/compositor-darwin-arm64': 4.0.434 + '@remotion/compositor-darwin-x64': 4.0.434 + '@remotion/compositor-linux-arm64-gnu': 4.0.434 + '@remotion/compositor-linux-arm64-musl': 4.0.434 + '@remotion/compositor-linux-x64-gnu': 4.0.434 + '@remotion/compositor-linux-x64-musl': 4.0.434 + '@remotion/compositor-win32-x64-msvc': 4.0.434 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@remotion/serverless-client@4.0.434': {} + + '@remotion/serverless@4.0.434(@swc/helpers@0.5.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@remotion/bundler': 4.0.434(@swc/helpers@0.5.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/licensing': 4.0.434 + '@remotion/renderer': 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/serverless-client': 4.0.434 + transitivePeerDependencies: + - '@swc/core' + - '@swc/helpers' + - bufferutil + - react + - react-dom + - supports-color + - uglify-js + - utf-8-validate + - webpack-cli + - webpack-hot-middleware + + '@remotion/shapes@4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@remotion/paths': 4.0.434 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@remotion/streaming@4.0.434': {} + + '@remotion/studio-server@4.0.434(@swc/helpers@0.5.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@babel/parser': 7.24.1 + '@remotion/bundler': 4.0.434(@swc/helpers@0.5.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/renderer': 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/studio-shared': 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + memfs: 3.4.3 + open: 8.4.2 + prettier: 3.8.1 + recast: 0.23.11 + remotion: 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + semver: 7.5.3 + source-map: 0.7.3 + transitivePeerDependencies: + - '@swc/core' + - '@swc/helpers' + - bufferutil + - react + - react-dom + - supports-color + - uglify-js + - utf-8-validate + - webpack-cli + - webpack-hot-middleware + + '@remotion/studio-shared@4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + remotion: 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + transitivePeerDependencies: + - react + - react-dom + + '@remotion/studio@4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@remotion/media-utils': 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/player': 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/renderer': 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/studio-shared': 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/web-renderer': 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@remotion/zod-types': 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@4.3.6) + mediabunny: 1.37.0 + memfs: 3.4.3 + open: 8.4.2 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + remotion: 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + semver: 7.5.3 + source-map: 0.7.3 + zod: 4.3.6 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@remotion/web-renderer@4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@mediabunny/aac-encoder': 1.37.0(mediabunny@1.37.0) + '@mediabunny/flac-encoder': 1.37.0(mediabunny@1.37.0) + '@mediabunny/mp3-encoder': 1.37.0(mediabunny@1.37.0) + '@remotion/licensing': 4.0.434 + mediabunny: 1.37.0 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + remotion: 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + + '@remotion/webcodecs@4.0.434': + dependencies: + '@remotion/media-parser': 4.0.434 + + '@remotion/zod-types@4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@4.3.6)': + dependencies: + remotion: 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + zod: 4.3.6 + transitivePeerDependencies: + - react + - react-dom + + '@rspack/binding-darwin-arm64@1.7.6': + optional: true + + '@rspack/binding-darwin-x64@1.7.6': + optional: true + + '@rspack/binding-linux-arm64-gnu@1.7.6': + optional: true + + '@rspack/binding-linux-arm64-musl@1.7.6': + optional: true + + '@rspack/binding-linux-x64-gnu@1.7.6': + optional: true + + '@rspack/binding-linux-x64-musl@1.7.6': + optional: true + + '@rspack/binding-wasm32-wasi@1.7.6': + dependencies: + '@napi-rs/wasm-runtime': 1.0.7 + optional: true + + '@rspack/binding-win32-arm64-msvc@1.7.6': + optional: true + + '@rspack/binding-win32-ia32-msvc@1.7.6': + optional: true + + '@rspack/binding-win32-x64-msvc@1.7.6': + optional: true + + '@rspack/binding@1.7.6': + optionalDependencies: + '@rspack/binding-darwin-arm64': 1.7.6 + '@rspack/binding-darwin-x64': 1.7.6 + '@rspack/binding-linux-arm64-gnu': 1.7.6 + '@rspack/binding-linux-arm64-musl': 1.7.6 + '@rspack/binding-linux-x64-gnu': 1.7.6 + '@rspack/binding-linux-x64-musl': 1.7.6 + '@rspack/binding-wasm32-wasi': 1.7.6 + '@rspack/binding-win32-arm64-msvc': 1.7.6 + '@rspack/binding-win32-ia32-msvc': 1.7.6 + '@rspack/binding-win32-x64-msvc': 1.7.6 + + '@rspack/core@1.7.6(@swc/helpers@0.5.15)': + dependencies: + '@module-federation/runtime-tools': 0.22.0 + '@rspack/binding': 1.7.6 + '@rspack/lite-tapable': 1.1.0 + optionalDependencies: + '@swc/helpers': 0.5.15 + + '@rspack/lite-tapable@1.1.0': {} + + '@rspack/plugin-react-refresh@1.6.1(react-refresh@0.18.0)': + dependencies: + error-stack-parser: 2.1.4 + html-entities: 2.6.0 + react-refresh: 0.18.0 + + '@rtsao/scc@1.1.0': {} + + '@rushstack/eslint-patch@1.16.1': {} + + '@smithy/abort-controller@4.0.1': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/abort-controller@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/chunked-blob-reader-native@4.2.3': + dependencies: + '@smithy/util-base64': 4.3.2 + tslib: 2.8.1 + + '@smithy/chunked-blob-reader@5.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/config-resolver@4.4.10': + dependencies: + '@smithy/node-config-provider': 4.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + tslib: 2.8.1 + + '@smithy/core@3.23.8': + dependencies: + '@smithy/middleware-serde': 4.2.12 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-stream': 4.5.17 + '@smithy/util-utf8': 4.2.2 + '@smithy/uuid': 1.1.2 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.2.11': + dependencies: + '@smithy/node-config-provider': 4.3.11 + '@smithy/property-provider': 4.2.11 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + tslib: 2.8.1 + + '@smithy/eventstream-codec@4.2.11': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 4.13.0 + '@smithy/util-hex-encoding': 4.2.2 + tslib: 2.8.1 + + '@smithy/eventstream-serde-browser@4.2.11': + dependencies: + '@smithy/eventstream-serde-universal': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-config-resolver@4.3.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-node@4.2.11': + dependencies: + '@smithy/eventstream-serde-universal': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-universal@4.2.11': + dependencies: + '@smithy/eventstream-codec': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.3.13': + dependencies: + '@smithy/protocol-http': 5.3.11 + '@smithy/querystring-builder': 4.2.11 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.2 + tslib: 2.8.1 + + '@smithy/hash-blob-browser@4.2.12': + dependencies: + '@smithy/chunked-blob-reader': 5.2.2 + '@smithy/chunked-blob-reader-native': 4.2.3 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/hash-node@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/hash-stream-node@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/invalid-dependency@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/is-array-buffer@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/md5-js@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/middleware-content-length@4.2.11': + dependencies: + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/middleware-endpoint@4.4.22': + dependencies: + '@smithy/core': 3.23.8 + '@smithy/middleware-serde': 4.2.12 + '@smithy/node-config-provider': 4.3.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-middleware': 4.2.11 + tslib: 2.8.1 + + '@smithy/middleware-retry@4.4.39': + dependencies: + '@smithy/node-config-provider': 4.3.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/service-error-classification': 4.2.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/uuid': 1.1.2 + tslib: 2.8.1 + + '@smithy/middleware-serde@4.2.12': + dependencies: + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/middleware-stack@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/node-config-provider@4.3.11': + dependencies: + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.4.14': + dependencies: + '@smithy/abort-controller': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/querystring-builder': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/property-provider@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/protocol-http@5.3.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/querystring-builder@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + '@smithy/util-uri-escape': 4.2.2 + tslib: 2.8.1 + + '@smithy/querystring-parser@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/service-error-classification@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + + '@smithy/shared-ini-file-loader@4.4.6': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/signature-v4@5.3.11': + dependencies: + '@smithy/is-array-buffer': 4.2.2 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-uri-escape': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/smithy-client@4.12.2': + dependencies: + '@smithy/core': 3.23.8 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/middleware-stack': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-stream': 4.5.17 + tslib: 2.8.1 + + '@smithy/types@4.13.0': + dependencies: + tslib: 2.8.1 + + '@smithy/url-parser@4.2.11': + dependencies: + '@smithy/querystring-parser': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-base64@4.3.2': + dependencies: + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/util-body-length-browser@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-body-length-node@4.2.3': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-buffer-from@4.2.2': + dependencies: + '@smithy/is-array-buffer': 4.2.2 + tslib: 2.8.1 + + '@smithy/util-config-provider@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-defaults-mode-browser@4.3.38': + dependencies: + '@smithy/property-provider': 4.2.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-defaults-mode-node@4.2.41': + dependencies: + '@smithy/config-resolver': 4.4.10 + '@smithy/credential-provider-imds': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/property-provider': 4.2.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-endpoints@3.3.2': + dependencies: + '@smithy/node-config-provider': 4.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-hex-encoding@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-middleware@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-retry@4.2.11': + dependencies: + '@smithy/service-error-classification': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-stream@4.5.17': + dependencies: + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/node-http-handler': 4.4.14 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.2 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/util-uri-escape@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-utf8@4.2.2': + dependencies: + '@smithy/util-buffer-from': 4.2.2 + tslib: 2.8.1 + + '@smithy/util-waiter@4.2.11': + dependencies: + '@smithy/abort-controller': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/uuid@1.1.2': + dependencies: + tslib: 2.8.1 + + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + + '@tweenjs/tween.js@23.1.3': {} + + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/d3-force@3.0.10': {} + + '@types/d3-hierarchy@3.1.7': {} + + '@types/d3-path@3.1.1': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-shape@3.1.8': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time@3.0.4': {} + + '@types/dom-mediacapture-transform@0.1.11': + dependencies: + '@types/dom-webcodecs': 0.1.13 + + '@types/dom-webcodecs@0.1.13': {} + + '@types/eslint-scope@3.7.7': + dependencies: + '@types/eslint': 9.6.1 + '@types/estree': 1.0.8 + + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + + '@types/estree@1.0.8': {} + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/node@20.12.14': + dependencies: + undici-types: 5.26.5 + + '@types/prismjs@1.26.6': {} + + '@types/react-dom@19.2.3(@types/react@19.2.7)': + dependencies: + '@types/react': 19.2.7 + + '@types/react@19.2.7': + dependencies: + csstype: 3.2.3 + + '@types/stats.js@0.17.4': {} + + '@types/three@0.182.0': + dependencies: + '@dimforge/rapier3d-compat': 0.12.0 + '@tweenjs/tween.js': 23.1.3 + '@types/stats.js': 0.17.4 + '@types/webxr': 0.5.24 + '@webgpu/types': 0.1.69 + fflate: 0.8.2 + meshoptimizer: 0.22.0 + + '@types/web@0.0.166': {} + + '@types/webxr@0.5.24': {} + + '@types/yauzl@2.10.3': + dependencies: + '@types/node': 20.12.14 + optional: true + + '@typescript-eslint/eslint-plugin@8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.19.0)(typescript@5.9.3))(eslint@9.19.0)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.21.0(eslint@9.19.0)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.21.0 + '@typescript-eslint/type-utils': 8.21.0(eslint@9.19.0)(typescript@5.9.3) + '@typescript-eslint/utils': 8.21.0(eslint@9.19.0)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.21.0 + eslint: 9.19.0 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/eslint-plugin@8.46.0(@typescript-eslint/parser@8.46.0(eslint@9.19.0)(typescript@5.9.3))(eslint@9.19.0)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.46.0(eslint@9.19.0)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.46.0 + '@typescript-eslint/type-utils': 8.46.0(eslint@9.19.0)(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.0(eslint@9.19.0)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.46.0 + eslint: 9.19.0 + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.21.0(eslint@9.19.0)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.21.0 + '@typescript-eslint/types': 8.21.0 + '@typescript-eslint/typescript-estree': 8.21.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.21.0 + debug: 4.4.3 + eslint: 9.19.0 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.46.0(eslint@9.19.0)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.46.0 + '@typescript-eslint/types': 8.46.0 + '@typescript-eslint/typescript-estree': 8.46.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.46.0 + debug: 4.4.3 + eslint: 9.19.0 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.46.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.46.0(typescript@5.9.3) + '@typescript-eslint/types': 8.46.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@5.19.0': + dependencies: + '@typescript-eslint/types': 5.19.0 + '@typescript-eslint/visitor-keys': 5.19.0 + + '@typescript-eslint/scope-manager@8.21.0': + dependencies: + '@typescript-eslint/types': 8.21.0 + '@typescript-eslint/visitor-keys': 8.21.0 + + '@typescript-eslint/scope-manager@8.46.0': + dependencies: + '@typescript-eslint/types': 8.46.0 + '@typescript-eslint/visitor-keys': 8.46.0 + + '@typescript-eslint/tsconfig-utils@8.46.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.21.0(eslint@9.19.0)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/typescript-estree': 8.21.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.21.0(eslint@9.19.0)(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.19.0 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/type-utils@8.46.0(eslint@9.19.0)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.46.0 + '@typescript-eslint/typescript-estree': 8.46.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.0(eslint@9.19.0)(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.19.0 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@5.19.0': {} + + '@typescript-eslint/types@8.21.0': {} + + '@typescript-eslint/types@8.46.0': {} + + '@typescript-eslint/typescript-estree@5.19.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 5.19.0 + '@typescript-eslint/visitor-keys': 5.19.0 + debug: 4.4.3 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.7.4 + tsutils: 3.21.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@8.21.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.21.0 + '@typescript-eslint/visitor-keys': 8.21.0 + debug: 4.4.3 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.9 + semver: 7.7.4 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@8.46.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.46.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.46.0(typescript@5.9.3) + '@typescript-eslint/types': 8.46.0 + '@typescript-eslint/visitor-keys': 8.46.0 + debug: 4.4.3 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.9 + semver: 7.7.4 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@5.19.0(eslint@9.19.0)(typescript@5.9.3)': + dependencies: + '@types/json-schema': 7.0.15 + '@typescript-eslint/scope-manager': 5.19.0 + '@typescript-eslint/types': 5.19.0 + '@typescript-eslint/typescript-estree': 5.19.0(typescript@5.9.3) + eslint: 9.19.0 + eslint-scope: 5.1.1 + eslint-utils: 3.0.0(eslint@9.19.0) + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/utils@8.21.0(eslint@9.19.0)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.19.0) + '@typescript-eslint/scope-manager': 8.21.0 + '@typescript-eslint/types': 8.21.0 + '@typescript-eslint/typescript-estree': 8.21.0(typescript@5.9.3) + eslint: 9.19.0 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.46.0(eslint@9.19.0)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.19.0) + '@typescript-eslint/scope-manager': 8.46.0 + '@typescript-eslint/types': 8.46.0 + '@typescript-eslint/typescript-estree': 8.46.0(typescript@5.9.3) + eslint: 9.19.0 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@5.19.0': + dependencies: + '@typescript-eslint/types': 5.19.0 + eslint-visitor-keys: 3.4.3 + + '@typescript-eslint/visitor-keys@8.21.0': + dependencies: + '@typescript-eslint/types': 8.21.0 + eslint-visitor-keys: 4.2.1 + + '@typescript-eslint/visitor-keys@8.46.0': + dependencies: + '@typescript-eslint/types': 8.46.0 + eslint-visitor-keys: 4.2.1 + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + optional: true + + '@unrs/resolver-binding-android-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + dependencies: + '@napi-rs/wasm-runtime': 0.2.12 + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + optional: true + + '@webassemblyjs/ast@1.14.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-numbers@1.13.2': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + 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 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/wasm-edit@1.14.1': + 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 + + '@webassemblyjs/wasm-gen@1.14.1': + 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 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-parser@1.14.1': + 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 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + + '@webgpu/types@0.1.69': {} + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + acorn-import-phases@1.0.4(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + ajv-formats@2.1.1(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + + ajv-keywords@3.5.2(ajv@6.14.0): + dependencies: + ajv: 6.14.0 + + ajv-keywords@5.1.0(ajv@8.18.0): + dependencies: + ajv: 8.18.0 + fast-deep-equal: 3.1.3 + + ajv@6.14.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.18.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array-union@2.1.0: {} + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + ast-types-flow@0.0.8: {} + + ast-types@0.16.1: + dependencies: + tslib: 2.8.1 + + async-function@1.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axe-core@4.11.1: {} + + axobject-query@4.1.0: {} + + balanced-match@1.0.2: {} + + base64-js@1.5.1: {} + + baseline-browser-mapping@2.10.0: {} + + big.js@5.2.2: {} + + bowser@2.14.1: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.10.0 + caniuse-lite: 1.0.30001776 + electron-to-chromium: 1.5.307 + node-releases: 2.0.36 + update-browserslist-db: 1.2.3(browserslist@4.28.1) + + buffer-crc32@0.2.13: {} + + buffer-from@1.1.2: {} + + buffer@5.6.0: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001776: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chrome-trace-event@1.0.4: {} + + client-only@0.0.1: {} + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + commander@2.20.3: {} + + concat-map@0.0.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-loader@5.2.7(webpack@5.105.0(esbuild@0.25.0)): + dependencies: + icss-utils: 5.1.0(postcss@8.5.8) + loader-utils: 2.0.4 + postcss: 8.5.8 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.8) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.8) + postcss-modules-scope: 3.2.1(postcss@8.5.8) + postcss-modules-values: 4.0.0(postcss@8.5.8) + postcss-value-parser: 4.2.0 + schema-utils: 3.3.0 + semver: 7.7.4 + webpack: 5.105.0(esbuild@0.25.0) + + cssesc@3.0.0: {} + + csstype@3.2.3: {} + + culori@4.0.2: {} + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-color@3.1.0: {} + + d3-dispatch@3.0.1: {} + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-format@3.1.2: {} + + d3-hierarchy@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@3.1.0: {} + + d3-quadtree@3.0.1: {} + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.2 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + damerau-levenshtein@1.0.8: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-lazy-prop@2.0.0: {} + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + detect-libc@2.1.2: + optional: true + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + dotenv@17.3.1: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + electron-to-chromium@1.5.307: {} + + emoji-regex@9.2.2: {} + + emojis-list@3.0.0: {} + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + enhanced-resolve@5.20.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + + error-stack-parser@2.1.4: + dependencies: + stackframe: 1.3.4 + + es-abstract@1.24.1: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.20 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-iterator-helpers@1.2.2: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + safe-array-concat: 1.1.3 + + es-module-lexer@2.0.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esbuild@0.25.0: + 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 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-next@15.1.6(eslint@9.19.0)(typescript@5.9.3): + dependencies: + '@next/eslint-plugin-next': 15.1.6 + '@rushstack/eslint-patch': 1.16.1 + '@typescript-eslint/eslint-plugin': 8.46.0(@typescript-eslint/parser@8.46.0(eslint@9.19.0)(typescript@5.9.3))(eslint@9.19.0)(typescript@5.9.3) + '@typescript-eslint/parser': 8.46.0(eslint@9.19.0)(typescript@5.9.3) + eslint: 9.19.0 + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.19.0) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.0(eslint@9.19.0)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.19.0) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.19.0) + eslint-plugin-react: 7.37.5(eslint@9.19.0) + eslint-plugin-react-hooks: 5.2.0(eslint@9.19.0) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - eslint-import-resolver-webpack + - eslint-plugin-import-x + - supports-color + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.1 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.19.0): + dependencies: + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.3 + eslint: 9.19.0 + get-tsconfig: 4.13.6 + is-bun-module: 2.0.0 + stable-hash: 0.0.5 + tinyglobby: 0.2.15 + unrs-resolver: 1.11.1 + optionalDependencies: + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.0(eslint@9.19.0)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.19.0) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.46.0(eslint@9.19.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.19.0): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.46.0(eslint@9.19.0)(typescript@5.9.3) + eslint: 9.19.0 + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.19.0) + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.0(eslint@9.19.0)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.19.0): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 9.19.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.46.0(eslint@9.19.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.19.0) + hasown: 2.0.2 + is-core-module: 2.16.1 + is-glob: 4.0.3 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.9 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.46.0(eslint@9.19.0)(typescript@5.9.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-jsx-a11y@6.10.2(eslint@9.19.0): + dependencies: + aria-query: 5.3.2 + array-includes: 3.1.9 + array.prototype.flatmap: 1.3.3 + ast-types-flow: 0.0.8 + axe-core: 4.11.1 + axobject-query: 4.1.0 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 9.19.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.9 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + safe-regex-test: 1.1.0 + string.prototype.includes: 2.0.1 + + eslint-plugin-react-hooks@5.2.0(eslint@9.19.0): + dependencies: + eslint: 9.19.0 + + eslint-plugin-react@7.37.5(eslint@9.19.0): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.2.2 + eslint: 9.19.0 + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.6 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-utils@3.0.0(eslint@9.19.0): + dependencies: + eslint: 9.19.0 + eslint-visitor-keys: 2.1.0 + + eslint-visitor-keys@2.1.0: {} + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.19.0: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.19.0) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.19.2 + '@eslint/core': 0.10.0 + '@eslint/eslintrc': 3.3.4 + '@eslint/js': 9.19.0 + '@eslint/plugin-kit': 0.2.8 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 4.2.1 + + esprima@4.0.1: {} + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + events@3.3.0: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + extract-zip@2.0.1: + dependencies: + debug: 4.4.3 + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.3 + transitivePeerDependencies: + - supports-color + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.1: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-uri@3.1.0: {} + + fast-xml-builder@1.0.0: {} + + fast-xml-parser@5.4.1: + dependencies: + fast-xml-builder: 1.0.0 + strnum: 2.2.0 + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fd-slicer@1.1.0: + dependencies: + pend: 1.2.0 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fflate@0.8.2: {} + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.4 + keyv: 4.5.4 + + flatted@3.3.4: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + fs-monkey@1.0.3: {} + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@5.2.0: + dependencies: + pump: 3.0.4 + + get-stream@6.0.1: {} + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.13.6: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob-to-regexp@0.4.1: {} + + globals@14.0.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + html-entities@2.6.0: {} + + human-signals@2.1.0: {} + + icss-utils@5.1.0(postcss@8.5.8): + dependencies: + postcss: 8.5.8 + + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inherits@2.0.4: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + internmap@2.0.3: {} + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-bun-module@2.0.0: + dependencies: + semver: 7.7.4 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-docker@2.2.1: {} + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-stream@2.0.1: {} + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.20 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + jest-worker@27.5.1: + dependencies: + '@types/node': 20.12.14 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + js-tokens@4.0.0: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + json5@2.2.3: {} + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kleur@3.0.3: {} + + language-subtag-registry@0.3.23: {} + + language-tags@1.0.9: + dependencies: + language-subtag-registry: 0.3.23 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + loader-runner@4.3.1: {} + + loader-utils@2.0.4: + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 2.2.3 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + lodash.sortby@4.7.0: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + math-intrinsics@1.1.0: {} + + mediabunny@1.37.0: + dependencies: + '@types/dom-mediacapture-transform': 0.1.11 + '@types/dom-webcodecs': 0.1.13 + + memfs@3.4.3: + dependencies: + fs-monkey: 1.0.3 + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + meshoptimizer@0.22.0: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mimic-fn@2.1.0: {} + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.9: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.6: {} + + minimist@1.2.8: {} + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + napi-postinstall@0.3.4: {} + + natural-compare@1.4.0: {} + + neo-async@2.6.2: {} + + next@16.1.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + '@next/env': 16.1.5 + '@swc/helpers': 0.5.15 + baseline-browser-mapping: 2.10.0 + caniuse-lite: 1.0.30001776 + postcss: 8.4.31 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + styled-jsx: 5.1.6(react@19.2.3) + optionalDependencies: + '@next/swc-darwin-arm64': 16.1.5 + '@next/swc-darwin-x64': 16.1.5 + '@next/swc-linux-arm64-gnu': 16.1.5 + '@next/swc-linux-arm64-musl': 16.1.5 + '@next/swc-linux-x64-gnu': 16.1.5 + '@next/swc-linux-x64-musl': 16.1.5 + '@next/swc-win32-arm64-msvc': 16.1.5 + '@next/swc-win32-x64-msvc': 16.1.5 + sharp: 0.34.5 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + + node-exports-info@1.6.0: + dependencies: + array.prototype.flatmap: 1.3.3 + es-errors: 1.3.0 + object.entries: 1.1.9 + semver: 6.3.1 + + node-releases@2.0.36: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-type@4.0.0: {} + + pend@1.2.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + possible-typed-array-names@1.1.0: {} + + postcss-modules-extract-imports@3.1.0(postcss@8.5.8): + dependencies: + postcss: 8.5.8 + + postcss-modules-local-by-default@4.2.0(postcss@8.5.8): + dependencies: + icss-utils: 5.1.0(postcss@8.5.8) + postcss: 8.5.8 + postcss-selector-parser: 7.1.1 + postcss-value-parser: 4.2.0 + + postcss-modules-scope@3.2.1(postcss@8.5.8): + dependencies: + postcss: 8.5.8 + postcss-selector-parser: 7.1.1 + + postcss-modules-values@4.0.0(postcss@8.5.8): + dependencies: + icss-utils: 5.1.0(postcss@8.5.8) + postcss: 8.5.8 + + postcss-selector-parser@7.1.1: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.4.31: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postcss@8.5.8: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier@3.8.1: {} + + prism-react-renderer@2.4.1(react@19.2.3): + dependencies: + '@types/prismjs': 1.26.6 + clsx: 2.1.1 + react: 19.2.3 + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + pump@3.0.4: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + react-dom@19.2.3(react@19.2.3): + dependencies: + react: 19.2.3 + scheduler: 0.27.0 + + react-is@16.13.1: {} + + react-refresh@0.18.0: {} + + react@19.2.3: {} + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + recast@0.23.11: + dependencies: + ast-types: 0.16.1 + esprima: 4.0.1 + source-map: 0.6.1 + tiny-invariant: 1.3.3 + tslib: 2.8.1 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + remotion-bits@0.1.14(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(remotion@4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3)): + dependencies: + '@types/three': 0.182.0 + prism-react-renderer: 2.4.1(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + remotion: 4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + three: 0.182.0 + + remotion@4.0.434(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + resolve@2.0.0-next.6: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.1 + node-exports-info: 1.6.0 + object-keys: 1.1.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.1.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.2.1: {} + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + scheduler@0.27.0: {} + + schema-utils@3.3.0: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.14.0 + ajv-keywords: 3.5.2(ajv@6.14.0) + + schema-utils@4.3.3: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.18.0 + ajv-formats: 2.1.1(ajv@8.18.0) + ajv-keywords: 5.1.0(ajv@8.18.0) + + semver@6.3.1: {} + + semver@7.5.3: + dependencies: + lru-cache: 6.0.0 + + semver@7.7.4: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + sharp@0.34.5: + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.7.4 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 + optional: true + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@3.0.7: {} + + sisteransi@1.0.5: {} + + slash@3.0.0: {} + + source-map-js@1.2.1: {} + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + source-map@0.7.3: {} + + source-map@0.8.0-beta.0: + dependencies: + whatwg-url: 7.1.0 + + stable-hash@0.0.5: {} + + stackframe@1.3.4: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + stream-browserify@3.0.0: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + + string.prototype.includes@2.0.1: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.1 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-bom@3.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-json-comments@3.1.1: {} + + strnum@2.2.0: {} + + style-loader@4.0.0(webpack@5.105.0(esbuild@0.25.0)): + dependencies: + webpack: 5.105.0(esbuild@0.25.0) + + styled-jsx@5.1.6(react@19.2.3): + dependencies: + client-only: 0.0.1 + react: 19.2.3 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + tapable@2.3.0: {} + + terser-webpack-plugin@5.3.17(esbuild@0.25.0)(webpack@5.105.0(esbuild@0.25.0)): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + terser: 5.46.0 + webpack: 5.105.0(esbuild@0.25.0) + optionalDependencies: + esbuild: 0.25.0 + + terser@5.46.0: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.16.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + three@0.182.0: {} + + tiny-invariant@1.3.3: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + tr46@1.0.1: + dependencies: + punycode: 2.3.1 + + ts-api-utils@2.4.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@1.14.1: {} + + tslib@2.8.1: {} + + tsutils@3.21.0(typescript@5.9.3): + dependencies: + tslib: 1.14.1 + typescript: 5.9.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript-eslint@8.21.0(eslint@9.19.0)(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.19.0)(typescript@5.9.3))(eslint@9.19.0)(typescript@5.9.3) + '@typescript-eslint/parser': 8.21.0(eslint@9.19.0)(typescript@5.9.3) + '@typescript-eslint/utils': 8.21.0(eslint@9.19.0)(typescript@5.9.3) + eslint: 9.19.0 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript-eslint@8.46.0(eslint@9.19.0)(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.46.0(@typescript-eslint/parser@8.46.0(eslint@9.19.0)(typescript@5.9.3))(eslint@9.19.0)(typescript@5.9.3) + '@typescript-eslint/parser': 8.46.0(eslint@9.19.0)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.46.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.0(eslint@9.19.0)(typescript@5.9.3) + eslint: 9.19.0 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@5.26.5: {} + + unrs-resolver@1.11.1: + dependencies: + napi-postinstall: 0.3.4 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.11.1 + '@unrs/resolver-binding-android-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-x64': 1.11.1 + '@unrs/resolver-binding-freebsd-x64': 1.11.1 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-arm64-musl': 1.11.1 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1 + '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-musl': 1.11.1 + '@unrs/resolver-binding-wasm32-wasi': 1.11.1 + '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1 + '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 + '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 + + update-browserslist-db@1.2.3(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + watchpack@2.5.1: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + + webidl-conversions@4.0.2: {} + + webpack-sources@3.3.4: {} + + webpack@5.105.0(esbuild@0.25.0): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.16.0 + acorn-import-phases: 1.0.4(acorn@8.16.0) + browserslist: 4.28.1 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.20.0 + es-module-lexer: 2.0.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.1 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.0 + terser-webpack-plugin: 5.3.17(esbuild@0.25.0)(webpack@5.105.0(esbuild@0.25.0)) + watchpack: 2.5.1 + webpack-sources: 3.3.4 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + whatwg-url@7.1.0: + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.20 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.20: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + wrappy@1.0.2: {} + + ws@8.17.1: {} + + yallist@4.0.0: {} + + yauzl@2.10.0: + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + + yocto-queue@0.1.0: {} + + zod@4.3.6: {} diff --git a/surfsense_video/remotion.config.ts b/surfsense_video/remotion.config.ts new file mode 100644 index 000000000..fa14ff003 --- /dev/null +++ b/surfsense_video/remotion.config.ts @@ -0,0 +1,8 @@ +// See all configuration options: https://remotion.dev/docs/config +// Each option also is available as a CLI flag: https://remotion.dev/docs/cli + +// Note: When using the Node.JS APIs, the config file doesn't apply. Instead, pass options directly to the APIs + +import { Config } from "@remotion/cli/config"; + +Config.setVideoImageFormat("jpeg"); diff --git a/surfsense_video/src/app/api/lambda/progress/route.ts b/surfsense_video/src/app/api/lambda/progress/route.ts new file mode 100644 index 000000000..948cee4d1 --- /dev/null +++ b/surfsense_video/src/app/api/lambda/progress/route.ts @@ -0,0 +1,44 @@ +import { + AwsRegion, + getRenderProgress, + speculateFunctionName, +} from "@remotion/lambda/client"; +import { DISK, RAM, REGION, TIMEOUT } from "../../../../../config.mjs"; +import { executeApi } from "../../../../helpers/api-response"; +import { ProgressRequest, ProgressResponse } from "../../../../types/schema"; + +export const POST = executeApi( + ProgressRequest, + async (req, body) => { + const renderProgress = await getRenderProgress({ + bucketName: body.bucketName, + functionName: speculateFunctionName({ + diskSizeInMb: DISK, + memorySizeInMb: RAM, + timeoutInSeconds: TIMEOUT, + }), + region: REGION as AwsRegion, + renderId: body.id, + }); + + if (renderProgress.fatalErrorEncountered) { + return { + type: "error", + message: renderProgress.errors[0].message, + }; + } + + if (renderProgress.done) { + return { + type: "done", + url: renderProgress.outputFile as string, + size: renderProgress.outputSizeInBytes as number, + }; + } + + return { + type: "progress", + progress: Math.max(0.03, renderProgress.overallProgress), + }; + }, +); diff --git a/surfsense_video/src/app/api/lambda/render/route.ts b/surfsense_video/src/app/api/lambda/render/route.ts new file mode 100644 index 000000000..0ca140605 --- /dev/null +++ b/surfsense_video/src/app/api/lambda/render/route.ts @@ -0,0 +1,58 @@ +import { + AwsRegion, + renderMediaOnLambda, + RenderMediaOnLambdaOutput, + speculateFunctionName, +} from "@remotion/lambda/client"; +import { + DISK, + RAM, + REGION, + SITE_NAME, + TIMEOUT, +} from "../../../../../config.mjs"; +import { executeApi } from "../../../../helpers/api-response"; +import { COMP_NAME } from "../../../../types/constants"; +import { RenderRequest } from "../../../../types/schema"; + +export const POST = executeApi( + RenderRequest, + async (req, body) => { + if ( + !process.env.AWS_ACCESS_KEY_ID && + !process.env.REMOTION_AWS_ACCESS_KEY_ID + ) { + throw new TypeError( + "Set up Remotion Lambda to render videos. See the README.md for how to do so.", + ); + } + if ( + !process.env.AWS_SECRET_ACCESS_KEY && + !process.env.REMOTION_AWS_SECRET_ACCESS_KEY + ) { + throw new TypeError( + "The environment variable REMOTION_AWS_SECRET_ACCESS_KEY is missing. Add it to your .env file.", + ); + } + + const result = await renderMediaOnLambda({ + codec: "h264", + functionName: speculateFunctionName({ + diskSizeInMb: DISK, + memorySizeInMb: RAM, + timeoutInSeconds: TIMEOUT, + }), + region: REGION as AwsRegion, + serveUrl: SITE_NAME, + composition: COMP_NAME, + inputProps: body.inputProps, + framesPerLambda: 10, + downloadBehavior: { + type: "download", + fileName: "video.mp4", + }, + }); + + return result; + }, +); diff --git a/surfsense_video/src/app/favicon.ico b/surfsense_video/src/app/favicon.ico new file mode 100644 index 000000000..718d6fea4 Binary files /dev/null and b/surfsense_video/src/app/favicon.ico differ diff --git a/surfsense_video/src/app/layout.tsx b/surfsense_video/src/app/layout.tsx new file mode 100644 index 000000000..0ab7e9194 --- /dev/null +++ b/surfsense_video/src/app/layout.tsx @@ -0,0 +1,27 @@ +import { Metadata, Viewport } from "next"; +import "../../styles/global.css"; + +export const metadata: Metadata = { + title: "Remotion and Next.js", + description: "Remotion and Next.js", +}; + +export const viewport: Viewport = { + width: "device-width", + initialScale: 1, + maximumScale: 1, +}; + +export default function RootLayout({ + // Layouts must accept a children prop. + // This will be populated with nested layouts or pages + children, +}: { + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/surfsense_video/src/app/page.tsx b/surfsense_video/src/app/page.tsx new file mode 100644 index 000000000..09308845e --- /dev/null +++ b/surfsense_video/src/app/page.tsx @@ -0,0 +1,62 @@ +"use client"; + +import { Player } from "@remotion/player"; +import type { NextPage } from "next"; +import React, { useMemo } from "react"; +import { Video, videoDuration } from "../remotion/Video"; +import { DEMO_VIDEO } from "../remotion/demo"; +import { + VIDEO_FPS, + VIDEO_HEIGHT, + VIDEO_WIDTH, +} from "../types/constants"; + +const container: React.CSSProperties = { + maxWidth: 960, + margin: "auto", + marginBottom: 20, + paddingLeft: 16, + paddingRight: 16, +}; + +const outer: React.CSSProperties = { + borderRadius: "var(--geist-border-radius)", + overflow: "hidden", + boxShadow: "0 0 200px rgba(0, 0, 0, 0.15)", + marginBottom: 40, + marginTop: 60, +}; + +const player: React.CSSProperties = { + width: "100%", +}; + +const Home: NextPage = () => { + const duration = useMemo( + () => videoDuration(DEMO_VIDEO.scenes, VIDEO_WIDTH, VIDEO_HEIGHT), + [], + ); + + return ( +
+
+
+ +
+
+
+ ); +}; + +export default Home; diff --git a/surfsense_video/src/helpers/api-response.ts b/surfsense_video/src/helpers/api-response.ts new file mode 100644 index 000000000..d4da4ce78 --- /dev/null +++ b/surfsense_video/src/helpers/api-response.ts @@ -0,0 +1,36 @@ +import { NextResponse } from "next/server"; +import { z, ZodType } from "zod"; + +export type ApiResponse = + | { + type: "error"; + message: string; + } + | { + type: "success"; + data: Res; + }; + +export const executeApi = + ( + schema: Req, + handler: (req: Request, body: z.infer) => Promise, + ) => + async (req: Request) => { + try { + const payload = await req.json(); + const parsed = schema.parse(payload); + const data = await handler(req, parsed); + return NextResponse.json({ + type: "success", + data: data, + }); + } catch (err) { + return NextResponse.json( + { type: "error", message: (err as Error).message }, + { + status: 500, + }, + ); + } + }; diff --git a/surfsense_video/src/helpers/use-rendering.ts b/surfsense_video/src/helpers/use-rendering.ts new file mode 100644 index 000000000..c84ea9c6b --- /dev/null +++ b/surfsense_video/src/helpers/use-rendering.ts @@ -0,0 +1,100 @@ +import { useCallback, useMemo, useState } from "react"; +import { z } from "zod"; +import { getProgress, renderVideo } from "../lambda/api"; +import { CompositionProps } from "../types/constants"; + +export type State = + | { + status: "init"; + } + | { + status: "invoking"; + } + | { + renderId: string; + bucketName: string; + progress: number; + status: "rendering"; + } + | { + renderId: string | null; + status: "error"; + error: Error; + } + | { + url: string; + size: number; + status: "done"; + }; + +const wait = (ms: number) => new Promise((r) => setTimeout(r, ms)); + +export const useRendering = ( + inputProps: z.infer, +) => { + const [state, setState] = useState({ + status: "init", + }); + + const renderMedia = useCallback(async () => { + setState({ status: "invoking" }); + try { + const { renderId, bucketName } = await renderVideo(inputProps); + setState({ + status: "rendering", + progress: 0, + renderId, + bucketName, + }); + + let pending = true; + + while (pending) { + const result = await getProgress({ id: renderId, bucketName }); + switch (result.type) { + case "error": { + setState({ + status: "error", + renderId, + error: new Error(result.message), + }); + pending = false; + break; + } + case "done": { + setState({ + size: result.size, + url: result.url, + status: "done", + }); + pending = false; + break; + } + case "progress": { + setState({ + status: "rendering", + bucketName, + progress: result.progress, + renderId, + }); + await wait(1000); + } + } + } + } catch (err) { + setState({ + status: "error", + error: err as Error, + renderId: null, + }); + } + }, [inputProps]); + + const undo = useCallback(() => { + setState({ status: "init" }); + }, []); + + return useMemo(() => { + return { renderMedia, state, undo }; + }, [renderMedia, state, undo]); +}; diff --git a/surfsense_video/src/lambda/api.ts b/surfsense_video/src/lambda/api.ts new file mode 100644 index 000000000..18b2c10ca --- /dev/null +++ b/surfsense_video/src/lambda/api.ts @@ -0,0 +1,52 @@ +import type { RenderMediaOnLambdaOutput } from "@remotion/lambda/client"; +import { z } from "zod"; +import type { ApiResponse } from "../helpers/api-response"; +import { CompositionProps } from "../types/constants"; +import type { + ProgressResponse, +} from "../types/schema"; +import { + ProgressRequest, + RenderRequest, +} from "../types/schema"; + +const makeRequest = async ( + endpoint: string, + body: unknown, +): Promise => { + const result = await fetch(endpoint, { + method: "post", + body: JSON.stringify(body), + headers: { + "content-type": "application/json", + }, + }); + const json = (await result.json()) as ApiResponse; + if (json.type === "error") { + throw new Error(json.message); + } + + return json.data; +}; + +export const renderVideo = async ( + inputProps: z.infer, +) => { + const body: z.infer = { inputProps }; + return makeRequest("/api/lambda/render", body); +}; + +export const getProgress = async ({ + id, + bucketName, +}: { + id: string; + bucketName: string; +}) => { + const body: z.infer = { + id, + bucketName, + }; + + return makeRequest("/api/lambda/progress", body); +}; diff --git a/surfsense_video/src/remotion/Root.tsx b/surfsense_video/src/remotion/Root.tsx new file mode 100644 index 000000000..4ba198bdf --- /dev/null +++ b/surfsense_video/src/remotion/Root.tsx @@ -0,0 +1,50 @@ +import React from "react"; +import { Composition } from "remotion"; +import { + COMP_NAME, + VIDEO_FPS, + VIDEO_HEIGHT, + VIDEO_WIDTH, +} from "../types/constants"; +import { Video, videoDuration } from "./Video"; +import { DEMO_VIDEO } from "./demo"; +import { introPreviews } from "./scenes/intro/preview"; +import { spotlightPreviews } from "./scenes/spotlight/preview"; +import { hierarchyPreviews } from "./scenes/hierarchy/preview"; +import { listPreviews } from "./scenes/list/preview"; +import { sequencePreviews } from "./scenes/sequence/preview"; +import { chartPreviews } from "./scenes/chart/preview"; +import { relationPreviews } from "./scenes/relation/preview"; +import { comparisonPreviews } from "./scenes/comparison/preview"; +import { outroPreviews } from "./scenes/outro/preview"; + +export const RemotionRoot: React.FC = () => { + return ( + <> + {/* Main composition — receives VideoInput as props, computes duration dynamically */} + ({ + durationInFrames: videoDuration(props.scenes, VIDEO_WIDTH, VIDEO_HEIGHT), + fps: VIDEO_FPS, + width: VIDEO_WIDTH, + height: VIDEO_HEIGHT, + })} + defaultProps={DEMO_VIDEO} + /> + + {/* Individual scene previews */} + {introPreviews} + {spotlightPreviews} + {hierarchyPreviews} + {listPreviews} + {sequencePreviews} + {chartPreviews} + {relationPreviews} + {comparisonPreviews} + {outroPreviews} + + ); +}; + diff --git a/surfsense_video/src/remotion/Video.tsx b/surfsense_video/src/remotion/Video.tsx new file mode 100644 index 000000000..655c79f63 --- /dev/null +++ b/surfsense_video/src/remotion/Video.tsx @@ -0,0 +1,162 @@ +/** + * Video compositor -- renders a sequence of scenes from structured JSON input. + * Each scene independently derives its own variant using Remotion's random(). + */ +import React, { useMemo } from "react"; +import { Series, useVideoConfig, random } from "remotion"; +import { THEMES } from "./theme"; +import type { VideoInput, SceneInput } from "./types"; + +import { IntroScene } from "./scenes/intro/IntroScene"; +import { SpotlightScene } from "./scenes/spotlight/SpotlightScene"; +import { HierarchyScene } from "./scenes/hierarchy/HierarchyScene"; +import { ListScene } from "./scenes/list/ListScene"; +import { SequenceScene } from "./scenes/sequence/SequenceScene"; +import { ChartScene } from "./scenes/chart/ChartScene"; +import { RelationScene } from "./scenes/relation/RelationScene"; +import { ComparisonScene } from "./scenes/comparison/ComparisonScene"; +import { OutroScene } from "./scenes/outro/OutroScene"; + +import { INTRO_DURATION } from "./scenes/intro/constants"; +import { spotlightSceneDuration } from "./scenes/spotlight/layout"; +import { hierarchySceneDuration } from "./scenes/hierarchy/layout"; +import { listSceneDuration } from "./scenes/list/layout"; +import { sequenceSceneDuration } from "./scenes/sequence/layout"; +import { chartSceneDuration } from "./scenes/chart/layout"; +import { relationSceneDuration } from "./scenes/relation/layout"; +import { comparisonSceneDuration } from "./scenes/comparison/layout"; +import { OUTRO_DURATION } from "./scenes/outro/constants"; + +import { deriveIntroVariant } from "./scenes/intro/variant"; +import { deriveSpotlightVariant } from "./scenes/spotlight/variant"; +import { deriveHierarchyVariant } from "./scenes/hierarchy/variant"; +import { deriveListVariant } from "./scenes/list/variant"; +import { deriveSequenceVariant } from "./scenes/sequence/variant"; +import { deriveChartVariant } from "./scenes/chart/variant"; +import { deriveRelationVariant } from "./scenes/relation/variant"; +import { deriveComparisonVariant } from "./scenes/comparison/variant"; +import { deriveOutroVariant } from "./scenes/outro/variant"; + +const THEME = THEMES["dark"]; + +function sceneSeed(scene: SceneInput, index: number): number { + return Math.floor(random(`${scene.type}-${index}`) * 2147483647); +} + +interface ResolvedScene { + scene: SceneInput; + duration: number; + element: React.ReactNode; +} + +function resolveScene( + scene: SceneInput, + index: number, + width: number, + height: number, +): ResolvedScene { + const seed = sceneSeed(scene, index); + const vmin = Math.min(width, height) / 100; + + switch (scene.type) { + case "intro": { + const variant = deriveIntroVariant(seed); + return { + scene, duration: INTRO_DURATION, + element: , + }; + } + case "spotlight": { + const variant = deriveSpotlightVariant(seed); + const duration = spotlightSceneDuration(scene.items.length, width, height); + return { + scene, duration, + element: , + }; + } + case "hierarchy": { + const variant = deriveHierarchyVariant(seed); + const isHoriz = variant.orientation === "left-right"; + const duration = hierarchySceneDuration(scene, width, height, isHoriz); + return { + scene, duration, + element: , + }; + } + case "list": { + const variant = deriveListVariant(seed); + const duration = listSceneDuration(scene, variant.layout, width, height); + return { + scene, duration, + element: , + }; + } + case "sequence": { + const variant = deriveSequenceVariant(seed); + const duration = sequenceSceneDuration(scene, variant.layout, width, height); + return { + scene, duration, + element: , + }; + } + case "chart": { + const variant = deriveChartVariant(seed); + const duration = chartSceneDuration(scene.items.length, variant.layout, width, height, vmin); + return { + scene, duration, + element: , + }; + } + case "relation": { + const variant = deriveRelationVariant(seed); + const duration = relationSceneDuration(scene, variant.layout, width, height); + return { + scene, duration, + element: , + }; + } + case "comparison": { + const variant = deriveComparisonVariant(seed); + const duration = comparisonSceneDuration(scene, variant.layout, width, height); + return { + scene, duration, + element: , + }; + } + case "outro": { + const variant = deriveOutroVariant(seed); + return { + scene, duration: OUTRO_DURATION, + element: , + }; + } + } +} + +export function videoDuration(scenes: SceneInput[], width: number, height: number): number { + let total = 0; + for (let i = 0; i < scenes.length; i++) { + const { duration } = resolveScene(scenes[i], i, width, height); + total += duration; + } + return total; +} + +export const Video: React.FC = ({ scenes }) => { + const { width, height } = useVideoConfig(); + + const resolved = useMemo( + () => scenes.map((s, i) => resolveScene(s, i, width, height)), + [scenes, width, height], + ); + + return ( + + {resolved.map((r, i) => ( + + {r.element} + + ))} + + ); +}; diff --git a/surfsense_video/src/remotion/demo.ts b/surfsense_video/src/remotion/demo.ts new file mode 100644 index 000000000..fdf92c01c --- /dev/null +++ b/surfsense_video/src/remotion/demo.ts @@ -0,0 +1,240 @@ +import type { VideoInput } from "./types"; + +export const DEMO_VIDEO: VideoInput = { + scenes: [ + { type: "intro", title: "The Future of AI", subtitle: "A visual exploration of artificial intelligence" }, + + { + type: "spotlight", + items: [ + { category: "stat", title: "AI Market Size", value: "$407B", desc: "Projected global AI market value by 2027, growing at 36.2% CAGR.", color: "#6c7dff" }, + ], + }, + + { + type: "hierarchy", + title: "AI Landscape", + items: [{ + label: "Artificial Intelligence", + desc: "Broad field of intelligent systems", + color: "#6c7dff", + children: [ + { label: "Machine Learning", desc: "Learning from data", color: "#3b82f6", children: [ + { label: "Supervised Learning", desc: "Labeled training data", color: "#60a5fa" }, + { label: "Unsupervised Learning", desc: "Pattern discovery", color: "#60a5fa" }, + { label: "Reinforcement Learning", desc: "Reward-based optimization", color: "#60a5fa" }, + ]}, + { label: "Deep Learning", desc: "Multi-layer neural networks", color: "#8b5cf6", children: [ + { label: "CNNs", desc: "Image recognition", color: "#a78bfa" }, + { label: "Transformers", desc: "Language understanding", color: "#a78bfa" }, + { label: "GANs", desc: "Generative models", color: "#a78bfa" }, + { label: "Diffusion Models", desc: "Image synthesis", color: "#a78bfa" }, + ]}, + { label: "NLP", desc: "Language processing", color: "#ec4899", children: [ + { label: "Sentiment Analysis", color: "#f472b6" }, + { label: "Machine Translation", color: "#f472b6" }, + { label: "Text Generation", color: "#f472b6" }, + ]}, + { label: "Computer Vision", desc: "Visual understanding", color: "#f59e0b", children: [ + { label: "Object Detection", color: "#fbbf24" }, + { label: "Image Segmentation", color: "#fbbf24" }, + ]}, + ], + }], + }, + + { + type: "list", + title: "Key Applications by Industry", + items: [ + { label: "Healthcare", desc: "Medical imaging diagnostics, drug discovery, and patient monitoring", color: "#ef4444" }, + { label: "Finance", desc: "Fraud detection, algorithmic trading, and risk assessment", color: "#3b82f6" }, + { label: "Manufacturing", desc: "Predictive maintenance, quality control, and supply chain optimization", color: "#f59e0b" }, + { label: "Retail", desc: "Recommendation engines, demand forecasting, and dynamic pricing", color: "#10b981" }, + { label: "Transportation", desc: "Autonomous vehicles, route optimization, and traffic management", color: "#8b5cf6" }, + { label: "Education", desc: "Personalized learning paths, automated grading, and adaptive tutoring", color: "#ec4899" }, + { label: "Energy", desc: "Grid optimization, consumption forecasting, and renewable integration", color: "#06b6d4" }, + ], + }, + + { + type: "sequence", + title: "Machine Learning Pipeline", + items: [ + { label: "Data Collection", desc: "Gather raw data from multiple sources and APIs", color: "#3b82f6" }, + { label: "Data Cleaning", desc: "Handle missing values, outliers, and inconsistencies", color: "#06b6d4" }, + { label: "Feature Engineering", desc: "Transform raw data into meaningful model inputs", color: "#10b981" }, + { label: "Model Training", desc: "Fit the model to training data using optimization", color: "#f59e0b" }, + { label: "Evaluation", desc: "Measure performance with test data and metrics", color: "#ef4444" }, + { label: "Deployment", desc: "Serve the model in production via APIs", color: "#8b5cf6" }, + { label: "Monitoring", desc: "Track model drift, latency, and accuracy over time", color: "#ec4899" }, + ], + }, + + { + type: "chart", + title: "Global AI Investment by Sector", + subtitle: "Billions USD (2024)", + yTitle: "Investment ($B)", + items: [ + { label: "Healthcare", value: 45, color: "#ef4444" }, + { label: "Finance", value: 38, color: "#3b82f6" }, + { label: "Automotive", value: 32, color: "#f59e0b" }, + { label: "Retail", value: 28, color: "#10b981" }, + { label: "Manufacturing", value: 24, color: "#8b5cf6" }, + { label: "Telecom", value: 19, color: "#06b6d4" }, + { label: "Energy", value: 15, color: "#ec4899" }, + { label: "Education", value: 12, color: "#f97316" }, + ], + }, + + { + type: "spotlight", + items: [ + { category: "quote", quote: "AI is probably the most important thing humanity has ever worked on. I think of it as something more profound than electricity or fire.", author: "Sundar Pichai", role: "CEO, Alphabet", color: "#f59e0b" }, + ], + }, + + { + type: "relation", + title: "AI Technology Ecosystem", + nodes: [ + { id: "llm", label: "Large Language Models", desc: "Foundation models for text", color: "#6c7dff" }, + { id: "rag", label: "RAG", desc: "Retrieval-augmented generation", color: "#3b82f6" }, + { id: "vec", label: "Vector Databases", desc: "Semantic search storage", color: "#10b981" }, + { id: "emb", label: "Embeddings", desc: "Dense vector representations", color: "#8b5cf6" }, + { id: "agent", label: "AI Agents", desc: "Autonomous task execution", color: "#ef4444" }, + { id: "tools", label: "Tool Use", desc: "External API integration", color: "#f59e0b" }, + { id: "ft", label: "Fine-Tuning", desc: "Domain-specific adaptation", color: "#ec4899" }, + { id: "eval", label: "Evaluation", desc: "Benchmarks and testing", color: "#06b6d4" }, + { id: "deploy", label: "Inference APIs", desc: "Serving infrastructure", color: "#14b8a6" }, + ], + edges: [ + { from: "llm", to: "rag", label: "powers" }, + { from: "rag", to: "vec", label: "queries" }, + { from: "emb", to: "vec", label: "indexes into" }, + { from: "llm", to: "emb", label: "generates" }, + { from: "llm", to: "agent", label: "drives" }, + { from: "agent", to: "tools", label: "invokes" }, + { from: "ft", to: "llm", label: "specializes" }, + { from: "eval", to: "llm", label: "measures" }, + { from: "llm", to: "deploy", label: "served via" }, + { from: "eval", to: "ft", label: "guides" }, + ], + }, + + { + type: "comparison", + title: "Traditional Software vs AI-Powered", + groups: [ + { + label: "Traditional Software", + color: "#3b82f6", + items: [ + { label: "Rule-Based Logic", desc: "Manually coded decision trees and if-else chains" }, + { label: "Static Analysis", desc: "Fixed algorithms that don't improve over time" }, + { label: "Manual Testing", desc: "Human-driven QA processes with scripted test cases" }, + { label: "Scheduled Updates", desc: "Quarterly release cycles with waterfall planning" }, + ], + }, + { + label: "AI-Powered Software", + color: "#10b981", + items: [ + { label: "Learned Patterns", desc: "Models that discover rules from data automatically" }, + { label: "Adaptive Analysis", desc: "Continuously improving with new data streams" }, + { label: "Automated Validation", desc: "Self-testing systems that detect regressions instantly" }, + { label: "Continuous Learning", desc: "Real-time model updates based on production feedback" }, + ], + }, + ], + }, + + { + type: "list", + title: "Challenges & Ethical Considerations", + items: [ + { label: "Bias & Fairness", desc: "Models can perpetuate or amplify societal biases present in training data", color: "#ef4444" }, + { label: "Privacy", desc: "Training on personal data raises consent and compliance concerns", color: "#f59e0b" }, + { label: "Transparency", desc: "Black-box models make it difficult to explain decisions to stakeholders", color: "#8b5cf6" }, + { label: "Job Displacement", desc: "Automation may displace workers faster than new roles are created", color: "#3b82f6" }, + { label: "Security", desc: "Adversarial attacks can manipulate model outputs in dangerous ways", color: "#ec4899" }, + { label: "Energy Consumption", desc: "Training large models requires massive computational resources", color: "#06b6d4" }, + ], + }, + + { + type: "chart", + title: "Enterprise AI Adoption Rate", + subtitle: "Percentage of enterprises using AI", + yTitle: "Adoption (%)", + items: [ + { label: "2018", value: 15, color: "#94a3b8" }, + { label: "2019", value: 22, color: "#94a3b8" }, + { label: "2020", value: 31, color: "#64748b" }, + { label: "2021", value: 42, color: "#64748b" }, + { label: "2022", value: 50, color: "#3b82f6" }, + { label: "2023", value: 58, color: "#3b82f6" }, + { label: "2024", value: 72, color: "#6c7dff" }, + { label: "2025", value: 78, color: "#6c7dff" }, + { label: "2026", value: 86, color: "#8b5cf6" }, + ], + }, + + { + type: "spotlight", + items: [ + { category: "definition", term: "Retrieval-Augmented Generation", definition: "A technique that combines large language models with external knowledge retrieval to produce more accurate, up-to-date, and verifiable responses.", example: "A chatbot that searches company documents before answering customer questions.", color: "#10b981" }, + ], + }, + + { + type: "sequence", + title: "AI Implementation Roadmap", + items: [ + { label: "Identify Use Cases", desc: "Map business problems to AI solution patterns", color: "#3b82f6" }, + { label: "Data Assessment", desc: "Audit data quality, availability, and governance", color: "#06b6d4" }, + { label: "Proof of Concept", desc: "Build and validate a small-scale prototype", color: "#10b981" }, + { label: "Infrastructure Setup", desc: "Provision compute, storage, and MLOps tooling", color: "#f59e0b" }, + { label: "Production Deployment", desc: "Scale the solution with monitoring and rollback", color: "#8b5cf6" }, + { label: "Continuous Improvement", desc: "Iterate based on user feedback and performance metrics", color: "#ec4899" }, + ], + }, + + { + type: "comparison", + title: "Open-Source vs Proprietary AI", + groups: [ + { + label: "Open-Source", + color: "#10b981", + items: [ + { label: "Full Transparency", desc: "Complete visibility into model weights and training data" }, + { label: "Community-Driven", desc: "Rapid innovation from global contributor ecosystems" }, + { label: "Cost Effective", desc: "No licensing fees, self-hosted on your infrastructure" }, + { label: "Customizable", desc: "Fine-tune and modify for specific domain needs" }, + ], + }, + { + label: "Proprietary", + color: "#6c7dff", + items: [ + { label: "Enterprise Support", desc: "Dedicated SLAs, uptime guarantees, and support teams" }, + { label: "Managed Infrastructure", desc: "No need to handle scaling, updates, or maintenance" }, + { label: "Advanced Capabilities", desc: "Access to cutting-edge models before public release" }, + { label: "Compliance Ready", desc: "Built-in certifications for regulated industries" }, + ], + }, + ], + }, + + { + type: "spotlight", + items: [ + { category: "fact", statement: "By 2030, AI is expected to contribute $15.7 trillion to the global economy — more than the current output of China and India combined.", source: "PwC Global AI Study", color: "#f59e0b" }, + ], + }, + + { type: "outro", title: "Thank You", subtitle: "Generated with SurfSense" }, + ], +}; diff --git a/surfsense_video/src/remotion/index.ts b/surfsense_video/src/remotion/index.ts new file mode 100644 index 000000000..f31c790ed --- /dev/null +++ b/surfsense_video/src/remotion/index.ts @@ -0,0 +1,4 @@ +import { registerRoot } from "remotion"; +import { RemotionRoot } from "./Root"; + +registerRoot(RemotionRoot); diff --git a/surfsense_video/src/remotion/scenes/chart/ChartScene.tsx b/surfsense_video/src/remotion/scenes/chart/ChartScene.tsx new file mode 100644 index 000000000..88ed2a540 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/chart/ChartScene.tsx @@ -0,0 +1,207 @@ +/** + * ChartScene — renders bar, column, pie, donut, or line chart + * with animated item reveals, camera paging for overflow, and optional title. + */ +import React, { useMemo } from "react"; +import { AbsoluteFill, useVideoConfig, useCurrentFrame, Easing } from "remotion"; +import type { ThemeColors } from "../../theme"; +import type { ChartSceneInput } from "./types"; +import type { ChartVariant } from "./variant"; +import { BarChart } from "./components/BarChart"; +import { PieChart } from "./components/PieChart"; +import { LineChart } from "./components/LineChart"; +import { + computeBarLayout, + buildBarWaypoints, + computeLineLayout, + buildLineWaypoints, + type Waypoint, +} from "./layout"; + +interface ChartSceneProps { + input: ChartSceneInput; + theme: ThemeColors; + variant: ChartVariant; +} + +function resolveCamera(waypoints: Waypoint[], frame: number): { cx: number; cy: number } { + let cam = { cx: waypoints[0].cx, cy: waypoints[0].cy }; + let cursor = 0; + + for (let w = 0; w < waypoints.length; w++) { + const wp = waypoints[w]; + if (frame < cursor + wp.holdFrames) { + cam = { cx: wp.cx, cy: wp.cy }; + break; + } + cursor += wp.holdFrames; + + if (wp.transitionAfter > 0 && w + 1 < waypoints.length) { + if (frame < cursor + wp.transitionAfter) { + const t = Easing.inOut(Easing.ease)( + (frame - cursor) / wp.transitionAfter, + ); + const next = waypoints[w + 1]; + cam = { + cx: wp.cx + (next.cx - wp.cx) * t, + cy: wp.cy + (next.cy - wp.cy) * t, + }; + break; + } + cursor += wp.transitionAfter; + } + + if (w === waypoints.length - 1) { + cam = { cx: wp.cx, cy: wp.cy }; + } + } + + return cam; +} + +export const ChartScene: React.FC = ({ + input, + theme, + variant, +}) => { + const { width, height } = useVideoConfig(); + const vmin = Math.min(width, height) / 100; + const frame = useCurrentFrame(); + + const titleH = input.title ? vmin * 12 : vmin * 4; + const pad = vmin * 10; + const chartW = width - pad * 2; + const chartH = height - titleH - pad * 1.2; + + const isBar = variant.layout === "bar" || variant.layout === "column"; + const isCol = variant.layout === "column"; + const isLine = variant.layout === "line"; + + const { barLayout, lineLayout, waypoints } = useMemo(() => { + if (isBar) { + const availableSize = isCol ? chartW : chartH; + const bl = computeBarLayout(input.items.length, availableSize, vmin); + const wps = buildBarWaypoints(input.items.length, bl, availableSize, isCol); + return { barLayout: bl, lineLayout: undefined, waypoints: wps }; + } + + if (isLine) { + const ll = computeLineLayout(input.items.length, chartW, vmin); + const wps = buildLineWaypoints(ll, chartW); + return { barLayout: undefined, lineLayout: ll, waypoints: wps }; + } + + return { barLayout: undefined, lineLayout: undefined, waypoints: undefined }; + }, [isBar, isCol, isLine, chartW, chartH, input.items.length, vmin]); + + const cam = waypoints ? resolveCamera(waypoints, frame) : { cx: 0, cy: 0 }; + + let chartOffsetX = 0; + let chartOffsetY = 0; + + if (isBar && waypoints) { + if (isCol) { + chartOffsetX = chartW / 2 - cam.cx; + } else { + chartOffsetY = chartH / 2 - cam.cy; + } + } else if (isLine && waypoints) { + chartOffsetX = chartW / 2 - cam.cx; + } + + const containerW = isCol && barLayout?.overflow + ? barLayout.totalSize + : isLine && lineLayout?.overflow + ? lineLayout.totalW + vmin * 8 + : chartW; + + const containerH = !isCol && isBar && barLayout?.overflow + ? barLayout.totalSize + : chartH; + + return ( + + {input.title && ( +
+
+ {input.title} +
+ {input.subtitle && ( +
+ {input.subtitle} +
+ )} +
+ )} + +
+ {isBar && barLayout && ( + + )} + {(variant.layout === "pie" || variant.layout === "donut") && ( + + )} + {isLine && ( + + )} +
+
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/chart/components/BarChart.tsx b/surfsense_video/src/remotion/scenes/chart/components/BarChart.tsx new file mode 100644 index 000000000..a0f34a82d --- /dev/null +++ b/surfsense_video/src/remotion/scenes/chart/components/BarChart.tsx @@ -0,0 +1,275 @@ +/** + * BarChart — horizontal or vertical bars with staggered animated growth. + * Uses d3-scale for linear value mapping. + */ +import React from "react"; +import { useCurrentFrame, interpolate } from "remotion"; +import { scaleLinear } from "d3-scale"; +import type { ThemeColors } from "../../../theme"; +import type { ChartItem } from "../types"; +import type { ChartVariant, ChartStyle } from "../variant"; +import { ITEM_STAGGER, ITEM_ANIM_DURATION } from "../constants"; +import type { BarLayoutInfo } from "../layout"; + +interface BarChartProps { + items: ChartItem[]; + chartW: number; + chartH: number; + vmin: number; + variant: ChartVariant; + theme: ThemeColors; + isColumn: boolean; + barLayout: BarLayoutInfo; +} + +function barFill(style: ChartStyle, color: string, isColumn: boolean): string { + switch (style) { + case "gradient": + return isColumn + ? `linear-gradient(180deg, ${color}, ${color}66)` + : `linear-gradient(90deg, ${color}66, ${color})`; + case "solid": + return color; + case "glass": + return `${color}55`; + case "outlined": + return `${color}20`; + } +} + +export const BarChart: React.FC = ({ + items, + chartW, + chartH, + vmin, + variant, + theme, + isColumn, + barLayout, +}) => { + const frame = useCurrentFrame(); + const maxVal = Math.max(...items.map((d) => d.value), 1); + + const scale = scaleLinear().domain([0, maxVal]).range([0, 1]); + + const { barThickness, gap } = barLayout; + const labelFontSize = vmin * 1.8; + const valueFontSize = vmin * 1.6; + const labelSpace = isColumn ? vmin * 8 : vmin * 18; + const valueSpace = variant.showValues ? vmin * 10 : 0; + + const barArea = isColumn ? chartW : chartW - labelSpace - valueSpace; + const gridLines = variant.showGrid ? [0.25, 0.5, 0.75, 1] : []; + + return ( +
+ {/* Grid lines */} + {gridLines.map((frac) => { + if (isColumn) { + const y = chartH - labelSpace - (chartH - labelSpace) * frac; + return ( +
+ ); + } + const x = labelSpace + barArea * frac; + return ( +
+ ); + })} + + {items.map((item, i) => { + const color = item.color ?? "#6c7dff"; + const enterF = i * ITEM_STAGGER; + const localFrame = frame - enterF; + + const progress = interpolate( + localFrame, + [0, ITEM_ANIM_DURATION], + [0, 1], + { extrapolateLeft: "clamp", extrapolateRight: "clamp" }, + ); + + const opacity = interpolate( + localFrame, + [0, ITEM_ANIM_DURATION * 0.5], + [0, 1], + { extrapolateLeft: "clamp", extrapolateRight: "clamp" }, + ); + + const ratio = scale(item.value) * progress; + const bg = barFill(variant.chartStyle, color, isColumn); + const borderRadius = vmin * 0.5; + + if (isColumn) { + const x = i * (barThickness + gap); + const maxBarH = chartH - labelSpace; + const barH = maxBarH * ratio; + + return ( + +
+ {/* Label below */} +
+ + {item.label} + +
+ {/* Value on top */} + {variant.showValues && ( +
+ + {item.value} + +
+ )} + + ); + } + + // Horizontal bar + const y = i * (barThickness + gap); + const barW = barArea * ratio; + + return ( + + {/* Label on left */} +
+ + {item.label} + +
+ {/* Bar */} +
+ {/* Value at end */} + {variant.showValues && ( +
+ + {item.value} + +
+ )} + + ); + })} +
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/chart/components/LineChart.tsx b/surfsense_video/src/remotion/scenes/chart/components/LineChart.tsx new file mode 100644 index 000000000..3e9c58d75 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/chart/components/LineChart.tsx @@ -0,0 +1,211 @@ +/** + * LineChart — animated line with data points and optional area fill. + * Uses d3-scale for axis mapping and d3-shape for curve generation. + * Supports overflow: when lineLayout.overflow is true, the chart is wider + * than the viewport and ChartScene pans the camera horizontally. + */ +import React from "react"; +import { useCurrentFrame, interpolate } from "remotion"; +import { scaleLinear, scalePoint } from "d3-scale"; +import { line as d3Line, area as d3Area, curveMonotoneX } from "d3-shape"; +import type { ThemeColors } from "../../../theme"; +import type { ChartItem } from "../types"; +import type { ChartVariant, ChartStyle } from "../variant"; +import { ITEM_STAGGER, ITEM_ANIM_DURATION } from "../constants"; +import type { LineLayoutInfo } from "../layout"; + +interface LineChartProps { + items: ChartItem[]; + chartW: number; + chartH: number; + vmin: number; + variant: ChartVariant; + theme: ThemeColors; + lineLayout?: LineLayoutInfo; +} + +function lineColor(style: ChartStyle, color: string): string { + switch (style) { + case "gradient": + case "solid": + return color; + case "glass": + return `${color}cc`; + case "outlined": + return color; + } +} + +export const LineChart: React.FC = ({ + items, + chartW, + chartH, + vmin, + variant, + theme, + lineLayout, +}) => { + const frame = useCurrentFrame(); + const padL = vmin * 4; + const padR = vmin * 4; + const padT = vmin * 5; + const padB = vmin * 8; + + const renderW = lineLayout?.overflow ? lineLayout.totalW + padL + padR : chartW; + const plotW = renderW - padL - padR; + const plotH = chartH - padT - padB; + + const maxVal = Math.max(...items.map((d) => d.value), 1); + const minVal = Math.min(...items.map((d) => d.value), 0); + const yRange = maxVal - minVal || 1; + + const xScale = scalePoint() + .domain(items.map((_, i) => i)) + .range([0, plotW]); + + const yScale = scaleLinear() + .domain([minVal - yRange * 0.1, maxVal + yRange * 0.1]) + .range([plotH, 0]); + + const points = items.map((d, i) => ({ + x: (xScale(i) ?? 0) + padL, + y: yScale(d.value) + padT, + item: d, + index: i, + })); + + const primaryColor = items[0]?.color ?? "#6c7dff"; + const strokeColor = lineColor(variant.chartStyle, primaryColor); + + const lineGen = d3Line<(typeof points)[number]>() + .x((d) => d.x) + .y((d) => d.y) + .curve(curveMonotoneX); + + const areaGen = d3Area<(typeof points)[number]>() + .x((d) => d.x) + .y0(padT + plotH) + .y1((d) => d.y) + .curve(curveMonotoneX); + + const totalPoints = points.length; + const totalAnimFrames = (totalPoints - 1) * ITEM_STAGGER + ITEM_ANIM_DURATION; + + const lineProgress = interpolate( + frame, + [0, totalAnimFrames], + [0, 1], + { extrapolateLeft: "clamp", extrapolateRight: "clamp" }, + ); + + const linePath = lineGen(points) ?? ""; + const areaPath = areaGen(points) ?? ""; + + const gridLines = variant.showGrid ? [0.25, 0.5, 0.75] : []; + const labelFontSize = vmin * 1.6; + const valueFontSize = vmin * 1.5; + const gradientId = "line-area-grad"; + const showArea = variant.chartStyle !== "outlined"; + + return ( + + + + + + + + + + + + {/* Grid */} + {gridLines.map((frac) => { + const y = padT + plotH * (1 - frac); + return ( + + ); + })} + + {/* Area fill */} + {showArea && ( + + )} + + {/* Line */} + + + {/* Data points + labels */} + {points.map((pt, i) => { + const color = pt.item.color ?? primaryColor; + const enterF = i * ITEM_STAGGER; + const localFrame = frame - enterF; + + const opacity = interpolate( + localFrame, + [0, ITEM_ANIM_DURATION * 0.5], + [0, 1], + { extrapolateLeft: "clamp", extrapolateRight: "clamp" }, + ); + + const dotR = vmin * 0.6; + + return ( + + + {variant.showValues && ( + + {pt.item.value} + + )} + + {pt.item.label} + + + ); + })} + + ); +}; diff --git a/surfsense_video/src/remotion/scenes/chart/components/PieChart.tsx b/surfsense_video/src/remotion/scenes/chart/components/PieChart.tsx new file mode 100644 index 000000000..0c1617395 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/chart/components/PieChart.tsx @@ -0,0 +1,161 @@ +/** + * PieChart — animated arc segments with labels. + * Uses d3-shape pie() and arc() generators. + * Supports donut mode via innerRadius. + */ +import React from "react"; +import { useCurrentFrame, interpolate } from "remotion"; +import { pie as d3Pie, arc as d3Arc } from "d3-shape"; +import type { ThemeColors } from "../../../theme"; +import type { ChartItem } from "../types"; +import type { ChartVariant, ChartStyle } from "../variant"; +import { ITEM_STAGGER, ITEM_ANIM_DURATION } from "../constants"; + +interface PieChartProps { + items: ChartItem[]; + chartW: number; + chartH: number; + vmin: number; + variant: ChartVariant; + theme: ThemeColors; + isDonut: boolean; +} + +function sliceFill(style: ChartStyle, color: string): string { + switch (style) { + case "gradient": + case "solid": + return color; + case "glass": + return `${color}88`; + case "outlined": + return `${color}20`; + } +} + +function sliceStroke(style: ChartStyle, color: string): string | undefined { + if (style === "outlined") return color; + if (style === "glass") return `${color}cc`; + return undefined; +} + +export const PieChart: React.FC = ({ + items, + chartW, + chartH, + vmin, + variant, + theme, + isDonut, +}) => { + const frame = useCurrentFrame(); + const size = Math.min(chartW, chartH); + const radius = size / 2 - vmin * 6; + const innerR = isDonut ? radius * 0.55 : 0; + const cx = chartW / 2; + const cy = chartH / 2; + + const pieGen = d3Pie() + .value((d) => d.value) + .sort(null) + .padAngle(vmin * 0.006); + + const arcs = pieGen(items); + + const arcGen = d3Arc<(typeof arcs)[number]>() + .innerRadius(innerR) + .outerRadius(radius) + .cornerRadius(vmin * 0.4); + + const labelRadius = radius + vmin * 4; + const labelFontSize = vmin * 1.8; + const valueFontSize = vmin * 1.5; + const total = items.reduce((s, d) => s + d.value, 0); + + return ( + + {arcs.map((d, i) => { + const color = d.data.color ?? "#6c7dff"; + const enterF = i * ITEM_STAGGER; + const localFrame = frame - enterF; + + const progress = interpolate( + localFrame, + [0, ITEM_ANIM_DURATION], + [0, 1], + { extrapolateLeft: "clamp", extrapolateRight: "clamp" }, + ); + + const opacity = interpolate( + localFrame, + [0, ITEM_ANIM_DURATION * 0.4], + [0, 1], + { extrapolateLeft: "clamp", extrapolateRight: "clamp" }, + ); + + const animatedArc = { + ...d, + endAngle: d.startAngle + (d.endAngle - d.startAngle) * progress, + }; + + const path = arcGen(animatedArc) ?? ""; + const midAngle = (d.startAngle + d.endAngle) / 2; + const lx = Math.cos(midAngle - Math.PI / 2) * labelRadius; + const ly = Math.sin(midAngle - Math.PI / 2) * labelRadius; + const isRight = lx > 0; + const fill = sliceFill(variant.chartStyle, color); + const stroke = sliceStroke(variant.chartStyle, color); + const pct = total > 0 ? Math.round((d.data.value / total) * 100) : 0; + + return ( + + + {/* Label line + text */} + {progress > 0.5 && ( + <> + + + {d.data.label} + + {variant.showValues && ( + + {d.data.value} ({pct}%) + + )} + + )} + + ); + })} + + ); +}; diff --git a/surfsense_video/src/remotion/scenes/chart/constants.ts b/surfsense_video/src/remotion/scenes/chart/constants.ts new file mode 100644 index 000000000..f0e896eb7 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/chart/constants.ts @@ -0,0 +1,10 @@ +/** Timing constants for chart scene animations. */ + +/** Frames each bar/slice/point staggers its entrance. */ +export const ITEM_STAGGER = 8; + +/** Frames for each item's grow/fade-in animation. */ +export const ITEM_ANIM_DURATION = 20; + +/** Frames to hold the fully revealed chart before scene ends. */ +export const HOLD_AFTER = 90; diff --git a/surfsense_video/src/remotion/scenes/chart/demo.ts b/surfsense_video/src/remotion/scenes/chart/demo.ts new file mode 100644 index 000000000..a77469971 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/chart/demo.ts @@ -0,0 +1,90 @@ +/** Sample chart data for Remotion Studio preview. */ +import type { ChartSceneInput } from "./types"; + +export const DEMO_CHART: ChartSceneInput = { + type: "chart", + title: "Quarterly Revenue", + subtitle: "Revenue by product line (in millions)", + items: [ + { label: "Cloud", value: 42, color: "#6c7dff" }, + { label: "SaaS", value: 35, color: "#00c9a7" }, + { label: "Enterprise", value: 28, color: "#ff6b6b" }, + { label: "Mobile", value: 22, color: "#ffd93d" }, + { label: "IoT", value: 18, color: "#c084fc" }, + { label: "AI/ML", value: 31, color: "#f97316" }, + { label: "Security", value: 15, color: "#14b8a6" }, + ], +}; + +/** 20 items — triggers overflow & camera paging for horizontal bar charts. */ +export const DEMO_CHART_LARGE: ChartSceneInput = { + type: "chart", + title: "Top 20 Markets", + subtitle: "Annual revenue by region (in millions)", + items: [ + { label: "North America", value: 65, color: "#6c7dff" }, + { label: "Europe", value: 52, color: "#00c9a7" }, + { label: "Asia Pacific", value: 48, color: "#ff6b6b" }, + { label: "Latin America", value: 34, color: "#ffd93d" }, + { label: "Middle East", value: 29, color: "#c084fc" }, + { label: "Africa", value: 22, color: "#f97316" }, + { label: "Oceania", value: 19, color: "#14b8a6" }, + { label: "Southeast Asia", value: 38, color: "#e879f9" }, + { label: "Eastern Europe", value: 26, color: "#38bdf8" }, + { label: "Central Asia", value: 15, color: "#a3e635" }, + { label: "Nordics", value: 21, color: "#fb923c" }, + { label: "Iberian", value: 17, color: "#f472b6" }, + { label: "South Asia", value: 33, color: "#2dd4bf" }, + { label: "Caribbean", value: 12, color: "#818cf8" }, + { label: "Sub-Saharan", value: 9, color: "#fbbf24" }, + { label: "Balkans", value: 14, color: "#34d399" }, + { label: "Scandinavia", value: 24, color: "#a78bfa" }, + { label: "Benelux", value: 20, color: "#f87171" }, + { label: "Indochina", value: 27, color: "#22d3ee" }, + { label: "Polynesia", value: 8, color: "#facc15" }, + ], +}; + +/** 35 items — triggers overflow & camera paging for column and line charts (wider viewport). */ +export const DEMO_CHART_XL: ChartSceneInput = { + type: "chart", + title: "Global Technology Index", + subtitle: "Innovation score by country", + items: [ + { label: "USA", value: 95, color: "#6c7dff" }, + { label: "China", value: 88, color: "#00c9a7" }, + { label: "Japan", value: 82, color: "#ff6b6b" }, + { label: "Germany", value: 79, color: "#ffd93d" }, + { label: "UK", value: 76, color: "#c084fc" }, + { label: "S.Korea", value: 85, color: "#f97316" }, + { label: "France", value: 73, color: "#14b8a6" }, + { label: "Canada", value: 71, color: "#e879f9" }, + { label: "India", value: 68, color: "#38bdf8" }, + { label: "Australia", value: 66, color: "#a3e635" }, + { label: "Israel", value: 80, color: "#fb923c" }, + { label: "Sweden", value: 74, color: "#f472b6" }, + { label: "Singapore", value: 78, color: "#2dd4bf" }, + { label: "Taiwan", value: 77, color: "#818cf8" }, + { label: "Netherlands", value: 72, color: "#fbbf24" }, + { label: "Switzerland", value: 75, color: "#34d399" }, + { label: "Finland", value: 69, color: "#a78bfa" }, + { label: "Denmark", value: 67, color: "#f87171" }, + { label: "Norway", value: 65, color: "#22d3ee" }, + { label: "Ireland", value: 63, color: "#facc15" }, + { label: "Austria", value: 60, color: "#6c7dff" }, + { label: "Belgium", value: 58, color: "#00c9a7" }, + { label: "Spain", value: 55, color: "#ff6b6b" }, + { label: "Italy", value: 54, color: "#ffd93d" }, + { label: "Poland", value: 50, color: "#c084fc" }, + { label: "Brazil", value: 48, color: "#f97316" }, + { label: "Mexico", value: 44, color: "#14b8a6" }, + { label: "Turkey", value: 42, color: "#e879f9" }, + { label: "Thailand", value: 40, color: "#38bdf8" }, + { label: "Malaysia", value: 45, color: "#a3e635" }, + { label: "Vietnam", value: 38, color: "#fb923c" }, + { label: "Indonesia", value: 36, color: "#f472b6" }, + { label: "Philippines", value: 33, color: "#2dd4bf" }, + { label: "Chile", value: 46, color: "#818cf8" }, + { label: "Argentina", value: 41, color: "#fbbf24" }, + ], +}; diff --git a/surfsense_video/src/remotion/scenes/chart/layout.ts b/surfsense_video/src/remotion/scenes/chart/layout.ts new file mode 100644 index 000000000..1adec61fd --- /dev/null +++ b/surfsense_video/src/remotion/scenes/chart/layout.ts @@ -0,0 +1,191 @@ +/** Chart scene layout utilities — bar/line sizing, camera waypoints, duration. */ +import { ITEM_STAGGER, ITEM_ANIM_DURATION, HOLD_AFTER } from "./constants"; +import type { ChartLayout } from "./variant"; + +export interface Waypoint { + cx: number; + cy: number; + holdFrames: number; + transitionAfter: number; +} + +const STOP_HOLD = 120; +const STOP_TRANSITION = 18; + +/* ─── Bar / Column layout ─── */ + +export interface BarLayoutInfo { + barThickness: number; + gap: number; + totalSize: number; + overflow: boolean; +} + +export function computeBarLayout( + itemCount: number, + availableSize: number, + vmin: number, +): BarLayoutInfo { + const gap = vmin * 1.2; + const minThickness = vmin * 4; + const maxThickness = vmin * 6; + const totalGaps = (itemCount - 1) * gap; + const rawThickness = (availableSize - totalGaps) / itemCount; + const barThickness = Math.max(minThickness, Math.min(rawThickness, maxThickness)); + const totalSize = itemCount * barThickness + totalGaps; + const overflow = totalSize > availableSize; + + return { barThickness, gap, totalSize, overflow }; +} + +export function buildBarWaypoints( + itemCount: number, + barLayout: BarLayoutInfo, + viewSize: number, + isColumn: boolean, +): Waypoint[] { + if (!barLayout.overflow) { + const center = barLayout.totalSize / 2; + return [{ + cx: isColumn ? center : 0, + cy: isColumn ? 0 : center, + holdFrames: STOP_HOLD, + transitionAfter: 0, + }]; + } + + const usable = viewSize * 0.85; + const { barThickness, gap } = barLayout; + const step = barThickness + gap; + + const stops: Waypoint[] = []; + let i = 0; + + while (i < itemCount) { + const startPos = i * step; + let lastIdx = i; + + while ( + lastIdx + 1 < itemCount && + (lastIdx + 1) * step + barThickness - startPos <= usable + ) { + lastIdx++; + } + + const groupEnd = lastIdx * step + barThickness; + const center = (startPos + groupEnd) / 2; + + stops.push({ + cx: isColumn ? center : 0, + cy: isColumn ? 0 : center, + holdFrames: STOP_HOLD, + transitionAfter: STOP_TRANSITION, + }); + + i = lastIdx + 1; + } + + if (stops.length > 0) stops[stops.length - 1].transitionAfter = 0; + return stops; +} + +/* ─── Line chart layout ─── */ + +export interface LineLayoutInfo { + totalW: number; + pointSpacing: number; + overflow: boolean; +} + +export function computeLineLayout( + itemCount: number, + availableW: number, + vmin: number, +): LineLayoutInfo { + const minSpacing = vmin * 8; + const naturalSpacing = availableW / Math.max(itemCount - 1, 1); + + if (naturalSpacing >= minSpacing) { + return { totalW: availableW, pointSpacing: naturalSpacing, overflow: false }; + } + + const totalW = (itemCount - 1) * minSpacing; + return { totalW, pointSpacing: minSpacing, overflow: true }; +} + +export function buildLineWaypoints( + lineLayout: LineLayoutInfo, + viewW: number, +): Waypoint[] { + if (!lineLayout.overflow) { + return [{ + cx: lineLayout.totalW / 2, + cy: 0, + holdFrames: STOP_HOLD, + transitionAfter: 0, + }]; + } + + const usable = viewW * 0.85; + const stops: Waypoint[] = []; + let pos = 0; + + while (pos < lineLayout.totalW) { + const windowEnd = Math.min(pos + usable, lineLayout.totalW); + const center = (pos + windowEnd) / 2; + + stops.push({ + cx: center, + cy: 0, + holdFrames: STOP_HOLD, + transitionAfter: STOP_TRANSITION, + }); + + if (windowEnd >= lineLayout.totalW) break; + pos = windowEnd - usable * 0.15; + } + + if (stops.length > 0) stops[stops.length - 1].transitionAfter = 0; + return stops; +} + +/* ─── Duration helpers ─── */ + +export function waypointsDuration(waypoints: Waypoint[]): number { + let total = 0; + for (const wp of waypoints) { + total += wp.holdFrames + wp.transitionAfter; + } + return Math.max(1, total); +} + +export function chartSceneDuration( + itemCount: number, + layout: ChartLayout, + viewW: number, + viewH: number, + vmin: number, +): number { + const animPhase = (itemCount - 1) * ITEM_STAGGER + ITEM_ANIM_DURATION; + + if (layout === "bar" || layout === "column") { + const isCol = layout === "column"; + const availableSize = isCol ? viewW : viewH; + const barInfo = computeBarLayout(itemCount, availableSize, vmin); + + if (barInfo.overflow) { + const wps = buildBarWaypoints(itemCount, barInfo, availableSize, isCol); + return animPhase + waypointsDuration(wps); + } + } + + if (layout === "line") { + const lineInfo = computeLineLayout(itemCount, viewW, vmin); + if (lineInfo.overflow) { + const wps = buildLineWaypoints(lineInfo, viewW); + return animPhase + waypointsDuration(wps); + } + } + + return animPhase + HOLD_AFTER; +} diff --git a/surfsense_video/src/remotion/scenes/chart/preview.tsx b/surfsense_video/src/remotion/scenes/chart/preview.tsx new file mode 100644 index 000000000..3c9be05e5 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/chart/preview.tsx @@ -0,0 +1,124 @@ +/** Remotion Studio compositions for chart scene. */ +import React from "react"; +import { Composition } from "remotion"; +import { ChartScene } from "./ChartScene"; +import { THEMES } from "../../theme"; +import type { ChartVariant, ChartLayout } from "./variant"; +import { DEMO_CHART, DEMO_CHART_LARGE, DEMO_CHART_XL } from "./demo"; +import { chartSceneDuration } from "./layout"; + +const THEME = "dark" as const; +const FPS = 30; +const WIDTH = 1920; +const HEIGHT = 1080; + +const vmin = Math.min(WIDTH, HEIGHT) / 100; + +const ChartPreview: React.FC<{ variant: ChartVariant; data?: typeof DEMO_CHART }> = ({ + variant, + data = DEMO_CHART, +}) => { + const theme = THEMES[THEME]; + return ; +}; + +const layouts: ChartLayout[] = ["bar", "column", "pie", "donut", "line"]; + +const base: ChartVariant = { + layout: "bar", + chartStyle: "gradient", + showValues: true, + showGrid: true, +}; + +function dur(itemCount: number, layout: ChartLayout) { + return chartSceneDuration(itemCount, layout, WIDTH, HEIGHT, vmin); +} + +export const chartPreviews = ( + <> + {/* Standard (7 items) — all layouts */} + {layouts.map((layout) => { + const v: ChartVariant = { ...base, layout }; + return ( + } + durationInFrames={dur(DEMO_CHART.items.length, layout)} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + ); + })} + ( + + )} + durationInFrames={dur(DEMO_CHART.items.length, "bar")} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + ( + + )} + durationInFrames={dur(DEMO_CHART.items.length, "pie")} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + + {/* Large (20 items) — bar overflows, camera pans vertically */} + ( + + )} + durationInFrames={dur(DEMO_CHART_LARGE.items.length, "bar")} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + + {/* XL (35 items) — column overflows horizontally, camera pans */} + ( + + )} + durationInFrames={dur(DEMO_CHART_XL.items.length, "column")} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + + {/* XL (35 items) — line overflows horizontally, camera pans */} + ( + + )} + durationInFrames={dur(DEMO_CHART_XL.items.length, "line")} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + + {/* XL bar — 35 items, horizontal bar with heavy paging */} + ( + + )} + durationInFrames={dur(DEMO_CHART_XL.items.length, "bar")} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + +); diff --git a/surfsense_video/src/remotion/scenes/chart/types.ts b/surfsense_video/src/remotion/scenes/chart/types.ts new file mode 100644 index 000000000..4a72bb10d --- /dev/null +++ b/surfsense_video/src/remotion/scenes/chart/types.ts @@ -0,0 +1,21 @@ +/** Zod schemas and inferred types for chart scene data. */ +import { z } from "zod"; + +export const ChartItemSchema = z.object({ + label: z.string(), + value: z.number(), + color: z.string().optional(), +}); + +export type ChartItem = z.infer; + +export const ChartSceneInput = z.object({ + type: z.literal("chart"), + title: z.string().optional(), + subtitle: z.string().optional(), + xTitle: z.string().optional(), + yTitle: z.string().optional(), + items: z.array(ChartItemSchema).min(1), +}); + +export type ChartSceneInput = z.infer; diff --git a/surfsense_video/src/remotion/scenes/chart/variant.ts b/surfsense_video/src/remotion/scenes/chart/variant.ts new file mode 100644 index 000000000..b1aa1cdcb --- /dev/null +++ b/surfsense_video/src/remotion/scenes/chart/variant.ts @@ -0,0 +1,25 @@ +/** Chart scene variant — seed-deterministic visual style. */ +import { random } from "remotion"; + +export type ChartLayout = "bar" | "column" | "pie" | "donut" | "line"; +export type ChartStyle = "gradient" | "solid" | "glass" | "outlined"; + +export interface ChartVariant { + layout: ChartLayout; + chartStyle: ChartStyle; + showValues: boolean; + showGrid: boolean; +} + +export function deriveChartVariant(seed: number): ChartVariant { + const s = (key: string) => random(`${seed}-${key}`); + const pick = (key: string, arr: T[]) => + arr[Math.floor(s(key) * arr.length)]; + + return { + layout: pick("layout", ["bar", "column", "pie", "donut", "line"] as ChartLayout[]), + chartStyle: pick("chartStyle", ["gradient", "solid", "glass", "outlined"] as ChartStyle[]), + showValues: s("values") > 0.3, + showGrid: s("grid") > 0.4, + }; +} diff --git a/surfsense_video/src/remotion/scenes/comparison/ComparisonScene.tsx b/surfsense_video/src/remotion/scenes/comparison/ComparisonScene.tsx new file mode 100644 index 000000000..a6b9bfa87 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/comparison/ComparisonScene.tsx @@ -0,0 +1,201 @@ +/** + * ComparisonScene — renders groups side-by-side in binary or table layout + * with staggered reveals, animated dividers, and camera paging. + */ +import React, { useMemo } from "react"; +import { AbsoluteFill, useVideoConfig, useCurrentFrame, Easing } from "remotion"; +import type { ThemeColors } from "../../theme"; +import type { ComparisonSceneInput, Waypoint } from "./types"; +import type { ComparisonVariant } from "./variant"; +import { ITEM_STAGGER, GROUP_STAGGER } from "./constants"; +import { CompareCard } from "./components/CompareCard"; +import { GroupHeader } from "./components/GroupHeader"; +import { CompareDivider } from "./components/Divider"; +import { computeComparisonLayout, buildWaypoints } from "./layout"; + +const DEFAULT_COLORS = ["#6c7dff", "#00c9a7", "#ff6b6b", "#ffd93d"]; + +interface ComparisonSceneProps { + input: ComparisonSceneInput; + theme: ThemeColors; + variant: ComparisonVariant; +} + +function resolveCamera( + waypoints: Waypoint[], + frame: number, +): { cx: number; cy: number } { + let cam = { cx: waypoints[0].cx, cy: waypoints[0].cy }; + let cursor = 0; + + for (let w = 0; w < waypoints.length; w++) { + const wp = waypoints[w]; + if (frame < cursor + wp.holdFrames) { + cam = { cx: wp.cx, cy: wp.cy }; + break; + } + cursor += wp.holdFrames; + + if (wp.transitionAfter > 0 && w + 1 < waypoints.length) { + if (frame < cursor + wp.transitionAfter) { + const t = Easing.inOut(Easing.ease)( + (frame - cursor) / wp.transitionAfter, + ); + const next = waypoints[w + 1]; + cam = { + cx: wp.cx + (next.cx - wp.cx) * t, + cy: wp.cy + (next.cy - wp.cy) * t, + }; + break; + } + cursor += wp.transitionAfter; + } + + if (w === waypoints.length - 1) { + cam = { cx: wp.cx, cy: wp.cy }; + } + } + + return cam; +} + +export const ComparisonScene: React.FC = ({ + input, + theme, + variant, +}) => { + const { width, height } = useVideoConfig(); + const vmin = Math.min(width, height) / 100; + const frame = useCurrentFrame(); + + const titleOffset = input.title ? vmin * 12 : 0; + + const { layoutResult, waypoints } = useMemo(() => { + const lr = computeComparisonLayout(input, variant.layout, vmin); + const wps = buildWaypoints(lr.contentW, lr.contentH, width, height, titleOffset); + return { layoutResult: lr, waypoints: wps }; + }, [input, variant.layout, vmin, width, height, titleOffset]); + + const cam = resolveCamera(waypoints, frame); + const panX = width / 2 - cam.cx; + const panY = height / 2 - cam.cy; + + const groupColors = input.groups.map( + (g, i) => g.color ?? DEFAULT_COLORS[i % DEFAULT_COLORS.length], + ); + + return ( + + {input.title && ( +
+
+ {input.title} +
+ {input.subtitle && ( +
+ {input.subtitle} +
+ )} +
+ )} + +
+ {layoutResult.dividers.map((d, i) => ( + + ))} + + {layoutResult.headers.map((h) => { + const enterF = h.groupIdx * GROUP_STAGGER; + const group = input.groups[h.groupIdx]; + if (!group || !group.label) return null; + return ( +
+ +
+ ); + })} + + {layoutResult.items.map((it) => { + const group = input.groups[it.groupIdx]; + if (!group) return null; + const item = group.items[it.itemIdx]; + if (!item) return null; + + let globalIdx = 0; + for (let g = 0; g < it.groupIdx; g++) { + globalIdx += input.groups[g].items.length; + } + globalIdx += it.itemIdx; + + const enterF = it.groupIdx * GROUP_STAGGER + globalIdx * ITEM_STAGGER; + + return ( +
+ +
+ ); + })} +
+
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/comparison/components/CompareCard.tsx b/surfsense_video/src/remotion/scenes/comparison/components/CompareCard.tsx new file mode 100644 index 000000000..464c778f0 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/comparison/components/CompareCard.tsx @@ -0,0 +1,127 @@ +/** Single comparison item card with variant-driven styling. */ +import React from "react"; +import { useCurrentFrame, interpolate, spring, useVideoConfig } from "remotion"; +import type { ThemeColors } from "../../../theme"; +import type { CompareItem } from "../types"; +import type { ComparisonCardStyle } from "../variant"; +import { ITEM_FADE_DURATION } from "../constants"; + +interface CompareCardProps { + item: CompareItem; + enterFrame: number; + vmin: number; + w: number; + h: number; + color: string; + cardStyle: ComparisonCardStyle; + theme: ThemeColors; +} + +function cardCSS( + style: ComparisonCardStyle, + color: string, + vmin: number, +): React.CSSProperties { + const bw = vmin * 0.14; + + switch (style) { + case "gradient": + return { + background: `linear-gradient(145deg, ${color}14, ${color}06)`, + border: `${bw}px solid ${color}35`, + boxShadow: `0 ${vmin * 0.4}px ${vmin * 1.5}px ${color}10`, + }; + case "glass": + return { + background: `${color}0a`, + backdropFilter: "blur(12px)", + border: `${bw}px solid ${color}20`, + boxShadow: `inset 0 ${vmin * 0.1}px ${vmin * 0.5}px ${color}12`, + }; + case "outline": + return { + background: "transparent", + border: `${vmin * 0.18}px solid ${color}55`, + }; + case "solid": + return { + background: `${color}15`, + border: `${bw}px solid ${color}28`, + }; + } +} + +export const CompareCard: React.FC = ({ + item, + enterFrame, + vmin, + w, + h, + color, + cardStyle, + theme, +}) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + const localFrame = frame - enterFrame; + + const opacity = interpolate( + localFrame, + [0, ITEM_FADE_DURATION], + [0, 1], + { extrapolateLeft: "clamp", extrapolateRight: "clamp" }, + ); + + const scale = spring({ + frame: Math.max(0, localFrame), + fps, + config: { damping: 14, stiffness: 120, mass: 0.5 }, + }); + + const fontSize = vmin * 2; + const descFontSize = vmin * 1.5; + + return ( +
+
+ {item.label} +
+ {item.desc && ( +
+ {item.desc} +
+ )} +
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/comparison/components/Divider.tsx b/surfsense_video/src/remotion/scenes/comparison/components/Divider.tsx new file mode 100644 index 000000000..dafd40bc1 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/comparison/components/Divider.tsx @@ -0,0 +1,110 @@ +/** VS divider for comparison layouts. */ +import React from "react"; +import { useCurrentFrame, interpolate, spring, useVideoConfig } from "remotion"; +import type { ThemeColors } from "../../../theme"; +import type { ComparisonDivider } from "../variant"; +import type { DividerInfo } from "../layout"; +import { DIVIDER_DELAY, ITEM_FADE_DURATION } from "../constants"; + +interface DividerProps { + info: DividerInfo; + dividerStyle: ComparisonDivider; + vmin: number; + theme: ThemeColors; + leftColor?: string; + rightColor?: string; +} + +export const CompareDivider: React.FC = ({ + info, + dividerStyle, + vmin, + theme, + leftColor = "#6c7dff", + rightColor = "#00c9a7", +}) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + if (dividerStyle === "none") return null; + + const opacity = interpolate( + frame - DIVIDER_DELAY, + [0, ITEM_FADE_DURATION], + [0, 1], + { extrapolateLeft: "clamp", extrapolateRight: "clamp" }, + ); + + const scale = spring({ + frame: Math.max(0, frame - DIVIDER_DELAY), + fps, + config: { damping: 12, stiffness: 100 }, + }); + + return ( +
+ {dividerStyle === "vs" && ( +
+
+
+ VS +
+
+
+ )} + {dividerStyle === "line" && ( +
+ )} +
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/comparison/components/GroupHeader.tsx b/surfsense_video/src/remotion/scenes/comparison/components/GroupHeader.tsx new file mode 100644 index 000000000..8ca4ade5b --- /dev/null +++ b/surfsense_video/src/remotion/scenes/comparison/components/GroupHeader.tsx @@ -0,0 +1,73 @@ +/** Group header pill badge for comparison columns. */ +import React from "react"; +import { useCurrentFrame, interpolate, spring, useVideoConfig } from "remotion"; +import type { ThemeColors } from "../../../theme"; +import { ITEM_FADE_DURATION } from "../constants"; + +interface GroupHeaderProps { + label: string; + color: string; + enterFrame: number; + vmin: number; + w: number; + h: number; + theme: ThemeColors; +} + +export const GroupHeader: React.FC = ({ + label, + color, + enterFrame, + vmin, + w, + h, + theme, +}) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + const localFrame = frame - enterFrame; + + const opacity = interpolate( + localFrame, + [0, ITEM_FADE_DURATION], + [0, 1], + { extrapolateLeft: "clamp", extrapolateRight: "clamp" }, + ); + + const scale = spring({ + frame: Math.max(0, localFrame), + fps, + config: { damping: 12, stiffness: 100, mass: 0.6 }, + }); + + return ( +
+
+ {label} +
+
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/comparison/components/itemSize.ts b/surfsense_video/src/remotion/scenes/comparison/components/itemSize.ts new file mode 100644 index 000000000..b037e05bf --- /dev/null +++ b/surfsense_video/src/remotion/scenes/comparison/components/itemSize.ts @@ -0,0 +1,99 @@ +/** + * Comparison item dimensions — width/height measured via canvas measureText + * so layout can compute positions before React rendering. + */ +import type { CompareItem } from "../types"; + +export interface ItemDimensions { + width: number; + height: number; + paddingX: number; + paddingY: number; + fontSize: number; + descFontSize: number; +} + +const LINE_HEIGHT = 1.3; +const GAP_FACTOR = 0.35; +const FONT_FAMILY = "Inter, system-ui, sans-serif"; + +let _ctx: CanvasRenderingContext2D | null = null; +function ctx(): CanvasRenderingContext2D { + if (!_ctx) _ctx = document.createElement("canvas").getContext("2d")!; + return _ctx; +} + +function measureLines( + text: string, + fontSize: number, + fontWeight: number | string, + availableWidth: number, +): number { + const c = ctx(); + c.font = `${fontWeight} ${fontSize}px ${FONT_FAMILY}`; + + const words = text.split(/\s+/).filter(Boolean); + if (words.length === 0) return 1; + + let lines = 1; + let lineWidth = 0; + const spaceW = c.measureText(" ").width; + + for (const word of words) { + const wordW = c.measureText(word).width; + if (lineWidth === 0) { + lineWidth = wordW; + } else if (lineWidth + spaceW + wordW <= availableWidth) { + lineWidth += spaceW + wordW; + } else { + lines++; + lineWidth = wordW; + } + } + + return lines; +} + +export function getItemDimensions( + item: CompareItem, + vmin: number, + fixedWidth?: number, +): ItemDimensions { + const paddingX = vmin * 2.5; + const paddingY = vmin * 1.5; + const fontSize = vmin * 2; + const descFontSize = vmin * 1.5; + const borderW = vmin * 0.14; + + const width = fixedWidth ?? vmin * 32; + const minHeight = vmin * 7; + + const innerW = width - 2 * paddingX - 2 * borderW; + const labelLines = measureLines(item.label, fontSize, 600, innerW); + const labelH = labelLines * fontSize * LINE_HEIGHT; + + let contentH = labelH; + if (item.desc) { + const descLines = measureLines(item.desc, descFontSize, 400, innerW); + const descH = descLines * descFontSize * LINE_HEIGHT; + contentH += vmin * GAP_FACTOR + descH; + } + + const height = Math.max(minHeight, contentH + 2 * paddingY + 2 * borderW); + + return { width, height, paddingX, paddingY, fontSize, descFontSize }; +} + +/** Returns the tallest item height across all items. */ +export function getMaxItemHeight( + items: CompareItem[], + vmin: number, + fixedWidth?: number, +): number { + let maxH = 0; + for (const item of items) { + const dims = getItemDimensions(item, vmin, fixedWidth); + maxH = Math.max(maxH, dims.height); + } + return maxH; +} diff --git a/surfsense_video/src/remotion/scenes/comparison/constants.ts b/surfsense_video/src/remotion/scenes/comparison/constants.ts new file mode 100644 index 000000000..efe34bc84 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/comparison/constants.ts @@ -0,0 +1,19 @@ +/** Timing constants for comparison scene animations. */ + +/** Frames each item staggers its entrance. */ +export const ITEM_STAGGER = 6; + +/** Item fade-in duration (frames). */ +export const ITEM_FADE_DURATION = 18; + +/** Group header stagger (frames). */ +export const GROUP_STAGGER = 10; + +/** Divider reveal delay (frames). */ +export const DIVIDER_DELAY = 15; + +/** Camera hold per stop (frames). */ +export const STOP_HOLD = 110; + +/** Camera transition between stops (frames). */ +export const STOP_TRANSITION = 18; diff --git a/surfsense_video/src/remotion/scenes/comparison/demo.ts b/surfsense_video/src/remotion/scenes/comparison/demo.ts new file mode 100644 index 000000000..b7005c862 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/comparison/demo.ts @@ -0,0 +1,124 @@ +/** Sample comparison data for Remotion Studio preview. */ +import type { ComparisonSceneInput } from "./types"; + +export const DEMO_COMPARE_BINARY: ComparisonSceneInput = { + type: "comparison", + title: "Traditional vs AI-Powered", + subtitle: "Software development approaches", + groups: [ + { + label: "Traditional", + color: "#3b82f6", + items: [ + { label: "Manual code reviews", desc: "Time-consuming peer reviews" }, + { label: "Waterfall planning", desc: "Sequential development phases" }, + { label: "Manual testing", desc: "QA team runs test suites" }, + { label: "Static documentation", desc: "Wikis and confluence pages" }, + ], + }, + { + label: "AI-Powered", + color: "#10b981", + items: [ + { label: "Automated analysis", desc: "AI-driven code review" }, + { label: "Agile with AI insights", desc: "Predictive sprint planning" }, + { label: "AI test generation", desc: "Auto-generated test cases" }, + { label: "Living documentation", desc: "AI-maintained docs" }, + ], + }, + ], +}; + +export const DEMO_COMPARE_TABLE: ComparisonSceneInput = { + type: "comparison", + title: "Cloud Provider Comparison", + subtitle: "Feature matrix across major platforms", + groups: [ + { + label: "AWS", + color: "#FF9900", + items: [ + { label: "Compute", desc: "EC2, Lambda, ECS" }, + { label: "Storage", desc: "S3, EBS, Glacier" }, + { label: "Database", desc: "RDS, DynamoDB, Aurora" }, + { label: "AI/ML", desc: "SageMaker, Bedrock" }, + ], + }, + { + label: "Azure", + color: "#0078D4", + items: [ + { label: "Compute", desc: "VMs, Functions, AKS" }, + { label: "Storage", desc: "Blob, Disk, Archive" }, + { label: "Database", desc: "SQL DB, Cosmos DB" }, + { label: "AI/ML", desc: "Azure AI, OpenAI" }, + ], + }, + { + label: "GCP", + color: "#4285F4", + items: [ + { label: "Compute", desc: "GCE, Cloud Run, GKE" }, + { label: "Storage", desc: "Cloud Storage, PD" }, + { label: "Database", desc: "Cloud SQL, Spanner" }, + { label: "AI/ML", desc: "Vertex AI, Gemini" }, + ], + }, + ], +}; + +export const DEMO_COMPARE_LARGE: ComparisonSceneInput = { + type: "comparison", + title: "Framework Comparison", + subtitle: "React vs Vue vs Svelte vs Angular", + groups: [ + { + label: "React", + color: "#61DAFB", + items: [ + { label: "Virtual DOM Diffing", desc: "Uses a lightweight in-memory representation of the UI to compute the minimal set of DOM mutations needed on each render cycle" }, + { label: "JSX Syntax", desc: "Combines JavaScript logic with HTML-like markup in a single file, enabling expressive component authoring with full language support" }, + { label: "Hooks API", desc: "Provides primitives like useState and useEffect for managing state and side effects in function components without classes" }, + { label: "Massive Ecosystem", desc: "Thousands of community-maintained libraries covering routing, state management, animation, forms, and more" }, + { label: "React Native", desc: "Enables building native iOS and Android applications using the same React component model and JavaScript codebase" }, + { label: "Server Components", desc: "Allows components to render on the server and stream HTML to the client, reducing bundle size and improving time-to-interactive" }, + ], + }, + { + label: "Vue", + color: "#4FC08D", + items: [ + { label: "Proxy-Based Reactivity", desc: "Leverages ES6 Proxy objects to track dependencies at a granular level, automatically re-rendering only the affected parts of the UI" }, + { label: "Single-File Components", desc: "Encapsulates template, script, and styles in a single .vue file with scoped CSS support for clean component boundaries" }, + { label: "Composition API", desc: "Organizes component logic into composable functions that can be shared and reused across components without mixins" }, + { label: "Official Ecosystem", desc: "Provides first-party solutions for routing with Vue Router and state management with Pinia, ensuring tight integration" }, + { label: "Nuxt.js Framework", desc: "A full-stack meta-framework offering SSR, static generation, API routes, and file-based routing out of the box" }, + { label: "Vapor Mode", desc: "An experimental compilation strategy that bypasses the virtual DOM entirely, generating direct DOM operations for maximum performance" }, + ], + }, + { + label: "Svelte", + color: "#FF3E00", + items: [ + { label: "Compile-Time Approach", desc: "Shifts reactivity and component logic to the build step, producing highly optimized vanilla JavaScript with zero framework runtime overhead" }, + { label: "Runes Reactivity", desc: "Introduces fine-grained reactive primitives that are compiled away, giving developers explicit control over state tracking and updates" }, + { label: "Minimal Boilerplate", desc: "Requires significantly less code than other frameworks for the same functionality, reducing cognitive load and maintenance burden" }, + { label: "Growing Community", desc: "A rapidly expanding ecosystem of libraries, tools, and learning resources backed by an enthusiastic open-source community" }, + { label: "SvelteKit Framework", desc: "Provides server-side rendering, API endpoints, file-based routing, and adapter-based deployment targets in a unified full-stack framework" }, + { label: "Tiny Bundle Sizes", desc: "Compiles components to surgically precise DOM updates, resulting in production bundles that are a fraction of the size of competing frameworks" }, + ], + }, + { + label: "Angular", + color: "#DD0031", + items: [ + { label: "Batteries-Included Framework", desc: "Ships with built-in solutions for routing, forms, HTTP, animations, and testing, so teams can start building without choosing third-party libraries" }, + { label: "TypeScript Native", desc: "Built from the ground up with TypeScript, offering first-class type safety, decorators, and IDE support throughout the entire framework" }, + { label: "Signals Reactivity", desc: "A modern fine-grained reactivity model that enables efficient change detection without Zone.js, improving performance and debuggability" }, + { label: "Enterprise Tooling", desc: "Mature CLI, schematics, and workspace management tools designed for large-scale teams and monorepo architectures" }, + { label: "Analog.js Meta-Framework", desc: "A community-driven meta-framework bringing file-based routing, API routes, and Vite-powered builds to the Angular ecosystem" }, + { label: "Dependency Injection", desc: "A hierarchical DI system that enables modular, testable, and scalable application architectures with provider scoping and lazy loading" }, + ], + }, + ], +}; diff --git a/surfsense_video/src/remotion/scenes/comparison/layout.ts b/surfsense_video/src/remotion/scenes/comparison/layout.ts new file mode 100644 index 000000000..64d8e8da0 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/comparison/layout.ts @@ -0,0 +1,238 @@ +/** + * Comparison scene layout — binary, table. + * Computes item positions, group headers, divider, camera waypoints, duration. + */ +import type { ComparisonSceneInput, Waypoint } from "./types"; +import type { ComparisonLayout } from "./variant"; +import { getMaxItemHeight } from "./components/itemSize"; +import { + ITEM_STAGGER, + ITEM_FADE_DURATION, + GROUP_STAGGER, + STOP_HOLD, + STOP_TRANSITION, +} from "./constants"; + +export interface LayoutItem { + groupIdx: number; + itemIdx: number; + x: number; + y: number; + w: number; + h: number; +} + +export interface LayoutHeader { + groupIdx: number; + x: number; + y: number; + w: number; + h: number; +} + +export interface DividerInfo { + x: number; + y: number; + w: number; + h: number; +} + +export interface ComparisonLayoutResult { + items: LayoutItem[]; + headers: LayoutHeader[]; + dividers: DividerInfo[]; + contentW: number; + contentH: number; +} + +/* ─── Binary layout (2 groups, side-by-side with VS divider) ─── */ + +function layoutBinary( + input: ComparisonSceneInput, + vmin: number, +): ComparisonLayoutResult { + const left = input.groups[0]; + const right = input.groups[1]; + const leftItems = left.items; + const rightItems = right.items; + + const colW = vmin * 32; + const rowGap = vmin * 3; + const headerH = vmin * 7; + const dividerW = vmin * 10; + const gap = vmin * 3; + + const allItems = [...leftItems, ...rightItems]; + const rowH = getMaxItemHeight(allItems, vmin, colW); + + const maxRows = Math.max(leftItems.length, rightItems.length); + const contentH = headerH + maxRows * (rowH + rowGap) - rowGap; + const contentW = colW + gap + dividerW + gap + colW; + + const leftX = 0; + const rightX = colW + gap + dividerW + gap; + + const items: LayoutItem[] = []; + const headers: LayoutHeader[] = [ + { groupIdx: 0, x: leftX, y: 0, w: colW, h: headerH }, + { groupIdx: 1, x: rightX, y: 0, w: colW, h: headerH }, + ]; + + leftItems.forEach((_, i) => { + items.push({ + groupIdx: 0, + itemIdx: i, + x: leftX, + y: headerH + i * (rowH + rowGap), + w: colW, + h: rowH, + }); + }); + + rightItems.forEach((_, i) => { + items.push({ + groupIdx: 1, + itemIdx: i, + x: rightX, + y: headerH + i * (rowH + rowGap), + w: colW, + h: rowH, + }); + }); + + const dividers: DividerInfo[] = [ + { + x: colW + gap, + y: 0, + w: dividerW, + h: contentH, + }, + ]; + + return { items, headers, dividers, contentW, contentH }; +} + +/* ─── Table layout (N groups as columns, items as rows) ─── */ + +function layoutTable( + input: ComparisonSceneInput, + vmin: number, +): ComparisonLayoutResult { + const groups = input.groups; + const colW = vmin * 28; + const colGap = vmin * 3; + const rowGap = vmin * 3; + const headerH = vmin * 7; + + const allItems = groups.flatMap((g) => g.items); + const rowH = getMaxItemHeight(allItems, vmin, colW); + + const maxRows = Math.max(...groups.map((g) => g.items.length)); + const contentW = groups.length * colW + (groups.length - 1) * colGap; + const contentH = headerH + maxRows * (rowH + rowGap) - rowGap; + + const items: LayoutItem[] = []; + const headers: LayoutHeader[] = []; + + groups.forEach((group, gi) => { + const colX = gi * (colW + colGap); + headers.push({ groupIdx: gi, x: colX, y: 0, w: colW, h: headerH }); + + group.items.forEach((_, ii) => { + items.push({ + groupIdx: gi, + itemIdx: ii, + x: colX, + y: headerH + ii * (rowH + rowGap), + w: colW, + h: rowH, + }); + }); + }); + + return { items, headers, dividers: [], contentW, contentH }; +} + +/* ─── Camera waypoints ─── */ + +export function buildWaypoints( + contentW: number, + contentH: number, + viewW: number, + viewH: number, + titleOffset: number = 0, +): Waypoint[] { + const effectiveH = viewH - titleOffset; + const cx = contentW / 2; + const cy = contentH / 2 - titleOffset / 2; + + if (contentW <= viewW * 0.85 && contentH <= effectiveH * 0.85) { + return [{ cx, cy, holdFrames: STOP_HOLD, transitionAfter: 0 }]; + } + + const colsNeeded = contentW > viewW ? Math.ceil(contentW / (viewW * 0.75)) : 1; + const rowsNeeded = contentH > effectiveH ? Math.ceil(contentH / (effectiveH * 0.75)) : 1; + + const stops: Waypoint[] = []; + + for (let r = 0; r < rowsNeeded; r++) { + for (let c = 0; c < colsNeeded; c++) { + const tx = colsNeeded === 1 + ? cx + : viewW / 2 + (c / (colsNeeded - 1)) * Math.max(0, contentW - viewW); + + let ty: number; + if (rowsNeeded === 1) { + ty = cy; + } else { + const firstCy = viewH / 2 - titleOffset; + const lastCy = contentH - viewH / 2; + ty = firstCy + (r / (rowsNeeded - 1)) * Math.max(0, lastCy - firstCy); + } + + stops.push({ cx: tx, cy: ty, holdFrames: STOP_HOLD, transitionAfter: STOP_TRANSITION }); + } + } + + if (stops.length > 0) stops[stops.length - 1].transitionAfter = 0; + return stops; +} + +/* ─── Public API ─── */ + +export function computeComparisonLayout( + input: ComparisonSceneInput, + layout: ComparisonLayout, + vmin: number, +): ComparisonLayoutResult { + switch (layout) { + case "binary": + return layoutBinary(input, vmin); + case "table": + return layoutTable(input, vmin); + } +} + +export function waypointsDuration(waypoints: Waypoint[]): number { + let total = 0; + for (const wp of waypoints) total += wp.holdFrames + wp.transitionAfter; + return Math.max(1, total); +} + +export function comparisonSceneDuration( + input: ComparisonSceneInput, + layout: ComparisonLayout, + viewW: number, + viewH: number, +): number { + const vmin = Math.min(viewW, viewH) / 100; + const titleOffset = input.title ? vmin * 12 : 0; + const result = computeComparisonLayout(input, layout, vmin); + const wps = buildWaypoints(result.contentW, result.contentH, viewW, viewH, titleOffset); + const totalItems = input.groups.reduce((s, g) => s + g.items.length, 0); + const animPhase = + (input.groups.length - 1) * GROUP_STAGGER + + (totalItems - 1) * ITEM_STAGGER + + ITEM_FADE_DURATION; + return animPhase + waypointsDuration(wps); +} diff --git a/surfsense_video/src/remotion/scenes/comparison/preview.tsx b/surfsense_video/src/remotion/scenes/comparison/preview.tsx new file mode 100644 index 000000000..c6286a10b --- /dev/null +++ b/surfsense_video/src/remotion/scenes/comparison/preview.tsx @@ -0,0 +1,160 @@ +/** Remotion Studio compositions for comparison scene. */ +import React from "react"; +import { Composition } from "remotion"; +import { ComparisonScene } from "./ComparisonScene"; +import { THEMES } from "../../theme"; +import type { ComparisonVariant } from "./variant"; +import type { ComparisonSceneInput } from "./types"; +import { + DEMO_COMPARE_BINARY, + DEMO_COMPARE_TABLE, + DEMO_COMPARE_LARGE, +} from "./demo"; +import { comparisonSceneDuration } from "./layout"; + +const THEME = "dark" as const; +const FPS = 30; +const WIDTH = 1920; +const HEIGHT = 1080; + +const Preview: React.FC<{ + variant: ComparisonVariant; + data: ComparisonSceneInput; +}> = ({ variant, data }) => { + const theme = THEMES[THEME]; + return ; +}; + +function dur(data: ComparisonSceneInput, layout: ComparisonVariant["layout"]) { + return comparisonSceneDuration(data, layout, WIDTH, HEIGHT); +} + +const base: ComparisonVariant = { + layout: "binary", + cardStyle: "gradient", + divider: "vs", +}; + +export const comparisonPreviews = ( + <> + {/* Binary — gradient + VS */} + ( + + )} + durationInFrames={dur(DEMO_COMPARE_BINARY, "binary")} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + {/* Binary — glass + line */} + ( + + )} + durationInFrames={dur(DEMO_COMPARE_BINARY, "binary")} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + {/* Binary — outline + none */} + ( + + )} + durationInFrames={dur(DEMO_COMPARE_BINARY, "binary")} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + {/* Binary — solid + VS */} + ( + + )} + durationInFrames={dur(DEMO_COMPARE_BINARY, "binary")} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + + {/* Table — gradient */} + ( + + )} + durationInFrames={dur(DEMO_COMPARE_TABLE, "table")} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + {/* Table — glass */} + ( + + )} + durationInFrames={dur(DEMO_COMPARE_TABLE, "table")} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + {/* Table — outline */} + ( + + )} + durationInFrames={dur(DEMO_COMPARE_TABLE, "table")} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + {/* Table — solid */} + ( + + )} + durationInFrames={dur(DEMO_COMPARE_TABLE, "table")} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + + {/* Large table — long text, camera paging */} + ( + + )} + durationInFrames={dur(DEMO_COMPARE_LARGE, "table")} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + +); diff --git a/surfsense_video/src/remotion/scenes/comparison/types.ts b/surfsense_video/src/remotion/scenes/comparison/types.ts new file mode 100644 index 000000000..7548aa3f9 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/comparison/types.ts @@ -0,0 +1,34 @@ +/** Zod schemas and inferred types for comparison scene data. */ +import { z } from "zod"; + +export const CompareItemSchema = z.object({ + label: z.string(), + desc: z.string().optional(), +}); + +export type CompareItem = z.infer; + +export const CompareGroupSchema = z.object({ + label: z.string(), + color: z.string().optional(), + items: z.array(CompareItemSchema).min(1), +}); + +export type CompareGroup = z.infer; + +export const ComparisonSceneInput = z.object({ + type: z.literal("comparison"), + title: z.string().optional(), + subtitle: z.string().optional(), + groups: z.array(CompareGroupSchema).min(2), +}); + +export type ComparisonSceneInput = z.infer; + +/** Camera stop. */ +export interface Waypoint { + cx: number; + cy: number; + holdFrames: number; + transitionAfter: number; +} diff --git a/surfsense_video/src/remotion/scenes/comparison/variant.ts b/surfsense_video/src/remotion/scenes/comparison/variant.ts new file mode 100644 index 000000000..3122a132b --- /dev/null +++ b/surfsense_video/src/remotion/scenes/comparison/variant.ts @@ -0,0 +1,28 @@ +/** Comparison scene variant — seed-deterministic visual style. */ +import { random } from "remotion"; + +export type ComparisonLayout = "binary" | "table"; +export type ComparisonCardStyle = "gradient" | "glass" | "outline" | "solid"; +export type ComparisonDivider = "vs" | "line" | "none"; + +export interface ComparisonVariant { + layout: ComparisonLayout; + cardStyle: ComparisonCardStyle; + divider: ComparisonDivider; +} + +export function deriveComparisonVariant(seed: number): ComparisonVariant { + const s = (key: string) => random(`${seed}-${key}`); + const pick = (key: string, arr: T[]) => + arr[Math.floor(s(key) * arr.length)]; + + return { + layout: pick("layout", [ + "binary", "table", + ] as ComparisonLayout[]), + cardStyle: pick("cardStyle", [ + "gradient", "glass", "outline", "solid", + ] as ComparisonCardStyle[]), + divider: pick("divider", ["vs", "line", "none"] as ComparisonDivider[]), + }; +} diff --git a/surfsense_video/src/remotion/scenes/hierarchy/HierarchyScene.tsx b/surfsense_video/src/remotion/scenes/hierarchy/HierarchyScene.tsx new file mode 100644 index 000000000..a5e9f6433 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/hierarchy/HierarchyScene.tsx @@ -0,0 +1,189 @@ +/** + * HierarchyScene — tree layout powered by d3-hierarchy. + * + * Nodes are placed via d3.tree(), then rendered with staggered + * per-level reveal animations and animated edge drawing. + */ +import React, { useMemo } from "react"; +import { AbsoluteFill, useVideoConfig, useCurrentFrame, Easing } from "remotion"; +import type { ThemeColors } from "../../theme"; +import type { HierarchySceneInput, LayoutNode, Waypoint } from "./types"; +import type { HierarchyVariant } from "./variant"; +import { NODE_STAGGER, EDGE_DRAW_DURATION } from "./constants"; +import { TreeNode } from "./components/TreeNode"; +import { TreeEdge } from "./components/TreeEdge"; +import { getNodeDimensions } from "./components/nodeSize"; +import { computeTreeLayout, buildWaypoints } from "./layout"; + +interface HierarchySceneProps { + input: HierarchySceneInput; + theme: ThemeColors; + variant: HierarchyVariant; +} + +/** Resolve camera position from waypoints at a given frame. */ +function resolveCamera(waypoints: Waypoint[], frame: number): { cx: number; cy: number } { + let cam = { cx: waypoints[0].cx, cy: waypoints[0].cy }; + let cursor = 0; + + for (let w = 0; w < waypoints.length; w++) { + const wp = waypoints[w]; + if (frame < cursor + wp.holdFrames) { + cam = { cx: wp.cx, cy: wp.cy }; + break; + } + cursor += wp.holdFrames; + + if (wp.transitionAfter > 0 && w + 1 < waypoints.length) { + if (frame < cursor + wp.transitionAfter) { + const t = Easing.inOut(Easing.ease)( + (frame - cursor) / wp.transitionAfter, + ); + const next = waypoints[w + 1]; + cam = { + cx: wp.cx + (next.cx - wp.cx) * t, + cy: wp.cy + (next.cy - wp.cy) * t, + }; + break; + } + cursor += wp.transitionAfter; + } + + if (w === waypoints.length - 1) { + cam = { cx: wp.cx, cy: wp.cy }; + } + } + + return cam; +} + +/** Build a map from depth → frame at which that depth's first waypoint starts. */ +function buildDepthEnterFrames(waypoints: Waypoint[]): Map { + const map = new Map(); + let f = 0; + for (const wp of waypoints) { + if (!map.has(wp.depth)) map.set(wp.depth, f); + f += wp.holdFrames + wp.transitionAfter; + } + return map; +} + +export const HierarchyScene: React.FC = ({ + input, + theme, + variant, +}) => { + const { width, height } = useVideoConfig(); + const isHoriz = variant.orientation === "left-right"; + const vmin = Math.min(width, height) / 100; + const frame = useCurrentFrame(); + + const { nodes, waypoints, depthEnterFrame } = useMemo(() => { + const { nodes: flat, crossSize, maxDepth } = computeTreeLayout(input, isHoriz, vmin); + const wps = buildWaypoints(flat, isHoriz, crossSize, width, height, maxDepth); + const def = buildDepthEnterFrames(wps); + return { nodes: flat, waypoints: wps, depthEnterFrame: def }; + }, [input, isHoriz, vmin, width, height]); + + const cam = resolveCamera(waypoints, frame); + const panX = -cam.cx; + const panY = -cam.cy; + + const toLocal = (n: LayoutNode) => { + const px = isHoriz ? n.y : n.x; + const py = isHoriz ? n.x : n.y; + return { lx: px, ly: py }; + }; + + const edges: { + from: { x: number; y: number }; + to: { x: number; y: number }; + parentColor: string; + childColor: string; + enterFrame: number; + id: string; + }[] = []; + + let edgeIdx = 0; + for (const n of nodes) { + if (!n.parent) continue; + const pLocal = toLocal(n.parent); + const cLocal = toLocal(n); + const pDims = getNodeDimensions(n.parent.data, vmin, n.parent.depth === 0); + const cDims = getNodeDimensions(n.data, vmin, n.depth === 0); + + const from = { x: pLocal.lx, y: pLocal.ly }; + const to = { x: cLocal.lx, y: cLocal.ly }; + + if (isHoriz) { + from.x += pDims.halfW; + to.x -= cDims.halfW; + } else { + from.y += pDims.halfH; + to.y -= cDims.halfH; + } + + const levelStart = depthEnterFrame.get(n.depth) ?? 0; + const enterF = levelStart - EDGE_DRAW_DURATION + n.siblingIndex * 4; + edges.push({ + from, + to, + parentColor: n.parent.data.color ?? "#6c7dff", + childColor: n.data.color ?? "#6c7dff", + enterFrame: Math.max(0, enterF), + id: `e${edgeIdx++}`, + }); + } + + return ( + +
+ {edges.map((e) => ( + + ))} + + {nodes.map((n, i) => { + const { lx, ly } = toLocal(n); + const levelStart = depthEnterFrame.get(n.depth) ?? 0; + const enterF = levelStart + n.siblingIndex * NODE_STAGGER; + return ( +
+ +
+ ); + })} +
+
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/hierarchy/components/TreeEdge.tsx b/surfsense_video/src/remotion/scenes/hierarchy/components/TreeEdge.tsx new file mode 100644 index 000000000..d820af6b2 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/hierarchy/components/TreeEdge.tsx @@ -0,0 +1,169 @@ +/** Animated edge between parent and child node in the tree. */ +import React from "react"; +import { useCurrentFrame, useVideoConfig, interpolate } from "remotion"; +import type { HierarchyVariant } from "../variant"; +import { EDGE_DRAW_DURATION } from "../constants"; + +interface Point { + x: number; + y: number; +} + +interface TreeEdgeProps { + from: Point; + to: Point; + parentColor: string; + childColor: string; + enterFrame: number; + variant: HierarchyVariant; + edgeId: string; +} + +function buildCurvedPath(from: Point, to: Point, isHorizontal: boolean): string { + if (isHorizontal) { + const mx = from.x + (to.x - from.x) / 2; + return `M ${from.x} ${from.y} C ${mx} ${from.y}, ${mx} ${to.y}, ${to.x} ${to.y}`; + } + const my = from.y + (to.y - from.y) / 2; + return `M ${from.x} ${from.y} C ${from.x} ${my}, ${to.x} ${my}, ${to.x} ${to.y}`; +} + +function buildStraightPath( + from: Point, + to: Point, + isHorizontal: boolean, + radius: number, +): string { + if (radius <= 0) { + if (isHorizontal) { + const mx = from.x + (to.x - from.x) / 2; + return `M ${from.x} ${from.y} L ${mx} ${from.y} L ${mx} ${to.y} L ${to.x} ${to.y}`; + } + const my = from.y + (to.y - from.y) / 2; + return `M ${from.x} ${from.y} L ${from.x} ${my} L ${to.x} ${my} L ${to.x} ${to.y}`; + } + + const isVert = !isHorizontal; + const deltaMain = isVert ? to.y - from.y : to.x - from.x; + const deltaCross = isVert ? to.x - from.x : to.y - from.y; + const signM = Math.sign(deltaMain) || 1; + const signC = Math.sign(deltaCross) || 1; + const mid = isVert + ? from.y + deltaMain / 2 + : from.x + deltaMain / 2; + const r = Math.min(radius, Math.abs(deltaMain) / 2, Math.abs(deltaCross) / 2); + + if (r === 0) { + return buildStraightPath(from, to, isHorizontal, 0); + } + + if (isVert) { + return [ + `M ${from.x} ${from.y}`, + `L ${from.x} ${mid - signM * r}`, + `Q ${from.x} ${mid} ${from.x + signC * r} ${mid}`, + `L ${to.x - signC * r} ${mid}`, + `Q ${to.x} ${mid} ${to.x} ${mid + signM * r}`, + `L ${to.x} ${to.y}`, + ].join(" "); + } + return [ + `M ${from.x} ${from.y}`, + `L ${mid - signM * r} ${from.y}`, + `Q ${mid} ${from.y} ${mid} ${from.y + signC * r}`, + `L ${mid} ${to.y - signC * r}`, + `Q ${mid} ${to.y} ${mid + signM * r} ${to.y}`, + `L ${to.x} ${to.y}`, + ].join(" "); +} + +export const TreeEdge: React.FC = ({ + from, + to, + parentColor, + childColor, + enterFrame, + variant, + edgeId, +}) => { + const frame = useCurrentFrame(); + const { width, height, fps } = useVideoConfig(); + const vmin = Math.min(width, height) / 100; + const isHoriz = variant.orientation === "left-right"; + + const dx = to.x - from.x; + const dy = to.y - from.y; + const pathLen = Math.sqrt(dx * dx + dy * dy) * 1.5; + + const d = + variant.edgeType === "curved" + ? buildCurvedPath(from, to, isHoriz) + : buildStraightPath(from, to, isHoriz, variant.edgeCornerRadius * vmin); + + const localFrame = frame - enterFrame; + + const progress = interpolate( + localFrame, + [0, EDGE_DRAW_DURATION], + [0, 1], + { extrapolateLeft: "clamp", extrapolateRight: "clamp" }, + ); + + const fadeIn = interpolate(localFrame, [0, 5], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + + const isDrawn = localFrame > EDGE_DRAW_DURATION; + const breathCycle = isDrawn + ? Math.sin(((localFrame - EDGE_DRAW_DURATION) / fps) * 1.8) * 0.15 + : 0; + const opacity = fadeIn * (1 + breathCycle); + const strokeW = vmin * 0.25 * (1 + breathCycle * 0.5); + + const useGradient = variant.edgeColorMode === "gradient"; + const gradientId = `edge-grad-${edgeId}`; + + const minX = Math.min(from.x, to.x) - 10; + const minY = Math.min(from.y, to.y) - 10; + const svgW = Math.abs(to.x - from.x) + 20; + const svgH = Math.abs(to.y - from.y) + 20; + + return ( + + {useGradient && ( + + + + + + + )} + + + ); +}; diff --git a/surfsense_video/src/remotion/scenes/hierarchy/components/TreeNode.tsx b/surfsense_video/src/remotion/scenes/hierarchy/components/TreeNode.tsx new file mode 100644 index 000000000..fc4acbb88 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/hierarchy/components/TreeNode.tsx @@ -0,0 +1,133 @@ +/** Single tree node — card with style determined by variant.cardStyle. */ +import React from "react"; +import { useCurrentFrame, interpolate, spring, useVideoConfig } from "remotion"; +import type { ThemeColors } from "../../../theme"; +import type { HierarchyNode } from "../types"; +import type { HierarchyVariant, HierarchyCardStyle } from "../variant"; +import { NODE_FADE_DURATION } from "../constants"; +import { getNodeDimensions } from "./nodeSize"; + +interface TreeNodeProps { + node: HierarchyNode; + enterFrame: number; + depth: number; + vmin: number; + variant: HierarchyVariant; + theme: ThemeColors; + isRoot: boolean; +} + +function cardCSS( + style: HierarchyCardStyle, + color: string, + vmin: number, + borderRadius: number, + isRoot: boolean, +): React.CSSProperties { + const bw = vmin * 0.14; + + switch (style) { + case "gradient": + return { + background: `linear-gradient(145deg, ${color}14, ${color}06)`, + border: `${bw}px solid ${color}35`, + borderBottom: isRoot ? `${vmin * 0.35}px solid ${color}` : `${bw}px solid ${color}35`, + boxShadow: `0 ${vmin * 0.5}px ${vmin * 2.5}px ${color}12`, + }; + case "glass": + return { + background: `${color}0a`, + backdropFilter: "blur(12px)", + WebkitBackdropFilter: "blur(12px)", + border: `${bw}px solid ${color}25`, + boxShadow: `inset 0 0 ${vmin * 2}px ${color}08, 0 ${vmin * 0.5}px ${vmin * 2}px rgba(0,0,0,0.15)`, + }; + case "outline": + return { + background: "transparent", + border: `${vmin * 0.2}px solid ${color}${isRoot ? "bb" : "66"}`, + boxShadow: `0 0 ${vmin * 1.5}px ${color}15`, + }; + case "solid": + return { + background: `${color}22`, + border: `${bw}px solid ${color}44`, + boxShadow: `0 ${vmin * 0.3}px ${vmin * 1.5}px rgba(0,0,0,0.2)`, + }; + } +} + +export const TreeNode: React.FC = ({ + node, + enterFrame, + vmin, + variant, + theme, + isRoot, +}) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + const color = node.color ?? "#6c7dff"; + const localFrame = frame - enterFrame; + const dims = getNodeDimensions(node, vmin, isRoot); + + const opacity = interpolate(localFrame, [0, NODE_FADE_DURATION], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + + const scale = spring({ + frame: Math.max(0, localFrame), + fps, + config: { damping: 14, stiffness: 120, mass: 0.8 }, + }); + + const borderRadius = variant.nodeShape === "pill" ? 999 : vmin * 1; + const styleCSS = cardCSS(variant.cardStyle, color, vmin, borderRadius, isRoot); + + return ( +
+ + {node.label} + + {node.desc && ( + + {node.desc} + + )} +
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/hierarchy/components/nodeSize.ts b/surfsense_video/src/remotion/scenes/hierarchy/components/nodeSize.ts new file mode 100644 index 000000000..e1fcca921 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/hierarchy/components/nodeSize.ts @@ -0,0 +1,121 @@ +/** + * Shared node dimensions — width is fixed per depth level, + * height is measured from content via canvas measureText. + */ +import type { HierarchyNode } from "../types"; + +interface NodeDimensions { + width: number; + height: number; + halfW: number; + halfH: number; + paddingX: number; + paddingY: number; + fontSize: number; + descFontSize: number; +} + +const LINE_HEIGHT = 1.3; +const GAP_FACTOR = 0.4; +const FONT_FAMILY = "Inter, system-ui, sans-serif"; +const BORDER_FACTOR = 0.14; + +let _ctx: CanvasRenderingContext2D | null = null; +function ctx(): CanvasRenderingContext2D { + if (!_ctx) { + _ctx = document.createElement("canvas").getContext("2d")!; + } + return _ctx; +} + +function measureLines( + text: string, + fontSize: number, + fontWeight: number | string, + availableWidth: number, +): number { + const c = ctx(); + c.font = `${fontWeight} ${fontSize}px ${FONT_FAMILY}`; + + const words = text.split(/\s+/).filter(Boolean); + if (words.length === 0) return 1; + + let lines = 1; + let lineWidth = 0; + const spaceW = c.measureText(" ").width; + + for (const word of words) { + const wordW = c.measureText(word).width; + if (lineWidth === 0) { + lineWidth = wordW; + } else if (lineWidth + spaceW + wordW <= availableWidth) { + lineWidth += spaceW + wordW; + } else { + lines++; + lineWidth = wordW; + } + } + + return lines; +} + +export function getNodeDimensions( + node: HierarchyNode, + vmin: number, + isRoot: boolean, +): NodeDimensions { + const paddingX = vmin * (isRoot ? 3 : 2); + const paddingY = vmin * (isRoot ? 1.5 : 1); + const fontSize = vmin * (isRoot ? 2.2 : 1.6); + const descFontSize = vmin * 1.2; + + const borderW = vmin * BORDER_FACTOR; + const width = vmin * (isRoot ? 22 : 18); + const minHeight = vmin * (isRoot ? 10 : 8); + + const innerW = width - 2 * paddingX - 2 * borderW; + const labelWeight = isRoot ? 700 : 500; + const labelLines = measureLines(node.label, fontSize, labelWeight, innerW); + const labelH = labelLines * fontSize * LINE_HEIGHT; + + let contentH = labelH; + if (node.desc) { + const descLines = measureLines(node.desc, descFontSize, 400, innerW); + const descH = descLines * descFontSize * LINE_HEIGHT; + contentH += vmin * GAP_FACTOR + descH; + } + + const height = Math.max(minHeight, contentH + 2 * paddingY + 2 * borderW); + + return { + width, + height, + halfW: width / 2, + halfH: height / 2, + paddingX, + paddingY, + fontSize, + descFontSize, + }; +} + +/** Returns the largest node box across all nodes in the tree. */ +export function getMaxNodeSize( + root: HierarchyNode, + vmin: number, +): { maxW: number; maxH: number } { + let maxW = 0; + let maxH = 0; + + function walk(node: HierarchyNode, isRoot: boolean) { + const dims = getNodeDimensions(node, vmin, isRoot); + maxW = Math.max(maxW, dims.width); + maxH = Math.max(maxH, dims.height); + for (const child of node.children ?? []) { + walk(child, false); + } + } + + walk(root, true); + return { maxW, maxH }; +} diff --git a/surfsense_video/src/remotion/scenes/hierarchy/constants.ts b/surfsense_video/src/remotion/scenes/hierarchy/constants.ts new file mode 100644 index 000000000..8c061a989 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/hierarchy/constants.ts @@ -0,0 +1,17 @@ +/** Hold duration on each camera stop (frames). */ +export const STOP_HOLD = 90; + +/** Fast snap transition between stops within a level (frames). */ +export const STOP_TRANSITION = 15; + +/** Transition between depth levels (frames). */ +export const LEVEL_TRANSITION = 40; + +/** Stagger delay per node within a level (frames). */ +export const NODE_STAGGER = 8; + +/** Edge draw animation duration (frames). */ +export const EDGE_DRAW_DURATION = 30; + +/** Node fade-in duration (frames). */ +export const NODE_FADE_DURATION = 20; diff --git a/surfsense_video/src/remotion/scenes/hierarchy/demo.ts b/surfsense_video/src/remotion/scenes/hierarchy/demo.ts new file mode 100644 index 000000000..cfce9353a --- /dev/null +++ b/surfsense_video/src/remotion/scenes/hierarchy/demo.ts @@ -0,0 +1,54 @@ +/** Sample hierarchy data for Remotion Studio preview. */ +import type { HierarchySceneInput } from "./types"; + +export const DEMO_HIERARCHY: HierarchySceneInput = { + type: "hierarchy", + title: "AI Architecture", + items: [ + { + label: "AI Platform", + color: "#6c7dff", + desc: "Enterprise-grade intelligent automation system", + children: [ + { + label: "Data Pipeline", + color: "#00c9a7", + desc: "ETL & streaming", + children: [ + { label: "Real-time Data Ingestion", color: "#00c9a7", desc: "High-throughput event streaming via Kafka and Kinesis" }, + { label: "Stream Processing Engine", color: "#00c9a7", desc: "Stateful transformations with Apache Flink" }, + { label: "Distributed Storage Layer", color: "#00c9a7", desc: "Petabyte-scale lakehouse on S3 and Delta Lake" }, + ], + }, + { + label: "ML Engine", + color: "#ff6b6b", + desc: "Model lifecycle", + children: [ + { label: "Training", color: "#ff6b6b", desc: "Distributed GPU clusters with auto-scaling" }, + { label: "Inference", color: "#ff6b6b", desc: "Sub-100ms latency model serving at scale" }, + ], + }, + { + label: "API Layer", + color: "#ffd93d", + desc: "Client interfaces", + children: [ + { label: "REST", color: "#ffd93d", desc: "CRUD endpoints" }, + { label: "GraphQL", color: "#ffd93d", desc: "Flexible queries" }, + { label: "WebSocket", color: "#ffd93d", desc: "Real-time events" }, + ], + }, + { + label: "Monitoring", + color: "#c084fc", + desc: "Observability", + children: [ + { label: "Metrics", color: "#c084fc", desc: "Prometheus, Grafana" }, + { label: "Alerts", color: "#c084fc", desc: "PagerDuty, Slack" }, + ], + }, + ], + }, + ], +}; diff --git a/surfsense_video/src/remotion/scenes/hierarchy/layout.ts b/surfsense_video/src/remotion/scenes/hierarchy/layout.ts new file mode 100644 index 000000000..fdcff9990 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/hierarchy/layout.ts @@ -0,0 +1,164 @@ +/** + * Tree layout utilities — d3 layout, node flattening, camera waypoints. + */ +import { hierarchy, tree } from "d3-hierarchy"; +import type { HierarchyNode, HierarchySceneInput, LayoutNode, Waypoint } from "./types"; +import { STOP_HOLD, STOP_TRANSITION, LEVEL_TRANSITION } from "./constants"; +import { getMaxNodeSize } from "./components/nodeSize"; + +/** Merge multiple root items into a single root. */ +export function normalizeToSingleRoot(items: HierarchyNode[]): HierarchyNode { + if (items.length === 1) return items[0]; + const [first, ...rest] = items; + return { + ...first, + children: [...(first.children ?? []), ...rest], + }; +} + +/** Flatten d3 hierarchy nodes and attach sibling index + parent refs. */ +export function flattenNodes( + root: ReturnType>, +): LayoutNode[] { + const result: LayoutNode[] = []; + root.each((d) => { + const siblings = d.parent?.children ?? [d]; + const sibIdx = siblings.indexOf(d); + result.push({ + data: d.data, + x: (d as any).x as number, + y: (d as any).y as number, + depth: d.depth, + parent: null, + siblingIndex: sibIdx, + }); + }); + root.each((d, i) => { + if (d.parent) { + const parentIdx = result.findIndex( + (n) => n.data === d.parent!.data && n.depth === d.parent!.depth, + ); + if (parentIdx >= 0) result[i].parent = result[parentIdx]; + } + }); + return result; +} + +/** + * Build camera waypoints for a given hierarchy layout. + * Each depth level gets one or more stops; stops within a level + * use STOP_TRANSITION, levels use LEVEL_TRANSITION. + */ +export function buildWaypoints( + nodes: LayoutNode[], + isHoriz: boolean, + crossSize: number, + viewW: number, + viewH: number, + maxDepth: number, +): Waypoint[] { + const viewCross = isHoriz ? viewH : viewW; + const cellHalf = crossSize / 2; + + const byDepth = new Map(); + for (const n of nodes) { + const px = isHoriz ? n.y : n.x; + const py = isHoriz ? n.x : n.y; + const mainPos = isHoriz ? px : py; + const crossPos = isHoriz ? py : px; + const entry = byDepth.get(n.depth) ?? { mainPos, crosses: [] }; + entry.crosses.push(crossPos); + byDepth.set(n.depth, entry); + } + + const waypoints: Waypoint[] = []; + + for (let d = 0; d <= maxDepth; d++) { + const entry = byDepth.get(d); + if (!entry) continue; + + const sorted = [...entry.crosses].sort((a, b) => a - b); + const crossMin = sorted[0] - cellHalf; + const crossMax = sorted[sorted.length - 1] + cellHalf; + const crossSpan = crossMax - crossMin; + + const stops: number[] = []; + if (crossSpan <= viewCross * 0.85) { + stops.push((crossMin + crossMax) / 2); + } else { + const margin = viewCross / 2; + const sweepFrom = crossMin + margin; + const sweepTo = crossMax - margin; + const chunkSize = viewCross * 0.75; + const numStops = Math.max(2, Math.ceil((sweepTo - sweepFrom) / chunkSize) + 1); + for (let s = 0; s < numStops; s++) { + const t = numStops === 1 ? 0.5 : s / (numStops - 1); + stops.push(sweepFrom + (sweepTo - sweepFrom) * t); + } + } + + for (let s = 0; s < stops.length; s++) { + const crossTarget = stops[s]; + const cx = isHoriz ? entry.mainPos : crossTarget; + const cy = isHoriz ? crossTarget : entry.mainPos; + const isLastStop = s === stops.length - 1; + const isLastLevel = d === maxDepth; + let transitionAfter: number; + if (isLastStop && isLastLevel) { + transitionAfter = 0; + } else if (isLastStop) { + transitionAfter = LEVEL_TRANSITION; + } else { + transitionAfter = STOP_TRANSITION; + } + waypoints.push({ cx, cy, holdFrames: STOP_HOLD, transitionAfter, depth: d }); + } + } + + return waypoints; +} + +/** Total duration from a waypoint list. */ +export function waypointsDuration(waypoints: Waypoint[]): number { + let total = 0; + for (const wp of waypoints) { + total += wp.holdFrames + wp.transitionAfter; + } + return Math.max(1, total); +} + +/** Run d3 tree layout and return flat nodes + layout params. */ +export function computeTreeLayout( + input: HierarchySceneInput, + isHoriz: boolean, + vmin: number, +) { + const rootData = normalizeToSingleRoot(input.items); + const root = hierarchy(rootData, (d) => d.children ?? undefined); + + const dims = getMaxNodeSize(rootData, vmin); + const siblingGap = vmin * 2; + const levelGap = vmin * 10; + const crossSize = (isHoriz ? dims.maxH : dims.maxW) + siblingGap; + const mainSize = (isHoriz ? dims.maxW : dims.maxH) + levelGap; + const treeLayout = tree() + .nodeSize([crossSize, mainSize]) + .separation(() => 1.2); + treeLayout(root); + + const nodes = flattenNodes(root); + return { nodes, crossSize, maxDepth: root.height }; +} + +/** Compute total scene duration for a hierarchy input. */ +export function hierarchySceneDuration( + input: HierarchySceneInput, + viewW: number, + viewH: number, + isHoriz: boolean, +): number { + const vmin = Math.min(viewW, viewH) / 100; + const { nodes, crossSize, maxDepth } = computeTreeLayout(input, isHoriz, vmin); + const wps = buildWaypoints(nodes, isHoriz, crossSize, viewW, viewH, maxDepth); + return waypointsDuration(wps); +} diff --git a/surfsense_video/src/remotion/scenes/hierarchy/preview.tsx b/surfsense_video/src/remotion/scenes/hierarchy/preview.tsx new file mode 100644 index 000000000..e8c3e6a6c --- /dev/null +++ b/surfsense_video/src/remotion/scenes/hierarchy/preview.tsx @@ -0,0 +1,56 @@ +/** Remotion Studio compositions for hierarchy scene. */ +import React from "react"; +import { Composition } from "remotion"; +import { HierarchyScene } from "./HierarchyScene"; +import { hierarchySceneDuration } from "./layout"; +import { THEMES } from "../../theme"; +import type { HierarchyVariant } from "./variant"; +import { DEMO_HIERARCHY } from "./demo"; + +const THEME = "dark" as const; +const FPS = 30; +const WIDTH = 1920; +const HEIGHT = 1080; + +const HierarchyPreview: React.FC<{ variant: HierarchyVariant }> = ({ variant }) => { + const theme = THEMES[THEME]; + return ; +}; + +const base: HierarchyVariant = { + orientation: "top-bottom", + edgeType: "curved", + edgeColorMode: "gradient", + nodeShape: "rounded", + cardStyle: "gradient", + edgeCornerRadius: 0.8, +}; + +const variants: { id: string; v: HierarchyVariant }[] = [ + { id: "tb-curved-gradient-rounded", v: { ...base } }, + { id: "tb-straight-solid-pill", v: { ...base, edgeType: "straight", edgeColorMode: "solid", nodeShape: "pill" } }, + { id: "tb-curved-solid-rounded", v: { ...base, edgeColorMode: "solid" } }, + { id: "tb-straight-gradient-rounded", v: { ...base, edgeType: "straight" } }, + { id: "lr-curved-gradient-rounded", v: { ...base, orientation: "left-right" } }, + { id: "lr-straight-solid-pill", v: { ...base, orientation: "left-right", edgeType: "straight", edgeColorMode: "solid", nodeShape: "pill" } }, +]; + +export const hierarchyPreviews = ( + <> + {variants.map(({ id, v }) => { + const isHoriz = v.orientation === "left-right"; + const dur = hierarchySceneDuration(DEMO_HIERARCHY, WIDTH, HEIGHT, isHoriz); + return ( + } + durationInFrames={dur} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + ); + })} + +); diff --git a/surfsense_video/src/remotion/scenes/hierarchy/types.ts b/surfsense_video/src/remotion/scenes/hierarchy/types.ts new file mode 100644 index 000000000..8e10b5b80 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/hierarchy/types.ts @@ -0,0 +1,46 @@ +/** Zod schemas and inferred types for hierarchy (tree) scene data. */ +import { z } from "zod"; + +export interface HierarchyNode { + label: string; + color?: string; + desc?: string; + children?: HierarchyNode[]; +} + +const HierarchyNodeSchema: z.ZodType = z.object({ + label: z.string(), + color: z.string().optional(), + desc: z.string().optional(), + children: z.lazy(() => z.array(HierarchyNodeSchema)).optional(), +}); + +export { HierarchyNodeSchema }; + +export const HierarchySceneInput = z.object({ + type: z.literal("hierarchy"), + title: z.string().optional(), + items: z.array(HierarchyNodeSchema).min(1), +}); + +export type HierarchySceneInput = z.infer; + +/** Flattened d3 hierarchy node with layout coordinates. */ +export interface LayoutNode { + data: HierarchyNode; + x: number; + y: number; + depth: number; + parent: LayoutNode | null; + children?: LayoutNode[]; + siblingIndex: number; +} + +/** Camera stop — position + hold duration + transition to next. */ +export interface Waypoint { + cx: number; + cy: number; + holdFrames: number; + transitionAfter: number; + depth: number; +} diff --git a/surfsense_video/src/remotion/scenes/hierarchy/variant.ts b/surfsense_video/src/remotion/scenes/hierarchy/variant.ts new file mode 100644 index 000000000..dc303382d --- /dev/null +++ b/surfsense_video/src/remotion/scenes/hierarchy/variant.ts @@ -0,0 +1,33 @@ +/** Hierarchy scene variant — seed-deterministic visual style. */ +import { random } from "remotion"; + +type Orientation = "top-bottom" | "left-right"; +type EdgeType = "curved" | "straight"; +type EdgeColorMode = "solid" | "gradient"; +type NodeShape = "rounded" | "pill"; +export type HierarchyCardStyle = "gradient" | "glass" | "outline" | "solid"; + +export interface HierarchyVariant { + orientation: Orientation; + edgeType: EdgeType; + edgeColorMode: EdgeColorMode; + nodeShape: NodeShape; + cardStyle: HierarchyCardStyle; + edgeCornerRadius: number; +} + +export function deriveHierarchyVariant(seed: number): HierarchyVariant { + const s = (key: string) => random(`${seed}-${key}`); + const pick = (key: string, arr: T[]) => + arr[Math.floor(s(key) * arr.length)]; + + return { + orientation: pick("orient", ["top-bottom", "left-right"] as Orientation[]), + edgeType: pick("edge", ["curved", "straight"] as EdgeType[]), + edgeColorMode: pick("edgeColor", ["solid", "gradient"] as EdgeColorMode[]), + nodeShape: pick("shape", ["rounded", "pill"] as NodeShape[]), + cardStyle: pick("cardStyle", ["gradient", "glass", "outline", "solid"] as HierarchyCardStyle[]), + /** Multiplier of vmin — resolved to pixels at render time. */ + edgeCornerRadius: s("corner") * 1.2 + 0.4, + }; +} diff --git a/surfsense_video/src/remotion/scenes/index.ts b/surfsense_video/src/remotion/scenes/index.ts new file mode 100644 index 000000000..a37fcab72 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/index.ts @@ -0,0 +1,9 @@ +export { IntroSceneInput } from "./intro/types"; +export { SpotlightSceneInput } from "./spotlight/types"; +export { HierarchySceneInput } from "./hierarchy/types"; +export { ListSceneInput } from "./list/types"; +export { SequenceSceneInput } from "./sequence/types"; +export { ChartSceneInput } from "./chart/types"; +export { RelationSceneInput } from "./relation/types"; +export { ComparisonSceneInput } from "./comparison/types"; +export { OutroSceneInput } from "./outro/types"; diff --git a/surfsense_video/src/remotion/scenes/intro/IntroScene.tsx b/surfsense_video/src/remotion/scenes/intro/IntroScene.tsx new file mode 100644 index 000000000..05471a027 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/intro/IntroScene.tsx @@ -0,0 +1,336 @@ +/** + * IntroScene -- animated title card with multiple reveal styles. + * 5 animation variants x 4 background styles x 4 decorative elements. + */ +import React from "react"; +import { + AbsoluteFill, + useCurrentFrame, + useVideoConfig, + interpolate, + spring, + Easing, +} from "remotion"; +import type { ThemeColors } from "../../theme"; +import type { IntroSceneInput } from "./types"; +import type { IntroVariant } from "./variant"; +import { TITLE_DELAY, SUBTITLE_DELAY, DECOR_DELAY } from "./constants"; + +interface IntroSceneProps { + input: IntroSceneInput; + theme: ThemeColors; + variant: IntroVariant; +} + +function accentColor(hue: number): string { + return `hsl(${hue}, 70%, 65%)`; +} + +function accentColorAlpha(hue: number, alpha: string): string { + return `hsla(${hue}, 70%, 65%, ${alpha})`; +} + +function renderBg( + variant: IntroVariant, + theme: ThemeColors, + frame: number, + vmin: number, + width: number, + height: number, +): React.ReactNode { + const accent = accentColor(variant.accentHue); + const progress = interpolate(frame, [0, 60], [0, 1], { + extrapolateRight: "clamp", + }); + + switch (variant.bgStyle) { + case "radialGlow": { + const radius = 30 + progress * 20; + return ( +
+ ); + } + case "gradientSweep": { + const angle = progress * 120; + return ( +
+ ); + } + case "particleDots": { + const dots = Array.from({ length: 12 }, (_, i) => { + const x = (Math.sin(i * 2.39) * 0.4 + 0.5) * width; + const y = (Math.cos(i * 2.39) * 0.4 + 0.5) * height; + const size = vmin * (0.3 + (i % 3) * 0.15); + const delay = i * 3; + const opacity = interpolate(frame - delay, [0, 20], [0, 0.3], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + return ( +
+ ); + }); + return <>{dots}; + } + case "minimal": + default: + return null; + } +} + +function renderDecor( + variant: IntroVariant, + frame: number, + vmin: number, + width: number, + height: number, +): React.ReactNode { + const accent = accentColor(variant.accentHue); + const progress = interpolate(frame - DECOR_DELAY, [0, 40], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + const eased = Easing.out(Easing.ease)(progress); + + switch (variant.decor) { + case "line": { + const lineW = eased * vmin * 30; + return ( +
+ ); + } + case "corners": { + const armLen = vmin * 4 * eased; + const strokeW = vmin * 0.12; + const inset = vmin * 8; + const opacity = eased * 0.5; + const cornerStyle = (left: number, top: number, bL: string, bT: string): React.CSSProperties => ({ + position: "absolute", + left, + top, + width: armLen, + height: armLen, + [bL]: `${strokeW}px solid ${accent}`, + [bT]: `${strokeW}px solid ${accent}`, + opacity, + }); + return ( + <> +
+
+
+
+ + ); + } + case "ring": { + const size = vmin * 20 * eased; + return ( +
+ ); + } + case "none": + default: + return null; + } +} + +export const IntroScene: React.FC = ({ + input, + theme, + variant, +}) => { + const frame = useCurrentFrame(); + const { fps, width, height } = useVideoConfig(); + const vmin = Math.min(width, height) / 100; + + const titleLocal = frame - TITLE_DELAY; + const subtitleLocal = frame - (TITLE_DELAY + SUBTITLE_DELAY); + + let titleOpacity: number; + let titleTransform: string; + + switch (variant.animation) { + case "fadeUp": { + titleOpacity = interpolate(titleLocal, [0, 20], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + const y = interpolate(titleLocal, [0, 20], [vmin * 4, 0], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + easing: Easing.out(Easing.ease), + }); + titleTransform = `translateY(${y}px)`; + break; + } + case "scaleIn": { + const s = spring({ + frame: Math.max(0, titleLocal), + fps, + config: { damping: 12, stiffness: 80, mass: 0.8 }, + }); + titleOpacity = interpolate(titleLocal, [0, 10], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + titleTransform = `scale(${s})`; + break; + } + case "typewriter": { + const totalChars = input.title.length; + const charsVisible = Math.floor( + interpolate(titleLocal, [0, totalChars * 1.5], [0, totalChars], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }), + ); + titleOpacity = titleLocal >= 0 ? 1 : 0; + titleTransform = ""; + input = { + ...input, + title: input.title.slice(0, charsVisible) + (charsVisible < totalChars && titleLocal >= 0 ? "\u258C" : ""), + }; + break; + } + case "splitReveal": { + titleOpacity = interpolate(titleLocal, [0, 15], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + const spread = interpolate(titleLocal, [0, 25], [vmin * 6, 0], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + easing: Easing.out(Easing.cubic), + }); + titleTransform = `translateX(${spread}px)`; + break; + } + case "glowIn": + default: { + titleOpacity = interpolate(titleLocal, [0, 25], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + const blur = interpolate(titleLocal, [0, 20], [vmin * 1, 0], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + titleTransform = `blur(${blur}px)`; + break; + } + } + + const subtitleOpacity = interpolate(subtitleLocal, [0, 18], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + const subtitleY = interpolate(subtitleLocal, [0, 18], [vmin * 2, 0], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + easing: Easing.out(Easing.ease), + }); + + const accent = accentColor(variant.accentHue); + const isGlow = variant.animation === "glowIn"; + + return ( + + {renderBg(variant, theme, frame, vmin, width, height)} + {renderDecor(variant, frame, vmin, width, height)} + +
+
+ {input.title} +
+ + {input.subtitle && ( +
+ {input.subtitle} +
+ )} +
+
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/intro/constants.ts b/surfsense_video/src/remotion/scenes/intro/constants.ts new file mode 100644 index 000000000..02762796c --- /dev/null +++ b/surfsense_video/src/remotion/scenes/intro/constants.ts @@ -0,0 +1,13 @@ +/** Timing constants for intro scene. */ + +/** Total intro duration (frames). */ +export const INTRO_DURATION = 90; + +/** Title reveal start (frames). */ +export const TITLE_DELAY = 10; + +/** Subtitle reveal start after title (frames). */ +export const SUBTITLE_DELAY = 30; + +/** Decorative element animation start (frames). */ +export const DECOR_DELAY = 5; diff --git a/surfsense_video/src/remotion/scenes/intro/demo.ts b/surfsense_video/src/remotion/scenes/intro/demo.ts new file mode 100644 index 000000000..6815c6a9e --- /dev/null +++ b/surfsense_video/src/remotion/scenes/intro/demo.ts @@ -0,0 +1,13 @@ +/** Sample data for intro scene preview. */ +import type { IntroSceneInput } from "./types"; + +export const DEMO_INTRO: IntroSceneInput = { + type: "intro", + title: "The Future of AI", + subtitle: "A visual exploration of artificial intelligence", +}; + +export const DEMO_INTRO_SHORT: IntroSceneInput = { + type: "intro", + title: "Domain-Driven Design", +}; diff --git a/surfsense_video/src/remotion/scenes/intro/preview.tsx b/surfsense_video/src/remotion/scenes/intro/preview.tsx new file mode 100644 index 000000000..14e8b1967 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/intro/preview.tsx @@ -0,0 +1,102 @@ +/** Remotion Studio compositions for intro scene. */ +import { Composition } from "remotion"; +import { IntroScene } from "./IntroScene"; +import { THEMES } from "../../theme"; +import type { IntroVariant } from "./variant"; +import { DEMO_INTRO, DEMO_INTRO_SHORT } from "./demo"; +import { INTRO_DURATION } from "./constants"; + +const THEME = "dark" as const; +const FPS = 30; +const WIDTH = 1920; +const HEIGHT = 1080; + +const base: IntroVariant = { + animation: "fadeUp", + bgStyle: "radialGlow", + decor: "line", + accentHue: 230, +}; + +export const introPreviews = ( + <> + } + durationInFrames={INTRO_DURATION} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + ( + + )} + durationInFrames={INTRO_DURATION} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + ( + + )} + durationInFrames={INTRO_DURATION} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + ( + + )} + durationInFrames={INTRO_DURATION} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + ( + + )} + durationInFrames={INTRO_DURATION} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + ( + + )} + durationInFrames={INTRO_DURATION} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + +); diff --git a/surfsense_video/src/remotion/scenes/intro/types.ts b/surfsense_video/src/remotion/scenes/intro/types.ts new file mode 100644 index 000000000..8fa49b63c --- /dev/null +++ b/surfsense_video/src/remotion/scenes/intro/types.ts @@ -0,0 +1,10 @@ +/** Zod schema and types for intro scene. */ +import { z } from "zod"; + +export const IntroSceneInput = z.object({ + type: z.literal("intro"), + title: z.string(), + subtitle: z.string().optional(), +}); + +export type IntroSceneInput = z.infer; diff --git a/surfsense_video/src/remotion/scenes/intro/variant.ts b/surfsense_video/src/remotion/scenes/intro/variant.ts new file mode 100644 index 000000000..b9521e456 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/intro/variant.ts @@ -0,0 +1,32 @@ +/** Intro scene variant -- seed-deterministic visual style. */ +import { random } from "remotion"; + +export type IntroAnimation = "fadeUp" | "scaleIn" | "typewriter" | "splitReveal" | "glowIn"; +export type IntroBgStyle = "radialGlow" | "gradientSweep" | "particleDots" | "minimal"; +export type IntroDecor = "line" | "corners" | "ring" | "none"; + +export interface IntroVariant { + animation: IntroAnimation; + bgStyle: IntroBgStyle; + decor: IntroDecor; + accentHue: number; +} + +export function deriveIntroVariant(seed: number): IntroVariant { + const s = (key: string) => random(`${seed}-${key}`); + const pick = (key: string, arr: T[]) => + arr[Math.floor(s(key) * arr.length)]; + + return { + animation: pick("anim", [ + "fadeUp", "scaleIn", "typewriter", "splitReveal", "glowIn", + ] as IntroAnimation[]), + bgStyle: pick("bg", [ + "radialGlow", "gradientSweep", "particleDots", "minimal", + ] as IntroBgStyle[]), + decor: pick("decor", [ + "line", "corners", "ring", "none", + ] as IntroDecor[]), + accentHue: Math.floor(s("hue") * 360), + }; +} diff --git a/surfsense_video/src/remotion/scenes/list/ListScene.tsx b/surfsense_video/src/remotion/scenes/list/ListScene.tsx new file mode 100644 index 000000000..0a0e42601 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/list/ListScene.tsx @@ -0,0 +1,147 @@ +/** + * ListScene — items positioned in zigzag, column, row, or pyramid layout + * with snap-hold camera and staggered reveal. + */ +import React, { useMemo } from "react"; +import { AbsoluteFill, useVideoConfig, useCurrentFrame, Easing } from "remotion"; +import type { ThemeColors } from "../../theme"; +import type { ListSceneInput, Waypoint } from "./types"; +import type { ListVariant } from "./variant"; +import { ITEM_STAGGER } from "./constants"; +import { ListItemCard } from "./components/ListItem"; +import { positionItems, buildWaypoints, getLayoutParams } from "./layout"; + +interface ListSceneProps { + input: ListSceneInput; + theme: ThemeColors; + variant: ListVariant; +} + +function resolveCamera(waypoints: Waypoint[], frame: number): { cx: number; cy: number } { + let cam = { cx: waypoints[0].cx, cy: waypoints[0].cy }; + let cursor = 0; + + for (let w = 0; w < waypoints.length; w++) { + const wp = waypoints[w]; + if (frame < cursor + wp.holdFrames) { + cam = { cx: wp.cx, cy: wp.cy }; + break; + } + cursor += wp.holdFrames; + + if (wp.transitionAfter > 0 && w + 1 < waypoints.length) { + if (frame < cursor + wp.transitionAfter) { + const t = Easing.inOut(Easing.ease)( + (frame - cursor) / wp.transitionAfter, + ); + const next = waypoints[w + 1]; + cam = { + cx: wp.cx + (next.cx - wp.cx) * t, + cy: wp.cy + (next.cy - wp.cy) * t, + }; + break; + } + cursor += wp.transitionAfter; + } + + if (w === waypoints.length - 1) { + cam = { cx: wp.cx, cy: wp.cy }; + } + } + + return cam; +} + +export const ListScene: React.FC = ({ + input, + theme, + variant, +}) => { + const { width, height } = useVideoConfig(); + const vmin = Math.min(width, height) / 100; + const frame = useCurrentFrame(); + + const showBadge = variant.showIndex || input.items.some((it) => it.value !== undefined); + + const { items, waypoints } = useMemo(() => { + const p = getLayoutParams(vmin); + const positioned = positionItems(input.items, variant.layout, p, vmin, showBadge); + const wps = buildWaypoints(positioned, variant.layout, width, height); + return { items: positioned, waypoints: wps }; + }, [input.items, variant.layout, vmin, width, height, showBadge]); + + const cam = resolveCamera(waypoints, frame); + + return ( + + {input.title && ( +
+
+ {input.title} +
+ {input.subtitle && ( +
+ {input.subtitle} +
+ )} +
+ )} + +
+ {items.map((it) => { + const enterF = it.index * ITEM_STAGGER; + return ( +
+ +
+ ); + })} +
+
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/list/components/ListItem.tsx b/surfsense_video/src/remotion/scenes/list/components/ListItem.tsx new file mode 100644 index 000000000..e96ae1cf4 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/list/components/ListItem.tsx @@ -0,0 +1,181 @@ +/** Single list item — card with style determined by variant.cardStyle. */ +import React from "react"; +import { useCurrentFrame, interpolate, spring, useVideoConfig } from "remotion"; +import type { ThemeColors } from "../../../theme"; +import type { ListItem as ListItemData } from "../types"; +import type { ListVariant, ListCardStyle } from "../variant"; +import { ITEM_FADE_DURATION } from "../constants"; + +interface ListItemProps { + item: ListItemData; + index: number; + enterFrame: number; + vmin: number; + variant: ListVariant; + theme: ThemeColors; + cardWidth: number; + cardHeight: number; +} + +function listCardCSS( + style: ListCardStyle, + color: string, + vmin: number, +): React.CSSProperties { + const bw = vmin * 0.14; + + switch (style) { + case "accent-left": + return { + background: `${color}0d`, + border: `${bw}px solid ${color}20`, + borderLeft: `${vmin * 0.45}px solid ${color}`, + boxShadow: `${vmin * 0.2}px ${vmin * 0.4}px ${vmin * 1.8}px rgba(0,0,0,0.25)`, + }; + case "accent-bottom": + return { + background: `linear-gradient(180deg, ${color}12, ${color}06)`, + border: `${bw}px solid ${color}25`, + borderBottom: `${vmin * 0.35}px solid ${color}`, + boxShadow: `0 ${vmin * 0.5}px ${vmin * 2}px ${color}10`, + }; + case "filled": + return { + background: `${color}22`, + border: `${bw}px solid ${color}44`, + boxShadow: `0 ${vmin * 0.3}px ${vmin * 1.5}px rgba(0,0,0,0.2)`, + }; + case "minimal": + return { + background: `${color}08`, + border: `${bw}px solid ${color}18`, + boxShadow: "none", + }; + } +} + +function listBadgeCSS( + style: ListCardStyle, + color: string, + vmin: number, +): React.CSSProperties { + switch (style) { + case "accent-left": + return { background: color, color: "#fff" }; + case "accent-bottom": + return { background: `${color}25`, color, border: `${vmin * 0.15}px solid ${color}55` }; + case "filled": + return { background: `${color}44`, color: "#fff" }; + case "minimal": + return { background: "transparent", color, border: `${vmin * 0.18}px solid ${color}` }; + } +} + +export const ListItemCard: React.FC = ({ + item, + index, + enterFrame, + vmin, + variant, + theme, + cardWidth, + cardHeight, +}) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + const color = item.color ?? "#6c7dff"; + const localFrame = frame - enterFrame; + + const opacity = interpolate(localFrame, [0, ITEM_FADE_DURATION], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + + const scale = spring({ + frame: Math.max(0, localFrame), + fps, + config: { damping: 14, stiffness: 120, mass: 0.8 }, + }); + + const borderRadius = variant.itemShape === "pill" ? 999 : vmin * 1.2; + const paddingX = vmin * 2; + const paddingY = vmin * 1.5; + const styleCSS = listCardCSS(variant.cardStyle, color, vmin); + const badgeCSS = listBadgeCSS(variant.cardStyle, color, vmin); + + return ( +
+ {(variant.showIndex || item.value !== undefined) && ( +
+ {item.value !== undefined ? item.value : String(index + 1).padStart(2, "0")} +
+ )} + +
+ + {item.label} + + {item.desc && ( + + {item.desc} + + )} +
+
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/list/components/itemSize.ts b/surfsense_video/src/remotion/scenes/list/components/itemSize.ts new file mode 100644 index 000000000..32deefb31 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/list/components/itemSize.ts @@ -0,0 +1,106 @@ +/** + * Measure list item dimensions using canvas.measureText. + * Same approach as hierarchy/nodeSize.ts — deterministic text measurement + * before layout so positioning is based on actual content size. + */ +import type { ListItem } from "../types"; + +export interface ItemDimensions { + width: number; + height: number; + paddingX: number; + paddingY: number; + labelFontSize: number; + descFontSize: number; + badgeSize: number; +} + +const LINE_HEIGHT_LABEL = 1.3; +const LINE_HEIGHT_DESC = 1.4; +const FONT_FAMILY = "Inter, system-ui, sans-serif"; +const BORDER_FACTOR = 0.14; + +let _ctx: CanvasRenderingContext2D | null = null; +function ctx(): CanvasRenderingContext2D { + if (!_ctx) { + _ctx = document.createElement("canvas").getContext("2d")!; + } + return _ctx; +} + +function measureLines( + text: string, + fontSize: number, + fontWeight: number | string, + availableWidth: number, +): number { + const c = ctx(); + c.font = `${fontWeight} ${fontSize}px ${FONT_FAMILY}`; + + const words = text.split(/\s+/).filter(Boolean); + if (words.length === 0) return 1; + + let lines = 1; + let lineWidth = 0; + const spaceW = c.measureText(" ").width; + + for (const word of words) { + const wordW = c.measureText(word).width; + if (lineWidth === 0) { + lineWidth = wordW; + } else if (lineWidth + spaceW + wordW <= availableWidth) { + lineWidth += spaceW + wordW; + } else { + lines++; + lineWidth = wordW; + } + } + + return lines; +} + +export function measureItemDimensions( + item: ListItem, + vmin: number, + cardW: number, + showBadge: boolean, +): ItemDimensions { + const paddingX = vmin * 2; + const paddingY = vmin * 1.5; + const borderW = vmin * BORDER_FACTOR; + const gap = vmin * 1.5; + const textGap = vmin * 0.3; + + const labelFontSize = vmin * 1.8; + const descFontSize = vmin * 1.2; + const badgeSize = vmin * 5; + + const badgeSpace = showBadge ? badgeSize + gap : 0; + const textW = cardW - 2 * paddingX - 2 * borderW - badgeSpace; + + const labelLines = measureLines(item.label, labelFontSize, 600, textW); + const labelH = labelLines * labelFontSize * LINE_HEIGHT_LABEL; + + let contentH = labelH; + if (item.desc) { + const descLines = measureLines(item.desc, descFontSize, 400, textW); + const descH = descLines * descFontSize * LINE_HEIGHT_DESC; + contentH += textGap + descH; + } + + const minHeight = Math.max( + showBadge ? badgeSize + 2 * paddingY : vmin * 5, + vmin * 6, + ); + const height = Math.max(minHeight, contentH + 2 * paddingY + 2 * borderW); + + return { + width: cardW, + height, + paddingX, + paddingY, + labelFontSize, + descFontSize, + badgeSize, + }; +} diff --git a/surfsense_video/src/remotion/scenes/list/constants.ts b/surfsense_video/src/remotion/scenes/list/constants.ts new file mode 100644 index 000000000..db71d24e5 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/list/constants.ts @@ -0,0 +1,11 @@ +/** Hold duration on each camera stop (frames @30fps ≈ 5s). */ +export const STOP_HOLD = 150; + +/** Fast snap transition between stops (frames). */ +export const STOP_TRANSITION = 20; + +/** Stagger delay per item within a visible group (frames). */ +export const ITEM_STAGGER = 12; + +/** Item fade-in duration (frames). */ +export const ITEM_FADE_DURATION = 20; diff --git a/surfsense_video/src/remotion/scenes/list/demo.ts b/surfsense_video/src/remotion/scenes/list/demo.ts new file mode 100644 index 000000000..74b573655 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/list/demo.ts @@ -0,0 +1,20 @@ +/** Sample list data for Remotion Studio preview. */ +import type { ListSceneInput } from "./types"; + +export const DEMO_LIST: ListSceneInput = { + type: "list", + title: "Enterprise Strengths", + subtitle: "Core advantages across key dimensions", + items: [ + { label: "Brand Influence", value: 85, color: "#6c7dff", desc: "Strong recognition and trust among target users built over years of consistent delivery" }, + { label: "R&D Capability", value: 90, color: "#00c9a7", desc: "Self-developed core systems with continuous innovation and patent portfolio" }, + { label: "Market Growth", value: 78, color: "#ff6b6b", desc: "Rapid user base expansion over the past year driven by organic channels" }, + { label: "Service Satisfaction", value: 88, color: "#ffd93d", desc: "High overall ratings for the service ecosystem across all regions" }, + { label: "Data Assets", value: 92, color: "#c084fc", desc: "Complete user tagging and profiling system enabling personalized experiences" }, + { label: "Innovation Speed", value: 83, color: "#f97316", desc: "New product launch rate above industry average with shorter development cycles" }, + { label: "Talent Acquisition", value: 87, color: "#14b8a6", desc: "Strong employer brand attracting top engineering and design talent globally" }, + { label: "Cloud Infrastructure", value: 91, color: "#8b5cf6", desc: "Highly available distributed systems with 99.99% uptime SLA guarantees" }, + { label: "Strategic Partnerships", value: 79, color: "#ec4899", desc: "Key alliances with industry leaders expanding market reach and credibility" }, + { label: "Sustainability Index", value: 76, color: "#22c55e", desc: "Carbon-neutral operations and commitment to renewable energy across data centers" }, + ], +}; diff --git a/surfsense_video/src/remotion/scenes/list/layout.ts b/surfsense_video/src/remotion/scenes/list/layout.ts new file mode 100644 index 000000000..2295872bc --- /dev/null +++ b/surfsense_video/src/remotion/scenes/list/layout.ts @@ -0,0 +1,288 @@ +/** + * List layout utilities — item positioning and camera waypoints. + * Item heights are measured via canvas.measureText before positioning, + * so layout adapts to actual content (same approach as hierarchy scene). + */ +import type { ListSceneInput, ListItem, LayoutItem, Waypoint } from "./types"; +import type { ListLayout } from "./variant"; +import { STOP_HOLD, STOP_TRANSITION } from "./constants"; +import { measureItemDimensions } from "./components/itemSize"; + +export interface LayoutParams { + cardW: number; + gapX: number; + gapY: number; +} + +interface MeasuredItem { + data: ListItem; + w: number; + h: number; +} + +/** Measure all items, then position based on actual content size. */ +export function positionItems( + items: ListItem[], + layout: ListLayout, + p: LayoutParams, + vmin: number, + showBadge: boolean, +): LayoutItem[] { + const measured: MeasuredItem[] = items.map((data) => { + const dims = measureItemDimensions(data, vmin, p.cardW, showBadge); + return { data, w: dims.width, h: dims.height }; + }); + + switch (layout) { + case "column": + return layoutColumn(measured, p); + + case "row": + return layoutRow(measured, p); + + case "zigzag": + return layoutZigzag(measured, p); + + case "pyramid": + return layoutPyramid(measured, p); + } +} + +function layoutColumn(items: MeasuredItem[], p: LayoutParams): LayoutItem[] { + const result: LayoutItem[] = []; + let y = 0; + for (let i = 0; i < items.length; i++) { + const { data, w, h } = items[i]; + result.push({ data, x: 0, y, w, h, index: i }); + y += h + p.gapY; + } + return result; +} + +function layoutRow(items: MeasuredItem[], p: LayoutParams): LayoutItem[] { + const maxH = Math.max(...items.map((it) => it.h)); + const result: LayoutItem[] = []; + let x = 0; + for (let i = 0; i < items.length; i++) { + const { data, w, h } = items[i]; + result.push({ data, x, y: (maxH - h) / 2, w, h, index: i }); + x += w + p.gapX; + } + return result; +} + +function layoutZigzag(items: MeasuredItem[], p: LayoutParams): LayoutItem[] { + const offset = p.cardW * 0.6; + const result: LayoutItem[] = []; + let y = 0; + for (let i = 0; i < items.length; i++) { + const { data, w, h } = items[i]; + result.push({ data, x: i % 2 === 0 ? 0 : offset, y, w, h, index: i }); + y += h + p.gapY; + } + return result; +} + +function layoutPyramid(items: MeasuredItem[], p: LayoutParams): LayoutItem[] { + const levels: number[] = []; + let remaining = items.length; + for (let level = 0; remaining > 0; level++) { + const count = Math.min(level + 1, remaining); + levels.push(count); + remaining -= count; + } + + const maxLevelSize = Math.max(...levels); + const maxLevelW = maxLevelSize * items[0].w + (maxLevelSize - 1) * p.gapX; + const centerX = maxLevelW / 2; + + const result: LayoutItem[] = []; + let itemIdx = 0; + let y = 0; + + for (const levelSize of levels) { + const rowItems = items.slice(itemIdx, itemIdx + levelSize); + const rowH = Math.max(...rowItems.map((it) => it.h)); + const totalW = levelSize * rowItems[0].w + (levelSize - 1) * p.gapX; + const startX = centerX - totalW / 2; + + for (let i = 0; i < levelSize; i++) { + const { data, w, h } = rowItems[i]; + result.push({ + data, + x: startX + i * (w + p.gapX), + y: y + (rowH - h) / 2, + w, + h, + index: itemIdx + i, + }); + } + + y += rowH + p.gapY; + itemIdx += levelSize; + } + + return result; +} + +/** + * Build snap-hold camera waypoints. + * cx/cy = world-space center point the viewport looks at. + * Scene renders container at: left = viewW/2 - cx, top = viewH/2 - cy. + */ +export function buildWaypoints( + items: LayoutItem[], + layout: ListLayout, + viewW: number, + viewH: number, +): Waypoint[] { + if (items.length === 0) { + return [{ cx: 0, cy: 0, holdFrames: STOP_HOLD, transitionAfter: 0 }]; + } + + const usableW = viewW * 0.8; + const usableH = viewH * 0.8; + + if (layout === "pyramid") { + return buildPyramidWaypoints(items, usableW, usableH); + } + + const isHoriz = layout === "row"; + const primaryAxis = isHoriz ? "x" : "y"; + const primarySize = isHoriz ? "w" : "h"; + const usable = isHoriz ? usableW : usableH; + + const sorted = [...items].sort((a, b) => a[primaryAxis] - b[primaryAxis]); + const stops: Waypoint[] = []; + let i = 0; + + while (i < sorted.length) { + const anchor = sorted[i][primaryAxis]; + let lastIdx = i; + while ( + lastIdx + 1 < sorted.length && + sorted[lastIdx + 1][primaryAxis] + + sorted[lastIdx + 1][primarySize] - + anchor <= + usable + ) { + lastIdx++; + } + + const group = sorted.slice(i, lastIdx + 1); + const bbox = groupBBox(group); + + stops.push({ + cx: (bbox.minX + bbox.maxX) / 2, + cy: (bbox.minY + bbox.maxY) / 2, + holdFrames: STOP_HOLD, + transitionAfter: STOP_TRANSITION, + }); + i = lastIdx + 1; + } + + if (stops.length > 0) stops[stops.length - 1].transitionAfter = 0; + return stops; +} + +/** + * Pyramid spreads in 2D — group items into viewport-sized windows, + * first by rows (vertical bands) that fit usableH, then within each + * band split horizontally if the level is wider than usableW. + */ +function buildPyramidWaypoints( + items: LayoutItem[], + usableW: number, + usableH: number, +): Waypoint[] { + const sorted = [...items].sort((a, b) => a.y - b.y || a.x - b.x); + const stops: Waypoint[] = []; + + let i = 0; + while (i < sorted.length) { + const anchorY = sorted[i].y; + let lastRow = i; + while ( + lastRow + 1 < sorted.length && + sorted[lastRow + 1].y + sorted[lastRow + 1].h - anchorY <= usableH + ) { + lastRow++; + } + + const rowBand = sorted.slice(i, lastRow + 1); + + const rowSorted = [...rowBand].sort((a, b) => a.x - b.x); + let j = 0; + while (j < rowSorted.length) { + const anchorX = rowSorted[j].x; + let lastCol = j; + while ( + lastCol + 1 < rowSorted.length && + rowSorted[lastCol + 1].x + rowSorted[lastCol + 1].w - anchorX <= usableW + ) { + lastCol++; + } + + const group = rowSorted.slice(j, lastCol + 1); + const bbox = groupBBox(group); + stops.push({ + cx: (bbox.minX + bbox.maxX) / 2, + cy: (bbox.minY + bbox.maxY) / 2, + holdFrames: STOP_HOLD, + transitionAfter: STOP_TRANSITION, + }); + j = lastCol + 1; + } + + i = lastRow + 1; + } + + if (stops.length > 0) stops[stops.length - 1].transitionAfter = 0; + return stops; +} + +function groupBBox(items: LayoutItem[]) { + let minX = Infinity, + minY = Infinity, + maxX = -Infinity, + maxY = -Infinity; + for (const it of items) { + minX = Math.min(minX, it.x); + minY = Math.min(minY, it.y); + maxX = Math.max(maxX, it.x + it.w); + maxY = Math.max(maxY, it.y + it.h); + } + return { minX, minY, maxX, maxY }; +} + +/** Total duration from waypoints. */ +export function waypointsDuration(waypoints: Waypoint[]): number { + let total = 0; + for (const wp of waypoints) { + total += wp.holdFrames + wp.transitionAfter; + } + return Math.max(1, total); +} + +/** Compute total scene duration for a list input. */ +export function listSceneDuration( + input: ListSceneInput, + layout: ListLayout, + viewW: number, + viewH: number, + showBadge = true, +): number { + const vmin = Math.min(viewW, viewH) / 100; + const p = getLayoutParams(vmin); + const positioned = positionItems(input.items, layout, p, vmin, showBadge); + const wps = buildWaypoints(positioned, layout, viewW, viewH); + return waypointsDuration(wps); +} + +export function getLayoutParams(vmin: number): LayoutParams { + return { + cardW: vmin * 45, + gapX: vmin * 4, + gapY: vmin * 3, + }; +} diff --git a/surfsense_video/src/remotion/scenes/list/preview.tsx b/surfsense_video/src/remotion/scenes/list/preview.tsx new file mode 100644 index 000000000..0dca647a8 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/list/preview.tsx @@ -0,0 +1,58 @@ +/** Remotion Studio compositions for list scene. */ +import React from "react"; +import { Composition } from "remotion"; +import { ListScene } from "./ListScene"; +import { THEMES } from "../../theme"; +import type { ListVariant, ListLayout } from "./variant"; +import { DEMO_LIST } from "./demo"; +import { listSceneDuration } from "./layout"; + +const THEME = "dark" as const; +const FPS = 30; +const WIDTH = 1920; +const HEIGHT = 1080; + +const ListPreview: React.FC<{ variant: ListVariant }> = ({ variant }) => { + const theme = THEMES[THEME]; + return ; +}; + +const layouts: ListLayout[] = ["zigzag", "column", "row", "pyramid"]; + +const base: ListVariant = { + layout: "zigzag", + itemShape: "rounded", + connectorStyle: "line", + cardStyle: "accent-left", + showIndex: true, +}; + +export const listPreviews = ( + <> + {layouts.map((layout) => { + const v: ListVariant = { ...base, layout }; + const dur = listSceneDuration(DEMO_LIST, layout, WIDTH, HEIGHT); + return ( + } + durationInFrames={dur} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + ); + })} + ( + + )} + durationInFrames={listSceneDuration(DEMO_LIST, "zigzag", WIDTH, HEIGHT, false)} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + +); diff --git a/surfsense_video/src/remotion/scenes/list/types.ts b/surfsense_video/src/remotion/scenes/list/types.ts new file mode 100644 index 000000000..2c8d8e3b0 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/list/types.ts @@ -0,0 +1,38 @@ +/** Zod schemas and inferred types for list scene data. */ +import { z } from "zod"; + +export const ListItemSchema = z.object({ + label: z.string(), + desc: z.string().optional(), + value: z.union([z.string(), z.number()]).optional(), + color: z.string().optional(), +}); + +export type ListItem = z.infer; + +export const ListSceneInput = z.object({ + type: z.literal("list"), + title: z.string().optional(), + subtitle: z.string().optional(), + items: z.array(ListItemSchema).min(1), +}); + +export type ListSceneInput = z.infer; + +/** Positioned list item for rendering. */ +export interface LayoutItem { + data: ListItem; + x: number; + y: number; + w: number; + h: number; + index: number; +} + +/** Camera stop. */ +export interface Waypoint { + cx: number; + cy: number; + holdFrames: number; + transitionAfter: number; +} diff --git a/surfsense_video/src/remotion/scenes/list/variant.ts b/surfsense_video/src/remotion/scenes/list/variant.ts new file mode 100644 index 000000000..bc863559b --- /dev/null +++ b/surfsense_video/src/remotion/scenes/list/variant.ts @@ -0,0 +1,29 @@ +/** List scene variant — seed-deterministic visual style. */ +import { random } from "remotion"; + +export type ListLayout = "zigzag" | "column" | "row" | "pyramid"; +type ItemShape = "rounded" | "pill"; +type ConnectorStyle = "line" | "dashed" | "none"; +export type ListCardStyle = "accent-left" | "accent-bottom" | "filled" | "minimal"; + +export interface ListVariant { + layout: ListLayout; + itemShape: ItemShape; + connectorStyle: ConnectorStyle; + cardStyle: ListCardStyle; + showIndex: boolean; +} + +export function deriveListVariant(seed: number): ListVariant { + const s = (key: string) => random(`${seed}-${key}`); + const pick = (key: string, arr: T[]) => + arr[Math.floor(s(key) * arr.length)]; + + return { + layout: pick("layout", ["zigzag", "column", "row", "pyramid"] as ListLayout[]), + itemShape: pick("shape", ["rounded", "pill"] as ItemShape[]), + connectorStyle: pick("connector", ["line", "dashed", "none"] as ConnectorStyle[]), + cardStyle: pick("cardStyle", ["accent-left", "accent-bottom", "filled", "minimal"] as ListCardStyle[]), + showIndex: s("index") > 0.4, + }; +} diff --git a/surfsense_video/src/remotion/scenes/outro/OutroScene.tsx b/surfsense_video/src/remotion/scenes/outro/OutroScene.tsx new file mode 100644 index 000000000..4f2cd69d4 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/outro/OutroScene.tsx @@ -0,0 +1,308 @@ +/** + * OutroScene -- animated closing card with reveal + fade-out. + * 5 animation variants x 4 background styles x 4 decorative elements. + */ +import React from "react"; +import { + AbsoluteFill, + useCurrentFrame, + useVideoConfig, + interpolate, + spring, + Easing, +} from "remotion"; +import type { ThemeColors } from "../../theme"; +import type { OutroSceneInput } from "./types"; +import type { OutroVariant } from "./variant"; +import { TEXT_DELAY, FADEOUT_START, OUTRO_DURATION } from "./constants"; + +interface OutroSceneProps { + input: OutroSceneInput; + theme: ThemeColors; + variant: OutroVariant; +} + +function accentColor(hue: number): string { + return `hsl(${hue}, 70%, 65%)`; +} + +function accentAlpha(hue: number, a: string): string { + return `hsla(${hue}, 70%, 65%, ${a})`; +} + +export const OutroScene: React.FC = ({ + input, + theme, + variant, +}) => { + const frame = useCurrentFrame(); + const { fps, width, height } = useVideoConfig(); + const vmin = Math.min(width, height) / 100; + + const title = input.title ?? "Thank you"; + const accent = accentColor(variant.accentHue); + const textLocal = frame - TEXT_DELAY; + + const fadeOutProgress = interpolate( + frame, + [OUTRO_DURATION - FADEOUT_START, OUTRO_DURATION], + [1, 0], + { extrapolateLeft: "clamp", extrapolateRight: "clamp" }, + ); + + let textOpacity: number; + let textTransform: string; + + switch (variant.animation) { + case "fadeCenter": { + textOpacity = interpolate(textLocal, [0, 18], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + textTransform = ""; + break; + } + case "shrinkOut": { + const s = spring({ + frame: Math.max(0, textLocal), + fps, + config: { damping: 14, stiffness: 90, mass: 0.7 }, + }); + textOpacity = interpolate(textLocal, [0, 10], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + const scaleOut = interpolate( + frame, + [OUTRO_DURATION - FADEOUT_START, OUTRO_DURATION], + [1, 0.85], + { extrapolateLeft: "clamp", extrapolateRight: "clamp" }, + ); + textTransform = `scale(${s * scaleOut})`; + break; + } + case "slideUp": { + textOpacity = interpolate(textLocal, [0, 15], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + const y = interpolate(textLocal, [0, 20], [vmin * 5, 0], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + easing: Easing.out(Easing.ease), + }); + textTransform = `translateY(${y}px)`; + break; + } + case "dissolve": { + textOpacity = interpolate(textLocal, [0, 25], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + const blur = interpolate(textLocal, [0, 20], [vmin * 0.8, 0], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + textTransform = `blur(${blur}px)`; + break; + } + case "wipeOut": + default: { + textOpacity = interpolate(textLocal, [0, 12], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + const x = interpolate(textLocal, [0, 18], [-vmin * 6, 0], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + easing: Easing.out(Easing.cubic), + }); + textTransform = `translateX(${x}px)`; + break; + } + } + + const isBlur = variant.animation === "dissolve"; + const subtitleOpacity = interpolate(textLocal - 12, [0, 15], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + + const bgNode = (() => { + const p = interpolate(frame, [0, 50], [0, 1], { extrapolateRight: "clamp" }); + switch (variant.bgStyle) { + case "radialGlow": { + const r = 25 + p * 15; + return ( +
+ ); + } + case "gradientSweep": { + const angle = 180 + p * 60; + return ( +
+ ); + } + case "vignette": + return ( +
+ ); + case "minimal": + default: + return null; + } + })(); + + const decorNode = (() => { + const dp = interpolate(frame - 5, [0, 35], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + const eased = Easing.out(Easing.ease)(dp); + + switch (variant.decor) { + case "line": { + const w = eased * vmin * 20; + return ( +
+ ); + } + case "ring": { + const size = vmin * 16 * eased; + return ( +
+ ); + } + case "particles": { + return ( + <> + {Array.from({ length: 8 }, (_, i) => { + const angle = (i / 8) * Math.PI * 2; + const dist = vmin * 14 * eased; + const x = width / 2 + Math.cos(angle) * dist; + const y = height / 2 + Math.sin(angle) * dist; + const s = vmin * 0.25; + const delay = i * 2; + const o = interpolate(frame - delay, [0, 20], [0, 0.35], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + return ( +
+ ); + })} + + ); + } + case "none": + default: + return null; + } + })(); + + return ( + + {bgNode} + {decorNode} + +
+
+ {title} +
+ + {input.subtitle && ( +
+ {input.subtitle} +
+ )} +
+
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/outro/constants.ts b/surfsense_video/src/remotion/scenes/outro/constants.ts new file mode 100644 index 000000000..a9722195d --- /dev/null +++ b/surfsense_video/src/remotion/scenes/outro/constants.ts @@ -0,0 +1,10 @@ +/** Timing constants for outro scene. */ + +/** Total outro duration (frames). */ +export const OUTRO_DURATION = 75; + +/** Text reveal start (frames). */ +export const TEXT_DELAY = 8; + +/** Fade-out start before end (frames from the end). */ +export const FADEOUT_START = 20; diff --git a/surfsense_video/src/remotion/scenes/outro/demo.ts b/surfsense_video/src/remotion/scenes/outro/demo.ts new file mode 100644 index 000000000..abf9cd19a --- /dev/null +++ b/surfsense_video/src/remotion/scenes/outro/demo.ts @@ -0,0 +1,12 @@ +/** Sample data for outro scene preview. */ +import type { OutroSceneInput } from "./types"; + +export const DEMO_OUTRO: OutroSceneInput = { + type: "outro", + title: "Thank You", + subtitle: "Generated with SurfSense", +}; + +export const DEMO_OUTRO_MINIMAL: OutroSceneInput = { + type: "outro", +}; diff --git a/surfsense_video/src/remotion/scenes/outro/preview.tsx b/surfsense_video/src/remotion/scenes/outro/preview.tsx new file mode 100644 index 000000000..04a88e9c1 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/outro/preview.tsx @@ -0,0 +1,102 @@ +/** Remotion Studio compositions for outro scene. */ +import { Composition } from "remotion"; +import { OutroScene } from "./OutroScene"; +import { THEMES } from "../../theme"; +import type { OutroVariant } from "./variant"; +import { DEMO_OUTRO, DEMO_OUTRO_MINIMAL } from "./demo"; +import { OUTRO_DURATION } from "./constants"; + +const THEME = "dark" as const; +const FPS = 30; +const WIDTH = 1920; +const HEIGHT = 1080; + +const base: OutroVariant = { + animation: "fadeCenter", + bgStyle: "radialGlow", + decor: "line", + accentHue: 230, +}; + +export const outroPreviews = ( + <> + } + durationInFrames={OUTRO_DURATION} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + ( + + )} + durationInFrames={OUTRO_DURATION} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + ( + + )} + durationInFrames={OUTRO_DURATION} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + ( + + )} + durationInFrames={OUTRO_DURATION} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + ( + + )} + durationInFrames={OUTRO_DURATION} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + ( + + )} + durationInFrames={OUTRO_DURATION} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + +); diff --git a/surfsense_video/src/remotion/scenes/outro/types.ts b/surfsense_video/src/remotion/scenes/outro/types.ts new file mode 100644 index 000000000..e93000259 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/outro/types.ts @@ -0,0 +1,10 @@ +/** Zod schema and types for outro scene. */ +import { z } from "zod"; + +export const OutroSceneInput = z.object({ + type: z.literal("outro"), + title: z.string().optional(), + subtitle: z.string().optional(), +}); + +export type OutroSceneInput = z.infer; diff --git a/surfsense_video/src/remotion/scenes/outro/variant.ts b/surfsense_video/src/remotion/scenes/outro/variant.ts new file mode 100644 index 000000000..99a29914c --- /dev/null +++ b/surfsense_video/src/remotion/scenes/outro/variant.ts @@ -0,0 +1,32 @@ +/** Outro scene variant -- seed-deterministic visual style. */ +import { random } from "remotion"; + +export type OutroAnimation = "fadeCenter" | "shrinkOut" | "slideUp" | "dissolve" | "wipeOut"; +export type OutroBgStyle = "radialGlow" | "gradientSweep" | "vignette" | "minimal"; +export type OutroDecor = "line" | "ring" | "particles" | "none"; + +export interface OutroVariant { + animation: OutroAnimation; + bgStyle: OutroBgStyle; + decor: OutroDecor; + accentHue: number; +} + +export function deriveOutroVariant(seed: number): OutroVariant { + const s = (key: string) => random(`${seed}-${key}`); + const pick = (key: string, arr: T[]) => + arr[Math.floor(s(key) * arr.length)]; + + return { + animation: pick("anim", [ + "fadeCenter", "shrinkOut", "slideUp", "dissolve", "wipeOut", + ] as OutroAnimation[]), + bgStyle: pick("bg", [ + "radialGlow", "gradientSweep", "vignette", "minimal", + ] as OutroBgStyle[]), + decor: pick("decor", [ + "line", "ring", "particles", "none", + ] as OutroDecor[]), + accentHue: Math.floor(s("hue") * 360), + }; +} diff --git a/surfsense_video/src/remotion/scenes/relation/RelationScene.tsx b/surfsense_video/src/remotion/scenes/relation/RelationScene.tsx new file mode 100644 index 000000000..4d8111d83 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/relation/RelationScene.tsx @@ -0,0 +1,230 @@ +/** + * RelationScene — renders nodes + edges in circle, network, or dagre layout + * with staggered reveals, animated edge drawing, and camera paging. + */ +import React, { useMemo } from "react"; +import { AbsoluteFill, useVideoConfig, useCurrentFrame, Easing } from "remotion"; +import type { ThemeColors } from "../../theme"; +import type { RelationSceneInput, Waypoint } from "./types"; +import type { RelationVariant } from "./variant"; +import { NODE_STAGGER } from "./constants"; +import { RelationNodeComponent } from "./components/RelationNode"; +import { RelationEdge } from "./components/RelationEdge"; +import { getNodeDimensions, getMaxNodeSize } from "./components/nodeSize"; +import { computeRelationLayout, buildWaypoints } from "./layout"; + +/** Find the point on a rectangle's border closest to a target direction. */ +function rectEdgePoint( + cx: number, cy: number, + halfW: number, halfH: number, + tx: number, ty: number, +): { x: number; y: number } { + const dx = tx - cx; + const dy = ty - cy; + if (dx === 0 && dy === 0) return { x: cx, y: cy - halfH }; + + const absDx = Math.abs(dx); + const absDy = Math.abs(dy); + const slopeRatio = halfH / halfW; + + if (absDy / (absDx || 1) <= slopeRatio) { + const sx = dx > 0 ? 1 : -1; + return { x: cx + sx * halfW, y: cy + (dy / absDx) * halfW }; + } + const sy = dy > 0 ? 1 : -1; + return { x: cx + (dx / absDy) * halfH, y: cy + sy * halfH }; +} + +interface RelationSceneProps { + input: RelationSceneInput; + theme: ThemeColors; + variant: RelationVariant; +} + +function resolveCamera( + waypoints: Waypoint[], + frame: number, +): { cx: number; cy: number } { + let cam = { cx: waypoints[0].cx, cy: waypoints[0].cy }; + let cursor = 0; + + for (let w = 0; w < waypoints.length; w++) { + const wp = waypoints[w]; + if (frame < cursor + wp.holdFrames) { + cam = { cx: wp.cx, cy: wp.cy }; + break; + } + cursor += wp.holdFrames; + + if (wp.transitionAfter > 0 && w + 1 < waypoints.length) { + if (frame < cursor + wp.transitionAfter) { + const t = Easing.inOut(Easing.ease)( + (frame - cursor) / wp.transitionAfter, + ); + const next = waypoints[w + 1]; + cam = { + cx: wp.cx + (next.cx - wp.cx) * t, + cy: wp.cy + (next.cy - wp.cy) * t, + }; + break; + } + cursor += wp.transitionAfter; + } + + if (w === waypoints.length - 1) { + cam = { cx: wp.cx, cy: wp.cy }; + } + } + + return cam; +} + +export const RelationScene: React.FC = ({ + input, + theme, + variant, +}) => { + const { width, height } = useVideoConfig(); + const vmin = Math.min(width, height) / 100; + const frame = useCurrentFrame(); + + const titleOffset = input.title ? vmin * 12 : 0; + + const { nodes, waypoints, idToNode } = useMemo(() => { + const laid = computeRelationLayout(input, variant.layout, vmin); + const { maxW, maxH } = getMaxNodeSize(input.nodes, vmin); + const wps = buildWaypoints(laid, maxW, maxH, width, height, titleOffset); + + const idMap = new Map(); + for (const n of laid) idMap.set(n.data.id, n); + + return { nodes: laid, waypoints: wps, idToNode: idMap }; + }, [input, variant.layout, vmin, width, height, titleOffset]); + + const cam = resolveCamera(waypoints, frame); + const panX = -cam.cx; + const panY = -cam.cy; + + interface ResolvedEdge { + from: { x: number; y: number }; + to: { x: number; y: number }; + fromColor: string; + toColor: string; + label?: string; + enterFrame: number; + id: string; + } + + const resolvedEdges = useMemo(() => { + const result: ResolvedEdge[] = []; + input.edges.forEach((e, i) => { + const src = idToNode.get(e.from); + const tgt = idToNode.get(e.to); + if (!src || !tgt) return; + + const srcDims = getNodeDimensions(src.data, vmin); + const tgtDims = getNodeDimensions(tgt.data, vmin); + + result.push({ + from: rectEdgePoint(src.x, src.y, srcDims.halfW, srcDims.halfH, tgt.x, tgt.y), + to: rectEdgePoint(tgt.x, tgt.y, tgtDims.halfW, tgtDims.halfH, src.x, src.y), + fromColor: src.data.color ?? "#6c7dff", + toColor: tgt.data.color ?? "#6c7dff", + label: e.label, + enterFrame: Math.max(0, Math.max(src.index, tgt.index) * NODE_STAGGER), + id: `re${i}`, + }); + }); + return result; + }, [input.edges, idToNode, vmin]); + + return ( + + {input.title && ( +
+
+ {input.title} +
+ {input.subtitle && ( +
+ {input.subtitle} +
+ )} +
+ )} + +
+ {resolvedEdges.map((e) => ( + + ))} + + {nodes.map((n) => { + const enterF = n.index * NODE_STAGGER; + return ( +
+ +
+ ); + })} +
+
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/relation/components/RelationEdge.tsx b/surfsense_video/src/remotion/scenes/relation/components/RelationEdge.tsx new file mode 100644 index 000000000..2fe0c6b00 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/relation/components/RelationEdge.tsx @@ -0,0 +1,145 @@ +/** SVG edge between two relation nodes with animated draw-in. */ +import React from "react"; +import { useCurrentFrame, interpolate } from "remotion"; +import type { ThemeColors } from "../../../theme"; +import type { RelationEdgeStyle, RelationEdgeColorMode } from "../variant"; +import { EDGE_DRAW_DURATION } from "../constants"; + +interface RelationEdgeProps { + from: { x: number; y: number }; + to: { x: number; y: number }; + fromColor: string; + toColor: string; + label?: string; + enterFrame: number; + vmin: number; + theme: ThemeColors; + edgeStyle: RelationEdgeStyle; + edgeColorMode: RelationEdgeColorMode; + showArrow: boolean; + showLabel: boolean; + edgeId: string; +} + +export const RelationEdge: React.FC = ({ + from, + to, + fromColor, + toColor, + label, + enterFrame, + vmin, + theme, + edgeStyle, + edgeColorMode, + showArrow, + showLabel, + edgeId, +}) => { + const frame = useCurrentFrame(); + const localFrame = frame - enterFrame; + + const progress = interpolate( + localFrame, + [0, EDGE_DRAW_DURATION], + [0, 1], + { extrapolateLeft: "clamp", extrapolateRight: "clamp" }, + ); + + const opacity = interpolate( + localFrame, + [0, EDGE_DRAW_DURATION * 0.5], + [0, 0.7], + { extrapolateLeft: "clamp", extrapolateRight: "clamp" }, + ); + + if (progress <= 0) return null; + + const mx = (from.x + to.x) / 2; + const my = (from.y + to.y) / 2; + + const strokeW = vmin * 0.12; + const gradId = `edge-grad-${edgeId}`; + const strokeColor = + edgeColorMode === "gradient" ? `url(#${gradId})` : `${fromColor}50`; + + const headSize = vmin * 0.7; + const angle = Math.atan2(to.y - from.y, to.x - from.x); + + const pad = vmin * 2; + const minX = Math.min(from.x, to.x) - pad; + const minY = Math.min(from.y, to.y) - pad; + const maxX = Math.max(from.x, to.x) + pad; + const maxY = Math.max(from.y, to.y) + pad; + const svgW = maxX - minX; + const svgH = maxY - minY; + + return ( + + + {edgeColorMode === "gradient" && ( + + + + + )} + + + + + {showArrow && progress > 0.9 && ( + + )} + + {showLabel && label && progress > 0.6 && ( + + {label} + + )} + + ); +}; diff --git a/surfsense_video/src/remotion/scenes/relation/components/RelationNode.tsx b/surfsense_video/src/remotion/scenes/relation/components/RelationNode.tsx new file mode 100644 index 000000000..d8b1d1eb9 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/relation/components/RelationNode.tsx @@ -0,0 +1,125 @@ +/** Single relation node card with variant-driven styling. */ +import React from "react"; +import { useCurrentFrame, interpolate, spring, useVideoConfig } from "remotion"; +import type { ThemeColors } from "../../../theme"; +import type { RelationNode as RelationNodeData } from "../types"; +import type { RelationCardStyle } from "../variant"; +import { NODE_FADE_DURATION } from "../constants"; +import { getNodeDimensions } from "./nodeSize"; + +interface RelationNodeProps { + node: RelationNodeData; + enterFrame: number; + vmin: number; + cardStyle: RelationCardStyle; + theme: ThemeColors; +} + +function cardCSS( + style: RelationCardStyle, + color: string, + vmin: number, +): React.CSSProperties { + const bw = vmin * 0.14; + + switch (style) { + case "gradient": + return { + background: `linear-gradient(145deg, ${color}14, ${color}06)`, + border: `${bw}px solid ${color}35`, + boxShadow: `0 ${vmin * 0.5}px ${vmin * 2}px ${color}12`, + }; + case "glass": + return { + background: `${color}0a`, + backdropFilter: "blur(12px)", + border: `${bw}px solid ${color}20`, + boxShadow: `inset 0 ${vmin * 0.1}px ${vmin * 0.6}px ${color}15`, + }; + case "outline": + return { + background: "transparent", + border: `${vmin * 0.2}px solid ${color}60`, + }; + case "solid": + return { + background: `${color}18`, + border: `${bw}px solid ${color}30`, + }; + } +} + +export const RelationNodeComponent: React.FC = ({ + node, + enterFrame, + vmin, + cardStyle, + theme, +}) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + const localFrame = frame - enterFrame; + + const opacity = interpolate( + localFrame, + [0, NODE_FADE_DURATION], + [0, 1], + { extrapolateLeft: "clamp", extrapolateRight: "clamp" }, + ); + + const scale = spring({ + frame: Math.max(0, localFrame), + fps, + config: { damping: 14, stiffness: 120, mass: 0.5 }, + }); + + const color = node.color ?? "#6c7dff"; + const dims = getNodeDimensions(node, vmin); + const borderRadius = vmin * 1; + + return ( +
+
+ {node.label} +
+ {node.desc && ( +
+ {node.desc} +
+ )} +
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/relation/components/nodeSize.ts b/surfsense_video/src/remotion/scenes/relation/components/nodeSize.ts new file mode 100644 index 000000000..b28ad9f78 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/relation/components/nodeSize.ts @@ -0,0 +1,110 @@ +/** + * Relation node dimensions — width/height measured via canvas measureText + * so layout can compute positions before React rendering. + */ +import type { RelationNode } from "../types"; + +export interface NodeDimensions { + width: number; + height: number; + halfW: number; + halfH: number; + paddingX: number; + paddingY: number; + fontSize: number; + descFontSize: number; +} + +const LINE_HEIGHT = 1.3; +const GAP_FACTOR = 0.4; +const FONT_FAMILY = "Inter, system-ui, sans-serif"; + +let _ctx: CanvasRenderingContext2D | null = null; +function ctx(): CanvasRenderingContext2D { + if (!_ctx) _ctx = document.createElement("canvas").getContext("2d")!; + return _ctx; +} + +function measureLines( + text: string, + fontSize: number, + fontWeight: number | string, + availableWidth: number, +): number { + const c = ctx(); + c.font = `${fontWeight} ${fontSize}px ${FONT_FAMILY}`; + + const words = text.split(/\s+/).filter(Boolean); + if (words.length === 0) return 1; + + let lines = 1; + let lineWidth = 0; + const spaceW = c.measureText(" ").width; + + for (const word of words) { + const wordW = c.measureText(word).width; + if (lineWidth === 0) { + lineWidth = wordW; + } else if (lineWidth + spaceW + wordW <= availableWidth) { + lineWidth += spaceW + wordW; + } else { + lines++; + lineWidth = wordW; + } + } + + return lines; +} + +export function getNodeDimensions( + node: RelationNode, + vmin: number, +): NodeDimensions { + const paddingX = vmin * 2; + const paddingY = vmin * 1; + const fontSize = vmin * 1.6; + const descFontSize = vmin * 1.2; + const borderW = vmin * 0.14; + + const width = vmin * 18; + const minHeight = vmin * 6; + + const innerW = width - 2 * paddingX - 2 * borderW; + const labelLines = measureLines(node.label, fontSize, 600, innerW); + const labelH = labelLines * fontSize * LINE_HEIGHT; + + let contentH = labelH; + if (node.desc) { + const descLines = measureLines(node.desc, descFontSize, 400, innerW); + const descH = descLines * descFontSize * LINE_HEIGHT; + contentH += vmin * GAP_FACTOR + descH; + } + + const height = Math.max(minHeight, contentH + 2 * paddingY + 2 * borderW); + + return { + width, + height, + halfW: width / 2, + halfH: height / 2, + paddingX, + paddingY, + fontSize, + descFontSize, + }; +} + +/** Returns the largest node box across all nodes. */ +export function getMaxNodeSize( + nodes: RelationNode[], + vmin: number, +): { maxW: number; maxH: number } { + let maxW = 0; + let maxH = 0; + for (const node of nodes) { + const dims = getNodeDimensions(node, vmin); + maxW = Math.max(maxW, dims.width); + maxH = Math.max(maxH, dims.height); + } + return { maxW, maxH }; +} diff --git a/surfsense_video/src/remotion/scenes/relation/constants.ts b/surfsense_video/src/remotion/scenes/relation/constants.ts new file mode 100644 index 000000000..c93ab8c37 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/relation/constants.ts @@ -0,0 +1,16 @@ +/** Timing constants for relation scene animations. */ + +/** Frames each node staggers its entrance. */ +export const NODE_STAGGER = 8; + +/** Node fade-in duration (frames). */ +export const NODE_FADE_DURATION = 20; + +/** Edge draw animation duration (frames). */ +export const EDGE_DRAW_DURATION = 25; + +/** Camera hold per stop (frames). */ +export const STOP_HOLD = 100; + +/** Camera transition between stops (frames). */ +export const STOP_TRANSITION = 18; diff --git a/surfsense_video/src/remotion/scenes/relation/demo.ts b/surfsense_video/src/remotion/scenes/relation/demo.ts new file mode 100644 index 000000000..30d7fa182 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/relation/demo.ts @@ -0,0 +1,139 @@ +/** Sample relation data for Remotion Studio preview. */ +import type { RelationSceneInput } from "./types"; + +export const DEMO_RELATION: RelationSceneInput = { + type: "relation", + title: "System Architecture", + subtitle: "Service communication flow", + nodes: [ + { id: "api", label: "API Gateway", desc: "Load balancer & rate limiter", color: "#6c7dff" }, + { id: "auth", label: "Auth Service", desc: "OAuth 2.0 / JWT tokens", color: "#00c9a7" }, + { id: "users", label: "User Service", desc: "Profile management", color: "#ff6b6b" }, + { id: "orders", label: "Order Service", desc: "Order lifecycle", color: "#ffd93d" }, + { id: "payments", label: "Payment Service", desc: "Stripe integration", color: "#c084fc" }, + { id: "notify", label: "Notification", desc: "Email & push alerts", color: "#f97316" }, + { id: "db", label: "Database", desc: "PostgreSQL cluster", color: "#14b8a6" }, + ], + edges: [ + { from: "api", to: "auth", label: "validates" }, + { from: "api", to: "users", label: "routes" }, + { from: "api", to: "orders", label: "routes" }, + { from: "orders", to: "payments", label: "charges" }, + { from: "orders", to: "notify", label: "triggers" }, + { from: "users", to: "db", label: "reads/writes" }, + { from: "orders", to: "db", label: "reads/writes" }, + { from: "auth", to: "db", label: "checks" }, + ], +}; + +export const DEMO_RELATION_LARGE: RelationSceneInput = { + type: "relation", + title: "Microservices Mesh", + subtitle: "15 services with inter-dependencies", + nodes: [ + { id: "gateway", label: "API Gateway", desc: "Entry point", color: "#6c7dff" }, + { id: "auth", label: "Auth", desc: "Identity provider", color: "#00c9a7" }, + { id: "users", label: "Users", desc: "User profiles", color: "#ff6b6b" }, + { id: "products", label: "Products", desc: "Catalog service", color: "#ffd93d" }, + { id: "orders", label: "Orders", desc: "Order processing", color: "#c084fc" }, + { id: "payments", label: "Payments", desc: "Billing engine", color: "#f97316" }, + { id: "inventory", label: "Inventory", desc: "Stock tracking", color: "#14b8a6" }, + { id: "shipping", label: "Shipping", desc: "Logistics API", color: "#e879f9" }, + { id: "analytics", label: "Analytics", desc: "Event streaming", color: "#38bdf8" }, + { id: "search", label: "Search", desc: "Elasticsearch", color: "#a3e635" }, + { id: "cache", label: "Cache", desc: "Redis cluster", color: "#fb923c" }, + { id: "queue", label: "Message Queue", desc: "RabbitMQ", color: "#f472b6" }, + { id: "storage", label: "Object Storage", desc: "S3 compatible", color: "#2dd4bf" }, + { id: "logging", label: "Logging", desc: "ELK stack", color: "#818cf8" }, + { id: "config", label: "Config Service", desc: "Feature flags", color: "#fbbf24" }, + ], + edges: [ + { from: "gateway", to: "auth" }, + { from: "gateway", to: "users" }, + { from: "gateway", to: "products" }, + { from: "gateway", to: "orders" }, + { from: "orders", to: "payments" }, + { from: "orders", to: "inventory" }, + { from: "orders", to: "shipping" }, + { from: "orders", to: "queue" }, + { from: "products", to: "search" }, + { from: "products", to: "cache" }, + { from: "analytics", to: "queue" }, + { from: "shipping", to: "queue" }, + { from: "users", to: "storage" }, + { from: "auth", to: "cache" }, + { from: "logging", to: "storage" }, + { from: "config", to: "gateway" }, + { from: "config", to: "auth" }, + ], +}; + +export const DEMO_RELATION_XL: RelationSceneInput = { + type: "relation", + title: "Enterprise Platform", + subtitle: "25-service cloud-native architecture", + nodes: [ + { id: "lb", label: "Load Balancer", desc: "HAProxy / Nginx ingress", color: "#6c7dff" }, + { id: "gateway", label: "API Gateway", desc: "Rate limiting & routing", color: "#818cf8" }, + { id: "auth", label: "Auth Service", desc: "OAuth 2.0 / SAML / MFA", color: "#00c9a7" }, + { id: "iam", label: "IAM", desc: "Role-based access control", color: "#2dd4bf" }, + { id: "users", label: "User Service", desc: "Profile & preferences", color: "#ff6b6b" }, + { id: "teams", label: "Team Service", desc: "Org management", color: "#f87171" }, + { id: "products", label: "Product Catalog", desc: "Inventory listing", color: "#ffd93d" }, + { id: "pricing", label: "Pricing Engine", desc: "Dynamic pricing rules", color: "#fbbf24" }, + { id: "orders", label: "Order Service", desc: "Order lifecycle mgmt", color: "#c084fc" }, + { id: "cart", label: "Cart Service", desc: "Shopping cart state", color: "#a78bfa" }, + { id: "payments", label: "Payment Service", desc: "Stripe / PayPal", color: "#f97316" }, + { id: "billing", label: "Billing", desc: "Invoice generation", color: "#fb923c" }, + { id: "inventory", label: "Inventory", desc: "Warehouse stock sync", color: "#14b8a6" }, + { id: "shipping", label: "Shipping", desc: "FedEx / UPS / DHL", color: "#e879f9" }, + { id: "returns", label: "Returns", desc: "RMA processing", color: "#d946ef" }, + { id: "notifications", label: "Notifications", desc: "Email / SMS / Push", color: "#f472b6" }, + { id: "search", label: "Search", desc: "Full-text search index", color: "#a3e635" }, + { id: "recommendations", label: "Recommendations", desc: "ML-based suggestions", color: "#84cc16" }, + { id: "analytics", label: "Analytics", desc: "Real-time dashboards", color: "#38bdf8" }, + { id: "queue", label: "Message Queue", desc: "Kafka / RabbitMQ", color: "#f472b6" }, + { id: "cache", label: "Cache Layer", desc: "Redis + CDN edge", color: "#fb923c" }, + { id: "db", label: "Primary DB", desc: "PostgreSQL cluster", color: "#64748b" }, + { id: "docdb", label: "Document DB", desc: "MongoDB replica set", color: "#94a3b8" }, + { id: "storage", label: "Object Storage", desc: "S3 / MinIO buckets", color: "#2dd4bf" }, + { id: "monitoring", label: "Monitoring", desc: "Prometheus + Grafana", color: "#818cf8" }, + ], + edges: [ + { from: "lb", to: "gateway" }, + { from: "gateway", to: "auth" }, + { from: "gateway", to: "users" }, + { from: "gateway", to: "products" }, + { from: "gateway", to: "orders" }, + { from: "gateway", to: "cart" }, + { from: "gateway", to: "search" }, + { from: "auth", to: "iam" }, + { from: "auth", to: "cache" }, + { from: "iam", to: "db" }, + { from: "users", to: "teams" }, + { from: "users", to: "db" }, + { from: "products", to: "pricing" }, + { from: "products", to: "search" }, + { from: "products", to: "recommendations" }, + { from: "products", to: "docdb" }, + { from: "cart", to: "orders" }, + { from: "orders", to: "payments" }, + { from: "orders", to: "inventory" }, + { from: "orders", to: "shipping" }, + { from: "orders", to: "notifications" }, + { from: "orders", to: "queue" }, + { from: "payments", to: "billing" }, + { from: "payments", to: "db" }, + { from: "shipping", to: "returns" }, + { from: "shipping", to: "notifications" }, + { from: "billing", to: "notifications" }, + { from: "inventory", to: "db" }, + { from: "analytics", to: "queue" }, + { from: "analytics", to: "docdb" }, + { from: "recommendations", to: "analytics" }, + { from: "notifications", to: "queue" }, + { from: "monitoring", to: "queue" }, + { from: "search", to: "cache" }, + { from: "storage", to: "monitoring" }, + ], +}; diff --git a/surfsense_video/src/remotion/scenes/relation/layout.ts b/surfsense_video/src/remotion/scenes/relation/layout.ts new file mode 100644 index 000000000..c98182176 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/relation/layout.ts @@ -0,0 +1,298 @@ +/** + * Relation scene layout — circle, network (force), dagre (layered). + * Computes node positions, camera waypoints, and scene duration. + */ +import { + forceSimulation, + forceLink, + forceManyBody, + forceCenter, + forceCollide, + type SimulationNodeDatum, + type SimulationLinkDatum, +} from "d3-force"; +import type { RelationSceneInput, LayoutNode, Waypoint } from "./types"; +import type { RelationLayout } from "./variant"; +import { getMaxNodeSize, getNodeDimensions } from "./components/nodeSize"; +import { + NODE_STAGGER, + NODE_FADE_DURATION, + STOP_HOLD, + STOP_TRANSITION, +} from "./constants"; + +/* ─── Circle layout ─── */ + +function layoutCircle( + input: RelationSceneInput, + vmin: number, +): LayoutNode[] { + const { maxW, maxH } = getMaxNodeSize(input.nodes, vmin); + const size = Math.max(maxW, maxH); + const count = input.nodes.length; + + if (count === 1) { + return [{ data: input.nodes[0], x: 0, y: 0, index: 0 }]; + } + + const minGap = vmin * 8; + const radius = Math.max( + size * 2, + (count * (size + minGap)) / (2 * Math.PI), + ); + + return input.nodes.map((node, i) => { + const angle = (2 * Math.PI * i) / count - Math.PI / 2; + return { + data: node, + x: radius * Math.cos(angle), + y: radius * Math.sin(angle), + index: i, + }; + }); +} + +/* ─── Network (force) layout ─── */ + +interface ForceNode extends SimulationNodeDatum { + id: string; + _index: number; +} + +function layoutNetwork( + input: RelationSceneInput, + vmin: number, +): LayoutNode[] { + const { maxW, maxH } = getMaxNodeSize(input.nodes, vmin); + const nodeSize = Math.max(maxW, maxH); + const spacing = nodeSize + vmin * 10; + const idSet = new Set(input.nodes.map((n) => n.id)); + + const forceNodes: ForceNode[] = input.nodes.map((n, i) => ({ + id: n.id, + _index: i, + })); + + const forceLinks: SimulationLinkDatum[] = input.edges + .filter((e) => idSet.has(e.from) && idSet.has(e.to)) + .map((e) => ({ source: e.from, target: e.to })); + + const sim = forceSimulation(forceNodes) + .force( + "link", + forceLink>(forceLinks) + .id((d) => d.id) + .distance(spacing) + .strength(0.6), + ) + .force("charge", forceManyBody().strength(-spacing * 1.5)) + .force("center", forceCenter(0, 0)) + .force("collision", forceCollide(nodeSize / 2 + vmin * 4)); + + for (let i = 0; i < 300; i++) sim.tick(); + sim.stop(); + + return forceNodes.map((fn) => ({ + data: input.nodes[fn._index], + x: fn.x ?? 0, + y: fn.y ?? 0, + index: fn._index, + })); +} + +/* ─── Dagre (simplified layered) layout ─── */ + +function layoutDagre( + input: RelationSceneInput, + vmin: number, + isHoriz: boolean, +): LayoutNode[] { + const idToIdx = new Map(input.nodes.map((n, i) => [n.id, i])); + const n = input.nodes.length; + + const adj = new Map(); + const inDeg = new Array(n).fill(0); + for (let i = 0; i < n; i++) adj.set(i, []); + + for (const e of input.edges) { + const si = idToIdx.get(e.from); + const ti = idToIdx.get(e.to); + if (si != null && ti != null && si !== ti) { + adj.get(si)!.push(ti); + inDeg[ti]++; + } + } + + const rank = new Array(n).fill(0); + const queue: number[] = []; + for (let i = 0; i < n; i++) { + if (inDeg[i] === 0) queue.push(i); + } + if (queue.length === 0) queue.push(0); + + const visited = new Set(); + while (queue.length > 0) { + const cur = queue.shift()!; + if (visited.has(cur)) continue; + visited.add(cur); + for (const next of adj.get(cur) ?? []) { + rank[next] = Math.max(rank[next], rank[cur] + 1); + if (!visited.has(next)) queue.push(next); + } + } + for (let i = 0; i < n; i++) { + if (!visited.has(i)) { + visited.add(i); + rank[i] = 0; + } + } + + const byRank = new Map(); + for (let i = 0; i < n; i++) { + const r = rank[i]; + if (!byRank.has(r)) byRank.set(r, []); + byRank.get(r)!.push(i); + } + + const dims = input.nodes.map((nd) => getNodeDimensions(nd, vmin)); + const gapCross = vmin * 8; + const gapBetweenRanks = vmin * 10; + + const maxMainSize = Math.max( + ...dims.map((d) => (isHoriz ? d.width : d.height)), + ); + const rankStep = maxMainSize + gapBetweenRanks; + + const nodes: LayoutNode[] = []; + + const rankEntries = Array.from(byRank.entries()); + for (const [r, indices] of rankEntries) { + const totalCross = indices.reduce( + (sum: number, i: number) => sum + (isHoriz ? dims[i].height : dims[i].width) + gapCross, + -gapCross, + ); + let cursor = -totalCross / 2; + + for (const i of indices) { + const d = dims[i]; + const crossSize = isHoriz ? d.height : d.width; + const crossPos = cursor + crossSize / 2; + cursor += crossSize + gapCross; + + const mainPos = r * rankStep; + nodes.push({ + data: input.nodes[i], + x: isHoriz ? mainPos : crossPos, + y: isHoriz ? crossPos : mainPos, + index: i, + }); + } + } + + return nodes; +} + +/* ─── Camera waypoints ─── */ + +export function buildWaypoints( + nodes: LayoutNode[], + nodeW: number, + nodeH: number, + viewW: number, + viewH: number, + titleOffset: number = 0, +): Waypoint[] { + if (nodes.length === 0) { + return [{ cx: 0, cy: -titleOffset / 2, holdFrames: STOP_HOLD, transitionAfter: 0 }]; + } + + const pad = Math.max(nodeW, nodeH) * 0.6; + const minX = Math.min(...nodes.map((n) => n.x)) - pad; + const maxX = Math.max(...nodes.map((n) => n.x)) + pad; + const minY = Math.min(...nodes.map((n) => n.y)) - pad; + const maxY = Math.max(...nodes.map((n) => n.y)) + pad; + + const spanW = maxX - minX; + const spanH = maxY - minY; + const contentCX = (minX + maxX) / 2; + const contentCY = (minY + maxY) / 2; + const effectiveH = viewH - titleOffset; + + const centeredCY = contentCY - titleOffset / 2; + + if (spanW <= viewW * 0.85 && spanH <= effectiveH * 0.85) { + return [{ cx: contentCX, cy: centeredCY, holdFrames: STOP_HOLD, transitionAfter: 0 }]; + } + + const colsNeeded = spanW > viewW ? Math.ceil(spanW / (viewW * 0.75)) : 1; + const rowsNeeded = spanH > effectiveH ? Math.ceil(spanH / (effectiveH * 0.75)) : 1; + + const stops: Waypoint[] = []; + + for (let r = 0; r < rowsNeeded; r++) { + for (let c = 0; c < colsNeeded; c++) { + const tx = colsNeeded === 1 + ? contentCX + : minX + viewW / 2 + (c / (colsNeeded - 1)) * (spanW - viewW); + + let ty: number; + if (rowsNeeded === 1) { + ty = centeredCY; + } else { + const firstCy = minY + viewH / 2 - titleOffset; + const lastCy = maxY - viewH / 2; + ty = firstCy + (r / (rowsNeeded - 1)) * (lastCy - firstCy); + } + + stops.push({ + cx: tx, + cy: ty, + holdFrames: STOP_HOLD, + transitionAfter: STOP_TRANSITION, + }); + } + } + + if (stops.length > 0) stops[stops.length - 1].transitionAfter = 0; + return stops; +} + +/* ─── Public API ─── */ + +export function computeRelationLayout( + input: RelationSceneInput, + layout: RelationLayout, + vmin: number, +): LayoutNode[] { + switch (layout) { + case "circle": + return layoutCircle(input, vmin); + case "network": + return layoutNetwork(input, vmin); + case "dagre-tb": + return layoutDagre(input, vmin, false); + case "dagre-lr": + return layoutDagre(input, vmin, true); + } +} + +export function waypointsDuration(waypoints: Waypoint[]): number { + let total = 0; + for (const wp of waypoints) total += wp.holdFrames + wp.transitionAfter; + return Math.max(1, total); +} + +export function relationSceneDuration( + input: RelationSceneInput, + layout: RelationLayout, + viewW: number, + viewH: number, +): number { + const vmin = Math.min(viewW, viewH) / 100; + const titleOffset = input.title ? vmin * 12 : 0; + const nodes = computeRelationLayout(input, layout, vmin); + const { maxW, maxH } = getMaxNodeSize(input.nodes, vmin); + const wps = buildWaypoints(nodes, maxW, maxH, viewW, viewH, titleOffset); + const animPhase = (input.nodes.length - 1) * NODE_STAGGER + NODE_FADE_DURATION; + return animPhase + waypointsDuration(wps); +} diff --git a/surfsense_video/src/remotion/scenes/relation/preview.tsx b/surfsense_video/src/remotion/scenes/relation/preview.tsx new file mode 100644 index 000000000..f5dedbe19 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/relation/preview.tsx @@ -0,0 +1,108 @@ +/** Remotion Studio compositions for relation scene. */ +import React from "react"; +import { Composition } from "remotion"; +import { RelationScene } from "./RelationScene"; +import { THEMES } from "../../theme"; +import type { RelationVariant, RelationLayout } from "./variant"; +import type { RelationSceneInput } from "./types"; +import { DEMO_RELATION, DEMO_RELATION_LARGE, DEMO_RELATION_XL } from "./demo"; +import { relationSceneDuration } from "./layout"; + +const THEME = "dark" as const; +const FPS = 30; +const WIDTH = 1920; +const HEIGHT = 1080; + +const RelationPreview: React.FC<{ + variant: RelationVariant; + data?: RelationSceneInput; +}> = ({ variant, data = DEMO_RELATION }) => { + const theme = THEMES[THEME]; + return ; +}; + +const base: RelationVariant = { + layout: "network", + cardStyle: "gradient", + edgeStyle: "solid", + edgeColorMode: "gradient", + showEdgeLabels: true, + showArrows: true, +}; + +const layouts: RelationLayout[] = ["circle", "network", "dagre-tb", "dagre-lr"]; + +function dur(data: RelationSceneInput, layout: RelationLayout) { + return relationSceneDuration(data, layout, WIDTH, HEIGHT); +} + +export const relationPreviews = ( + <> + {layouts.map((layout) => { + const v: RelationVariant = { ...base, layout }; + return ( + } + durationInFrames={dur(DEMO_RELATION, layout)} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + ); + })} + ( + + )} + durationInFrames={dur(DEMO_RELATION, "network")} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + ( + + )} + durationInFrames={dur(DEMO_RELATION, "dagre-tb")} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + {/* Large dataset — camera paging */} + {layouts.map((layout) => { + const v: RelationVariant = { ...base, layout }; + return ( + } + durationInFrames={dur(DEMO_RELATION_LARGE, layout)} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + ); + })} + {/* XL dataset — heavy camera paging on both axes */} + {layouts.map((layout) => { + const v: RelationVariant = { ...base, layout }; + return ( + } + durationInFrames={dur(DEMO_RELATION_XL, layout)} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + ); + })} + +); diff --git a/surfsense_video/src/remotion/scenes/relation/types.ts b/surfsense_video/src/remotion/scenes/relation/types.ts new file mode 100644 index 000000000..ab16c4b4b --- /dev/null +++ b/surfsense_video/src/remotion/scenes/relation/types.ts @@ -0,0 +1,45 @@ +/** Zod schemas and inferred types for relation scene data. */ +import { z } from "zod"; + +export const RelationNodeSchema = z.object({ + id: z.string(), + label: z.string(), + desc: z.string().optional(), + color: z.string().optional(), +}); + +export type RelationNode = z.infer; + +export const RelationEdgeSchema = z.object({ + from: z.string(), + to: z.string(), + label: z.string().optional(), +}); + +export type RelationEdge = z.infer; + +export const RelationSceneInput = z.object({ + type: z.literal("relation"), + title: z.string().optional(), + subtitle: z.string().optional(), + nodes: z.array(RelationNodeSchema).min(1), + edges: z.array(RelationEdgeSchema).default([]), +}); + +export type RelationSceneInput = z.infer; + +/** Node with computed layout position. */ +export interface LayoutNode { + data: RelationNode; + x: number; + y: number; + index: number; +} + +/** Camera stop. */ +export interface Waypoint { + cx: number; + cy: number; + holdFrames: number; + transitionAfter: number; +} diff --git a/surfsense_video/src/remotion/scenes/relation/variant.ts b/surfsense_video/src/remotion/scenes/relation/variant.ts new file mode 100644 index 000000000..209ecb2ec --- /dev/null +++ b/surfsense_video/src/remotion/scenes/relation/variant.ts @@ -0,0 +1,35 @@ +/** Relation scene variant — seed-deterministic visual style. */ +import { random } from "remotion"; + +export type RelationLayout = "circle" | "network" | "dagre-tb" | "dagre-lr"; +export type RelationCardStyle = "gradient" | "glass" | "outline" | "solid"; +export type RelationEdgeStyle = "solid" | "dashed"; +export type RelationEdgeColorMode = "solid" | "gradient"; + +export interface RelationVariant { + layout: RelationLayout; + cardStyle: RelationCardStyle; + edgeStyle: RelationEdgeStyle; + edgeColorMode: RelationEdgeColorMode; + showEdgeLabels: boolean; + showArrows: boolean; +} + +export function deriveRelationVariant(seed: number): RelationVariant { + const s = (key: string) => random(`${seed}-${key}`); + const pick = (key: string, arr: T[]) => + arr[Math.floor(s(key) * arr.length)]; + + return { + layout: pick("layout", [ + "circle", "network", "dagre-tb", "dagre-lr", + ] as RelationLayout[]), + cardStyle: pick("cardStyle", [ + "gradient", "glass", "outline", "solid", + ] as RelationCardStyle[]), + edgeStyle: pick("edgeStyle", ["solid", "dashed"] as RelationEdgeStyle[]), + edgeColorMode: pick("edgeColor", ["solid", "gradient"] as RelationEdgeColorMode[]), + showEdgeLabels: s("edgeLabels") > 0.4, + showArrows: s("arrows") > 0.3, + }; +} diff --git a/surfsense_video/src/remotion/scenes/sequence/SequenceScene.tsx b/surfsense_video/src/remotion/scenes/sequence/SequenceScene.tsx new file mode 100644 index 000000000..ca0df7f43 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/sequence/SequenceScene.tsx @@ -0,0 +1,168 @@ +/** + * SequenceScene — items positioned in steps, timeline, snake, or ascending layout + * with directional arrow connectors, snap-hold camera, and staggered reveal. + */ +import React, { useMemo } from "react"; +import { AbsoluteFill, useVideoConfig, useCurrentFrame, Easing } from "remotion"; +import type { ThemeColors } from "../../theme"; +import type { SequenceSceneInput, Waypoint } from "./types"; +import type { SequenceVariant } from "./variant"; +import { ITEM_STAGGER } from "./constants"; +import { SequenceItemCard } from "./components/SequenceItem"; +import { Arrow } from "./components/Arrow"; +import { positionItems, buildConnectors, buildWaypoints, getLayoutParams } from "./layout"; + +interface SequenceSceneProps { + input: SequenceSceneInput; + theme: ThemeColors; + variant: SequenceVariant; +} + +function resolveCamera(waypoints: Waypoint[], frame: number): { cx: number; cy: number } { + let cam = { cx: waypoints[0].cx, cy: waypoints[0].cy }; + let cursor = 0; + + for (let w = 0; w < waypoints.length; w++) { + const wp = waypoints[w]; + if (frame < cursor + wp.holdFrames) { + cam = { cx: wp.cx, cy: wp.cy }; + break; + } + cursor += wp.holdFrames; + + if (wp.transitionAfter > 0 && w + 1 < waypoints.length) { + if (frame < cursor + wp.transitionAfter) { + const t = Easing.inOut(Easing.ease)( + (frame - cursor) / wp.transitionAfter, + ); + const next = waypoints[w + 1]; + cam = { + cx: wp.cx + (next.cx - wp.cx) * t, + cy: wp.cy + (next.cy - wp.cy) * t, + }; + break; + } + cursor += wp.transitionAfter; + } + + if (w === waypoints.length - 1) { + cam = { cx: wp.cx, cy: wp.cy }; + } + } + + return cam; +} + +export const SequenceScene: React.FC = ({ + input, + theme, + variant, +}) => { + const { width, height } = useVideoConfig(); + const vmin = Math.min(width, height) / 100; + const frame = useCurrentFrame(); + + const showBadge = variant.showStepNumber; + + const { items, connectors, waypoints } = useMemo(() => { + const p = getLayoutParams(vmin); + const positioned = positionItems(input.items, variant.layout, p, vmin, showBadge); + const conns = buildConnectors(positioned, variant.layout, vmin); + const wps = buildWaypoints(positioned, variant.layout, width, height); + return { items: positioned, connectors: conns, waypoints: wps }; + }, [input.items, variant.layout, vmin, width, height, showBadge]); + + const cam = resolveCamera(waypoints, frame); + + return ( + + {input.title && ( +
+
+ {input.title} +
+ {input.subtitle && ( +
+ {input.subtitle} +
+ )} +
+ )} + +
+ {connectors.map((conn) => { + const enterF = (conn.index + 1) * ITEM_STAGGER; + const fromItem = items[conn.index]; + const color = fromItem.data.color ?? "#6c7dff"; + return ( + + ); + })} + {items.map((it) => { + const enterF = it.index * ITEM_STAGGER; + return ( +
+ +
+ ); + })} +
+
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/sequence/components/Arrow.tsx b/surfsense_video/src/remotion/scenes/sequence/components/Arrow.tsx new file mode 100644 index 000000000..2c2e80a48 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/sequence/components/Arrow.tsx @@ -0,0 +1,136 @@ +/** Directional arrow connector between sequence steps. */ +import React from "react"; +import { useCurrentFrame, interpolate } from "remotion"; +import type { SequenceVariant } from "../variant"; +import { ARROW_FADE_DURATION } from "../constants"; + +interface ArrowProps { + fromX: number; + fromY: number; + toX: number; + toY: number; + curvePath?: string; + enterFrame: number; + vmin: number; + color: string; + variant: SequenceVariant; +} + +export const Arrow: React.FC = ({ + fromX, + fromY, + toX, + toY, + curvePath, + enterFrame, + vmin, + color, + variant, +}) => { + const frame = useCurrentFrame(); + const localFrame = frame - enterFrame; + + const opacity = interpolate(localFrame, [0, ARROW_FADE_DURATION], [0, 0.6], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + + const headSize = vmin * 1.5; + const strokeW = vmin * 0.25; + const dashArr = variant.arrowStyle === "dashed" + ? `${vmin * 0.8} ${vmin * 0.5}` + : "none"; + + const allX = [fromX, toX]; + const allY = [fromY, toY]; + + if (curvePath) { + const nums = curvePath.match(/-?\d+(\.\d+)?/g)?.map(Number) ?? []; + for (let n = 0; n < nums.length; n += 2) { + if (n + 1 < nums.length) { + allX.push(nums[n]); + allY.push(nums[n + 1]); + } + } + } + + const pad = headSize * 2; + const minX = Math.min(...allX) - pad; + const minY = Math.min(...allY) - pad; + const maxX = Math.max(...allX) + pad; + const maxY = Math.max(...allY) + pad; + const svgW = maxX - minX; + const svgH = maxY - minY; + + const ox = -minX; + const oy = -minY; + + let pathD: string; + let tipDx: number; + let tipDy: number; + + if (curvePath) { + pathD = curvePath.replace( + /(-?\d+(\.\d+)?)/g, + (match, _p1, _p2, offset) => { + const before = curvePath!.substring(0, offset); + const numsBefore = before.match(/-?\d+(\.\d+)?/g) ?? []; + const idx = numsBefore.length; + if (idx % 2 === 0) return String(Number(match) + ox); + return String(Number(match) + oy); + }, + ); + const controlNums = curvePath.match(/-?\d+(\.\d+)?/g)?.map(Number) ?? []; + const n = controlNums.length; + if (n >= 4) { + tipDx = toX - controlNums[n - 4]; + tipDy = toY - controlNums[n - 3]; + } else { + tipDx = toX - fromX; + tipDy = toY - fromY; + } + } else { + pathD = `M ${fromX + ox} ${fromY + oy} L ${toX + ox} ${toY + oy}`; + tipDx = toX - fromX; + tipDy = toY - fromY; + } + + const tipLen = Math.sqrt(tipDx * tipDx + tipDy * tipDy); + if (tipLen < 0.5) return null; + + const ux = tipDx / tipLen; + const uy = tipDy / tipLen; + const tx = toX + ox; + const ty = toY + oy; + const bx = tx - ux * headSize; + const by = ty - uy * headSize; + const px = -uy * headSize * 0.5; + const py = ux * headSize * 0.5; + + return ( + + + + + ); +}; diff --git a/surfsense_video/src/remotion/scenes/sequence/components/SequenceItem.tsx b/surfsense_video/src/remotion/scenes/sequence/components/SequenceItem.tsx new file mode 100644 index 000000000..5321b4bf4 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/sequence/components/SequenceItem.tsx @@ -0,0 +1,206 @@ +/** Single sequence step — card with style determined by variant.cardStyle. */ +import React from "react"; +import { useCurrentFrame, interpolate, spring, useVideoConfig } from "remotion"; +import type { ThemeColors } from "../../../theme"; +import type { SequenceItem as SequenceItemData } from "../types"; +import type { SequenceVariant, SequenceCardStyle } from "../variant"; +import { ITEM_FADE_DURATION } from "../constants"; + +interface SequenceItemProps { + item: SequenceItemData; + index: number; + enterFrame: number; + vmin: number; + variant: SequenceVariant; + theme: ThemeColors; + cardWidth: number; + cardHeight: number; +} + +function seqCardCSS( + style: SequenceCardStyle, + color: string, + vmin: number, +): { card: React.CSSProperties; showTopBar: boolean } { + const bw = vmin * 0.14; + + switch (style) { + case "top-bar": + return { + showTopBar: true, + card: { + background: `linear-gradient(90deg, ${color}18, ${color}08)`, + border: `${bw}px solid ${color}15`, + boxShadow: `0 0 ${vmin * 3}px ${color}0c`, + }, + }; + case "glow": + return { + showTopBar: false, + card: { + background: `${color}10`, + border: `${bw}px solid ${color}30`, + boxShadow: `0 0 ${vmin * 4}px ${color}25, 0 0 ${vmin * 1.5}px ${color}15`, + }, + }; + case "bordered": + return { + showTopBar: false, + card: { + background: `linear-gradient(135deg, ${color}0c, ${color}04)`, + border: `${vmin * 0.22}px solid ${color}88`, + boxShadow: `0 ${vmin * 0.3}px ${vmin * 1.5}px rgba(0,0,0,0.15)`, + }, + }; + case "minimal": + return { + showTopBar: false, + card: { + background: `${color}08`, + border: `${bw}px solid ${color}12`, + boxShadow: "none", + }, + }; + } +} + +function seqBadgeCSS( + style: SequenceCardStyle, + color: string, + vmin: number, +): React.CSSProperties { + switch (style) { + case "top-bar": + return { background: "transparent", border: `${vmin * 0.25}px solid ${color}`, color }; + case "glow": + return { background: color, color: "#fff", boxShadow: `0 0 ${vmin * 2}px ${color}40` }; + case "bordered": + return { background: `${color}30`, color, border: `${vmin * 0.15}px solid ${color}66` }; + case "minimal": + return { background: "transparent", color: `${color}aa`, fontWeight: 600 }; + } +} + +export const SequenceItemCard: React.FC = ({ + item, + index, + enterFrame, + vmin, + variant, + theme, + cardWidth, + cardHeight, +}) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + const color = item.color ?? "#6c7dff"; + const localFrame = frame - enterFrame; + + const opacity = interpolate(localFrame, [0, ITEM_FADE_DURATION], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + + const scale = spring({ + frame: Math.max(0, localFrame), + fps, + config: { damping: 14, stiffness: 120, mass: 0.8 }, + }); + + const borderRadius = variant.itemShape === "pill" ? 999 : vmin * 1.2; + const paddingX = vmin * 2; + const paddingY = vmin * 1.5; + const { card: styleCSS, showTopBar } = seqCardCSS(variant.cardStyle, color, vmin); + const badgeCSS = seqBadgeCSS(variant.cardStyle, color, vmin); + const topBarH = vmin * 0.35; + + return ( +
+ {showTopBar && ( +
+ )} + {variant.showStepNumber && ( +
+ {String(index + 1).padStart(2, "0")} +
+ )} + +
+ + {item.label} + + {item.desc && ( + + {item.desc} + + )} +
+
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/sequence/components/itemSize.ts b/surfsense_video/src/remotion/scenes/sequence/components/itemSize.ts new file mode 100644 index 000000000..e182096dd --- /dev/null +++ b/surfsense_video/src/remotion/scenes/sequence/components/itemSize.ts @@ -0,0 +1,106 @@ +/** + * Measure sequence item dimensions using canvas.measureText. + * Same approach as list/itemSize.ts — deterministic text measurement + * before layout so positioning is based on actual content size. + */ +import type { SequenceItem } from "../types"; + +export interface ItemDimensions { + width: number; + height: number; + paddingX: number; + paddingY: number; + labelFontSize: number; + descFontSize: number; + badgeSize: number; +} + +const LINE_HEIGHT_LABEL = 1.3; +const LINE_HEIGHT_DESC = 1.4; +const FONT_FAMILY = "Inter, system-ui, sans-serif"; +const BORDER_FACTOR = 0.14; + +let _ctx: CanvasRenderingContext2D | null = null; +function ctx(): CanvasRenderingContext2D { + if (!_ctx) { + _ctx = document.createElement("canvas").getContext("2d")!; + } + return _ctx; +} + +function measureLines( + text: string, + fontSize: number, + fontWeight: number | string, + availableWidth: number, +): number { + const c = ctx(); + c.font = `${fontWeight} ${fontSize}px ${FONT_FAMILY}`; + + const words = text.split(/\s+/).filter(Boolean); + if (words.length === 0) return 1; + + let lines = 1; + let lineWidth = 0; + const spaceW = c.measureText(" ").width; + + for (const word of words) { + const wordW = c.measureText(word).width; + if (lineWidth === 0) { + lineWidth = wordW; + } else if (lineWidth + spaceW + wordW <= availableWidth) { + lineWidth += spaceW + wordW; + } else { + lines++; + lineWidth = wordW; + } + } + + return lines; +} + +export function measureItemDimensions( + item: SequenceItem, + vmin: number, + cardW: number, + showBadge: boolean, +): ItemDimensions { + const paddingX = vmin * 2; + const paddingY = vmin * 1.5; + const borderW = vmin * BORDER_FACTOR; + const gap = vmin * 1.5; + const textGap = vmin * 0.3; + + const labelFontSize = vmin * 1.8; + const descFontSize = vmin * 1.2; + const badgeSize = vmin * 5; + + const badgeSpace = showBadge ? badgeSize + gap : 0; + const textW = cardW - 2 * paddingX - 2 * borderW - badgeSpace; + + const labelLines = measureLines(item.label, labelFontSize, 600, textW); + const labelH = labelLines * labelFontSize * LINE_HEIGHT_LABEL; + + let contentH = labelH; + if (item.desc) { + const descLines = measureLines(item.desc, descFontSize, 400, textW); + const descH = descLines * descFontSize * LINE_HEIGHT_DESC; + contentH += textGap + descH; + } + + const minHeight = Math.max( + showBadge ? badgeSize + 2 * paddingY : vmin * 5, + vmin * 6, + ); + const height = Math.max(minHeight, contentH + 2 * paddingY + 2 * borderW); + + return { + width: cardW, + height, + paddingX, + paddingY, + labelFontSize, + descFontSize, + badgeSize, + }; +} diff --git a/surfsense_video/src/remotion/scenes/sequence/constants.ts b/surfsense_video/src/remotion/scenes/sequence/constants.ts new file mode 100644 index 000000000..b895bcd63 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/sequence/constants.ts @@ -0,0 +1,14 @@ +/** Hold duration on each camera stop (frames @30fps ≈ 5s). */ +export const STOP_HOLD = 150; + +/** Fast snap transition between stops (frames). */ +export const STOP_TRANSITION = 20; + +/** Stagger delay per item within a visible group (frames). */ +export const ITEM_STAGGER = 12; + +/** Item fade-in duration (frames). */ +export const ITEM_FADE_DURATION = 20; + +/** Arrow fade-in duration (frames). */ +export const ARROW_FADE_DURATION = 15; diff --git a/surfsense_video/src/remotion/scenes/sequence/demo.ts b/surfsense_video/src/remotion/scenes/sequence/demo.ts new file mode 100644 index 000000000..6ff16c300 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/sequence/demo.ts @@ -0,0 +1,20 @@ +/** Sample sequence data for Remotion Studio preview. */ +import type { SequenceSceneInput } from "./types"; + +export const DEMO_SEQUENCE: SequenceSceneInput = { + type: "sequence", + title: "Product Launch Pipeline", + subtitle: "From ideation to market release", + items: [ + { label: "Research & Discovery", color: "#6c7dff", desc: "Identify market gaps and validate problem space with user interviews and competitive analysis" }, + { label: "Concept Design", color: "#00c9a7", desc: "Create wireframes and interactive prototypes exploring multiple solution approaches" }, + { label: "Technical Planning", color: "#ff6b6b", desc: "Define architecture, select technology stack, and estimate development timeline" }, + { label: "MVP Development", color: "#ffd93d", desc: "Build core features with iterative sprints and continuous integration pipeline" }, + { label: "Quality Assurance", color: "#c084fc", desc: "Comprehensive testing including unit, integration, and user acceptance testing" }, + { label: "Beta Launch", color: "#f97316", desc: "Release to select users, gather feedback, and measure key performance metrics" }, + { label: "Iteration & Polish", color: "#14b8a6", desc: "Address feedback, optimize performance, and refine the user experience" }, + { label: "Go to Market", color: "#8b5cf6", desc: "Full public launch with marketing campaign, press outreach, and partner activations" }, + { label: "Growth & Scale", color: "#ec4899", desc: "Monitor adoption metrics, scale infrastructure, and expand to new market segments" }, + { label: "Continuous Improvement", color: "#22c55e", desc: "Ongoing feature development driven by data analytics and user feedback loops" }, + ], +}; diff --git a/surfsense_video/src/remotion/scenes/sequence/layout.ts b/surfsense_video/src/remotion/scenes/sequence/layout.ts new file mode 100644 index 000000000..cd9ee01f7 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/sequence/layout.ts @@ -0,0 +1,384 @@ +/** + * Sequence layout utilities — item positioning, connectors, and camera waypoints. + * Same pattern as list: measure content → position items → build waypoints. + */ +import type { SequenceSceneInput, SequenceItem, LayoutItem, Connector, Waypoint } from "./types"; +import type { SequenceLayout } from "./variant"; +import { STOP_HOLD, STOP_TRANSITION } from "./constants"; +import { measureItemDimensions } from "./components/itemSize"; + +export interface LayoutParams { + cardW: number; + gapX: number; + gapY: number; +} + +interface MeasuredItem { + data: SequenceItem; + w: number; + h: number; +} + +/** Measure all items, then position based on actual content size. */ +export function positionItems( + items: SequenceItem[], + layout: SequenceLayout, + p: LayoutParams, + vmin: number, + showBadge: boolean, +): LayoutItem[] { + const measured: MeasuredItem[] = items.map((data) => { + const dims = measureItemDimensions(data, vmin, p.cardW, showBadge); + return { data, w: dims.width, h: dims.height }; + }); + + switch (layout) { + case "steps": + return layoutSteps(measured, p); + case "timeline": + return layoutTimeline(measured, p); + case "snake": + return layoutSnake(measured, p); + case "ascending": + return layoutAscending(measured, p); + case "zigzag": + return layoutZigzag(measured, p); + } +} + +/** Horizontal row — items side by side with gaps. */ +function layoutSteps(items: MeasuredItem[], p: LayoutParams): LayoutItem[] { + const maxH = Math.max(...items.map((it) => it.h)); + const result: LayoutItem[] = []; + let x = 0; + for (let i = 0; i < items.length; i++) { + const { data, w, h } = items[i]; + result.push({ data, x, y: (maxH - h) / 2, w, h, index: i }); + x += w + p.gapX; + } + return result; +} + +/** Vertical timeline — items stacked with gap. */ +function layoutTimeline(items: MeasuredItem[], p: LayoutParams): LayoutItem[] { + const result: LayoutItem[] = []; + let y = 0; + for (let i = 0; i < items.length; i++) { + const { data, w, h } = items[i]; + result.push({ data, x: 0, y, w, h, index: i }); + y += h + p.gapY; + } + return result; +} + +/** Snake — items in rows that alternate direction. */ +function layoutSnake(items: MeasuredItem[], p: LayoutParams): LayoutItem[] { + const cols = 3; + const result: LayoutItem[] = []; + + const rows: MeasuredItem[][] = []; + for (let i = 0; i < items.length; i += cols) { + rows.push(items.slice(i, i + cols)); + } + + let y = 0; + let globalIdx = 0; + for (let r = 0; r < rows.length; r++) { + const row = rows[r]; + const reversed = r % 2 === 1; + const rowH = Math.max(...row.map((it) => it.h)); + + for (let c = 0; c < row.length; c++) { + const colIdx = reversed ? cols - 1 - c : c; + const { data, w, h } = row[c]; + const x = colIdx * (p.cardW + p.gapX); + result.push({ data, x, y: y + (rowH - h) / 2, w, h, index: globalIdx }); + globalIdx++; + } + + y += rowH + p.gapY; + } + + return result; +} + +/** Ascending staircase — each step offset diagonally, no overlap. */ +function layoutAscending(items: MeasuredItem[], p: LayoutParams): LayoutItem[] { + const result: LayoutItem[] = []; + const n = items.length; + const stepX = p.cardW + p.gapX; + const maxH = Math.max(...items.map((it) => it.h)); + const stepY = maxH + p.gapY; + + for (let i = 0; i < n; i++) { + const { data, w, h } = items[i]; + const x = i * stepX; + const y = (n - 1 - i) * stepY; + result.push({ data, x, y, w, h, index: i }); + } + + return result; +} + +/** Horizontal zigzag — items alternate between high and low vertical positions. */ +function layoutZigzag(items: MeasuredItem[], p: LayoutParams): LayoutItem[] { + const result: LayoutItem[] = []; + const maxH = Math.max(...items.map((it) => it.h)); + const levelOffset = maxH + p.gapY; + const stepX = p.cardW + p.gapX; + + for (let i = 0; i < items.length; i++) { + const { data, w, h } = items[i]; + const x = i * stepX; + const isOdd = i % 2 === 1; + const baseY = isOdd ? levelOffset : 0; + result.push({ data, x, y: baseY + (maxH - h) / 2, w, h, index: i }); + } + + return result; +} + +/** Build connectors between consecutive items. */ +export function buildConnectors( + items: LayoutItem[], + layout: SequenceLayout, + vmin: number, +): Connector[] { + const connectors: Connector[] = []; + + for (let i = 0; i < items.length - 1; i++) { + const from = items[i]; + const to = items[i + 1]; + + let fromX: number, fromY: number, toX: number, toY: number; + let curvePath: string | undefined; + + if (layout === "steps") { + fromX = from.x + from.w; + fromY = from.y + from.h / 2; + toX = to.x; + toY = to.y + to.h / 2; + const pull = Math.abs(toX - fromX) * 0.4; + curvePath = `M ${fromX} ${fromY} C ${fromX + pull} ${fromY}, ${toX - pull} ${toY}, ${toX} ${toY}`; + } else if (layout === "timeline") { + fromX = from.x + from.w / 2; + fromY = from.y + from.h; + toX = to.x + to.w / 2; + toY = to.y; + const pull = Math.abs(toY - fromY) * 0.4; + curvePath = `M ${fromX} ${fromY} C ${fromX} ${fromY + pull}, ${toX} ${toY - pull}, ${toX} ${toY}`; + } else if (layout === "snake") { + const cols = 3; + const fromRow = Math.floor(i / cols); + const toRow = Math.floor((i + 1) / cols); + + if (fromRow === toRow) { + const reversed = fromRow % 2 === 1; + if (reversed) { + fromX = from.x; + fromY = from.y + from.h / 2; + toX = to.x + to.w; + toY = to.y + to.h / 2; + } else { + fromX = from.x + from.w; + fromY = from.y + from.h / 2; + toX = to.x; + toY = to.y + to.h / 2; + } + const pull = Math.abs(toX - fromX) * 0.4; + const dir = toX > fromX ? 1 : -1; + curvePath = `M ${fromX} ${fromY} C ${fromX + dir * pull} ${fromY}, ${toX - dir * pull} ${toY}, ${toX} ${toY}`; + } else { + const reversed = fromRow % 2 === 1; + if (reversed) { + fromX = from.x; + fromY = from.y + from.h / 2; + toX = to.x; + toY = to.y + to.h / 2; + const cx = Math.min(fromX, toX) - vmin * 6; + curvePath = `M ${fromX} ${fromY} C ${cx} ${fromY}, ${cx} ${toY}, ${toX} ${toY}`; + } else { + fromX = from.x + from.w; + fromY = from.y + from.h / 2; + toX = to.x + to.w; + toY = to.y + to.h / 2; + const cx = Math.max(fromX, toX) + vmin * 6; + curvePath = `M ${fromX} ${fromY} C ${cx} ${fromY}, ${cx} ${toY}, ${toX} ${toY}`; + } + } + } else if (layout === "zigzag") { + fromX = from.x + from.w; + fromY = from.y + from.h / 2; + toX = to.x; + toY = to.y + to.h / 2; + const midX = (fromX + toX) / 2; + curvePath = `M ${fromX} ${fromY} C ${midX} ${fromY}, ${midX} ${toY}, ${toX} ${toY}`; + } else { + fromX = from.x + from.w; + fromY = from.y + from.h / 2; + toX = to.x; + toY = to.y + to.h / 2; + const pull = Math.abs(toX - fromX) * 0.4; + curvePath = `M ${fromX} ${fromY} C ${fromX + pull} ${fromY}, ${toX - pull} ${toY}, ${toX} ${toY}`; + } + + connectors.push({ fromX, fromY, toX, toY, curvePath, index: i }); + } + + return connectors; +} + +/** + * Build snap-hold camera waypoints. + * Groups items that fit within 80% of the viewport, then centers camera on each group. + */ +export function buildWaypoints( + items: LayoutItem[], + layout: SequenceLayout, + viewW: number, + viewH: number, +): Waypoint[] { + if (items.length === 0) { + return [{ cx: 0, cy: 0, holdFrames: STOP_HOLD, transitionAfter: 0 }]; + } + + const usableW = viewW * 0.8; + const usableH = viewH * 0.8; + + if (layout === "snake" || layout === "ascending") { + return build2DWaypoints(items, usableW, usableH); + } + + const isHoriz = layout === "steps" || layout === "zigzag"; + const primaryAxis = isHoriz ? "x" : "y"; + const primarySize = isHoriz ? "w" : "h"; + const usable = isHoriz ? usableW : usableH; + + const sorted = [...items].sort((a, b) => a[primaryAxis] - b[primaryAxis]); + const stops: Waypoint[] = []; + let i = 0; + + while (i < sorted.length) { + const anchor = sorted[i][primaryAxis]; + let lastIdx = i; + while ( + lastIdx + 1 < sorted.length && + sorted[lastIdx + 1][primaryAxis] + + sorted[lastIdx + 1][primarySize] - + anchor <= + usable + ) { + lastIdx++; + } + + const group = sorted.slice(i, lastIdx + 1); + const bbox = groupBBox(group); + stops.push({ + cx: (bbox.minX + bbox.maxX) / 2, + cy: (bbox.minY + bbox.maxY) / 2, + holdFrames: STOP_HOLD, + transitionAfter: STOP_TRANSITION, + }); + i = lastIdx + 1; + } + + if (stops.length > 0) stops[stops.length - 1].transitionAfter = 0; + return stops; +} + +function build2DWaypoints( + items: LayoutItem[], + usableW: number, + usableH: number, +): Waypoint[] { + const sorted = [...items].sort((a, b) => a.y - b.y || a.x - b.x); + const stops: Waypoint[] = []; + let i = 0; + + while (i < sorted.length) { + const anchorY = sorted[i].y; + let lastRow = i; + while ( + lastRow + 1 < sorted.length && + sorted[lastRow + 1].y + sorted[lastRow + 1].h - anchorY <= usableH + ) { + lastRow++; + } + + const rowBand = sorted.slice(i, lastRow + 1); + const rowSorted = [...rowBand].sort((a, b) => a.x - b.x); + let j = 0; + + while (j < rowSorted.length) { + const anchorX = rowSorted[j].x; + let lastCol = j; + while ( + lastCol + 1 < rowSorted.length && + rowSorted[lastCol + 1].x + rowSorted[lastCol + 1].w - anchorX <= usableW + ) { + lastCol++; + } + + const group = rowSorted.slice(j, lastCol + 1); + const bbox = groupBBox(group); + stops.push({ + cx: (bbox.minX + bbox.maxX) / 2, + cy: (bbox.minY + bbox.maxY) / 2, + holdFrames: STOP_HOLD, + transitionAfter: STOP_TRANSITION, + }); + j = lastCol + 1; + } + + i = lastRow + 1; + } + + if (stops.length > 0) stops[stops.length - 1].transitionAfter = 0; + return stops; +} + +function groupBBox(items: LayoutItem[]) { + let minX = Infinity, + minY = Infinity, + maxX = -Infinity, + maxY = -Infinity; + for (const it of items) { + minX = Math.min(minX, it.x); + minY = Math.min(minY, it.y); + maxX = Math.max(maxX, it.x + it.w); + maxY = Math.max(maxY, it.y + it.h); + } + return { minX, minY, maxX, maxY }; +} + +/** Total duration from waypoints. */ +export function waypointsDuration(waypoints: Waypoint[]): number { + let total = 0; + for (const wp of waypoints) { + total += wp.holdFrames + wp.transitionAfter; + } + return Math.max(1, total); +} + +/** Compute total scene duration for a sequence input. */ +export function sequenceSceneDuration( + input: SequenceSceneInput, + layout: SequenceLayout, + viewW: number, + viewH: number, + showBadge = true, +): number { + const vmin = Math.min(viewW, viewH) / 100; + const p = getLayoutParams(vmin); + const positioned = positionItems(input.items, layout, p, vmin, showBadge); + const wps = buildWaypoints(positioned, layout, viewW, viewH); + return waypointsDuration(wps); +} + +export function getLayoutParams(vmin: number): LayoutParams { + return { + cardW: vmin * 45, + gapX: vmin * 8, + gapY: vmin * 6, + }; +} diff --git a/surfsense_video/src/remotion/scenes/sequence/preview.tsx b/surfsense_video/src/remotion/scenes/sequence/preview.tsx new file mode 100644 index 000000000..0a045e515 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/sequence/preview.tsx @@ -0,0 +1,58 @@ +/** Remotion Studio compositions for sequence scene. */ +import React from "react"; +import { Composition } from "remotion"; +import { SequenceScene } from "./SequenceScene"; +import { THEMES } from "../../theme"; +import type { SequenceVariant, SequenceLayout } from "./variant"; +import { DEMO_SEQUENCE } from "./demo"; +import { sequenceSceneDuration } from "./layout"; + +const THEME = "dark" as const; +const FPS = 30; +const WIDTH = 1920; +const HEIGHT = 1080; + +const SequencePreview: React.FC<{ variant: SequenceVariant }> = ({ variant }) => { + const theme = THEMES[THEME]; + return ; +}; + +const layouts: SequenceLayout[] = ["steps", "timeline", "snake", "ascending", "zigzag"]; + +const base: SequenceVariant = { + layout: "steps", + itemShape: "rounded", + arrowStyle: "solid", + cardStyle: "top-bar", + showStepNumber: true, +}; + +export const sequencePreviews = ( + <> + {layouts.map((layout) => { + const v: SequenceVariant = { ...base, layout }; + const dur = sequenceSceneDuration(DEMO_SEQUENCE, layout, WIDTH, HEIGHT); + return ( + } + durationInFrames={dur} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + ); + })} + ( + + )} + durationInFrames={sequenceSceneDuration(DEMO_SEQUENCE, "timeline", WIDTH, HEIGHT, false)} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + +); diff --git a/surfsense_video/src/remotion/scenes/sequence/types.ts b/surfsense_video/src/remotion/scenes/sequence/types.ts new file mode 100644 index 000000000..13ac52136 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/sequence/types.ts @@ -0,0 +1,48 @@ +/** Zod schemas and inferred types for sequence scene data. */ +import { z } from "zod"; + +export const SequenceItemSchema = z.object({ + label: z.string(), + desc: z.string().optional(), + color: z.string().optional(), +}); + +export type SequenceItem = z.infer; + +export const SequenceSceneInput = z.object({ + type: z.literal("sequence"), + title: z.string().optional(), + subtitle: z.string().optional(), + items: z.array(SequenceItemSchema).min(1), +}); + +export type SequenceSceneInput = z.infer; + +/** Positioned sequence item for rendering. */ +export interface LayoutItem { + data: SequenceItem; + x: number; + y: number; + w: number; + h: number; + index: number; +} + +/** Connector between two items. */ +export interface Connector { + fromX: number; + fromY: number; + toX: number; + toY: number; + /** Optional SVG path for curved connectors (overrides straight line). */ + curvePath?: string; + index: number; +} + +/** Camera stop. */ +export interface Waypoint { + cx: number; + cy: number; + holdFrames: number; + transitionAfter: number; +} diff --git a/surfsense_video/src/remotion/scenes/sequence/variant.ts b/surfsense_video/src/remotion/scenes/sequence/variant.ts new file mode 100644 index 000000000..4c3fa1c7e --- /dev/null +++ b/surfsense_video/src/remotion/scenes/sequence/variant.ts @@ -0,0 +1,29 @@ +/** Sequence scene variant — seed-deterministic visual style. */ +import { random } from "remotion"; + +export type SequenceLayout = "steps" | "timeline" | "snake" | "ascending" | "zigzag"; +type ItemShape = "rounded" | "pill"; +type ArrowStyle = "solid" | "dashed"; +export type SequenceCardStyle = "top-bar" | "glow" | "bordered" | "minimal"; + +export interface SequenceVariant { + layout: SequenceLayout; + itemShape: ItemShape; + arrowStyle: ArrowStyle; + cardStyle: SequenceCardStyle; + showStepNumber: boolean; +} + +export function deriveSequenceVariant(seed: number): SequenceVariant { + const s = (key: string) => random(`${seed}-${key}`); + const pick = (key: string, arr: T[]) => + arr[Math.floor(s(key) * arr.length)]; + + return { + layout: pick("layout", ["steps", "timeline", "snake", "ascending", "zigzag"] as SequenceLayout[]), + itemShape: pick("shape", ["rounded", "pill"] as ItemShape[]), + arrowStyle: pick("arrow", ["solid", "dashed"] as ArrowStyle[]), + cardStyle: pick("cardStyle", ["top-bar", "glow", "bordered", "minimal"] as SequenceCardStyle[]), + showStepNumber: s("stepNum") > 0.3, + }; +} diff --git a/surfsense_video/src/remotion/scenes/spotlight/SpotlightScene.tsx b/surfsense_video/src/remotion/scenes/spotlight/SpotlightScene.tsx new file mode 100644 index 000000000..959126de4 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/spotlight/SpotlightScene.tsx @@ -0,0 +1,110 @@ +/** + * SpotlightScene -- shows cards one at a time with full-viewport treatment. + * Uses the standard camera system (waypoints + eased transitions). + * Each card gets its own camera stop with a stroke-reveal entrance. + */ +import React, { useMemo } from "react"; +import { AbsoluteFill, useVideoConfig, useCurrentFrame, Easing } from "remotion"; +import type { ThemeColors } from "../../theme"; +import type { SpotlightSceneInput, Waypoint } from "./types"; +import type { SpotlightVariant } from "./variant"; +import { computeSpotlightLayout } from "./layout"; +import { SpotlightCard } from "./components/SpotlightCard"; +import { STOP_HOLD, STOP_TRANSITION } from "./constants"; + +interface SpotlightSceneProps { + input: SpotlightSceneInput; + theme: ThemeColors; + variant: SpotlightVariant; +} + +function resolveCamera( + waypoints: Waypoint[], + frame: number, +): { cx: number; cy: number } { + let cam = { cx: waypoints[0].cx, cy: waypoints[0].cy }; + let cursor = 0; + + for (let w = 0; w < waypoints.length; w++) { + const wp = waypoints[w]; + if (frame < cursor + wp.holdFrames) { + cam = { cx: wp.cx, cy: wp.cy }; + break; + } + cursor += wp.holdFrames; + + if (wp.transitionAfter > 0 && w + 1 < waypoints.length) { + if (frame < cursor + wp.transitionAfter) { + const t = Easing.inOut(Easing.ease)( + (frame - cursor) / wp.transitionAfter, + ); + const next = waypoints[w + 1]; + cam = { + cx: wp.cx + (next.cx - wp.cx) * t, + cy: wp.cy + (next.cy - wp.cy) * t, + }; + break; + } + cursor += wp.transitionAfter; + } + + if (w === waypoints.length - 1) { + cam = { cx: wp.cx, cy: wp.cy }; + } + } + + return cam; +} + +function getEnterFrame(index: number): number { + return index * (STOP_HOLD + STOP_TRANSITION); +} + +export const SpotlightScene: React.FC = ({ + input, theme, variant, +}) => { + const { width, height } = useVideoConfig(); + const frame = useCurrentFrame(); + + const { cards, waypoints } = useMemo( + () => computeSpotlightLayout(input.items.length, width, height), + [input.items.length, width, height], + ); + + const cam = resolveCamera(waypoints, frame); + const panX = width / 2 - cam.cx; + const panY = height / 2 - cam.cy; + + return ( + +
+ {input.items.map((item, i) => { + const distY = Math.abs(cards[i].y + cards[i].h / 2 - cam.cy); + if (distY > height * 1.5) return null; + + return ( +
+ +
+ ); + })} +
+
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/spotlight/components/CardReveal.tsx b/surfsense_video/src/remotion/scenes/spotlight/components/CardReveal.tsx new file mode 100644 index 000000000..24e0c858f --- /dev/null +++ b/surfsense_video/src/remotion/scenes/spotlight/components/CardReveal.tsx @@ -0,0 +1,208 @@ +/** + * CardReveal — animated SVG stroke reveal for card borders. + * Uses @remotion/paths evolvePath to draw a rounded-rect stroke. + * Content fades in as the stroke draws. + */ +import React from "react"; +import { useCurrentFrame, interpolate } from "remotion"; +import { evolvePath } from "@remotion/paths"; +import { DRAW_DURATION } from "../constants"; + +type RevealStyle = + | "drawSingle" + | "drawDouble" + | "drawEdges" + | "drawBrackets" + | "drawNoisy"; + +function roundedPath(w: number, h: number, r: number, startCorner: number): string { + const corners = [ + { mx: r, my: 0, lx: w - r, ly: 0, qx: w, qy: 0, qex: w, qey: r }, + { mx: w, my: r, lx: w, ly: h - r, qx: w, qy: h, qex: w - r, qey: h }, + { mx: w - r, my: h, lx: r, ly: h, qx: 0, qy: h, qex: 0, qey: h - r }, + { mx: 0, my: h - r, lx: 0, ly: r, qx: 0, qy: 0, qex: r, qey: 0 }, + ]; + const shifted = [...corners.slice(startCorner), ...corners.slice(0, startCorner)]; + const first = shifted[0]; + const parts = [`M ${first.mx} ${first.my}`]; + for (const c of shifted) { + parts.push(`L ${c.lx} ${c.ly}`, `Q ${c.qx} ${c.qy} ${c.qex} ${c.qey}`); + } + parts.push("Z"); + return parts.join(" "); +} + +function roundedEdgePaths(w: number, h: number, r: number): string[] { + return [ + `M ${r} 0 L ${w - r} 0 Q ${w} 0 ${w} ${r}`, + `M ${w} ${r} L ${w} ${h - r} Q ${w} ${h} ${w - r} ${h}`, + `M ${w - r} ${h} L ${r} ${h} Q 0 ${h} 0 ${h - r}`, + `M 0 ${h - r} L 0 ${r} Q 0 0 ${r} 0`, + ]; +} + +function roundedBracketPaths(w: number, h: number, r: number) { + const armLen = Math.min(w, h) * 0.18; + return [ + { path: `M ${r + armLen} 0 L ${r} 0 Q 0 0 0 ${r} L 0 ${r + armLen}`, delay: 0 }, + { path: `M ${w - r - armLen} 0 L ${w - r} 0 Q ${w} 0 ${w} ${r} L ${w} ${r + armLen}`, delay: 0.1 }, + { path: `M ${w} ${h - r - armLen} L ${w} ${h - r} Q ${w} ${h} ${w - r} ${h} L ${w - r - armLen} ${h}`, delay: 0.2 }, + { path: `M 0 ${h - r - armLen} L 0 ${h - r} Q 0 ${h} ${r} ${h} L ${r + armLen} ${h}`, delay: 0.3 }, + ]; +} + +interface CardRevealProps { + enterFrame: number; + index: number; + width: number; + height: number; + radius: number; + color: string; + vmin: number; + reveal: RevealStyle; + children: React.ReactNode; +} + +export const CardReveal: React.FC = ({ + enterFrame, index, width, height, radius, color, vmin, reveal, children, +}) => { + const frame = useCurrentFrame(); + const local = frame - enterFrame; + + let progress: number; + if (local < 0) progress = 0; + else if (local >= DRAW_DURATION) progress = 1; + else { + const raw = local / DRAW_DURATION; + progress = 1 - Math.pow(1 - raw, 3); + } + + let strokeOpacity: number; + if (local < 0) strokeOpacity = 0; + else if (local < 2) strokeOpacity = interpolate(local, [0, 2], [0, 1], { extrapolateRight: "clamp" }); + else strokeOpacity = 1; + + const contentOpacity = interpolate(progress, [0.15, 0.6], [0, 1], { + extrapolateLeft: "clamp", extrapolateRight: "clamp", + }); + + const strokeW = vmin * 0.06; + const glowFilter = `drop-shadow(0 0 ${vmin * 0.8}px ${color}80)`; + const contentClip: React.CSSProperties = { borderRadius: radius, overflow: "hidden" }; + const svgStyle: React.CSSProperties = { + position: "absolute", top: 0, left: 0, + pointerEvents: "none", overflow: "visible", + }; + + const clamp = Math.min(Math.max(progress, 0), 1); + + if (reveal === "drawSingle") { + const path = roundedPath(width, height, radius, index % 4); + const evolved = evolvePath(clamp, path); + return ( +
+ + + +
{children}
+
+ ); + } + + if (reveal === "drawDouble") { + const pathA = roundedPath(width, height, radius, 0); + const pathB = roundedPath(width, height, radius, 2); + const eA = evolvePath(clamp, pathA); + const eB = evolvePath(clamp, pathB); + return ( +
+ + + + +
{children}
+
+ ); + } + + if (reveal === "drawEdges") { + const edgePaths = roundedEdgePaths(width, height, radius); + return ( +
+ + {edgePaths.map((ep, i) => { + const stagger = i * 0.15; + const edgeProgress = interpolate(clamp, [stagger, Math.min(stagger + 0.7, 1)], [0, 1], { + extrapolateLeft: "clamp", extrapolateRight: "clamp", + }); + const evolved = evolvePath(edgeProgress, ep); + return ( + + ); + })} + +
{children}
+
+ ); + } + + if (reveal === "drawNoisy") { + const path = roundedPath(width, height, radius, index % 4); + const evolved = evolvePath(clamp, path); + const filterId = `noise-${index}`; + const turbScale = interpolate(clamp, [0, 0.3, 1], [0.08, 0.05, 0.02]); + return ( +
+ + + + + + + + + + + + + + +
{children}
+
+ ); + } + + const brackets = roundedBracketPaths(width, height, radius); + return ( +
+ + {brackets.map((corner, i) => { + const cornerProgress = interpolate(clamp, + [corner.delay, Math.min(corner.delay + 0.5, 1)], [0, 1], { + extrapolateLeft: "clamp", extrapolateRight: "clamp", + }); + const eased = 1 - Math.pow(1 - cornerProgress, 3); + const evolved = evolvePath(eased, corner.path); + return ( + + ); + })} + +
{children}
+
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/spotlight/components/SpotlightCard.tsx b/surfsense_video/src/remotion/scenes/spotlight/components/SpotlightCard.tsx new file mode 100644 index 000000000..1034a5618 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/spotlight/components/SpotlightCard.tsx @@ -0,0 +1,92 @@ +/** + * SpotlightCard -- shared card shell for all categories. + * Wraps each card in CardReveal for stroke animation, + * applies variant background style and glow overlay, + * then delegates to the category-specific renderer. + */ +import React from "react"; +import type { ThemeColors } from "../../../theme"; +import type { SpotlightVariant } from "../variant"; +import type { CardItem } from "../types"; +import { renderCardContent } from "./categories"; +import { CardReveal } from "./CardReveal"; + +interface SpotlightCardProps { + item: CardItem; + index: number; + enterFrame: number; + vmin: number; + w: number; + h: number; + variant: SpotlightVariant; + theme: ThemeColors; +} + +export const SpotlightCard: React.FC = ({ + item, index, enterFrame, vmin, w, h, variant, theme, +}) => { + if (!item) return null; + + const color = item.color; + const pad = vmin * 4; + const radius = vmin * 1.5; + + const cardAngle = variant.glowAngle + index * 137.5; + const glowX = 50 + 40 * Math.cos((cardAngle * Math.PI) / 180); + const glowY = 50 + 40 * Math.sin((cardAngle * Math.PI) / 180); + + const bgStyle = (): React.CSSProperties => { + switch (variant.cardBg) { + case "glass": + return { + background: `linear-gradient(135deg, ${color}12 0%, ${color}06 100%)`, + backdropFilter: "blur(12px)", + WebkitBackdropFilter: "blur(12px)", + }; + case "gradient": + return { + background: `linear-gradient(${cardAngle}deg, ${color}18 0%, transparent 50%, ${color}10 100%)`, + }; + case "subtle": + return { + background: `radial-gradient(ellipse at ${glowX}% ${glowY}%, ${color}15 0%, ${color}05 40%, transparent 70%)`, + }; + case "solid": + default: + return {}; + } + }; + + return ( + +
+
+
+ {renderCardContent(item, { index, enterFrame, vmin, theme, variant })} +
+ + ); +}; diff --git a/surfsense_video/src/remotion/scenes/spotlight/components/categories/DefinitionContent.tsx b/surfsense_video/src/remotion/scenes/spotlight/components/categories/DefinitionContent.tsx new file mode 100644 index 000000000..249444e42 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/spotlight/components/categories/DefinitionContent.tsx @@ -0,0 +1,37 @@ +/** Definition card -- term, definition, and optional usage example. */ +import React from "react"; +import type { DefinitionItem } from "../../types"; +import type { CardRendererProps } from "./types"; + +export const DefinitionContent: React.FC> = ({ + item, vmin, theme, +}) => ( +
+
+ {item.term} +
+
+
+ {item.definition} +
+ {item.example && ( +
+ e.g. {item.example} +
+ )} +
+); diff --git a/surfsense_video/src/remotion/scenes/spotlight/components/categories/FactContent.tsx b/surfsense_video/src/remotion/scenes/spotlight/components/categories/FactContent.tsx new file mode 100644 index 000000000..0c4287b3d --- /dev/null +++ b/surfsense_video/src/remotion/scenes/spotlight/components/categories/FactContent.tsx @@ -0,0 +1,26 @@ +/** Fact card -- bold statement with optional source attribution. */ +import React from "react"; +import type { FactItem } from "../../types"; +import type { CardRendererProps } from "./types"; + +export const FactContent: React.FC> = ({ + item, vmin, theme, +}) => ( +
+
+ {item.statement} +
+ {item.source && ( +
+ {item.source} +
+ )} +
+); diff --git a/surfsense_video/src/remotion/scenes/spotlight/components/categories/InfoContent.tsx b/surfsense_video/src/remotion/scenes/spotlight/components/categories/InfoContent.tsx new file mode 100644 index 000000000..969855891 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/spotlight/components/categories/InfoContent.tsx @@ -0,0 +1,46 @@ +/** Info card — title, subtitle, description, and optional tag badge. */ +import React from "react"; +import type { InfoItem } from "../../types"; +import type { CardRendererProps } from "./types"; + +export const InfoContent: React.FC> = ({ + item, vmin, theme, +}) => ( +
+ {item.tag && ( +
+ {item.tag} +
+ )} +
+ {item.title} +
+ {item.subtitle && ( +
+ {item.subtitle} +
+ )} +
+ {item.desc} +
+
+); diff --git a/surfsense_video/src/remotion/scenes/spotlight/components/categories/ProfileContent.tsx b/surfsense_video/src/remotion/scenes/spotlight/components/categories/ProfileContent.tsx new file mode 100644 index 000000000..ba6fc9ab3 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/spotlight/components/categories/ProfileContent.tsx @@ -0,0 +1,55 @@ +/** Profile card -- avatar initials, name, role, optional tag and description. */ +import React from "react"; +import type { ProfileItem } from "../../types"; +import type { CardRendererProps } from "./types"; + +export const ProfileContent: React.FC> = ({ + item, vmin, theme, +}) => { + const initials = item.name.split(" ").map((w) => w[0]).join("").slice(0, 2).toUpperCase(); + + return ( +
+
+
+ {initials} +
+
+
+ {item.name} +
+
+ {item.role} +
+
+
+ {item.tag && ( +
+ {item.tag} +
+ )} + {item.desc && ( +
+ {item.desc} +
+ )} +
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/spotlight/components/categories/ProgressContent.tsx b/surfsense_video/src/remotion/scenes/spotlight/components/categories/ProgressContent.tsx new file mode 100644 index 000000000..17b6d46fa --- /dev/null +++ b/surfsense_video/src/remotion/scenes/spotlight/components/categories/ProgressContent.tsx @@ -0,0 +1,65 @@ +/** Progress card -- animated bar with spring physics. */ +import React from "react"; +import { useCurrentFrame, spring, useVideoConfig } from "remotion"; +import type { ProgressItem } from "../../types"; +import type { CardRendererProps } from "./types"; + +interface ProgressProps extends CardRendererProps { + enterFrame: number; +} + +export const ProgressContent: React.FC = ({ + item, enterFrame, vmin, theme, +}) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const max = item.max ?? 100; + const pct = Math.min(Math.max(item.value / max, 0), 1); + + const localFrame = Math.max(0, frame - enterFrame); + + const barProgress = spring({ + frame: localFrame, fps, + config: { damping: 18, stiffness: 40, mass: 1.2 }, + }); + const animatedPct = pct * barProgress; + const displayValue = Math.round(item.value * barProgress); + + return ( +
+
+ {item.title} +
+
+ + {displayValue} + + + {item.max ? `/ ${item.max}` : "%"} + +
+
+
+
+ {item.desc && ( +
+ {item.desc} +
+ )} +
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/spotlight/components/categories/QuoteContent.tsx b/surfsense_video/src/remotion/scenes/spotlight/components/categories/QuoteContent.tsx new file mode 100644 index 000000000..140c916d7 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/spotlight/components/categories/QuoteContent.tsx @@ -0,0 +1,35 @@ +/** Quote card — large quotation mark, text, author, and role. */ +import React from "react"; +import type { QuoteItem } from "../../types"; +import type { CardRendererProps } from "./types"; + +export const QuoteContent: React.FC> = ({ + item, vmin, theme, +}) => ( +
+
+ {"\u201C"} +
+
+ {item.quote} +
+
+
+ {item.author} +
+ {item.role && ( +
+ {item.role} +
+ )} +
+
+); diff --git a/surfsense_video/src/remotion/scenes/spotlight/components/categories/StatContent.tsx b/surfsense_video/src/remotion/scenes/spotlight/components/categories/StatContent.tsx new file mode 100644 index 000000000..e0808f426 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/spotlight/components/categories/StatContent.tsx @@ -0,0 +1,50 @@ +/** Stat card -- large hero value with title and description. */ +import React from "react"; +import type { StatItem } from "../../types"; +import type { CardRendererProps } from "./types"; + +export const StatContent: React.FC> = ({ + item, vmin, theme, variant, +}) => { + const isHero = variant.valueStyle === "hero" || variant.valueStyle === "colored"; + + return ( +
+ {isHero && ( +
+ {item.title} +
+ )} +
+ {item.value} +
+ {!isHero && ( +
+ {item.title} +
+ )} + {item.desc && ( +
+ {item.desc} +
+ )} +
+ ); +}; diff --git a/surfsense_video/src/remotion/scenes/spotlight/components/categories/index.tsx b/surfsense_video/src/remotion/scenes/spotlight/components/categories/index.tsx new file mode 100644 index 000000000..d2c42bfd9 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/spotlight/components/categories/index.tsx @@ -0,0 +1,27 @@ +/** Category dispatcher -- routes a CardItem to its renderer. */ +import React from "react"; +import type { CardItem } from "../../types"; +import type { BaseCardProps } from "./types"; +import { StatContent } from "./StatContent"; +import { InfoContent } from "./InfoContent"; +import { QuoteContent } from "./QuoteContent"; +import { ProfileContent } from "./ProfileContent"; +import { ProgressContent } from "./ProgressContent"; +import { FactContent } from "./FactContent"; +import { DefinitionContent } from "./DefinitionContent"; + +interface RenderProps extends BaseCardProps { + enterFrame: number; +} + +export function renderCardContent(item: CardItem, props: RenderProps): React.ReactNode { + switch (item.category) { + case "stat": return ; + case "info": return ; + case "quote": return ; + case "profile": return ; + case "progress": return ; + case "fact": return ; + case "definition": return ; + } +} diff --git a/surfsense_video/src/remotion/scenes/spotlight/components/categories/types.ts b/surfsense_video/src/remotion/scenes/spotlight/components/categories/types.ts new file mode 100644 index 000000000..5ef0034b2 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/spotlight/components/categories/types.ts @@ -0,0 +1,15 @@ +/** Shared prop types for category card renderers. */ +import type { ThemeColors } from "../../../../theme"; +import type { SpotlightVariant } from "../../variant"; +import type { CardItem } from "../../types"; + +export interface BaseCardProps { + index: number; + vmin: number; + theme: ThemeColors; + variant: SpotlightVariant; +} + +export type CardRendererProps = BaseCardProps & { + item: T; +}; diff --git a/surfsense_video/src/remotion/scenes/spotlight/constants.ts b/surfsense_video/src/remotion/scenes/spotlight/constants.ts new file mode 100644 index 000000000..0c13de13b --- /dev/null +++ b/surfsense_video/src/remotion/scenes/spotlight/constants.ts @@ -0,0 +1,10 @@ +/** Timing constants for spotlight scene animations. */ + +/** Camera hold per card (frames). */ +export const STOP_HOLD = 180; + +/** Camera transition between cards (frames). */ +export const STOP_TRANSITION = 30; + +/** SVG stroke reveal animation duration (frames). */ +export const DRAW_DURATION = 50; diff --git a/surfsense_video/src/remotion/scenes/spotlight/demo.ts b/surfsense_video/src/remotion/scenes/spotlight/demo.ts new file mode 100644 index 000000000..30ceb1571 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/spotlight/demo.ts @@ -0,0 +1,29 @@ +/** Sample data for Remotion Studio preview. */ +import type { SpotlightSceneInput } from "./types"; + +export const DEMO_SPOTLIGHT_SINGLE: SpotlightSceneInput = { + type: "spotlight", + items: [ + { category: "stat", title: "Generative AI Market", value: "$67B", desc: "Content creation, code generation, and synthetic data production reshaping creative workflows and software development pipelines.", color: "#6c7dff" }, + ], +}; + +export const DEMO_SPOTLIGHT_STATS: SpotlightSceneInput = { + type: "spotlight", + items: [ + { category: "stat", title: "Natural Language Processing", value: "$43B", desc: "Text understanding and generation powering human-computer interaction across enterprise applications and consumer products worldwide.", color: "#3b82f6" }, + { category: "stat", title: "Computer Vision", value: "$28B", desc: "Image recognition and video analysis transforming manufacturing quality control, medical imaging diagnostics, and autonomous navigation systems.", color: "#8b5cf6" }, + { category: "stat", title: "Generative AI", value: "$67B", desc: "Content creation, code generation, and synthetic data production reshaping creative workflows and software development pipelines.", color: "#06b6d4" }, + ], +}; + +export const DEMO_SPOTLIGHT_MIXED: SpotlightSceneInput = { + type: "spotlight", + items: [ + { category: "stat", title: "AI Market Size", value: "$407B", desc: "Projected global AI market value by 2027.", color: "#3b82f6" }, + { category: "quote", quote: "The best way to predict the future is to invent it.", author: "Alan Kay", role: "Computer Scientist", color: "#8b5cf6" }, + { category: "definition", term: "Overfitting", definition: "When a model learns noise in training data rather than the underlying pattern, performing well on training but poorly on unseen data.", example: "99% train accuracy, 60% test accuracy", color: "#06b6d4" }, + { category: "fact", statement: "GPT-4 was trained on over 13 trillion tokens of text data.", source: "OpenAI Technical Report", color: "#10b981" }, + { category: "progress", title: "Model Training", value: 87, desc: "Epoch 43 of 50 completed.", color: "#f59e0b" }, + ], +}; diff --git a/surfsense_video/src/remotion/scenes/spotlight/layout.ts b/surfsense_video/src/remotion/scenes/spotlight/layout.ts new file mode 100644 index 000000000..76d53c7bc --- /dev/null +++ b/surfsense_video/src/remotion/scenes/spotlight/layout.ts @@ -0,0 +1,65 @@ +/** + * Spotlight scene layout — positions cards and generates one waypoint per card. + * Each card gets full-viewport treatment; the camera steps between them. + */ +import type { Waypoint } from "./types"; +import { STOP_HOLD, STOP_TRANSITION } from "./constants"; + +export interface CardPosition { + x: number; + y: number; + w: number; + h: number; +} + +export function computeSpotlightLayout( + count: number, + viewW: number, + viewH: number, +): { cards: CardPosition[]; waypoints: Waypoint[] } { + const vmin = Math.min(viewW, viewH) / 100; + const cardW = vmin * 60; + const cardH = vmin * 45; + + const cards: CardPosition[] = []; + const waypoints: Waypoint[] = []; + + const xOffsets = [0, 0.15, -0.15, 0.1, -0.1, 0.12, -0.12, 0.08]; + + for (let i = 0; i < count; i++) { + const xShift = count > 1 ? viewW * (xOffsets[i % xOffsets.length]) : 0; + const cx = viewW / 2 + xShift; + const cy = viewH / 2 + i * viewH; + + cards.push({ + x: cx - cardW / 2, + y: cy - cardH / 2, + w: cardW, + h: cardH, + }); + + waypoints.push({ + cx, + cy, + holdFrames: STOP_HOLD, + transitionAfter: i < count - 1 ? STOP_TRANSITION : 0, + }); + } + + return { cards, waypoints }; +} + +export function waypointsDuration(waypoints: Waypoint[]): number { + let total = 0; + for (const wp of waypoints) total += wp.holdFrames + wp.transitionAfter; + return Math.max(1, total); +} + +export function spotlightSceneDuration( + itemCount: number, + viewW: number, + viewH: number, +): number { + const { waypoints } = computeSpotlightLayout(itemCount, viewW, viewH); + return waypointsDuration(waypoints); +} diff --git a/surfsense_video/src/remotion/scenes/spotlight/preview.tsx b/surfsense_video/src/remotion/scenes/spotlight/preview.tsx new file mode 100644 index 000000000..6680a6908 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/spotlight/preview.tsx @@ -0,0 +1,118 @@ +/** Remotion Studio compositions for spotlight scene. */ +import React from "react"; +import { Composition } from "remotion"; +import { SpotlightScene } from "./SpotlightScene"; +import { THEMES } from "../../theme"; +import type { SpotlightVariant } from "./variant"; +import type { SpotlightSceneInput } from "./types"; +import { + DEMO_SPOTLIGHT_SINGLE, + DEMO_SPOTLIGHT_STATS, + DEMO_SPOTLIGHT_MIXED, +} from "./demo"; +import { spotlightSceneDuration } from "./layout"; + +const THEME = "dark" as const; +const FPS = 30; +const WIDTH = 1920; +const HEIGHT = 1080; + +const Preview: React.FC<{ + variant: SpotlightVariant; + data: SpotlightSceneInput; +}> = ({ variant, data }) => { + const theme = THEMES[THEME]; + return ; +}; + +function dur(data: SpotlightSceneInput) { + return spotlightSceneDuration(data.items.length, WIDTH, HEIGHT); +} + +const base: SpotlightVariant = { + cardBg: "gradient", + valueStyle: "hero", + reveal: "drawSingle", + glowAngle: 45, +}; + +export const spotlightPreviews = ( + <> + {/* Single card */} + } + durationInFrames={dur(DEMO_SPOTLIGHT_SINGLE)} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + + {/* Stats -- drawSingle */} + } + durationInFrames={dur(DEMO_SPOTLIGHT_STATS)} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + {/* Stats -- drawDouble + glass */} + ( + + )} + durationInFrames={dur(DEMO_SPOTLIGHT_STATS)} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + {/* Stats -- drawBrackets + subtle */} + ( + + )} + durationInFrames={dur(DEMO_SPOTLIGHT_STATS)} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + {/* Stats -- drawNoisy + solid */} + ( + + )} + durationInFrames={dur(DEMO_SPOTLIGHT_STATS)} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + + {/* Mixed categories */} + ( + + )} + durationInFrames={dur(DEMO_SPOTLIGHT_MIXED)} + fps={FPS} + width={WIDTH} + height={HEIGHT} + /> + +); diff --git a/surfsense_video/src/remotion/scenes/spotlight/types.ts b/surfsense_video/src/remotion/scenes/spotlight/types.ts new file mode 100644 index 000000000..494f57075 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/spotlight/types.ts @@ -0,0 +1,88 @@ +/** Zod schemas and inferred types for spotlight scene card data. */ +import { z } from "zod"; + +export const StatItem = z.object({ + category: z.literal("stat"), + title: z.string(), + value: z.string(), + desc: z.string().optional(), + color: z.string(), +}); + +export const InfoItem = z.object({ + category: z.literal("info"), + title: z.string(), + subtitle: z.string().optional(), + desc: z.string(), + tag: z.string().optional(), + color: z.string(), +}); + +export const QuoteItem = z.object({ + category: z.literal("quote"), + quote: z.string(), + author: z.string(), + role: z.string().optional(), + color: z.string(), +}); + +export const ProfileItem = z.object({ + category: z.literal("profile"), + name: z.string(), + role: z.string(), + desc: z.string().optional(), + tag: z.string().optional(), + color: z.string(), +}); + +export const ProgressItem = z.object({ + category: z.literal("progress"), + title: z.string(), + value: z.number(), + max: z.number().optional(), + desc: z.string().optional(), + color: z.string(), +}); + +export const FactItem = z.object({ + category: z.literal("fact"), + statement: z.string(), + source: z.string().optional(), + color: z.string(), +}); + +export const DefinitionItem = z.object({ + category: z.literal("definition"), + term: z.string(), + definition: z.string(), + example: z.string().optional(), + color: z.string(), +}); + +export const CardItem = z.discriminatedUnion("category", [ + StatItem, InfoItem, QuoteItem, ProfileItem, + ProgressItem, FactItem, DefinitionItem, +]); + +export const SpotlightSceneInput = z.object({ + type: z.literal("spotlight"), + items: z.array(CardItem).min(1).max(8), +}); + +export type StatItem = z.infer; +export type InfoItem = z.infer; +export type QuoteItem = z.infer; +export type ProfileItem = z.infer; +export type ProgressItem = z.infer; +export type FactItem = z.infer; +export type DefinitionItem = z.infer; +export type CardItem = z.infer; +export type CardCategory = CardItem["category"]; +export type SpotlightSceneInput = z.infer; + +export interface Waypoint { + cx: number; + cy: number; + holdFrames: number; + transitionAfter: number; +} diff --git a/surfsense_video/src/remotion/scenes/spotlight/variant.ts b/surfsense_video/src/remotion/scenes/spotlight/variant.ts new file mode 100644 index 000000000..d1cba6f79 --- /dev/null +++ b/surfsense_video/src/remotion/scenes/spotlight/variant.ts @@ -0,0 +1,38 @@ +/** Spotlight scene variant — seed-deterministic visual style. */ +import { random } from "remotion"; + +type RevealStyle = + | "drawSingle" + | "drawDouble" + | "drawEdges" + | "drawBrackets" + | "drawNoisy"; + +export type SpotlightCardBg = "solid" | "glass" | "gradient" | "subtle"; +export type SpotlightValueStyle = "hero" | "inline" | "badge" | "colored"; + +export interface SpotlightVariant { + cardBg: SpotlightCardBg; + valueStyle: SpotlightValueStyle; + reveal: RevealStyle; + glowAngle: number; +} + +export function deriveSpotlightVariant(seed: number): SpotlightVariant { + const s = (key: string) => random(`${seed}-${key}`); + const pick = (key: string, arr: T[]) => + arr[Math.floor(s(key) * arr.length)]; + + return { + cardBg: pick("cardBg", [ + "solid", "glass", "gradient", "subtle", + ] as SpotlightCardBg[]), + valueStyle: pick("value", [ + "hero", "inline", "badge", "colored", + ] as SpotlightValueStyle[]), + reveal: pick("reveal", [ + "drawSingle", "drawDouble", "drawEdges", "drawBrackets", "drawNoisy", + ] as RevealStyle[]), + glowAngle: s("glow") * 360, + }; +} diff --git a/surfsense_video/src/remotion/theme.ts b/surfsense_video/src/remotion/theme.ts new file mode 100644 index 000000000..e9f73748a --- /dev/null +++ b/surfsense_video/src/remotion/theme.ts @@ -0,0 +1,39 @@ +export interface ThemeColors { + bg: string; + textPrimary: string; + textSecondary: string; + textMuted: string; + border: string; + glowOpacity: string; + badgeBg: string; + badgeBorder: string; + accentGlowSuffix: string; + gradientBase: [string, string, string]; +} + +export const THEMES: Record<"dark" | "light", ThemeColors> = { + dark: { + bg: "#050510", + textPrimary: "rgba(255,255,255,0.95)", + textSecondary: "rgba(255,255,255,0.5)", + textMuted: "rgba(255,255,255,0.35)", + border: "rgba(255,255,255,0.04)", + glowOpacity: "0d", + badgeBg: "15", + badgeBorder: "25", + accentGlowSuffix: "50", + gradientBase: ["#0a0a2e", "#1a0a3e", "#0a1a2e"], + }, + light: { + bg: "#f5f5f7", + textPrimary: "rgba(0,0,0,0.88)", + textSecondary: "rgba(0,0,0,0.45)", + textMuted: "rgba(0,0,0,0.3)", + border: "rgba(0,0,0,0.06)", + glowOpacity: "12", + badgeBg: "10", + badgeBorder: "20", + accentGlowSuffix: "40", + gradientBase: ["#e8e8f0", "#f0e8f4", "#e8f0f0"], + }, +}; \ No newline at end of file diff --git a/surfsense_video/src/remotion/types.ts b/surfsense_video/src/remotion/types.ts new file mode 100644 index 000000000..ed99dd241 --- /dev/null +++ b/surfsense_video/src/remotion/types.ts @@ -0,0 +1,29 @@ +import { z } from "zod"; +import { IntroSceneInput } from "./scenes/intro/types"; +import { SpotlightSceneInput } from "./scenes/spotlight/types"; +import { HierarchySceneInput } from "./scenes/hierarchy/types"; +import { ListSceneInput } from "./scenes/list/types"; +import { SequenceSceneInput } from "./scenes/sequence/types"; +import { ChartSceneInput } from "./scenes/chart/types"; +import { RelationSceneInput } from "./scenes/relation/types"; +import { ComparisonSceneInput } from "./scenes/comparison/types"; +import { OutroSceneInput } from "./scenes/outro/types"; + +export const SceneInput = z.discriminatedUnion("type", [ + IntroSceneInput, + SpotlightSceneInput, + HierarchySceneInput, + ListSceneInput, + SequenceSceneInput, + ChartSceneInput, + RelationSceneInput, + ComparisonSceneInput, + OutroSceneInput, +]); + +export const VideoInput = z.object({ + scenes: z.array(SceneInput).min(1), +}); + +export type SceneInput = z.infer; +export type VideoInput = z.infer; \ No newline at end of file diff --git a/surfsense_video/src/types/constants.ts b/surfsense_video/src/types/constants.ts new file mode 100644 index 000000000..71af94c26 --- /dev/null +++ b/surfsense_video/src/types/constants.ts @@ -0,0 +1,9 @@ +import { VideoInput } from "../remotion/types"; + +export const COMP_NAME = "SurfSenseVideo"; + +export const CompositionProps = VideoInput; + +export const VIDEO_WIDTH = 1920; +export const VIDEO_HEIGHT = 1080; +export const VIDEO_FPS = 30; diff --git a/surfsense_video/src/types/schema.ts b/surfsense_video/src/types/schema.ts new file mode 100644 index 000000000..d97912806 --- /dev/null +++ b/surfsense_video/src/types/schema.ts @@ -0,0 +1,26 @@ +import { z } from "zod"; +import { CompositionProps } from "./constants"; + +export const RenderRequest = z.object({ + inputProps: CompositionProps, +}); + +export const ProgressRequest = z.object({ + bucketName: z.string(), + id: z.string(), +}); + +export type ProgressResponse = + | { + type: "error"; + message: string; + } + | { + type: "progress"; + progress: number; + } + | { + type: "done"; + url: string; + size: number; + }; diff --git a/surfsense_video/styles/global.css b/surfsense_video/styles/global.css new file mode 100644 index 000000000..6aa2d70cb --- /dev/null +++ b/surfsense_video/styles/global.css @@ -0,0 +1,58 @@ +:root { + --background: #fff; + --foreground: #000; + --unfocused-border-color: #eaeaea; + --focused-border-color: #666; + + --button-disabled-color: #fafafa; + --disabled-text-color: #999; + + --geist-border-radius: 5px; + --geist-quarter-pad: 6px; + --geist-half-pad: 12px; + --geist-pad: 24px; + --geist-font: "Inter"; + + --geist-error: #e00; + + --subtitle: #666; +} + +* { + box-sizing: border-box; +} + +.cinematics { + box-shadow: 0 0 200px rgba(0, 0, 0, 0.15); +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #000000; + --unfocused-border-color: #333; + --focused-border-color: #888; + --foreground: #fff; + --button-disabled-color: #111; + --geist-error: red; + --subtitle: #8D8D8D + + } + .cinematics { + box-shadow: 0 0 200px rgba(255, 255, 255, 0.15); + } +} + +body { + background-color: var(--background); +} + + +input { + border: 1px solid var(--unfocused-border-color); + transition: border-color 0.15s ease; + outline: none; +} + +input:focus { + border-color: var(--focused-border-color); +} diff --git a/surfsense_video/tsconfig.json b/surfsense_video/tsconfig.json new file mode 100644 index 000000000..816e8b3f2 --- /dev/null +++ b/surfsense_video/tsconfig.json @@ -0,0 +1,39 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "noUnusedLocals": true, + "plugins": [ + { + "name": "next" + } + ], + "incremental": true + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ], + "exclude": [ + "node_modules", + "remotion.config.ts" + ] +} diff --git a/surfsense_video/vercel.json b/surfsense_video/vercel.json new file mode 100644 index 000000000..b2129dd8e --- /dev/null +++ b/surfsense_video/vercel.json @@ -0,0 +1,4 @@ +{ + "buildCommand": "node deploy.mjs && next build" + } + \ No newline at end of file diff --git a/surfsense_web/app/api/video/progress/route.ts b/surfsense_web/app/api/video/progress/route.ts new file mode 100644 index 000000000..70bfc1768 --- /dev/null +++ b/surfsense_web/app/api/video/progress/route.ts @@ -0,0 +1,67 @@ +import { NextRequest, NextResponse } from "next/server"; +import { + getRenderProgress, + speculateFunctionName, + type AwsRegion, +} from "@remotion/lambda/client"; + +const REGION = process.env.REMOTION_AWS_REGION || "us-east-1"; +const RAM = Number(process.env.REMOTION_LAMBDA_RAM || 3009); +const DISK = Number(process.env.REMOTION_LAMBDA_DISK || 10240); +const TIMEOUT = Number(process.env.REMOTION_LAMBDA_TIMEOUT || 240); + +export async function POST(req: NextRequest) { + try { + const body = await req.json(); + const { id, bucketName } = body; + + if (!id || !bucketName) { + return NextResponse.json( + { type: "error", message: "id and bucketName are required" }, + { status: 400 }, + ); + } + + const renderProgress = await getRenderProgress({ + bucketName, + functionName: speculateFunctionName({ + diskSizeInMb: DISK, + memorySizeInMb: RAM, + timeoutInSeconds: TIMEOUT, + }), + region: REGION as AwsRegion, + renderId: id, + }); + + if (renderProgress.fatalErrorEncountered) { + return NextResponse.json({ + type: "success", + data: { type: "error", message: renderProgress.errors[0].message }, + }); + } + + if (renderProgress.done) { + return NextResponse.json({ + type: "success", + data: { + type: "done", + url: renderProgress.outputFile as string, + size: renderProgress.outputSizeInBytes as number, + }, + }); + } + + return NextResponse.json({ + type: "success", + data: { + type: "progress", + progress: Math.max(0.03, renderProgress.overallProgress), + }, + }); + } catch (err) { + return NextResponse.json( + { type: "error", message: (err as Error).message }, + { status: 500 }, + ); + } +} diff --git a/surfsense_web/app/api/video/render/route.ts b/surfsense_web/app/api/video/render/route.ts new file mode 100644 index 000000000..2661ca203 --- /dev/null +++ b/surfsense_web/app/api/video/render/route.ts @@ -0,0 +1,62 @@ +import { NextRequest, NextResponse } from "next/server"; +import { + renderMediaOnLambda, + speculateFunctionName, + type AwsRegion, +} from "@remotion/lambda/client"; + +const REGION = process.env.REMOTION_AWS_REGION || "us-east-1"; +const SITE_NAME = process.env.REMOTION_SITE_NAME || "surfsense-video"; +const RAM = Number(process.env.REMOTION_LAMBDA_RAM || 3009); +const DISK = Number(process.env.REMOTION_LAMBDA_DISK || 10240); +const TIMEOUT = Number(process.env.REMOTION_LAMBDA_TIMEOUT || 240); +const COMPOSITION_ID = "SurfSenseVideo"; + +export async function POST(req: NextRequest) { + try { + const body = await req.json(); + const { inputProps } = body; + + if (!inputProps?.scenes?.length) { + return NextResponse.json( + { type: "error", message: "inputProps with scenes is required" }, + { status: 400 }, + ); + } + + if ( + !process.env.AWS_ACCESS_KEY_ID && + !process.env.REMOTION_AWS_ACCESS_KEY_ID + ) { + return NextResponse.json( + { type: "error", message: "AWS credentials not configured for video rendering" }, + { status: 500 }, + ); + } + + const result = await renderMediaOnLambda({ + codec: "h264", + functionName: speculateFunctionName({ + diskSizeInMb: DISK, + memorySizeInMb: RAM, + timeoutInSeconds: TIMEOUT, + }), + region: REGION as AwsRegion, + serveUrl: SITE_NAME, + composition: COMPOSITION_ID, + inputProps, + framesPerLambda: 10, + downloadBehavior: { + type: "download", + fileName: "video.mp4", + }, + }); + + return NextResponse.json({ type: "success", data: result }); + } catch (err) { + return NextResponse.json( + { type: "error", message: (err as Error).message }, + { status: 500 }, + ); + } +} diff --git a/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx index d1d98bbf6..95dcdc9b5 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx @@ -38,6 +38,7 @@ import type { ThinkingStep } from "@/components/tool-ui/deepagent-thinking"; import { DisplayImageToolUI } from "@/components/tool-ui/display-image"; import { GeneratePodcastToolUI } from "@/components/tool-ui/generate-podcast"; import { GenerateReportToolUI } from "@/components/tool-ui/generate-report"; +import { GenerateVideoToolUI } from "@/components/tool-ui/generate-video"; import { CreateGoogleDriveFileToolUI, DeleteGoogleDriveFileToolUI, @@ -147,6 +148,7 @@ function extractMentionedDocuments(content: unknown): MentionedDocumentInfo[] { const TOOLS_WITH_UI = new Set([ "generate_podcast", "generate_report", + "generate_video", "link_preview", "display_image", "delete_notion_page", @@ -1661,6 +1663,7 @@ export default function NewChatPage() { + diff --git a/surfsense_web/components/public-chat/public-chat-view.tsx b/surfsense_web/components/public-chat/public-chat-view.tsx index bfe370bf0..febbf497d 100644 --- a/surfsense_web/components/public-chat/public-chat-view.tsx +++ b/surfsense_web/components/public-chat/public-chat-view.tsx @@ -6,6 +6,7 @@ import { ReportPanel } from "@/components/report-panel/report-panel"; import { DisplayImageToolUI } from "@/components/tool-ui/display-image"; import { GeneratePodcastToolUI } from "@/components/tool-ui/generate-podcast"; import { GenerateReportToolUI } from "@/components/tool-ui/generate-report"; +import { GenerateVideoToolUI } from "@/components/tool-ui/generate-video"; import { LinkPreviewToolUI } from "@/components/tool-ui/link-preview"; import { ScrapeWebpageToolUI } from "@/components/tool-ui/scrape-webpage"; import { Spinner } from "@/components/ui/spinner"; @@ -45,6 +46,7 @@ export function PublicChatView({ shareToken }: PublicChatViewProps) { {/* Tool UIs for rendering tool results */} + diff --git a/surfsense_web/components/settings/llm-role-manager.tsx b/surfsense_web/components/settings/llm-role-manager.tsx index 051df855b..3366a5e2a 100644 --- a/surfsense_web/components/settings/llm-role-manager.tsx +++ b/surfsense_web/components/settings/llm-role-manager.tsx @@ -7,6 +7,7 @@ import { CheckCircle, CircleDashed, FileText, + Film, ImageIcon, RefreshCw, RotateCcw, @@ -72,6 +73,15 @@ const ROLE_DESCRIPTIONS = { prefKey: "image_generation_config_id" as const, configType: "image" as const, }, + video: { + icon: Film, + title: "Video LLM", + description: "LLM for Remotion video code generation — use a reasoning model for best results", + color: "text-orange-600 dark:text-orange-400", + bgColor: "bg-orange-500/10", + prefKey: "video_llm_id" as const, + configType: "llm" as const, + }, }; interface LLMRoleManagerProps { @@ -117,6 +127,7 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) { agent_llm_id: preferences.agent_llm_id ?? "", document_summary_llm_id: preferences.document_summary_llm_id ?? "", image_generation_config_id: preferences.image_generation_config_id ?? "", + video_llm_id: preferences.video_llm_id ?? "", }); const [hasChanges, setHasChanges] = useState(false); @@ -127,6 +138,7 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) { agent_llm_id: preferences.agent_llm_id ?? "", document_summary_llm_id: preferences.document_summary_llm_id ?? "", image_generation_config_id: preferences.image_generation_config_id ?? "", + video_llm_id: preferences.video_llm_id ?? "", }; setAssignments(newAssignments); setHasChanges(false); @@ -144,6 +156,7 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) { agent_llm_id: preferences.agent_llm_id ?? "", document_summary_llm_id: preferences.document_summary_llm_id ?? "", image_generation_config_id: preferences.image_generation_config_id ?? "", + video_llm_id: preferences.video_llm_id ?? "", }; const hasChangesNow = Object.keys(newAssignments).some( @@ -165,6 +178,7 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) { agent_llm_id: toNumericOrUndefined(assignments.agent_llm_id), document_summary_llm_id: toNumericOrUndefined(assignments.document_summary_llm_id), image_generation_config_id: toNumericOrUndefined(assignments.image_generation_config_id), + video_llm_id: toNumericOrUndefined(assignments.video_llm_id), }; await updatePreferences({ @@ -183,6 +197,7 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) { agent_llm_id: preferences.agent_llm_id ?? "", document_summary_llm_id: preferences.document_summary_llm_id ?? "", image_generation_config_id: preferences.image_generation_config_id ?? "", + video_llm_id: preferences.video_llm_id ?? "", }); setHasChanges(false); }; diff --git a/surfsense_web/components/tool-ui/generate-video/components/VideoErrorState.tsx b/surfsense_web/components/tool-ui/generate-video/components/VideoErrorState.tsx new file mode 100644 index 000000000..b79a09cc7 --- /dev/null +++ b/surfsense_web/components/tool-ui/generate-video/components/VideoErrorState.tsx @@ -0,0 +1,26 @@ +import { VideoIcon } from "lucide-react"; + +interface VideoErrorStateProps { + title: string; + error: string; +} + +export function VideoErrorState({ title, error }: VideoErrorStateProps) { + return ( +
+
+
+ +
+
+

+ {title} +

+

+ {error} +

+
+
+
+ ); +} diff --git a/surfsense_web/components/tool-ui/generate-video/components/VideoLoadingState.tsx b/surfsense_web/components/tool-ui/generate-video/components/VideoLoadingState.tsx new file mode 100644 index 000000000..917d9d8a3 --- /dev/null +++ b/surfsense_web/components/tool-ui/generate-video/components/VideoLoadingState.tsx @@ -0,0 +1,65 @@ +import { VideoIcon } from "lucide-react"; +import { Spinner } from "@/components/ui/spinner"; + +type VideoStep = "generating_script" | "rendering" | "running"; + +interface VideoLoadingStateProps { + topic: string; + step?: VideoStep; + progress?: number; +} + +const STEP_LABELS: Record = { + running: "Preparing video generation...", + generating_script: "Generating video script with AI...", + rendering: "Rendering video...", +}; + +export function VideoLoadingState({ topic, step = "running", progress }: VideoLoadingStateProps) { + const label = STEP_LABELS[step]; + const showProgress = step === "rendering" && progress !== undefined; + + return ( +
+
+
+
+ +
+ {!showProgress && ( +
+ )} +
+
+

+ {topic} +

+
+ + {label} +
+ {showProgress && ( +
+
+
+
+

+ {Math.round(progress * 100)}% +

+
+ )} + {!showProgress && ( +
+
+
+
+
+ )} +
+
+
+ ); +} diff --git a/surfsense_web/components/tool-ui/generate-video/index.tsx b/surfsense_web/components/tool-ui/generate-video/index.tsx new file mode 100644 index 000000000..162db8f10 --- /dev/null +++ b/surfsense_web/components/tool-ui/generate-video/index.tsx @@ -0,0 +1,190 @@ +"use client"; + +import { makeAssistantToolUI } from "@assistant-ui/react"; +import { DownloadIcon, VideoIcon } from "lucide-react"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { VideoErrorState } from "./components/VideoErrorState"; +import { VideoLoadingState } from "./components/VideoLoadingState"; +import type { GenerateVideoArgs, GenerateVideoResult } from "./types"; +import { + generateVideoScript, + renderVideo, + getRenderProgress, + type VideoInput, + type ProgressResponse, +} from "@/lib/apis/video-api.service"; + +type PipelineState = + | { step: "idle" } + | { step: "generating_script" } + | { step: "rendering"; progress: number } + | { step: "done"; url: string; size: number } + | { step: "error"; message: string }; + +function VideoPlayer({ url, topic, size }: { url: string; topic: string; size?: number }) { + const sizeLabel = size ? `${(size / 1_048_576).toFixed(1)} MB` : null; + + return ( +
+ +
+
+ + {topic} + {sizeLabel && ( + + ({sizeLabel}) + + )} +
+ + + Download + +
+
+ ); +} + +const wait = (ms: number) => new Promise((r) => setTimeout(r, ms)); + +function VideoGenerationPipeline({ + topic, + sourceContent, + searchSpaceId, +}: { + topic: string; + sourceContent: string; + searchSpaceId: number; +}) { + const [state, setState] = useState({ step: "idle" }); + const startedRef = useRef(false); + + const run = useCallback(async () => { + if (startedRef.current) return; + startedRef.current = true; + + try { + setState({ step: "generating_script" }); + const videoInput: VideoInput = await generateVideoScript( + searchSpaceId, + topic, + sourceContent, + ); + + setState({ step: "rendering", progress: 0 }); + const { renderId, bucketName } = await renderVideo(videoInput); + + let pending = true; + while (pending) { + const result: ProgressResponse = await getRenderProgress(renderId, bucketName); + switch (result.type) { + case "error": + setState({ step: "error", message: result.message }); + pending = false; + break; + case "done": + setState({ step: "done", url: result.url, size: result.size }); + pending = false; + break; + case "progress": + setState({ step: "rendering", progress: result.progress }); + await wait(1500); + break; + } + } + } catch (err) { + setState({ + step: "error", + message: err instanceof Error ? err.message : "Video generation failed", + }); + } + }, [topic, sourceContent, searchSpaceId]); + + useEffect(() => { + run(); + }, [run]); + + switch (state.step) { + case "idle": + case "generating_script": + return ; + case "rendering": + return ; + case "done": + return ; + case "error": + return ; + } +} + +export const GenerateVideoToolUI = makeAssistantToolUI({ + toolName: "generate_video", + render: function GenerateVideoUI({ args, result, status }) { + const topic = args.topic || "Video"; + + if (status.type === "running" || status.type === "requires-action") { + return ; + } + + if (status.type === "incomplete") { + const errorMessage = + status.reason === "cancelled" + ? "Video generation cancelled" + : typeof status.error === "string" + ? status.error + : "An error occurred"; + + if (status.reason === "cancelled") { + return ( +
+

+ + {errorMessage} +

+
+ ); + } + + return ; + } + + if (!result) { + return ; + } + + if (result.status === "error") { + return ; + } + + if ( + result.status === "success" && + result.source_content && + result.search_space_id + ) { + return ( + + ); + } + + return ; + }, +}); diff --git a/surfsense_web/components/tool-ui/generate-video/types.ts b/surfsense_web/components/tool-ui/generate-video/types.ts new file mode 100644 index 000000000..9b189d9a9 --- /dev/null +++ b/surfsense_web/components/tool-ui/generate-video/types.ts @@ -0,0 +1,17 @@ +import { z } from "zod"; + +export const GenerateVideoArgsSchema = z.object({ + topic: z.string(), + source_content: z.string(), +}); + +export const GenerateVideoResultSchema = z.object({ + status: z.enum(["success", "error"]), + topic: z.string(), + source_content: z.string().optional(), + search_space_id: z.number().optional(), + error: z.string().optional(), +}); + +export type GenerateVideoArgs = z.infer; +export type GenerateVideoResult = z.infer; diff --git a/surfsense_web/components/tool-ui/index.ts b/surfsense_web/components/tool-ui/index.ts index b6462f80a..725577f85 100644 --- a/surfsense_web/components/tool-ui/index.ts +++ b/surfsense_web/components/tool-ui/index.ts @@ -32,6 +32,7 @@ export { } from "./display-image"; export { GeneratePodcastToolUI } from "./generate-podcast"; export { GenerateReportToolUI } from "./generate-report"; +export { GenerateVideoToolUI } from "./generate-video"; export { CreateGoogleDriveFileToolUI, DeleteGoogleDriveFileToolUI } from "./google-drive"; export { Image, diff --git a/surfsense_web/contracts/types/new-llm-config.types.ts b/surfsense_web/contracts/types/new-llm-config.types.ts index 2814f2f25..3dbc7086b 100644 --- a/surfsense_web/contracts/types/new-llm-config.types.ts +++ b/surfsense_web/contracts/types/new-llm-config.types.ts @@ -263,9 +263,11 @@ export const llmPreferences = z.object({ agent_llm_id: z.union([z.number(), z.null()]).optional(), document_summary_llm_id: z.union([z.number(), z.null()]).optional(), image_generation_config_id: z.union([z.number(), z.null()]).optional(), + video_llm_id: z.union([z.number(), z.null()]).optional(), agent_llm: z.union([z.record(z.string(), z.unknown()), z.null()]).optional(), document_summary_llm: z.union([z.record(z.string(), z.unknown()), z.null()]).optional(), image_generation_config: z.union([z.record(z.string(), z.unknown()), z.null()]).optional(), + video_llm: z.union([z.record(z.string(), z.unknown()), z.null()]).optional(), }); /** @@ -286,6 +288,7 @@ export const updateLLMPreferencesRequest = z.object({ agent_llm_id: true, document_summary_llm_id: true, image_generation_config_id: true, + video_llm_id: true, }), }); diff --git a/surfsense_web/lib/apis/video-api.service.ts b/surfsense_web/lib/apis/video-api.service.ts new file mode 100644 index 000000000..02a4042b6 --- /dev/null +++ b/surfsense_web/lib/apis/video-api.service.ts @@ -0,0 +1,55 @@ +import { baseApiService } from "./base-api.service"; + +export interface VideoInput { + scenes: Record[]; +} + +export async function generateVideoScript( + searchSpaceId: number, + topic: string, + sourceContent: string, + signal?: AbortSignal, +): Promise { + return baseApiService.post( + `/api/v1/video/generate-script?search_space_id=${searchSpaceId}`, + undefined, + { + body: { topic, source_content: sourceContent }, + signal, + }, + ); +} + +export interface RenderResponse { + renderId: string; + bucketName: string; +} + +export type ProgressResponse = + | { type: "error"; message: string } + | { type: "progress"; progress: number } + | { type: "done"; url: string; size: number }; + +async function videoApiRequest(endpoint: string, body: unknown): Promise { + const res = await fetch(endpoint, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(body), + }); + const json = await res.json(); + if (json.type === "error") { + throw new Error(json.message); + } + return json.data; +} + +export async function renderVideo(inputProps: VideoInput): Promise { + return videoApiRequest("/api/video/render", { inputProps }); +} + +export async function getRenderProgress(renderId: string, bucketName: string): Promise { + return videoApiRequest("/api/video/progress", { + id: renderId, + bucketName, + }); +} diff --git a/surfsense_web/package.json b/surfsense_web/package.json index 5505c614f..5e86b739f 100644 --- a/surfsense_web/package.json +++ b/surfsense_web/package.json @@ -72,6 +72,7 @@ "@radix-ui/react-toggle-group": "^1.1.10", "@radix-ui/react-toolbar": "^1.1.11", "@radix-ui/react-tooltip": "^1.2.7", + "@remotion/lambda": "4.0.434", "@streamdown/code": "^1.0.2", "@streamdown/math": "^1.0.2", "@tabler/icons-react": "^3.34.1", diff --git a/surfsense_web/pnpm-lock.yaml b/surfsense_web/pnpm-lock.yaml index 26ba0f2ea..71772b50f 100644 --- a/surfsense_web/pnpm-lock.yaml +++ b/surfsense_web/pnpm-lock.yaml @@ -161,6 +161,9 @@ importers: '@radix-ui/react-tooltip': specifier: ^1.2.7 version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/lambda': + specifier: 4.0.434 + version: 4.0.434(@remotion/bundler@4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@streamdown/code': specifier: ^1.0.2 version: 1.0.3(react@19.2.4) @@ -223,7 +226,7 @@ importers: version: 16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.477.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6) fumadocs-mdx: specifier: ^14.2.1 - version: 14.2.8(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.14)(fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.477.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(vite@7.3.1(@types/node@20.19.33)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)) + version: 14.2.8(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.14)(fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.477.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(vite@7.3.1(@types/node@20.19.33)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)) fumadocs-ui: specifier: ^16.3.1 version: 16.6.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.477.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tailwindcss@4.2.1) @@ -407,7 +410,7 @@ importers: version: 5.9.3 vite: specifier: ^7.3.1 - version: 7.3.1(@types/node@20.19.33)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0) + version: 7.3.1(@types/node@20.19.33)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0) packages: @@ -525,6 +528,203 @@ packages: react: optional: true + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/crc32c@5.2.0': + resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} + + '@aws-crypto/sha1-browser@5.2.0': + resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/client-cloudwatch-logs@3.986.0': + resolution: {integrity: sha512-i1rpVE4gn7t1lakamh2EfePKJrG2sJM8y3o9xsEzm33oDpWWERUWO/ojB4gBAF1oCguMDqkL2l3cFJKtz0PivQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-iam@3.986.0': + resolution: {integrity: sha512-Xww27O4Zb+KSoBqCu7703o1IelX/oUEiIk40LS2l84RT8/YdYrfwlO9Q6U3qevcRTDPv05IEUU3tVj3WwgcaVg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-lambda@3.986.0': + resolution: {integrity: sha512-R0VrqSH622b0MmIULLCNbupyU9qqEn+vofIeKng+ALPJY6U7pq7MG0p+bbxLCGztLl0u2vmO237SPZYcFm3hCQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-s3@3.986.0': + resolution: {integrity: sha512-IcDJ8shVVvbxgMe8+dLWcv6uhSwmX65PHTVGX81BhWAElPnp3CL8w/5uzOPRo4n4/bqIk9eskGVEIicw2o+SrA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-service-quotas@3.986.0': + resolution: {integrity: sha512-Pa6CSHBID+ystd1s8iMoF8tYay9YavuLefo6RO6qH65tApbn7qSUIICZvXCjFU1hVPb/zXLHl+LYo1JOcrR12Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-sts@3.986.0': + resolution: {integrity: sha512-MKL3OSaUXJ4Xftl+sm7n7o8pJmX5FQgIFc/suWPoNvKEdMJOC+/+2DYjjpASw5X89LL4+9xL2BHhz0y32m5FYw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.973.18': + resolution: {integrity: sha512-GUIlegfcK2LO1J2Y98sCJy63rQSiLiDOgVw7HiHPRqfI2vb3XozTVqemwO0VSGXp54ngCnAQz0Lf0YPCBINNxA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/crc64-nvme@3.972.0': + resolution: {integrity: sha512-ThlLhTqX68jvoIVv+pryOdb5coP1cX1/MaTbB9xkGDCbWbsqQcLqzPxuSoW1DCnAAIacmXCWpzUNOB9pv+xXQw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.972.16': + resolution: {integrity: sha512-HrdtnadvTGAQUr18sPzGlE5El3ICphnH6SU7UQOMOWFgRKbTRNN8msTxM4emzguUso9CzaHU2xy5ctSrmK5YNA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-http@3.972.18': + resolution: {integrity: sha512-NyB6smuZAixND5jZumkpkunQ0voc4Mwgkd+SZ6cvAzIB7gK8HV8Zd4rS8Kn5MmoGgusyNfVGG+RLoYc4yFiw+A==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-ini@3.972.16': + resolution: {integrity: sha512-hzAnzNXKV0A4knFRWGu2NCt72P4WWxpEGnOc6H3DptUjC4oX3hGw846oN76M1rTHAOwDdbhjU0GAOWR4OUfTZg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-login@3.972.16': + resolution: {integrity: sha512-VI0kXTlr0o1FTay+Jvx6AKqx5ECBgp7X4VevGBEbuXdCXnNp7SPU0KvjsOLVhIz3OoPK4/lTXphk43t0IVk65w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-node@3.972.17': + resolution: {integrity: sha512-98MAcQ2Dk7zkvgwZ5f6fLX2lTyptC3gTSDx4EpvTdJWET8qs9lBPYggoYx7GmKp/5uk0OwVl0hxIDZsDNS/Y9g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-process@3.972.16': + resolution: {integrity: sha512-n89ibATwnLEg0ZdZmUds5bq8AfBAdoYEDpqP3uzPLaRuGelsKlIvCYSNNvfgGLi8NaHPNNhs1HjJZYbqkW9b+g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-sso@3.972.16': + resolution: {integrity: sha512-b9of7tQgERxgcEcwAFWvRe84ivw+Kw6b3jVuz/6LQzonkomiY5UoWfprkbjc8FSCQ2VjDqKTvIRA9F0KSQ025w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.972.16': + resolution: {integrity: sha512-PaOH5jFoPQX4WkqpKzKh9cM7rieKtbgEGqrZ+ybGmotJhcvhI/xl69yCwMbHGnpQJJmHZIX9q2zaPB7HTBn/4w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/lib-storage@3.986.0': + resolution: {integrity: sha512-tcP8NmpBidRHNhGqRQWEHG1vEsMTjOLd28aIS8dfhaFIAxFGMe5CH+fXunYE5ie52NZ5pLUrCvtAnwfBfSBbUw==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@aws-sdk/client-s3': ^3.986.0 + + '@aws-sdk/middleware-bucket-endpoint@3.972.7': + resolution: {integrity: sha512-goX+axlJ6PQlRnzE2bQisZ8wVrlm6dXJfBzMJhd8LhAIBan/w1Kl73fJnalM/S+18VnpzIHumyV6DtgmvqG5IA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-expect-continue@3.972.7': + resolution: {integrity: sha512-mvWqvm61bmZUKmmrtl2uWbokqpenY3Mc3Jf4nXB/Hse6gWxLPaCQThmhPBDzsPSV8/Odn8V6ovWt3pZ7vy4BFQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-flexible-checksums@3.972.5': + resolution: {integrity: sha512-SF/1MYWx67OyCrLA4icIpWUfCkdlOi8Y1KecQ9xYxkL10GMjVdPTGPnYhAg0dw5U43Y9PVUWhAV2ezOaG+0BLg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-host-header@3.972.7': + resolution: {integrity: sha512-aHQZgztBFEpDU1BB00VWCIIm85JjGjQW1OG9+98BdmaOpguJvzmXBGbnAiYcciCd+IS4e9BEq664lhzGnWJHgQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-location-constraint@3.972.7': + resolution: {integrity: sha512-vdK1LJfffBp87Lj0Bw3WdK1rJk9OLDYdQpqoKgmpIZPe+4+HawZ6THTbvjhJt4C4MNnRrHTKHQjkwBiIpDBoig==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-logger@3.972.7': + resolution: {integrity: sha512-LXhiWlWb26txCU1vcI9PneESSeRp/RYY/McuM4SpdrimQR5NgwaPb4VJCadVeuGWgh6QmqZ6rAKSoL1ob16W6w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.972.7': + resolution: {integrity: sha512-l2VQdcBcYLzIzykCHtXlbpiVCZ94/xniLIkAj0jpnpjY4xlgZx7f56Ypn+uV1y3gG0tNVytJqo3K9bfMFee7SQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.972.18': + resolution: {integrity: sha512-5E3XxaElrdyk6ZJ0TjH7Qm6ios4b/qQCiLr6oQ8NK7e4Kn6JBTJCaYioQCQ65BpZ1+l1mK5wTAac2+pEz0Smpw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-ssec@3.972.7': + resolution: {integrity: sha512-G9clGVuAml7d8DYzY6DnRi7TIIDRvZ3YpqJPz/8wnWS5fYx/FNWNmkO6iJVlVkQg9BfeMzd+bVPtPJOvC4B+nQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-user-agent@3.972.18': + resolution: {integrity: sha512-KcqQDs/7WtoEnp52+879f8/i1XAJkgka5i4arOtOCPR10o4wWo3VRecDI9Gxoh6oghmLCnIiOSKyRcXI/50E+w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/nested-clients@3.996.6': + resolution: {integrity: sha512-blNJ3ugn4gCQ9ZSZi/firzKCvVl5LvPFVxv24LprENeWI4R8UApG006UQkF4SkmLygKq2BQXRad2/anQ13Te4Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/region-config-resolver@3.972.7': + resolution: {integrity: sha512-/Ev/6AI8bvt4HAAptzSjThGUMjcWaX3GX8oERkB0F0F9x2dLSBdgFDiyrRz3i0u0ZFZFQ1b28is4QhyqXTUsVA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/s3-request-presigner@3.986.0': + resolution: {integrity: sha512-+yopxtoXwRXZ2Ai9H4GzkN+T2D07sGrURYcm7Eh2OQe3p+Ys/3VrR6UrzILssaJGYtR2vQqVKnGJBHVYqaM1EQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.986.0': + resolution: {integrity: sha512-Upw+rw7wCH93E6QWxqpAqJLrUmJYVUAWrk4tCOBnkeuwzGERZvJFL5UQ6TAJFj9T18Ih+vNFaACh8J5aP4oTBw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1003.0': + resolution: {integrity: sha512-SOyyWNdT7njKRwtZ1JhwHlH1csv6Pkgf305X96/OIfnhq1pU/EjmT6W6por57rVrjrKuHBuEIXgpWv8OgoMHpg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.973.5': + resolution: {integrity: sha512-hl7BGwDCWsjH8NkZfx+HgS7H2LyM2lTMAI7ba9c8O0KqdBLTdNJivsHpqjg9rNlAlPyREb6DeDRXUl0s8uFdmQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-arn-parser@3.972.3': + resolution: {integrity: sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-endpoints@3.986.0': + resolution: {integrity: sha512-Mqi79L38qi1gCG3adlVdbNrSxvcm1IPDLiJPA3OBypY5ewxUyWbaA3DD4goG+EwET6LSFgZJcRSIh6KBNpP5pA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-endpoints@3.996.4': + resolution: {integrity: sha512-Hek90FBmd4joCFj+Vc98KLJh73Zqj3s2W56gjAcTkrNLMDI5nIFkG9YpfcJiVI1YlE2Ne1uOQNe+IgQ/Vz2XRA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-format-url@3.972.7': + resolution: {integrity: sha512-V+PbnWfUl93GuFwsOHsAq7hY/fnm9kElRqR8IexIJr5Rvif9e614X5sGSyz3mVSf1YAZ+VTy63W1/pGdA55zyA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-locate-window@3.965.5': + resolution: {integrity: sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-user-agent-browser@3.972.7': + resolution: {integrity: sha512-7SJVuvhKhMF/BkNS1n0QAJYgvEwYbK2QLKBrzDiwQGiTRU6Yf1f3nehTzm/l21xdAOtWSfp2uWSddPnP2ZtsVw==} + + '@aws-sdk/util-user-agent-node@3.973.3': + resolution: {integrity: sha512-8s2cQmTUOwcBlIJyI9PAZNnnnF+cGtdhHc1yzMMsSD/GR/Hxj7m0IGUE92CslXXb8/p5Q76iqOCjN1GFwyf+1A==} + engines: {node: '>=20.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/xml-builder@3.972.10': + resolution: {integrity: sha512-OnejAIVD+CxzyAUrVic7lG+3QRltyja9LoNqCE/1YVs8ichoTbJlVSaZ9iSMcnHLyzrSNtvaOGjSDRP+d/ouFA==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.2.3': + resolution: {integrity: sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==} + engines: {node: '>=18.0.0'} + '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} @@ -628,6 +828,11 @@ packages: resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} engines: {node: '>=6.9.0'} + '@babel/parser@7.24.1': + resolution: {integrity: sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/parser@7.29.0': resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} engines: {node: '>=6.0.0'} @@ -1180,6 +1385,12 @@ packages: resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} deprecated: 'Merged into tsx: https://tsx.is' + '@esbuild/aix-ppc64@0.25.0': + resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} @@ -1198,6 +1409,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.25.0': + resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.25.12': resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} engines: {node: '>=18'} @@ -1216,6 +1433,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.25.0': + resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.25.12': resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} engines: {node: '>=18'} @@ -1234,6 +1457,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.25.0': + resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.25.12': resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} engines: {node: '>=18'} @@ -1252,6 +1481,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.25.0': + resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.25.12': resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} engines: {node: '>=18'} @@ -1270,6 +1505,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.25.0': + resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.25.12': resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} engines: {node: '>=18'} @@ -1288,6 +1529,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.25.0': + resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.25.12': resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} engines: {node: '>=18'} @@ -1306,6 +1553,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.25.0': + resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.25.12': resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} engines: {node: '>=18'} @@ -1324,6 +1577,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.25.0': + resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.25.12': resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} engines: {node: '>=18'} @@ -1342,6 +1601,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.25.0': + resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.25.12': resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} engines: {node: '>=18'} @@ -1360,6 +1625,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.25.0': + resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.25.12': resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} engines: {node: '>=18'} @@ -1378,6 +1649,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.25.0': + resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.25.12': resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} engines: {node: '>=18'} @@ -1396,6 +1673,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.25.0': + resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.25.12': resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} engines: {node: '>=18'} @@ -1414,6 +1697,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.25.0': + resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.25.12': resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} engines: {node: '>=18'} @@ -1432,6 +1721,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.25.0': + resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.25.12': resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} engines: {node: '>=18'} @@ -1450,6 +1745,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.25.0': + resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.25.12': resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} engines: {node: '>=18'} @@ -1468,6 +1769,12 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.25.0': + resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.25.12': resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} engines: {node: '>=18'} @@ -1480,6 +1787,12 @@ packages: cpu: [x64] os: [linux] + '@esbuild/netbsd-arm64@0.25.0': + resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-arm64@0.25.12': resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} engines: {node: '>=18'} @@ -1498,6 +1811,12 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.25.0': + resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.25.12': resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} engines: {node: '>=18'} @@ -1510,6 +1829,12 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/openbsd-arm64@0.25.0': + resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-arm64@0.25.12': resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} engines: {node: '>=18'} @@ -1528,6 +1853,12 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.25.0': + resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.25.12': resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} engines: {node: '>=18'} @@ -1558,6 +1889,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.25.0': + resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.25.12': resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} engines: {node: '>=18'} @@ -1576,6 +1913,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.25.0': + resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.25.12': resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} engines: {node: '>=18'} @@ -1594,6 +1937,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.25.0': + resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.25.12': resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} engines: {node: '>=18'} @@ -1612,6 +1961,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.25.0': + resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.25.12': resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} engines: {node: '>=18'} @@ -1874,6 +2229,9 @@ packages: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} @@ -1886,12 +2244,48 @@ packages: '@mdx-js/mdx@3.1.1': resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} + '@mediabunny/aac-encoder@1.37.0': + resolution: {integrity: sha512-mYnF1sObnPE+7+QWn9H7c0rbl5Dwu50JejUD/GJefRl8ozYp0sz3tt+zBLCeif5GXBkPhABIX3JVg1eGfqx6tg==} + peerDependencies: + mediabunny: ^1.0.0 + + '@mediabunny/flac-encoder@1.37.0': + resolution: {integrity: sha512-VwKIL5p1WZE4dSwZ1SVv/bd2ksul8a4run4S1eEbPRysnG87nmCXddO5ajD3b2k2478XWitKnVDXl/kxdIIWBw==} + peerDependencies: + mediabunny: ^1.0.0 + + '@mediabunny/mp3-encoder@1.37.0': + resolution: {integrity: sha512-6tXBO3iHDA55WiMhOoaOmeCCOQ51U38mdXRxYNS9/hUCpR0ScRo+NtWu2YUa/jp2q99JNPrA4yYjahE5xHDxpg==} + peerDependencies: + mediabunny: ^1.0.0 + '@microsoft/fetch-event-source@2.0.1': resolution: {integrity: sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA==} + '@module-federation/error-codes@0.22.0': + resolution: {integrity: sha512-xF9SjnEy7vTdx+xekjPCV5cIHOGCkdn3pIxo9vU7gEZMIw0SvAEdsy6Uh17xaCpm8V0FWvR0SZoK9Ik6jGOaug==} + + '@module-federation/runtime-core@0.22.0': + resolution: {integrity: sha512-GR1TcD6/s7zqItfhC87zAp30PqzvceoeDGYTgF3Vx2TXvsfDrhP6Qw9T4vudDQL3uJRne6t7CzdT29YyVxlgIA==} + + '@module-federation/runtime-tools@0.22.0': + resolution: {integrity: sha512-4ScUJ/aUfEernb+4PbLdhM/c60VHl698Gn1gY21m9vyC1Ucn69fPCA1y2EwcCB7IItseRMoNhdcWQnzt/OPCNA==} + + '@module-federation/runtime@0.22.0': + resolution: {integrity: sha512-38g5iPju2tPC3KHMPxRKmy4k4onNp6ypFPS1eKGsNLUkXgHsPMBFqAjDw96iEcjri91BrahG4XcdyKi97xZzlA==} + + '@module-federation/sdk@0.22.0': + resolution: {integrity: sha512-x4aFNBKn2KVQRuNVC5A7SnrSCSqyfIWmm1DvubjbO9iKFe7ith5niw8dqSFBekYBg2Fwy+eMg4sEFNVvCAdo6g==} + + '@module-federation/webpack-bundler-runtime@0.22.0': + resolution: {integrity: sha512-aM8gCqXu+/4wBmJtVeMeeMN5guw3chf+2i6HajKtQv7SJfxV/f4IyNQJUeUQu9HfiAZHjqtMV5Lvq/Lvh8LdyA==} + '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + '@napi-rs/wasm-runtime@1.0.7': + resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} + '@next/env@16.1.6': resolution: {integrity: sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==} @@ -3323,6 +3717,121 @@ packages: '@react-dnd/shallowequal@4.0.2': resolution: {integrity: sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==} + '@remotion/bundler@4.0.434': + resolution: {integrity: sha512-S62GkQnMbS/svtNTxZwWvKE7oCSVGaeZllMMig07bFXsf+xLbgfZDBhe4iMqdOyk9+++OVmwgz0qNo+1q2r0FQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/cli@4.0.434': + resolution: {integrity: sha512-yuZYBQ1mXCJJaQHAf60Y1HupCWGqIM3hV+BYrGwx/TiXqv1GVMU4On5yuJSGVKoOc8ZTZkorOB0gNzB8fzC8qg==} + hasBin: true + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/compositor-darwin-arm64@4.0.434': + resolution: {integrity: sha512-9nJgIQcUrOYhr9EsbvCMNhC7g/HD/R+cDPsatFaD2FONHHNLKLZQp/wHGyPfcriblvoxy2hRjfqV8Z7nmkaWAQ==} + cpu: [arm64] + os: [darwin] + + '@remotion/compositor-darwin-x64@4.0.434': + resolution: {integrity: sha512-oIlEnStUCdVQbP5DB0GSXCavoWUaSpd+1z1VsSd7x9QfHiv4BsVjit3Say/HejoJB27YxuSzol47qhRDrruPEA==} + cpu: [x64] + os: [darwin] + + '@remotion/compositor-linux-arm64-gnu@4.0.434': + resolution: {integrity: sha512-928YpjfSKcVBIIJl1HI7KYiYYYQpsxdVldzvDjl3eWViqExeErzsS/R0lH/cDSaxtHttFDw+FgKr3RKsX9Yc8Q==} + cpu: [arm64] + os: [linux] + + '@remotion/compositor-linux-arm64-musl@4.0.434': + resolution: {integrity: sha512-8xEEhKk0E+dTtHnr/ESIZX1USBc6w/pbsxddzLFtaQLsfyiM+lfpvL/j/gYVdTZbqKe9idOkX14n3GlIu4N9rA==} + cpu: [arm64] + os: [linux] + + '@remotion/compositor-linux-x64-gnu@4.0.434': + resolution: {integrity: sha512-zvzLVeSK08j9U0z2FokwQ4DXAdbvL5oUboikR7G5l4e+ziYGx7dukCsTBDBb5Egd8eT+497viaWNSi1dK7hY0Q==} + cpu: [x64] + os: [linux] + + '@remotion/compositor-linux-x64-musl@4.0.434': + resolution: {integrity: sha512-Dze2Dsu+1oMmBrTQXWIfL+PwlA7XG2wBoaIE2W6/UAWAdKRb7vRybenDT7vIL9ErVREnVDxwHLF4NwpB2WeMFA==} + cpu: [x64] + os: [linux] + + '@remotion/compositor-win32-x64-msvc@4.0.434': + resolution: {integrity: sha512-3PmLo03LBYf123FvjlUNphbtnTJnvToHMDgBQYdrlQBGbAQpJGLsX7IViQwWSNFGZVzonwliNtC9568Nbts8lw==} + cpu: [x64] + os: [win32] + + '@remotion/lambda-client@4.0.434': + resolution: {integrity: sha512-HGSQxejOYI0NC0RlDpMA+ZbdRa9aH6CQyv0rwUvD0Etal57L7DrhISVV0gkR711A48rtutfOpqXDZhc/BoiVfQ==} + + '@remotion/lambda@4.0.434': + resolution: {integrity: sha512-l4ru024+0biM7uEilweZGqvl5ijpw/jmy+wgfriBLEcOXSy3ep8MvjG/+KEj1fUN3mDkm9EJQcTB9OsbqsmtdQ==} + peerDependencies: + '@remotion/bundler': 4.0.434 + + '@remotion/licensing@4.0.434': + resolution: {integrity: sha512-Jp9pHUlQBOX7ZjDuG9OVIEDGU5JiN2eSjvHEDS8zwyIIGUaRwDhZRzvGULEiZpu1IJ6UMDpHqIylJ18Inl1PMg==} + + '@remotion/media-parser@4.0.434': + resolution: {integrity: sha512-+vuVOTd3v9BR2WQE3N4CUsCCUb4z8U1bvvxKrP6NGL9nqudu6IQpueXHNwxc/RzDTaxJ1aH6xOLzB3Iqc6EnIA==} + + '@remotion/media-utils@4.0.434': + resolution: {integrity: sha512-3wBtgZSBNXqwQFqQp6eLwsw3m32zlGhZHsD8bn1TeBJQkHflUXo3WhmGkLoUoZdU8nGutFOoFtckjVq1DEXzpg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/player@4.0.434': + resolution: {integrity: sha512-H3SugDjf0QS7lU85A/hej+zUqJLRd0EHfW0jBsu4kerYVXZqi2vswqf7MRqEwQhMGtySVrhI+n9fGBqfcAbRpw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/renderer@4.0.434': + resolution: {integrity: sha512-I0MT0A6YHpO420ntQjF0KzERetpYcnsHlMNUWlNFDeAzFuEjrOgUcZ+6NcUV1lX9nOhixLANvfuMpCrdZazdYg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/serverless-client@4.0.434': + resolution: {integrity: sha512-GSB0Qe538dsVt4nfnm+fNIkvCfTpTym+jfrM7g6uZQ5BeqCa/Sg0ZP4uXTUY8S+/Uwk0FhN5jiWTBOkOSmzW+A==} + + '@remotion/serverless@4.0.434': + resolution: {integrity: sha512-18YKrx++q6UhA2LDPsQmtEPec3rIFdQYHAmPru36k3FC4G2BZv0qdNVFxF2g2prC1NCcA//eVO2YRmhuM9K1+Q==} + + '@remotion/streaming@4.0.434': + resolution: {integrity: sha512-Qlb7sSSXs3nkbs/C/62fLIn/ZxUiNdqtnim3AzAMmi9HdC1qcCMAJiQfugnpnKQ57xbS5Okp1HzSPQalpcgXgw==} + + '@remotion/studio-server@4.0.434': + resolution: {integrity: sha512-k1j7he3H6H19t0iiJ6qNRqzJiGqLzpSfFoi7Hb1NVAwFbcrBOYCsRh/sr+ecL5VMAcWZf2s+cgnaL+mZD6sCrQ==} + + '@remotion/studio-shared@4.0.434': + resolution: {integrity: sha512-5HL2ciT8BpGW8izhwOehMBVh2fR+/2X5EjoIUlWrXoDWpSgzSXmT8yI2nkKmBCxbZyYyyW/pQZKkvDj56jcB2Q==} + + '@remotion/studio@4.0.434': + resolution: {integrity: sha512-faNm11hVsjFyjNg9R7TQZcjsDflUfqkWGgbrq5jSubpVJD+eHEVBo1HIZwrDHIlvee8gTyAscR0tFrH27PLShQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/web-renderer@4.0.434': + resolution: {integrity: sha512-yYLQ/BbIlHd61pFH0B979UYWUnSMwubXvf1jeyu6w7/44Z2ZbI4h05fzzvIKEr9XZFZSVntBWeFUHuNCf/Tomg==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@remotion/webcodecs@4.0.434': + resolution: {integrity: sha512-Eq/3EB9w0L2F4Q2lgTXcc7maoGYn0nkfvoKclVkA4N6gsfgNand+SX/nDe8n8JMOD8NsEKOs5EEd3YWYY3oN5A==} + + '@remotion/zod-types@4.0.434': + resolution: {integrity: sha512-5urhO+1NYCWvddy7cPdqqfHH+VLHP1+5pREO8fqwemIU6jmJR9SFR0vDgMa9wyrQfGyQA7Q0sTMX73KInwszFg==} + peerDependencies: + zod: 4.3.6 + '@rollup/rollup-android-arm-eabi@4.59.0': resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} cpu: [arm] @@ -3448,20 +3957,93 @@ packages: cpu: [x64] os: [win32] - '@rtsao/scc@1.1.0': - resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} - - '@rushstack/eslint-patch@1.16.1': - resolution: {integrity: sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==} + '@rspack/binding-darwin-arm64@1.7.6': + resolution: {integrity: sha512-NZ9AWtB1COLUX1tA9HQQvWpTy07NSFfKBU8A6ylWd5KH8AePZztpNgLLAVPTuNO4CZXYpwcoclf8jG/luJcQdQ==} + cpu: [arm64] + os: [darwin] - '@schummar/icu-type-parser@1.21.5': - resolution: {integrity: sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==} + '@rspack/binding-darwin-x64@1.7.6': + resolution: {integrity: sha512-J2g6xk8ZS7uc024dNTGTHxoFzFovAZIRixUG7PiciLKTMP78svbSSWrmW6N8oAsAkzYfJWwQpVgWfFNRHvYxSw==} + cpu: [x64] + os: [darwin] - '@shikijs/core@3.22.0': - resolution: {integrity: sha512-iAlTtSDDbJiRpvgL5ugKEATDtHdUVkqgHDm/gbD2ZS9c88mx7G1zSYjjOxp5Qa0eaW0MAQosFRmJSk354PRoQA==} + '@rspack/binding-linux-arm64-gnu@1.7.6': + resolution: {integrity: sha512-eQfcsaxhFrv5FmtaA7+O1F9/2yFDNIoPZzV/ZvqvFz5bBXVc4FAm/1fVpBg8Po/kX1h0chBc7Xkpry3cabFW8w==} + cpu: [arm64] + os: [linux] - '@shikijs/engine-javascript@3.22.0': - resolution: {integrity: sha512-jdKhfgW9CRtj3Tor0L7+yPwdG3CgP7W+ZEqSsojrMzCjD1e0IxIbwUMDDpYlVBlC08TACg4puwFGkZfLS+56Tw==} + '@rspack/binding-linux-arm64-musl@1.7.6': + resolution: {integrity: sha512-DfQXKiyPIl7i1yECHy4eAkSmlUzzsSAbOjgMuKn7pudsWf483jg0UUYutNgXSlBjc/QSUp7906Cg8oty9OfwPA==} + cpu: [arm64] + os: [linux] + + '@rspack/binding-linux-x64-gnu@1.7.6': + resolution: {integrity: sha512-NdA+2X3lk2GGrMMnTGyYTzM3pn+zNjaqXqlgKmFBXvjfZqzSsKq3pdD1KHZCd5QHN+Fwvoszj0JFsquEVhE1og==} + cpu: [x64] + os: [linux] + + '@rspack/binding-linux-x64-musl@1.7.6': + resolution: {integrity: sha512-rEy6MHKob02t/77YNgr6dREyJ0e0tv1X6Xsg8Z5E7rPXead06zefUbfazj4RELYySWnM38ovZyJAkPx/gOn3VA==} + cpu: [x64] + os: [linux] + + '@rspack/binding-wasm32-wasi@1.7.6': + resolution: {integrity: sha512-YupOrz0daSG+YBbCIgpDgzfMM38YpChv+afZpaxx5Ml7xPeAZIIdgWmLHnQ2rts73N2M1NspAiBwV00Xx0N4Vg==} + cpu: [wasm32] + + '@rspack/binding-win32-arm64-msvc@1.7.6': + resolution: {integrity: sha512-INj7aVXjBvlZ84kEhSK4kJ484ub0i+BzgnjDWOWM1K+eFYDZjLdAsQSS3fGGXwVc3qKbPIssFfnftATDMTEJHQ==} + cpu: [arm64] + os: [win32] + + '@rspack/binding-win32-ia32-msvc@1.7.6': + resolution: {integrity: sha512-lXGvC+z67UMcw58In12h8zCa9IyYRmuptUBMItQJzu+M278aMuD1nETyGLL7e4+OZ2lvrnnBIcjXN1hfw2yRzw==} + cpu: [ia32] + os: [win32] + + '@rspack/binding-win32-x64-msvc@1.7.6': + resolution: {integrity: sha512-zeUxEc0ZaPpmaYlCeWcjSJUPuRRySiSHN23oJ2Xyw0jsQ01Qm4OScPdr0RhEOFuK/UE+ANyRtDo4zJsY52Hadw==} + cpu: [x64] + os: [win32] + + '@rspack/binding@1.7.6': + resolution: {integrity: sha512-/NrEcfo8Gx22hLGysanrV6gHMuqZSxToSci/3M4kzEQtF5cPjfOv5pqeLK/+B6cr56ul/OmE96cCdWcXeVnFjQ==} + + '@rspack/core@1.7.6': + resolution: {integrity: sha512-Iax6UhrfZqJajA778c1d5DBFbSIqPOSrI34kpNIiNpWd8Jq7mFIa+Z60SQb5ZQDZuUxcCZikjz5BxinFjTkg7Q==} + engines: {node: '>=18.12.0'} + peerDependencies: + '@swc/helpers': '>=0.5.1' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@rspack/lite-tapable@1.1.0': + resolution: {integrity: sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==} + + '@rspack/plugin-react-refresh@1.6.1': + resolution: {integrity: sha512-eqqW5645VG3CzGzFgNg5HqNdHVXY+567PGjtDhhrM8t67caxmsSzRmT5qfoEIfBcGgFkH9vEg7kzXwmCYQdQDw==} + peerDependencies: + react-refresh: '>=0.10.0 <1.0.0' + webpack-hot-middleware: 2.x + peerDependenciesMeta: + webpack-hot-middleware: + optional: true + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@rushstack/eslint-patch@1.16.1': + resolution: {integrity: sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==} + + '@schummar/icu-type-parser@1.21.5': + resolution: {integrity: sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==} + + '@shikijs/core@3.22.0': + resolution: {integrity: sha512-iAlTtSDDbJiRpvgL5ugKEATDtHdUVkqgHDm/gbD2ZS9c88mx7G1zSYjjOxp5Qa0eaW0MAQosFRmJSk354PRoQA==} + + '@shikijs/engine-javascript@3.22.0': + resolution: {integrity: sha512-jdKhfgW9CRtj3Tor0L7+yPwdG3CgP7W+ZEqSsojrMzCjD1e0IxIbwUMDDpYlVBlC08TACg4puwFGkZfLS+56Tw==} '@shikijs/engine-oniguruma@3.22.0': resolution: {integrity: sha512-DyXsOG0vGtNtl7ygvabHd7Mt5EY8gCNqR9Y7Lpbbd/PbJvgWrqaKzH1JW6H6qFkuUa8aCxoiYVv8/YfFljiQxA==} @@ -3484,6 +4066,226 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@smithy/abort-controller@4.0.1': + resolution: {integrity: sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g==} + engines: {node: '>=18.0.0'} + + '@smithy/abort-controller@4.2.11': + resolution: {integrity: sha512-Hj4WoYWMJnSpM6/kchsm4bUNTL9XiSyhvoMb2KIq4VJzyDt7JpGHUZHkVNPZVC7YE1tf8tPeVauxpFBKGW4/KQ==} + engines: {node: '>=18.0.0'} + + '@smithy/chunked-blob-reader-native@4.2.3': + resolution: {integrity: sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw==} + engines: {node: '>=18.0.0'} + + '@smithy/chunked-blob-reader@5.2.2': + resolution: {integrity: sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw==} + engines: {node: '>=18.0.0'} + + '@smithy/config-resolver@4.4.10': + resolution: {integrity: sha512-IRTkd6ps0ru+lTWnfnsbXzW80A8Od8p3pYiZnW98K2Hb20rqfsX7VTlfUwhrcOeSSy68Gn9WBofwPuw3e5CCsg==} + engines: {node: '>=18.0.0'} + + '@smithy/core@3.23.8': + resolution: {integrity: sha512-f7uPeBi7ehmLT4YF2u9j3qx6lSnurG1DLXOsTtJrIRNDF7VXio4BGHQ+SQteN/BrUVudbkuL4v7oOsRCzq4BqA==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.2.11': + resolution: {integrity: sha512-lBXrS6ku0kTj3xLmsJW0WwqWbGQ6ueooYyp/1L9lkyT0M02C+DWwYwc5aTyXFbRaK38ojALxNixg+LxKSHZc0g==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-codec@4.2.11': + resolution: {integrity: sha512-Sf39Ml0iVX+ba/bgMPxaXWAAFmHqYLTmbjAPfLPLY8CrYkRDEqZdUsKC1OwVMCdJXfAt0v4j49GIJ8DoSYAe6w==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-browser@4.2.11': + resolution: {integrity: sha512-3rEpo3G6f/nRS7fQDsZmxw/ius6rnlIpz4UX6FlALEzz8JoSxFmdBt0SZnthis+km7sQo6q5/3e+UJcuQivoXA==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-config-resolver@4.3.11': + resolution: {integrity: sha512-XeNIA8tcP/GDWnnKkO7qEm/bg0B/bP9lvIXZBXcGZwZ+VYM8h8k9wuDvUODtdQ2Wcp2RcBkPTCSMmaniVHrMlA==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-node@4.2.11': + resolution: {integrity: sha512-fzbCh18rscBDTQSCrsp1fGcclLNF//nJyhjldsEl/5wCYmgpHblv5JSppQAyQI24lClsFT0wV06N1Porn0IsEw==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-universal@4.2.11': + resolution: {integrity: sha512-MJ7HcI+jEkqoWT5vp+uoVaAjBrmxBtKhZTeynDRG/seEjJfqyg3SiqMMqyPnAMzmIfLaeJ/uiuSDP/l9AnMy/Q==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.3.13': + resolution: {integrity: sha512-U2Hcfl2s3XaYjikN9cT4mPu8ybDbImV3baXR0PkVlC0TTx808bRP3FaPGAzPtB8OByI+JqJ1kyS+7GEgae7+qQ==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-blob-browser@4.2.12': + resolution: {integrity: sha512-1wQE33DsxkM/waftAhCH9VtJbUGyt1PJ9YRDpOu+q9FUi73LLFUZ2fD8A61g2mT1UY9k7b99+V1xZ41Rz4SHRQ==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-node@4.2.11': + resolution: {integrity: sha512-T+p1pNynRkydpdL015ruIoyPSRw9e/SQOWmSAMmmprfswMrd5Ow5igOWNVlvyVFZlxXqGmyH3NQwfwy8r5Jx0A==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-stream-node@4.2.11': + resolution: {integrity: sha512-hQsTjwPCRY8w9GK07w1RqJi3e+myh0UaOWBBhZ1UMSDgofH/Q1fEYzU1teaX6HkpX/eWDdm7tAGR0jBPlz9QEQ==} + engines: {node: '>=18.0.0'} + + '@smithy/invalid-dependency@4.2.11': + resolution: {integrity: sha512-cGNMrgykRmddrNhYy1yBdrp5GwIgEkniS7k9O1VLB38yxQtlvrxpZtUVvo6T4cKpeZsriukBuuxfJcdZQc/f/g==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@4.2.2': + resolution: {integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==} + engines: {node: '>=18.0.0'} + + '@smithy/md5-js@4.2.11': + resolution: {integrity: sha512-350X4kGIrty0Snx2OWv7rPM6p6vM7RzryvFs6B/56Cux3w3sChOb3bymo5oidXJlPcP9fIRxGUCk7GqpiSOtng==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-content-length@4.2.11': + resolution: {integrity: sha512-UvIfKYAKhCzr4p6jFevPlKhQwyQwlJ6IeKLDhmV1PlYfcW3RL4ROjNEDtSik4NYMi9kDkH7eSwyTP3vNJ/u/Dw==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-endpoint@4.4.22': + resolution: {integrity: sha512-sc81w1o4Jy+/MAQlY3sQ8C7CmSpcvIi3TAzXblUv2hjG11BBSJi/Cw8vDx5BxMxapuH2I+Gc+45vWsgU07WZRQ==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-retry@4.4.39': + resolution: {integrity: sha512-MCVCxaCzuZgiHtHGV2Ke44nh6t4+8/tO+rTYOzrr2+G4nMLU/qbzNCWKBX54lyEaVcGQrfOJiG2f8imtiw+nIQ==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-serde@4.2.12': + resolution: {integrity: sha512-W9g1bOLui7Xn5FABRVS0o3rXL0gfN37d/8I/W7i0N7oxjx9QecUmXEMSUMADTODwdtka9cN43t5BI2CodLJpng==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-stack@4.2.11': + resolution: {integrity: sha512-s+eenEPW6RgliDk2IhjD2hWOxIx1NKrOHxEwNUaUXxYBxIyCcDfNULZ2Mu15E3kwcJWBedTET/kEASPV1A1Akg==} + engines: {node: '>=18.0.0'} + + '@smithy/node-config-provider@4.3.11': + resolution: {integrity: sha512-xD17eE7kaLgBBGf5CZQ58hh2YmwK1Z0O8YhffwB/De2jsL0U3JklmhVYJ9Uf37OtUDLF2gsW40Xwwag9U869Gg==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.4.14': + resolution: {integrity: sha512-DamSqaU8nuk0xTJDrYnRzZndHwwRnyj/n/+RqGGCcBKB4qrQem0mSDiWdupaNWdwxzyMU91qxDmHOCazfhtO3A==} + engines: {node: '>=18.0.0'} + + '@smithy/property-provider@4.2.11': + resolution: {integrity: sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg==} + engines: {node: '>=18.0.0'} + + '@smithy/protocol-http@5.3.11': + resolution: {integrity: sha512-hI+barOVDJBkNt4y0L2mu3Ugc0w7+BpJ2CZuLwXtSltGAAwCb3IvnalGlbDV/UCS6a9ZuT3+exd1WxNdLb5IlQ==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-builder@4.2.11': + resolution: {integrity: sha512-7spdikrYiljpket6u0up2Ck2mxhy7dZ0+TDd+S53Dg2DHd6wg+YNJrTCHiLdgZmEXZKI7LJZcwL3721ZRDFiqA==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-parser@4.2.11': + resolution: {integrity: sha512-nE3IRNjDltvGcoThD2abTozI1dkSy8aX+a2N1Rs55en5UsdyyIXgGEmevUL3okZFoJC77JgRGe99xYohhsjivQ==} + engines: {node: '>=18.0.0'} + + '@smithy/service-error-classification@4.2.11': + resolution: {integrity: sha512-HkMFJZJUhzU3HvND1+Yw/kYWXp4RPDLBWLcK1n+Vqw8xn4y2YiBhdww8IxhkQjP/QlZun5bwm3vcHc8AqIU3zw==} + engines: {node: '>=18.0.0'} + + '@smithy/shared-ini-file-loader@4.4.6': + resolution: {integrity: sha512-IB/M5I8G0EeXZTHsAxpx51tMQ5R719F3aq+fjEB6VtNcCHDc0ajFDIGDZw+FW9GxtEkgTduiPpjveJdA/CX7sw==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.3.11': + resolution: {integrity: sha512-V1L6N9aKOBAN4wEHLyqjLBnAz13mtILU0SeDrjOaIZEeN6IFa6DxwRt1NNpOdmSpQUfkBj0qeD3m6P77uzMhgQ==} + engines: {node: '>=18.0.0'} + + '@smithy/smithy-client@4.12.2': + resolution: {integrity: sha512-HezY3UuG0k4T+4xhFKctLXCA5N2oN+Rtv+mmL8Gt7YmsUY2yhmcLyW75qrSzldfj75IsCW/4UhY3s20KcFnZqA==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.13.0': + resolution: {integrity: sha512-COuLsZILbbQsdrwKQpkkpyep7lCsByxwj7m0Mg5v66/ZTyenlfBc40/QFQ5chO0YN/PNEH1Bi3fGtfXPnYNeDw==} + engines: {node: '>=18.0.0'} + + '@smithy/url-parser@4.2.11': + resolution: {integrity: sha512-oTAGGHo8ZYc5VZsBREzuf5lf2pAurJQsccMusVZ85wDkX66ojEc/XauiGjzCj50A61ObFTPe6d7Pyt6UBYaing==} + engines: {node: '>=18.0.0'} + + '@smithy/util-base64@4.3.2': + resolution: {integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-browser@4.2.2': + resolution: {integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-node@4.2.3': + resolution: {integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@4.2.2': + resolution: {integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==} + engines: {node: '>=18.0.0'} + + '@smithy/util-config-provider@4.2.2': + resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-browser@4.3.38': + resolution: {integrity: sha512-c8P1mFLNxcsdAMabB8/VUQUbWzFmgujWi4bAXSggcqLYPc8V4U5abqFqOyn+dK4YT+q8UyCVkTO8807t4t2syA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-node@4.2.41': + resolution: {integrity: sha512-/UG+9MT3UZAR0fLzOtMJMfWGcjjHvgggq924x/CRy8vRbL+yFf3Z6vETlvq8vDH92+31P/1gSOFoo7303wN8WQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-endpoints@3.3.2': + resolution: {integrity: sha512-+4HFLpE5u29AbFlTdlKIT7jfOzZ8PDYZKTb3e+AgLz986OYwqTourQ5H+jg79/66DB69Un1+qKecLnkZdAsYcA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-hex-encoding@4.2.2': + resolution: {integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-middleware@4.2.11': + resolution: {integrity: sha512-r3dtF9F+TpSZUxpOVVtPfk09Rlo4lT6ORBqEvX3IBT6SkQAdDSVKR5GcfmZbtl7WKhKnmb3wbDTQ6ibR2XHClw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-retry@4.2.11': + resolution: {integrity: sha512-XSZULmL5x6aCTTii59wJqKsY1l3eMIAomRAccW7Tzh9r8s7T/7rdo03oektuH5jeYRlJMPcNP92EuRDvk9aXbw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-stream@4.5.17': + resolution: {integrity: sha512-793BYZ4h2JAQkNHcEnyFxDTcZbm9bVybD0UV/LEWmZ5bkTms7JqjfrLMi2Qy0E5WFcCzLwCAPgcvcvxoeALbAQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-uri-escape@4.2.2': + resolution: {integrity: sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@4.2.2': + resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-waiter@4.2.11': + resolution: {integrity: sha512-x7Rh2azQPs3XxbvCzcttRErKKvLnbZfqRf/gOjw2pb+ZscX88e5UkRPCB67bVnsFHxayvMvmePfKTqsRb+is1A==} + engines: {node: '>=18.0.0'} + + '@smithy/uuid@1.1.2': + resolution: {integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==} + engines: {node: '>=18.0.0'} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -3801,6 +4603,18 @@ packages: '@types/diff-match-patch@1.0.36': resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==} + '@types/dom-mediacapture-transform@0.1.11': + resolution: {integrity: sha512-Y2p+nGf1bF2XMttBnsVPHUWzRRZzqUoJAKmiP10b5umnO6DDrWI0BrGDJy1pOHoOULVmGSfFNkQrAlC5dcj6nQ==} + + '@types/dom-webcodecs@0.1.13': + resolution: {integrity: sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ==} + + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} @@ -3857,6 +4671,9 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/yauzl@2.10.3': + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + '@typescript-eslint/eslint-plugin@8.56.0': resolution: {integrity: sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4041,6 +4858,63 @@ packages: resolution: {integrity: sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==} engines: {node: '>= 20'} + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} + + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} + + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} + + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} + + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + + acorn-import-phases@1.0.4: + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + engines: {node: '>=10.13.0'} + peerDependencies: + acorn: ^8.14.0 + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -4067,9 +4941,30 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-keywords@3.5.2: + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + ajv@6.14.0: resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -4129,6 +5024,10 @@ packages: ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + ast-types@0.16.1: + resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} + engines: {node: '>=4'} + astring@1.9.0: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true @@ -4178,14 +5077,23 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + baseline-browser-mapping@2.10.0: resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} engines: {node: '>=6.0.0'} hasBin: true + big.js@5.2.2: + resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -4202,9 +5110,15 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer@5.6.0: + resolution: {integrity: sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -4267,6 +5181,10 @@ packages: resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} engines: {node: '>= 20.19.0'} + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} @@ -4308,6 +5226,9 @@ packages: comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + commander@7.2.0: resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} engines: {node: '>= 10'} @@ -4352,6 +5273,12 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + css-loader@5.2.7: + resolution: {integrity: sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.27.0 || ^5.0.0 + css-select@5.2.2: resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} @@ -4434,6 +5361,10 @@ packages: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} @@ -4601,6 +5532,13 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + emojis-list@3.0.0: + resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} + engines: {node: '>= 4'} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + enhanced-resolve@5.19.0: resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==} engines: {node: '>=10.13.0'} @@ -4616,6 +5554,9 @@ packages: error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + error-stack-parser@2.1.4: + resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} + es-abstract@1.24.1: resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} engines: {node: '>= 0.4'} @@ -4632,6 +5573,9 @@ packages: resolution: {integrity: sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==} engines: {node: '>= 0.4'} + es-module-lexer@2.0.0: + resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -4664,6 +5608,11 @@ packages: engines: {node: '>=12'} hasBin: true + esbuild@0.25.0: + resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} + engines: {node: '>=18'} + hasBin: true + esbuild@0.25.12: resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} engines: {node: '>=18'} @@ -4760,6 +5709,10 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + eslint-scope@8.4.0: resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4793,6 +5746,11 @@ packages: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + esquery@1.7.0: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} @@ -4801,6 +5759,10 @@ packages: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} @@ -4833,13 +5795,26 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + eventsource-parser@3.0.6: resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} engines: {node: '>=18.0.0'} + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + extract-zip@2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -4853,12 +5828,25 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + + fast-xml-builder@1.0.0: + resolution: {integrity: sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ==} + + fast-xml-parser@5.4.1: + resolution: {integrity: sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==} + hasBin: true + fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} fault@1.0.4: resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -4916,6 +5904,9 @@ packages: react-dom: optional: true + fs-monkey@1.0.3: + resolution: {integrity: sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -5060,6 +6051,14 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + get-symbol-description@1.1.0: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} @@ -5078,6 +6077,9 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -5196,9 +6198,22 @@ packages: html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + icss-utils@5.1.0: + resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + icu-minify@4.8.3: resolution: {integrity: sha512-65Av7FLosNk7bPbmQx5z5XG2Y3T2GFppcjiXh4z1idHeVgQxlDpAmkGoYI0eFzAvrOnjpWTL5FmPDhsdfRMPEA==} + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -5223,6 +6238,9 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} @@ -5289,6 +6307,11 @@ packages: is-decimal@2.0.1: resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -5350,6 +6373,10 @@ packages: resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} engines: {node: '>= 0.4'} + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + is-string@1.1.1: resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} engines: {node: '>= 0.4'} @@ -5374,6 +6401,10 @@ packages: resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} engines: {node: '>= 0.4'} + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -5384,6 +6415,10 @@ packages: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true @@ -5470,6 +6505,9 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} @@ -5505,6 +6543,10 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + language-subtag-registry@0.3.23: resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} @@ -5603,6 +6645,14 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + loader-runner@4.3.1: + resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} + engines: {node: '>=6.11.5'} + + loader-utils@2.0.4: + resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} + engines: {node: '>=8.9.0'} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -5616,6 +6666,9 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + lodash@4.17.23: resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} @@ -5641,6 +6694,10 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + lucide-react@0.477.0: resolution: {integrity: sha512-yCf7aYxerFZAbd8jHJxjwe1j7jEMPptjnaOqdYeirFnEy85cNR3/L+o0I875CYFYya+eEVzZSbNuRk8BZPDpVw==} peerDependencies: @@ -5732,6 +6789,16 @@ packages: mdn-data@2.0.30: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + mediabunny@1.37.0: + resolution: {integrity: sha512-eV7M9IJ29pr/8RNL1sYtIxNbdMfDMN1hMwMaOFfNLhwuKKGSC+eKwiJFpdVjEJ3zrMA4LGerF4Hps0SENFSAlg==} + + memfs@3.4.3: + resolution: {integrity: sha512-eivjfi7Ahr6eQTn44nvTnR60e4a1Fs1Via2kCR5lHo/kyNoiMWaXCNJ/GpSd0ilXas2JSOl9B5FTIhflXu0hlg==} + engines: {node: '>= 4.0.0'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -5848,6 +6915,18 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + minimatch@3.1.3: resolution: {integrity: sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==} @@ -5855,6 +6934,9 @@ packages: resolution: {integrity: sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==} engines: {node: '>=16 || 14 >=14.17'} + minimist@1.2.6: + resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -5907,6 +6989,9 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + next-intl-swc-plugin-extractor@4.8.3: resolution: {integrity: sha512-YcaT+R9z69XkGhpDarVFWUprrCMbxgIQYPUaXoE6LGVnLjGdo8hu3gL6bramDVjNKViYY8a/pXPy7Bna0mXORg==} @@ -5960,6 +7045,10 @@ packages: node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + npm-to-yarn@3.0.1: resolution: {integrity: sha512-tt6PvKu4WyzPwWUzy/hvPFqn+uwXO0K1ZHka8az3NnrhWJDmSqI8ncWq0fkL0k/lmmi5tAC11FXwXuh0rFbt1A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6002,12 +7091,23 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + oniguruma-parser@0.12.1: resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} oniguruma-to-es@4.3.4: resolution: {integrity: sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==} + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + optics-ts@2.4.1: resolution: {integrity: sha512-HaYzMHvC80r7U/LqAd4hQyopDezC60PO2qF5GuIwALut2cl5rK1VWHsqTp0oqoJJWjiv6uXKqsO+Q2OO0C3MmQ==} @@ -6062,6 +7162,9 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + performance-now@2.1.0: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} @@ -6123,6 +7226,30 @@ packages: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} + postcss-modules-extract-imports@3.1.0: + resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-local-by-default@4.2.0: + resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-scope@3.2.1: + resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-values@4.0.0: + resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + postcss-selector-parser@6.0.10: resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} engines: {node: '>=4'} @@ -6131,6 +7258,9 @@ packages: resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} engines: {node: '>=4'} + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss@8.4.31: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} @@ -6173,6 +7303,11 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prettier@3.8.1: + resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} + engines: {node: '>=14'} + hasBin: true + prismjs@1.27.0: resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} engines: {node: '>=6'} @@ -6181,6 +7316,10 @@ packages: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -6197,6 +7336,9 @@ packages: proxy-compare@2.6.0: resolution: {integrity: sha512-8xuCeM3l8yqdmbPoYeLbrAXCBWu19XEYc5/F28f5qOaoAIMyfmBUkl5axiK+x9olUvRlcekvnm98AP9RDngOIw==} + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -6297,6 +7439,10 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-refresh@0.18.0: + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} + engines: {node: '>=0.10.0'} + react-remove-scroll-bar@2.3.8: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} engines: {node: '>=10'} @@ -6380,10 +7526,18 @@ packages: resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} engines: {node: '>=0.10.0'} + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + readdirp@5.0.0: resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} engines: {node: '>= 20.19.0'} + recast@0.23.11: + resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} + engines: {node: '>= 4'} + recma-build-jsx@1.0.0: resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} @@ -6478,6 +7632,16 @@ packages: remend@1.2.1: resolution: {integrity: sha512-4wC12bgXsfKAjF1ewwkNIQz5sqewz/z1xgIgjEMb3r1pEytQ37F0Cm6i+OhbTWEvguJD7lhOUJhK5fSasw9f0w==} + remotion@4.0.434: + resolution: {integrity: sha512-r5SRjrB9lFeZPkNGTcFG0qJJOhV7m/W/xandMvReCZyvV8D3ScmfpJWqRoq/zGzBt6t2F6TW/3sUQ0QTU110ZQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -6511,6 +7675,9 @@ packages: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-push-apply@1.0.0: resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} engines: {node: '>= 0.4'} @@ -6522,6 +7689,14 @@ packages: scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + schema-utils@3.3.0: + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} + engines: {node: '>= 10.13.0'} + + schema-utils@4.3.3: + resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} + engines: {node: '>= 10.13.0'} + scroll-into-view-if-needed@3.1.0: resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} @@ -6535,6 +7710,11 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true + semver@7.5.3: + resolution: {integrity: sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==} + engines: {node: '>=10'} + hasBin: true + semver@7.7.4: resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} engines: {node: '>=10'} @@ -6586,6 +7766,12 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + slate-dom@0.119.0: resolution: {integrity: sha512-foc8a2NkE+1SldDIYaoqjhVKupt8RSuvHI868rfYOcypD4we5TT7qunjRKJ852EIRh/Ql8sSTepXgXKOUJnt1w==} peerDependencies: @@ -6627,10 +7813,19 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + source-map@0.7.3: + resolution: {integrity: sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==} + engines: {node: '>= 8'} + source-map@0.7.6: resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} engines: {node: '>= 12'} + source-map@0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + deprecated: The work that was done in this beta branch won't be included in future versions + space-separated-tokens@1.1.5: resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} @@ -6644,10 +7839,16 @@ packages: stable-hash@0.0.5: resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} + stackframe@1.3.4: + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + stream-browserify@3.0.0: + resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + streamdown@2.3.0: resolution: {integrity: sha512-OqS3by/lt91lSicE8RQP2nTsYI6Q/dQgGP2vcyn9YesCmRHhNjswAuBAZA1z0F4+oBU3II/eV51LqjCqwTb1lw==} peerDependencies: @@ -6677,6 +7878,9 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} @@ -6684,10 +7888,23 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strnum@2.2.0: + resolution: {integrity: sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==} + + style-loader@4.0.0: + resolution: {integrity: sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==} + engines: {node: '>= 18.12.0'} + peerDependencies: + webpack: ^5.27.0 + style-to-js@1.1.21: resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} @@ -6711,6 +7928,10 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -6751,6 +7972,27 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} + terser-webpack-plugin@5.3.17: + resolution: {integrity: sha512-YR7PtUp6GMU91BgSJmlaX/rS2lGDbAF7D+Wtq7hRO+MiljNmodYvqslzCFiYVAgW+Qoaaia/QUIP4lGXufjdZw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + + terser@5.46.0: + resolution: {integrity: sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==} + engines: {node: '>=10'} + hasBin: true + throttleit@2.1.0: resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} engines: {node: '>=18'} @@ -6758,6 +8000,9 @@ packages: tiny-invariant@1.3.1: resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==} + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinyexec@1.0.2: resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} engines: {node: '>=18'} @@ -6773,6 +8018,9 @@ packages: toggle-selection@1.0.6: resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -7029,12 +8277,36 @@ packages: yaml: optional: true + watchpack@2.5.1: + resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==} + engines: {node: '>=10.13.0'} + web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} web-vitals@5.1.0: resolution: {integrity: sha512-ArI3kx5jI0atlTtmV0fWU3fjpLmq/nD3Zr1iFFlJLaqa5wLBkUSzINwBPySCX/8jRyjlmy1Volw1kz1g9XE4Jg==} + webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + + webpack-sources@3.3.4: + resolution: {integrity: sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==} + engines: {node: '>=10.13.0'} + + webpack@5.105.0: + resolution: {integrity: sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + + whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -7060,6 +8332,21 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + 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 + xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -7067,6 +8354,12 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -7239,6 +8532,714 @@ snapshots: '@types/react': 19.2.14 react: 19.2.4 + '@aws-crypto/crc32@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.5 + tslib: 2.8.1 + + '@aws-crypto/crc32c@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.5 + tslib: 2.8.1 + + '@aws-crypto/sha1-browser@5.2.0': + dependencies: + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-locate-window': 3.965.5 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-locate-window': 3.965.5 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.5 + tslib: 2.8.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-sdk/client-cloudwatch-logs@3.986.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/credential-provider-node': 3.972.17 + '@aws-sdk/middleware-host-header': 3.972.7 + '@aws-sdk/middleware-logger': 3.972.7 + '@aws-sdk/middleware-recursion-detection': 3.972.7 + '@aws-sdk/middleware-user-agent': 3.972.18 + '@aws-sdk/region-config-resolver': 3.972.7 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-endpoints': 3.986.0 + '@aws-sdk/util-user-agent-browser': 3.972.7 + '@aws-sdk/util-user-agent-node': 3.973.3 + '@smithy/config-resolver': 4.4.10 + '@smithy/core': 3.23.8 + '@smithy/eventstream-serde-browser': 4.2.11 + '@smithy/eventstream-serde-config-resolver': 4.3.11 + '@smithy/eventstream-serde-node': 4.2.11 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/hash-node': 4.2.11 + '@smithy/invalid-dependency': 4.2.11 + '@smithy/middleware-content-length': 4.2.11 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/middleware-retry': 4.4.39 + '@smithy/middleware-serde': 4.2.12 + '@smithy/middleware-stack': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/node-http-handler': 4.4.14 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.38 + '@smithy/util-defaults-mode-node': 4.2.41 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-iam@3.986.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/credential-provider-node': 3.972.17 + '@aws-sdk/middleware-host-header': 3.972.7 + '@aws-sdk/middleware-logger': 3.972.7 + '@aws-sdk/middleware-recursion-detection': 3.972.7 + '@aws-sdk/middleware-user-agent': 3.972.18 + '@aws-sdk/region-config-resolver': 3.972.7 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-endpoints': 3.986.0 + '@aws-sdk/util-user-agent-browser': 3.972.7 + '@aws-sdk/util-user-agent-node': 3.973.3 + '@smithy/config-resolver': 4.4.10 + '@smithy/core': 3.23.8 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/hash-node': 4.2.11 + '@smithy/invalid-dependency': 4.2.11 + '@smithy/middleware-content-length': 4.2.11 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/middleware-retry': 4.4.39 + '@smithy/middleware-serde': 4.2.12 + '@smithy/middleware-stack': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/node-http-handler': 4.4.14 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.38 + '@smithy/util-defaults-mode-node': 4.2.41 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/util-utf8': 4.2.2 + '@smithy/util-waiter': 4.2.11 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-lambda@3.986.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/credential-provider-node': 3.972.17 + '@aws-sdk/middleware-host-header': 3.972.7 + '@aws-sdk/middleware-logger': 3.972.7 + '@aws-sdk/middleware-recursion-detection': 3.972.7 + '@aws-sdk/middleware-user-agent': 3.972.18 + '@aws-sdk/region-config-resolver': 3.972.7 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-endpoints': 3.986.0 + '@aws-sdk/util-user-agent-browser': 3.972.7 + '@aws-sdk/util-user-agent-node': 3.973.3 + '@smithy/config-resolver': 4.4.10 + '@smithy/core': 3.23.8 + '@smithy/eventstream-serde-browser': 4.2.11 + '@smithy/eventstream-serde-config-resolver': 4.3.11 + '@smithy/eventstream-serde-node': 4.2.11 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/hash-node': 4.2.11 + '@smithy/invalid-dependency': 4.2.11 + '@smithy/middleware-content-length': 4.2.11 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/middleware-retry': 4.4.39 + '@smithy/middleware-serde': 4.2.12 + '@smithy/middleware-stack': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/node-http-handler': 4.4.14 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.38 + '@smithy/util-defaults-mode-node': 4.2.41 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/util-stream': 4.5.17 + '@smithy/util-utf8': 4.2.2 + '@smithy/util-waiter': 4.2.11 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-s3@3.986.0': + dependencies: + '@aws-crypto/sha1-browser': 5.2.0 + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/credential-provider-node': 3.972.17 + '@aws-sdk/middleware-bucket-endpoint': 3.972.7 + '@aws-sdk/middleware-expect-continue': 3.972.7 + '@aws-sdk/middleware-flexible-checksums': 3.972.5 + '@aws-sdk/middleware-host-header': 3.972.7 + '@aws-sdk/middleware-location-constraint': 3.972.7 + '@aws-sdk/middleware-logger': 3.972.7 + '@aws-sdk/middleware-recursion-detection': 3.972.7 + '@aws-sdk/middleware-sdk-s3': 3.972.18 + '@aws-sdk/middleware-ssec': 3.972.7 + '@aws-sdk/middleware-user-agent': 3.972.18 + '@aws-sdk/region-config-resolver': 3.972.7 + '@aws-sdk/signature-v4-multi-region': 3.986.0 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-endpoints': 3.986.0 + '@aws-sdk/util-user-agent-browser': 3.972.7 + '@aws-sdk/util-user-agent-node': 3.973.3 + '@smithy/config-resolver': 4.4.10 + '@smithy/core': 3.23.8 + '@smithy/eventstream-serde-browser': 4.2.11 + '@smithy/eventstream-serde-config-resolver': 4.3.11 + '@smithy/eventstream-serde-node': 4.2.11 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/hash-blob-browser': 4.2.12 + '@smithy/hash-node': 4.2.11 + '@smithy/hash-stream-node': 4.2.11 + '@smithy/invalid-dependency': 4.2.11 + '@smithy/md5-js': 4.2.11 + '@smithy/middleware-content-length': 4.2.11 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/middleware-retry': 4.4.39 + '@smithy/middleware-serde': 4.2.12 + '@smithy/middleware-stack': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/node-http-handler': 4.4.14 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.38 + '@smithy/util-defaults-mode-node': 4.2.41 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/util-stream': 4.5.17 + '@smithy/util-utf8': 4.2.2 + '@smithy/util-waiter': 4.2.11 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-service-quotas@3.986.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/credential-provider-node': 3.972.17 + '@aws-sdk/middleware-host-header': 3.972.7 + '@aws-sdk/middleware-logger': 3.972.7 + '@aws-sdk/middleware-recursion-detection': 3.972.7 + '@aws-sdk/middleware-user-agent': 3.972.18 + '@aws-sdk/region-config-resolver': 3.972.7 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-endpoints': 3.986.0 + '@aws-sdk/util-user-agent-browser': 3.972.7 + '@aws-sdk/util-user-agent-node': 3.973.3 + '@smithy/config-resolver': 4.4.10 + '@smithy/core': 3.23.8 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/hash-node': 4.2.11 + '@smithy/invalid-dependency': 4.2.11 + '@smithy/middleware-content-length': 4.2.11 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/middleware-retry': 4.4.39 + '@smithy/middleware-serde': 4.2.12 + '@smithy/middleware-stack': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/node-http-handler': 4.4.14 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.38 + '@smithy/util-defaults-mode-node': 4.2.41 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sts@3.986.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/credential-provider-node': 3.972.17 + '@aws-sdk/middleware-host-header': 3.972.7 + '@aws-sdk/middleware-logger': 3.972.7 + '@aws-sdk/middleware-recursion-detection': 3.972.7 + '@aws-sdk/middleware-user-agent': 3.972.18 + '@aws-sdk/region-config-resolver': 3.972.7 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-endpoints': 3.986.0 + '@aws-sdk/util-user-agent-browser': 3.972.7 + '@aws-sdk/util-user-agent-node': 3.973.3 + '@smithy/config-resolver': 4.4.10 + '@smithy/core': 3.23.8 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/hash-node': 4.2.11 + '@smithy/invalid-dependency': 4.2.11 + '@smithy/middleware-content-length': 4.2.11 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/middleware-retry': 4.4.39 + '@smithy/middleware-serde': 4.2.12 + '@smithy/middleware-stack': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/node-http-handler': 4.4.14 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.38 + '@smithy/util-defaults-mode-node': 4.2.41 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/core@3.973.18': + dependencies: + '@aws-sdk/types': 3.973.5 + '@aws-sdk/xml-builder': 3.972.10 + '@smithy/core': 3.23.8 + '@smithy/node-config-provider': 4.3.11 + '@smithy/property-provider': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/signature-v4': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/crc64-nvme@3.972.0': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.972.16': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/types': 3.973.5 + '@smithy/property-provider': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.972.18': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/types': 3.973.5 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/node-http-handler': 4.4.14 + '@smithy/property-provider': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/util-stream': 4.5.17 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.972.16': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/credential-provider-env': 3.972.16 + '@aws-sdk/credential-provider-http': 3.972.18 + '@aws-sdk/credential-provider-login': 3.972.16 + '@aws-sdk/credential-provider-process': 3.972.16 + '@aws-sdk/credential-provider-sso': 3.972.16 + '@aws-sdk/credential-provider-web-identity': 3.972.16 + '@aws-sdk/nested-clients': 3.996.6 + '@aws-sdk/types': 3.973.5 + '@smithy/credential-provider-imds': 4.2.11 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-login@3.972.16': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/nested-clients': 3.996.6 + '@aws-sdk/types': 3.973.5 + '@smithy/property-provider': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-node@3.972.17': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.16 + '@aws-sdk/credential-provider-http': 3.972.18 + '@aws-sdk/credential-provider-ini': 3.972.16 + '@aws-sdk/credential-provider-process': 3.972.16 + '@aws-sdk/credential-provider-sso': 3.972.16 + '@aws-sdk/credential-provider-web-identity': 3.972.16 + '@aws-sdk/types': 3.973.5 + '@smithy/credential-provider-imds': 4.2.11 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-process@3.972.16': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/types': 3.973.5 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.972.16': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/nested-clients': 3.996.6 + '@aws-sdk/token-providers': 3.1003.0 + '@aws-sdk/types': 3.973.5 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-web-identity@3.972.16': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/nested-clients': 3.996.6 + '@aws-sdk/types': 3.973.5 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/lib-storage@3.986.0(@aws-sdk/client-s3@3.986.0)': + dependencies: + '@aws-sdk/client-s3': 3.986.0 + '@smithy/abort-controller': 4.2.11 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/smithy-client': 4.12.2 + buffer: 5.6.0 + events: 3.3.0 + stream-browserify: 3.0.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-bucket-endpoint@3.972.7': + dependencies: + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-arn-parser': 3.972.3 + '@smithy/node-config-provider': 4.3.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-config-provider': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-expect-continue@3.972.7': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-flexible-checksums@3.972.5': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@aws-crypto/crc32c': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/crc64-nvme': 3.972.0 + '@aws-sdk/types': 3.973.5 + '@smithy/is-array-buffer': 4.2.2 + '@smithy/node-config-provider': 4.3.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-stream': 4.5.17 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-host-header@3.972.7': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-location-constraint@3.972.7': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-logger@3.972.7': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-recursion-detection@3.972.7': + dependencies: + '@aws-sdk/types': 3.973.5 + '@aws/lambda-invoke-store': 0.2.3 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-sdk-s3@3.972.18': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-arn-parser': 3.972.3 + '@smithy/core': 3.23.8 + '@smithy/node-config-provider': 4.3.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/signature-v4': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-stream': 4.5.17 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-ssec@3.972.7': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-user-agent@3.972.18': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-endpoints': 3.996.4 + '@smithy/core': 3.23.8 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.996.6': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/middleware-host-header': 3.972.7 + '@aws-sdk/middleware-logger': 3.972.7 + '@aws-sdk/middleware-recursion-detection': 3.972.7 + '@aws-sdk/middleware-user-agent': 3.972.18 + '@aws-sdk/region-config-resolver': 3.972.7 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-endpoints': 3.996.4 + '@aws-sdk/util-user-agent-browser': 3.972.7 + '@aws-sdk/util-user-agent-node': 3.973.3 + '@smithy/config-resolver': 4.4.10 + '@smithy/core': 3.23.8 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/hash-node': 4.2.11 + '@smithy/invalid-dependency': 4.2.11 + '@smithy/middleware-content-length': 4.2.11 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/middleware-retry': 4.4.39 + '@smithy/middleware-serde': 4.2.12 + '@smithy/middleware-stack': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/node-http-handler': 4.4.14 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.38 + '@smithy/util-defaults-mode-node': 4.2.41 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/region-config-resolver@3.972.7': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/config-resolver': 4.4.10 + '@smithy/node-config-provider': 4.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/s3-request-presigner@3.986.0': + dependencies: + '@aws-sdk/signature-v4-multi-region': 3.986.0 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-format-url': 3.972.7 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.986.0': + dependencies: + '@aws-sdk/middleware-sdk-s3': 3.972.18 + '@aws-sdk/types': 3.973.5 + '@smithy/protocol-http': 5.3.11 + '@smithy/signature-v4': 5.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.1003.0': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/nested-clients': 3.996.6 + '@aws-sdk/types': 3.973.5 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/types@3.973.5': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/util-arn-parser@3.972.3': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-endpoints@3.986.0': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-endpoints': 3.3.2 + tslib: 2.8.1 + + '@aws-sdk/util-endpoints@3.996.4': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-endpoints': 3.3.2 + tslib: 2.8.1 + + '@aws-sdk/util-format-url@3.972.7': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/querystring-builder': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.965.5': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-browser@3.972.7': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/types': 4.13.0 + bowser: 2.14.1 + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-node@3.973.3': + dependencies: + '@aws-sdk/middleware-user-agent': 3.972.18 + '@aws-sdk/types': 3.973.5 + '@smithy/node-config-provider': 4.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.972.10': + dependencies: + '@smithy/types': 4.13.0 + fast-xml-parser: 5.4.1 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.2.3': {} + '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -7393,6 +9394,10 @@ snapshots: '@babel/template': 7.28.6 '@babel/types': 7.29.0 + '@babel/parser@7.24.1': + dependencies: + '@babel/types': 7.29.0 + '@babel/parser@7.29.0': dependencies: '@babel/types': 7.29.0 @@ -8063,6 +10068,9 @@ snapshots: '@esbuild-kit/core-utils': 3.3.2 get-tsconfig: 4.13.6 + '@esbuild/aix-ppc64@0.25.0': + optional: true + '@esbuild/aix-ppc64@0.25.12': optional: true @@ -8072,6 +10080,9 @@ snapshots: '@esbuild/android-arm64@0.18.20': optional: true + '@esbuild/android-arm64@0.25.0': + optional: true + '@esbuild/android-arm64@0.25.12': optional: true @@ -8081,6 +10092,9 @@ snapshots: '@esbuild/android-arm@0.18.20': optional: true + '@esbuild/android-arm@0.25.0': + optional: true + '@esbuild/android-arm@0.25.12': optional: true @@ -8090,6 +10104,9 @@ snapshots: '@esbuild/android-x64@0.18.20': optional: true + '@esbuild/android-x64@0.25.0': + optional: true + '@esbuild/android-x64@0.25.12': optional: true @@ -8099,6 +10116,9 @@ snapshots: '@esbuild/darwin-arm64@0.18.20': optional: true + '@esbuild/darwin-arm64@0.25.0': + optional: true + '@esbuild/darwin-arm64@0.25.12': optional: true @@ -8108,6 +10128,9 @@ snapshots: '@esbuild/darwin-x64@0.18.20': optional: true + '@esbuild/darwin-x64@0.25.0': + optional: true + '@esbuild/darwin-x64@0.25.12': optional: true @@ -8117,6 +10140,9 @@ snapshots: '@esbuild/freebsd-arm64@0.18.20': optional: true + '@esbuild/freebsd-arm64@0.25.0': + optional: true + '@esbuild/freebsd-arm64@0.25.12': optional: true @@ -8126,6 +10152,9 @@ snapshots: '@esbuild/freebsd-x64@0.18.20': optional: true + '@esbuild/freebsd-x64@0.25.0': + optional: true + '@esbuild/freebsd-x64@0.25.12': optional: true @@ -8135,6 +10164,9 @@ snapshots: '@esbuild/linux-arm64@0.18.20': optional: true + '@esbuild/linux-arm64@0.25.0': + optional: true + '@esbuild/linux-arm64@0.25.12': optional: true @@ -8144,7 +10176,10 @@ snapshots: '@esbuild/linux-arm@0.18.20': optional: true - '@esbuild/linux-arm@0.25.12': + '@esbuild/linux-arm@0.25.0': + optional: true + + '@esbuild/linux-arm@0.25.12': optional: true '@esbuild/linux-arm@0.27.3': @@ -8153,6 +10188,9 @@ snapshots: '@esbuild/linux-ia32@0.18.20': optional: true + '@esbuild/linux-ia32@0.25.0': + optional: true + '@esbuild/linux-ia32@0.25.12': optional: true @@ -8162,6 +10200,9 @@ snapshots: '@esbuild/linux-loong64@0.18.20': optional: true + '@esbuild/linux-loong64@0.25.0': + optional: true + '@esbuild/linux-loong64@0.25.12': optional: true @@ -8171,6 +10212,9 @@ snapshots: '@esbuild/linux-mips64el@0.18.20': optional: true + '@esbuild/linux-mips64el@0.25.0': + optional: true + '@esbuild/linux-mips64el@0.25.12': optional: true @@ -8180,6 +10224,9 @@ snapshots: '@esbuild/linux-ppc64@0.18.20': optional: true + '@esbuild/linux-ppc64@0.25.0': + optional: true + '@esbuild/linux-ppc64@0.25.12': optional: true @@ -8189,6 +10236,9 @@ snapshots: '@esbuild/linux-riscv64@0.18.20': optional: true + '@esbuild/linux-riscv64@0.25.0': + optional: true + '@esbuild/linux-riscv64@0.25.12': optional: true @@ -8198,6 +10248,9 @@ snapshots: '@esbuild/linux-s390x@0.18.20': optional: true + '@esbuild/linux-s390x@0.25.0': + optional: true + '@esbuild/linux-s390x@0.25.12': optional: true @@ -8207,12 +10260,18 @@ snapshots: '@esbuild/linux-x64@0.18.20': optional: true + '@esbuild/linux-x64@0.25.0': + optional: true + '@esbuild/linux-x64@0.25.12': optional: true '@esbuild/linux-x64@0.27.3': optional: true + '@esbuild/netbsd-arm64@0.25.0': + optional: true + '@esbuild/netbsd-arm64@0.25.12': optional: true @@ -8222,12 +10281,18 @@ snapshots: '@esbuild/netbsd-x64@0.18.20': optional: true + '@esbuild/netbsd-x64@0.25.0': + optional: true + '@esbuild/netbsd-x64@0.25.12': optional: true '@esbuild/netbsd-x64@0.27.3': optional: true + '@esbuild/openbsd-arm64@0.25.0': + optional: true + '@esbuild/openbsd-arm64@0.25.12': optional: true @@ -8237,6 +10302,9 @@ snapshots: '@esbuild/openbsd-x64@0.18.20': optional: true + '@esbuild/openbsd-x64@0.25.0': + optional: true + '@esbuild/openbsd-x64@0.25.12': optional: true @@ -8252,6 +10320,9 @@ snapshots: '@esbuild/sunos-x64@0.18.20': optional: true + '@esbuild/sunos-x64@0.25.0': + optional: true + '@esbuild/sunos-x64@0.25.12': optional: true @@ -8261,6 +10332,9 @@ snapshots: '@esbuild/win32-arm64@0.18.20': optional: true + '@esbuild/win32-arm64@0.25.0': + optional: true + '@esbuild/win32-arm64@0.25.12': optional: true @@ -8270,6 +10344,9 @@ snapshots: '@esbuild/win32-ia32@0.18.20': optional: true + '@esbuild/win32-ia32@0.25.0': + optional: true + '@esbuild/win32-ia32@0.25.12': optional: true @@ -8279,6 +10356,9 @@ snapshots: '@esbuild/win32-x64@0.18.20': optional: true + '@esbuild/win32-x64@0.25.0': + optional: true + '@esbuild/win32-x64@0.25.12': optional: true @@ -8514,6 +10594,11 @@ snapshots: '@jridgewell/resolve-uri@3.1.2': {} + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/sourcemap-codec@1.5.5': {} '@jridgewell/trace-mapping@0.3.31': @@ -8553,8 +10638,45 @@ snapshots: transitivePeerDependencies: - supports-color + '@mediabunny/aac-encoder@1.37.0(mediabunny@1.37.0)': + dependencies: + mediabunny: 1.37.0 + + '@mediabunny/flac-encoder@1.37.0(mediabunny@1.37.0)': + dependencies: + mediabunny: 1.37.0 + + '@mediabunny/mp3-encoder@1.37.0(mediabunny@1.37.0)': + dependencies: + mediabunny: 1.37.0 + '@microsoft/fetch-event-source@2.0.1': {} + '@module-federation/error-codes@0.22.0': {} + + '@module-federation/runtime-core@0.22.0': + dependencies: + '@module-federation/error-codes': 0.22.0 + '@module-federation/sdk': 0.22.0 + + '@module-federation/runtime-tools@0.22.0': + dependencies: + '@module-federation/runtime': 0.22.0 + '@module-federation/webpack-bundler-runtime': 0.22.0 + + '@module-federation/runtime@0.22.0': + dependencies: + '@module-federation/error-codes': 0.22.0 + '@module-federation/runtime-core': 0.22.0 + '@module-federation/sdk': 0.22.0 + + '@module-federation/sdk@0.22.0': {} + + '@module-federation/webpack-bundler-runtime@0.22.0': + dependencies: + '@module-federation/runtime': 0.22.0 + '@module-federation/sdk': 0.22.0 + '@napi-rs/wasm-runtime@0.2.12': dependencies: '@emnapi/core': 1.8.1 @@ -8562,6 +10684,13 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true + '@napi-rs/wasm-runtime@1.0.7': + dependencies: + '@emnapi/core': 1.8.1 + '@emnapi/runtime': 1.8.1 + '@tybys/wasm-util': 0.10.1 + optional: true + '@next/env@16.1.6': {} '@next/eslint-plugin-next@15.2.0': @@ -10066,6 +12195,255 @@ snapshots: '@react-dnd/shallowequal@4.0.2': {} + '@remotion/bundler@4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@remotion/media-parser': 4.0.434 + '@remotion/studio': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/studio-shared': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@rspack/core': 1.7.6 + '@rspack/plugin-react-refresh': 1.6.1(react-refresh@0.18.0) + css-loader: 5.2.7(webpack@5.105.0(esbuild@0.25.12)) + esbuild: 0.25.0 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + react-refresh: 0.18.0 + remotion: 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + source-map: 0.7.3 + style-loader: 4.0.0(webpack@5.105.0(esbuild@0.25.12)) + webpack: 5.105.0(esbuild@0.25.0) + transitivePeerDependencies: + - '@swc/core' + - '@swc/helpers' + - bufferutil + - supports-color + - uglify-js + - utf-8-validate + - webpack-cli + - webpack-hot-middleware + + '@remotion/cli@4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@remotion/bundler': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/media-utils': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/player': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/renderer': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/studio': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/studio-server': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/studio-shared': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + dotenv: 17.3.1 + minimist: 1.2.6 + prompts: 2.4.2 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + remotion: 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + transitivePeerDependencies: + - '@swc/core' + - '@swc/helpers' + - bufferutil + - supports-color + - uglify-js + - utf-8-validate + - webpack-cli + - webpack-hot-middleware + + '@remotion/compositor-darwin-arm64@4.0.434': + optional: true + + '@remotion/compositor-darwin-x64@4.0.434': + optional: true + + '@remotion/compositor-linux-arm64-gnu@4.0.434': + optional: true + + '@remotion/compositor-linux-arm64-musl@4.0.434': + optional: true + + '@remotion/compositor-linux-x64-gnu@4.0.434': + optional: true + + '@remotion/compositor-linux-x64-musl@4.0.434': + optional: true + + '@remotion/compositor-win32-x64-msvc@4.0.434': + optional: true + + '@remotion/lambda-client@4.0.434': {} + + '@remotion/lambda@4.0.434(@remotion/bundler@4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@aws-sdk/client-cloudwatch-logs': 3.986.0 + '@aws-sdk/client-iam': 3.986.0 + '@aws-sdk/client-lambda': 3.986.0 + '@aws-sdk/client-s3': 3.986.0 + '@aws-sdk/client-service-quotas': 3.986.0 + '@aws-sdk/client-sts': 3.986.0 + '@aws-sdk/lib-storage': 3.986.0(@aws-sdk/client-s3@3.986.0) + '@aws-sdk/middleware-flexible-checksums': 3.972.5 + '@aws-sdk/s3-request-presigner': 3.986.0 + '@remotion/bundler': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/cli': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/lambda-client': 4.0.434 + '@remotion/renderer': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/serverless': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/streaming': 4.0.434 + '@smithy/abort-controller': 4.0.1 + remotion: 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + zod: 4.3.6 + transitivePeerDependencies: + - '@swc/core' + - '@swc/helpers' + - aws-crt + - bufferutil + - react + - react-dom + - supports-color + - uglify-js + - utf-8-validate + - webpack-cli + - webpack-hot-middleware + + '@remotion/licensing@4.0.434': {} + + '@remotion/media-parser@4.0.434': {} + + '@remotion/media-utils@4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@remotion/media-parser': 4.0.434 + '@remotion/webcodecs': 4.0.434 + mediabunny: 1.37.0 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + remotion: 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + + '@remotion/player@4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + remotion: 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + + '@remotion/renderer@4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@remotion/licensing': 4.0.434 + '@remotion/streaming': 4.0.434 + execa: 5.1.1 + extract-zip: 2.0.1 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + remotion: 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + source-map: 0.8.0-beta.0 + ws: 8.17.1 + optionalDependencies: + '@remotion/compositor-darwin-arm64': 4.0.434 + '@remotion/compositor-darwin-x64': 4.0.434 + '@remotion/compositor-linux-arm64-gnu': 4.0.434 + '@remotion/compositor-linux-arm64-musl': 4.0.434 + '@remotion/compositor-linux-x64-gnu': 4.0.434 + '@remotion/compositor-linux-x64-musl': 4.0.434 + '@remotion/compositor-win32-x64-msvc': 4.0.434 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@remotion/serverless-client@4.0.434': {} + + '@remotion/serverless@4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@remotion/bundler': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/licensing': 4.0.434 + '@remotion/renderer': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/serverless-client': 4.0.434 + transitivePeerDependencies: + - '@swc/core' + - '@swc/helpers' + - bufferutil + - react + - react-dom + - supports-color + - uglify-js + - utf-8-validate + - webpack-cli + - webpack-hot-middleware + + '@remotion/streaming@4.0.434': {} + + '@remotion/studio-server@4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@babel/parser': 7.24.1 + '@remotion/bundler': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/renderer': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/studio-shared': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + memfs: 3.4.3 + open: 8.4.2 + prettier: 3.8.1 + recast: 0.23.11 + remotion: 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + semver: 7.5.3 + source-map: 0.7.3 + transitivePeerDependencies: + - '@swc/core' + - '@swc/helpers' + - bufferutil + - react + - react-dom + - supports-color + - uglify-js + - utf-8-validate + - webpack-cli + - webpack-hot-middleware + + '@remotion/studio-shared@4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + remotion: 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + transitivePeerDependencies: + - react + - react-dom + + '@remotion/studio@4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@remotion/media-utils': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/player': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/renderer': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/studio-shared': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/web-renderer': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/zod-types': 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6) + mediabunny: 1.37.0 + memfs: 3.4.3 + open: 8.4.2 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + remotion: 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + semver: 7.5.3 + source-map: 0.7.3 + zod: 4.3.6 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@remotion/web-renderer@4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@mediabunny/aac-encoder': 1.37.0(mediabunny@1.37.0) + '@mediabunny/flac-encoder': 1.37.0(mediabunny@1.37.0) + '@mediabunny/mp3-encoder': 1.37.0(mediabunny@1.37.0) + '@remotion/licensing': 4.0.434 + mediabunny: 1.37.0 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + remotion: 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + + '@remotion/webcodecs@4.0.434': + dependencies: + '@remotion/media-parser': 4.0.434 + + '@remotion/zod-types@4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6)': + dependencies: + remotion: 4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + zod: 4.3.6 + transitivePeerDependencies: + - react + - react-dom + '@rollup/rollup-android-arm-eabi@4.59.0': optional: true @@ -10141,6 +12519,65 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.59.0': optional: true + '@rspack/binding-darwin-arm64@1.7.6': + optional: true + + '@rspack/binding-darwin-x64@1.7.6': + optional: true + + '@rspack/binding-linux-arm64-gnu@1.7.6': + optional: true + + '@rspack/binding-linux-arm64-musl@1.7.6': + optional: true + + '@rspack/binding-linux-x64-gnu@1.7.6': + optional: true + + '@rspack/binding-linux-x64-musl@1.7.6': + optional: true + + '@rspack/binding-wasm32-wasi@1.7.6': + dependencies: + '@napi-rs/wasm-runtime': 1.0.7 + optional: true + + '@rspack/binding-win32-arm64-msvc@1.7.6': + optional: true + + '@rspack/binding-win32-ia32-msvc@1.7.6': + optional: true + + '@rspack/binding-win32-x64-msvc@1.7.6': + optional: true + + '@rspack/binding@1.7.6': + optionalDependencies: + '@rspack/binding-darwin-arm64': 1.7.6 + '@rspack/binding-darwin-x64': 1.7.6 + '@rspack/binding-linux-arm64-gnu': 1.7.6 + '@rspack/binding-linux-arm64-musl': 1.7.6 + '@rspack/binding-linux-x64-gnu': 1.7.6 + '@rspack/binding-linux-x64-musl': 1.7.6 + '@rspack/binding-wasm32-wasi': 1.7.6 + '@rspack/binding-win32-arm64-msvc': 1.7.6 + '@rspack/binding-win32-ia32-msvc': 1.7.6 + '@rspack/binding-win32-x64-msvc': 1.7.6 + + '@rspack/core@1.7.6': + dependencies: + '@module-federation/runtime-tools': 0.22.0 + '@rspack/binding': 1.7.6 + '@rspack/lite-tapable': 1.1.0 + + '@rspack/lite-tapable@1.1.0': {} + + '@rspack/plugin-react-refresh@1.6.1(react-refresh@0.18.0)': + dependencies: + error-stack-parser: 2.1.4 + html-entities: 2.6.0 + react-refresh: 0.18.0 + '@rtsao/scc@1.1.0': {} '@rushstack/eslint-patch@1.16.1': {} @@ -10194,6 +12631,349 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} + '@smithy/abort-controller@4.0.1': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/abort-controller@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/chunked-blob-reader-native@4.2.3': + dependencies: + '@smithy/util-base64': 4.3.2 + tslib: 2.8.1 + + '@smithy/chunked-blob-reader@5.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/config-resolver@4.4.10': + dependencies: + '@smithy/node-config-provider': 4.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + tslib: 2.8.1 + + '@smithy/core@3.23.8': + dependencies: + '@smithy/middleware-serde': 4.2.12 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-stream': 4.5.17 + '@smithy/util-utf8': 4.2.2 + '@smithy/uuid': 1.1.2 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.2.11': + dependencies: + '@smithy/node-config-provider': 4.3.11 + '@smithy/property-provider': 4.2.11 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + tslib: 2.8.1 + + '@smithy/eventstream-codec@4.2.11': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 4.13.0 + '@smithy/util-hex-encoding': 4.2.2 + tslib: 2.8.1 + + '@smithy/eventstream-serde-browser@4.2.11': + dependencies: + '@smithy/eventstream-serde-universal': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-config-resolver@4.3.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-node@4.2.11': + dependencies: + '@smithy/eventstream-serde-universal': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-universal@4.2.11': + dependencies: + '@smithy/eventstream-codec': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.3.13': + dependencies: + '@smithy/protocol-http': 5.3.11 + '@smithy/querystring-builder': 4.2.11 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.2 + tslib: 2.8.1 + + '@smithy/hash-blob-browser@4.2.12': + dependencies: + '@smithy/chunked-blob-reader': 5.2.2 + '@smithy/chunked-blob-reader-native': 4.2.3 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/hash-node@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/hash-stream-node@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/invalid-dependency@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/is-array-buffer@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/md5-js@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/middleware-content-length@4.2.11': + dependencies: + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/middleware-endpoint@4.4.22': + dependencies: + '@smithy/core': 3.23.8 + '@smithy/middleware-serde': 4.2.12 + '@smithy/node-config-provider': 4.3.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-middleware': 4.2.11 + tslib: 2.8.1 + + '@smithy/middleware-retry@4.4.39': + dependencies: + '@smithy/node-config-provider': 4.3.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/service-error-classification': 4.2.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/uuid': 1.1.2 + tslib: 2.8.1 + + '@smithy/middleware-serde@4.2.12': + dependencies: + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/middleware-stack@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/node-config-provider@4.3.11': + dependencies: + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.4.14': + dependencies: + '@smithy/abort-controller': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/querystring-builder': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/property-provider@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/protocol-http@5.3.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/querystring-builder@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + '@smithy/util-uri-escape': 4.2.2 + tslib: 2.8.1 + + '@smithy/querystring-parser@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/service-error-classification@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + + '@smithy/shared-ini-file-loader@4.4.6': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/signature-v4@5.3.11': + dependencies: + '@smithy/is-array-buffer': 4.2.2 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-uri-escape': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/smithy-client@4.12.2': + dependencies: + '@smithy/core': 3.23.8 + '@smithy/middleware-endpoint': 4.4.22 + '@smithy/middleware-stack': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-stream': 4.5.17 + tslib: 2.8.1 + + '@smithy/types@4.13.0': + dependencies: + tslib: 2.8.1 + + '@smithy/url-parser@4.2.11': + dependencies: + '@smithy/querystring-parser': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-base64@4.3.2': + dependencies: + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/util-body-length-browser@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-body-length-node@4.2.3': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-buffer-from@4.2.2': + dependencies: + '@smithy/is-array-buffer': 4.2.2 + tslib: 2.8.1 + + '@smithy/util-config-provider@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-defaults-mode-browser@4.3.38': + dependencies: + '@smithy/property-provider': 4.2.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-defaults-mode-node@4.2.41': + dependencies: + '@smithy/config-resolver': 4.4.10 + '@smithy/credential-provider-imds': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/property-provider': 4.2.11 + '@smithy/smithy-client': 4.12.2 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-endpoints@3.3.2': + dependencies: + '@smithy/node-config-provider': 4.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-hex-encoding@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-middleware@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-retry@4.2.11': + dependencies: + '@smithy/service-error-classification': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-stream@4.5.17': + dependencies: + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/node-http-handler': 4.4.14 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.2 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/util-uri-escape@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-utf8@4.2.2': + dependencies: + '@smithy/util-buffer-from': 4.2.2 + tslib: 2.8.1 + + '@smithy/util-waiter@4.2.11': + dependencies: + '@smithy/abort-controller': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/uuid@1.1.2': + dependencies: + tslib: 2.8.1 + '@standard-schema/spec@1.1.0': {} '@standard-schema/utils@0.3.0': {} @@ -10474,11 +13254,27 @@ snapshots: '@types/canvas-confetti@1.9.0': {} - '@types/debug@4.1.12': + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + + '@types/diff-match-patch@1.0.36': {} + + '@types/dom-mediacapture-transform@0.1.11': + dependencies: + '@types/dom-webcodecs': 0.1.13 + + '@types/dom-webcodecs@0.1.13': {} + + '@types/eslint-scope@3.7.7': dependencies: - '@types/ms': 2.1.0 + '@types/eslint': 9.6.1 + '@types/estree': 1.0.8 - '@types/diff-match-patch@1.0.36': {} + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 '@types/estree-jsx@1.0.5': dependencies: @@ -10537,6 +13333,11 @@ snapshots: '@types/unist@3.0.3': {} + '@types/yauzl@2.10.3': + dependencies: + '@types/node': 20.19.33 + optional: true + '@typescript-eslint/eslint-plugin@8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -10721,6 +13522,90 @@ snapshots: '@vercel/oidc@3.1.0': {} + '@webassemblyjs/ast@1.14.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-numbers@1.13.2': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + 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 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/wasm-edit@1.14.1': + 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 + + '@webassemblyjs/wasm-gen@1.14.1': + 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 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-parser@1.14.1': + 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 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + acorn-import-phases@1.0.4(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: acorn: 8.16.0 @@ -10747,6 +13632,19 @@ snapshots: '@opentelemetry/api': 1.9.0 zod: 4.3.6 + ajv-formats@2.1.1(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + + ajv-keywords@3.5.2(ajv@6.14.0): + dependencies: + ajv: 6.14.0 + + ajv-keywords@5.1.0(ajv@8.18.0): + dependencies: + ajv: 8.18.0 + fast-deep-equal: 3.1.3 + ajv@6.14.0: dependencies: fast-deep-equal: 3.1.3 @@ -10754,6 +13652,13 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.18.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 @@ -10851,6 +13756,10 @@ snapshots: ast-types-flow@0.0.8: {} + ast-types@0.16.1: + dependencies: + tslib: 2.8.1 + astring@1.9.0: {} async-function@1.0.0: {} @@ -10895,10 +13804,16 @@ snapshots: balanced-match@4.0.4: {} + base64-js@1.5.1: {} + baseline-browser-mapping@2.10.0: {} + big.js@5.2.2: {} + boolbase@1.0.0: {} + bowser@2.14.1: {} + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 @@ -10920,8 +13835,15 @@ snapshots: node-releases: 2.0.27 update-browserslist-db: 1.2.3(browserslist@4.28.1) + buffer-crc32@0.2.13: {} + buffer-from@1.1.2: {} + buffer@5.6.0: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -10974,6 +13896,8 @@ snapshots: dependencies: readdirp: 5.0.0 + chrome-trace-event@1.0.4: {} + class-variance-authority@0.7.1: dependencies: clsx: 2.1.1 @@ -11016,6 +13940,8 @@ snapshots: comma-separated-tokens@2.0.3: {} + commander@2.20.3: {} + commander@7.2.0: {} commander@8.3.0: {} @@ -11055,6 +13981,20 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + css-loader@5.2.7(webpack@5.105.0(esbuild@0.25.12)): + dependencies: + icss-utils: 5.1.0(postcss@8.5.6) + loader-utils: 2.0.4 + postcss: 8.5.6 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.6) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.6) + postcss-modules-scope: 3.2.1(postcss@8.5.6) + postcss-modules-values: 4.0.0(postcss@8.5.6) + postcss-value-parser: 4.2.0 + schema-utils: 3.3.0 + semver: 7.7.4 + webpack: 5.105.0(esbuild@0.25.12) + css-select@5.2.2: dependencies: boolbase: 1.0.0 @@ -11131,6 +14071,8 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + define-lazy-prop@2.0.0: {} + define-properties@1.2.1: dependencies: define-data-property: 1.1.4 @@ -11233,6 +14175,12 @@ snapshots: emoji-regex@9.2.2: {} + emojis-list@3.0.0: {} + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + enhanced-resolve@5.19.0: dependencies: graceful-fs: 4.2.11 @@ -11246,6 +14194,10 @@ snapshots: dependencies: is-arrayish: 0.2.1 + error-stack-parser@2.1.4: + dependencies: + stackframe: 1.3.4 + es-abstract@1.24.1: dependencies: array-buffer-byte-length: 1.0.2 @@ -11326,6 +14278,8 @@ snapshots: iterator.prototype: 1.1.5 safe-array-concat: 1.1.3 + es-module-lexer@2.0.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -11393,6 +14347,34 @@ snapshots: '@esbuild/win32-ia32': 0.18.20 '@esbuild/win32-x64': 0.18.20 + esbuild@0.25.0: + 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 + esbuild@0.25.12: optionalDependencies: '@esbuild/aix-ppc64': 0.25.12 @@ -11585,6 +14567,11 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 @@ -11645,6 +14632,8 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.16.0) eslint-visitor-keys: 4.2.1 + esprima@4.0.1: {} + esquery@1.7.0: dependencies: estraverse: 5.3.0 @@ -11653,6 +14642,8 @@ snapshots: dependencies: estraverse: 5.3.0 + estraverse@4.3.0: {} + estraverse@5.3.0: {} estree-util-attach-comments@3.0.0: @@ -11694,10 +14685,34 @@ snapshots: esutils@2.0.3: {} + events@3.3.0: {} + eventsource-parser@3.0.6: {} + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + extend@3.0.2: {} + extract-zip@2.0.1: + dependencies: + debug: 4.4.3 + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.3 + transitivePeerDependencies: + - supports-color + fast-deep-equal@3.1.3: {} fast-glob@3.3.1: @@ -11712,6 +14727,15 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-uri@3.1.0: {} + + fast-xml-builder@1.0.0: {} + + fast-xml-parser@5.4.1: + dependencies: + fast-xml-builder: 1.0.0 + strnum: 2.2.0 + fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -11720,6 +14744,10 @@ snapshots: dependencies: format: 0.2.2 + fd-slicer@1.1.0: + dependencies: + pend: 1.2.0 + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 @@ -11765,6 +14793,8 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) + fs-monkey@1.0.3: {} + fsevents@2.3.3: optional: true @@ -11807,7 +14837,7 @@ snapshots: transitivePeerDependencies: - supports-color - fumadocs-mdx@14.2.8(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.14)(fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.477.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(vite@7.3.1(@types/node@20.19.33)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)): + fumadocs-mdx@14.2.8(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.14)(fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.477.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(vite@7.3.1(@types/node@20.19.33)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)): dependencies: '@mdx-js/mdx': 3.1.1 '@standard-schema/spec': 1.1.0 @@ -11833,7 +14863,7 @@ snapshots: '@types/react': 19.2.14 next: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: 19.2.4 - vite: 7.3.1(@types/node@20.19.33)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0) + vite: 7.3.1(@types/node@20.19.33)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0) transitivePeerDependencies: - supports-color @@ -11912,6 +14942,12 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 + get-stream@5.2.0: + dependencies: + pump: 3.0.4 + + get-stream@6.0.1: {} + get-symbol-description@1.1.0: dependencies: call-bound: 1.0.4 @@ -11932,6 +14968,8 @@ snapshots: dependencies: is-glob: 4.0.3 + glob-to-regexp@0.4.1: {} + globals@14.0.0: {} globalthis@1.0.4: @@ -12142,10 +15180,18 @@ snapshots: html-void-elements@3.0.0: {} + human-signals@2.1.0: {} + + icss-utils@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + icu-minify@4.8.3: dependencies: '@formatjs/icu-messageformat-parser': 3.5.1 + ieee754@1.2.1: {} + ignore@5.3.2: {} ignore@7.0.5: {} @@ -12161,6 +15207,8 @@ snapshots: imurmurhash@0.1.4: {} + inherits@2.0.4: {} + inline-style-parser@0.2.7: {} internal-slot@1.1.0: @@ -12240,6 +15288,8 @@ snapshots: is-decimal@2.0.1: {} + is-docker@2.2.1: {} + is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: @@ -12292,6 +15342,8 @@ snapshots: dependencies: call-bound: 1.0.4 + is-stream@2.0.1: {} + is-string@1.1.1: dependencies: call-bound: 1.0.4 @@ -12318,6 +15370,10 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + isarray@2.0.5: {} isexe@2.0.0: {} @@ -12331,6 +15387,12 @@ snapshots: has-symbols: 1.1.0 set-function-name: 2.0.2 + jest-worker@27.5.1: + dependencies: + '@types/node': 20.19.33 + merge-stream: 2.0.0 + supports-color: 8.1.1 + jiti@2.6.1: {} jotai-optics@0.4.0(jotai@2.8.4(@types/react@19.2.14)(react@19.2.4))(optics-ts@2.4.1): @@ -12379,6 +15441,8 @@ snapshots: json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-schema@0.4.0: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -12414,6 +15478,8 @@ snapshots: dependencies: json-buffer: 3.0.1 + kleur@3.0.3: {} + language-subtag-registry@0.3.23: {} language-tags@1.0.9: @@ -12480,6 +15546,14 @@ snapshots: lines-and-columns@1.2.4: {} + loader-runner@4.3.1: {} + + loader-utils@2.0.4: + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 2.2.3 + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -12490,6 +15564,8 @@ snapshots: lodash.merge@4.6.2: {} + lodash.sortby@4.7.0: {} + lodash@4.17.23: {} long@5.3.2: {} @@ -12519,6 +15595,10 @@ snapshots: dependencies: yallist: 3.1.1 + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + lucide-react@0.477.0(react@19.2.4): dependencies: react: 19.2.4 @@ -12720,6 +15800,17 @@ snapshots: mdn-data@2.0.30: {} + mediabunny@1.37.0: + dependencies: + '@types/dom-mediacapture-transform': 0.1.11 + '@types/dom-webcodecs': 0.1.13 + + memfs@3.4.3: + dependencies: + fs-monkey: 1.0.3 + + merge-stream@2.0.0: {} + merge2@1.4.1: {} micromark-core-commonmark@2.0.3: @@ -13001,6 +16092,14 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mimic-fn@2.1.0: {} + minimatch@3.1.3: dependencies: brace-expansion: 1.1.12 @@ -13009,6 +16108,8 @@ snapshots: dependencies: brace-expansion: 5.0.3 + minimist@1.2.6: {} + minimist@1.2.8: {} motion-dom@12.34.3: @@ -13039,6 +16140,8 @@ snapshots: negotiator@1.0.0: {} + neo-async@2.6.2: {} + next-intl-swc-plugin-extractor@4.8.3: {} next-intl@4.8.3(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3): @@ -13104,6 +16207,10 @@ snapshots: node-releases@2.0.27: {} + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + npm-to-yarn@3.0.1: {} nth-check@2.1.1: @@ -13156,6 +16263,14 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + oniguruma-parser@0.12.1: {} oniguruma-to-es@4.3.4: @@ -13164,6 +16279,12 @@ snapshots: regex: 6.1.0 regex-recursion: 6.0.2 + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + optics-ts@2.4.1: {} optionator@0.9.4: @@ -13233,6 +16354,8 @@ snapshots: path-type@4.0.0: {} + pend@1.2.0: {} + performance-now@2.1.0: {} pg-cloudflare@1.3.0: @@ -13298,6 +16421,27 @@ snapshots: possible-typed-array-names@1.1.0: {} + postcss-modules-extract-imports@3.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-modules-local-by-default@4.2.0(postcss@8.5.6): + dependencies: + icss-utils: 5.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-selector-parser: 7.1.1 + postcss-value-parser: 4.2.0 + + postcss-modules-scope@3.2.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.1 + + postcss-modules-values@4.0.0(postcss@8.5.6): + dependencies: + icss-utils: 5.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-selector-parser@6.0.10: dependencies: cssesc: 3.0.0 @@ -13308,6 +16452,8 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 + postcss-value-parser@4.2.0: {} + postcss@8.4.31: dependencies: nanoid: 3.3.11 @@ -13356,10 +16502,17 @@ snapshots: prelude-ls@1.2.1: {} + prettier@3.8.1: {} + prismjs@1.27.0: {} prismjs@1.30.0: {} + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 @@ -13389,6 +16542,11 @@ snapshots: proxy-compare@2.6.0: {} + pump@3.0.4: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + punycode@2.3.1: {} query-selector-shadow-dom@1.0.1: {} @@ -13540,6 +16698,8 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) + react-refresh@0.18.0: {} + react-remove-scroll-bar@2.3.8(@types/react@19.2.14)(react@19.2.4): dependencies: react: 19.2.4 @@ -13623,8 +16783,22 @@ snapshots: react@19.2.4: {} + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + readdirp@5.0.0: {} + recast@0.23.11: + dependencies: + ast-types: 0.16.1 + esprima: 4.0.1 + source-map: 0.6.1 + tiny-invariant: 1.3.3 + tslib: 2.8.1 + recma-build-jsx@1.0.0: dependencies: '@types/estree': 1.0.8 @@ -13809,6 +16983,13 @@ snapshots: remend@1.2.1: {} + remotion@4.0.434(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + require-from-string@2.0.2: {} + resolve-from@4.0.0: {} resolve-pkg-maps@1.0.0: {} @@ -13873,6 +17054,8 @@ snapshots: has-symbols: 1.1.0 isarray: 2.0.5 + safe-buffer@5.2.1: {} + safe-push-apply@1.0.0: dependencies: es-errors: 1.3.0 @@ -13886,6 +17069,19 @@ snapshots: scheduler@0.27.0: {} + schema-utils@3.3.0: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.14.0 + ajv-keywords: 3.5.2(ajv@6.14.0) + + schema-utils@4.3.3: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.18.0 + ajv-formats: 2.1.1(ajv@8.18.0) + ajv-keywords: 5.1.0(ajv@8.18.0) + scroll-into-view-if-needed@3.1.0: dependencies: compute-scroll-into-view: 3.1.1 @@ -13896,6 +17092,10 @@ snapshots: semver@6.3.1: {} + semver@7.5.3: + dependencies: + lru-cache: 6.0.0 + semver@7.7.4: {} server-only@0.0.1: {} @@ -13999,6 +17199,10 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + signal-exit@3.0.7: {} + + sisteransi@1.0.5: {} + slate-dom@0.119.0(slate@0.120.0): dependencies: '@juggle/resize-observer': 3.4.0 @@ -14049,8 +17253,14 @@ snapshots: source-map@0.6.1: {} + source-map@0.7.3: {} + source-map@0.7.6: {} + source-map@0.8.0-beta.0: + dependencies: + whatwg-url: 7.1.0 + space-separated-tokens@1.1.5: {} space-separated-tokens@2.0.2: {} @@ -14059,11 +17269,18 @@ snapshots: stable-hash@0.0.5: {} + stackframe@1.3.4: {} + stop-iteration-iterator@1.1.0: dependencies: es-errors: 1.3.0 internal-slot: 1.1.0 + stream-browserify@3.0.0: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + streamdown@2.3.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: clsx: 2.1.1 @@ -14136,6 +17353,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 @@ -14143,8 +17364,16 @@ snapshots: strip-bom@3.0.0: {} + strip-final-newline@2.0.0: {} + strip-json-comments@3.1.1: {} + strnum@2.2.0: {} + + style-loader@4.0.0(webpack@5.105.0(esbuild@0.25.12)): + dependencies: + webpack: 5.105.0(esbuild@0.25.12) + style-to-js@1.1.21: dependencies: style-to-object: 1.0.14 @@ -14164,6 +17393,10 @@ snapshots: dependencies: has-flag: 4.0.0 + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + supports-preserve-symlinks-flag@1.0.0: {} svg-parser@2.0.4: {} @@ -14200,10 +17433,39 @@ snapshots: tapable@2.3.0: {} + terser-webpack-plugin@5.3.17(esbuild@0.25.0)(webpack@5.105.0(esbuild@0.25.12)): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + terser: 5.46.0 + webpack: 5.105.0(esbuild@0.25.12) + optionalDependencies: + esbuild: 0.25.0 + + terser-webpack-plugin@5.3.17(esbuild@0.25.12)(webpack@5.105.0(esbuild@0.25.12)): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + terser: 5.46.0 + webpack: 5.105.0(esbuild@0.25.12) + optionalDependencies: + esbuild: 0.25.12 + + terser@5.46.0: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.16.0 + commander: 2.20.3 + source-map-support: 0.5.21 + throttleit@2.1.0: {} tiny-invariant@1.3.1: {} + tiny-invariant@1.3.3: {} + tinyexec@1.0.2: {} tinyglobby@0.2.15: @@ -14217,6 +17479,10 @@ snapshots: toggle-selection@1.0.6: {} + tr46@1.0.1: + dependencies: + punycode: 2.3.1 + trim-lines@3.0.1: {} trough@2.2.0: {} @@ -14473,7 +17739,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@7.3.1(@types/node@20.19.33)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0): + vite@7.3.1(@types/node@20.19.33)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.3) @@ -14486,12 +17752,92 @@ snapshots: fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.31.1 + terser: 5.46.0 tsx: 4.21.0 + watchpack@2.5.1: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + web-namespaces@2.0.1: {} web-vitals@5.1.0: {} + webidl-conversions@4.0.2: {} + + webpack-sources@3.3.4: {} + + webpack@5.105.0(esbuild@0.25.0): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.16.0 + acorn-import-phases: 1.0.4(acorn@8.16.0) + browserslist: 4.28.1 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.19.0 + es-module-lexer: 2.0.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.1 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.0 + terser-webpack-plugin: 5.3.17(esbuild@0.25.0)(webpack@5.105.0(esbuild@0.25.12)) + watchpack: 2.5.1 + webpack-sources: 3.3.4 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + webpack@5.105.0(esbuild@0.25.12): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.16.0 + acorn-import-phases: 1.0.4(acorn@8.16.0) + browserslist: 4.28.1 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.19.0 + es-module-lexer: 2.0.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.1 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.0 + terser-webpack-plugin: 5.3.17(esbuild@0.25.12)(webpack@5.105.0(esbuild@0.25.12)) + watchpack: 2.5.1 + webpack-sources: 3.3.4 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + whatwg-url@7.1.0: + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -14539,10 +17885,21 @@ snapshots: word-wrap@1.2.5: {} + wrappy@1.0.2: {} + + ws@8.17.1: {} + xtend@4.0.2: {} yallist@3.1.1: {} + yallist@4.0.0: {} + + yauzl@2.10.0: + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + yocto-queue@0.1.0: {} zod-to-json-schema@3.25.1(zod@4.3.6):