commit 45b96ec20f4f82daa2a686ea52194571b9ebdff9
Author: ToxicCrzay270 <185776014+ToxicCrzay270@users.noreply.github.com>
Date: Fri May 15 00:41:38 2026 +0200
Initialize League GUI prototype
diff --git a/.codex/project.md b/.codex/project.md
new file mode 100644
index 0000000..04f7325
--- /dev/null
+++ b/.codex/project.md
@@ -0,0 +1,79 @@
+# Codex Project Notes
+
+## Project
+
+`League of Legends GUI Overhaul` is a React/Vite prototype for a modern, dark, MOBA-/fantasy-inspired client interface.
+
+Repository:
+
+```text
+Toxic/league-of-legends-gui-overhaul
+```
+
+Remote:
+
+```text
+https://git.wilkensxl.de/Toxic/league-of-legends-gui-overhaul.git
+```
+
+## Commands
+
+Use these commands as the source of truth:
+
+```bash
+npm install
+npm run dev
+npm run build
+npm test
+git diff --check
+```
+
+There is no separate lint script yet. `npm run build` runs `tsc --noEmit` before the Vite build. There is no audit or release-check script yet.
+
+## Stack
+
+```text
+React, Vite, TypeScript, React Router, Vitest, Testing Library, CSS custom properties.
+```
+
+Package manager or build tool:
+
+```text
+npm
+```
+
+## Build Artifacts
+
+Release artifacts are produced in:
+
+Expected files:
+
+```text
+dist/
+```
+
+## Security Rules
+
+- Do not commit secrets, tokens, `.env` files, certificates, or private keys.
+- Treat generated credentials as sensitive.
+- Prefer local generation and local processing for user data.
+- Keep dependency audit results visible in CI once dependencies exist.
+- Do not add external network calls unless the feature explicitly requires them.
+- Document any external assets, API calls, telemetry, or package publishing behavior when implementation begins.
+
+## Release Rules
+
+No published release process exists yet.
+
+Before a release:
+
+1. run `npm run build`,
+2. run `npm test`,
+3. run the release checklist,
+4. verify CI is green,
+5. verify download links when publishing artifacts,
+6. update README and changelog,
+7. create a tag,
+8. create the release.
+
+Do not create releases unless the user explicitly asks for a release.
diff --git a/.gitea/workflows/dependency-check.yml b/.gitea/workflows/dependency-check.yml
new file mode 100644
index 0000000..f601304
--- /dev/null
+++ b/.gitea/workflows/dependency-check.yml
@@ -0,0 +1,114 @@
+name: Scheduled Dependency Check
+
+on:
+ schedule:
+ - cron: "29 3 * * 2"
+ workflow_dispatch:
+
+jobs:
+ dependency-check:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Detect project stack
+ id: detect
+ shell: bash
+ run: |
+ stacks=""
+
+ [ -f package.json ] && stacks="${stacks} node"
+ { [ -f pyproject.toml ] || [ -f requirements.txt ]; } && stacks="${stacks} python"
+ [ -f Cargo.toml ] && stacks="${stacks} rust"
+ [ -f go.mod ] && stacks="${stacks} go"
+ { [ -f Dockerfile ] || [ -f compose.yml ] || [ -f docker-compose.yml ]; } && stacks="${stacks} docker"
+
+ echo "stacks=${stacks:-generic}" >> "$GITHUB_OUTPUT"
+ echo "Detected stacks:${stacks:- generic}"
+
+ - name: Node dependency report
+ if: contains(steps.detect.outputs.stacks, 'node')
+ shell: bash
+ run: |
+ if [ -f package-lock.json ] || [ -f npm-shrinkwrap.json ]; then
+ npm ci
+ else
+ npm install --package-lock-only --ignore-scripts
+ fi
+
+ echo "Security audit:"
+ npm audit --omit=dev --audit-level=high
+
+ echo
+ echo "Outdated dependencies:"
+ npm outdated || true
+
+ - name: Python dependency report
+ if: contains(steps.detect.outputs.stacks, 'python')
+ shell: bash
+ run: |
+ python -m pip install --upgrade pip pip-audit
+
+ echo "Security audit:"
+ if [ -f requirements.txt ]; then
+ pip-audit -r requirements.txt
+ else
+ pip-audit
+ fi
+
+ echo
+ echo "Outdated packages:"
+ python -m pip list --outdated || true
+
+ - name: Rust dependency report
+ if: contains(steps.detect.outputs.stacks, 'rust')
+ shell: bash
+ run: |
+ cargo install cargo-audit cargo-outdated --locked
+
+ echo "Security audit:"
+ cargo audit
+
+ echo
+ echo "Outdated crates:"
+ cargo outdated || true
+
+ - name: Go dependency report
+ if: contains(steps.detect.outputs.stacks, 'go')
+ shell: bash
+ run: |
+ go install golang.org/x/vuln/cmd/govulncheck@latest
+
+ echo "Security audit:"
+ govulncheck ./...
+
+ echo
+ echo "Available dependency updates:"
+ go list -u -m all || true
+
+ - name: Docker base image report
+ if: contains(steps.detect.outputs.stacks, 'docker')
+ shell: bash
+ run: |
+ echo "Docker image references:"
+ grep -RInE --exclude-dir=.git --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=build '^\s*FROM\s+' Dockerfile* . 2>/dev/null || true
+
+ echo
+ echo "Review Docker base images manually for pinned versions, official sources, and current security status."
+
+ - name: Dependency guidance
+ shell: bash
+ run: |
+ cat <<'EOF'
+ Dependency check completed.
+
+ This workflow reports vulnerabilities and available updates. It does
+ not modify dependency files, create pull requests, or publish packages.
+
+ Recommended manual follow-up:
+ - update dependencies in a focused branch,
+ - run the project test/build commands,
+ - review lockfile diffs carefully,
+ - document intentionally held versions.
+ EOF
diff --git a/.gitea/workflows/release-dry-run.yml b/.gitea/workflows/release-dry-run.yml
new file mode 100644
index 0000000..25e61ab
--- /dev/null
+++ b/.gitea/workflows/release-dry-run.yml
@@ -0,0 +1,133 @@
+name: Release Dry Run
+
+on:
+ push:
+ branches:
+ - main
+ - master
+ workflow_dispatch:
+
+jobs:
+ release-dry-run:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Inspect release metadata
+ shell: bash
+ run: |
+ missing=0
+
+ required_docs=(
+ "README.md"
+ "CHANGELOG.md"
+ "SECURITY.md"
+ "docs/release-checklist.md"
+ )
+
+ for file in "${required_docs[@]}"; do
+ if [ ! -f "$file" ]; then
+ echo "Missing release document: $file"
+ missing=1
+ fi
+ done
+
+ placeholder_paths=(README.md AGENTS.md .codex docs)
+ placeholder_pattern='PROJECT_NAME|PROJECT_DESCRIPTION|REPOSITORY_OWNER|REPOSITORY_NAME|PACKAGE_NAME|ARTIFACT_NAME|ARTIFACT_OUTPUT_DIRECTORY|DOWNLOAD_URL|BUILD_COMMAND|TEST_COMMAND|LINT_COMMAND|AUDIT_COMMAND'
+
+ for path in "${placeholder_paths[@]}"; do
+ [ -e "$path" ] || continue
+ if grep -RInE --exclude-dir=.git "$placeholder_pattern" "$path"; then
+ echo "Unresolved template placeholders found."
+ missing=1
+ fi
+ done
+
+ if [ "$missing" -eq 1 ]; then
+ exit 1
+ fi
+
+ - name: Detect project stack
+ id: detect
+ shell: bash
+ run: |
+ stacks=""
+
+ [ -f package.json ] && stacks="${stacks} node"
+ { [ -f pyproject.toml ] || [ -f requirements.txt ]; } && stacks="${stacks} python"
+ [ -f Cargo.toml ] && stacks="${stacks} rust"
+ [ -f go.mod ] && stacks="${stacks} go"
+
+ echo "stacks=${stacks:-generic}" >> "$GITHUB_OUTPUT"
+ echo "Detected stacks:${stacks:- generic}"
+
+ - name: Node release checks
+ if: contains(steps.detect.outputs.stacks, 'node')
+ shell: bash
+ run: |
+ if [ -f package-lock.json ] || [ -f npm-shrinkwrap.json ]; then
+ npm ci
+ else
+ npm install
+ fi
+
+ node -e "const p=require('./package.json'); if(!p.name||!p.version){throw new Error('package.json needs name and version')}; console.log(p.name+'@'+p.version)"
+
+ npm run lint --if-present
+ npm test --if-present
+ npm run build --if-present
+ npm run release:check --if-present
+
+ - name: Python release checks
+ if: contains(steps.detect.outputs.stacks, 'python')
+ shell: bash
+ run: |
+ python -m pip install --upgrade pip
+
+ if [ -f requirements.txt ]; then
+ python -m pip install -r requirements.txt
+ fi
+
+ if [ -f pyproject.toml ]; then
+ python -m pip install build
+ python -m build
+ else
+ echo "No pyproject.toml found; skipped Python package build."
+ fi
+
+ - name: Rust release checks
+ if: contains(steps.detect.outputs.stacks, 'rust')
+ shell: bash
+ run: |
+ cargo test
+ cargo build --release
+
+ - name: Go release checks
+ if: contains(steps.detect.outputs.stacks, 'go')
+ shell: bash
+ run: |
+ go test ./...
+ go build ./...
+
+ - name: Artifact report
+ shell: bash
+ run: |
+ echo "Potential release artifacts:"
+ find . \
+ -path ./.git -prune -o \
+ -path ./node_modules -prune -o \
+ -path './dist/*' -type f -print -o \
+ -path './build/*' -type f -print -o \
+ -path './release/*' -type f -print -o \
+ -path './target/release/*' -type f -print \
+ | sed 's#^\./##' \
+ | head -200
+
+ cat <<'EOF'
+
+ Release dry run completed.
+
+ This workflow verifies release readiness. It does not create tags,
+ releases, packages, or upload artifacts.
+ EOF
diff --git a/.gitea/workflows/repo-cleanup.yml b/.gitea/workflows/repo-cleanup.yml
new file mode 100644
index 0000000..a4b7156
--- /dev/null
+++ b/.gitea/workflows/repo-cleanup.yml
@@ -0,0 +1,139 @@
+name: Scheduled Repository Cleanup Check
+
+on:
+ schedule:
+ - cron: "43 3 * * 1"
+ workflow_dispatch:
+
+jobs:
+ cleanup-check:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Check ignored and untracked generated files
+ shell: bash
+ run: |
+ echo "Ignored files that would be skipped by git:"
+ git status --ignored --short || true
+
+ echo
+ echo "Tracked generated files check:"
+ generated_patterns=(
+ '(^|/)node_modules/'
+ '(^|/)dist/'
+ '(^|/)build/'
+ '(^|/)out/'
+ '(^|/)release/'
+ '(^|/)target/'
+ '(^|/)coverage/'
+ '\.log$'
+ '\.tmp$'
+ '\.temp$'
+ )
+
+ found=0
+ tracked_files="$(git ls-files)"
+ for pattern in "${generated_patterns[@]}"; do
+ if echo "$tracked_files" | grep -Ei "$pattern"; then
+ found=1
+ fi
+ done
+
+ if [ "$found" -eq 1 ]; then
+ echo "Generated files appear to be tracked. Review .gitignore and remove generated outputs from version control if appropriate."
+ exit 1
+ fi
+
+ - name: Check large tracked files
+ shell: bash
+ run: |
+ limit_bytes="${LARGE_FILE_LIMIT_BYTES:-5242880}"
+ found=0
+
+ while IFS= read -r file; do
+ [ -f "$file" ] || continue
+ size="$(wc -c < "$file")"
+ if [ "$size" -gt "$limit_bytes" ]; then
+ echo "${file} is ${size} bytes, above limit ${limit_bytes}."
+ found=1
+ fi
+ done < <(git ls-files)
+
+ if [ "$found" -eq 1 ]; then
+ echo "Large tracked files found. Move release artifacts to packages/releases or document why they belong in git."
+ exit 1
+ fi
+
+ - name: Check local config and secret-prone files
+ shell: bash
+ run: |
+ found=0
+
+ risky_patterns=(
+ '^\.env$'
+ '^\.env\.'
+ '\.pfx$'
+ '\.p12$'
+ '\.pem$'
+ '\.key$'
+ '\.token$'
+ '(^|/)secrets/'
+ )
+
+ tracked_files="$(git ls-files)"
+ for pattern in "${risky_patterns[@]}"; do
+ if echo "$tracked_files" | grep -Ei "$pattern" | grep -vE '^\.env\.example$'; then
+ found=1
+ fi
+ done
+
+ if [ "$found" -eq 1 ]; then
+ echo "Secret-prone local config files are tracked. Review immediately."
+ exit 1
+ fi
+
+ - name: Check stale branches
+ shell: bash
+ run: |
+ git fetch --all --prune
+
+ protected='^(main|master|develop|dev|release|staging|production)$'
+ cutoff="$(date -u -d '90 days ago' +%s)"
+ found=0
+
+ while IFS='|' read -r branch timestamp; do
+ branch="${branch#origin/}"
+ [ "$branch" = "HEAD" ] && continue
+ echo "$branch" | grep -Eq "$protected" && continue
+
+ if [ "$timestamp" -lt "$cutoff" ]; then
+ echo "Stale remote branch candidate: ${branch}"
+ found=1
+ fi
+ done < <(git for-each-ref refs/remotes/origin --format='%(refname:short)|%(committerdate:unix)')
+
+ if [ "$found" -eq 1 ]; then
+ echo "Stale branch candidates found. Review manually before deleting anything."
+ exit 1
+ fi
+
+ - name: Cleanup guidance
+ shell: bash
+ run: |
+ cat <<'EOF'
+ Repository cleanup check completed.
+
+ This workflow reports cleanup candidates. It does not delete branches,
+ packages, releases, or files automatically.
+
+ Recommended manual follow-up:
+ - remove generated files from git,
+ - update .gitignore,
+ - move large artifacts to releases or package registry,
+ - review stale branches,
+ - document intentional exceptions.
+ EOF
diff --git a/.gitea/workflows/security-scan.yml b/.gitea/workflows/security-scan.yml
new file mode 100644
index 0000000..211d8ae
--- /dev/null
+++ b/.gitea/workflows/security-scan.yml
@@ -0,0 +1,173 @@
+name: Scheduled Security Scan
+
+on:
+ schedule:
+ - cron: "17 3 * * 1"
+ workflow_dispatch:
+
+jobs:
+ security-scan:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Detect project stack
+ id: detect
+ shell: bash
+ run: |
+ stacks=""
+
+ [ -f package.json ] && stacks="${stacks} node"
+ { [ -f pyproject.toml ] || [ -f requirements.txt ]; } && stacks="${stacks} python"
+ [ -f Cargo.toml ] && stacks="${stacks} rust"
+ [ -f go.mod ] && stacks="${stacks} go"
+ { [ -f Dockerfile ] || [ -f compose.yml ] || [ -f docker-compose.yml ]; } && stacks="${stacks} docker"
+
+ echo "stacks=${stacks:-generic}" >> "$GITHUB_OUTPUT"
+ echo "Detected stacks:${stacks:- generic}"
+
+ - name: Node production dependency audit
+ if: contains(steps.detect.outputs.stacks, 'node')
+ run: npm audit --omit=dev --audit-level=high
+
+ - name: Python dependency audit
+ if: contains(steps.detect.outputs.stacks, 'python')
+ shell: bash
+ run: |
+ python -m pip install --upgrade pip pip-audit
+ if [ -f requirements.txt ]; then
+ pip-audit -r requirements.txt
+ else
+ pip-audit
+ fi
+
+ - name: Rust dependency audit
+ if: contains(steps.detect.outputs.stacks, 'rust')
+ shell: bash
+ run: |
+ cargo install cargo-audit --locked
+ cargo audit
+
+ - name: Go vulnerability scan
+ if: contains(steps.detect.outputs.stacks, 'go')
+ shell: bash
+ run: |
+ go install golang.org/x/vuln/cmd/govulncheck@latest
+ govulncheck ./...
+
+ - name: Suspicious code pattern scan
+ shell: bash
+ run: |
+ grep_excludes=(
+ --exclude-dir=.git
+ --exclude-dir=node_modules
+ --exclude-dir=dist
+ --exclude-dir=build
+ --exclude-dir=release
+ --exclude=security-scan.yml
+ )
+
+ patterns=(
+ 'eval\s*\('
+ 'new Function\s*\('
+ 'dangerouslySetInnerHTML'
+ 'innerHTML\s*='
+ 'child_process'
+ 'exec\s*\('
+ 'spawn\s*\('
+ 'shell\.openExternal'
+ 'nodeIntegration:\s*true'
+ 'webSecurity:\s*false'
+ 'allowRunningInsecureContent:\s*true'
+ 'curl .*sh'
+ 'wget .*sh'
+ )
+
+ found=0
+ for pattern in "${patterns[@]}"; do
+ if grep -RInE "${grep_excludes[@]}" "$pattern" .; then
+ found=1
+ fi
+ done
+
+ if [ "$found" -eq 1 ]; then
+ echo "Suspicious code patterns were found. Review the matches above."
+ exit 1
+ fi
+
+ - name: Secret and config leak scan
+ shell: bash
+ run: |
+ grep_excludes=(
+ --exclude-dir=.git
+ --exclude-dir=node_modules
+ --exclude-dir=dist
+ --exclude-dir=build
+ --exclude-dir=release
+ --exclude=security-scan.yml
+ )
+
+ patterns=(
+ 'BEGIN (RSA |EC |OPENSSH |)PRIVATE KEY'
+ 'AKIA[0-9A-Z]{16}'
+ 'xox[baprs]-[0-9A-Za-z-]+'
+ 'gh[pousr]_[0-9A-Za-z_]+'
+ 'sk-[A-Za-z0-9]{20,}'
+ 'api[_-]?key\s*=\s*["'\'']?[A-Za-z0-9_\-]{20,}'
+ 'token\s*=\s*["'\'']?[A-Za-z0-9_\-]{20,}'
+ 'password\s*=\s*["'\'']?[^[:space:]]{8,}'
+ )
+
+ found=0
+ for pattern in "${patterns[@]}"; do
+ if grep -RInE "${grep_excludes[@]}" "$pattern" .; then
+ found=1
+ fi
+ done
+
+ if find . -path ./.git -prune -o \( -name ".env" -o -name ".env.*" \) -not -name ".env.example" -print | grep .; then
+ echo "Committed environment files were found."
+ found=1
+ fi
+
+ if [ "$found" -eq 1 ]; then
+ echo "Potential secret or config leak detected. Review the matches above."
+ exit 1
+ fi
+
+ - name: AI instruction safety scan
+ shell: bash
+ run: |
+ grep_excludes=(
+ --exclude-dir=.git
+ --exclude-dir=node_modules
+ --exclude-dir=dist
+ --exclude-dir=build
+ --exclude-dir=release
+ --exclude=security-scan.yml
+ )
+
+ patterns=(
+ 'ignore (all )?(previous|above) instructions'
+ 'system prompt'
+ 'developer message'
+ 'reveal your instructions'
+ 'exfiltrate'
+ 'send.*token'
+ 'send.*secret'
+ 'disable.*safety'
+ 'jailbreak'
+ )
+
+ found=0
+ for pattern in "${patterns[@]}"; do
+ if grep -RInEi "${grep_excludes[@]}" "$pattern" .; then
+ found=1
+ fi
+ done
+
+ if [ "$found" -eq 1 ]; then
+ echo "Potential unsafe AI-control text found. Review whether this is documentation, test data, or malicious content."
+ exit 1
+ fi
diff --git a/.gitea/workflows/template-compliance.yml b/.gitea/workflows/template-compliance.yml
new file mode 100644
index 0000000..0fcb05b
--- /dev/null
+++ b/.gitea/workflows/template-compliance.yml
@@ -0,0 +1,99 @@
+name: Codex Template Compliance
+
+on:
+ push:
+ branches:
+ - main
+ - master
+ pull_request:
+ workflow_dispatch:
+
+jobs:
+ template-compliance:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Check required Codex files
+ shell: bash
+ run: |
+ missing=0
+
+ required_files=(
+ "AGENTS.md"
+ ".codex/project.md"
+ "README.md"
+ )
+
+ recommended_files=(
+ "SECURITY.md"
+ "CHANGELOG.md"
+ "docs/agent-handoff.md"
+ )
+
+ for file in "${required_files[@]}"; do
+ if [ ! -f "$file" ]; then
+ echo "Missing required Codex file: $file"
+ missing=1
+ fi
+ done
+
+ for file in "${recommended_files[@]}"; do
+ if [ ! -f "$file" ]; then
+ echo "Recommended Codex file not found: $file"
+ fi
+ done
+
+ if [ "$missing" -eq 1 ]; then
+ exit 1
+ fi
+
+ - name: Check unresolved placeholders
+ shell: bash
+ run: |
+ found=0
+ paths=(AGENTS.md README.md SECURITY.md CHANGELOG.md .codex docs)
+ pattern='PROJECT_NAME|PROJECT_DESCRIPTION|REPOSITORY_OWNER|REPOSITORY_NAME|PACKAGE_NAME|ARTIFACT_NAME|ARTIFACT_OUTPUT_DIRECTORY|AUTHOR_NAME|PROJECT_STACK|DOWNLOAD_URL|BUILD_COMMAND|TEST_COMMAND|LINT_COMMAND|AUDIT_COMMAND|README_COMMAND|INSTALL_COMMAND|DEV_COMMAND|PACKAGE_MANAGER|PROJECT_VERSION'
+
+ for path in "${paths[@]}"; do
+ [ -e "$path" ] || continue
+ if grep -RInE --exclude-dir=.git "$pattern" "$path"; then
+ found=1
+ fi
+ done
+
+ if [ "$found" -eq 1 ]; then
+ echo "Unresolved template placeholders found. Replace real values or mark genuinely unknown values as PENDING."
+ exit 1
+ fi
+
+ - name: Check workflow baseline
+ shell: bash
+ run: |
+ echo "Detected Gitea workflows:"
+ find .gitea/workflows -maxdepth 1 -type f -name '*.yml' -print 2>/dev/null || true
+
+ if [ ! -f ".gitea/workflows/security-scan.yml" ]; then
+ echo "Recommended workflow missing: .gitea/workflows/security-scan.yml"
+ fi
+
+ if [ ! -f ".gitea/workflows/repo-cleanup.yml" ]; then
+ echo "Recommended workflow missing: .gitea/workflows/repo-cleanup.yml"
+ fi
+
+ - name: Compliance guidance
+ shell: bash
+ run: |
+ cat <<'EOF'
+ Codex template compliance check completed.
+
+ This workflow verifies agent context and template hygiene. It does
+ not change files automatically.
+
+ Recommended manual follow-up:
+ - add missing required Codex context files,
+ - replace unresolved placeholders,
+ - keep README and project context aligned,
+ - document intentional exceptions in .codex/project.md.
+ EOF
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..12b73c8
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+node_modules/
+dist/
+coverage/
+.vite/
+.codex-agent-repository-kit/
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..e6c0ee1
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,66 @@
+# Agent Instructions
+
+## Project
+
+League of Legends GUI Overhaul: React/Vite prototype for a modern, dark, MOBA-/fantasy-inspired client interface.
+
+## Repository Rules
+
+- Prefer existing project patterns over new abstractions.
+- Keep changes scoped to the user's request.
+- Do not commit secrets, `.env` files, private keys, certificates, or tokens.
+- Do not rewrite history or run destructive git commands unless explicitly requested.
+- Do not create a release unless explicitly requested.
+- Check repository state before editing and before finishing. Preserve unrelated user changes.
+- Replace applicable placeholders in copied templates. Remove non-applicable placeholder sections instead of leaving fake values.
+- The current stack is Node, React, Vite, TypeScript, React Router, Vitest, Testing Library, and CSS custom properties.
+- If `GITEA_TOKEN` is available locally, use it only for read-only Gitea API checks such as private repository metadata, package-read visibility, and Actions run status. Never print, commit, or store the token.
+- After pushing commits that trigger a Gitea workflow, poll the workflow run until it succeeds or a concrete blocker is known.
+- Repository cleanup automation must be non-destructive. Do not delete branches, packages, releases, or tracked files without explicit user approval.
+- Dependency, compliance, and release dry-run automation must report findings only. Do not auto-update dependencies, auto-open PRs, create tags, publish packages, or create releases without explicit user approval.
+
+## Commands
+
+Use these commands:
+
+```bash
+npm install
+npm run dev
+npm run build
+npm test
+git diff --check
+```
+
+There is no separate lint script yet. `npm run build` runs `tsc --noEmit` before the Vite build.
+
+Keep `.codex/project.md` and this `AGENTS.md` aligned when commands, artifact paths, or release rules change.
+
+## Artifacts
+
+Build output is produced in:
+
+```text
+dist/
+```
+
+No release package naming or download verification process exists yet.
+
+## Security Notes
+
+- Review `docs/security-review.md` before release work.
+- Fill `docs/security-review.md` with actual checked commands and results when performing release-readiness work.
+- Review scheduled security workflow failures before changing code. Treat matches as leads: they may be true positives, documentation examples, or test fixtures.
+- Review repository cleanup workflow failures as maintenance leads. Document intentional exceptions instead of blindly deleting files.
+- Review dependency and template compliance workflow failures as maintenance leads. Preserve project-specific conventions when they are documented.
+- Treat generated credentials and config files as sensitive.
+- Keep external network calls documented.
+- Prefer local processing for user data.
+- Keep CI publishing secrets in repository or organization secrets, not in tracked files.
+
+## Finish Checklist
+
+- `git diff --check` passes.
+- The cheapest reliable verification command has been run, or the reason it could not run is documented.
+- README, changelog, security review, and release checklist are updated when the change touches release behavior.
+- `docs/agent-handoff.md` is updated when work is interrupted, risky, or spans multiple sessions.
+- Any pushed Gitea workflow has been polled to success or a concrete blocker has been reported.
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..4cb4331
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,8 @@
+# Changelog
+
+All notable changes to this project are documented here.
+
+## Unreleased
+
+- Added the Codex Agent Repository Kit baseline.
+- Documented that the project currently has no selected implementation stack or runnable build commands.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..04c6530
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,41 @@
+# Contributing
+
+## Working Rules
+
+- Keep changes scoped to the issue or user request.
+- Prefer existing project patterns.
+- Do not commit secrets, generated credentials, local `.env` files, or private keys.
+- Do not create releases unless explicitly requested.
+- Preserve unrelated user changes.
+
+## Before Committing
+
+Run the cheapest reliable verification commands for this project.
+
+Current available check:
+
+```bash
+git diff --check
+```
+
+Once a real stack exists, add and document lint, test, build, and audit commands.
+
+## Pull Requests
+
+Pull requests should include:
+
+- summary of changes,
+- verification performed,
+- known risks or skipped checks,
+- artifact or download notes when relevant.
+
+## Releases
+
+Before release work, update:
+
+```text
+CHANGELOG.md
+docs/release-checklist.md
+docs/security-review.md
+README.md
+```
diff --git a/INSTALL.md b/INSTALL.md
new file mode 100644
index 0000000..2275f96
--- /dev/null
+++ b/INSTALL.md
@@ -0,0 +1,119 @@
+# Installation
+
+This guide explains how to install and run the League of Legends GUI Overhaul prototype locally.
+
+The project is a React/Vite/TypeScript app. It does not install a game client, connect to Riot services, or include official Riot assets.
+
+## Requirements
+
+- Node.js with npm available in your terminal.
+- Recommended npm version: 10 or newer.
+- A local clone of this repository.
+
+Check your tools:
+
+```powershell
+node --version
+npm --version
+```
+
+## Install Dependencies
+
+Open a terminal in this project folder:
+
+```powershell
+cd D:\Codex\Main\CodexTest\projects\league-of-legends-gui-overhaul
+```
+
+Install dependencies:
+
+```powershell
+npm install
+```
+
+This creates `node_modules/` locally. The folder is ignored by Git.
+
+## Start The GUI
+
+Start the local development server:
+
+```powershell
+npm run dev
+```
+
+Vite prints a local URL, usually:
+
+```text
+http://localhost:5173
+```
+
+Open that URL in your browser.
+
+## Verify The Installation
+
+Run the production build:
+
+```powershell
+npm run build
+```
+
+Run the test suite:
+
+```powershell
+npm test
+```
+
+Optional dependency audit:
+
+```powershell
+npm audit --audit-level=moderate
+```
+
+## Preview A Production Build
+
+After `npm run build`, preview the built app:
+
+```powershell
+npm run preview
+```
+
+## Common Problems
+
+### `npm` is not recognized
+
+Install Node.js from the official Node.js distribution or add an existing Node/npm installation to your `PATH`.
+
+After updating `PATH`, open a new terminal and check again:
+
+```powershell
+npm --version
+```
+
+### Port 5173 is already in use
+
+Start Vite on another port:
+
+```powershell
+npm run dev -- --port 5174
+```
+
+### Dependencies changed
+
+If `package-lock.json` changed or dependencies were updated, reinstall:
+
+```powershell
+npm install
+```
+
+## What This Installs
+
+The install step only installs frontend development dependencies for this prototype:
+
+- React
+- Vite
+- TypeScript
+- React Router
+- Vitest
+- Testing Library
+
+It does not install any Riot software, game files, launchers, or protected assets.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6ff187a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,125 @@
+# League of Legends GUI Overhaul
+
+A React/Vite prototype for a modern, dark, MOBA-/fantasy-inspired client interface. The goal of iteration 1 is a stable base GUI: routing, layout, reusable components, mock data, centralized theme tokens, tests, and documentation.
+
+This project does not use Riot assets and is not a 1:1 clone of the official League of Legends client.
+
+Repository:
+
+```text
+https://git.wilkensxl.de/Toxic/league-of-legends-gui-overhaul.git
+```
+
+## Tech Stack
+
+- React
+- Vite
+- TypeScript
+- React Router
+- Vitest
+- Testing Library
+- CSS Custom Properties
+
+## Setup
+
+For detailed installation steps, see [INSTALL.md](INSTALL.md).
+
+Install dependencies:
+
+```powershell
+npm install
+```
+
+Start the dev server:
+
+```powershell
+npm run dev
+```
+
+Build:
+
+```powershell
+npm run build
+```
+
+Run tests:
+
+```powershell
+npm test
+```
+
+## Project Structure
+
+```text
+src/
+|-- app/
+|-- components/
+| `-- ui/
+|-- config/
+|-- data/
+|-- features/
+|-- layouts/
+|-- pages/
+|-- styles/
+|-- test/
+`-- types/
+```
+
+## Extending The App
+
+Add a page:
+
+1. Create a component in `src/pages/`.
+2. Register it in `src/config/routes.tsx`.
+3. Add a navigation item in `src/config/navigation.ts` only if it should appear in the sidebar.
+
+Add a navigation item:
+
+1. Add an entry with `id`, `label`, `path`, and optional `iconName`, `section`, `enabled`.
+2. Keep sidebar rendering generic; do not hardcode menu entries in `AppShell`.
+
+Adjust theme tokens:
+
+1. Edit `src/styles/theme.css`.
+2. Add preset overrides with `[data-theme="preset-name"]`.
+3. Prefer tokens over page-level hardcoded colors, spacing, shadows, or radii.
+
+Extend mock data:
+
+1. Add or edit records in `src/data/`.
+2. Keep display logic in pages or feature modules, not inside mock files.
+
+## Iteration 1 Includes
+
+- App Shell with header, sidebar, content area, and status footer
+- Routes for Home, Play, Champions, Profile, and Settings
+- A mock champion detail route at `/champions/:championId`
+- A right-side Social Sidebar powered by friend mock data
+- Session-only theme presets from Settings
+- Prototype Toast flows from header, settings, and social actions
+- Base UI components: Button, Panel, SearchInput, Dropdown, Modal, Tabs, Tooltip, Toast
+- Mock data for champions, friends, news, and match history
+- Central CSS theme tokens
+- Smoke tests for rendering, navigation, routing, and core UI components
+
+## Iteration 1 Does Not Include
+
+- Login
+- Riot API integration
+- Matchmaking
+- Persistence
+- Full champion detail content beyond the first placeholder route
+- Plugin engine
+- Final visual polish
+- Official Riot or League assets
+
+## Next Iterations
+
+- Polish the App Shell visuals
+- Add ability, skin, and progression modules to champion detail pages
+- Expand the social sidebar with parties, invites, and richer presence
+- Add persisted theme preferences
+- Use Modal flows in more page-level actions
+- Improve responsive behavior
+- Add subtle transitions and animation states
+- Plan a lightweight plugin/module extension layer
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..ee5b5b1
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,22 @@
+# Security Policy
+
+## Supported Versions
+
+| Version | Supported |
+| --- | --- |
+| Latest project state | Yes |
+
+## Reporting A Vulnerability
+
+Report security issues privately to the project owner.
+
+Do not include secrets, production data, private credentials, or unreleased exploit details in public issues.
+
+## Project Security Principles
+
+- Keep secrets out of the repository.
+- Do not commit `.env` files, tokens, certificates, private keys, or generated credentials.
+- Prefer local processing for user data.
+- Document external network calls when implementation begins.
+- Keep release artifacts reproducible through CI once release artifacts exist.
+- Run dependency audits before releases once dependencies exist.
diff --git a/docs/agent-handoff.md b/docs/agent-handoff.md
new file mode 100644
index 0000000..573a29e
--- /dev/null
+++ b/docs/agent-handoff.md
@@ -0,0 +1,38 @@
+# Agent Handoff
+
+Use this file when a task spans multiple sessions, has unresolved follow-up work, or changes release behavior.
+
+## Current State
+
+```text
+Codex Agent Repository Kit baseline applied. The project is a React/Vite/TypeScript prototype with npm scripts for development, build, and tests.
+```
+
+## Changes Made
+
+- Added agent instructions and project notes.
+- Added security, contribution, changelog, release checklist, security review, and release notes documents.
+- Added non-destructive Gitea maintenance workflows.
+- Project remote set to `https://git.wilkensxl.de/Toxic/league-of-legends-gui-overhaul.git`.
+- Documented current npm build and test commands.
+
+## Verification
+
+| Check | Result |
+| --- | --- |
+| `git diff --check` | PENDING |
+
+## Open Questions
+
+- Separate lint, audit, and release-check scripts.
+- Release packaging and download target.
+
+## Next Steps
+
+- Add separate lint, audit, and release-check scripts if needed.
+- Add or update build workflow around `npm run build` and `npm test`.
+- Update README, `AGENTS.md`, `.codex/project.md`, and release docs when implementation begins.
+
+## Risks
+
+- Release packaging is intentionally limited until download targets are defined.
diff --git a/docs/release-checklist.md b/docs/release-checklist.md
new file mode 100644
index 0000000..72928f8
--- /dev/null
+++ b/docs/release-checklist.md
@@ -0,0 +1,38 @@
+# Release Checklist
+
+No release process exists yet. Complete this checklist only after the project has real build, test, audit, and artifact commands.
+
+## Version
+
+- [ ] Version number updated.
+- [ ] Changelog updated.
+- [ ] README updated.
+
+## Quality
+
+- [ ] Working tree is clean.
+- [ ] Lint or type checks pass.
+- [ ] Tests pass or missing tests are documented.
+- [ ] Build succeeds in CI.
+
+## Security
+
+- [ ] Security review is current.
+- [ ] Dependency audit is clean or documented.
+- [ ] No secrets are committed.
+- [ ] Release artifacts do not contain local config files.
+
+## Artifacts
+
+- [ ] Artifacts are produced by documented commands.
+- [ ] Artifacts are uploaded.
+- [ ] Download links work.
+- [ ] Package registry links work if used.
+- [ ] Installer, portable, or archive naming is clear.
+
+## Release
+
+- [ ] Git tag created.
+- [ ] Release notes written.
+- [ ] Release published.
+- [ ] Post-release download smoke test completed.
diff --git a/docs/release-notes.md b/docs/release-notes.md
new file mode 100644
index 0000000..1174451
--- /dev/null
+++ b/docs/release-notes.md
@@ -0,0 +1,25 @@
+# League of Legends GUI Overhaul PENDING
+
+## Downloads
+
+No release artifacts exist yet.
+
+## Highlights
+
+- Codex Agent Repository Kit baseline applied.
+
+## Security
+
+- Dependency audit: pending until dependencies exist.
+- Secret handling: secrets must not be committed.
+- External network calls: pending until implementation exists.
+
+## Verification
+
+| Check | Result |
+| --- | --- |
+| `git diff --check` | PENDING |
+
+## Notes
+
+Release notes are a placeholder until the project has a releasable artifact.
diff --git a/docs/security-review.md b/docs/security-review.md
new file mode 100644
index 0000000..15cfb91
--- /dev/null
+++ b/docs/security-review.md
@@ -0,0 +1,54 @@
+# Security Review
+
+## Scope
+
+Project:
+
+```text
+League of Legends GUI Overhaul
+```
+
+Reviewed version or commit:
+
+```text
+PENDING
+```
+
+## Code Patterns Checked
+
+- [ ] No `eval`.
+- [ ] No dynamic `Function` constructor.
+- [ ] No unsafe HTML injection.
+- [ ] No unexpected shell execution.
+- [ ] No unexpected external network calls.
+- [ ] No secrets committed.
+- [ ] No unsafe file writes outside expected user-selected paths.
+
+## Dependency Review
+
+Command:
+
+```text
+PENDING
+```
+
+Result:
+
+```text
+No dependency audit exists yet because no implementation stack or dependency manifest exists.
+```
+
+## Runtime Review
+
+- [ ] Least-privilege runtime configuration.
+- [ ] External URLs documented.
+- [ ] Local data storage documented.
+- [ ] Sensitive data is not persisted unless explicitly required.
+
+## Release Notes
+
+Known residual risks:
+
+```text
+The implementation stack, runtime behavior, and artifact process are not defined yet.
+```
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..c0107f2
--- /dev/null
+++ b/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ League GUI Overhaul
+
+
+
+
+
+
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..42ef3a6
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,2534 @@
+{
+ "name": "league-of-legends-gui-overhaul",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "league-of-legends-gui-overhaul",
+ "version": "0.1.0",
+ "dependencies": {
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-router-dom": "^6.30.1"
+ },
+ "devDependencies": {
+ "@testing-library/jest-dom": "^6.6.3",
+ "@testing-library/react": "^16.1.0",
+ "@testing-library/user-event": "^14.5.2",
+ "@types/react": "^18.3.12",
+ "@types/react-dom": "^18.3.1",
+ "@vitejs/plugin-react": "^6.0.1",
+ "jsdom": "^25.0.1",
+ "typescript": "^5.6.3",
+ "vite": "^8.0.11",
+ "vitest": "^4.1.5"
+ }
+ },
+ "node_modules/@adobe/css-tools": {
+ "version": "4.4.4",
+ "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz",
+ "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@asamuzakjp/css-color": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz",
+ "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/css-calc": "^2.1.3",
+ "@csstools/css-color-parser": "^3.0.9",
+ "@csstools/css-parser-algorithms": "^3.0.4",
+ "@csstools/css-tokenizer": "^3.0.3",
+ "lru-cache": "^10.4.3"
+ }
+ },
+ "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
+ "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
+ "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@csstools/color-helpers": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
+ "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@csstools/css-calc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz",
+ "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/css-color-parser": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz",
+ "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/color-helpers": "^5.1.0",
+ "@csstools/css-calc": "^2.1.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/css-parser-algorithms": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz",
+ "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/css-tokenizer": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
+ "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@emnapi/core": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
+ "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.1",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
+ "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
+ "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
+ "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tybys/wasm-util": "^0.10.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1"
+ }
+ },
+ "node_modules/@oxc-project/types": {
+ "version": "0.128.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.128.0.tgz",
+ "integrity": "sha512-huv1Y/LzBJkBVHt3OlC7u0zHBW9qXf1FdD7sGmc1rXc2P1mTwHssYv7jyGx5KAACSCH+9B3Bhn6Z9luHRvf7pQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ }
+ },
+ "node_modules/@remix-run/router": {
+ "version": "1.23.2",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz",
+ "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.0.0-rc.18",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.18.tgz",
+ "integrity": "sha512-lIDyUAfD7U3+BWKzdxMbJcsYHuqXqmGz40aeRqvuAm3y5TkJSYTBW2RDrn65DJFPQqVjUAUqq5uz8urzQ8aBdQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.0.0-rc.18",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.18.tgz",
+ "integrity": "sha512-apJq2ktnGp27nSInMR5Vcj8kY6xJzDAvfdIFlpDcAK/w4cDO58qVoi1YQsES/SKiFNge/6e4CUzgjfHduYqWpQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.0.0-rc.18",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.18.tgz",
+ "integrity": "sha512-5Ofot8xbs+pxRHJqm9/9N/4sTQOvdrwEsmPE9pdLEEoAbdZtG6F2LMDfO1sp6ZAtXJuJV/21ew2srq3W8NXB5g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.0.0-rc.18",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.18.tgz",
+ "integrity": "sha512-7h8eeOTT1eyqJyx64BFCnWZpNm486hGWt2sqeLLgDxA0xI1oGZ9H7gK1S85uNGmBhkdPwa/6reTxfFFKvIsebw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.0.0-rc.18",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.18.tgz",
+ "integrity": "sha512-eRcm/HVt9U/JFu5RKAEKwGQYtDCKWLiaH6wOnsSEp6NMBb/3Os8LgHZlNyzMpFVNmiiMFlfb2zEnebfzJrHFmg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.0.0-rc.18",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.18.tgz",
+ "integrity": "sha512-SOrT/cT4ukTmgnrEz/Hg3m7LBnuCLW9psDeMKrimRWY4I8DmnO7Lco8W2vtqPmMkbVu8iJ+g4GFLVLLOVjJ9DQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.0.0-rc.18",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.18.tgz",
+ "integrity": "sha512-QWjdxN1HJCpBTAcZ5N5F7wju3gVPzRzSpmGzx7na0c/1qpN9CFil+xt+l9lV/1M6/gqHSNXCiqPfwhVJPeLnug==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.0.0-rc.18",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.18.tgz",
+ "integrity": "sha512-ugCOyj7a4d9h3q9B+wXmf6g3a68UsjGh6dob5DHevHGMwDUbhsYNbSPxJsENcIttJZ9jv7qGM2UesLw5jqIhdg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.0.0-rc.18",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.18.tgz",
+ "integrity": "sha512-kKWRhbsotpXkGbcd5dllUWg5gEXcDAa8u5YnP9AV5DYNbvJHGzzuwv7dpmhc8NqKMJldl0a+x76IHbspEpEmdA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.0.0-rc.18",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.18.tgz",
+ "integrity": "sha512-uCo8ElcCIAMyYAZyuIZ81oFkhTSIllNvUCHCAlbhlN4ji3uC28h7IIdlXyIvGO7HsuqnV9p3rD/bpH7XhIyhRw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.0.0-rc.18",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.18.tgz",
+ "integrity": "sha512-XNOQZtuE6yUIvx4rwGemwh8kpL1xvU41FXy/s9K7T/3JVcqGzo3NfKM2HrbrGgfPYGFW42f07Wk++aOC6B9NWA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.0.0-rc.18",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.18.tgz",
+ "integrity": "sha512-tSn/kzrfa7tNOXr7sEacDBN4YsIqTyLqh45IO0nHDwtpKIDNDJr+VFojt+4klSpChxB29JLyduSsE0MKEwa65A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-wasm32-wasi": {
+ "version": "1.0.0-rc.18",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.18.tgz",
+ "integrity": "sha512-+J9YGmc+czgqlhYmwun3S3O0FIZhsH8ep2456xwjAdIOmuJxM7xz4P4PtrxU+Bz17a/5bqPA8o3HAAoX0teUdg==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "1.10.0",
+ "@emnapi/runtime": "1.10.0",
+ "@napi-rs/wasm-runtime": "^1.1.4"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.0.0-rc.18",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.18.tgz",
+ "integrity": "sha512-zsu47DgU0FQzSwi6sU9dZoEdUv7pc1AptSEz/Z8HBg54sV0Pbs3N0+CrIbTsgiu6EyoaNN9CHboqbLaz9lhOyQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.0.0-rc.18",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.18.tgz",
+ "integrity": "sha512-7H+3yqGgmnlDTRRhw/xpYY9J1kf4GC681nVc4GqKhExZTDrVVrV2tsOR9kso0fvgBdcTCcQShx4SLLoHgaLwhg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-rc.7",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz",
+ "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@standard-schema/spec": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
+ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@testing-library/dom": {
+ "version": "10.4.1",
+ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
+ "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/runtime": "^7.12.5",
+ "@types/aria-query": "^5.0.1",
+ "aria-query": "5.3.0",
+ "dom-accessibility-api": "^0.5.9",
+ "lz-string": "^1.5.0",
+ "picocolors": "1.1.1",
+ "pretty-format": "^27.0.2"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@testing-library/jest-dom": {
+ "version": "6.9.1",
+ "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz",
+ "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@adobe/css-tools": "^4.4.0",
+ "aria-query": "^5.0.0",
+ "css.escape": "^1.5.1",
+ "dom-accessibility-api": "^0.6.3",
+ "picocolors": "^1.1.1",
+ "redent": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6",
+ "yarn": ">=1"
+ }
+ },
+ "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz",
+ "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@testing-library/react": {
+ "version": "16.3.2",
+ "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz",
+ "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@testing-library/dom": "^10.0.0",
+ "@types/react": "^18.0.0 || ^19.0.0",
+ "@types/react-dom": "^18.0.0 || ^19.0.0",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@testing-library/user-event": {
+ "version": "14.6.1",
+ "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz",
+ "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ },
+ "peerDependencies": {
+ "@testing-library/dom": ">=7.21.4"
+ }
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.2",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
+ "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/aria-query": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
+ "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/chai": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
+ "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/deep-eql": "*",
+ "assertion-error": "^2.0.1"
+ }
+ },
+ "node_modules/@types/deep-eql": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
+ "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/prop-types": {
+ "version": "15.7.15",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
+ "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/react": {
+ "version": "18.3.28",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz",
+ "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/prop-types": "*",
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "18.3.7",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
+ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^18.0.0"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz",
+ "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rolldown/pluginutils": "1.0.0-rc.7"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0",
+ "babel-plugin-react-compiler": "^1.0.0",
+ "vite": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@rolldown/plugin-babel": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vitest/expect": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz",
+ "integrity": "sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@standard-schema/spec": "^1.1.0",
+ "@types/chai": "^5.2.2",
+ "@vitest/spy": "4.1.5",
+ "@vitest/utils": "4.1.5",
+ "chai": "^6.2.2",
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/mocker": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.5.tgz",
+ "integrity": "sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "4.1.5",
+ "estree-walker": "^3.0.3",
+ "magic-string": "^0.30.21"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "msw": "^2.4.9",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "msw": {
+ "optional": true
+ },
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vitest/pretty-format": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.5.tgz",
+ "integrity": "sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.5.tgz",
+ "integrity": "sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/utils": "4.1.5",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/snapshot": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.5.tgz",
+ "integrity": "sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.1.5",
+ "@vitest/utils": "4.1.5",
+ "magic-string": "^0.30.21",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/spy": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.5.tgz",
+ "integrity": "sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.5.tgz",
+ "integrity": "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.1.5",
+ "convert-source-map": "^2.0.0",
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/aria-query": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
+ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "dequal": "^2.0.3"
+ }
+ },
+ "node_modules/assertion-error": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/chai": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
+ "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/css.escape": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
+ "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cssstyle": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz",
+ "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@asamuzakjp/css-color": "^3.2.0",
+ "rrweb-cssom": "^0.8.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/cssstyle/node_modules/rrweb-cssom": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
+ "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/data-urls": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
+ "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-mimetype": "^4.0.0",
+ "whatwg-url": "^14.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decimal.js": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
+ "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dom-accessibility-api": {
+ "version": "0.5.16",
+ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
+ "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/entities": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz",
+ "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/expect-type": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
+ "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
+ "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/html-encoding-sniffer": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
+ "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-encoding": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/indent-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-potential-custom-element-name": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "license": "MIT"
+ },
+ "node_modules/jsdom": {
+ "version": "25.0.1",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz",
+ "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssstyle": "^4.1.0",
+ "data-urls": "^5.0.0",
+ "decimal.js": "^10.4.3",
+ "form-data": "^4.0.0",
+ "html-encoding-sniffer": "^4.0.0",
+ "http-proxy-agent": "^7.0.2",
+ "https-proxy-agent": "^7.0.5",
+ "is-potential-custom-element-name": "^1.0.1",
+ "nwsapi": "^2.2.12",
+ "parse5": "^7.1.2",
+ "rrweb-cssom": "^0.7.1",
+ "saxes": "^6.0.0",
+ "symbol-tree": "^3.2.4",
+ "tough-cookie": "^5.0.0",
+ "w3c-xmlserializer": "^5.0.0",
+ "webidl-conversions": "^7.0.0",
+ "whatwg-encoding": "^3.1.1",
+ "whatwg-mimetype": "^4.0.0",
+ "whatwg-url": "^14.0.0",
+ "ws": "^8.18.0",
+ "xml-name-validator": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "canvas": "^2.11.2"
+ },
+ "peerDependenciesMeta": {
+ "canvas": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
+ "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.32.0",
+ "lightningcss-darwin-arm64": "1.32.0",
+ "lightningcss-darwin-x64": "1.32.0",
+ "lightningcss-freebsd-x64": "1.32.0",
+ "lightningcss-linux-arm-gnueabihf": "1.32.0",
+ "lightningcss-linux-arm64-gnu": "1.32.0",
+ "lightningcss-linux-arm64-musl": "1.32.0",
+ "lightningcss-linux-x64-gnu": "1.32.0",
+ "lightningcss-linux-x64-musl": "1.32.0",
+ "lightningcss-win32-arm64-msvc": "1.32.0",
+ "lightningcss-win32-x64-msvc": "1.32.0"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
+ "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
+ "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
+ "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
+ "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
+ "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
+ "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
+ "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
+ "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
+ "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
+ "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
+ "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lz-string": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
+ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "lz-string": "bin/bin.js"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/min-indent": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
+ "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.12",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
+ "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/nwsapi": {
+ "version": "2.2.23",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz",
+ "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/obug": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
+ "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/sxzz",
+ "https://opencollective.com/debug"
+ ],
+ "license": "MIT"
+ },
+ "node_modules/parse5": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
+ "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "entities": "^6.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/pathe": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.14",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz",
+ "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/pretty-format": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
+ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^17.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/react": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.2"
+ },
+ "peerDependencies": {
+ "react": "^18.3.1"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/react-router": {
+ "version": "6.30.3",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz",
+ "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==",
+ "license": "MIT",
+ "dependencies": {
+ "@remix-run/router": "1.23.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.30.3",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz",
+ "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==",
+ "license": "MIT",
+ "dependencies": {
+ "@remix-run/router": "1.23.2",
+ "react-router": "6.30.3"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
+ "node_modules/redent": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
+ "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "indent-string": "^4.0.0",
+ "strip-indent": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/rolldown": {
+ "version": "1.0.0-rc.18",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.18.tgz",
+ "integrity": "sha512-phmyKBpuBdRYDf4hgyynGAYn/rDDe+iZXKVJ7WX5b1zQzpLkP5oJRPGsfJuHdzPMlyyEO/4sPW6yfSx2gf7lVg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/types": "=0.128.0",
+ "@rolldown/pluginutils": "1.0.0-rc.18"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm64": "1.0.0-rc.18",
+ "@rolldown/binding-darwin-arm64": "1.0.0-rc.18",
+ "@rolldown/binding-darwin-x64": "1.0.0-rc.18",
+ "@rolldown/binding-freebsd-x64": "1.0.0-rc.18",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.18",
+ "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.18",
+ "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.18",
+ "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.18",
+ "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.18",
+ "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.18",
+ "@rolldown/binding-linux-x64-musl": "1.0.0-rc.18",
+ "@rolldown/binding-openharmony-arm64": "1.0.0-rc.18",
+ "@rolldown/binding-wasm32-wasi": "1.0.0-rc.18",
+ "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.18",
+ "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.18"
+ }
+ },
+ "node_modules/rolldown/node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-rc.18",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.18.tgz",
+ "integrity": "sha512-CUY5Mnhe64xQBGZEEXQ5WyZwsc1JU3vAZLIxtrsBt3LO6UOb+C8GunVKqe9sT8NeWb4lqSaoJtp2xo6GxT1MNw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/rrweb-cssom": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz",
+ "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/saxes": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "xmlchars": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=v12.22.7"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "node_modules/siginfo": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stackback": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/std-env": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz",
+ "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/strip-indent": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
+ "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "min-indent": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/symbol-tree": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinybench": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyexec": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz",
+ "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.16",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
+ "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyrainbow": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz",
+ "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tldts": {
+ "version": "6.1.86",
+ "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",
+ "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tldts-core": "^6.1.86"
+ },
+ "bin": {
+ "tldts": "bin/cli.js"
+ }
+ },
+ "node_modules/tldts-core": {
+ "version": "6.1.86",
+ "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz",
+ "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tough-cookie": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz",
+ "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "tldts": "^6.1.32"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
+ "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "punycode": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD",
+ "optional": true
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/vite": {
+ "version": "8.0.11",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.11.tgz",
+ "integrity": "sha512-Jz1mxtUBR5xTT65VOdJZUUeoyLtqljmFkiUXhPTLZka3RDc9vpi/xXkyrnsdRcm2lIi3l3GPMnAidTsEGIj3Ow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lightningcss": "^1.32.0",
+ "picomatch": "^4.0.4",
+ "postcss": "^8.5.14",
+ "rolldown": "1.0.0-rc.18",
+ "tinyglobby": "^0.2.16"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.1.18",
+ "esbuild": "^0.27.0 || ^0.28.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "@vitejs/devtools": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitest": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.5.tgz",
+ "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/expect": "4.1.5",
+ "@vitest/mocker": "4.1.5",
+ "@vitest/pretty-format": "4.1.5",
+ "@vitest/runner": "4.1.5",
+ "@vitest/snapshot": "4.1.5",
+ "@vitest/spy": "4.1.5",
+ "@vitest/utils": "4.1.5",
+ "es-module-lexer": "^2.0.0",
+ "expect-type": "^1.3.0",
+ "magic-string": "^0.30.21",
+ "obug": "^2.1.1",
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.3",
+ "std-env": "^4.0.0-rc.1",
+ "tinybench": "^2.9.0",
+ "tinyexec": "^1.0.2",
+ "tinyglobby": "^0.2.15",
+ "tinyrainbow": "^3.1.0",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0",
+ "why-is-node-running": "^2.3.0"
+ },
+ "bin": {
+ "vitest": "vitest.mjs"
+ },
+ "engines": {
+ "node": "^20.0.0 || ^22.0.0 || >=24.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@edge-runtime/vm": "*",
+ "@opentelemetry/api": "^1.9.0",
+ "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
+ "@vitest/browser-playwright": "4.1.5",
+ "@vitest/browser-preview": "4.1.5",
+ "@vitest/browser-webdriverio": "4.1.5",
+ "@vitest/coverage-istanbul": "4.1.5",
+ "@vitest/coverage-v8": "4.1.5",
+ "@vitest/ui": "4.1.5",
+ "happy-dom": "*",
+ "jsdom": "*",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "@vitest/browser-playwright": {
+ "optional": true
+ },
+ "@vitest/browser-preview": {
+ "optional": true
+ },
+ "@vitest/browser-webdriverio": {
+ "optional": true
+ },
+ "@vitest/coverage-istanbul": {
+ "optional": true
+ },
+ "@vitest/coverage-v8": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ },
+ "vite": {
+ "optional": false
+ }
+ }
+ },
+ "node_modules/w3c-xmlserializer": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
+ "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "xml-name-validator": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/whatwg-encoding": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
+ "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
+ "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "iconv-lite": "0.6.3"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/whatwg-mimetype": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
+ "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/whatwg-url": {
+ "version": "14.2.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
+ "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tr46": "^5.1.0",
+ "webidl-conversions": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/why-is-node-running": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+ "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "siginfo": "^2.0.0",
+ "stackback": "0.0.2"
+ },
+ "bin": {
+ "why-is-node-running": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.20.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz",
+ "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xml-name-validator": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
+ "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "dev": true,
+ "license": "MIT"
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..f12c240
--- /dev/null
+++ b/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "league-of-legends-gui-overhaul",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc --noEmit && vite build",
+ "preview": "vite preview",
+ "test": "vitest run"
+ },
+ "dependencies": {
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-router-dom": "^6.30.1"
+ },
+ "devDependencies": {
+ "@testing-library/jest-dom": "^6.6.3",
+ "@testing-library/react": "^16.1.0",
+ "@testing-library/user-event": "^14.5.2",
+ "@types/react": "^18.3.12",
+ "@types/react-dom": "^18.3.1",
+ "@vitejs/plugin-react": "^6.0.1",
+ "jsdom": "^25.0.1",
+ "typescript": "^5.6.3",
+ "vite": "^8.0.11",
+ "vitest": "^4.1.5"
+ }
+}
diff --git a/src/app/App.test.tsx b/src/app/App.test.tsx
new file mode 100644
index 0000000..1de82c5
--- /dev/null
+++ b/src/app/App.test.tsx
@@ -0,0 +1,78 @@
+import { screen, within } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { describe, expect, it } from "vitest";
+import { renderAppAt } from "../test/renderWithRouter";
+
+describe("App shell", () => {
+ it("renders the main layout and navigation", () => {
+ renderAppAt("/");
+
+ const navigation = screen.getByRole("navigation", { name: /primary navigation/i });
+
+ expect(screen.getByText("Nexus Overhaul")).toBeInTheDocument();
+ expect(navigation).toBeInTheDocument();
+ expect(within(navigation).getByRole("link", { name: /home/i })).toBeInTheDocument();
+ expect(within(navigation).getByRole("link", { name: /play/i })).toBeInTheDocument();
+ expect(within(navigation).getByRole("link", { name: /champions/i })).toBeInTheDocument();
+ expect(within(navigation).getByRole("link", { name: /profile/i })).toBeInTheDocument();
+ expect(within(navigation).getByRole("link", { name: /settings/i })).toBeInTheDocument();
+ expect(screen.getByRole("complementary", { name: /friends and social status/i })).toBeInTheDocument();
+ expect(screen.getByText(/Status: local prototype/i)).toBeInTheDocument();
+ expect(screen.getByText(/Theme: dusk-gold/i)).toBeInTheDocument();
+ });
+
+ it.each([
+ ["/", "Dashboard"],
+ ["/play", "Queue prototype"],
+ ["/champions", "Collection prototype"],
+ ["/champions/nyra", "Combat Profile"],
+ ["/profile", "Player profile"],
+ ["/settings", "Configuration prototype"]
+ ])("renders route %s", (path, expectedText) => {
+ renderAppAt(path);
+
+ expect(screen.getByText(expectedText)).toBeInTheDocument();
+ });
+
+ it("switches views through sidebar navigation", async () => {
+ const user = userEvent.setup();
+ renderAppAt("/");
+ const navigation = screen.getByRole("navigation", { name: /primary navigation/i });
+
+ await user.click(within(navigation).getByRole("link", { name: /champions/i }));
+
+ expect(screen.getByRole("heading", { name: "Champions" })).toBeInTheDocument();
+ });
+
+ it("opens a champion detail page from the champion grid", async () => {
+ const user = userEvent.setup();
+ renderAppAt("/champions");
+
+ await user.click(screen.getByRole("link", { name: /nyra/i }));
+
+ expect(screen.getByRole("heading", { name: "Nyra" })).toBeInTheDocument();
+ expect(screen.getByText("Combat Profile")).toBeInTheDocument();
+ });
+
+ it("applies a theme preset from settings", async () => {
+ const user = userEvent.setup();
+ const { container } = renderAppAt("/settings");
+
+ await user.selectOptions(screen.getByLabelText(/theme preset/i), "arcane-teal");
+
+ expect(container.querySelector(".app-shell")).toHaveAttribute("data-theme", "arcane-teal");
+ expect(screen.getByText(/Theme: arcane-teal/i)).toBeInTheDocument();
+ expect(screen.getByText("Theme preview applied")).toBeInTheDocument();
+ });
+
+ it("shows a toast when sending a social invite", async () => {
+ const user = userEvent.setup();
+ renderAppAt("/");
+ const socialSidebar = screen.getByRole("complementary", { name: /friends and social status/i });
+
+ await user.click(within(socialSidebar).getAllByRole("button", { name: /invite/i })[0]);
+
+ expect(screen.getByText("Invite sent")).toBeInTheDocument();
+ expect(screen.getByText(/received a prototype party invite/i)).toBeInTheDocument();
+ });
+});
diff --git a/src/app/App.tsx b/src/app/App.tsx
new file mode 100644
index 0000000..2cd82fc
--- /dev/null
+++ b/src/app/App.tsx
@@ -0,0 +1,16 @@
+import { BrowserRouter } from "react-router-dom";
+import { AppProvider } from "./AppContext";
+import { AppRoutes } from "./router";
+import { AppShell } from "../layouts/AppShell";
+
+export function App() {
+ return (
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/AppContext.tsx b/src/app/AppContext.tsx
new file mode 100644
index 0000000..e868f9c
--- /dev/null
+++ b/src/app/AppContext.tsx
@@ -0,0 +1,64 @@
+import { createContext, useContext, useMemo, useState } from "react";
+import type { ReactNode } from "react";
+import type { ToastVariant } from "../components/ui";
+
+export type ThemePreset = "dusk-gold" | "arcane-teal" | "obsidian";
+
+export type AppNotification = {
+ id: string;
+ title: string;
+ message: string;
+ variant?: ToastVariant;
+};
+
+type AppContextValue = {
+ theme: ThemePreset;
+ setTheme: (theme: ThemePreset) => void;
+ notifications: AppNotification[];
+ notify: (notification: Omit) => void;
+ dismissNotification: (id: string) => void;
+};
+
+const AppContext = createContext(undefined);
+
+type AppProviderProps = {
+ children: ReactNode;
+};
+
+export function AppProvider({ children }: AppProviderProps) {
+ const [theme, setTheme] = useState("dusk-gold");
+ const [notifications, setNotifications] = useState([]);
+
+ const value = useMemo(
+ () => ({
+ theme,
+ setTheme,
+ notifications,
+ notify: (notification) => {
+ setNotifications((current) => [
+ ...current.slice(-2),
+ {
+ ...notification,
+ id: crypto.randomUUID()
+ }
+ ]);
+ },
+ dismissNotification: (id) => {
+ setNotifications((current) => current.filter((notification) => notification.id !== id));
+ }
+ }),
+ [notifications, theme]
+ );
+
+ return {children};
+}
+
+export function useAppContext() {
+ const context = useContext(AppContext);
+
+ if (!context) {
+ throw new Error("useAppContext must be used inside AppProvider");
+ }
+
+ return context;
+}
diff --git a/src/app/router.tsx b/src/app/router.tsx
new file mode 100644
index 0000000..f5396d1
--- /dev/null
+++ b/src/app/router.tsx
@@ -0,0 +1,13 @@
+import { Navigate, Route, Routes } from "react-router-dom";
+import { appRoutes } from "../config/routes";
+
+export function AppRoutes() {
+ return (
+
+ {appRoutes.map((route) => (
+ } />
+ ))}
+ } />
+
+ );
+}
diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx
new file mode 100644
index 0000000..0e33af9
--- /dev/null
+++ b/src/components/ui/Button.tsx
@@ -0,0 +1,30 @@
+import type { ButtonHTMLAttributes, ReactNode } from "react";
+
+type ButtonVariant = "primary" | "secondary" | "ghost";
+type ButtonSize = "sm" | "md" | "lg";
+
+type ButtonProps = ButtonHTMLAttributes & {
+ children: ReactNode;
+ variant?: ButtonVariant;
+ size?: ButtonSize;
+ active?: boolean;
+};
+
+export function Button({
+ children,
+ variant = "primary",
+ size = "md",
+ active = false,
+ className = "",
+ ...props
+}: ButtonProps) {
+ const classes = ["ui-button", `ui-button-${variant}`, `ui-button-${size}`, active ? "is-active" : "", className]
+ .filter(Boolean)
+ .join(" ");
+
+ return (
+
+ );
+}
diff --git a/src/components/ui/Dropdown.tsx b/src/components/ui/Dropdown.tsx
new file mode 100644
index 0000000..892c2d4
--- /dev/null
+++ b/src/components/ui/Dropdown.tsx
@@ -0,0 +1,26 @@
+import type { SelectHTMLAttributes } from "react";
+
+export type DropdownOption = {
+ label: string;
+ value: string;
+};
+
+type DropdownProps = SelectHTMLAttributes & {
+ label: string;
+ options: DropdownOption[];
+};
+
+export function Dropdown({ label, options, className = "", ...props }: DropdownProps) {
+ return (
+
+ );
+}
diff --git a/src/components/ui/Modal.tsx b/src/components/ui/Modal.tsx
new file mode 100644
index 0000000..9262ba3
--- /dev/null
+++ b/src/components/ui/Modal.tsx
@@ -0,0 +1,29 @@
+import type { ReactNode } from "react";
+import { Button } from "./Button";
+
+type ModalProps = {
+ open: boolean;
+ title: string;
+ children: ReactNode;
+ onClose: () => void;
+};
+
+export function Modal({ open, title, children, onClose }: ModalProps) {
+ if (!open) {
+ return null;
+ }
+
+ return (
+
+ );
+}
diff --git a/src/components/ui/Panel.tsx b/src/components/ui/Panel.tsx
new file mode 100644
index 0000000..b834b93
--- /dev/null
+++ b/src/components/ui/Panel.tsx
@@ -0,0 +1,21 @@
+import type { HTMLAttributes, ReactNode } from "react";
+
+type PanelProps = HTMLAttributes & {
+ children: ReactNode;
+ title?: string;
+ subtitle?: string;
+};
+
+export function Panel({ children, title, subtitle, className = "", ...props }: PanelProps) {
+ return (
+
+ {(title || subtitle) && (
+
+ {title && {title}
}
+ {subtitle && {subtitle}
}
+
+ )}
+ {children}
+
+ );
+}
diff --git a/src/components/ui/SearchInput.tsx b/src/components/ui/SearchInput.tsx
new file mode 100644
index 0000000..9e281de
--- /dev/null
+++ b/src/components/ui/SearchInput.tsx
@@ -0,0 +1,14 @@
+import type { InputHTMLAttributes } from "react";
+
+type SearchInputProps = InputHTMLAttributes & {
+ label?: string;
+};
+
+export function SearchInput({ label = "Search", className = "", ...props }: SearchInputProps) {
+ return (
+
+ );
+}
diff --git a/src/components/ui/Tabs.tsx b/src/components/ui/Tabs.tsx
new file mode 100644
index 0000000..6e8e337
--- /dev/null
+++ b/src/components/ui/Tabs.tsx
@@ -0,0 +1,42 @@
+import { useState } from "react";
+import type { ReactNode } from "react";
+import { Button } from "./Button";
+
+export type TabItem = {
+ id: string;
+ label: string;
+ content: ReactNode;
+};
+
+type TabsProps = {
+ tabs: TabItem[];
+ defaultTabId?: string;
+};
+
+export function Tabs({ tabs, defaultTabId }: TabsProps) {
+ const [activeId, setActiveId] = useState(defaultTabId ?? tabs[0]?.id);
+ const activeTab = tabs.find((tab) => tab.id === activeId) ?? tabs[0];
+
+ return (
+
+
+ {tabs.map((tab) => (
+
+ ))}
+
+
+ {activeTab.content}
+
+
+ );
+}
diff --git a/src/components/ui/Toast.tsx b/src/components/ui/Toast.tsx
new file mode 100644
index 0000000..6c5e361
--- /dev/null
+++ b/src/components/ui/Toast.tsx
@@ -0,0 +1,24 @@
+export type ToastVariant = "info" | "success" | "warning";
+
+type ToastProps = {
+ title: string;
+ message: string;
+ variant?: ToastVariant;
+ onDismiss?: () => void;
+};
+
+export function Toast({ title, message, variant = "info", onDismiss }: ToastProps) {
+ return (
+
+ );
+}
diff --git a/src/components/ui/Tooltip.tsx b/src/components/ui/Tooltip.tsx
new file mode 100644
index 0000000..406611e
--- /dev/null
+++ b/src/components/ui/Tooltip.tsx
@@ -0,0 +1,14 @@
+import type { ReactNode } from "react";
+
+type TooltipProps = {
+ content: string;
+ children: ReactNode;
+};
+
+export function Tooltip({ content, children }: TooltipProps) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts
new file mode 100644
index 0000000..d84cffa
--- /dev/null
+++ b/src/components/ui/index.ts
@@ -0,0 +1,11 @@
+export { Button } from "./Button";
+export { Dropdown } from "./Dropdown";
+export type { DropdownOption } from "./Dropdown";
+export { Modal } from "./Modal";
+export { Panel } from "./Panel";
+export { SearchInput } from "./SearchInput";
+export { Tabs } from "./Tabs";
+export type { TabItem } from "./Tabs";
+export { Toast } from "./Toast";
+export type { ToastVariant } from "./Toast";
+export { Tooltip } from "./Tooltip";
diff --git a/src/components/ui/ui.test.tsx b/src/components/ui/ui.test.tsx
new file mode 100644
index 0000000..73b2f72
--- /dev/null
+++ b/src/components/ui/ui.test.tsx
@@ -0,0 +1,44 @@
+import { screen } from "@testing-library/react";
+import { describe, expect, it } from "vitest";
+import { Button, Dropdown, Panel, SearchInput } from ".";
+import { renderWithRouter } from "../../test/renderWithRouter";
+
+describe("base UI components", () => {
+ it("renders a button", () => {
+ renderWithRouter();
+
+ expect(screen.getByRole("button", { name: /confirm/i })).toBeInTheDocument();
+ });
+
+ it("renders a panel with title and content", () => {
+ renderWithRouter(
+
+ Panel body
+
+ );
+
+ expect(screen.getByRole("heading", { name: /panel title/i })).toBeInTheDocument();
+ expect(screen.getByText("Panel body")).toBeInTheDocument();
+ });
+
+ it("renders a search input", () => {
+ renderWithRouter();
+
+ expect(screen.getByLabelText(/champion search/i)).toBeInTheDocument();
+ });
+
+ it("renders a dropdown", () => {
+ renderWithRouter(
+
+ );
+
+ expect(screen.getByLabelText(/role/i)).toBeInTheDocument();
+ expect(screen.getByRole("option", { name: /duelist/i })).toBeInTheDocument();
+ });
+});
diff --git a/src/config/navigation.ts b/src/config/navigation.ts
new file mode 100644
index 0000000..f141951
--- /dev/null
+++ b/src/config/navigation.ts
@@ -0,0 +1,44 @@
+import type { NavigationItem } from "../types/navigation";
+
+export const navigationItems: NavigationItem[] = [
+ {
+ id: "home",
+ label: "Home",
+ path: "/",
+ iconName: "H",
+ section: "primary",
+ enabled: true
+ },
+ {
+ id: "play",
+ label: "Play",
+ path: "/play",
+ iconName: "P",
+ section: "primary",
+ enabled: true
+ },
+ {
+ id: "champions",
+ label: "Champions",
+ path: "/champions",
+ iconName: "C",
+ section: "collection",
+ enabled: true
+ },
+ {
+ id: "profile",
+ label: "Profile",
+ path: "/profile",
+ iconName: "R",
+ section: "account",
+ enabled: true
+ },
+ {
+ id: "settings",
+ label: "Settings",
+ path: "/settings",
+ iconName: "S",
+ section: "system",
+ enabled: true
+ }
+];
diff --git a/src/config/routes.tsx b/src/config/routes.tsx
new file mode 100644
index 0000000..a7abcc7
--- /dev/null
+++ b/src/config/routes.tsx
@@ -0,0 +1,49 @@
+import type { ComponentType } from "react";
+import { ChampionDetailPage } from "../pages/ChampionDetailPage";
+import { ChampionsPage } from "../pages/ChampionsPage";
+import { HomePage } from "../pages/HomePage";
+import { PlayPage } from "../pages/PlayPage";
+import { ProfilePage } from "../pages/ProfilePage";
+import { SettingsPage } from "../pages/SettingsPage";
+import type { AppRoute } from "../types/navigation";
+
+type RouteComponent = ComponentType;
+
+export const appRoutes: Array = [
+ {
+ id: "home",
+ title: "Home",
+ path: "/",
+ component: HomePage
+ },
+ {
+ id: "play",
+ title: "Play",
+ path: "/play",
+ component: PlayPage
+ },
+ {
+ id: "champions",
+ title: "Champions",
+ path: "/champions",
+ component: ChampionsPage
+ },
+ {
+ id: "champion-detail",
+ title: "Champion Detail",
+ path: "/champions/:championId",
+ component: ChampionDetailPage
+ },
+ {
+ id: "profile",
+ title: "Profile",
+ path: "/profile",
+ component: ProfilePage
+ },
+ {
+ id: "settings",
+ title: "Settings",
+ path: "/settings",
+ component: SettingsPage
+ }
+];
diff --git a/src/data/champions.mock.ts b/src/data/champions.mock.ts
new file mode 100644
index 0000000..d974861
--- /dev/null
+++ b/src/data/champions.mock.ts
@@ -0,0 +1,40 @@
+import type { Champion } from "../types/domain";
+
+export const champions: Champion[] = [
+ {
+ id: "nyra",
+ name: "Nyra",
+ role: "Invoker",
+ difficulty: "Medium",
+ tags: ["arcane", "control", "mid"],
+ shortDescription: "A stormbound tactician who shapes fights with precise zones.",
+ accentColor: "#36d7d0"
+ },
+ {
+ id: "kael",
+ name: "Kael",
+ role: "Duelist",
+ difficulty: "High",
+ tags: ["melee", "burst", "solo"],
+ shortDescription: "A blade-focused skirmisher built around timing and pressure.",
+ accentColor: "#c89b3c"
+ },
+ {
+ id: "maera",
+ name: "Maera",
+ role: "Guardian",
+ difficulty: "Low",
+ tags: ["support", "shield", "teamfight"],
+ shortDescription: "A steady frontliner who protects allies and anchors engages.",
+ accentColor: "#5b8cff"
+ },
+ {
+ id: "oren",
+ name: "Oren",
+ role: "Striker",
+ difficulty: "Medium",
+ tags: ["ranged", "tempo", "objective"],
+ shortDescription: "A marksman prototype focused on objective windows and spacing.",
+ accentColor: "#e56b6f"
+ }
+];
diff --git a/src/data/friends.mock.ts b/src/data/friends.mock.ts
new file mode 100644
index 0000000..5863756
--- /dev/null
+++ b/src/data/friends.mock.ts
@@ -0,0 +1,25 @@
+import type { Friend } from "../types/domain";
+
+export const friends: Friend[] = [
+ {
+ id: "friend-1",
+ displayName: "Astra",
+ status: "online",
+ activity: "Browsing champions",
+ level: 42
+ },
+ {
+ id: "friend-2",
+ displayName: "Vey",
+ status: "in-game",
+ activity: "Draft lobby",
+ level: 57
+ },
+ {
+ id: "friend-3",
+ displayName: "Solin",
+ status: "away",
+ activity: "Idle",
+ level: 31
+ }
+];
diff --git a/src/data/matchHistory.mock.ts b/src/data/matchHistory.mock.ts
new file mode 100644
index 0000000..8806ddc
--- /dev/null
+++ b/src/data/matchHistory.mock.ts
@@ -0,0 +1,22 @@
+import type { MatchHistoryEntry } from "../types/domain";
+
+export const matchHistory: MatchHistoryEntry[] = [
+ {
+ id: "match-1",
+ championName: "Nyra",
+ mode: "Summoner Rift",
+ result: "Victory",
+ kda: "8 / 2 / 11",
+ duration: "31m 12s",
+ date: "Today"
+ },
+ {
+ id: "match-2",
+ championName: "Maera",
+ mode: "ARAM",
+ result: "Defeat",
+ kda: "2 / 6 / 19",
+ duration: "18m 44s",
+ date: "Yesterday"
+ }
+];
diff --git a/src/data/news.mock.ts b/src/data/news.mock.ts
new file mode 100644
index 0000000..017614f
--- /dev/null
+++ b/src/data/news.mock.ts
@@ -0,0 +1,18 @@
+import type { NewsItem } from "../types/domain";
+
+export const newsItems: NewsItem[] = [
+ {
+ id: "news-1",
+ title: "Base Shell Prototype Ready",
+ category: "Development",
+ date: "2026-05-10",
+ summary: "The first interface foundation focuses on layout, routing, reusable UI, and mock data."
+ },
+ {
+ id: "news-2",
+ title: "Champion Grid Planning",
+ category: "Collection",
+ date: "2026-05-10",
+ summary: "The champion overview starts with searchable mock cards and can later gain filters."
+ }
+];
diff --git a/src/features/champions/championLookup.ts b/src/features/champions/championLookup.ts
new file mode 100644
index 0000000..5c01b0f
--- /dev/null
+++ b/src/features/champions/championLookup.ts
@@ -0,0 +1,9 @@
+import { champions } from "../../data/champions.mock";
+
+export function getChampionById(championId: string | undefined) {
+ if (!championId) {
+ return undefined;
+ }
+
+ return champions.find((champion) => champion.id === championId);
+}
diff --git a/src/features/champions/filterChampions.ts b/src/features/champions/filterChampions.ts
new file mode 100644
index 0000000..20e8dc4
--- /dev/null
+++ b/src/features/champions/filterChampions.ts
@@ -0,0 +1,15 @@
+import type { Champion } from "../../types/domain";
+
+export function filterChampions(champions: Champion[], query: string, role: string): Champion[] {
+ const normalizedQuery = query.trim().toLowerCase();
+
+ return champions.filter((champion) => {
+ const matchesRole = role === "all" || champion.role === role;
+ const matchesQuery =
+ normalizedQuery.length === 0 ||
+ champion.name.toLowerCase().includes(normalizedQuery) ||
+ champion.tags.some((tag) => tag.includes(normalizedQuery));
+
+ return matchesRole && matchesQuery;
+ });
+}
diff --git a/src/features/profile/profileSummary.ts b/src/features/profile/profileSummary.ts
new file mode 100644
index 0000000..a8fbec9
--- /dev/null
+++ b/src/features/profile/profileSummary.ts
@@ -0,0 +1,6 @@
+export const profileSummary = {
+ displayName: "Toxic",
+ level: 42,
+ rank: "Gold prototype rank",
+ note: "No persistence in iteration 1."
+};
diff --git a/src/features/social/SocialSidebar.tsx b/src/features/social/SocialSidebar.tsx
new file mode 100644
index 0000000..0f2d51a
--- /dev/null
+++ b/src/features/social/SocialSidebar.tsx
@@ -0,0 +1,44 @@
+import { useAppContext } from "../../app/AppContext";
+import { Button } from "../../components/ui";
+import { friends } from "../../data/friends.mock";
+import { getFriendStatusLabel } from "./friendStatus";
+
+export function SocialSidebar() {
+ const { notify } = useAppContext();
+
+ return (
+
+ );
+}
diff --git a/src/features/social/friendStatus.ts b/src/features/social/friendStatus.ts
new file mode 100644
index 0000000..925c477
--- /dev/null
+++ b/src/features/social/friendStatus.ts
@@ -0,0 +1,12 @@
+import type { FriendStatus } from "../../types/domain";
+
+export function getFriendStatusLabel(status: FriendStatus): string {
+ const labels: Record = {
+ online: "Online",
+ away: "Away",
+ "in-game": "In game",
+ offline: "Offline"
+ };
+
+ return labels[status];
+}
diff --git a/src/layouts/AppShell.tsx b/src/layouts/AppShell.tsx
new file mode 100644
index 0000000..f15c197
--- /dev/null
+++ b/src/layouts/AppShell.tsx
@@ -0,0 +1,106 @@
+import type { ReactNode } from "react";
+import { NavLink } from "react-router-dom";
+import { useAppContext } from "../app/AppContext";
+import { navigationItems } from "../config/navigation";
+import { Button } from "../components/ui/Button";
+import { Toast } from "../components/ui/Toast";
+import { SocialSidebar } from "../features/social/SocialSidebar";
+
+type AppShellProps = {
+ children: ReactNode;
+};
+
+export function AppShell({ children }: AppShellProps) {
+ const enabledItems = navigationItems.filter((item) => item.enabled !== false);
+ const { dismissNotification, notifications, notify, theme } = useAppContext();
+
+ return (
+
+
+
+
+
+
+ Iteration 1
+
MOBA Interface Foundation
+
+
+
+
+
+
+
+
{children}
+
+
+
+
+
+
+ {notifications.length > 0 && (
+
+ {notifications.map((notification) => (
+ dismissNotification(notification.id)}
+ />
+ ))}
+
+ )}
+
+ );
+}
diff --git a/src/main.tsx b/src/main.tsx
new file mode 100644
index 0000000..3ece158
--- /dev/null
+++ b/src/main.tsx
@@ -0,0 +1,11 @@
+import React from "react";
+import ReactDOM from "react-dom/client";
+import { App } from "./app/App";
+import "./styles/theme.css";
+import "./styles/global.css";
+
+ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
+
+
+
+);
diff --git a/src/pages/ChampionDetailPage.tsx b/src/pages/ChampionDetailPage.tsx
new file mode 100644
index 0000000..cc51e6f
--- /dev/null
+++ b/src/pages/ChampionDetailPage.tsx
@@ -0,0 +1,67 @@
+import type { CSSProperties } from "react";
+import { Link, useParams } from "react-router-dom";
+import { Panel } from "../components/ui";
+import { getChampionById } from "../features/champions/championLookup";
+
+export function ChampionDetailPage() {
+ const { championId } = useParams();
+ const champion = getChampionById(championId);
+
+ if (!champion) {
+ return (
+
+
+
+ Return to champions
+
+
+
+ );
+ }
+
+ return (
+
+
+
+ {champion.name.charAt(0)}
+
+
+
{champion.role}
+
{champion.name}
+
{champion.shortDescription}
+
+ {champion.tags.map((tag) => (
+ {tag}
+ ))}
+
+
+
+
+
+
+
+
+
- Difficulty
+ - {champion.difficulty}
+
+
+
- Primary Role
+ - {champion.role}
+
+
+
- Theme Hint
+ - {champion.accentColor ?? "Default accent"}
+
+
+
+
+
+ Abilities, skins, progression, lore, and build recommendations can be added as independent modules.
+
+ Back to overview
+
+
+
+
+ );
+}
diff --git a/src/pages/ChampionsPage.tsx b/src/pages/ChampionsPage.tsx
new file mode 100644
index 0000000..1afc918
--- /dev/null
+++ b/src/pages/ChampionsPage.tsx
@@ -0,0 +1,72 @@
+import type { CSSProperties } from "react";
+import { useMemo, useState } from "react";
+import { Link } from "react-router-dom";
+import { Dropdown, Panel, SearchInput } from "../components/ui";
+import { champions } from "../data/champions.mock";
+import { filterChampions } from "../features/champions/filterChampions";
+
+const roleOptions = [
+ { label: "All roles", value: "all" },
+ { label: "Controller", value: "Controller" },
+ { label: "Duelist", value: "Duelist" },
+ { label: "Guardian", value: "Guardian" },
+ { label: "Invoker", value: "Invoker" },
+ { label: "Striker", value: "Striker" }
+];
+
+export function ChampionsPage() {
+ const [query, setQuery] = useState("");
+ const [role, setRole] = useState("all");
+
+ const filteredChampions = useMemo(() => {
+ return filterChampions(champions, query, role);
+ }, [query, role]);
+
+ return (
+
+
+ Collection prototype
+ Champions
+ Searchable mock champion cards with filtering hooks for later collection features.
+
+
+
+ setQuery(event.target.value)}
+ />
+ setRole(event.target.value)} />
+
+
+
+ {filteredChampions.map((champion) => (
+
+
+ {champion.name.charAt(0)}
+
+
+
+
+ {champion.role}
+
{champion.name}
+
+
+ {champion.difficulty}
+
+
+
{champion.shortDescription}
+
+ {champion.tags.map((tag) => (
+ {tag}
+ ))}
+
+
View details
+
+
+ ))}
+
+
+ );
+}
diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx
new file mode 100644
index 0000000..6619fb3
--- /dev/null
+++ b/src/pages/HomePage.tsx
@@ -0,0 +1,68 @@
+import { Link } from "react-router-dom";
+import { newsItems } from "../data/news.mock";
+import { Button, Panel, Tabs, Toast } from "../components/ui";
+
+export function HomePage() {
+ return (
+
+
+
+
Dashboard
+
Build the foundation before the spectacle.
+
+ A modular shell for future MOBA-inspired views, themes, social panels, and champion experiences.
+
+
+
+
+ Play Prototype
+
+
+ Browse champions
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {newsItems.map((item) => (
+
+ {item.category}
+ {item.title}
+ {item.summary}
+
+ ))}
+
+
+
+
+ Shell, navigation, status bar, and content regions are wired.
+ },
+ {
+ id: "data",
+ label: "Data",
+ content: Mock data is isolated from page and UI components.
+ }
+ ]}
+ />
+
+
+
+
+
+ );
+}
diff --git a/src/pages/PlayPage.tsx b/src/pages/PlayPage.tsx
new file mode 100644
index 0000000..3a5a9ed
--- /dev/null
+++ b/src/pages/PlayPage.tsx
@@ -0,0 +1,49 @@
+import { useState } from "react";
+import { Button, Modal, Panel, Tooltip } from "../components/ui";
+
+const playModes = [
+ {
+ id: "rift",
+ name: "Summoner Rift",
+ description: "Structured 5v5 queue placeholder for the main arena experience."
+ },
+ {
+ id: "aram",
+ name: "ARAM",
+ description: "Compact teamfight mode placeholder for faster sessions."
+ },
+ {
+ id: "custom",
+ name: "Custom",
+ description: "Private lobby placeholder for future party and settings flows."
+ }
+];
+
+export function PlayPage() {
+ const [modalOpen, setModalOpen] = useState(false);
+
+ return (
+
+
+ Queue prototype
+ Play
+ Select a mock mode and open the lobby dialog. No matchmaking exists in iteration 1.
+
+
+
+ {playModes.map((mode) => (
+
+ {mode.description}
+
+
+
+
+ ))}
+
+
+
setModalOpen(false)}>
+ This dialog proves that modal composition is available for later queue and party flows.
+
+
+ );
+}
diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx
new file mode 100644
index 0000000..a1e2c7a
--- /dev/null
+++ b/src/pages/ProfilePage.tsx
@@ -0,0 +1,59 @@
+import { Panel } from "../components/ui";
+import { friends } from "../data/friends.mock";
+import { matchHistory } from "../data/matchHistory.mock";
+import { profileSummary } from "../features/profile/profileSummary";
+import { getFriendStatusLabel } from "../features/social/friendStatus";
+
+export function ProfilePage() {
+ return (
+
+
+
+ T
+
+
+
Player profile
+
{profileSummary.displayName}
+
+ Level {profileSummary.level} - {profileSummary.rank} - {profileSummary.note}
+
+
+
+
+
+
+
+ {matchHistory.map((match) => (
+
+
+
{match.championName}
+
+ {match.mode} - {match.duration} - {match.date}
+
+
+ {match.result}
+ {match.kda}
+
+ ))}
+
+
+
+
+
+ {friends.map((friend) => (
+
+
+
+
{friend.displayName}
+
+ {getFriendStatusLabel(friend.status)} - {friend.activity} {friend.level ? `- Level ${friend.level}` : ""}
+
+
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx
new file mode 100644
index 0000000..19fb02c
--- /dev/null
+++ b/src/pages/SettingsPage.tsx
@@ -0,0 +1,76 @@
+import type { ThemePreset } from "../app/AppContext";
+import { useAppContext } from "../app/AppContext";
+import { Button, Dropdown, Panel, SearchInput } from "../components/ui";
+
+const densityOptions = [
+ { label: "Comfortable", value: "comfortable" },
+ { label: "Compact", value: "compact" },
+ { label: "Dense", value: "dense" }
+];
+
+const themeOptions = [
+ { label: "Dusk Gold", value: "dusk-gold" },
+ { label: "Arcane Teal", value: "arcane-teal" },
+ { label: "Obsidian", value: "obsidian" }
+];
+
+export function SettingsPage() {
+ const { notify, setTheme, theme } = useAppContext();
+
+ return (
+
+
+ Configuration prototype
+ Settings
+ Demonstrates form controls for future theme and interface preferences.
+
+
+
+
+ {
+ const nextTheme = event.target.value as ThemePreset;
+ setTheme(nextTheme);
+ notify({
+ title: "Theme preview applied",
+ message: `${nextTheme} is active for this session.`,
+ variant: "success"
+ });
+ }}
+ />
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/styles/global.css b/src/styles/global.css
new file mode 100644
index 0000000..a47b006
--- /dev/null
+++ b/src/styles/global.css
@@ -0,0 +1,681 @@
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ min-width: 320px;
+ min-height: 100vh;
+ background:
+ radial-gradient(circle at top left, rgb(54 215 208 / 0.14), transparent 28rem),
+ linear-gradient(135deg, #05070d 0%, var(--color-bg) 46%, #0b1220 100%);
+ color: var(--color-text);
+ font-family: var(--font-body);
+}
+
+button,
+input,
+select {
+ font: inherit;
+}
+
+a {
+ color: inherit;
+ text-decoration: none;
+}
+
+h1,
+h2,
+h3,
+p {
+ margin: 0;
+}
+
+p {
+ color: var(--color-text-muted);
+ line-height: 1.6;
+}
+
+.app-shell {
+ display: grid;
+ grid-template-columns: 17rem minmax(0, 1fr) 18rem;
+ min-height: 100vh;
+}
+
+.sidebar {
+ border-right: 1px solid var(--color-border);
+ background: rgb(7 10 18 / 0.92);
+ padding: var(--space-5);
+}
+
+.social-sidebar {
+ border-left: 1px solid var(--color-border);
+ background: linear-gradient(180deg, rgb(13 20 34 / 0.94), rgb(7 10 18 / 0.92));
+ padding: var(--space-5);
+}
+
+.social-sidebar h2 {
+ font-family: var(--font-display);
+ font-size: 1.2rem;
+}
+
+.social-list {
+ display: grid;
+ gap: var(--space-3);
+ margin-top: var(--space-4);
+}
+
+.social-card {
+ display: grid;
+ grid-template-columns: auto minmax(0, 1fr) auto;
+ gap: var(--space-3);
+ align-items: center;
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-md);
+ background: rgb(17 27 45 / 0.76);
+ padding: var(--space-3);
+}
+
+.social-card h3 {
+ margin-bottom: var(--space-1);
+ font-size: 0.95rem;
+}
+
+.social-card span:not(.status-dot) {
+ color: var(--color-text-muted);
+ font-size: 0.8rem;
+}
+
+.brand {
+ display: flex;
+ gap: var(--space-3);
+ align-items: center;
+ margin-bottom: var(--space-6);
+}
+
+.brand span:last-child {
+ display: block;
+ color: var(--color-text-muted);
+ font-size: 0.82rem;
+}
+
+.brand-mark,
+.nav-icon,
+.profile-avatar {
+ display: inline-grid;
+ place-items: center;
+ border: 1px solid var(--color-border-strong);
+ background: linear-gradient(135deg, rgb(200 155 60 / 0.28), rgb(54 215 208 / 0.1));
+ color: var(--color-gold-bright);
+ font-weight: 700;
+}
+
+.brand-mark {
+ width: 2.5rem;
+ height: 2.5rem;
+ border-radius: var(--radius-md);
+}
+
+.nav-list {
+ display: grid;
+ gap: var(--space-2);
+}
+
+.nav-item {
+ display: flex;
+ align-items: center;
+ gap: var(--space-3);
+ min-height: 2.75rem;
+ border: 1px solid transparent;
+ border-radius: var(--radius-md);
+ padding: 0 var(--space-3);
+ color: var(--color-text-muted);
+ transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
+}
+
+.nav-item:hover,
+.nav-item-active {
+ border-color: var(--color-border-strong);
+ background: rgb(200 155 60 / 0.1);
+ color: var(--color-text);
+}
+
+.nav-icon {
+ width: 1.75rem;
+ height: 1.75rem;
+ border-radius: var(--radius-sm);
+ font-size: 0.8rem;
+}
+
+.app-frame {
+ display: grid;
+ grid-template-rows: auto 1fr auto;
+ min-width: 0;
+}
+
+.topbar,
+.status-bar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: var(--space-4);
+ border-bottom: 1px solid var(--color-border);
+ padding: var(--space-4) var(--space-5);
+ background: rgb(13 20 34 / 0.82);
+}
+
+.topbar h1 {
+ font-family: var(--font-display);
+ font-size: 1.35rem;
+}
+
+.topbar-actions,
+.button-row,
+.hero-actions {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-3);
+ align-items: center;
+}
+
+.content-area {
+ min-width: 0;
+ padding: var(--space-5);
+}
+
+.status-bar {
+ border-top: 1px solid var(--color-border);
+ border-bottom: 0;
+ color: var(--color-text-muted);
+ font-size: 0.85rem;
+}
+
+.page-stack {
+ display: grid;
+ gap: var(--space-5);
+}
+
+.page-heading,
+.hero-panel,
+.profile-header {
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-lg);
+ background: linear-gradient(135deg, rgb(22 36 58 / 0.95), rgb(13 20 34 / 0.88));
+ box-shadow: var(--shadow-panel);
+ padding: var(--space-5);
+}
+
+.hero-panel {
+ display: flex;
+ justify-content: space-between;
+ gap: var(--space-5);
+}
+
+.hero-panel h2,
+.page-heading h2,
+.profile-header h2 {
+ margin-bottom: var(--space-2);
+ font-family: var(--font-display);
+ font-size: 2rem;
+}
+
+.eyebrow {
+ display: inline-block;
+ margin-bottom: var(--space-2);
+ color: var(--color-gold-bright);
+ font-size: 0.74rem;
+ font-weight: 700;
+ text-transform: uppercase;
+}
+
+.dashboard-grid,
+.mode-grid {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: var(--space-4);
+}
+
+.champion-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
+ gap: var(--space-4);
+}
+
+.ui-panel,
+.champion-card,
+.compact-card,
+.ui-toast {
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-md);
+ background: rgb(17 27 45 / 0.9);
+ box-shadow: var(--shadow-panel);
+ padding: var(--space-4);
+}
+
+.ui-panel-header {
+ margin-bottom: var(--space-4);
+}
+
+.ui-panel-header h2 {
+ margin-bottom: var(--space-1);
+ font-size: 1.1rem;
+}
+
+.ui-button,
+.action-link {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 2.5rem;
+ border: 1px solid var(--color-border-strong);
+ border-radius: var(--radius-sm);
+ padding: 0 var(--space-4);
+ color: var(--color-text);
+ cursor: pointer;
+ transition: transform var(--transition-fast), border-color var(--transition-fast), background var(--transition-fast);
+}
+
+.ui-button:hover,
+.action-link:hover {
+ transform: translateY(-1px);
+ border-color: var(--color-gold-bright);
+}
+
+.ui-button-primary,
+.action-link {
+ background: linear-gradient(135deg, #9b752b, #312613);
+}
+
+.ui-button-secondary {
+ background: rgb(54 215 208 / 0.12);
+ border-color: rgb(54 215 208 / 0.55);
+}
+
+.ui-button-ghost {
+ background: transparent;
+ border-color: var(--color-border);
+ color: var(--color-text-muted);
+}
+
+.ui-button-sm {
+ min-height: 2rem;
+ padding: 0 var(--space-3);
+ font-size: 0.86rem;
+}
+
+.ui-button-lg {
+ min-height: 3rem;
+}
+
+.ui-button:disabled {
+ cursor: not-allowed;
+ opacity: 0.55;
+}
+
+.ui-button.is-active {
+ border-color: var(--color-teal);
+ color: var(--color-text);
+}
+
+.ui-field {
+ display: grid;
+ gap: var(--space-2);
+ color: var(--color-text-muted);
+}
+
+.ui-input,
+.ui-select {
+ width: 100%;
+ min-height: 2.75rem;
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-sm);
+ background: var(--color-bg-elevated);
+ color: var(--color-text);
+ padding: 0 var(--space-3);
+}
+
+.toolbar,
+.settings-grid {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) 14rem;
+ gap: var(--space-4);
+}
+
+.settings-grid {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ margin-bottom: var(--space-4);
+}
+
+.list-stack {
+ display: grid;
+ gap: var(--space-3);
+}
+
+.compact-card h3,
+.friend-row h3,
+.champion-card h3 {
+ margin-bottom: var(--space-1);
+}
+
+.compact-card-row,
+.friend-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: var(--space-3);
+}
+
+.champion-card {
+ display: grid;
+ gap: var(--space-4);
+ transition: border-color var(--transition-fast), box-shadow var(--transition-fast), transform var(--transition-fast);
+}
+
+.champion-card:hover {
+ border-color: var(--color-gold);
+ box-shadow: var(--shadow-glow), var(--shadow-panel);
+ transform: translateY(-2px);
+}
+
+.champion-card-header {
+ display: flex;
+ justify-content: space-between;
+ gap: var(--space-3);
+ align-items: flex-start;
+}
+
+.difficulty-badge,
+.card-action {
+ border: 1px solid var(--color-border);
+ border-radius: 999px;
+ padding: var(--space-1) var(--space-2);
+ color: var(--color-text-muted);
+ font-size: 0.78rem;
+ font-weight: 700;
+}
+
+.difficulty-low {
+ border-color: var(--color-success);
+ color: var(--color-success);
+}
+
+.difficulty-medium {
+ border-color: var(--color-warning);
+ color: var(--color-warning);
+}
+
+.difficulty-high {
+ border-color: var(--color-danger);
+ color: var(--color-danger);
+}
+
+.card-action {
+ display: inline-flex;
+ margin-top: var(--space-4);
+ border-color: var(--color-gold);
+ color: var(--color-gold-bright);
+}
+
+.champion-sigil {
+ width: 4rem;
+ height: 4rem;
+ display: grid;
+ place-items: center;
+ border: 1px solid var(--champion-accent, var(--color-gold));
+ border-radius: var(--radius-md);
+ background: color-mix(in srgb, var(--champion-accent, var(--color-gold)) 20%, transparent);
+ color: var(--color-text);
+ font-size: 1.5rem;
+ font-weight: 800;
+}
+
+.tag-row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-2);
+ margin-top: var(--space-3);
+}
+
+.tag-row span {
+ border: 1px solid var(--color-border);
+ border-radius: 999px;
+ padding: var(--space-1) var(--space-2);
+ color: var(--color-text-muted);
+ font-size: 0.8rem;
+}
+
+.profile-header {
+ display: flex;
+ align-items: center;
+ gap: var(--space-4);
+}
+
+.champion-detail-hero {
+ display: grid;
+ grid-template-columns: auto minmax(0, 1fr);
+ gap: var(--space-5);
+ align-items: center;
+ border: 1px solid var(--color-border-strong);
+ border-radius: var(--radius-lg);
+ background:
+ radial-gradient(circle at top left, color-mix(in srgb, var(--champion-accent, var(--color-gold)) 24%, transparent), transparent 24rem),
+ linear-gradient(135deg, rgb(22 36 58 / 0.96), rgb(7 10 18 / 0.94));
+ box-shadow: var(--shadow-glow), var(--shadow-panel);
+ padding: var(--space-6);
+}
+
+.champion-detail-hero h2 {
+ margin-bottom: var(--space-2);
+ font-family: var(--font-display);
+ font-size: 2.5rem;
+}
+
+.champion-portrait {
+ display: grid;
+ place-items: center;
+ width: 8rem;
+ height: 8rem;
+ border: 1px solid var(--champion-accent, var(--color-gold));
+ border-radius: var(--radius-lg);
+ background:
+ linear-gradient(135deg, color-mix(in srgb, var(--champion-accent, var(--color-gold)) 28%, transparent), transparent),
+ var(--color-surface-strong);
+ color: var(--color-text);
+ font-family: var(--font-display);
+ font-size: 3rem;
+}
+
+.stat-list {
+ display: grid;
+ gap: var(--space-3);
+}
+
+.stat-list div {
+ display: flex;
+ justify-content: space-between;
+ gap: var(--space-4);
+ border-bottom: 1px solid var(--color-border);
+ padding-bottom: var(--space-2);
+}
+
+.stat-list dt {
+ color: var(--color-text-muted);
+}
+
+.stat-list dd {
+ margin: 0;
+ color: var(--color-text);
+}
+
+.action-link-inline {
+ margin-top: var(--space-4);
+}
+
+.profile-avatar {
+ width: 5rem;
+ height: 5rem;
+ border-radius: 50%;
+ font-size: 2rem;
+}
+
+.status-dot {
+ width: 0.75rem;
+ height: 0.75rem;
+ flex: 0 0 auto;
+ border-radius: 50%;
+ background: var(--color-text-muted);
+}
+
+.status-online,
+.status-in-game {
+ background: var(--color-success);
+}
+
+.status-away {
+ background: var(--color-warning);
+}
+
+.result-win {
+ color: var(--color-success);
+}
+
+.result-loss {
+ color: var(--color-danger);
+}
+
+.text-link {
+ color: var(--color-teal);
+}
+
+.ui-tabs {
+ display: grid;
+ gap: var(--space-3);
+}
+
+.ui-tab-list {
+ display: flex;
+ gap: var(--space-2);
+}
+
+.ui-tab-panel {
+ color: var(--color-text-muted);
+}
+
+.ui-tooltip {
+ position: relative;
+ display: inline-flex;
+}
+
+.ui-tooltip:hover::after {
+ position: absolute;
+ left: 0;
+ bottom: calc(100% + var(--space-2));
+ z-index: 10;
+ min-width: 12rem;
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-sm);
+ background: var(--color-bg-elevated);
+ color: var(--color-text);
+ content: attr(data-tooltip);
+ padding: var(--space-2);
+}
+
+.ui-toast {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ gap: var(--space-1);
+ border-color: rgb(54 215 208 / 0.45);
+}
+
+.ui-toast div {
+ display: grid;
+ gap: var(--space-1);
+}
+
+.toast-stack {
+ position: fixed;
+ right: var(--space-5);
+ bottom: var(--space-5);
+ z-index: 20;
+ display: grid;
+ width: min(24rem, calc(100vw - 2rem));
+ gap: var(--space-3);
+}
+
+.toast-dismiss {
+ border: 0;
+ background: transparent;
+ color: var(--color-text-muted);
+ cursor: pointer;
+ font-weight: 700;
+}
+
+.ui-toast-success {
+ border-color: var(--color-success);
+}
+
+.ui-toast-warning {
+ border-color: var(--color-warning);
+}
+
+.ui-modal-backdrop {
+ position: fixed;
+ inset: 0;
+ display: grid;
+ place-items: center;
+ background: rgb(0 0 0 / 0.62);
+ padding: var(--space-5);
+}
+
+.ui-modal {
+ width: min(34rem, 100%);
+ border: 1px solid var(--color-border-strong);
+ border-radius: var(--radius-lg);
+ background: var(--color-surface);
+ box-shadow: var(--shadow-glow), var(--shadow-panel);
+ padding: var(--space-5);
+}
+
+.ui-modal-header {
+ display: flex;
+ justify-content: space-between;
+ gap: var(--space-4);
+ margin-bottom: var(--space-4);
+}
+
+@media (max-width: 860px) {
+ .app-shell {
+ grid-template-columns: 1fr;
+ }
+
+ .sidebar {
+ border-right: 0;
+ border-bottom: 1px solid var(--color-border);
+ }
+
+ .social-sidebar {
+ border-top: 1px solid var(--color-border);
+ border-left: 0;
+ }
+
+ .nav-list,
+ .dashboard-grid,
+ .mode-grid,
+ .toolbar,
+ .settings-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .hero-panel,
+ .champion-detail-hero,
+ .topbar,
+ .status-bar {
+ align-items: flex-start;
+ flex-direction: column;
+ }
+
+ .champion-detail-hero {
+ grid-template-columns: 1fr;
+ }
+}
diff --git a/src/styles/theme.css b/src/styles/theme.css
new file mode 100644
index 0000000..8a90ed4
--- /dev/null
+++ b/src/styles/theme.css
@@ -0,0 +1,60 @@
+:root {
+ color-scheme: dark;
+ --color-bg: #070a12;
+ --color-bg-elevated: #0d1422;
+ --color-surface: #111b2d;
+ --color-surface-strong: #16243a;
+ --color-border: #2b3a52;
+ --color-border-strong: #6f5622;
+ --color-text: #f2f0e8;
+ --color-text-muted: #aeb8c8;
+ --color-gold: #c89b3c;
+ --color-gold-bright: #f0c66c;
+ --color-teal: #36d7d0;
+ --color-blue: #5b8cff;
+ --color-danger: #e56b6f;
+ --color-warning: #f0b35f;
+ --color-success: #61d394;
+ --space-1: 0.25rem;
+ --space-2: 0.5rem;
+ --space-3: 0.75rem;
+ --space-4: 1rem;
+ --space-5: 1.5rem;
+ --space-6: 2rem;
+ --radius-sm: 4px;
+ --radius-md: 8px;
+ --radius-lg: 12px;
+ --shadow-panel: 0 18px 48px rgb(0 0 0 / 0.32);
+ --shadow-glow: 0 0 28px rgb(54 215 208 / 0.14);
+ --font-body: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ --font-display: Georgia, "Times New Roman", serif;
+ --transition-fast: 160ms ease;
+}
+
+[data-theme="arcane-teal"] {
+ --color-bg: #041216;
+ --color-bg-elevated: #092029;
+ --color-surface: #0c2a33;
+ --color-surface-strong: #123945;
+ --color-border: #1d5561;
+ --color-border-strong: #36d7d0;
+ --color-gold: #b7a46a;
+ --color-gold-bright: #ead58a;
+ --color-teal: #55f2e8;
+ --color-blue: #61a4ff;
+ --shadow-glow: 0 0 30px rgb(85 242 232 / 0.18);
+}
+
+[data-theme="obsidian"] {
+ --color-bg: #050507;
+ --color-bg-elevated: #0d0d12;
+ --color-surface: #15151c;
+ --color-surface-strong: #20202a;
+ --color-border: #343444;
+ --color-border-strong: #8d6d2e;
+ --color-gold: #b98a32;
+ --color-gold-bright: #e4bc65;
+ --color-teal: #6796a7;
+ --color-blue: #7f8fbf;
+ --shadow-glow: 0 0 24px rgb(185 138 50 / 0.14);
+}
diff --git a/src/test/renderWithRouter.tsx b/src/test/renderWithRouter.tsx
new file mode 100644
index 0000000..fe508f8
--- /dev/null
+++ b/src/test/renderWithRouter.tsx
@@ -0,0 +1,24 @@
+import type { ReactNode } from "react";
+import { MemoryRouter } from "react-router-dom";
+import { render } from "@testing-library/react";
+import { AppProvider } from "../app/AppContext";
+import { AppRoutes } from "../app/router";
+import { AppShell } from "../layouts/AppShell";
+
+export function renderAppAt(path = "/") {
+ return render(
+
+
+
+
+
+
+
+ );
+}
+
+export function renderWithRouter(children: ReactNode) {
+ return render(
+ {children}
+ );
+}
diff --git a/src/test/setup.ts b/src/test/setup.ts
new file mode 100644
index 0000000..f149f27
--- /dev/null
+++ b/src/test/setup.ts
@@ -0,0 +1 @@
+import "@testing-library/jest-dom/vitest";
diff --git a/src/types/domain.ts b/src/types/domain.ts
new file mode 100644
index 0000000..07a7f35
--- /dev/null
+++ b/src/types/domain.ts
@@ -0,0 +1,37 @@
+export type Champion = {
+ id: string;
+ name: string;
+ role: "Controller" | "Duelist" | "Guardian" | "Invoker" | "Striker";
+ difficulty: "Low" | "Medium" | "High";
+ tags: string[];
+ shortDescription: string;
+ accentColor?: string;
+};
+
+export type FriendStatus = "online" | "away" | "in-game" | "offline";
+
+export type Friend = {
+ id: string;
+ displayName: string;
+ status: FriendStatus;
+ activity: string;
+ level?: number;
+};
+
+export type NewsItem = {
+ id: string;
+ title: string;
+ category: string;
+ date: string;
+ summary: string;
+};
+
+export type MatchHistoryEntry = {
+ id: string;
+ championName: string;
+ mode: string;
+ result: "Victory" | "Defeat";
+ kda: string;
+ duration: string;
+ date: string;
+};
diff --git a/src/types/navigation.ts b/src/types/navigation.ts
new file mode 100644
index 0000000..2535f8a
--- /dev/null
+++ b/src/types/navigation.ts
@@ -0,0 +1,16 @@
+export type NavigationSection = "primary" | "collection" | "account" | "system";
+
+export type NavigationItem = {
+ id: string;
+ label: string;
+ path: string;
+ iconName?: string;
+ section?: NavigationSection;
+ enabled?: boolean;
+};
+
+export type AppRoute = {
+ id: string;
+ title: string;
+ path: string;
+};
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..a216c8b
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["DOM", "DOM.Iterable", "ES2020"],
+ "allowJs": false,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx"
+ },
+ "include": ["src", "vite.config.ts"]
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..78f1f4f
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,13 @@
+///
+
+import react from "@vitejs/plugin-react";
+import { defineConfig } from "vitest/config";
+
+export default defineConfig({
+ plugins: [react()],
+ test: {
+ environment: "jsdom",
+ globals: true,
+ setupFiles: "./src/test/setup.ts"
+ }
+});