From 45b96ec20f4f82daa2a686ea52194571b9ebdff9 Mon Sep 17 00:00:00 2001
From: ToxicCrzay270 <185776014+ToxicCrzay270@users.noreply.github.com>
Date: Fri, 15 May 2026 00:41:38 +0200
Subject: [PATCH] Initialize League GUI prototype
---
.codex/project.md | 79 +
.gitea/workflows/dependency-check.yml | 114 +
.gitea/workflows/release-dry-run.yml | 133 ++
.gitea/workflows/repo-cleanup.yml | 139 ++
.gitea/workflows/security-scan.yml | 173 ++
.gitea/workflows/template-compliance.yml | 99 +
.gitignore | 5 +
AGENTS.md | 66 +
CHANGELOG.md | 8 +
CONTRIBUTING.md | 41 +
INSTALL.md | 119 +
README.md | 125 +
SECURITY.md | 22 +
docs/agent-handoff.md | 38 +
docs/release-checklist.md | 38 +
docs/release-notes.md | 25 +
docs/security-review.md | 54 +
index.html | 12 +
package-lock.json | 2534 +++++++++++++++++++++
package.json | 29 +
src/app/App.test.tsx | 78 +
src/app/App.tsx | 16 +
src/app/AppContext.tsx | 64 +
src/app/router.tsx | 13 +
src/components/ui/Button.tsx | 30 +
src/components/ui/Dropdown.tsx | 26 +
src/components/ui/Modal.tsx | 29 +
src/components/ui/Panel.tsx | 21 +
src/components/ui/SearchInput.tsx | 14 +
src/components/ui/Tabs.tsx | 42 +
src/components/ui/Toast.tsx | 24 +
src/components/ui/Tooltip.tsx | 14 +
src/components/ui/index.ts | 11 +
src/components/ui/ui.test.tsx | 44 +
src/config/navigation.ts | 44 +
src/config/routes.tsx | 49 +
src/data/champions.mock.ts | 40 +
src/data/friends.mock.ts | 25 +
src/data/matchHistory.mock.ts | 22 +
src/data/news.mock.ts | 18 +
src/features/champions/championLookup.ts | 9 +
src/features/champions/filterChampions.ts | 15 +
src/features/profile/profileSummary.ts | 6 +
src/features/social/SocialSidebar.tsx | 44 +
src/features/social/friendStatus.ts | 12 +
src/layouts/AppShell.tsx | 106 +
src/main.tsx | 11 +
src/pages/ChampionDetailPage.tsx | 67 +
src/pages/ChampionsPage.tsx | 72 +
src/pages/HomePage.tsx | 68 +
src/pages/PlayPage.tsx | 49 +
src/pages/ProfilePage.tsx | 59 +
src/pages/SettingsPage.tsx | 76 +
src/styles/global.css | 681 ++++++
src/styles/theme.css | 60 +
src/test/renderWithRouter.tsx | 24 +
src/test/setup.ts | 1 +
src/types/domain.ts | 37 +
src/types/navigation.ts | 16 +
src/vite-env.d.ts | 1 +
tsconfig.json | 20 +
vite.config.ts | 13 +
62 files changed, 5924 insertions(+)
create mode 100644 .codex/project.md
create mode 100644 .gitea/workflows/dependency-check.yml
create mode 100644 .gitea/workflows/release-dry-run.yml
create mode 100644 .gitea/workflows/repo-cleanup.yml
create mode 100644 .gitea/workflows/security-scan.yml
create mode 100644 .gitea/workflows/template-compliance.yml
create mode 100644 .gitignore
create mode 100644 AGENTS.md
create mode 100644 CHANGELOG.md
create mode 100644 CONTRIBUTING.md
create mode 100644 INSTALL.md
create mode 100644 README.md
create mode 100644 SECURITY.md
create mode 100644 docs/agent-handoff.md
create mode 100644 docs/release-checklist.md
create mode 100644 docs/release-notes.md
create mode 100644 docs/security-review.md
create mode 100644 index.html
create mode 100644 package-lock.json
create mode 100644 package.json
create mode 100644 src/app/App.test.tsx
create mode 100644 src/app/App.tsx
create mode 100644 src/app/AppContext.tsx
create mode 100644 src/app/router.tsx
create mode 100644 src/components/ui/Button.tsx
create mode 100644 src/components/ui/Dropdown.tsx
create mode 100644 src/components/ui/Modal.tsx
create mode 100644 src/components/ui/Panel.tsx
create mode 100644 src/components/ui/SearchInput.tsx
create mode 100644 src/components/ui/Tabs.tsx
create mode 100644 src/components/ui/Toast.tsx
create mode 100644 src/components/ui/Tooltip.tsx
create mode 100644 src/components/ui/index.ts
create mode 100644 src/components/ui/ui.test.tsx
create mode 100644 src/config/navigation.ts
create mode 100644 src/config/routes.tsx
create mode 100644 src/data/champions.mock.ts
create mode 100644 src/data/friends.mock.ts
create mode 100644 src/data/matchHistory.mock.ts
create mode 100644 src/data/news.mock.ts
create mode 100644 src/features/champions/championLookup.ts
create mode 100644 src/features/champions/filterChampions.ts
create mode 100644 src/features/profile/profileSummary.ts
create mode 100644 src/features/social/SocialSidebar.tsx
create mode 100644 src/features/social/friendStatus.ts
create mode 100644 src/layouts/AppShell.tsx
create mode 100644 src/main.tsx
create mode 100644 src/pages/ChampionDetailPage.tsx
create mode 100644 src/pages/ChampionsPage.tsx
create mode 100644 src/pages/HomePage.tsx
create mode 100644 src/pages/PlayPage.tsx
create mode 100644 src/pages/ProfilePage.tsx
create mode 100644 src/pages/SettingsPage.tsx
create mode 100644 src/styles/global.css
create mode 100644 src/styles/theme.css
create mode 100644 src/test/renderWithRouter.tsx
create mode 100644 src/test/setup.ts
create mode 100644 src/types/domain.ts
create mode 100644 src/types/navigation.ts
create mode 100644 src/vite-env.d.ts
create mode 100644 tsconfig.json
create mode 100644 vite.config.ts
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"
+ }
+});