diff --git a/.gitea/workflows/ci-chrome.yml b/.gitea/workflows/ci-chrome.yml
new file mode 100644
index 0000000..c2cb80d
--- /dev/null
+++ b/.gitea/workflows/ci-chrome.yml
@@ -0,0 +1,80 @@
+name: CI — Chrome
+
+on:
+ push:
+ branches: [main, develop]
+ paths:
+ - 'packages/chrome/**'
+ - 'src/lib/**'
+ - 'tests/**'
+ - '.gitea/workflows/ci-chrome.yml'
+ pull_request:
+ branches: [main]
+ paths:
+ - 'packages/chrome/**'
+ - 'src/lib/**'
+ - 'tests/**'
+ - '.gitea/workflows/ci-chrome.yml'
+
+jobs:
+ chrome:
+ name: Chrome — typecheck, test, build
+ runs-on: ubuntu-latest
+ timeout-minutes: 15
+ container:
+ image: node:22-bookworm
+
+ steps:
+ - name: Install system deps
+ run: apt-get update -qq && apt-get install -y git ca-certificates -qq
+
+ - name: Clone repository
+ run: |
+ git clone ${{ gitea.server_url }}/${{ gitea.repository }}.git /tmp/lexai
+ cd /tmp/lexai
+ if [ "${{ gitea.event_name }}" = "pull_request" ]; then
+ git fetch origin pull/${{ gitea.event.pull_request.number }}/head:pr
+ git checkout pr
+ else
+ git checkout ${{ gitea.sha }}
+ fi
+
+ - name: Install dependencies
+ run: npm ci --prefer-offline --no-audit --no-fund
+ working-directory: /tmp/lexai/packages/chrome
+
+ - name: Prepare WXT types
+ run: npx wxt prepare
+ working-directory: /tmp/lexai/packages/chrome
+
+ - name: Typecheck
+ run: npm run typecheck
+ working-directory: /tmp/lexai/packages/chrome
+
+ - name: Unit tests
+ run: npm test -- --run
+ working-directory: /tmp/lexai/packages/chrome
+
+ - name: Build
+ run: npm run build
+ working-directory: /tmp/lexai/packages/chrome
+
+ - name: Verify output
+ run: ls -la .output/chrome-mv3/
+ working-directory: /tmp/lexai/packages/chrome
+
+ - name: Notify Success
+ if: success()
+ run: |
+ SHA=$(echo "${{ gitea.sha }}" | cut -c1-7)
+ curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
+ -d "chat_id=${{ secrets.TELEGRAM_CHAT_ID }}" \
+ -d "text=✅ LexAI CI Chrome Passed%0ABranch: ${{ gitea.ref_name }}%0ACommit: ${SHA}"
+
+ - name: Notify Failure
+ if: failure()
+ run: |
+ SHA=$(echo "${{ gitea.sha }}" | cut -c1-7)
+ curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
+ -d "chat_id=${{ secrets.TELEGRAM_CHAT_ID }}" \
+ -d "text=❌ LexAI CI Chrome FAILED%0ABranch: ${{ gitea.ref_name }}%0ACommit: ${SHA}%0Ahttps://git.juankibin.space/kibin/LexAI/actions"
diff --git a/.gitea/workflows/ci-cli.yml b/.gitea/workflows/ci-cli.yml
new file mode 100644
index 0000000..b94edbc
--- /dev/null
+++ b/.gitea/workflows/ci-cli.yml
@@ -0,0 +1,72 @@
+name: CI — CLI
+
+on:
+ push:
+ branches: [main, develop]
+ paths:
+ - 'packages/cli/**'
+ - 'src/lib/**'
+ - '.gitea/workflows/ci-cli.yml'
+ pull_request:
+ branches: [main]
+ paths:
+ - 'packages/cli/**'
+ - 'src/lib/**'
+ - '.gitea/workflows/ci-cli.yml'
+
+jobs:
+ cli:
+ name: CLI — typecheck, build, smoke
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ container:
+ image: node:22-bookworm
+
+ steps:
+ - name: Install system deps
+ run: apt-get update -qq && apt-get install -y git ca-certificates -qq
+
+ - name: Clone repository
+ run: |
+ git clone ${{ gitea.server_url }}/${{ gitea.repository }}.git /tmp/lexai
+ cd /tmp/lexai
+ if [ "${{ gitea.event_name }}" = "pull_request" ]; then
+ git fetch origin pull/${{ gitea.event.pull_request.number }}/head:pr
+ git checkout pr
+ else
+ git checkout ${{ gitea.sha }}
+ fi
+
+ - name: Install dependencies
+ run: npm ci --prefer-offline --no-audit --no-fund
+ working-directory: /tmp/lexai/packages/cli
+
+ - name: Typecheck
+ run: npm run typecheck
+ working-directory: /tmp/lexai/packages/cli
+
+ - name: Build
+ run: npm run build
+ working-directory: /tmp/lexai/packages/cli
+
+ - name: Smoke (help + list, no API key)
+ run: |
+ node out/cli.js --help
+ node out/cli.js prompt --list
+ working-directory: /tmp/lexai/packages/cli
+
+ - name: Notify Success
+ if: success()
+ run: |
+ SHA=$(echo "${{ gitea.sha }}" | cut -c1-7)
+ curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
+ -d "chat_id=${{ secrets.TELEGRAM_CHAT_ID }}" \
+ -d "text=✅ LexAI CI CLI Passed%0ABranch: ${{ gitea.ref_name }}%0ACommit: ${SHA}"
+
+ - name: Notify Failure
+ if: failure()
+ run: |
+ SHA=$(echo "${{ gitea.sha }}" | cut -c1-7)
+ curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
+ -d "chat_id=${{ secrets.TELEGRAM_CHAT_ID }}" \
+ -d "text=❌ LexAI CI CLI FAILED%0ABranch: ${{ gitea.ref_name }}%0ACommit: ${SHA}%0Ahttps://git.juankibin.space/kibin/LexAI/actions"
diff --git a/.gitea/workflows/ci-vscode.yml b/.gitea/workflows/ci-vscode.yml
new file mode 100644
index 0000000..ae3e4a1
--- /dev/null
+++ b/.gitea/workflows/ci-vscode.yml
@@ -0,0 +1,70 @@
+name: CI — VS Code
+
+on:
+ push:
+ branches: [main, develop]
+ paths:
+ - 'packages/vscode/**'
+ - 'src/lib/**'
+ - '.gitea/workflows/ci-vscode.yml'
+ pull_request:
+ branches: [main]
+ paths:
+ - 'packages/vscode/**'
+ - 'src/lib/**'
+ - '.gitea/workflows/ci-vscode.yml'
+
+jobs:
+ vscode:
+ name: VS Code — typecheck, build
+ runs-on: ubuntu-latest
+ timeout-minutes: 15
+ container:
+ image: node:22-bookworm
+
+ steps:
+ - name: Install system deps
+ run: apt-get update -qq && apt-get install -y git ca-certificates -qq
+
+ - name: Clone repository
+ run: |
+ git clone ${{ gitea.server_url }}/${{ gitea.repository }}.git /tmp/lexai
+ cd /tmp/lexai
+ if [ "${{ gitea.event_name }}" = "pull_request" ]; then
+ git fetch origin pull/${{ gitea.event.pull_request.number }}/head:pr
+ git checkout pr
+ else
+ git checkout ${{ gitea.sha }}
+ fi
+
+ - name: Install dependencies
+ run: npm ci --prefer-offline --no-audit --no-fund
+ working-directory: /tmp/lexai/packages/vscode
+
+ - name: Typecheck
+ run: npm run typecheck
+ working-directory: /tmp/lexai/packages/vscode
+
+ - name: Build
+ run: npm run build
+ working-directory: /tmp/lexai/packages/vscode
+
+ - name: Verify output
+ run: ls -la out/extension.js
+ working-directory: /tmp/lexai/packages/vscode
+
+ - name: Notify Success
+ if: success()
+ run: |
+ SHA=$(echo "${{ gitea.sha }}" | cut -c1-7)
+ curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
+ -d "chat_id=${{ secrets.TELEGRAM_CHAT_ID }}" \
+ -d "text=✅ LexAI CI VS Code Passed%0ABranch: ${{ gitea.ref_name }}%0ACommit: ${SHA}"
+
+ - name: Notify Failure
+ if: failure()
+ run: |
+ SHA=$(echo "${{ gitea.sha }}" | cut -c1-7)
+ curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
+ -d "chat_id=${{ secrets.TELEGRAM_CHAT_ID }}" \
+ -d "text=❌ LexAI CI VS Code FAILED%0ABranch: ${{ gitea.ref_name }}%0ACommit: ${SHA}%0Ahttps://git.juankibin.space/kibin/LexAI/actions"
diff --git a/.gitea/workflows/ci-website.yml b/.gitea/workflows/ci-website.yml
new file mode 100644
index 0000000..7778f30
--- /dev/null
+++ b/.gitea/workflows/ci-website.yml
@@ -0,0 +1,52 @@
+name: CI — Website
+
+on:
+ push:
+ branches: [main, develop]
+ paths:
+ - 'packages/website/**'
+ - '.gitea/workflows/ci-website.yml'
+ pull_request:
+ branches: [main]
+ paths:
+ - 'packages/website/**'
+ - '.gitea/workflows/ci-website.yml'
+
+jobs:
+ website:
+ name: Website — typecheck, build
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ container:
+ image: node:22-bookworm
+
+ steps:
+ - name: Install system deps
+ run: apt-get update -qq && apt-get install -y git ca-certificates -qq
+
+ - name: Clone repository
+ run: |
+ git clone ${{ gitea.server_url }}/${{ gitea.repository }}.git /tmp/lexai
+ cd /tmp/lexai
+ if [ "${{ gitea.event_name }}" = "pull_request" ]; then
+ git fetch origin pull/${{ gitea.event.pull_request.number }}/head:pr
+ git checkout pr
+ else
+ git checkout ${{ gitea.sha }}
+ fi
+
+ - name: Install dependencies
+ run: npm ci --prefer-offline --no-audit --no-fund
+ working-directory: /tmp/lexai/packages/website
+
+ - name: Typecheck
+ run: npm run typecheck
+ working-directory: /tmp/lexai/packages/website
+
+ - name: Build
+ run: npm run build
+ working-directory: /tmp/lexai/packages/website
+
+ - name: Verify output
+ run: ls -la dist/
+ working-directory: /tmp/lexai/packages/website
diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml
deleted file mode 100644
index 40e1624..0000000
--- a/.gitea/workflows/ci.yml
+++ /dev/null
@@ -1,100 +0,0 @@
-name: CI — Test & Build
-
-on:
- push:
- branches: [main, develop]
- pull_request:
- branches: [main]
-
-jobs:
- test-and-build:
- name: Test & Build
- runs-on: ubuntu-latest
- timeout-minutes: 15
- container:
- image: node:22-bookworm
-
- steps:
- - name: Install system deps
- run: apt-get update -qq && apt-get install -y zip curl git ca-certificates -qq
-
- - name: Clone repository (triggering ref)
- run: |
- git clone ${{ gitea.server_url }}/${{ gitea.repository }}.git /tmp/lexai
- cd /tmp/lexai
- if [ "${{ gitea.event_name }}" = "pull_request" ]; then
- git fetch origin pull/${{ gitea.event.pull_request.number }}/head:pr
- git checkout pr
- else
- git checkout ${{ gitea.sha }}
- fi
-
- - name: Install Chrome package dependencies
- run: npm ci --prefer-offline --no-audit --no-fund
- working-directory: /tmp/lexai/packages/chrome
-
- # Generates .wxt/types (ImportMeta.env etc.) required by typecheck.
- - name: Prepare WXT types
- run: npx wxt prepare
- working-directory: /tmp/lexai/packages/chrome
-
- - name: Type check
- run: npm run typecheck
- working-directory: /tmp/lexai/packages/chrome
-
- - name: Run unit tests
- run: npm test -- --run
- working-directory: /tmp/lexai/packages/chrome
-
- - name: Build extension
- run: npm run build
- working-directory: /tmp/lexai/packages/chrome
-
- - name: Verify build output
- run: ls -la .output/chrome-mv3/
- working-directory: /tmp/lexai/packages/chrome
-
- - name: Package as ZIP
- run: |
- SHA=$(echo "${{ gitea.sha }}" | cut -c1-7)
- TS=$(date +%Y%m%d%H%M%S)
- VERSION=$(node -p "require('./package.json').version")-${SHA}-${TS}
- zip -r /tmp/lexai-chrome-mv3-${VERSION}.zip .output/chrome-mv3/
- echo "PACKAGE_VERSION=${VERSION}" >> $GITHUB_ENV
- echo "✅ Packaged: lexai-chrome-mv3-${VERSION}.zip"
- working-directory: /tmp/lexai/packages/chrome
-
- - name: Publish to Gitea Package Registry
- if: gitea.event_name == 'push'
- run: |
- RESPONSE=$(curl -s -w "\n%{http_code}" -X PUT \
- "${{ gitea.server_url }}/api/packages/kibin/generic/lexai-extension/${PACKAGE_VERSION}/lexai-chrome-mv3-${PACKAGE_VERSION}.zip" \
- -H "Authorization: token ${{ secrets.GITEATOKEN }}" \
- -T "/tmp/lexai-chrome-mv3-${PACKAGE_VERSION}.zip")
- HTTP_CODE=$(echo "$RESPONSE" | tail -n 1)
- BODY=$(echo "$RESPONSE" | sed '$d')
- echo "HTTP: $HTTP_CODE | Response: $BODY"
- if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then
- echo "✅ Published: https://git.juankibin.space/kibin/-/packages"
- else
- echo "⚠️ Package publish returned $HTTP_CODE: $BODY"
- exit 1
- fi
- env:
- PACKAGE_VERSION: ${{ env.PACKAGE_VERSION }}
-
- - name: Notify Success
- if: success()
- run: |
- SHA=$(echo "${{ gitea.sha }}" | cut -c1-7)
- curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
- -d "chat_id=${{ secrets.TELEGRAM_CHAT_ID }}" \
- -d "text=✅ LexAI CI Passed%0A%0ABranch: ${{ gitea.ref_name }}%0ACommit: ${SHA}%0A%0AAll steps green — new build published to packages."
-
- - name: Notify Failure
- if: failure()
- run: |
- SHA=$(echo "${{ gitea.sha }}" | cut -c1-7)
- curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
- -d "chat_id=${{ secrets.TELEGRAM_CHAT_ID }}" \
- -d "text=❌ LexAI CI FAILED%0A%0ABranch: ${{ gitea.ref_name }}%0ACommit: ${SHA}%0A%0ACheck: https://git.juankibin.space/kibin/LexAI/actions"
diff --git a/.gitea/workflows/deploy-chrome.yml b/.gitea/workflows/deploy-chrome.yml
deleted file mode 100644
index df88041..0000000
--- a/.gitea/workflows/deploy-chrome.yml
+++ /dev/null
@@ -1,132 +0,0 @@
-name: Deploy — Chrome Web Store
-
-on:
- push:
- tags:
- - 'v*.*.*'
- workflow_dispatch:
- inputs:
- publish:
- description: 'Publish after upload? (yes/no)'
- required: false
- default: 'yes'
-
-jobs:
- deploy:
- name: Build & Deploy to Chrome Web Store
- runs-on: ubuntu-latest
- timeout-minutes: 20
- container:
- image: node:22-bookworm
-
- steps:
- - name: Install deps
- run: apt-get update -qq && apt-get install -y zip curl
-
- - name: Checkout
- run: |
- git clone ${{ gitea.server_url }}/${{ gitea.repository }}.git .
- git checkout ${{ gitea.sha }}
-
- - name: Install dependencies
- run: npm ci
- working-directory: packages/chrome
-
- # Same gates as CI — a release must never check less than a push.
- - name: Prepare WXT types
- run: npx wxt prepare
- working-directory: packages/chrome
-
- - name: Type check
- run: npm run typecheck
- working-directory: packages/chrome
-
- - name: Run tests
- run: npm test -- --run
- working-directory: packages/chrome
-
- - name: Build extension
- run: npm run build
- working-directory: packages/chrome
-
- - name: Package as ZIP
- run: |
- VERSION=${{ gitea.ref_name || 'manual' }}
- ZIPFILE="$(cd ../.. && pwd)/lexai-chrome-mv3-${VERSION}.zip"
- # Verify manifest exists and check version
- node -e "console.log('Manifest version:', require('./.output/chrome-mv3/manifest.json').version)"
- # Zip from inside the chrome-mv3 dir so manifest.json is at root
- cd .output/chrome-mv3 && zip -r "$ZIPFILE" . && cd -
- echo "ZIP_FILE=${ZIPFILE}" >> $GITHUB_ENV
- echo "VERSION=${VERSION}" >> $GITHUB_ENV
- echo "✅ Packaged: lexai-chrome-mv3-${VERSION}.zip"
- working-directory: packages/chrome
-
-
- - name: Get Chrome Web Store OAuth2 Token
- run: |
- TOKEN_RESPONSE=$(curl -s -X POST "https://oauth2.googleapis.com/token" \
- -d "client_id=${{ secrets.CWS_CLIENT_ID }}" \
- -d "client_secret=${{ secrets.CWS_CLIENT_SECRET }}" \
- -d "refresh_token=${{ secrets.CWS_REFRESH_TOKEN }}" \
- -d "grant_type=refresh_token")
- ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | node -e "let s='';process.stdin.on('data',d=>s+=d);process.stdin.on('end',()=>{let d;try{d=JSON.parse(s)}catch(e){console.error('Invalid JSON token response:',s);process.exit(1)};if(!d.access_token){console.error('No access_token in response:',s);process.exit(1)};console.log(d.access_token)})")
- if [ -z "$ACCESS_TOKEN" ]; then
- echo "❌ Failed to get access token. Response: $TOKEN_RESPONSE"
- exit 1
- fi
- echo "CWS_ACCESS_TOKEN=${ACCESS_TOKEN}" >> $GITHUB_ENV
- echo "✅ OAuth2 token obtained"
-
- - name: Upload to Chrome Web Store
- run: |
- UPLOAD_BODY=$(curl -s -X PUT \
- "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${{ secrets.CWS_EXTENSION_ID }}" \
- -H "Authorization: Bearer ${{ env.CWS_ACCESS_TOKEN }}" \
- -H "x-goog-api-version: 2" \
- -T "${{ env.ZIP_FILE }}")
- echo "Upload Response: $UPLOAD_BODY"
- UPLOAD_STATE=$(echo "$UPLOAD_BODY" | node -e "let s='';process.stdin.on('data',d=>s+=d);process.stdin.on('end',()=>{let d;try{d=JSON.parse(s)}catch(e){console.error('Invalid JSON upload response:',s);process.exit(1)};console.log(d.uploadState||'')})")
- echo "Upload state: $UPLOAD_STATE"
- if [ "$UPLOAD_STATE" != "SUCCESS" ]; then
- echo "❌ Upload failed — state: $UPLOAD_STATE"
- exit 1
- fi
- echo "✅ Uploaded to Chrome Web Store"
-
- - name: Publish to Chrome Web Store
- if: ${{ gitea.event_name == 'push' || github.event.inputs.publish == 'yes' }}
- run: |
- PUBLISH_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \
- "https://www.googleapis.com/chromewebstore/v1.1/items/${{ secrets.CWS_EXTENSION_ID }}/publish" \
- -H "Authorization: Bearer ${{ env.CWS_ACCESS_TOKEN }}" \
- -H "x-goog-api-version: 2" \
- -H "Content-Length: 0")
- HTTP_CODE=$(echo "$PUBLISH_RESPONSE" | tail -n 1)
- BODY=$(echo "$PUBLISH_RESPONSE" | sed '$d')
- echo "Publish HTTP: $HTTP_CODE"
- echo "Publish Response: $BODY"
- STATUS=$(echo "$BODY" | grep -o '"status":\["[^"]*"\]' | head -1)
- echo "Publish status: $STATUS"
- if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then
- echo "✅ Published to Chrome Web Store!"
- else
- echo "❌ Publish failed: HTTP $HTTP_CODE"
- exit 1
- fi
-
- - name: Notify Success
- if: success()
- run: |
- VERSION=${{ env.VERSION }}
- curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
- -d "chat_id=${{ secrets.TELEGRAM_CHAT_ID }}" \
- -d "text=🚀 LexAI ${VERSION} deployed to Chrome Web Store!%0A%0A✅ Built%0A✅ Packaged%0A✅ Uploaded%0A✅ Published%0A%0Ahttps://chromewebstore.google.com/detail/${{ secrets.CWS_EXTENSION_ID }}"
-
- - name: Notify Failure
- if: failure()
- run: |
- VERSION=${{ env.VERSION }}
- curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
- -d "chat_id=${{ secrets.TELEGRAM_CHAT_ID }}" \
- -d "text=❌ LexAI CWS Deploy FAILED%0A%0AVersion: ${VERSION}%0A%0ACheck: https://git.juankibin.space/kibin/LexAI/actions"
diff --git a/.gitea/workflows/preview.yml b/.gitea/workflows/preview.yml
deleted file mode 100644
index ed46ee4..0000000
--- a/.gitea/workflows/preview.yml
+++ /dev/null
@@ -1,53 +0,0 @@
-name: Preview — PR Build Check
-
-on:
- pull_request:
- types: [opened, synchronize, reopened]
-
-jobs:
- preview-build:
- name: PR Preview Build
- runs-on: ubuntu-latest
- container:
- image: node:22-bookworm
-
- steps:
- - name: Install zip curl
- run: apt-get update -qq && apt-get install -y zip curl
-
- - name: Checkout PR
- run: |
- git clone ${{ gitea.server_url }}/${{ gitea.repository }}.git .
- git fetch origin pull/${{ gitea.event.pull_request.number }}/head:pr
- git checkout pr
-
- - name: Install dependencies
- run: npm ci
- working-directory: packages/chrome
-
- - name: Type check
- run: npm run typecheck
- working-directory: packages/chrome
-
- - name: Run unit tests
- run: npm test -- --run
- working-directory: packages/chrome
-
- - name: Build extension
- run: npm run build
- working-directory: packages/chrome
-
- - name: Package preview ZIP
- run: |
- PR_NUM=${{ gitea.event.pull_request.number }}
- zip -r ../../lexai-pr-${PR_NUM}-preview.zip .output/chrome-mv3/
- echo "✅ PR #${PR_NUM} build successful"
- working-directory: packages/chrome
-
- - name: Comment on PR
- run: |
- PR_NUM=${{ gitea.event.pull_request.number }}
- curl -s -X POST "${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/issues/${PR_NUM}/comments" \
- -H "Authorization: token ${{ secrets.GITEATOKEN }}" \
- -H "Content-Type: application/json" \
- -d "{\"body\": \"## ⚡ LexAI Preview Build Ready\n\n✅ Tests passed\n✅ Build successful\n✅ TypeScript clean\n\nCommit: \`${{ gitea.sha }}\`\"}"
diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml
deleted file mode 100644
index dfe43ea..0000000
--- a/.gitea/workflows/release.yml
+++ /dev/null
@@ -1,102 +0,0 @@
-name: Release — Package & Publish
-
-on:
- push:
- tags:
- - 'v*.*.*'
-
-jobs:
- release:
- name: Build & Package Release
- runs-on: ubuntu-latest
- container:
- image: node:22-bookworm
-
- steps:
- - name: Install zip
- run: apt-get update -qq && apt-get install -y zip curl
-
- - name: Checkout
- run: |
- git clone ${{ gitea.server_url }}/${{ gitea.repository }}.git .
- git checkout ${{ gitea.sha }}
-
- - name: Install dependencies
- run: npm ci
- working-directory: packages/chrome
-
- - name: Run tests
- run: npm test -- --run
- working-directory: packages/chrome
-
- - name: Build extension
- run: npm run build
- working-directory: packages/chrome
-
- - name: Package as ZIP
- run: |
- VERSION=${{ gitea.ref_name }}
- ZIPFILE="$(cd ../.. && pwd)/lexai-chrome-mv3-${VERSION}.zip"
- # Zip from inside the chrome-mv3 dir so manifest.json is at root of the archive
- cd .output/chrome-mv3 && zip -r "$ZIPFILE" . && cd -
- echo "ZIP_FILE=${ZIPFILE}" >> $GITHUB_ENV
- working-directory: packages/chrome
-
- - name: Create Release
- run: |
- VERSION=${{ gitea.ref_name }}
- cat > build-release-payload.js <<'EOF'
- const fs = require('fs');
- const tag = process.env.VERSION;
- const version = tag.replace(/^v/, '');
- const installSteps = '### Installation\n1. Download ZIP below\n2. Extract it\n3. Open Chrome → chrome://extensions\n4. Enable Developer Mode\n5. Click Load unpacked → select the extracted folder';
- let body = '## LexAI ' + tag + '\n\n' + installSteps;
- try {
- const changelog = fs.readFileSync('CHANGELOG.md', 'utf8');
- const headingRe = new RegExp('^## \\[' + version.replace(/\./g, '\\.') + '\\].*$', 'm');
- const match = headingRe.exec(changelog);
- if (match) {
- const rest = changelog.slice(match.index + match[0].length);
- const nextStop = rest.search(/^(## \[|\[[^\]]+\]:\s)/m);
- const section = (nextStop === -1 ? rest : rest.slice(0, nextStop)).trim();
- body = match[0] + '\n\n' + section + '\n\n' + installSteps;
- } else {
- console.error('No CHANGELOG.md section found for ' + version + ', falling back to generic body');
- }
- } catch (e) {
- console.error('Could not read CHANGELOG.md, falling back to generic body:', e.message);
- }
- const payload = {
- tag_name: tag,
- name: 'LexAI ' + tag,
- body,
- draft: false,
- prerelease: false,
- };
- fs.writeFileSync('payload.json', JSON.stringify(payload));
- EOF
- VERSION="$VERSION" node build-release-payload.js
- curl -s -X POST "${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases" \
- -H "Authorization: token ${{ secrets.GITEATOKEN }}" \
- -H "Content-Type: application/json" \
- -d @payload.json > release.json
- cat release.json
- RELEASE_ID=$(node -e "const r=require('./release.json'); if(!r.id) { console.error('No release id in response:', JSON.stringify(r)); process.exit(1); } console.log(r.id)")
- echo "RELEASE_ID=${RELEASE_ID}" >> $GITHUB_ENV
-
- - name: Upload ZIP to Release
- run: |
- RELEASE_ID=${{ env.RELEASE_ID }}
- curl -s -X POST "${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases/${RELEASE_ID}/assets" \
- -H "Authorization: token ${{ secrets.GITEATOKEN }}" \
- -F "attachment=@${{ env.ZIP_FILE }}"
- echo "✅ Release ${{ gitea.ref_name }} published!"
-
- - name: Publish to Gitea Package Registry
- run: |
- VERSION=${{ gitea.ref_name }}
- curl -s -X PUT "https://git.juankibin.space/api/packages/kibin/generic/lexai-extension/${VERSION}/lexai-chrome-mv3-${VERSION}.zip" \
- -H "Authorization: token ${{ secrets.GITEATOKEN }}" \
- -T "${{ env.ZIP_FILE }}"
- echo "✅ Published lexai-chrome-mv3-${VERSION}.zip to package registry"
- echo "📦 Download: https://git.juankibin.space/kibin/LexAI/packages"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index cd8c23a..9597775 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -42,6 +42,19 @@ Shared code lives in `src/lib/`. **Do not** import `@lib/crypto` or `@lib/messag
- **VS Code:** build then F5 or Install from VSIX (`packages/vscode`).
- **CLI:** set `LEXAI_API_KEY`; never commit keys or put them in `~/.lexai/config.json`.
+## CI
+
+Gitea Actions runs one workflow per package (path-filtered):
+
+| Workflow | Package |
+| --- | --- |
+| `.gitea/workflows/ci-chrome.yml` | `packages/chrome` (+ `src/lib`, `tests`) |
+| `.gitea/workflows/ci-vscode.yml` | `packages/vscode` (+ `src/lib`) |
+| `.gitea/workflows/ci-cli.yml` | `packages/cli` (+ `src/lib`) |
+| `.gitea/workflows/ci-website.yml` | `packages/website` |
+
+Deploy / preview / release workflows are not enabled yet.
+
## Pull request process
1. Fork / branch from `main` (or `develop` if that is the active integration branch).
diff --git a/README.md b/README.md
index c27421f..5511ae6 100644
--- a/README.md
+++ b/README.md
@@ -5,8 +5,9 @@
| Package | What it is | Docs |
| --- | --- | --- |
| **[packages/chrome](packages/chrome)** | Chrome MV3 extension — select text on any page → Fix / Rephrase / Shorten / Expand / Explain / Make Prompt | [README](packages/chrome/README.md) · [Chrome Web Store](https://chromewebstore.google.com/detail/bagpcheidbkfgijnnmolnkgagibbjfnk) |
-| **[packages/vscode](packages/vscode)** | VS Code / Cursor extension — writing actions + workspace-aware **Code Assist** | [README](packages/vscode/README.md) · [Deploy](packages/vscode/DEPLOY.md) |
+| **[packages/vscode](packages/vscode)** | VS Code / Cursor extension — writing actions + workspace-aware **Code Assist** | [README](packages/vscode/README.md) · [Marketplace](https://marketplace.visualstudio.com/items?itemName=JuanKibin.lexai-vscode) |
| **[packages/cli](packages/cli)** | CLI **Prompt Builder** — improve a rough idea into a paste-ready prompt | [README](packages/cli/README.md) |
+| **[packages/website](packages/website)** | Marketing site + docs (features & all packages) | [README](packages/website/README.md) |
Shared provider/prompt core: [`src/lib`](src/lib).
@@ -25,12 +26,17 @@ npm run chrome:dev
# VS Code extension
npm run vscode:build
-# then F5 / Install from VSIX — see packages/vscode/README.md
+# or install: https://marketplace.visualstudio.com/items?itemName=JuanKibin.lexai-vscode
# CLI Prompt Builder
npm run cli:build
-export LEXAI_API_KEY=sk-... # PowerShell: $env:LEXAI_API_KEY="sk-..."
+export LEXAI_API_KEY=sk-... # required; PowerShell: $env:LEXAI_API_KEY="sk-..."
+export LEXAI_PROVIDER=openai # optional: openai | anthropic | groq | openrouter
+export LEXAI_MODEL=gpt-4o # optional
node packages/cli/out/cli.js prompt "add rate limiting to the auth routes"
+
+# Website (features + docs)
+npm run website:dev
```
## Repository layout
@@ -40,7 +46,8 @@ LexAI/
├── packages/
│ ├── chrome/ # Manifest V3 extension (WXT + React)
│ ├── vscode/ # VS Code / Cursor extension
-│ └── cli/ # lexai prompt CLI
+│ ├── cli/ # lexai prompt CLI
+│ └── website/ # Marketing site + docs
├── src/lib/ # Shared providers, actions, types
├── tests/ # Unit + e2e tests (run via chrome package)
├── docs/ # Project operating docs (agents / planning)
@@ -56,7 +63,7 @@ OpenAI · Anthropic · Groq · OpenRouter
- No LexAI servers and no LexAI telemetry
- Chrome: key encrypted in `chrome.storage.local`
- VS Code: key in Secret Storage
-- CLI: key via `LEXAI_API_KEY` environment variable only
+- CLI: key via `LEXAI_API_KEY` only; optional `LEXAI_PROVIDER` / `LEXAI_MODEL` (never put the key in `~/.lexai/config.json`)
## Contributing
@@ -69,4 +76,5 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) and our [Code of Conduct](CODE_OF_CONDUCT
## Links
- Chrome Web Store: https://chromewebstore.google.com/detail/bagpcheidbkfgijnnmolnkgagibbjfnk
+- VS Code Marketplace: https://marketplace.visualstudio.com/items?itemName=JuanKibin.lexai-vscode
- Source: https://git.juankibin.space/kibin/LexAI
diff --git a/docs/DESIGN_SYSTEM.md b/docs/DESIGN_SYSTEM.md
new file mode 100644
index 0000000..97eee41
--- /dev/null
+++ b/docs/DESIGN_SYSTEM.md
@@ -0,0 +1,33 @@
+# Design system (LexAI website)
+
+## Principles
+
+- Quiet, professional, minimal. Typography and space do the work.
+- Fewest clicks to install or docs.
+- Privacy (BYO-key, no LexAI servers) is a first-class message.
+
+## Foundations
+
+| Token | Value |
+| --- | --- |
+| `--bg` | `#f7f7f5` |
+| `--surface` | `#ffffff` |
+| `--ink` | `#171717` |
+| `--muted` | `#5c5c57` |
+| `--line` | `#e6e6e2` |
+| `--indigo` | `#4f46e5` (logo tile) |
+| `--bolt` | `#f5c14a` (logo bolt) |
+| Mark | Flat rounded square + bolt (`LogoMark`) |
+| Type | Inter |
+
+Tap targets ≥ 40px; body text contrast AA.
+
+## Components
+
+| Component | Use |
+| --- | --- |
+| SiteHeader | Wordmark + Docs / Contribute / Community / Star on Gitea |
+| Hero | One kicker, one H1, one line, CTAs |
+| Feature list | Two-column hairline rows |
+| PackageCard | Chrome / VS Code / CLI |
+| DocsSection | Package docs blocks |
diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md
index 751b3fc..8a35bad 100644
--- a/docs/HANDOFF.md
+++ b/docs/HANDOFF.md
@@ -4,7 +4,8 @@
## Current state
-- **Outcome:** Monorepo OSS layout — Chrome in `packages/chrome`; VS Code + CLI alongside; root LICENSE/README/CONTRIBUTING/CODE_OF_CONDUCT; Gitea issue/PR templates.
-- **Verified:** run `npm run chrome:install` + typecheck/test/build after pull (session in progress).
-- **CI:** workflows use `packages/chrome` for install/build/zip paths.
-- **Next smallest action:** `npm run chrome:install && npm run chrome:typecheck && npm run chrome:test && npm run chrome:build` then commit when ready.
+- **Outcome:** `packages/website` now uses the four-page structure: Home, Docs, Contribute, Community.
+- **Home:** hero + features + Getting started (Chrome / VS Code / CLI) + privacy.
+- **Routes:** `/` `/docs` `/contribute` `/community` (HashRouter).
+- **Verified:** `website:typecheck` + `website:build` passed.
+- **Next smallest action:** Deploy `packages/website` on Coolify (base `/packages/website`, static, publish `/dist`).
diff --git a/docs/MEMORY.md b/docs/MEMORY.md
index 4e12587..932ae45 100644
--- a/docs/MEMORY.md
+++ b/docs/MEMORY.md
@@ -4,11 +4,11 @@
## Verified facts
-- LexAI monorepo: `packages/chrome` (WXT), `packages/vscode`, `packages/cli`; shared `src/lib`.
+- LexAI monorepo: `packages/{chrome,vscode,cli,website}`; shared `src/lib`.
- Portable core: `providers.ts`, `actions.ts`, `types.ts`. Chrome-only: `crypto.ts`, `messaging.ts`.
- Providers: OpenAI, Anthropic, Groq, OpenRouter — `callProvider` / `PROVIDER_SPECS`.
- VS Code: Secret Storage; Code Assist; prefs `lexai.*`.
-- CLI: `lexai prompt`; `LEXAI_API_KEY`; optional `~/.lexai/config.json` (no apiKey).
+- CLI: `lexai prompt`; `LEXAI_API_KEY` required; optional `LEXAI_PROVIDER` / `LEXAI_MODEL`; `~/.lexai/config.json` never holds apiKey.
- Chrome entrypoints: `packages/chrome/entrypoints/`; build → `packages/chrome/.output/chrome-mv3/`.
- Selection minimum: `MIN_SELECTION_LENGTH` (10). Chrome `onMessage` must `return true`; snapshot before `await`.
@@ -32,8 +32,10 @@
| Shared lib | `src/lib/` |
| VS Code | `packages/vscode/` |
| CLI | `packages/cli/` |
+| Website | `packages/website/` (Vite React; Home, Docs, Contribute, Community) |
| OSS meta | root `README.md`, `LICENSE`, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md` |
| Issue/PR templates | `.gitea/ISSUE_TEMPLATE/`, `.gitea/PULL_REQUEST_TEMPLATE.md` |
+| CI workflows | `.gitea/workflows/ci-{chrome,vscode,cli,website}.yml` (no deploy/release yet) |
## Expiring notes
diff --git a/docs/design/website.md b/docs/design/website.md
new file mode 100644
index 0000000..53ac2d4
--- /dev/null
+++ b/docs/design/website.md
@@ -0,0 +1,33 @@
+# Design spec — LexAI website
+
+## User + job
+
+Open-source visitors evaluating LexAI. Success: understand BYO-key privacy, pick Chrome / VS Code / CLI, reach install or docs in ≤ 2 clicks from the hero.
+
+## Flow
+
+Landing → Features → Packages → Docs (deep) or Chrome Web Store / Git. Secondary: Docs nav anchors per package.
+
+## Screens
+
+| Route | Purpose |
+| --- | --- |
+| `/` | Brand hero, feature highlights, package cards, privacy, CTAs |
+| `/docs` | All packages + features, install pointers, shared core |
+
+States: static marketing (no loading/error beyond 404 → home link).
+
+## Visual
+
+- Brand **LexAI** is the hero signal; bolt mark from product icon.
+- Atmosphere: deep slate field + amber bolt accent (from icon gold), not purple-on-white.
+- Type: Syne (display) + Source Sans 3 (body).
+- No card grid in hero; package cards only where choosing a product is the interaction.
+
+## Acceptance
+
+1. First viewport: LexAI brand, one headline, one support line, CTA group, dominant visual.
+2. Features of writing + Code Assist + Prompt Builder called out.
+3. Docs lists Chrome, VS Code, CLI with feature bullets and links to store/repo READMEs.
+4. Works desktop + mobile.
+5. `npm run website:build` succeeds.
diff --git a/package.json b/package.json
index 14dac56..fb0ee0a 100644
--- a/package.json
+++ b/package.json
@@ -28,8 +28,13 @@
"cli:install": "npm install --prefix packages/cli",
"cli:build": "npm run compile --prefix packages/cli",
"cli:typecheck": "npm run typecheck --prefix packages/cli",
- "install:all": "npm run chrome:install && npm run vscode:install && npm run cli:install",
- "typecheck:all": "npm run chrome:typecheck && npm run vscode:typecheck && npm run cli:typecheck",
- "build:all": "npm run chrome:build && npm run vscode:build && npm run cli:build"
+ "website:install": "npm install --prefix packages/website",
+ "website:dev": "npm run dev --prefix packages/website",
+ "website:build": "npm run build --prefix packages/website",
+ "website:preview": "npm run preview --prefix packages/website",
+ "website:typecheck": "npm run typecheck --prefix packages/website",
+ "install:all": "npm run chrome:install && npm run vscode:install && npm run cli:install && npm run website:install",
+ "typecheck:all": "npm run chrome:typecheck && npm run vscode:typecheck && npm run cli:typecheck && npm run website:typecheck",
+ "build:all": "npm run chrome:build && npm run vscode:build && npm run cli:build && npm run website:build"
}
}
diff --git a/packages/chrome/vitest.config.ts b/packages/chrome/vitest.config.ts
index a4363c0..f92d064 100644
--- a/packages/chrome/vitest.config.ts
+++ b/packages/chrome/vitest.config.ts
@@ -1,17 +1,27 @@
import { fileURLToPath } from 'node:url';
import { defineConfig } from 'vitest/config';
+const repoRoot = fileURLToPath(new URL('../..', import.meta.url));
+const libRoot = fileURLToPath(new URL('../../src/lib', import.meta.url));
+const setupFile = fileURLToPath(new URL('../../tests/unit/setup.ts', import.meta.url));
+
export default defineConfig({
+ root: repoRoot,
resolve: {
alias: {
- '@lib': fileURLToPath(new URL('../../src/lib', import.meta.url)),
+ '@lib': libRoot,
+ },
+ },
+ server: {
+ fs: {
+ allow: [repoRoot],
},
},
test: {
environment: 'jsdom',
globals: true,
- setupFiles: ['../../tests/unit/setup.ts'],
- include: ['../../tests/unit/**/*.test.ts'],
- exclude: ['../../tests/e2e/**/*'],
+ setupFiles: [setupFile],
+ include: ['tests/unit/**/*.test.ts'],
+ exclude: ['tests/e2e/**/*', 'packages/**'],
},
});
diff --git a/packages/vscode/DEPLOY.md b/packages/vscode/DEPLOY.md
index e724499..10ff109 100644
--- a/packages/vscode/DEPLOY.md
+++ b/packages/vscode/DEPLOY.md
@@ -10,7 +10,15 @@
---
-## A. Install on your machine (sideload)
+## A. Install from the Marketplace (recommended)
+
+[LexAI on the Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=JuanKibin.lexai-vscode) — search **LexAI** or:
+
+```powershell
+code --install-extension JuanKibin.lexai-vscode
+```
+
+## B. Install on your machine (sideload)
### VS Code
diff --git a/packages/vscode/README.md b/packages/vscode/README.md
index caedd08..acc8495 100644
--- a/packages/vscode/README.md
+++ b/packages/vscode/README.md
@@ -78,7 +78,7 @@ Change in Settings or via option chips in the suggestion zone.
## Quick start
-1. Install this extension (Marketplace, Open VSX, or **Install from VSIX**).
+1. Install from the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=JuanKibin.lexai-vscode), or **Install from VSIX**.
2. Command Palette → **LexAI: Open Settings** → provider, model, API key.
3. Select text or code → click **LexAI** on the line → pick an action or **Code Assist**.
4. Review in the suggestion zone → Accept to replace the selection (or Copy from the sidebar / panel).
@@ -99,6 +99,7 @@ Install / publish details: [DEPLOY.md](./DEPLOY.md).
| | |
| --- | --- |
+| **This extension** | [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=JuanKibin.lexai-vscode) (`JuanKibin.lexai-vscode`) |
| **Chrome extension** | [Chrome Web Store](https://chromewebstore.google.com/detail/bagpcheidbkfgijnnmolnkgagibbjfnk) |
| **Source** | [LexAI repository](https://git.juankibin.space/kibin/LexAI) |
| **Publisher** | JuanKibin |
diff --git a/packages/website/README.md b/packages/website/README.md
new file mode 100644
index 0000000..2e97908
--- /dev/null
+++ b/packages/website/README.md
@@ -0,0 +1,50 @@
+# LexAI website
+
+Marketing site for the LexAI monorepo: Home, Docs, Contribute, Community.
+
+## Develop
+
+```bash
+# from repo root
+npm run website:install
+npm run website:dev
+```
+
+Or inside this package:
+
+```bash
+npm install
+npm run dev
+```
+
+## Build
+
+```bash
+npm run website:build
+# → packages/website/dist/
+```
+
+Preview production build: `npm run website:preview`.
+
+Uses HashRouter so the site works on static hosts without server-side rewrite rules.
+
+## Deploy on Coolify
+
+This package is a **static** Vite build (`dist/`). No LexAI API keys or env vars.
+
+1. In Coolify: **+ New** → **Resource** → **Private Repository (with deploy key)** (Gitea).
+2. Repo SSH URL, for example `git@git.juankibin.space:kibin/LexAI.git`. If Gitea SSH is not on port 22, use `ssh://git@git.juankibin.space:PORT/kibin/LexAI.git` (Coolify sometimes needs `ssh:///` with three slashes).
+3. Add Coolify’s public deploy key to the Gitea repo: **Settings → Deploy / Access Keys** (read-only).
+4. Branch: `dev` or `main`.
+5. **Build Pack:** Nixpacks.
+6. **Base Directory:** `/packages/website` — required. Root `npm run build` builds Chrome, not this site.
+7. Enable **Is it a static site?**
+8. **Publish Directory:** `/dist`
+9. **Install command** (if the build says `vite: not found`): `npm ci` (do not omit devDependencies — Vite lives there).
+10. Optional env: `NIXPACKS_NODE_VERSION=22`
+11. Optional **Watch Paths:** `packages/website` so Chrome/VS Code/CLI commits do not redeploy.
+12. Set the domain, **Deploy**, then open `https://your-domain/#/docs`.
+
+Auto-deploy: Coolify resource → **Webhooks** → copy the URL + secret into Gitea **Settings → Webhooks** (push events).
+
+Do not add `LEXAI_API_KEY` or any provider key to this Coolify app.
diff --git a/packages/website/index.html b/packages/website/index.html
new file mode 100644
index 0000000..956150c
--- /dev/null
+++ b/packages/website/index.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+ LexAI — BYO-LLM writing & code assist
+
+
+
+
+
+
diff --git a/packages/website/nixpacks.toml b/packages/website/nixpacks.toml
new file mode 100644
index 0000000..a99c45c
--- /dev/null
+++ b/packages/website/nixpacks.toml
@@ -0,0 +1,8 @@
+[phases.setup]
+nixPkgs = ["nodejs_22"]
+
+[phases.install]
+cmds = ["npm ci"]
+
+[phases.build]
+cmds = ["npm run build"]
diff --git a/packages/website/package-lock.json b/packages/website/package-lock.json
new file mode 100644
index 0000000..461505d
--- /dev/null
+++ b/packages/website/package-lock.json
@@ -0,0 +1,1951 @@
+{
+ "name": "lexai-website",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "lexai-website",
+ "version": "1.0.0",
+ "dependencies": {
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-router-dom": "^6.29.0"
+ },
+ "devDependencies": {
+ "@types/react": "^18.3.1",
+ "@types/react-dom": "^18.3.1",
+ "@vitejs/plugin-react": "^4.3.4",
+ "typescript": "^5.7.3",
+ "vite": "^6.1.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
+ "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
+ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-compilation-targets": "^7.29.7",
+ "@babel/helper-module-transforms": "^7.29.7",
+ "@babel/helpers": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz",
+ "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.8",
+ "@babel/types": "^7.29.8",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+ "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.29.7",
+ "@babel/helper-validator-option": "^7.29.7",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
+ "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+ "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
+ "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz",
+ "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.8"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz",
+ "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz",
+ "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz",
+ "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.8",
+ "@babel/helper-globals": "^7.29.7",
+ "@babel/parser": "^7.29.8",
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.8",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz",
+ "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@napi-rs/lzma-linux-x64-gnu": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz",
+ "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^22.20 || ^24.12 || >=25"
+ }
+ },
+ "node_modules/@remix-run/router": {
+ "version": "1.23.3",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.3.tgz",
+ "integrity": "sha512-4An71tdz9X8+3sI4Qqqd2LWd9vS39J7sqd9EU4Scw7TJE/qB10Flv/UuqbPVgfQV9XoK8Np6jNquZitnZq5i+Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.27",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
+ "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz",
+ "integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz",
+ "integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz",
+ "integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz",
+ "integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz",
+ "integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz",
+ "integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz",
+ "integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz",
+ "integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz",
+ "integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz",
+ "integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz",
+ "integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz",
+ "integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz",
+ "integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz",
+ "integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz",
+ "integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz",
+ "integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz",
+ "integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.4.tgz",
+ "integrity": "sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.4.tgz",
+ "integrity": "sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz",
+ "integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz",
+ "integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz",
+ "integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz",
+ "integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz",
+ "integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz",
+ "integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/prop-types": {
+ "version": "15.7.15",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
+ "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/react": {
+ "version": "18.3.31",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz",
+ "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/prop-types": "*",
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "18.3.7",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
+ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^18.0.0"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
+ "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.28.0",
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+ "@rolldown/pluginutils": "1.0.0-beta.27",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.17.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ }
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.11.13",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.13.tgz",
+ "integrity": "sha512-k9HNuUVMlqVjQ9UHzfPjIqiDbWw7WqT1AoT7GL8VwvF3r0ZfArtgiSPAlmupyNquNgOJHTuH4CKYf8ttMTWBTQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.8",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz",
+ "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.11.12",
+ "caniuse-lite": "^1.0.30001809",
+ "electron-to-chromium": "^1.5.402",
+ "node-releases": "^2.0.53",
+ "update-browserslist-db": "^1.3.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001809",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz",
+ "integrity": "sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.405",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.405.tgz",
+ "integrity": "sha512-bNglH7lPH5l+yHOes7Zr4VqxhOy4BQ9ZBUX4VdoFgxMpzJk7W1ZoO3Vgd9Pxa9PyjQ76sfm2aKH/nzEcCNRlew==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "license": "MIT"
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.18",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
+ "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.53",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz",
+ "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.26",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz",
+ "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.17",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/react": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.2"
+ },
+ "peerDependencies": {
+ "react": "^18.3.1"
+ }
+ },
+ "node_modules/react-refresh": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
+ "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-router": {
+ "version": "6.30.4",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.4.tgz",
+ "integrity": "sha512-SVUsDe+DybHM/WmYKIVYhZh1o5Dcuf16yM6WjG02Q9XVFMZIJyHYhwrr6bFBXZkVP6z69kNkMyBCujt8FaFLJA==",
+ "license": "MIT",
+ "dependencies": {
+ "@remix-run/router": "1.23.3"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.30.4",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.4.tgz",
+ "integrity": "sha512-q4HvNl+mmDdkS0g+MqiBZNteQJCuimWoOyHMy4T/RQLAn9Z29+E91QXRaxOujeMl2HTzRSS0KFPd7lxX3PjV0Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@remix-run/router": "1.23.3",
+ "react-router": "6.30.4"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz",
+ "integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.9"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@napi-rs/lzma-linux-x64-gnu": "1.5.1",
+ "@rollup/rollup-android-arm-eabi": "4.62.4",
+ "@rollup/rollup-android-arm64": "4.62.4",
+ "@rollup/rollup-darwin-arm64": "4.62.4",
+ "@rollup/rollup-darwin-x64": "4.62.4",
+ "@rollup/rollup-freebsd-arm64": "4.62.4",
+ "@rollup/rollup-freebsd-x64": "4.62.4",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.62.4",
+ "@rollup/rollup-linux-arm-musleabihf": "4.62.4",
+ "@rollup/rollup-linux-arm64-gnu": "4.62.4",
+ "@rollup/rollup-linux-arm64-musl": "4.62.4",
+ "@rollup/rollup-linux-loong64-gnu": "4.62.4",
+ "@rollup/rollup-linux-loong64-musl": "4.62.4",
+ "@rollup/rollup-linux-ppc64-gnu": "4.62.4",
+ "@rollup/rollup-linux-ppc64-musl": "4.62.4",
+ "@rollup/rollup-linux-riscv64-gnu": "4.62.4",
+ "@rollup/rollup-linux-riscv64-musl": "4.62.4",
+ "@rollup/rollup-linux-s390x-gnu": "4.62.4",
+ "@rollup/rollup-linux-x64-gnu": "4.62.4",
+ "@rollup/rollup-linux-x64-musl": "4.62.4",
+ "@rollup/rollup-openbsd-x64": "4.62.4",
+ "@rollup/rollup-openharmony-arm64": "4.62.4",
+ "@rollup/rollup-win32-arm64-msvc": "4.62.4",
+ "@rollup/rollup-win32-ia32-msvc": "4.62.4",
+ "@rollup/rollup-win32-x64-gnu": "4.62.4",
+ "@rollup/rollup-win32-x64-msvc": "4.62.4",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz",
+ "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "6.4.3",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz",
+ "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "jiti": ">=1.21.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ }
+ }
+}
diff --git a/packages/website/package.json b/packages/website/package.json
new file mode 100644
index 0000000..7bd220e
--- /dev/null
+++ b/packages/website/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "lexai-website",
+ "version": "1.0.0",
+ "private": true,
+ "type": "module",
+ "engines": {
+ "node": ">=22"
+ },
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc --noEmit && vite build",
+ "preview": "vite preview",
+ "typecheck": "tsc --noEmit"
+ },
+ "dependencies": {
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-router-dom": "^6.29.0"
+ },
+ "devDependencies": {
+ "@types/react": "^18.3.1",
+ "@types/react-dom": "^18.3.1",
+ "@vitejs/plugin-react": "^4.3.4",
+ "typescript": "^5.7.3",
+ "vite": "^6.1.0"
+ }
+}
diff --git a/packages/website/public/icon.svg b/packages/website/public/icon.svg
new file mode 100644
index 0000000..26a7b11
--- /dev/null
+++ b/packages/website/public/icon.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/packages/website/src/App.tsx b/packages/website/src/App.tsx
new file mode 100644
index 0000000..1ba9445
--- /dev/null
+++ b/packages/website/src/App.tsx
@@ -0,0 +1,25 @@
+import { Navigate, Route, Routes } from 'react-router-dom';
+import { SiteHeader } from './components/SiteHeader';
+import { SiteFooter } from './components/SiteFooter';
+import { HomePage } from './pages/HomePage';
+import { DocsPage } from './pages/DocsPage';
+import { ContributePage } from './pages/ContributePage';
+import { CommunityPage } from './pages/CommunityPage';
+
+export default function App() {
+ return (
+
+
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+
+
+ );
+}
diff --git a/packages/website/src/components/LogoMark.tsx b/packages/website/src/components/LogoMark.tsx
new file mode 100644
index 0000000..85bf170
--- /dev/null
+++ b/packages/website/src/components/LogoMark.tsx
@@ -0,0 +1,21 @@
+type LogoMarkProps = {
+ size?: number;
+ className?: string;
+};
+
+/** Flat LexAI mark — indigo tile + gold bolt, for the light site. */
+export function LogoMark({ size = 28, className }: LogoMarkProps) {
+ return (
+
+
+
+
+ );
+}
diff --git a/packages/website/src/components/SiteFooter.tsx b/packages/website/src/components/SiteFooter.tsx
new file mode 100644
index 0000000..4ee868a
--- /dev/null
+++ b/packages/website/src/components/SiteFooter.tsx
@@ -0,0 +1,20 @@
+import { Link } from 'react-router-dom';
+import { REPO } from '../links';
+
+export function SiteFooter() {
+ return (
+
+ );
+}
diff --git a/packages/website/src/components/SiteHeader.tsx b/packages/website/src/components/SiteHeader.tsx
new file mode 100644
index 0000000..8715888
--- /dev/null
+++ b/packages/website/src/components/SiteHeader.tsx
@@ -0,0 +1,28 @@
+import { Link, NavLink } from 'react-router-dom';
+import { LogoMark } from './LogoMark';
+import { REPO } from '../links';
+
+export function SiteHeader() {
+ return (
+
+ );
+}
diff --git a/packages/website/src/links.ts b/packages/website/src/links.ts
new file mode 100644
index 0000000..e0ebf02
--- /dev/null
+++ b/packages/website/src/links.ts
@@ -0,0 +1,9 @@
+export const CHROME_STORE =
+ 'https://chromewebstore.google.com/detail/bagpcheidbkfgijnnmolnkgagibbjfnk';
+export const VSCODE_MARKETPLACE =
+ 'https://marketplace.visualstudio.com/items?itemName=JuanKibin.lexai-vscode';
+export const REPO = 'https://git.juankibin.space/kibin/LexAI';
+export const REPO_ISSUES = `${REPO}/issues/new`;
+export const REPO_CONTRIBUTING = `${REPO}/src/branch/main/CONTRIBUTING.md`;
+export const REPO_CONDUCT = `${REPO}/src/branch/main/CODE_OF_CONDUCT.md`;
+export const REPO_LICENSE = `${REPO}/src/branch/main/LICENSE`;
diff --git a/packages/website/src/main.tsx b/packages/website/src/main.tsx
new file mode 100644
index 0000000..224bfab
--- /dev/null
+++ b/packages/website/src/main.tsx
@@ -0,0 +1,13 @@
+import { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+import { HashRouter } from 'react-router-dom';
+import App from './App';
+import './styles.css';
+
+createRoot(document.getElementById('root')!).render(
+
+
+
+
+ ,
+);
diff --git a/packages/website/src/pages/CommunityPage.tsx b/packages/website/src/pages/CommunityPage.tsx
new file mode 100644
index 0000000..390aa09
--- /dev/null
+++ b/packages/website/src/pages/CommunityPage.tsx
@@ -0,0 +1,83 @@
+import { Link } from 'react-router-dom';
+import {
+ CHROME_STORE,
+ REPO,
+ REPO_CONDUCT,
+ REPO_ISSUES,
+ REPO_LICENSE,
+ VSCODE_MARKETPLACE,
+} from '../links';
+
+export function CommunityPage() {
+ return (
+
+
+ Community
+
+ LexAI is maintained in the open. Use these links to install, report issues, and follow the
+ project.
+
+
+
+ Source & discussion
+
+
+
+
+
+
+ Who it’s for
+
+ People who already hold an LLM API key and want inline writing or prompt help without a
+ SaaS subscription. Developers using Chrome, VS Code / Cursor, or a terminal agent loop.
+
+
+
+
+
+
+ );
+}
diff --git a/packages/website/src/pages/ContributePage.tsx b/packages/website/src/pages/ContributePage.tsx
new file mode 100644
index 0000000..929e4c3
--- /dev/null
+++ b/packages/website/src/pages/ContributePage.tsx
@@ -0,0 +1,88 @@
+import { REPO, REPO_CONDUCT, REPO_CONTRIBUTING } from '../links';
+
+export function ContributePage() {
+ return (
+
+
+ Contribute
+
+ LexAI is a monorepo: Chrome, VS Code, CLI, website, and a shared library. Please follow
+ the{' '}
+
+ Code of Conduct
+
+ .
+
+
+
+ Ways to help
+
+ Bug reports with reproduction steps
+ Documentation fixes
+ Small, focused pull requests (one concern per PR)
+ Design discussion when a change is large or security-sensitive
+
+
+
+
+ Development setup
+ Node.js 22+ and npm 11+.
+ {`git clone https://git.juankibin.space/kibin/LexAI.git
+cd LexAI
+npm run install:all`}
+
+
+ npm run chrome:dev / chrome:test /{' '}
+ chrome:typecheck
+
+
+ npm run vscode:build / vscode:typecheck
+
+
+ npm run cli:build / cli:typecheck
+
+
+ npm run website:dev
+
+
+
+ Shared code lives in src/lib/. Do not import @lib/crypto or{' '}
+ @lib/messaging from VS Code or CLI.
+
+
+
+
+ Pull requests
+
+
+ Branch from main or develop.
+
+ Keep the change scoped. Update docs when behavior changes.
+ Run the checks for the package you touched.
+
+ Use the PR template in .gitea/PULL_REQUEST_TEMPLATE.md.
+
+ Never commit API keys, tokens, or personal data.
+
+
+
+
+ Security
+
+ If you find a vulnerability around API-key handling or data exfiltration, do not open a
+ public issue with exploit details. Contact the maintainer privately (repository owner /
+ Chrome Web Store listing).
+
+
+
+
+
+ );
+}
diff --git a/packages/website/src/pages/DocsPage.tsx b/packages/website/src/pages/DocsPage.tsx
new file mode 100644
index 0000000..bc14abb
--- /dev/null
+++ b/packages/website/src/pages/DocsPage.tsx
@@ -0,0 +1,278 @@
+import { useEffect } from 'react';
+import { Link } from 'react-router-dom';
+import { CHROME_STORE, REPO, VSCODE_MARKETPLACE } from '../links';
+
+function scrollToSection(id: string) {
+ document.getElementById(id)?.scrollIntoView({ behavior: 'smooth', block: 'start' });
+}
+
+export function DocsPage() {
+ useEffect(() => {
+ const fromPath = window.location.href.match(/docs[#/]+([\w-]+)/i)?.[1];
+ const target = fromPath && fromPath !== 'docs' ? fromPath : undefined;
+ if (target) {
+ window.setTimeout(() => scrollToSection(target), 80);
+ }
+ }, []);
+
+ const nav = [
+ ['overview', 'Overview'],
+ ['chrome', 'Chrome'],
+ ['vscode', 'VS Code'],
+ ['cli', 'CLI'],
+ ['shared', 'Shared core'],
+ ['install', 'Install'],
+ ['privacy', 'Privacy'],
+ ] as const;
+
+ return (
+
+
+ {nav.map(([id, label]) => (
+ scrollToSection(id)}>
+ {label}
+
+ ))}
+
+
+
+ Documentation
+
+ LexAI is an open-source, bring-your-own-LLM assistant. This page covers every package and
+ its features. Contributor setup lives on the{' '}
+ Contribute page.
+
+
+
+ Overview
+
+ Three clients share one provider/prompt core. There is no LexAI backend — you supply an
+ API key for OpenAI, Anthropic, Groq, or OpenRouter.
+
+
+
+ Chrome — writing help on any webpage
+
+
+ VS Code / Cursor — writing actions + workspace-aware Code Assist
+
+
+ CLI — Prompt Builder for the terminal
+
+
+
+
+
+ Chrome extension
+
+ Manifest V3. Select text on any page, run an action, Replace or Copy. API key encrypted
+ locally with tweetnacl secretbox.
+
+ Setup
+
+ Options page → choose provider (OpenAI, Anthropic, Groq, or OpenRouter) and paste your
+ API key. There are no environment variables.
+
+ Features
+
+ Fix Grammar, Rephrase, Shorten, Expand, Explain, Make Prompt
+ Writing styles: Formal, Casual, Academic, Creative, Concise
+ Floating toolbar, right-click context menu, popup editor
+ Prompt Builder — patterns, persona, output format
+ Providers: OpenAI · Anthropic · Groq · OpenRouter
+ Works in text boxes, email composers, forms, and contenteditable pages
+
+
+
+
+
+ VS Code / Cursor extension
+
+ Editor twin of LexAI. Select text or code → action or Code Assist → Accept / Regenerate
+ / Discard. Key lives in VS Code Secret Storage.
+
+
+ Published listing:{' '}
+
+ JuanKibin.lexai-vscode
+
+
+ Setup
+
+ Command Palette → LexAI: Open Settings → provider, model, and API key.
+ The key is stored in VS Code Secret Storage. There are no environment variables.
+
+ Code Assist
+
+ Freeform instruction in an inline prompt above the selection
+
+ Workspace context: surrounding code, imports, and symbol definitions from other files
+
+ Entry points: CodeLens, context menu, Command Palette, Activity Bar sidebar
+
+ Writing actions
+
+ Fix Grammar · Rephrase · Shorten · Expand · Explain · Make Prompt
+ Writing styles + Prompt Builder option chips in the suggestion zone
+ Suggestion UI: zone (default), side panel, or both
+ Status bar: ready / processing / not configured
+ Sidebar workspace panel for paste → run → Copy
+
+
+
+
+
+ CLI — Prompt Builder
+
+ Improves a rough idea into a paste-ready engineered prompt before you send it to another
+ agent.
+
+ Setup
+ Environment variables (PowerShell shown; bash uses export NAME=value):
+ {`$env:LEXAI_API_KEY = "sk-..." # required
+$env:LEXAI_PROVIDER = "openai" # optional: openai | anthropic | groq | openrouter
+$env:LEXAI_MODEL = "gpt-4o" # optional`}
+
+ Optional defaults file (never put the API key here): ~/.lexai/config.json
+
+ {`{
+ "provider": "openai",
+ "model": "gpt-4o",
+ "pattern": "role",
+ "persona": "Expert Developer",
+ "format": "Markdown",
+ "style": "Concise"
+}`}
+
+ Precedence: flags → config file → env → defaults. The key is always{' '}
+ LEXAI_API_KEY.
+
+ Features
+
+
+ Command: lexai prompt
+
+
+ Input from args, stdin, or -f/--file
+
+ Flags: pattern, persona, format, style, provider, model
+
+ Also --list (patterns, personas, formats, styles) and{' '}
+ --help
+
+ Engineered prompt on stdout (pipe-safe); progress and errors on stderr
+
+ Examples
+ {`lexai prompt "add rate limiting to the express auth routes"
+
+echo "debug flaky playwright on CI" | lexai prompt --pattern role
+
+lexai prompt -f draft.txt --format Markdown --model gpt-4o`}
+
+
+
+
+ Shared core
+
+
+ Location: src/lib (providers, actions,{' '}
+ types)
+
+ Chrome-only modules: crypto (key encryption), messaging helpers
+ VS Code and CLI must not import Chrome-only modules
+
+
+
+
+ Install
+ Published builds:
+
+ From source (Node.js 22+):
+ {`git clone https://git.juankibin.space/kibin/LexAI.git
+cd LexAI
+npm run install:all
+
+# Chrome
+npm run chrome:dev # or chrome:build → load packages/chrome/.output/chrome-mv3
+
+# VS Code
+npm run vscode:build # or install from Marketplace
+
+# CLI
+npm run cli:build
+export LEXAI_API_KEY=sk-... # required
+export LEXAI_PROVIDER=openai # optional: openai | anthropic | groq | openrouter
+export LEXAI_MODEL=gpt-4o # optional
+node packages/cli/out/cli.js prompt "your rough idea"
+
+# Website
+npm run website:dev`}
+
+
+
+ Privacy
+
+ No LexAI backend, account, or subscription
+ No LexAI telemetry
+
+ Chrome: encrypted key in chrome.storage.local
+
+ VS Code: Secret Storage
+
+ CLI: LEXAI_API_KEY required; optional LEXAI_PROVIDER and{' '}
+ LEXAI_MODEL. Never store the key in ~/.lexai/config.json
+
+ Text is sent only to the LLM provider you configure
+
+
+
+
+ );
+}
diff --git a/packages/website/src/pages/HomePage.tsx b/packages/website/src/pages/HomePage.tsx
new file mode 100644
index 0000000..3e9c0db
--- /dev/null
+++ b/packages/website/src/pages/HomePage.tsx
@@ -0,0 +1,175 @@
+import { Link, useNavigate } from 'react-router-dom';
+import { LogoMark } from '../components/LogoMark';
+import { CHROME_STORE, REPO, VSCODE_MARKETPLACE } from '../links';
+
+function openDocsSection(navigate: ReturnType, id: string) {
+ navigate('/docs');
+ window.setTimeout(() => {
+ document.getElementById(id)?.scrollIntoView({ behavior: 'smooth', block: 'start' });
+ }, 60);
+}
+
+const FEATURES = [
+ {
+ title: 'Your key. Your provider.',
+ body: 'OpenAI, Anthropic, Groq, or OpenRouter. No LexAI account or servers.',
+ },
+ {
+ title: 'Writing help, like Grammarly',
+ body: 'Select text on any page → Fix, Rephrase, Shorten, Expand, Explain, or Make Prompt.',
+ },
+ {
+ title: 'Coding assist, like Copilot',
+ body: 'VS Code and Cursor: select code, give an instruction, apply a suggestion with file context.',
+ },
+ {
+ title: 'Prompt Builder',
+ body: (
+ <>
+ Turn a rough idea into a paste-ready prompt — in the app or via lexai prompt.
+ >
+ ),
+ },
+ {
+ title: 'Private by design',
+ body: 'Keys stay on your device. Text goes only to the provider you choose.',
+ },
+ {
+ title: 'Open source (MIT)',
+ body: 'Chrome, VS Code, and CLI share one core. Audit it, fork it, ship your own build.',
+ },
+];
+
+export function HomePage() {
+ const navigate = useNavigate();
+
+ return (
+ <>
+
+
+
+
Bring your own LLM
+
Skip Grammarly and Copilot subscriptions
+
+ Writing help on any webpage. Coding assist in VS Code or Cursor. You bring the API key —
+ LexAI has no account, no servers, and no fee.
+
+
+
+
+
+
+
+
Why try it
+
The jobs you already pay for — without a second subscription
+
+ Same idea as the tools you know. You pay your LLM provider, not LexAI.
+
+
+
+
Instead of Grammarly
+
+ Fix grammar, rephrase, shorten, expand, or explain on any webpage. Your key stays on
+ your device.
+
+
Chrome extension
+
+
+
Instead of GitHub Copilot
+
+ Select code, type what you want, and apply a suggestion with context from other
+ files. Not tab-complete — instruct and review.
+
+
VS Code / Cursor
+
+
+
+
+
+
+
+
Product
+
What LexAI does
+
+ One family across browser, editor, and CLI. Same providers. No LexAI backend.
+
+
+ {FEATURES.map((f) => (
+
+ {f.title}
+ {f.body}
+
+ ))}
+
+
+
+
+
+
+
Getting started
+
Install in minutes
+
Published builds, or clone the repo if you want to develop.
+
+
+
+
+
+
+
Privacy
+
No LexAI cloud
+
+ Your API key never leaves your device except to the provider you configure.
+ No LexAI telemetry and no LexAI servers in the path.
+
+ Open source — inspect the shared core in src/lib.
+
+
+
+
+ >
+ );
+}
diff --git a/packages/website/src/styles.css b/packages/website/src/styles.css
new file mode 100644
index 0000000..455d937
--- /dev/null
+++ b/packages/website/src/styles.css
@@ -0,0 +1,536 @@
+:root {
+ --bg: #f7f7f5;
+ --surface: #ffffff;
+ --ink: #171717;
+ --muted: #5c5c57;
+ --line: #e6e6e2;
+ --indigo: #4f46e5;
+ --indigo-deep: #4338ca;
+ --bolt: #f5c14a;
+ --focus: #4f46e5;
+ --radius: 8px;
+ --font: 'Inter', 'Segoe UI', system-ui, sans-serif;
+ --mono: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
+ color-scheme: light;
+}
+
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+}
+
+html {
+ scroll-behavior: smooth;
+}
+
+body {
+ margin: 0;
+ min-height: 100vh;
+ font-family: var(--font);
+ font-size: 16px;
+ line-height: 1.6;
+ color: var(--ink);
+ background: var(--bg);
+ -webkit-font-smoothing: antialiased;
+}
+
+a {
+ color: var(--ink);
+ text-underline-offset: 0.15em;
+}
+
+a:hover {
+ color: var(--muted);
+}
+
+img {
+ max-width: 100%;
+ display: block;
+}
+
+button {
+ font: inherit;
+}
+
+.shell {
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+}
+
+main {
+ flex: 1;
+}
+
+.wrap {
+ width: min(920px, calc(100% - 2.5rem));
+ margin-inline: auto;
+}
+
+/* Header */
+.header {
+ position: sticky;
+ top: 0;
+ z-index: 20;
+ background: rgba(247, 247, 245, 0.86);
+ backdrop-filter: blur(10px);
+ border-bottom: 1px solid var(--line);
+}
+
+.header-inner {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1rem;
+ padding: 0.85rem 0;
+}
+
+.brand {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.5rem;
+ color: var(--ink);
+ text-decoration: none;
+ font-weight: 600;
+ font-size: 0.95rem;
+ letter-spacing: -0.02em;
+}
+
+.brand:hover {
+ color: var(--ink);
+}
+
+.brand svg {
+ display: block;
+ border-radius: 6px;
+}
+
+.nav {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 0.15rem 1.15rem;
+}
+
+.nav a:not(.btn),
+.nav-link-btn {
+ color: var(--muted);
+ text-decoration: none;
+ font-weight: 500;
+ font-size: 0.875rem;
+ background: none;
+ border: none;
+ padding: 0;
+ cursor: pointer;
+}
+
+.nav a:not(.btn):hover,
+.nav-link-btn:hover,
+.nav a[aria-current='page']:not(.btn) {
+ color: var(--ink);
+}
+
+/* Buttons */
+.btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 40px;
+ padding: 0.5rem 0.95rem;
+ border-radius: 6px;
+ border: 1px solid transparent;
+ font-weight: 550;
+ font-size: 0.875rem;
+ text-decoration: none;
+ cursor: pointer;
+ transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
+}
+
+.btn:focus-visible {
+ outline: 2px solid var(--focus);
+ outline-offset: 2px;
+}
+
+.btn-primary {
+ background: var(--indigo);
+ color: #fff;
+ border-color: var(--indigo);
+}
+
+.btn-primary:hover {
+ background: var(--indigo-deep);
+ color: var(--bolt);
+ border-color: var(--indigo-deep);
+}
+
+.btn-ghost {
+ background: transparent;
+ border-color: var(--line);
+ color: var(--ink);
+}
+
+.btn-ghost:hover {
+ border-color: #cfcfc9;
+ color: var(--ink);
+}
+
+/* Hero */
+.hero {
+ padding: clamp(4.5rem, 12vw, 7.5rem) 0 4rem;
+}
+
+.hero-lockup {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ margin: 0 0 1.5rem;
+}
+
+.hero-lockup svg {
+ display: block;
+ border-radius: 14px;
+}
+
+.hero-wordmark {
+ font-weight: 600;
+ font-size: 1.35rem;
+ letter-spacing: -0.03em;
+}
+
+.hero-wordmark span {
+ color: var(--indigo);
+}
+
+.hero-kicker {
+ margin: 0 0 1rem;
+ font-size: 0.75rem;
+ font-weight: 500;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--muted);
+}
+
+.hero h1 {
+ margin: 0 0 1rem;
+ font-weight: 560;
+ font-size: clamp(2rem, 4.6vw, 2.75rem);
+ line-height: 1.15;
+ letter-spacing: -0.035em;
+ max-width: 18ch;
+}
+
+.hero-lead {
+ margin: 0 0 1.75rem;
+ max-width: 42ch;
+ color: var(--muted);
+ font-size: 1.05rem;
+}
+
+.cta-row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.6rem;
+}
+
+/* Sections */
+.section {
+ padding: 3.5rem 0;
+}
+
+.section + .section {
+ border-top: 1px solid var(--line);
+}
+
+.section-kicker {
+ margin: 0 0 0.4rem;
+ font-size: 0.75rem;
+ font-weight: 500;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--indigo);
+}
+
+.section h2 {
+ margin: 0 0 0.5rem;
+ font-weight: 560;
+ font-size: 1.45rem;
+ letter-spacing: -0.025em;
+}
+
+.section-lead {
+ margin: 0 0 2rem;
+ max-width: 48ch;
+ color: var(--muted);
+}
+
+.feature-list {
+ display: grid;
+ gap: 0;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+@media (min-width: 700px) {
+ .feature-list {
+ grid-template-columns: 1fr 1fr;
+ column-gap: 3rem;
+ }
+}
+
+.feature-list li {
+ padding: 1.15rem 0;
+ border-top: 1px solid var(--line);
+}
+
+.feature-list h3 {
+ margin: 0 0 0.3rem;
+ font-size: 0.98rem;
+ font-weight: 600;
+}
+
+.feature-list p {
+ margin: 0;
+ color: var(--muted);
+ font-size: 0.92rem;
+}
+
+.compare-grid {
+ display: grid;
+ gap: 0.75rem;
+}
+
+@media (min-width: 700px) {
+ .compare-grid {
+ grid-template-columns: 1fr 1fr;
+ }
+}
+
+.package-grid {
+ display: grid;
+ gap: 0.75rem;
+}
+
+@media (min-width: 760px) {
+ .package-grid {
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
+
+a.package-card {
+ text-decoration: none;
+}
+
+.package-card {
+ display: flex;
+ flex-direction: column;
+ gap: 0.55rem;
+ padding: 1.2rem 1.15rem 1.25rem;
+ border-radius: var(--radius);
+ background: var(--surface);
+ border: 1px solid var(--line);
+ color: inherit;
+ min-height: 100%;
+ text-align: left;
+ cursor: pointer;
+ width: 100%;
+}
+
+.package-card:hover {
+ border-color: #c8c8c2;
+}
+
+.package-card strong {
+ font-size: 0.98rem;
+ font-weight: 600;
+}
+
+.package-card p {
+ margin: 0;
+ flex: 1;
+ color: var(--muted);
+ font-size: 0.9rem;
+}
+
+.package-card .more {
+ font-weight: 500;
+ font-size: 0.82rem;
+ color: var(--ink);
+}
+
+.privacy {
+ padding-top: 0.25rem;
+}
+
+.privacy h2 {
+ margin-top: 0;
+}
+
+.privacy ul {
+ margin: 0.75rem 0 0;
+ padding-left: 1.1rem;
+ color: var(--muted);
+}
+
+/* Inner pages (Contribute, Community) */
+.page {
+ padding: 2.75rem 0 4.5rem;
+}
+
+.docs-block ol {
+ margin: 0.45rem 0 0;
+ padding-left: 1.15rem;
+ color: var(--muted);
+}
+
+.docs-block ol li + li {
+ margin-top: 0.22rem;
+}
+
+/* Docs */
+.docs-layout {
+ display: grid;
+ gap: 2.25rem;
+ padding: 2.75rem 0 4.5rem;
+}
+
+@media (min-width: 860px) {
+ .docs-layout {
+ grid-template-columns: 160px 1fr;
+ gap: 3rem;
+ align-items: start;
+ }
+}
+
+.docs-nav {
+ position: sticky;
+ top: 4.25rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.15rem;
+}
+
+.docs-nav .nav-link-btn {
+ color: var(--muted);
+ font-weight: 500;
+ font-size: 0.875rem;
+ padding: 0.3rem 0;
+ text-align: left;
+ background: none;
+ border: none;
+ cursor: pointer;
+}
+
+.docs-nav .nav-link-btn:hover {
+ color: var(--ink);
+}
+
+.docs-content h1 {
+ margin: 0 0 0.65rem;
+ font-weight: 560;
+ font-size: clamp(1.75rem, 3.5vw, 2.15rem);
+ letter-spacing: -0.03em;
+}
+
+.docs-content .intro {
+ margin: 0 0 1.75rem;
+ color: var(--muted);
+ max-width: 58ch;
+}
+
+.docs-block {
+ scroll-margin-top: 5rem;
+ padding: 1.6rem 0;
+ border-top: 1px solid var(--line);
+}
+
+.docs-block h2 {
+ margin: 0 0 0.45rem;
+ font-weight: 560;
+ font-size: 1.25rem;
+ letter-spacing: -0.02em;
+}
+
+.docs-block h3 {
+ margin: 1.15rem 0 0.35rem;
+ font-size: 0.82rem;
+ font-weight: 600;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+ color: var(--muted);
+}
+
+.docs-block > p {
+ color: var(--muted);
+ margin: 0;
+}
+
+.docs-block ul {
+ margin: 0.45rem 0 0;
+ padding-left: 1.15rem;
+ color: var(--muted);
+}
+
+.docs-block li + li {
+ margin-top: 0.22rem;
+}
+
+.docs-meta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ margin-top: 1rem;
+}
+
+code,
+.mono {
+ font-family: var(--mono);
+ font-size: 0.86em;
+ color: var(--ink);
+ background: #eeebe6;
+ padding: 0.08em 0.32em;
+ border-radius: 4px;
+}
+
+pre.mono {
+ display: block;
+ padding: 0.95rem 1rem;
+ overflow-x: auto;
+ border-radius: 6px;
+ border: 1px solid var(--line);
+ background: var(--surface);
+ line-height: 1.5;
+}
+
+/* Footer */
+.footer {
+ border-top: 1px solid var(--line);
+ padding: 1.35rem 0 1.75rem;
+ color: var(--muted);
+ font-size: 0.82rem;
+}
+
+.footer-inner {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.65rem 1.25rem;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.footer a {
+ color: var(--muted);
+ text-decoration: none;
+ font-weight: 500;
+}
+
+.footer a:hover {
+ color: var(--ink);
+}
+
+@media (max-width: 640px) {
+ .nav .hide-sm {
+ display: none;
+ }
+}
diff --git a/packages/website/src/vite-env.d.ts b/packages/website/src/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/packages/website/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/packages/website/tsconfig.json b/packages/website/tsconfig.json
new file mode 100644
index 0000000..6b7b651
--- /dev/null
+++ b/packages/website/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "useDefineForClassFields": true,
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "moduleResolution": "Bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "noEmitOnError": true
+ },
+ "include": ["src"]
+}
diff --git a/packages/website/tsconfig.node.json b/packages/website/tsconfig.node.json
new file mode 100644
index 0000000..60e6d5c
--- /dev/null
+++ b/packages/website/tsconfig.node.json
@@ -0,0 +1,12 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "lib": ["ES2023"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "moduleResolution": "Bundler",
+ "strict": true,
+ "noEmit": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/packages/website/vite.config.ts b/packages/website/vite.config.ts
new file mode 100644
index 0000000..01fbbfd
--- /dev/null
+++ b/packages/website/vite.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+
+export default defineConfig({
+ plugins: [react()],
+ base: './',
+});