From 3c8a46e5a6422312c5620216c0d44e15e59c9bf3 Mon Sep 17 00:00:00 2001 From: Naomi Carrigan Date: Wed, 21 Jan 2026 20:18:03 -0800 Subject: [PATCH] feat: add meeting transcription app scaffolding - Add Python backend structure with FastAPI for transcription/summarization - Add React UI with audio recording, transcript, and summary views - Configure Tauri to manage Python backend lifecycle - Set up Windows cross-compilation with cargo-xwin - Add Gitea CI workflow for lint, test, and multi-platform builds - Configure ESLint, Prettier, and Vitest for code quality Note: App scaffolding only - Python env and models not yet set up --- .cargo/config.toml | 4 - .gitea/issue_template/bug_report.yaml | 5 +- .gitea/issue_template/config.yml | 2 +- .gitea/issue_template/feature_proposal.yml | 2 +- .gitea/issue_template/other.yml | 2 +- .gitea/workflows/ci.yml | 189 ++ .gitea/workflows/security.yml | 26 +- .gitignore | 30 + .prettierignore | 8 + .prettierrc | 7 + PROJECT_INFO.md | 120 + README.md | 2 +- build-all.sh | 89 - check-all.sh | 40 + eslint.config.js | 35 +- package.json | 28 +- pnpm-lock.yaml | 2416 +++++++------------- pyproject.toml | 27 + requirements.txt | 10 + scripts/build_windows.py | 128 ++ scripts/download_models.py | 90 + scripts/start_backend.py | 24 + src-tauri/capabilities/default.json | 5 +- src-tauri/src/lib.rs | 62 +- src-tauri/tauri.conf.json | 21 +- src/App.css | 380 ++- src/App.tsx | 210 +- src/__tests__/App.test.tsx | 7 + src/backend/__init__.py | 1 + src/backend/main.py | 74 + src/backend/models/__init__.py | 1 + src/backend/models/audio.py | 73 + src/backend/models/llm.py | 67 + src/backend/models/transcriber.py | 88 + src/components/AudioRecorder.tsx | 83 + src/components/SummaryDisplay.tsx | 32 + src/components/TranscriptDisplay.tsx | 57 + src/main.tsx | 2 +- tsconfig.json | 16 +- vitest.config.ts | 12 + vitest.setup.ts | 1 + 41 files changed, 2679 insertions(+), 1797 deletions(-) delete mode 100644 .cargo/config.toml create mode 100644 .gitea/workflows/ci.yml create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 PROJECT_INFO.md delete mode 100755 build-all.sh create mode 100755 check-all.sh create mode 100644 pyproject.toml create mode 100644 requirements.txt create mode 100644 scripts/build_windows.py create mode 100644 scripts/download_models.py create mode 100644 scripts/start_backend.py create mode 100644 src/__tests__/App.test.tsx create mode 100644 src/backend/__init__.py create mode 100644 src/backend/main.py create mode 100644 src/backend/models/__init__.py create mode 100644 src/backend/models/audio.py create mode 100644 src/backend/models/llm.py create mode 100644 src/backend/models/transcriber.py create mode 100644 src/components/AudioRecorder.tsx create mode 100644 src/components/SummaryDisplay.tsx create mode 100644 src/components/TranscriptDisplay.tsx create mode 100644 vitest.config.ts create mode 100644 vitest.setup.ts diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index bffe8e8..0000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,4 +0,0 @@ -[target.x86_64-pc-windows-gnu] -linker = "x86_64-w64-mingw32-gcc" -ar = "x86_64-w64-mingw32-ar" -rustflags = ["-C", "link-arg=-lws2_32", "-C", "link-arg=-lbcrypt", "-C", "link-arg=-lole32", "-C", "link-arg=-luuid"] \ No newline at end of file diff --git a/.gitea/issue_template/bug_report.yaml b/.gitea/issue_template/bug_report.yaml index bf17745..24ef890 100644 --- a/.gitea/issue_template/bug_report.yaml +++ b/.gitea/issue_template/bug_report.yaml @@ -1,6 +1,6 @@ name: ๐Ÿ› Bug Report description: Something isn't working as expected? Let us know! -title: '[BUG] - ' +title: "[BUG] - " labels: - "status/awaiting triage" body: @@ -50,7 +50,7 @@ body: description: The operating system you are using, including the version/build number. validations: required: true -# Remove this section for non-web apps. + # Remove this section for non-web apps. - type: input id: browser attributes: @@ -66,4 +66,3 @@ body: - No validations: required: true - diff --git a/.gitea/issue_template/config.yml b/.gitea/issue_template/config.yml index f1cdc86..268aa3e 100644 --- a/.gitea/issue_template/config.yml +++ b/.gitea/issue_template/config.yml @@ -2,4 +2,4 @@ blank_issues_enabled: false contact_links: - name: "Discord" url: "https://chat.nhcarrigan.com" - about: "Chat with us directly." \ No newline at end of file + about: "Chat with us directly." diff --git a/.gitea/issue_template/feature_proposal.yml b/.gitea/issue_template/feature_proposal.yml index b3fae97..37af46c 100644 --- a/.gitea/issue_template/feature_proposal.yml +++ b/.gitea/issue_template/feature_proposal.yml @@ -1,6 +1,6 @@ name: ๐Ÿ’ญ Feature Proposal description: Have an idea for how we can improve? Share it here! -title: '[FEAT] - ' +title: "[FEAT] - " labels: - "status/awaiting triage" body: diff --git a/.gitea/issue_template/other.yml b/.gitea/issue_template/other.yml index 2f1335f..51ae0cd 100644 --- a/.gitea/issue_template/other.yml +++ b/.gitea/issue_template/other.yml @@ -1,6 +1,6 @@ name: โ“ Other Issue description: I have something that is neither a bug nor a feature request. -title: '[OTHER] - ' +title: "[OTHER] - " labels: - "status/awaiting triage" body: diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..7871bbb --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,189 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +jobs: + lint-and-test: + name: Lint & Test + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Linux dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + libwebkit2gtk-4.1-dev \ + librsvg2-dev \ + patchelf \ + libgtk-3-dev \ + libayatana-appindicator3-dev + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Install frontend dependencies + run: pnpm install + + - name: Run ESLint + run: pnpm lint + + - name: Run Prettier check + run: pnpm format:check + + - name: Build frontend + run: pnpm build + + - name: Run frontend tests + run: pnpm test + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + components: clippy + + - name: Cache Rust dependencies + uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + src-tauri/target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Run Clippy + working-directory: src-tauri + run: cargo clippy --all-targets --all-features -- -D warnings + + - name: Run Rust tests + working-directory: src-tauri + run: cargo test + + build-linux: + name: Build Linux + runs-on: ubuntu-latest + needs: lint-and-test + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Linux dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + libwebkit2gtk-4.1-dev \ + librsvg2-dev \ + patchelf \ + libgtk-3-dev \ + libayatana-appindicator3-dev \ + xdg-utils + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Install frontend dependencies + run: pnpm install + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + + - name: Cache Rust dependencies + uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + src-tauri/target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Build Linux + run: pnpm build:linux + + build-windows: + name: Build Windows (cross-compile) + runs-on: ubuntu-latest + needs: lint-and-test + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Linux dependencies for cross-compilation + run: | + sudo apt-get update + sudo apt-get install -y \ + libwebkit2gtk-4.1-dev \ + librsvg2-dev \ + patchelf \ + libgtk-3-dev \ + libayatana-appindicator3-dev \ + clang \ + lld \ + llvm \ + nsis + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Install frontend dependencies + run: pnpm install + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: x86_64-pc-windows-msvc + + - name: Install cargo-xwin + run: | + curl -fsSL https://github.com/rust-cross/cargo-xwin/releases/download/v0.20.2/cargo-xwin-v0.20.2.x86_64-unknown-linux-musl.tar.gz | tar xz + sudo mv cargo-xwin /usr/local/bin/ + + - name: Cache Rust dependencies + uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + src-tauri/target/ + key: ${{ runner.os }}-cargo-windows-${{ hashFiles('**/Cargo.lock') }} + + - name: Build Windows + run: pnpm build:windows diff --git a/.gitea/workflows/security.yml b/.gitea/workflows/security.yml index 3169f83..e7a3807 100644 --- a/.gitea/workflows/security.yml +++ b/.gitea/workflows/security.yml @@ -2,11 +2,11 @@ name: Security Scan and Upload on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] schedule: - - cron: '0 0 * * 1' + - cron: "0 0 * * 1" workflow_dispatch: jobs: @@ -24,18 +24,18 @@ jobs: env: DD_URL: ${{ secrets.DD_URL }} DD_TOKEN: ${{ secrets.DD_TOKEN }} - PRODUCT_NAME: ${{ github.repository }} - PRODUCT_TYPE_ID: 1 + PRODUCT_NAME: ${{ github.repository }} + PRODUCT_TYPE_ID: 1 run: | sudo apt-get install jq -y > /dev/null - + echo "Checking connection to $DD_URL..." - + # Check if product exists - capture HTTP code to debug connection issues RESPONSE=$(curl --write-out "%{http_code}" --silent --output /tmp/response.json \ -H "Authorization: Token $DD_TOKEN" \ "$DD_URL/api/v2/products/?name=$PRODUCT_NAME") - + # If response is not 200, print error if [ "$RESPONSE" != "200" ]; then echo "::error::Failed to query DefectDojo. HTTP Code: $RESPONSE" @@ -44,7 +44,7 @@ jobs: fi COUNT=$(cat /tmp/response.json | jq -r '.count') - + if [ "$COUNT" = "0" ]; then echo "Creating product '$PRODUCT_NAME'..." curl -s -X POST "$DD_URL/api/v2/products/" \ @@ -75,7 +75,7 @@ jobs: echo "Uploading Trivy results..." # Generate today's date in YYYY-MM-DD format TODAY=$(date +%Y-%m-%d) - + HTTP_CODE=$(curl --write-out "%{http_code}" --output response.txt --silent -X POST "$DD_URL/api/v2/import-scan/" \ -H "Authorization: Token $DD_TOKEN" \ -F "active=true" \ @@ -86,7 +86,7 @@ jobs: -F "scan_date=$TODAY" \ -F "auto_create_context=true" \ -F "file=@trivy-results.json") - + if [[ "$HTTP_CODE" != "200" && "$HTTP_CODE" != "201" ]]; then echo "::error::Upload Failed with HTTP $HTTP_CODE" echo "--- SERVER RESPONSE ---" @@ -154,7 +154,7 @@ jobs: run: | echo "Uploading Semgrep results..." TODAY=$(date +%Y-%m-%d) - + HTTP_CODE=$(curl --write-out "%{http_code}" --output response.txt --silent -X POST "$DD_URL/api/v2/import-scan/" \ -H "Authorization: Token $DD_TOKEN" \ -F "active=true" \ @@ -174,4 +174,4 @@ jobs: exit 1 else echo "Upload Success!" - fi \ No newline at end of file + fi diff --git a/.gitignore b/.gitignore index a547bf3..cdc5ba1 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,33 @@ dist-ssr *.njsproj *.sln *.sw? + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +env/ +venv/ +ENV/ +.venv/ +*.egg-info/ + +# Models (we'll add these to git lfs later) +models/*.gguf +models/*.bin + +# Tauri +src-tauri/target/ +src-tauri/WixTools/ + +# App data +recordings/ +transcripts/ +summaries/ + +# Environment +.env +*.env.local +prod.env diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..582afc9 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,8 @@ +build/ +.svelte-kit/ +dist/ +src-tauri/target/ +src-tauri/gen/ +node_modules/ +.pnpm-store/ +pnpm-lock.yaml diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..1a88ab1 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": false, + "tabWidth": 2, + "trailingComma": "es5", + "printWidth": 100 +} diff --git a/PROJECT_INFO.md b/PROJECT_INFO.md new file mode 100644 index 0000000..16dc1a3 --- /dev/null +++ b/PROJECT_INFO.md @@ -0,0 +1,120 @@ +# Chronara - Local Meeting Transcription & Summarization + +A Windows desktop application that transcribes, diarizes, and summarizes meetings using only locally-running models. + +## Features + +- ๐ŸŽ™๏ธ Real-time audio transcription with speaker diarization (WhisperX) +- ๐Ÿ“ Intelligent meeting summarization (Llama 3.2) +- ๐Ÿ–ฅ๏ธ Everything runs locally - no cloud services required +- ๐Ÿ“ฆ All models bundled - no separate downloads needed + +## Tech Stack + +- **Transcription**: WhisperX (Whisper + speaker diarization) +- **Summarization**: Llama 3.2 1B/3B +- **Backend**: Python with FastAPI +- **Frontend**: Tauri + React +- **Model Runtime**: llama-cpp-python + +## Project Structure + +``` +chronara/ +โ”œโ”€โ”€ src/ +โ”‚ โ”œโ”€โ”€ backend/ # Python FastAPI backend +โ”‚ โ”œโ”€โ”€ components/ # React components +โ”‚ โ””โ”€โ”€ App.tsx # Main React app +โ”œโ”€โ”€ src-tauri/ # Tauri configuration +โ”œโ”€โ”€ models/ # Bundled model files +โ”œโ”€โ”€ scripts/ # Build and setup scripts +โ””โ”€โ”€ assets/ # Icons, resources +``` + +## Development Setup + +### Prerequisites + +- Node.js 18+ with pnpm +- Python 3.10+ +- Rust (for Tauri) +- Windows build tools (for native modules) + +### Installation + +1. Clone the repository: + +```bash +git clone https://github.com/naomi-lgbt/chronara.git +cd chronara +``` + +2. Install frontend dependencies: + +```bash +pnpm install +``` + +3. Install Python dependencies: + +```bash +pip install -r requirements.txt +``` + +4. Download the AI models: + +```bash +python scripts/download_models.py +``` + +5. Run in development mode: + +```bash +pnpm tauri:dev +``` + +## Building for Production + +### Windows + +1. Download models if not already done: + +```bash +python scripts/download_models.py +``` + +2. Build the Windows executable: + +```bash +python scripts/build_windows.py +``` + +The installer will be created in `src-tauri/target/release/bundle/nsis/`. + +## Usage + +1. **Start Recording**: Click the "Start Recording" button to begin capturing audio +2. **Real-time Transcription**: Watch as the conversation is transcribed with speaker labels +3. **Generate Summary**: After recording, click "Generate Summary" for an AI-powered meeting summary +4. **Export**: Download both the full transcript and summary as text files + +## Model Information + +### Transcription (WhisperX) + +- **Model**: OpenAI Whisper base model with WhisperX enhancements +- **Features**: Speaker diarization, timestamp alignment +- **Size**: ~150MB + +### Summarization (Llama 3.2) + +- **1B Model**: Fast, good for basic summaries (~1.2GB) +- **3B Model**: Better quality summaries (~2.5GB) +- **Format**: GGUF quantized models + +## Privacy & Security + +- All processing happens locally on your machine +- No audio or text data is sent to external servers +- Models are bundled with the application +- Meeting data stays on your device diff --git a/README.md b/README.md index 76f5916..e98cb85 100644 --- a/README.md +++ b/README.md @@ -26,4 +26,4 @@ Copyright held by Naomi Carrigan. ## Contact -We may be contacted through our [Chat Server](http://chat.nhcarrigan.com) or via email at `contact@nhcarrigan.com`. \ No newline at end of file +We may be contacted through our [Chat Server](http://chat.nhcarrigan.com) or via email at `contact@nhcarrigan.com`. diff --git a/build-all.sh b/build-all.sh deleted file mode 100755 index 865f82b..0000000 --- a/build-all.sh +++ /dev/null @@ -1,89 +0,0 @@ -#!/bin/bash - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -echo -e "${BLUE}๐Ÿš€ Building Chronara for all platforms...${NC}" - -# Function to run a build and check its status -run_build() { - local target=$1 - local desc=$2 - - echo -e "\n${YELLOW}Building: ${desc}${NC}" - - if pnpm tauri build --target "$target"; then - echo -e "${GREEN}โœ“ ${desc} build succeeded${NC}" - return 0 - else - echo -e "${RED}โœ— ${desc} build failed${NC}" - return 1 - fi -} - -# Ensure we're using the correct Node version -source /home/naomi/.nvm/nvm.sh -nvm use 24.11.1 - -# Install dependencies if needed -echo -e "${YELLOW}Installing dependencies...${NC}" -pnpm install - -# Build frontend first -echo -e "${YELLOW}Building frontend...${NC}" -pnpm build - -# Track if any builds fail -failed=0 - -# Linux builds (native in WSL) -echo -e "\n${BLUE}Building Linux targets...${NC}" -run_build "x86_64-unknown-linux-gnu" "Linux AppImage/Deb/RPM" || failed=1 - -# Windows build (cross-compile from WSL) -echo -e "\n${BLUE}Building Windows target...${NC}" - -# Check if Windows target is installed -if ! rustup target list --installed | grep -q "x86_64-pc-windows-gnu"; then - echo -e "${YELLOW}Installing Windows target...${NC}" - rustup target add x86_64-pc-windows-gnu -fi - -# Check if full mingw-w64 toolchain is installed -if ! command -v x86_64-w64-mingw32-gcc &> /dev/null || ! command -v x86_64-w64-mingw32-dlltool &> /dev/null; then - echo -e "${RED}Windows cross-compilation tools are missing!${NC}" - echo -e "${YELLOW}Please install the full mingw-w64 toolchain:${NC}" - echo -e "${YELLOW} sudo apt-get update${NC}" - echo -e "${YELLOW} sudo apt-get install -y gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 mingw-w64-tools${NC}" - echo -e "${RED}Skipping Windows build...${NC}" - SKIP_WINDOWS=1 -fi - -# Set up environment for Windows cross-compilation -export CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER="x86_64-w64-mingw32-gcc" -export CC_x86_64_pc_windows_gnu="x86_64-w64-mingw32-gcc" -export CXX_x86_64_pc_windows_gnu="x86_64-w64-mingw32-g++" -export AR_x86_64_pc_windows_gnu="x86_64-w64-mingw32-ar" - -if [ -z "$SKIP_WINDOWS" ]; then - run_build "x86_64-pc-windows-gnu" "Windows NSIS" || failed=1 -fi - -# Summary -echo -e "\n${YELLOW}========================================${NC}" -if [ $failed -eq 0 ]; then - echo -e "${GREEN}โœจ All builds completed successfully!${NC}" - echo -e "${GREEN}Build outputs:${NC}" - echo -e "${GREEN} Linux: src-tauri/target/release/bundle/appimage/chronara_*.AppImage${NC}" - echo -e "${GREEN} Linux: src-tauri/target/release/bundle/deb/chronara_*.deb${NC}" - echo -e "${GREEN} Linux: src-tauri/target/release/bundle/rpm/chronara-*.rpm${NC}" - echo -e "${GREEN} Windows: src-tauri/target/x86_64-pc-windows-gnu/release/bundle/nsis/Chronara_*.exe${NC}" - exit 0 -else - echo -e "${RED}โŒ Some builds failed. Check the output above for details.${NC}" - exit 1 -fi \ No newline at end of file diff --git a/check-all.sh b/check-all.sh new file mode 100755 index 0000000..30dbedc --- /dev/null +++ b/check-all.sh @@ -0,0 +1,40 @@ +#!/bin/bash +set -e + +echo "๐Ÿ” Running all checks..." +echo "========================================" + +echo "" +echo "๐Ÿ“ฆ Installing dependencies..." +pnpm install + +echo "" +echo "๐Ÿ”Ž Running ESLint..." +pnpm lint + +echo "" +echo "๐Ÿ’… Running Prettier check..." +pnpm format:check + +echo "" +echo "๐Ÿ—๏ธ Building frontend..." +pnpm build + +echo "" +echo "๐Ÿงช Running frontend tests..." +pnpm test + +echo "" +echo "๐Ÿฆ€ Running Clippy..." +cd src-tauri +cargo clippy --all-targets --all-features -- -D warnings + +echo "" +echo "๐Ÿงช Running Rust tests..." +cargo test + +cd .. + +echo "" +echo "========================================" +echo "โœ… All checks passed!" diff --git a/eslint.config.js b/eslint.config.js index a53f0ab..2050d88 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,3 +1,34 @@ -import nhcarriganConfig from "@nhcarrigan/eslint-config"; +import js from "@eslint/js"; +import tseslint from "typescript-eslint"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import prettier from "eslint-config-prettier"; +import globals from "globals"; -export default [...nhcarriganConfig]; \ No newline at end of file +export default tseslint.config( + js.configs.recommended, + ...tseslint.configs.recommended, + prettier, + { + languageOptions: { + globals: { + ...globals.browser, + ...globals.node, + }, + }, + }, + { + files: ["**/*.{ts,tsx}"], + plugins: { + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], + }, + }, + { + ignores: ["build/", "dist/", "src-tauri/target/", "node_modules/"], + } +); diff --git a/package.json b/package.json index 31e364b..8583206 100644 --- a/package.json +++ b/package.json @@ -7,11 +7,18 @@ "dev": "vite", "build": "tsc && vite build", "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "prettier --write .", + "format:check": "prettier --check .", "preview": "vite preview", "tauri": "tauri", "tauri:dev": "tauri dev", - "tauri:build": "tauri build", - "build:all": "./build-all.sh" + "build:linux": "tauri build", + "build:windows": "tauri build --runner cargo-xwin --target x86_64-pc-windows-msvc", + "build:all": "pnpm build:linux && pnpm build:windows", + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage" }, "dependencies": { "@tauri-apps/api": "^2", @@ -20,14 +27,23 @@ "react-dom": "^19.1.0" }, "devDependencies": { - "@nhcarrigan/eslint-config": "2.0.0", - "@nhcarrigan/typescript-config": "4.0.0", + "@eslint/js": "^9.19.0", "@tauri-apps/cli": "^2", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.0", "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "@vitejs/plugin-react": "^4.6.0", - "eslint": "9.19.0", + "eslint": "^9.19.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^17.0.0", + "jsdom": "^27.4.0", + "prettier": "^3.8.0", "typescript": "~5.8.3", - "vite": "^7.0.4" + "typescript-eslint": "^8.53.0", + "vite": "^7.0.4", + "vitest": "^4.0.17" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9c816f0..af98fb1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,15 +21,18 @@ importers: specifier: ^19.1.0 version: 19.2.3(react@19.2.3) devDependencies: - '@nhcarrigan/eslint-config': - specifier: 2.0.0 - version: 2.0.0(eslint@9.19.0)(prettier@3.8.1)(typescript@5.8.3) - '@nhcarrigan/typescript-config': - specifier: 4.0.0 - version: 4.0.0(typescript@5.8.3) + '@eslint/js': + specifier: ^9.19.0 + version: 9.19.0 '@tauri-apps/cli': specifier: ^2 version: 2.9.6 + '@testing-library/jest-dom': + specifier: ^6.9.1 + version: 6.9.1 + '@testing-library/react': + specifier: ^16.3.0 + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@types/react': specifier: ^19.1.8 version: 19.2.9 @@ -40,17 +43,56 @@ importers: specifier: ^4.6.0 version: 4.7.0(vite@7.3.1) eslint: - specifier: 9.19.0 + specifier: ^9.19.0 version: 9.19.0 + eslint-config-prettier: + specifier: ^10.1.8 + version: 10.1.8(eslint@9.19.0) + eslint-plugin-react-hooks: + specifier: ^5.2.0 + version: 5.2.0(eslint@9.19.0) + eslint-plugin-react-refresh: + specifier: ^0.4.20 + version: 0.4.26(eslint@9.19.0) + globals: + specifier: ^17.0.0 + version: 17.0.0 + jsdom: + specifier: ^27.4.0 + version: 27.4.0 + prettier: + specifier: ^3.8.0 + version: 3.8.1 typescript: specifier: ~5.8.3 version: 5.8.3 + typescript-eslint: + specifier: ^8.53.0 + version: 8.53.1(eslint@9.19.0)(typescript@5.8.3) vite: specifier: ^7.0.4 version: 7.3.1 + vitest: + specifier: ^4.0.17 + version: 4.0.17(jsdom@27.4.0) packages: + '@acemir/cssom@0.9.31': + resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==} + + '@adobe/css-tools@4.4.4': + resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} + + '@asamuzakjp/css-color@4.1.1': + resolution: {integrity: sha512-B0Hv6G3gWGMn0xKJ0txEi/jM5iFpT3MfDxmhZFb4W047GvytCf1DHQ1D69W3zHI4yWe2aTZAA0JnbMZ7Xc8DuQ==} + + '@asamuzakjp/dom-selector@6.7.6': + resolution: {integrity: sha512-hBaJER6A9MpdG3WgdlOolHmbOYvSk46y7IQN/1+iqiCuUu6iWdQrs9DGKF8ocqsEqWujWf/V7b7vaDgiUmIvUg==} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + '@babel/code-frame@7.28.6': resolution: {integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==} engines: {node: '>=6.9.0'} @@ -122,6 +164,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/runtime@7.28.6': + resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} + engines: {node: '>=6.9.0'} + '@babel/template@7.28.6': resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} engines: {node: '>=6.9.0'} @@ -134,9 +180,37 @@ packages: resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==} engines: {node: '>=6.9.0'} - '@es-joy/jsdoccomment@0.37.1': - resolution: {integrity: sha512-5vxWJ1gEkEF0yRd0O+uK6dHJf7adrxwQSX8PuRiPfFSAbNLnY0ZJfXaZucoz14Jj2N11xn2DnlEPwWRpYpvRjg==} - engines: {node: ^14 || ^16 || ^17 || ^18 || ^19 || ^20} + '@csstools/color-helpers@5.1.0': + resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} + engines: {node: '>=18'} + + '@csstools/css-calc@2.1.4': + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-color-parser@3.1.0': + resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-syntax-patches-for-csstree@1.0.25': + resolution: {integrity: sha512-g0Kw9W3vjx5BEBAF8c5Fm2NcB/Fs8jJXh85aXqwEXiL+tqtOut07TWgyaGzAAfTM+gKckrrncyeGEZPcaRgm2Q==} + engines: {node: '>=18'} + + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} '@esbuild/aix-ppc64@0.27.2': resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} @@ -332,6 +406,15 @@ packages: resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@exodus/bytes@1.9.0': + resolution: {integrity: sha512-lagqsvnk09NKogQaN/XrtlWeUF8SRhT12odMvbTIIaVObqzwAogL6jhR4DAp0gPuKoM1AOVrKUshJpRdpMFrww==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@noble/hashes': ^1.8.0 || ^2.0.0 + peerDependenciesMeta: + '@noble/hashes': + optional: true + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -364,33 +447,6 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@nhcarrigan/eslint-config@2.0.0': - resolution: {integrity: sha512-05R2ihuVx5n3JGkaBccAot7y8q1uvPekRcKwceqq0ySJOuiZb9lGDDJ9Xo1QqaTZRocwBtQOxjBuFBMDUJxz2A==} - peerDependencies: - eslint: '>=8' - - '@nhcarrigan/typescript-config@4.0.0': - resolution: {integrity: sha512-969HVha7A/Sg77fuMwOm6p14a+7C5iE6g55OD71srqwKIgksQl+Ex/hAI/pyzTQFDQ/FBJbpnHlR4Ov25QV/rw==} - engines: {node: '20', pnpm: '9'} - peerDependencies: - typescript: '>=5.5.2' - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - - '@pkgr/core@0.1.2': - resolution: {integrity: sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} @@ -519,6 +575,9 @@ packages: cpu: [x64] os: [win32] + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@tauri-apps/api@2.9.1': resolution: {integrity: sha512-IGlhP6EivjXHepbBic618GOmiWe4URJiIeZFlB7x3czM0yDHHYviH1Xvoiv4FefdkQtn6v7TuwWCRfOGdnVUGw==} @@ -596,6 +655,32 @@ packages: '@tauri-apps/plugin-opener@2.5.3': resolution: {integrity: sha512-CCcUltXMOfUEArbf3db3kCE7Ggy1ExBEBl51Ko2ODJ6GDYHRp1nSNlQm5uNCFY5k7/ufaK5Ib3Du/Zir19IYQQ==} + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + + '@testing-library/jest-dom@6.9.1': + resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + + '@testing-library/react@16.3.2': + resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} + 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 + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -608,15 +693,18 @@ packages: '@types/babel__traverse@7.28.0': resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/json5@0.0.29': - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: @@ -625,66 +713,64 @@ packages: '@types/react@19.2.9': resolution: {integrity: sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==} - '@types/semver@7.7.1': - resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} - - '@typescript-eslint/eslint-plugin@5.62.0': - resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/eslint-plugin@8.53.1': + resolution: {integrity: sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/parser': ^8.53.1 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@5.62.0': - resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/parser@8.53.1': + resolution: {integrity: sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@5.62.0': - resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - '@typescript-eslint/type-utils@5.62.0': - resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/project-service@8.53.1': + resolution: {integrity: sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: '*' - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@5.62.0': - resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/scope-manager@8.53.1': + resolution: {integrity: sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@5.62.0': - resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/tsconfig-utils@8.53.1': + resolution: {integrity: sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@5.62.0': - resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/type-utils@8.53.1': + resolution: {integrity: sha512-MOrdtNvyhy0rHyv0ENzub1d4wQYKb2NmIqG7qEqPWFW7Mpy2jzFC3pQ2yKDvirZB7jypm5uGjF2Qqs6OIqu47w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@5.62.0': - resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/types@8.53.1': + resolution: {integrity: sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.53.1': + resolution: {integrity: sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.53.1': + resolution: {integrity: sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.53.1': + resolution: {integrity: sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitejs/plugin-react@4.7.0': resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} @@ -692,6 +778,35 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + '@vitest/expect@4.0.17': + resolution: {integrity: sha512-mEoqP3RqhKlbmUmntNDDCJeTDavDR+fVYkSOw8qRwJFaW/0/5zA9zFeTrHqNtcmwh6j26yMmwx2PqUDPzt5ZAQ==} + + '@vitest/mocker@4.0.17': + resolution: {integrity: sha512-+ZtQhLA3lDh1tI2wxe3yMsGzbp7uuJSWBM1iTIKCbppWTSBN09PUC+L+fyNlQApQoR+Ps8twt2pbSSXg2fQVEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.0.17': + resolution: {integrity: sha512-Ah3VAYmjcEdHg6+MwFE17qyLqBHZ+ni2ScKCiW2XrlSBV4H3Z7vYfPfz7CWQ33gyu76oc0Ai36+kgLU3rfF4nw==} + + '@vitest/runner@4.0.17': + resolution: {integrity: sha512-JmuQyf8aMWoo/LmNFppdpkfRVHJcsgzkbCA+/Bk7VfNH7RE6Ut2qxegeyx2j3ojtJtKIbIGy3h+KxGfYfk28YQ==} + + '@vitest/snapshot@4.0.17': + resolution: {integrity: sha512-npPelD7oyL+YQM2gbIYvlavlMVWUfNNGZPcu0aEUQXt7FXTuqhmgiYupPnAanhKvyP6Srs2pIbWo30K0RbDtRQ==} + + '@vitest/spy@4.0.17': + resolution: {integrity: sha512-I1bQo8QaP6tZlTomQNWKJE6ym4SHf3oLS7ceNjozxxgzavRAgZDc06T7kD8gb9bXKEgcLNt00Z+kZO6KaJ62Ew==} + + '@vitest/utils@4.0.17': + resolution: {integrity: sha512-RG6iy+IzQpa9SB8HAFHJ9Y+pTzI+h8553MrciN9eC6TFBErqrQaTas4vG+MVj8S4uKk8uTT2p0vgZPnTdxd96w==} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -702,55 +817,38 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - are-docs-informative@0.0.2: - resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} - engines: {node: '>=14'} + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - array-buffer-byte-length@1.0.2: - resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} - array-includes@3.1.9: - resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} - engines: {node: '>= 0.4'} - - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - array.prototype.findlastindex@1.2.6: - resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} - engines: {node: '>= 0.4'} - - array.prototype.flat@1.3.3: - resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} - engines: {node: '>= 0.4'} - - array.prototype.flatmap@1.3.3: - resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} - engines: {node: '>= 0.4'} - - arraybuffer.prototype.slice@1.0.4: - resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} - engines: {node: '>= 0.4'} - - async-function@1.0.0: - resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} - engines: {node: '>= 0.4'} - - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -759,30 +857,20 @@ packages: resolution: {integrity: sha512-agD0MgJFUP/4nvjqzIB29zRPUuCF7Ge6mEv9s8dHrtYD7QWXRcx75rOADE/d5ah1NI+0vkDl0yorDd5U852IQQ==} hasBin: true + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} browserslist@4.28.1: resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - call-bind@1.0.8: - resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -790,6 +878,10 @@ packages: caniuse-lite@1.0.30001765: resolution: {integrity: sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ==} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -801,10 +893,6 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - comment-parser@1.3.1: - resolution: {integrity: sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==} - engines: {node: '>= 12.0.0'} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -815,28 +903,23 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + css-tree@3.1.0: + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + + cssstyle@5.3.7: + resolution: {integrity: sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==} + engines: {node: '>=20'} + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - data-view-buffer@1.0.2: - resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} - engines: {node: '>= 0.4'} - - data-view-byte-length@1.0.2: - resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} - engines: {node: '>= 0.4'} - - data-view-byte-offset@1.0.1: - resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} - engines: {node: '>= 0.4'} - - debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + data-urls@6.0.1: + resolution: {integrity: sha512-euIQENZg6x8mj3fO6o9+fOW8MimUI4PpD/fZBhJfeioZVy9TUpM4UY7KjQNVZFlqwJ0UdzRDzkycB997HEq1BQ==} + engines: {node: '>=20'} debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} @@ -847,59 +930,31 @@ packages: supports-color: optional: true + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - - doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} - - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} electron-to-chromium@1.5.267: resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} - es-abstract@1.24.1: - resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} - engines: {node: '>= 0.4'} + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} - - es-shim-unscopables@1.1.0: - resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} - engines: {node: '>= 0.4'} - - es-to-primitive@1.3.0: - resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} - engines: {node: '>= 0.4'} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} esbuild@0.27.2: resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} @@ -914,73 +969,22 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-config-prettier@9.0.0: - resolution: {integrity: sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==} + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} hasBin: true peerDependencies: eslint: '>=7.0.0' - eslint-import-resolver-node@0.3.9: - resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} - - eslint-module-utils@2.12.1: - resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} - engines: {node: '>=4'} + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - eslint-plugin-import@2.28.0: - resolution: {integrity: sha512-B8s/n+ZluN7sxj9eUf7/pRFERX0r5bnFA2dCaLHy2ZeaQEAz0k+ZZkFWRFHJAqxfxQDx6KLv9LeIki7cFdwW+Q==} - engines: {node: '>=4'} + eslint-plugin-react-refresh@0.4.26: + resolution: {integrity: sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==} peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - - eslint-plugin-jsdoc@41.1.2: - resolution: {integrity: sha512-MePJXdGiPW7AG06CU5GbKzYtKpoHwTq1lKijjq+RwL/cQkZtBZ59Zbv5Ep0RVxSMnq6242249/n+w4XrTZ1Afg==} - engines: {node: ^14 || ^16 || ^17 || ^18 || ^19} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - - eslint-plugin-no-only-tests@3.1.0: - resolution: {integrity: sha512-Lf4YW/bL6Un1R6A76pRZyE1dl1vr31G/ev8UzIc/geCgFWyrKil8hVjYqWVKGB/UIGmb6Slzs9T0wNezdSVegw==} - engines: {node: '>=5.0.0'} - - eslint-plugin-prettier@5.0.0: - resolution: {integrity: sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - '@types/eslint': '>=8.0.0' - eslint: '>=8.0.0' - eslint-config-prettier: '*' - prettier: '>=3.0.0' - peerDependenciesMeta: - '@types/eslint': - optional: true - eslint-config-prettier: - optional: true - - eslint-scope@5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} + eslint: '>=8.40' eslint-scope@8.4.0: resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} @@ -1016,37 +1020,30 @@ packages: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} - estraverse@4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} - engines: {node: '>=4.0'} - estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fastq@1.20.1: - resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} - fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -1060,10 +1057,6 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -1075,49 +1068,15 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - for-each@0.3.5: - resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} - engines: {node: '>= 0.4'} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - function.prototype.name@1.1.8: - resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} - engines: {node: '>= 0.4'} - - functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - - generator-function@2.0.1: - resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} - engines: {node: '>= 0.4'} - gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - get-symbol-description@1.1.0: - resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} - engines: {node: '>= 0.4'} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -1126,56 +1085,34 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} - - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - - has-bigints@1.1.0: - resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} - engines: {node: '>= 0.4'} + globals@17.0.0: + resolution: {integrity: sha512-gv5BeD2EssA793rlFWVPMMCqefTlpusw6/2TbAVMy0FzcG8wKJn4O+NqJ4+XWmmwrayJgw5TzrmWjFgmz1XPqw==} + engines: {node: '>=18'} has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + html-encoding-sniffer@6.0.0: + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - has-proto@1.2.0: - resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} - engines: {node: '>= 0.4'} + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - has@1.0.4: - resolution: {integrity: sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==} - engines: {node: '>= 0.4.0'} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -1184,112 +1121,20 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - internal-slot@1.1.0: - resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} - engines: {node: '>= 0.4'} - - is-array-buffer@3.0.5: - resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} - engines: {node: '>= 0.4'} - - is-async-function@2.1.1: - resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} - engines: {node: '>= 0.4'} - - is-bigint@1.1.0: - resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} - engines: {node: '>= 0.4'} - - is-boolean-object@1.2.2: - resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} - engines: {node: '>= 0.4'} - - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} - engines: {node: '>= 0.4'} - - is-data-view@1.0.2: - resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} - engines: {node: '>= 0.4'} - - is-date-object@1.1.0: - resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} - engines: {node: '>= 0.4'} + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-finalizationregistry@1.1.1: - resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} - engines: {node: '>= 0.4'} - - is-generator-function@1.1.2: - resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} - engines: {node: '>= 0.4'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-map@2.0.3: - resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} - engines: {node: '>= 0.4'} - - is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} - - is-number-object@1.1.1: - resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} - engines: {node: '>= 0.4'} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-regex@1.2.1: - resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} - engines: {node: '>= 0.4'} - - is-set@2.0.3: - resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} - engines: {node: '>= 0.4'} - - is-shared-array-buffer@1.0.4: - resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} - engines: {node: '>= 0.4'} - - is-string@1.1.1: - resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} - engines: {node: '>= 0.4'} - - is-symbol@1.1.1: - resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} - engines: {node: '>= 0.4'} - - is-typed-array@1.1.15: - resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} - engines: {node: '>= 0.4'} - - is-weakmap@2.0.2: - resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} - engines: {node: '>= 0.4'} - - is-weakref@1.1.1: - resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} - engines: {node: '>= 0.4'} - - is-weakset@2.0.4: - resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} - engines: {node: '>= 0.4'} - - isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -1301,9 +1146,14 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true - jsdoc-type-pratt-parser@4.0.0: - resolution: {integrity: sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==} - engines: {node: '>=12.0.0'} + jsdom@27.4.0: + resolution: {integrity: sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} @@ -1319,10 +1169,6 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true - json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -1342,26 +1188,33 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} + mdn-data@2.12.2: + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -1371,47 +1224,19 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - natural-compare-lite@1.4.0: - resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} - natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - object.assign@4.1.7: - resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} - engines: {node: '>= 0.4'} - - object.fromentries@2.0.8: - resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} - engines: {node: '>= 0.4'} - - object.groupby@1.0.3: - resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} - engines: {node: '>= 0.4'} - - object.values@1.2.1: - resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} - engines: {node: '>= 0.4'} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} - own-keys@1.0.1: - resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} - engines: {node: '>= 0.4'} - p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -1424,6 +1249,9 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse5@8.0.0: + resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -1432,28 +1260,16 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - picomatch@4.0.3: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} - possible-typed-array-names@1.1.0: - resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} - engines: {node: '>= 0.4'} - postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -1462,27 +1278,27 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier-linter-helpers@1.0.1: - resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} - engines: {node: '>=6.0.0'} - prettier@3.8.1: resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} engines: {node: '>=14'} hasBin: true + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - react-dom@19.2.3: resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} peerDependencies: react: ^19.2.3 + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-refresh@0.17.0: resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} @@ -1491,46 +1307,26 @@ packages: resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} engines: {node: '>=0.10.0'} - reflect.getprototypeof@1.0.10: - resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} - engines: {node: '>= 0.4'} + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} - regexp.prototype.flags@1.5.4: - resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} - engines: {node: '>= 0.4'} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - resolve@1.22.11: - resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} - engines: {node: '>= 0.4'} - hasBin: true - - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.55.3: resolution: {integrity: sha512-y9yUpfQvetAjiDLtNMf1hL9NXchIJgWt6zIKeoB+tCd3npX08Eqfzg60V9DhIGVMtQ0AlMkFw5xa+AQ37zxnAA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - safe-array-concat@1.1.3: - resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} - engines: {node: '>=0.4'} - - safe-push-apply@1.0.0: - resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} - engines: {node: '>= 0.4'} - - safe-regex-test@1.1.0: - resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} - engines: {node: '>= 0.4'} + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} @@ -1544,18 +1340,6 @@ packages: engines: {node: '>=10'} hasBin: true - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} - - set-proto@1.0.0: - resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} - engines: {node: '>= 0.4'} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1564,58 +1348,22 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} - spdx-license-ids@3.0.22: - resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} - - stop-iteration-iterator@1.1.0: - resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} - engines: {node: '>= 0.4'} - - string.prototype.trim@1.2.10: - resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} - engines: {node: '>= 0.4'} - - string.prototype.trimend@1.0.9: - resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} - engines: {node: '>= 0.4'} - - string.prototype.trimstart@1.0.8: - resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} - engines: {node: '>= 0.4'} - - strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} @@ -1625,66 +1373,61 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - synckit@0.8.8: - resolution: {integrity: sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==} - engines: {node: ^14.18.0 || >=16.0.0} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + engines: {node: '>=14.0.0'} - tsconfig-paths@3.15.0: - resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tldts-core@7.0.19: + resolution: {integrity: sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==} - tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + tldts@7.0.19: + resolution: {integrity: sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==} + hasBin: true - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tough-cookie@6.0.0: + resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} + engines: {node: '>=16'} - tsutils@3.21.0: - resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} - engines: {node: '>= 6'} + tr46@6.0.0: + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} + + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} + engines: {node: '>=18.12'} peerDependencies: - typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + typescript: '>=4.8.4' type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - typed-array-buffer@1.0.3: - resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} - engines: {node: '>= 0.4'} - - typed-array-byte-length@1.0.3: - resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} - engines: {node: '>= 0.4'} - - typed-array-byte-offset@1.0.4: - resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} - engines: {node: '>= 0.4'} - - typed-array-length@1.0.7: - resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} - engines: {node: '>= 0.4'} + typescript-eslint@8.53.1: + resolution: {integrity: sha512-gB+EVQfP5RDElh9ittfXlhZJdjSU4jUSTyE2+ia8CYyNvet4ElfaLlAIqDvQV9JPknKx0jQH1racTYe/4LaLSg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' typescript@5.8.3: resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} hasBin: true - unbox-primitive@1.1.0: - resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} - engines: {node: '>= 0.4'} - update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -1734,31 +1477,93 @@ packages: yaml: optional: true - which-boxed-primitive@1.1.1: - resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} - engines: {node: '>= 0.4'} + vitest@4.0.17: + resolution: {integrity: sha512-FQMeF0DJdWY0iOnbv466n/0BudNdKj1l5jYgl5JVTwjSsZSlqyXFt/9+1sEyhR6CLowbZpV7O1sCHrzBhucKKg==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.17 + '@vitest/browser-preview': 4.0.17 + '@vitest/browser-webdriverio': 4.0.17 + '@vitest/ui': 4.0.17 + happy-dom: '*' + jsdom: '*' + 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/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true - which-builtin-type@1.2.1: - resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} - engines: {node: '>= 0.4'} + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} - which-collection@1.0.2: - resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} - engines: {node: '>= 0.4'} + webidl-conversions@8.0.1: + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + engines: {node: '>=20'} - which-typed-array@1.1.20: - resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} - engines: {node: '>= 0.4'} + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-mimetype@5.0.0: + resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} + engines: {node: '>=20'} + + whatwg-url@15.1.0: + resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==} + engines: {node: '>=20'} which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + ws@8.19.0: + resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} + 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 + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -1768,6 +1573,28 @@ packages: snapshots: + '@acemir/cssom@0.9.31': {} + + '@adobe/css-tools@4.4.4': {} + + '@asamuzakjp/css-color@4.1.1': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + lru-cache: 11.2.4 + + '@asamuzakjp/dom-selector@6.7.6': + dependencies: + '@asamuzakjp/nwsapi': 2.3.9 + bidi-js: 1.0.3 + css-tree: 3.1.0 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.2.4 + + '@asamuzakjp/nwsapi@2.3.9': {} + '@babel/code-frame@7.28.6': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -1857,6 +1684,8 @@ snapshots: '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 + '@babel/runtime@7.28.6': {} + '@babel/template@7.28.6': dependencies: '@babel/code-frame': 7.28.6 @@ -1880,11 +1709,27 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@es-joy/jsdoccomment@0.37.1': + '@csstools/color-helpers@5.1.0': {} + + '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': dependencies: - comment-parser: 1.3.1 - esquery: 1.7.0 - jsdoc-type-pratt-parser: 4.0.0 + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/color-helpers': 5.1.0 + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-syntax-patches-for-csstree@1.0.25': {} + + '@csstools/css-tokenizer@3.0.4': {} '@esbuild/aix-ppc64@0.27.2': optional: true @@ -2010,6 +1855,8 @@ snapshots: '@eslint/core': 0.13.0 levn: 0.4.1 + '@exodus/bytes@1.9.0': {} + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -2040,42 +1887,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@nhcarrigan/eslint-config@2.0.0(eslint@9.19.0)(prettier@3.8.1)(typescript@5.8.3)': - dependencies: - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.19.0)(typescript@5.8.3))(eslint@9.19.0)(typescript@5.8.3) - '@typescript-eslint/parser': 5.62.0(eslint@9.19.0)(typescript@5.8.3) - eslint: 9.19.0 - eslint-config-prettier: 9.0.0(eslint@9.19.0) - eslint-plugin-import: 2.28.0(@typescript-eslint/parser@5.62.0(eslint@9.19.0)(typescript@5.8.3))(eslint@9.19.0) - eslint-plugin-jsdoc: 41.1.2(eslint@9.19.0) - eslint-plugin-no-only-tests: 3.1.0 - eslint-plugin-prettier: 5.0.0(eslint-config-prettier@9.0.0(eslint@9.19.0))(eslint@9.19.0)(prettier@3.8.1) - transitivePeerDependencies: - - '@types/eslint' - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - prettier - - supports-color - - typescript - - '@nhcarrigan/typescript-config@4.0.0(typescript@5.8.3)': - dependencies: - typescript: 5.8.3 - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.1 - - '@pkgr/core@0.1.2': {} - '@rolldown/pluginutils@1.0.0-beta.27': {} '@rollup/rollup-android-arm-eabi@4.55.3': @@ -2153,6 +1964,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.55.3': optional: true + '@standard-schema/spec@1.1.0': {} + '@tauri-apps/api@2.9.1': {} '@tauri-apps/cli-darwin-arm64@2.9.6': @@ -2206,6 +2019,38 @@ snapshots: dependencies: '@tauri-apps/api': 2.9.1 + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.28.6 + '@babel/runtime': 7.28.6 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@6.9.1': + dependencies: + '@adobe/css-tools': 4.4.4 + aria-query: 5.3.2 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + picocolors: 1.1.1 + redent: 3.0.0 + + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.6 + '@testing-library/dom': 10.4.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.9 + '@types/react-dom': 19.2.3(@types/react@19.2.9) + + '@types/aria-query@5.0.4': {} + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.28.6 @@ -2227,12 +2072,17 @@ snapshots: dependencies: '@babel/types': 7.28.6 + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + '@types/estree@1.0.8': {} '@types/json-schema@7.0.15': {} - '@types/json5@0.0.29': {} - '@types/react-dom@19.2.3(@types/react@19.2.9)': dependencies: '@types/react': 19.2.9 @@ -2241,91 +2091,96 @@ snapshots: dependencies: csstype: 3.2.3 - '@types/semver@7.7.1': {} - - '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.19.0)(typescript@5.8.3))(eslint@9.19.0)(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.53.1(@typescript-eslint/parser@8.53.1(eslint@9.19.0)(typescript@5.8.3))(eslint@9.19.0)(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 5.62.0(eslint@9.19.0)(typescript@5.8.3) - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/type-utils': 5.62.0(eslint@9.19.0)(typescript@5.8.3) - '@typescript-eslint/utils': 5.62.0(eslint@9.19.0)(typescript@5.8.3) + '@typescript-eslint/parser': 8.53.1(eslint@9.19.0)(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.53.1 + '@typescript-eslint/type-utils': 8.53.1(eslint@9.19.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.53.1(eslint@9.19.0)(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.53.1 + eslint: 9.19.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.4.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.53.1(eslint@9.19.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.53.1 + '@typescript-eslint/types': 8.53.1 + '@typescript-eslint/typescript-estree': 8.53.1(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.53.1 debug: 4.4.3 eslint: 9.19.0 - graphemer: 1.4.0 - ignore: 5.3.2 - natural-compare-lite: 1.4.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.53.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.53.1(typescript@5.8.3) + '@typescript-eslint/types': 8.53.1 + debug: 4.4.3 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.53.1': + dependencies: + '@typescript-eslint/types': 8.53.1 + '@typescript-eslint/visitor-keys': 8.53.1 + + '@typescript-eslint/tsconfig-utils@8.53.1(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + + '@typescript-eslint/type-utils@8.53.1(eslint@9.19.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/types': 8.53.1 + '@typescript-eslint/typescript-estree': 8.53.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.53.1(eslint@9.19.0)(typescript@5.8.3) + debug: 4.4.3 + eslint: 9.19.0 + ts-api-utils: 2.4.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.53.1': {} + + '@typescript-eslint/typescript-estree@8.53.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/project-service': 8.53.1(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.53.1(typescript@5.8.3) + '@typescript-eslint/types': 8.53.1 + '@typescript-eslint/visitor-keys': 8.53.1 + debug: 4.4.3 + minimatch: 9.0.5 semver: 7.7.3 - tsutils: 3.21.0(typescript@5.8.3) - optionalDependencies: + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@5.62.0(eslint@9.19.0)(typescript@5.8.3)': - dependencies: - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.8.3) - debug: 4.4.3 - eslint: 9.19.0 - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@5.62.0': - dependencies: - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/visitor-keys': 5.62.0 - - '@typescript-eslint/type-utils@5.62.0(eslint@9.19.0)(typescript@5.8.3)': - dependencies: - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.8.3) - '@typescript-eslint/utils': 5.62.0(eslint@9.19.0)(typescript@5.8.3) - debug: 4.4.3 - eslint: 9.19.0 - tsutils: 3.21.0(typescript@5.8.3) - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/types@5.62.0': {} - - '@typescript-eslint/typescript-estree@5.62.0(typescript@5.8.3)': - dependencies: - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.4.3 - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.7.3 - tsutils: 3.21.0(typescript@5.8.3) - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@5.62.0(eslint@9.19.0)(typescript@5.8.3)': + '@typescript-eslint/utils@8.53.1(eslint@9.19.0)(typescript@5.8.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.19.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.7.1 - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.53.1 + '@typescript-eslint/types': 8.53.1 + '@typescript-eslint/typescript-estree': 8.53.1(typescript@5.8.3) eslint: 9.19.0 - eslint-scope: 5.1.1 - semver: 7.7.3 + typescript: 5.8.3 transitivePeerDependencies: - supports-color - - typescript - '@typescript-eslint/visitor-keys@5.62.0': + '@typescript-eslint/visitor-keys@8.53.1': dependencies: - '@typescript-eslint/types': 5.62.0 - eslint-visitor-keys: 3.4.3 + '@typescript-eslint/types': 8.53.1 + eslint-visitor-keys: 4.2.1 '@vitejs/plugin-react@4.7.0(vite@7.3.1)': dependencies: @@ -2339,12 +2194,53 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitest/expect@4.0.17': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.0.17 + '@vitest/utils': 4.0.17 + chai: 6.2.2 + tinyrainbow: 3.0.3 + + '@vitest/mocker@4.0.17(vite@7.3.1)': + dependencies: + '@vitest/spy': 4.0.17 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.1 + + '@vitest/pretty-format@4.0.17': + dependencies: + tinyrainbow: 3.0.3 + + '@vitest/runner@4.0.17': + dependencies: + '@vitest/utils': 4.0.17 + pathe: 2.0.3 + + '@vitest/snapshot@4.0.17': + dependencies: + '@vitest/pretty-format': 4.0.17 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.0.17': {} + + '@vitest/utils@4.0.17': + dependencies: + '@vitest/pretty-format': 4.0.17 + tinyrainbow: 3.0.3 + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 acorn@8.15.0: {} + agent-base@7.1.4: {} + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -2352,84 +2248,40 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ansi-regex@5.0.1: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - are-docs-informative@0.0.2: {} + ansi-styles@5.2.0: {} argparse@2.0.1: {} - array-buffer-byte-length@1.0.2: + aria-query@5.3.0: dependencies: - call-bound: 1.0.4 - is-array-buffer: 3.0.5 + dequal: 2.0.3 - array-includes@3.1.9: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - is-string: 1.1.1 - math-intrinsics: 1.1.0 + aria-query@5.3.2: {} - array-union@2.1.0: {} - - array.prototype.findlastindex@1.2.6: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - es-shim-unscopables: 1.1.0 - - array.prototype.flat@1.3.3: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-shim-unscopables: 1.1.0 - - array.prototype.flatmap@1.3.3: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-shim-unscopables: 1.1.0 - - arraybuffer.prototype.slice@1.0.4: - dependencies: - array-buffer-byte-length: 1.0.2 - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - is-array-buffer: 3.0.5 - - async-function@1.0.0: {} - - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.1.0 + assertion-error@2.0.1: {} balanced-match@1.0.2: {} baseline-browser-mapping@2.9.17: {} + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - braces@3.0.3: + brace-expansion@2.0.2: dependencies: - fill-range: 7.1.1 + balanced-match: 1.0.2 browserslist@4.28.1: dependencies: @@ -2439,27 +2291,12 @@ snapshots: node-releases: 2.0.27 update-browserslist-db: 1.2.3(browserslist@4.28.1) - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bind@1.0.8: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - get-intrinsic: 1.3.0 - set-function-length: 1.2.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - callsites@3.1.0: {} caniuse-lite@1.0.30001765: {} + chai@6.2.2: {} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -2471,8 +2308,6 @@ snapshots: color-name@1.1.4: {} - comment-parser@1.3.1: {} - concat-map@0.0.1: {} convert-source-map@2.0.0: {} @@ -2483,145 +2318,46 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + css-tree@3.1.0: + dependencies: + mdn-data: 2.12.2 + source-map-js: 1.2.1 + + css.escape@1.5.1: {} + + cssstyle@5.3.7: + dependencies: + '@asamuzakjp/css-color': 4.1.1 + '@csstools/css-syntax-patches-for-csstree': 1.0.25 + css-tree: 3.1.0 + lru-cache: 11.2.4 + csstype@3.2.3: {} - data-view-buffer@1.0.2: + data-urls@6.0.1: dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - data-view-byte-length@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - data-view-byte-offset@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - debug@3.2.7: - dependencies: - ms: 2.1.3 + whatwg-mimetype: 5.0.0 + whatwg-url: 15.1.0 debug@4.4.3: dependencies: ms: 2.1.3 + decimal.js@10.6.0: {} + deep-is@0.1.4: {} - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 + dequal@2.0.3: {} - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 + dom-accessibility-api@0.5.16: {} - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - - doctrine@2.1.0: - dependencies: - esutils: 2.0.3 - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 + dom-accessibility-api@0.6.3: {} electron-to-chromium@1.5.267: {} - es-abstract@1.24.1: - dependencies: - array-buffer-byte-length: 1.0.2 - arraybuffer.prototype.slice: 1.0.4 - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - data-view-buffer: 1.0.2 - data-view-byte-length: 1.0.2 - data-view-byte-offset: 1.0.1 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - es-set-tostringtag: 2.1.0 - es-to-primitive: 1.3.0 - function.prototype.name: 1.1.8 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - get-symbol-description: 1.1.0 - globalthis: 1.0.4 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - has-proto: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - internal-slot: 1.1.0 - is-array-buffer: 3.0.5 - is-callable: 1.2.7 - is-data-view: 1.0.2 - is-negative-zero: 2.0.3 - is-regex: 1.2.1 - is-set: 2.0.3 - is-shared-array-buffer: 1.0.4 - is-string: 1.1.1 - is-typed-array: 1.1.15 - is-weakref: 1.1.1 - math-intrinsics: 1.1.0 - object-inspect: 1.13.4 - object-keys: 1.1.1 - object.assign: 4.1.7 - own-keys: 1.0.1 - regexp.prototype.flags: 1.5.4 - safe-array-concat: 1.1.3 - safe-push-apply: 1.0.0 - safe-regex-test: 1.1.0 - set-proto: 1.0.0 - stop-iteration-iterator: 1.1.0 - string.prototype.trim: 1.2.10 - string.prototype.trimend: 1.0.9 - string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.3 - typed-array-byte-length: 1.0.3 - typed-array-byte-offset: 1.0.4 - typed-array-length: 1.0.7 - unbox-primitive: 1.1.0 - which-typed-array: 1.1.20 + entities@6.0.1: {} - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - es-shim-unscopables@1.1.0: - dependencies: - hasown: 2.0.2 - - es-to-primitive@1.3.0: - dependencies: - is-callable: 1.2.7 - is-date-object: 1.1.0 - is-symbol: 1.1.1 + es-module-lexer@1.7.0: {} esbuild@0.27.2: optionalDependencies: @@ -2656,85 +2392,17 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-prettier@9.0.0(eslint@9.19.0): + eslint-config-prettier@10.1.8(eslint@9.19.0): dependencies: eslint: 9.19.0 - eslint-import-resolver-node@0.3.9: - dependencies: - debug: 3.2.7 - is-core-module: 2.16.1 - resolve: 1.22.11 - transitivePeerDependencies: - - supports-color - - eslint-module-utils@2.12.1(@typescript-eslint/parser@5.62.0(eslint@9.19.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.19.0): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 5.62.0(eslint@9.19.0)(typescript@5.8.3) - eslint: 9.19.0 - eslint-import-resolver-node: 0.3.9 - transitivePeerDependencies: - - supports-color - - eslint-plugin-import@2.28.0(@typescript-eslint/parser@5.62.0(eslint@9.19.0)(typescript@5.8.3))(eslint@9.19.0): - dependencies: - array-includes: 3.1.9 - array.prototype.findlastindex: 1.2.6 - array.prototype.flat: 1.3.3 - array.prototype.flatmap: 1.3.3 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 9.19.0 - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@5.62.0(eslint@9.19.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.19.0) - has: 1.0.4 - is-core-module: 2.16.1 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.1 - resolve: 1.22.11 - semver: 6.3.1 - tsconfig-paths: 3.15.0 - optionalDependencies: - '@typescript-eslint/parser': 5.62.0(eslint@9.19.0)(typescript@5.8.3) - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - - eslint-plugin-jsdoc@41.1.2(eslint@9.19.0): - dependencies: - '@es-joy/jsdoccomment': 0.37.1 - are-docs-informative: 0.0.2 - comment-parser: 1.3.1 - debug: 4.4.3 - escape-string-regexp: 4.0.0 - eslint: 9.19.0 - esquery: 1.7.0 - semver: 7.7.3 - spdx-expression-parse: 3.0.1 - transitivePeerDependencies: - - supports-color - - eslint-plugin-no-only-tests@3.1.0: {} - - eslint-plugin-prettier@5.0.0(eslint-config-prettier@9.0.0(eslint@9.19.0))(eslint@9.19.0)(prettier@3.8.1): + eslint-plugin-react-hooks@5.2.0(eslint@9.19.0): dependencies: eslint: 9.19.0 - prettier: 3.8.1 - prettier-linter-helpers: 1.0.1 - synckit: 0.8.8 - optionalDependencies: - eslint-config-prettier: 9.0.0(eslint@9.19.0) - eslint-scope@5.1.1: + eslint-plugin-react-refresh@0.4.26(eslint@9.19.0): dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 + eslint: 9.19.0 eslint-scope@8.4.0: dependencies: @@ -2798,32 +2466,22 @@ snapshots: dependencies: estraverse: 5.3.0 - estraverse@4.3.0: {} - estraverse@5.3.0: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + esutils@2.0.3: {} + expect-type@1.3.0: {} + fast-deep-equal@3.1.3: {} - fast-diff@1.3.0: {} - - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} - fastq@1.20.1: - dependencies: - reusify: 1.1.0 - fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 @@ -2832,10 +2490,6 @@ snapshots: dependencies: flat-cache: 4.0.1 - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -2848,108 +2502,45 @@ snapshots: flatted@3.3.3: {} - for-each@0.3.5: - dependencies: - is-callable: 1.2.7 - fsevents@2.3.3: optional: true - function-bind@1.1.2: {} - - function.prototype.name@1.1.8: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - functions-have-names: 1.2.3 - hasown: 2.0.2 - is-callable: 1.2.7 - - functions-have-names@1.2.3: {} - - generator-function@2.0.1: {} - gensync@1.0.0-beta.2: {} - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - - get-symbol-description@1.1.0: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - glob-parent@6.0.2: dependencies: is-glob: 4.0.3 globals@14.0.0: {} - globalthis@1.0.4: - dependencies: - define-properties: 1.2.1 - gopd: 1.2.0 - - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - - gopd@1.2.0: {} - - graphemer@1.4.0: {} - - has-bigints@1.1.0: {} + globals@17.0.0: {} has-flag@4.0.0: {} - has-property-descriptors@1.0.2: + html-encoding-sniffer@6.0.0: dependencies: - es-define-property: 1.0.1 + '@exodus/bytes': 1.9.0 + transitivePeerDependencies: + - '@noble/hashes' - has-proto@1.2.0: + http-proxy-agent@7.0.2: dependencies: - dunder-proto: 1.0.1 + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: + https-proxy-agent@7.0.6: dependencies: - has-symbols: 1.1.0 - - has@1.0.4: {} - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color ignore@5.3.2: {} + ignore@7.0.5: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -2957,121 +2548,15 @@ snapshots: imurmurhash@0.1.4: {} - internal-slot@1.1.0: - dependencies: - es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.1.0 - - is-array-buffer@3.0.5: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - - is-async-function@2.1.1: - dependencies: - async-function: 1.0.0 - call-bound: 1.0.4 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - - is-bigint@1.1.0: - dependencies: - has-bigints: 1.1.0 - - is-boolean-object@1.2.2: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-callable@1.2.7: {} - - is-core-module@2.16.1: - dependencies: - hasown: 2.0.2 - - is-data-view@1.0.2: - dependencies: - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - is-typed-array: 1.1.15 - - is-date-object@1.1.0: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 + indent-string@4.0.0: {} is-extglob@2.1.1: {} - is-finalizationregistry@1.1.1: - dependencies: - call-bound: 1.0.4 - - is-generator-function@1.1.2: - dependencies: - call-bound: 1.0.4 - generator-function: 2.0.1 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - is-map@2.0.3: {} - - is-negative-zero@2.0.3: {} - - is-number-object@1.1.1: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-number@7.0.0: {} - - is-regex@1.2.1: - dependencies: - call-bound: 1.0.4 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - is-set@2.0.3: {} - - is-shared-array-buffer@1.0.4: - dependencies: - call-bound: 1.0.4 - - is-string@1.1.1: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-symbol@1.1.1: - dependencies: - call-bound: 1.0.4 - has-symbols: 1.1.0 - safe-regex-test: 1.1.0 - - is-typed-array@1.1.15: - dependencies: - which-typed-array: 1.1.20 - - is-weakmap@2.0.2: {} - - is-weakref@1.1.1: - dependencies: - call-bound: 1.0.4 - - is-weakset@2.0.4: - dependencies: - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - - isarray@2.0.5: {} + is-potential-custom-element-name@1.0.1: {} isexe@2.0.0: {} @@ -3081,7 +2566,33 @@ snapshots: dependencies: argparse: 2.0.1 - jsdoc-type-pratt-parser@4.0.0: {} + jsdom@27.4.0: + dependencies: + '@acemir/cssom': 0.9.31 + '@asamuzakjp/dom-selector': 6.7.6 + '@exodus/bytes': 1.9.0 + cssstyle: 5.3.7 + data-urls: 6.0.1 + decimal.js: 10.6.0 + html-encoding-sniffer: 6.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + parse5: 8.0.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 6.0.0 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 8.0.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 15.1.0 + ws: 8.19.0 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - '@noble/hashes' + - bufferutil + - supports-color + - utf-8-validate jsesc@3.1.0: {} @@ -3091,10 +2602,6 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} - json5@1.0.2: - dependencies: - minimist: 1.2.8 - json5@2.2.3: {} keyv@4.5.4: @@ -3112,67 +2619,39 @@ snapshots: lodash.merge@4.6.2: {} + lru-cache@11.2.4: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 - math-intrinsics@1.1.0: {} + lz-string@1.5.0: {} - merge2@1.4.1: {} - - micromatch@4.0.8: + magic-string@0.30.21: dependencies: - braces: 3.0.3 - picomatch: 2.3.1 + '@jridgewell/sourcemap-codec': 1.5.5 + + mdn-data@2.12.2: {} + + min-indent@1.0.1: {} minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 - minimist@1.2.8: {} + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 ms@2.1.3: {} nanoid@3.3.11: {} - natural-compare-lite@1.4.0: {} - natural-compare@1.4.0: {} node-releases@2.0.27: {} - object-inspect@1.13.4: {} - - object-keys@1.1.1: {} - - object.assign@4.1.7: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - has-symbols: 1.1.0 - object-keys: 1.1.1 - - object.fromentries@2.0.8: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-object-atoms: 1.1.1 - - object.groupby@1.0.3: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.1 - - object.values@1.2.1: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 + obug@2.1.1: {} optionator@0.9.4: dependencies: @@ -3183,12 +2662,6 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 - own-keys@1.0.1: - dependencies: - get-intrinsic: 1.3.0 - object-keys: 1.1.1 - safe-push-apply: 1.0.0 - p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -3201,22 +2674,20 @@ snapshots: dependencies: callsites: 3.1.0 + parse5@8.0.0: + dependencies: + entities: 6.0.1 + path-exists@4.0.0: {} path-key@3.1.1: {} - path-parse@1.0.7: {} - - path-type@4.0.0: {} + pathe@2.0.3: {} picocolors@1.1.1: {} - picomatch@2.3.1: {} - picomatch@4.0.3: {} - possible-typed-array-names@1.1.0: {} - postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -3225,55 +2696,36 @@ snapshots: prelude-ls@1.2.1: {} - prettier-linter-helpers@1.0.1: - dependencies: - fast-diff: 1.3.0 - prettier@3.8.1: {} - punycode@2.3.1: {} + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 - queue-microtask@1.2.3: {} + punycode@2.3.1: {} react-dom@19.2.3(react@19.2.3): dependencies: react: 19.2.3 scheduler: 0.27.0 + react-is@17.0.2: {} + react-refresh@0.17.0: {} react@19.2.3: {} - reflect.getprototypeof@1.0.10: + redent@3.0.0: dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - which-builtin-type: 1.2.1 + indent-string: 4.0.0 + strip-indent: 3.0.0 - regexp.prototype.flags@1.5.4: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-errors: 1.3.0 - get-proto: 1.0.1 - gopd: 1.2.0 - set-function-name: 2.0.2 + require-from-string@2.0.2: {} resolve-from@4.0.0: {} - resolve@1.22.11: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - reusify@1.1.0: {} - rollup@4.55.3: dependencies: '@types/estree': 1.0.8 @@ -3305,28 +2757,9 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.55.3 fsevents: 2.3.3 - run-parallel@1.2.0: + saxes@6.0.0: dependencies: - queue-microtask: 1.2.3 - - safe-array-concat@1.1.3: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - has-symbols: 1.1.0 - isarray: 2.0.5 - - safe-push-apply@1.0.0: - dependencies: - es-errors: 1.3.0 - isarray: 2.0.5 - - safe-regex-test@1.1.0: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-regex: 1.2.1 + xmlchars: 2.2.0 scheduler@0.27.0: {} @@ -3334,104 +2767,23 @@ snapshots: semver@7.7.3: {} - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - - set-function-name@2.0.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 - - set-proto@1.0.0: - dependencies: - dunder-proto: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 shebang-regex@3.0.0: {} - side-channel-list@1.0.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.0 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - - slash@3.0.0: {} + siginfo@2.0.0: {} source-map-js@1.2.1: {} - spdx-exceptions@2.5.0: {} + stackback@0.0.2: {} - spdx-expression-parse@3.0.1: + std-env@3.10.0: {} + + strip-indent@3.0.0: dependencies: - spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.22 - - spdx-license-ids@3.0.22: {} - - stop-iteration-iterator@1.1.0: - dependencies: - es-errors: 1.3.0 - internal-slot: 1.1.0 - - string.prototype.trim@1.2.10: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-data-property: 1.1.4 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-object-atoms: 1.1.1 - has-property-descriptors: 1.0.2 - - string.prototype.trimend@1.0.9: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - - string.prototype.trimstart@1.0.8: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - - strip-bom@3.0.0: {} + min-indent: 1.0.1 strip-json-comments@3.1.1: {} @@ -3439,84 +2791,54 @@ snapshots: dependencies: has-flag: 4.0.0 - supports-preserve-symlinks-flag@1.0.0: {} + symbol-tree@3.2.4: {} - synckit@0.8.8: - dependencies: - '@pkgr/core': 0.1.2 - tslib: 2.8.1 + tinybench@2.9.0: {} + + tinyexec@1.0.2: {} tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 - to-regex-range@5.0.1: + tinyrainbow@3.0.3: {} + + tldts-core@7.0.19: {} + + tldts@7.0.19: dependencies: - is-number: 7.0.0 + tldts-core: 7.0.19 - tsconfig-paths@3.15.0: + tough-cookie@6.0.0: dependencies: - '@types/json5': 0.0.29 - json5: 1.0.2 - minimist: 1.2.8 - strip-bom: 3.0.0 + tldts: 7.0.19 - tslib@1.14.1: {} - - tslib@2.8.1: {} - - tsutils@3.21.0(typescript@5.8.3): + tr46@6.0.0: + dependencies: + punycode: 2.3.1 + + ts-api-utils@2.4.0(typescript@5.8.3): dependencies: - tslib: 1.14.1 typescript: 5.8.3 type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - typed-array-buffer@1.0.3: + typescript-eslint@8.53.1(eslint@9.19.0)(typescript@5.8.3): dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-typed-array: 1.1.15 - - typed-array-byte-length@1.0.3: - dependencies: - call-bind: 1.0.8 - for-each: 0.3.5 - gopd: 1.2.0 - has-proto: 1.2.0 - is-typed-array: 1.1.15 - - typed-array-byte-offset@1.0.4: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - for-each: 0.3.5 - gopd: 1.2.0 - has-proto: 1.2.0 - is-typed-array: 1.1.15 - reflect.getprototypeof: 1.0.10 - - typed-array-length@1.0.7: - dependencies: - call-bind: 1.0.8 - for-each: 0.3.5 - gopd: 1.2.0 - is-typed-array: 1.1.15 - possible-typed-array-names: 1.1.0 - reflect.getprototypeof: 1.0.10 + '@typescript-eslint/eslint-plugin': 8.53.1(@typescript-eslint/parser@8.53.1(eslint@9.19.0)(typescript@5.8.3))(eslint@9.19.0)(typescript@5.8.3) + '@typescript-eslint/parser': 8.53.1(eslint@9.19.0)(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.53.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.53.1(eslint@9.19.0)(typescript@5.8.3) + eslint: 9.19.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color typescript@5.8.3: {} - unbox-primitive@1.1.0: - dependencies: - call-bound: 1.0.4 - has-bigints: 1.1.0 - has-symbols: 1.1.0 - which-boxed-primitive: 1.1.1 - update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: browserslist: 4.28.1 @@ -3538,53 +2860,75 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - which-boxed-primitive@1.1.1: + vitest@4.0.17(jsdom@27.4.0): dependencies: - is-bigint: 1.1.0 - is-boolean-object: 1.2.2 - is-number-object: 1.1.1 - is-string: 1.1.1 - is-symbol: 1.1.1 + '@vitest/expect': 4.0.17 + '@vitest/mocker': 4.0.17(vite@7.3.1) + '@vitest/pretty-format': 4.0.17 + '@vitest/runner': 4.0.17 + '@vitest/snapshot': 4.0.17 + '@vitest/spy': 4.0.17 + '@vitest/utils': 4.0.17 + es-module-lexer: 1.7.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: 3.10.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.3.1 + why-is-node-running: 2.3.0 + optionalDependencies: + jsdom: 27.4.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml - which-builtin-type@1.2.1: + w3c-xmlserializer@5.0.0: dependencies: - call-bound: 1.0.4 - function.prototype.name: 1.1.8 - has-tostringtag: 1.0.2 - is-async-function: 2.1.1 - is-date-object: 1.1.0 - is-finalizationregistry: 1.1.1 - is-generator-function: 1.1.2 - is-regex: 1.2.1 - is-weakref: 1.1.1 - isarray: 2.0.5 - which-boxed-primitive: 1.1.1 - which-collection: 1.0.2 - which-typed-array: 1.1.20 + xml-name-validator: 5.0.0 - which-collection@1.0.2: - dependencies: - is-map: 2.0.3 - is-set: 2.0.3 - is-weakmap: 2.0.2 - is-weakset: 2.0.4 + webidl-conversions@8.0.1: {} - which-typed-array@1.1.20: + whatwg-mimetype@4.0.0: {} + + whatwg-mimetype@5.0.0: {} + + whatwg-url@15.1.0: dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - for-each: 0.3.5 - get-proto: 1.0.1 - gopd: 1.2.0 - has-tostringtag: 1.0.2 + tr46: 6.0.0 + webidl-conversions: 8.0.1 which@2.0.2: dependencies: isexe: 2.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + word-wrap@1.2.5: {} + ws@8.19.0: {} + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + yallist@3.1.1: {} yocto-queue@0.1.0: {} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..5fadd86 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "chronara" +version = "0.1.0" +description = "Local meeting transcription and summarization app" +requires-python = ">=3.10" +dependencies = [ + "fastapi==0.115.6", + "uvicorn==0.34.0", + "whisperx==3.1.6", + "llama-cpp-python==0.3.4", + "pyaudio==0.2.14", + "numpy==1.26.4", + "torch==2.5.1", + "pydantic==2.10.4", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.ruff] +line-length = 100 +target-version = "py310" + +[tool.ruff.lint] +select = ["E", "F", "I", "N", "W", "B", "C90", "D"] +ignore = ["D100", "D101", "D102", "D103", "D104", "D105", "D106", "D107"] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..91718bc --- /dev/null +++ b/requirements.txt @@ -0,0 +1,10 @@ +fastapi==0.115.6 +uvicorn==0.134.0 +whisperx==3.1.6 +llama-cpp-python==0.3.4 +pyaudio==0.2.14 +numpy==1.26.4 +torch==2.5.1 +pydantic==2.10.4 +python-multipart==0.0.18 +websockets==14.1 \ No newline at end of file diff --git a/scripts/build_windows.py b/scripts/build_windows.py new file mode 100644 index 0000000..7ecd64c --- /dev/null +++ b/scripts/build_windows.py @@ -0,0 +1,128 @@ +"""Build script to create a Windows executable with bundled Python environment. + +This script should be run ON WINDOWS, not cross-compiled from Linux. +""" + +import shutil +import subprocess +import sys +from pathlib import Path + + +def main(): + """Create a Windows executable bundle for Chronara.""" + project_root = Path(__file__).parent.parent + dist_dir = project_root / "dist-bundle" + + print("๐Ÿ”จ Chronara Windows Build Script") + print("=" * 50) + print("\nโš ๏ธ NOTE: This script must be run on Windows!") + print(" Cross-compilation from Linux is not supported.\n") + + if sys.platform != "win32": + print("โŒ This script must be run on Windows.") + print(" Please run this on a Windows machine or in a Windows VM.") + sys.exit(1) + + # Clean previous builds + if dist_dir.exists(): + print("Cleaning previous build...") + shutil.rmtree(dist_dir) + + dist_dir.mkdir(exist_ok=True) + + # Step 1: Create Python bundle using PyInstaller + print("\n๐Ÿ“ฆ Creating Python bundle...") + + # Create a temporary spec file for PyInstaller + spec_content = f""" +# -*- mode: python ; coding: utf-8 -*- + +a = Analysis( + ['{project_root / "src" / "backend" / "main.py"}'], + pathex=['{project_root / "src"}'], + binaries=[], + datas=[ + ('{project_root / "models" / "*.gguf"}', 'models'), + ], + hiddenimports=['uvicorn', 'fastapi', 'whisperx', 'llama_cpp'], + hookspath=[], + hooksconfig={{}}, + runtime_hooks=[], + excludes=[], + noarchive=False, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.datas, + [], + name='chronara-backend', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=False, + upx_exclude=[], + runtime_tmpdir=None, + console=True, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +) +""" + + spec_path = project_root / "chronara-backend.spec" + spec_path.write_text(spec_content) + + try: + subprocess.run([ + sys.executable, + "-m", + "PyInstaller", + str(spec_path), + "--clean", + "--noconfirm", + "--distpath", str(dist_dir / "backend") + ], check=True) + except subprocess.CalledProcessError: + print("โš ๏ธ PyInstaller not found. Installing...") + subprocess.run([sys.executable, "-m", "pip", "install", "pyinstaller"], check=True) + subprocess.run([ + sys.executable, + "-m", + "PyInstaller", + str(spec_path), + "--clean", + "--noconfirm", + "--distpath", str(dist_dir / "backend") + ], check=True) + + # Clean up spec file + spec_path.unlink() + + # Step 2: Build Tauri app (creates NSIS installer on Windows) + print("\n๐Ÿฆ€ Building Tauri app with NSIS installer...") + subprocess.run( + "pnpm tauri build", + cwd=project_root, + check=True, + shell=True + ) + + print("\nโœ… Build complete!") + print("\nWindows installer (.exe) location:") + print(" src-tauri/target/release/bundle/nsis/Chronara_0.1.0_x64_en-US.exe") + print("\nThis installer includes:") + print(" - Tauri app with React frontend") + print(" - Python backend (bundled)") + print(" - Llama model files") + print(" - All dependencies") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/scripts/download_models.py b/scripts/download_models.py new file mode 100644 index 0000000..502f7aa --- /dev/null +++ b/scripts/download_models.py @@ -0,0 +1,90 @@ +"""Download required models for Chronara.""" + +import os +import sys +from pathlib import Path +from urllib.request import urlretrieve + +# Model download URLs +MODELS = { + "llama-3.2-1B": { + "url": "https://huggingface.co/bartowski/Llama-3.2-1B-Instruct-GGUF/resolve/main/Llama-3.2-1B-Instruct-Q4_K_M.gguf", + "filename": "llama-3.2-1B-instruct-Q4_K_M.gguf", + "size": "1.2GB", + }, + "llama-3.2-3B": { + "url": "https://huggingface.co/bartowski/Llama-3.2-3B-Instruct-GGUF/resolve/main/Llama-3.2-3B-Instruct-Q4_K_M.gguf", + "filename": "llama-3.2-3B-instruct-Q4_K_M.gguf", + "size": "2.5GB", + }, +} + + +def download_with_progress(url: str, filepath: Path): + """Download file with progress bar.""" + + def _progress(block_num, block_size, total_size): + downloaded = block_num * block_size + percent = min(downloaded * 100 / total_size, 100) + progress = int(50 * percent / 100) + sys.stdout.write( + f"\r[{'=' * progress}{' ' * (50 - progress)}] {percent:.1f}%" + ) + sys.stdout.flush() + + print(f"Downloading {filepath.name}...") + urlretrieve(url, filepath, reporthook=_progress) + print() # New line after progress bar + + +def main(): + """Download all required models.""" + # Get project root + project_root = Path(__file__).parent.parent + models_dir = project_root / "models" + models_dir.mkdir(exist_ok=True) + + print("๐Ÿค– Chronara Model Downloader") + print("=" * 50) + + # Ask which model to download + print("\nWhich Llama model would you like to use?") + print("1. Llama 3.2 1B (1.2GB) - Faster, good for basic summaries") + print("2. Llama 3.2 3B (2.5GB) - Better quality summaries") + print("3. Both models") + + choice = input("\nEnter your choice (1/2/3): ").strip() + + models_to_download = [] + if choice == "1": + models_to_download = ["llama-3.2-1B"] + elif choice == "2": + models_to_download = ["llama-3.2-3B"] + elif choice == "3": + models_to_download = ["llama-3.2-1B", "llama-3.2-3B"] + else: + print("Invalid choice!") + return + + # Download selected models + for model_name in models_to_download: + model_info = MODELS[model_name] + filepath = models_dir / model_info["filename"] + + if filepath.exists(): + print(f"\nโœ“ {model_name} already downloaded") + continue + + print(f"\n๐Ÿ“ฅ Downloading {model_name} ({model_info['size']})...") + try: + download_with_progress(model_info["url"], filepath) + print(f"โœ“ Downloaded {model_name} successfully!") + except Exception as e: + print(f"โœ— Failed to download {model_name}: {e}") + + print("\nโœจ Model download complete!") + print("\nNote: WhisperX models will be downloaded automatically on first run.") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/scripts/start_backend.py b/scripts/start_backend.py new file mode 100644 index 0000000..1cb674e --- /dev/null +++ b/scripts/start_backend.py @@ -0,0 +1,24 @@ +"""Start the Chronara backend server.""" + +import os +import sys +import subprocess +from pathlib import Path + +# Add src directory to Python path +src_path = Path(__file__).parent.parent / "src" +sys.path.insert(0, str(src_path)) + +# Set environment for bundled deployment +os.environ["CHRONARA_BUNDLED"] = "1" + +# Start the FastAPI server +subprocess.run([ + sys.executable, + "-m", + "uvicorn", + "backend.main:app", + "--host", "127.0.0.1", + "--port", "8000", + "--reload" if os.environ.get("CHRONARA_DEV") else "" +]) \ No newline at end of file diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 4cdbf49..f778364 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -3,8 +3,5 @@ "identifier": "default", "description": "Capability for the main window", "windows": ["main"], - "permissions": [ - "core:default", - "opener:default" - ] + "permissions": ["core:default", "opener:default"] } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 4a277ef..1b49797 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,14 +1,68 @@ -// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ +use std::process::{Child, Command}; +use std::sync::Mutex; +use tauri::{Manager, State}; + +struct PythonBackend { + process: Mutex>, +} + #[tauri::command] -fn greet(name: &str) -> String { - format!("Hello, {}! You've been greeted from Rust!", name) +fn start_backend(backend: State) -> Result { + let mut process_lock = backend.process.lock().map_err(|e| e.to_string())?; + + if process_lock.is_some() { + return Ok("Backend already running".to_string()); + } + + // Get the resource path for the bundled Python executable + let python_cmd = if cfg!(windows) { + "python" + } else { + "python3" + }; + + // Start the Python backend + let child = Command::new(python_cmd) + .args(["-m", "uvicorn", "backend.main:app", "--host", "127.0.0.1", "--port", "8000"]) + .spawn() + .map_err(|e| format!("Failed to start backend: {}", e))?; + + *process_lock = Some(child); + Ok("Backend started successfully".to_string()) +} + +#[tauri::command] +fn stop_backend(backend: State) -> Result { + let mut process_lock = backend.process.lock().map_err(|e| e.to_string())?; + + if let Some(mut child) = process_lock.take() { + child.kill().map_err(|e| format!("Failed to stop backend: {}", e))?; + Ok("Backend stopped".to_string()) + } else { + Ok("Backend not running".to_string()) + } } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_opener::init()) - .invoke_handler(tauri::generate_handler![greet]) + .manage(PythonBackend { + process: Mutex::new(None), + }) + .invoke_handler(tauri::generate_handler![start_backend, stop_backend]) + .on_window_event(|window, event| { + // Stop backend when window closes + if let tauri::WindowEvent::CloseRequested { .. } = event { + if let Some(backend) = window.try_state::() { + if let Ok(mut process_lock) = backend.process.lock() { + if let Some(mut child) = process_lock.take() { + let _ = child.kill(); + } + } + } + } + }) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index ec8f9d4..92a0dca 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -12,9 +12,11 @@ "app": { "windows": [ { - "title": "Chronara", - "width": 800, - "height": 600 + "title": "Chronara - Meeting Transcription", + "width": 1200, + "height": 800, + "minWidth": 800, + "minHeight": 600 } ], "security": { @@ -23,23 +25,12 @@ }, "bundle": { "active": true, - "targets": ["app", "deb", "rpm", "appimage", "nsis"], + "targets": ["nsis"], "publisher": "NHCarrigan", "copyright": "Copyright ยฉ 2026 Naomi Carrigan", "category": "Productivity", "shortDescription": "Meeting transcription and summarization tool using local AI models", "longDescription": "Chronara provides real-time meeting transcription with speaker diarization and AI-powered summaries, all processed locally for maximum privacy.", - "linux": { - "deb": { - "depends": [] - }, - "rpm": { - "release": "1" - }, - "appimage": { - "bundleMediaFramework": true - } - }, "windows": { "nsis": {} }, diff --git a/src/App.css b/src/App.css index 85f7a4a..e106544 100644 --- a/src/App.css +++ b/src/App.css @@ -1,116 +1,344 @@ -.logo.vite:hover { - filter: drop-shadow(0 0 2em #747bff); -} - -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafb); -} :root { font-family: Inter, Avenir, Helvetica, Arial, sans-serif; font-size: 16px; - line-height: 24px; + line-height: 1.5; font-weight: 400; - color: #0f0f0f; - background-color: #f6f6f6; + --primary-color: #3b82f6; + --primary-hover: #2563eb; + --secondary-color: #10b981; + --danger-color: #ef4444; + --bg-color: #ffffff; + --surface-color: #f9fafb; + --text-color: #111827; + --text-secondary: #6b7280; + --border-color: #e5e7eb; + --shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); + + color: var(--text-color); + background-color: var(--bg-color); font-synthesis: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + margin: 0; + min-height: 100vh; } .container { - margin: 0; - padding-top: 10vh; + max-width: 1200px; + margin: 0 auto; + padding: 2rem; + min-height: 100vh; display: flex; flex-direction: column; - justify-content: center; +} + +/* Header */ +.app-header { + text-align: center; + margin-bottom: 2rem; +} + +.app-header h1 { + font-size: 2.5rem; + font-weight: 700; + margin-bottom: 0.5rem; +} + +.app-header p { + color: var(--text-secondary); + font-size: 1.125rem; +} + +/* Warning Banner */ +.warning-banner { + background-color: #fef3c7; + color: #92400e; + padding: 1rem; + border-radius: 0.5rem; + margin-bottom: 2rem; text-align: center; } -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: 0.75s; -} - -.logo.tauri:hover { - filter: drop-shadow(0 0 2em #24c8db); -} - -.row { +/* App Content */ +.app-content { + flex: 1; display: flex; - justify-content: center; + flex-direction: column; + gap: 2rem; } -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; +/* Controls Section */ +.controls-section { + display: flex; + flex-direction: column; + align-items: center; + gap: 1.5rem; } -a:hover { - color: #535bf2; +/* Audio Recorder */ +.audio-recorder { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; } -h1 { - text-align: center; -} - -input, -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - color: #0f0f0f; - background-color: #ffffff; - transition: border-color 0.25s; - box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); -} - -button { +.record-button { + font-size: 1.25rem; + padding: 1rem 2rem; + border-radius: 0.75rem; + border: 2px solid transparent; + background-color: var(--primary-color); + color: white; + font-weight: 600; cursor: pointer; + transition: all 0.2s; + box-shadow: var(--shadow); } -button:hover { - border-color: #396cd8; -} -button:active { - border-color: #396cd8; - background-color: #e8e8e8; +.record-button:hover { + background-color: var(--primary-hover); + transform: translateY(-2px); + box-shadow: + 0 4px 6px -1px rgba(0, 0, 0, 0.1), + 0 2px 4px -1px rgba(0, 0, 0, 0.06); } -input, -button { - outline: none; +.record-button.recording { + background-color: var(--danger-color); } -#greet-input { - margin-right: 5px; +.record-button.recording:hover { + background-color: #dc2626; } +.recording-indicator { + display: flex; + align-items: center; + gap: 0.5rem; + color: var(--danger-color); + font-weight: 500; +} + +.pulse { + width: 0.75rem; + height: 0.75rem; + background-color: var(--danger-color); + border-radius: 50%; + animation: pulse 1.5s infinite; +} + +@keyframes pulse { + 0% { + opacity: 1; + transform: scale(1); + } + 50% { + opacity: 0.5; + transform: scale(1.2); + } + 100% { + opacity: 1; + transform: scale(1); + } +} + +/* Action Buttons */ +.action-buttons { + display: flex; + gap: 1rem; +} + +.primary-button, +.secondary-button { + padding: 0.75rem 1.5rem; + border-radius: 0.5rem; + border: none; + font-size: 1rem; + font-weight: 500; + cursor: pointer; + transition: all 0.2s; + box-shadow: var(--shadow); +} + +.primary-button { + background-color: var(--secondary-color); + color: white; +} + +.primary-button:hover:not(:disabled) { + background-color: #059669; + transform: translateY(-1px); +} + +.primary-button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.secondary-button { + background-color: var(--surface-color); + color: var(--text-color); + border: 1px solid var(--border-color); +} + +.secondary-button:hover { + background-color: var(--border-color); +} + +/* Content Grid */ +.content-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 2rem; + flex: 1; +} + +@media (max-width: 768px) { + .content-grid { + grid-template-columns: 1fr; + } +} + +/* Transcript Display */ +.transcript-display, +.summary-display { + background-color: var(--surface-color); + border: 1px solid var(--border-color); + border-radius: 0.75rem; + padding: 1.5rem; + display: flex; + flex-direction: column; + max-height: 600px; +} + +.transcript-display h2, +.summary-display h2 { + font-size: 1.25rem; + font-weight: 600; + margin-bottom: 1rem; +} + +.transcript-segments, +.summary-content { + flex: 1; + overflow-y: auto; +} + +.empty-state { + color: var(--text-secondary); + text-align: center; + padding: 2rem; +} + +.segment { + margin-bottom: 1rem; + padding-bottom: 1rem; + border-bottom: 1px solid var(--border-color); +} + +.segment:last-child { + border-bottom: none; +} + +.segment-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.5rem; +} + +.speaker { + font-weight: 600; + font-size: 0.875rem; +} + +.timestamp { + font-size: 0.75rem; + color: var(--text-secondary); +} + +.segment-text { + line-height: 1.6; +} + +/* Summary Display */ +.summary-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; +} + +.download-button { + padding: 0.5rem 1rem; + border-radius: 0.375rem; + border: 1px solid var(--border-color); + background-color: var(--bg-color); + font-size: 0.875rem; + cursor: pointer; + transition: all 0.2s; +} + +.download-button:hover { + background-color: var(--surface-color); +} + +.summary-text { + white-space: pre-wrap; + line-height: 1.6; +} + +/* Loading */ +.loading { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 3rem; + gap: 1rem; +} + +.spinner { + width: 3rem; + height: 3rem; + border: 3px solid var(--border-color); + border-top-color: var(--primary-color); + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +/* Dark Mode */ @media (prefers-color-scheme: dark) { :root { - color: #f6f6f6; - background-color: #2f2f2f; + --bg-color: #111827; + --surface-color: #1f2937; + --text-color: #f3f4f6; + --text-secondary: #9ca3af; + --border-color: #374151; + --shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.3), 0 1px 2px 0 rgba(0, 0, 0, 0.2); } - a:hover { - color: #24c8db; - } - - input, - button { - color: #ffffff; - background-color: #0f0f0f98; - } - button:active { - background-color: #0f0f0f69; + .warning-banner { + background-color: #451a03; + color: #fbbf24; } } diff --git a/src/App.tsx b/src/App.tsx index 8286a76..9bb6b34 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,49 +1,187 @@ -import { useState } from "react"; -import reactLogo from "./assets/react.svg"; +import { useState, useEffect, useRef } from "react"; import { invoke } from "@tauri-apps/api/core"; import "./App.css"; +import { AudioRecorder } from "./components/AudioRecorder"; +import { TranscriptDisplay } from "./components/TranscriptDisplay"; +import { SummaryDisplay } from "./components/SummaryDisplay"; + +interface TranscriptSegment { + start: number; + end: number; + text: string; + speaker: string; +} function App() { - const [greetMsg, setGreetMsg] = useState(""); - const [name, setName] = useState(""); + const [isRecording, setIsRecording] = useState(false); + const [transcriptSegments, setTranscriptSegments] = useState([]); + const [summary, setSummary] = useState(null); + const [isGeneratingSummary, setIsGeneratingSummary] = useState(false); + const [backendReady, setBackendReady] = useState(false); + const wsRef = useRef(null); - async function greet() { - // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ - setGreetMsg(await invoke("greet", { name })); - } + useEffect(() => { + // Start Python backend through Tauri + startPythonBackend(); + }, []); + + const startPythonBackend = async () => { + try { + // Start backend through Tauri command + await invoke("start_backend"); + + // Give backend time to start up + setTimeout(() => { + checkBackendHealth(); + }, 2000); + } catch (error) { + console.error("Failed to start backend:", error); + } + }; + + const checkBackendHealth = async () => { + try { + const response = await fetch("http://localhost:8000/health"); + if (response.ok) { + setBackendReady(true); + } + } catch (error) { + console.error("Backend not ready:", error); + // In production, Tauri will start the backend automatically + } + }; + + const handleAudioData = (audioData: ArrayBuffer) => { + if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) { + // Create WebSocket connection + wsRef.current = new WebSocket("ws://localhost:8000/ws/transcribe"); + + wsRef.current.onopen = () => { + console.log("WebSocket connected"); + // Send the audio data + wsRef.current?.send(audioData); + }; + + wsRef.current.onmessage = (event) => { + const data = JSON.parse(event.data); + if (data.type === "transcription" && data.data.segments) { + setTranscriptSegments((prev) => [...prev, ...data.data.segments]); + } + }; + + wsRef.current.onclose = () => { + console.log("WebSocket disconnected"); + }; + } else { + // Send audio data through existing connection + wsRef.current.send(audioData); + } + }; + + const generateSummary = async () => { + if (transcriptSegments.length === 0) return; + + setIsGeneratingSummary(true); + + // Combine all transcript segments into text + const fullTranscript = transcriptSegments + .map((seg) => `${seg.speaker}: ${seg.text}`) + .join("\n"); + + try { + const response = await fetch("http://localhost:8000/summarize", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ transcript: fullTranscript }), + }); + + const data = await response.json(); + setSummary(data.summary); + } catch (error) { + console.error("Failed to generate summary:", error); + } finally { + setIsGeneratingSummary(false); + } + }; + + const downloadTranscript = () => { + const content = transcriptSegments + .map((seg) => `[${formatTime(seg.start)}] ${seg.speaker}: ${seg.text}`) + .join("\n"); + + const blob = new Blob([content], { type: "text/plain" }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = `meeting-transcript-${new Date().toISOString().split("T")[0]}.txt`; + a.click(); + URL.revokeObjectURL(url); + }; + + const downloadSummary = () => { + if (!summary) return; + + const blob = new Blob([summary], { type: "text/plain" }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = `meeting-summary-${new Date().toISOString().split("T")[0]}.txt`; + a.click(); + URL.revokeObjectURL(url); + }; + + const formatTime = (seconds: number) => { + const mins = Math.floor(seconds / 60); + const secs = Math.floor(seconds % 60); + return `${mins}:${secs.toString().padStart(2, "0")}`; + }; return (
-

Welcome to Tauri + React

+
+

๐ŸŽ™๏ธ Chronara

+

Local Meeting Transcription & Summarization

+
-
- - Vite logo - - - Tauri logo - - - React logo - + {!backendReady && ( +
โš ๏ธ Backend is starting up. This may take a moment...
+ )} + +
+
+ + + {!isRecording && transcriptSegments.length > 0 && ( +
+ + +
+ )} +
+ +
+ + +
-

Click on the Tauri, Vite, and React logos to learn more.

- -
{ - e.preventDefault(); - greet(); - }} - > - setName(e.currentTarget.value)} - placeholder="Enter a name..." - /> - -
-

{greetMsg}

); } diff --git a/src/__tests__/App.test.tsx b/src/__tests__/App.test.tsx new file mode 100644 index 0000000..884cdce --- /dev/null +++ b/src/__tests__/App.test.tsx @@ -0,0 +1,7 @@ +import { describe, it, expect } from "vitest"; + +describe("App", () => { + it("placeholder test", () => { + expect(true).toBe(true); + }); +}); diff --git a/src/backend/__init__.py b/src/backend/__init__.py new file mode 100644 index 0000000..88f6f30 --- /dev/null +++ b/src/backend/__init__.py @@ -0,0 +1 @@ +"""Chronara backend - Local meeting transcription and summarization.""" \ No newline at end of file diff --git a/src/backend/main.py b/src/backend/main.py new file mode 100644 index 0000000..d076aae --- /dev/null +++ b/src/backend/main.py @@ -0,0 +1,74 @@ +"""Main FastAPI application for Chronara.""" + +import os +from pathlib import Path + +from fastapi import FastAPI, WebSocket +from fastapi.middleware.cors import CORSMiddleware + +from .models.audio import AudioProcessor +from .models.llm import LlamaSummarizer +from .models.transcriber import WhisperXTranscriber + +app = FastAPI(title="Chronara API", version="0.1.0") + +# Enable CORS for Tauri frontend +app.add_middleware( + CORSMiddleware, + allow_origins=["tauri://localhost", "http://localhost:*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Initialize models +MODEL_DIR = Path(__file__).parent.parent.parent / "models" +transcriber = WhisperXTranscriber(model_dir=MODEL_DIR) +summarizer = LlamaSummarizer(model_dir=MODEL_DIR) +audio_processor = AudioProcessor() + + +@app.get("/health") +async def health_check(): + """Check if the API is running and models are loaded.""" + return { + "status": "healthy", + "models": { + "whisper": transcriber.is_loaded, + "llama": summarizer.is_loaded, + }, + } + + +@app.websocket("/ws/transcribe") +async def transcribe_audio(websocket: WebSocket): + """WebSocket endpoint for real-time audio transcription.""" + await websocket.accept() + + try: + while True: + # Receive audio chunk + audio_data = await websocket.receive_bytes() + + # Process audio + audio_chunk = audio_processor.process_chunk(audio_data) + + # Transcribe if we have enough audio + if audio_processor.has_speech(audio_chunk): + result = await transcriber.transcribe_chunk(audio_chunk) + + if result: + await websocket.send_json({ + "type": "transcription", + "data": result, + }) + + except Exception as e: + await websocket.close(code=1000, reason=str(e)) + + +@app.post("/summarize") +async def summarize_transcript(transcript: str): + """Summarize a meeting transcript.""" + summary = await summarizer.summarize(transcript) + return {"summary": summary} \ No newline at end of file diff --git a/src/backend/models/__init__.py b/src/backend/models/__init__.py new file mode 100644 index 0000000..2dc254d --- /dev/null +++ b/src/backend/models/__init__.py @@ -0,0 +1 @@ +"""Model modules for Chronara.""" \ No newline at end of file diff --git a/src/backend/models/audio.py b/src/backend/models/audio.py new file mode 100644 index 0000000..b46e0ca --- /dev/null +++ b/src/backend/models/audio.py @@ -0,0 +1,73 @@ +"""Audio processing utilities.""" + +import io +import wave +from typing import Optional + +import numpy as np +import pyaudio + + +class AudioProcessor: + """Handles audio capture and processing.""" + + def __init__(self, sample_rate: int = 16000, channels: int = 1): + """Initialize audio processor.""" + self.sample_rate = sample_rate + self.channels = channels + self.chunk_size = 1024 + self.format = pyaudio.paInt16 + + # Initialize PyAudio + self.audio = pyaudio.PyAudio() + + # Audio buffer for accumulating chunks + self.buffer = [] + self.min_speech_duration = 0.5 # seconds + + def start_recording(self) -> pyaudio.Stream: + """Start audio recording stream.""" + stream = self.audio.open( + format=self.format, + channels=self.channels, + rate=self.sample_rate, + input=True, + frames_per_buffer=self.chunk_size, + ) + return stream + + def stop_recording(self, stream: pyaudio.Stream) -> None: + """Stop audio recording.""" + stream.stop_stream() + stream.close() + + def process_chunk(self, audio_bytes: bytes) -> np.ndarray: + """Convert audio bytes to numpy array.""" + # Convert bytes to numpy array + audio_array = np.frombuffer(audio_bytes, dtype=np.int16) + + # Normalize to [-1, 1] + audio_float = audio_array.astype(np.float32) / 32768.0 + + return audio_float + + def has_speech(self, audio_chunk: np.ndarray, energy_threshold: float = 0.01) -> bool: + """Simple voice activity detection based on energy.""" + # Calculate RMS energy + energy = np.sqrt(np.mean(audio_chunk**2)) + + # Check if energy exceeds threshold + return energy > energy_threshold + + def save_audio(self, audio_data: bytes, filepath: str) -> None: + """Save audio data to WAV file.""" + with wave.open(filepath, "wb") as wf: + wf.setnchannels(self.channels) + wf.setsampwidth(self.audio.get_sample_size(self.format)) + wf.setframerate(self.sample_rate) + wf.writeframes(audio_data) + + def __del__(self): + """Cleanup PyAudio.""" + if hasattr(self, "audio"): + self.audio.terminate() \ No newline at end of file diff --git a/src/backend/models/llm.py b/src/backend/models/llm.py new file mode 100644 index 0000000..a943a7f --- /dev/null +++ b/src/backend/models/llm.py @@ -0,0 +1,67 @@ +"""Local LLM for meeting summarization using Llama.""" + +from pathlib import Path +from typing import Optional + +from llama_cpp import Llama + + +class LlamaSummarizer: + """Handles meeting summarization using local Llama model.""" + + def __init__(self, model_dir: Path, model_size: str = "1B"): + """Initialize Llama model.""" + self.model_dir = model_dir + self.is_loaded = False + + model_path = model_dir / f"llama-3.2-{model_size}-instruct-Q4_K_M.gguf" + + try: + self.llm = Llama( + model_path=str(model_path), + n_ctx=8192, # Context window + n_threads=4, # CPU threads + n_gpu_layers=-1, # Use GPU if available + verbose=False, + ) + self.is_loaded = True + except Exception as e: + print(f"Failed to load Llama model: {e}") + self.is_loaded = False + + async def summarize(self, transcript: str) -> Optional[str]: + """Generate a meeting summary from transcript.""" + if not self.is_loaded: + return None + + prompt = f"""<|begin_of_text|><|start_header_id|>system<|end_header_id|> + +You are a helpful assistant that creates concise meeting summaries. Focus on: +- Key decisions made +- Action items and who owns them +- Important discussions and their outcomes +- Next steps + +Keep the summary structured and easy to scan.<|eot_id|><|start_header_id|>user<|end_header_id|> + +Please summarize this meeting transcript: + +{transcript}<|eot_id|><|start_header_id|>assistant<|end_header_id|> + +Meeting Summary: +""" + + try: + response = self.llm( + prompt, + max_tokens=1024, + temperature=0.7, + top_p=0.9, + stop=["<|eot_id|>", "<|end_of_text|>"], + ) + + return response["choices"][0]["text"].strip() + + except Exception as e: + print(f"Summarization error: {e}") + return None \ No newline at end of file diff --git a/src/backend/models/transcriber.py b/src/backend/models/transcriber.py new file mode 100644 index 0000000..5dd325b --- /dev/null +++ b/src/backend/models/transcriber.py @@ -0,0 +1,88 @@ +"""WhisperX transcription with speaker diarization.""" + +import json +from pathlib import Path +from typing import Any, Optional + +import numpy as np +import torch +import whisperx + + +class WhisperXTranscriber: + """Handles audio transcription and speaker diarization using WhisperX.""" + + def __init__(self, model_dir: Path, model_size: str = "base"): + """Initialize WhisperX with local models.""" + self.model_dir = model_dir + self.device = "cuda" if torch.cuda.is_available() else "cpu" + self.compute_type = "float16" if self.device == "cuda" else "int8" + self.is_loaded = False + + try: + # Load ASR model + self.model = whisperx.load_model( + model_size, + self.device, + compute_type=self.compute_type, + download_root=str(model_dir / "whisper"), + ) + + # Load alignment model + self.align_model, self.align_metadata = whisperx.load_align_model( + language_code="en", + device=self.device, + model_dir=str(model_dir / "alignment"), + ) + + # Load diarization pipeline + self.diarize_model = whisperx.DiarizationPipeline( + device=self.device, + model_name=str(model_dir / "diarization"), + ) + + self.is_loaded = True + except Exception as e: + print(f"Failed to load WhisperX models: {e}") + self.is_loaded = False + + async def transcribe_chunk(self, audio_chunk: np.ndarray) -> Optional[dict[str, Any]]: + """Transcribe an audio chunk with speaker diarization.""" + if not self.is_loaded: + return None + + try: + # Transcribe + result = self.model.transcribe( + audio_chunk, + batch_size=16, + ) + + # Align whisper output + result = whisperx.align( + result["segments"], + self.align_model, + self.align_metadata, + audio_chunk, + self.device, + ) + + # Diarize + diarize_segments = self.diarize_model(audio_chunk) + result = whisperx.assign_word_speakers(diarize_segments, result) + + # Format output + formatted_result = [] + for segment in result["segments"]: + formatted_result.append({ + "start": segment["start"], + "end": segment["end"], + "text": segment["text"], + "speaker": segment.get("speaker", "Unknown"), + }) + + return {"segments": formatted_result} + + except Exception as e: + print(f"Transcription error: {e}") + return None \ No newline at end of file diff --git a/src/components/AudioRecorder.tsx b/src/components/AudioRecorder.tsx new file mode 100644 index 0000000..e4a8856 --- /dev/null +++ b/src/components/AudioRecorder.tsx @@ -0,0 +1,83 @@ +import { useRef, useEffect } from "react"; + +interface AudioRecorderProps { + onAudioData: (data: ArrayBuffer) => void; + isRecording: boolean; + setIsRecording: (recording: boolean) => void; +} + +export function AudioRecorder({ onAudioData, isRecording, setIsRecording }: AudioRecorderProps) { + const mediaRecorderRef = useRef(null); + const streamRef = useRef(null); + + useEffect(() => { + return () => { + // Cleanup on unmount + if (streamRef.current) { + streamRef.current.getTracks().forEach((track) => track.stop()); + } + }; + }, []); + + const startRecording = async () => { + try { + const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); + streamRef.current = stream; + + const mediaRecorder = new MediaRecorder(stream, { + mimeType: "audio/webm", + }); + + mediaRecorderRef.current = mediaRecorder; + + const chunks: Blob[] = []; + + mediaRecorder.ondataavailable = (event) => { + if (event.data.size > 0) { + chunks.push(event.data); + } + }; + + mediaRecorder.onstop = async () => { + const blob = new Blob(chunks, { type: "audio/webm" }); + const arrayBuffer = await blob.arrayBuffer(); + onAudioData(arrayBuffer); + chunks.length = 0; + }; + + // Send data every second for real-time processing + mediaRecorder.start(1000); + setIsRecording(true); + } catch (error) { + console.error("Error starting recording:", error); + alert("Failed to access microphone"); + } + }; + + const stopRecording = () => { + if (mediaRecorderRef.current && isRecording) { + mediaRecorderRef.current.stop(); + setIsRecording(false); + + if (streamRef.current) { + streamRef.current.getTracks().forEach((track) => track.stop()); + } + } + }; + + return ( +
+ + {isRecording && ( +
+ Recording... +
+ )} +
+ ); +} diff --git a/src/components/SummaryDisplay.tsx b/src/components/SummaryDisplay.tsx new file mode 100644 index 0000000..ce85da8 --- /dev/null +++ b/src/components/SummaryDisplay.tsx @@ -0,0 +1,32 @@ +interface SummaryDisplayProps { + summary: string | null; + isLoading: boolean; + onDownload: () => void; +} + +export function SummaryDisplay({ summary, isLoading, onDownload }: SummaryDisplayProps) { + return ( +
+
+

Meeting Summary

+ {summary && ( + + )} +
+
+ {isLoading ? ( +
+
+

Generating summary...

+
+ ) : summary ? ( +
{summary}
+ ) : ( +

Summary will appear here after recording is complete.

+ )} +
+
+ ); +} diff --git a/src/components/TranscriptDisplay.tsx b/src/components/TranscriptDisplay.tsx new file mode 100644 index 0000000..5defdf8 --- /dev/null +++ b/src/components/TranscriptDisplay.tsx @@ -0,0 +1,57 @@ +interface TranscriptSegment { + start: number; + end: number; + text: string; + speaker: string; +} + +interface TranscriptDisplayProps { + segments: TranscriptSegment[]; +} + +export function TranscriptDisplay({ segments }: TranscriptDisplayProps) { + const formatTime = (seconds: number) => { + const mins = Math.floor(seconds / 60); + const secs = Math.floor(seconds % 60); + return `${mins}:${secs.toString().padStart(2, "0")}`; + }; + + const getSpeakerColor = (speaker: string) => { + const colors = [ + "#3b82f6", // blue + "#10b981", // green + "#f59e0b", // amber + "#ef4444", // red + "#8b5cf6", // purple + "#14b8a6", // teal + ]; + const speakerParts = speaker.split("_"); + const index = speakerParts[1] ? parseInt(speakerParts[1], 10) % colors.length : 0; + return colors[index]; + }; + + return ( +
+

Transcript

+
+ {segments.length === 0 ? ( +

No transcript yet. Start recording to begin.

+ ) : ( + segments.map((segment, index) => ( +
+
+ + {segment.speaker} + + + {formatTime(segment.start)} - {formatTime(segment.end)} + +
+

{segment.text}

+
+ )) + )} +
+
+ ); +} diff --git a/src/main.tsx b/src/main.tsx index 2be325e..3fd3a69 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -5,5 +5,5 @@ import App from "./App"; ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - , + ); diff --git a/tsconfig.json b/tsconfig.json index 7efd43c..3934b8f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,13 +1,21 @@ { - "extends": "@nhcarrigan/typescript-config", "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "ESNext", + "skipLibCheck": true, "moduleResolution": "bundler", "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, "noEmit": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "jsx": "react-jsx" + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true }, "include": ["src"], "references": [{ "path": "./tsconfig.node.json" }] -} \ No newline at end of file +} diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..192d830 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "vitest/config"; +import react from "@vitejs/plugin-react"; + +export default defineConfig({ + plugins: [react()], + test: { + include: ["src/**/*.{test,spec}.{js,ts,tsx}"], + environment: "jsdom", + setupFiles: ["./vitest.setup.ts"], + globals: true, + }, +}); diff --git a/vitest.setup.ts b/vitest.setup.ts new file mode 100644 index 0000000..f149f27 --- /dev/null +++ b/vitest.setup.ts @@ -0,0 +1 @@ +import "@testing-library/jest-dom/vitest";