diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
index 4fd1ebed..64231bb3 100644
--- a/.github/workflows/deploy-docs.yml
+++ b/.github/workflows/deploy-docs.yml
@@ -1,12 +1,12 @@
-name: Deploy VitePress Docs
+name: Deploy React Site
on:
push:
branches: [main]
paths:
- 'docs/**'
+ - 'site/**'
- 'package.json'
- - 'package-lock.json'
- '.github/workflows/deploy-docs.yml'
workflow_dispatch:
@@ -33,20 +33,21 @@ jobs:
with:
node-version: 22
cache: npm
+ cache-dependency-path: site/package-lock.json
- name: Setup Pages
uses: actions/configure-pages@v4
- - name: Install dependencies
- run: npm ci
+ - name: Install site dependencies
+ run: npm --prefix site ci
- - name: Build with VitePress
- run: npm run docs:build
+ - name: Build React site
+ run: npm --prefix site run build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
- path: docs/.vitepress/dist
+ path: site/dist
deploy:
environment:
diff --git a/.github/workflows/pr-quality.yml b/.github/workflows/pr-quality.yml
index 3565d9b9..21acdc62 100644
--- a/.github/workflows/pr-quality.yml
+++ b/.github/workflows/pr-quality.yml
@@ -245,8 +245,9 @@ jobs:
with:
node-version: 22
cache: npm
+ cache-dependency-path: site/package-lock.json
- name: Run docs checks
- run: npm ci && npm run docs:build
+ run: npm --prefix site ci && npm --prefix site run build && npm --prefix site run check
coverage-checks:
name: coverage-checks
diff --git a/.gitignore b/.gitignore
index 18eaf123..81c3d1cb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -64,9 +64,6 @@ credentials*.json
# Temp markdown snapshots
sidebar-snapshot.md
-# VitePress
-docs/.vitepress/dist
-docs/.vitepress/cache
runtime/__pycache__
# IDE
diff --git a/AGENTS.md b/AGENTS.md
index 616a5355..a1eec78f 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -18,7 +18,7 @@ Rules closer to the code take precedence. Before editing `.github/`, `src/`, `de
- `src/`: CLI, Ink UI, commands, services, tools, shared runtime utilities, and the local API/WebSocket server.
- `desktop/`: React desktop UI, Electron host, native/sidecar resources, and desktop build scripts.
- `adapters/`: Telegram, Feishu, WeChat, DingTalk, and shared IM adapter utilities.
-- `docs/` and `docs/en/`: VitePress documentation; keep Chinese and English counterparts aligned when both exist.
+- `site/`: React documentation site and build tooling. `docs/` and `docs/en/` are its Chinese and English Markdown content sources; keep counterparts aligned when both exist.
- `.github/workflows/`, `scripts/pr/`, and `scripts/quality-gate/`: CI routing and quality policy.
- `release-notes/`, `scripts/release.ts`, and `.github/workflows/release-desktop.yml`: desktop release automation.
diff --git a/bun.lock b/bun.lock
index 8cb5e492..8f4c5574 100644
--- a/bun.lock
+++ b/bun.lock
@@ -73,9 +73,6 @@
},
"devDependencies": {
"mermaid": "^11.14.0",
- "vitepress": "^1.6.3",
- "vitepress-plugin-mermaid": "^2.0.17",
- "vue": "^3.5.13",
},
},
},
@@ -85,42 +82,6 @@
"packages": {
"@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.2.5", "https://registry.npmmirror.com/@alcalzone/ansi-tokenize/-/ansi-tokenize-0.2.5.tgz", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-3NX/MpTdroi0aKz134A6RC2Gb2iXVECN4QaAXnvCIxxIm3C3AVB1mkUe8NaaiyvOpDfsrqWhYtj+Q6a62RrTsw=="],
- "@algolia/abtesting": ["@algolia/abtesting@1.16.1", "https://registry.npmmirror.com/@algolia/abtesting/-/abtesting-1.16.1.tgz", { "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" } }, "sha512-Xxk4l00pYI+jE0PNw8y0MvsQWh5278WRtZQav8/BMMi3HKi2xmeuqe11WJ3y8/6nuBHdv39w76OpJb09TMfAVQ=="],
-
- "@algolia/autocomplete-core": ["@algolia/autocomplete-core@1.17.7", "https://registry.npmmirror.com/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", { "dependencies": { "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", "@algolia/autocomplete-shared": "1.17.7" } }, "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q=="],
-
- "@algolia/autocomplete-plugin-algolia-insights": ["@algolia/autocomplete-plugin-algolia-insights@1.17.7", "https://registry.npmmirror.com/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", { "dependencies": { "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A=="],
-
- "@algolia/autocomplete-preset-algolia": ["@algolia/autocomplete-preset-algolia@1.17.7", "https://registry.npmmirror.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", { "dependencies": { "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA=="],
-
- "@algolia/autocomplete-shared": ["@algolia/autocomplete-shared@1.17.7", "https://registry.npmmirror.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", { "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg=="],
-
- "@algolia/client-abtesting": ["@algolia/client-abtesting@5.50.1", "https://registry.npmmirror.com/@algolia/client-abtesting/-/client-abtesting-5.50.1.tgz", { "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" } }, "sha512-4peZlPXMwTOey9q1rQKMdCnwZb/E95/1e+7KujXpLLSh0FawJzg//U2NM+r4AiJy4+naT2MTBhj0K30yshnVTA=="],
-
- "@algolia/client-analytics": ["@algolia/client-analytics@5.50.1", "https://registry.npmmirror.com/@algolia/client-analytics/-/client-analytics-5.50.1.tgz", { "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" } }, "sha512-i+aWHHG8NZvGFHtPeMZkxL2Loc6Fm7iaRo15lYSMx8gFL+at9vgdWxhka7mD1fqxkrxXsQstUBCIsSY8FvkEOw=="],
-
- "@algolia/client-common": ["@algolia/client-common@5.50.1", "https://registry.npmmirror.com/@algolia/client-common/-/client-common-5.50.1.tgz", {}, "sha512-Hw52Fwapyk/7hMSV/fI4+s3H9MGZEUcRh4VphyXLAk2oLYdndVUkc6KBi0zwHSzwPAr+ZBwFPe2x6naUt9mZGw=="],
-
- "@algolia/client-insights": ["@algolia/client-insights@5.50.1", "https://registry.npmmirror.com/@algolia/client-insights/-/client-insights-5.50.1.tgz", { "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" } }, "sha512-Bn/wtwhJ7p1OD/6pY+Zzn+zlu2N/SJnH46md/PAbvqIzmjVuwjNwD4y0vV5Ov8naeukXdd7UU9v550+v8+mtlg=="],
-
- "@algolia/client-personalization": ["@algolia/client-personalization@5.50.1", "https://registry.npmmirror.com/@algolia/client-personalization/-/client-personalization-5.50.1.tgz", { "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" } }, "sha512-0V4Tu0RWR8YxkgI9EPVOZHGE4K5pEIhkLNN0CTkP/rnPsqaaSQpNMYW3/mGWdiKOWbX0iVmwLB9QESk3H0jS5g=="],
-
- "@algolia/client-query-suggestions": ["@algolia/client-query-suggestions@5.50.1", "https://registry.npmmirror.com/@algolia/client-query-suggestions/-/client-query-suggestions-5.50.1.tgz", { "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" } }, "sha512-jofcWNYMXJDDr87Z2eivlWY6o71Zn7F7aOvQCXSDAo9QTlyf7BhXEsZymLUvF0O1yU9Q9wvrjAWn8uVHYnAvgw=="],
-
- "@algolia/client-search": ["@algolia/client-search@5.50.1", "https://registry.npmmirror.com/@algolia/client-search/-/client-search-5.50.1.tgz", { "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" } }, "sha512-OteRb8WubcmEvU0YlMJwCXs3Q6xrdkb0v50/qZBJP1TF0CvujFZQM++9BjEkTER/Jr9wbPHvjSFKnbMta0b4dQ=="],
-
- "@algolia/ingestion": ["@algolia/ingestion@1.50.1", "https://registry.npmmirror.com/@algolia/ingestion/-/ingestion-1.50.1.tgz", { "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" } }, "sha512-0GmfSgDQK6oiIVXnJvGxtNFOfosBspRTR7csCOYCTL1P8QtxX2vDCIKwTM7xdSAEbJaZ43QlWg25q0Qdsndz8Q=="],
-
- "@algolia/monitoring": ["@algolia/monitoring@1.50.1", "https://registry.npmmirror.com/@algolia/monitoring/-/monitoring-1.50.1.tgz", { "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" } }, "sha512-ySuigKEe4YjYV3si8NVk9BHQpFj/1B+ON7DhhvTvbrZJseHQQloxzq0yHwKmznSdlO6C956fx4pcfOKkZClsyg=="],
-
- "@algolia/recommend": ["@algolia/recommend@5.50.1", "https://registry.npmmirror.com/@algolia/recommend/-/recommend-5.50.1.tgz", { "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" } }, "sha512-Cp8T/B0gVmjFlzzp6eP47hwKh5FGyeqQp1N48/ANDdvdiQkPqLyFHQVDwLBH0LddfIPQE+yqmZIgmKc82haF4A=="],
-
- "@algolia/requester-browser-xhr": ["@algolia/requester-browser-xhr@5.50.1", "https://registry.npmmirror.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.50.1.tgz", { "dependencies": { "@algolia/client-common": "5.50.1" } }, "sha512-XKdGGLikfrlK66ZSXh/vWcXZZ8Vg3byDFbJD8pwEvN1FoBRGxhxya476IY2ohoTymLa4qB5LBRlIa+2TLHx3Uw=="],
-
- "@algolia/requester-fetch": ["@algolia/requester-fetch@5.50.1", "https://registry.npmmirror.com/@algolia/requester-fetch/-/requester-fetch-5.50.1.tgz", { "dependencies": { "@algolia/client-common": "5.50.1" } }, "sha512-mBAU6WyVsDwhHyGM+nodt1/oebHxgvuLlOAoMGbj/1i6LygDHZWDgL1t5JEs37x9Aywv7ZGhqbM1GsfZ54sU6g=="],
-
- "@algolia/requester-node-http": ["@algolia/requester-node-http@5.50.1", "https://registry.npmmirror.com/@algolia/requester-node-http/-/requester-node-http-5.50.1.tgz", { "dependencies": { "@algolia/client-common": "5.50.1" } }, "sha512-qmo1LXrNKLHvJE6mdQbLnsZAoZvj7VyF2ft4xmbSGWI2WWm87fx/CjUX4kEExt4y0a6T6nEts6ofpUfH5TEE1A=="],
-
"@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "https://registry.npmmirror.com/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="],
"@anthropic-ai/sandbox-runtime": ["@anthropic-ai/sandbox-runtime@0.0.44", "https://registry.npmmirror.com/@anthropic-ai/sandbox-runtime/-/sandbox-runtime-0.0.44.tgz", { "dependencies": { "@pondwader/socks5-server": "^1.0.10", "@types/lodash-es": "^4.17.12", "commander": "^12.1.0", "lodash-es": "^4.17.23", "shell-quote": "^1.8.3", "zod": "^3.24.1" }, "bin": { "srt": "dist/cli.js" } }, "sha512-mmyjq0mzsHnQZyiU+FGYyaiJcPckuQpP78VB8iqFi2IOu8rcb9i5SmaOKyJENJNfY8l/1grzLMQgWq4Apvmozw=="],
@@ -193,16 +154,8 @@
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.4", "https://registry.npmmirror.com/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz", {}, "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ=="],
- "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
-
- "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
-
- "@babel/parser": ["@babel/parser@7.29.2", "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.2.tgz", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="],
-
"@babel/runtime": ["@babel/runtime@7.29.2", "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.29.2.tgz", {}, "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="],
- "@babel/types": ["@babel/types@7.29.0", "https://registry.npmmirror.com/@babel/types/-/types-7.29.0.tgz", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
-
"@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.2", "https://registry.npmmirror.com/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", {}, "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA=="],
"@chevrotain/cst-dts-gen": ["@chevrotain/cst-dts-gen@12.0.0", "https://registry.npmmirror.com/@chevrotain/cst-dts-gen/-/cst-dts-gen-12.0.0.tgz", { "dependencies": { "@chevrotain/gast": "12.0.0", "@chevrotain/types": "12.0.0" } }, "sha512-fSL4KXjTl7cDgf0B5Rip9Q05BOrYvkJV/RrBTE/bKDN096E4hN/ySpcBK5B24T76dlQ2i32Zc3PAE27jFnFrKg=="],
@@ -217,72 +170,14 @@
"@commander-js/extra-typings": ["@commander-js/extra-typings@14.0.0", "https://registry.npmmirror.com/@commander-js/extra-typings/-/extra-typings-14.0.0.tgz", { "peerDependencies": { "commander": "~14.0.0" } }, "sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg=="],
- "@docsearch/css": ["@docsearch/css@3.8.2", "https://registry.npmmirror.com/@docsearch/css/-/css-3.8.2.tgz", {}, "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ=="],
-
- "@docsearch/js": ["@docsearch/js@3.8.2", "https://registry.npmmirror.com/@docsearch/js/-/js-3.8.2.tgz", { "dependencies": { "@docsearch/react": "3.8.2", "preact": "^10.0.0" } }, "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ=="],
-
- "@docsearch/react": ["@docsearch/react@3.8.2", "https://registry.npmmirror.com/@docsearch/react/-/react-3.8.2.tgz", { "dependencies": { "@algolia/autocomplete-core": "1.17.7", "@algolia/autocomplete-preset-algolia": "1.17.7", "@docsearch/css": "3.8.2", "algoliasearch": "^5.14.2" }, "peerDependencies": { "@types/react": ">= 16.8.0 < 19.0.0", "react": ">= 16.8.0 < 19.0.0", "react-dom": ">= 16.8.0 < 19.0.0", "search-insights": ">= 1 < 3" }, "optionalPeers": ["@types/react", "react", "react-dom", "search-insights"] }, "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg=="],
-
- "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
-
- "@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],
-
- "@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="],
-
- "@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="],
-
- "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="],
-
- "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="],
-
- "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="],
-
- "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="],
-
- "@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="],
-
- "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="],
-
- "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="],
-
- "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="],
-
- "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="],
-
- "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="],
-
- "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="],
-
- "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="],
-
- "@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="],
-
- "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="],
-
- "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="],
-
- "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="],
-
- "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="],
-
- "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="],
-
- "@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
-
"@growthbook/growthbook": ["@growthbook/growthbook@1.6.5", "https://registry.npmmirror.com/@growthbook/growthbook/-/growthbook-1.6.5.tgz", { "dependencies": { "dom-mutator": "^0.6.0" } }, "sha512-mUaMsgeUTpRIUOTn33EUXHRK6j7pxBjwqH4WpQyq+pukjd1AIzWlEa6w7i6bInJUcweGgP2beXZmaP6b6UPn7A=="],
"@hono/node-server": ["@hono/node-server@1.19.12", "https://registry.npmmirror.com/@hono/node-server/-/node-server-1.19.12.tgz", { "peerDependencies": { "hono": "^4" } }, "sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw=="],
- "@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.77", "https://registry.npmmirror.com/@iconify-json/simple-icons/-/simple-icons-1.2.77.tgz", { "dependencies": { "@iconify/types": "*" } }, "sha512-oaENvo6C3BkAEWMlcQA3XemxU9v2SFOTlApSUCODAkIu1haeLCjzrmH3HgmGqjRnJjM+LevO8sA+MgdMHBFBDA=="],
-
"@iconify/types": ["@iconify/types@2.0.0", "https://registry.npmmirror.com/@iconify/types/-/types-2.0.0.tgz", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
"@iconify/utils": ["@iconify/utils@3.1.0", "https://registry.npmmirror.com/@iconify/utils/-/utils-3.1.0.tgz", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="],
- "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
-
- "@mermaid-js/mermaid-mindmap": ["@mermaid-js/mermaid-mindmap@9.3.0", "https://registry.npmmirror.com/@mermaid-js/mermaid-mindmap/-/mermaid-mindmap-9.3.0.tgz", { "dependencies": { "@braintree/sanitize-url": "^6.0.0", "cytoscape": "^3.23.0", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.1.0", "d3": "^7.0.0", "khroma": "^2.0.0", "non-layered-tidy-tree-layout": "^2.0.2" } }, "sha512-IhtYSVBBRYviH1Ehu8gk69pMDF8DSRqXBRDMWrEfHoaMruHeaP2DXA3PBnuwsMaCdPQhlUUcy/7DBLAEIXvCAw=="],
-
"@mermaid-js/parser": ["@mermaid-js/parser@1.1.0", "https://registry.npmmirror.com/@mermaid-js/parser/-/parser-1.1.0.tgz", { "dependencies": { "langium": "^4.0.0" } }, "sha512-gxK9ZX2+Fex5zu8LhRQoMeMPEHbc73UKZ0FQ54YrQtUxE1VVhMwzeNtKRPAu5aXks4FasbMe4xB4bWrmq6Jlxw=="],
"@mixmark-io/domino": ["@mixmark-io/domino@2.2.0", "https://registry.npmmirror.com/@mixmark-io/domino/-/domino-2.2.0.tgz", {}, "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw=="],
@@ -307,74 +202,8 @@
"@pondwader/socks5-server": ["@pondwader/socks5-server@1.0.10", "https://registry.npmmirror.com/@pondwader/socks5-server/-/socks5-server-1.0.10.tgz", {}, "sha512-bQY06wzzR8D2+vVCUoBsr5QS2U6UgPUQRmErNwtsuI6vLcyRKkafjkr3KxbtGFf9aBBIV2mcvlsKD1UYaIV+sg=="],
- "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", { "os": "android", "cpu": "arm" }, "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA=="],
-
- "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", { "os": "android", "cpu": "arm64" }, "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA=="],
-
- "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw=="],
-
- "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew=="],
-
- "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w=="],
-
- "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g=="],
-
- "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g=="],
-
- "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg=="],
-
- "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ=="],
-
- "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA=="],
-
- "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ=="],
-
- "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw=="],
-
- "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw=="],
-
- "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg=="],
-
- "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg=="],
-
- "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg=="],
-
- "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ=="],
-
- "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg=="],
-
- "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w=="],
-
- "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw=="],
-
- "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", { "os": "none", "cpu": "arm64" }, "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA=="],
-
- "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g=="],
-
- "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg=="],
-
- "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", { "os": "win32", "cpu": "x64" }, "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg=="],
-
- "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", { "os": "win32", "cpu": "x64" }, "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ=="],
-
"@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "https://registry.npmmirror.com/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="],
- "@shikijs/core": ["@shikijs/core@2.5.0", "https://registry.npmmirror.com/@shikijs/core/-/core-2.5.0.tgz", { "dependencies": { "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" } }, "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg=="],
-
- "@shikijs/engine-javascript": ["@shikijs/engine-javascript@2.5.0", "https://registry.npmmirror.com/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^3.1.0" } }, "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w=="],
-
- "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@2.5.0", "https://registry.npmmirror.com/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw=="],
-
- "@shikijs/langs": ["@shikijs/langs@2.5.0", "https://registry.npmmirror.com/@shikijs/langs/-/langs-2.5.0.tgz", { "dependencies": { "@shikijs/types": "2.5.0" } }, "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w=="],
-
- "@shikijs/themes": ["@shikijs/themes@2.5.0", "https://registry.npmmirror.com/@shikijs/themes/-/themes-2.5.0.tgz", { "dependencies": { "@shikijs/types": "2.5.0" } }, "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw=="],
-
- "@shikijs/transformers": ["@shikijs/transformers@2.5.0", "https://registry.npmmirror.com/@shikijs/transformers/-/transformers-2.5.0.tgz", { "dependencies": { "@shikijs/core": "2.5.0", "@shikijs/types": "2.5.0" } }, "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg=="],
-
- "@shikijs/types": ["@shikijs/types@2.5.0", "https://registry.npmmirror.com/@shikijs/types/-/types-2.5.0.tgz", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw=="],
-
- "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "https://registry.npmmirror.com/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
-
"@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "https://registry.npmmirror.com/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="],
"@smithy/config-resolver": ["@smithy/config-resolver@4.4.13", "https://registry.npmmirror.com/@smithy/config-resolver/-/config-resolver-4.4.13.tgz", { "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/types": "^4.13.1", "@smithy/util-config-provider": "^4.2.2", "@smithy/util-endpoints": "^3.3.3", "@smithy/util-middleware": "^4.2.12", "tslib": "^2.6.2" } }, "sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg=="],
@@ -527,68 +356,16 @@
"@types/d3-zoom": ["@types/d3-zoom@3.0.8", "https://registry.npmmirror.com/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", { "dependencies": { "@types/d3-interpolate": "*", "@types/d3-selection": "*" } }, "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw=="],
- "@types/estree": ["@types/estree@1.0.8", "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
-
"@types/geojson": ["@types/geojson@7946.0.16", "https://registry.npmmirror.com/@types/geojson/-/geojson-7946.0.16.tgz", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="],
- "@types/hast": ["@types/hast@3.0.4", "https://registry.npmmirror.com/@types/hast/-/hast-3.0.4.tgz", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
-
- "@types/linkify-it": ["@types/linkify-it@5.0.0", "https://registry.npmmirror.com/@types/linkify-it/-/linkify-it-5.0.0.tgz", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="],
-
"@types/lodash": ["@types/lodash@4.17.24", "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.24.tgz", {}, "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ=="],
"@types/lodash-es": ["@types/lodash-es@4.17.12", "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz", { "dependencies": { "@types/lodash": "*" } }, "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ=="],
- "@types/markdown-it": ["@types/markdown-it@14.1.2", "https://registry.npmmirror.com/@types/markdown-it/-/markdown-it-14.1.2.tgz", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog=="],
-
- "@types/mdast": ["@types/mdast@4.0.4", "https://registry.npmmirror.com/@types/mdast/-/mdast-4.0.4.tgz", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
-
- "@types/mdurl": ["@types/mdurl@2.0.0", "https://registry.npmmirror.com/@types/mdurl/-/mdurl-2.0.0.tgz", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="],
-
"@types/trusted-types": ["@types/trusted-types@2.0.7", "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
- "@types/unist": ["@types/unist@3.0.3", "https://registry.npmmirror.com/@types/unist/-/unist-3.0.3.tgz", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
-
- "@types/web-bluetooth": ["@types/web-bluetooth@0.0.21", "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", {}, "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA=="],
-
- "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
-
"@upsetjs/venn.js": ["@upsetjs/venn.js@2.0.0", "https://registry.npmmirror.com/@upsetjs/venn.js/-/venn.js-2.0.0.tgz", { "optionalDependencies": { "d3-selection": "^3.0.0", "d3-transition": "^3.0.1" } }, "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw=="],
- "@vitejs/plugin-vue": ["@vitejs/plugin-vue@5.2.4", "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", { "peerDependencies": { "vite": "^5.0.0 || ^6.0.0", "vue": "^3.2.25" } }, "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA=="],
-
- "@vue/compiler-core": ["@vue/compiler-core@3.5.32", "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.32.tgz", { "dependencies": { "@babel/parser": "^7.29.2", "@vue/shared": "3.5.32", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-4x74Tbtqnda8s/NSD6e1Dr5p1c8HdMU5RWSjMSUzb8RTcUQqevDCxVAitcLBKT+ie3o0Dl9crc/S/opJM7qBGQ=="],
-
- "@vue/compiler-dom": ["@vue/compiler-dom@3.5.32", "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.32.tgz", { "dependencies": { "@vue/compiler-core": "3.5.32", "@vue/shared": "3.5.32" } }, "sha512-ybHAu70NtiEI1fvAUz3oXZqkUYEe5J98GjMDpTGl5iHb0T15wQYLR4wE3h9xfuTNA+Cm2f4czfe8B4s+CCH57Q=="],
-
- "@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.32", "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.32.tgz", { "dependencies": { "@babel/parser": "^7.29.2", "@vue/compiler-core": "3.5.32", "@vue/compiler-dom": "3.5.32", "@vue/compiler-ssr": "3.5.32", "@vue/shared": "3.5.32", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.8", "source-map-js": "^1.2.1" } }, "sha512-8UYUYo71cP/0YHMO814TRZlPuUUw3oifHuMR7Wp9SNoRSrxRQnhMLNlCeaODNn6kNTJsjFoQ/kqIj4qGvya4Xg=="],
-
- "@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.32", "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.32.tgz", { "dependencies": { "@vue/compiler-dom": "3.5.32", "@vue/shared": "3.5.32" } }, "sha512-Gp4gTs22T3DgRotZ8aA/6m2jMR+GMztvBXUBEUOYOcST+giyGWJ4WvFd7QLHBkzTxkfOt8IELKNdpzITLbA2rw=="],
-
- "@vue/devtools-api": ["@vue/devtools-api@7.7.9", "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.9.tgz", { "dependencies": { "@vue/devtools-kit": "^7.7.9" } }, "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g=="],
-
- "@vue/devtools-kit": ["@vue/devtools-kit@7.7.9", "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz", { "dependencies": { "@vue/devtools-shared": "^7.7.9", "birpc": "^2.3.0", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^1.0.0", "speakingurl": "^14.0.1", "superjson": "^2.2.2" } }, "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA=="],
-
- "@vue/devtools-shared": ["@vue/devtools-shared@7.7.9", "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz", { "dependencies": { "rfdc": "^1.4.1" } }, "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA=="],
-
- "@vue/reactivity": ["@vue/reactivity@3.5.32", "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.32.tgz", { "dependencies": { "@vue/shared": "3.5.32" } }, "sha512-/ORasxSGvZ6MN5gc+uE364SxFdJ0+WqVG0CENXaGW58TOCdrAW76WWaplDtECeS1qphvtBZtR+3/o1g1zL4xPQ=="],
-
- "@vue/runtime-core": ["@vue/runtime-core@3.5.32", "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.32.tgz", { "dependencies": { "@vue/reactivity": "3.5.32", "@vue/shared": "3.5.32" } }, "sha512-pDrXCejn4UpFDFmMd27AcJEbHaLemaE5o4pbb7sLk79SRIhc6/t34BQA7SGNgYtbMnvbF/HHOftYBgFJtUoJUQ=="],
-
- "@vue/runtime-dom": ["@vue/runtime-dom@3.5.32", "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.32.tgz", { "dependencies": { "@vue/reactivity": "3.5.32", "@vue/runtime-core": "3.5.32", "@vue/shared": "3.5.32", "csstype": "^3.2.3" } }, "sha512-1CDVv7tv/IV13V8Nip1k/aaObVbWqRlVCVezTwx3K07p7Vxossp5JU1dcPNhJk3w347gonIUT9jQOGutyJrSVQ=="],
-
- "@vue/server-renderer": ["@vue/server-renderer@3.5.32", "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.32.tgz", { "dependencies": { "@vue/compiler-ssr": "3.5.32", "@vue/shared": "3.5.32" }, "peerDependencies": { "vue": "3.5.32" } }, "sha512-IOjm2+JQwRFS7W28HNuJeXQle9KdZbODFY7hFGVtnnghF51ta20EWAZJHX+zLGtsHhaU6uC9BGPV52KVpYryMQ=="],
-
- "@vue/shared": ["@vue/shared@3.5.32", "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.32.tgz", {}, "sha512-ksNyrmRQzWJJ8n3cRDuSF7zNNontuJg1YHnmWRJd2AMu8Ij2bqwiiri2lH5rHtYPZjj4STkNcgcmiQqlOjiYGg=="],
-
- "@vueuse/core": ["@vueuse/core@12.8.2", "https://registry.npmmirror.com/@vueuse/core/-/core-12.8.2.tgz", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "12.8.2", "@vueuse/shared": "12.8.2", "vue": "^3.5.13" } }, "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ=="],
-
- "@vueuse/integrations": ["@vueuse/integrations@12.8.2", "https://registry.npmmirror.com/@vueuse/integrations/-/integrations-12.8.2.tgz", { "dependencies": { "@vueuse/core": "12.8.2", "@vueuse/shared": "12.8.2", "vue": "^3.5.13" }, "peerDependencies": { "async-validator": "^4", "axios": "^1", "change-case": "^5", "drauu": "^0.4", "focus-trap": "^7", "fuse.js": "^7", "idb-keyval": "^6", "jwt-decode": "^4", "nprogress": "^0.2", "qrcode": "^1.5", "sortablejs": "^1", "universal-cookie": "^7" }, "optionalPeers": ["async-validator", "axios", "change-case", "drauu", "focus-trap", "fuse.js", "idb-keyval", "jwt-decode", "nprogress", "qrcode", "sortablejs", "universal-cookie"] }, "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g=="],
-
- "@vueuse/metadata": ["@vueuse/metadata@12.8.2", "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-12.8.2.tgz", {}, "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A=="],
-
- "@vueuse/shared": ["@vueuse/shared@12.8.2", "https://registry.npmmirror.com/@vueuse/shared/-/shared-12.8.2.tgz", { "dependencies": { "vue": "^3.5.13" } }, "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w=="],
-
"accepts": ["accepts@2.0.0", "https://registry.npmmirror.com/accepts/-/accepts-2.0.0.tgz", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
"acorn": ["acorn@8.16.0", "https://registry.npmmirror.com/acorn/-/acorn-8.16.0.tgz", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="],
@@ -599,8 +376,6 @@
"ajv-formats": ["ajv-formats@3.0.1", "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-3.0.1.tgz", { "dependencies": { "ajv": "^8.0.0" }, "peerDependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
- "algoliasearch": ["algoliasearch@5.50.1", "https://registry.npmmirror.com/algoliasearch/-/algoliasearch-5.50.1.tgz", { "dependencies": { "@algolia/abtesting": "1.16.1", "@algolia/client-abtesting": "5.50.1", "@algolia/client-analytics": "5.50.1", "@algolia/client-common": "5.50.1", "@algolia/client-insights": "5.50.1", "@algolia/client-personalization": "5.50.1", "@algolia/client-query-suggestions": "5.50.1", "@algolia/client-search": "5.50.1", "@algolia/ingestion": "1.50.1", "@algolia/monitoring": "1.50.1", "@algolia/recommend": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" } }, "sha512-/bwdue1/8LWELn/DBalGRfuLsXBLXULJo/yOeavJtDu8rBwxIzC6/Rz9Jg19S21VkJvRuZO1k8CZXBMS73mYbA=="],
-
"ansi-escapes": ["ansi-escapes@7.3.0", "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-7.3.0.tgz", { "dependencies": { "environment": "^1.0.0" } }, "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg=="],
"ansi-regex": ["ansi-regex@6.2.2", "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
@@ -621,8 +396,6 @@
"bignumber.js": ["bignumber.js@9.3.1", "https://registry.npmmirror.com/bignumber.js/-/bignumber.js-9.3.1.tgz", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
- "birpc": ["birpc@2.9.0", "https://registry.npmmirror.com/birpc/-/birpc-2.9.0.tgz", {}, "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw=="],
-
"body-parser": ["body-parser@2.2.2", "https://registry.npmmirror.com/body-parser/-/body-parser-2.2.2.tgz", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
"bowser": ["bowser@2.14.1", "https://registry.npmmirror.com/bowser/-/bowser-2.14.1.tgz", {}, "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg=="],
@@ -637,14 +410,8 @@
"camelcase": ["camelcase@5.3.1", "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="],
- "ccount": ["ccount@2.0.1", "https://registry.npmmirror.com/ccount/-/ccount-2.0.1.tgz", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
-
"chalk": ["chalk@5.6.2", "https://registry.npmmirror.com/chalk/-/chalk-5.6.2.tgz", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
- "character-entities-html4": ["character-entities-html4@2.1.0", "https://registry.npmmirror.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="],
-
- "character-entities-legacy": ["character-entities-legacy@3.0.0", "https://registry.npmmirror.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
-
"chevrotain": ["chevrotain@12.0.0", "https://registry.npmmirror.com/chevrotain/-/chevrotain-12.0.0.tgz", { "dependencies": { "@chevrotain/cst-dts-gen": "12.0.0", "@chevrotain/gast": "12.0.0", "@chevrotain/regexp-to-ast": "12.0.0", "@chevrotain/types": "12.0.0", "@chevrotain/utils": "12.0.0" } }, "sha512-csJvb+6kEiQaqo1woTdSAuOWdN0WTLIydkKrBnS+V5gZz0oqBrp4kQ35519QgK6TpBThiG3V1vNSHlIkv4AglQ=="],
"chevrotain-allstar": ["chevrotain-allstar@0.4.1", "https://registry.npmmirror.com/chevrotain-allstar/-/chevrotain-allstar-0.4.1.tgz", { "dependencies": { "lodash-es": "^4.17.21" }, "peerDependencies": { "chevrotain": "^12.0.0" } }, "sha512-PvVJm3oGqrveUVW2Vt/eZGeiAIsJszYweUcYwcskg9e+IubNYKKD+rHHem7A6XVO22eDAL+inxNIGAzZ/VIWlA=="],
@@ -667,8 +434,6 @@
"combined-stream": ["combined-stream@1.0.8", "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
- "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "https://registry.npmmirror.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
-
"commander": ["commander@12.1.0", "https://registry.npmmirror.com/commander/-/commander-12.1.0.tgz", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
"confbox": ["confbox@0.1.8", "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
@@ -683,8 +448,6 @@
"cookie-signature": ["cookie-signature@1.2.2", "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.2.2.tgz", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
- "copy-anything": ["copy-anything@4.0.5", "https://registry.npmmirror.com/copy-anything/-/copy-anything-4.0.5.tgz", { "dependencies": { "is-what": "^5.2.0" } }, "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA=="],
-
"cors": ["cors@2.8.6", "https://registry.npmmirror.com/cors/-/cors-2.8.6.tgz", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="],
"cose-base": ["cose-base@1.0.3", "https://registry.npmmirror.com/cose-base/-/cose-base-1.0.3.tgz", { "dependencies": { "layout-base": "^1.0.0" } }, "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg=="],
@@ -693,8 +456,6 @@
"cssfilter": ["cssfilter@0.0.10", "https://registry.npmmirror.com/cssfilter/-/cssfilter-0.0.10.tgz", {}, "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw=="],
- "csstype": ["csstype@3.2.3", "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
-
"cytoscape": ["cytoscape@3.33.2", "https://registry.npmmirror.com/cytoscape/-/cytoscape-3.33.2.tgz", {}, "sha512-sj4HXd3DokGhzZAdjDejGvTPLqlt84vNFN8m7bGsOzDY5DyVcxIb2ejIXat2Iy7HxWhdT/N1oKyheJ5YdpsGuw=="],
"cytoscape-cose-bilkent": ["cytoscape-cose-bilkent@4.1.0", "https://registry.npmmirror.com/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", { "dependencies": { "cose-base": "^1.0.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ=="],
@@ -781,10 +542,6 @@
"depd": ["depd@2.0.0", "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
- "dequal": ["dequal@2.0.3", "https://registry.npmmirror.com/dequal/-/dequal-2.0.3.tgz", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
-
- "devlop": ["devlop@1.1.0", "https://registry.npmmirror.com/devlop/-/devlop-1.1.0.tgz", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
-
"diff": ["diff@8.0.4", "https://registry.npmmirror.com/diff/-/diff-8.0.4.tgz", {}, "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw=="],
"dijkstrajs": ["dijkstrajs@1.0.3", "https://registry.npmmirror.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz", {}, "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="],
@@ -801,12 +558,8 @@
"emoji-regex": ["emoji-regex@10.6.0", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.6.0.tgz", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
- "emoji-regex-xs": ["emoji-regex-xs@1.0.0", "https://registry.npmmirror.com/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", {}, "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg=="],
-
"encodeurl": ["encodeurl@2.0.0", "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
- "entities": ["entities@7.0.1", "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
-
"env-paths": ["env-paths@4.0.0", "https://registry.npmmirror.com/env-paths/-/env-paths-4.0.0.tgz", { "dependencies": { "is-safe-filename": "^0.1.0" } }, "sha512-pxP8eL2SwwaTRi/KHYwLYXinDs7gL3jxFcBYmEdYfZmZXbaVDvdppd0XBU8qVz03rDfKZMXg1omHCbsJjZrMsw=="],
"environment": ["environment@1.1.0", "https://registry.npmmirror.com/environment/-/environment-1.1.0.tgz", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="],
@@ -821,14 +574,10 @@
"es-toolkit": ["es-toolkit@1.45.1", "https://registry.npmmirror.com/es-toolkit/-/es-toolkit-1.45.1.tgz", {}, "sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw=="],
- "esbuild": ["esbuild@0.21.5", "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
-
"escape-html": ["escape-html@1.0.3", "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
"escape-string-regexp": ["escape-string-regexp@2.0.0", "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="],
- "estree-walker": ["estree-walker@2.0.2", "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
-
"etag": ["etag@1.8.1", "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
"eventsource": ["eventsource@3.0.7", "https://registry.npmmirror.com/eventsource/-/eventsource-3.0.7.tgz", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="],
@@ -861,8 +610,6 @@
"find-up": ["find-up@4.1.0", "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
- "focus-trap": ["focus-trap@7.8.0", "https://registry.npmmirror.com/focus-trap/-/focus-trap-7.8.0.tgz", { "dependencies": { "tabbable": "^6.4.0" } }, "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA=="],
-
"follow-redirects": ["follow-redirects@1.16.0", "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.16.0.tgz", {}, "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="],
"form-data": ["form-data@4.0.5", "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
@@ -873,8 +620,6 @@
"fresh": ["fresh@2.0.0", "https://registry.npmmirror.com/fresh/-/fresh-2.0.0.tgz", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
- "fsevents": ["fsevents@2.3.3", "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
-
"function-bind": ["function-bind@1.1.2", "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
"fuse.js": ["fuse.js@7.1.0", "https://registry.npmmirror.com/fuse.js/-/fuse.js-7.1.0.tgz", {}, "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ=="],
@@ -911,18 +656,10 @@
"hasown": ["hasown@2.0.2", "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
- "hast-util-to-html": ["hast-util-to-html@9.0.5", "https://registry.npmmirror.com/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
-
- "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "https://registry.npmmirror.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="],
-
"highlight.js": ["highlight.js@11.11.1", "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.11.1.tgz", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="],
"hono": ["hono@4.12.9", "https://registry.npmmirror.com/hono/-/hono-4.12.9.tgz", {}, "sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA=="],
- "hookable": ["hookable@5.5.3", "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="],
-
- "html-void-elements": ["html-void-elements@3.0.0", "https://registry.npmmirror.com/html-void-elements/-/html-void-elements-3.0.0.tgz", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
-
"http-errors": ["http-errors@2.0.1", "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.1.tgz", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
"https-proxy-agent": ["https-proxy-agent@8.0.0", "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-8.0.0.tgz", { "dependencies": { "agent-base": "8.0.0", "debug": "^4.3.4" } }, "sha512-YYeW+iCnAS3xhvj2dvVoWgsbca3RfQy/IlaNHHOtDmU0jMqPI9euIq3Y9BJETdxk16h9NHHCKqp/KB9nIMStCQ=="],
@@ -959,8 +696,6 @@
"is-unicode-supported": ["is-unicode-supported@2.1.0", "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="],
- "is-what": ["is-what@5.5.0", "https://registry.npmmirror.com/is-what/-/is-what-5.5.0.tgz", {}, "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw=="],
-
"isexe": ["isexe@2.0.0", "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"jose": ["jose@6.2.2", "https://registry.npmmirror.com/jose/-/jose-6.2.2.tgz", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="],
@@ -995,16 +730,10 @@
"lru-cache": ["lru-cache@11.2.7", "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.2.7.tgz", {}, "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA=="],
- "magic-string": ["magic-string@0.30.21", "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
-
- "mark.js": ["mark.js@8.11.1", "https://registry.npmmirror.com/mark.js/-/mark.js-8.11.1.tgz", {}, "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ=="],
-
"marked": ["marked@17.0.5", "https://registry.npmmirror.com/marked/-/marked-17.0.5.tgz", { "bin": "bin/marked.js" }, "sha512-6hLvc0/JEbRjRgzI6wnT2P1XuM1/RrrDEX0kPt0N7jGm1133g6X7DlxFasUIx+72aKAr904GTxhSLDrd5DIlZg=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
- "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "https://registry.npmmirror.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="],
-
"media-typer": ["media-typer@1.1.0", "https://registry.npmmirror.com/media-typer/-/media-typer-1.1.0.tgz", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
"medium-zoom": ["medium-zoom@1.1.0", "https://registry.npmmirror.com/medium-zoom/-/medium-zoom-1.1.0.tgz", {}, "sha512-ewyDsp7k4InCUp3jRmwHBRFGyjBimKps/AJLjRSox+2q/2H4p/PNpQf+pwONWlJiOudkBXtbdmVbFjqyybfTmQ=="],
@@ -1013,40 +742,22 @@
"mermaid": ["mermaid@11.14.0", "https://registry.npmmirror.com/mermaid/-/mermaid-11.14.0.tgz", { "dependencies": { "@braintree/sanitize-url": "^7.1.1", "@iconify/utils": "^3.0.2", "@mermaid-js/parser": "^1.1.0", "@types/d3": "^7.4.3", "@upsetjs/venn.js": "^2.0.0", "cytoscape": "^3.33.1", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.14", "dayjs": "^1.11.19", "dompurify": "^3.3.1", "katex": "^0.16.25", "khroma": "^2.1.0", "lodash-es": "^4.17.23", "marked": "^16.3.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0" } }, "sha512-GSGloRsBs+JINmmhl0JDwjpuezCsHB4WGI4NASHxL3fHo3o/BRXTxhDLKnln8/Q0lRFRyDdEjmk1/d5Sn1Xz8g=="],
- "micromark-util-character": ["micromark-util-character@2.1.1", "https://registry.npmmirror.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="],
-
- "micromark-util-encode": ["micromark-util-encode@2.0.1", "https://registry.npmmirror.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="],
-
- "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "https://registry.npmmirror.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="],
-
- "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "https://registry.npmmirror.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="],
-
- "micromark-util-types": ["micromark-util-types@2.0.2", "https://registry.npmmirror.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="],
-
"mime-db": ["mime-db@1.54.0", "https://registry.npmmirror.com/mime-db/-/mime-db-1.54.0.tgz", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
"mime-types": ["mime-types@3.0.2", "https://registry.npmmirror.com/mime-types/-/mime-types-3.0.2.tgz", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
"mimic-fn": ["mimic-fn@2.1.0", "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
- "minisearch": ["minisearch@7.2.0", "https://registry.npmmirror.com/minisearch/-/minisearch-7.2.0.tgz", {}, "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg=="],
-
- "mitt": ["mitt@3.0.1", "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="],
-
"mlly": ["mlly@1.8.2", "https://registry.npmmirror.com/mlly/-/mlly-1.8.2.tgz", { "dependencies": { "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.3" } }, "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA=="],
"ms": ["ms@2.1.3", "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
- "nanoid": ["nanoid@3.3.11", "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
-
"negotiator": ["negotiator@1.0.0", "https://registry.npmmirror.com/negotiator/-/negotiator-1.0.0.tgz", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
"node-domexception": ["node-domexception@1.0.0", "https://registry.npmmirror.com/node-domexception/-/node-domexception-1.0.0.tgz", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
"node-fetch": ["node-fetch@3.3.2", "https://registry.npmmirror.com/node-fetch/-/node-fetch-3.3.2.tgz", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
- "non-layered-tidy-tree-layout": ["non-layered-tidy-tree-layout@2.0.2", "https://registry.npmmirror.com/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz", {}, "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw=="],
-
"npm-run-path": ["npm-run-path@6.0.0", "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-6.0.0.tgz", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="],
"object-assign": ["object-assign@4.1.1", "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
@@ -1059,8 +770,6 @@
"onetime": ["onetime@5.1.2", "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
- "oniguruma-to-es": ["oniguruma-to-es@3.1.1", "https://registry.npmmirror.com/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ=="],
-
"p-limit": ["p-limit@2.3.0", "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
"p-locate": ["p-locate@4.1.0", "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
@@ -1089,10 +798,6 @@
"pathe": ["pathe@2.0.3", "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
- "perfect-debounce": ["perfect-debounce@1.0.0", "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="],
-
- "picocolors": ["picocolors@1.1.1", "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
-
"picomatch": ["picomatch@4.0.4", "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.4.tgz", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
"pkce-challenge": ["pkce-challenge@5.0.1", "https://registry.npmmirror.com/pkce-challenge/-/pkce-challenge-5.0.1.tgz", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="],
@@ -1105,16 +810,10 @@
"points-on-path": ["points-on-path@0.2.1", "https://registry.npmmirror.com/points-on-path/-/points-on-path-0.2.1.tgz", { "dependencies": { "path-data-parser": "0.1.0", "points-on-curve": "0.2.0" } }, "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g=="],
- "postcss": ["postcss@8.5.9", "https://registry.npmmirror.com/postcss/-/postcss-8.5.9.tgz", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw=="],
-
- "preact": ["preact@10.29.1", "https://registry.npmmirror.com/preact/-/preact-10.29.1.tgz", {}, "sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg=="],
-
"pretty-ms": ["pretty-ms@9.3.0", "https://registry.npmmirror.com/pretty-ms/-/pretty-ms-9.3.0.tgz", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ=="],
"proper-lockfile": ["proper-lockfile@4.1.2", "https://registry.npmmirror.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz", { "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", "signal-exit": "^3.0.2" } }, "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA=="],
- "property-information": ["property-information@7.1.0", "https://registry.npmmirror.com/property-information/-/property-information-7.1.0.tgz", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
-
"proxy-addr": ["proxy-addr@2.0.7", "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
"proxy-from-env": ["proxy-from-env@2.1.0", "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="],
@@ -1133,12 +832,6 @@
"readdirp": ["readdirp@5.0.0", "https://registry.npmmirror.com/readdirp/-/readdirp-5.0.0.tgz", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
- "regex": ["regex@6.1.0", "https://registry.npmmirror.com/regex/-/regex-6.1.0.tgz", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="],
-
- "regex-recursion": ["regex-recursion@6.0.2", "https://registry.npmmirror.com/regex-recursion/-/regex-recursion-6.0.2.tgz", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="],
-
- "regex-utilities": ["regex-utilities@2.3.0", "https://registry.npmmirror.com/regex-utilities/-/regex-utilities-2.3.0.tgz", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="],
-
"require-directory": ["require-directory@2.1.1", "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
"require-from-string": ["require-from-string@2.0.2", "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
@@ -1149,12 +842,8 @@
"retry": ["retry@0.12.0", "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
- "rfdc": ["rfdc@1.4.1", "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="],
-
"robust-predicates": ["robust-predicates@3.0.3", "https://registry.npmmirror.com/robust-predicates/-/robust-predicates-3.0.3.tgz", {}, "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA=="],
- "rollup": ["rollup@4.60.1", "https://registry.npmmirror.com/rollup/-/rollup-4.60.1.tgz", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.1", "@rollup/rollup-android-arm64": "4.60.1", "@rollup/rollup-darwin-arm64": "4.60.1", "@rollup/rollup-darwin-x64": "4.60.1", "@rollup/rollup-freebsd-arm64": "4.60.1", "@rollup/rollup-freebsd-x64": "4.60.1", "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", "@rollup/rollup-linux-arm-musleabihf": "4.60.1", "@rollup/rollup-linux-arm64-gnu": "4.60.1", "@rollup/rollup-linux-arm64-musl": "4.60.1", "@rollup/rollup-linux-loong64-gnu": "4.60.1", "@rollup/rollup-linux-loong64-musl": "4.60.1", "@rollup/rollup-linux-ppc64-gnu": "4.60.1", "@rollup/rollup-linux-ppc64-musl": "4.60.1", "@rollup/rollup-linux-riscv64-gnu": "4.60.1", "@rollup/rollup-linux-riscv64-musl": "4.60.1", "@rollup/rollup-linux-s390x-gnu": "4.60.1", "@rollup/rollup-linux-x64-gnu": "4.60.1", "@rollup/rollup-linux-x64-musl": "4.60.1", "@rollup/rollup-openbsd-x64": "4.60.1", "@rollup/rollup-openharmony-arm64": "4.60.1", "@rollup/rollup-win32-arm64-msvc": "4.60.1", "@rollup/rollup-win32-ia32-msvc": "4.60.1", "@rollup/rollup-win32-x64-gnu": "4.60.1", "@rollup/rollup-win32-x64-msvc": "4.60.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w=="],
-
"roughjs": ["roughjs@4.6.6", "https://registry.npmmirror.com/roughjs/-/roughjs-4.6.6.tgz", { "dependencies": { "hachure-fill": "^0.5.2", "path-data-parser": "^0.1.0", "points-on-curve": "^0.2.0", "points-on-path": "^0.2.1" } }, "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ=="],
"router": ["router@2.2.0", "https://registry.npmmirror.com/router/-/router-2.2.0.tgz", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
@@ -1167,8 +856,6 @@
"scheduler": ["scheduler@0.27.0", "https://registry.npmmirror.com/scheduler/-/scheduler-0.27.0.tgz", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
- "search-insights": ["search-insights@2.17.3", "https://registry.npmmirror.com/search-insights/-/search-insights-2.17.3.tgz", {}, "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ=="],
-
"semver": ["semver@7.7.4", "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz", { "bin": "bin/semver.js" }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
"send": ["send@1.2.1", "https://registry.npmmirror.com/send/-/send-1.2.1.tgz", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="],
@@ -1185,8 +872,6 @@
"shell-quote": ["shell-quote@1.8.3", "https://registry.npmmirror.com/shell-quote/-/shell-quote-1.8.3.tgz", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="],
- "shiki": ["shiki@2.5.0", "https://registry.npmmirror.com/shiki/-/shiki-2.5.0.tgz", { "dependencies": { "@shikijs/core": "2.5.0", "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/langs": "2.5.0", "@shikijs/themes": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ=="],
-
"side-channel": ["side-channel@1.1.0", "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
"side-channel-list": ["side-channel-list@1.0.0", "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
@@ -1199,20 +884,12 @@
"slice-ansi": ["slice-ansi@8.0.0", "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-8.0.0.tgz", { "dependencies": { "ansi-styles": "^6.2.3", "is-fullwidth-code-point": "^5.1.0" } }, "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg=="],
- "source-map-js": ["source-map-js@1.2.1", "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
-
- "space-separated-tokens": ["space-separated-tokens@2.0.2", "https://registry.npmmirror.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
-
- "speakingurl": ["speakingurl@14.0.1", "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz", {}, "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ=="],
-
"stack-utils": ["stack-utils@2.0.6", "https://registry.npmmirror.com/stack-utils/-/stack-utils-2.0.6.tgz", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="],
"statuses": ["statuses@2.0.2", "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
"string-width": ["string-width@8.2.0", "https://registry.npmmirror.com/string-width/-/string-width-8.2.0.tgz", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw=="],
- "stringify-entities": ["stringify-entities@4.0.4", "https://registry.npmmirror.com/stringify-entities/-/stringify-entities-4.0.4.tgz", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
-
"strip-ansi": ["strip-ansi@7.2.0", "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
"strip-final-newline": ["strip-final-newline@4.0.0", "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-4.0.0.tgz", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="],
@@ -1221,14 +898,10 @@
"stylis": ["stylis@4.3.6", "https://registry.npmmirror.com/stylis/-/stylis-4.3.6.tgz", {}, "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ=="],
- "superjson": ["superjson@2.2.6", "https://registry.npmmirror.com/superjson/-/superjson-2.2.6.tgz", { "dependencies": { "copy-anything": "^4" } }, "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA=="],
-
"supports-color": ["supports-color@10.2.2", "https://registry.npmmirror.com/supports-color/-/supports-color-10.2.2.tgz", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
"supports-hyperlinks": ["supports-hyperlinks@4.4.0", "https://registry.npmmirror.com/supports-hyperlinks/-/supports-hyperlinks-4.4.0.tgz", { "dependencies": { "has-flag": "^5.0.1", "supports-color": "^10.2.2" } }, "sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg=="],
- "tabbable": ["tabbable@6.4.0", "https://registry.npmmirror.com/tabbable/-/tabbable-6.4.0.tgz", {}, "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg=="],
-
"tagged-tag": ["tagged-tag@1.0.0", "https://registry.npmmirror.com/tagged-tag/-/tagged-tag-1.0.0.tgz", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="],
"terminal-size": ["terminal-size@4.0.1", "https://registry.npmmirror.com/terminal-size/-/terminal-size-4.0.1.tgz", {}, "sha512-avMLDQpUI9I5XFrklECw1ZEUPJhqzcwSWsyyI8blhRLT+8N1jLJWLWWYQpB2q2xthq8xDvjZPISVh53T/+CLYQ=="],
@@ -1239,8 +912,6 @@
"tree-kill": ["tree-kill@1.2.2", "https://registry.npmmirror.com/tree-kill/-/tree-kill-1.2.2.tgz", { "bin": "cli.js" }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="],
- "trim-lines": ["trim-lines@3.0.1", "https://registry.npmmirror.com/trim-lines/-/trim-lines-3.0.1.tgz", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
-
"ts-algebra": ["ts-algebra@2.0.0", "https://registry.npmmirror.com/ts-algebra/-/ts-algebra-2.0.0.tgz", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="],
"ts-dedent": ["ts-dedent@2.2.0", "https://registry.npmmirror.com/ts-dedent/-/ts-dedent-2.2.0.tgz", {}, "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ=="],
@@ -1259,16 +930,6 @@
"unicorn-magic": ["unicorn-magic@0.3.0", "https://registry.npmmirror.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="],
- "unist-util-is": ["unist-util-is@6.0.1", "https://registry.npmmirror.com/unist-util-is/-/unist-util-is-6.0.1.tgz", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="],
-
- "unist-util-position": ["unist-util-position@5.0.0", "https://registry.npmmirror.com/unist-util-position/-/unist-util-position-5.0.0.tgz", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="],
-
- "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "https://registry.npmmirror.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="],
-
- "unist-util-visit": ["unist-util-visit@5.1.0", "https://registry.npmmirror.com/unist-util-visit/-/unist-util-visit-5.1.0.tgz", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="],
-
- "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "https://registry.npmmirror.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="],
-
"unpipe": ["unpipe@1.0.0", "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
"usehooks-ts": ["usehooks-ts@3.1.1", "https://registry.npmmirror.com/usehooks-ts/-/usehooks-ts-3.1.1.tgz", { "dependencies": { "lodash.debounce": "^4.0.8" }, "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA=="],
@@ -1277,16 +938,6 @@
"vary": ["vary@1.1.2", "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
- "vfile": ["vfile@6.0.3", "https://registry.npmmirror.com/vfile/-/vfile-6.0.3.tgz", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
-
- "vfile-message": ["vfile-message@4.0.3", "https://registry.npmmirror.com/vfile-message/-/vfile-message-4.0.3.tgz", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
-
- "vite": ["vite@5.4.21", "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="],
-
- "vitepress": ["vitepress@1.6.4", "https://registry.npmmirror.com/vitepress/-/vitepress-1.6.4.tgz", { "dependencies": { "@docsearch/css": "3.8.2", "@docsearch/js": "3.8.2", "@iconify-json/simple-icons": "^1.2.21", "@shikijs/core": "^2.1.0", "@shikijs/transformers": "^2.1.0", "@shikijs/types": "^2.1.0", "@types/markdown-it": "^14.1.2", "@vitejs/plugin-vue": "^5.2.1", "@vue/devtools-api": "^7.7.0", "@vue/shared": "^3.5.13", "@vueuse/core": "^12.4.0", "@vueuse/integrations": "^12.4.0", "focus-trap": "^7.6.4", "mark.js": "8.11.1", "minisearch": "^7.1.1", "shiki": "^2.1.0", "vite": "^5.4.14", "vue": "^3.5.13" }, "peerDependencies": { "markdown-it-mathjax3": "^4", "postcss": "^8" }, "optionalPeers": ["markdown-it-mathjax3", "postcss"], "bin": { "vitepress": "bin/vitepress.js" } }, "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg=="],
-
- "vitepress-plugin-mermaid": ["vitepress-plugin-mermaid@2.0.17", "https://registry.npmmirror.com/vitepress-plugin-mermaid/-/vitepress-plugin-mermaid-2.0.17.tgz", { "optionalDependencies": { "@mermaid-js/mermaid-mindmap": "^9.3.0" }, "peerDependencies": { "mermaid": "10 || 11", "vitepress": "^1.0.0 || ^1.0.0-alpha" } }, "sha512-IUzYpwf61GC6k0XzfmAmNrLvMi9TRrVRMsUyCA8KNXhg/mQ1VqWnO0/tBVPiX5UoKF1mDUwqn5QV4qAJl6JnUg=="],
-
"vscode-jsonrpc": ["vscode-jsonrpc@8.2.1", "https://registry.npmmirror.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.1.tgz", {}, "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ=="],
"vscode-languageserver": ["vscode-languageserver@9.0.1", "https://registry.npmmirror.com/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", { "dependencies": { "vscode-languageserver-protocol": "3.17.5" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g=="],
@@ -1299,8 +950,6 @@
"vscode-uri": ["vscode-uri@3.1.0", "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="],
- "vue": ["vue@3.5.32", "https://registry.npmmirror.com/vue/-/vue-3.5.32.tgz", { "dependencies": { "@vue/compiler-dom": "3.5.32", "@vue/compiler-sfc": "3.5.32", "@vue/runtime-dom": "3.5.32", "@vue/server-renderer": "3.5.32", "@vue/shared": "3.5.32" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-vM4z4Q9tTafVfMAK7IVzmxg34rSzTFMyIe0UUEijUCkn9+23lj0WRfA83dg7eQZIUlgOSGrkViIaCfqSAUXsMw=="],
-
"web-streams-polyfill": ["web-streams-polyfill@3.3.3", "https://registry.npmmirror.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
"which": ["which@2.0.2", "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
@@ -1333,8 +982,6 @@
"zod-to-json-schema": ["zod-to-json-schema@3.25.2", "https://registry.npmmirror.com/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
- "zwitch": ["zwitch@2.0.4", "https://registry.npmmirror.com/zwitch/-/zwitch-2.0.4.tgz", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
-
"@anthropic-ai/sandbox-runtime/zod": ["zod@3.25.76", "https://registry.npmmirror.com/zod/-/zod-3.25.76.tgz", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "https://registry.npmmirror.com/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
@@ -1343,8 +990,6 @@
"@commander-js/extra-typings/commander": ["commander@14.0.3", "https://registry.npmmirror.com/commander/-/commander-14.0.3.tgz", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="],
- "@mermaid-js/mermaid-mindmap/@braintree/sanitize-url": ["@braintree/sanitize-url@6.0.4", "https://registry.npmmirror.com/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz", {}, "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A=="],
-
"cliui/string-width": ["string-width@4.2.3", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
"cliui/strip-ansi": ["strip-ansi@6.0.1", "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts
deleted file mode 100644
index 15478de1..00000000
--- a/docs/.vitepress/config.mts
+++ /dev/null
@@ -1,263 +0,0 @@
-import { defineConfig } from 'vitepress'
-import { withMermaid } from 'vitepress-plugin-mermaid'
-
-// GitHub-compatible slugify (matches github-slugger algorithm)
-// Makes heading anchor IDs consistent between VitePress and GitHub rendering
-function slugify(str: string): string {
- return str
- .replace(/<[^>]*>/g, '')
- .replace(/</g, '<')
- .replace(/>/g, '>')
- .replace(/&/g, '&')
- .trim()
- .toLowerCase()
- .replace(/[^\p{L}\p{M}\p{N}\p{Pc}\- ]/gu, '')
- .replace(/ /g, '-')
-}
-
-const zhSidebar = [
- {
- text: '快速开始',
- items: [
- { text: '安装与启动', link: '/guide/quick-start' },
- { text: '环境变量', link: '/guide/env-vars' },
- { text: '第三方模型', link: '/guide/third-party-models' },
- { text: '全局使用', link: '/guide/global-usage' },
- { text: '常见问题', link: '/guide/faq' },
- { text: '贡献与质量门禁', link: '/guide/contributing' },
- ],
- },
- {
- text: '记忆系统',
- collapsed: false,
- items: [
- { text: '概览', link: '/memory/' },
- { text: '使用指南', link: '/memory/01-usage-guide' },
- { text: '实现原理', link: '/memory/02-implementation' },
- { text: 'AutoDream 记忆整合', link: '/memory/03-autodream' },
- ],
- },
- {
- text: '多 Agent 系统',
- collapsed: false,
- items: [
- { text: '概览', link: '/agent/' },
- { text: '使用指南', link: '/agent/01-usage-guide' },
- { text: '实现原理', link: '/agent/02-implementation' },
- { text: 'Agent 框架解析', link: '/agent/03-agent-framework' },
- ],
- },
- {
- text: 'Skills 系统',
- collapsed: false,
- items: [
- { text: '使用指南', link: '/skills/01-usage-guide' },
- { text: '实现原理', link: '/skills/02-implementation' },
- ],
- },
- {
- text: 'IM 接入',
- collapsed: false,
- items: [
- { text: '总览', link: '/im/' },
- { text: '微信', link: '/im/wechat' },
- { text: '钉钉', link: '/im/dingtalk' },
- { text: 'Telegram', link: '/im/telegram' },
- { text: '飞书', link: '/im/feishu' },
- ],
- },
- {
- text: 'Channel 源码研究',
- collapsed: false,
- items: [
- { text: '概览', link: '/channel/' },
- { text: '架构解析', link: '/channel/01-channel-system' },
- ],
- },
- {
- text: 'Computer Use',
- collapsed: false,
- items: [
- { text: '功能指南', link: '/features/computer-use' },
- { text: '架构解析', link: '/features/computer-use-architecture' },
- ],
- },
- {
- text: '桌面端',
- collapsed: false,
- items: [
- { text: '概览', link: '/desktop/' },
- { text: '快速上手', link: '/desktop/01-quick-start' },
- { text: '架构设计', link: '/desktop/02-architecture' },
- { text: '功能详解', link: '/desktop/03-features' },
- { text: '安装与构建', link: '/desktop/04-installation' },
- { text: 'H5 访问', link: '/desktop/06-h5-access' },
- { text: 'Electron 迁移调研', link: '/desktop/07-electron-migration-research' },
- { text: 'Electron 迁移任务', link: '/desktop/08-electron-migration-tasks' },
- { text: 'Electron 迁移验证', link: '/desktop/09-electron-migration-validation-checklist' },
- { text: 'Electron 发布与更新', link: '/desktop/10-release-auto-update' },
- ],
- },
- {
- text: '参考',
- collapsed: true,
- items: [
- { text: '源码修复记录', link: '/reference/fixes' },
- { text: '项目结构', link: '/reference/project-structure' },
- ],
- },
-]
-
-const enSidebar = [
- {
- text: 'Getting Started',
- items: [
- { text: 'Quick Start', link: '/en/guide/quick-start' },
- { text: 'Environment Variables', link: '/en/guide/env-vars' },
- { text: 'Third-Party Models', link: '/en/guide/third-party-models' },
- { text: 'Global Usage', link: '/en/guide/global-usage' },
- { text: 'FAQ', link: '/en/guide/faq' },
- { text: 'Contributing', link: '/en/guide/contributing' },
- ],
- },
- {
- text: 'Memory System',
- collapsed: false,
- items: [
- { text: 'Overview', link: '/en/memory/' },
- { text: 'Usage Guide', link: '/en/memory/01-usage-guide' },
- { text: 'Implementation', link: '/en/memory/02-implementation' },
- { text: 'AutoDream', link: '/en/memory/03-autodream' },
- ],
- },
- {
- text: 'Multi-Agent System',
- collapsed: false,
- items: [
- { text: 'Overview', link: '/en/agent/' },
- { text: 'Usage Guide', link: '/en/agent/01-usage-guide' },
- { text: 'Implementation', link: '/en/agent/02-implementation' },
- { text: 'Framework Deep Dive', link: '/en/agent/03-agent-framework' },
- ],
- },
- {
- text: 'Skills System',
- collapsed: false,
- items: [
- { text: 'Usage Guide', link: '/en/skills/01-usage-guide' },
- { text: 'Implementation', link: '/en/skills/02-implementation' },
- ],
- },
- {
- text: 'Channel System',
- collapsed: false,
- items: [
- { text: 'Overview', link: '/en/channel/' },
- { text: 'Architecture', link: '/en/channel/01-channel-system' },
- ],
- },
- {
- text: 'Computer Use',
- collapsed: false,
- items: [
- { text: 'Guide', link: '/en/features/computer-use' },
- { text: 'Architecture', link: '/en/features/computer-use-architecture' },
- ],
- },
- {
- text: 'Desktop',
- collapsed: false,
- items: [
- { text: 'Overview', link: '/en/desktop/' },
- { text: 'Quick Start', link: '/en/desktop/01-quick-start' },
- { text: 'Architecture', link: '/en/desktop/02-architecture' },
- { text: 'Features', link: '/en/desktop/03-features' },
- { text: 'Installation & Build', link: '/en/desktop/04-installation' },
- ],
- },
- {
- text: 'Reference',
- collapsed: true,
- items: [
- { text: 'Source Fixes', link: '/en/reference/fixes' },
- { text: 'Project Structure', link: '/en/reference/project-structure' },
- ],
- },
-]
-
-export default withMermaid(defineConfig({
- title: 'Claude Code Haha',
- description: '基于 Claude Code 泄露源码修复的本地可运行版本,支持接入任意 Anthropic 兼容 API',
- lastUpdated: true,
- base: '/',
-
- markdown: {
- anchor: {
- slugify,
- },
- },
-
- vite: {
- build: {
- chunkSizeWarningLimit: 1800,
- },
- },
-
- head: [
- ['script', { async: '', src: 'https://www.googletagmanager.com/gtag/js?id=G-D42DM82263' }],
- ['script', {}, `window.dataLayer = window.dataLayer || [];\nfunction gtag(){dataLayer.push(arguments);}\ngtag('js', new Date());\ngtag('config', 'G-D42DM82263');`],
- ],
-
- locales: {
- root: {
- label: '中文',
- lang: 'zh-CN',
- themeConfig: {
- nav: [
- { text: '首页', link: '/' },
- { text: '快速开始', link: '/guide/quick-start' },
- ],
- sidebar: zhSidebar,
- outline: { label: '页面导航' },
- returnToTopLabel: '返回顶部',
- sidebarMenuLabel: '菜单',
- darkModeSwitchLabel: '主题',
- lastUpdated: { text: '最后更新于' },
- docFooter: { prev: '上一页', next: '下一页' },
- },
- },
- en: {
- label: 'English',
- lang: 'en-US',
- description: 'A locally runnable version repaired from the leaked Claude Code source, with support for any Anthropic-compatible API endpoint.',
- themeConfig: {
- editLink: {
- pattern: 'https://github.com/NanmiCoder/cc-haha/edit/main/docs/:path',
- text: 'Edit this page on GitHub',
- },
- nav: [
- { text: 'Home', link: '/en/' },
- { text: 'Quick Start', link: '/en/guide/quick-start' },
- ],
- sidebar: enSidebar,
- },
- },
- },
-
- themeConfig: {
- editLink: {
- pattern: 'https://github.com/NanmiCoder/cc-haha/edit/main/docs/:path',
- text: '在 GitHub 上编辑此页',
- },
- search: {
- provider: 'local',
- },
- socialLinks: [
- { icon: 'github', link: 'https://github.com/NanmiCoder/cc-haha' },
- ],
- footer: {
- message: 'Released under the MIT License.',
- copyright: 'Copyright 2026 Claude Code Haha Contributors',
- },
- },
-}))
diff --git a/docs/.vitepress/theme/custom.css b/docs/.vitepress/theme/custom.css
deleted file mode 100644
index 2dd5e409..00000000
--- a/docs/.vitepress/theme/custom.css
+++ /dev/null
@@ -1,34 +0,0 @@
-:root {
- --vp-c-brand-1: #D97757;
- --vp-c-brand-2: #c5684a;
- --vp-c-brand-3: #b15a3e;
- --vp-c-brand-soft: rgba(217, 119, 87, 0.14);
- --vp-sidebar-width: 280px;
-}
-
-.dark {
- --vp-c-brand-1: #D97757;
- --vp-c-brand-2: #e08868;
- --vp-c-brand-3: #ea9a7a;
- --vp-c-brand-soft: rgba(217, 119, 87, 0.16);
-}
-
-/* Hero image larger */
-.VPHero .image-container {
- max-width: 500px !important;
- max-height: 500px !important;
-}
-
-.VPHero .image-container .image-src {
- max-width: 500px !important;
- max-height: 500px !important;
-}
-
-/* medium-zoom overlay */
-.medium-zoom-overlay {
- z-index: 30;
-}
-
-.medium-zoom-image--opened {
- z-index: 31;
-}
diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts
deleted file mode 100644
index b5b24411..00000000
--- a/docs/.vitepress/theme/index.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import DefaultTheme from 'vitepress/theme'
-import mediumZoom from 'medium-zoom'
-import { onMounted, watch, nextTick } from 'vue'
-import { useRoute } from 'vitepress'
-import './custom.css'
-
-export default {
- extends: DefaultTheme,
- setup() {
- const route = useRoute()
- const initZoom = () => {
- mediumZoom('.main img', { background: 'var(--vp-c-bg)' })
- }
- onMounted(() => initZoom())
- watch(
- () => route.path,
- () => nextTick(() => initZoom())
- )
- },
-}
diff --git a/docs/AGENTS.md b/docs/AGENTS.md
index b44cef2d..813b5bc9 100644
--- a/docs/AGENTS.md
+++ b/docs/AGENTS.md
@@ -3,7 +3,7 @@
These rules apply to `docs/` changes in addition to the root instructions.
- Keep Chinese pages and their `docs/en/` counterparts aligned when both versions exist.
-- Preserve VitePress structure and existing terminology; do not replace reference screenshots or media unless the task requires it.
+- Treat `docs/` as the bilingual content and media source for the React site in `site/`; preserve existing terminology and do not replace reference screenshots or media unless the task requires it.
- Run `bun run check:docs` when selected by `bun run check:impact`.
-- `check:docs` runs `npm ci` and may rebuild root `node_modules`; run it sequentially with other checks that use the root dependency tree.
+- `check:docs` installs the isolated `site/` dependency tree, then builds and validates the React site.
- Release instructions must stay consistent with `scripts/release.ts`, `.github/workflows/release-desktop.yml`, and the versioned release-notes convention.
diff --git a/docs/desktop/01-quick-start.md b/docs/desktop/01-quick-start.md
index 4e46bd94..51162142 100644
--- a/docs/desktop/01-quick-start.md
+++ b/docs/desktop/01-quick-start.md
@@ -1,389 +1,201 @@
-# Claude Code 桌面端 — 快速上手
+# Claude Code Haha Desktop 快速上手
-> 告别纯黑终端,用图形界面释放 Claude Code 的全部潜能。
+这篇指南从安装后的第一次打开开始,带你完成服务商配置、创建会话、审阅代码和查看后台任务。建议先用「询问权限」完成一轮小任务,再逐步启用 Auto、H5 或 Computer Use。
-
-界面布局 · 对话操作 · 多标签系统 · 权限控制 · 项目管理 · 模型与提供商 · IM 适配器 · 定时任务
-
+
----
+## 一、首次启动:先让一个模型可用
-## 一、界面布局
+打开「设置 → 服务商」。桌面端提供两类接入方式。
-
+### 官方登录
-桌面端采用**三栏 + 标签页**的经典 IDE 布局,从左到右分为:
+不想手动填写 API Key 时,可以选择:
-| 区域 | 位置 | 功能 |
-|------|------|------|
-| **侧边栏** | 左侧 | 会话列表、项目筛选、搜索、导航入口 |
-| **标签栏** | 顶部 | 多标签切换、拖拽排序、右键菜单 |
-| **内容区** | 中央 | 对话界面 / 设置页 / 任务页 |
-| **状态栏** | 底部 | 当前项目名、模型名称 |
+- **Claude 官方**:浏览器登录 Claude.ai。
+- **ChatGPT 官方**:通过 OpenAI OAuth 登录。
+- **Grok 官方**:通过 xAI OAuth 登录。
-### 侧边栏
+授权完成后回到桌面端,再从模型选择器中选择账号实际开放的模型。模型目录会随账号和服务端能力变化;文档中出现某个模型名称,不代表所有账号都一定可用。
-侧边栏是你与 Claude Code 交互的导航中心:
+### API 服务商
-- **品牌区域**:显示「Claude Code Haha」标识,点击 GitHub 图标跳转仓库
-- **操作按钮**:
- - `+` 新建会话
- - 时钟图标 → 定时任务管理
- - 齿轮图标 → 设置页面
-- **项目筛选器**:按工作目录过滤会话列表
-- **搜索框**:实时搜索会话标题(不区分大小写)
-- **会话列表**:按时间自动分组 —— 今天 / 昨天 / 最近 7 天 / 最近 30 天 / 更早
+使用 API Key 时:
-**右键菜单**:在会话项上右键可以 **重命名** 或 **删除** 会话。
+1. 从内置预设中选择服务商;常用预设随桌面端提供,不依赖临时的远程列表。
+2. 填写 API Key 和 Base URL。
+3. 确认 API 格式与鉴权方式:
+ - Anthropic Messages
+ - OpenAI Chat Completions
+ - OpenAI Responses
+4. 核对主模型及 Haiku、Sonnet、Opus 等角色模型映射。
+5. 先测试连接,成功后再保存并设为默认服务商。
-### 状态栏
+不在预设中的服务可以使用 Custom。不要把 API Key、OAuth 凭证或完整设置文件贴到公开 Issue。
-底部状态栏显示当前会话的项目路径和正在使用的 AI 模型名称,一目了然。
+当前内置能力包括 Claude 官方模型目录、OpenAI GPT-5.6 系列、Grok 官方模型,以及 Kimi Code K3 等预设。它们的上下文、thinking 和 effort 支持以当前服务商返回的能力为准。
----
+## 二、创建第一条会话
-## 二、对话操作
+1. 点击侧边栏的新建按钮,或按 `Ctrl/Cmd + N`。
+2. 选择真实的项目目录。Agent 的文件搜索、Git 状态、Diff 和工具操作都会以它为边界。
+3. 选择模型、思考强度和权限模式。
+4. 输入一个结果可验证的小任务,例如:
-### 新建会话
+ ```text
+ 先阅读这个项目的 README 和 package.json,告诉我本地开发如何启动。不要修改文件。
+ ```
-1. 点击侧边栏 `+` 按钮,或使用快捷键 `Cmd/Ctrl + N`
-2. 在空白会话页选择**工作目录**(关联本地项目文件夹)
-3. 输入第一条消息开始对话
+5. 观察流式回复、工具调用和权限请求。需要停止时按 `Ctrl/Cmd + .`。
-### 发送消息
+每个标签对应一个会话。正在运行、失败和空闲状态会显示在标签或会话列表中;关闭仍在运行的标签前,桌面端会要求确认。
-在底部输入框编写消息:
+## 三、选择正确的权限模式
-- **Enter** → 发送消息
-- **Shift + Enter** → 换行(多行输入)
-- 输入框会自动调整高度(最高 200px)
+桌面端提供五种会话权限模式。
-### 附件上传
+
-桌面端支持多种附件方式:
-
-| 方式 | 操作 |
-|------|------|
-| **粘贴图片** | 在输入框中 `Cmd/Ctrl + V` 粘贴剪贴板图片 |
-| **拖拽上传** | 将文件拖入输入区域 |
-| **文件选择** | 点击 `+` 按钮 → 「添加文件」 |
-
-附件会在输入框上方以缩略图画廊展示,支持预览和移除。
-
-### 斜杠命令
-
-
-
-输入 `/` 触发命令自动补全菜单:
-
-```
-/commit — 提交代码
-/review-pr — 审查 PR
-/memory — 管理记忆
-/debug — 调试模式
-...
-```
-
-- **上下箭头** 导航选项
-- **Enter / Tab** 确认选择
-- **Esc** 关闭菜单
-- 命令列表由服务端动态提供,支持搜索过滤
-
-### @ 文件引用
-
-
-
-输入 `@` 触发文件搜索菜单,快速将文件引入对话上下文:
-
-- 支持路径和文件名自动补全
-- 上下箭头导航、Enter 选择
-
-### 消息类型
-
-对话中会展示多种消息类型:
-
-| 类型 | 说明 | 展示形式 |
+| 模式 | 行为 | 建议场景 |
|------|------|----------|
-| **用户消息** | 你发送的文本和附件 | 用户头像 + 消息气泡 |
-| **助手消息** | AI 回复内容 | 带边框气泡 + Markdown 渲染 |
-| **思考块** | AI 的推理过程(Extended Thinking) | 可展开/折叠,显示思考预览 |
-| **工具调用** | Bash、Edit、Read 等工具操作 | 工具图标 + 可展开详情 |
-| **工具结果** | 工具执行的输出 | 代码块展示,带成功/错误状态 |
-| **权限请求** | 需要用户审批的操作 | 交互式对话框,含允许/拒绝按钮 |
-| **AskUser** | AI 向你提问 | 特殊输入框 |
+| **询问权限** (`default`) | 工具执行前逐项询问 | 第一次进入项目、陌生仓库、风险不明确 |
+| **接受编辑** (`acceptEdits`) | 自动允许文件编辑,其他操作仍询问 | 已确认修改范围,仍希望审查命令 |
+| **自动模式** (`auto`) | Claude 审查工具调用,执行其认为安全的操作并阻止高风险操作 | 熟悉项目后减少重复审批 |
+| **计划模式** (`plan`) | 只研究和规划,不执行修改 | 先评审方案、做只读调查 |
+| **跳过全部** (`bypassPermissions`) | 跳过所有权限检查 | 仅限完全可信、可恢复的隔离环境 |
-### 流式输出
+第一次启用自动模式会显示风险确认。Auto 能减少权限询问,但分类并不等于绝对安全;仍应限制工作目录、检查 Diff,并保留版本控制或备份。
-AI 回复时实时流式展示,支持:
+跳过全部的风险高于 Auto。不要在含生产凭证、个人资料或不可恢复数据的目录中使用。
-- 打字效果的闪烁光标 `▍`
-- 流式代码高亮(使用 Shiki 引擎,VS Code 级别语法着色)
-- 随时可点击 **停止** 按钮(或 `Cmd/Ctrl + .`)中断生成
+活跃任务运行期间不能切换权限模式。请先等待当前轮结束或主动停止,再修改模式。
-### 消息操作
+## 四、对话、附件与长内容定位
-悬浮在消息上时,右下角出现操作栏:
+### 输入与附件
-- **复制** — 复制消息文本内容
+- `Enter` 默认发送,`Shift + Enter` 换行;也可以在通用设置中改为 `Ctrl/Cmd + Enter` 发送。
+- 输入 `/` 打开斜杠命令。
+- 输入 `@` 搜索并引用工作区文件。
+- 可粘贴图片、拖入文件或通过文件选择器添加文件、目录和 PDF。
+- 聊天中的本地附件可以用系统默认应用打开,或从「打开方式」中选择外部应用。
----
+附件内容会随会话恢复。若原文件已经移动、删除或当前系统无法访问,打开操作会给出失败提示;这不代表历史消息被删除。
-## 三、多标签系统
+### 查找与导航
-桌面端支持同时打开多个对话标签,像浏览器一样管理会话。
+- `Ctrl/Cmd + F`:在当前页面或长对话中查找,并在匹配项之间跳转。
+- 对话导航:从用户消息和 AI 回复生成目录,适合快速回到长任务的关键节点。
+- 消息操作:复制或从某条消息分叉,触屏 H5 中也会保持可发现。
-### 标签操作
+侧边栏的会话搜索、对话内查找和工作区文件搜索是三种不同入口:一个找会话,一个找当前对话内容,一个找项目文件。
-| 操作 | 方式 |
-|------|------|
-| **新建标签** | `Cmd/Ctrl + N` 或点击 `+` |
-| **切换标签** | 点击目标标签 |
-| **关闭标签** | 悬浮标签 → 点击 `×` |
-| **拖拽排序** | 按住标签拖动到目标位置 |
-| **左右滚动** | 标签过多时出现滚动箭头 |
+## 五、在工作区审阅改动
-### 标签状态指示
+
-- **绿色脉冲点** → 会话正在运行
-- **红色点** → 会话出错
-- **无标记** → 会话空闲
+打开右侧工作区后,可以:
-### 右键菜单
+- 浏览 Git 工作区与变更文件。
+- 搜索尚未在目录树中展开的文件;正式桌面安装包已内置对应平台的 ripgrep。
+- 预览文本、图片和常见文件。
+- 在 Diff 中切换文件,查看旧行、新行和语法高亮。
+- 点击单行添加评论,或按住 `Shift` 选择同一侧的连续行。
+- 把评论和代码引用带回聊天输入框,让 Agent 根据明确位置继续修改。
-右键标签打开上下文菜单:
+被拒绝的 Write/Edit 不会因为工具“尝试过”就算作真实文件变更。最终仍应以磁盘内容和 Git Diff 为准。
-- 关闭当前标签
-- 关闭其他标签
-- 关闭左侧标签
-- 关闭右侧标签
-- 关闭全部标签
+
-### 关闭保护
+## 六、查看任务、SubAgent 与团队
-当关闭一个**正在运行的会话标签**时,会弹出确认对话框:
+当会话启动 Task、后台任务、SubAgent 或 Agent Team 后,活动面板会集中展示状态。
-- **继续运行** — 保留标签
-- **停止并关闭** — 终止会话后关闭
-- **取消** — 放弃操作
+- 运行中的 SubAgent 可以立即打开,不必等到结束。
+- 详情会继续刷新运行记录和工具调用。
+- 后台任务支持停止,并区分完成、失败与已停止。
+- 完全退出再启动后,已经持久化的终态不会被简单恢复成“进行中”。
-标签状态自动保存到 `localStorage`,下次启动时恢复。
+短暂的轮询失败可能只影响面板刷新,不等于任务数据已经丢失。遇到持续异常时,先刷新,再到「设置 → 诊断」收集信息。
----
+## 七、安装技能与管理 Agent
-## 四、权限控制
+### 技能市场
-
+从技能市场可以搜索、筛选、查看来源与安全提示,并安装支持的第三方技能。安装前应阅读技能内容、来源和免责声明;技能本质上可能扩大 Agent 可执行的工作流。
-Claude Code 在执行文件修改、Shell 命令等操作前,会请求你的权限。
+
-### 权限请求对话框
+### Agent 管理
-当 AI 需要执行敏感操作时,会弹出权限请求卡片,显示:
+进入「设置 → Agents」可以:
-- 工具类型(如 Bash、Edit、Write)
-- 操作预览(命令内容、Diff 变更等)
-- 详细输入参数(可展开查看)
+- 查看内置、用户、项目、插件、策略和 CLI 参数等来源。
+- 判断同名 Agent 当前哪个定义生效、哪个被覆盖。
+- 创建、编辑或删除用户级和项目级 Agent。
+- 为每个 Agent 分别配置模型、思考强度、允许工具和系统提示词。
-你可以选择:
+
-| 按钮 | 效果 |
-|------|------|
-| **允许** | 仅本次允许执行 |
-| **一直允许** | 本次会话内自动允许同类操作 |
-| **拒绝** | 拒绝本次执行 |
+用户 Agent 写入 `~/.claude/agents/`,项目 Agent 写入当前项目的 `.claude/agents/`。内置、插件与策略来源保持只读。
-### 四种权限模式
+模型和 effort 可以继承主会话,也可以单独指定;不受所选模型支持的 effort 可能被降低或忽略。完整规则见[多 Agent 使用指南](../agent/01-usage-guide.md#六自定义-agent)。
-在设置中可切换全局权限模式:
+## 八、使用桌面宠物
-| 模式 | 说明 | 适用场景 |
-|------|------|----------|
-| **询问权限** (default) | 每个操作都需确认 | 安全第一 |
-| **自动接受** (acceptEdits) | 自动允许编辑操作 | 信任文件修改 |
-| **计划模式** (plan) | 只展示计划不执行 | 审查方案 |
-| **绕过权限** (bypassPermissions) | 全部自动执行 | 完全信任(需二次确认) |
+
-> 切换到「绕过权限」时需要确认警告对话框,防止误操作。
+按下面的顺序即可开始使用:
----
+1. 进入「设置 → 宠物」。
+2. 从搭搭 Dada、弧弧 Huhu、补补 Bubu、回回 Huihui 中选择一个内置角色。
+3. 打开「显示桌面宠物」,透明悬浮窗口会立即出现。
+4. 按需要调整 96–192px 大小、动画和「显示运行中的任务面板」。
+5. 在桌面上拖动宠物调整位置;单击宠物会唤起主窗口,单击任务行会返回对应会话,右键可以关闭宠物。
-## 五、项目管理
+任务面板只显示正在工作、等待处理或失败的活跃任务;没有活跃任务时会自动隐藏。右键关闭宠物不会停止正在执行的任务,下次可以回到「设置 → 宠物」重新开启。
-每个会话都绑定一个**工作目录**,即你的本地项目文件夹。
+还可以通过「添加宠物」导入透明 PNG / WebP 单图,或导入符合规格的 v2 动画图集。导入在本地完成,不会调用当前聊天模型;AI 生成完整动画目前不可用。图片要求、创建步骤、存储位置和故障排查见[桌面宠物使用指南](./pets.md)。
-### 工作目录
+宠物仅在 Electron 桌面端运行,不会出现在 H5 页面中。窗口置顶、拖动与多显示器行为仍受操作系统窗口管理影响。
-- 新建会话时通过 **DirectoryPicker** 选择工作目录
-- 系统自动检测目录是否存在,不存在时显示警告
-- 支持 Git 仓库识别,状态栏和会话信息中展示:
- - 仓库名称
- - 当前分支
- - 项目路径
+## 九、从手机或 IM 继续使用
-### 项目筛选
+### H5
-侧边栏中的 **ProjectFilter** 组件可按项目过滤会话:
+在「设置 → H5 访问」开启后,桌面服务会提供带 Token 的二维码和连接地址。普通局域网访问使用同一个 H5 页面、REST API 和 WebSocket 服务;需要稳定书签或反向代理时可固定端口。
-1. 点击项目筛选下拉框
-2. 选择目标项目
-3. 会话列表仅显示该项目的会话
+H5 会暴露当前桌面服务的核心能力,只应在可信网络和自己的代理中使用。详细步骤见[H5 访问](./06-h5-access.md)。
-### 最近项目
+### IM
-通过 API 获取最近使用的项目列表,包含:
+「设置 → IM 接入」支持:
-- 项目路径和名称
-- 是否为 Git 仓库
-- 分支名
-- 会话数量
-- 最后修改时间
+- 微信
+- 钉钉
+- WhatsApp
+- Telegram
+- 飞书
----
+扫码绑定平台账号并不等于授权所有联系人。用户仍需发送一次性配对码,或被加入允许列表。详见[IM 接入总览](../im/index.md)。
-## 六、模型与提供商
-
-### 模型选择
-
-点击状态栏的模型名称,或在设置中打开模型选择器:
-
-- 显示所有可用模型(带图标区分 Opus/Sonnet/Haiku)
-- 单选切换当前模型
-- 支持 **Effort 级别** 调节:Low / Medium / High / Max
-
-### 自定义 Provider
-
-在设置 → **Providers** 标签页管理 AI 提供商:
-
-#### 预设提供商
-
-系统内置多种预设,快速配置:
-
-- **Anthropic** — 官方 API
-- **OpenAI** — GPT 系列
-- **OpenRouter** — 多模型聚合
-- **Ollama** — 本地模型
-- 其他 Anthropic 兼容服务
-
-#### 配置项
-
-| 字段 | 说明 |
-|------|------|
-| API Key | 提供商密钥 |
-| Base URL | API 基础地址 |
-| API 格式 | anthropic / openai_chat / openai_responses |
-| 模型映射 | main / haiku / sonnet / opus 对应的实际模型名 |
-
-#### 连接测试
-
-配置完成后可点击 **测试连接**,系统会发送测试请求验证配置是否正确(两步验证:连接性 + 模型可用性)。
-
----
-
-## 七、IM 适配器接入
-
-桌面端不仅提供本地 GUI,还支持通过 **Telegram 和飞书** 远程对话。
-
-### 配置步骤
-
-1. 打开设置 → **Adapters** 标签页
-2. 填入平台凭证:
- - **Telegram**:Bot Token
- - **飞书**:App ID + App Secret
-3. 配置默认工作目录和允许的用户
-4. 启动适配器进程
-
-### 用户配对
-
-为了安全,IM 用户需要通过**配对码**授权:
-
-1. 在桌面端设置中点击「生成配对码」
-2. 获取一个 **6 位安全字符码**(60 分钟有效、一次性使用)
-3. 在 Telegram/飞书中向 Bot 发送该配对码
-4. 配对成功后即可开始对话
-
-安全特性:
-- 排除易混淆字符(0/O/1/I/L)
-- 同一用户 5 分钟内最多失败 5 次
-- 密钥在 API 返回时自动脱敏(仅显示后 4 位)
-
-### IM 内操作
-
-配对成功后,可在 IM 中:
-
-| 命令 | 效果 |
-|------|------|
-| 直接发文本 | 与 Claude Code 对话 |
-| `/new [项目名]` 或 `新会话` | 开始新会话(支持模糊匹配项目) |
-| `/projects` 或 `项目列表` | 查看最近项目 |
-| `/stop` 或 `停止` | 停止当前生成 |
-
-权限请求在 IM 中以**按钮卡片**形式展示(Telegram Inline Keyboard / 飞书 Interactive Card),点击即可审批。
-
----
-
-## 八、定时任务
-
-桌面端支持创建 **Cron 定时任务**,让 Claude Code 按计划自动执行。
-
-### 进入任务管理
-
-点击侧边栏的**时钟图标**进入定时任务页面。
-
-页面顶部显示统计卡片:总任务数 / 活跃数 / 禁用数。
-
-### 创建任务
-
-点击「新建任务」,填写:
-
-| 字段 | 说明 |
-|------|------|
-| 任务名称 | 描述性名称 |
-| 提示词 | Claude Code 执行的 Prompt |
-| Cron 表达式 | 执行时间计划 |
-| 周日期 | 可视化选择执行的星期几 |
-| 模型 | 使用的 AI 模型 |
-| 权限模式 | 任务执行时的权限策略 |
-
-### 任务管理
-
-每个任务行显示:
-- 名称和描述
-- Cron 表达式(人性化展示)
-- 启用/禁用开关
-- 运行历史(可展开查看)
-- 手动运行按钮
-- 删除按钮
-
-> 注意:定时任务在桌面应用运行时才会执行。
-
----
-
-## 九、键盘快捷键速查
+## 十、快捷键
| 快捷键 | 功能 |
|--------|------|
-| `Cmd/Ctrl + N` | 新建会话 |
-| `Cmd/Ctrl + K` | 聚焦侧边栏搜索 |
-| `Cmd/Ctrl + .` | 停止当前生成 |
-| `Escape` | 关闭模态框 |
-| `Enter` | 发送消息 |
-| `Shift + Enter` | 输入框换行 |
-| `/` | 触发斜杠命令菜单 |
-| `@` | 触发文件搜索菜单 |
+| `Ctrl/Cmd + N` | 新建会话 |
+| `Ctrl/Cmd + F` | 当前页面或对话内查找 |
+| `Ctrl/Cmd + K` | 打开全局会话搜索 |
+| `Ctrl/Cmd + .` | 停止当前生成 |
+| `Enter` | 按当前发送设置发送 |
+| `Shift + Enter` | 默认设置下换行 |
+| `/` | 打开斜杠命令 |
+| `@` | 搜索工作区文件 |
+| `Escape` | 关闭当前弹层 |
----
+## 下一步
-## 十、主题与国际化
-
-### 主题切换
-
-在设置中切换 **浅色 / 深色** 主题。
-
-桌面端采用暖色调设计系统:
-- 浅色模式:奶油色背景(#FAF9F5)
-- 深色模式:深色背景
-- 品牌色:褐红色(#8F482F)
-
-### 国际化
-
-支持 **中文** 和 **英文** 两种语言,在设置 → 通用 中切换。
+- 系统了解能力:[功能详解](./03-features.md)
+- 安装、更新和 Web UI:[安装指南](./04-installation.md)
+- 连接、索引或恢复异常:[常见问题](./05-FAQ.md)
+- 手机访问与安全部署:[H5 访问](./06-h5-access.md)
diff --git a/docs/desktop/02-architecture.md b/docs/desktop/02-architecture.md
index 744a4b20..b8a1fbb9 100644
--- a/docs/desktop/02-architecture.md
+++ b/docs/desktop/02-architecture.md
@@ -1,394 +1,202 @@
-# 架构设计
+# 桌面端架构
-> 从 Electron host 到 CLI 子进程,桌面端的三层通信架构。
+> 本页面向维护者,说明 Claude Code Haha 桌面端当前的 Electron、Bun Server 和 CLI 运行边界。
----
+## 架构概览
-## 技术栈
+桌面端不是把 CLI 直接嵌进 React。它由四类进程协作:
-### 前端
-
-| 技术 | 版本 | 职责 |
-|------|------|------|
-| React | 18 | UI 框架 |
-| Zustand | 5 | 状态管理 |
-| Vite | 6 | 构建工具 |
-| Tailwind CSS | 4 | 样式 |
-| Shiki | 4 | 代码高亮 |
-| Mermaid | 11 | 图表渲染 |
-| marked + DOMPurify | - | Markdown 渲染 |
-| react-diff-viewer | 4 | Diff 展示 |
-| Lucide React | - | 图标 |
-
-### 桌面层
-
-| 技术 | 版本 | 职责 |
-|------|------|------|
-| Electron | - | 跨平台桌面 host,管理 Chromium renderer、窗口、IPC 和系统能力 |
-| electron-builder | - | macOS/Windows/Linux 打包与 release artifact 生成 |
-| electron-updater | - | 自动更新检查、下载与安装 |
-| node-pty | - | Electron main 中的终端 PTY runtime |
-| `desktop/src-tauri` 资源目录 | - | 历史资源位置,暂存 sidecar 二进制、图标和 preview agent;不再作为桌面 runtime |
-
-### 服务端
-
-| 技术 | 职责 |
-|------|------|
-| Bun | 运行时 + 构建工具 |
-| Bun.serve | HTTP/WebSocket 服务器 |
-
-### 字体
-
-- **Inter** — 正文
-- **Manrope** — 标题
-- **JetBrains Mono** — 代码
-- **Material Symbols Outlined** — 图标
-
----
-
-## 三层架构
-
-```
-┌─────────────────────────────────────┐
-│ Electron Host (main/preload) │
-│ ┌─────────────────────────────┐ │
-│ │ Chromium Renderer │ │ ← React 用户界面
-│ └───────────┬─────────────────┘ │
-│ │ HTTP + WebSocket │
-│ ┌───────────▼─────────────────┐ │
-│ │ Server Sidecar (Bun) │ │ ← API 服务 + 会话管理
-│ │ Port: 动态分配 │ │
-│ └───────────┬─────────────────┘ │
-│ │ 子进程 spawn │
-│ ┌───────────▼─────────────────┐ │
-│ │ CLI 子进程 │ │ ← AI 对话 + 工具执行
-│ └─────────────────────────────┘ │
-│ │
-│ ┌─────────────────────────────┐ │
-│ │ Adapter Sidecar (可选) │ │ ← Telegram/飞书接入
-│ └─────────────────────────────┘ │
-└─────────────────────────────────────┘
+```text
+Electron main
+├── Chromium renderer(React UI)
+├── claude-sidecar server
+│ ├── Bun.serve HTTP API
+│ ├── Bun WebSocket 会话网关
+│ └── 按会话启动 CLI 子进程
+└── claude-sidecar adapters(按平台独立启动)
+ ├── Telegram
+ ├── Feishu
+ ├── WeChat
+ ├── DingTalk
+ └── WhatsApp
```
-### 第一层:Electron Host
+- **Electron main** 负责窗口、系统对话框、更新、终端、原生预览、宠物窗口和 Sidecar 生命周期。
+- **Renderer** 只负责界面,通过 preload 暴露的 `window.desktopHost` 使用原生能力。
+- **Server Sidecar** 是桌面端和 H5 共用的本地服务,提供 REST、WebSocket、Provider 代理和会话管理。
+- **CLI 子进程** 执行模型请求、工具调用和 Agent 编排。
+- **Adapter Sidecar** 把 IM 平台消息桥接到同一套 Server/CLI 会话。
-**职责**:窗口管理、Sidecar 进程编排、原生 API 桥接
+`desktop/src-tauri/` 目前只是保留打包资源和历史代码的位置,不是桌面运行时。当前桌面 Host 是 Electron。
-核心文件位于 `desktop/electron/`,通过 preload 暴露 `window.desktopHost`,renderer 只调用 typed host contract,不直接调用 Electron IPC。
+## 当前技术栈
-| 能力 | 说明 |
-|------|------|
-| `runtime.getServerUrl` | 前端获取 Server Sidecar 的 HTTP 地址 |
-| `dialogs.open/save` | 系统文件/目录选择与保存对话框 |
-| `shell.open/openPath` | 外链与本地路径打开,main 侧做 scheme/path allowlist |
-| `updates.check/download/install` | electron-updater 检查、下载、安装与 relaunch |
-| `terminal.*` | node-pty 会话创建、写入、resize、kill 和事件转发 |
-| `preview.*` | WebContentsView 预览面板与 preview-agent message bridge |
-| `appMode.*` | 默认/便携模式和 `CLAUDE_CONFIG_DIR` 启动语义 |
+| 层 | 主要技术 | 用途 |
+|---|---|---|
+| Renderer | React 18、Vite 8、TypeScript、Zustand 5 | 桌面 UI 与状态管理 |
+| 样式与内容 | Tailwind CSS 4、Marked、DOMPurify、Shiki 4、Mermaid、KaTeX | 布局、Markdown、代码和图表 |
+| Diff | `react-diff-viewer-continued` | 对话和工作区中的 Diff 展示 |
+| Electron Host | Electron、electron-builder、electron-updater | 原生窗口、打包和更新 |
+| 终端 | node-pty、xterm.js | 原生 PTY 与终端渲染 |
+| 本地服务 | Bun、`Bun.serve` | HTTP API 与 WebSocket |
-**状态管理**:
+版本以 `desktop/package.json` 为准。本页只列会影响架构理解的主版本,避免复制完整依赖清单。
-| 模块 | 说明 |
-|------|------|
-| `serverRuntime.ts` | server/adapters sidecar 生命周期、动态端口、日志和 env 注入 |
-| `sidecarManager.ts` | sidecar 二进制定位、参数构造、子进程管理 |
-| `windows.ts` | 窗口状态持久化、显示/聚焦、close-to-hide 生命周期 |
-| `updater.ts` | 更新元数据归一化、下载进度和安装前状态校验 |
+## Electron Host
-**启动流程**:
+核心入口与边界:
-1. 应用启动时解析便携模式,必要时设置 `CLAUDE_CONFIG_DIR`。
-2. 创建 `BrowserWindow` 并注入 preload host;dev 模式只允许 localhost renderer,packaged 模式加载 `app.asar/dist/index.html`。
-3. renderer 调用 `runtime.getServerUrl()`,Electron host 启动 `claude-sidecar server --app-root ... --host 0.0.0.0 --port {dynamic}`。
-4. server 健康检查通过后,React 继续加载设置、会话和 WebSocket。
-5. 修改 IM adapter 配置后,renderer 调用 `adapters.restartSidecar()`,host 重启 `claude-sidecar adapters` 并注入 `ADAPTER_SERVER_URL`。
+| 路径 | 职责 |
+|---|---|
+| `desktop/electron/main.ts` | Electron main 入口、窗口与 IPC 注册 |
+| `desktop/electron/preload.ts` | 向主 renderer 暴露类型化 Host API |
+| `desktop/electron/preview-preload.ts` | 原生网页预览的隔离桥接 |
+| `desktop/electron/pet-preload.ts` | 宠物窗口的最小能力桥接 |
+| `desktop/electron/ipc/` | IPC channel 注册和 payload 校验 |
+| `desktop/electron/services/` | Sidecar、更新、终端、预览、窗口、代理等系统服务 |
-**退出处理**:窗口默认 close-to-hide;明确退出、更新安装或 app mode restart 前会停止 server/adapters sidecar。
+Renderer 不应直接导入 Electron,也不应自行拼接任意 IPC channel。新增原生能力时,应同时更新 Host contract、main 侧校验和相关测试。
-**平台差异**:
-- **macOS**:overlay titlebar + 自定义菜单(关于、设置 Cmd+,、编辑、窗口)
-- **Windows**:`set_decorations(false)` 隐藏原生标题栏,前端自定义渲染 `TitleBar` + `WindowControls`
+### 启动流程
-### 第二层:Server Sidecar
+1. Electron 解析默认或便携存储模式,并准备应用配置目录。
+2. Host 选择可用端口,启动统一的 `claude-sidecar server` 二进制。
+3. Sidecar 从 `src/server/index.ts` 进入 `startServer()`,使用 `Bun.serve` 同时承载 HTTP 和 WebSocket。
+4. 健康检查通过后,Renderer 获取 loopback Server URL 并加载会话与设置。
+5. Host 根据已配置的平台启动对应 Adapter Sidecar。
+6. Server 在用户开始会话时按需启动 CLI 子进程。
-**职责**:HTTP REST API + WebSocket 网关 + 会话管理 + 协议代理
+Server 可绑定局域网可访问地址以支持 H5,但桌面 Renderer 使用 loopback 控制地址。H5、远程访问和宠物窗口分别经过其对应的 Token 与能力限制,不能因为本地 Server 已启动就假定所有来源都可信。
-核心文件 `src/server/`(项目根目录),分层结构:
+### Sidecar 入口
+`desktop/sidecars/claude-sidecar.ts` 是统一入口:
+
+```text
+claude-sidecar server --app-root --host --port
+claude-sidecar cli --app-root [CLI arguments]
+claude-sidecar adapters --app-root --telegram|--feishu|--wechat|--dingtalk|--whatsapp
```
+
+Sidecar 在导入业务模块前设置 `CLAUDE_APP_ROOT`、`CALLER_DIR` 和启动参数,因为 Server、CLI 与 Adapter 的顶层模块会读取这些值。
+
+## Server Sidecar
+
+真实 Server 入口是 `src/server/index.ts`,不是单独的 `server.ts` 包装层。
+
+```text
src/server/
-├── index.ts # 入口
-├── server.ts # HTTP 服务器
-├── router.ts # 路由注册
-├── sessionManager.ts # 会话管理器
-├── api/ # REST 路由层 (14 个模块)
-├── services/ # 业务服务层 (14 个模块)
-├── ws/ # WebSocket 处理
-├── proxy/ # API 代理(Anthropic/OpenAI 协议转换)
-├── middleware/ # auth、cors、errorHandler
-└── config/ # Provider 预设
+├── index.ts # Bun.serve、鉴权、CORS、升级和静态 H5
+├── router.ts # REST 资源路由
+├── api/ # API 边界
+├── services/ # 会话、Provider、索引、诊断等业务服务
+├── ws/ # WebSocket 协议与会话生命周期
+├── proxy/ # Provider 协议与流式响应转换
+├── middleware/ # Auth、CORS 和错误边界
+└── config/ # Provider 预设
```
-### 第三层:CLI 子进程
+`Bun.serve` 的同一个 `fetch` 边界处理:
-**职责**:AI 对话核心、工具执行、Agent 编排
+- `/api/*` REST 请求
+- `/ws/:sessionId` 桌面、H5 和宠物客户端连接
+- `/sdk/:sessionId` CLI 内部连接
+- OAuth 回调
+- 受限的预览与本地文件访问
+- 打包后的 H5 静态资源
-Server 为每个 Session spawn 一个 CLI 子进程,通过 stdin/stdout JSON 通信。
+鉴权规则按客户端能力区分。新增路由时,必须先确定它属于本地桌面、H5、宠物、内部 SDK 还是公开静态资源,再放进对应的认证和 CORS 边界。
-### Sidecar 构建
+## WebSocket 语义
-使用 Bun 编译为独立二进制(`desktop/scripts/build-sidecars.ts`):
+Renderer 为每个会话维护一条连接:
-三种模式共用一个入口 `desktop/sidecars/claude-sidecar.ts`:
-- `server` — 启动 HTTP/WS 服务
-- `cli` — 启动 CLI 子进程
-- `adapters` — 启动 IM 适配器(解析 `--feishu`/`--telegram` 参数,检查凭据后按需加载)
-
-编译产物仍放置在 `desktop/src-tauri/binaries/`,作为历史稳定资源目录;Electron Builder 通过 `files` 和 `asarUnpack` 把它们打入 `app.asar.unpacked`。
-
----
-
-## WebSocket 通信协议
-
-### 连接地址
-
-```
-ws://127.0.0.1:{port}/ws/{sessionId}
+```text
+ws:///ws/
```
-前端通过 `WebSocketManager`(per-session 连接)管理,支持自动重连、消息队列缓冲、ping 心跳保活。
+如果 Server 启用了认证,客户端会把 Token 放在连接查询参数中。连接成功后,Server 会发送当前会话标识、尚未处理的权限请求快照和运行状态。
-### 客户端 → 服务端
+### 心跳与重连
-| type | 说明 |
-|------|------|
-| `user_message` | 用户消息(含 content, attachments) |
-| `permission_response` | 权限审批(requestId, allowed, rule) |
-| `set_permission_mode` | 切换权限模式 |
-| `stop_generation` | 停止生成 |
-| `ping` | 心跳 |
+当前 `desktop/src/api/websocket.ts` 的行为是:
-### 服务端 → 客户端
+- 连接后每 30 秒发送一次 `ping`。
+- 10 秒内没有收到 `pong`,客户端主动关闭连接并进入重连。
+- 重连延迟从 1 秒开始指数增长,最高封顶 30 秒。
+- 自动重连没有固定的最大尝试次数;显式关闭会话才停止。
+- 断线期间发送的消息进入内存队列。
+- 重连成功后先发送队列中的消息,再发送 `sync_state` 获取 Server 的权威运行状态。
-| type | 说明 |
-|------|------|
-| `connected` | 连接成功 |
-| `status` | 状态变更(thinking/generating) |
-| `content_start` / `content_delta` | 流式文本 |
-| `thinking` | Extended Thinking |
-| `tool_use_complete` | 工具调用就绪 |
-| `tool_result` | 工具执行结果 |
-| `permission_request` | 权限请求 |
-| `message_complete` | 消息完成(含 Token 统计) |
-| `error` | 错误通知 |
-| `session_title_updated` | 标题更新 |
-| `team_update` / `team_created` / `team_deleted` | 团队事件 |
-| `task_update` | 任务变更 |
-| `pong` | 心跳响应 |
+因此,不应把旧文档里的“最多重试 10 次”当成当前语义。
-### 连接管理
+### 客户端断开不等于停止任务
-- **心跳**:30s 间隔 ping/pong
-- **重连**:指数退避 `min(1000ms × 2^n, 30000ms)`,最多 10 次
-- **缓冲**:未连接时消息暂存队列,恢复后自动发送
+最后一个客户端断开后:
----
+- 正在运行的前台回合或后台任务继续执行。
+- 工作结束后才进入空闲宽限期。
+- 客户端在宽限期内重连会取消清理。
+- 超过宽限期且没有客户端,Server 才停止对应 CLI。
+- 等待权限的会话有独立的有界清理策略,避免永久占用进程。
-## HTTP API
+这让手机锁屏、Renderer 刷新或短暂网络切换不会直接中断正在运行的任务。
-### 会话
+## CLI 与 Provider 代理
-| 方法 | 端点 | 说明 |
-|------|------|------|
-| `GET` | `/api/sessions` | 列表(支持 project/limit/offset 筛选) |
-| `POST` | `/api/sessions` | 创建 |
-| `GET` | `/api/sessions/:id/messages` | 历史消息 |
-| `PATCH` | `/api/sessions/:id` | 重命名 |
-| `DELETE` | `/api/sessions/:id` | 删除 |
-| `GET` | `/api/sessions/:id/git-info` | Git 信息 |
-| `GET` | `/api/sessions/:id/slash-commands` | 可用斜杠命令 |
-| `GET` | `/api/sessions/recent-projects` | 最近项目 |
+Server 按会话启动 CLI,并通过内部协议转发输出、权限请求、工具结果和后台任务状态。会话的 Provider、模型、effort 和权限模式由 Server/CLI 共同维护,Renderer 不是唯一真相来源。
-### 模型与提供商
+`src/server/proxy/` 处理支持的 Provider 协议:
-| 方法 | 端点 | 说明 |
-|------|------|------|
-| `GET/PUT` | `/api/models/current` | 当前模型 |
-| `GET` | `/api/models` | 可用模型列表 |
-| `GET/PUT` | `/api/effort` | Effort 级别 |
-| `GET/POST/PUT/DELETE` | `/api/providers` | 提供商 CRUD |
-| `POST` | `/api/providers/:id/activate` | 激活 |
-| `POST` | `/api/providers/:id/test` | 测试连接 |
-| `GET` | `/api/providers/presets` | 预设列表 |
+- Anthropic Messages
+- OpenAI Chat Completions
+- OpenAI Responses
-### 定时任务
+Provider 的模型映射、认证方式和上下文设置以实际 Provider 配置为准,不应在架构文档中硬编码厂商清单。
-| 方法 | 端点 | 说明 |
-|------|------|------|
-| `GET/POST/PUT/DELETE` | `/api/scheduled-tasks` | 任务 CRUD |
-| `POST` | `/api/scheduled-tasks/:id/run` | 手动运行 |
-| `GET` | `/api/scheduled-tasks/runs` | 运行记录 |
+## IM Adapter
-### 其他
+每个平台使用独立 Adapter Sidecar,避免一个平台的凭据或启动失败拖垮其他平台。
-| 方法 | 端点 | 说明 |
-|------|------|------|
-| `GET` | `/api/teams` | Agent 团队 |
-| `GET` | `/api/teams/:name/members/:agentId/transcript` | 成员转录 |
-| `GET` | `/api/agents` | Agent 定义 |
-| `GET` | `/api/skills` | 技能列表 |
-| `GET/PUT` | `/api/adapters` | 适配器配置 |
-| `GET/PUT` | `/api/settings/user` | 用户设置 |
-| `GET/PUT` | `/api/permissions/mode` | 权限模式 |
-| `GET` | `/api/tasks/lists` | CLI 任务清单 |
-| `GET` | `/health` | 健康检查 |
-
----
-
-## 状态管理
-
-使用 Zustand,按领域拆分为 12 个 Store:
-
-| Store | 核心状态 |
-|-------|----------|
-| `chatStore` | per-session 消息、流式状态、权限请求、Token 统计 |
-| `sessionStore` | 会话列表、activeSessionId、项目筛选 |
-| `tabStore` | 标签页顺序(localStorage 持久化) |
-| `settingsStore` | 权限模式、当前模型、effort、语言 |
-| `providerStore` | Provider 列表、activeId |
-| `uiStore` | 主题、侧边栏、activeView、Toast、设置标签页 |
-| `taskStore` | 定时任务、运行记录 |
-| `teamStore` | 团队列表、成员转录轮询 |
-| `agentStore` | Agent 定义列表 |
-| `skillStore` | 技能元数据、详情 |
-| `adapterStore` | 适配器配置、配对码 |
-| `cliTaskStore` | per-session CLI 任务追踪 |
-
-### 数据流
-
-```
-用户操作 → Component → Store → API/WebSocket → Server → Store → Component 重渲染
+```text
+IM 平台
+ → adapters/
+ → adapters/common WebSocket bridge
+ → Server Sidecar
+ → CLI 会话
```
-### 持久化
+共享层位于 `adapters/common/`,负责配置、配对、会话映射、消息缓冲、去重、附件和 Server WebSocket 桥接。当前平台目录包括:
-| 数据 | 存储 |
-|------|------|
-| 标签页状态 | `localStorage` |
-| 语言偏好 | `localStorage` |
-| 会话数据 | Server JSONL (`~/.claude/sessions/`) |
-| 设置 | Server API |
-| 适配器配置 | `~/.claude/adapters.json` |
+- `adapters/telegram/`
+- `adapters/feishu/`
+- `adapters/wechat/`
+- `adapters/dingtalk/`
+- `adapters/whatsapp/`
----
+## 持久化边界
-## 协议代理层
+不同数据不共享同一个存储:
-Server 内置代理层(`src/server/proxy/`),统一不同 AI 提供商的 API 格式。
+| 数据 | 主要边界 |
+|---|---|
+| Renderer UI 偏好与打开标签 | 浏览器存储及其迁移 |
+| 会话与消息 | Server/CLI 管理的本地会话数据 |
+| Provider、H5、Computer Use 等设置 | Server 管理的配置文件 |
+| Electron 窗口和原生状态 | Electron user data / 应用模式目录 |
+| IM 配置和配对状态 | Adapter 配置与各平台状态目录 |
-### 支持格式
+任何 JSON、`localStorage` 或应用配置形状变化都必须带前向迁移和旧数据回归测试。不要直接覆盖用户的共享 Claude 配置,也不要在测试中读取真实用户目录。
-| 格式 | 典型提供商 |
-|------|-----------|
-| `anthropic` | Anthropic、OpenRouter、MiniMax |
-| `openai_chat` | OpenAI、DeepSeek、Ollama |
-| `openai_responses` | OpenAI Responses API |
+## 构建与验证
-### 模型映射
+桌面构建由 `desktop/package.json` 的脚本编排:
-每个 Provider 配置 4 个模型槽位:`main`、`haiku`、`sonnet`、`opus`,前端按槽位名调用,代理层自动映射为实际模型名。
-
----
-
-## 适配器架构
-
-适配器系统让 Telegram/飞书等 IM 平台接入 Claude Code。
-
-```
-IM 平台 → Adapter 进程 → HTTP + WebSocket → Server → CLI
+```bash
+cd desktop
+bun run build:sidecars
+bun run build
+bun run build:electron
```
-### 共享模块 `adapters/common/`
-
-| 模块 | 职责 |
-|------|------|
-| `config.ts` | 配置加载(env > JSON > 默认值) |
-| `ws-bridge.ts` | WebSocket 桥接(心跳、重连、消息路由) |
-| `pairing.ts` | 用户配对(6 位安全码、速率限制) |
-| `session-store.ts` | Chat → Session 映射持久化 |
-| `message-buffer.ts` | 流式缓冲(500ms / 200 字符阈值) |
-| `message-dedup.ts` | 消息去重 |
-| `chat-queue.ts` | 同一 Chat 消息串行队列 |
-| `http-client.ts` | HTTP 客户端 |
-
-### 分片限制
-
-- Telegram: 4000 字符/消息
-- 飞书: 30000 字符/消息
-
----
-
-## 项目目录结构
-
-```
-desktop/
-├── src/ # React 前端
-│ ├── api/ # API 客户端 (15 个模块)
-│ ├── components/
-│ │ ├── layout/ # AppShell, Sidebar, TabBar, TitleBar,
-│ │ │ # WindowControls, StatusBar, ContentRouter,
-│ │ │ # ProjectFilter
-│ │ ├── chat/ # ChatInput, MessageList, AssistantMessage,
-│ │ │ # ToolCallBlock, ToolCallGroup, ThinkingBlock,
-│ │ │ # PermissionDialog, CodeViewer, DiffViewer,
-│ │ │ # ImageGalleryModal, StreamingIndicator ...
-│ │ ├── shared/ # Button, Modal, Toast, Spinner,
-│ │ │ # UpdateChecker, DirectoryPicker ...
-│ │ ├── controls/ # ModelSelector, PermissionModeSelector
-│ │ ├── markdown/ # MarkdownRenderer, MermaidRenderer
-│ │ ├── skills/ # SkillList, SkillDetail
-│ │ ├── tasks/ # TaskList, TaskRow, NewTaskModal,
-│ │ │ # DayOfWeekPicker, PromptEditor ...
-│ │ └── teams/ # TeamStatusBar
-│ ├── pages/ # ActiveSession, EmptySession, Settings,
-│ │ # AdapterSettings, AgentTeams, ScheduledTasks,
-│ │ # ComputerUseSettings, ToolInspection ...
-│ ├── stores/ # 12 个 Zustand store
-│ ├── types/ # TypeScript 类型 (9 个模块)
-│ ├── hooks/ # useKeyboardShortcuts
-│ ├── i18n/ # 中/英 国际化
-│ ├── config/ # providerPresets, spinnerVerbs
-│ └── lib/ # desktopRuntime, cronDescribe, parseRunOutput
-├── src-tauri/
-│ ├── binaries/ # Electron 打包使用的 sidecar 二进制
-│ ├── resources/preview-agent.js # 预览页面注入脚本
-│ ├── src/main.rs # 历史 Tauri 入口,非当前 runtime
-│ ├── src/lib.rs # 历史 Tauri host,非当前 runtime
-│ ├── Cargo.toml
-│ └── tauri.conf.json
-├── electron/
-│ ├── main.ts # Electron main 入口
-│ ├── preload.ts # renderer host bridge
-│ ├── preview-preload.ts # preview WebContentsView bridge
-│ ├── ipc/ # channel registry + payload validators
-│ └── services/ # dialogs, shell, updater, terminal, preview ...
-├── sidecars/
-│ └── claude-sidecar.ts # 统一入口 (server/cli/adapters)
-└── scripts/
- ├── build-sidecars.ts
- ├── build-macos-arm64.sh
- └── build-windows-x64.ps1
-
-src/server/ # 服务端(项目根目录)
-├── api/ # REST 路由 (14 个模块)
-├── services/ # 业务服务 (14 个模块)
-├── ws/ # WebSocket 处理
-├── proxy/ # 协议代理转换
-├── middleware/ # auth, cors, errorHandler
-└── config/ # Provider 预设
-
-adapters/ # IM 适配器
-├── common/ # 共享模块 (8 个)
-├── telegram/ # Telegram Bot
-└── feishu/ # 飞书 Bot
-```
+`electron:package` 在此基础上运行 electron-builder。打包产物验证只能证明资源和安装包结构正确;涉及窗口、权限、终端、预览、更新或 Computer Use 的用户流程,还需要真实桌面 Smoke 验证。
diff --git a/docs/desktop/03-features.md b/docs/desktop/03-features.md
index 7c4984eb..ca316258 100644
--- a/docs/desktop/03-features.md
+++ b/docs/desktop/03-features.md
@@ -1,278 +1,258 @@
-# 功能详解
+# 桌面端功能详解
-> 桌面端核心功能模块一览。
+本页按用户工作流说明当前桌面端的主要能力和边界。第一次使用建议先读[快速上手](./01-quick-start.md)。
----
+## 会话与项目
-## 聊天引擎
+每个会话绑定一个工作目录。桌面端会把会话列表、标签页、模型、权限模式和项目上下文组织到同一个工作台中。
-
+- 新建会话时选择目录、模型、思考强度与权限模式。
+- 侧边栏按项目和时间组织历史会话。
+- 多标签支持切换、排序和批量关闭。
+- 会话恢复会保留已提交的消息、附件上下文、任务终态和阅读位置。
+- 异常结束的当前轮可以基于服务端记录的 checkpoint 撤销,不依赖界面猜测文件状态。
-聊天引擎是桌面端核心,负责消息收发、流式渲染和工具交互。
+如果工作目录已经移动或删除,历史消息仍可阅读,但文件搜索、预览和工具操作会受到限制。
-### 消息类型
+## 对话与内容导航
-| 类型 | 组件 | 说明 |
-|------|------|------|
-| 用户消息 | `UserMessage` | 文本 + 附件画廊 |
-| 助手消息 | `AssistantMessage` | Markdown 渲染 + 流式光标 `▍` |
-| 思考块 | `ThinkingBlock` | Extended Thinking,默认折叠 |
-| 工具调用 | `ToolCallBlock` / `ToolCallGroup` | 按工具类型专门展示 |
-| 工具结果 | `ToolResultBlock` | 成功/失败状态 + 输出内容 |
-| 权限请求 | `PermissionDialog` | 操作预览 + 允许/一直允许/拒绝 |
-| AI 提问 | `AskUserQuestion` | AI 向用户提问的专用输入框 |
-| 任务摘要 | `InlineTaskSummary` | 后台任务进度内联展示 |
+对话区支持 Markdown、代码高亮、Mermaid、思考块、工具调用、权限请求和 AI 提问。
-### 工具展示
+### 长对话
-不同工具有专门的渲染方式:
+- `Ctrl/Cmd + F` 在当前对话中查找,并支持上一个、下一个匹配项。
+- 对话导航从用户消息和 AI 回复生成可扫描目录。
+- 虚拟化长列表会恢复阅读位置,避免重新挂载后直接跳到底部。
+- 可以从历史节点继续或分叉任务;最终状态以服务端会话记录为准。
-- **Bash** — `TerminalChrome` 终端风格,深色背景 + `$` 提示符
-- **Edit / Write** — `DiffViewer` 展示文件变更,词级别高亮
-- **Read** — `CodeViewer` 语法高亮
-- **Glob / Grep** — 搜索模式 + 匹配结果
-- **其他** — JSON 格式展示输入参数
+### 附件
-### 输入系统
+输入框支持粘贴图片、拖放上传和文件选择,可添加图片、普通文件、目录与 PDF。
-`ChatInput` 组合组件:
+- 本地附件可以直接用默认应用打开,也可以选择「打开方式」。
+- 完全退出或恢复会话后,已经提交的附件仍会作为上下文恢复。
+- 文件被移动或删除时,桌面端会报告无法打开,但不会删除历史消息。
+- 外部反馈或诊断报告不会主动包含附件正文。
-- 自适应高度文本框(最高 200px)
-- 附件画廊(粘贴图片 / 拖拽上传 / 文件选择)
-- 项目上下文芯片(Git 仓库 + 分支)
-- `/` 斜杠命令菜单(服务端动态提供,支持搜索)
-- `@` 文件搜索菜单(路径自动补全)
-- 图片画廊(`ImageGalleryModal` / `InlineImageGallery`)
+## 工作区、搜索与 Diff 评审
-### 流式输出
+右侧工作区面向“查看真实文件和审阅改动”,不是聊天消息的装饰视图。
-- Shiki 实时语法高亮
-- 闪烁光标动画
-- `StreamingIndicator` 状态指示
-- 随时可停止(`Cmd/Ctrl + .`)
+### 文件与搜索
----
+- 浏览项目目录、Git 状态和变更文件。
+- 搜索尚未展开或加载的目录。
+- 正式桌面安装包内置对应平台与架构的 ripgrep,不要求用户单独安装 `rg`。
+- 搜索结果可以继续预览或加入聊天上下文。
+- 文件不存在、越过工作目录或结果过大时会返回明确状态。
-## 代码展示
+### Diff 与评论
-
+
-### CodeViewer
+- 变更文件按状态展示,并支持在预览标签之间切换。
+- Diff 保留旧行、新行和语法高亮。
+- 点击一行可添加评论。
+- 按住 `Shift` 可选择同一侧连续多行。
+- 评论会连同文件路径、行号和代码引用回到聊天输入框。
-基于 Shiki(VS Code 同款引擎),支持行号、超 20 行自动折叠、一键复制。
+工具调用被拒绝后,未落盘的 Write/Edit 不应显示成真实变更。交付前仍建议检查 Git Diff。
-### DiffViewer
+### Browser Preview
-基于 react-diff-viewer-continued,单列模式,词级别变更高亮,自动识别文件语言。
+工作台可以从「文件」切换到「浏览器」,在应用内预览网页:
-### MarkdownRenderer
+
-基于 marked + DOMPurify,代码块自动 Shiki 高亮,支持表格、列表、引用、链接。
+- 地址栏支持本地开发地址和普通 HTTPS 页面。
+- 「截图」会把当前页面画面带回会话。
+- 「选择元素」会把页面元素、选择器和截图作为明确上下文交给 Agent。
+- 网页登录态、Cookie 和外部站点权限仍属于敏感数据;公开截图应使用无登录的演示页面。
-### MermaidRenderer
+## 五种权限模式
-支持流程图、时序图、甘特图等,`securityLevel: 'strict'`,渲染失败回退显示源码。
+| 模式 | 自动执行范围 | 风险提示 |
+|------|--------------|----------|
+| 询问权限 | 每次工具执行前确认 | 默认且最适合陌生项目 |
+| 接受编辑 | 自动批准文件编辑,其他操作仍询问 | 仍需审查命令与外部访问 |
+| 自动模式 | 由 Claude 审查工具调用并执行其认为安全的操作 | 不能保证绝对安全,首次启用需确认 |
+| 计划模式 | 研究与规划,不执行修改 | 适合先评审方案 |
+| 跳过全部 | 跳过所有权限检查 | 只用于完全可信、可恢复的隔离环境 |
----
+同一会话中的多个权限请求会分别保留。活跃轮次期间权限模式保持锁定,避免界面选择与实际运行状态不一致。
-## 多标签系统
+## 服务商、官方登录与模型
-- `Cmd/Ctrl + N` 新建标签
-- 拖拽排序、右键菜单(关闭当前/其他/左侧/右侧/全部)
-- 标签状态:绿色脉冲 = 运行中,红色 = 出错
-- 关闭运行中标签需确认
-- 标签状态 localStorage 持久化,重启恢复
+「设置 → 服务商」同时覆盖官方账号和 API Provider。
----
+### 官方登录
-## 权限控制
+- Claude 官方:使用 Claude.ai 账号。
+- ChatGPT 官方:使用 OpenAI OAuth,模型目录包括账号实际开放的 GPT 系列。
+- Grok 官方:使用 xAI OAuth,支持登录、刷新和动态模型目录。
-### 四种模式
+桌面端会展示当前账号与 Provider 实际返回的模型目录,也支持 Claude、OpenAI、Grok 官方登录和第三方自定义模型。名称出现在目录中不等于当前账号一定拥有调用权限。
-| 模式 | 说明 |
-|------|------|
-| 询问权限 (default) | 每个操作需确认 |
-| 自动接受 (acceptEdits) | 自动允许编辑 |
-| 计划模式 (plan) | 只展示计划不执行 |
-| 绕过权限 (bypassPermissions) | 全部自动(需二次确认) |
+### API Provider
-### 权限请求对话框
+- 可选 Anthropic Messages、OpenAI Chat Completions 或 OpenAI Responses。
+- 内置常用预设,也保留可编辑的 Custom。
+- 自定义 Provider 支持主模型和 Haiku、Sonnet、Opus 角色映射。
+- 可按模型配置上下文、thinking 和 effort 能力。
+- Kimi Code 预设使用 K3 Coding API;具体模型、上下文和鉴权仍以当前预设及服务端为准。
-显示工具类型、操作预览(Diff/命令内容)、可展开详情。三个按钮:允许 / 一直允许 / 拒绝。
+系统代理会按请求目标动态解析,模型请求、OAuth 与适配器不必永久沿用应用启动瞬间的代理结果。代理抖动后仍可能需要等待连接恢复;不要把“支持系统代理”理解成所有代理软件和规则都已完成实机覆盖。
----
+## 思考强度
-## Agent Teams
+模型选择器会根据当前模型与 Provider 显示可用的 effort 档位,并尽量在会话恢复后保留选择。
-当 AI 创建 Agent Team 时,桌面端可视化展示协作状态。
+- 档位可能包括 `low`、`medium`、`high`、`xhigh` 和 `max`。
+- 不支持某档的模型可能向下调整或忽略该值。
+- effort 不是固定的 thinking token 数。
+- 某些模型会强制 required 或 adaptive thinking,优先于通用开关。
-- **团队视图** (`AgentTeams` 页面) — 团队列表 + 成员状态(running/idle/completed/error)
-- **成员转录** — 点击成员查看完整对话记录(1.5s 轮询更新)
-- **TeamStatusBar** — 活跃会话底部展示团队名称和成员状态
+## 技能市场
----
+技能市场提供搜索、筛选、详情、来源、安装状态和安全提示。
-## 提供商管理
+
-
+- 安装前阅读技能内容和来源。
+- 第三方技能可能带来新的工具、脚本或外部依赖。
+- 暗色主题和窄窗口下仍可浏览详情。
+- 设置页中的技能列表会与市场安装结果同步。
-在设置 → Providers 标签页管理 AI 提供商。
+不要仅凭名称安装技能,也不要把实验性技能当作桌面端内置承诺。
-### 预设
+## Agent 管理
-点击「添加提供商」从预设快速创建:Anthropic、OpenAI、OpenRouter、Ollama、Azure OpenAI、Google AI 等。预设自动填充 Base URL 和 API 格式。
+「设置 → Agents」展示内置、用户、项目、插件、策略和 CLI 参数等来源,以及同名定义的覆盖关系。
-### 配置项
+
-| 字段 | 说明 |
-|------|------|
-| API Key | 密钥(密码输入) |
-| Base URL | API 地址 |
-| API 格式 | `anthropic` / `openai_chat` / `openai_responses` |
-| 模型映射 | main / haiku / sonnet / opus 对应的实际模型名 |
+### 可编辑范围
-### 连接测试
+- **用户 Agent**:对所有项目可用,写入 `~/.claude/agents/*.md`。
+- **项目 Agent**:只在当前项目可用,写入 `<项目>/.claude/agents/*.md`。
+- 内置、插件、策略和 CLI 参数来源只读。
-两步验证:连接性 + 模型可用性,结果以 Toast 通知展示。
+### 每个 Agent 的设置
----
+- 名称、描述与系统提示词。
+- 继承或指定模型,包括自定义模型 ID。
+- 单独设置 effort。
+- 使用全部工具、禁用工具,或选择内置、MCP 与自定义工具规则。
-## 技能与 Agent
+保存成功后,桌面端会尝试刷新当前会话。刷新失败不会回滚已经写入的 Agent 文件;下一次启动仍会重新读取。模型或 effort 是否完整生效取决于最终解析出的 Provider 能力。
-### 技能浏览
+完整格式、来源优先级和继承规则见[多 Agent 使用指南](../agent/01-usage-guide.md#六自定义-agent)。
-设置 → Skills 标签页:
+## 活动、SubAgent 与 Task
-- 按来源分类(bundled / user / project / plugin)
-- 搜索过滤
-- 详情视图:元数据 + 源代码目录树 + 代码内容
+会话活动面板汇总:
-### Agent 定义
+- 后台任务
+- SubAgent
+- Task
+- Agent Team
+- 相关来源和状态
-设置 → Agents 标签页:
+运行中的 SubAgent 可以立即打开,详情会持续刷新执行记录与工具调用。Task 状态会防止旧响应覆盖较新的完成状态;短暂轮询失败时,列表会保留最近的可信数据。
-管理 Agent 类型定义(agentType、description、model、tools、systemPrompt、color),支持 built-in / plugin / userSettings / projectSettings / localSettings 多种来源。
-
----
+完成、失败和停止等终态会写入会话历史。恢复后仍应以服务端保存的状态为准,而不是只看某次页面刷新。
## 定时任务
-
+
-侧边栏时钟图标进入,顶部统计卡片(总计/活跃/禁用)。
+定时任务可以保存提示词、工作目录、模型和 Cron 计划,并查看运行历史或手动触发。当前新建任务固定使用「所有权限」运行,创建表单不会让用户选择权限模式,因此应使用最小范围工作目录并先审查提示词。
-### 创建任务
+定时任务依赖本机桌面服务运行。关机、应用未启动或网络不可用时,不应把它当作云端永久在线调度器。
-| 字段 | 说明 |
-|------|------|
-| 任务名称 | 描述性名称 |
-| 提示词 | `PromptEditor` 多行编辑 |
-| Cron 表达式 | 标准语法 + `cronDescribe` 人类可读描述 |
-| 星期几 | `DayOfWeekPicker` 可视化多选 |
-| 模型 | 选择 AI 模型 |
-| 权限模式 | 执行时的权限策略 |
+## 桌面宠物
-### 任务管理
+「设置 → 宠物」控制 Electron 透明悬浮窗口。选择搭搭、弧弧、补补或回回后,打开「显示桌面宠物」即可使用;大小可在 96–192px 之间调整,也可以单独关闭动画。
-启用/禁用开关、运行历史(可展开)、手动运行、删除。
+- 内置宠物会响应空闲、工作、等待处理和失败状态。
+- 空闲时会注视鼠标,鼠标移入会跳跃;单击会挥手并唤起主窗口。
+- 可直接拖动宠物改变位置,窗口会在下次启动时恢复;右键选择「关闭宠物」只关闭悬浮窗口,不会停止任务。
+- 开启「显示运行中的任务面板」后,可以查看最近的活跃任务并单击返回对应会话;没有活跃任务时面板自动隐藏。
+- 关闭任务面板后,有活跃任务时会保留数字角标,单击角标可以再次展开。
----
+### 添加自己的宠物
-## IM 适配器
+
-
+点击「添加宠物」,填写宠物 ID、显示名称和描述,再选择创建方式;前两种当前可用:
-设置 → Adapters 标签页,配置 Telegram / 飞书接入。
+- **一张图片制作轻动画**:导入透明 PNG / WebP,由应用在本地添加呼吸、漂浮和任务状态等轻动画。
+- **专业动画图集**:导入已经制作好的 `1536 × 2288` v2 图集,使用完整逐帧动作。
+- **AI 生成完整动画**:当前不可用,需要独立的图片生成服务,不会调用当前聊天模型。
-### 配置
+创建成功后,新宠物会自动被选中。自定义宠物保存在本机 `~/.claude/cc-haha/pets`(设置 `CLAUDE_CONFIG_DIR` 时跟随该目录);当前没有界面内删除按钮,需要通过「打开文件夹」移除对应宠物包后再刷新。
-**Telegram**: Bot Token + 允许的用户 ID
+宠物仅在桌面端运行,不在 H5 中显示,也不能直接批准权限或回答交互问题。窗口置顶、拖动与多显示器行为仍受不同操作系统约束。完整图片规格、安全限制与故障排查见[桌面宠物使用指南](./pets.md)。
-**飞书**: App ID + App Secret + 加密密钥 + 验证 Token + 允许的用户 open_id + 流式卡片开关
+## IM 接入
-### 用户配对
+「设置 → IM 接入」支持五个平台:
-6 位安全码(排除易混淆字符),60 分钟有效,一次性使用。同一用户 5 分钟内最多失败 5 次。
+| 平台 | 连接方式 |
+|------|----------|
+| 微信 | 扫码绑定账号 |
+| 钉钉 | 扫码或凭证接入 Stream |
+| WhatsApp | 通过已连接设备扫码 |
+| Telegram | Bot Token |
+| 飞书 | App ID / App Secret 与长连接 |
-### IM 操作
+平台账号绑定完成后,实际联系人仍需要一次性配对码或允许列表授权。两者都为空时默认拒绝访问。
-| 命令 | 效果 |
-|------|------|
-| 直接发文本 | 与 Claude Code 对话 |
-| `/new [项目]` 或 `新会话` | 开始新会话 |
-| `/projects` 或 `项目列表` | 查看最近项目 |
-| `/stop` 或 `停止` | 停止生成 |
+发布版桌面端会随本地服务启动 adapter sidecar,并在保存、绑定或解绑后应用新配置。详细平台差异见[IM 接入总览](../im/index.md)。
-权限请求在 IM 中以按钮形式展示(Telegram Inline Keyboard / 飞书 Interactive Card)。
+## H5 访问
----
+H5 把当前桌面服务的浏览器界面开放到可信局域网或自己的反向代理。
+
+- H5 页面、REST API 与 WebSocket 可以由同一个服务和端口提供。
+- 设置页会生成带 Token 的二维码与连接地址。
+- 可配置固定端口和手机断连后的空闲保活时间。
+- 手机锁屏或切后台时,正在运行的任务不会因为短暂断连被直接停止;空闲且无人连接后才按保活时间清理。
+
+H5 默认关闭,也不是公开 SaaS。详情见[H5 访问](./06-h5-access.md)。
## Computer Use
-
+Computer Use 在获得系统与会话权限后,可以截图、点击和输入。设置页会显示平台、运行环境、依赖和权限状态。
-设置 → Computer Use 标签页,查看和配置 Computer Use 功能状态。
+操作系统权限、桌面会话状态和应用可访问性都会影响结果。安装包构建成功不等于当前机器已经完成真实桌面控制验证。使用前请阅读[Computer Use 指南](../features/computer-use.md)。
-`ComputerUseSettings` 页面展示:平台信息、Python 环境、venv 状态、依赖安装情况、权限配置。
+## 本地索引与诊断
----
+桌面端使用 SQLite 派生索引加速会话列表、全局搜索、活动统计、定时任务记录和 Trace 读取。
-## 工具检查
+- 原始 JSON / JSONL 会话与设置仍是事实源。
+- 索引构建时,界面可以等待或回退到文件读取。
+- 索引损坏或部分来源降级时,核心数据不会因为重建索引而删除。
+- 「设置 → 诊断」会显示索引状态、数据库/WAL 大小、降级来源和错误代码。
-`ToolInspection` 页面,查看当前会话中可用的工具列表和详情。
+诊断页还可以:
----
+- 查看最近事件和事件 ID。
+- 复制错误摘要或经过尽力脱敏的 Issue 报告。
+- 导出诊断包、打开日志目录或清理日志。
+- 运行 Doctor 检查用户与当前项目状态。
+- 只在明确确认后重置可再生的桌面 UI 状态。
-## 设计系统
+Doctor 和安全 UI 重置不会自动修改聊天历史、模型配置、Skills、MCP、IM 或 OAuth。分享任何诊断材料前仍应人工检查隐私内容。
-### 颜色
+## 更新与平台边界
-暖色调设计语言:
+正式安装包通过 GitHub Releases 检查更新。更新前应保存未提交工作并完全退出旧版本。
-- **品牌色**: `#8F482F`(褐红色)
-- **浅色背景**: `#FAF9F5`(奶油色)
-- **信息色**: `#2D628F` / **成功色**: `#4F6237` / **错误色**: `#BA1A1A`
+- macOS 正式 Release 使用签名和公证产物。
+- Windows 未签名安装包可能显示 SmartScreen;应以当前用户运行,不要主动提升为管理员。
+- Windows 覆盖升级包含旧数据识别与恢复保护,但遇到进程占用、来源歧义或复制失败时会停止,而不是猜测迁移。
+- Linux 提供 AppImage 与 deb,桌面环境、FUSE 和系统库差异仍可能影响运行。
-### 主题
-
-浅色/深色切换,设置 → 通用中配置。
-
-### 动画
-
-| 名称 | 用途 |
-|------|------|
-| `shimmer` | 流式输出闪烁光标 |
-| `pulse-dot` | 运行中会话指示 |
-| `spin` | 加载旋转 |
-| `progress-fill` | 进度条填充 |
-
-### Toast 通知
-
-右下角固定通知,四种类型(success/error/warning/info),自动消失 + 手动关闭。
-
-### 自动更新
-
-`UpdateChecker` 组件:启动后检查 GitHub Releases,有新版本时弹出更新提示,支持自动下载安装重启。
-
----
-
-## 国际化
-
-支持中文 (`zh`) 和英文 (`en`),设置 → 通用中切换,`localStorage` 持久化。
-
-Key 命名空间:`common.*`、`sidebar.*`、`chat.*`、`settings.*`、`status.*`、`titlebar.*` 等。
-
----
-
-## 键盘快捷键
-
-| 快捷键 | 功能 |
-|--------|------|
-| `Cmd/Ctrl + N` | 新建会话 |
-| `Cmd/Ctrl + K` | 聚焦搜索 |
-| `Cmd/Ctrl + .` | 停止生成 |
-| `Escape` | 关闭模态框 |
-| `Enter` | 发送消息 |
-| `Shift + Enter` | 换行 |
-| `/` | 斜杠命令 |
-| `@` | 文件搜索 |
+安装与升级步骤见[安装指南](./04-installation.md),异常排查见[常见问题](./05-FAQ.md)。
diff --git a/docs/desktop/04-installation.md b/docs/desktop/04-installation.md
index c865ff1b..6d8260d1 100644
--- a/docs/desktop/04-installation.md
+++ b/docs/desktop/04-installation.md
@@ -1,98 +1,169 @@
-# 安装指南
+# 安装、升级与 Web UI
-桌面端基于 **Electron**,提供 macOS / Windows / Linux 安装包。`v0.4.3` 起的正式 macOS Release 使用 Developer ID 签名和 notarization;更早版本或临时开发包仍可能需要手动放行。
+Claude Code Haha Desktop 提供 macOS、Windows 和 Linux 安装包。普通用户应优先使用 GitHub Release,而不是从源码启动。
-## 下载
+## 下载正确的安装包
-前往 [GitHub Releases](https://github.com/NanmiCoder/cc-haha/releases) 下载对应平台的安装包:
+前往 [GitHub Releases](https://github.com/NanmiCoder/cc-haha/releases) 下载最新正式版本:
-| 平台 | 文件 |
-|------|------|
-| macOS (Apple Silicon / M 系列) | `Claude-Code-Haha-<版本>-mac-arm64.dmg` |
-| macOS (Intel) | `Claude-Code-Haha-<版本>-mac-x64.dmg` |
-| Windows (x64) | `Claude-Code-Haha-<版本>-win-x64.exe` |
-| Windows (ARM64) | `Claude-Code-Haha-<版本>-win-arm64.exe` |
-| Linux (x64) | `Claude-Code-Haha-<版本>-linux-x86_64.AppImage` 或 `...-linux-amd64.deb` |
-| Linux (ARM64) | `Claude-Code-Haha-<版本>-linux-arm64.AppImage` 或 `...-linux-arm64.deb` |
+| 平台 | 安装文件 |
+|------|----------|
+| macOS Apple Silicon(M 系列) | `Claude-Code-Haha-<版本>-mac-arm64.dmg` |
+| macOS Intel | `Claude-Code-Haha-<版本>-mac-x64.dmg` |
+| Windows x64 | `Claude-Code-Haha-<版本>-win-x64.exe` |
+| Windows ARM64 | `Claude-Code-Haha-<版本>-win-arm64.exe` |
+| Linux x64 | `Claude-Code-Haha-<版本>-linux-x86_64.AppImage` 或 `...-linux-amd64.deb` |
+| Linux ARM64 | `Claude-Code-Haha-<版本>-linux-arm64.AppImage` 或 `...-linux-arm64.deb` |
-> 不确定 Mac 架构?点击左上角 → 关于本机,芯片为「Apple M…」选 arm64,「Intel」选 x64。
+macOS 可从「关于本机」查看芯片;Windows 可在「设置 → 系统 → 系统信息」查看系统类型。不要仅凭设备品牌猜测架构。
-## macOS 安装
+## macOS
-双击 DMG 把应用拖入 `Applications`。`v0.4.3` 起的正式 Release 正常情况下只会出现 macOS 的标准下载来源确认,不需要执行 `xattr`。
+1. 打开 DMG。
+2. 把 Claude Code Haha 拖到「应用程序」。
+3. 从「应用程序」打开。
-如果安装的是旧版或 unsigned 临时包,首次打开可能提示**"已损坏"**或**"无法验证开发者"**,再在终端执行:
+正式 macOS Release 使用 Developer ID 签名和公证,通常只会显示标准的下载来源确认。
-```bash
-xattr -cr /Applications/Claude\ Code\ Haha.app
-```
+只有旧版或明确标注为 unsigned 的临时构建才可能需要手动放行。确认安装包确实来自本仓库后,可以在「系统设置 → 隐私与安全性」选择仍要打开。不要对来源不明的应用运行解除隔离命令。
-也可以在「系统设置 → 隐私与安全性」里点"仍要打开"。
+## Windows
-## Windows 安装
+1. 完全退出正在运行的旧版 Claude Code Haha。
+2. 直接双击 `.exe`,以当前用户安装。
+3. 不要主动选择「以管理员身份运行」。
-双击 `.exe` 安装。首次运行如果 SmartScreen 弹出 **"Windows 已保护你的电脑"**,点击 **「更多信息」** → **「仍要运行」**。
+未签名构建可能触发 SmartScreen。确认文件来自本仓库 Release 后,可选择「更多信息 → 仍要运行」。
-## Linux 安装
+覆盖升级时,安装器会检查相关进程和旧安装目录中的用户数据。它会在可以确认来源与用户身份时恢复历史数据;遇到进程占用、来源歧义或复制失败时会停止安装,而不是猜测迁移。
-AppImage:
+如果安装器提示程序仍在运行:
+
+1. 退出主窗口和托盘中的应用。
+2. 等待几秒,让 sidecar 和 adapter 退出。
+3. 仍失败时,在任务管理器中检查 Claude Code Haha 相关进程。
+4. 重新运行安装器,不要先删除旧数据目录。
+
+若你曾把重要资料放进安装目录,升级前仍建议单独备份。安装器的恢复保护不是通用备份方案。
+
+## Linux
+
+### AppImage
```bash
chmod +x Claude-Code-Haha-<版本>-linux-x86_64.AppImage
./Claude-Code-Haha-<版本>-linux-x86_64.AppImage
```
-> 提示缺少 FUSE:Ubuntu 22.04 及更早 `sudo apt install libfuse2`,24.04+ `sudo apt install libfuse2t64`。
+ARM64 机器请换用 `linux-arm64.AppImage`。
-deb:
+Ubuntu 22.04 及更早版本缺少 FUSE 时可安装 `libfuse2`;Ubuntu 24.04 及更新版本通常使用 `libfuse2t64`。
+
+### deb
```bash
sudo apt install ./Claude-Code-Haha-<版本>-linux-amd64.deb
```
-## Web UI 模式
+ARM64 机器请换用对应的 `linux-arm64.deb`。
-如果桌面端安装遇到问题,可以直接通过浏览器使用 Web UI。在项目根目录下分别启动服务端和前端:
+## 首次启动检查
+
+安装完成后,按这个顺序验证:
+
+1. 打开「设置 → 服务商」,完成官方登录或 API Provider 配置。
+2. 新建会话并选择一个可读写的测试项目。
+3. 使用「询问权限」发送一个只读任务。
+4. 确认回复、工具调用和停止按钮正常。
+5. 再测试文件修改、Diff 和权限拒绝。
+
+不要把 H5、Auto、跳过权限、Computer Use 和 IM 一次全部打开。逐项启用更容易判断故障位于模型、桌面服务还是外部入口。
+
+## 应用内更新
+
+正式安装包会通过 GitHub Releases 检查更新,并下载当前平台的更新资产。
+
+更新前:
+
+- 保存项目中的未提交工作。
+- 等待或停止正在运行的会话、定时任务和 SubAgent。
+- 完全退出应用后再安装。
+
+覆盖安装和应用内更新不应删除正常用户目录中的会话、Provider、Skills、Agents、记忆和自定义宠物。若升级后列表暂时为空,先不要删除配置;打开「设置 → 诊断」检查本地索引状态,必要时只重建派生索引。
+
+Windows 与 Linux 的真实安装行为依赖对应 Release 产物;其他平台上的本地构建通过不等于当前平台已经完成安装验证。
+
+## 从源码运行同源 Web UI
+
+服务端可以直接提供构建后的 Web UI。这样页面、REST API 和 WebSocket 使用同一个 `3456` 端口,最接近 H5 与发布版的同源路径。
```bash
-# 1. 启动服务端(在项目根目录)
-SERVER_PORT=3456 bun run src/server/index.ts
+# 项目根目录
+bun install
-# 2. 启动前端(在 desktop 目录)
+# 构建 Web UI
+cd desktop
+bun install
+bun run build
+cd ..
+
+# 只允许本机访问
+SERVER_HOST=127.0.0.1 SERVER_PORT=3456 bun run src/server/index.ts
+```
+
+打开:
+
+```text
+http://127.0.0.1:3456/
+```
+
+本机回环访问不需要开启 H5,也不需要 H5 Token。这个豁免只适用于真正的本地访问,不会自动扩展到局域网地址或反向代理。
+
+如果服务不是从项目根目录启动,设置 `CLAUDE_H5_DIST_DIR` 指向 `desktop/dist` 的绝对路径。缺少构建产物时 API 仍可能启动,但浏览器根路径会返回 404。
+
+## 前端热更新开发模式
+
+需要修改 React 页面时,可以把前端开发服务器和后端分开运行:
+
+```bash
+# 终端 1:项目根目录
+SERVER_HOST=127.0.0.1 SERVER_PORT=3456 bun run src/server/index.ts
+
+# 终端 2
cd desktop
bun run dev --host 127.0.0.1 --port 2024
```
-启动后浏览器访问 `http://127.0.0.1:2024` 即可。
-
-本机通过 `127.0.0.1` 使用 Web UI 时,不需要开启 H5 访问,也不需要填写 H5 Token。服务端会把真正的本机回环请求视为可信本地访问;这个豁免不会扩展到局域网地址或反向代理。
-
-### 无界面 Linux 服务器
-
-如果服务器没有桌面环境,推荐先使用 SSH 同时转发前端和后端端口:
-
-```bash
-# 在服务器上分别启动后端与前端,仍只监听回环地址
-SERVER_PORT=3456 bun run src/server/index.ts
-cd desktop
-bun run dev --host 127.0.0.1 --port 2024
-
-# 在自己的电脑上执行
-ssh -L 2024:127.0.0.1:2024 -L 3456:127.0.0.1:3456 user@example.com
-```
-
-然后在自己的电脑上打开:
+打开:
```text
http://127.0.0.1:2024/?serverUrl=http%3A%2F%2F127.0.0.1%3A3456
```
-这条路径不把服务暴露到局域网,因此同样不需要 H5 Token。如果确实要从局域网或反向代理访问,请按 [H5 访问](./06-h5-access.md#无桌面界面时开启) 在服务器本机启用 Token、配置允许来源,再让服务监听外部接口。
+这条双端口路径只用于开发热更新。手机访问、反向代理和部署排查应优先使用构建后的 `3456` 同源路径,减少 CORS 与 WebSocket 配置变量。
-## 常见问题
+## 无桌面环境的 Linux 主机
-**Q: 这个版本会自动更新吗?**
+最安全的个人访问方式是 SSH 端口转发:
-`v0.4.3` 起的正式 Release 会通过 GitHub Releases 检查更新,并下载对应平台的更新包。覆盖安装或应用内更新不会删除本地配置和会话数据(`~/.claude`)。
+```bash
+# 服务器:按上一节构建后,只监听回环地址
+SERVER_HOST=127.0.0.1 SERVER_PORT=3456 bun run src/server/index.ts
-正式公开的 macOS Release 需要签名和公证;draft/unsigned 临时包仍可能需要手动放行。Windows 签名不是发布阻塞项,未签名安装包仍可更新,但可能出现 SmartScreen 提示。
+# 自己的电脑
+ssh -L 3456:127.0.0.1:3456 user@example.com
+```
+
+随后在自己的电脑打开 `http://127.0.0.1:3456/`。因为服务没有暴露到局域网,这条路径不需要 H5 Token。
+
+需要让手机或反向代理直接访问时,必须显式启用 H5、生成 Token,并理解公网暴露边界。参见[H5 访问](./06-h5-access.md)。
+
+## 安装后仍无法启动
+
+按以下顺序排查:
+
+1. 确认下载的平台和 CPU 架构正确。
+2. 确认旧版本进程已经退出。
+3. 重新启动应用,而不是反复覆盖安装。
+4. 打开「设置 → 诊断」复制错误摘要或导出诊断包。
+5. 查看[常见问题](./05-FAQ.md),再携带版本、系统、复现步骤和脱敏诊断信息求助。
diff --git a/docs/desktop/05-FAQ.md b/docs/desktop/05-FAQ.md
index 7139dacd..fe8beeba 100644
--- a/docs/desktop/05-FAQ.md
+++ b/docs/desktop/05-FAQ.md
@@ -1,56 +1,271 @@
-# Desktop 常见问题 (FAQ)
+# Desktop 常见问题
-## 使用自定义 Provider(如 Kimi)时,Web UI 提示 "401 API Key invalid" 怎么办?
+先确认你使用的是 [GitHub Releases](https://github.com/NanmiCoder/cc-haha/releases) 中的最新正式版本。下面的排查顺序尽量避免直接修改 `~/.claude`、删除会话或重装应用。
-### 现象
-在 Desktop UI 中配置好自定义 Provider 后,发送聊天消息时返回 401 认证错误:
-```
-Error: Failed to authenticate. API Error: 401
-{"error":{"type":"authentication_error","message":"The API Key appears to be invalid..."}}
-```
+## 服务商与模型
-### 根因
-服务器曾将 Provider 的 API Key 以 `ANTHROPIC_AUTH_TOKEN` 的形式写入 `~/.claude/cc-haha/settings.json`,但 Anthropic SDK 对这两个环境变量的处理不同:
+### 应该选择官方登录还是 API Provider?
-- `ANTHROPIC_AUTH_TOKEN` → 作为 `Authorization: Bearer ` 发送
-- `ANTHROPIC_API_KEY` → 作为 `x-api-key` 发送
+- 已有 Claude.ai、ChatGPT 或 Grok 账号,并希望使用官方账号授权:选择对应的官方登录。
+- 已有 API Key、企业网关、本地模型或第三方兼容服务:选择内置预设或 Custom。
+- 服务只支持 OpenAI 协议:在桌面端选择 OpenAI Chat Completions 或 OpenAI Responses,不必先假设必须部署 LiteLLM。
+- 从源码使用 CLI 环境变量:参见[第三方模型指南](../guide/third-party-models.md),不要把 CLI 配置步骤和桌面 Provider 设置混在一起。
-像 Kimi 这样的第三方 Provider 期望的是 `x-api-key`,错误的请求头导致认证失败。
+### Provider 返回 401 或 “API Key invalid” 怎么办?
-此外,服务器在启动 CLI 子进程时会剥离所有继承的 `ANTHROPIC_*` 环境变量,但却没有将 `settings.json` 中管理的 Provider 配置重新注入,导致 CLI 子进程根本没有拿到任何凭证。
+不要先编辑原始 `settings.json`。在「设置 → 服务商」依次检查:
-### 修复内容
+1. Base URL 是否是 API 根地址,而不是网页地址。
+2. API 格式是否与服务商一致。
+3. 鉴权方式是否正确:
+ - `x-api-key` 常用于 Anthropic 官方 API。
+ - `Authorization: Bearer` 常用于第三方兼容服务。
+ - OpenRouter / Ollama 等场景可能需要避免回退到 Anthropic API Key。
+4. API Key 前后是否包含空格或已失效。
+5. 当前模型 ID 是否确实存在。
+6. 保存后重新测试连接,并新建一条测试会话。
-#### 1. 修复 Provider 同步逻辑 (`src/server/services/providerService.ts`)
-- 将 `ANTHROPIC_API_KEY` 加入 `MANAGED_ENV_KEYS`
-- 修改 `syncToSettings()`,对于直连(非代理)Provider,写入 `ANTHROPIC_API_KEY` 而不是 `ANTHROPIC_AUTH_TOKEN`
+Kimi Code 当前预设使用 K3 Coding API 和 API Key 鉴权。旧文档中手动把某个环境变量替换为另一个变量的修复方法,针对的是历史版本,不应作为当前首选步骤。
-#### 2. 修复子进程环境变量注入 (`src/server/services/conversationService.ts`)
-- 在清理继承的环境变量后,服务器会读取 `~/.claude/cc-haha/settings.json`,并将托管的 Provider 配置重新注入 CLI 子进程
+### 登录成功,为什么看不到文档里提到的模型?
-#### 3. 更新本地配置文件
-将 `~/.claude/cc-haha/settings.json` 中的 `ANTHROPIC_AUTH_TOKEN` 替换为 `ANTHROPIC_API_KEY`:
+模型目录受账号权限、地区、套餐和服务商动态配置影响。桌面端能识别 GPT、Grok 以及 Fable、Opus、Sonnet、Haiku 等模型,不代表每个账号都能调用全部条目。
-**修改前:**
-```json
-{
- "env": {
- "ANTHROPIC_BASE_URL": "https://api.kimi.com/coding/",
- "ANTHROPIC_AUTH_TOKEN": "sk-kimi-...",
- "ANTHROPIC_MODEL": "kimi-for-coding"
- }
-}
-```
+先刷新服务商状态,再重新打开模型选择器。仍缺失时,请以账号官方控制台或 Provider 返回结果为准。
-**修改后:**
-```json
-{
- "env": {
- "ANTHROPIC_BASE_URL": "https://api.kimi.com/coding/",
- "ANTHROPIC_API_KEY": "sk-kimi-...",
- "ANTHROPIC_MODEL": "kimi-for-coding"
- }
-}
-```
+### 为什么 effort 被降低或没有效果?
-修改完成后,重启服务器即可生效。
+`low`、`medium`、`high`、`xhigh`、`max` 不是所有模型都支持。运行时会按模型能力规范化;某些组合会向下调整或忽略,required/adaptive thinking 也可能覆盖通用选择。
+
+逐 Agent effort 同样遵守这个边界。它不是固定的 thinking token 预算。
+
+## 会话、任务与权限
+
+### 对话一直显示运行中,应该直接重启吗?
+
+先按顺序尝试:
+
+1. 等待短暂网络波动恢复。
+2. 点击停止,或按 `Ctrl/Cmd + .`。
+3. 查看活动面板中 Task、后台任务和 SubAgent 的实际状态。
+4. 切换到其他会话再返回,排除单次界面刷新问题。
+5. 完全退出并重新打开应用。
+6. 在「设置 → 诊断」复制错误摘要。
+
+当前版本会持久化完成、失败和停止等终态,但永不完成的网络握手或持续异常的 sidecar 仍可能需要人工重启。不要只根据一个加载动画判断服务端状态。
+
+### 为什么运行中不能切换权限模式?
+
+活跃轮次期间切换会造成界面选择与实际工具权限不一致,因此选择器会锁定。先等待当前轮结束或停止,再切换模式。
+
+### Auto 与“跳过全部”有什么区别?
+
+Auto 会让 Claude 审查工具调用,并执行其认为安全的操作;它会减少审批,但不能保证绝对安全。
+
+“跳过全部”不会进行这种逐项权限检查,风险更高。二者都不适合包含生产密钥、个人资料或不可恢复数据的陌生目录。
+
+### 拒绝 Write/Edit 后为什么要检查 Git Diff?
+
+当前版本会尽量避免把未落盘的拒绝操作显示成变更,但磁盘和 Git 才是最终事实源。遇到中断、外部编辑器并发修改或工具异常时,交付前仍应检查真实 Diff。
+
+## 搜索、工作区与附件
+
+### 会话搜索、`Ctrl/Cmd + F` 和文件搜索有什么区别?
+
+| 入口 | 搜索范围 |
+|------|----------|
+| 侧边栏搜索 | 历史会话 |
+| `Ctrl/Cmd + F` | 当前页面或当前长对话 |
+| `@` 菜单 | 引用工作区文件 |
+| 右侧工作区搜索 | 整个工作目录,包括未展开的目录 |
+
+正式桌面安装包已内置 ripgrep。源码开发环境的依赖问题不等于正式安装包也缺少文件搜索。
+
+### 历史会话突然不在列表里了,数据丢了吗?
+
+不一定。本地 SQLite 只是一份可重建的派生索引,原始会话仍以 JSON / JSONL 文件为准。
+
+1. 打开「设置 → 诊断」。
+2. 查看本地索引是否正在构建或处于降级状态。
+3. 先等待后台构建完成。
+4. 必要时点击「重建本地索引」。
+
+重建索引不会删除原始对话和设置。不要为了恢复列表先删除 `~/.claude`。
+
+### 附件恢复后只看到路径,或无法打开怎么办?
+
+当前版本会恢复已经提交的文件、目录、图片和 PDF 上下文。若打开失败:
+
+- 检查原文件是否移动、重命名或删除。
+- 检查当前系统是否有可打开该格式的应用。
+- Windows 路径与另一个系统上的路径不能自动互换。
+- 使用「打开方式」选择合适的本地应用。
+
+历史消息存在不代表原始文件仍在磁盘。
+
+### Diff 中如何评论连续多行?
+
+先点击起始行,再按住 `Shift` 选择同一侧的结束行。评论会带着文件路径、行号和引用回到聊天输入框。
+
+不要跨旧文件侧和新文件侧连续选择;折叠或切换文件后需要重新确认当前选区。
+
+## 安装与更新
+
+### Windows 安装器说应用仍在运行
+
+1. 退出主窗口。
+2. 检查系统托盘并退出应用。
+3. 等待 sidecar、终端和 IM adapter 结束。
+4. 必要时在任务管理器中结束相关进程。
+5. 重新双击安装器,以当前用户运行。
+
+不要主动选择「以管理员身份运行」,也不要先删除旧安装目录中的数据。
+
+### Windows 显示 SmartScreen
+
+Windows 签名不是当前 Release 的强制发布条件。确认安装包来自本仓库官方 Release 后,未签名版本可选择「更多信息 → 仍要运行」。
+
+来源不明、文件名或校验异常时不要绕过 SmartScreen。
+
+### 覆盖升级会删除 Provider、会话或 Skills 吗?
+
+正常用户目录中的 Provider、会话、Skills、Agents、记忆和自定义宠物应被保留。Windows 安装器也会保护旧安装目录中可确认归属的历史数据。
+
+但安装器恢复不是备份。遇到来源歧义、进程占用或复制失败时,它会中止;重要数据仍应自行备份。
+
+### 更新后会话列表为空
+
+先检查本地索引,不要立即重装:
+
+1. 打开「设置 → 诊断」。
+2. 等待索引构建。
+3. 查看降级来源与错误代码。
+4. 重建派生索引。
+5. 若仍失败,导出诊断包后求助。
+
+## H5 与 IM
+
+### 手机扫码后打不开 H5
+
+检查:
+
+1. 手机和电脑是否在同一可信网络,或反向代理是否可达。
+2. H5 页面显示的“访问主机 / IP”是否仍属于电脑当前网卡。
+3. 二维码中的端口是否等于“当前端口”。
+4. 修改固定端口后是否重启了应用。
+5. 防火墙是否允许当前端口。
+6. Token 是否被重新生成;重新生成后旧二维码立即失效。
+
+源码同源部署默认可使用 `http://<主机>:3456/`。详细步骤见[H5 访问](./06-h5-access.md)。
+
+### 手机锁屏会停止正在运行的任务吗?
+
+短暂断连不会直接停止正在执行的任务;它会在后台完成,重连后可以看到结果。只有任务已经空闲且无人连接时,才会按“断连保活”设置停止对应 CLI,默认值为 30 秒。
+
+这不等于移动网络下永远不会断开。系统休眠、进程退出、代理故障或服务重启仍会终止连接。
+
+### H5 可以直接暴露到公网吗?
+
+不建议裸露公网。H5 不是公开 SaaS,也没有多租户账号体系。拿到有效 Token 的设备可以访问桌面服务暴露的核心能力。
+
+需要远程访问时,应使用自己的 HTTPS 反向代理或 VPN,保留正确的 Host / 代理头,并保护 Token。
+
+### IM 已扫码,为什么联系人仍不能聊天?
+
+扫码只绑定微信、钉钉或 WhatsApp 的平台账号。实际 IM 用户仍需:
+
+- 发送桌面端生成的一次性配对码;或
+- 被加入允许列表。
+
+两者都为空时默认拒绝访问。五个平台的差异见[IM 接入总览](../im/index.md)。
+
+## Agent、技能与宠物
+
+桌面宠物的完整开启与导入步骤见[桌面宠物指南](./pets.md)。
+
+### Agent 已保存,但当前会话没有使用新配置
+
+保存 Agent 文件和刷新运行时是两个步骤。刷新失败时文件仍可能已经保存,桌面端会给出非阻塞警告。
+
+先打开 Agent 详情确认生效来源和覆盖关系,再新建会话或重启应用。项目 Agent 需要当前会话绑定正确的项目目录。
+
+### 为什么内置或插件 Agent 不能编辑?
+
+桌面端只负责用户级和项目级 Agent 文件。内置、插件、策略与 CLI 参数来源保持只读,避免误改不属于桌面端的事实源。
+
+### 技能市场的技能都安全吗?
+
+不是。市场会展示来源、安装状态和安全提示,但第三方技能仍可能包含脚本、依赖或外部访问。安装前应阅读内容并确认来源。
+
+### 单张图片能生成完整宠物动画吗?
+
+不能。透明 PNG / WebP 单图只会在本地添加呼吸、漂浮和状态等轻动画。完整逐帧动作需要符合规格的 v2 动画图集;AI 生成路径需要单独的图片生成服务,不会调用当前聊天模型。
+
+宠物只运行在 Electron 桌面端,不显示在 H5。
+
+### 已经打开任务面板,为什么看不到任务?
+
+任务面板只列出正在运行、等待处理或失败等非空闲会话。所有任务都已完成或处于空闲状态时,面板会自动隐藏;「显示进行中的任务区域」不会让历史任务一直常驻。
+
+先确认「设置 → 宠物 → 显示进行中的任务区域」已开启,再启动一条真实任务。状态会定时刷新;等待几秒仍未出现时,关闭并重新显示宠物。
+
+### 宠物为什么不动?
+
+先确认「设置 → 宠物 → 播放动画」已开启。系统启用“减少动态效果”时,应用也会尊重该设置并降低或关闭动画。
+
+单图自定义宠物只有呼吸、漂浮和任务状态等轻动画;它不会像 v2 图集宠物一样播放完整逐帧动作。鼠标悬停或单击宠物可以触发跳跃、挥手等互动。
+
+### 为什么自定义宠物导入失败?
+
+逐项检查:
+
+- 单图必须是 PNG 或 WebP,单边为 32–4096 像素,文件不超过 8 MB,总像素不超过 16,777,216。
+- v2 动画图集必须恰好为 1536×2288 像素,并符合 8 列 × 11 行的帧布局。
+- 宠物 ID 最多 73 个字符,只能包含小写字母、数字和单个连字符,且不能与已有 ID 重复。
+- 显示名称与描述不能为空。
+
+导入只读取你在系统文件选择器中确认的本地文件。失败后不要直接编辑清单文件,先按提示修正源图片,再重新创建。
+
+### 关闭宠物后如何恢复?
+
+右键宠物并选择关闭,或关闭「设置 → 宠物 → 显示桌面宠物」,都会保存关闭状态。需要恢复时,重新打开这个开关即可;不需要重装应用。
+
+如果宠物曾被拖到另一块屏幕,关闭并重新显示时,保存的位置会被限制在当前可见工作区。
+
+### 如何删除自定义宠物?
+
+当前版本没有“删除”按钮。先切换到一个内置宠物,再点击「设置 → 宠物 → 打开文件夹」,删除目标自定义宠物对应的文件夹,最后返回设置点击「刷新」。
+
+只删除 `${CLAUDE_CONFIG_DIR:-~/.claude}/cc-haha/pets` 下确认属于该宠物的目录,不要删除整个 `~/.claude`。内置宠物随应用提供,不在这里删除。
+
+### 为什么“AI 生成完整动画”不可用?
+
+该入口需要单独配置图片生成服务,当前聊天模型不会被拿来生成宠物图片或动作帧。未配置对应服务时,入口会明确显示不可用。
+
+现在可以使用推荐的单图轻动画,或导入已经制作好的 v2 动画图集。
+
+### 能在宠物窗口里批准权限或直接回复吗?
+
+不能。宠物窗口用于展示任务状态、切回主窗口和定位对应会话,不提供消息输入框,也不会批准文件、命令或 Computer Use 权限。
+
+看到“等待你处理”时,点击对应任务返回主窗口,再在完整会话界面中阅读上下文、批准或拒绝权限并继续回复。宠物不会绕过当前权限模式。
+
+## 诊断与求助
+
+### 应该提供哪些信息?
+
+在「设置 → 诊断」中:
+
+1. 刷新并记录最近事件 ID。
+2. 复制错误摘要或 Issue 报告。
+3. 需要时导出诊断包。
+4. 补充应用版本、操作系统、CPU 架构、服务商类型和最短复现步骤。
+5. 明确问题是安装、界面、模型请求、H5、IM 还是某个 Agent 工作流。
+
+诊断报告会尽力省略聊天、文件正文、完整环境变量和 API Key,但分享前仍需人工检查是否含私密路径、账号信息或其他元数据。
+
+### Doctor 会修改什么?
+
+Doctor 默认检查受保护的用户与项目状态。只有你明确确认“重置安全 UI 状态”时,它才会移除允许重新生成的桌面界面键。
+
+聊天历史、模型配置、Skills、MCP、IM 和 OAuth 不属于自动修复范围。不要根据旧教程手动删除这些目录。
diff --git a/docs/desktop/06-h5-access.md b/docs/desktop/06-h5-access.md
index 44f5b81e..75617e99 100644
--- a/docs/desktop/06-h5-access.md
+++ b/docs/desktop/06-h5-access.md
@@ -1,139 +1,225 @@
# H5 访问
-H5 访问是一个可选的个人/团队使用入口。开启后,你可以把桌面端使用的本地服务通过局域网或自己的反向代理暴露出去,然后在手机浏览器里打开同一套聊天 UI。
+H5 是桌面服务的可选浏览器入口。启用后,手机可以通过局域网或你自己的反向代理访问同一套会话、消息、附件和权限流程。
-它不是公开 SaaS 登录系统。任何拿到 H5 Token 的人,都可以访问当前桌面端服务暴露的核心聊天能力,所以只应该在你可控的网络、域名和团队范围内使用。
+它不是公开 SaaS,也不是多租户账号系统。任何拿到有效 H5 Token 的设备都可以访问桌面服务暴露的核心能力,只应在可信网络、VPN 或自己维护的 HTTPS 代理中使用。
-## 开启方式
+## 推荐结构:一个地址、一个端口
-1. 在桌面端打开 `设置 -> General -> H5 访问`。
-2. 打开 `启用 H5 访问`。
-3. 点击生成或重新生成 Token。
-4. 复制生成的 Token。之后仍可在本机设置页或本机可信接口中取回;需要撤销旧 Token 时再重新生成。
-5. 按你的访问方式填写允许来源:
- - 局域网访问:例如 `http://192.168.1.20:5173`
- - 反向代理访问:例如 `https://cc.example.com`
-6. 如果你使用固定域名,可以填写公开访问地址,设置页会据此生成 H5 URL。
+当前服务端可以同时提供:
-H5 设置保存到 `~/.claude/cc-haha/settings.json`。为让已配对设备在重启后继续使用,当前版本会持久化完整 Token;只有本机可信的控制接口可以读回它。请像保护 API Key 一样保护这个文件和 Token。
+- H5 静态页面
+- REST API
+- Provider 代理
+- WebSocket / SDK 连接
-### 无桌面界面时开启
+因此推荐让浏览器只访问一个来源:
-无界面的 Linux 服务器也可以通过只允许本机调用的控制接口启用 H5。源码运行时先构建 Web UI,再从项目根目录启动服务端;服务端会自动提供 `desktop/dist` 中的页面。需要局域网或反向代理访问时,让它监听外部接口:
-
-```bash
-cd desktop
-bun run build
-cd ..
-SERVER_HOST=0.0.0.0 SERVER_PORT=3456 bun run src/server/index.ts
+```text
+http://192.168.1.20:3456/
```
-如果不是从项目根目录启动服务端,请额外设置 `CLAUDE_H5_DIST_DIR` 为 `desktop/dist` 的绝对路径。没有构建产物时,控制接口仍可生成 Token,但浏览器入口会返回 404。
+同源路径不需要再维护独立的前端地址,也能减少 Token 和 WebSocket 代理配置错误。源码运行时默认服务端口是 `3456`;发布版应以设置页显示的“当前端口”为准。
-再从服务器本机的另一个终端生成 Token。响应 JSON 的 `token` 字段就是浏览器需要填写的完整 Token:
+安全策略不会因为前后端同源就自动信任一个远程浏览器。该来源仍需出现在 H5 允许来源中;桌面端正常开启流程会沿用已保存的 H5 配置,源码或手工部署应按下文显式填写 `allowedOrigins`。
+
+## 桌面端开启步骤
+
+1. 打开「设置 → H5 访问」。
+2. 打开「启用 H5 访问」,阅读风险提示后确认。
+3. 查看自动生成的 H5 链接和“当前端口”。
+4. 如果保存的局域网 IP 已失效,使用设置页建议的新 IP,或手动填写电脑当前的局域网 IP。
+5. 需要稳定书签或反向代理时,在“固定端口”填写 `3456` 或其他浏览器允许的端口并保存。
+6. 修改固定端口后重启桌面端,确认“当前端口”已经变成新值。
+7. 用手机扫描二维码,或复制“扫码链接”到可信设备。
+
+二维码链接会携带 Server URL 和 H5 Token。浏览器连接成功后会在本机保存连接信息,后续打开可继续使用。
+
+### 访问主机 / IP
+
+普通局域网访问只填写电脑真实的私有 IP,例如:
+
+```text
+192.168.1.20
+```
+
+设置页会结合当前服务端口生成完整地址。如果电脑切换 Wi-Fi、网线或 VPN,原 IP 可能不再属于当前网卡;桌面端会提示切换到可用地址。
+
+使用反向代理时可以直接填写完整 URL:
+
+```text
+https://cc.example.com
+```
+
+桌面端无法替你验证外部域名、隧道或证书是否持续可用。
+
+### 固定端口
+
+不设置固定端口时,桌面端会使用当前运行端口,并尽量复用已有端口。下列场景建议固定:
+
+- 手机书签需要长期不变。
+- 防火墙只允许指定端口。
+- Nginx、Caddy 或隧道固定转发一个上游端口。
+
+端口必须是 `1024–65535` 之间且浏览器允许访问的整数。修改后要重启应用才会生效。
+
+### 断连保活
+
+手机锁屏、切后台或短暂换网时:
+
+- 正在执行的任务不会因为 H5 断连被直接停止。
+- 任务可以在后台完成,重连后查看结果。
+- 只有任务已经空闲且无人连接时,才会在保活时间到期后停止对应 CLI。
+
+默认空闲保活时间为 30 秒,可配置范围是 5 秒到 24 小时。调大数值会让无人观察的空闲进程保留更久,不应把它当作云端永久任务。
+
+## Token 生命周期
+
+- Token 会保存在本机设置中,重启桌面端后仍可查看和生成二维码。
+- **关闭 H5** 会立即拒绝远程访问,但会保留 Token,方便以后重新启用已配对设备。
+- **重新生成 Token** 会轮换凭证,旧二维码和旧 Token 立即失效。
+- 不要把带 Token 的扫码链接贴到群聊、公开 Issue、日志截图或公共网页。
+
+怀疑链接泄露时,应重新生成 Token,而不只是关闭再打开 H5。
+
+## 手机端可用范围
+
+H5 优先保证对话主流程:
+
+- 会话列表与项目切换。
+- 消息发送、停止和流式回复。
+- 图片和文件附件。
+- 权限按钮与 AI 提问。
+- 消息复制、分叉等操作。
+- `@` 文件引用与适合触屏的输入布局。
+- 手机安全区、软键盘和横竖屏下的基础布局。
+
+桌面工作区、底部终端、原生文件“打开方式”、Computer Use 系统授权和桌面宠物不属于 H5 的完整等价体验。
+
+## 从源码运行同源 H5
+
+先构建 Web UI,再让服务端在 `3456` 同时提供页面与 API:
+
+```bash
+# 项目根目录
+bun install
+cd desktop
+bun install
+bun run build
+cd ..
+
+SERVER_HOST=0.0.0.0 \
+SERVER_PORT=3456 \
+CLAUDE_H5_AUTO_PUBLIC_URL=1 \
+bun run src/server/index.ts
+```
+
+如果不是从项目根目录启动,设置:
+
+```bash
+CLAUDE_H5_DIST_DIR=/absolute/path/to/claude-code-haha/desktop/dist
+```
+
+没有 `desktop/dist` 时,控制 API 可以启动,但浏览器根路径会返回 404。
+
+### 在服务器本机启用
+
+H5 控制接口只接受本机可信请求。另开一个服务器终端:
```bash
curl -sS -X POST http://127.0.0.1:3456/api/h5-access/enable
```
-配置实际前端来源;`allowedOrigins` 必须写浏览器地址栏中前端页面的来源,不能使用通配符。使用反向代理时还可以设置公开访问地址:
+响应中的 `token` 是完整 Token。为局域网地址设置同源入口和固定端口:
+
+```bash
+curl -sS -X PUT http://127.0.0.1:3456/api/h5-access \
+ -H 'Content-Type: application/json' \
+ --data '{
+ "allowedOrigins": ["http://192.168.1.20:3456"],
+ "publicBaseUrl": "http://192.168.1.20:3456",
+ "fixedPort": 3456,
+ "disconnectGraceSeconds": 30
+ }'
+```
+
+把示例 IP 替换成服务器真实的局域网 IP。不要填写另一台机器或已经失效的网卡地址。
+
+随后打开:
+
+```text
+http://192.168.1.20:3456/
+```
+
+更安全的个人方案是保持 `SERVER_HOST=127.0.0.1`,通过 SSH、VPN 或受控隧道访问;单纯 SSH 转发到本机时不需要启用 H5。
+
+## 反向代理
+
+推荐用一个 HTTPS 域名把静态页面和所有后端路径转到同一个上游:
+
+```text
+https://cc.example.com -> http://127.0.0.1:3456
+```
+
+至少确保下列路径进入同一个桌面服务:
+
+- `/`
+- `/api/*`
+- `/proxy/*`
+- `/ws/*`
+
+WebSocket 路径必须支持协议升级。
+
+### Nginx 必要请求头
+
+反向代理连接本机服务时,后端看到的来源地址通常是 `127.0.0.1`。必须保留公开 Host,或传递标准代理头,防止远程请求被误判为本地直连:
+
+```nginx
+proxy_set_header Host $host;
+proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+proxy_set_header X-Forwarded-Proto $scheme;
+proxy_set_header Upgrade $http_upgrade;
+proxy_set_header Connection "upgrade";
+```
+
+Caddy 默认会保留原始 Host 并补充 `X-Forwarded-*`。如果自定义 `header_up`,不要同时把 Host 改成 `127.0.0.1` 并删除全部代理头。
+
+反向代理域名也要加入 H5 允许来源。可从服务器本机调用控制接口:
```bash
curl -sS -X PUT http://127.0.0.1:3456/api/h5-access \
-H 'Content-Type: application/json' \
--data '{
"allowedOrigins": ["https://cc.example.com"],
- "publicBaseUrl": "https://cc-api.example.com"
+ "publicBaseUrl": "https://cc.example.com"
}'
```
-稍后需要重新取回完整 Token 或核对配置时,只能在服务器本机调用:
+如果必须把页面与 API 放在不同来源,需要把页面来源加入 `allowedOrigins`,并另外维护 Server URL。不要使用通配来源。除非现有基础设施强制要求,否则优先使用同源方案。
-```bash
-curl -sS http://127.0.0.1:3456/api/h5-access
-```
-
-如果只是通过 SSH 端口转发在自己的电脑上访问,请保持服务监听 `127.0.0.1`,无需开启 H5;完整命令见 [安装指南](./04-installation.md#无界面-linux-服务器)。
-
-## 访问地址
-
-浏览器入口使用前端地址加 `serverUrl` 参数:
-
-```text
-https://cc.example.com/?serverUrl=https%3A%2F%2Fcc-api.example.com
-```
-
-如果前端和后端由同一个反向代理路径提供,可以把 `serverUrl` 指向同一个公开服务根地址。
-
-首次打开时,H5 页面会要求输入:
-
-| 字段 | 说明 |
-|------|------|
-| Server URL | 桌面端服务或反向代理后的后端地址 |
-| H5 Token | 设置页生成的 Token |
-
-验证成功后,浏览器会把 Server URL 和 Token 保存在当前浏览器的 `localStorage`,之后自动带上 REST `Authorization` 和 WebSocket `token` 参数。
-
-## 推荐部署
-
-### 局域网
-
-适合个人手机测试:
-
-1. 让后端监听局域网地址或由你自己的代理转发到本机端口。
-2. 在设置页把手机浏览器打开的前端来源加入允许来源。
-3. 手机和电脑连接同一个可信网络。
-4. 手机打开 H5 URL,输入 Token。
-
-### 反向代理
-
-适合小团队访问:
-
-1. 使用 HTTPS 域名代理前端静态资源和后端 API。
-2. 把前端来源加入 H5 允许来源,不要使用通配来源。
-3. 确保 `/api/*`、`/proxy/*` 和 `/ws/*` 都转发到桌面端服务。
-4. WebSocket 代理需要支持协议升级。
-5. 只把域名分享给可信成员,并单独发送 Token。
-
-反向代理连接本机服务时,后端看到的来源地址通常也是 `127.0.0.1`。为避免把远程请求误认成本地直连,请保留公开 `Host`,或至少传递一个标准代理头:`Forwarded`、`X-Forwarded-For`、`X-Forwarded-Host`、`X-Forwarded-Proto`、`X-Real-IP`、`Via`。
-
-Nginx 可以这样配置:
-
-```nginx
-proxy_set_header Host $host;
-proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-proxy_set_header X-Forwarded-Proto $scheme;
-```
-
-Caddy 的 `reverse_proxy` 默认会保留原始 Host,并补充 `X-Forwarded-*` 头。如果你自定义了 `header_up`,请确保仍保留公开 Host 或上述任一代理头。不要同时把 Host 改成上游的 `127.0.0.1` 并删除全部代理头;缺少这些信息时,服务无法安全区分远程反代请求和本地直连请求。
-
-## 手机体验范围
-
-H5 的第一版优先保证聊天主流程:
-
-- 会话列表通过抽屉打开,默认收起。
-- 聊天区是主界面。
-- 输入框、发送/停止、附件和权限按钮使用手机可点的尺寸。
-- `@` 文件菜单会适配手机宽度。
-- Workspace 面板和底部 Terminal 面板在手机宽度下不作为主流程显示。
-
-Electron 桌面端仍保留原来的布局和交互。
-
-## 安全注意
+## 安全检查
- H5 默认关闭。
-- 完整 Token 会持久化,并可从本机可信的设置界面或控制接口读回。
-- 远程 API、代理接口和 WebSocket 都需要 H5 Token。
-- CORS 只允许设置页配置的来源。
-- 禁用 H5 或重新生成 Token 后,旧 Token 会失效。
-- 不要把 H5 暴露到公共网络后再把 Token 发到公开渠道。
+- 远程 REST、Provider 代理与 WebSocket 都需要有效 Token。
+- 本机回环豁免不会扩展到真实局域网或带代理痕迹的请求。
+- 远程预览与桌面可信 session 分离,不会自动获得摄像头、麦克风或通知权限。
+- 不使用时关闭 H5。
+- 泄露时重新生成 Token。
+- 公网使用 HTTPS、VPN、访问控制与防火墙,不要只依赖一个长期 Token。
+- 不要把桌面服务直接暴露给不受信任的团队或互联网扫描。
## 排查
-| 现象 | 处理 |
+| 现象 | 检查 |
|------|------|
-| 页面提示需要 Token | 在设置页复制最新 Token,重新输入 |
-| Token 无效 | 重新生成 Token,并更新手机浏览器里保存的 Token |
-| 无法连接 Server URL | 检查后端地址、端口、防火墙和反向代理 |
-| 浏览器被 CORS 拦截 | 把当前前端来源加入 H5 允许来源 |
-| WebSocket 连接失败 | 检查反向代理是否转发 `/ws/*` 并启用 WebSocket upgrade |
+| 二维码打不开 | 核对访问主机是否属于当前网卡、手机与电脑是否同网 |
+| 地址端口不一致 | 以“当前端口”为准;固定端口修改后重启 |
+| 页面 404 | 构建 `desktop/dist`,或检查 `CLAUDE_H5_DIST_DIR` |
+| Token 无效 | 确认 H5 已启用;重新生成后更新旧书签 |
+| 浏览器提示 CORS | 把浏览器地址栏中的来源精确加入 `allowedOrigins`,不要使用 `*` |
+| REST 可用但消息不流式 | 检查 `/ws/*` 的 WebSocket upgrade |
+| 反代后被当成本机或被拒绝 | 保留公开 Host 和标准代理头 |
+| 切换网络后旧 IP 失效 | 在设置页应用建议 IP 或更新反向代理地址 |
+| 手机切后台后连接消失 | 重新打开页面;运行中任务应继续,空闲任务受保活时间限制 |
+
+仍无法定位时,到「设置 → 诊断」复制错误摘要与事件 ID。分享诊断材料前检查其中是否含私有域名、IP、用户名或路径。
diff --git a/docs/desktop/index.md b/docs/desktop/index.md
index 47858664..af29e2b1 100644
--- a/docs/desktop/index.md
+++ b/docs/desktop/index.md
@@ -1,84 +1,77 @@
# 桌面端文档
-> 图形化的 AI Code Editor,支持多会话、多标签、IM 接入的完整桌面体验。
+Claude Code Haha Desktop 是项目的主要使用入口。它把会话、项目、代码审阅、权限审批、模型服务商、Agent、技能、Computer Use、H5 与 IM 接入放在同一个 macOS、Windows 和 Linux 工作台中。
-
+
----
+## 第一次使用
-## 文档目录
+按下面的顺序可以最快完成一轮真实任务:
-### [快速上手](./01-quick-start.md)
+1. 从 [GitHub Releases](https://github.com/NanmiCoder/cc-haha/releases) 下载适合系统与 CPU 架构的安装包,参照[安装指南](./04-installation.md)完成安装。
+2. 首次打开后进入「设置 → 服务商」,选择 Claude、ChatGPT 或 Grok 官方登录,也可以使用内置预设或 Custom 配置 API。
+3. 新建会话,选择一个工作目录、模型和权限模式。
+4. 先发送一个范围清楚的小任务,观察回复、工具调用、权限请求和右侧工作区。
+5. 熟悉主流程后,再启用 Auto、H5、IM、Computer Use 等扩大权限或访问范围的能力。
-面向用户的桌面端使用指南:界面布局、对话操作、多标签、权限控制、项目管理、模型配置、IM 适配器、定时任务。
+完整操作见[快速上手](./01-quick-start.md)。
-### [架构设计](./02-architecture.md)
+## 你可以完成什么
-面向开发者的技术架构:三层架构(Electron Host → Server → CLI)、WebSocket 协议、HTTP API、状态管理、协议代理、适配器桥接、目录结构。
+### 对话与代码工作台
-### [功能详解](./03-features.md)
+- 同时管理多个项目和会话,并在标签页之间切换。
+- 上传图片、文件、目录和 PDF;恢复会话后继续保留附件上下文。
+- 在右侧工作区搜索文件、查看变更与 Diff,并把单行或连续行评论带回聊天。
+- 使用 `Ctrl/Cmd + F` 在长对话中查找,或通过对话导航快速跳转。
-深入每个功能模块:聊天引擎、代码展示、工具调用、Agent Teams、提供商管理、技能/Agent、定时任务、IM 适配器、设计系统。
+### 模型与执行控制
-### [安装指南](./04-installation.md)
+- 使用 Claude、ChatGPT、Grok 官方登录,或配置 Anthropic / OpenAI 兼容 API。
+- 为当前会话选择模型、思考强度和五种权限模式。
+- 在活动面板查看后台任务、Task、SubAgent 和 Agent Team 的运行状态。
+- 浏览技能市场,管理用户级与项目级 Agent,并分别配置模型、思考强度和工具。
-下载安装、macOS/Windows 常见问题、Web UI 模式。
+### 桌面与远程入口
-### [H5 访问](./06-h5-access.md)
+- 在授权后使用 Computer Use 控制桌面应用。
+- 使用互动桌面宠物观察任务状态并返回对应会话。
+- 通过 H5 在可信局域网或自己的反向代理中访问当前桌面服务。
+- 通过微信、钉钉、WhatsApp、Telegram 或飞书连接同一套本地会话。
-面向个人和团队的可选浏览器访问:开启 H5、生成 Token、配置允许来源、通过局域网或反向代理在手机上访问聊天界面。
+## 用户文档
-### [Tauri 迁移 Electron 调研索引](./07-electron-migration-research.md)
+| 文档 | 适合谁 | 内容 |
+|------|--------|------|
+| [快速上手](./01-quick-start.md) | 第一次打开桌面端的用户 | 从服务商配置到首轮任务,以及工作区、权限、Agent、宠物和快捷键 |
+| [功能详解](./03-features.md) | 想系统了解能力边界的用户 | 对话、Diff、搜索、活动、技能、Agent、诊断、H5 与 IM |
+| [安装指南](./04-installation.md) | 安装、升级或从源码运行的用户 | 各平台安装包、覆盖升级、同源 Web UI 与更新 |
+| [常见问题](./05-FAQ.md) | 遇到连接、搜索、升级或恢复问题的用户 | 面向当前版本的排查顺序与求助材料 |
+| [H5 访问](./06-h5-access.md) | 需要手机或浏览器远程入口的用户 | 端口、二维码、Token、同源部署与安全边界 |
+| [桌面宠物](./pets.md) | 想让桌面伙伴跟随任务状态的用户 | 开启宠物、切换角色、调整外观、导入自定义宠物与理解交互边界 |
-桌面端从 Tauri 2 迁移到 Electron 的系统能力盘点、React 复用边界、目标架构、安全要求和迁移路径。
+相关专题:
-### [Electron 迁移任务清单](./08-electron-migration-tasks.md)
+- [多 Agent 使用指南](../agent/01-usage-guide.md)
+- [IM 接入总览](../im/index.md)
+- [Computer Use](../features/computer-use.md)
+- [第三方模型](../guide/third-party-models.md)
-逐阶段执行清单:host adapter、Electron main/preload、系统能力、build/release、跨平台 smoke 和 Computer Use 验收。
+## 使用边界
-### [Electron 迁移验证清单](./09-electron-migration-validation-checklist.md)
+- Auto 与跳过权限都会扩大自动执行范围。Auto 会审查工具调用,但不能保证绝对安全;跳过权限只适合你完全信任的工作目录和任务。
+- H5 不是公开 SaaS 或多租户系统。持有有效 Token 的设备可以访问桌面服务暴露的核心能力。
+- 本地索引是可重建的派生数据,原始会话与设置仍是事实源。
+- 模型、上下文长度和思考强度的实际可用性取决于账号权限及服务商能力。
+- 桌面宠物运行在 Electron 桌面端,不会显示在 H5 中;单图导入只生成本地轻动画,不是 AI 逐帧动画。
-迁移收口验证:自动化证据、Review 收口项、macOS Computer Use smoke、Windows/Linux 实机验收边界。
+## 维护者资料
-### [Electron 发布与自动更新](./10-release-auto-update.md)
+下面的页面记录架构、迁移和发布流程,不是普通用户的操作指南:
-维护者发版 runbook:Developer ID 签名、GitHub Release metadata、`v0.4.10 -> v0.4.11` 自动更新验证路径。
-
-### [Electron 迁移交互式验收清单](/desktop/electron-migration-qa-checklist.html)
-
-本地浏览器可直接打开的验收 checklist,支持勾选进度、记录问题、导出 Markdown。
-
----
-
-## 快速开始
-
-### 用户
-
-1. 阅读 [安装指南](./04-installation.md) 下载安装
-2. 阅读 [快速上手](./01-quick-start.md) 了解界面和操作
-3. 配置 AI 模型提供商,开始对话
-
-### 开发者
-
-1. 阅读 [架构设计](./02-architecture.md) 理解三层架构
-2. 关键源码位置:
- - `desktop/src/` — React 前端
- - `desktop/electron/` — Electron main/preload/系统能力 host
- - `desktop/src-tauri/` — 历史资源目录,当前仅作为 sidecar、图标和 preview agent 的 Electron 打包输入
- - `desktop/sidecars/` — Sidecar 入口
- - `src/server/` — Express API 服务端
- - `adapters/` — IM 适配器
-
----
-
-## 核心概念
-
-| 概念 | 说明 |
-|------|------|
-| **Electron Host** | 跨平台桌面壳层,管理窗口、系统能力、Sidecar 进程和更新 |
-| **Sidecar** | 随主进程启动的后台服务,运行 API 服务器 |
-| **Session** | 一次对话会话,绑定工作目录,通过 WebSocket 通信 |
-| **Tab** | 标签页,对应一个 Session 或特殊页面 |
-| **Provider** | AI 模型提供商,支持 Anthropic/OpenAI 兼容接口 |
-| **Adapter** | IM 适配器,Telegram/飞书接入 Claude Code |
-| **Store** | Zustand 状态容器,按领域拆分管理 |
+- [架构设计](./02-architecture.md)
+- [Electron 迁移调研](./07-electron-migration-research.md)
+- [Electron 迁移任务清单](./08-electron-migration-tasks.md)
+- [Electron 迁移验证清单](./09-electron-migration-validation-checklist.md)
+- [Electron 发布与自动更新](./10-release-auto-update.md)
+- [迁移交互式验收清单](/desktop/electron-migration-qa-checklist.html)
diff --git a/docs/desktop/pets.md b/docs/desktop/pets.md
new file mode 100644
index 00000000..465b1d9a
--- /dev/null
+++ b/docs/desktop/pets.md
@@ -0,0 +1,133 @@
+# 桌面宠物
+
+桌面宠物是 Claude Code Haha Desktop 的透明悬浮窗口。它会用动作反映本机任务状态,并提供一个轻量的活跃任务入口。宠物不会替你审批权限,也不能在悬浮窗口里直接输入追问。
+
+::: info 适用范围
+宠物只在 Electron 桌面端运行,不会显示在 H5 页面中。它不会扩大当前会话、模型或工具的权限。
+:::
+
+## 开启宠物
+
+1. 打开 Claude Code Haha Desktop,点击左下角的设置按钮。
+2. 在设置侧边栏选择「宠物」。
+3. 从「内置宠物」中选择一个角色。
+4. 打开「显示桌面宠物」。
+5. 按需要调整宠物大小、动画和任务面板。
+
+
+
+应用内置四个完整动画角色:
+
+| 角色 | 特点 |
+|------|------|
+| 搭搭 Dada | 沉稳的协作机器人 |
+| 弧弧 Huhu | 拿着铅笔和计划本的路线机器人 |
+| 补补 Bubu | 举着修补扳手的修复机器人 |
+| 回回 Huihui | 抱着构建齿轮的构建机器人 |
+
+选择新角色后,已经打开的宠物窗口会同步更新。关闭「播放动画」只会停止动作,不会关闭宠物或任务入口。
+
+## 和宠物互动
+
+宠物窗口支持这些直接操作:
+
+- **悬停**:宠物空闲时会跳跃,并跟随指针改变视线。
+- **单击宠物**:唤起 Claude Code Haha 主窗口,同时播放挥手动作。
+- **拖动宠物**:按住宠物本体移动到桌面的其他位置;重新打开应用后会尽量恢复上次位置。
+- **右键宠物**:在系统菜单中选择「关闭宠物」。
+- **重新打开**:回到「设置 → 宠物」,重新打开「显示桌面宠物」。
+
+单击宠物只会唤起主窗口,不会自动进入某条会话。要返回具体任务,请使用宠物旁边的任务面板。
+
+## 查看活跃任务
+
+开启「显示进行中的任务区域」后,有活跃任务时,宠物旁边会出现任务面板。关闭这个选项时,活跃任务会收起为数字徽标;点击徽标即可展开。
+
+面板会反映当前仍需要关注的本机会话,例如:
+
+- **工作中**:会话、后台任务或 Agent 正在运行。
+- **等待你处理**:会话正在等待权限审批或其他用户操作。
+- **需要关注**:会话最近一次运行失败或出现错误。
+
+点击任务行会唤起主窗口并返回对应会话。权限仍要在主窗口的会话界面中检查和批准,宠物本身不能批准或拒绝权限。
+
+任务完成并回到空闲状态后,会从活跃任务面板中消失;这不代表会话或历史记录被删除。面板只显示最近的活跃会话,不是完整的会话列表。
+
+## 添加自定义宠物
+
+在「你的宠物」右侧点击「添加宠物」,可以选择两条本地导入路径。
+
+
+
+无论选择哪种方式,都需要填写:
+
+- **宠物 ID**:最多 73 个字符,只使用小写字母、数字和单个连字符,例如 `docs-bot`。
+- **显示名称**:在宠物列表中显示的名称。
+- **宠物描述**:帮助你区分不同角色和用途。
+
+创建成功后,新角色会出现在「你的宠物」中并被自动选中。
+
+
+
+### 用一张图片制作轻动画
+
+这是普通用户最容易使用的方式。选择一张透明背景的静态 PNG 或 WebP,应用会在本地添加呼吸、漂浮和任务状态等轻动画。
+
+图片需要满足:
+
+- 文件格式为静态 PNG 或 WebP,不支持 APNG 或动态 WebP。
+- 宽和高都在 `32–4096` 像素之间。
+- 总像素数不超过 `16,777,216`。
+- 文件大小不超过 `8 MB`。
+
+单图轻动画不是 AI 生成的逐帧动画,也不会调用当前聊天模型。
+
+### 导入 v2 动画图集
+
+已经制作好完整动作帧时,可以导入 v2 动画图集。图片必须满足:
+
+| 项目 | 规格 |
+|------|------|
+| 格式 | 静态、透明背景的 PNG 或 WebP |
+| 总尺寸 | 精确为 `1536 × 2288` |
+| 网格 | `8` 列 × `11` 行,无单元格间距 |
+| 单格尺寸 | `192 × 208` |
+
+动作行的顺序依次是:待机、向右跑、向左跑、挥手、跳跃、失败、等待、工作、复核,以及最后两行的 16 个视线方向。
+
+「复核」行属于图集格式的预留动作,不代表当前运行时会自动显示一个独立的复核状态。
+
+尺寸、格式或图像内容不符合要求时,应用会拒绝创建并显示对应错误,不会把无效图片加入宠物列表。
+
+### AI 生成为什么不可选
+
+「AI 生成完整动画」目前只是预留入口,需要单独配置图片生成服务,当前版本还不能在这里直接使用。它不会自动调用当前会话选择的 Claude、ChatGPT、Grok 或其他聊天模型。
+
+## 存储与删除
+
+自定义宠物包默认保存在:
+
+```text
+${CLAUDE_CONFIG_DIR:-~/.claude}/cc-haha/pets
+```
+
+可以在宠物设置页底部点击「打开文件夹」查看实际目录。每只自定义宠物使用独立子目录,其中包含 `pet.json` 和对应图片。
+
+当前设置页没有自定义宠物删除按钮。需要删除时:
+
+1. 先在设置页选择一个内置宠物,避免继续使用准备删除的角色。
+2. 点击「打开文件夹」。
+3. 只删除目标宠物对应的子目录,不要删除整个 `~/.claude`、`cc-haha` 或 `pets` 目录。
+4. 返回设置页点击「刷新」。
+
+手工修改 `pet.json`、替换图片或加入符号链接可能使宠物包校验失败。无效包会被跳过,并在设置页显示提示。
+
+## 使用边界
+
+- 宠物读取的是当前桌面服务中的任务状态,不是云端常驻监控。
+- 退出 Claude Code Haha 或本机休眠后,宠物不能继续运行任务。
+- 宠物只提供状态提示和会话跳转,不能审批权限、发送追问或替代完整活动面板。
+- 自定义图片导入在本机完成,但之后由你主动运行的会话、模型和集成仍遵循各自的数据与权限边界。
+- H5 可以继续访问对话主流程,但不会显示或远程控制桌面宠物。
+
+如果宠物没有出现,先确认「显示桌面宠物」已经开启,再完全退出并重新打开应用。导入失败时,优先核对图片格式、尺寸、文件大小和宠物 ID。
diff --git a/docs/en/channel/index.md b/docs/en/channel/index.md
index a0a7810a..987cb773 100644
--- a/docs/en/channel/index.md
+++ b/docs/en/channel/index.md
@@ -1,83 +1,56 @@
-# Claude Code Channel System Documentation
+# Channel System Research
-> Complete technical analysis of remote Agent control via IM platforms
+This section documents the upstream Claude Code Channel/MCP architecture. It is not the recommended setup path for the IM integrations shipped by this repository.
----
+To connect WeChat, DingTalk, WhatsApp, Telegram, or Feishu to the current Desktop app, start with [IM Integrations](../im/).
-## Documentation
+## Current product path
-### [01-channel-system.md](./01-channel-system.md) — Channel System Architecture
+The working integration in this repository is:
-A deep dive into the design and implementation of Claude Code's Channel system from a source code perspective, covering:
+```text
+Desktop settings
+ → /api/adapters
+ → ~/.claude/adapters.json
+ → adapters/*
+ → pairing and allowlist
+ → HTTP session creation
+ → /ws/:sessionId
+ → Claude Code session
+```
-- **What is a Channel**: Core concepts of IM integration, MCP protocol foundations
-- **Architecture Overview**: End-to-end message flow, component relationships
-- **Message Protocol**: Inbound notifications, XML wrapping, outbound tool calls
-- **Six-Layer Access Control**: Capability → Runtime gate → OAuth → Org policy → Session allowlist → Plugin approval
-- **Permission Relay System**: Remote tool execution approval, 5-letter request IDs, multi-source racing
-- **Plugin Architecture**: Channel manifest declarations, user config flow, scoped naming
-- **UI Components**: Terminal message rendering, status notices, developer warning dialogs
-- **Security Design**: XML injection prevention, marketplace verification, trust boundary analysis
+This adapter path is smaller and separate from the upstream Channel mechanism. It matches the Desktop server’s REST and WebSocket architecture and keeps platform authorization in dedicated sidecar processes.
-**Target audience**: Developers, architects, and plugin authors interested in AI Agent IM integration architecture
+## Documents
----
+### [Channel System Architecture](./01-channel-system.md)
-## Illustrations
+A source-level analysis of the upstream system, including:
-All illustrations use a dark background (#1a1a2e) with Claude Code Haha orange-blue accents (#FF7A00).
+- MCP Channel capability and notification flow;
+- inbound XML wrapping and outbound tool calls;
+- runtime, OAuth, organization, session, and plugin gates;
+- permission relay and short request IDs;
+- plugin registration and trust boundaries.
-| Image | Description | Document |
-|-------|-------------|----------|
-| `01-channel-overview.png` | Channel system architecture overview | Architecture |
-| `02-message-flow.png` | End-to-end message flow: IM → Agent → IM | Architecture |
-| `03-access-control.png` | Six-layer access control gates | Architecture |
-| `04-permission-relay.png` | Permission relay system flow | Architecture |
+### Historical IM Gateway proposal
----
+The Chinese documentation retains an early IM Gateway proposal as architecture history. It explains why the implementation moved toward independent adapters and `/ws/:sessionId`. It is not a current setup guide and is intentionally not presented as an English user workflow.
-## Quick Start
+## When to read this section
-### Users
+Use these pages when you are:
-1. Read the [Channel System Architecture](./01-channel-system.md)
-2. Learn how to start IM integration with `--channels`
-3. Understand how different platforms (Telegram, Feishu, Discord) connect
+- studying the upstream Claude Code Channel design;
+- comparing Channel plugins with the repository’s adapter implementation;
+- designing a future protocol or plugin integration;
+- reviewing the security boundaries of remote Agent control.
-### Plugin Developers
+For a working bot or linked account, use:
-1. Read the [Plugin Architecture](./01-channel-system.md#7-plugin-channel-architecture) section
-2. Understand the `plugin.json` Channel declaration format
-3. Implement the MCP Server `notifications/claude/channel` protocol
-4. Key source files:
- - `src/services/mcp/channelNotification.ts` — Core gating and message wrapping
- - `src/services/mcp/channelPermissions.ts` — Permission relay system
- - `src/services/mcp/channelAllowlist.ts` — Allowlist management
- - `src/utils/plugins/mcpPluginIntegration.ts` — Plugin MCP integration
- - `src/utils/plugins/schemas.ts` — Plugin manifest Channel schema
-
----
-
-## Core Concepts Reference
-
-| Concept | Description |
-|---------|-------------|
-| **Channel** | An MCP Server declaring `claude/channel` capability that can push IM messages to the Agent |
-| **Channel Entry** | Parsed `--channels` argument entry, either plugin or server kind |
-| **Channel Gate** | Six-layer access control gate deciding whether to register notification handlers |
-| **Permission Relay** | Mechanism for forwarding tool execution approval prompts to IM platforms |
-| **Channel Plugin** | A plugin declaring `channels` field in its `plugin.json` |
-| **Scoped Name** | Plugin server name with scope prefix: `plugin:{pluginName}:{serverName}` |
-| **Short Request ID** | 5-letter permission request identifier generated via FNV-1a hash |
-| **Channel Tag** | `` XML tag wrapping IM message content and metadata |
-| **Dev Channels** | Channels loaded via `--dangerously-load-development-channels` for local development |
-| **tengu_harbor** | GrowthBook runtime feature flag controlling the Channel system master switch |
-
----
-
-## Related Resources
-
-- [Claude Code Haha Home](/en/)
-- [Agent Framework Deep Dive](/en/agent/03-agent-framework)
-- [Skills System Documentation](/en/skills/01-usage-guide)
-- [GitHub Issues](https://github.com/NanmiCoder/cc-haha/issues)
+- [IM Integration overview](../im/)
+- [Telegram](../im/telegram.md)
+- [Feishu](../im/feishu.md)
+- [WeChat](../im/wechat.md)
+- [DingTalk](../im/dingtalk.md)
+- [WhatsApp](../im/whatsapp.md)
diff --git a/docs/en/desktop/01-quick-start.md b/docs/en/desktop/01-quick-start.md
new file mode 100644
index 00000000..ef7c7084
--- /dev/null
+++ b/docs/en/desktop/01-quick-start.md
@@ -0,0 +1,138 @@
+# Desktop Quick Start
+
+This walkthrough takes you from installation to a working session.
+
+
+
+## 1. Install and open the app
+
+Download the package for your platform from [GitHub Releases](https://github.com/NanmiCoder/cc-haha/releases). Detailed package names and platform notes are in the [Installation Guide](./04-installation.md).
+
+On first launch:
+
+- macOS may show the standard downloaded-app confirmation. Official releases from `v0.4.3` onward are signed and notarized.
+- An unsigned Windows build may show SmartScreen. Confirm that the package came from the project’s GitHub Release before choosing **More info → Run anyway**.
+- Linux AppImage users may need FUSE, depending on the distribution.
+
+## 2. Confirm the data location
+
+Desktop uses `~/.claude` by default for sessions, providers, settings, Skills, Agents, memory, tasks, traces, and adapter state. Most users should keep this default. If you need portable or isolated storage, select an explicit custom directory in General settings and restart when prompted.
+
+Do not point automated tests or temporary experiments at your real user data directory.
+
+## 3. Connect a model provider
+
+Open **Settings → Providers**. There are four main paths:
+
+| Path | Use it when |
+|---|---|
+| Claude official login | You want models available to your Claude account |
+| ChatGPT official login | You want the current GPT and Codex catalog available to your account |
+| Grok official login | You want the xAI model catalog available to your account |
+| Custom provider | You have an Anthropic-compatible or OpenAI-compatible endpoint and API key |
+
+Custom providers can use `anthropic`, `openai_chat`, or `openai_responses` format. Presets are editable, and the final model list still depends on the endpoint.
+
+Use **Test connection** before starting a session. Never paste API keys into chat messages or screenshots.
+
+## 4. Create a session
+
+Select **New session**, then choose:
+
+- a project directory;
+- the current working tree or an isolated Worktree when Git options are available;
+- a model and supported reasoning effort;
+- a permission mode.
+
+Sessions are bound to their working directory. The sidebar groups history by project and time, and open sessions can be kept in separate tabs.
+
+## 5. Choose a permission mode
+
+The current desktop supports five modes:
+
+
+
+| Mode | Behavior |
+|---|---|
+| Ask | Requests approval before protected operations |
+| Accept Edits | Automatically accepts eligible file edits, while other protected actions can still ask |
+| Plan | Lets the model investigate and propose a plan without carrying out normal implementation work |
+| Auto | Reviews operations automatically and allows or blocks them according to the Auto policy |
+| Bypass Permissions | Skips eligible approval prompts; explicit denials and tools that require user interaction still apply |
+
+Auto and Bypass Permissions expand what can run without a click. Read the confirmation dialog and use them only in a project and environment you trust. The mode cannot be changed while an active turn makes that unsafe.
+
+## 6. Chat and review work
+
+The composer supports:
+
+- text and multiline input;
+- pasted images, drag-and-drop, and file selection;
+- `/` commands;
+- `@` workspace file search;
+- model and effort selection when supported.
+
+While a turn runs, review tool calls, permission requests, tasks, SubAgents, teams, sources, changed files, and previews. Use the workspace panel for files and diffs, and the activity panel for background work.
+
+## 7. Explore the workspace
+
+The workspace can search the complete project without requiring every directory to be expanded first. Packaged desktop builds include the appropriate ripgrep binary.
+
+
+
+You can:
+
+- preview files and local attachments;
+- open files with the system default app or a configured editor;
+- select one or several diff lines and send review comments back to chat;
+- inspect the current branch, Worktree, and changed-file summary;
+- open the embedded terminal and browser preview.
+
+
+
+## 8. Manage Skills, Agents, and pets
+
+### Install Skills
+
+**Skills** shows installed Skills and a marketplace for discovering, reviewing, installing, and removing supported third-party Skills. Review source and risk information before installation.
+
+
+
+### Create or edit an Agent
+
+Open **Settings → Agents**, then select **Create Agent**.
+
+
+
+1. Choose **User** scope to reuse the Agent across projects, or **Project** scope to keep it with the current project.
+2. Add a name, description, and system prompt that clearly define the Agent's responsibility and expected output.
+3. Inherit the main session model and effort, or select a model and supported effort specifically for this Agent.
+4. Keep all tools, disable tools, or choose searchable built-in, MCP, and custom tool rules.
+5. Save the Agent. User Agents are stored in `~/.claude/agents/`; project Agents are stored in the current project's `.claude/agents/`.
+
+Built-in, plugin, and policy Agents remain read-only. See the [Multi-Agent Usage Guide](../agent/01-usage-guide.md#6-custom-agents) for definition formats, precedence, and inheritance.
+
+### Turn on the desktop pet
+
+Open **Settings → Pets** and enable **Show desktop pet**.
+
+
+
+1. Choose Dada, Huhu, Bubu, or Huihui.
+2. Adjust the size and animation setting.
+3. Enable the active-task panel if you want it to open automatically while a task is running. The panel remains hidden when no task is active.
+4. Hover for a small reaction, click the pet to focus the main window, or drag it to another position. Right-click the pet to close it; return to **Settings → Pets** to show it again.
+
+Select **Add pet** to make a lightweight local animation from a transparent PNG or WebP, or import a supported `1536×2288` v2 animation atlas. The AI full-animation option is currently unavailable. Pets run only in the Electron desktop app and are not supported in H5.
+
+Follow the complete [Desktop Pet Guide](./pets.md) for custom-image requirements, task states, and interaction details.
+
+## 9. Optional remote access
+
+- Use [IM integrations](../im/) for explicitly paired private-chat users.
+- Use [H5 Access](./06-h5-access.md) only on a trusted LAN or behind a reverse proxy you control.
+- Local loopback Web UI access is separate from H5 and does not grant remote access.
+
+## 10. If something fails
+
+Open **Settings → Diagnostics** to inspect the desktop runtime, local index, sidecar, providers, and recoverable state. Read [FAQ](./05-FAQ.md) before manually editing stored JSON.
diff --git a/docs/en/desktop/03-features.md b/docs/en/desktop/03-features.md
new file mode 100644
index 00000000..e1ebec04
--- /dev/null
+++ b/docs/en/desktop/03-features.md
@@ -0,0 +1,141 @@
+# Desktop Feature Guide
+
+This page summarizes the current user-visible Desktop surface.
+
+## Sessions and chat
+
+- Multiple tabs and project-filtered session history
+- Streaming Markdown, code, Mermaid, tool calls, thinking blocks, and permission requests
+- Image, file, directory, and PDF attachments with context preserved across restart and resume
+- Slash commands, `@` file search, model selection, and supported reasoning-effort levels
+- Stop, resume, search, navigation outline, and checkpoint-based undo for the current turn
+- Restored scroll position and stable search in long, virtualized conversations
+
+## Permission controls
+
+Desktop supports Ask, Accept Edits, Plan, Auto, and Bypass Permissions. Auto evaluates operations according to its policy; Bypass skips only approvals that are eligible to be skipped. Explicit denials, protected boundaries, and tools that require direct user interaction remain enforced.
+
+
+
+The selected mode is visible in the session composer. Higher-autonomy modes require an explicit warning confirmation.
+
+## Workspace and code review
+
+The workspace panel combines:
+
+
+
+- full-project file search using the packaged ripgrep binary;
+- a file tree, previews, and external-editor actions;
+- changed-file summaries and inline diffs;
+- single-line or contiguous-line review comments sent back into chat;
+- embedded terminal and local browser preview.
+
+Branch and Current/Isolated Worktree selection belongs to the new-session launch controls. The workspace then shows the selected session's files, Git status, and diffs.
+
+
+
+### Browser Preview
+
+Switch the workbench from **Files** to **Browser** to open a local development page or HTTPS URL inside the app.
+
+
+
+- **Screenshot** sends the current page image back to the session.
+- **Select element** captures a page element, selector, and screenshot as explicit Agent context.
+- Treat authenticated pages, cookies, and external-site content as sensitive. Use a public demo page for documentation screenshots.
+
+## Tasks, SubAgents, and activity
+
+The activity panel collects foreground tasks, background tasks, SubAgents, team members, and sources without flooding the main transcript.
+
+Running SubAgents can be opened before they finish. Their status, output, tool activity, duration, and terminal state continue to refresh. Completed, failed, and stopped terminal states are persisted so they do not revert to “running” after restart.
+
+
+
+Scheduled tasks support cron-based runs, model selection, enable/disable controls, manual execution, notifications, and history. New scheduled tasks currently run with Bypass Permissions; the form does not provide a permission selector. Use the smallest practical working directory and review the prompt before saving. Tasks run only while the Desktop app and computer remain available.
+
+## Providers and models
+
+Provider settings support:
+
+- Claude, OpenAI, and Grok official login;
+- editable built-in presets;
+- Custom Anthropic, OpenAI Chat, and OpenAI Responses endpoints;
+- per-provider model mappings, context metadata, proxy mode, and supported effort levels;
+- connection testing and runtime model discovery where available.
+
+The visible model catalog and supported effort levels depend on the current account, provider, and runtime response. Release-specific model names belong in release notes rather than this evergreen guide.
+
+## Skills and marketplace
+
+The Skills area groups local Skills by source and exposes their metadata and files. The marketplace aggregates supported third-party sources with search, filters, details, file previews, installation confirmation, local installation state, and risk information.
+
+
+
+Marketplace metadata is not a security guarantee. Review the source and requested behavior before installing a Skill.
+
+## Agent management
+
+The Agents page shows built-in, user, project, plugin, and policy sources together with override and effective-state information.
+
+
+
+Editable user and project Agents support:
+
+- reusable user scope or current-project scope;
+- model inheritance or an explicit model ID;
+- supported effort from `low` through `max`;
+- searchable tool selection plus custom and MCP tool rules;
+- a system prompt and description.
+
+User Agents are stored in `~/.claude/agents/`, while project Agents are stored in the current project's `.claude/agents/`. Built-in, plugin, and policy definitions remain read-only. Saving a definition and refreshing a running session are separate outcomes; a refresh failure does not silently discard a saved file.
+
+## Desktop pets
+
+The optional pet window is a native transparent Electron surface. Open **Settings → Pets**, enable the window, and choose one of the four built-in companions: Dada, Huhu, Bubu, or Huihui.
+
+
+
+The pet reflects working, waiting-for-you, failed, and idle states. While a task is active, its task panel can show the session title and status; selecting a row returns to that session. The panel remains hidden when no task is active. If automatic display is disabled, an activity-count badge lets you open it when work is running.
+
+The window also supports direct interaction:
+
+- hover to trigger a small reaction and idle gaze tracking;
+- click to wave and focus the main window;
+- drag to move the window, with its position restored after restart;
+- right-click to close it, then use **Settings → Pets** to show it again.
+
+### Add a custom pet
+
+Select **Add pet** to choose a local creation path.
+
+
+
+- **Animate one image** turns a static transparent PNG or WebP into a lightweight locally animated pet.
+- **Import professional animation atlas** accepts the supported `1536×2288` v2 sprite-sheet format.
+- **Generate full animation with AI** is visible for context but is currently unavailable.
+
+Imports stay local and do not call the selected chat model. After a successful import, the custom companion appears under **Your pets** and becomes the selected pet.
+
+
+
+Size, animation, active-task-panel, and window-position preferences are restored between launches. Pets run only in the Electron desktop app and are not supported in H5. See the [Desktop Pet Guide](./pets.md) for image limits, atlas layout, storage, and troubleshooting.
+
+## Local data and search
+
+A rebuildable SQLite projection accelerates session lists, global search, activity statistics, scheduled runs, teams, and traces. Original JSON and JSONL files remain authoritative. If the index is unavailable or damaged, readers can fall back to source files, and Diagnostics provides a confirmed rebuild action.
+
+Rebuilding the index does not delete chats, settings, Skills, memory, tasks, or traces.
+
+## IM and H5
+
+The desktop can launch sidecar adapters for WeChat, DingTalk, WhatsApp, Telegram, and Feishu. Each platform is private-chat oriented and requires an allowlisted or paired user. See [IM Integrations](../im/).
+
+[H5 Access](./06-h5-access.md) serves the browser UI from the local server for a trusted LAN or controlled reverse proxy. It uses a separate token and origin policy and is not a public multi-user account system.
+
+## Diagnostics and recovery
+
+Diagnostics surfaces local-index health, sidecar and runtime information, fallback state, storage information, and safe rebuild actions. Startup failures that cannot be recovered automatically should remain visible rather than leaving only a background process.
+
+Doctor and repair flows are intentionally conservative: protected user data is not silently reset.
diff --git a/docs/en/desktop/04-installation.md b/docs/en/desktop/04-installation.md
new file mode 100644
index 00000000..fd9cc1f1
--- /dev/null
+++ b/docs/en/desktop/04-installation.md
@@ -0,0 +1,103 @@
+# Installation
+
+Claude Code Haha Desktop is built with Electron and ships packages for macOS, Windows, and Linux. Official macOS releases from `v0.4.3` onward use Developer ID signing and notarization; older or temporary builds may still require manual approval.
+
+## Download
+
+Open [GitHub Releases](https://github.com/NanmiCoder/cc-haha/releases) and select the package for your platform:
+
+| Platform | Package |
+|---|---|
+| macOS Apple Silicon | `Claude-Code-Haha--mac-arm64.dmg` |
+| macOS Intel | `Claude-Code-Haha--mac-x64.dmg` |
+| Windows x64 | `Claude-Code-Haha--win-x64.exe` |
+| Windows ARM64 | `Claude-Code-Haha--win-arm64.exe` |
+| Linux x64 | `...-linux-x86_64.AppImage` or `...-linux-amd64.deb` |
+| Linux ARM64 | `...-linux-arm64.AppImage` or `...-linux-arm64.deb` |
+
+On macOS, **Apple M-series** means arm64; **Intel** means x64.
+
+## macOS
+
+Open the DMG and drag the app to `Applications`. An official signed release should only show the normal downloaded-app confirmation.
+
+For an older or explicitly unsigned temporary build, macOS may report that the app is damaged or cannot verify the developer. After confirming the package source, either use **System Settings → Privacy & Security → Open Anyway**, or run:
+
+```bash
+xattr -cr /Applications/Claude\ Code\ Haha.app
+```
+
+Do not use this workaround for an untrusted download.
+
+## Windows
+
+Run the `.exe` installer. If an unsigned package triggers SmartScreen, verify that it came from the expected GitHub Release, then select **More info → Run anyway**.
+
+The installer supports x64 and ARM64 packages. Close running Claude Code Haha processes before an overwrite install. User data is kept separately from application files.
+
+## Linux
+
+For AppImage:
+
+```bash
+chmod +x Claude-Code-Haha--linux-x86_64.AppImage
+./Claude-Code-Haha--linux-x86_64.AppImage
+```
+
+If FUSE is missing, Ubuntu 22.04 and earlier normally use `libfuse2`; Ubuntu 24.04 and later normally use `libfuse2t64`.
+
+For a deb package:
+
+```bash
+sudo apt install ./Claude-Code-Haha--linux-amd64.deb
+```
+
+## Local Web UI
+
+For source development, run the server from the repository root and Vite from `desktop/`:
+
+```bash
+SERVER_PORT=3456 bun run src/server/index.ts
+```
+
+In a second terminal:
+
+```bash
+cd desktop
+bun run dev --host 127.0.0.1 --port 2024
+```
+
+Open `http://127.0.0.1:2024`.
+
+True loopback access from the same machine does not require H5 to be enabled or an H5 token. This trust does not extend to LAN addresses or reverse proxies.
+
+## Headless Linux over SSH
+
+Keep both processes bound to loopback:
+
+```bash
+SERVER_PORT=3456 bun run src/server/index.ts
+
+cd desktop
+bun run dev --host 127.0.0.1 --port 2024
+```
+
+Forward both ports from your own computer:
+
+```bash
+ssh -L 2024:127.0.0.1:2024 -L 3456:127.0.0.1:3456 user@example.com
+```
+
+Then open:
+
+```text
+http://127.0.0.1:2024/?serverUrl=http%3A%2F%2F127.0.0.1%3A3456
+```
+
+This does not expose the service to the LAN. To serve the built Web UI to a trusted LAN or reverse proxy, follow [H5 Access](./06-h5-access.md#enable-h5-without-the-desktop-ui).
+
+## Updates and data
+
+Official releases check GitHub Releases for updates. In-place updates and overwrite installs are designed to preserve local settings and sessions.
+
+The default data location is under `~/.claude`. A custom data directory can be selected in the app. Before changing storage manually, use the in-app controls and read the recovery guidance in [FAQ](./05-FAQ.md).
diff --git a/docs/en/desktop/05-FAQ.md b/docs/en/desktop/05-FAQ.md
new file mode 100644
index 00000000..ada8839f
--- /dev/null
+++ b/docs/en/desktop/05-FAQ.md
@@ -0,0 +1,123 @@
+# Desktop FAQ
+
+## Where is my data stored?
+
+The default data root is under `~/.claude`. It contains user-owned sessions, provider settings, Skills, Agents, memory, tasks, traces, adapter configuration, and derived desktop state.
+
+Use the General settings control when changing the data directory. Do not delete or replace the directory as a first troubleshooting step.
+
+## Why does macOS say the app is damaged?
+
+Official macOS releases from `v0.4.3` onward are signed and notarized. This message is more likely with an older or temporary unsigned package. Confirm that the file came from the project’s GitHub Release, then follow the macOS steps in [Installation](./04-installation.md#macos).
+
+## Why does Windows show SmartScreen?
+
+Windows signing is not a release requirement for every build. An unsigned installer can trigger SmartScreen even when the package is intact. Verify the GitHub Release and architecture before selecting **More info → Run anyway**.
+
+## A Custom provider returns 401. What should I check?
+
+Open **Settings → Providers**, verify the Base URL, API format, API key, and model ID, then run **Test connection**.
+
+Authentication headers differ by provider:
+
+- standard API-key providers normally use the API key field;
+- bearer-token endpoints require the corresponding provider/auth configuration;
+- OpenAI-compatible endpoints must use the correct `openai_chat` or `openai_responses` format;
+- Kimi Code uses the current K3 Coding API preset and model metadata.
+
+Prefer editing or recreating the provider in the UI over manually rewriting stored JSON. Never share the stored credential or a diagnostic screenshot containing it.
+
+## Why is an official model missing?
+
+Official-login catalogs are filtered by the models available to the account and by runtime discovery. A model listed in the app’s metadata is not a guarantee that every account can call it.
+
+Sign out and back in, refresh the catalog, and confirm account access. For Custom providers, verify the endpoint’s real model ID and capability support.
+
+## Why was my reasoning effort reduced or ignored?
+
+Effort is applied only when the selected model and provider support it. Per-Agent effort follows the same rule. Unsupported combinations can be normalized, reduced, or omitted rather than forcing an invalid API request.
+
+## My old sessions are missing from the list. Were they deleted?
+
+Usually not. The SQLite local index is a rebuildable projection; original session files remain authoritative. Open **Settings → Diagnostics**, inspect index state, and use the confirmed rebuild action if needed.
+
+Rebuilding the index does not delete source sessions, settings, Skills, memory, tasks, or traces.
+
+## The app opens to a blank or startup error screen
+
+Restart once and open Diagnostics if the app recovers. If a visible startup error remains:
+
+1. record the exact error and platform;
+2. confirm the package architecture;
+3. check that security software did not quarantine a sidecar;
+4. avoid deleting the data directory;
+5. report the issue with the release version and sanitized logs.
+
+The desktop has bounded renderer and sidecar recovery, but it intentionally leaves an unrecoverable startup failure visible.
+
+## H5 asks for a token
+
+Remote browser access requires H5 to be enabled and a current token. True same-machine loopback development access is separate and does not require H5.
+
+Regenerating the token invalidates the previous one. See [H5 Access](./06-h5-access.md).
+
+## An IM bot says I am unauthorized
+
+Binding a bot or linked account does not authorize every chat user. The user must be in `allowedUsers` or complete pairing with the current six-character code. Codes expire after 60 minutes, are one-time use, and are rate limited after repeated failures.
+
+See [IM Integrations](../im/).
+
+## Will updates delete my providers or chats?
+
+Official in-app and overwrite updates are designed to preserve the user data directory. Back up important user-owned data before unusual manual migrations, and do not treat application build directories as the data source.
+
+## Desktop pets
+
+See [Desktop Pets](./pets.md) for the complete enable, customization, and import workflow.
+
+### Why is the active-task panel missing?
+
+The panel lists only non-idle sessions, such as tasks that are running, waiting for you, or need attention. It hides automatically when every task is complete or idle; enabling **Show active task panel** does not keep historical tasks visible.
+
+Confirm **Settings → Pets → Show active task panel** is enabled, then start a real task. Status refreshes periodically; if it still does not appear after a few seconds, hide and show the pet again.
+
+### Why is my pet not moving?
+
+Confirm **Settings → Pets → Play animations** is enabled. The app also respects the operating system’s reduced-motion preference, which can reduce or disable animation.
+
+A custom pet made from one image receives lightweight breathing, floating, and task-state motion. It does not have the complete frame-by-frame actions of a v2 atlas pet. Hover over or click the pet to trigger interactions such as jumping or waving.
+
+### Why did my custom-pet import fail?
+
+Check each requirement:
+
+- A single image must be PNG or WebP, 32–4096 pixels on each side, no larger than 8 MB, and no more than 16,777,216 total pixels.
+- A v2 animation atlas must be exactly 1536×2288 pixels and use the documented 8-column × 11-row frame layout.
+- The pet ID may be at most 73 characters, contain only lowercase letters, numbers, and single hyphens, and must not duplicate an existing ID.
+- Display name and description are required.
+
+The import reads only the local file you confirm in the system picker. Fix the source image and create the pet again instead of manually editing its manifest.
+
+### How do I restore a pet after closing it?
+
+Right-clicking the pet and choosing close, or turning off **Settings → Pets → Show desktop pet**, saves the disabled state. Turn that setting on again to restore the pet; reinstalling the app is not necessary.
+
+If the pet was dragged onto another display, hide and show it again. Its saved position is clamped into the currently visible work area when the pet window is recreated.
+
+### How do I delete a custom pet?
+
+There is no in-app delete button in the current version. Select a built-in pet first, choose **Settings → Pets → Open folder**, delete only the folder for the custom pet, then return to Settings and select **Refresh**.
+
+Delete only the confirmed pet directory under `${CLAUDE_CONFIG_DIR:-~/.claude}/cc-haha/pets`. Do not delete the entire `~/.claude` directory. Built-in pets ship with the app and are not removed here.
+
+### Why is “Generate full animation with AI” unavailable?
+
+That path requires a separately configured image-generation service. The current chat model is not used to generate pet artwork or action frames, so the option is explicitly unavailable when no suitable service is configured.
+
+You can use the recommended single-image animation path now, or import a prepared v2 animation atlas.
+
+### Can the pet approve permissions or send replies?
+
+No. The pet window reports task state, focuses the main window, and opens the corresponding session. It has no message composer and cannot approve file, command, or Computer Use permissions.
+
+When it shows **Waiting for you**, open the task in the main window, review the full context, approve or reject the request, and reply there. The pet never bypasses the selected permission mode.
diff --git a/docs/en/desktop/06-h5-access.md b/docs/en/desktop/06-h5-access.md
new file mode 100644
index 00000000..d3df9876
--- /dev/null
+++ b/docs/en/desktop/06-h5-access.md
@@ -0,0 +1,120 @@
+# H5 Access
+
+H5 is an optional browser entry point to the same local chat UI used by Desktop. It is intended for a trusted LAN or a reverse proxy you control.
+
+It is not a public SaaS authentication system. Anyone with the reachable service URL and current H5 token can access the exposed chat capabilities.
+
+## Enable H5
+
+1. Open **Settings → H5 Access**.
+2. Enable H5.
+3. Generate a token.
+4. Set the LAN access host or public base URL used to generate a launch link and QR code.
+5. Choose the current port, or configure a fixed port when you need a stable bookmark or reverse proxy.
+
+Desktop same-origin access automatically allows the origin derived from the configured public base URL. `allowedOrigins` is an advanced local-server API option for custom cross-origin deployments; it is not a field in the Desktop H5 settings page.
+
+For a same-origin LAN deployment, a typical public base URL is:
+
+```text
+http://192.168.1.20:3456
+```
+
+The generated launch URL contains the server URL and token. Treat it like an API credential and do not post it publicly.
+
+H5 settings are stored in `~/.claude/cc-haha/settings.json`. The full token is persisted so paired devices can reconnect after restart. Only trusted local control surfaces should be able to read it back.
+
+## Enable H5 without the Desktop UI
+
+Build the Web UI, then start the server from the repository root:
+
+```bash
+cd desktop
+bun run build
+cd ..
+SERVER_HOST=0.0.0.0 SERVER_PORT=3456 bun run src/server/index.ts
+```
+
+The server serves `desktop/dist`. If it is started outside the repository root, set `CLAUDE_H5_DIST_DIR` to the absolute `desktop/dist` path.
+
+From another terminal on the server itself, generate a token:
+
+```bash
+curl -sS -X POST http://127.0.0.1:3456/api/h5-access/enable
+```
+
+Configure exact origins and an optional public base URL:
+
+```bash
+curl -sS -X PUT http://127.0.0.1:3456/api/h5-access \
+ -H 'Content-Type: application/json' \
+ --data '{
+ "allowedOrigins": ["https://cc.example.com"],
+ "publicBaseUrl": "https://cc.example.com"
+ }'
+```
+
+Read the current configuration and full token only from server loopback:
+
+```bash
+curl -sS http://127.0.0.1:3456/api/h5-access
+```
+
+If SSH port forwarding is sufficient, keep the service on `127.0.0.1` and use the [headless installation path](./04-installation.md#headless-linux-over-ssh) instead of enabling H5.
+
+## Browser launch URL
+
+For same-origin hosting, the page and API share the public server root:
+
+```text
+https://cc.example.com/?serverUrl=https%3A%2F%2Fcc.example.com
+```
+
+The generated QR link also includes `h5Token`. On first connection, a manually opened page can ask for:
+
+| Field | Meaning |
+|---|---|
+| Server URL | Reachable Desktop server or reverse-proxy URL |
+| H5 Token | Current token generated by the trusted local settings surface |
+
+The browser stores the Server URL and token in its local storage and sends authorization with REST and WebSocket traffic.
+
+## Reverse proxy requirements
+
+Use HTTPS and proxy all required routes, including:
+
+- `/api/*`
+- `/proxy/*`
+- `/ws/*` with WebSocket upgrade
+- the built Web UI
+
+Preserve the public `Host` and normal proxy information. For example:
+
+```nginx
+proxy_set_header Host $host;
+proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+proxy_set_header X-Forwarded-Proto $scheme;
+```
+
+Do not rewrite `Host` to loopback while stripping every forwarding header. The server needs enough information to distinguish a remote proxied request from a trusted local request.
+
+## Mobile scope
+
+H5 prioritizes the chat flow:
+
+- session navigation uses a compact drawer;
+- composer, stop, attachments, and permission actions remain touch accessible;
+- `@` file results adapt to mobile width;
+- desktop-only workspace and terminal panels are not the primary mobile surface;
+- Desktop pets do not run in H5.
+
+## Security checklist
+
+- Keep H5 disabled unless it is needed.
+- Use exact allowed origins; do not use a wildcard.
+- Use HTTPS outside a trusted LAN.
+- Share the token separately from the public URL.
+- Regenerate the token when access should be revoked.
+- Do not expose H5 directly to the public internet as if it were a multi-user account service.
+
+Disabling H5 blocks remote access but keeps the current token for a later re-enable. Regenerating the token revokes the old token and QR links immediately.
diff --git a/docs/en/desktop/index.md b/docs/en/desktop/index.md
new file mode 100644
index 00000000..a1077ea9
--- /dev/null
+++ b/docs/en/desktop/index.md
@@ -0,0 +1,32 @@
+# Desktop App
+
+Claude Code Haha Desktop is the Electron-based workbench for local coding sessions. It combines chat, projects, workspace review, terminals, providers, Skills, Agents, background activity, scheduled tasks, IM adapters, and optional H5 access in one application.
+
+This guide describes the current Desktop experience. For exact changes between releases, see [GitHub Releases](https://github.com/NanmiCoder/cc-haha/releases).
+
+## Start here
+
+- [Quick Start](./01-quick-start.md) — install the app, connect a provider, open a project, and send the first message
+- [Feature Guide](./03-features.md) — sessions, workspace tools, Skills, Agents, pets, activity, search, and diagnostics
+- [Installation](./04-installation.md) — platform packages, first-launch prompts, Web UI, and headless Linux
+- [FAQ](./05-FAQ.md) — provider, data, startup, update, and recovery questions
+- [H5 Access](./06-h5-access.md) — optional browser access from a trusted LAN or reverse proxy
+- [Desktop Pets](./pets.md) — enable a companion, follow active tasks, customize its appearance, and import your own pet
+- [IM Integrations](../im/) — connect WeChat, DingTalk, WhatsApp, Telegram, or Feishu
+
+## Typical workflow
+
+1. Install the package for your operating system.
+2. Complete first-run setup and choose where local data should be stored.
+3. Sign in with Claude, OpenAI, or Grok, or add a Custom provider.
+4. Create a session and select a project directory, branch, or isolated Worktree.
+5. Review tool calls, file changes, previews, tasks, and SubAgent activity while the session runs.
+6. Use Diagnostics when a provider, local index, sidecar, or desktop runtime needs attention.
+
+## Important boundaries
+
+- Desktop data stays local unless a configured provider or integration needs to send it to an external service.
+- H5 is not a public SaaS login system. It exposes the local service to anyone who has the configured URL and token.
+- IM adapters deny access unless a user is explicitly allowlisted or paired.
+- Desktop pets are an Electron feature and do not run in H5.
+- Model availability, context size, and reasoning effort still depend on the selected account and provider.
diff --git a/docs/en/desktop/pets.md b/docs/en/desktop/pets.md
new file mode 100644
index 00000000..8e002f5e
--- /dev/null
+++ b/docs/en/desktop/pets.md
@@ -0,0 +1,124 @@
+# Desktop Pets
+
+Desktop Pets are optional companions that live in a small transparent window outside the main app. They reflect a few useful session states, provide a quick way back to active sessions, and can be replaced with your own local artwork.
+
+Pets are an **Electron Desktop feature**. They do not appear in H5 or the browser-only Web UI.
+
+
+
+## Enable a pet
+
+1. Open **Settings → Pets**.
+2. Turn on **Show desktop pet**.
+3. Choose one of the four built-in companions:
+ - **Dada**, the coding companion;
+ - **Huhu**, the planning companion;
+ - **Bubu**, the fixing companion;
+ - **Huihui**, the building companion.
+4. Adjust the size between `96px` and `192px`.
+5. Choose whether to play animations and show the active-task panel.
+
+The selected pet, size, window position, animation preference, and task-panel preference are restored between launches.
+
+## Interact with the floating pet
+
+| Action | Result |
+|---|---|
+| Move the pointer near an idle pet | Its gaze follows the pointer; entering the pet also triggers a short jump |
+| Click the pet | Bring the main Claude Code Haha window forward and play a wave |
+| Drag the pet | Move the floating window; the pet runs in the drag direction |
+| Right-click the pet | Open the menu for closing the pet window |
+| Click the numbered task badge | Expand the active-session panel |
+| Click a session in the panel | Return to that session in the main window |
+| Click the arrow below the task list | Collapse the panel back to the numbered badge |
+
+The pet can visibly distinguish working, waiting for you, failed, and idle states. The panel is a navigator, not an approval surface: return to the main app to handle any pending interaction, approve or deny a tool, stop work, or inspect full output.
+
+When there are no active sessions, the task panel stays hidden. Disabling **Show active task panel** keeps active work behind the numbered badge instead of removing or stopping it.
+
+The animation switch affects the pet only. Turning it off does not stop sessions. Claude Code Haha also respects the operating system’s reduced-motion preference.
+
+## Create a custom pet
+
+Select **Add pet** under **Your pets**. The creation dialog offers two working local import paths and shows a third, currently unavailable AI path.
+
+
+
+Before choosing a file, enter:
+
+- a Pet ID of at most 73 characters, containing only lowercase letters, numbers, and single hyphens, such as `docs-helper`;
+- a display name;
+- a short description.
+
+The ID must be unique among your custom pets.
+
+### Option 1: animate one image
+
+This is the recommended path for most users.
+
+Use a static image with:
+
+- PNG or WebP format;
+- a transparent background for the cleanest floating-window result;
+- width and height between `32px` and `4096px`;
+- no more than `16,777,216` total pixels;
+- a file size no larger than `8MB`.
+
+Claude Code Haha copies and validates the image locally, then adds lightweight breathing, floating, and status motion. It does not invent new action frames, and it does not call the model selected for chat.
+
+### Option 2: import a v2 animation atlas
+
+Use this path only when you already have a correctly prepared frame atlas.
+
+The atlas must be a static PNG or WebP with:
+
+| Property | Required value |
+|---|---|
+| Full size | Exactly `1536×2288` |
+| Grid | `8` columns × `11` rows |
+| Cell size | `192×208` |
+| Spacing | No padding or gaps between cells |
+| Background | Transparent |
+
+Rows are assigned to idle, run right, run left, wave, jump, failed, waiting, working, review, and two rows containing 16 gaze directions. The review row is part of the asset format; its presence does not guarantee that the current runtime will surface a separate review state.
+
+Every row may use fewer than eight visible action frames, but the grid dimensions and row positions must remain unchanged.
+
+### AI animation is not available
+
+**AI-generate full animation** is displayed as an unavailable future path. It requires a separate image-generation service and does not fall back to the current chat model. A disabled card in this dialog is therefore expected behavior, not a provider configuration error.
+
+After a successful import, the new pet is selected automatically and appears under **Your pets**.
+
+
+
+## Storage and removal
+
+Custom pet packages are stored under:
+
+```text
+${CLAUDE_CONFIG_DIR:-~/.claude}/cc-haha/pets
+```
+
+Use **Open folder** in Pet settings to open the resolved directory. This is also the current removal path:
+
+1. Select a built-in pet first if the package you are removing is active.
+2. Open the custom pet folder.
+3. Remove only the custom pet’s own package directory.
+4. Return to Pet settings and select **Refresh**.
+
+Closing or disabling the floating window does not delete a custom pet. If a selected package goes missing, the app falls back to a built-in pet.
+
+The loader skips invalid or unsafe packages and reports how many folders could not be loaded. Do not replace package files while an import is still running, and avoid symbolic links or unsupported animated image formats.
+
+## Privacy, safety, and boundaries
+
+- Image selection, validation, copying, and lightweight animation are local operations.
+- Importing a pet does not send the image to the selected chat model.
+- A pet can show summarized task state and navigate to a session, but it cannot approve permissions, answer model questions, or control a task directly.
+- Pets do not run in H5, IM integrations, or a browser-only deployment.
+- The pet observes the local Desktop service; it is not a cloud monitor and does not stay active after the Desktop app exits.
+- Always-on-top behavior, dragging across displays, and window placement can vary by operating system and desktop environment.
+- A successful application build does not by itself prove pet-window behavior on every supported operating system.
+
+If the pet does not move, check both **Play animations** and the operating system’s reduced-motion setting. If it does not appear at all, turn **Show desktop pet** off and on once, then restart the Desktop app before editing stored files manually.
diff --git a/docs/en/features/computer-use-architecture.md b/docs/en/features/computer-use-architecture.md
index f4912e86..21982eaf 100644
--- a/docs/en/features/computer-use-architecture.md
+++ b/docs/en/features/computer-use-architecture.md
@@ -1,602 +1,225 @@
-# Computer Use Architecture Deep Dive
+# Computer Use Architecture
-> A deep dive into the Computer Use implementation: from MCP tool definitions to Python Bridge, from 9-layer security gates to feature flag bypasses.
+> This page describes the Computer Use tools, authorization model, platform executors, and current safety boundaries. For setup, start with the [Computer Use guide](./computer-use.md).
-
-Patch Environment ·
-Layered Architecture ·
-MCP Tool Layer ·
-Security Gates ·
-Python Bridge ·
-Interaction Loop ·
-Source File Index
-
+## Layers
-
-
----
-
-## 1. Patch Environment Overview
-
-The original Claude Code's Computer Use feature (internal codename **Chicago**) depends on three components that are not publicly available:
-
-| Component | Purpose | Availability |
-|-----------|---------|-------------|
-| `@ant/computer-use-swift` | Screenshots, display enumeration | Anthropic private npm package |
-| `@ant/computer-use-input` | Mouse/keyboard simulation | Anthropic private npm package |
-| GrowthBook remote config | Feature flags, kill switch | Anthropic internal service |
-
-Our approach: **preserve the original MCP tool definitions and security mechanisms, only replace the execution layer and feature flag controls**.
-
-
-
-### What We Changed
-
-```
-Original Claude Code Claude Code Haha (Patched)
-──────────────────── ─────────────────────────
-@ant/computer-use-swift ──replaced──→ Python Bridge (mac_helper.py)
-@ant/computer-use-input ──replaced──→ pyautogui + pyobjc
-GrowthBook feature flags ──bypassed──→ gates.ts hardcoded return true
-Subscription check (Max/Pro) ──bypassed──→ getChicagoEnabled() = true
-Build macro CHICAGO_MCP ──replaced──→ true
-isDefaultDisabledBuiltin ──modified──→ returns false
+```text
+Model / MCP client
+ ↓
+Tool definitions and argument validation
+ src/vendor/computer-use-mcp/tools.ts
+ ↓
+Session authorization and safe dispatch
+ src/vendor/computer-use-mcp/toolCalls.ts
+ src/vendor/computer-use-mcp/mcpServer.ts
+ ↓
+Claude Code Haha integration
+ src/utils/computerUse/
+ ↓
+Python bridge
+ src/utils/computerUse/pythonBridge.ts
+ ↓
+Platform helper
+ runtime/mac_helper.py
+ runtime/win_helper.py
```
-### What We Kept Intact
+The tool and authorization layers remain platform-independent where possible. Platform differences are concentrated in `ComputerExecutor` capabilities and the Python helpers.
-- **MCP tool definitions** (24 tools with their schema and parameter validation)
-- **9-layer security gates** (TCC permissions, app allowlist, permission tiers, pixel validation, etc.)
-- **App classification system** (191 bundle IDs categorized with permission mappings)
-- **Session context management** (global lock, screenshot cache, state synchronization)
-- **Keyboard shortcut blocklist** (system-level dangerous operation interception)
+## Tool registration
-### Feature Flag Bypass Details
+`buildComputerUseTools()` creates the MCP tool definitions. The codebase contains 27 schemas:
-The original code uses three layers of gating to restrict Computer Use access:
+- 24 base control tools
+- 3 Teach tools: `request_teach_access`, `teach_step`, and `teach_batch`
-```typescript
-// Original code (simplified)
-function getChicagoEnabled(): boolean {
- // Layer 1: GrowthBook remote config
- const config = getDynamicConfig('tengu_malort_pedway')
- // Layer 2: Subscription check
- const hasSubscription = hasRequiredSubscription() // Max/Pro
- // Layer 3: Build-time macro
- return feature('CHICAGO_MCP') && config.enabled && hasSubscription
-}
+Teach tools are gated by `caps.teachMode`. When a host does not enable that capability, `ListTools` returns only the base tools. “The code defines 27 tools” does not mean that every runtime always exposes all 27.
+
+There are two MCP Server construction paths:
+
+- `src/utils/computerUse/mcpServer.ts` creates the CLI server and adds sanitized installed-application names to the access tool description.
+- `src/vendor/computer-use-mcp/mcpServer.ts` binds the real session context, including the latest screenshot and grant state.
+
+`src/utils/computerUse/setup.ts` creates the dynamic MCP configuration and allowed tool names. Calls are still controlled by Computer Use's own application authorization flow; being present in `allowedTools` is not permission to operate arbitrary applications.
+
+## Session state
+
+Each bound session tracks:
+
+- authorized applications and permission tiers
+- clipboard and system-key grants
+- selected display
+- latest screenshot and coordinate geometry
+- Computer Use session lock
+- whether Teach mode is active
+
+The latest screenshot is the coordinate reference for later clicks. Screenshot dimensions, logical display size, scale factor, and display origin must move together or Retina and multi-display setups will target the wrong location.
+
+## Coordinates and screenshots
+
+Tool descriptions expose exactly one coordinate mode to the model:
+
+- `pixels`: coordinates come from the most recent screenshot seen by the model
+- `normalized_0_100`: coordinates are percentages of screen width and height
+
+The tool description and executor read the same frozen coordinate configuration so the model cannot be instructed in one mode while the host converts another.
+
+Typical mapping:
+
+```text
+Model screenshot coordinate
+ → map through screenshot size to logical display size
+ → add target display origin
+ → send to the platform helper
```
-Our modification:
+Screenshots are resized to an image budget. Documentation should not hard-code a single output resolution.
-```typescript
-// gates.ts — our change
-export function getChicagoEnabled(): boolean {
- return true // ← all three gate layers bypassed
-}
+## Authorization and action checks
+
+Computer Use combines several checks before an action rather than relying on a single permission dialog.
+
+### Global switch and OS permissions
+
+- `CLAUDE_COMPUTER_USE_ENABLED=0` or managed configuration can disable the feature.
+- macOS requires Accessibility and Screen Recording.
+- Windows does not use macOS TCC, but it still follows application grants and action checks.
+
+### Session exclusion
+
+A session lock ensures that only one session controls system input at a time. The lock carries process and session information and supports stale-process recovery. Normal recovery should not require users to delete it manually.
+
+### Application allowlist and frontmost check
+
+Before input, Computer Use reads the frontmost application. If it is not in the session allowlist, the action is rejected. Grants use `read`, `click`, and `full` tiers.
+
+Screenshot behavior differs by platform:
+
+| Platform | `screenshotFiltering` | Behavior |
+|---|---|---|
+| macOS | `native` | Excludes unauthorized application windows at screenshot composition time |
+| Windows | `none` | May capture every visible window; the input allowlist still applies |
+
+“Input cannot target an unauthorized Windows application” does not imply that screenshots hide other Windows applications.
+
+### Clipboard and system keys
+
+Clipboard read, clipboard write, and system-level key combinations use separate grant flags. Multiline typing may use a clipboard fast path, and the executor attempts to preserve and restore clipboard contents, but only after the user grants the relevant capability.
+
+High-risk system key combinations also pass a dedicated key check. Ordinary application authorization does not automatically permit quitting applications, switching applications, locking the screen, or similar system actions.
+
+### Pixel staleness
+
+The tool layer retains pixel comparison support for detecting a UI that changed after the model saw it. `pixelValidation` is disabled in the current default configuration, so the model should take a fresh screenshot after UI changes.
+
+## Capabilities that are not currently provided
+
+### No global Escape abort
+
+`src/utils/computerUse/escHotkey.ts` does not register a system-wide Escape abort. The desktop host and documentation must not promise that Escape can stop Computer Use from every application.
+
+### No automatic window hiding
+
+The executor keeps `prepareForAction()` and `previewHideSet()` interfaces, but the current macOS and Windows helpers do not hide windows based on the allowlist. `unhideComputerUseApps()` is also a no-op.
+
+Consequences:
+
+- Do not present `hideBeforeAction` as an implemented privacy guarantee.
+- On Windows, users should close or minimize sensitive windows themselves.
+- On macOS, the privacy boundary comes from native screenshot filtering, not window hiding.
+
+## Teach flow
+
+Teach adds a guided overlay on top of the same authorization and action dispatcher:
+
+```text
+request_teach_access
+ → user approves teaching applications
+ → Teach session activates
+ → teach_step / teach_batch
+ → show guidance, wait for Next, run actions, return a new screenshot
+ → user exits or the turn ends
```
-**Note**: Sub-gates (pixelValidation, mouseAnimation, etc.) still retain the original logic and can be controlled via configuration.
-
----
-
-## 2. Layered Architecture
-
-Computer Use uses a **6-layer architecture** with clear responsibilities and boundaries:
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ Layer 1 — MCP Tool Interface │
-│ tools.ts: 24 tool schemas + parameter validation │
-│ buildComputerUseTools() → MCP Tool Definition │
-├─────────────────────────────────────────────────────────────┤
-│ Layer 2 — Tool Dispatch & Security Control │
-│ toolCalls.ts: handleToolCall() + 9 security gates │
-│ deniedApps.ts: 191 app classifications + permission tiers │
-├─────────────────────────────────────────────────────────────┤
-│ Layer 3 — MCP Server Binding │
-│ mcpServer.ts: session context + global lock + screenshot │
-│ bindSessionContext() → per-call overrides │
-├─────────────────────────────────────────────────────────────┤
-│ Layer 4 — CLI Integration │
-│ wrapper.tsx: permission dialogs + state read/write │
-│ setup.ts: MCP config initialization │
-│ gates.ts: feature flags (bypassed) │
-├─────────────────────────────────────────────────────────────┤
-│ Layer 5 — Python Bridge IPC [PATCH] │
-│ pythonBridge.ts: venv mgmt + JSON RPC + error handling │
-│ callPythonHelper(command, payload) → T │
-├─────────────────────────────────────────────────────────────┤
-│ Layer 6 — Python Runtime Execution [PATCH] │
-│ mac_helper.py: pyautogui + mss + pyobjc │
-│ 660 lines of Python implementing all system interactions │
-└─────────────────────────────────────────────────────────────┘
-```
-
-Layers marked **[PATCH]** are our replaced/new code. **All other layers** are preserved from the original Claude Code.
-
-### Why This Layering?
-
-| Layer | Source | Reusability |
-|-------|--------|------------|
-| Layer 1-2 | `vendor/computer-use-mcp/` | Platform-agnostic, reusable for Electron, Web hosts |
-| Layer 3 | `vendor/computer-use-mcp/` | Platform-agnostic, standard MCP protocol |
-| Layer 4 | `utils/computerUse/` | CLI-specific, bound to app state |
-| Layer 5-6 | `utils/computerUse/` + `runtime/` | macOS-specific, Python implementation |
-
----
-
-## 3. MCP Tool Layer
-
-### 24 Tools Overview
-
-Computer Use exposes 24 tools to the model via MCP (Model Context Protocol):
-
-| Category | Tools | Permission Tier |
-|----------|-------|----------------|
-| **Permission** | `request_access`, `list_granted_applications` | None required |
-| **Screenshot** | `screenshot`, `zoom` | read |
-| **Mouse Click** | `left_click`, `double_click`, `triple_click` | click |
-| **Mouse Advanced** | `right_click`, `middle_click`, `left_click_drag` | full |
-| **Mouse Move** | `mouse_move`, `cursor_position`, `scroll` | click |
-| **Mouse Low-level** | `left_mouse_down`, `left_mouse_up` | full |
-| **Keyboard** | `type`, `key`, `hold_key` | full |
-| **Application** | `open_application`, `switch_display` | full |
-| **Clipboard** | `read_clipboard`, `write_clipboard` | full |
-| **Batch** | `computer_batch` | Inherits from sub-operations |
-| **Wait** | `wait` | None required |
-
-### Coordinate System
-
-The model interacts with the screen through two coordinate modes:
-
-```
-pixels mode (default):
- Model sees screenshot size (1176 x 784)
- Model outputs coordinate [588, 392]
- scaleCoord() conversion:
- x_logical = (588 * displayWidth / 1176) + originX
- y_logical = (392 * displayHeight / 784) + originY
-
-normalized_0_100 mode:
- Model outputs coordinate [50, 50] (percentage)
- scaleCoord() conversion:
- x_logical = (50 / 100) * displayWidth + originX
- y_logical = (50 / 100) * displayHeight + originY
-```
-
-Screenshot dimensions are calculated by `imageResize.ts` to ensure:
-- Long edge <= 1568 pixels
-- Token budget <= 1568 (vision encoder at 28px/token)
-- Aspect ratio preserved
-
-### App Classification System
-
-`deniedApps.ts` precisely classifies 191 applications:
-
-**Browsers** (55 bundle IDs) -> tier `read`
-```
-Safari, Chrome, Firefox, Arc, Edge, Opera, Brave, Vivaldi...
-Reason: browser operations should use Chrome MCP, not blind clicking
-```
-
-**Terminals** (102 bundle IDs) -> tier `click`
-```
-Terminal, iTerm2, VS Code, Cursor, JetBrains IDEs, Xcode...
-Reason: terminal operations should use Bash Tool, limited to click only
-```
-
-**Trading** (34 bundle IDs) -> tier `read`
-```
-Webull, Fidelity, Interactive Brokers, Binance, Kraken...
-Reason: financial operations are extremely high-risk, screenshot only
-```
-
-**Completely Blocked** (policy deny list):
-```
-Netflix, Spotify, Apple Music, Kindle...
-Reason: copyright compliance, rejected without permission dialog
-```
-
----
-
-## 4. Security Gate System
-
-Every input action (click, keyboard, drag) must pass through **9 security gates** before execution:
-
-
-
-### Gate Details
-
-#### Gate 1: Kill Switch
-```typescript
-if (adapter.isDisabled()) return errorResult("Computer Use is disabled")
-```
-Reads `getChicagoEnabled()` — always returns `true` in patched version.
-
-#### Gate 2: TCC Permission Check
-```typescript
-await adapter.ensureOsPermissions()
-// → Python: check_permissions
-// → Accessibility: osascript "tell System Events..."
-// → Screen Recording: CGDisplayCaptureDisplay()
-```
-Reports error if macOS Accessibility or Screen Recording permissions are missing.
-
-#### Gate 3: Global Mutex Lock
-```typescript
-await tryAcquireComputerUseLock(sessionId)
-// File lock: ~/.claude/computer-use.lock
-// JSON: { sessionId, pid, acquiredAt }
-```
-Ensures only one Claude session can control the computer at a time. Supports stale PID recovery.
-
-#### Gate 4: Hide Non-Allowlisted Apps
-```typescript
-await executor.prepareForAction(allowlistBundleIds)
-// Hide all app windows not in the allowlist
-// Ensures screenshots only contain authorized apps
-```
-
-#### Gate 5: Frontmost App Check
-```typescript
-const frontmost = await executor.getFrontmostApp()
-if (!allowlist.includes(frontmost.bundleId)) {
- return errorResult("Application not authorized")
-}
-```
-Even after passing the allowlist, verifies the current foreground app is authorized.
-
-#### Gate 6: Permission Tier Check
-
-Three-tier permission model:
-
-| Tier | Allowed Operations | Prohibited Operations |
-|------|-------------------|----------------------|
-| `read` | Screenshot viewing | Any input action |
-| `click` | Left click, scroll | Right-click, drag, keyboard input |
-| `full` | All operations | No restrictions |
-
-```typescript
-function tierSatisfies(tier: CuAppPermTier, required: ActionKind): boolean {
- const order = { read: 0, click: 1, full: 2 }
- return order[tier] >= order[required]
-}
-```
-
-**Anti-subversion**: If permissions are insufficient, the response includes a `TIER_ANTI_SUBVERSION` hint to prevent the model from bypassing restrictions via AppleScript or System Events.
-
-#### Gate 7: Clipboard Guard
-
-**Threat model**:
-```
-1. Agent calls write_clipboard("rm -rf /")
-2. Switches to Terminal (click-tier allows clicking)
-3. Model clicks Terminal's paste button
-4. Malicious command executed
-```
-
-**Protection**:
-```
-When click-tier app becomes frontmost:
- → Save current clipboard content (stash)
- → Clear clipboard
- → Re-clear after each operation
-
-When non-click-tier app becomes frontmost:
- → Restore original clipboard content
-```
-
-#### Gate 8: Pixel Validation (Staleness Guard)
-
-```
-Last screenshot Current actual screen
-┌────────────┐ ┌────────────┐
-│ Button A │ │ Dialog │ ← UI has changed
-│ [756,342] │ │ Confirm? │
-└────────────┘ └────────────┘
-
-Validation: sample 9x9 pixel grid at [756,342]
- → Compare last screenshot vs live screenshot pixels
- → Different → reject click + prompt to re-screenshot
- → Same → allow click
-```
-
-**Note**: In patched version, `pixelValidation` is off by default (`hostAdapter.cropRawPatch()` returns null).
-
-#### Gate 9: System Shortcut Interception
-
-`keyBlocklist.ts` blocks dangerous shortcuts:
-
-| Shortcut | Dangerous Action |
-|----------|-----------------|
-| `Cmd+Q` | Quit application |
-| `Shift+Cmd+Q` | Log out |
-| `Option+Cmd+Esc` | Force quit dialog |
-| `Cmd+Tab` | App switcher |
-| `Cmd+Space` | Spotlight |
-| `Ctrl+Cmd+Q` | Lock screen |
-
----
-
-## 5. Python Bridge Mechanism
-
-
-
-### Architecture Design
-
-Original Claude Code uses compiled Swift native modules (.node NAPI plugins) to directly call macOS APIs. We replaced this layer with **Python subprocess + JSON RPC**:
-
-```
-TypeScript (Bun Runtime) Python (venv)
-┌────────────────────┐ ┌────────────────────┐
-│ executor.ts │ │ mac_helper.py │
-│ │ execFile() │ │
-│ callPythonHelper │ ──────────────→ │ main() │
-│ ('click', │ command + │ ├─ parse argv │
-│ {x:756,y:342}) │ --payload JSON │ ├─ dispatch() │
-│ │ │ └─ click() │
-│ ← JSON.parse ──── │ ←────────────── │ pyautogui │
-│ {ok:true, │ stdout JSON │ │
-│ result:true} │ │ json_output(...) │
-└────────────────────┘ └────────────────────┘
-```
-
-### Bootstrap Flow
-
-First call to `callPythonHelper()` automatically completes environment setup:
-
-```
-ensureBootstrapped()
- │
- ├─ Check if .runtime/venv/bin/python3 exists
- │ └─ Missing → python3 -m venv .runtime/venv/
- │
- ├─ Check if pip is available
- │ └─ Missing → python3 -m ensurepip --upgrade
- │
- ├─ Compute SHA256 of runtime/requirements.txt
- │ └─ Compare with .runtime/requirements.sha256
- │ └─ Different → pip install -r requirements.txt
- │ Write new SHA256 hash
- │ └─ Same → skip installation
- │
- └─ Ready, return venv Python path
-```
-
-**Dependencies** (`runtime/requirements.txt`):
-
-| Library | Purpose |
-|---------|---------|
-| `mss` | High-performance screen capture |
-| `Pillow` | JPEG encoding and image processing |
-| `pyautogui` | Mouse click, keyboard input |
-| `pyobjc-core` | macOS Objective-C bridge |
-| `pyobjc-framework-Cocoa` | NSWorkspace (app management), NSPasteboard (clipboard) |
-| `pyobjc-framework-Quartz` | CGDisplay (monitors), CGWindow (window list) |
-
-### Command Mapping
-
-`mac_helper.py` (660 lines) implements the following commands:
-
-| Command | Python Implementation | Return Value |
-|---------|----------------------|-------------|
-| `screenshot` | `mss.grab()` + `PIL.Image` JPEG encoding | `{base64, width, height, displayWidth, displayHeight}` |
-| `zoom` | `mss.grab(region)` region capture | `{base64, width, height}` |
-| `click` | `pyautogui.moveTo()` + `pyautogui.click()` | `true` |
-| `key` | `pyautogui.hotkey()` / `pyautogui.press()` | `true` |
-| `type` | `pyautogui.write(interval=0.008)` | `true` |
-| `drag` | `pyautogui.dragTo(duration=0.2)` | `true` |
-| `scroll` | `pyautogui.scroll()` / `pyautogui.hscroll()` | `true` |
-| `hold_key` | `pyautogui.keyDown()` + `sleep` + `pyautogui.keyUp()` | `true` |
-| `frontmost_app` | `NSWorkspace.frontmostApplication()` | `{bundleId, displayName}` |
-| `list_displays` | `CGGetActiveDisplayList()` + `CGDisplayBounds()` | `[DisplayGeometry...]` |
-| `open_app` | `NSWorkspace.launchApplicationAtURL_options_` | `void` |
-| `read_clipboard` | `NSPasteboard.stringForType_()` | `string` |
-| `write_clipboard` | `NSPasteboard.setString_forType_()` | `void` |
-| `check_permissions` | `osascript` + `CGDisplayCaptureDisplay` | `{accessibility, screenRecording}` |
-
-### Error Handling
-
-```python
-# mac_helper.py unified error handling
-def main():
- try:
- result = dispatch(command, payload)
- json_output({"ok": True, "result": result})
- except Exception as e:
- error_output({"ok": False, "error": {"message": str(e)}})
-```
-
-TypeScript side:
-```typescript
-// pythonBridge.ts
-const parsed = JSON.parse(stdout)
-if (!parsed.ok) {
- throw new Error(parsed.error.message) // → MCP tool error
-}
-return parsed.result
-```
-
----
-
-## 6. Screenshot-Analyze-Act Loop
-
-A complete Computer Use interaction consists of multiple **screenshot-analyze-act** cycles:
-
-### Typical Interaction Flow
-
-```
-User: "Open NetEase Music and search for a song"
-
-┌─ Cycle 1: Discover and open application ─────────────────┐
-│ │
-│ Step 1: request_access │
-│ → Permission dialog, user authorizes allowed apps │
-│ → Set allowedApps, grantFlags │
-│ │
-│ Step 2: screenshot │
-│ → Full screen capture → JPEG encode → base64 │
-│ → Cache screenshot dimensions (lastScreenshotDims) │
-│ → Return to model │
-│ │
-│ Step 3: Model analyzes screenshot │
-│ → "NetEase Music not on desktop, need to open it" │
-│ → Decides to call open_application │
-│ │
-│ Step 4: open_application("com.netease.163music") │
-│ → Gates 1-9 all pass │
-│ → Python: NSWorkspace.launchApplicationAtURL_() │
-│ │
-└───────────────────────────────────────────────────────────┘
-
-┌─ Cycle 2: Locate search box ─────────────────────────────┐
-│ │
-│ Step 5: screenshot │
-│ → Full screen (app now open) │
-│ → Update lastScreenshotDims │
-│ │
-│ Step 6: Model analyzes screenshot │
-│ → Vision identifies search box at (756, 342) │
-│ → Decides to click search box │
-│ │
-│ Step 7: left_click({coordinate: [756, 342]}) │
-│ → Gate 4: Hide non-allowlisted apps │
-│ → Gate 5: Frontmost is NetEase Music ✓ │
-│ → Gate 6: tier=full >= click ✓ │
-│ → scaleCoord(756, 342) → screen coordinates │
-│ → Python: pyautogui.click(x_logical, y_logical) │
-│ │
-└───────────────────────────────────────────────────────────┘
-
-┌─ Cycle 3: Type search query ─────────────────────────────┐
-│ │
-│ Step 8: type({text: "my favorite song"}) │
-│ → Gate 6: tier=full >= keyboard ✓ │
-│ → Python: pyautogui.write("...", interval=0.008) │
-│ │
-│ Step 9: screenshot │
-│ → Confirm search results appeared │
-│ │
-│ Step 10: left_click({coordinate: [...]}) │
-│ → Click target song │
-│ │
-└───────────────────────────────────────────────────────────┘
-```
-
-### Coordinate Conversion
-
-**What the model sees** vs **actual screen** are different coordinate spaces:
-
-```
-Physical screen (2560 x 1600, Retina 2x)
- ├─ Logical size: 1280 x 800
- └─ Physical pixels: 2560 x 1600
-
-After imageResize:
- ├─ Scaled size: 1176 x 735 (≤1568px budget)
- └─ This is the screenshot size the model "sees"
-
-Model outputs coordinate: [588, 368] (in image space)
-
-scaleCoord conversion:
- x_logical = (588 / 1176) * 1280 + originX = 640
- y_logical = (368 / 735) * 800 + originY = 400
-
-Python executes:
- pyautogui.moveTo(640, 400) ← logical coords (macOS handles Retina)
-```
-
-### Screenshot Cache & State Sync
-
-```
-bindSessionContext closure
- │
- ├─ lastScreenshot (in-memory)
- │ ├─ base64: JPEG data (for pixel validation)
- │ ├─ width/height: model-visible dimensions
- │ └─ displayWidth/displayHeight/originX/originY: display geometry
- │
- └─ AppState.computerUseMcpState (persisted)
- ├─ allowedApps: AppGrant[] — authorized app list
- ├─ grantFlags: {...} — clipboard/system shortcut permissions
- ├─ selectedDisplayId?: number — selected display
- ├─ lastScreenshotDims?: {...} — screenshot geometry (survives restart)
- └─ hiddenDuringTurn?: Set — apps hidden this turn
-```
-
----
-
-## 7. Key Source File Index
-
-### vendor/computer-use-mcp/ (Original Code Layer)
-
-| File | Lines | Responsibility |
-|------|-------|---------------|
-| `types.ts` | 622 | Permission model, session context, all type definitions |
-| `tools.ts` | 707 | 24 MCP tool schemas and parameter validation |
-| `toolCalls.ts` | 1600+ | **Core**: tool dispatch, 9 security gates, permission flow |
-| `deniedApps.ts` | 554 | 191 app classifications (browser/terminal/trading) and permission mappings |
-| `sentinelApps.ts` | 44 | Sensitive app warning labels (shell/filesystem/system_settings) |
-| `mcpServer.ts` | 314 | MCP server factory, session context binding, global lock |
-| `pixelCompare.ts` | 172 | Click target pixel validation (staleness guard) |
-| `imageResize.ts` | 109 | Screenshot dimension calculation (API image transcoder) |
-| `keyBlocklist.ts` | 154 | System shortcut interception (Cmd+Q, Cmd+Tab, etc.) |
-| `executor.ts` | 101 | ComputerExecutor interface definition |
-| `subGates.ts` | 20 | Feature flag sub-gate presets |
-
-### utils/computerUse/ (CLI Adaptation Layer)
-
-| File | Lines | Responsibility | Patched? |
-|------|-------|---------------|----------|
-| `executor.ts` | 231 | ComputerExecutor Python bridge implementation | Yes, rewritten |
-| `pythonBridge.ts` | 111 | Python subprocess management, venv bootstrap, JSON RPC | Yes, new |
-| `hostAdapter.ts` | 54 | HostAdapter implementation (permission checks, flag reading) | Partially |
-| `gates.ts` | 51 | GrowthBook feature flags (`getChicagoEnabled` bypass) | Yes, modified |
-| `wrapper.tsx` | 300+ | Session context construction, permission dialogs, lock management | Unchanged |
-| `setup.ts` | 54 | MCP config initialization | Unchanged |
-| `computerUseLock.ts` | 216 | Global file lock (`~/.claude/computer-use.lock`) | Unchanged |
-| `common.ts` | 62 | Constants (server name, bundle ID) | Unchanged |
-| `cleanup.ts` | — | Turn-end cleanup (app restore, clipboard restore) | Unchanged |
-| `toolRendering.tsx` | — | Tool result UI rendering | Unchanged |
-
-### runtime/ (Python Runtime)
-
-| File | Lines | Responsibility | Patched? |
-|------|-------|---------------|----------|
-| `mac_helper.py` | 660 | All system interactions in Python | Yes, new |
-| `requirements.txt` | 6 | Python dependency declarations | Yes, new |
-
----
-
-## 8. Design Trade-offs
-
-### Why Python Bridge?
-
-| Dimension | Native Swift (.node) | Python Bridge |
-|-----------|---------------------|---------------|
-| **Performance** | ~0ms (in-process call) | ~50-100ms (subprocess startup) |
-| **Readability** | Not readable after compilation | 660 lines of clear Python |
-| **Modifiability** | Requires Swift build environment | Edit .py files directly |
-| **Dependencies** | Specific Bun version NAPI | Any Python 3.8+ |
-| **Cross-platform** | macOS only | pyautogui/mss are natively cross-platform |
-| **User experience** | Imperceptible | Imperceptible (model thinking takes 2-5s) |
-
-**Conclusion**: The 50-100ms extra latency is completely negligible in Computer Use scenarios — the model typically takes 2-5 seconds for screenshot analysis and decision-making, so users won't notice the additional 100ms for underlying operations.
-
-### Approaches We Tried But Abandoned
-
-**Approach 1: Extract native .node modules**
-- Successfully extracted `computer-use-swift.node` (ARM64 424KB) from the Claude Code binary
-- Synchronous methods worked, but **Swift async method continuations never resumed**
-- Root cause: .node files compiled for Claude Code's built-in Bun, incompatible with user's Bun version
-
-**Approach 2: Empty stub packages**
-- Code compiled but all operations threw errors — no actual execution capability
-
----
-
-## Related Documentation
-
-- [Computer Use Guide](./computer-use.md) — Usage, quick start, environment variables
-- [Source Fixes](/en/reference/fixes) — Detailed records of other fixes and patches
+Important constraints:
+
+- Teach has a separate authorization entry and does not inherit regular clipboard or system-key grants.
+- The `teach_step` explanation is the primary narration visible in the overlay.
+- `teach_batch` is appropriate for predictable consecutive steps; use individual steps when the UI outcome is uncertain.
+- A regular permission dialog cannot be requested while Teach is hiding the main window.
+- After the user exits, further Teach calls must stop.
+- The host capability decides whether Teach tools are exposed.
+
+## Python bridge
+
+`src/utils/computerUse/pythonBridge.ts`:
+
+1. Resolves `.runtime` in the managed user configuration directory.
+2. Synchronizes the current platform helper and requirements.
+3. Creates a venv with the detected or configured Python.
+4. Installs or updates dependencies based on the requirements hash.
+5. invokes the helper with `command + JSON payload`.
+6. returns a uniform JSON result or error to TypeScript.
+
+Runtime files:
+
+| Path | Responsibility |
+|---|---|
+| `runtime/mac_helper.py` | macOS screenshots, applications, mouse, keyboard, and clipboard |
+| `runtime/win_helper.py` | Windows equivalents |
+| `runtime/requirements.txt` | macOS Python dependencies |
+| `runtime/requirements-win.txt` | Windows Python dependencies |
+
+Each helper invocation is a bounded subprocess request. Helpers do not read model state; TypeScript owns session authorization and action policy.
+
+## Host integration
+
+### CLI
+
+`src/utils/computerUse/` is responsible for:
+
+- macOS/Windows support detection
+- `ComputerExecutor` creation
+- dynamic MCP setup
+- permission UI and session state
+- preauthorized applications and Python selection
+
+### Desktop
+
+The desktop Settings page uses `src/server/api/computer-use.ts` to:
+
+- read and change the enabled state
+- inspect and install the Python runtime
+- check macOS system permissions
+- manage preauthorized applications
+- manage clipboard and system-key grants
+
+Settings is the configuration entry point. Actual calls still execute inside the CLI session's MCP and authorization boundaries.
+
+## Key source files
+
+| Path | Responsibility |
+|---|---|
+| `src/vendor/computer-use-mcp/tools.ts` | Tool schemas and Teach tools |
+| `src/vendor/computer-use-mcp/toolCalls.ts` | Dispatch, authorization, and safety checks |
+| `src/vendor/computer-use-mcp/mcpServer.ts` | MCP Server and session binding |
+| `src/vendor/computer-use-mcp/types.ts` | Capabilities, grants, and session types |
+| `src/utils/computerUse/common.ts` | Platform support and capabilities |
+| `src/utils/computerUse/gates.ts` | Global switch and sub-capability defaults |
+| `src/utils/computerUse/executor.ts` | Python bridge executor |
+| `src/utils/computerUse/pythonBridge.ts` | venv, dependencies, and subprocess protocol |
+| `src/utils/computerUse/wrapper.tsx` | CLI permission UI and session context |
+| `src/server/api/computer-use.ts` | Desktop Settings API |
+| `desktop/src/pages/ComputerUseSettings.tsx` | Desktop Settings UI |
+
+## Verification priorities
+
+- Keep tool schemas synchronized with dispatchable actions.
+- Keep model-facing coordinate descriptions aligned with executor conversion.
+- Do not mix macOS and Windows capability claims.
+- Reject input when the frontmost application is unauthorized.
+- Windows tests must not assume screenshot filtering.
+- Do not expose Teach tools when the host capability is off.
+- Preserve unknown fields during configuration migration.
+- Use temporary configuration directories in Python tests; never read the user's real runtime.
diff --git a/docs/en/features/computer-use.md b/docs/en/features/computer-use.md
index 4903b127..ea5bd977 100644
--- a/docs/en/features/computer-use.md
+++ b/docs/en/features/computer-use.md
@@ -1,162 +1,54 @@
# Computer Use Guide
+Computer Use lets a model inspect the screen and operate the mouse, keyboard, applications, and clipboard. It acts on the current computer, so review the authorization scope and platform differences before enabling it.
-> **Modified Version**: This feature is a **heavily modified version** of the Computer Use (internal codename "Chicago") found in the leaked Claude Code source. The official implementation relies on Anthropic's private native modules (`@ant/computer-use-swift`, `@ant/computer-use-input`) that are not publicly available. We **replaced the entire underlying operation layer** with a Python bridge: macOS uses `pyautogui` + `mss` + `pyobjc`, and Windows uses `pyautogui` + `mss` + `win32gui` + `psutil`.
+## Supported platforms
----
+| Platform | Status | Important difference |
+|---|---|---|
+| macOS Apple Silicon / Intel | Supported | Requires Accessibility and Screen Recording; supports native screenshot filtering |
+| Windows | Supported | Uses the Windows Python runtime; screenshots are not filtered to authorized windows |
+| Linux | Unsupported | There is currently no Linux executor |
-## Table of Contents
+The packaged desktop app does not require Bun. Computer Use does require a working Python 3 installation. The app creates an isolated virtual environment under the managed user configuration directory and installs the platform dependencies there.
-- [Overview](#overview)
-- [Supported Platforms](#supported-platforms)
-- [How It Works](#how-it-works)
-- [Quick Start](#quick-start)
-- [Usage](#usage)
-- [Security](#security)
-- [Environment Variables](#environment-variables)
-- [Technical Architecture](#technical-architecture)
-- [Approaches We Tried](#approaches-we-tried)
-- [Known Limitations](#known-limitations)
-- [References and Credits](#references-and-credits)
+## Quick start
----
+### Desktop app
-## Overview
+1. Open Settings → Computer Use.
+2. Enable Computer Use.
+3. Check Python status. If automatic discovery fails, select a Python executable.
+4. Run Install or Repair so the app can create the virtual environment and install dependencies.
+5. On macOS, grant Accessibility and Screen Recording, then check again.
+6. Choose the applications that may be controlled and, if needed, enable clipboard and system key permissions.
+7. Start a session and describe both the goal and the applications involved.
-Computer Use allows AI models to **directly control your computer** — taking screenshots, moving the mouse, clicking buttons, typing text, and managing application windows.
+Start with a small, reversible task:
-24 MCP tools are available:
-
-| Category | Tools |
-|----------|-------|
-| Screenshot | `screenshot`, `zoom` |
-| Mouse | `left_click`, `right_click`, `middle_click`, `double_click`, `triple_click`, `left_click_drag`, `mouse_move`, `left_mouse_down`, `left_mouse_up`, `cursor_position`, `scroll` |
-| Keyboard | `type`, `key`, `hold_key` |
-| Apps | `open_application`, `switch_display` |
-| Permissions | `request_access`, `list_granted_applications` |
-| Clipboard | `read_clipboard`, `write_clipboard` |
-| Other | `wait`, `computer_batch` |
-
----
-
-## Supported Platforms
-
-| Platform | Architecture | Status | Notes |
-|----------|-------------|--------|-------|
-| macOS | Apple Silicon (M1/M2/M3/M4) | ✅ Fully supported | Recommended |
-| macOS | Intel x86_64 | ✅ Fully supported | |
-| Windows | x64 | ✅ Fully supported | Uses `win32gui` + `psutil` + `pyperclip` + `screeninfo` instead of macOS APIs |
-| Linux | Any | ⚠️ Theoretically possible | Same as above — `pyobjc` needs to be replaced with `wmctrl` + `xdotool`. Not yet adapted |
-
-### Requirements
-
-- [Bun](https://bun.sh) >= 1.1.0
-- Python >= 3.8 (venv and dependencies are auto-installed on first use)
-- macOS permissions: Accessibility + Screen Recording
-- Windows: no extra OS permission setup
-
----
-
-## How It Works
-
-Computer Use operates through a **screenshot → analyze → act** feedback loop:
-
-```
-┌────────────────────────────────────────────────────┐
-│ AI Model (Claude / any Anthropic-protocol model) │
-│ │
-│ 1. Receives user request: "open Music app" │
-│ 2. Calls screenshot tool → receives screen image │
-│ 3. Model analyzes pixels, identifies UI elements │
-│ → "search box is at (756, 342)" │
-│ 4. Calls left_click { coordinate: [756, 342] } │
-│ 5. Calls type { text: "search query" } │
-│ 6. Calls screenshot again → verify → next step... │
-└───────────────┬────────────────────────────────────┘
- │ MCP Tool Call
- ▼
-┌────────────────────────────────────────────────────┐
-│ TypeScript Tool Layer (vendor/computer-use-mcp) │
-│ - Security checks (app allowlist, TCC permissions) │
-│ - Coordinate transformation │
-│ - Tool dispatch → executor │
-└───────────────┬────────────────────────────────────┘
- │ callPythonHelper()
- ▼
-┌────────────────────────────────────────────────────┐
-│ Python Bridge │
-│ macOS: runtime/mac_helper.py │
-│ Windows: runtime/win_helper.py │
-│ pyautogui.click(756, 342) ← mouse control │
-│ mss.grab(monitor) ← screenshot │
-│ NSWorkspace / win32gui ← app management │
-└────────────────────────────────────────────────────┘
+```text
+Take a screenshot and tell me what you can see.
+Open Notes and create an empty note titled "Test".
+Find the settings entry in the authorized app, but do not change anything.
```
-**Key**: Coordinate analysis is performed entirely by the model's vision capabilities — it "sees" the screenshot like a human sees a screen, identifying buttons, text fields, and other UI elements directly from pixels.
+### CLI
----
-
-## Quick Start
-
-### 1. Install dependencies
+Source mode requires the project dependencies and Python 3:
```bash
bun install
-```
-
-### 2. Ensure Python 3 is available
-
-```bash
-python3 --version # >= 3.8 required
-```
-
-> Python dependencies are **automatically installed** into `.runtime/venv/` on first Computer Use invocation.
-
-### 3. Grant macOS permissions
-
-**Accessibility:**
-
-```bash
-open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"
-```
-
-Add your **terminal app** (iTerm, Terminal, Ghostty, etc.) to the allow list.
-
-**Screen Recording:**
-
-```bash
-open "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture"
-```
-
-Add your terminal app as well. You may need to **restart your terminal** after granting permission.
-
-### 4. Start
-
-```bash
+python3 --version
./bin/claude-haha
```
-### 5. Use
-
-Just ask in natural language:
-
-```
-> Take a screenshot of my desktop
-> Open Safari and search for something
-> Type "hello" in the text editor
-```
-
-### Disable Computer Use
-
-If you only want the regular Coding Agent and do not want to expose `computer-use` MCP tools, disable it with either command:
+Disable the dynamic Computer Use MCP with:
```bash
-claude-haha --no-computer-use
-CLAUDE_COMPUTER_USE_ENABLED=0 claude-haha
+CLAUDE_COMPUTER_USE_ENABLED=0 ./bin/claude-haha
```
-You can also write the global config file at `~/.claude/cc-haha/computer-use-config.json`:
+Or set the managed configuration in `~/.claude/cc-haha/computer-use-config.json`:
```json
{
@@ -164,99 +56,133 @@ You can also write the global config file at `~/.claude/cc-haha/computer-use-con
}
```
-The desktop Settings > Computer Use switch writes the same config. Once disabled, new sessions will not inject the dynamic `computer-use` MCP server or add its desktop-control tools to `allowedTools`.
+The desktop Settings page updates the same managed configuration. Prefer the UI and do not overwrite unknown fields by hand.
----
+## Tools and Teach capability
-## Security
+The codebase defines 27 Computer Use tools:
-| Mechanism | Description |
-|-----------|-------------|
-| **App allowlist** | Each session requires explicit authorization for which apps Claude can interact with |
-| **Concurrency lock** | Only one Claude session can use Computer Use at a time (file lock) |
-| **Clipboard guard** | Original clipboard content is saved and restored when typing via clipboard |
-| **Sensitive action gates** | System keyboard shortcuts require additional authorization |
+| Category | Tools |
+|---|---|
+| Authorization | `request_access`, `list_granted_applications` |
+| Screenshot | `screenshot`, `zoom` |
+| Mouse | `left_click`, `right_click`, `middle_click`, `double_click`, `triple_click`, `left_click_drag`, `mouse_move`, `left_mouse_down`, `left_mouse_up`, `cursor_position`, `scroll` |
+| Keyboard | `type`, `key`, `hold_key` |
+| Applications | `open_application`, `switch_display` |
+| Clipboard | `read_clipboard`, `write_clipboard` |
+| Control flow | `wait`, `computer_batch` |
+| Teach | `request_teach_access`, `teach_step`, `teach_batch` |
-> Note: Since we replaced the native modules with Python bridge, the global Escape hotkey abort and auto-hide features from the original implementation are not available. Use `Ctrl+C` to abort instead.
+There are 24 base control tools. The three Teach tools are exposed only when the host enables the Teach capability; otherwise a session sees only the base tools.
----
+Teach is intended for requests where the user wants to learn a workflow:
-## Environment Variables
+1. `request_teach_access` requests access to the applications used by the tour.
+2. `teach_step` shows an anchored explanation and waits for the user to choose Next.
+3. `teach_batch` groups predictable steps to reduce model round trips.
-| Variable | Default | Description |
-|----------|---------|-------------|
-| `CLAUDE_COMPUTER_USE_ENABLED` | `1` | Set to `0` to disable Computer Use |
-| `CLAUDE_COMPUTER_USE_COORDINATE_MODE` | `pixels` | Coordinate mode: `pixels` or `normalized_0_100` |
-| `CLAUDE_COMPUTER_USE_CLIPBOARD_PASTE` | `1` | Enable clipboard-based text input |
-| `CLAUDE_COMPUTER_USE_MOUSE_ANIMATION` | `1` | Enable mouse animation |
-| `CLAUDE_COMPUTER_USE_DEBUG` | `0` | Debug mode |
+Teach authorization is separate from regular control authorization. Actions still pass the application allowlist and input safety checks. After the user exits a tour, the model must stop calling Teach tools.
----
+## How it works
-## Technical Architecture
+Computer Use follows a screenshot → analyze → act → screenshot loop:
-### Gate Bypass
+```text
+Model
+ → Computer Use MCP tool
+ → TypeScript dispatch and safety checks
+ → Python bridge
+ → macOS / Windows system operation
+ → Screenshot or action result returned to the model
+```
-The official Claude Code gates Computer Use behind three layers:
+- Tool definitions and authorization logic live in `src/vendor/computer-use-mcp/`.
+- CLI integration and the Python bridge live in `src/utils/computerUse/`.
+- Platform executors live in `runtime/mac_helper.py` and `runtime/win_helper.py`.
+- Desktop setup, permissions, and preauthorization are managed by `src/server/api/computer-use.ts` and `desktop/src/pages/ComputerUseSettings.tsx`.
-| Layer | Original Mechanism | Our Approach |
-|-------|-------------------|--------------|
-| Compile-time | `feature('CHICAGO_MCP')` (Bun macro) | Replaced with `true` |
-| Subscription | `hasRequiredSubscription()` (Max/Pro only) | `getChicagoEnabled()` returns `true` directly |
-| Remote config | GrowthBook `tengu_malort_pedway` | Same — no remote dependency |
-| Default-disabled | `isDefaultDisabledBuiltin('computer-use')` | Returns `false` |
+## Authorization model
-### Python Bridge
+### Application access
-On first invocation, the bridge automatically:
-1. Creates a Python virtual environment (`.runtime/venv/`)
-2. Installs pip
-3. Installs dependencies (`mss`, `Pillow`, `pyautogui`, `pyobjc-*`)
-4. Validates via SHA256 hash (only reinstalls when `requirements.txt` changes)
+The model first calls `request_access` with the required applications and a reason. The user can approve or deny the request. Applications preauthorized in Settings provide managed defaults; they do not grant arbitrary access to every application. Adding an application during a run still follows the relevant authorization flow.
----
+Applications use three permission tiers:
-## Approaches We Tried
+| Tier | Capability |
+|---|---|
+| `read` | Inspect screenshots without input |
+| `click` | Click, move, and scroll without typing or higher-risk input |
+| `full` | Allow keyboard, drag, and other complete input after the remaining checks pass |
-### Approach 1: Extract native .node modules from Claude Code binary ❌
+### Clipboard and system keys
-Extracted `computer-use-swift.node` and `computer-use-input.node` from the installed Claude Code Mach-O binary. Synchronous methods worked, but async Swift methods (screenshot) hung due to N-API async incompatibility between Bun versions.
+Clipboard read, clipboard write, and system-level key combinations are separate grants. Authorizing an application does not automatically authorize these capabilities.
-### Approach 2: Create empty stub packages ❌
+### Concurrency
-Stub packages allowed compilation but provided no actual functionality.
+A session lock prevents multiple sessions from competing for the mouse and keyboard. If Computer Use reports that another session owns the lock, finish or stop that session instead of deleting the lock file.
-### Approach 3: Python Bridge ✅ (current)
+## Platform safety boundaries
-Replaced all native module calls with Python subprocess calls via `callPythonHelper()`. Zero binary dependencies, auto-bootstrapping, full functionality on any macOS.
+### macOS
----
+- Accessibility is required for application input.
+- Screen Recording is required for screenshots.
+- Screenshots support native window filtering, leaving authorized applications and the desktop visible.
-## Known Limitations
+### Windows
-| Limitation | Description |
-|------------|-------------|
-| Linux not adapted | Linux needs `wmctrl` + `xdotool` style platform integration |
-| No global Escape abort | Original used CGEventTap; use `Ctrl+C` instead |
-| No auto-hide windows | Original's `prepareDisplay` relied on Swift |
-| Slightly higher latency | ~100ms Python process startup overhead per call |
+- The current screenshot filtering capability is `none`, so screenshots may include every visible window.
+- The application allowlist still rejects input aimed at an unauthorized frontmost application.
+- Because screenshot content is not filtered, close or minimize windows containing sensitive information before starting.
----
+### Protections that are not currently available
-## References and Credits
+- **There is no global Escape abort hotkey.** Use the current task's Stop action in the desktop app; a CLI run can be interrupted from its terminal.
+- **Unauthorized windows are not automatically hidden before every action.** Do not rely on auto-hide for privacy.
+- **Pixel staleness validation is disabled by default.** After the UI changes, the model should take another screenshot before clicking.
-| Project | License | Contribution |
-|---------|---------|-------------|
-| [wimi321/macos-computer-use-skill](https://github.com/wimi321/macos-computer-use-skill) | MIT | Python bridge architecture, `mac_helper.py` runtime, executor adaptation |
-| [domdomegg/computer-use-mcp](https://github.com/domdomegg/computer-use-mcp) | MIT | Independent Computer Use MCP server (nut.js based), used as reference |
-| [paoloanzn/free-code](https://github.com/paoloanzn/free-code) | - | Feature flag system analysis |
-| [oboard/claude-code-rev](https://github.com/oboard/claude-code-rev) | - | Early leaked source restoration, stub package reference |
+These are current implementation boundaries and must not be described as completed safeguards.
-### Underlying Libraries
+## Python runtime
-| Library | Purpose |
-|---------|---------|
-| [pyautogui](https://github.com/asweigart/pyautogui) | Mouse and keyboard control |
-| [mss](https://github.com/BoboTiG/python-mss) | Screenshot capture |
-| [Pillow](https://github.com/python-pillow/Pillow) | Image processing and compression |
-| [pyobjc](https://github.com/ronaldoussoren/pyobjc) | macOS Cocoa/Quartz framework bindings |
+During first install or repair, the app:
+
+1. Synchronizes the helper and requirements for the current platform into the managed user directory.
+2. Creates a venv with the detected or selected Python.
+3. Installs or upgrades pip.
+4. Uses the requirements content hash to decide whether dependencies need reinstalling.
+5. Calls the platform helper with a JSON payload and parses a uniform JSON response.
+
+macOS primarily uses `mss`, Pillow, PyAutoGUI, and PyObjC. Windows also uses pywin32, psutil, pyperclip, and screeninfo. See `runtime/requirements*.txt` for the current version constraints.
+
+## Troubleshooting
+
+### macOS still reports missing permissions
+
+- Make sure the authorization applies to the app that actually launches Claude Code Haha.
+- Fully quit and reopen the app after changing permissions.
+- Run the permission check again from Settings.
+
+### Python installation fails
+
+- Select a specific Python 3 executable in Settings.
+- Confirm that Python supports `venv`.
+- Run Install or Repair again.
+- Review the Computer Use installation log in Diagnostics.
+
+### Screenshots work but clicks fail
+
+- Confirm that the target application is authorized.
+- Confirm that it is the frontmost application.
+- Check whether the permission tier allows the requested action.
+- Take a new screenshot after any UI change instead of reusing old coordinates.
+
+### Other windows appear in Windows screenshots
+
+This is a known boundary of the current Windows screenshot capability. The input allowlist does not filter screenshot content. Close or minimize sensitive windows first.
+
+## Learn more
+
+- [Computer Use architecture](./computer-use-architecture.md)
diff --git a/docs/en/guide/cli-reference.md b/docs/en/guide/cli-reference.md
new file mode 100644
index 00000000..0333243e
--- /dev/null
+++ b/docs/en/guide/cli-reference.md
@@ -0,0 +1,162 @@
+# CLI Reference
+
+Claude Code Haha starts an interactive session by default. With `--print`, it can also run as a non-interactive agent in scripts, CI, or another program. The command in a source checkout is `./bin/claude-haha`; the installed executable name depends on the installation method.
+
+```bash
+./bin/claude-haha --help
+```
+
+`--help` is the source of truth for the installed version. This page explains common options by task instead of duplicating a complete help listing that would quickly become stale.
+
+## Quick examples
+
+```bash
+# Start an interactive session in the current directory
+./bin/claude-haha
+
+# Start with an initial request
+./bin/claude-haha "Explain how this repository starts"
+
+# Return text and exit
+./bin/claude-haha --print "Summarize the latest commit"
+
+# Return one complete JSON result
+./bin/claude-haha --print --output-format json "List the main modules"
+
+# Continue the latest session in this directory
+./bin/claude-haha --continue
+
+# Resume a session but fork it to a new session ID
+./bin/claude-haha --resume --fork-session
+
+# Work in a new Git worktree
+./bin/claude-haha --worktree docs-refresh
+```
+
+## Understand the `--print` trust boundary first
+
+`-p, --print` skips the workspace trust dialog. It can still load enabled user, project, and local settings. Hooks, MCP servers, tool permissions, and environment configuration in a trusted project can also affect execution.
+
+Consequently:
+
+- Run `--print`, `doctor`, and automation only in directories you trust.
+- Before processing an unfamiliar repository, inspect `.claude/`, `.mcp.json`, plugins, and workspace instructions.
+- `--dangerously-skip-permissions` bypasses every permission check. Use it only in a disposable, isolated sandbox with no network access.
+- `--allow-dangerously-skip-permissions` only makes bypass mode selectable; it does not enable bypass by itself.
+- In automation, explicitly limit tools, allowed directories, and budget.
+
+## Non-interactive input and output
+
+| Option | Description |
+|--------|-------------|
+| `-p, --print` | Process a request, write the result, and exit |
+| `--output-format text` | Plain text; the default |
+| `--output-format json` | One complete JSON result |
+| `--output-format stream-json` | A stream of JSON events |
+| `--input-format text` | Plain-text input; the default |
+| `--input-format stream-json` | Receive a stream of JSON events on standard input |
+| `--json-schema ''` | Require the final structured output to match a JSON Schema |
+| `--include-partial-messages` | Include incremental message chunks in `stream-json` |
+| `--include-hook-events` | Include hook lifecycle events in `stream-json` |
+| `--replay-user-messages` | Echo streamed user messages to standard output as acknowledgements |
+| `--no-session-persistence` | Do not save the non-interactive session |
+| `--max-budget-usd ` | Set the maximum API spend for `--print` |
+
+Structured output example:
+
+```bash
+./bin/claude-haha --print \
+ --output-format json \
+ --json-schema '{"type":"object","properties":{"risk":{"type":"string"}},"required":["risk"]}' \
+ "Review the current changes and return only the risk level"
+```
+
+Streaming output is an event protocol, not ordinary JSON split across lines. Consumers should dispatch on `type` and tolerate additional event fields.
+
+## Sessions and workspaces
+
+| Option | Description |
+|--------|-------------|
+| `-c, --continue` | Continue the latest session in the current directory |
+| `-r, --resume [value]` | Resume by session ID; omit the value for the picker, or pass a search term |
+| `--fork-session` | Create a new session ID when resuming |
+| `--session-id ` | Use a specific session ID |
+| `-n, --name ` | Set the name shown in the resume list and terminal title |
+| `--from-pr [value]` | Resume a session linked to a PR number or URL |
+| `--add-dir ` | Allow tools to access additional directories |
+| `-w, --worktree [name]` | Create a Git worktree for the session |
+| `--tmux` | Create a tmux session with `--worktree`; uses an iTerm2 pane when supported |
+
+`--worktree` changes the local Git workspace layout. Check repository status first and provide an explicit name in automation.
+
+## Models and context
+
+| Option | Description |
+|--------|-------------|
+| `--model ` | Select a model alias or complete model ID |
+| `--fallback-model ` | Use a fallback if the primary model is overloaded in `--print` mode |
+| `--effort ` | Set reasoning effort, such as `low`, `medium`, `high`, `xhigh`, or `max` |
+| `--agent ` | Use a configured agent |
+| `--agents ''` | Define custom agents for this session |
+| `--system-prompt ` | Replace the system prompt for this session |
+| `--append-system-prompt ` | Append text to the default system prompt |
+| `--file ` | Download file resources at startup as `file_id:relative_path` |
+| `--bare` | Start a minimal runtime without hooks, LSP, plugin sync, auto-memory, or other automatic discovery |
+
+`--bare` is not a less restricted mode. It reduces implicit context and credential sources, so the caller must explicitly provide prompts, directories, MCP configuration, settings, agents, or plugins.
+
+## Tools and permissions
+
+| Option | Description |
+|--------|-------------|
+| `--tools ` | Select built-in tools; `""` disables all tools and `default` uses the default set |
+| `--allowed-tools ` | Allow matching tools, for example `Bash(git:*) Edit` |
+| `--disallowed-tools ` | Deny matching tools |
+| `--permission-mode ` | Choose `default`, `acceptEdits`, `plan`, `dontAsk`, `bypassPermissions`, or `auto` |
+| `--no-computer-use` | Do not load the Computer Use MCP for this session |
+| `--chrome` / `--no-chrome` | Enable or disable Chrome integration |
+| `--ide` | Connect automatically when exactly one valid IDE is available |
+
+Deny rules and platform policy can still override allow rules. Production automation should select the smallest tool set instead of relying on approvals stored on a developer machine.
+
+## Settings, MCP, and plugins
+
+| Option | Description |
+|--------|-------------|
+| `--settings ` | Load an additional settings file or JSON string |
+| `--setting-sources ` | Select `user`, `project`, and `local` settings sources |
+| `--mcp-config ` | Load MCP servers from JSON files or JSON strings |
+| `--strict-mcp-config` | Ignore other MCP configuration and use only command-line entries |
+| `--plugin-dir ` | Load a plugin directory for this session; repeat the option for multiple paths |
+| `--disable-slash-commands` | Disable all skills |
+| `-d, --debug [filter]` | Enable debug logging with optional category filtering |
+| `--debug-file ` | Write debug logs to a file |
+
+`--mcp-debug` is deprecated; use `--debug`.
+
+## Subcommands
+
+| Subcommand | Purpose |
+|------------|---------|
+| `agents` | List configured agents |
+| `auth` | Manage authentication |
+| `auto-mode` | Inspect the Auto mode classifier configuration |
+| `doctor` | Check updater and runtime health |
+| `install` | Install a selected native-build channel or version |
+| `mcp` | Configure and manage MCP servers |
+| `plugin` / `plugins` | Manage plugins |
+| `setup-token` | Create a long-lived token for a Claude subscription |
+| `update` / `upgrade` | Check for and install updates |
+
+Each subcommand has separate help:
+
+```bash
+./bin/claude-haha mcp --help
+./bin/claude-haha plugin --help
+```
+
+## Interactive commands
+
+Enter `/help` in an interactive session to see the commands available in the current build. Commands such as `/commit` and `/review` come from the built-in command set. Skills and plugins can add commands dynamically, so do not rely on a fixed list in documentation.
+
+If a command is missing, trust the current `/help` output first and check whether `--disable-slash-commands` was used.
diff --git a/docs/en/guide/contributing.md b/docs/en/guide/contributing.md
index 98d4dd6b..bf937ef8 100644
--- a/docs/en/guide/contributing.md
+++ b/docs/en/guide/contributing.md
@@ -143,7 +143,7 @@ bun run check:native # Desktop sidecars, Electron host, and package-smoke c
bun run check:provider-contract # Offline provider/runtime/proxy contract tests
bun run check:chat-contract # WebSocket, session, and desktop chat-store contracts
bun run check:persistence-upgrade # Persistence migrations and old-fixture compatibility
-bun run check:docs # Docs build, using npm ci + docs:build
+bun run check:docs # Isolated install, build, and validation for the site/ React docs
bun run check:quarantine # Maintainer baseline/release quarantine audit
bun run check:coverage # Root, desktop, and adapter coverage reports plus ratchet enforcement
```
diff --git a/docs/en/guide/env-vars.md b/docs/en/guide/env-vars.md
index eace61d4..fb92d65e 100644
--- a/docs/en/guide/env-vars.md
+++ b/docs/en/guide/env-vars.md
@@ -1,61 +1,131 @@
# Environment Variables
+Claude Code Haha has two configuration paths:
+
+- Desktop users should select, test, and activate a provider under **Settings → Providers**. The app manages authentication, model mappings, and protocol translation.
+- When running the CLI from source, use a repository `.env`, shell variables, or Claude Code `settings.json`.
+
+Do not store the same API key in several places. When troubleshooting, first check whether a Desktop provider is active.
+
+## Common variables
+
+### Anthropic-compatible endpoints
| Variable | Required | Description |
-|------|------|------|
-| `ANTHROPIC_API_KEY` | One of two | API key sent via the `x-api-key` header |
-| `ANTHROPIC_AUTH_TOKEN` | One of two | Auth token sent via the `Authorization: Bearer` header |
-| `ANTHROPIC_BASE_URL` | No | Custom API endpoint, defaults to Anthropic |
-| `ANTHROPIC_MODEL` | No | Default model |
-| `ANTHROPIC_DEFAULT_SONNET_MODEL` | No | Sonnet-tier model mapping |
-| `ANTHROPIC_DEFAULT_HAIKU_MODEL` | No | Haiku-tier model mapping |
-| `ANTHROPIC_DEFAULT_OPUS_MODEL` | No | Opus-tier model mapping |
-| `API_TIMEOUT_MS` | No | API request timeout, default `600000` (10min) |
-| `DISABLE_TELEMETRY` | No | Set to `1` to disable telemetry |
-| `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` | No | Set to `1` to disable non-essential network traffic |
+|----------|----------|-------------|
+| `ANTHROPIC_API_KEY` | Choose one authentication variable | Sent in the `x-api-key` request header |
+| `ANTHROPIC_AUTH_TOKEN` | Choose one authentication variable | Sent as `Authorization: Bearer` |
+| `ANTHROPIC_BASE_URL` | No | Base URL of an Anthropic Messages-compatible endpoint |
+| `ANTHROPIC_MODEL` | No | Default model for the session |
+| `ANTHROPIC_DEFAULT_FABLE_MODEL` | No | Fable model slot; configure it only when the provider supports it |
+| `ANTHROPIC_DEFAULT_HAIKU_MODEL` | No | Haiku model slot |
+| `ANTHROPIC_DEFAULT_SONNET_MODEL` | No | Sonnet model slot |
+| `ANTHROPIC_DEFAULT_OPUS_MODEL` | No | Opus model slot |
+| `API_TIMEOUT_MS` | No | API request timeout in milliseconds; defaults to `600000` |
-## Configuration Methods
+The correct authentication variable depends on the header required by the service. Do not infer it from the provider name alone. For a `401` response, check the provider documentation and the authentication strategy selected in Desktop.
-### Option 1: `.env` File
+### Azure OpenAI
+
+Azure OpenAI uses a dedicated Responses API path:
+
+| Variable | Required | Description |
+|----------|----------|-------------|
+| `CLAUDE_CODE_USE_AZURE_OPENAI` | Yes | Set to `1` to enable Azure OpenAI |
+| `AZURE_OPENAI_BASE_URL` | Yes | Azure resource base URL; `AZURE_OPENAI_ENDPOINT` is also accepted |
+| `AZURE_OPENAI_API_VERSION` | No | API version; defaults to `2025-04-01-preview` |
+| `AZURE_OPENAI_API_KEY` | Yes | Azure OpenAI API key |
+| `AZURE_OPENAI_CODEX_DEPLOYMENT` | Model-dependent | Azure deployment name for Codex models |
+
+Example:
+
+```bash
+CLAUDE_CODE_USE_AZURE_OPENAI=1
+AZURE_OPENAI_BASE_URL=https://your-resource.cognitiveservices.azure.com
+AZURE_OPENAI_API_VERSION=2025-04-01-preview
+AZURE_OPENAI_API_KEY=your_azure_openai_key
+AZURE_OPENAI_CODEX_DEPLOYMENT=your_codex_deployment
+```
+
+### Local runtime and privacy
+
+| Variable | Description |
+|----------|-------------|
+| `CLAUDE_CONFIG_DIR` | Use a specific configuration directory instead of `~/.claude`; useful for portable mode and isolated tests |
+| `CLAUDE_CODE_FORCE_RECOVERY_CLI` | Set to `1` to use the simplified Recovery CLI |
+| `CLAUDE_CODE_SHELL_PREFIX` | Prefix Bash tool commands, for example `wsl -e bash -lc` on Windows |
+| `DISABLE_TELEMETRY` | Set to `1` to disable telemetry |
+| `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` | Set to `1` to disable non-essential network requests |
+
+See [Local Server](../reference/local-server.md) for `SERVER_HOST`, `SERVER_PORT`, `SERVER_AUTH_REQUIRED`, and related variables.
+
+## Configuration methods
+
+### Desktop providers
+
+Desktop stores the provider index at:
+
+```text
+~/.claude/cc-haha/providers.json
+```
+
+Provider-managed environment data is written to an isolated Haha configuration. You do not need to copy it into `~/.claude/settings.json`. When the CLI finds an active provider, it reuses its credentials, models, and protocol settings. Providers using `openai_chat` or `openai_responses` automatically use a loopback proxy.
+
+See [Third-Party Models](./third-party-models.md) for the setup flow.
+
+### Repository `.env`
+
+The source `bin/claude-haha` launcher loads a `.env` file from the repository root when it exists:
```bash
cp .env.example .env
```
-Edit `.env` (the example below uses [MiniMax](https://platform.minimaxi.com/subscribe/token-plan?code=1TG2Cseab2&source=link) as the API provider — you can replace it with any compatible service):
+Minimal example for an Anthropic-compatible endpoint:
```bash
-# API authentication (choose one)
-ANTHROPIC_API_KEY=sk-xxx # Standard API key via x-api-key header
-ANTHROPIC_AUTH_TOKEN=sk-xxx # Bearer token via Authorization header
-
-# API endpoint (optional, defaults to Anthropic)
-ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic
-
-# Model configuration
-ANTHROPIC_MODEL=MiniMax-M3
-ANTHROPIC_DEFAULT_SONNET_MODEL=MiniMax-M3
-ANTHROPIC_DEFAULT_HAIKU_MODEL=MiniMax-M2.7-highspeed
-ANTHROPIC_DEFAULT_OPUS_MODEL=MiniMax-M3
-
-# Timeout in milliseconds
-API_TIMEOUT_MS=3000000
-
-# Disable telemetry and non-essential network traffic
-DISABLE_TELEMETRY=1
-CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
+ANTHROPIC_AUTH_TOKEN=sk-example
+ANTHROPIC_BASE_URL=https://provider.example.com/anthropic
+ANTHROPIC_MODEL=provider-model
+ANTHROPIC_DEFAULT_HAIKU_MODEL=provider-model
+ANTHROPIC_DEFAULT_SONNET_MODEL=provider-model
+ANTHROPIC_DEFAULT_OPUS_MODEL=provider-model
```
-### Option 2: `~/.claude/settings.json`
+The repository `.env` is only for source launches. CLI processes created by Desktop skip it so a stale key cannot replace the active provider.
+
+### `settings.json`
+
+User settings live at `~/.claude/settings.json`:
```json
{
"env": {
- "ANTHROPIC_AUTH_TOKEN": "sk-xxx",
- "ANTHROPIC_BASE_URL": "https://api.minimaxi.com/anthropic",
- "ANTHROPIC_MODEL": "MiniMax-M3"
+ "ANTHROPIC_AUTH_TOKEN": "sk-example",
+ "ANTHROPIC_BASE_URL": "https://provider.example.com/anthropic",
+ "ANTHROPIC_MODEL": "provider-model"
}
}
```
-> Priority: Environment variables > `.env` file > `~/.claude/settings.json`
+A project can also contain `.claude/settings.json` or `.claude/settings.local.json`. These files are workspace input. Use them only in trusted projects, especially when they define `PATH`, `LD_PRELOAD`, proxy endpoints, or authentication variables.
+
+## Effective precedence
+
+There is no reliable three-step rule such as “shell > `.env` > settings”:
+
+1. `bin/claude-haha` first lets Bun load the repository `.env`.
+2. CLI initialization merges enabled user, project, local, command-line, and managed setting sources.
+3. An active Haha provider overrides provider-routing values from ordinary Claude settings so the two clients do not contaminate each other.
+4. Runtime values injected by the Desktop host are protected from same-name fields in `settings.json`.
+5. Enterprise policy and `--setting-sources` can also change the effective result.
+
+Keep one primary provider configuration source. Desktop users should use the Providers page; CLI-only users should choose either `.env` or user-level `settings.json`.
+
+## Security guidance
+
+- Never commit `.env`, provider configuration, or a `settings.json` containing secrets.
+- Do not expose full tokens in screenshots, issues, logs, or diagnostic archives.
+- Use `CLAUDE_CONFIG_DIR` to isolate tests from real user configuration.
+- `--print` skips the workspace trust dialog and must only run in trusted directories. See [CLI Reference](./cli-reference.md).
+- Do not treat CORS as authentication when exposing the local server. Enable an H5 token or explicit authentication.
diff --git a/docs/en/guide/faq.md b/docs/en/guide/faq.md
index 46be2889..c50e86bf 100644
--- a/docs/en/guide/faq.md
+++ b/docs/en/guide/faq.md
@@ -1,34 +1,153 @@
-# FAQ
+# FAQ and Support
+## What is the fastest way to get help?
-## Q: `undefined is not an object (evaluating 'usage.input_tokens')`
+First, confirm that you are using [GitHub Releases Latest](https://github.com/NanmiCoder/cc-haha/releases/latest), then retry the shortest sequence that reproduces the problem reliably.
-**Cause**: `ANTHROPIC_BASE_URL` is misconfigured. The API endpoint is returning HTML or another non-JSON format instead of a valid Anthropic protocol response.
+If the Desktop app still opens:
-This project uses the **Anthropic Messages API protocol**. `ANTHROPIC_BASE_URL` must point to an endpoint compatible with Anthropic's `/v1/messages` interface. The Anthropic SDK automatically appends `/v1/messages` to the base URL, so:
+1. Go to **Settings → Diagnostics**.
+2. Select **Copy Issue Report**.
+3. Search [GitHub Issues](https://github.com/NanmiCoder/cc-haha/issues) for the same problem, and create a new issue only if there is no duplicate.
+4. Paste the report and add the reproduction steps, expected result, and actual result.
-- MiniMax: `ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic` ✅
-- OpenRouter: `ANTHROPIC_BASE_URL=https://openrouter.ai/api` ✅
-- OpenRouter (wrong): `ANTHROPIC_BASE_URL=https://openrouter.ai/anthropic` ❌ (returns HTML)
+If the report is not enough, you can also select **Export Bundle**. Issue reports and bundles attempt to omit chat content, file content, complete environment variables, and API keys. They can still contain private metadata such as local paths or provider hostnames, so **review everything before sharing it**.
-If your model provider only supports the OpenAI protocol, you need a proxy like LiteLLM for protocol translation. See the [Third-Party Models Guide](./third-party-models.md).
+Include:
-## Q: `Cannot find package 'bundle'`
+- The Claude Code Haha version
+- Operating system, CPU architecture, and installer type
+- Provider type and API format, but never the API key
+- The shortest reproduction steps and complete error text
+- Whether the problem occurs in Desktop, H5, or the CLI
-```
-error: Cannot find package 'bundle' from '.../claude-code-haha/src/entrypoints/cli.tsx'
-```
+If the app cannot start, provide the installer version, a screenshot of the system error, and the last visible action before the failure. Do not delete or overwrite `~/.claude` as a troubleshooting shortcut.
-**Cause**: Your Bun version is too old and doesn't support the required `bun:bundle` built-in module.
+## Providers and OAuth
-**Fix**: Upgrade Bun to the latest version:
+### Do OpenAI-compatible services always require LiteLLM?
+
+No. A Custom Provider in the Desktop app supports:
+
+- Anthropic Messages (native)
+- OpenAI Chat Completions (local proxy translation)
+- OpenAI Responses API (local proxy translation)
+
+Consider an external gateway such as LiteLLM only when the upstream service uses a protocol or custom fields that the app does not support.
+
+### A custom provider returns 401 or “API Key invalid”
+
+Edit the provider under **Settings → Providers**, then check:
+
+1. The base URL is the API root required by the provider, without a missing or duplicated path.
+2. The API format matches the real upstream endpoint.
+3. The authentication strategy is correct. Some Anthropic-compatible services use a Bearer token, while the official Anthropic API uses `x-api-key`.
+4. The main model ID exists and the current credential can access it.
+5. Select **Test Connection**. Resolve the first connectivity failure before the second proxy-translation result shown for OpenAI formats.
+
+Do not expose URL query credentials, tokens, or API keys in screenshots, issues, or diagnostic attachments.
+
+### Claude, ChatGPT, or Grok sign-in does not complete
+
+- Keep Claude Code Haha running while the sign-in flow is open.
+- Allow the system browser to open the authorization page and complete authorization with the intended account.
+- Confirm that the system clock is correct and that a proxy, firewall, or browser extension is not blocking the provider page or local OAuth callback.
+- If the browser reports success but the app does not update, return to **Settings → Providers** and start the sign-in flow again.
+
+If it still fails, copy an Issue Report and state whether the browser did not open, the authorization page failed, or the callback did not return to the app. Never share an OAuth code, access token, or browser cookie.
+
+### The connection test succeeds, but chat still fails
+
+Confirm that the current task selected the new Provider and model; saving a Provider does not necessarily select it for every task. Also check that the account supports the configured main and role-model mappings.
+
+Create a new task and retry with a short plain-text message. If it still fails, copy an Issue Report from Diagnostics. A successful connection test proves basic endpoint, authentication, and translation behavior; it does not verify every model capability, tool call, or long-context combination.
+
+## Installation and Updates
+
+### macOS says the app cannot be verified, is damaged, or will not open
+
+Confirm that the installer came from the [official Latest Release](https://github.com/NanmiCoder/cc-haha/releases/latest) and that you selected the correct Intel or Apple Silicon architecture. A signed and notarized public release should normally show only the standard download-source confirmation.
+
+Older, draft, or temporary unsigned builds may need extra approval. See the [Desktop installation guide](/en/desktop/04-installation#macos). Do not bypass system security prompts for packages from unknown mirrors.
+
+### Windows shows SmartScreen
+
+Confirm that the installer came from the official Release. An unsigned Windows installer may trigger SmartScreen. Expand “More info,” verify the file name and source, and then decide whether to run it.
+
+Quit Claude Code Haha completely before an in-place update. If the installer reports that a process is still running, close the relevant window and background process before retrying. Do not delete the user configuration directory first.
+
+### A Linux AppImage does not start
+
+Make it executable:
```bash
-bun upgrade
+chmod +x Claude-Code-Haha--linux-.AppImage
```
-## Q: How to use OpenAI / DeepSeek / Ollama or other non-Anthropic models?
+Some distributions also require FUSE. See the [Desktop installation guide](/en/desktop/04-installation#linux) for distribution-specific instructions.
-This project only supports the Anthropic protocol. If your model provider doesn't natively support the Anthropic protocol, you need a proxy like [LiteLLM](https://github.com/BerriAI/litellm) for protocol translation (OpenAI → Anthropic).
+## H5 Access
-See the [Third-Party Models Guide](./third-party-models.md) for detailed setup instructions.
+### A phone or another computer cannot connect to H5
+
+Check each item in order:
+
+1. **H5 Access** is enabled in the Desktop app.
+2. You are using the Server URL currently shown in Settings or encoded in the current QR code, not an old LAN address.
+3. The client has the current H5 token.
+4. Both devices are on networks that can reach each other, and the system firewall allows the configured port.
+5. A reverse proxy forwards both HTTP and WebSocket traffic and uses an allowed origin.
+
+H5 is a remote entry point to the current Desktop service, not the complete Desktop environment. Terminals, native previews, pet windows, and some system capabilities remain Desktop-only.
+
+See [H5 access](/en/desktop/06-h5-access) for deployment and security details.
+
+## Git Branches and Worktrees
+
+### Creating an isolated worktree fails
+
+Common causes include:
+
+- The selected directory is not a Git repository
+- The branch does not exist or is already checked out by another worktree
+- Uncommitted changes prevent the requested Git operation from running safely
+- The target worktree path already exists or is not writable
+
+Read the exact error shown in the app. You can use the current working tree, select a different branch, or handle the existing Git changes safely before retrying. Do not automatically delete an existing directory or discard uncommitted changes to make a worktree.
+
+### Should I use the current working tree or an isolated worktree?
+
+- **Current working tree**: continue work that already exists in the selected directory.
+- **Isolated worktree**: run a parallel task or keep a branch separate from the current checkout.
+
+If the current directory contains important uncommitted changes, inspect the Git status and make a backup before choosing.
+
+## Computer Use
+
+### Computer Use is unavailable or cannot control an app
+
+Open **Settings → Computer Use** and confirm:
+
+- The global feature switch is enabled
+- Python and dependency checks pass
+- Required macOS or Windows system permissions are granted
+- The target app is in the list of apps allowed for control
+- The current session's permission request was explicitly approved
+
+After granting a system permission, reopen Claude Code Haha or the target app. Computer Use is not currently supported on Linux. A successful Desktop installation does not mean that Computer Use is fully configured.
+
+If Settings still shows an error, copy an Issue Report and include the status shown on that page. Avoid uploading a full-screen screenshot that exposes content from other apps.
+
+## CLI
+
+### `bun install` or the CLI fails to start
+
+Confirm that the shell is in the repository root and that Bun is current enough for the project:
+
+```bash
+bun --version
+bun install
+./bin/claude-haha --help
+```
+
+If the error mentions a missing Bun built-in such as `bun:bundle`, upgrade Bun. See [Get Started in 3 Minutes](./quick-start.md#option-2-run-the-cli-from-source) for installation and the [CLI reference](./cli-reference.md) for commands.
diff --git a/docs/en/guide/quick-start.md b/docs/en/guide/quick-start.md
index 109e24fb..e092906a 100644
--- a/docs/en/guide/quick-start.md
+++ b/docs/en/guide/quick-start.md
@@ -1,64 +1,109 @@
-# Quick Start
+# Get Started in 3 Minutes
-## 1. Install Bun
+Claude Code Haha has two ways to run. Install the **Desktop app** for everyday development. Run the **CLI from source** only when you need a terminal workflow, scripting, or local development.
-```bash
-# macOS / Linux
-curl -fsSL https://bun.sh/install | bash
+| Option | Best for | What you need |
+|--------|----------|---------------|
+| **Desktop (recommended)** | Managing projects, sessions, worktrees, code diffs, and permission reviews | The installer for your platform; **Bun is not required** |
+| **CLI** | Terminal users, `--print` automation, and contributors | Git, Bun, and access to a model provider |
-# macOS (Homebrew)
-brew install bun
+## Option 1: Install the Desktop App
-# Windows (PowerShell)
-powershell -c "irm bun.sh/install.ps1 | iex"
+### 1. Download the current stable release
+
+Open [GitHub Releases Latest](https://github.com/NanmiCoder/cc-haha/releases/latest), then select the installer for your operating system and CPU:
+
+- macOS on Apple Silicon (M-series): `mac-arm64.dmg`
+- macOS on Intel: `mac-x64.dmg`
+- Windows: `win-x64.exe` or `win-arm64.exe`
+- Linux: the matching `.AppImage` or `.deb`
+
+See the [Desktop installation guide](/en/desktop/04-installation) for platform-specific installation prompts.
+
+### 2. Configure a model provider
+
+Launch the app and open **Settings → Providers**.
+
+The shortest path is to select an official option and follow its on-screen connection or sign-in flow:
+
+- **Claude Official**
+- **ChatGPT Official**: sign in with a ChatGPT account through OAuth
+- **Grok Official**: sign in with an xAI account through OAuth
+
+These official options do not require you to paste an API key. You can also select **Add Provider**, choose a built-in preset or Custom, and enter the API key, base URL, API format, and model mapping.
+
+Before saving a custom provider, select **Test Connection**. When the test succeeds, make the provider the default. The app can translate OpenAI Chat Completions and OpenAI Responses requests through its built-in local proxy, so LiteLLM is not required by default.
+
+See [Third-party models and custom providers](./third-party-models.md) for configuration details.
+
+### 3. Create your first task
+
+Select `+` in the sidebar:
+
+1. Choose a local project directory.
+2. If it is a Git repository, choose the branch to use.
+3. Decide whether to use the current working tree or create an isolated worktree.
+
+The current working tree shares any uncommitted changes already in that directory. An isolated worktree is better for parallel tasks or changes that should not affect your current checkout.
+
+### 4. Confirm the model and permissions
+
+Before sending the first message, confirm the Provider, model, and effort. For a first run, keep **Default** permission mode: the app will ask before sensitive tools or commands run.
+
+Use automatic approval or bypass modes only when you understand their impact. See [Desktop quick start](/en/desktop/01-quick-start#5-choose-a-permission-mode) for the permission modes.
+
+### 5. Send the first message
+
+Start with a small, verifiable request:
+
+```text
+Inspect this project in read-only mode. Explain how to start it and what its main directories do. Do not modify files.
```
-> On minimal Linux images, if you see `unzip is required`, run `apt update && apt install -y unzip` first.
+If you see a streaming response, tool calls, and permission requests, the Desktop app, provider, and project directory are connected.
-## 2. Install Dependencies and Configure
+## Option 2: Run the CLI from Source
+
+### 1. Clone the repository and install Bun
+
+Install [Git](https://git-scm.com/downloads) and [Bun](https://bun.sh), then run:
```bash
+git clone https://github.com/NanmiCoder/cc-haha.git
+cd cc-haha
bun install
-cp .env.example .env
-# Edit .env with your API key
```
-See [Environment Variables](./env-vars.md) for the full reference.
-
-## 3. Start
-
-### macOS / Linux
+### 2. Configure a model provider
```bash
-./bin/claude-haha # Interactive TUI mode
-./bin/claude-haha -p "your prompt here" # Headless mode
-./bin/claude-haha --help # Show all options
+cp .env.example .env
```
-### Windows
+Edit `.env` with at least one valid authentication method, base URL, and model. See [Environment variables](./env-vars.md) for the variable definitions and authentication-header differences.
-> **Prerequisite**: [Git for Windows](https://git-scm.com/download/win) must be installed.
+Never commit a real API key to Git or expose it in an issue, screenshot, or diagnostic attachment.
+
+### 3. Start and verify
+
+On macOS, Linux, or Git Bash:
+
+```bash
+./bin/claude-haha
+./bin/claude-haha -p "Summarize the current project structure"
+```
+
+On Windows PowerShell or Command Prompt:
```powershell
-# PowerShell / cmd — call Bun directly
bun --env-file=.env ./src/entrypoints/cli.tsx
-
-# Or run inside Git Bash
-./bin/claude-haha
```
-## 4. Global Usage (Optional)
+See the [CLI reference](./cli-reference.md) for command options, headless mode, recovery mode, and global usage.
-Add `bin/` to your PATH to run from any directory. See [Global Usage Guide](./global-usage.md):
+## Next Steps
-```bash
-export PATH="$HOME/path/to/claude-code-haha/bin:$PATH"
-```
-
-## 5. Recovery Mode
-
-If the Ink TUI has issues, use the fallback Recovery CLI mode:
-
-```bash
-CLAUDE_CODE_FORCE_RECOVERY_CLI=1 ./bin/claude-haha
-```
+- [Desktop quick start](/en/desktop/01-quick-start): sessions, permissions, attachments, and workspace actions
+- [Third-party models and custom providers](./third-party-models.md): providers, API formats, and model mapping
+- [FAQ](./faq.md): installation, OAuth, H5, worktree, and Computer Use troubleshooting
+- [Global usage](./global-usage.md): start the CLI from any directory
diff --git a/docs/en/guide/third-party-models.md b/docs/en/guide/third-party-models.md
index 90134b0a..5bbf5251 100644
--- a/docs/en/guide/third-party-models.md
+++ b/docs/en/guide/third-party-models.md
@@ -1,265 +1,98 @@
-# Using Third-Party Models (OpenAI / DeepSeek / Local Models)
+# Third-Party Models
-This project communicates with LLMs via the Anthropic protocol. By using a protocol translation proxy, you can use any model including OpenAI, DeepSeek, Ollama, etc.
+Claude Code Haha can connect to Anthropic Messages, OpenAI Chat Completions, and OpenAI Responses APIs. For Desktop users, the reliable entry point is **Settings → Providers**, not a hand-written environment file. The app stores authentication and model mappings and starts a local protocol proxy when one is required.
-## How It Works
+## Recommended setup
-```
-claude-code-haha ──Anthropic protocol──▶ LiteLLM Proxy ──OpenAI protocol──▶ Target Model API
- (translation)
-```
+1. Open **Settings → Providers** in Desktop.
+2. Choose a built-in preset or create a custom provider.
+3. Enter the base URL, authentication details, and protocol format.
+4. Configure at least the primary model. Add Haiku, Sonnet, and Opus mappings when the service uses different model IDs.
+5. Test the provider before activating it.
+6. Start a new session and verify a tool call. A text-only response does not prove that the full agent workflow works.
-This project sends Anthropic Messages API requests. The LiteLLM proxy automatically translates them to OpenAI Chat Completions API format and forwards them to the target model.
+After activation, Desktop and the CLI processes it launches reuse the same provider configuration. Do not keep a second, stale key in `.env` or `~/.claude/settings.json`.
----
+## Choose the correct protocol
-## Option 1: LiteLLM Proxy (Recommended)
+| Provider format | Upstream API | Use it when |
+|-----------------|--------------|-------------|
+| `anthropic` | Anthropic Messages | The service natively accepts Anthropic requests and responses |
+| `openai_chat` | `/v1/chat/completions` | The service implements OpenAI Chat Completions |
+| `openai_responses` | `/v1/responses` | The service implements OpenAI Responses |
-[LiteLLM](https://github.com/BerriAI/litellm) is a unified proxy gateway supporting 100+ LLMs (41k+ GitHub Stars), with native support for receiving Anthropic protocol requests.
+The `anthropic` format calls the service directly without changing the protocol. For `openai_chat` and `openai_responses`, Claude Code Haha starts a loopback proxy that translates Claude agent traffic into the selected protocol. This proxy belongs to the local runtime and does not need to be exposed to a LAN.
-### 1. Install LiteLLM
-
-```bash
-pip install 'litellm[proxy]'
-```
-
-### 2. Create Configuration File
-
-Create `litellm_config.yaml`:
-
-#### Using OpenAI Models
-
-```yaml
-model_list:
- - model_name: gpt-4o
- litellm_params:
- model: openai/gpt-4o
- api_key: os.environ/OPENAI_API_KEY
-
-litellm_settings:
- drop_params: true # Drop Anthropic-specific params (thinking, etc.)
-```
-
-#### Using DeepSeek Models
-
-```yaml
-model_list:
- - model_name: deepseek-chat
- litellm_params:
- model: deepseek/deepseek-chat
- api_key: os.environ/DEEPSEEK_API_KEY
- api_base: https://api.deepseek.com
-
-litellm_settings:
- drop_params: true
-```
-
-#### Using Ollama Local Models
-
-```yaml
-model_list:
- - model_name: llama3
- litellm_params:
- model: ollama/llama3
- api_base: http://localhost:11434
-
-litellm_settings:
- drop_params: true
-```
-
-#### Using Multiple Models (switchable after startup)
-
-```yaml
-model_list:
- - model_name: gpt-4o
- litellm_params:
- model: openai/gpt-4o
- api_key: os.environ/OPENAI_API_KEY
-
- - model_name: deepseek-chat
- litellm_params:
- model: deepseek/deepseek-chat
- api_key: os.environ/DEEPSEEK_API_KEY
- api_base: https://api.deepseek.com
-
- - model_name: llama3
- litellm_params:
- model: ollama/llama3
- api_base: http://localhost:11434
-
-litellm_settings:
- drop_params: true
-```
-
-### 3. Start the Proxy
-
-```bash
-# Set your target model's API key
-export OPENAI_API_KEY=sk-xxx
-# or
-export DEEPSEEK_API_KEY=sk-xxx
-
-# Start the proxy
-litellm --config litellm_config.yaml --port 4000
-```
-
-The proxy will listen on `http://localhost:4000` and expose an Anthropic-compatible `/v1/messages` endpoint.
-
-### 4. Configure This Project
-
-Choose one of two configuration methods:
-
-#### Method A: Via `.env` File
-
-```bash
-ANTHROPIC_AUTH_TOKEN=sk-anything
-ANTHROPIC_BASE_URL=http://localhost:4000
-ANTHROPIC_MODEL=gpt-4o
-ANTHROPIC_DEFAULT_SONNET_MODEL=gpt-4o
-ANTHROPIC_DEFAULT_HAIKU_MODEL=gpt-4o
-ANTHROPIC_DEFAULT_OPUS_MODEL=gpt-4o
-API_TIMEOUT_MS=3000000
-DISABLE_TELEMETRY=1
-CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
-```
-
-#### Method B: Via `~/.claude/settings.json`
-
-```json
-{
- "env": {
- "ANTHROPIC_AUTH_TOKEN": "sk-anything",
- "ANTHROPIC_BASE_URL": "http://localhost:4000",
- "ANTHROPIC_MODEL": "gpt-4o",
- "ANTHROPIC_DEFAULT_SONNET_MODEL": "gpt-4o",
- "ANTHROPIC_DEFAULT_HAIKU_MODEL": "gpt-4o",
- "ANTHROPIC_DEFAULT_OPUS_MODEL": "gpt-4o",
- "API_TIMEOUT_MS": "3000000",
- "DISABLE_TELEMETRY": "1",
- "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
- }
-}
-```
-
-> **Note**: The `ANTHROPIC_AUTH_TOKEN` value can be any string when using the LiteLLM proxy (LiteLLM uses its own configured key for forwarding), unless you've set a `master_key` on the LiteLLM side.
-
-### 5. Start and Verify
+If a provider advertises both “OpenAI compatible” and “Anthropic compatible,” choose the API that it implements completely and that handles tool calls reliably. Do not infer the protocol only from a `/v1` path.
+
+## Authentication and model mappings
+
+The service determines the authentication header:
+
+- Anthropic API keys usually use `x-api-key`.
+- Bearer tokens usually use `Authorization: Bearer`.
+- OpenAI-compatible services usually use bearer tokens, but private gateways can differ.
+
+Model slots are logical tiers requested by the Claude agent, not additional downloads. The primary model must support tool use. Other slots can map to that same model or to separate models chosen for cost and capability. Leave a slot empty when the provider does not support it.
+
+Model names and provider capabilities change frequently, so this page intentionally does not maintain a soon-to-be-stale model catalog. Use the current model ID reported by the provider and confirm it with the test action in Settings.
+
+## Built-in runtimes
+
+Claude Code Haha also includes Claude, OpenAI, and Grok runtimes. Available sign-in methods depend on the current build and local account state; follow the authorization flow shown on the Providers page.
+
+Built-in runtimes and custom compatible endpoints are separate paths. Prefer a built-in runtime for an existing official account. Create a custom provider for a relay service, private gateway, or local model server.
+
+## CLI-only configuration
+
+CLI-only users can configure an Anthropic Messages-compatible endpoint directly:
```bash
+ANTHROPIC_AUTH_TOKEN=sk-example
+ANTHROPIC_BASE_URL=https://provider.example.com/anthropic
+ANTHROPIC_MODEL=provider-model
./bin/claude-haha
```
-If everything is configured correctly, you should see the normal chat interface, with your configured target model handling the requests.
+This path does not translate Anthropic requests into an OpenAI protocol. Configure OpenAI Chat Completions or Responses services as Desktop providers so the app can manage the protocol proxy. See [Environment Variables](./env-vars.md) for the complete variables and effective precedence.
----
+Azure OpenAI uses the project's dedicated Responses path. See [Azure OpenAI environment variables](./env-vars.md#azure-openai).
-## Option 2: Direct Connection to Anthropic-Compatible Services
+## LiteLLM: an advanced compatibility layer
-Some third-party services directly support the Anthropic Messages API, no proxy needed:
+Add LiteLLM only when the service has no reliable Anthropic endpoint and the built-in provider translation is not suitable. It introduces another service, another protocol conversion, and another troubleshooting boundary.
-### OpenRouter
-
-```bash
-ANTHROPIC_AUTH_TOKEN=sk-or-v1-xxx
-ANTHROPIC_BASE_URL=https://openrouter.ai/api/v1
-ANTHROPIC_MODEL=openai/gpt-4o
-ANTHROPIC_DEFAULT_SONNET_MODEL=openai/gpt-4o
-ANTHROPIC_DEFAULT_HAIKU_MODEL=openai/gpt-4o-mini
-ANTHROPIC_DEFAULT_OPUS_MODEL=openai/gpt-4o
-DISABLE_TELEMETRY=1
-CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
-```
-
-### MiniMax (pre-configured in .env.example)
-
-MiniMax provides an Anthropic-compatible API endpoint and can be connected directly without any proxy. Available models:
-
-| Model | Description |
-|-------|-------------|
-| `MiniMax-M3` | Default recommended, latest generation with excellent overall performance and 1M context |
-| `MiniMax-M2.7` | Previous stable release |
-| `MiniMax-M2.7-highspeed` | Faster responses, suitable for latency-sensitive use cases |
-
-```bash
-ANTHROPIC_AUTH_TOKEN=your_minimax_api_key_here
-# International users: api.minimax.io; China users may use api.minimaxi.com
-ANTHROPIC_BASE_URL=https://api.minimax.io/anthropic
-ANTHROPIC_MODEL=MiniMax-M3
-ANTHROPIC_DEFAULT_SONNET_MODEL=MiniMax-M3
-ANTHROPIC_DEFAULT_HAIKU_MODEL=MiniMax-M2.7-highspeed
-ANTHROPIC_DEFAULT_OPUS_MODEL=MiniMax-M3
-API_TIMEOUT_MS=3000000
-DISABLE_TELEMETRY=1
-CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
-```
-
-> **Get API Key**: Visit [MiniMax Open Platform](https://platform.minimax.io) to register and obtain an API key.
-
----
-
-## Option 3: Other Proxy Tools
-
-The community has built several proxy tools specifically for Claude Code:
-
-| Tool | Description | Link |
-|------|-------------|------|
-| **a2o** | Anthropic → OpenAI single binary, zero dependencies | [Twitter](https://x.com/mantou543/status/2018846154855940200) |
-| **Empero Proxy** | Full Anthropic Messages API to OpenAI translation | [Twitter](https://x.com/EmperoAI/status/2036840854065762551) |
-| **Alma** | Client with built-in OpenAI → Anthropic proxy | [Twitter](https://x.com/yetone/status/2003508782127833332) |
-| **Chutes** | Docker container supporting 60+ open-source models | [Twitter](https://x.com/chutes_ai/status/2027039742915662232) |
-
----
-
-## Known Limitations
-
-### 1. `drop_params: true` Is Essential
-
-This project sends Anthropic-specific parameters (e.g., `thinking`, `cache_control`) that don't exist in the OpenAI API. You must set `drop_params: true` in the LiteLLM config, otherwise requests will fail.
-
-### 2. Extended Thinking Unavailable
-
-Anthropic's Extended Thinking is a proprietary feature not supported by other models. It is automatically disabled when using third-party models.
-
-### 3. Prompt Caching Unavailable
-
-`cache_control` is an Anthropic-specific feature. Prompt caching won't work with third-party models (but won't cause errors — it's silently ignored by `drop_params`).
-
-### 4. Tool Calling Compatibility
-
-This project heavily uses tool calling (tool_use). LiteLLM automatically translates Anthropic's tool_use format to OpenAI's function_calling format. This works in most cases, but some complex tool calls may have compatibility issues. If you encounter problems, try using a more capable model (e.g., GPT-4o).
-
-### 5. Telemetry and Non-Essential Requests
-
-Configure these environment variables to avoid unnecessary network requests:
-```
-DISABLE_TELEMETRY=1
-CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
-```
-
----
-
-## FAQ
-
-### Q: LiteLLM proxy returns `/v1/responses` not found?
-
-Some OpenAI-compatible services only support `/v1/chat/completions`. Add this to your LiteLLM config:
+Minimal example:
```yaml
-litellm_settings:
- use_chat_completions_url_for_anthropic_messages: true
+model_list:
+ - model_name: provider-model
+ litellm_params:
+ model: openai/provider-model
+ api_base: https://provider.example.com/v1
+ api_key: os.environ/PROVIDER_API_KEY
```
-### Q: What's the difference between `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN`?
+After starting LiteLLM, connect its Anthropic-compatible URL as an `anthropic` provider. Follow the [official LiteLLM documentation](https://docs.litellm.ai/) for deployment, authentication, and model prefixes.
-- `ANTHROPIC_API_KEY` → Sent via `x-api-key` header
-- `ANTHROPIC_AUTH_TOKEN` → Sent via `Authorization: Bearer` header
+## Capability boundaries
-LiteLLM proxy accepts Bearer Token format by default, so `ANTHROPIC_AUTH_TOKEN` is recommended.
+A third-party model needs at least the following behavior for a stable agent workflow:
-### Q: Can I configure multiple models?
+- correct handling of multi-turn messages, system content, and tool calls;
+- preservation of tool-call IDs and acceptance of matching tool results;
+- sufficient context and output limits;
+- complete, correctly ordered events in streaming mode.
-Yes. Define multiple `model_name` entries in `litellm_config.yaml`, then switch by changing the `ANTHROPIC_MODEL` value.
+Thinking modes, effort, prompt caching, image input, and structured output depend on the protocol, provider, and exact model. They cannot be labeled universally supported or unsupported. When diagnosing a failure, disable optional capabilities, verify basic text plus tool use, and restore features one at a time.
-### Q: Local Ollama models don't work well?
+## Troubleshooting
-This project's system prompts and tool calls require strong model capabilities. Use larger models (e.g., Llama 3 70B+, Qwen 72B+). Smaller models may fail to handle tool calling correctly.
+| Symptom | Check first |
+|---------|-------------|
+| `401` / `403` | Authentication header, token permissions, and whether the base URL belongs to that credential |
+| `404` | Provider format and whether the base URL duplicates an API path |
+| Model not found | Use the provider's real model ID, not an alias from another platform |
+| Text works but tools never run | Confirm that both the model and gateway fully support tool calls |
+| Old endpoint after switching providers | Remove duplicate configuration from `.env`, `settings.json`, or Desktop |
+| Streaming stops early | Disable optional capabilities and check whether a gateway buffers or rewrites the event stream |
diff --git a/docs/en/im/dingtalk.md b/docs/en/im/dingtalk.md
new file mode 100644
index 00000000..8bedc903
--- /dev/null
+++ b/docs/en/im/dingtalk.md
@@ -0,0 +1,93 @@
+# DingTalk Integration
+
+The DingTalk adapter uses DingTalk Stream, so it does not require a public callback URL. It supports private chats only.
+
+Current behavior includes text, image attachments, project selection, status, stop, AI Card streaming, and text or card-based permission approval.
+
+## Bind a DingTalk bot
+
+Open **Settings → IM Integration → DingTalk**.
+
+The recommended flow is:
+
+1. Select **Scan to bind**.
+2. Scan with the DingTalk mobile app.
+3. Confirm bot creation and authorization.
+4. Wait for Desktop to show the bound state.
+5. Save the configuration.
+
+Desktop stores `clientId` and `clientSecret` in `~/.claude/adapters.json` and restarts the adapter sidecar.
+
+## Manual credentials
+
+If QR binding is unavailable, enter:
+
+- **Client ID** — DingTalk application `appKey`
+- **Client Secret** — application `appSecret`
+- **Stream Endpoint** — optional; defaults to `https://api.dingtalk.com`
+- **Permission card template ID** — optional
+- **Allowed Users** — optional explicit user IDs
+
+Saving starts a DingTalk Stream connection.
+
+## Authorize a user
+
+Binding the bot does not authorize every DingTalk account. Generate a six-character pairing code in Desktop and send it to the bot in a private chat.
+
+Codes expire after 60 minutes, are one-time use, and are rate limited after repeated failures. Paired users can be removed from Desktop at any time.
+
+## Projects and commands
+
+If no default project is configured, the adapter returns recent projects. Reply with a number, project name, or absolute path. The mapping is persisted in `~/.claude/adapter-sessions.json`.
+
+Supported commands:
+
+- `/help` or `帮助`
+- `/status` or `状态`
+- `/projects` or `项目列表`
+- `/new` or `新会话`
+- `/new `
+- `/clear` or `清空`
+- `/stop` or `停止`
+
+## Permission approval
+
+Without a published card template, reply:
+
+- `/allow `
+- `/always `
+- `/deny `
+
+With a configured permission-card template, the adapter prefers an interactive card and receives its callback over DingTalk Stream. Text commands remain the fallback.
+
+## Replies and attachments
+
+- Private messages arrive through `dingtalk-stream`.
+- Normal replies use the message `sessionWebhook`.
+- AI Card is preferred for streaming output.
+- Image attachments are downloaded and passed as inline image input.
+- Shared attachment size limits are enforced.
+
+## Unbind
+
+Unbinding the bot clears DingTalk credentials, allowlisted and paired users, and the permission-card template ID. Removing a single paired user revokes only that user.
+
+## Development
+
+Packaged Desktop starts the sidecar automatically. For source development:
+
+```bash
+cd adapters
+bun install
+bun run dingtalk
+```
+
+Optional overrides:
+
+```bash
+export DINGTALK_CLIENT_ID="..."
+export DINGTALK_CLIENT_SECRET="..."
+export DINGTALK_STREAM_ENDPOINT="https://api.dingtalk.com"
+export DINGTALK_PERMISSION_CARD_TEMPLATE_ID="..."
+export ADAPTER_SERVER_URL="ws://127.0.0.1:3456"
+```
diff --git a/docs/en/im/feishu.md b/docs/en/im/feishu.md
new file mode 100644
index 00000000..17b29da9
--- /dev/null
+++ b/docs/en/im/feishu.md
@@ -0,0 +1,72 @@
+# Feishu Integration
+
+The Feishu adapter connects an enterprise custom app to local Desktop sessions. It handles `p2p` private chats only; group chats are not supported.
+
+## Create the Feishu app
+
+Feishu provides a template with the messaging, event, and card capabilities needed by this integration:
+
+[Create a Feishu bot from the template](https://open.feishu.cn/page/openclaw?form=multiAgent)
+
+Choose a name, create the app, and save its **App ID** and **App Secret**.
+
+In the [Feishu developer console](https://open.feishu.cn/app?lang=en-US), create and publish a bot version. Optional menu entries can invoke:
+
+- `/projects`
+- `/new`
+- `/clear`
+
+The app must be published before users can reliably receive the configured bot behavior.
+
+## Configure Desktop
+
+Open **Settings → IM Integration → Feishu**:
+
+1. Enter the App ID and App Secret.
+2. Generate a six-character pairing code.
+3. Save the configuration.
+4. Send a private message to the bot and provide the code.
+
+Pairing codes expire after 60 minutes, work once, and are rate limited after repeated failures. App credentials do not authorize every organization user.
+
+## Commands
+
+- `/help` or `帮助`
+- `/status` or `状态`
+- `/clear` or `清空`
+- `/projects` or `项目列表`
+- `/new` or `新会话`
+- `/stop` or `停止`
+
+## Permission approval
+
+Permission requests are sent as interactive cards. Selecting allow or deny returns the result to the pending Desktop session.
+
+If card actions do not work, confirm that the latest application version is published and includes the required card-action capability.
+
+## Reply behavior
+
+- Normal text uses Feishu post messages.
+- Permission approval uses cards.
+- Streaming output prefers patching the same message.
+- Long completed content is split to respect platform limits.
+
+## Development
+
+Packaged Desktop starts the sidecar automatically. For source development:
+
+```bash
+cd adapters
+bun install
+bun run feishu
+```
+
+Optional overrides:
+
+```bash
+export FEISHU_APP_ID="cli_xxx"
+export FEISHU_APP_SECRET="xxx"
+export ADAPTER_SERVER_URL="ws://127.0.0.1:3456"
+```
+
+If messages are missing, confirm that the app is published, the conversation is a private chat, and the sender is paired or explicitly allowlisted.
diff --git a/docs/en/im/index.md b/docs/en/im/index.md
new file mode 100644
index 00000000..d15a4d75
--- /dev/null
+++ b/docs/en/im/index.md
@@ -0,0 +1,76 @@
+# IM Integrations
+
+Claude Code Haha can bridge private messages from WeChat, DingTalk, WhatsApp, Telegram, and Feishu into local Desktop sessions.
+
+These integrations do not make the local agent public. A user is accepted only when they are explicitly listed in `allowedUsers` or have completed pairing.
+
+## Current architecture
+
+The supported path is the Desktop adapter architecture:
+
+```mermaid
+flowchart LR
+ A["Desktop Settings"] --> B["/api/adapters"]
+ B --> C["~/.claude/adapters.json"]
+ C --> D["Platform adapter sidecar"]
+ D --> E["Pairing and allowlist check"]
+ E --> F["HTTP session creation"]
+ F --> G["/ws/:sessionId"]
+ G --> H["Claude Code session"]
+```
+
+Platform adapters are separate local processes. They read their platform configuration, enforce authorization, map a private chat to a project session, and bridge messages over the Desktop server.
+
+This is different from the upstream Claude Code Channel/MCP research retained under [Channel System](../channel/).
+
+## Set up an integration
+
+1. Keep the Desktop app running.
+2. Open **Settings → IM Integration**.
+3. Configure or bind one platform.
+4. Set a default project if desired.
+5. Generate a six-character pairing code.
+6. Send the code in a private chat with the bot or bound account.
+
+The code expires after 60 minutes, can be used once, and is invalidated when a new code is generated. Repeated failures are rate limited.
+
+## Platforms
+
+- [WeChat](./wechat.md) — QR-bound bot account, private chats
+- [DingTalk](./dingtalk.md) — DingTalk Stream, private chats
+- [WhatsApp](./whatsapp.md) — personal linked-device session through WhatsApp Web
+- [Telegram](./telegram.md) — BotFather token, private chats
+- [Feishu](./feishu.md) — enterprise custom app, `p2p` chats
+
+## Local state
+
+`~/.claude/adapters.json` stores platform configuration, allowlists, and pairing state. Sensitive fields returned by the settings API are masked.
+
+`~/.claude/adapter-sessions.json` stores chat-to-session mappings, including the session ID, working directory, and update time. This allows an adapter to reconnect to an existing session after restart.
+
+Both paths follow `CLAUDE_CONFIG_DIR` when a custom data directory is active.
+
+## Authorization model
+
+- `allowedUsers` and `pairedUsers` are combined.
+- If both are empty, access is denied.
+- Binding a bot or linked account does not authorize its contacts.
+- Removing a paired user requires that user to pair again.
+- Removing platform credentials stops that platform from connecting.
+
+## Common commands
+
+The exact presentation differs by platform, but adapters support the same core operations:
+
+- `/help` — show available commands
+- `/status` — show the current project, model, and run state
+- `/projects` — list or switch recent projects
+- `/new` — start a new session or choose another project
+- `/clear` — clear current context while keeping the project binding
+- `/stop` — stop the current generation
+
+Permission requests are returned as platform buttons, cards, or explicit text commands. A request ID must match the pending Desktop session request.
+
+## Development
+
+Packaged Desktop starts configured adapter sidecars automatically. Manual `bun run ` commands are for source development and isolated troubleshooting.
diff --git a/docs/en/im/telegram.md b/docs/en/im/telegram.md
new file mode 100644
index 00000000..32269d54
--- /dev/null
+++ b/docs/en/im/telegram.md
@@ -0,0 +1,72 @@
+# Telegram Integration
+
+The Telegram adapter connects a BotFather bot to local Desktop sessions. It accepts private chats only; groups are not supported.
+
+## Create a bot
+
+In Telegram, open the official **@BotFather** account:
+
+1. Send `/newbot`.
+2. Choose a display name.
+3. Choose a username ending in `_bot`.
+4. Copy the Bot Token returned by BotFather.
+
+Treat the token as a credential.
+
+## Configure Desktop
+
+Open **Settings → IM Integration → Telegram**:
+
+1. Paste the Bot Token.
+2. Generate a six-character pairing code.
+3. Save the configuration.
+4. Send a message to the new bot and provide the pairing code when prompted.
+
+The code expires after 60 minutes, works once, and is rate limited after repeated failures. Bot configuration alone does not authorize all Telegram users.
+
+## Commands
+
+- `/start` — show help
+- `/help` — show available commands
+- `/projects` — list or switch recent projects
+- `/status` — show project, model, run state, and task summary
+- `/clear` — clear context while keeping the project
+- `/new` — start a new session and choose a project
+- `/stop` — stop the current generation
+
+## Permission approval
+
+Telegram presents buttons for:
+
+- allow once;
+- always allow the matching operation;
+- deny.
+
+The callback is converted to a `permission_response` for the pending Desktop session.
+
+## Reply behavior
+
+The adapter buffers streaming output:
+
+- a placeholder can be sent during thinking;
+- text deltas are accumulated;
+- completed text is split into platform-sized messages.
+
+## Development
+
+Packaged Desktop starts the adapter automatically. For source development:
+
+```bash
+cd adapters
+bun install
+bun run telegram
+```
+
+Optional overrides:
+
+```bash
+export TELEGRAM_BOT_TOKEN="123456:ABC-DEF..."
+export ADAPTER_SERVER_URL="ws://127.0.0.1:3456"
+```
+
+If a sender is rejected, verify that the current pairing code was sent to the correct bot private chat and that the sender is now present in the paired or allowed list.
diff --git a/docs/en/im/wechat.md b/docs/en/im/wechat.md
new file mode 100644
index 00000000..d9896cd0
--- /dev/null
+++ b/docs/en/im/wechat.md
@@ -0,0 +1,94 @@
+# WeChat Integration
+
+The WeChat adapter binds a bot account by QR code, then authorizes individual private-chat users through pairing or an allowlist.
+
+It supports text, transcribed voice content, image and file attachments, project selection, status, stop, and text-based permission approval. Group chats are not supported.
+
+## Bind the WeChat bot account
+
+Open **Settings → IM Integration → WeChat**:
+
+1. Select **Scan to bind**.
+2. Scan the QR code with WeChat.
+3. Confirm the login in WeChat.
+4. Wait for Desktop to show the bound state.
+5. Save the configuration.
+
+Desktop stores the returned `accountId`, `botToken`, `baseUrl`, and `userId` in the `wechat` section of `~/.claude/adapters.json`, then restarts the adapter sidecar.
+
+Binding credentials does not authorize every WeChat user.
+
+## Authorize a user
+
+Generate a pairing code at the top of **IM Integration**, then send the six-character code to the bound bot in a private chat.
+
+The code:
+
+- expires after 60 minutes;
+- can be used once;
+- is replaced immediately when a new code is generated;
+- is rate limited after repeated failures.
+
+Known WeChat user IDs can instead be entered in `Allowed Users`.
+
+## Projects and sessions
+
+With a default project configured, the first accepted message creates or resumes a session in that directory.
+
+Without a default project, the adapter returns recent projects. Reply with a number, project name, or absolute path. The resulting chat-to-session mapping is stored in `~/.claude/adapter-sessions.json`.
+
+Use `/new` to choose another project and start a new session.
+
+## Commands
+
+- `/help` or `帮助`
+- `/status` or `状态`
+- `/projects` or `项目列表`
+- `/new` or `新会话`
+- `/new `
+- `/clear` or `清空`
+- `/stop` or `停止`
+
+## Permission approval
+
+Reply to the text approval message with:
+
+- `/allow ` — allow once
+- `/always ` — persist the matching approval
+- `/deny ` — deny
+
+The adapter sends the response back to the pending Desktop session.
+
+## Attachments and replies
+
+- WeChat messages are received through long polling.
+- Long text is split into platform-sized messages.
+- Images enter model input as inline images.
+- Other files are downloaded to a local temporary path for the session.
+- Shared attachment size limits are enforced.
+
+## Unbind
+
+Unbinding the WeChat account clears its credentials and platform authorization lists. Removing one paired user only revokes that user.
+
+## Development
+
+Packaged Desktop starts the sidecar automatically. For source development:
+
+```bash
+cd adapters
+bun install
+bun run wechat
+```
+
+Optional overrides:
+
+```bash
+export WECHAT_ACCOUNT_ID="..."
+export WECHAT_BOT_TOKEN="..."
+export WECHAT_BASE_URL="https://ilinkai.weixin.qq.com"
+export WECHAT_USER_ID="..."
+export ADAPTER_SERVER_URL="ws://127.0.0.1:3456"
+```
+
+If messages are rejected, confirm that Desktop is running, the account is bound, the sender is paired or allowlisted, and both adapter state files are writable.
diff --git a/docs/en/im/whatsapp.md b/docs/en/im/whatsapp.md
new file mode 100644
index 00000000..9ab31ccc
--- /dev/null
+++ b/docs/en/im/whatsapp.md
@@ -0,0 +1,96 @@
+# WhatsApp Integration
+
+The WhatsApp adapter uses a personal WhatsApp Web linked-device session through `@whiskeysockets/baileys`. It is not the official WhatsApp Business Platform or Cloud API.
+
+It handles personal private chats only. Groups, channels, and status broadcasts are not supported.
+
+## What linked-device access means
+
+You do not need a Meta developer app, WABA, Phone Number ID, Cloud API access token, webhook, or message template.
+
+Instead:
+
+1. Desktop creates a WhatsApp Web login QR code.
+2. You scan it from **WhatsApp → Linked devices**.
+3. Local Baileys auth state is saved.
+4. The adapter observes private messages received by that account.
+5. Only allowlisted or paired senders can reach a Claude Code session.
+
+The chat recipient is the WhatsApp account you bound, not a separately created bot.
+
+## Bind the account
+
+Open **Settings → IM Integration → WhatsApp**:
+
+1. Select **Scan to bind**.
+2. Open **Linked devices** on the phone.
+3. Scan the QR code.
+4. Wait for Desktop to confirm the binding.
+
+The default local auth directory is:
+
+```text
+~/.claude/whatsapp-auth/default
+```
+
+Do not publish or share this directory.
+
+## Authorize a sender
+
+Linked-device login does not authorize all contacts.
+
+Generate a six-character pairing code in Desktop, then send it in a private WhatsApp chat to the bound account. The sender’s JID is added to `whatsapp.pairedUsers`.
+
+Known JIDs can be entered in `Allowed Users`, for example:
+
+```text
+@s.whatsapp.net
+```
+
+## Commands
+
+- `/start` or `/help`
+- `/projects`
+- `/status`
+- `/clear`
+- `/new [project]`
+- `/stop`
+
+## Permission approval
+
+WhatsApp uses explicit text replies:
+
+- `1` or `/allow ` — allow once
+- `2` or `/always ` — persist the matching approval
+- `3` or `/deny ` — deny
+
+## Reply behavior
+
+- Thinking can produce a short status message.
+- Completed text is split into platform-sized messages.
+- Recognized Markdown image output can be sent as an image message.
+- The adapter does not depend on editing one WhatsApp message for token-level streaming.
+
+## Unbind
+
+Use the WhatsApp settings page to unbind, remove local auth state, and scan again. Removing only a paired user revokes that sender without unlinking the account.
+
+## Development
+
+Packaged Desktop starts the sidecar automatically. For source development:
+
+```bash
+cd adapters
+bun install
+bun run whatsapp
+```
+
+Optional overrides:
+
+```bash
+export WHATSAPP_AUTH_DIR="$HOME/.claude/whatsapp-auth/default"
+export WHATSAPP_ACCOUNT_JID="@s.whatsapp.net"
+export ADAPTER_SERVER_URL="ws://127.0.0.1:3456"
+```
+
+If the adapter reports that no account is bound, complete QR binding in Desktop first; the manual adapter command does not provide a separate login UI.
diff --git a/docs/en/index.md b/docs/en/index.md
deleted file mode 100644
index 05acb4e1..00000000
--- a/docs/en/index.md
+++ /dev/null
@@ -1,40 +0,0 @@
----
-layout: home
-
-hero:
- name: Claude Code Haha
- text: Locally Runnable Claude Code
- tagline: Repaired from leaked source, supports any Anthropic-compatible API endpoint (MiniMax, OpenRouter, etc.)
- image:
- src: /images/logo-horizontal.png
- alt: Claude Code Haha
- actions:
- - theme: brand
- text: Quick Start
- link: /en/guide/quick-start
- - theme: alt
- text: GitHub
- link: https://github.com/NanmiCoder/cc-haha
-
-features:
- - icon: "\U0001F5A5"
- title: Full TUI Experience
- details: Ink terminal UI matching the official Claude Code interface, with --print headless mode
- - icon: "\U0001F9E0"
- title: Memory System
- details: Cross-session persistent memory with auto-extraction, smart retrieval, and AutoDream consolidation
- - icon: "\U0001F916"
- title: Multi-Agent System
- details: Agent orchestration, parallel tasks, Teams collaboration, Worktree isolation
- - icon: "\U0001F9E9"
- title: Skills System
- details: Extensible capability plugins, custom workflows, conditional activation
- - icon: "\U0001F4BB"
- title: Computer Use
- details: Desktop control — screenshots, mouse, and keyboard automation
- link: /en/features/computer-use
- - icon: "\U0001F310"
- title: Third-Party Models
- details: Support for OpenAI, DeepSeek, Ollama, and any compatible model
- link: /en/guide/third-party-models
----
diff --git a/docs/en/reference/local-server.md b/docs/en/reference/local-server.md
new file mode 100644
index 00000000..5ce694ae
--- /dev/null
+++ b/docs/en/reference/local-server.md
@@ -0,0 +1,148 @@
+# Local Server
+
+The local server is the runtime boundary between Desktop, the H5 browser client, and the Claude CLI. It provides REST APIs, the chat WebSocket, provider protocol translation, and Desktop web assets. The packaged Desktop app manages it automatically. Start it manually only for source development, headless deployment, or a custom client.
+
+## Start the server
+
+From the repository root:
+
+```bash
+bun run src/server/index.ts
+```
+
+It listens on `127.0.0.1:3456` by default. Check readiness with:
+
+```bash
+curl http://127.0.0.1:3456/health
+```
+
+Response shape:
+
+```json
+{
+ "status": "ok",
+ "timestamp": "2026-01-01T00:00:00.000Z"
+}
+```
+
+`/health` is an always-public startup probe. A successful response does not prove that another endpoint is authenticated.
+
+## Startup options
+
+| Option | Environment variable | Default | Description |
+|--------|----------------------|---------|-------------|
+| `--host ` | `SERVER_HOST` | `127.0.0.1` | Listen address |
+| `--port ` | `SERVER_PORT` | `3456` | HTTP and WebSocket port |
+| `--cli-path ` | `CLAUDE_CLI_PATH` | Automatically resolved | CLI started by the server |
+| `--auth-required` | `SERVER_AUTH_REQUIRED=1` | Off | Require explicit authentication for capability endpoints |
+
+Command-line host and port values take precedence over environment variables. Keep the loopback address for development. Listening on `0.0.0.0` accepts external connections but does not configure H5 authorization, TLS, or a reverse proxy.
+
+## Serve the H5 client
+
+For a source launch, build the Desktop web assets first:
+
+```bash
+cd desktop
+bun run build
+cd ..
+bun run src/server/index.ts
+```
+
+The server automatically finds `desktop/dist` in the repository. When starting from another directory, provide the absolute build path:
+
+```bash
+CLAUDE_H5_DIST_DIR=/absolute/path/to/desktop/dist \
+ bun run /absolute/path/to/src/server/index.ts
+```
+
+| Variable | Description |
+|----------|-------------|
+| `CLAUDE_H5_DIST_DIR` | H5 build directory; it must contain `index.html` |
+| `CLAUDE_H5_PUBLIC_BASE_URL` | Fixed public server URL |
+| `CLAUDE_H5_AUTO_PUBLIC_URL=1` | Try to derive a LAN URL while H5 access is enabled |
+
+Configure the H5 token and exact allowed browser origins in **Settings → H5 Access**. Treat the token like an API key and regenerate it to revoke existing browser access.
+
+## Access control
+
+The server classifies the source of each request before granting access to a capability path:
+
+| Request | Default behavior |
+|---------|------------------|
+| `GET /health` | Public startup probe |
+| H5 shell and static assets | Public bootstrap content; they do not contain session data |
+| Direct loopback request | Trusted only when client address, Host, and Origin are local and no proxy-trace header is present |
+| Remote capability request while H5 is disabled | Denied for `/api`, `/proxy`, `/ws`, and file capabilities |
+| Remote capability request while H5 is enabled | Requires a valid H5 token and an allowed browser Origin |
+| `--auth-required` / `SERVER_AUTH_REQUIRED=1` | Requires explicit authentication for capability endpoints even when H5 is disabled |
+
+A TCP peer address of `127.0.0.1` does not prove that the end user is local. A reverse proxy must preserve the public `Host` or send at least one of `Forwarded`, `X-Forwarded-*`, `X-Real-IP`, or `Via`, allowing the server to distinguish proxied traffic from direct loopback traffic.
+
+### Passing tokens
+
+- REST, provider proxy, and file endpoints: `Authorization: Bearer `
+- Browser WebSocket: `/ws/?token=`
+
+H5 mode uses the token generated in Settings. Explicit `--auth-required` mode can also accept a bearer token equal to the server's `ANTHROPIC_API_KEY`, but exposing a model credential for remote access is discouraged. Prefer H5 with its separate token.
+
+CORS restricts browser access to responses; it is not authentication. It does not make a non-browser client safe.
+
+## HTTP API surface
+
+Business REST APIs live under `/api/*` and cover:
+
+- sessions, conversations, search, and filesystem access;
+- settings, permissions, models, effort, and providers;
+- agents, tasks, teams, and scheduled tasks;
+- skills, plugins, the market, and MCP;
+- IM adapters and Computer Use;
+- diagnostics, Doctor, activity statistics, memory, and traces;
+- H5 access control.
+
+For exact request and response shapes, use the current handlers under `src/server/api/`. The internal `/sdk/` WebSocket is used by the Claude CLI process started by the server; it is not a third-party client API.
+
+`/proxy/*` is the provider protocol-translation boundary and depends on runtime authentication and model-routing state. Do not expose it as a general-purpose stateless OpenAI proxy.
+
+## Chat WebSocket
+
+Clients connect to:
+
+```text
+ws://127.0.0.1:3456/ws/
+```
+
+Common client messages include:
+
+- `user_message` and `stop_generation`;
+- `permission_response` and `computer_use_permission_response`;
+- `set_permission_mode` and `set_runtime_config`;
+- `sync_state` and `prewarm_session`;
+- `ping`.
+
+The server sends connection and session state, text deltas, thinking, tool calls and results, permission requests, retry or fallback state, errors, task or team updates, and `pong`. Use `src/server/ws/events.ts` as the complete field contract.
+
+The Desktop client sends a ping every 30 seconds and reconnects if no pong arrives within 10 seconds. Reconnect delay is capped at 30 seconds; it does not stop permanently after a fixed number of attempts. A custom client should reconnect, resynchronize state, and ignore unknown fields added to future messages.
+
+## Reverse-proxy checklist
+
+For remote use:
+
+1. Enable H5 on the server, generate a separate token, and configure exact allowed origins.
+2. Use HTTPS so the token is never sent over a public network in cleartext.
+3. Proxy static assets, `/api/*`, `/proxy/*`, and `/ws/*`.
+4. Enable WebSocket upgrade for `/ws/*`.
+5. Preserve the public Host and standard proxy headers.
+6. Do not expose the internal `/sdk/*` path.
+
+## Troubleshooting
+
+| Symptom | Check |
+|---------|-------|
+| Port does not bind | Whether `SERVER_PORT` is occupied and contains a valid number |
+| `/health` works but an API returns `403` | The request is classified as remote while H5 is disabled |
+| API or WebSocket returns `401` | H5 token is missing or stale, or the WebSocket lacks a query token |
+| Browser reports CORS | The page's exact Origin is in the H5 allowed-origin list |
+| WebSocket reconnects repeatedly | Proxy upgrade support, token forwarding, and proxy idle timeouts |
+| Page returns `404` | `desktop/dist` was not built or `CLAUDE_H5_DIST_DIR` is wrong |
+| Remote traffic is treated as local | The proxy removed both the public Host and every proxy-trace header |
diff --git a/docs/en/reference/project-structure.md b/docs/en/reference/project-structure.md
index d0f071e8..4014dbe4 100644
--- a/docs/en/reference/project-structure.md
+++ b/docs/en/reference/project-structure.md
@@ -1,22 +1,81 @@
# Project Structure
+The repository contains the CLI/TUI, local Server, Electron desktop app, IM adapters, and documentation site. This map lists stable responsibility boundaries rather than every file.
```text
-bin/claude-haha # Entry script
-preload.ts # Bun preload (sets MACRO globals)
-.env.example # Environment variable template
-src/
-├── entrypoints/cli.tsx # Main CLI entry
-├── main.tsx # Main TUI logic (Commander.js + React/Ink)
-├── localRecoveryCli.ts # Fallback Recovery CLI
-├── setup.ts # Startup initialization
-├── screens/REPL.tsx # Interactive REPL screen
-├── ink/ # Ink terminal rendering engine
-├── components/ # UI components
-├── tools/ # Agent tools (Bash, Edit, Grep, etc.)
-├── commands/ # Slash commands (/commit, /review, etc.)
-├── skills/ # Skill system
-├── services/ # Service layer (API, MCP, OAuth, etc.)
-├── hooks/ # React hooks
-└── utils/ # Utility functions
+.
+├── bin/
+│ └── claude-haha # CLI launcher
+├── preload.ts # Bun preload and build compatibility entry
+├── package.json # Root scripts and dependencies
+├── src/
+│ ├── entrypoints/
+│ │ └── cli.tsx # Main CLI entry
+│ ├── main.tsx # TUI flow
+│ ├── setup.ts # Startup initialization
+│ ├── screens/ # REPL and other terminal screens
+│ ├── components/ # Ink UI components
+│ ├── tools/ # Agent tools such as Bash, Edit, and Grep
+│ ├── commands/ # Slash commands
+│ ├── services/ # Shared Provider, MCP, and OAuth services
+│ ├── utils/ # Runtime utilities and Computer Use integration
+│ ├── vendor/ # Controlled vendored implementations
+│ └── server/
+│ ├── index.ts # Bun.serve HTTP/WebSocket entry
+│ ├── router.ts # REST resource routing
+│ ├── api/ # API boundaries
+│ ├── services/ # Session, Provider, index, and diagnostics services
+│ ├── ws/ # WebSocket protocol and lifecycle
+│ ├── proxy/ # Provider protocol conversion
+│ ├── middleware/ # Auth and CORS
+│ └── config/ # Provider presets
+├── desktop/
+│ ├── src/ # React Renderer
+│ │ ├── api/ # Server API and WebSocket clients
+│ │ ├── components/ # Chat, Workspace, Browser, and layout
+│ │ ├── features/ # Isolated features such as Pets
+│ │ ├── pages/ # Sessions, Settings, tasks, diagnostics
+│ │ ├── stores/ # Zustand state
+│ │ ├── i18n/ # Desktop locales
+│ │ └── lib/ # Renderer runtime utilities
+│ ├── electron/
+│ │ ├── main.ts # Electron main entry
+│ │ ├── preload.ts # Main-window Host bridge
+│ │ ├── preview-preload.ts # Native preview bridge
+│ │ ├── pet-preload.ts # Pet-window bridge
+│ │ ├── ipc/ # IPC channels and validation
+│ │ └── services/ # Sidecar, terminal, updater, and preview services
+│ ├── sidecars/
+│ │ └── claude-sidecar.ts # Unified server / cli / adapters entry
+│ ├── scripts/ # Build, packaging, and resource preparation
+│ └── src-tauri/ # Historical code and current package resources; not the Host
+├── adapters/
+│ ├── common/ # Shared config, pairing, messaging, and WS bridge
+│ ├── telegram/
+│ ├── feishu/
+│ ├── wechat/
+│ ├── dingtalk/
+│ └── whatsapp/
+├── runtime/
+│ ├── mac_helper.py # macOS Computer Use helper
+│ ├── win_helper.py # Windows Computer Use helper
+│ ├── requirements.txt
+│ └── requirements-win.txt
+├── scripts/ # Root quality, release, and maintenance scripts
+├── tests/ # Cross-module tests and fixtures
+├── site/ # React docs site, content index, and static build
+└── docs/ # Chinese and English Markdown content
```
+
+## Runtime boundaries
+
+| Entry | Runtime | Responsibility |
+|---|---|---|
+| `src/entrypoints/cli.tsx` | Bun | CLI/TUI and Agent tools |
+| `src/server/index.ts` | Bun / `Bun.serve` | Local HTTP, WebSocket, and H5 |
+| `desktop/electron/main.ts` | Electron main | Native desktop Host |
+| `desktop/src/` | Chromium Renderer | React desktop UI |
+| `desktop/sidecars/claude-sidecar.ts` | Bun-compiled Sidecar | Packaged Server, CLI, and Adapter entry |
+| `adapters//` | Bun Sidecar | Platform messaging integration |
+
+Place new code at the boundary that owns the responsibility: native desktop capabilities belong in `desktop/electron/`, shared business APIs in `src/server/`, and platform-specific messaging behavior in `adapters//`. The Renderer should not bypass these boundaries.
diff --git a/docs/features/computer-use-architecture.md b/docs/features/computer-use-architecture.md
index 36b381b2..20eb312c 100644
--- a/docs/features/computer-use-architecture.md
+++ b/docs/features/computer-use-architecture.md
@@ -1,607 +1,225 @@
-# Computer Use 架构深度解析
+# Computer Use 架构
-> 深入解析 Computer Use 功能的底层实现:从 MCP 工具定义到 Python Bridge,从 9 层安全关卡到灰度控制绕过。
+> 本页说明 Computer Use 的工具、授权、平台执行器与当前安全边界。使用步骤请先阅读 [Computer Use 使用指南](./computer-use.md)。
-
-补丁环境 ·
-分层架构 ·
-MCP 工具层 ·
-安全关卡 ·
-Python Bridge ·
-交互闭环 ·
-源文件索引
-
+## 分层结构
-
-
----
-
-## 一、补丁环境总览
-
-Claude Code 原版的 Computer Use 功能(内部代号 **Chicago**)依赖三个不可公开获取的组件:
-
-| 组件 | 作用 | 获取方式 |
-|------|------|----------|
-| `@ant/computer-use-swift` | 屏幕截图、显示器枚举 | Anthropic 私有 npm 包 |
-| `@ant/computer-use-input` | 鼠标/键盘模拟 | Anthropic 私有 npm 包 |
-| GrowthBook 远程配置 | 灰度控制、功能开关 | Anthropic 内部服务 |
-
-我们的方案是:**保留原始 MCP 工具定义和安全机制不变,仅替换底层执行层和灰度控制**。
-
-
-
-### 我们做了什么
-
-```
-原始 Claude Code Claude Code Haha (补丁版)
-───────────────── ─────────────────────────
-@ant/computer-use-swift ──替换为──→ Python Bridge (mac_helper.py)
-@ant/computer-use-input ──替换为──→ pyautogui + pyobjc
-GrowthBook 灰度控制 ──绕过──→ gates.ts 硬编码 return true
-订阅检查 (Max/Pro) ──绕过──→ getChicagoEnabled() = true
-编译宏 CHICAGO_MCP ──替换为──→ true
-isDefaultDisabledBuiltin ──修改──→ 返回 false
+```text
+模型 / MCP Client
+ ↓
+工具定义与参数校验
+ src/vendor/computer-use-mcp/tools.ts
+ ↓
+会话授权与安全调度
+ src/vendor/computer-use-mcp/toolCalls.ts
+ src/vendor/computer-use-mcp/mcpServer.ts
+ ↓
+Claude Code Haha 集成
+ src/utils/computerUse/
+ ↓
+Python Bridge
+ src/utils/computerUse/pythonBridge.ts
+ ↓
+平台 Helper
+ runtime/mac_helper.py
+ runtime/win_helper.py
```
-### 我们没有改什么
+工具层与安全调度尽量保持平台无关。平台差异集中在 `ComputerExecutor` capability 和 Python helper 中。
-- **MCP 工具定义**(24 个工具的 schema 和参数校验)
-- **9 层安全关卡**(TCC 权限、应用白名单、权限等级、像素验证等)
-- **应用分类系统**(191 个 bundle ID 的分类和权限映射)
-- **会话上下文管理**(全局锁、截图缓存、状态同步)
-- **键盘快捷键阻止列表**(系统级危险操作拦截)
+## 工具注册
-### 灰度绕过细节
+`buildComputerUseTools()` 生成 MCP 工具定义。代码中共有 27 个 schema:
-原始代码通过三层门控限制 Computer Use 的访问:
+- 24 个基础控制工具
+- 3 个 Teach 工具:`request_teach_access`、`teach_step`、`teach_batch`
-```typescript
-// 原始代码(简化)
-function getChicagoEnabled(): boolean {
- // 层1:GrowthBook 远程配置
- const config = getDynamicConfig('tengu_malort_pedway')
- // 层2:订阅检查
- const hasSubscription = hasRequiredSubscription() // Max/Pro
- // 层3:编译时宏
- return feature('CHICAGO_MCP') && config.enabled && hasSubscription
-}
+Teach 工具由 `caps.teachMode` 控制。宿主没有开启该 capability 时,`ListTools` 只返回基础工具。因此,“代码定义 27 个”与“每个运行时固定暴露 27 个”不是同一个事实。
+
+MCP Server 有两条使用路径:
+
+- `src/utils/computerUse/mcpServer.ts` 构造 CLI 使用的服务,并把已安装应用名称加入授权工具说明。
+- `src/vendor/computer-use-mcp/mcpServer.ts` 绑定实际会话上下文,保存最近截图和授权状态。
+
+`src/utils/computerUse/setup.ts` 生成动态 MCP 配置和允许工具名。实际工具调用仍由 Computer Use 自己的应用授权流程控制,而不是绕过授权直接执行。
+
+## 会话状态
+
+每个绑定会话维护:
+
+- 已授权应用及其权限等级
+- 剪贴板和系统组合键授权
+- 当前显示器
+- 最近一次截图及坐标几何
+- Computer Use 会话锁
+- Teach 是否处于活动状态
+
+最近截图是后续点击坐标的参考。截图尺寸、逻辑显示尺寸、缩放比例和显示器原点必须作为一个整体保存,否则 Retina 或多显示器环境会点击到错误位置。
+
+## 坐标与截图
+
+工具只向模型公开一种坐标模式:
+
+- `pixels`:坐标来自模型最近看到的截图
+- `normalized_0_100`:以屏幕宽高百分比表示
+
+工具描述与执行器读取同一份冻结后的坐标配置,避免模型按一种坐标描述输出,Host 却按另一种模式换算。
+
+典型换算:
+
+```text
+模型截图坐标
+ → 按截图尺寸映射到逻辑显示尺寸
+ → 加上目标显示器原点
+ → 交给平台 helper
```
-我们的处理:
+截图会按图像预算缩放,但不应在文档中硬编码某个固定输出尺寸。
-```typescript
-// gates.ts — 我们的修改
-export function getChicagoEnabled(): boolean {
- return true // ← 三层门控全部绕过
-}
+## 授权与动作检查
+
+Computer Use 在动作执行前组合多类检查,而不是依赖单一权限弹窗。
+
+### 全局开关与系统权限
+
+- `CLAUDE_COMPUTER_USE_ENABLED=0` 或托管配置可关闭能力。
+- macOS 需要 Accessibility 和 Screen Recording。
+- Windows 不使用 macOS TCC,但仍受应用授权和动作检查约束。
+
+### 会话互斥
+
+会话锁确保同一时间只有一个会话控制系统输入。锁带进程和会话信息,并支持失效进程恢复。正常使用时不应要求用户手工删除锁文件。
+
+### 应用白名单与前台检查
+
+输入动作前会读取当前前台应用。如果它不在会话白名单中,动作被拒绝。应用授权按 `read`、`click` 和 `full` 三个等级限制能力。
+
+平台截图能力不同:
+
+| 平台 | `screenshotFiltering` | 行为 |
+|---|---|---|
+| macOS | `native` | 在截图合成层排除未授权应用窗口 |
+| Windows | `none` | 截图可能包含所有可见窗口;输入白名单仍生效 |
+
+Windows 的“输入不会落到未授权应用”不能推导为“截图不会泄露其他窗口”。
+
+### 剪贴板与系统按键
+
+剪贴板读取、写入和系统级组合键使用独立 grant flags。多行输入可能使用剪贴板快路径,执行器会尽力保存并恢复原内容,但只有用户批准相应标志后才能使用。
+
+危险系统组合键还会经过专门的按键检查。普通应用授权不会自动放开退出应用、切换应用或锁屏等系统动作。
+
+### 像素陈旧检查
+
+工具层保留点击位置的像素比较能力,用于检测“模型看到截图后 UI 已变化”的情况。当前默认配置中 `pixelValidation` 为关闭状态,所以模型仍应在界面变化后主动重新截图。
+
+## 当前不提供的能力
+
+### 没有全局 Escape
+
+`src/utils/computerUse/escHotkey.ts` 当前不注册系统级 Escape 中止。桌面 Host 和文档不能宣称用户可以在任何应用中按 Escape 强制停止。
+
+### 不自动隐藏窗口
+
+执行器保留 `prepareForAction()` 和 `previewHideSet()` 接口,但当前 macOS 与 Windows helper 不会根据白名单自动隐藏窗口;回合结束的 `unhideComputerUseApps()` 也是空操作。
+
+因此:
+
+- 不能把 `hideBeforeAction` 配置解释为已经实现的隐私保证。
+- Windows 截图前应由用户自己关闭或最小化敏感窗口。
+- macOS 的隐私保证来自原生截图过滤,而不是窗口自动隐藏。
+
+## Teach 工作流
+
+Teach 是在同一授权和动作调度之上的引导层:
+
+```text
+request_teach_access
+ → 用户批准教学应用
+ → Teach 会话激活
+ → teach_step / teach_batch
+ → 展示提示、等待 Next、执行动作、返回新截图
+ → 用户退出或回合结束
```
-**注意**:子开关(pixelValidation、mouseAnimation 等)仍然保留原始逻辑,可通过配置控制。
-
----
-
-## 二、分层架构
-
-Computer Use 采用 **6 层架构**,每层职责清晰、边界明确:
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ Layer 1 — MCP 工具接口层 │
-│ tools.ts: 24 个工具 schema + 参数校验 │
-│ buildComputerUseTools() → MCP Tool Definition │
-├─────────────────────────────────────────────────────────────┤
-│ Layer 2 — 工具调度与安全控制层 │
-│ toolCalls.ts: handleToolCall() + 9 层安全关卡 │
-│ deniedApps.ts: 191 个应用分类 + 权限等级 │
-├─────────────────────────────────────────────────────────────┤
-│ Layer 3 — MCP 服务器绑定层 │
-│ mcpServer.ts: 会话上下文 + 全局锁 + 截图缓存 │
-│ bindSessionContext() → per-call overrides │
-├─────────────────────────────────────────────────────────────┤
-│ Layer 4 — CLI 集成层 │
-│ wrapper.tsx: 权限对话框 + 状态读写 │
-│ setup.ts: MCP 配置初始化 │
-│ gates.ts: 灰度控制(已绕过) │
-├─────────────────────────────────────────────────────────────┤
-│ Layer 5 — Python Bridge 进程通信层 [补丁] │
-│ pythonBridge.ts: venv 管理 + JSON RPC + 错误处理 │
-│ callPythonHelper(command, payload) → T │
-├─────────────────────────────────────────────────────────────┤
-│ Layer 6 — Python 运行时执行层 [补丁] │
-│ mac_helper.py: pyautogui + mss + pyobjc │
-│ 660 行 Python 实现所有系统交互 │
-└─────────────────────────────────────────────────────────────┘
-```
-
-**标 `[补丁]` 的层**是我们替换/新增的代码,**其余层完全保留**原始 Claude Code 的逻辑。
-
-### 为什么这样分层?
-
-| 层 | 来源 | 可复用性 |
-|----|------|----------|
-| Layer 1-2 | `vendor/computer-use-mcp/` | 平台无关,可用于 Electron、Web 等宿主 |
-| Layer 3 | `vendor/computer-use-mcp/` | 平台无关,MCP 标准协议 |
-| Layer 4 | `utils/computerUse/` | CLI 专用,绑定应用状态 |
-| Layer 5-6 | `utils/computerUse/` + `runtime/` | macOS 专用,Python 实现 |
-
----
-
-## 三、MCP 工具层
-
-### 24 个工具一览
-
-Computer Use 通过 MCP(Model Context Protocol)暴露 24 个工具给模型:
-
-| 类别 | 工具 | 权限等级 |
-|------|------|----------|
-| **权限** | `request_access`, `list_granted_applications` | 无需权限 |
-| **截屏** | `screenshot`, `zoom` | read |
-| **鼠标点击** | `left_click`, `double_click`, `triple_click` | click |
-| **鼠标高级** | `right_click`, `middle_click`, `left_click_drag` | full |
-| **鼠标移动** | `mouse_move`, `cursor_position`, `scroll` | click |
-| **鼠标底层** | `left_mouse_down`, `left_mouse_up` | full |
-| **键盘** | `type`, `key`, `hold_key` | full |
-| **应用** | `open_application`, `switch_display` | full |
-| **剪贴板** | `read_clipboard`, `write_clipboard` | full |
-| **批量** | `computer_batch` | 继承子操作 |
-| **等待** | `wait` | 无需权限 |
-
-### 坐标系统
-
-模型通过两种坐标模式与屏幕交互:
-
-```
-pixels 模式(默认):
- 模型看到截图尺寸 (1176 x 784)
- 模型输出坐标 [588, 392]
- scaleCoord() 转换:
- x_logical = (588 * displayWidth / 1176) + originX
- y_logical = (392 * displayHeight / 784) + originY
-
-normalized_0_100 模式:
- 模型输出坐标 [50, 50](百分比)
- scaleCoord() 转换:
- x_logical = (50 / 100) * displayWidth + originX
- y_logical = (50 / 100) * displayHeight + originY
-```
-
-截图尺寸经过 `imageResize.ts` 计算,确保:
-- 长边 ≤ 1568 像素
-- Token 预算 ≤ 1568(视觉编码器 28px/token)
-- 保持宽高比
-
-### 应用分类系统
-
-`deniedApps.ts` 对 191 个应用进行精细分类:
-
-**浏览器类**(55 个 bundle ID)→ 权限等级 `read`
-```
-Safari, Chrome, Firefox, Arc, Edge, Opera, Brave, Vivaldi...
-理由:浏览器操作应使用 Chrome MCP,而非盲点击
-```
-
-**终端类**(102 个 bundle ID)→ 权限等级 `click`
-```
-Terminal, iTerm2, VS Code, Cursor, JetBrains 全家桶, Xcode...
-理由:终端操作应使用 Bash Tool,限制为只能点击不能输入
-```
-
-**交易类**(34 个 bundle ID)→ 权限等级 `read`
-```
-Webull, Fidelity, Interactive Brokers, Binance, Kraken...
-理由:金融操作风险极高,仅允许截图查看
-```
-
-**完全禁止**(策略拒绝名单):
-```
-Netflix, Spotify, Apple Music, Kindle...
-理由:版权合规,不进入权限对话框直接拒绝
-```
-
----
-
-## 四、安全关卡体系
-
-每个输入操作(点击、键盘、拖拽)在执行前需通过 **9 层安全关卡**:
-
-
-
-### 关卡详解
-
-#### Gate 1:Kill Switch
-```typescript
-if (adapter.isDisabled()) return errorResult("Computer Use is disabled")
-```
-读取 `getChicagoEnabled()` — 在补丁版中永远返回 `true`。
-
-#### Gate 2:TCC 权限检查
-```typescript
-await adapter.ensureOsPermissions()
-// → Python: check_permissions
-// → Accessibility: osascript "tell System Events..."
-// → Screen Recording: CGDisplayCaptureDisplay()
-```
-如果缺少 macOS Accessibility 或 Screen Recording 权限,直接报错。
-
-#### Gate 3:全局互斥锁
-```typescript
-await tryAcquireComputerUseLock(sessionId)
-// 文件锁: ~/.claude/computer-use.lock
-// JSON: { sessionId, pid, acquiredAt }
-```
-确保同一时间只有一个 Claude 会话可以控制电脑。支持 stale PID 恢复。
-
-#### Gate 4:隐藏非白名单应用
-```typescript
-await executor.prepareForAction(allowlistBundleIds)
-// 隐藏所有不在白名单中的应用窗口
-// 确保截图中只出现授权应用
-```
-
-#### Gate 5:前台应用检查
-```typescript
-const frontmost = await executor.getFrontmostApp()
-if (!allowlist.includes(frontmost.bundleId)) {
- return errorResult("Application not authorized")
-}
-```
-即使通过了白名单,还需确认当前前台应用是已授权的。
-
-#### Gate 6:权限等级检查
-
-三级权限模型:
-
-| Tier | 允许的操作 | 禁止的操作 |
-|------|-----------|-----------|
-| `read` | 截图查看 | 任何输入操作 |
-| `click` | 左键点击、滚动 | 右键、拖拽、键盘输入 |
-| `full` | 全部操作 | 无限制 |
-
-```typescript
-function tierSatisfies(tier: CuAppPermTier, required: ActionKind): boolean {
- const order = { read: 0, click: 1, full: 2 }
- return order[tier] >= order[required]
-}
-```
-
-**反绕过机制**:如果权限不足,响应中会附加 `TIER_ANTI_SUBVERSION` 提示,防止模型通过 AppleScript 或 System Events 绕过限制。
-
-#### Gate 7:剪贴板防护
-
-**威胁模型**:
-```
-1. Agent 调用 write_clipboard("rm -rf /")
-2. 切换到 Terminal(click-tier 允许点击)
-3. 模型点击 Terminal 的粘贴按钮
-4. 恶意命令被执行
-```
-
-**防护机制**:
-```
-当 click-tier 应用成为前台时:
- → 保存当前剪贴板内容(stash)
- → 清空剪贴板
- → 每次操作后再次清空
-
-当非 click-tier 应用成为前台时:
- → 恢复原始剪贴板内容
-```
-
-#### Gate 8:像素验证(Staleness Guard)
-
-```
-上次截图 当前实际屏幕
-┌────────────┐ ┌────────────┐
-│ 按钮A │ │ 对话框 │ ← UI 已变化
-│ [756,342] │ │ 确认? │
-└────────────┘ └────────────┘
-
-像素验证:在 [756,342] 取 9×9 网格
- → 对比上次截图 vs 实时截图的像素
- → 不同 → 拒绝点击 + 提示重新截图
- → 相同 → 允许点击
-```
-
-**注意**:补丁版中 `pixelValidation` 默认关闭(`hostAdapter.cropRawPatch()` 返回 null)。
-
-#### Gate 9:系统快捷键拦截
-
-`keyBlocklist.ts` 阻止危险快捷键:
-
-| 快捷键 | 危险操作 |
-|--------|---------|
-| `⌘Q` | 退出应用 |
-| `⇧⌘Q` | 登出系统 |
-| `⌥⌘⎋` | 强制退出对话框 |
-| `⌘Tab` | 应用切换器 |
-| `⌘Space` | Spotlight |
-| `⌃⌘Q` | 锁屏 |
-
----
-
-## 五、Python Bridge 机制
-
-
-
-### 架构设计
-
-原始 Claude Code 使用编译好的 Swift 原生模块(.node NAPI 插件)直接调用 macOS API。我们用 **Python 子进程 + JSON RPC** 替代了这一层:
-
-```
-TypeScript (Bun 运行时) Python (venv)
-┌────────────────────┐ ┌────────────────────┐
-│ executor.ts │ │ mac_helper.py │
-│ │ execFile() │ │
-│ callPythonHelper │ ──────────────→ │ main() │
-│ ('click', │ command + │ ├─ 解析 argv │
-│ {x:756,y:342}) │ --payload JSON │ ├─ dispatch() │
-│ │ │ └─ click() │
-│ ← JSON.parse ──── │ ←────────────── │ pyautogui │
-│ {ok:true, │ stdout JSON │ │
-│ result:true} │ │ json_output(...) │
-└────────────────────┘ └────────────────────┘
-```
-
-### 启动引导流程
-
-首次调用 `callPythonHelper()` 时自动完成环境初始化:
-
-```
-ensureBootstrapped()
- │
- ├─ 检查 .runtime/venv/bin/python3 是否存在
- │ └─ 不存在 → python3 -m venv .runtime/venv/
- │
- ├─ 检查 pip 是否可用
- │ └─ 不可用 → python3 -m ensurepip --upgrade
- │
- ├─ 计算 runtime/requirements.txt 的 SHA256
- │ └─ 与 .runtime/requirements.sha256 对比
- │ └─ 不同 → pip install -r requirements.txt
- │ 写入新的 SHA256 哈希
- │ └─ 相同 → 跳过安装
- │
- └─ 就绪,返回 venv Python 路径
-```
-
-**依赖清单**(`runtime/requirements.txt`):
-
-| 库 | 用途 |
-|----|------|
-| `mss` | 高性能屏幕截图 |
-| `Pillow` | JPEG 编码和图像处理 |
-| `pyautogui` | 鼠标点击、键盘输入 |
-| `pyobjc-core` | macOS Objective-C 桥接 |
-| `pyobjc-framework-Cocoa` | NSWorkspace(应用管理)、NSPasteboard(剪贴板) |
-| `pyobjc-framework-Quartz` | CGDisplay(显示器)、CGWindow(窗口列表) |
-
-### 命令映射表
-
-`mac_helper.py`(660 行)实现了以下命令:
-
-| 命令 | Python 实现 | 返回值 |
-|------|------------|--------|
-| `screenshot` | `mss.grab()` + `PIL.Image` JPEG 编码 | `{base64, width, height, displayWidth, displayHeight}` |
-| `zoom` | `mss.grab(region)` 区域截图 | `{base64, width, height}` |
-| `click` | `pyautogui.moveTo()` + `pyautogui.click()` | `true` |
-| `key` | `pyautogui.hotkey()` / `pyautogui.press()` | `true` |
-| `type` | `pyautogui.write(interval=0.008)` | `true` |
-| `drag` | `pyautogui.dragTo(duration=0.2)` | `true` |
-| `scroll` | `pyautogui.scroll()` / `pyautogui.hscroll()` | `true` |
-| `hold_key` | `pyautogui.keyDown()` + `sleep` + `pyautogui.keyUp()` | `true` |
-| `frontmost_app` | `NSWorkspace.frontmostApplication()` | `{bundleId, displayName}` |
-| `list_displays` | `CGGetActiveDisplayList()` + `CGDisplayBounds()` | `[DisplayGeometry...]` |
-| `find_window_displays` | `CGWindowListCopyWindowInfo()` + 交集计算 | `[{bundleId, displayIds}...]` |
-| `list_installed_apps` | 递归扫描 `/Applications` + `plistlib` | `[InstalledApp...]` |
-| `list_running_apps` | `NSWorkspace.runningApplications()` | `[RunningApp...]` |
-| `open_app` | `NSWorkspace.launchApplicationAtURL_options_` | `void` |
-| `read_clipboard` | `NSPasteboard.stringForType_()` | `string` |
-| `write_clipboard` | `NSPasteboard.setString_forType_()` | `void` |
-| `check_permissions` | `osascript` + `CGDisplayCaptureDisplay` | `{accessibility, screenRecording}` |
-
-### 错误处理
-
-```python
-# mac_helper.py 的统一错误处理
-def main():
- try:
- result = dispatch(command, payload)
- json_output({"ok": True, "result": result})
- except Exception as e:
- error_output({"ok": False, "error": {"message": str(e)}})
-```
-
-TypeScript 端:
-```typescript
-// pythonBridge.ts
-const parsed = JSON.parse(stdout)
-if (!parsed.ok) {
- throw new Error(parsed.error.message) // → MCP tool error
-}
-return parsed.result
-```
-
----
-
-## 六、截图-分析-操作闭环
-
-一个完整的 Computer Use 交互由多个 **截图-分析-操作** 循环组成:
-
-### 典型交互流程
-
-```
-用户: "帮我打开网易云音乐,搜索一首歌"
-
-┌─ 循环 1:发现并打开应用 ─────────────────────────────────┐
-│ │
-│ Step 1: request_access │
-│ → 弹出权限对话框,用户授权允许操作的应用 │
-│ → 设置 allowedApps, grantFlags │
-│ │
-│ Step 2: screenshot │
-│ → 截取全屏 → JPEG 编码 → base64 │
-│ → 缓存截图尺寸 (lastScreenshotDims) │
-│ → 返回给模型 │
-│ │
-│ Step 3: 模型分析截图 │
-│ → "桌面上没有网易云音乐,需要打开它" │
-│ → 决定调用 open_application │
-│ │
-│ Step 4: open_application("com.netease.163music") │
-│ → Gate 1-9 全部通过 │
-│ → Python: NSWorkspace.launchApplicationAtURL_() │
-│ │
-└──────────────────────────────────────────────────────────┘
-
-┌─ 循环 2:定位搜索框 ────────────────────────────────────┐
-│ │
-│ Step 5: screenshot │
-│ → 截取全屏(网易云已打开) │
-│ → 更新 lastScreenshotDims │
-│ │
-│ Step 6: 模型分析截图 │
-│ → 视觉识别搜索框位置 → (756, 342) │
-│ → 决定点击搜索框 │
-│ │
-│ Step 7: left_click({coordinate: [756, 342]}) │
-│ → Gate 4: 隐藏非白名单应用 │
-│ → Gate 5: 前台是网易云 ✓ │
-│ → Gate 6: tier=full ≥ click ✓ │
-│ → Gate 7: 非 click-tier 应用,跳过 │
-│ → scaleCoord(756, 342) 转换为屏幕坐标 │
-│ → Python: pyautogui.click(x_logical, y_logical) │
-│ │
-└──────────────────────────────────────────────────────────┘
-
-┌─ 循环 3:输入搜索词 ────────────────────────────────────┐
-│ │
-│ Step 8: type({text: "喜欢你"}) │
-│ → Gate 6: tier=full ≥ keyboard ✓ │
-│ → Python: pyautogui.write("喜欢你", interval=0.008) │
-│ │
-│ Step 9: screenshot │
-│ → 确认搜索结果已出现 │
-│ → 模型分析:"搜索结果列表中第一个就是目标歌曲" │
-│ │
-│ Step 10: left_click({coordinate: [...]}) │
-│ → 点击目标歌曲 │
-│ │
-└──────────────────────────────────────────────────────────┘
-```
-
-### 坐标转换的关键
-
-**模型看到的**和**屏幕实际的**是不同的坐标空间:
-
-```
-原始屏幕 (2560 x 1600, Retina 2x)
- ├─ 逻辑尺寸: 1280 x 800
- └─ 物理像素: 2560 x 1600
-
-imageResize 计算后:
- ├─ 缩放后尺寸: 1176 x 735 (≤1568px 预算)
- └─ 这就是模型"看到"的截图尺寸
-
-模型输出坐标: [588, 368](图像空间的像素位置)
-
-scaleCoord 转换:
- x_logical = (588 / 1176) * 1280 + originX = 640 + 0 = 640
- y_logical = (368 / 735) * 800 + originY = 400 + 0 = 400
-
-Python 执行:
- pyautogui.moveTo(640, 400) ← 逻辑坐标(macOS 自动处理 Retina)
-```
-
-### 截图缓存与状态同步
-
-```
-bindSessionContext 闭包
- │
- ├─ lastScreenshot (内存)
- │ ├─ base64: JPEG 数据(用于 pixel validation)
- │ ├─ width/height: 模型看到的尺寸
- │ └─ displayWidth/displayHeight/originX/originY: 显示器几何
- │
- └─ AppState.computerUseMcpState (持久化)
- ├─ allowedApps: AppGrant[] — 已授权应用列表
- ├─ grantFlags: {...} — 剪贴板/系统快捷键权限
- ├─ selectedDisplayId?: number — 选定的显示器
- ├─ lastScreenshotDims?: {...} — 截图几何(跨重启恢复)
- └─ hiddenDuringTurn?: Set — 本轮隐藏的应用
-```
-
----
-
-## 七、关键源文件索引
-
-### vendor/computer-use-mcp/(原始代码层)
-
-| 文件 | 行数 | 职责 |
-|------|------|------|
-| `types.ts` | 622 | 权限模型、会话上下文、全部类型定义 |
-| `tools.ts` | 707 | 24 个 MCP 工具的 schema 和参数校验 |
-| `toolCalls.ts` | 1600+ | **核心**:工具派发、9 层安全关卡、权限流程 |
-| `deniedApps.ts` | 554 | 191 个应用的分类(browser/terminal/trading)和权限映射 |
-| `sentinelApps.ts` | 44 | 敏感应用预警标签(shell/filesystem/system_settings) |
-| `mcpServer.ts` | 314 | MCP 服务器工厂、会话上下文绑定、全局锁 |
-| `pixelCompare.ts` | 172 | 点击目标像素验证(staleness guard) |
-| `imageResize.ts` | 109 | 截图尺寸计算(API 图像转码算法) |
-| `keyBlocklist.ts` | 154 | 系统快捷键拦截(⌘Q、⌘Tab 等) |
-| `executor.ts` | 101 | ComputerExecutor 接口定义 |
-| `subGates.ts` | 20 | 灰度子开关预设组合 |
-
-### utils/computerUse/(CLI 适配层)
-
-| 文件 | 行数 | 职责 | 补丁? |
-|------|------|------|-------|
-| `executor.ts` | 231 | ComputerExecutor 的 Python bridge 实现 | ✅ 重写 |
-| `pythonBridge.ts` | 111 | Python 子进程管理、venv 引导、JSON RPC | ✅ 新增 |
-| `hostAdapter.ts` | 54 | HostAdapter 实现(权限检查、灰度读取) | 部分修改 |
-| `gates.ts` | 51 | GrowthBook 灰度控制(`getChicagoEnabled` 绕过) | ✅ 修改 |
-| `wrapper.tsx` | 300+ | 会话上下文构建、权限对话框、锁管理 | 未修改 |
-| `setup.ts` | 54 | MCP 配置初始化 | 未修改 |
-| `computerUseLock.ts` | 216 | 全局文件锁(`~/.claude/computer-use.lock`) | 未修改 |
-| `common.ts` | 62 | 常量定义(server name、bundle ID) | 未修改 |
-| `cleanup.ts` | — | turn-end 清理(应用恢复、剪贴板恢复) | 未修改 |
-| `toolRendering.tsx` | — | 工具结果 UI 渲染 | 未修改 |
-
-### runtime/(Python 运行时)
-
-| 文件 | 行数 | 职责 | 补丁? |
-|------|------|------|-------|
-| `mac_helper.py` | 660 | 所有系统交互的 Python 实现 | ✅ 新增 |
-| `requirements.txt` | 6 | Python 依赖声明 | ✅ 新增 |
-
----
-
-## 八、设计权衡
-
-### 为什么选择 Python Bridge?
-
-| 维度 | 原生 Swift (.node) | Python Bridge |
-|------|-------------------|---------------|
-| **性能** | ~0ms(进程内调用) | ~50-100ms(子进程启动) |
-| **可读性** | 编译后不可读 | 660 行清晰的 Python |
-| **可修改性** | 需要 Swift 编译环境 | 直接编辑 .py 文件 |
-| **依赖** | 特定 Bun 版本的 NAPI | 任意 Python 3.8+ |
-| **跨平台** | 仅 macOS | pyautogui/mss 天然跨平台 |
-| **用户体验** | 无感知 | 无感知(模型思考时间秒级) |
-
-**结论**:50-100ms 的额外延迟在 Computer Use 的场景下完全可以忽略——模型分析截图和决策的时间通常是 2-5 秒,用户不会注意到底层操作多了 100ms。
-
-### 我们尝试过但放弃的方案
-
-**方案一:提取原生 .node 模块**
-- 从 Claude Code 二进制中成功提取了 `computer-use-swift.node`(ARM64 424KB)
-- 同步方法正常工作,但 **Swift 异步方法的 continuation 永远不会 resume**
-- 根因:.node 文件针对 Claude Code 内置 Bun 编译,与用户 Bun 版本不兼容
-
-**方案二:空 Stub 包**
-- 代码能编译但所有操作报错——没有实际执行能力
-
----
-
-## 相关文档
-
-- [Computer Use 功能指南](./computer-use.md) — 使用方式、快速开始、环境变量
-- [源码修复记录](/reference/fixes) — 其他修复和补丁的详细记录
+关键约束:
+
+- Teach 使用独立授权入口,不继承普通控制中的剪贴板和系统按键标志。
+- `teach_step` 的文案是教学覆盖层中用户能看到的主要说明。
+- `teach_batch` 适合界面可预判的连续步骤;界面变化不可预判时应回到单步。
+- Teach 活动期间不能弹出会被隐藏的普通授权对话框。
+- 用户选择退出后,后续 Teach 调用必须停止。
+- 是否公开 Teach 工具由宿主 capability 决定。
+
+## Python Bridge
+
+`src/utils/computerUse/pythonBridge.ts` 负责:
+
+1. 确定用户配置目录中的 `.runtime`。
+2. 同步当前平台的 helper 和 requirements。
+3. 使用自动检测或用户配置的 Python 创建 venv。
+4. 依据 requirements 哈希安装或更新依赖。
+5. 以 `command + JSON payload` 调用 helper。
+6. 把统一 JSON 结果或错误返回 TypeScript。
+
+运行时文件:
+
+| 路径 | 职责 |
+|---|---|
+| `runtime/mac_helper.py` | macOS 截图、应用、鼠标、键盘和剪贴板 |
+| `runtime/win_helper.py` | Windows 对应实现 |
+| `runtime/requirements.txt` | macOS Python 依赖 |
+| `runtime/requirements-win.txt` | Windows Python 依赖 |
+
+Helper 每次调用是一个有边界的子进程请求。它不会直接读取模型状态;所有会话授权和动作策略都在 TypeScript 层决定。
+
+## Host 集成
+
+### CLI
+
+`src/utils/computerUse/` 负责:
+
+- 判断 macOS/Windows 支持状态
+- 创建 `ComputerExecutor`
+- 构造动态 MCP
+- 绑定权限 UI 和会话状态
+- 管理预授权应用及 Python 路径
+
+### Desktop
+
+桌面设置页通过 `src/server/api/computer-use.ts`:
+
+- 读取和修改启用状态
+- 检查与安装 Python 运行时
+- 检查 macOS 系统权限
+- 管理预授权应用
+- 管理剪贴板和系统组合键标志
+
+桌面设置只是配置入口。实际工具调用仍在 CLI 会话的 MCP 和授权边界内执行。
+
+## 关键源文件
+
+| 路径 | 职责 |
+|---|---|
+| `src/vendor/computer-use-mcp/tools.ts` | 工具 schema 和 Teach 工具 |
+| `src/vendor/computer-use-mcp/toolCalls.ts` | 动作分发、授权与安全检查 |
+| `src/vendor/computer-use-mcp/mcpServer.ts` | MCP Server 和会话绑定 |
+| `src/vendor/computer-use-mcp/types.ts` | capability、授权和会话类型 |
+| `src/utils/computerUse/common.ts` | 平台支持和 capability |
+| `src/utils/computerUse/gates.ts` | 启用开关和子能力默认值 |
+| `src/utils/computerUse/executor.ts` | Python Bridge 执行器 |
+| `src/utils/computerUse/pythonBridge.ts` | venv、依赖和子进程协议 |
+| `src/utils/computerUse/wrapper.tsx` | CLI 权限交互和会话上下文 |
+| `src/server/api/computer-use.ts` | 桌面设置 API |
+| `desktop/src/pages/ComputerUseSettings.tsx` | 桌面设置界面 |
+
+## 修改时的验证重点
+
+- 工具 schema 与 dispatch 支持的 action 保持同步。
+- Tool 描述的坐标模式与执行器换算一致。
+- macOS 与 Windows capability 文案不混用。
+- 未授权前台应用的输入必须被拒绝。
+- Windows 测试不能假设截图过滤存在。
+- Teach capability 关闭时不应公开 Teach 工具。
+- 配置迁移不能覆盖用户未知字段。
+- Python 测试使用临时配置目录,不读取真实用户运行时。
diff --git a/docs/features/computer-use.md b/docs/features/computer-use.md
index 8bd3cdaa..70c8293f 100644
--- a/docs/features/computer-use.md
+++ b/docs/features/computer-use.md
@@ -1,176 +1,54 @@
-# Computer Use 功能指南
+# Computer Use 使用指南
+Computer Use 允许模型读取屏幕并操作鼠标、键盘、应用和剪贴板。它直接作用于当前电脑,启用前请先理解授权范围和平台差异。
-> **魔改说明**:本功能是基于 Claude Code 泄露源码中的 Computer Use(内部代号 "Chicago")进行的**深度改造版本**。官方实现依赖 Anthropic 内部私有原生模块(`@ant/computer-use-swift`、`@ant/computer-use-input`),无法公开获取。我们**替换了整个底层操作层**,使用 Python bridge 实现所有系统交互——macOS 使用 `pyautogui` + `mss` + `pyobjc`,Windows 使用 `pyautogui` + `mss` + `win32gui` + `psutil`,使得任何人都可以在 macOS 和 Windows 上运行 Computer Use 功能。
+## 支持平台
----
+| 平台 | 状态 | 关键差异 |
+|---|---|---|
+| macOS Apple Silicon / Intel | 支持 | 需要“辅助功能”和“屏幕录制”权限;支持原生截图过滤 |
+| Windows | 支持 | 使用 Windows Python 运行时;截图不会过滤未授权窗口 |
+| Linux | 不支持 | 当前没有 Linux 执行器 |
-## 目录
-
-- [功能简介](#功能简介)
-- [支持的设备与平台](#支持的设备与平台)
-- [工作原理](#工作原理)
-- [快速开始](#快速开始)
-- [使用方式](#使用方式)
-- [安全机制](#安全机制)
-- [环境变量](#环境变量)
-- [技术架构详解](#技术架构详解)
-- [我们尝试过的方案](#我们尝试过的方案)
-- [已知限制](#已知限制)
-- [参考项目与致谢](#参考项目与致谢)
-
----
-
-## 功能简介
-
-Computer Use 让 AI 模型能够**直接控制你的电脑**——截屏、移动鼠标、点击按钮、输入文字、管理应用窗口。
-
-支持的操作(共 24 个 MCP 工具):
-
-| 类别 | 工具 |
-|------|------|
-| 截屏 | `screenshot`、`zoom` |
-| 鼠标 | `left_click`、`right_click`、`middle_click`、`double_click`、`triple_click`、`left_click_drag`、`mouse_move`、`left_mouse_down`、`left_mouse_up`、`cursor_position`、`scroll` |
-| 键盘 | `type`、`key`、`hold_key` |
-| 应用 | `open_application`、`switch_display` |
-| 权限 | `request_access`、`list_granted_applications` |
-| 剪贴板 | `read_clipboard`、`write_clipboard` |
-| 其他 | `wait`、`computer_batch` |
-
----
-
-## 支持的设备与平台
-
-| 平台 | 芯片 | 状态 | 说明 |
-|------|------|------|------|
-| macOS | Apple Silicon (M1/M2/M3/M4) | ✅ 完整支持 | 推荐平台 |
-| macOS | Intel x86_64 | ✅ 完整支持 | |
-| Windows | x64 | ✅ 完整支持 | 使用 `win32gui` + `psutil` + `pyperclip` + `screeninfo` 替代 macOS 专有 API |
-| Linux | 任意 | ⚠️ 理论可行 | 需替换平台专有部分为 `wmctrl` + `xdotool`,当前未适配 |
-
-### 运行环境要求
-
-- [Bun](https://bun.sh) >= 1.1.0
-- Python >= 3.8(首次运行自动创建 venv 并安装依赖)
-- **macOS**:系统权限 Accessibility(辅助功能)+ Screen Recording(屏幕录制)
-- **Windows**:无需额外权限配置
-
----
-
-## 工作原理
-
-Computer Use 的核心是一个**截图-分析-操作**的闭环:
-
-```
-┌──────────────────────────────────────────────┐
-│ AI 模型(Claude / 其他 Anthropic 协议模型) │
-│ │
-│ 1. 收到用户请求 "打开网易云搜索喜欢你" │
-│ 2. 调用 screenshot 工具 → 收到屏幕截图 │
-│ 3. 模型分析截图像素,识别 UI 元素位置 │
-│ → "搜索框在 (756, 342)" │
-│ 4. 调用 left_click { coordinate: [756, 342] } │
-│ 5. 调用 type { text: "喜欢你" } │
-│ 6. 再次 screenshot → 确认结果 → 下一步... │
-└──────────────┬───────────────────────────────┘
- │ MCP Tool Call
- ▼
-┌──────────────────────────────────────────────┐
-│ TypeScript 工具层 │
-│ (vendor/computer-use-mcp) │
-│ │
-│ - 安全检查(应用白名单、TCC 权限) │
-│ - 坐标模式转换(pixels / normalized) │
-│ - 工具分发 → executor │
-└──────────────┬───────────────────────────────┘
- │ callPythonHelper()
- ▼
-┌──────────────────────────────────────────────┐
-│ Python Bridge │
-│ macOS: runtime/mac_helper.py │
-│ Windows: runtime/win_helper.py │
-│ │
-│ pyautogui.click(756, 342) ← 鼠标控制 │
-│ mss.grab(monitor) ← 截图 │
-│ macOS: NSWorkspace.open() ← 应用管理 │
-│ Windows: win32gui / psutil ← 应用管理 │
-└──────────────────────────────────────────────┘
-```
-
-**关键:坐标分析完全由模型的视觉能力完成**——模型"看"截图就像人看屏幕一样,直接从像素中识别按钮、输入框等 UI 元素的位置。
-
----
+打包桌面端不要求用户安装 Bun。Computer Use 需要可用的 Python 3,应用会在用户配置目录下创建隔离的虚拟环境并安装平台依赖。
## 快速开始
-### 1. 安装依赖
+### 桌面端
+
+1. 打开“设置 → Computer Use”。
+2. 开启 Computer Use。
+3. 检查 Python 状态;自动检测失败时,可选择 Python 可执行文件。
+4. 执行安装或修复,让应用创建虚拟环境和安装依赖。
+5. macOS 用户授予“辅助功能”和“屏幕录制”权限,然后重新检查。
+6. 选择允许控制的应用,并按需要开启剪贴板和系统组合键权限。
+7. 新建会话,用自然语言描述目标和允许操作的应用。
+
+可以从简单、可撤销的任务开始:
+
+```text
+截取当前屏幕并告诉我你看到了什么。
+打开 Notes,新建一条标题为“测试”的空白笔记。
+在已授权的应用中,帮我找到设置入口,但不要修改任何内容。
+```
+
+### CLI
+
+源码模式需要先安装项目依赖,并保证 Python 3 可用:
```bash
bun install
-```
-
-### 2. 确保 Python 3 可用
-
-```bash
-python3 --version # 需要 >= 3.8
-```
-
-> Python 依赖会在首次使用 Computer Use 时**自动安装**到 `.runtime/venv/`,无需手动操作。
-
-### 3. 授予 macOS 权限
-
-#### Accessibility(辅助功能)
-
-```bash
-open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"
-```
-
-将你的**终端应用**(如 iTerm、Terminal、Ghostty 等)添加到允许列表。
-
-#### Screen Recording(屏幕录制)
-
-```bash
-open "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture"
-```
-
-同样添加你的终端应用。授权后可能需要**重启终端**。
-
-### 4. 启动
-
-```bash
+python3 --version
./bin/claude-haha
```
-### 5. 使用
-
-在对话中用自然语言请求即可:
-
-```
-> 帮我打开网易云音乐,搜索一首歌
-> 截个屏看看当前桌面
-> 帮我在 VS Code 里打开终端
-```
-
----
-
-## 使用方式
-
-首次使用 Computer Use 时,系统会弹出**应用授权对话框**,你需要选择允许 Claude 操作的应用。
-
-- 模型会先调用 `request_access` 请求权限
-- 你在终端中确认允许哪些应用
-- 之后模型就可以截图、点击、输入了
-
-### 禁用 Computer Use
-
-如果你只想使用普通 Coding Agent,不希望暴露 `computer-use` MCP 工具,可以使用任一方式禁用:
+可以通过环境变量关闭动态 Computer Use MCP:
```bash
-claude-haha --no-computer-use
-CLAUDE_COMPUTER_USE_ENABLED=0 claude-haha
+CLAUDE_COMPUTER_USE_ENABLED=0 ./bin/claude-haha
```
-也可以写入全局配置文件 `~/.claude/cc-haha/computer-use-config.json`:
+也可以在 `~/.claude/cc-haha/computer-use-config.json` 中设置:
```json
{
@@ -178,164 +56,134 @@ CLAUDE_COMPUTER_USE_ENABLED=0 claude-haha
}
```
-桌面端的 Settings > Computer Use 开关写入同一个配置。关闭后,新会话不会注入 `computer-use` 动态 MCP,也不会把桌面控制工具加入 `allowedTools`。
+桌面设置页修改的是同一份托管配置。优先使用设置页,不要手工覆盖其中未知字段。
----
+## 工具与 Teach 能力
-## 安全机制
+代码中定义了 27 个 Computer Use 工具:
-| 机制 | 说明 |
-|------|------|
-| **应用白名单** | 每次会话需要明确授权允许操作的应用 |
-| **并发保护** | 同一时间只有一个 Claude 会话可使用 Computer Use(文件锁机制) |
-| **剪贴板保护** | 通过剪贴板输入文本时会自动保存和恢复原始剪贴板内容 |
-| **操作确认** | 敏感操作(如系统快捷键)需要额外授权 |
+| 类别 | 工具 |
+|---|---|
+| 授权 | `request_access`、`list_granted_applications` |
+| 截图 | `screenshot`、`zoom` |
+| 鼠标 | `left_click`、`right_click`、`middle_click`、`double_click`、`triple_click`、`left_click_drag`、`mouse_move`、`left_mouse_down`、`left_mouse_up`、`cursor_position`、`scroll` |
+| 键盘 | `type`、`key`、`hold_key` |
+| 应用 | `open_application`、`switch_display` |
+| 剪贴板 | `read_clipboard`、`write_clipboard` |
+| 控制流 | `wait`、`computer_batch` |
+| Teach | `request_teach_access`、`teach_step`、`teach_batch` |
-> 注意:由于底层改为 Python bridge,原生方案中的全局 Escape 快捷键中止和操作前自动隐藏应用功能暂不可用。可使用 `Ctrl+C` 中止。
+基础控制能力包含 24 个工具。Teach 的 3 个工具只在宿主启用 Teach capability 时公开;未启用时,当前会话只会看到基础工具。
----
+Teach 用于“带着用户一步一步操作”的场景:
-## 环境变量
+1. `request_teach_access` 请求教学所需的应用授权。
+2. `teach_step` 展示一个带锚点的说明,并等待用户点击下一步。
+3. `teach_batch` 把可以预判的多个教学步骤合并,减少模型往返。
-| 变量 | 默认值 | 说明 |
-|------|--------|------|
-| `CLAUDE_COMPUTER_USE_ENABLED` | `1` | 设为 `0` 可禁用 Computer Use |
-| `CLAUDE_COMPUTER_USE_COORDINATE_MODE` | `pixels` | 坐标模式:`pixels` 或 `normalized_0_100` |
-| `CLAUDE_COMPUTER_USE_CLIPBOARD_PASTE` | `1` | 是否启用剪贴板粘贴输入 |
-| `CLAUDE_COMPUTER_USE_MOUSE_ANIMATION` | `1` | 是否启用鼠标动画 |
-| `CLAUDE_COMPUTER_USE_HIDE_BEFORE_ACTION` | `0` | 操作前是否隐藏其他窗口 |
-| `CLAUDE_COMPUTER_USE_DEBUG` | `0` | 调试模式 |
+Teach 授权与普通控制授权相互独立。教学步骤仍经过应用白名单和输入安全检查;用户退出教学后,模型不应继续调用 Teach 工具。
----
+## 工作原理
-## 技术架构详解
+Computer Use 使用“截图 → 分析 → 操作 → 再截图”的闭环:
-### 整体分层
-
-```
-src/
-├── vendor/computer-use-mcp/ ← MCP 工具定义与分发(12 个文件)
-│ ├── tools.ts ← 24 个工具的 schema 定义
-│ ├── toolCalls.ts ← 安全检查 + 工具分发
-│ ├── mcpServer.ts ← MCP 服务器创建
-│ ├── types.ts ← 全部类型定义
-│ └── ...
-├── utils/computerUse/
-│ ├── executor.ts ← 执行器(调用 Python bridge)
-│ ├── pythonBridge.ts ← Python 子进程管理
-│ ├── hostAdapter.ts ← 权限检查适配器
-│ ├── gates.ts ← 功能开关(已绕过灰度)
-│ ├── wrapper.tsx ← MCP 工具覆写层
-│ ├── setup.ts ← MCP 配置初始化
-│ └── ...
-└── runtime/
- ├── mac_helper.py ← macOS Python 实现
- ├── win_helper.py ← Windows Python 实现
- ├── requirements.txt ← macOS Python 依赖
- └── requirements-win.txt ← Windows Python 依赖
+```text
+模型
+ → 调用 Computer Use MCP 工具
+ → TypeScript 调度与安全检查
+ → Python Bridge
+ → macOS / Windows 系统操作
+ → 截图或操作结果返回模型
```
-### 灰度控制绕过
+- 工具定义与授权逻辑位于 `src/vendor/computer-use-mcp/`。
+- CLI 集成和 Python Bridge 位于 `src/utils/computerUse/`。
+- 平台执行器位于 `runtime/mac_helper.py` 和 `runtime/win_helper.py`。
+- 桌面安装、权限和预授权由 `src/server/api/computer-use.ts` 与 `desktop/src/pages/ComputerUseSettings.tsx` 管理。
-官方 Claude Code 中 Computer Use 通过三层门控限制访问:
+## 授权模型
-| 层级 | 原始机制 | 我们的处理 |
-|------|----------|-----------|
-| 编译时 | `feature('CHICAGO_MCP')` (Bun 编译宏) | 替换为 `true` |
-| 订阅检查 | `hasRequiredSubscription()` (Max/Pro) | `getChicagoEnabled()` 直接返回 `true` |
-| 远程配置 | GrowthBook `tengu_malort_pedway` | 同上,不再依赖远程配置 |
-| 默认禁用 | `isDefaultDisabledBuiltin('computer-use')` | `isDefaultDisabledBuiltin()` 返回 `false` |
+### 应用授权
-### Python Bridge 工作机制
+模型必须先调用 `request_access`,说明需要哪些应用以及原因。用户可以批准或拒绝。设置页中的预授权应用是默认授权配置,不代表任意应用都可以被控制;运行中新增应用仍需要走相应授权流程。
-```typescript
-// pythonBridge.ts
-async function callPythonHelper(command: string, payload: object): Promise {
- await ensureBootstrapped() // 首次调用自动创建 venv + pip install
-
- // 调用: python3 runtime/mac_helper.py --payload '{...}'
- const result = execFile(pythonBin, ['mac_helper.py', command, '--payload', JSON.stringify(payload)])
-
- return JSON.parse(result.stdout) // { ok: true, result: T }
-}
-```
+应用按能力分为三个等级:
-首次运行自动完成:
-1. 创建 Python 虚拟环境 (`.runtime/venv/`)
-2. 安装 pip
-3. 按平台安装依赖:
- - **macOS**: `mss`, `Pillow`, `pyautogui`, `pyobjc-*`(`requirements.txt`)
- - **Windows**: `mss`, `Pillow`, `pyautogui`, `win32gui`, `psutil`, `pyperclip`, `screeninfo`(`requirements-win.txt`)
-4. SHA256 哈希验证(仅 requirements 变更时重新安装)
+| 等级 | 能力 |
+|---|---|
+| `read` | 读取截图,不执行输入 |
+| `click` | 点击、移动和滚动,不输入文字或执行高权限动作 |
+| `full` | 在其他安全检查通过后允许键盘、拖拽等完整操作 |
----
+### 剪贴板与系统组合键
-## 我们尝试过的方案
+剪贴板读取、剪贴板写入和系统级组合键是单独的授权标志。允许控制某个应用,不会自动获得这些权限。
-### 方案一:从 Claude Code 二进制提取原生 .node 模块 ❌
+### 并发
-**思路**:从已安装的 Claude Code 二进制 (`~/.local/share/claude/versions/2.1.91`,189MB Mach-O) 中定位并提取嵌入的原生 NAPI 模块。
+Computer Use 使用会话锁防止多个会话同时争夺鼠标和键盘。看到“正在被其他会话使用”时,应先停止或完成原会话,而不是删除锁文件。
-**实施**:
-- 成功从 Bun `$bunfs` 虚拟文件系统中提取了 `computer-use-swift.node` (ARM64 424KB + x64 430KB) 和 `computer-use-input.node` (ARM64 836KB + x64 821KB)
-- 同步方法(TCC 权限检查、显示枚举)正常工作
-- 创建了 npm 包装包并通过 workspace 注册
+## 平台安全边界
-**失败原因**:
-- Swift 异步方法(`screenshot.captureExcluding`)的 continuation 永远不会 resume
-- 根因:提取的 .node 文件是针对 Claude Code 内置的 Bun 运行时编译的,与用户系统的 Bun 版本的 N-API 异步实现不兼容
-- 错误信息:`SWIFT TASK CONTINUATION MISUSE: captureScreenWithExclusion leaked its continuation without resuming it`
+### macOS
-### 方案二:创建空 Stub 包 ❌
+- 需要“辅助功能”才能输入和操作应用。
+- 需要“屏幕录制”才能截图。
+- 截图支持原生窗口过滤,只保留授权应用和桌面。
-**思路**:为 `@ant/computer-use-mcp`、`@ant/computer-use-input`、`@ant/computer-use-swift` 创建最小化的 stub 包,使代码能编译加载。
+### Windows
-**失败原因**:代码能编译但 MCP 服务器注册后无法执行任何实际操作——截图、点击等全部报错。
+- 当前截图过滤能力为 `none`:截图中可能出现所有可见窗口。
+- 应用白名单仍会阻止把输入动作发送给未授权的前台应用。
+- 因为截图本身不做过滤,开始前应主动关闭或最小化包含敏感信息的窗口。
-### 方案三:Python Bridge 替代原生模块 ✅(当前方案)
+### 当前明确没有的保护
-**思路**:参考 [wimi321/macos-computer-use-skill](https://github.com/wimi321/macos-computer-use-skill),用 Python 子进程替代所有原生模块调用。
+- **没有全局 Escape 中止热键。** 桌面端应使用当前任务的停止操作;CLI 运行可用终端中断。
+- **不会在每次操作前自动隐藏未授权窗口。** 不要依赖自动隐藏来保护敏感内容。
+- **像素陈旧验证默认关闭。** UI 变化后,模型应重新截图再点击。
-**优势**:
-- 零二进制依赖,不依赖特定 Bun/Node 版本
-- 纯 Python 实现,首次运行自动安装
-- 截图、鼠标、键盘、应用管理全部可用
-- macOS ARM64 + x86_64 均支持
+这些限制是当前实现边界,不应在文档或 UI 中描述成已经可用。
----
+## Python 运行时
-## 已知限制
+首次安装或修复时,应用会:
-| 限制 | 说明 |
-|------|------|
-| 不支持 Linux | 需要适配平台专有 API 部分 |
-| 无全局 Escape 中止 | 原生方案用 CGEventTap 实现,Python 版暂不支持,用 `Ctrl+C` 代替 |
-| 操作前不自动隐藏窗口 | 原生方案的 `prepareDisplay` 依赖 Swift,Python 版未实现 |
-| 性能略低 | Python 进程启动 ~100ms 开销,但模型思考时间通常是秒级,用户感知不到 |
-| 像素验证关闭 | `pixelValidation` 默认关闭 |
+1. 把当前平台的 helper 和 requirements 同步到用户配置目录。
+2. 使用自动检测或用户选择的 Python 创建 venv。
+3. 安装或升级 pip。
+4. 按 requirements 内容哈希决定是否重新安装依赖。
+5. 通过 JSON payload 调用平台 helper,并解析统一 JSON 结果。
----
+macOS 主要依赖 `mss`、Pillow、PyAutoGUI 和 PyObjC;Windows 还使用 pywin32、psutil、pyperclip 与 screeninfo。准确版本约束以 `runtime/requirements*.txt` 为准。
-## 参考项目与致谢
+## 故障排查
-本功能的实现参考了以下开源项目,在此致以感谢:
+### macOS 仍提示缺少权限
-| 项目 | 许可证 | 贡献 |
-|------|--------|------|
-| [wimi321/macos-computer-use-skill](https://github.com/wimi321/macos-computer-use-skill) | MIT | Python bridge 架构、`mac_helper.py` 运行时、`executor.ts` 适配方案。该项目从 Claude Code 工作流中提取了可复用的 TypeScript 逻辑,并用完全公开的 Python 库替代了私有原生模块 |
-| [domdomegg/computer-use-mcp](https://github.com/domdomegg/computer-use-mcp) | MIT | 独立的 Computer Use MCP 服务器实现(基于 nut.js),跨平台可用。在方案调研阶段提供了参考 |
-| [paoloanzn/free-code](https://github.com/paoloanzn/free-code) | - | Feature flag 系统分析和构建系统参考 |
-| [oboard/claude-code-rev](https://github.com/oboard/claude-code-rev) | - | 泄露源码的早期恢复工作,提供了 stub 包的参考实现 |
+- 确认授权的是实际启动 Claude Code Haha 的应用。
+- 权限变更后完全退出并重新打开应用。
+- 在设置页重新执行权限检查。
-### 底层依赖
+### Python 安装失败
-| 库 | 平台 | 用途 |
-|----|------|------|
-| [pyautogui](https://github.com/asweigart/pyautogui) | 跨平台 | 鼠标和键盘控制 |
-| [mss](https://github.com/BoboTiG/python-mss) | 跨平台 | 屏幕截图 |
-| [Pillow](https://github.com/python-pillow/Pillow) | 跨平台 | 图像处理和压缩 |
-| [pyobjc](https://github.com/ronaldoussoren/pyobjc) | macOS | Cocoa/Quartz 框架绑定(应用管理、显示枚举) |
-| [pywin32](https://github.com/mhammond/pywin32) | Windows | Win32 API 绑定(窗口管理) |
-| [psutil](https://github.com/giampaolo/psutil) | Windows | 进程管理(应用列表、进程操作) |
-| [pyperclip](https://github.com/asweigart/pyperclip) | Windows | 剪贴板操作 |
-| [screeninfo](https://github.com/rr-/screeninfo) | Windows | 显示器信息(多屏支持) |
+- 在设置页选择明确的 Python 3 可执行文件。
+- 确认该 Python 支持 `venv`。
+- 使用“安装/修复”重新创建运行时。
+- 查看“诊断”页中的 Computer Use 安装日志。
+
+### 截图可以但点击失败
+
+- 确认目标应用处于授权列表。
+- 确认它是当前前台应用。
+- 检查授权等级是否允许该动作。
+- UI 已变化时重新截图,不要复用旧坐标。
+
+### Windows 截图出现其他窗口
+
+这是当前 Windows 截图能力的已知边界。输入白名单不会过滤截图内容;执行前请关闭或最小化敏感窗口。
+
+## 深入阅读
+
+- [Computer Use 架构](./computer-use-architecture.md)
+- [桌面端架构](../desktop/02-architecture.md)
diff --git a/docs/guide/cli-reference.md b/docs/guide/cli-reference.md
new file mode 100644
index 00000000..ae0a52b9
--- /dev/null
+++ b/docs/guide/cli-reference.md
@@ -0,0 +1,162 @@
+# CLI 参考
+
+Claude Code Haha 默认启动交互式会话,也可以用 `--print` 作为脚本、CI 或其他程序中的非交互式 Agent。源码仓库中的命令是 `./bin/claude-haha`;安装后的可执行文件名称以安装方式为准。
+
+```bash
+./bin/claude-haha --help
+```
+
+`--help` 是当前版本参数的最终依据。本页按任务解释最常用的参数,避免复制一份很快过期的完整帮助输出。
+
+## 快速示例
+
+```bash
+# 在当前目录开始交互式会话
+./bin/claude-haha
+
+# 带着第一条需求进入会话
+./bin/claude-haha "解释这个仓库的启动路径"
+
+# 返回一段文本后退出
+./bin/claude-haha --print "总结最近一次提交"
+
+# 返回一个完整 JSON 结果
+./bin/claude-haha --print --output-format json "列出主要模块"
+
+# 继续当前目录最近的会话
+./bin/claude-haha --continue
+
+# 恢复指定会话,同时创建新的会话分支
+./bin/claude-haha --resume --fork-session
+
+# 在新 Git worktree 中工作
+./bin/claude-haha --worktree docs-refresh
+```
+
+## 先理解 `--print` 的安全边界
+
+`-p, --print` 会跳过工作区信任对话框。它仍会读取启用的用户、项目和本地设置;可信项目中的 hooks、MCP、工具权限和环境配置也可能影响运行。
+
+因此:
+
+- 只在你信任的目录中运行 `--print`、`doctor` 和自动化任务。
+- 处理陌生仓库时,先检查 `.claude/`、`.mcp.json`、插件和工作区说明。
+- `--dangerously-skip-permissions` 会绕过所有权限检查,只适合无网络、可丢弃的隔离沙箱。
+- `--allow-dangerously-skip-permissions` 只是允许会话选择绕过模式,并不会自动开启它。
+- 自动化中明确限制 `--tools`、`--allowed-tools`、预算和可访问目录。
+
+## 非交互输入与输出
+
+| 参数 | 说明 |
+|------|------|
+| `-p, --print` | 处理请求,输出结果并退出 |
+| `--output-format text` | 纯文本;默认格式 |
+| `--output-format json` | 单个完整 JSON 结果 |
+| `--output-format stream-json` | 持续输出 JSON 事件 |
+| `--input-format text` | 普通文本输入;默认格式 |
+| `--input-format stream-json` | 从标准输入持续接收 JSON 事件 |
+| `--json-schema ''` | 要求最终结构化输出符合 JSON Schema |
+| `--include-partial-messages` | 在 `stream-json` 中包含增量消息片段 |
+| `--include-hook-events` | 在 `stream-json` 中包含 hook 生命周期事件 |
+| `--replay-user-messages` | 流式输入输出时,把用户消息重新发到标准输出作为确认 |
+| `--no-session-persistence` | 非交互运行后不保存会话 |
+| `--max-budget-usd ` | 为 `--print` 设置最高 API 费用 |
+
+结构化输出示例:
+
+```bash
+./bin/claude-haha --print \
+ --output-format json \
+ --json-schema '{"type":"object","properties":{"risk":{"type":"string"}},"required":["risk"]}' \
+ "审查当前改动,只返回风险级别"
+```
+
+流式输出是事件协议,不等同于把普通 JSON 拆成多行。消费端应按 `type` 处理事件,并允许新事件字段出现。
+
+## 会话与工作区
+
+| 参数 | 说明 |
+|------|------|
+| `-c, --continue` | 继续当前目录最近一次会话 |
+| `-r, --resume [value]` | 按会话 ID 恢复;省略值时打开选择器,也可传搜索词 |
+| `--fork-session` | 恢复时创建新会话 ID,不覆盖原会话路径 |
+| `--session-id ` | 指定本次会话 ID |
+| `-n, --name ` | 设置在恢复列表和终端标题中显示的名称 |
+| `--from-pr [value]` | 按 PR 编号或 URL 恢复关联会话 |
+| `--add-dir ` | 额外允许工具访问的目录 |
+| `-w, --worktree [name]` | 为会话创建 Git worktree |
+| `--tmux` | 与 `--worktree` 配合创建 tmux 会话;受支持时使用 iTerm2 pane |
+
+`--worktree` 会改变本地 Git 工作区布局。运行前先确认仓库状态,并在自动化中提供明确名称。
+
+## 模型与上下文
+
+| 参数 | 说明 |
+|------|------|
+| `--model ` | 使用模型别名或完整模型 ID |
+| `--fallback-model ` | `--print` 模式下主模型过载时使用备用模型 |
+| `--effort ` | 设置推理投入,如 `low`、`medium`、`high`、`xhigh` 或 `max` |
+| `--agent ` | 使用已配置的 Agent |
+| `--agents ''` | 为本次会话定义自定义 Agent |
+| `--system-prompt ` | 替换本次会话的 system prompt |
+| `--append-system-prompt ` | 在默认 system prompt 后追加内容 |
+| `--file ` | 启动时下载文件资源,格式为 `file_id:relative_path` |
+| `--bare` | 启动最小运行时,跳过 hooks、LSP、插件同步、自动记忆等自动发现能力 |
+
+`--bare` 不是“更宽松”的模式。它减少隐式上下文和凭据来源,调用方需要显式提供 prompts、目录、MCP、设置、Agents 或插件。
+
+## 工具与权限
+
+| 参数 | 说明 |
+|------|------|
+| `--tools ` | 指定可用的内置工具;`""` 禁用全部,`default` 使用默认集合 |
+| `--allowed-tools ` | 允许匹配的工具,例如 `Bash(git:*) Edit` |
+| `--disallowed-tools ` | 拒绝匹配的工具 |
+| `--permission-mode ` | 选择 `default`、`acceptEdits`、`plan`、`dontAsk`、`bypassPermissions` 或 `auto` |
+| `--no-computer-use` | 本次会话不加载 Computer Use MCP |
+| `--chrome` / `--no-chrome` | 开启或关闭 Chrome 集成 |
+| `--ide` | 只有一个有效 IDE 时自动连接 |
+
+拒绝规则和平台策略仍可能覆盖允许规则。生产自动化应使用最小工具集,而不是依赖一次人工批准后的本机状态。
+
+## 设置、MCP 与插件
+
+| 参数 | 说明 |
+|------|------|
+| `--settings ` | 加载额外的设置文件或 JSON 字符串 |
+| `--setting-sources ` | 选择 `user`、`project`、`local` 设置来源 |
+| `--mcp-config ` | 从 JSON 文件或 JSON 字符串加载 MCP 服务 |
+| `--strict-mcp-config` | 忽略其他 MCP 配置,只使用命令行指定的配置 |
+| `--plugin-dir ` | 为本次会话加载插件目录;多个目录要重复传参 |
+| `--disable-slash-commands` | 禁用所有 Skills |
+| `-d, --debug [filter]` | 开启调试日志,可按分类筛选 |
+| `--debug-file ` | 把调试日志写入指定文件 |
+
+`--mcp-debug` 已弃用,请使用 `--debug`。
+
+## 子命令
+
+| 子命令 | 用途 |
+|--------|------|
+| `agents` | 列出已配置的 Agents |
+| `auth` | 管理认证 |
+| `auto-mode` | 查看 Auto mode 分类器配置 |
+| `doctor` | 检查更新器和运行环境健康状态 |
+| `install` | 安装指定渠道或版本的原生构建 |
+| `mcp` | 配置和管理 MCP 服务 |
+| `plugin` / `plugins` | 管理插件 |
+| `setup-token` | 为 Claude 订阅创建长期认证 Token |
+| `update` / `upgrade` | 检查并安装更新 |
+
+每个子命令都有独立帮助:
+
+```bash
+./bin/claude-haha mcp --help
+./bin/claude-haha plugin --help
+```
+
+## 交互式命令
+
+交互会话中输入 `/help` 查看当前可用命令。`/commit`、`/review` 等命令来自内置命令集;Skills 和插件也能动态加入命令,所以不要依赖文档中的固定清单。
+
+如果命令不存在,先以 `/help` 的当前输出为准,并检查是否使用了 `--disable-slash-commands`。
diff --git a/docs/guide/contributing.md b/docs/guide/contributing.md
index c6e6ea1b..ee3fb0f4 100644
--- a/docs/guide/contributing.md
+++ b/docs/guide/contributing.md
@@ -143,7 +143,7 @@ bun run check:native # 桌面 sidecar、Electron host 与 package-smoke 检
bun run check:provider-contract # Provider/runtime/proxy 的离线契约测试
bun run check:chat-contract # WebSocket、会话与桌面 chat store 契约测试
bun run check:persistence-upgrade # 持久化迁移和旧 fixture 兼容性
-bun run check:docs # 文档构建,使用 npm ci + docs:build
+bun run check:docs # 独立安装、构建并检查 site/ React 文档站
bun run check:quarantine # 维护者 baseline/release quarantine 审计
bun run check:coverage # root、desktop、adapters 覆盖率报告和 ratchet 门禁
```
diff --git a/docs/guide/env-vars.md b/docs/guide/env-vars.md
index 42da60d4..d470fd49 100644
--- a/docs/guide/env-vars.md
+++ b/docs/guide/env-vars.md
@@ -1,61 +1,131 @@
-# 环境变量说明
+# 环境变量
+Claude Code Haha 有两条配置路径:
+
+- 桌面端用户优先在 **设置 → Providers** 中选择、测试并激活提供商。应用会管理对应的认证、模型映射和协议代理。
+- 从源码运行 CLI 时,可以使用 `.env`、Shell 环境变量或 Claude Code 的 `settings.json`。
+
+不要在多个位置重复保存同一把 API Key。排查问题时,先确认当前是否激活了桌面端 Provider。
+
+## 常用变量
+
+### Anthropic 兼容接口
| 变量 | 必填 | 说明 |
|------|------|------|
-| `ANTHROPIC_API_KEY` | 二选一 | API Key,通过 `x-api-key` 头发送 |
-| `ANTHROPIC_AUTH_TOKEN` | 二选一 | Auth Token,通过 `Authorization: Bearer` 头发送 |
-| `ANTHROPIC_BASE_URL` | 否 | 自定义 API 端点,默认 Anthropic 官方 |
-| `ANTHROPIC_MODEL` | 否 | 默认模型 |
-| `ANTHROPIC_DEFAULT_SONNET_MODEL` | 否 | Sonnet 级别模型映射 |
-| `ANTHROPIC_DEFAULT_HAIKU_MODEL` | 否 | Haiku 级别模型映射 |
-| `ANTHROPIC_DEFAULT_OPUS_MODEL` | 否 | Opus 级别模型映射 |
-| `API_TIMEOUT_MS` | 否 | API 请求超时,默认 600000 (10min) |
-| `DISABLE_TELEMETRY` | 否 | 设为 `1` 禁用遥测 |
-| `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` | 否 | 设为 `1` 禁用非必要网络请求 |
+| `ANTHROPIC_API_KEY` | 与 Auth Token 二选一 | 通过 `x-api-key` 请求头发送 |
+| `ANTHROPIC_AUTH_TOKEN` | 与 API Key 二选一 | 通过 `Authorization: Bearer` 请求头发送 |
+| `ANTHROPIC_BASE_URL` | 否 | Anthropic Messages 兼容端点的基础地址 |
+| `ANTHROPIC_MODEL` | 否 | 当前会话的默认模型 |
+| `ANTHROPIC_DEFAULT_FABLE_MODEL` | 否 | Fable 模型槽位;仅在提供商支持时配置 |
+| `ANTHROPIC_DEFAULT_HAIKU_MODEL` | 否 | Haiku 模型槽位 |
+| `ANTHROPIC_DEFAULT_SONNET_MODEL` | 否 | Sonnet 模型槽位 |
+| `ANTHROPIC_DEFAULT_OPUS_MODEL` | 否 | Opus 模型槽位 |
+| `API_TIMEOUT_MS` | 否 | API 请求超时,单位为毫秒;默认 `600000` |
+
+认证变量取决于服务端要求的请求头,不能仅凭提供商名称判断。如果返回 `401`,先核对服务商文档和桌面端 Provider 的认证策略。
+
+### Azure OpenAI
+
+Azure OpenAI 使用独立的 Responses API 路径:
+
+| 变量 | 必填 | 说明 |
+|------|------|------|
+| `CLAUDE_CODE_USE_AZURE_OPENAI` | 是 | 设为 `1` 启用 Azure OpenAI |
+| `AZURE_OPENAI_BASE_URL` | 是 | Azure 资源基础地址;也接受 `AZURE_OPENAI_ENDPOINT` |
+| `AZURE_OPENAI_API_VERSION` | 否 | API 版本;默认 `2025-04-01-preview` |
+| `AZURE_OPENAI_API_KEY` | 是 | Azure OpenAI API Key |
+| `AZURE_OPENAI_CODEX_DEPLOYMENT` | 视模型而定 | Codex 模型对应的 Azure deployment 名称 |
+
+示例:
+
+```bash
+CLAUDE_CODE_USE_AZURE_OPENAI=1
+AZURE_OPENAI_BASE_URL=https://your-resource.cognitiveservices.azure.com
+AZURE_OPENAI_API_VERSION=2025-04-01-preview
+AZURE_OPENAI_API_KEY=your_azure_openai_key
+AZURE_OPENAI_CODEX_DEPLOYMENT=your_codex_deployment
+```
+
+### 本地运行与隐私
+
+| 变量 | 说明 |
+|------|------|
+| `CLAUDE_CONFIG_DIR` | 改用指定配置目录,而不是默认的 `~/.claude`;适合便携模式和隔离测试 |
+| `CLAUDE_CODE_FORCE_RECOVERY_CLI` | 设为 `1` 使用简化的 Recovery CLI |
+| `CLAUDE_CODE_SHELL_PREFIX` | 为 Bash 工具增加 Shell 前缀,例如 Windows 下的 `wsl -e bash -lc` |
+| `DISABLE_TELEMETRY` | 设为 `1` 禁用遥测 |
+| `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` | 设为 `1` 禁用非必要网络请求 |
+
+本地 Server 的 `SERVER_HOST`、`SERVER_PORT`、`SERVER_AUTH_REQUIRED` 等变量见 [本地 Server](../reference/local-server.md)。
## 配置方式
-### 方式一:`.env` 文件
+### 桌面端 Provider
+
+桌面端把 Provider 索引保存到:
+
+```text
+~/.claude/cc-haha/providers.json
+```
+
+应用管理的 Provider 环境写入隔离的 Haha 配置,不需要手工复制到 `~/.claude/settings.json`。当 CLI 读取到已激活的 Provider 时,会复用其认证、模型和协议设置;`openai_chat` 与 `openai_responses` Provider 会自动使用本机回环代理。
+
+详细流程见 [第三方模型](./third-party-models.md)。
+
+### `.env` 文件
+
+源码仓库中的 `bin/claude-haha` 会在项目根目录存在 `.env` 时加载它:
```bash
cp .env.example .env
```
-编辑 `.env`(以下示例使用 [MiniMax](https://platform.minimaxi.com/subscribe/token-plan?code=1TG2Cseab2&source=link) 作为 API 提供商,也可替换为其他兼容服务):
+一个 Anthropic 兼容接口的最小示例:
```bash
-# API 认证(二选一)
-ANTHROPIC_API_KEY=sk-xxx # 标准 API Key(x-api-key 头)
-ANTHROPIC_AUTH_TOKEN=sk-xxx # Bearer Token(Authorization 头)
-
-# API 端点(可选,默认 Anthropic 官方)
-ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic
-
-# 模型配置
-ANTHROPIC_MODEL=MiniMax-M3
-ANTHROPIC_DEFAULT_SONNET_MODEL=MiniMax-M3
-ANTHROPIC_DEFAULT_HAIKU_MODEL=MiniMax-M2.7-highspeed
-ANTHROPIC_DEFAULT_OPUS_MODEL=MiniMax-M3
-
-# 超时(毫秒)
-API_TIMEOUT_MS=3000000
-
-# 禁用遥测和非必要网络请求
-DISABLE_TELEMETRY=1
-CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
+ANTHROPIC_AUTH_TOKEN=sk-example
+ANTHROPIC_BASE_URL=https://provider.example.com/anthropic
+ANTHROPIC_MODEL=provider-model
+ANTHROPIC_DEFAULT_HAIKU_MODEL=provider-model
+ANTHROPIC_DEFAULT_SONNET_MODEL=provider-model
+ANTHROPIC_DEFAULT_OPUS_MODEL=provider-model
```
-### 方式二:`~/.claude/settings.json`
+`.env` 只用于源码启动脚本。桌面端创建的 CLI 子进程会跳过仓库 `.env`,避免旧密钥覆盖当前激活的 Provider。
+
+### `settings.json`
+
+用户级设置位于 `~/.claude/settings.json`:
```json
{
"env": {
- "ANTHROPIC_AUTH_TOKEN": "sk-xxx",
- "ANTHROPIC_BASE_URL": "https://api.minimaxi.com/anthropic",
- "ANTHROPIC_MODEL": "MiniMax-M3"
+ "ANTHROPIC_AUTH_TOKEN": "sk-example",
+ "ANTHROPIC_BASE_URL": "https://provider.example.com/anthropic",
+ "ANTHROPIC_MODEL": "provider-model"
}
}
```
-> 配置优先级:环境变量 > `.env` 文件 > `~/.claude/settings.json`
+项目还可能包含 `.claude/settings.json` 或 `.claude/settings.local.json`。这些文件属于工作区输入;只在可信项目中使用,尤其不要从不可信仓库接受 `PATH`、`LD_PRELOAD`、代理地址或认证相关环境变量。
+
+## 实际生效顺序
+
+这里不存在可靠的“Shell > `.env` > settings”三段式规则:
+
+1. `bin/claude-haha` 先让 Bun 加载仓库 `.env`。
+2. CLI 初始化时合并已启用的用户、项目、本地、命令行和受管设置来源。
+3. 已激活的 Haha Provider 会覆盖普通 Claude 设置中的 Provider 路由变量,防止两个客户端互相污染。
+4. 桌面端 host 注入的运行时变量受到保护,不能被 `settings.json` 中的同名字段替换。
+5. 企业受管策略和 `--setting-sources` 也会影响最终结果。
+
+因此,切换模型提供商时应只保留一个主要配置入口。桌面端用户使用 Providers 设置页;纯 CLI 用户使用 `.env` 或用户级 `settings.json` 之一。
+
+## 安全建议
+
+- 不要提交 `.env`、Provider 配置或包含密钥的 `settings.json`。
+- 不要在截图、Issue、日志或诊断包中暴露完整 Token。
+- 使用 `CLAUDE_CONFIG_DIR` 做测试隔离,避免读写真实用户配置。
+- `--print` 会跳过工作区信任对话框,只能在可信目录运行。更多限制见 [CLI 参考](./cli-reference.md)。
+- 远程访问本地 Server 时,不要把 CORS 当成身份认证;请启用 H5 Token 或显式鉴权。
diff --git a/docs/guide/faq.md b/docs/guide/faq.md
index 77c11870..7cb5fdaa 100644
--- a/docs/guide/faq.md
+++ b/docs/guide/faq.md
@@ -1,34 +1,153 @@
-# 常见问题
+# 常见问题与求助
+## 遇到问题时,怎样最快获得帮助?
-## Q: `undefined is not an object (evaluating 'usage.input_tokens')`
+先确认正在使用 [GitHub Releases Latest](https://github.com/NanmiCoder/cc-haha/releases/latest),并重试一次能稳定复现问题的最短步骤。
-**原因**:`ANTHROPIC_BASE_URL` 配置不正确,API 端点返回的不是 Anthropic 协议格式的 JSON,而是 HTML 页面或其他格式。
+如果 Desktop 仍能打开:
-本项目使用 **Anthropic Messages API 协议**,`ANTHROPIC_BASE_URL` 必须指向一个兼容 Anthropic `/v1/messages` 接口的端点。Anthropic SDK 会自动在 base URL 后面拼接 `/v1/messages`,所以:
+1. 进入 **设置 → 诊断**。
+2. 点击 **复制 Issue 报告**。
+3. 在 [GitHub Issues](https://github.com/NanmiCoder/cc-haha/issues) 搜索相同问题;没有重复 Issue 时再新建。
+4. 粘贴报告,并补充复现步骤、预期结果和实际结果。
-- MiniMax:`ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic` ✅
-- OpenRouter:`ANTHROPIC_BASE_URL=https://openrouter.ai/api` ✅
-- OpenRouter 错误写法:`ANTHROPIC_BASE_URL=https://openrouter.ai/anthropic` ❌(返回 HTML)
+如果仅凭报告无法定位,可以再点击 **导出诊断包**。Issue 报告和诊断包会尽力省略聊天内容、文件内容、完整环境变量和 API Key,但仍可能含有路径、Provider 主机名等私密元数据;**分享前必须自行检查**。
-如果你的模型供应商只支持 OpenAI 协议,需要通过 LiteLLM 等代理做协议转换,详见 [第三方模型使用指南](./third-party-models.md)。
+建议同时提供:
-## Q: `Cannot find package 'bundle'`
+- Claude Code Haha 版本
+- 操作系统、CPU 架构和安装包类型
+- 使用的 Provider 类型与 API 格式,不要提供 API Key
+- 最短复现步骤和完整错误文字
+- 问题发生在 Desktop、H5 还是 CLI
-```
-error: Cannot find package 'bundle' from '.../claude-code-haha/src/entrypoints/cli.tsx'
-```
+如果应用无法启动,提供安装包版本、系统错误截图和启动前最后一个可见操作即可。不要为了排错直接删除或覆盖 `~/.claude`。
-**原因**:Bun 版本过低,不支持项目所需的 `bun:bundle` 等内置模块。
+## Provider 与 OAuth
-**解决**:升级 Bun 到最新版本:
+### 我一定要安装 LiteLLM 才能用 OpenAI 兼容服务吗?
+
+不需要。Desktop 的 Custom Provider 支持:
+
+- Anthropic Messages(原生)
+- OpenAI Chat Completions(本地代理转换)
+- OpenAI Responses API(本地代理转换)
+
+只有当上游服务使用了应用尚未支持的协议或特殊字段时,才需要考虑 LiteLLM 等外部网关。
+
+### 自定义 Provider 返回 401 或 API Key invalid
+
+在 **设置 → 服务商** 中编辑该 Provider,依次检查:
+
+1. Base URL 是否是服务商要求的 API 根地址,没有多写或漏写路径。
+2. API 格式是否与服务商真实接口一致。
+3. 认证方式是否正确:部分 Anthropic 兼容服务使用 Bearer Token,官方 Anthropic API 使用 `x-api-key`。
+4. 主模型 ID 是否真实存在,并且当前 Key 有权访问。
+5. 点击 **测试连接**,先处理第一步连通失败;OpenAI 格式还会显示第二步代理转换结果。
+
+不要同时在截图、Issue 或诊断附件中公开 Base URL 查询参数、Token 或 API Key。
+
+### Claude、ChatGPT 或 Grok 官方登录没有完成
+
+- 保持 Claude Code Haha 正在运行,不要提前关闭登录页面或应用。
+- 允许系统浏览器打开授权页,并完成同一个账号的授权。
+- 检查系统时间是否正确,并确认代理、防火墙或浏览器扩展没有阻止服务商页面和本机 OAuth 回调。
+- 如果浏览器显示授权成功但应用没有更新,回到 **设置 → 服务商** 重新发起登录。
+
+仍然失败时,复制 Issue 报告,并注明卡在“未打开浏览器”“授权页报错”还是“授权后未回到应用”。不要分享 OAuth code、access token 或浏览器 Cookie。
+
+### 测试连接成功,聊天仍然失败
+
+确认当前任务实际选择了刚配置的 Provider 和模型,而不只是把 Provider 保存到了列表中。再检查主模型和角色模型映射是否受该账号支持。
+
+用新任务发送一条最短纯文本消息复测;如果仍失败,在诊断页复制 Issue 报告。连接测试证明接口、认证和基础转换可用,不等于所有模型能力、工具调用和长上下文组合都已验证。
+
+## 安装与更新
+
+### macOS 提示无法验证、已损坏或打不开
+
+优先确认安装包来自 [官方 Latest Release](https://github.com/NanmiCoder/cc-haha/releases/latest),并选择了正确的 Intel 或 Apple Silicon 架构。正式签名和公证的版本通常只显示标准的下载来源确认。
+
+旧版、draft 或临时 unsigned 包可能需要额外放行,见 [Desktop 安装指南](/desktop/04-installation#macos)。不要从不明镜像下载后绕过系统安全提示。
+
+### Windows 出现 SmartScreen
+
+确认安装包来自官方 Release。未签名的 Windows 安装包可能显示 SmartScreen,可以展开“更多信息”核对发布者和文件名后再决定是否运行。
+
+覆盖升级前先完全退出 Claude Code Haha。如果安装器提示进程仍在运行,关闭对应窗口和后台进程后重试,不要先删除用户配置目录。
+
+### Linux AppImage 无法启动
+
+先赋予执行权限:
```bash
-bun upgrade
+chmod +x Claude-Code-Haha-<版本>-linux-<架构>.AppImage
```
-## Q: 怎么接入 OpenAI / DeepSeek / Ollama 等非 Anthropic 模型?
+部分发行版还需要 FUSE。不同发行版的安装方式见 [Desktop 安装指南](/desktop/04-installation#linux)。
-本项目只支持 Anthropic 协议。如果模型供应商不直接支持 Anthropic 协议,需要用 [LiteLLM](https://github.com/BerriAI/litellm) 等代理做协议转换(OpenAI → Anthropic)。
+## H5 访问
-详细配置步骤请参考:[第三方模型使用指南](./third-party-models.md)
+### 手机或另一台电脑无法连接 H5
+
+依次确认:
+
+1. Desktop 的 **H5 访问**已经开启。
+2. 使用设置页当前显示或二维码生成的 Server URL,不要复用旧局域网 IP。
+3. 客户端填写了当前 H5 Token。
+4. 两台设备位于可互通的网络,系统防火墙允许当前端口。
+5. 经过反向代理时,已正确转发 HTTP 和 WebSocket,并配置允许的来源。
+
+H5 是当前 Desktop 服务的远程入口,不等同于完整 Desktop。终端、原生预览、宠物窗口和部分系统能力只能在桌面应用中使用。
+
+详细部署与安全边界见 [H5 访问](/desktop/06-h5-access)。
+
+## Git 分支与 Worktree
+
+### 创建隔离 Worktree 失败
+
+常见原因包括:
+
+- 所选目录不是 Git 仓库
+- 分支不存在,或已被另一个 Worktree 占用
+- 当前工作树有未提交修改,无法安全执行预期操作
+- 目标 Worktree 路径已存在或不可写
+
+先阅读界面显示的具体错误。可以改用当前工作树、选择其他分支,或在 Git 中安全处理已有改动后重试。不要为了创建 Worktree 自动删除现有目录或丢弃未提交修改。
+
+### 当前工作树和隔离 Worktree 应该选哪个?
+
+- **当前工作树**:适合继续处理当前目录里已经存在的修改。
+- **隔离 Worktree**:适合并行任务、独立分支,或希望与当前目录改动分开的工作。
+
+如果不确定且当前目录已有重要的未提交修改,先查看 Git 状态并备份,再决定使用哪条路径。
+
+## Computer Use
+
+### Computer Use 不可用或无法控制应用
+
+先打开 **设置 → Computer Use**,检查:
+
+- 全局开关已经开启
+- Python 环境和依赖检查通过
+- macOS 或 Windows 所需的系统权限已经授予
+- 目标应用已在允许控制的应用列表中
+- 当前会话的权限请求已经明确批准
+
+授予系统权限后,通常需要重新打开 Claude Code Haha 或目标应用。Linux 当前不支持 Computer Use;请不要把 Desktop 安装成功当成 Computer Use 已配置完成。
+
+如果设置页仍显示错误,复制 Issue 报告,并附上设置页状态;不要上传包含其他应用内容的完整屏幕截图。
+
+## CLI
+
+### `bun install` 或 CLI 启动失败
+
+确认已经进入仓库根目录,并使用项目支持的 Bun 版本:
+
+```bash
+bun --version
+bun install
+./bin/claude-haha --help
+```
+
+如果提示缺少 `bun:bundle` 等 Bun 内置模块,先升级 Bun。完整安装路径见 [3 分钟上手](./quick-start.md#路径二从源码运行-cli),参数说明见 [CLI 参考](./cli-reference.md)。
diff --git a/docs/guide/quick-start.md b/docs/guide/quick-start.md
index fb262d61..a4007a3a 100644
--- a/docs/guide/quick-start.md
+++ b/docs/guide/quick-start.md
@@ -1,64 +1,109 @@
-# 快速开始
+# 3 分钟上手
-## 1. 安装 Bun
+Claude Code Haha 有两种使用方式。日常开发推荐安装 **Desktop**;只有在需要终端交互、脚本调用或参与源码开发时,才需要从源码运行 **CLI**。
-```bash
-# macOS / Linux
-curl -fsSL https://bun.sh/install | bash
+| 方式 | 适合谁 | 需要准备 |
+|------|--------|----------|
+| **Desktop(推荐)** | 想直接管理项目、会话、Worktree、代码 Diff 和权限审批 | 下载对应系统的安装包;**不需要安装 Bun** |
+| **CLI** | 偏好终端、需要 `--print` 自动化或准备参与开发 | Git、Bun 和一个模型服务 |
-# macOS (Homebrew)
-brew install bun
+## 路径一:安装 Desktop
-# Windows (PowerShell)
-powershell -c "irm bun.sh/install.ps1 | iex"
+### 1. 下载当前稳定版
+
+打开 [GitHub Releases Latest](https://github.com/NanmiCoder/cc-haha/releases/latest),按系统和 CPU 架构选择安装包:
+
+- macOS Apple Silicon(M 系列):`mac-arm64.dmg`
+- macOS Intel:`mac-x64.dmg`
+- Windows:`win-x64.exe` 或 `win-arm64.exe`
+- Linux:对应架构的 `.AppImage` 或 `.deb`
+
+安装细节和系统提示处理见 [Desktop 安装指南](/desktop/04-installation)。
+
+### 2. 首次配置模型服务
+
+启动应用,打开 **设置 → 服务商**。
+
+最快的方式是选择一个官方入口,并按界面提示完成连接或登录:
+
+- **Claude 官方**
+- **ChatGPT 官方**:使用 ChatGPT 账号完成 OAuth
+- **Grok 官方**:使用 xAI 账号完成 OAuth
+
+这些官方入口不要求手工填写 API Key。也可以点击 **添加服务商**,选择内置预设或 Custom,填写 API Key、接口地址、API 格式和模型映射。
+
+自定义服务商保存前先点击 **测试连接**;测试通过后,将它 **设为默认**。OpenAI Chat Completions 和 OpenAI Responses API 可以由应用内置的本地代理转换,不必默认再安装 LiteLLM。
+
+更多配置解释见 [第三方模型与自定义服务商](./third-party-models.md)。
+
+### 3. 新建第一个任务
+
+点击侧边栏的 `+`:
+
+1. 选择本地项目目录。
+2. 如果目录是 Git 仓库,选择要使用的分支。
+3. 决定在当前工作树中运行,还是创建隔离 Worktree。
+
+当前工作树会直接看到目录里已有的未提交修改;隔离 Worktree 更适合并行任务或不希望影响当前工作目录的改动。
+
+### 4. 确认模型和权限
+
+发送第一条消息前,确认当前 Provider、模型和 effort。第一次使用建议保留默认的 **询问权限(Default)**:应用会在执行敏感工具或命令前请求确认。
+
+只有明确理解影响时再使用自动接受或绕过权限模式。权限模式的区别见 [Desktop 快速上手](/desktop/01-quick-start#三选择正确的权限模式)。
+
+### 5. 发送第一条消息
+
+可以从一个可验证的小任务开始,例如:
+
+```text
+先只读分析这个项目,告诉我它如何启动、主要目录分别负责什么,不要修改文件。
```
-> 精简版 Linux 如提示 `unzip is required`,先运行 `apt update && apt install -y unzip`
+看到流式回复、工具调用和权限请求后,说明 Desktop、模型服务和项目目录已经连通。
-## 2. 安装依赖并配置
+## 路径二:从源码运行 CLI
+
+### 1. 获取源码并安装 Bun
+
+先安装 [Git](https://git-scm.com/downloads) 和 [Bun](https://bun.sh),然后执行:
```bash
+git clone https://github.com/NanmiCoder/cc-haha.git
+cd cc-haha
bun install
-cp .env.example .env
-# 编辑 .env 填入你的 API Key,详见「环境变量」文档
```
-环境变量的完整说明请参考 [环境变量配置](./env-vars.md)。
-
-## 3. 启动
-
-### macOS / Linux
+### 2. 配置模型服务
```bash
-./bin/claude-haha # 交互 TUI 模式
-./bin/claude-haha -p "your prompt here" # 无头模式
-./bin/claude-haha --help # 查看所有选项
+cp .env.example .env
```
-### Windows
+编辑 `.env`,至少配置一种可用的认证方式、接口地址和模型。变量含义与认证头区别见 [环境变量](./env-vars.md)。
-> **前置要求**:必须安装 [Git for Windows](https://git-scm.com/download/win)
+不要把真实 API Key 提交到 Git,也不要在 Issue、截图或诊断附件中公开它。
+
+### 3. 启动并验证
+
+macOS、Linux 或 Git Bash:
+
+```bash
+./bin/claude-haha
+./bin/claude-haha -p "概括当前项目的目录结构"
+```
+
+Windows PowerShell 或 cmd:
```powershell
-# PowerShell / cmd 直接调用 Bun
bun --env-file=.env ./src/entrypoints/cli.tsx
-
-# 或在 Git Bash 中运行
-./bin/claude-haha
```
-## 4. 全局使用(可选)
+命令参数、无头模式、恢复模式和全局调用方式见 [CLI 参考](./cli-reference.md)。
-将 `bin/` 加入 PATH 后可在任意目录启动,详见 [全局使用指南](./global-usage.md):
+## 下一步
-```bash
-export PATH="$HOME/path/to/claude-code-haha/bin:$PATH"
-```
-
-## 5. 降级模式
-
-如果 Ink TUI 出现问题,可以使用降级 Recovery CLI 模式:
-
-```bash
-CLAUDE_CODE_FORCE_RECOVERY_CLI=1 ./bin/claude-haha
-```
+- [Desktop 快速上手](/desktop/01-quick-start):会话、权限、附件和工作区操作
+- [第三方模型与自定义服务商](./third-party-models.md):Provider、API 格式与模型映射
+- [常见问题](./faq.md):安装、OAuth、H5、Worktree 和 Computer Use 排查
+- [全局使用](./global-usage.md):在任意目录启动 CLI
diff --git a/docs/guide/third-party-models.md b/docs/guide/third-party-models.md
index f5be6f87..1aaa924b 100644
--- a/docs/guide/third-party-models.md
+++ b/docs/guide/third-party-models.md
@@ -1,265 +1,98 @@
-# 使用第三方模型(OpenAI / DeepSeek / 本地模型)
+# 第三方模型
-本项目基于 Anthropic 协议与 LLM 通信。通过协议转换代理,可以使用 OpenAI、DeepSeek、Ollama 等任意模型。
+Claude Code Haha 可以连接 Anthropic Messages、OpenAI Chat Completions 和 OpenAI Responses 三类接口。对桌面端用户来说,最可靠的入口不是手写环境变量,而是 **设置 → Providers**:应用会保存认证方式、模型映射,并在需要时启动本机协议代理。
-## 原理
+## 推荐配置流程
-```
-claude-code-haha ──Anthropic协议──▶ LiteLLM Proxy ──OpenAI协议──▶ 目标模型 API
- (协议转换)
-```
+1. 打开桌面端的 **设置 → Providers**。
+2. 选择内置预设,或新建自定义 Provider。
+3. 填写 Base URL、认证信息和协议格式。
+4. 至少配置主模型;服务商使用不同模型名时,再配置 Haiku、Sonnet 和 Opus 槽位。
+5. 先点击测试,确认认证和模型可用,再激活 Provider。
+6. 新建会话验证工具调用;只返回文本并不代表 Agent 工作流完整可用。
-本项目发出 Anthropic Messages API 请求,LiteLLM 代理将其自动转换为 OpenAI Chat Completions API 格式并转发给目标模型。
+激活后,桌面端和由它启动的 CLI 会复用同一份 Provider 配置。不要再把另一套旧密钥写进 `.env` 或 `~/.claude/settings.json`。
----
+## 选择正确的协议
-## 方式一:LiteLLM 代理(推荐)
+| Provider 格式 | 上游接口 | 适用场景 |
+|----------------|----------|----------|
+| `anthropic` | Anthropic Messages | 服务商原生兼容 Anthropic 请求与响应 |
+| `openai_chat` | `/v1/chat/completions` | 服务商提供 OpenAI Chat Completions |
+| `openai_responses` | `/v1/responses` | 服务商提供 OpenAI Responses |
-[LiteLLM](https://github.com/BerriAI/litellm) 是一个支持 100+ LLM 的统一代理网关(41k+ GitHub Stars),原生支持接收 Anthropic 协议请求。
+`anthropic` 会直接请求服务商,不替换协议。`openai_chat` 和 `openai_responses` 会由 Claude Code Haha 在本机启动回环代理,把 Claude Agent 流量转换成所选协议。这个代理只用于当前运行时,不需要把端口手工暴露到局域网。
-### 1. 安装 LiteLLM
-
-```bash
-pip install 'litellm[proxy]'
-```
-
-### 2. 创建配置文件
-
-新建 `litellm_config.yaml`:
-
-#### 使用 OpenAI 模型
-
-```yaml
-model_list:
- - model_name: gpt-4o
- litellm_params:
- model: openai/gpt-4o
- api_key: os.environ/OPENAI_API_KEY
-
-litellm_settings:
- drop_params: true # 丢弃 Anthropic 专有参数(thinking 等)
-```
-
-#### 使用 DeepSeek 模型
-
-```yaml
-model_list:
- - model_name: deepseek-chat
- litellm_params:
- model: deepseek/deepseek-chat
- api_key: os.environ/DEEPSEEK_API_KEY
- api_base: https://api.deepseek.com
-
-litellm_settings:
- drop_params: true
-```
-
-#### 使用 Ollama 本地模型
-
-```yaml
-model_list:
- - model_name: llama3
- litellm_params:
- model: ollama/llama3
- api_base: http://localhost:11434
-
-litellm_settings:
- drop_params: true
-```
-
-#### 使用多个模型(可在启动后切换)
-
-```yaml
-model_list:
- - model_name: gpt-4o
- litellm_params:
- model: openai/gpt-4o
- api_key: os.environ/OPENAI_API_KEY
-
- - model_name: deepseek-chat
- litellm_params:
- model: deepseek/deepseek-chat
- api_key: os.environ/DEEPSEEK_API_KEY
- api_base: https://api.deepseek.com
-
- - model_name: llama3
- litellm_params:
- model: ollama/llama3
- api_base: http://localhost:11434
-
-litellm_settings:
- drop_params: true
-```
-
-### 3. 启动代理
-
-```bash
-# 设置目标模型的 API Key
-export OPENAI_API_KEY=sk-xxx
-# 或
-export DEEPSEEK_API_KEY=sk-xxx
-
-# 启动代理
-litellm --config litellm_config.yaml --port 4000
-```
-
-代理启动后会在 `http://localhost:4000` 监听,并暴露 Anthropic 兼容的 `/v1/messages` 端点。
-
-### 4. 配置本项目
-
-有两种配置方式,任选其一:
-
-#### 方式 A:通过 `.env` 文件
-
-```bash
-ANTHROPIC_AUTH_TOKEN=sk-anything
-ANTHROPIC_BASE_URL=http://localhost:4000
-ANTHROPIC_MODEL=gpt-4o
-ANTHROPIC_DEFAULT_SONNET_MODEL=gpt-4o
-ANTHROPIC_DEFAULT_HAIKU_MODEL=gpt-4o
-ANTHROPIC_DEFAULT_OPUS_MODEL=gpt-4o
-API_TIMEOUT_MS=3000000
-DISABLE_TELEMETRY=1
-CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
-```
-
-#### 方式 B:通过 `~/.claude/settings.json`
-
-```json
-{
- "env": {
- "ANTHROPIC_AUTH_TOKEN": "sk-anything",
- "ANTHROPIC_BASE_URL": "http://localhost:4000",
- "ANTHROPIC_MODEL": "gpt-4o",
- "ANTHROPIC_DEFAULT_SONNET_MODEL": "gpt-4o",
- "ANTHROPIC_DEFAULT_HAIKU_MODEL": "gpt-4o",
- "ANTHROPIC_DEFAULT_OPUS_MODEL": "gpt-4o",
- "API_TIMEOUT_MS": "3000000",
- "DISABLE_TELEMETRY": "1",
- "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
- }
-}
-```
-
-> **说明**:`ANTHROPIC_AUTH_TOKEN` 的值在使用 LiteLLM 代理时可以是任意字符串(LiteLLM 会用自己配置的 key 转发),除非你在 LiteLLM 端设置了 `master_key` 校验。
-
-### 5. 启动并验证
+如果提供商同时宣传“OpenAI 兼容”和“Anthropic 兼容”,以它实际实现完整、工具调用稳定的接口为准。不要仅根据 URL 中是否含有 `/v1` 推断协议。
+
+## 认证与模型映射
+
+认证头由服务商决定:
+
+- Anthropic API Key 通常使用 `x-api-key`。
+- Bearer Token 通常使用 `Authorization: Bearer`。
+- OpenAI 兼容服务通常使用 Bearer Token,但私有网关可能不同。
+
+模型槽位不是额外下载的模型,而是 Claude Agent 在不同任务中请求的逻辑档位。主模型必须能处理工具调用;其他槽位可以映射到同一个模型,也可以按成本和能力分别配置。某个服务商不支持的槽位可以留空。
+
+模型名和服务商能力会持续变化,所以本文不维护一份容易过期的型号清单。请在 Provider 设置页使用服务商当前返回的模型 ID,并通过测试按钮确认。
+
+## 内置运行时
+
+Claude Code Haha 还提供 Claude、OpenAI 和 Grok 的内置运行时。可用的登录方式取决于当前版本和本机账号状态;按照 Provider 页面显示的授权流程完成登录即可。
+
+内置运行时与“自定义兼容接口”是两条路径。已有官方账号时优先使用内置运行时;连接中转服务、自建网关或本地模型时再创建自定义 Provider。
+
+## 仅使用 CLI
+
+纯 CLI 用户可以直接配置 Anthropic Messages 兼容端点:
```bash
+ANTHROPIC_AUTH_TOKEN=sk-example
+ANTHROPIC_BASE_URL=https://provider.example.com/anthropic
+ANTHROPIC_MODEL=provider-model
./bin/claude-haha
```
-如果一切正常,你应该能看到正常的对话界面,实际调用的是你配置的目标模型。
+这个方式不会自动把 Anthropic 请求转换成 OpenAI 协议。OpenAI Chat Completions 或 Responses 服务应先在桌面端创建 Provider,让应用管理协议代理。完整变量和生效顺序见 [环境变量](./env-vars.md)。
----
+Azure OpenAI 使用项目内置的专用 Responses 路径,配置项见 [Azure OpenAI 环境变量](./env-vars.md#azure-openai)。
-## 方式二:直连兼容 Anthropic 协议的第三方服务
+## LiteLLM:进阶兼容层
-部分第三方服务直接兼容 Anthropic Messages API,无需额外代理:
+只有在服务商没有可靠的 Anthropic 接口、又无法直接使用 Provider 协议转换时,才需要额外部署 LiteLLM。它会增加一个服务、一次协议转换和一层排查成本。
-### OpenRouter
-
-```bash
-ANTHROPIC_AUTH_TOKEN=sk-or-v1-xxx
-ANTHROPIC_BASE_URL=https://openrouter.ai/api/v1
-ANTHROPIC_MODEL=openai/gpt-4o
-ANTHROPIC_DEFAULT_SONNET_MODEL=openai/gpt-4o
-ANTHROPIC_DEFAULT_HAIKU_MODEL=openai/gpt-4o-mini
-ANTHROPIC_DEFAULT_OPUS_MODEL=openai/gpt-4o
-DISABLE_TELEMETRY=1
-CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
-```
-
-### MiniMax(已在 .env.example 中配置)
-
-MiniMax 提供 Anthropic 兼容接口,支持直接接入,无需代理。可用模型:
-
-| 模型 | 说明 |
-|------|------|
-| `MiniMax-M3` | 默认推荐,最新一代综合性能优秀,支持 1M 上下文 |
-| `MiniMax-M2.7` | 上一代稳定版本 |
-| `MiniMax-M2.7-highspeed` | 响应更快,适合对速度有要求的场景 |
-
-```bash
-ANTHROPIC_AUTH_TOKEN=your_minimax_api_key_here
-# 海外用户使用 api.minimax.io,国内用户可改为 api.minimaxi.com
-ANTHROPIC_BASE_URL=https://api.minimax.io/anthropic
-ANTHROPIC_MODEL=MiniMax-M3
-ANTHROPIC_DEFAULT_SONNET_MODEL=MiniMax-M3
-ANTHROPIC_DEFAULT_HAIKU_MODEL=MiniMax-M2.7-highspeed
-ANTHROPIC_DEFAULT_OPUS_MODEL=MiniMax-M3
-API_TIMEOUT_MS=3000000
-DISABLE_TELEMETRY=1
-CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
-```
-
-> **获取 API Key**:访问 [MiniMax 开放平台](https://platform.minimax.io) 注册并获取 API Key。
-
----
-
-## 方式三:其他代理工具
-
-社区还有一些专门为 Claude Code 做的代理工具:
-
-| 工具 | 说明 | 链接 |
-|------|------|------|
-| **a2o** | Anthropic → OpenAI 单二进制文件,零依赖 | [Twitter](https://x.com/mantou543/status/2018846154855940200) |
-| **Empero Proxy** | 完整的 Anthropic Messages API 转 OpenAI 代理 | [Twitter](https://x.com/EmperoAI/status/2036840854065762551) |
-| **Alma** | 内置 OpenAI → Anthropic 转换代理的客户端 | [Twitter](https://x.com/yetone/status/2003508782127833332) |
-| **Chutes** | Docker 容器,支持 60+ 开源模型 | [Twitter](https://x.com/chutes_ai/status/2027039742915662232) |
-
----
-
-## 注意事项与已知限制
-
-### 1. `drop_params: true` 很重要
-
-本项目会发送 Anthropic 专有参数(如 `thinking`、`cache_control`),这些参数在 OpenAI API 中不存在。LiteLLM 配置中必须设置 `drop_params: true`,否则请求会报错。
-
-### 2. Extended Thinking 不可用
-
-Anthropic 的 Extended Thinking 功能是专有特性,其他模型不支持。使用第三方模型时此功能自动失效。
-
-### 3. Prompt Caching 不可用
-
-`cache_control` 是 Anthropic 专有功能。使用第三方模型时,prompt caching 不会生效(但不会导致报错,会被 `drop_params` 忽略)。
-
-### 4. 工具调用兼容性
-
-本项目大量使用工具调用(tool_use),LiteLLM 会自动转换 Anthropic tool_use 格式到 OpenAI function_calling 格式。大部分情况下可以正常工作,但某些复杂工具调用可能存在兼容性问题。如遇问题,建议使用能力较强的模型(如 GPT-4o)。
-
-### 5. 遥测和非必要网络请求
-
-建议配置以下环境变量以避免不必要的网络请求:
-```
-DISABLE_TELEMETRY=1
-CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
-```
-
----
-
-## FAQ
-
-### Q: LiteLLM 代理报错 `/v1/responses` 找不到?
-
-部分 OpenAI 兼容服务只支持 `/v1/chat/completions`。在 LiteLLM 配置中添加:
+最小示例:
```yaml
-litellm_settings:
- use_chat_completions_url_for_anthropic_messages: true
+model_list:
+ - model_name: provider-model
+ litellm_params:
+ model: openai/provider-model
+ api_base: https://provider.example.com/v1
+ api_key: os.environ/PROVIDER_API_KEY
```
-### Q: `ANTHROPIC_API_KEY` 和 `ANTHROPIC_AUTH_TOKEN` 有什么区别?
+启动 LiteLLM 后,把它的 Anthropic 兼容地址作为 `anthropic` Provider 接入。部署、鉴权和模型前缀以 [LiteLLM 官方文档](https://docs.litellm.ai/) 为准。
-- `ANTHROPIC_API_KEY` → 通过 `x-api-key` 请求头发送
-- `ANTHROPIC_AUTH_TOKEN` → 通过 `Authorization: Bearer` 请求头发送
+## 能力边界
-LiteLLM 代理默认接受 Bearer Token 格式,建议使用 `ANTHROPIC_AUTH_TOKEN`。
+第三方模型要稳定运行 Agent 工作流,至少需要:
-### Q: 可以同时配置多个模型吗?
+- 正确处理多轮消息、system 内容和工具调用;
+- 保留 tool call ID,并能接收对应的 tool result;
+- 支持足够长的上下文和输出;
+- 在流式模式下发送结构完整、顺序正确的事件。
-可以。在 `litellm_config.yaml` 中配置多个 `model_name`,然后通过修改 `ANTHROPIC_MODEL` 切换。
+思考模式、effort、Prompt Cache、图像输入和结构化输出是否可用,取决于所选协议、服务商和具体模型,不能统一认定为“支持”或“不支持”。遇到问题时先关闭可选能力,验证最小文本与工具调用,再逐项恢复。
-### Q: 本地 Ollama 模型效果不好怎么办?
+## 常见问题
-本项目的系统提示和工具调用对模型能力要求较高。建议使用参数量较大的模型(如 Llama 3 70B+, Qwen 72B+),小模型可能无法正确处理工具调用。
+| 现象 | 优先检查 |
+|------|----------|
+| `401` / `403` | 认证头类型、Token 权限、Base URL 是否属于同一服务 |
+| `404` | Provider 格式是否选错、Base URL 是否重复包含接口路径 |
+| 模型不存在 | 使用服务商真实模型 ID,不要沿用其他平台的别名 |
+| 一直输出文本但不执行工具 | 模型或网关是否完整支持工具调用 |
+| 切换 Provider 后仍请求旧地址 | 是否同时在 `.env`、`settings.json` 和桌面端保存了配置 |
+| 流式响应中断 | 先关闭可选能力,并检查网关是否改写或缓冲事件流 |
diff --git a/docs/images/desktop_ui/06_settings_computer_use.png b/docs/images/desktop_ui/06_settings_computer_use.png
index 7e7aa51a..c3f9bff1 100644
Binary files a/docs/images/desktop_ui/06_settings_computer_use.png and b/docs/images/desktop_ui/06_settings_computer_use.png differ
diff --git a/docs/images/desktop_ui/14_pet_settings_overview.png b/docs/images/desktop_ui/14_pet_settings_overview.png
new file mode 100644
index 00000000..988c5198
Binary files /dev/null and b/docs/images/desktop_ui/14_pet_settings_overview.png differ
diff --git a/docs/images/desktop_ui/15_pet_create_methods.png b/docs/images/desktop_ui/15_pet_create_methods.png
new file mode 100644
index 00000000..ab4ec543
Binary files /dev/null and b/docs/images/desktop_ui/15_pet_create_methods.png differ
diff --git a/docs/images/desktop_ui/16_pet_custom_result.png b/docs/images/desktop_ui/16_pet_custom_result.png
new file mode 100644
index 00000000..35412139
Binary files /dev/null and b/docs/images/desktop_ui/16_pet_custom_result.png differ
diff --git a/docs/images/desktop_ui/17_agent_create.png b/docs/images/desktop_ui/17_agent_create.png
new file mode 100644
index 00000000..d14b9c86
Binary files /dev/null and b/docs/images/desktop_ui/17_agent_create.png differ
diff --git a/docs/images/desktop_ui/18_permission_modes.png b/docs/images/desktop_ui/18_permission_modes.png
new file mode 100644
index 00000000..c6b36cb1
Binary files /dev/null and b/docs/images/desktop_ui/18_permission_modes.png differ
diff --git a/docs/images/desktop_ui/20_scheduled_task.png b/docs/images/desktop_ui/20_scheduled_task.png
new file mode 100644
index 00000000..50f7c4b4
Binary files /dev/null and b/docs/images/desktop_ui/20_scheduled_task.png differ
diff --git a/docs/images/desktop_ui/21_skill_marketplace.png b/docs/images/desktop_ui/21_skill_marketplace.png
new file mode 100644
index 00000000..cb4f7ef0
Binary files /dev/null and b/docs/images/desktop_ui/21_skill_marketplace.png differ
diff --git a/docs/images/desktop_ui/22_workspace_changed_files.png b/docs/images/desktop_ui/22_workspace_changed_files.png
new file mode 100644
index 00000000..f732b71a
Binary files /dev/null and b/docs/images/desktop_ui/22_workspace_changed_files.png differ
diff --git a/docs/images/desktop_ui/23_workspace_diff_review.png b/docs/images/desktop_ui/23_workspace_diff_review.png
new file mode 100644
index 00000000..65a295d3
Binary files /dev/null and b/docs/images/desktop_ui/23_workspace_diff_review.png differ
diff --git a/docs/images/desktop_ui/24_browser_preview.png b/docs/images/desktop_ui/24_browser_preview.png
new file mode 100644
index 00000000..11995e16
Binary files /dev/null and b/docs/images/desktop_ui/24_browser_preview.png differ
diff --git a/docs/images/desktop_ui/25_main_session.png b/docs/images/desktop_ui/25_main_session.png
new file mode 100644
index 00000000..71cae246
Binary files /dev/null and b/docs/images/desktop_ui/25_main_session.png differ
diff --git a/docs/index.md b/docs/index.md
deleted file mode 100644
index ca9eb836..00000000
--- a/docs/index.md
+++ /dev/null
@@ -1,51 +0,0 @@
----
-layout: home
-
-hero:
- name: Claude Code Haha
- text: 本地可运行的 Claude Code
- tagline: 基于泄露源码修复,支持接入任意 Anthropic 兼容 API(MiniMax、OpenRouter 等)
- image:
- src: /images/logo-horizontal.png
- alt: Claude Code Haha
- actions:
- - theme: brand
- text: 快速开始
- link: /guide/quick-start
- - theme: alt
- text: GitHub
- link: https://github.com/NanmiCoder/cc-haha
-
-features:
- - icon: "\U0001F5A5"
- title: 完整 TUI 交互
- details: 与官方 Claude Code 一致的 Ink 终端界面,支持 --print 无头模式
- - icon: "\U0001F9E0"
- title: 记忆系统
- details: 跨会话持久化记忆,自动提取、智能检索、AutoDream 做梦整合
- link: /memory/
- - icon: "\U0001F916"
- title: 多 Agent 系统
- details: 多代理编排、并行任务执行、Teams 协作、Worktree 隔离
- link: /agent/
- - icon: "\U0001F9E9"
- title: Skills 系统
- details: 可扩展能力插件、自定义工作流、条件激活
- link: /skills/01-usage-guide
- - icon: "\U0001F310"
- title: 第三方模型支持
- details: 接入 OpenAI、DeepSeek、Ollama 等任意兼容模型
- link: /guide/third-party-models
- - icon: "\U0001F4AC"
- title: IM 接入
- details: 在桌面端 webapp 配置 Telegram / 飞书,并通过独立 adapter 进程远程对话 Claude Code
- link: /im/
- - icon: "\U0001F4BB"
- title: Computer Use
- details: 桌面控制功能 — 截屏、鼠标、键盘操作(Python Bridge 实现)
- link: /features/computer-use
- - icon: "\U0001F5A5"
- title: 桌面端
- details: 基于 Electron + React 的图形化客户端,多标签、多会话、IM 适配器接入,支持 macOS、Windows 和 Linux
- link: /desktop/
----
diff --git a/docs/public/images/banner.png b/docs/public/images/banner.png
index 770d1494..6b3ab1a0 100644
Binary files a/docs/public/images/banner.png and b/docs/public/images/banner.png differ
diff --git a/docs/reference/local-server.md b/docs/reference/local-server.md
new file mode 100644
index 00000000..031b1520
--- /dev/null
+++ b/docs/reference/local-server.md
@@ -0,0 +1,148 @@
+# 本地 Server
+
+本地 Server 是桌面端、H5 页面和 Claude CLI 之间的运行时边界。它提供 REST API、聊天 WebSocket、Provider 协议代理和桌面 Web 静态资源。打包后的桌面应用会自动管理它;只有源码开发、无界面部署或自定义客户端才需要手工启动。
+
+## 启动
+
+在仓库根目录运行:
+
+```bash
+bun run src/server/index.ts
+```
+
+默认监听 `127.0.0.1:3456`。确认服务就绪:
+
+```bash
+curl http://127.0.0.1:3456/health
+```
+
+返回格式:
+
+```json
+{
+ "status": "ok",
+ "timestamp": "2026-01-01T00:00:00.000Z"
+}
+```
+
+`/health` 是启动探针,始终公开,不代表其他接口已通过认证。
+
+## 启动参数
+
+| 参数 | 环境变量 | 默认值 | 说明 |
+|------|----------|--------|------|
+| `--host ` | `SERVER_HOST` | `127.0.0.1` | 监听地址 |
+| `--port ` | `SERVER_PORT` | `3456` | HTTP 和 WebSocket 端口 |
+| `--cli-path ` | `CLAUDE_CLI_PATH` | 自动解析 | 指定 Server 拉起的 CLI |
+| `--auth-required` | `SERVER_AUTH_REQUIRED=1` | 关闭 | 对能力接口强制显式鉴权 |
+
+命令行的 host 和 port 优先于环境变量。开发时建议保留回环地址;`0.0.0.0` 只表示接受外部连接,并不会自动完成 H5 授权、TLS 或反向代理配置。
+
+## 提供 H5 页面
+
+源码运行时先构建桌面 Web 资源:
+
+```bash
+cd desktop
+bun run build
+cd ..
+bun run src/server/index.ts
+```
+
+Server 会自动查找仓库的 `desktop/dist`。从其他目录启动时,用绝对路径指定构建产物:
+
+```bash
+CLAUDE_H5_DIST_DIR=/absolute/path/to/desktop/dist \
+ bun run /absolute/path/to/src/server/index.ts
+```
+
+| 变量 | 说明 |
+|------|------|
+| `CLAUDE_H5_DIST_DIR` | H5 构建产物目录,目录内必须有 `index.html` |
+| `CLAUDE_H5_PUBLIC_BASE_URL` | 固定的公开服务地址 |
+| `CLAUDE_H5_AUTO_PUBLIC_URL=1` | 在启用 H5 时尝试生成局域网公开地址 |
+
+完整的 Token、允许来源、手机访问和 Nginx 配置见 [H5 访问](../desktop/06-h5-access.md)。
+
+## 访问控制
+
+Server 根据请求来源和能力路径决定是否允许访问:
+
+| 请求 | 默认行为 |
+|------|----------|
+| `GET /health` | 公开,用于启动探针 |
+| H5 静态页面和 assets | 公开的启动外壳;页面本身不包含会话数据 |
+| 直接回环请求 | 仅当客户端地址、Host 和 Origin 都是本机,且没有反向代理跟踪头时视为本机可信 |
+| H5 关闭时的远程能力请求 | 拒绝 `/api`、`/proxy`、`/ws` 和文件能力 |
+| H5 开启时的远程能力请求 | 要求有效 H5 Token,并校验浏览器 Origin |
+| `--auth-required` / `SERVER_AUTH_REQUIRED=1` | 即使 H5 未开启,也对能力接口要求显式认证 |
+
+“连接来自 `127.0.0.1`”本身不足以证明是本机用户。反向代理必须保留公开 `Host`,或传递 `Forwarded`、`X-Forwarded-*`、`X-Real-IP`、`Via` 中至少一种,让 Server 能区分反代流量和直接回环流量。
+
+### Token 传递
+
+- REST、协议代理和文件接口:`Authorization: Bearer `
+- 浏览器 WebSocket:`/ws/?token=`
+
+H5 模式使用设置页生成的 H5 Token。显式 `--auth-required` 模式也能接受与服务端 `ANTHROPIC_API_KEY` 相同的 Bearer Token,但不建议为了远程访问暴露模型密钥;优先启用 H5 并使用独立 Token。
+
+CORS 只限制浏览器读取响应,不是身份认证。非浏览器客户端不会因为 CORS 而安全。
+
+## HTTP 接口范围
+
+业务 REST API 位于 `/api/*`,主要覆盖:
+
+- 会话、对话、搜索和文件系统;
+- 设置、权限、模型、effort 和 Providers;
+- Agents、任务、团队和计划任务;
+- Skills、插件、市场和 MCP;
+- IM 适配器和 Computer Use;
+- 诊断、Doctor、活动统计、记忆和 traces;
+- H5 访问控制。
+
+具体请求与响应以 `src/server/api/` 的当前处理器为准。内部 `/sdk/` WebSocket 是 Server 为自己拉起的 Claude CLI 使用的通道,不是第三方客户端 API。
+
+`/proxy/*` 是 Provider 的协议转换入口,包含运行时认证和模型路由状态。不要把它当成通用的、无状态 OpenAI 代理公开出去。
+
+## 聊天 WebSocket
+
+客户端连接:
+
+```text
+ws://127.0.0.1:3456/ws/
+```
+
+常用客户端消息包括:
+
+- `user_message`、`stop_generation`
+- `permission_response`、`computer_use_permission_response`
+- `set_permission_mode`、`set_runtime_config`
+- `sync_state`、`prewarm_session`
+- `ping`
+
+服务端会发送连接与会话状态、文本增量、思考、工具调用与结果、权限请求、重试/降级状态、错误、任务/团队更新和 `pong`。完整字段以 `src/server/ws/events.ts` 为准。
+
+桌面客户端每 30 秒发送一次 ping;等待 pong 10 秒后会主动重连。重连退避上限为 30 秒,并不会在固定次数后永久停止。自定义客户端应能重复连接、重新同步状态,并忽略未知的新增消息字段。
+
+## 反向代理清单
+
+远程使用时至少完成:
+
+1. 在 Server 端启用 H5,生成独立 Token,并配置精确的允许来源。
+2. 使用 HTTPS,不在公开网络传输明文 Token。
+3. 转发静态页面、`/api/*`、`/proxy/*` 和 `/ws/*`。
+4. 为 `/ws/*` 开启 WebSocket upgrade。
+5. 保留公开 Host 和标准代理头。
+6. 不向公网转发内部 `/sdk/*`。
+
+## 排查
+
+| 现象 | 检查 |
+|------|------|
+| 端口无法监听 | `SERVER_PORT` 是否被占用;是否传入了有效数字 |
+| `/health` 正常但 API 为 `403` | 请求被判定为远程,而 H5 尚未启用 |
+| API 或 WebSocket 为 `401` | H5 Token 过期、缺失,或 WebSocket 没有 query token |
+| 浏览器提示 CORS | 当前页面的精确 Origin 是否在 H5 允许列表 |
+| WebSocket 反复重连 | 代理是否支持 upgrade、Token 是否传入、空闲连接是否被代理关闭 |
+| 页面 `404` | 尚未构建 `desktop/dist`,或 `CLAUDE_H5_DIST_DIR` 指向错误 |
+| 远程请求被当成本机 | 反向代理是否删除了公开 Host 和全部代理跟踪头 |
diff --git a/docs/reference/project-structure.md b/docs/reference/project-structure.md
index 8e6c0918..5393f3cf 100644
--- a/docs/reference/project-structure.md
+++ b/docs/reference/project-structure.md
@@ -1,22 +1,81 @@
# 项目结构
+本项目包含 CLI/TUI、本地 Server、Electron 桌面端、IM Adapter 和文档站。下面只列稳定的职责边界;具体文件以当前源码为准。
+```text
+.
+├── bin/
+│ └── claude-haha # CLI 启动脚本
+├── preload.ts # Bun preload 与构建期兼容入口
+├── package.json # 根项目脚本和依赖
+├── src/
+│ ├── entrypoints/
+│ │ └── cli.tsx # CLI 主入口
+│ ├── main.tsx # TUI 主流程
+│ ├── setup.ts # 启动初始化
+│ ├── screens/ # REPL 等终端界面
+│ ├── components/ # Ink UI 组件
+│ ├── tools/ # Bash、Edit、Grep 等 Agent 工具
+│ ├── commands/ # 斜杠命令
+│ ├── services/ # Provider、MCP、OAuth 等共享服务
+│ ├── utils/ # 运行时工具与 Computer Use 集成
+│ ├── vendor/ # 受控的 vendored 实现
+│ └── server/
+│ ├── index.ts # Bun.serve HTTP/WebSocket 入口
+│ ├── router.ts # REST 资源路由
+│ ├── api/ # API 边界
+│ ├── services/ # 会话、Provider、索引、诊断等服务
+│ ├── ws/ # WebSocket 协议和生命周期
+│ ├── proxy/ # Provider 协议转换
+│ ├── middleware/ # Auth 与 CORS
+│ └── config/ # Provider 预设
+├── desktop/
+│ ├── src/ # React Renderer
+│ │ ├── api/ # Server API / WebSocket 客户端
+│ │ ├── components/ # Chat、Workspace、Browser、布局组件
+│ │ ├── features/ # Pets 等独立功能
+│ │ ├── pages/ # 会话、设置、任务、诊断等页面
+│ │ ├── stores/ # Zustand 状态
+│ │ ├── i18n/ # 桌面多语言
+│ │ └── lib/ # Renderer 运行时工具
+│ ├── electron/
+│ │ ├── main.ts # Electron main 入口
+│ │ ├── preload.ts # 主窗口 Host bridge
+│ │ ├── preview-preload.ts # 原生预览 bridge
+│ │ ├── pet-preload.ts # 宠物窗口 bridge
+│ │ ├── ipc/ # IPC channel 与校验
+│ │ └── services/ # Sidecar、终端、更新、预览等服务
+│ ├── sidecars/
+│ │ └── claude-sidecar.ts # server / cli / adapters 统一入口
+│ ├── scripts/ # 构建、打包和资源准备
+│ └── src-tauri/ # 历史代码及现用打包资源;非当前 Host
+├── adapters/
+│ ├── common/ # 配置、配对、消息与 WS 共享层
+│ ├── telegram/
+│ ├── feishu/
+│ ├── wechat/
+│ ├── dingtalk/
+│ └── whatsapp/
+├── runtime/
+│ ├── mac_helper.py # macOS Computer Use helper
+│ ├── win_helper.py # Windows Computer Use helper
+│ ├── requirements.txt
+│ └── requirements-win.txt
+├── scripts/ # 根项目质量门禁、发布与维护脚本
+├── tests/ # 跨模块测试与测试资源
+├── site/ # React 文档站、内容索引与静态构建
+└── docs/ # 中英文 Markdown 文档内容
```
-bin/claude-haha # 入口脚本
-preload.ts # Bun preload(设置 MACRO 全局变量)
-.env.example # 环境变量模板
-src/
-├── entrypoints/cli.tsx # CLI 主入口
-├── main.tsx # TUI 主逻辑(Commander.js + React/Ink)
-├── localRecoveryCli.ts # 降级 Recovery CLI
-├── setup.ts # 启动初始化
-├── screens/REPL.tsx # 交互 REPL 界面
-├── ink/ # Ink 终端渲染引擎
-├── components/ # UI 组件
-├── tools/ # Agent 工具(Bash, Edit, Grep 等)
-├── commands/ # 斜杠命令(/commit, /review 等)
-├── skills/ # Skill 系统
-├── services/ # 服务层(API, MCP, OAuth 等)
-├── hooks/ # React hooks
-└── utils/ # 工具函数
-```
+
+## 运行边界
+
+| 入口 | 运行时 | 说明 |
+|---|---|---|
+| `src/entrypoints/cli.tsx` | Bun | CLI/TUI 与 Agent 工具 |
+| `src/server/index.ts` | Bun / `Bun.serve` | 本地 HTTP、WebSocket 与 H5 |
+| `desktop/electron/main.ts` | Electron main | 原生桌面 Host |
+| `desktop/src/` | Chromium Renderer | React 桌面 UI |
+| `desktop/sidecars/claude-sidecar.ts` | Bun 编译 Sidecar | 打包后的 Server、CLI 与 Adapter 入口 |
+| `adapters//` | Bun Sidecar | 平台消息接入 |
+
+新增代码时应放在拥有该职责的边界内:桌面原生能力进入 `desktop/electron/`,共享业务 API 进入 `src/server/`,IM 平台差异进入 `adapters//`,不要在 Renderer 中绕过这些边界。
diff --git a/package-lock.json b/package-lock.json
index b0a212a9..db763919 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -78,10 +78,7 @@
"claude-haha": "bin/claude-haha"
},
"devDependencies": {
- "mermaid": "^11.14.0",
- "vitepress": "^1.6.3",
- "vitepress-plugin-mermaid": "^2.0.17",
- "vue": "^3.5.13"
+ "mermaid": "^11.14.0"
}
},
"node_modules/@alcalzone/ansi-tokenize": {
@@ -97,264 +94,6 @@
"node": ">=18"
}
},
- "node_modules/@algolia/abtesting": {
- "version": "1.16.1",
- "resolved": "https://registry.npmmirror.com/@algolia/abtesting/-/abtesting-1.16.1.tgz",
- "integrity": "sha512-Xxk4l00pYI+jE0PNw8y0MvsQWh5278WRtZQav8/BMMi3HKi2xmeuqe11WJ3y8/6nuBHdv39w76OpJb09TMfAVQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.50.1",
- "@algolia/requester-browser-xhr": "5.50.1",
- "@algolia/requester-fetch": "5.50.1",
- "@algolia/requester-node-http": "5.50.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/autocomplete-core": {
- "version": "1.17.7",
- "resolved": "https://registry.npmmirror.com/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz",
- "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/autocomplete-plugin-algolia-insights": "1.17.7",
- "@algolia/autocomplete-shared": "1.17.7"
- }
- },
- "node_modules/@algolia/autocomplete-plugin-algolia-insights": {
- "version": "1.17.7",
- "resolved": "https://registry.npmmirror.com/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz",
- "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/autocomplete-shared": "1.17.7"
- },
- "peerDependencies": {
- "search-insights": ">= 1 < 3"
- }
- },
- "node_modules/@algolia/autocomplete-preset-algolia": {
- "version": "1.17.7",
- "resolved": "https://registry.npmmirror.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz",
- "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/autocomplete-shared": "1.17.7"
- },
- "peerDependencies": {
- "@algolia/client-search": ">= 4.9.1 < 6",
- "algoliasearch": ">= 4.9.1 < 6"
- }
- },
- "node_modules/@algolia/autocomplete-shared": {
- "version": "1.17.7",
- "resolved": "https://registry.npmmirror.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz",
- "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==",
- "dev": true,
- "license": "MIT",
- "peerDependencies": {
- "@algolia/client-search": ">= 4.9.1 < 6",
- "algoliasearch": ">= 4.9.1 < 6"
- }
- },
- "node_modules/@algolia/client-abtesting": {
- "version": "5.50.1",
- "resolved": "https://registry.npmmirror.com/@algolia/client-abtesting/-/client-abtesting-5.50.1.tgz",
- "integrity": "sha512-4peZlPXMwTOey9q1rQKMdCnwZb/E95/1e+7KujXpLLSh0FawJzg//U2NM+r4AiJy4+naT2MTBhj0K30yshnVTA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.50.1",
- "@algolia/requester-browser-xhr": "5.50.1",
- "@algolia/requester-fetch": "5.50.1",
- "@algolia/requester-node-http": "5.50.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/client-analytics": {
- "version": "5.50.1",
- "resolved": "https://registry.npmmirror.com/@algolia/client-analytics/-/client-analytics-5.50.1.tgz",
- "integrity": "sha512-i+aWHHG8NZvGFHtPeMZkxL2Loc6Fm7iaRo15lYSMx8gFL+at9vgdWxhka7mD1fqxkrxXsQstUBCIsSY8FvkEOw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.50.1",
- "@algolia/requester-browser-xhr": "5.50.1",
- "@algolia/requester-fetch": "5.50.1",
- "@algolia/requester-node-http": "5.50.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/client-common": {
- "version": "5.50.1",
- "resolved": "https://registry.npmmirror.com/@algolia/client-common/-/client-common-5.50.1.tgz",
- "integrity": "sha512-Hw52Fwapyk/7hMSV/fI4+s3H9MGZEUcRh4VphyXLAk2oLYdndVUkc6KBi0zwHSzwPAr+ZBwFPe2x6naUt9mZGw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/client-insights": {
- "version": "5.50.1",
- "resolved": "https://registry.npmmirror.com/@algolia/client-insights/-/client-insights-5.50.1.tgz",
- "integrity": "sha512-Bn/wtwhJ7p1OD/6pY+Zzn+zlu2N/SJnH46md/PAbvqIzmjVuwjNwD4y0vV5Ov8naeukXdd7UU9v550+v8+mtlg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.50.1",
- "@algolia/requester-browser-xhr": "5.50.1",
- "@algolia/requester-fetch": "5.50.1",
- "@algolia/requester-node-http": "5.50.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/client-personalization": {
- "version": "5.50.1",
- "resolved": "https://registry.npmmirror.com/@algolia/client-personalization/-/client-personalization-5.50.1.tgz",
- "integrity": "sha512-0V4Tu0RWR8YxkgI9EPVOZHGE4K5pEIhkLNN0CTkP/rnPsqaaSQpNMYW3/mGWdiKOWbX0iVmwLB9QESk3H0jS5g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.50.1",
- "@algolia/requester-browser-xhr": "5.50.1",
- "@algolia/requester-fetch": "5.50.1",
- "@algolia/requester-node-http": "5.50.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/client-query-suggestions": {
- "version": "5.50.1",
- "resolved": "https://registry.npmmirror.com/@algolia/client-query-suggestions/-/client-query-suggestions-5.50.1.tgz",
- "integrity": "sha512-jofcWNYMXJDDr87Z2eivlWY6o71Zn7F7aOvQCXSDAo9QTlyf7BhXEsZymLUvF0O1yU9Q9wvrjAWn8uVHYnAvgw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.50.1",
- "@algolia/requester-browser-xhr": "5.50.1",
- "@algolia/requester-fetch": "5.50.1",
- "@algolia/requester-node-http": "5.50.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/client-search": {
- "version": "5.50.1",
- "resolved": "https://registry.npmmirror.com/@algolia/client-search/-/client-search-5.50.1.tgz",
- "integrity": "sha512-OteRb8WubcmEvU0YlMJwCXs3Q6xrdkb0v50/qZBJP1TF0CvujFZQM++9BjEkTER/Jr9wbPHvjSFKnbMta0b4dQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.50.1",
- "@algolia/requester-browser-xhr": "5.50.1",
- "@algolia/requester-fetch": "5.50.1",
- "@algolia/requester-node-http": "5.50.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/ingestion": {
- "version": "1.50.1",
- "resolved": "https://registry.npmmirror.com/@algolia/ingestion/-/ingestion-1.50.1.tgz",
- "integrity": "sha512-0GmfSgDQK6oiIVXnJvGxtNFOfosBspRTR7csCOYCTL1P8QtxX2vDCIKwTM7xdSAEbJaZ43QlWg25q0Qdsndz8Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.50.1",
- "@algolia/requester-browser-xhr": "5.50.1",
- "@algolia/requester-fetch": "5.50.1",
- "@algolia/requester-node-http": "5.50.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/monitoring": {
- "version": "1.50.1",
- "resolved": "https://registry.npmmirror.com/@algolia/monitoring/-/monitoring-1.50.1.tgz",
- "integrity": "sha512-ySuigKEe4YjYV3si8NVk9BHQpFj/1B+ON7DhhvTvbrZJseHQQloxzq0yHwKmznSdlO6C956fx4pcfOKkZClsyg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.50.1",
- "@algolia/requester-browser-xhr": "5.50.1",
- "@algolia/requester-fetch": "5.50.1",
- "@algolia/requester-node-http": "5.50.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/recommend": {
- "version": "5.50.1",
- "resolved": "https://registry.npmmirror.com/@algolia/recommend/-/recommend-5.50.1.tgz",
- "integrity": "sha512-Cp8T/B0gVmjFlzzp6eP47hwKh5FGyeqQp1N48/ANDdvdiQkPqLyFHQVDwLBH0LddfIPQE+yqmZIgmKc82haF4A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.50.1",
- "@algolia/requester-browser-xhr": "5.50.1",
- "@algolia/requester-fetch": "5.50.1",
- "@algolia/requester-node-http": "5.50.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/requester-browser-xhr": {
- "version": "5.50.1",
- "resolved": "https://registry.npmmirror.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.50.1.tgz",
- "integrity": "sha512-XKdGGLikfrlK66ZSXh/vWcXZZ8Vg3byDFbJD8pwEvN1FoBRGxhxya476IY2ohoTymLa4qB5LBRlIa+2TLHx3Uw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.50.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/requester-fetch": {
- "version": "5.50.1",
- "resolved": "https://registry.npmmirror.com/@algolia/requester-fetch/-/requester-fetch-5.50.1.tgz",
- "integrity": "sha512-mBAU6WyVsDwhHyGM+nodt1/oebHxgvuLlOAoMGbj/1i6LygDHZWDgL1t5JEs37x9Aywv7ZGhqbM1GsfZ54sU6g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.50.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/requester-node-http": {
- "version": "5.50.1",
- "resolved": "https://registry.npmmirror.com/@algolia/requester-node-http/-/requester-node-http-5.50.1.tgz",
- "integrity": "sha512-qmo1LXrNKLHvJE6mdQbLnsZAoZvj7VyF2ft4xmbSGWI2WWm87fx/CjUX4kEExt4y0a6T6nEts6ofpUfH5TEE1A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.50.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
"node_modules/@antfu/install-pkg": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/@antfu/install-pkg/-/install-pkg-1.1.0.tgz",
@@ -1122,42 +861,6 @@
"node": ">=18.0.0"
}
},
- "node_modules/@babel/helper-string-parser": {
- "version": "7.27.1",
- "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
- "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-validator-identifier": {
- "version": "7.28.5",
- "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
- "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/parser": {
- "version": "7.29.2",
- "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.2.tgz",
- "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/types": "^7.29.0"
- },
- "bin": {
- "parser": "bin/babel-parser.js"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
"node_modules/@babel/runtime": {
"version": "7.29.2",
"resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.29.2.tgz",
@@ -1167,20 +870,6 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/types": {
- "version": "7.29.0",
- "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.0.tgz",
- "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-string-parser": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.28.5"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@braintree/sanitize-url": {
"version": "7.1.2",
"resolved": "https://registry.npmmirror.com/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz",
@@ -1239,463 +928,6 @@
"commander": "~14.0.0"
}
},
- "node_modules/@docsearch/css": {
- "version": "3.8.2",
- "resolved": "https://registry.npmmirror.com/@docsearch/css/-/css-3.8.2.tgz",
- "integrity": "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@docsearch/js": {
- "version": "3.8.2",
- "resolved": "https://registry.npmmirror.com/@docsearch/js/-/js-3.8.2.tgz",
- "integrity": "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@docsearch/react": "3.8.2",
- "preact": "^10.0.0"
- }
- },
- "node_modules/@docsearch/js/node_modules/@docsearch/react": {
- "version": "3.8.2",
- "resolved": "https://registry.npmmirror.com/@docsearch/react/-/react-3.8.2.tgz",
- "integrity": "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/autocomplete-core": "1.17.7",
- "@algolia/autocomplete-preset-algolia": "1.17.7",
- "@docsearch/css": "3.8.2",
- "algoliasearch": "^5.14.2"
- },
- "peerDependencies": {
- "@types/react": ">= 16.8.0 < 19.0.0",
- "react": ">= 16.8.0 < 19.0.0",
- "react-dom": ">= 16.8.0 < 19.0.0",
- "search-insights": ">= 1 < 3"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- },
- "react": {
- "optional": true
- },
- "react-dom": {
- "optional": true
- },
- "search-insights": {
- "optional": true
- }
- }
- },
- "node_modules/@docsearch/js/node_modules/react": {
- "version": "18.3.1",
- "resolved": "https://registry.npmmirror.com/react/-/react-18.3.1.tgz",
- "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "loose-envify": "^1.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/@esbuild/aix-ppc64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
- "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "aix"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/android-arm": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
- "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/android-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
- "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/android-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
- "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/darwin-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
- "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/darwin-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
- "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/freebsd-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
- "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/freebsd-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
- "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-arm": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
- "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
- "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-ia32": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
- "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-loong64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
- "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-mips64el": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
- "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-ppc64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
- "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-riscv64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
- "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-s390x": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
- "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
- "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/netbsd-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
- "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/openbsd-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
- "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/sunos-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
- "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/win32-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
- "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/win32-ia32": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
- "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/win32-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
- "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/@growthbook/growthbook": {
"version": "1.6.5",
"resolved": "https://registry.npmmirror.com/@growthbook/growthbook/-/growthbook-1.6.5.tgz",
@@ -1720,16 +952,6 @@
"hono": "^4"
}
},
- "node_modules/@iconify-json/simple-icons": {
- "version": "1.2.76",
- "resolved": "https://registry.npmmirror.com/@iconify-json/simple-icons/-/simple-icons-1.2.76.tgz",
- "integrity": "sha512-lLRlA8yaf+1L5VCPRvR9lynoSklsddKHEylchmZJKdj/q2xVQ1ZAEJ8SCQlv9cbgtMefnlyM98U+8Si2aoFZPA==",
- "dev": true,
- "license": "CC0-1.0",
- "dependencies": {
- "@iconify/types": "*"
- }
- },
"node_modules/@iconify/types": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/@iconify/types/-/types-2.0.0.tgz",
@@ -1749,38 +971,6 @@
"mlly": "^1.8.0"
}
},
- "node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.5.5",
- "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
- "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@mermaid-js/mermaid-mindmap": {
- "version": "9.3.0",
- "resolved": "https://registry.npmmirror.com/@mermaid-js/mermaid-mindmap/-/mermaid-mindmap-9.3.0.tgz",
- "integrity": "sha512-IhtYSVBBRYviH1Ehu8gk69pMDF8DSRqXBRDMWrEfHoaMruHeaP2DXA3PBnuwsMaCdPQhlUUcy/7DBLAEIXvCAw==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@braintree/sanitize-url": "^6.0.0",
- "cytoscape": "^3.23.0",
- "cytoscape-cose-bilkent": "^4.1.0",
- "cytoscape-fcose": "^2.1.0",
- "d3": "^7.0.0",
- "khroma": "^2.0.0",
- "non-layered-tidy-tree-layout": "^2.0.2"
- }
- },
- "node_modules/@mermaid-js/mermaid-mindmap/node_modules/@braintree/sanitize-url": {
- "version": "6.0.4",
- "resolved": "https://registry.npmmirror.com/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz",
- "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
"node_modules/@mermaid-js/parser": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/@mermaid-js/parser/-/parser-1.1.0.tgz",
@@ -1955,449 +1145,12 @@
"integrity": "sha512-bQY06wzzR8D2+vVCUoBsr5QS2U6UgPUQRmErNwtsuI6vLcyRKkafjkr3KxbtGFf9aBBIV2mcvlsKD1UYaIV+sg==",
"license": "MIT"
},
- "node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz",
- "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-android-arm64": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz",
- "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz",
- "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz",
- "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz",
- "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ]
- },
- "node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz",
- "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz",
- "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz",
- "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz",
- "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz",
- "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-loong64-gnu": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz",
- "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-loong64-musl": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz",
- "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-ppc64-gnu": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz",
- "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-ppc64-musl": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz",
- "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz",
- "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-riscv64-musl": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz",
- "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz",
- "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz",
- "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz",
- "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-openbsd-x64": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz",
- "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ]
- },
- "node_modules/@rollup/rollup-openharmony-arm64": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz",
- "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openharmony"
- ]
- },
- "node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz",
- "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz",
- "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-x64-gnu": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz",
- "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz",
- "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
"node_modules/@sec-ant/readable-stream": {
"version": "0.4.1",
"resolved": "https://registry.npmmirror.com/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz",
"integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==",
"license": "MIT"
},
- "node_modules/@shikijs/core": {
- "version": "2.5.0",
- "resolved": "https://registry.npmmirror.com/@shikijs/core/-/core-2.5.0.tgz",
- "integrity": "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@shikijs/engine-javascript": "2.5.0",
- "@shikijs/engine-oniguruma": "2.5.0",
- "@shikijs/types": "2.5.0",
- "@shikijs/vscode-textmate": "^10.0.2",
- "@types/hast": "^3.0.4",
- "hast-util-to-html": "^9.0.4"
- }
- },
- "node_modules/@shikijs/engine-javascript": {
- "version": "2.5.0",
- "resolved": "https://registry.npmmirror.com/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz",
- "integrity": "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@shikijs/types": "2.5.0",
- "@shikijs/vscode-textmate": "^10.0.2",
- "oniguruma-to-es": "^3.1.0"
- }
- },
- "node_modules/@shikijs/engine-oniguruma": {
- "version": "2.5.0",
- "resolved": "https://registry.npmmirror.com/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz",
- "integrity": "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@shikijs/types": "2.5.0",
- "@shikijs/vscode-textmate": "^10.0.2"
- }
- },
- "node_modules/@shikijs/langs": {
- "version": "2.5.0",
- "resolved": "https://registry.npmmirror.com/@shikijs/langs/-/langs-2.5.0.tgz",
- "integrity": "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@shikijs/types": "2.5.0"
- }
- },
- "node_modules/@shikijs/themes": {
- "version": "2.5.0",
- "resolved": "https://registry.npmmirror.com/@shikijs/themes/-/themes-2.5.0.tgz",
- "integrity": "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@shikijs/types": "2.5.0"
- }
- },
- "node_modules/@shikijs/transformers": {
- "version": "2.5.0",
- "resolved": "https://registry.npmmirror.com/@shikijs/transformers/-/transformers-2.5.0.tgz",
- "integrity": "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@shikijs/core": "2.5.0",
- "@shikijs/types": "2.5.0"
- }
- },
- "node_modules/@shikijs/types": {
- "version": "2.5.0",
- "resolved": "https://registry.npmmirror.com/@shikijs/types/-/types-2.5.0.tgz",
- "integrity": "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@shikijs/vscode-textmate": "^10.0.2",
- "@types/hast": "^3.0.4"
- }
- },
- "node_modules/@shikijs/vscode-textmate": {
- "version": "10.0.2",
- "resolved": "https://registry.npmmirror.com/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz",
- "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@sindresorhus/merge-streams": {
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz",
@@ -3331,13 +2084,6 @@
"@types/d3-selection": "*"
}
},
- "node_modules/@types/estree": {
- "version": "1.0.8",
- "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz",
- "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@types/geojson": {
"version": "7946.0.16",
"resolved": "https://registry.npmmirror.com/@types/geojson/-/geojson-7946.0.16.tgz",
@@ -3345,23 +2091,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@types/hast": {
- "version": "3.0.4",
- "resolved": "https://registry.npmmirror.com/@types/hast/-/hast-3.0.4.tgz",
- "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/unist": "*"
- }
- },
- "node_modules/@types/linkify-it": {
- "version": "5.0.0",
- "resolved": "https://registry.npmmirror.com/@types/linkify-it/-/linkify-it-5.0.0.tgz",
- "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@types/lodash": {
"version": "4.17.24",
"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.24.tgz",
@@ -3377,34 +2106,6 @@
"@types/lodash": "*"
}
},
- "node_modules/@types/markdown-it": {
- "version": "14.1.2",
- "resolved": "https://registry.npmmirror.com/@types/markdown-it/-/markdown-it-14.1.2.tgz",
- "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/linkify-it": "^5",
- "@types/mdurl": "^2"
- }
- },
- "node_modules/@types/mdast": {
- "version": "4.0.4",
- "resolved": "https://registry.npmmirror.com/@types/mdast/-/mdast-4.0.4.tgz",
- "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/unist": "*"
- }
- },
- "node_modules/@types/mdurl": {
- "version": "2.0.0",
- "resolved": "https://registry.npmmirror.com/@types/mdurl/-/mdurl-2.0.0.tgz",
- "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz",
@@ -3413,27 +2114,6 @@
"license": "MIT",
"optional": true
},
- "node_modules/@types/unist": {
- "version": "3.0.3",
- "resolved": "https://registry.npmmirror.com/@types/unist/-/unist-3.0.3.tgz",
- "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/web-bluetooth": {
- "version": "0.0.21",
- "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
- "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@ungap/structured-clone": {
- "version": "1.3.0",
- "resolved": "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
- "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/@upsetjs/venn.js": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/@upsetjs/venn.js/-/venn.js-2.0.0.tgz",
@@ -3445,271 +2125,6 @@
"d3-transition": "^3.0.1"
}
},
- "node_modules/@vitejs/plugin-vue": {
- "version": "5.2.4",
- "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz",
- "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.0.0 || >=20.0.0"
- },
- "peerDependencies": {
- "vite": "^5.0.0 || ^6.0.0",
- "vue": "^3.2.25"
- }
- },
- "node_modules/@vue/compiler-core": {
- "version": "3.5.32",
- "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.32.tgz",
- "integrity": "sha512-4x74Tbtqnda8s/NSD6e1Dr5p1c8HdMU5RWSjMSUzb8RTcUQqevDCxVAitcLBKT+ie3o0Dl9crc/S/opJM7qBGQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/parser": "^7.29.2",
- "@vue/shared": "3.5.32",
- "entities": "^7.0.1",
- "estree-walker": "^2.0.2",
- "source-map-js": "^1.2.1"
- }
- },
- "node_modules/@vue/compiler-dom": {
- "version": "3.5.32",
- "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.32.tgz",
- "integrity": "sha512-ybHAu70NtiEI1fvAUz3oXZqkUYEe5J98GjMDpTGl5iHb0T15wQYLR4wE3h9xfuTNA+Cm2f4czfe8B4s+CCH57Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vue/compiler-core": "3.5.32",
- "@vue/shared": "3.5.32"
- }
- },
- "node_modules/@vue/compiler-sfc": {
- "version": "3.5.32",
- "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.32.tgz",
- "integrity": "sha512-8UYUYo71cP/0YHMO814TRZlPuUUw3oifHuMR7Wp9SNoRSrxRQnhMLNlCeaODNn6kNTJsjFoQ/kqIj4qGvya4Xg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/parser": "^7.29.2",
- "@vue/compiler-core": "3.5.32",
- "@vue/compiler-dom": "3.5.32",
- "@vue/compiler-ssr": "3.5.32",
- "@vue/shared": "3.5.32",
- "estree-walker": "^2.0.2",
- "magic-string": "^0.30.21",
- "postcss": "^8.5.8",
- "source-map-js": "^1.2.1"
- }
- },
- "node_modules/@vue/compiler-ssr": {
- "version": "3.5.32",
- "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.32.tgz",
- "integrity": "sha512-Gp4gTs22T3DgRotZ8aA/6m2jMR+GMztvBXUBEUOYOcST+giyGWJ4WvFd7QLHBkzTxkfOt8IELKNdpzITLbA2rw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vue/compiler-dom": "3.5.32",
- "@vue/shared": "3.5.32"
- }
- },
- "node_modules/@vue/devtools-api": {
- "version": "7.7.9",
- "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.9.tgz",
- "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vue/devtools-kit": "^7.7.9"
- }
- },
- "node_modules/@vue/devtools-kit": {
- "version": "7.7.9",
- "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz",
- "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vue/devtools-shared": "^7.7.9",
- "birpc": "^2.3.0",
- "hookable": "^5.5.3",
- "mitt": "^3.0.1",
- "perfect-debounce": "^1.0.0",
- "speakingurl": "^14.0.1",
- "superjson": "^2.2.2"
- }
- },
- "node_modules/@vue/devtools-shared": {
- "version": "7.7.9",
- "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz",
- "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "rfdc": "^1.4.1"
- }
- },
- "node_modules/@vue/reactivity": {
- "version": "3.5.32",
- "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.32.tgz",
- "integrity": "sha512-/ORasxSGvZ6MN5gc+uE364SxFdJ0+WqVG0CENXaGW58TOCdrAW76WWaplDtECeS1qphvtBZtR+3/o1g1zL4xPQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vue/shared": "3.5.32"
- }
- },
- "node_modules/@vue/runtime-core": {
- "version": "3.5.32",
- "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.32.tgz",
- "integrity": "sha512-pDrXCejn4UpFDFmMd27AcJEbHaLemaE5o4pbb7sLk79SRIhc6/t34BQA7SGNgYtbMnvbF/HHOftYBgFJtUoJUQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vue/reactivity": "3.5.32",
- "@vue/shared": "3.5.32"
- }
- },
- "node_modules/@vue/runtime-dom": {
- "version": "3.5.32",
- "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.32.tgz",
- "integrity": "sha512-1CDVv7tv/IV13V8Nip1k/aaObVbWqRlVCVezTwx3K07p7Vxossp5JU1dcPNhJk3w347gonIUT9jQOGutyJrSVQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vue/reactivity": "3.5.32",
- "@vue/runtime-core": "3.5.32",
- "@vue/shared": "3.5.32",
- "csstype": "^3.2.3"
- }
- },
- "node_modules/@vue/server-renderer": {
- "version": "3.5.32",
- "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.32.tgz",
- "integrity": "sha512-IOjm2+JQwRFS7W28HNuJeXQle9KdZbODFY7hFGVtnnghF51ta20EWAZJHX+zLGtsHhaU6uC9BGPV52KVpYryMQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vue/compiler-ssr": "3.5.32",
- "@vue/shared": "3.5.32"
- },
- "peerDependencies": {
- "vue": "3.5.32"
- }
- },
- "node_modules/@vue/shared": {
- "version": "3.5.32",
- "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.32.tgz",
- "integrity": "sha512-ksNyrmRQzWJJ8n3cRDuSF7zNNontuJg1YHnmWRJd2AMu8Ij2bqwiiri2lH5rHtYPZjj4STkNcgcmiQqlOjiYGg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@vueuse/core": {
- "version": "12.8.2",
- "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-12.8.2.tgz",
- "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/web-bluetooth": "^0.0.21",
- "@vueuse/metadata": "12.8.2",
- "@vueuse/shared": "12.8.2",
- "vue": "^3.5.13"
- },
- "funding": {
- "url": "https://github.com/sponsors/antfu"
- }
- },
- "node_modules/@vueuse/integrations": {
- "version": "12.8.2",
- "resolved": "https://registry.npmmirror.com/@vueuse/integrations/-/integrations-12.8.2.tgz",
- "integrity": "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vueuse/core": "12.8.2",
- "@vueuse/shared": "12.8.2",
- "vue": "^3.5.13"
- },
- "funding": {
- "url": "https://github.com/sponsors/antfu"
- },
- "peerDependencies": {
- "async-validator": "^4",
- "axios": "^1",
- "change-case": "^5",
- "drauu": "^0.4",
- "focus-trap": "^7",
- "fuse.js": "^7",
- "idb-keyval": "^6",
- "jwt-decode": "^4",
- "nprogress": "^0.2",
- "qrcode": "^1.5",
- "sortablejs": "^1",
- "universal-cookie": "^7"
- },
- "peerDependenciesMeta": {
- "async-validator": {
- "optional": true
- },
- "axios": {
- "optional": true
- },
- "change-case": {
- "optional": true
- },
- "drauu": {
- "optional": true
- },
- "focus-trap": {
- "optional": true
- },
- "fuse.js": {
- "optional": true
- },
- "idb-keyval": {
- "optional": true
- },
- "jwt-decode": {
- "optional": true
- },
- "nprogress": {
- "optional": true
- },
- "qrcode": {
- "optional": true
- },
- "sortablejs": {
- "optional": true
- },
- "universal-cookie": {
- "optional": true
- }
- }
- },
- "node_modules/@vueuse/metadata": {
- "version": "12.8.2",
- "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-12.8.2.tgz",
- "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/antfu"
- }
- },
- "node_modules/@vueuse/shared": {
- "version": "12.8.2",
- "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-12.8.2.tgz",
- "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "vue": "^3.5.13"
- },
- "funding": {
- "url": "https://github.com/sponsors/antfu"
- }
- },
"node_modules/accepts": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/accepts/-/accepts-2.0.0.tgz",
@@ -3778,32 +2193,6 @@
}
}
},
- "node_modules/algoliasearch": {
- "version": "5.50.1",
- "resolved": "https://registry.npmmirror.com/algoliasearch/-/algoliasearch-5.50.1.tgz",
- "integrity": "sha512-/bwdue1/8LWELn/DBalGRfuLsXBLXULJo/yOeavJtDu8rBwxIzC6/Rz9Jg19S21VkJvRuZO1k8CZXBMS73mYbA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/abtesting": "1.16.1",
- "@algolia/client-abtesting": "5.50.1",
- "@algolia/client-analytics": "5.50.1",
- "@algolia/client-common": "5.50.1",
- "@algolia/client-insights": "5.50.1",
- "@algolia/client-personalization": "5.50.1",
- "@algolia/client-query-suggestions": "5.50.1",
- "@algolia/client-search": "5.50.1",
- "@algolia/ingestion": "1.50.1",
- "@algolia/monitoring": "1.50.1",
- "@algolia/recommend": "5.50.1",
- "@algolia/requester-browser-xhr": "5.50.1",
- "@algolia/requester-fetch": "5.50.1",
- "@algolia/requester-node-http": "5.50.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
"node_modules/ansi-escapes": {
"version": "7.3.0",
"resolved": "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-7.3.0.tgz",
@@ -3916,16 +2305,6 @@
"node": "*"
}
},
- "node_modules/birpc": {
- "version": "2.9.0",
- "resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.9.0.tgz",
- "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/antfu"
- }
- },
"node_modules/body-parser": {
"version": "2.2.2",
"resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-2.2.2.tgz",
@@ -4009,17 +2388,6 @@
"node": ">=6"
}
},
- "node_modules/ccount": {
- "version": "2.0.1",
- "resolved": "https://registry.npmmirror.com/ccount/-/ccount-2.0.1.tgz",
- "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
"node_modules/chalk": {
"version": "5.6.2",
"resolved": "https://registry.npmmirror.com/chalk/-/chalk-5.6.2.tgz",
@@ -4032,28 +2400,6 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
- "node_modules/character-entities-html4": {
- "version": "2.1.0",
- "resolved": "https://registry.npmmirror.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
- "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/character-entities-legacy": {
- "version": "3.0.0",
- "resolved": "https://registry.npmmirror.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
- "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
"node_modules/chevrotain": {
"version": "12.0.0",
"resolved": "https://registry.npmmirror.com/chevrotain/-/chevrotain-12.0.0.tgz",
@@ -4274,17 +2620,6 @@
"node": ">= 0.8"
}
},
- "node_modules/comma-separated-tokens": {
- "version": "2.0.3",
- "resolved": "https://registry.npmmirror.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
- "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
"node_modules/commander": {
"version": "14.0.3",
"resolved": "https://registry.npmmirror.com/commander/-/commander-14.0.3.tgz",
@@ -4351,22 +2686,6 @@
"node": ">=6.6.0"
}
},
- "node_modules/copy-anything": {
- "version": "4.0.5",
- "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-4.0.5.tgz",
- "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-what": "^5.2.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/mesqueeb"
- }
- },
"node_modules/cors": {
"version": "2.8.6",
"resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.6.tgz",
@@ -4414,13 +2733,6 @@
"integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==",
"license": "MIT"
},
- "node_modules/csstype": {
- "version": "3.2.3",
- "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz",
- "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/cytoscape": {
"version": "3.33.2",
"resolved": "https://registry.npmmirror.com/cytoscape/-/cytoscape-3.33.2.tgz",
@@ -5055,30 +3367,6 @@
"node": ">= 0.8"
}
},
- "node_modules/dequal": {
- "version": "2.0.3",
- "resolved": "https://registry.npmmirror.com/dequal/-/dequal-2.0.3.tgz",
- "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/devlop": {
- "version": "1.1.0",
- "resolved": "https://registry.npmmirror.com/devlop/-/devlop-1.1.0.tgz",
- "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "dequal": "^2.0.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
"node_modules/diff": {
"version": "8.0.4",
"resolved": "https://registry.npmmirror.com/diff/-/diff-8.0.4.tgz",
@@ -5148,13 +3436,6 @@
"integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
"license": "MIT"
},
- "node_modules/emoji-regex-xs": {
- "version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz",
- "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/encodeurl": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz",
@@ -5164,19 +3445,6 @@
"node": ">= 0.8"
}
},
- "node_modules/entities": {
- "version": "7.0.1",
- "resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz",
- "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
- "dev": true,
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=0.12"
- },
- "funding": {
- "url": "https://github.com/fb55/entities?sponsor=1"
- }
- },
"node_modules/env-paths": {
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/env-paths/-/env-paths-4.0.0.tgz",
@@ -5259,45 +3527,6 @@
"benchmarks"
]
},
- "node_modules/esbuild": {
- "version": "0.21.5",
- "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz",
- "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=12"
- },
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.21.5",
- "@esbuild/android-arm": "0.21.5",
- "@esbuild/android-arm64": "0.21.5",
- "@esbuild/android-x64": "0.21.5",
- "@esbuild/darwin-arm64": "0.21.5",
- "@esbuild/darwin-x64": "0.21.5",
- "@esbuild/freebsd-arm64": "0.21.5",
- "@esbuild/freebsd-x64": "0.21.5",
- "@esbuild/linux-arm": "0.21.5",
- "@esbuild/linux-arm64": "0.21.5",
- "@esbuild/linux-ia32": "0.21.5",
- "@esbuild/linux-loong64": "0.21.5",
- "@esbuild/linux-mips64el": "0.21.5",
- "@esbuild/linux-ppc64": "0.21.5",
- "@esbuild/linux-riscv64": "0.21.5",
- "@esbuild/linux-s390x": "0.21.5",
- "@esbuild/linux-x64": "0.21.5",
- "@esbuild/netbsd-x64": "0.21.5",
- "@esbuild/openbsd-x64": "0.21.5",
- "@esbuild/sunos-x64": "0.21.5",
- "@esbuild/win32-arm64": "0.21.5",
- "@esbuild/win32-ia32": "0.21.5",
- "@esbuild/win32-x64": "0.21.5"
- }
- },
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz",
@@ -5313,13 +3542,6 @@
"node": ">=8"
}
},
- "node_modules/estree-walker": {
- "version": "2.0.2",
- "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
- "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz",
@@ -5578,16 +3800,6 @@
"node": ">=8"
}
},
- "node_modules/focus-trap": {
- "version": "7.8.0",
- "resolved": "https://registry.npmmirror.com/focus-trap/-/focus-trap-7.8.0.tgz",
- "integrity": "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "tabbable": "^6.4.0"
- }
- },
"node_modules/follow-redirects": {
"version": "1.16.0",
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.16.0.tgz",
@@ -5675,21 +3887,6 @@
"node": ">= 0.8"
}
},
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
@@ -5934,44 +4131,6 @@
"node": ">= 0.4"
}
},
- "node_modules/hast-util-to-html": {
- "version": "9.0.5",
- "resolved": "https://registry.npmmirror.com/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz",
- "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/hast": "^3.0.0",
- "@types/unist": "^3.0.0",
- "ccount": "^2.0.0",
- "comma-separated-tokens": "^2.0.0",
- "hast-util-whitespace": "^3.0.0",
- "html-void-elements": "^3.0.0",
- "mdast-util-to-hast": "^13.0.0",
- "property-information": "^7.0.0",
- "space-separated-tokens": "^2.0.0",
- "stringify-entities": "^4.0.0",
- "zwitch": "^2.0.4"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
- }
- },
- "node_modules/hast-util-whitespace": {
- "version": "3.0.0",
- "resolved": "https://registry.npmmirror.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
- "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/hast": "^3.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
- }
- },
"node_modules/highlight.js": {
"version": "11.11.1",
"resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.11.1.tgz",
@@ -5990,24 +4149,6 @@
"node": ">=16.9.0"
}
},
- "node_modules/hookable": {
- "version": "5.5.3",
- "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz",
- "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/html-void-elements": {
- "version": "3.0.0",
- "resolved": "https://registry.npmmirror.com/html-void-elements/-/html-void-elements-3.0.0.tgz",
- "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
"node_modules/http-errors": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.1.tgz",
@@ -6306,19 +4447,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/is-what": {
- "version": "5.5.0",
- "resolved": "https://registry.npmmirror.com/is-what/-/is-what-5.5.0.tgz",
- "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/mesqueeb"
- }
- },
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz",
@@ -6334,15 +4462,6 @@
"url": "https://github.com/sponsors/panva"
}
},
- "node_modules/js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "peer": true
- },
"node_modules/json-bigint": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/json-bigint/-/json-bigint-1.0.0.tgz",
@@ -6487,21 +4606,6 @@
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
"license": "MIT"
},
- "node_modules/loose-envify": {
- "version": "1.4.0",
- "resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz",
- "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "js-tokens": "^3.0.0 || ^4.0.0"
- },
- "bin": {
- "loose-envify": "cli.js"
- }
- },
"node_modules/lru-cache": {
"version": "11.2.7",
"resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.2.7.tgz",
@@ -6511,23 +4615,6 @@
"node": "20 || >=22"
}
},
- "node_modules/magic-string": {
- "version": "0.30.21",
- "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
- "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/sourcemap-codec": "^1.5.5"
- }
- },
- "node_modules/mark.js": {
- "version": "8.11.1",
- "resolved": "https://registry.npmmirror.com/mark.js/-/mark.js-8.11.1.tgz",
- "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/marked": {
"version": "17.0.5",
"resolved": "https://registry.npmmirror.com/marked/-/marked-17.0.5.tgz",
@@ -6549,28 +4636,6 @@
"node": ">= 0.4"
}
},
- "node_modules/mdast-util-to-hast": {
- "version": "13.2.1",
- "resolved": "https://registry.npmmirror.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz",
- "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/hast": "^3.0.0",
- "@types/mdast": "^4.0.0",
- "@ungap/structured-clone": "^1.0.0",
- "devlop": "^1.0.0",
- "micromark-util-sanitize-uri": "^2.0.0",
- "trim-lines": "^3.0.0",
- "unist-util-position": "^5.0.0",
- "unist-util-visit": "^5.0.0",
- "vfile": "^6.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
- }
- },
"node_modules/media-typer": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-1.1.0.tgz",
@@ -6641,100 +4706,6 @@
"node": ">= 20"
}
},
- "node_modules/micromark-util-character": {
- "version": "2.1.1",
- "resolved": "https://registry.npmmirror.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
- "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "micromark-util-symbol": "^2.0.0",
- "micromark-util-types": "^2.0.0"
- }
- },
- "node_modules/micromark-util-encode": {
- "version": "2.0.1",
- "resolved": "https://registry.npmmirror.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
- "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT"
- },
- "node_modules/micromark-util-sanitize-uri": {
- "version": "2.0.1",
- "resolved": "https://registry.npmmirror.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
- "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "micromark-util-character": "^2.0.0",
- "micromark-util-encode": "^2.0.0",
- "micromark-util-symbol": "^2.0.0"
- }
- },
- "node_modules/micromark-util-symbol": {
- "version": "2.0.1",
- "resolved": "https://registry.npmmirror.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
- "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT"
- },
- "node_modules/micromark-util-types": {
- "version": "2.0.2",
- "resolved": "https://registry.npmmirror.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
- "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT"
- },
"node_modules/mime-db": {
"version": "1.54.0",
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.54.0.tgz",
@@ -6769,20 +4740,6 @@
"node": ">=6"
}
},
- "node_modules/minisearch": {
- "version": "7.2.0",
- "resolved": "https://registry.npmmirror.com/minisearch/-/minisearch-7.2.0.tgz",
- "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/mitt": {
- "version": "3.0.1",
- "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz",
- "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/mlly": {
"version": "1.8.2",
"resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.8.2.tgz",
@@ -6802,25 +4759,6 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
- "node_modules/nanoid": {
- "version": "3.3.11",
- "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
- "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
- "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/negotiator": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-1.0.0.tgz",
@@ -6868,14 +4806,6 @@
"url": "https://opencollective.com/node-fetch"
}
},
- "node_modules/non-layered-tidy-tree-layout": {
- "version": "2.0.2",
- "resolved": "https://registry.npmmirror.com/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz",
- "integrity": "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
"node_modules/npm-run-path": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-6.0.0.tgz",
@@ -6961,18 +4891,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/oniguruma-to-es": {
- "version": "3.1.1",
- "resolved": "https://registry.npmmirror.com/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz",
- "integrity": "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex-xs": "^1.0.0",
- "regex": "^6.0.1",
- "regex-recursion": "^6.0.2"
- }
- },
"node_modules/p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz",
@@ -7115,20 +5033,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/perfect-debounce": {
- "version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
- "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/picocolors": {
- "version": "1.1.1",
- "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
- "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/picomatch": {
"version": "4.0.4",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.4.tgz",
@@ -7189,46 +5093,6 @@
"points-on-curve": "0.2.0"
}
},
- "node_modules/postcss": {
- "version": "8.5.8",
- "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.8.tgz",
- "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "nanoid": "^3.3.11",
- "picocolors": "^1.1.1",
- "source-map-js": "^1.2.1"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
- "node_modules/preact": {
- "version": "10.29.1",
- "resolved": "https://registry.npmmirror.com/preact/-/preact-10.29.1.tgz",
- "integrity": "sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/preact"
- }
- },
"node_modules/pretty-ms": {
"version": "9.3.0",
"resolved": "https://registry.npmmirror.com/pretty-ms/-/pretty-ms-9.3.0.tgz",
@@ -7261,17 +5125,6 @@
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"license": "ISC"
},
- "node_modules/property-information": {
- "version": "7.1.0",
- "resolved": "https://registry.npmmirror.com/property-information/-/property-information-7.1.0.tgz",
- "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -7387,33 +5240,6 @@
"url": "https://paulmillr.com/funding/"
}
},
- "node_modules/regex": {
- "version": "6.1.0",
- "resolved": "https://registry.npmmirror.com/regex/-/regex-6.1.0.tgz",
- "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "regex-utilities": "^2.3.0"
- }
- },
- "node_modules/regex-recursion": {
- "version": "6.0.2",
- "resolved": "https://registry.npmmirror.com/regex-recursion/-/regex-recursion-6.0.2.tgz",
- "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "regex-utilities": "^2.3.0"
- }
- },
- "node_modules/regex-utilities": {
- "version": "2.3.0",
- "resolved": "https://registry.npmmirror.com/regex-utilities/-/regex-utilities-2.3.0.tgz",
- "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
@@ -7469,13 +5295,6 @@
"node": ">= 4"
}
},
- "node_modules/rfdc": {
- "version": "1.4.1",
- "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz",
- "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/robust-predicates": {
"version": "3.0.3",
"resolved": "https://registry.npmmirror.com/robust-predicates/-/robust-predicates-3.0.3.tgz",
@@ -7483,51 +5302,6 @@
"dev": true,
"license": "Unlicense"
},
- "node_modules/rollup": {
- "version": "4.60.1",
- "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.60.1.tgz",
- "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/estree": "1.0.8"
- },
- "bin": {
- "rollup": "dist/bin/rollup"
- },
- "engines": {
- "node": ">=18.0.0",
- "npm": ">=8.0.0"
- },
- "optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.60.1",
- "@rollup/rollup-android-arm64": "4.60.1",
- "@rollup/rollup-darwin-arm64": "4.60.1",
- "@rollup/rollup-darwin-x64": "4.60.1",
- "@rollup/rollup-freebsd-arm64": "4.60.1",
- "@rollup/rollup-freebsd-x64": "4.60.1",
- "@rollup/rollup-linux-arm-gnueabihf": "4.60.1",
- "@rollup/rollup-linux-arm-musleabihf": "4.60.1",
- "@rollup/rollup-linux-arm64-gnu": "4.60.1",
- "@rollup/rollup-linux-arm64-musl": "4.60.1",
- "@rollup/rollup-linux-loong64-gnu": "4.60.1",
- "@rollup/rollup-linux-loong64-musl": "4.60.1",
- "@rollup/rollup-linux-ppc64-gnu": "4.60.1",
- "@rollup/rollup-linux-ppc64-musl": "4.60.1",
- "@rollup/rollup-linux-riscv64-gnu": "4.60.1",
- "@rollup/rollup-linux-riscv64-musl": "4.60.1",
- "@rollup/rollup-linux-s390x-gnu": "4.60.1",
- "@rollup/rollup-linux-x64-gnu": "4.60.1",
- "@rollup/rollup-linux-x64-musl": "4.60.1",
- "@rollup/rollup-openbsd-x64": "4.60.1",
- "@rollup/rollup-openharmony-arm64": "4.60.1",
- "@rollup/rollup-win32-arm64-msvc": "4.60.1",
- "@rollup/rollup-win32-ia32-msvc": "4.60.1",
- "@rollup/rollup-win32-x64-gnu": "4.60.1",
- "@rollup/rollup-win32-x64-msvc": "4.60.1",
- "fsevents": "~2.3.2"
- }
- },
"node_modules/roughjs": {
"version": "4.6.6",
"resolved": "https://registry.npmmirror.com/roughjs/-/roughjs-4.6.6.tgz",
@@ -7596,14 +5370,6 @@
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
"license": "MIT"
},
- "node_modules/search-insights": {
- "version": "2.17.3",
- "resolved": "https://registry.npmmirror.com/search-insights/-/search-insights-2.17.3.tgz",
- "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==",
- "dev": true,
- "license": "MIT",
- "peer": true
- },
"node_modules/semver": {
"version": "7.7.4",
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz",
@@ -7706,23 +5472,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/shiki": {
- "version": "2.5.0",
- "resolved": "https://registry.npmmirror.com/shiki/-/shiki-2.5.0.tgz",
- "integrity": "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@shikijs/core": "2.5.0",
- "@shikijs/engine-javascript": "2.5.0",
- "@shikijs/engine-oniguruma": "2.5.0",
- "@shikijs/langs": "2.5.0",
- "@shikijs/themes": "2.5.0",
- "@shikijs/types": "2.5.0",
- "@shikijs/vscode-textmate": "^10.0.2",
- "@types/hast": "^3.0.4"
- }
- },
"node_modules/side-channel": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz",
@@ -7823,37 +5572,6 @@
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
}
},
- "node_modules/source-map-js": {
- "version": "1.2.1",
- "resolved": "https://registry.npmmirror.com/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/space-separated-tokens": {
- "version": "2.0.2",
- "resolved": "https://registry.npmmirror.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
- "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/speakingurl": {
- "version": "14.0.1",
- "resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz",
- "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/stack-utils": {
"version": "2.0.6",
"resolved": "https://registry.npmmirror.com/stack-utils/-/stack-utils-2.0.6.tgz",
@@ -7891,21 +5609,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/stringify-entities": {
- "version": "4.0.4",
- "resolved": "https://registry.npmmirror.com/stringify-entities/-/stringify-entities-4.0.4.tgz",
- "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "character-entities-html4": "^2.0.0",
- "character-entities-legacy": "^3.0.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
"node_modules/strip-ansi": {
"version": "7.2.0",
"resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz",
@@ -7952,19 +5655,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/superjson": {
- "version": "2.2.6",
- "resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.6.tgz",
- "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "copy-anything": "^4"
- },
- "engines": {
- "node": ">=16"
- }
- },
"node_modules/supports-color": {
"version": "10.2.2",
"resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-10.2.2.tgz",
@@ -7993,13 +5683,6 @@
"url": "https://github.com/chalk/supports-hyperlinks?sponsor=1"
}
},
- "node_modules/tabbable": {
- "version": "6.4.0",
- "resolved": "https://registry.npmmirror.com/tabbable/-/tabbable-6.4.0.tgz",
- "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/tagged-tag": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/tagged-tag/-/tagged-tag-1.0.0.tgz",
@@ -8052,17 +5735,6 @@
"tree-kill": "cli.js"
}
},
- "node_modules/trim-lines": {
- "version": "3.0.1",
- "resolved": "https://registry.npmmirror.com/trim-lines/-/trim-lines-3.0.1.tgz",
- "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
"node_modules/ts-algebra": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/ts-algebra/-/ts-algebra-2.0.0.tgz",
@@ -8155,79 +5827,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/unist-util-is": {
- "version": "6.0.1",
- "resolved": "https://registry.npmmirror.com/unist-util-is/-/unist-util-is-6.0.1.tgz",
- "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/unist": "^3.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
- }
- },
- "node_modules/unist-util-position": {
- "version": "5.0.0",
- "resolved": "https://registry.npmmirror.com/unist-util-position/-/unist-util-position-5.0.0.tgz",
- "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/unist": "^3.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
- }
- },
- "node_modules/unist-util-stringify-position": {
- "version": "4.0.0",
- "resolved": "https://registry.npmmirror.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
- "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/unist": "^3.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
- }
- },
- "node_modules/unist-util-visit": {
- "version": "5.1.0",
- "resolved": "https://registry.npmmirror.com/unist-util-visit/-/unist-util-visit-5.1.0.tgz",
- "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/unist": "^3.0.0",
- "unist-util-is": "^6.0.0",
- "unist-util-visit-parents": "^6.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
- }
- },
- "node_modules/unist-util-visit-parents": {
- "version": "6.0.2",
- "resolved": "https://registry.npmmirror.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz",
- "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/unist": "^3.0.0",
- "unist-util-is": "^6.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
- }
- },
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz",
@@ -8275,152 +5874,6 @@
"node": ">= 0.8"
}
},
- "node_modules/vfile": {
- "version": "6.0.3",
- "resolved": "https://registry.npmmirror.com/vfile/-/vfile-6.0.3.tgz",
- "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/unist": "^3.0.0",
- "vfile-message": "^4.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
- }
- },
- "node_modules/vfile-message": {
- "version": "4.0.3",
- "resolved": "https://registry.npmmirror.com/vfile-message/-/vfile-message-4.0.3.tgz",
- "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/unist": "^3.0.0",
- "unist-util-stringify-position": "^4.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
- }
- },
- "node_modules/vite": {
- "version": "5.4.21",
- "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz",
- "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "esbuild": "^0.21.3",
- "postcss": "^8.4.43",
- "rollup": "^4.20.0"
- },
- "bin": {
- "vite": "bin/vite.js"
- },
- "engines": {
- "node": "^18.0.0 || >=20.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",
- "less": "*",
- "lightningcss": "^1.21.0",
- "sass": "*",
- "sass-embedded": "*",
- "stylus": "*",
- "sugarss": "*",
- "terser": "^5.4.0"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- },
- "less": {
- "optional": true
- },
- "lightningcss": {
- "optional": true
- },
- "sass": {
- "optional": true
- },
- "sass-embedded": {
- "optional": true
- },
- "stylus": {
- "optional": true
- },
- "sugarss": {
- "optional": true
- },
- "terser": {
- "optional": true
- }
- }
- },
- "node_modules/vitepress": {
- "version": "1.6.4",
- "resolved": "https://registry.npmmirror.com/vitepress/-/vitepress-1.6.4.tgz",
- "integrity": "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@docsearch/css": "3.8.2",
- "@docsearch/js": "3.8.2",
- "@iconify-json/simple-icons": "^1.2.21",
- "@shikijs/core": "^2.1.0",
- "@shikijs/transformers": "^2.1.0",
- "@shikijs/types": "^2.1.0",
- "@types/markdown-it": "^14.1.2",
- "@vitejs/plugin-vue": "^5.2.1",
- "@vue/devtools-api": "^7.7.0",
- "@vue/shared": "^3.5.13",
- "@vueuse/core": "^12.4.0",
- "@vueuse/integrations": "^12.4.0",
- "focus-trap": "^7.6.4",
- "mark.js": "8.11.1",
- "minisearch": "^7.1.1",
- "shiki": "^2.1.0",
- "vite": "^5.4.14",
- "vue": "^3.5.13"
- },
- "bin": {
- "vitepress": "bin/vitepress.js"
- },
- "peerDependencies": {
- "markdown-it-mathjax3": "^4",
- "postcss": "^8"
- },
- "peerDependenciesMeta": {
- "markdown-it-mathjax3": {
- "optional": true
- },
- "postcss": {
- "optional": true
- }
- }
- },
- "node_modules/vitepress-plugin-mermaid": {
- "version": "2.0.17",
- "resolved": "https://registry.npmmirror.com/vitepress-plugin-mermaid/-/vitepress-plugin-mermaid-2.0.17.tgz",
- "integrity": "sha512-IUzYpwf61GC6k0XzfmAmNrLvMi9TRrVRMsUyCA8KNXhg/mQ1VqWnO0/tBVPiX5UoKF1mDUwqn5QV4qAJl6JnUg==",
- "dev": true,
- "license": "MIT",
- "optionalDependencies": {
- "@mermaid-js/mermaid-mindmap": "^9.3.0"
- },
- "peerDependencies": {
- "mermaid": "10 || 11",
- "vitepress": "^1.0.0 || ^1.0.0-alpha"
- }
- },
"node_modules/vscode-jsonrpc": {
"version": "8.2.1",
"resolved": "https://registry.npmmirror.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.1.tgz",
@@ -8484,28 +5937,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/vue": {
- "version": "3.5.32",
- "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.32.tgz",
- "integrity": "sha512-vM4z4Q9tTafVfMAK7IVzmxg34rSzTFMyIe0UUEijUCkn9+23lj0WRfA83dg7eQZIUlgOSGrkViIaCfqSAUXsMw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vue/compiler-dom": "3.5.32",
- "@vue/compiler-sfc": "3.5.32",
- "@vue/runtime-dom": "3.5.32",
- "@vue/server-renderer": "3.5.32",
- "@vue/shared": "3.5.32"
- },
- "peerDependencies": {
- "typescript": "*"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
"node_modules/web-streams-polyfill": {
"version": "3.3.3",
"resolved": "https://registry.npmmirror.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
@@ -8758,17 +6189,6 @@
"peerDependencies": {
"zod": "^3.25.28 || ^4"
}
- },
- "node_modules/zwitch": {
- "version": "2.0.4",
- "resolved": "https://registry.npmmirror.com/zwitch/-/zwitch-2.0.4.tgz",
- "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
}
}
}
diff --git a/package.json b/package.json
index 785ddb4b..e8b0608e 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,7 @@
"check:electron": "cd desktop && bun run check:electron",
"check:adapters": "cd adapters && bun test",
"check:native": "cd desktop && bun run build:sidecars && bun run test:compiled-sidecar-smoke && bun run check:electron && CSC_IDENTITY_AUTO_DISCOVERY=false bun run electron:package:dir && cd .. && bun run test:package-smoke:current",
- "check:docs": "npm ci --loglevel=error && npm run --loglevel=error docs:build",
+ "check:docs": "npm --prefix site ci --loglevel=error && npm --prefix site run --loglevel=error build && npm --prefix site run --loglevel=error check",
"check:persistence-upgrade": "bun run scripts/quality-gate/persistence-upgrade.ts",
"check:quarantine": "bun run scripts/quality-gate/quarantine.ts --enforce-review-date",
"check:coverage": "bun run scripts/quality-gate/coverage.ts",
@@ -38,9 +38,9 @@
"quality:smoke": "bun run quality:gate --mode baseline --allow-live --only 'provider-smoke:*' --only 'desktop-smoke:*'",
"test:package-smoke": "bun run scripts/quality-gate/package-smoke/index.ts",
"test:package-smoke:current": "bun run scripts/quality-gate/package-smoke/current.ts",
- "docs:dev": "vitepress dev docs",
- "docs:build": "vitepress build docs",
- "docs:preview": "vitepress preview docs"
+ "docs:dev": "npm --prefix site run dev",
+ "docs:build": "npm --prefix site run build",
+ "docs:preview": "npm --prefix site run preview"
},
"dependencies": {
"@anthropic-ai/sandbox-runtime": "^0.0.44",
@@ -113,9 +113,6 @@
"follow-redirects": "^1.16.0"
},
"devDependencies": {
- "mermaid": "^11.14.0",
- "vitepress": "^1.6.3",
- "vitepress-plugin-mermaid": "^2.0.17",
- "vue": "^3.5.13"
+ "mermaid": "^11.14.0"
}
}
diff --git a/scripts/pr/change-policy.test.ts b/scripts/pr/change-policy.test.ts
index a50be2cc..8ebfce4a 100644
--- a/scripts/pr/change-policy.test.ts
+++ b/scripts/pr/change-policy.test.ts
@@ -33,6 +33,7 @@ describe('evaluateChangePolicy', () => {
test('keeps docs-only changes on the docs lane', () => {
const result = evaluateChangePolicy([
'docs/index.md',
+ 'site/src/App.tsx',
'README.md',
])
diff --git a/scripts/pr/change-policy.ts b/scripts/pr/change-policy.ts
index 1ff70444..1fed2c1c 100644
--- a/scripts/pr/change-policy.ts
+++ b/scripts/pr/change-policy.ts
@@ -208,6 +208,7 @@ function areasForPath(path: string): ChangeArea[] {
if (
path.startsWith('docs/') ||
+ path.startsWith('site/') ||
path.startsWith('release-notes/') ||
docsExactPaths.has(path)
) {
@@ -330,6 +331,7 @@ export function evaluateChangePolicy(
const touchesDocs = files.some((file) => (
!isAgentInstructionPath(file) && (
file.startsWith('docs/') ||
+ file.startsWith('site/') ||
file.startsWith('release-notes/') ||
docsExactPaths.has(file)
)
diff --git a/scripts/pr/pr-quality-workflow.test.ts b/scripts/pr/pr-quality-workflow.test.ts
index 23a10b74..ea813115 100644
--- a/scripts/pr/pr-quality-workflow.test.ts
+++ b/scripts/pr/pr-quality-workflow.test.ts
@@ -79,6 +79,19 @@ describe('PR quality workflow', () => {
expect(workflow).toContain('retention-days: 14')
})
+ test('installs and validates the isolated React site for docs changes', () => {
+ const workflow = readFileSync('.github/workflows/pr-quality.yml', 'utf8')
+ const jobs = workflowJobs(workflow)
+ const docsSteps = jobs['docs-checks'].steps ?? []
+ const runDocs = docsSteps.find((step) => step.name === 'Run docs checks')
+
+ expect(workflow).toContain('cache-dependency-path: site/package-lock.json')
+ expect(runDocs?.run).toBe(
+ 'npm --prefix site ci && npm --prefix site run build && npm --prefix site run check',
+ )
+ expect(workflow).not.toContain('vitepress')
+ })
+
test('keeps required PR checks deterministic and secret-free', () => {
const workflow = readFileSync('.github/workflows/pr-quality.yml', 'utf8')
@@ -105,3 +118,17 @@ describe('PR quality workflow', () => {
expect(workflow).toContain('require_selected "coverage-checks"')
})
})
+
+describe('docs deployment workflow', () => {
+ test('builds and uploads the isolated React site', () => {
+ const workflow = readFileSync('.github/workflows/deploy-docs.yml', 'utf8')
+
+ expect(workflow).toContain(" - 'site/**'")
+ expect(workflow).toContain('cache-dependency-path: site/package-lock.json')
+ expect(workflow).toContain('run: npm --prefix site ci')
+ expect(workflow).toContain('run: npm --prefix site run build')
+ expect(workflow).toContain('path: site/dist')
+ expect(workflow).not.toContain('vitepress')
+ expect(workflow).not.toContain('docs/.vitepress/dist')
+ })
+})
diff --git a/scripts/quality-gate/modes.ts b/scripts/quality-gate/modes.ts
index e4d2bb6c..3f758696 100644
--- a/scripts/quality-gate/modes.ts
+++ b/scripts/quality-gate/modes.ts
@@ -109,7 +109,7 @@ export function lanesForMode(mode: QualityGateMode, baselineTargets: BaselineTar
{
id: 'docs-checks',
title: 'Docs checks',
- description: 'Run docs install and VitePress build when docs paths changed.',
+ description: 'Install, build, and validate the React site when docs or site paths change.',
kind: 'command',
command: ['bun', 'run', 'check:docs'],
impactRequiredCheck: 'bun run check:docs',
diff --git a/site/.gitignore b/site/.gitignore
new file mode 100644
index 00000000..c22c8be5
--- /dev/null
+++ b/site/.gitignore
@@ -0,0 +1,3 @@
+dist/
+node_modules/
+src/generated/docs-manifest.js
diff --git a/site/AGENTS.md b/site/AGENTS.md
new file mode 100644
index 00000000..803693d4
--- /dev/null
+++ b/site/AGENTS.md
@@ -0,0 +1,10 @@
+# React Site Instructions
+
+These rules apply to the public landing page and documentation experience under `site/`.
+
+- Keep the site independently installable with `npm ci` and buildable with `npm run build`.
+- Keep `npm run check` deterministic, offline, and responsible for site-specific validation beyond compilation.
+- Preserve the GitHub Pages custom-domain contract; production assets and routes must work from the root of `claudecode-haha.relakkesyang.org`.
+- Treat files under `docs/` as the source of truth for long-form Chinese and English documentation. Keep paired public routes aligned when both languages exist.
+- Do not copy private user state, credentials, local filesystem paths, or unredacted product screenshots into the site.
+- Run `bun run check:docs` after site or docs changes and include desktop plus narrow-mobile browser evidence for user-visible layout changes.
diff --git a/site/index.html b/site/index.html
new file mode 100644
index 00000000..a6ab5446
--- /dev/null
+++ b/site/index.html
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Claude Code Haha — 会工作的桌面 AI 搭档
+
+
+
+
+
+
diff --git a/site/package-lock.json b/site/package-lock.json
new file mode 100644
index 00000000..9b8c3fec
--- /dev/null
+++ b/site/package-lock.json
@@ -0,0 +1,2011 @@
+{
+ "name": "claude-code-haha-site",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "claude-code-haha-site",
+ "version": "0.1.0",
+ "dependencies": {
+ "dompurify": "^3.3.0",
+ "marked": "^17.0.5",
+ "mermaid": "^11.14.0",
+ "react": "^19.2.4",
+ "react-dom": "^19.2.4"
+ },
+ "devDependencies": {
+ "vite": "^8.0.10"
+ }
+ },
+ "node_modules/@antfu/install-pkg": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/@antfu/install-pkg/-/install-pkg-1.1.0.tgz",
+ "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==",
+ "license": "MIT",
+ "dependencies": {
+ "package-manager-detector": "^1.3.0",
+ "tinyexec": "^1.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/@braintree/sanitize-url": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmmirror.com/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz",
+ "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==",
+ "license": "MIT"
+ },
+ "node_modules/@chevrotain/types": {
+ "version": "11.1.2",
+ "resolved": "https://registry.npmmirror.com/@chevrotain/types/-/types-11.1.2.tgz",
+ "integrity": "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@emnapi/core": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmmirror.com/@emnapi/core/-/core-1.11.1.tgz",
+ "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.2",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmmirror.com/@emnapi/runtime/-/runtime-1.11.1.tgz",
+ "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmmirror.com/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
+ "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@iconify/types": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/@iconify/types/-/types-2.0.0.tgz",
+ "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",
+ "license": "MIT"
+ },
+ "node_modules/@iconify/utils": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmmirror.com/@iconify/utils/-/utils-3.1.4.tgz",
+ "integrity": "sha512-b1S7B1k9ohZ+iNTi2ATxbRYG9fTrJmUT0rc46bvVnNxqNRGW7dyo/vRREwyniI5IRN2RSJHDcm+s3BjWrSAjHw==",
+ "license": "MIT",
+ "dependencies": {
+ "@antfu/install-pkg": "^1.1.0",
+ "@iconify/types": "^2.0.0",
+ "import-meta-resolve": "^4.2.0"
+ }
+ },
+ "node_modules/@mermaid-js/parser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/@mermaid-js/parser/-/parser-1.2.0.tgz",
+ "integrity": "sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==",
+ "license": "MIT",
+ "dependencies": {
+ "@chevrotain/types": "~11.1.2"
+ }
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmmirror.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz",
+ "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tybys/wasm-util": "^0.10.3"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1"
+ }
+ },
+ "node_modules/@oxc-project/types": {
+ "version": "0.139.0",
+ "resolved": "https://registry.npmmirror.com/@oxc-project/types/-/types-0.139.0.tgz",
+ "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ }
+ },
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz",
+ "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz",
+ "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz",
+ "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz",
+ "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz",
+ "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz",
+ "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz",
+ "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz",
+ "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz",
+ "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz",
+ "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz",
+ "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz",
+ "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-wasm32-wasi": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz",
+ "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "1.11.1",
+ "@emnapi/runtime": "1.11.1",
+ "@napi-rs/wasm-runtime": "^1.1.6"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz",
+ "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz",
+ "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
+ "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmmirror.com/@tybys/wasm-util/-/wasm-util-0.10.3.tgz",
+ "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/d3": {
+ "version": "7.4.3",
+ "resolved": "https://registry.npmmirror.com/@types/d3/-/d3-7.4.3.tgz",
+ "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-array": "*",
+ "@types/d3-axis": "*",
+ "@types/d3-brush": "*",
+ "@types/d3-chord": "*",
+ "@types/d3-color": "*",
+ "@types/d3-contour": "*",
+ "@types/d3-delaunay": "*",
+ "@types/d3-dispatch": "*",
+ "@types/d3-drag": "*",
+ "@types/d3-dsv": "*",
+ "@types/d3-ease": "*",
+ "@types/d3-fetch": "*",
+ "@types/d3-force": "*",
+ "@types/d3-format": "*",
+ "@types/d3-geo": "*",
+ "@types/d3-hierarchy": "*",
+ "@types/d3-interpolate": "*",
+ "@types/d3-path": "*",
+ "@types/d3-polygon": "*",
+ "@types/d3-quadtree": "*",
+ "@types/d3-random": "*",
+ "@types/d3-scale": "*",
+ "@types/d3-scale-chromatic": "*",
+ "@types/d3-selection": "*",
+ "@types/d3-shape": "*",
+ "@types/d3-time": "*",
+ "@types/d3-time-format": "*",
+ "@types/d3-timer": "*",
+ "@types/d3-transition": "*",
+ "@types/d3-zoom": "*"
+ }
+ },
+ "node_modules/@types/d3-array": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmmirror.com/@types/d3-array/-/d3-array-3.2.2.tgz",
+ "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-axis": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmmirror.com/@types/d3-axis/-/d3-axis-3.0.6.tgz",
+ "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-brush": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmmirror.com/@types/d3-brush/-/d3-brush-3.0.6.tgz",
+ "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-chord": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmmirror.com/@types/d3-chord/-/d3-chord-3.0.6.tgz",
+ "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-color": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmmirror.com/@types/d3-color/-/d3-color-3.1.3.tgz",
+ "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-contour": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmmirror.com/@types/d3-contour/-/d3-contour-3.0.6.tgz",
+ "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-array": "*",
+ "@types/geojson": "*"
+ }
+ },
+ "node_modules/@types/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmmirror.com/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-dispatch": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmmirror.com/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz",
+ "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-drag": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmmirror.com/@types/d3-drag/-/d3-drag-3.0.7.tgz",
+ "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-dsv": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmmirror.com/@types/d3-dsv/-/d3-dsv-3.0.7.tgz",
+ "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-ease": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmmirror.com/@types/d3-ease/-/d3-ease-3.0.2.tgz",
+ "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-fetch": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmmirror.com/@types/d3-fetch/-/d3-fetch-3.0.7.tgz",
+ "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-dsv": "*"
+ }
+ },
+ "node_modules/@types/d3-force": {
+ "version": "3.0.10",
+ "resolved": "https://registry.npmmirror.com/@types/d3-force/-/d3-force-3.0.10.tgz",
+ "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-format": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmmirror.com/@types/d3-format/-/d3-format-3.0.4.tgz",
+ "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-geo": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/@types/d3-geo/-/d3-geo-3.1.0.tgz",
+ "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/geojson": "*"
+ }
+ },
+ "node_modules/@types/d3-hierarchy": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmmirror.com/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz",
+ "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-interpolate": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmmirror.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+ "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-color": "*"
+ }
+ },
+ "node_modules/@types/d3-path": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/@types/d3-path/-/d3-path-3.1.1.tgz",
+ "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-polygon": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmmirror.com/@types/d3-polygon/-/d3-polygon-3.0.2.tgz",
+ "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-quadtree": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmmirror.com/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz",
+ "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-random": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmmirror.com/@types/d3-random/-/d3-random-3.0.4.tgz",
+ "integrity": "sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-scale": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmmirror.com/@types/d3-scale/-/d3-scale-4.0.9.tgz",
+ "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-time": "*"
+ }
+ },
+ "node_modules/@types/d3-scale-chromatic": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
+ "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-selection": {
+ "version": "3.0.11",
+ "resolved": "https://registry.npmmirror.com/@types/d3-selection/-/d3-selection-3.0.11.tgz",
+ "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-shape": {
+ "version": "3.1.8",
+ "resolved": "https://registry.npmmirror.com/@types/d3-shape/-/d3-shape-3.1.8.tgz",
+ "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-path": "*"
+ }
+ },
+ "node_modules/@types/d3-time": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmmirror.com/@types/d3-time/-/d3-time-3.0.4.tgz",
+ "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-time-format": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmmirror.com/@types/d3-time-format/-/d3-time-format-4.0.3.tgz",
+ "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-timer": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmmirror.com/@types/d3-timer/-/d3-timer-3.0.2.tgz",
+ "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-transition": {
+ "version": "3.0.9",
+ "resolved": "https://registry.npmmirror.com/@types/d3-transition/-/d3-transition-3.0.9.tgz",
+ "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-zoom": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmmirror.com/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
+ "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-interpolate": "*",
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/geojson": {
+ "version": "7946.0.16",
+ "resolved": "https://registry.npmmirror.com/@types/geojson/-/geojson-7946.0.16.tgz",
+ "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/trusted-types": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@upsetjs/venn.js": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/@upsetjs/venn.js/-/venn.js-2.0.0.tgz",
+ "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==",
+ "license": "MIT",
+ "optionalDependencies": {
+ "d3-selection": "^3.0.0",
+ "d3-transition": "^3.0.1"
+ }
+ },
+ "node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/cose-base": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmmirror.com/cose-base/-/cose-base-1.0.3.tgz",
+ "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==",
+ "license": "MIT",
+ "dependencies": {
+ "layout-base": "^1.0.0"
+ }
+ },
+ "node_modules/cytoscape": {
+ "version": "3.34.0",
+ "resolved": "https://registry.npmmirror.com/cytoscape/-/cytoscape-3.34.0.tgz",
+ "integrity": "sha512-62rNSrioXw93uliKFBwjukeQyeWwH2PqDrTac31r2P6464u3AUvTk0xS4LVvT251g7IgkFunrI48ZEZGjywSOg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/cytoscape-cose-bilkent": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmmirror.com/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz",
+ "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==",
+ "license": "MIT",
+ "dependencies": {
+ "cose-base": "^1.0.0"
+ },
+ "peerDependencies": {
+ "cytoscape": "^3.2.0"
+ }
+ },
+ "node_modules/cytoscape-fcose": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmmirror.com/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz",
+ "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==",
+ "license": "MIT",
+ "dependencies": {
+ "cose-base": "^2.2.0"
+ },
+ "peerDependencies": {
+ "cytoscape": "^3.2.0"
+ }
+ },
+ "node_modules/cytoscape-fcose/node_modules/cose-base": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmmirror.com/cose-base/-/cose-base-2.2.0.tgz",
+ "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==",
+ "license": "MIT",
+ "dependencies": {
+ "layout-base": "^2.0.0"
+ }
+ },
+ "node_modules/cytoscape-fcose/node_modules/layout-base": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/layout-base/-/layout-base-2.0.1.tgz",
+ "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==",
+ "license": "MIT"
+ },
+ "node_modules/d3": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmmirror.com/d3/-/d3-7.9.0.tgz",
+ "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "3",
+ "d3-axis": "3",
+ "d3-brush": "3",
+ "d3-chord": "3",
+ "d3-color": "3",
+ "d3-contour": "4",
+ "d3-delaunay": "6",
+ "d3-dispatch": "3",
+ "d3-drag": "3",
+ "d3-dsv": "3",
+ "d3-ease": "3",
+ "d3-fetch": "3",
+ "d3-force": "3",
+ "d3-format": "3",
+ "d3-geo": "3",
+ "d3-hierarchy": "3",
+ "d3-interpolate": "3",
+ "d3-path": "3",
+ "d3-polygon": "3",
+ "d3-quadtree": "3",
+ "d3-random": "3",
+ "d3-scale": "4",
+ "d3-scale-chromatic": "3",
+ "d3-selection": "3",
+ "d3-shape": "3",
+ "d3-time": "3",
+ "d3-time-format": "4",
+ "d3-timer": "3",
+ "d3-transition": "3",
+ "d3-zoom": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-array": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmmirror.com/d3-array/-/d3-array-3.2.4.tgz",
+ "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
+ "license": "ISC",
+ "dependencies": {
+ "internmap": "1 - 2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-axis": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/d3-axis/-/d3-axis-3.0.0.tgz",
+ "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-brush": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/d3-brush/-/d3-brush-3.0.0.tgz",
+ "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "3",
+ "d3-transition": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-chord": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/d3-chord/-/d3-chord-3.0.1.tgz",
+ "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-path": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-contour": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmmirror.com/d3-contour/-/d3-contour-4.0.2.tgz",
+ "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmmirror.com/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
+ "license": "ISC",
+ "dependencies": {
+ "delaunator": "5"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dispatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+ "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-drag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/d3-drag/-/d3-drag-3.0.0.tgz",
+ "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-selection": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dsv": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/d3-dsv/-/d3-dsv-3.0.1.tgz",
+ "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
+ "license": "ISC",
+ "dependencies": {
+ "commander": "7",
+ "iconv-lite": "0.6",
+ "rw": "1"
+ },
+ "bin": {
+ "csv2json": "bin/dsv2json.js",
+ "csv2tsv": "bin/dsv2dsv.js",
+ "dsv2dsv": "bin/dsv2dsv.js",
+ "dsv2json": "bin/dsv2json.js",
+ "json2csv": "bin/json2dsv.js",
+ "json2dsv": "bin/json2dsv.js",
+ "json2tsv": "bin/json2dsv.js",
+ "tsv2csv": "bin/dsv2dsv.js",
+ "tsv2json": "bin/dsv2json.js"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-ease": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz",
+ "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-fetch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/d3-fetch/-/d3-fetch-3.0.1.tgz",
+ "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dsv": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-force": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/d3-force/-/d3-force-3.0.0.tgz",
+ "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-quadtree": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-format": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmmirror.com/d3-format/-/d3-format-3.1.2.tgz",
+ "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-geo": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/d3-geo/-/d3-geo-3.1.1.tgz",
+ "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2.5.0 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-hierarchy": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmmirror.com/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
+ "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-polygon": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/d3-polygon/-/d3-polygon-3.0.1.tgz",
+ "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-quadtree": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
+ "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-random": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/d3-random/-/d3-random-3.0.1.tgz",
+ "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-sankey": {
+ "version": "0.12.3",
+ "resolved": "https://registry.npmmirror.com/d3-sankey/-/d3-sankey-0.12.3.tgz",
+ "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-array": "1 - 2",
+ "d3-shape": "^1.2.0"
+ }
+ },
+ "node_modules/d3-sankey/node_modules/d3-array": {
+ "version": "2.12.1",
+ "resolved": "https://registry.npmmirror.com/d3-array/-/d3-array-2.12.1.tgz",
+ "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "internmap": "^1.0.0"
+ }
+ },
+ "node_modules/d3-sankey/node_modules/d3-path": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmmirror.com/d3-path/-/d3-path-1.0.9.tgz",
+ "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-sankey/node_modules/d3-shape": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmmirror.com/d3-shape/-/d3-shape-1.3.7.tgz",
+ "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-path": "1"
+ }
+ },
+ "node_modules/d3-sankey/node_modules/internmap": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/internmap/-/internmap-1.0.1.tgz",
+ "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==",
+ "license": "ISC"
+ },
+ "node_modules/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmmirror.com/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale-chromatic": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
+ "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-interpolate": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-selection": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz",
+ "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-shape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmmirror.com/d3-shape/-/d3-shape-3.2.0.tgz",
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-path": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/d3-time/-/d3-time-3.1.0.tgz",
+ "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmmirror.com/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-time": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-transition": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/d3-transition/-/d3-transition-3.0.1.tgz",
+ "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-dispatch": "1 - 3",
+ "d3-ease": "1 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "d3-selection": "2 - 3"
+ }
+ },
+ "node_modules/d3-zoom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/d3-zoom/-/d3-zoom-3.0.0.tgz",
+ "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "2 - 3",
+ "d3-transition": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/dagre-d3-es": {
+ "version": "7.0.14",
+ "resolved": "https://registry.npmmirror.com/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz",
+ "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==",
+ "license": "MIT",
+ "dependencies": {
+ "d3": "^7.9.0",
+ "lodash-es": "^4.17.21"
+ }
+ },
+ "node_modules/dayjs": {
+ "version": "1.11.21",
+ "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.21.tgz",
+ "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==",
+ "license": "MIT"
+ },
+ "node_modules/delaunator": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmmirror.com/delaunator/-/delaunator-5.1.0.tgz",
+ "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==",
+ "license": "ISC",
+ "dependencies": {
+ "robust-predicates": "^3.0.2"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dompurify": {
+ "version": "3.4.12",
+ "resolved": "https://registry.npmmirror.com/dompurify/-/dompurify-3.4.12.tgz",
+ "integrity": "sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==",
+ "license": "(MPL-2.0 OR Apache-2.0)",
+ "optionalDependencies": {
+ "@types/trusted-types": "^2.0.7"
+ }
+ },
+ "node_modules/es-toolkit": {
+ "version": "1.49.0",
+ "resolved": "https://registry.npmmirror.com/es-toolkit/-/es-toolkit-1.49.0.tgz",
+ "integrity": "sha512-G5iZ6Pc/FNRY/soKZHC+TxGDD83rHUDXxzaWhGCX44vAv/tMs56WMusnm/KMNK+luUPsgA9U28cGr4RDlSzL2g==",
+ "license": "MIT",
+ "workspaces": [
+ "docs",
+ "benchmarks"
+ ]
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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/hachure-fill": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmmirror.com/hachure-fill/-/hachure-fill-0.5.2.tgz",
+ "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==",
+ "license": "MIT"
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/import-meta-resolve": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmmirror.com/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz",
+ "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/internmap": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/internmap/-/internmap-2.0.3.tgz",
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/katex": {
+ "version": "0.16.47",
+ "resolved": "https://registry.npmmirror.com/katex/-/katex-0.16.47.tgz",
+ "integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==",
+ "funding": [
+ "https://opencollective.com/katex",
+ "https://github.com/sponsors/katex"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "commander": "^8.3.0"
+ },
+ "bin": {
+ "katex": "cli.js"
+ }
+ },
+ "node_modules/katex/node_modules/commander": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmmirror.com/commander/-/commander-8.3.0.tgz",
+ "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/khroma": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/khroma/-/khroma-2.1.0.tgz",
+ "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="
+ },
+ "node_modules/layout-base": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/layout-base/-/layout-base-1.0.2.tgz",
+ "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==",
+ "license": "MIT"
+ },
+ "node_modules/lightningcss": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmmirror.com/lightningcss/-/lightningcss-1.33.0.tgz",
+ "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.33.0",
+ "lightningcss-darwin-arm64": "1.33.0",
+ "lightningcss-darwin-x64": "1.33.0",
+ "lightningcss-freebsd-x64": "1.33.0",
+ "lightningcss-linux-arm-gnueabihf": "1.33.0",
+ "lightningcss-linux-arm64-gnu": "1.33.0",
+ "lightningcss-linux-arm64-musl": "1.33.0",
+ "lightningcss-linux-x64-gnu": "1.33.0",
+ "lightningcss-linux-x64-musl": "1.33.0",
+ "lightningcss-win32-arm64-msvc": "1.33.0",
+ "lightningcss-win32-x64-msvc": "1.33.0"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmmirror.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz",
+ "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmmirror.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz",
+ "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmmirror.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz",
+ "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmmirror.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz",
+ "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz",
+ "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz",
+ "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz",
+ "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmmirror.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz",
+ "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmmirror.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz",
+ "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmmirror.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz",
+ "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmmirror.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz",
+ "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lodash-es": {
+ "version": "4.18.1",
+ "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.18.1.tgz",
+ "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
+ "license": "MIT"
+ },
+ "node_modules/marked": {
+ "version": "17.0.6",
+ "resolved": "https://registry.npmmirror.com/marked/-/marked-17.0.6.tgz",
+ "integrity": "sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA==",
+ "license": "MIT",
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/mermaid": {
+ "version": "11.16.0",
+ "resolved": "https://registry.npmmirror.com/mermaid/-/mermaid-11.16.0.tgz",
+ "integrity": "sha512-Zvm3kbstgdpvIJPPItlL7fppIZ3kibvc1oZIGxdvk9t6UFz6flv+Jw7FtRGKwfcI8OckmH04LqG6LlS6X4B1pA==",
+ "license": "MIT",
+ "dependencies": {
+ "@braintree/sanitize-url": "^7.1.2",
+ "@iconify/utils": "^3.0.2",
+ "@mermaid-js/parser": "^1.2.0",
+ "@types/d3": "^7.4.3",
+ "@upsetjs/venn.js": "^2.0.0",
+ "cytoscape": "^3.33.3",
+ "cytoscape-cose-bilkent": "^4.1.0",
+ "cytoscape-fcose": "^2.2.0",
+ "d3": "^7.9.0",
+ "d3-sankey": "^0.12.3",
+ "dagre-d3-es": "7.0.14",
+ "dayjs": "^1.11.20",
+ "dompurify": "^3.3.3",
+ "es-toolkit": "^1.45.1",
+ "katex": "^0.16.45",
+ "khroma": "^2.1.0",
+ "marked": "^16.3.0",
+ "roughjs": "^4.6.6",
+ "stylis": "^4.3.6",
+ "ts-dedent": "^2.2.0",
+ "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0"
+ }
+ },
+ "node_modules/mermaid/node_modules/marked": {
+ "version": "16.4.2",
+ "resolved": "https://registry.npmmirror.com/marked/-/marked-16.4.2.tgz",
+ "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==",
+ "license": "MIT",
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.16",
+ "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.16.tgz",
+ "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
+ "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/package-manager-detector": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmmirror.com/package-manager-detector/-/package-manager-detector-1.8.0.tgz",
+ "integrity": "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==",
+ "license": "MIT"
+ },
+ "node_modules/path-data-parser": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmmirror.com/path-data-parser/-/path-data-parser-0.1.0.tgz",
+ "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==",
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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/points-on-curve": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmmirror.com/points-on-curve/-/points-on-curve-0.2.0.tgz",
+ "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==",
+ "license": "MIT"
+ },
+ "node_modules/points-on-path": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmmirror.com/points-on-path/-/points-on-path-0.2.1.tgz",
+ "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==",
+ "license": "MIT",
+ "dependencies": {
+ "path-data-parser": "0.1.0",
+ "points-on-curve": "0.2.0"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.22",
+ "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.22.tgz",
+ "integrity": "sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ==",
+ "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.16",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.8",
+ "resolved": "https://registry.npmmirror.com/react/-/react-19.2.8.tgz",
+ "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.8",
+ "resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-19.2.8.tgz",
+ "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.8"
+ }
+ },
+ "node_modules/robust-predicates": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmmirror.com/robust-predicates/-/robust-predicates-3.0.3.tgz",
+ "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==",
+ "license": "Unlicense"
+ },
+ "node_modules/rolldown": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/rolldown/-/rolldown-1.1.5.tgz",
+ "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/types": "=0.139.0",
+ "@rolldown/pluginutils": "^1.0.0"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm64": "1.1.5",
+ "@rolldown/binding-darwin-arm64": "1.1.5",
+ "@rolldown/binding-darwin-x64": "1.1.5",
+ "@rolldown/binding-freebsd-x64": "1.1.5",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.1.5",
+ "@rolldown/binding-linux-arm64-gnu": "1.1.5",
+ "@rolldown/binding-linux-arm64-musl": "1.1.5",
+ "@rolldown/binding-linux-ppc64-gnu": "1.1.5",
+ "@rolldown/binding-linux-s390x-gnu": "1.1.5",
+ "@rolldown/binding-linux-x64-gnu": "1.1.5",
+ "@rolldown/binding-linux-x64-musl": "1.1.5",
+ "@rolldown/binding-openharmony-arm64": "1.1.5",
+ "@rolldown/binding-wasm32-wasi": "1.1.5",
+ "@rolldown/binding-win32-arm64-msvc": "1.1.5",
+ "@rolldown/binding-win32-x64-msvc": "1.1.5"
+ }
+ },
+ "node_modules/roughjs": {
+ "version": "4.6.6",
+ "resolved": "https://registry.npmmirror.com/roughjs/-/roughjs-4.6.6.tgz",
+ "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==",
+ "license": "MIT",
+ "dependencies": {
+ "hachure-fill": "^0.5.2",
+ "path-data-parser": "^0.1.0",
+ "points-on-curve": "^0.2.0",
+ "points-on-path": "^0.2.1"
+ }
+ },
+ "node_modules/rw": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmmirror.com/rw/-/rw-1.3.3.tgz",
+ "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmmirror.com/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/stylis": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmmirror.com/stylis/-/stylis-4.4.0.tgz",
+ "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==",
+ "license": "MIT"
+ },
+ "node_modules/tinyexec": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-1.2.4.tgz",
+ "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmmirror.com/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/ts-dedent": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmmirror.com/ts-dedent/-/ts-dedent-2.3.0.tgz",
+ "integrity": "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.10"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD",
+ "optional": true
+ },
+ "node_modules/uuid": {
+ "version": "14.0.1",
+ "resolved": "https://registry.npmmirror.com/uuid/-/uuid-14.0.1.tgz",
+ "integrity": "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist-node/bin/uuid"
+ }
+ },
+ "node_modules/vite": {
+ "version": "8.1.5",
+ "resolved": "https://registry.npmmirror.com/vite/-/vite-8.1.5.tgz",
+ "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lightningcss": "^1.32.0",
+ "picomatch": "^4.0.5",
+ "postcss": "^8.5.17",
+ "rolldown": "~1.1.5",
+ "tinyglobby": "^0.2.17"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.3.0",
+ "esbuild": "^0.27.0 || ^0.28.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "@vitejs/devtools": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ }
+ }
+}
diff --git a/site/package.json b/site/package.json
new file mode 100644
index 00000000..76170148
--- /dev/null
+++ b/site/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "claude-code-haha-site",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "predev": "node scripts/generate-docs-manifest.mjs",
+ "dev": "vite --host 0.0.0.0",
+ "prebuild": "node scripts/generate-docs-manifest.mjs",
+ "build": "vite build",
+ "postbuild": "node scripts/prepare-static-output.mjs",
+ "preview": "vite preview --host 0.0.0.0",
+ "check:docs": "node scripts/check-docs.mjs",
+ "check": "npm run check:docs"
+ },
+ "dependencies": {
+ "dompurify": "^3.3.0",
+ "marked": "^17.0.5",
+ "mermaid": "^11.14.0",
+ "react": "^19.2.4",
+ "react-dom": "^19.2.4"
+ },
+ "devDependencies": {
+ "vite": "^8.0.10"
+ }
+}
diff --git a/site/scripts/check-docs.mjs b/site/scripts/check-docs.mjs
new file mode 100644
index 00000000..1ad01382
--- /dev/null
+++ b/site/scripts/check-docs.mjs
@@ -0,0 +1,113 @@
+import { promises as fs } from 'node:fs'
+import path from 'node:path'
+
+import { generateDocsManifest, paths } from './generate-docs-manifest.mjs'
+
+const markdownTargetPattern = /!?\[[^\]]*]\(([^)\s]+)(?:\s+["'][^"']*["'])?\)/g
+const htmlTargetPattern = /<(?:a|img)\b[^>]*?\b(?:href|src)=["']([^"']+)["'][^>]*>/gi
+
+function withoutSuffix(target) {
+ return target.split(/[?#]/, 1)[0]
+}
+
+function isExternal(target) {
+ return /^(?:[a-z]+:|\/\/|#)/i.test(target)
+}
+
+function normalizeRoute(target) {
+ const decoded = decodeURIComponent(withoutSuffix(target))
+ const withoutExtension = decoded
+ .replace(/(?:\/index)?\.html$/i, '')
+ .replace(/\.md$/i, '')
+ const normalized = `/${withoutExtension}`.replace(/\/+/g, '/').replace(/\/$/, '')
+ return normalized || '/'
+}
+
+async function exists(targetPath) {
+ return fs.access(targetPath).then(() => true, () => false)
+}
+
+function collectTargets(markdown) {
+ const targets = []
+ const prose = markdown.replace(/```[\s\S]*?```/g, '')
+
+ for (const match of prose.matchAll(markdownTargetPattern)) {
+ targets.push(match[1])
+ }
+
+ for (const match of prose.matchAll(htmlTargetPattern)) {
+ targets.push(match[1])
+ }
+
+ return [...new Set(targets)]
+}
+
+function isImageTarget(target) {
+ return /\.(?:avif|gif|jpe?g|png|svg|webp)$/i.test(withoutSuffix(target))
+}
+
+async function main() {
+ const { records } = await generateDocsManifest()
+ const routes = new Set([
+ '/',
+ '/docs',
+ '/en',
+ '/en/docs',
+ ...records.map((record) => record.path),
+ ])
+ const problems = []
+ let checkedTargets = 0
+
+ for (const record of records) {
+ const sourceAbsolutePath = path.join(paths.repoDir, record.sourcePath)
+ const sourceDirectory = path.dirname(sourceAbsolutePath)
+
+ for (const target of collectTargets(record.content)) {
+ if (!target || isExternal(target)) {
+ continue
+ }
+
+ checkedTargets += 1
+ const pathname = withoutSuffix(target)
+ const image = isImageTarget(pathname)
+
+ if (pathname.startsWith('/')) {
+ const publicFile = path.join(paths.docsDir, 'public', pathname.replace(/^\//, ''))
+ const docsFile = path.join(paths.docsDir, pathname.replace(/^\//, ''))
+ const valid = image
+ ? await exists(publicFile) || await exists(docsFile)
+ : routes.has(normalizeRoute(pathname)) || await exists(publicFile)
+
+ if (!valid) {
+ problems.push(`${record.sourcePath}: unresolved ${image ? 'image' : 'link'} ${target}`)
+ }
+ continue
+ }
+
+ const resolvedFile = path.resolve(sourceDirectory, pathname)
+ const markdownRoute = normalizeRoute(path.relative(paths.docsDir, resolvedFile))
+ const valid = image
+ ? await exists(resolvedFile)
+ : await exists(resolvedFile)
+ || routes.has(markdownRoute)
+ || routes.has(normalizeRoute(`${path.relative(paths.docsDir, resolvedFile)}.md`))
+
+ if (!valid) {
+ problems.push(`${record.sourcePath}: unresolved ${image ? 'image' : 'link'} ${target}`)
+ }
+ }
+ }
+
+ if (problems.length > 0) {
+ console.error(`Documentation check found ${problems.length} problem(s):`)
+ for (const problem of problems) {
+ console.error(`- ${problem}`)
+ }
+ process.exitCode = 1
+ return
+ }
+
+ console.log(`Documentation check passed: ${records.length} pages, ${checkedTargets} local links and images.`)
+}
+
+await main()
diff --git a/site/scripts/generate-docs-manifest.mjs b/site/scripts/generate-docs-manifest.mjs
new file mode 100644
index 00000000..eeba1c51
--- /dev/null
+++ b/site/scripts/generate-docs-manifest.mjs
@@ -0,0 +1,304 @@
+import { promises as fs } from 'node:fs'
+import path from 'node:path'
+import { fileURLToPath, pathToFileURL } from 'node:url'
+
+const scriptDir = path.dirname(fileURLToPath(import.meta.url))
+const siteDir = path.resolve(scriptDir, '..')
+const repoDir = path.resolve(siteDir, '..')
+const docsDir = path.join(repoDir, 'docs')
+const outputPath = path.join(siteDir, 'src', 'generated', 'docs-manifest.js')
+
+export const paths = {
+ docsDir,
+ outputPath,
+ repoDir,
+ siteDir
+}
+
+const excludedDirectoryNames = new Set(['superpowers', 'ui-clone'])
+const excludedFiles = new Set(['index.md', 'en/index.md', 'AGENTS.md'])
+
+const sectionLabels = {
+ zh: {
+ guide: '开始使用',
+ desktop: '桌面应用',
+ features: '核心能力',
+ im: '消息接入',
+ agent: '多 Agent',
+ skills: '技能系统',
+ memory: '记忆系统',
+ channel: 'Channel 研究',
+ reference: '参考资料'
+ },
+ en: {
+ guide: 'Get started',
+ desktop: 'Desktop app',
+ features: 'Core capabilities',
+ im: 'Messaging',
+ agent: 'Multi-agent',
+ skills: 'Skills',
+ memory: 'Memory',
+ channel: 'Channel research',
+ reference: 'Reference'
+ }
+}
+
+const sectionOrder = [
+ 'guide',
+ 'desktop',
+ 'features',
+ 'im',
+ 'agent',
+ 'skills',
+ 'memory',
+ 'channel',
+ 'reference'
+]
+
+function toPosix(value) {
+ return value.split(path.sep).join('/')
+}
+
+function shouldInclude(relativePath) {
+ const normalized = toPosix(relativePath)
+ const parts = normalized.split('/')
+
+ return normalized.endsWith('.md')
+ && !excludedFiles.has(normalized)
+ && !parts.some((part) => excludedDirectoryNames.has(part))
+}
+
+async function listMarkdownFiles(directory, prefix = '') {
+ const entries = await fs.readdir(directory, { withFileTypes: true })
+ const files = []
+
+ for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
+ const relativePath = prefix ? path.join(prefix, entry.name) : entry.name
+ const absolutePath = path.join(directory, entry.name)
+
+ if (entry.isDirectory()) {
+ if (!excludedDirectoryNames.has(entry.name)) {
+ files.push(...await listMarkdownFiles(absolutePath, relativePath))
+ }
+ continue
+ }
+
+ if (entry.isFile() && shouldInclude(relativePath)) {
+ files.push(toPosix(relativePath))
+ }
+ }
+
+ return files
+}
+
+function parseFrontmatter(markdown) {
+ if (!markdown.startsWith('---\n')) {
+ return { body: markdown, attributes: {} }
+ }
+
+ const closingIndex = markdown.indexOf('\n---\n', 4)
+ if (closingIndex === -1) {
+ return { body: markdown, attributes: {} }
+ }
+
+ const rawFrontmatter = markdown.slice(4, closingIndex)
+ const attributes = {}
+
+ for (const line of rawFrontmatter.split('\n')) {
+ const match = line.match(/^([A-Za-z][\w-]*):\s*(.+)$/)
+ if (match) {
+ attributes[match[1]] = match[2].replace(/^(['"])(.*)\1$/, '$2').trim()
+ }
+ }
+
+ return {
+ body: markdown.slice(closingIndex + 5),
+ attributes
+ }
+}
+
+function plainText(value) {
+ return value
+ .replace(//g, ' ')
+ .replace(/!\[[^\]]*]\([^)]*\)/g, ' ')
+ .replace(/\[([^\]]+)]\([^)]*\)/g, '$1')
+ .replace(/<[^>]+>/g, ' ')
+ .replace(/[`*_~>#|]/g, ' ')
+ .replace(/\s+/g, ' ')
+ .trim()
+}
+
+function extractTitle(body, frontmatter, fallback) {
+ if (frontmatter.title) {
+ return plainText(frontmatter.title)
+ }
+
+ const heading = body.match(/^#\s+(.+)$/m)
+ return heading ? plainText(heading[1]) : fallback
+}
+
+function extractDescription(body, frontmatter) {
+ if (frontmatter.description) {
+ return plainText(frontmatter.description).slice(0, 180)
+ }
+
+ const withoutFences = body.replace(/```[\s\S]*?```/g, '')
+ const paragraphs = withoutFences.split(/\n\s*\n/)
+ const paragraph = paragraphs.find((candidate) => {
+ const value = candidate.trim()
+ return value
+ && !value.startsWith('#')
+ && !value.startsWith('!')
+ && !value.startsWith('<')
+ && !value.startsWith('|')
+ && !value.startsWith(':::')
+ })
+
+ return plainText(paragraph || '').slice(0, 180)
+}
+
+function routeFromRelativePath(relativePath) {
+ const withoutExtension = relativePath.replace(/\.md$/i, '')
+ const withoutIndex = withoutExtension.replace(/(^|\/)index$/i, '$1')
+ return `/${withoutIndex}`.replace(/\/+/g, '/').replace(/\/$/, '') || '/'
+}
+
+function sortKey(relativePath) {
+ const basename = path.posix.basename(relativePath, '.md')
+ if (basename === 'index') {
+ return '0000-index'
+ }
+
+ return basename.replace(/^(\d+)-/, (_, value) => value.padStart(4, '0'))
+}
+
+function buildRecord(relativePath, markdown) {
+ const { body, attributes } = parseFrontmatter(markdown)
+ const segments = relativePath.split('/')
+ const locale = segments[0] === 'en' ? 'en' : 'zh'
+ const localeRelativePath = locale === 'en' ? segments.slice(1).join('/') : relativePath
+ const section = localeRelativePath.split('/')[0]
+ const route = routeFromRelativePath(relativePath)
+ const basename = path.posix.basename(relativePath, '.md')
+ const fallbackTitle = basename === 'index'
+ ? section
+ : basename.replace(/^\d+-/, '').replaceAll('-', ' ')
+
+ return {
+ slug: route.replace(/^\//, ''),
+ path: route,
+ locale,
+ section,
+ title: extractTitle(body, attributes, fallbackTitle),
+ description: extractDescription(body, attributes),
+ sourcePath: `docs/${relativePath}`,
+ sortKey: sortKey(relativePath),
+ content: markdown
+ }
+}
+
+function buildNavigation(records, locale) {
+ const localeRecords = records.filter((record) => record.locale === locale)
+ const sections = [...new Set(localeRecords.map((record) => record.section))]
+ .sort((left, right) => {
+ const leftIndex = sectionOrder.indexOf(left)
+ const rightIndex = sectionOrder.indexOf(right)
+ return (leftIndex === -1 ? Number.MAX_SAFE_INTEGER : leftIndex)
+ - (rightIndex === -1 ? Number.MAX_SAFE_INTEGER : rightIndex)
+ || left.localeCompare(right)
+ })
+
+ return sections.map((section) => ({
+ key: section,
+ title: sectionLabels[locale][section] || section,
+ items: localeRecords
+ .filter((record) => record.section === section)
+ .sort((left, right) => left.sortKey.localeCompare(right.sortKey))
+ .map(({ content, sortKey: _sortKey, section: _section, ...item }) => item)
+ }))
+}
+
+function serialize(value) {
+ return JSON.stringify(value, null, 2)
+ .replaceAll('\u2028', '\\u2028')
+ .replaceAll('\u2029', '\\u2029')
+}
+
+export async function generateDocsManifest() {
+ const relativePaths = await listMarkdownFiles(docsDir)
+ const records = await Promise.all(relativePaths.map(async (relativePath) => {
+ const markdown = await fs.readFile(path.join(docsDir, relativePath), 'utf8')
+ return buildRecord(relativePath, markdown)
+ }))
+
+ records.sort((left, right) => left.path.localeCompare(right.path))
+ const navigation = {
+ zh: buildNavigation(records, 'zh'),
+ en: buildNavigation(records, 'en')
+ }
+
+ const moduleSource = `// Generated by site/scripts/generate-docs-manifest.mjs. Do not edit.
+export const docsManifest = ${serialize(records)}
+
+export const docsNavigation = ${serialize(navigation)}
+
+export const docsBySlug = Object.fromEntries(
+ docsManifest.flatMap((document) => [
+ [document.slug, document],
+ [document.path, document],
+ [\`\${document.path}/\`, document]
+ ])
+)
+
+export function normalizeDocsPath(value) {
+ const pathname = String(value || '/').split(/[?#]/, 1)[0]
+ const decoded = decodeURIComponent(pathname)
+ const withoutHtml = decoded.replace(/(?:\\/index)?\\.html$/i, '')
+ const withoutMarkdown = withoutHtml.replace(/\\.md$/i, '')
+ const normalized = \`/\${withoutMarkdown}\`.replace(/\\/+/g, '/').replace(/\\/$/, '')
+ return normalized || '/'
+}
+
+export function resolveDocsPath(value) {
+ const normalized = normalizeDocsPath(value)
+ return docsBySlug[normalized]
+ || docsBySlug[normalized.replace(/^\\//, '')]
+ || null
+}
+
+export function resolveDocsHref(href, sourcePath) {
+ if (!href || /^(?:[a-z]+:|\\/\\/|#)/i.test(href)) {
+ return href
+ }
+
+ const suffixIndex = href.search(/[?#]/)
+ const pathname = suffixIndex === -1 ? href : href.slice(0, suffixIndex)
+ const suffix = suffixIndex === -1 ? '' : href.slice(suffixIndex)
+ if (pathname.startsWith('/')) {
+ return \`\${normalizeDocsPath(pathname)}\${suffix}\`
+ }
+
+ const sourceRelative = sourcePath.replace(/^docs\\//, '')
+ const resolved = new URL(pathname, \`https://docs.local/\${sourceRelative}\`).pathname
+ const isMarkdown = /\\.md$/i.test(resolved)
+ const target = isMarkdown ? normalizeDocsPath(resolved) : resolved
+ return \`\${target}\${suffix}\`
+}
+`
+
+ await fs.mkdir(path.dirname(outputPath), { recursive: true })
+ const current = await fs.readFile(outputPath, 'utf8').catch(() => '')
+ if (current !== moduleSource) {
+ await fs.writeFile(outputPath, moduleSource)
+ }
+
+ return { records, navigation, outputPath }
+}
+
+const isDirectRun = process.argv[1]
+ && pathToFileURL(path.resolve(process.argv[1])).href === import.meta.url
+
+if (isDirectRun) {
+ const { records } = await generateDocsManifest()
+ console.log(`Generated ${records.length} documentation routes.`)
+}
diff --git a/site/scripts/prepare-static-output.mjs b/site/scripts/prepare-static-output.mjs
new file mode 100644
index 00000000..709aac99
--- /dev/null
+++ b/site/scripts/prepare-static-output.mjs
@@ -0,0 +1,108 @@
+import { promises as fs } from 'node:fs'
+import path from 'node:path'
+
+import { generateDocsManifest, paths } from './generate-docs-manifest.mjs'
+
+const distDir = path.join(paths.siteDir, 'dist')
+const expectedCustomDomain = 'claudecode-haha.relakkesyang.org'
+
+async function pathExists(targetPath) {
+ return fs.access(targetPath).then(() => true, () => false)
+}
+
+async function copyDirectory(source, destination) {
+ if (!await pathExists(source)) {
+ return
+ }
+
+ await fs.mkdir(path.dirname(destination), { recursive: true })
+ await fs.cp(source, destination, { recursive: true, force: true })
+}
+
+const markdownImagePattern = /!\[[^\]]*]\(([^)\s]+)(?:\s+["'][^"']*["'])?\)/g
+const htmlImagePattern = / ]*?\bsrc=["']([^"']+)["'][^>]*>/gi
+
+function imageTargets(markdown) {
+ const prose = markdown.replace(/```[\s\S]*?```/g, '')
+ return [
+ ...prose.matchAll(markdownImagePattern),
+ ...prose.matchAll(htmlImagePattern),
+ ].map((match) => match[1])
+}
+
+async function copyReferencedDocImages(records) {
+ const sources = new Set()
+
+ for (const record of records) {
+ const sourceDirectory = path.dirname(path.join(paths.repoDir, record.sourcePath))
+
+ for (const target of imageTargets(record.content)) {
+ const pathname = target.split(/[?#]/, 1)[0]
+ if (!pathname || /^(?:[a-z]+:|\/\/|data:)/i.test(pathname)) continue
+
+ const source = pathname.startsWith('/')
+ ? path.join(paths.docsDir, pathname.replace(/^\/+/, ''))
+ : path.resolve(sourceDirectory, pathname)
+ const relative = path.relative(paths.docsDir, source)
+
+ if (
+ relative.startsWith('..')
+ || path.isAbsolute(relative)
+ || relative === 'public'
+ || relative.startsWith(`public${path.sep}`)
+ ) {
+ continue
+ }
+
+ sources.add(source)
+ }
+ }
+
+ for (const source of sources) {
+ if (!await pathExists(source)) continue
+ const destination = path.join(distDir, path.relative(paths.docsDir, source))
+ await fs.mkdir(path.dirname(destination), { recursive: true })
+ await fs.copyFile(source, destination)
+ }
+
+ console.log(`Copied ${sources.size} referenced documentation images.`)
+}
+
+async function createRouteEntry(route, shell) {
+ const relativeRoute = route.replace(/^\/+|\/+$/g, '')
+ if (!relativeRoute) {
+ return
+ }
+
+ const routeDirectory = path.join(distDir, ...relativeRoute.split('/'))
+ await fs.mkdir(routeDirectory, { recursive: true })
+ await fs.writeFile(path.join(routeDirectory, 'index.html'), shell)
+}
+
+async function main() {
+ const { records } = await generateDocsManifest()
+ const shellPath = path.join(distDir, 'index.html')
+ const shell = await fs.readFile(shellPath, 'utf8')
+
+ await copyDirectory(path.join(paths.docsDir, 'public'), distDir)
+ await copyReferencedDocImages(records)
+
+ const customDomain = (await fs.readFile(path.join(distDir, 'CNAME'), 'utf8')).trim()
+ if (customDomain !== expectedCustomDomain) {
+ throw new Error(`Expected CNAME to contain ${expectedCustomDomain}, received ${customDomain || 'an empty value'}.`)
+ }
+
+ const routes = new Set(records.map((record) => record.path))
+ routes.add('/en')
+ routes.add('/docs')
+ routes.add('/en/docs')
+
+ for (const route of routes) {
+ await createRouteEntry(route, shell)
+ }
+
+ await fs.writeFile(path.join(distDir, '404.html'), shell)
+ console.log(`Prepared static output for ${routes.size} client-side routes.`)
+}
+
+await main()
diff --git a/site/src/App.jsx b/site/src/App.jsx
new file mode 100644
index 00000000..6de45a49
--- /dev/null
+++ b/site/src/App.jsx
@@ -0,0 +1,72 @@
+import { lazy, Suspense, useEffect, useState } from 'react'
+import HomePage from './pages/home/HomePage'
+
+const DocPage = lazy(() => import('./components/DocPage'))
+
+function currentPath() {
+ return window.location.pathname.replace(/\/+$/, '') || '/'
+}
+
+export default function App() {
+ const [path, setPath] = useState(currentPath)
+
+ useEffect(() => {
+ const onPopState = () => setPath(currentPath())
+ const onClick = (event) => {
+ const anchor = event.target.closest('a')
+ if (
+ !anchor
+ || event.defaultPrevented
+ || anchor.target === '_blank'
+ || anchor.hasAttribute('download')
+ || event.metaKey
+ || event.ctrlKey
+ || event.shiftKey
+ || event.altKey
+ ) {
+ return
+ }
+
+ const target = new URL(anchor.href, window.location.href)
+ if (target.origin !== window.location.origin) return
+ if (/\.html$/i.test(target.pathname)) return
+ if (
+ target.pathname === window.location.pathname
+ && target.search === window.location.search
+ ) {
+ return
+ }
+
+ event.preventDefault()
+ window.history.pushState({}, '', `${target.pathname}${target.search}${target.hash}`)
+ setPath(currentPath())
+ }
+
+ window.addEventListener('popstate', onPopState)
+ document.addEventListener('click', onClick)
+ return () => {
+ window.removeEventListener('popstate', onPopState)
+ document.removeEventListener('click', onClick)
+ }
+ }, [])
+
+ if (path === '/' || path === '/en') {
+ return
+ }
+
+ return (
+ Opening the field manual…}>
+ (
+
+ 404 · OFF THE MAP
+ 这里还没有留下脚印。
+ The crew could not find this page.
+ 回到工作室 →
+
+ )}
+ />
+
+ )
+}
diff --git a/site/src/components/DocPage.jsx b/site/src/components/DocPage.jsx
new file mode 100644
index 00000000..ca715789
--- /dev/null
+++ b/site/src/components/DocPage.jsx
@@ -0,0 +1,214 @@
+import React, { useEffect, useMemo } from 'react'
+import {
+ findDoc,
+ getAdjacentDocs,
+ renderMarkdown,
+ toSiteHref,
+} from '../content/docs'
+import {
+ alternateLocaleRoute,
+ getDocNavigation,
+} from '../content/navigation'
+import { DocPager } from './DocPager'
+import { DocSidebar } from './DocSidebar'
+import { DocToc } from './DocToc'
+import '../docs/doc.css'
+
+function defaultNavigate(href) {
+ window.history.pushState({}, '', toSiteHref(href))
+ window.dispatchEvent(new PopStateEvent('popstate'))
+}
+
+async function scrollToDocHeading(id) {
+ const images = [...document.querySelectorAll('.doc-content img')]
+ images.forEach((image) => {
+ image.loading = 'eager'
+ })
+ await Promise.all(images.map((image) => {
+ if (image.complete && image.naturalWidth > 0) return Promise.resolve()
+ return image.decode?.().catch(() => undefined) || Promise.resolve()
+ }))
+
+ await new Promise((resolve) => requestAnimationFrame(resolve))
+ document.getElementById(id)?.scrollIntoView()
+}
+
+export function DocPage({
+ onNavigate = defaultNavigate,
+ onNotFound,
+ path,
+ pathname = path || window.location.pathname,
+}) {
+ const doc = useMemo(() => findDoc(pathname), [pathname])
+ const navigation = useMemo(
+ () => getDocNavigation(doc?.locale || 'zh'),
+ [doc?.locale],
+ )
+ const rendered = useMemo(
+ () => doc ? renderMarkdown(doc) : null,
+ [doc],
+ )
+ const adjacent = useMemo(
+ () => doc ? getAdjacentDocs(doc, navigation) : {},
+ [doc, navigation],
+ )
+
+ useEffect(() => {
+ if (!doc) return
+ document.documentElement.lang = doc.locale === 'en' ? 'en' : 'zh-CN'
+ document.title = `${doc.title} · Claude Code Haha`
+
+ const hash = window.location.hash
+ if (hash) scrollToDocHeading(decodeURIComponent(hash.slice(1)))
+ else requestAnimationFrame(() => window.scrollTo({ top: 0 }))
+ }, [doc])
+
+ useEffect(() => {
+ const diagrams = [...document.querySelectorAll('.doc-mermaid[data-mermaid-pending="true"]')]
+ if (diagrams.length === 0) return undefined
+
+ let cancelled = false
+ async function renderDiagrams() {
+ const { default: mermaid } = await import('mermaid')
+ mermaid.initialize({
+ securityLevel: 'strict',
+ startOnLoad: false,
+ theme: 'base',
+ themeVariables: {
+ fontFamily: 'DM Sans, Noto Sans SC, sans-serif',
+ lineColor: '#171915',
+ primaryColor: '#eef2ff',
+ primaryBorderColor: '#171915',
+ primaryTextColor: '#171915',
+ secondaryColor: '#fff8d7',
+ tertiaryColor: '#fffdf7',
+ },
+ })
+
+ for (const [index, diagram] of diagrams.entries()) {
+ if (cancelled) return
+ const source = diagram.querySelector('pre')?.textContent || ''
+ try {
+ const id = `doc-mermaid-${Date.now()}-${index}`
+ const { svg } = await mermaid.render(id, source)
+ if (!cancelled) {
+ diagram.innerHTML = svg
+ diagram.dataset.mermaidPending = 'false'
+ }
+ } catch {
+ diagram.dataset.mermaidPending = 'error'
+ }
+ }
+ }
+
+ renderDiagrams()
+ return () => {
+ cancelled = true
+ }
+ }, [doc, rendered])
+
+ if (!doc) return onNotFound ? onNotFound(pathname) : null
+
+ function handleArticleClick(event) {
+ const hashAnchor = event.target.closest('a[href^="#"]')
+ if (hashAnchor && !event.defaultPrevented) {
+ event.preventDefault()
+ const id = decodeURIComponent(hashAnchor.hash.slice(1))
+ window.history.pushState({}, '', hashAnchor.hash)
+ scrollToDocHeading(id)
+ return
+ }
+
+ const anchor = event.target.closest('a[data-doc-link]')
+ if (!anchor || event.defaultPrevented) return
+ if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return
+
+ event.preventDefault()
+ onNavigate(anchor.dataset.docRoute)
+ }
+
+ const alternateRoute = alternateLocaleRoute(doc)
+ const localeLabel = doc.locale === 'en' ? '阅读中文' : 'Read in English'
+ const homeRoute = doc.locale === 'en' ? '/en' : '/'
+ const handleTocNavigate = (event, id) => {
+ event.preventDefault()
+ window.history.pushState({}, '', `#${encodeURIComponent(id)}`)
+ scrollToDocHeading(id)
+ }
+
+ return (
+ <>
+
+
+
+ >
+ )
+}
+
+export default DocPage
diff --git a/site/src/components/DocPager.jsx b/site/src/components/DocPager.jsx
new file mode 100644
index 00000000..5d5581f3
--- /dev/null
+++ b/site/src/components/DocPager.jsx
@@ -0,0 +1,29 @@
+import React from 'react'
+import { toSiteHref } from '../content/docs'
+
+export function DocPager({ next, onNavigate, previous }) {
+ function handleClick(event, route) {
+ if (!onNavigate) return
+ event.preventDefault()
+ onNavigate(route)
+ }
+
+ if (!previous && !next) return null
+
+ return (
+
+ {previous ? (
+ handleClick(event, previous.route)}>
+ ← Previous
+ {previous.title}
+
+ ) : }
+ {next ? (
+ handleClick(event, next.route)}>
+ Next →
+ {next.title}
+
+ ) : }
+
+ )
+}
diff --git a/site/src/components/DocSidebar.jsx b/site/src/components/DocSidebar.jsx
new file mode 100644
index 00000000..f9dcf53d
--- /dev/null
+++ b/site/src/components/DocSidebar.jsx
@@ -0,0 +1,41 @@
+import React from 'react'
+import { toSiteHref } from '../content/docs'
+
+export function DocSidebar({ activeRoute, navigation, onNavigate }) {
+ function handleClick(event, route) {
+ if (!onNavigate) return
+ event.preventDefault()
+ onNavigate(route)
+ }
+
+ return (
+
+ Field manual
+
+ {navigation.map((group, index) => (
+ item.route === activeRoute)}
+ >
+ {group.label}
+
+
+ ))}
+
+
+ )
+}
diff --git a/site/src/components/DocToc.jsx b/site/src/components/DocToc.jsx
new file mode 100644
index 00000000..261c6333
--- /dev/null
+++ b/site/src/components/DocToc.jsx
@@ -0,0 +1,23 @@
+import React from 'react'
+
+export function DocToc({ headings, locale, onAnchorNavigate }) {
+ if (!headings.length) return null
+
+ return (
+
+ )
+}
diff --git a/site/src/components/Icon.jsx b/site/src/components/Icon.jsx
new file mode 100644
index 00000000..d582fb54
--- /dev/null
+++ b/site/src/components/Icon.jsx
@@ -0,0 +1,28 @@
+const paths = {
+ arrow: <> >,
+ check: ,
+ chevron: ,
+ copy: <> >,
+ download: <> >,
+ github: ,
+ menu: <> >,
+ play: ,
+ spark: <> >,
+}
+
+export default function Icon({ name, size = 20, className = '' }) {
+ return (
+
+
+ {paths[name]}
+
+
+ )
+}
diff --git a/site/src/content/docs.js b/site/src/content/docs.js
new file mode 100644
index 00000000..1083cc14
--- /dev/null
+++ b/site/src/content/docs.js
@@ -0,0 +1,354 @@
+import { Marked, Renderer } from 'marked'
+import DOMPurify from 'dompurify'
+import { docsManifest } from '../generated/docs-manifest'
+
+const EXTERNAL_PROTOCOL = /^(?:[a-z][a-z\d+.-]*:|\/\/)/i
+const SITE_BASE = `/${String(import.meta.env.BASE_URL || '/').replace(/^\/+|\/+$/g, '')}`
+ .replace(/\/{2,}/g, '/')
+ .replace(/\/$/, '') || '/'
+
+function cleanRoute(route) {
+ const withoutQuery = route.split(/[?#]/, 1)[0]
+ let normalized = decodeURIComponent(withoutQuery || '/')
+ .replace(/\\/g, '/')
+ .replace(/\/{2,}/g, '/')
+ .replace(/(?:\.md|\.html)$/i, '')
+
+ if (!normalized.startsWith('/')) normalized = `/${normalized}`
+ if (normalized !== '/') normalized = normalized.replace(/\/+$/, '')
+ return normalized
+}
+
+export function toSiteHref(href) {
+ if (!href || href.startsWith('#') || EXTERNAL_PROTOCOL.test(href)) return href
+
+ const { path, suffix } = splitHref(href)
+ const route = /\.html$/i.test(path)
+ ? `/${decodeURIComponent(path).replace(/^\/+/, '').replace(/\/{2,}/g, '/')}`
+ : cleanRoute(path)
+ if (SITE_BASE === '/') return `${route}${suffix}`
+ if (route === SITE_BASE || route.startsWith(`${SITE_BASE}/`)) {
+ return `${route}${suffix}`
+ }
+ return `${SITE_BASE}${route}${suffix}`
+}
+
+function withoutSiteBase(pathname) {
+ const route = cleanRoute(pathname)
+ if (SITE_BASE === '/') return route
+ if (route === SITE_BASE) return '/'
+ if (route.startsWith(`${SITE_BASE}/`)) return route.slice(SITE_BASE.length)
+ return route
+}
+
+function routeFromSourcePath(sourcePath) {
+ const withoutExtension = sourcePath.replace(/\.md$/i, '')
+
+ if (withoutExtension === 'index') return '/docs'
+ if (withoutExtension === 'en/index') return '/en/docs'
+ if (withoutExtension.endsWith('/index')) {
+ return cleanRoute(`/${withoutExtension.slice(0, -'/index'.length)}`)
+ }
+
+ return cleanRoute(`/${withoutExtension}`)
+}
+
+function parseFrontmatter(source) {
+ if (!source.startsWith('---\n')) return { attributes: {}, body: source }
+
+ const closingIndex = source.indexOf('\n---\n', 4)
+ if (closingIndex === -1) return { attributes: {}, body: source }
+
+ const attributes = {}
+ const block = source.slice(4, closingIndex)
+ for (const line of block.split('\n')) {
+ const separator = line.indexOf(':')
+ if (separator === -1) continue
+ const key = line.slice(0, separator).trim()
+ let value = line.slice(separator + 1).trim()
+ if (!key || !value) continue
+ value = value.replace(/^(['"])(.*)\1$/, '$2')
+ attributes[key] = value
+ }
+
+ return {
+ attributes,
+ body: source.slice(closingIndex + 5),
+ }
+}
+
+function plainText(value) {
+ return value
+ .replace(/<[^>]*>/g, '')
+ .replace(/!\[([^\]]*)\]\([^)]*\)/g, '$1')
+ .replace(/\[([^\]]+)\]\([^)]*\)/g, '$1')
+ .replace(/[*_`~]/g, '')
+ .trim()
+}
+
+function titleFromBody(body, sourcePath) {
+ const match = body.match(/^#\s+(.+)$/m)
+ if (match) return plainText(match[1])
+
+ const filename = sourcePath.split('/').pop().replace(/\.md$/i, '')
+ return filename
+ .replace(/^\d+[-_.]?/, '')
+ .replace(/[-_]+/g, ' ')
+ .replace(/\b\w/g, (letter) => letter.toUpperCase())
+}
+
+function descriptionFromBody(body) {
+ const content = body
+ .replace(/^#.*$/gm, '')
+ .replace(/^>.*$/gm, '')
+ .replace(/```[\s\S]*?```/g, '')
+ .split(/\n\s*\n/)
+ .map(plainText)
+ .find((paragraph) => paragraph.length > 30)
+
+ return content ? content.slice(0, 180) : ''
+}
+
+function orderFromSourcePath(sourcePath) {
+ const filename = sourcePath.split('/').pop()
+ const match = filename.match(/^(\d+)/)
+ return match ? Number(match[1]) : filename === 'index.md' ? 0 : 999
+}
+
+function sectionFromSourcePath(sourcePath) {
+ const parts = sourcePath.split('/')
+ const offset = parts[0] === 'en' ? 1 : 0
+ return parts[offset] === 'index.md' ? 'guide' : parts[offset]
+}
+
+function preprocessDirectives(markdown) {
+ return markdown.replace(
+ /^(:{3,})\s*(info|tip|warning|danger)?\s*([^\n]*)\n([\s\S]*?)^\1\s*$/gm,
+ (_, _fence, kind = 'info', label, content) => {
+ const safeKind = kind || 'info'
+ const heading = label.trim() || {
+ danger: 'Danger',
+ info: 'Info',
+ tip: 'Tip',
+ warning: 'Warning',
+ }[safeKind]
+ return `\n${escapeAttribute(heading)} \n\n${content.trim()}\n \n\n`
+ },
+ )
+}
+
+const docs = docsManifest
+ .map((record) => {
+ const sourcePath = record.sourcePath.replace(/^docs\//, '')
+ const { attributes, body } = parseFrontmatter(String(record.content))
+ return {
+ attributes,
+ body,
+ description: record.description || attributes.description || descriptionFromBody(body),
+ locale: record.locale,
+ order: orderFromSourcePath(sourcePath),
+ route: record.path,
+ section: record.section || sectionFromSourcePath(sourcePath),
+ sourcePath,
+ title: record.title || attributes.title || titleFromBody(body, sourcePath),
+ }
+ })
+ .sort((left, right) => left.sourcePath.localeCompare(right.sourcePath))
+
+const docsByRoute = new Map(docs.map((doc) => [doc.route.toLowerCase(), doc]))
+const docsBySource = new Map(docs.map((doc) => [doc.sourcePath.toLowerCase(), doc]))
+
+function normalizeRelativePath(basePath, targetPath) {
+ const segments = basePath.split('/')
+ segments.pop()
+
+ for (const segment of targetPath.split('/')) {
+ if (!segment || segment === '.') continue
+ if (segment === '..') segments.pop()
+ else segments.push(segment)
+ }
+
+ return segments.join('/')
+}
+
+function splitHref(href) {
+ const match = href.match(/^([^?#]*)([?#].*)?$/)
+ return {
+ path: match?.[1] || '',
+ suffix: match?.[2] || '',
+ }
+}
+
+function resolveSourceReference(doc, href) {
+ const { path, suffix } = splitHref(href)
+ if (!path || path.startsWith('#') || EXTERNAL_PROTOCOL.test(path)) return href
+
+ const sourcePath = path.startsWith('/')
+ ? path.replace(/^\/+/, '')
+ : normalizeRelativePath(doc.sourcePath, path)
+
+ return { sourcePath, suffix }
+}
+
+export function resolveDocHref(doc, href) {
+ const reference = resolveSourceReference(doc, href)
+ if (typeof reference === 'string') return reference
+
+ const { sourcePath, suffix } = reference
+ if (/\.html$/i.test(sourcePath)) {
+ return `/${sourcePath.replace(/^\/+/, '')}${suffix}`
+ }
+
+ const markdownPath = sourcePath.endsWith('.md')
+ ? sourcePath
+ : `${sourcePath.replace(/\/+$/, '')}.md`
+ const indexPath = `${sourcePath.replace(/\/+$/, '')}/index.md`
+ const target = docsBySource.get(markdownPath.toLowerCase())
+ || docsBySource.get(indexPath.toLowerCase())
+
+ if (target) return `${target.route}${suffix}`
+ return `${cleanRoute(`/${sourcePath}`)}${suffix}`
+}
+
+export function resolveDocAsset(doc, href) {
+ if (!href || EXTERNAL_PROTOCOL.test(href) || href.startsWith('data:')) return href
+
+ const reference = resolveSourceReference(doc, href)
+ if (typeof reference === 'string') return reference
+ return `${toSiteHref(`/${reference.sourcePath}`)}${reference.suffix}`
+}
+
+function textFromTokens(tokens = []) {
+ return tokens.map((token) => {
+ if (typeof token.text === 'string') return plainText(token.text)
+ if (Array.isArray(token.tokens)) return textFromTokens(token.tokens)
+ return ''
+ }).join('')
+}
+
+export function slugifyHeading(value) {
+ return plainText(value)
+ .toLowerCase()
+ .replace(/[^\p{L}\p{M}\p{N}\p{Pc}\- ]/gu, '')
+ .trim()
+ .replace(/\s+/g, '-')
+}
+
+function escapeAttribute(value) {
+ return String(value)
+ .replace(/&/g, '&')
+ .replace(/"/g, '"')
+ .replace(//g, '>')
+}
+
+export function renderMarkdown(doc) {
+ const renderer = new Renderer()
+ const headingCounts = new Map()
+ const tableOfContents = []
+
+ renderer.heading = function heading({ tokens, depth }) {
+ const content = this.parser.parseInline(tokens)
+ const text = textFromTokens(tokens) || plainText(content)
+ const baseSlug = slugifyHeading(text) || 'section'
+ const count = headingCounts.get(baseSlug) || 0
+ const id = count === 0 ? baseSlug : `${baseSlug}-${count}`
+ headingCounts.set(baseSlug, count + 1)
+
+ if (depth >= 2 && depth <= 3) {
+ tableOfContents.push({ depth, id, text })
+ }
+
+ return `${content}# `
+ }
+
+ renderer.link = function link({ href, title, tokens }) {
+ const resolvedHref = resolveDocHref(doc, href)
+ const isExternal = EXTERNAL_PROTOCOL.test(resolvedHref)
+ const isStaticHtml = /\.html(?:[?#]|$)/i.test(resolvedHref)
+ const titleAttribute = title ? ` title="${escapeAttribute(title)}"` : ''
+ const externalAttributes = isExternal
+ ? ' target="_blank" rel="noreferrer noopener"'
+ : isStaticHtml
+ ? ''
+ : ` data-doc-link data-doc-route="${escapeAttribute(resolvedHref)}"`
+ const publicHref = isExternal ? resolvedHref : toSiteHref(resolvedHref)
+ return `${this.parser.parseInline(tokens)} `
+ }
+
+ renderer.image = function image({ href, title, text }) {
+ const resolvedHref = resolveDocAsset(doc, href)
+ const titleAttribute = title ? ` title="${escapeAttribute(title)}"` : ''
+ return ` `
+ }
+
+ renderer.code = function code({ text, lang, escaped }) {
+ const language = (lang || '').trim().split(/\s+/)[0]
+ if (language === 'mermaid') {
+ return ``
+ }
+
+ const className = language ? ` class="language-${escapeAttribute(language)}"` : ''
+ const codeText = escaped ? text : escapeAttribute(text)
+ return `${codeText} `
+ }
+
+ const parser = new Marked({
+ gfm: true,
+ renderer,
+ })
+ const html = DOMPurify.sanitize(parser.parse(preprocessDirectives(doc.body)), {
+ ADD_ATTR: [
+ 'aria-label',
+ 'data-doc-link',
+ 'data-doc-route',
+ 'data-language',
+ 'data-mermaid-pending',
+ 'decoding',
+ 'loading',
+ 'target',
+ ],
+ FORBID_TAGS: ['embed', 'form', 'iframe', 'input', 'object', 'script', 'style', 'textarea'],
+ })
+
+ return {
+ html,
+ tableOfContents,
+ }
+}
+
+function normalizeRequestedRoute(pathname) {
+ let route = withoutSiteBase(pathname)
+
+ if (route === '/docs') return '/desktop'
+ if (route === '/en/docs') return '/en/desktop'
+ if (route.startsWith('/docs/')) route = route.slice('/docs'.length)
+ if (route.startsWith('/en/docs/')) route = `/en${route.slice('/en/docs'.length)}`
+ return route
+}
+
+export function findDoc(pathname) {
+ const route = normalizeRequestedRoute(pathname)
+ return docsByRoute.get(route.toLowerCase()) || null
+}
+
+export function getAllDocs(locale) {
+ return docs.filter((doc) => !locale || doc.locale === locale)
+}
+
+export function getAdjacentDocs(doc, navigation) {
+ const routes = navigation.flatMap((group) => group.items.map((item) => item.route))
+ const index = routes.indexOf(doc.route)
+ return {
+ next: index >= 0 ? findDoc(routes[index + 1]) : null,
+ previous: index > 0 ? findDoc(routes[index - 1]) : null,
+ }
+}
+
+export function isDocRoute(pathname) {
+ return Boolean(findDoc(pathname))
+ || cleanRoute(pathname) === '/docs'
+ || cleanRoute(pathname) === '/en/docs'
+}
+
+export { docs }
diff --git a/site/src/content/navigation.js b/site/src/content/navigation.js
new file mode 100644
index 00000000..e45e47fb
--- /dev/null
+++ b/site/src/content/navigation.js
@@ -0,0 +1,105 @@
+import { findDoc, getAllDocs } from './docs'
+
+const sectionOrder = [
+ 'guide',
+ 'desktop',
+ 'features',
+ 'agent',
+ 'skills',
+ 'memory',
+ 'im',
+ 'channel',
+ 'reference',
+]
+
+const sectionLabels = {
+ zh: {
+ agent: '多 Agent',
+ channel: 'Channel 研究',
+ desktop: '桌面工作台',
+ features: '核心能力',
+ guide: '开始使用',
+ im: '远程连接',
+ memory: '记忆系统',
+ reference: '开发者参考',
+ skills: 'Skills 系统',
+ },
+ en: {
+ agent: 'Multi-Agent',
+ channel: 'Channel research',
+ desktop: 'Desktop workspace',
+ features: 'Core capabilities',
+ guide: 'Get started',
+ im: 'Remote access',
+ memory: 'Memory',
+ reference: 'Developer reference',
+ skills: 'Skills',
+ },
+}
+
+const preferredLabels = new Map([
+ ['/desktop', '桌面端概览'],
+ ['/desktop/01-quick-start', '三分钟快速上手'],
+ ['/desktop/03-features', '完整功能地图'],
+ ['/desktop/04-installation', '安装与更新'],
+ ['/desktop/05-FAQ', '桌面端排障'],
+ ['/desktop/06-h5-access', '手机与浏览器访问'],
+ ['/desktop/pets', '桌面宠物'],
+ ['/features/computer-use', 'Computer Use'],
+ ['/guide/quick-start', 'CLI 安装与启动'],
+ ['/guide/third-party-models', '连接模型服务'],
+ ['/im', 'IM 接入总览'],
+ ['/reference/local-server', '本地服务与 API'],
+ ['/en/desktop', 'Desktop overview'],
+ ['/en/desktop/01-quick-start', 'Quick start'],
+ ['/en/desktop/03-features', 'Feature map'],
+ ['/en/desktop/04-installation', 'Install & update'],
+ ['/en/desktop/05-FAQ', 'Desktop troubleshooting'],
+ ['/en/desktop/06-h5-access', 'Browser & mobile access'],
+ ['/en/desktop/pets', 'Desktop pets'],
+ ['/en/features/computer-use', 'Computer Use'],
+ ['/en/guide/quick-start', 'CLI quick start'],
+ ['/en/guide/third-party-models', 'Connect a model'],
+ ['/en/im', 'IM overview'],
+ ['/en/reference/local-server', 'Local server & API'],
+])
+
+export function getDocNavigation(locale = 'zh') {
+ const grouped = new Map()
+ for (const doc of getAllDocs(locale)) {
+ const sectionDocs = grouped.get(doc.section) || []
+ sectionDocs.push(doc)
+ grouped.set(doc.section, sectionDocs)
+ }
+
+ return [...grouped.entries()]
+ .sort(([left], [right]) => {
+ const leftIndex = sectionOrder.indexOf(left)
+ const rightIndex = sectionOrder.indexOf(right)
+ return (leftIndex === -1 ? Number.MAX_SAFE_INTEGER : leftIndex)
+ - (rightIndex === -1 ? Number.MAX_SAFE_INTEGER : rightIndex)
+ || left.localeCompare(right)
+ })
+ .map(([section, sectionDocs]) => ({
+ label: sectionLabels[locale]?.[section] || section,
+ items: sectionDocs
+ .sort((left, right) => left.order - right.order || left.title.localeCompare(right.title))
+ .map((doc) => ({
+ label: preferredLabels.get(doc.route) || doc.title,
+ route: doc.route,
+ title: doc.title,
+ })),
+ }))
+}
+
+export function alternateLocaleRoute(doc) {
+ if (!doc) return '/'
+
+ const sourcePath = doc.locale === 'en'
+ ? doc.sourcePath.replace(/^en\//, '')
+ : `en/${doc.sourcePath}`
+ const alternate = findDoc(`/${sourcePath.replace(/(?:\/index)?\.md$/i, '')}`)
+
+ if (alternate) return alternate.route
+ return doc.locale === 'en' ? '/docs' : '/en/docs'
+}
diff --git a/site/src/docs/doc.css b/site/src/docs/doc.css
new file mode 100644
index 00000000..2c915ae9
--- /dev/null
+++ b/site/src/docs/doc.css
@@ -0,0 +1,649 @@
+.doc-site-header {
+ position: sticky;
+ top: 0;
+ z-index: 40;
+ display: grid;
+ height: 68px;
+ padding: 0 28px;
+ grid-template-columns: 1fr auto 1fr;
+ align-items: center;
+ border-bottom: 1px solid var(--ink);
+ background: color-mix(in srgb, var(--paper-bright) 94%, transparent);
+ backdrop-filter: blur(14px);
+}
+
+.doc-site-header__brand,
+.doc-site-header__actions {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.doc-site-header__brand {
+ width: fit-content;
+ font-weight: 800;
+ letter-spacing: -0.02em;
+}
+
+.doc-site-header__brand img {
+ width: 31px;
+ height: 31px;
+ object-fit: contain;
+}
+
+.doc-site-header__brand small {
+ color: var(--ink-soft);
+ font-family: 'DM Mono', monospace;
+ font-size: 10px;
+ font-weight: 500;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.doc-site-header__marker {
+ justify-self: center;
+ padding: 7px 11px;
+ border: 1px solid var(--ink);
+ background: var(--yellow);
+ box-shadow: 3px 3px 0 var(--ink);
+ font-family: 'DM Mono', monospace;
+ font-size: 10px;
+ font-weight: 700;
+ letter-spacing: 0.07em;
+ transform: rotate(-1deg);
+}
+
+.doc-site-header__actions {
+ justify-self: end;
+ font-family: 'DM Mono', 'Noto Sans SC', monospace;
+ font-size: 11px;
+}
+
+.doc-site-header__actions a {
+ border-bottom: 1px solid transparent;
+}
+
+.doc-site-header__actions a:hover {
+ border-color: currentColor;
+}
+
+.doc-shell {
+ display: grid;
+ max-width: 1480px;
+ min-height: calc(100vh - 68px);
+ margin: 0 auto;
+ grid-template-columns: 270px minmax(0, 820px) 220px;
+ justify-content: center;
+ background:
+ linear-gradient(90deg, transparent 0 49.9%, rgba(31, 31, 29, 0.035) 50% 50.1%, transparent 50.2%),
+ var(--paper-bright);
+}
+
+.doc-sidebar {
+ position: sticky;
+ top: 68px;
+ align-self: start;
+ height: calc(100vh - 68px);
+ overflow-y: auto;
+ padding: 36px 28px 64px;
+ border-right: 1px solid rgba(31, 31, 29, 0.18);
+ scrollbar-width: thin;
+}
+
+.doc-sidebar__eyebrow {
+ margin-bottom: 28px;
+ color: var(--coral);
+ font-family: 'DM Mono', monospace;
+ font-size: 10px;
+ font-weight: 700;
+ letter-spacing: 0.13em;
+ text-transform: uppercase;
+}
+
+.doc-sidebar__group {
+ padding: 14px 0;
+ border-top: 1px solid rgba(31, 31, 29, 0.18);
+}
+
+.doc-sidebar__group:last-child {
+ border-bottom: 1px solid rgba(31, 31, 29, 0.18);
+}
+
+.doc-sidebar__group summary {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ color: var(--ink);
+ cursor: pointer;
+ font-size: 13px;
+ font-weight: 800;
+ list-style: none;
+}
+
+.doc-sidebar__group summary::-webkit-details-marker {
+ display: none;
+}
+
+.doc-sidebar__group summary::after {
+ color: var(--blue);
+ content: '+';
+ font-family: 'DM Mono', monospace;
+}
+
+.doc-sidebar__group[open] summary::after {
+ content: '−';
+}
+
+.doc-sidebar__group ul {
+ display: grid;
+ gap: 3px;
+ margin: 10px 0 0;
+ padding: 0;
+ list-style: none;
+}
+
+.doc-sidebar__group li {
+ margin: 0;
+}
+
+.doc-sidebar__group a {
+ display: grid;
+ padding: 7px 9px;
+ grid-template-columns: 16px 1fr;
+ align-items: start;
+ color: #60615d;
+ font-size: 12px;
+ line-height: 1.4;
+}
+
+.doc-sidebar__group a span {
+ color: #b8b6ad;
+ font-family: 'DM Mono', monospace;
+}
+
+.doc-sidebar__group a:hover {
+ color: var(--ink);
+ background: rgba(245, 200, 76, 0.32);
+}
+
+.doc-sidebar__group a[aria-current='page'] {
+ color: #fff;
+ background: var(--blue);
+}
+
+.doc-sidebar__group a[aria-current='page'] span {
+ color: var(--yellow);
+}
+
+.doc-main {
+ min-width: 0;
+ padding: 62px clamp(34px, 6vw, 88px) 100px;
+}
+
+.doc-main__meta {
+ display: flex;
+ margin-bottom: 44px;
+ align-items: center;
+ justify-content: space-between;
+ color: var(--ink-soft);
+ font-family: 'DM Mono', 'Noto Sans SC', monospace;
+ font-size: 10px;
+ letter-spacing: 0.08em;
+}
+
+.doc-main__meta span {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.doc-main__meta span::before {
+ width: 8px;
+ height: 8px;
+ border: 1px solid var(--ink);
+ border-radius: 50%;
+ background: #77db72;
+ content: '';
+}
+
+.doc-main__meta a {
+ padding-bottom: 4px;
+ border-bottom: 1px solid currentColor;
+}
+
+.doc-content {
+ color: #292a27;
+ font-size: 16px;
+ line-height: 1.82;
+}
+
+.doc-content > :first-child {
+ margin-top: 0;
+}
+
+.doc-content h1 {
+ max-width: 780px;
+ margin: 0 0 30px;
+ font-size: clamp(42px, 5vw, 68px);
+ line-height: 1.02;
+ letter-spacing: -0.055em;
+}
+
+.doc-content h1::after {
+ display: block;
+ width: 84px;
+ height: 9px;
+ margin-top: 28px;
+ background: var(--coral);
+ box-shadow: 90px 0 0 var(--yellow);
+ content: '';
+}
+
+.doc-content h2 {
+ margin: 72px 0 20px;
+ padding-top: 18px;
+ border-top: 2px solid var(--ink);
+ font-size: clamp(28px, 3vw, 38px);
+ line-height: 1.18;
+ letter-spacing: -0.035em;
+}
+
+.doc-content h3 {
+ margin: 42px 0 14px;
+ font-size: 23px;
+ line-height: 1.25;
+ letter-spacing: -0.025em;
+}
+
+.doc-content h4 {
+ margin: 32px 0 12px;
+ font-size: 18px;
+}
+
+.doc-heading-anchor {
+ margin-left: 8px;
+ color: var(--coral);
+ font-family: 'DM Mono', monospace;
+ font-size: 0.55em;
+ opacity: 0;
+}
+
+.doc-content :is(h2, h3, h4):hover .doc-heading-anchor,
+.doc-heading-anchor:focus-visible {
+ opacity: 1;
+}
+
+.doc-content p,
+.doc-content ul,
+.doc-content ol {
+ margin: 18px 0;
+}
+
+.doc-content li + li {
+ margin-top: 6px;
+}
+
+.doc-content a {
+ color: #244fcd;
+ text-decoration: underline;
+ text-decoration-color: rgba(36, 79, 205, 0.35);
+ text-decoration-thickness: 1px;
+ text-underline-offset: 4px;
+}
+
+.doc-content a:hover {
+ color: #e74e31;
+ text-decoration-color: currentColor;
+}
+
+.doc-content blockquote {
+ margin: 30px 0;
+ padding: 18px 22px;
+ border: 1px solid var(--ink);
+ border-left: 8px solid var(--blue);
+ background: #eef2ff;
+ box-shadow: 5px 5px 0 rgba(31, 31, 29, 0.12);
+}
+
+.doc-content blockquote > :first-child,
+.doc-callout > :first-child {
+ margin-top: 0;
+}
+
+.doc-content blockquote > :last-child,
+.doc-callout > :last-child {
+ margin-bottom: 0;
+}
+
+.doc-callout {
+ margin: 30px 0;
+ padding: 20px 22px;
+ border: 1px solid var(--ink);
+ background: #fff8d7;
+ box-shadow: 6px 6px 0 var(--yellow);
+}
+
+.doc-callout > strong:first-child {
+ display: block;
+ margin-bottom: 8px;
+ font-family: 'DM Mono', 'Noto Sans SC', monospace;
+ font-size: 11px;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.doc-callout--warning,
+.doc-callout--danger {
+ background: #fff0ec;
+ box-shadow: 6px 6px 0 var(--coral);
+}
+
+.doc-content code {
+ padding: 0.12em 0.38em;
+ color: #b93320;
+ border: 1px solid rgba(31, 31, 29, 0.15);
+ background: #f1ede2;
+ font-family: 'DM Mono', ui-monospace, monospace;
+ font-size: 0.86em;
+}
+
+.doc-content pre {
+ position: relative;
+ overflow-x: auto;
+ margin: 28px 0;
+ padding: 45px 24px 24px;
+ color: #f5f1e8;
+ border: 1px solid #000;
+ background: #1c1c1d;
+ box-shadow: 7px 7px 0 var(--blue);
+ line-height: 1.65;
+}
+
+.doc-content pre::before {
+ position: absolute;
+ top: 14px;
+ left: 22px;
+ color: #a7a8a3;
+ content: attr(data-language);
+ font-family: 'DM Mono', monospace;
+ font-size: 9px;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+}
+
+.doc-content pre::after {
+ position: absolute;
+ top: 16px;
+ right: 19px;
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: var(--coral);
+ box-shadow: -13px 0 0 var(--yellow), -26px 0 0 #5bd3b0;
+ content: '';
+}
+
+.doc-content pre code {
+ padding: 0;
+ color: inherit;
+ border: 0;
+ background: transparent;
+ font-size: 12px;
+}
+
+.doc-mermaid {
+ display: grid;
+ min-height: 220px;
+ margin: 34px 0;
+ padding: 30px;
+ place-items: center;
+ overflow-x: auto;
+ border: 1px solid var(--ink);
+ background:
+ linear-gradient(rgba(23, 25, 21, 0.055) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(23, 25, 21, 0.055) 1px, transparent 1px),
+ var(--paper-bright);
+ background-size: 24px 24px;
+ box-shadow: 8px 8px 0 var(--yellow);
+}
+
+.doc-mermaid > svg {
+ width: auto;
+ max-width: 100%;
+ height: auto;
+}
+
+.doc-mermaid[data-mermaid-pending='true'] pre,
+.doc-mermaid[data-mermaid-pending='error'] pre {
+ width: 100%;
+ margin: 0;
+}
+
+.doc-mermaid[data-mermaid-pending='error']::before {
+ align-self: end;
+ color: var(--coral);
+ content: 'DIAGRAM SOURCE';
+ font-family: 'DM Mono', monospace;
+ font-size: 9px;
+ letter-spacing: 0.08em;
+}
+
+.doc-content table {
+ display: block;
+ width: 100%;
+ margin: 30px 0;
+ overflow-x: auto;
+ border-spacing: 0;
+ border-collapse: collapse;
+ font-size: 13px;
+}
+
+.doc-content :is(th, td) {
+ min-width: 120px;
+ padding: 12px 14px;
+ border: 1px solid rgba(31, 31, 29, 0.25);
+ text-align: left;
+ vertical-align: top;
+}
+
+.doc-content th {
+ color: #fff;
+ background: var(--blue);
+}
+
+.doc-content tr:nth-child(even) td {
+ background: rgba(238, 233, 218, 0.55);
+}
+
+.doc-content img {
+ display: block;
+ width: auto;
+ max-width: 100%;
+ height: auto;
+ margin: 34px auto;
+ border: 1px solid var(--ink);
+ background: #fff;
+ box-shadow: 9px 9px 0 rgba(31, 31, 29, 0.14);
+}
+
+.doc-content hr {
+ height: 1px;
+ margin: 54px 0;
+ border: 0;
+ background: rgba(31, 31, 29, 0.22);
+}
+
+.doc-toc {
+ position: sticky;
+ top: 68px;
+ align-self: start;
+ max-height: calc(100vh - 68px);
+ overflow-y: auto;
+ padding: 54px 24px 54px 28px;
+ border-left: 1px solid rgba(31, 31, 29, 0.18);
+}
+
+.doc-toc > p {
+ margin: 0 0 18px;
+ color: var(--coral);
+ font-family: 'DM Mono', 'Noto Sans SC', monospace;
+ font-size: 10px;
+ font-weight: 700;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.doc-toc ol {
+ display: grid;
+ gap: 10px;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+.doc-toc a {
+ display: block;
+ color: #73746f;
+ font-size: 11px;
+ line-height: 1.45;
+}
+
+.doc-toc a:hover {
+ color: var(--blue);
+}
+
+.doc-toc__depth-3 {
+ padding-left: 12px;
+}
+
+.doc-pager {
+ display: grid;
+ margin-top: 88px;
+ grid-template-columns: 1fr 1fr;
+ gap: 14px;
+}
+
+.doc-pager > a {
+ display: grid;
+ min-height: 110px;
+ padding: 20px;
+ align-content: space-between;
+ border: 1px solid var(--ink);
+ background: var(--paper);
+ transition: color 160ms ease, background 160ms ease, transform 160ms ease;
+}
+
+.doc-pager > a:last-child {
+ text-align: right;
+}
+
+.doc-pager > a:hover {
+ color: #fff;
+ background: var(--blue);
+ transform: translateY(-3px);
+}
+
+.doc-pager small {
+ font-family: 'DM Mono', monospace;
+ font-size: 9px;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.doc-pager span {
+ font-size: 15px;
+ font-weight: 800;
+}
+
+@media (max-width: 1180px) {
+ .doc-shell {
+ grid-template-columns: 250px minmax(0, 820px);
+ }
+
+ .doc-toc {
+ display: none;
+ }
+}
+
+@media (max-width: 760px) {
+ .doc-site-header {
+ height: 62px;
+ padding: 0 18px;
+ grid-template-columns: 1fr auto;
+ }
+
+ .doc-site-header__brand small,
+ .doc-site-header__marker,
+ .doc-site-header__actions a:first-child {
+ display: none;
+ }
+
+ .doc-shell {
+ display: block;
+ }
+
+ .doc-sidebar {
+ position: static;
+ height: auto;
+ max-height: 44vh;
+ padding: 24px 20px 28px;
+ border-right: 0;
+ border-bottom: 1px solid var(--ink);
+ }
+
+ .doc-sidebar__eyebrow {
+ margin-bottom: 12px;
+ }
+
+ .doc-sidebar nav {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 0 22px;
+ }
+
+ .doc-main {
+ padding: 46px 22px 78px;
+ }
+
+ .doc-main__meta {
+ margin-bottom: 32px;
+ }
+
+ .doc-content h1 {
+ font-size: clamp(38px, 12vw, 56px);
+ }
+
+ .doc-content h2 {
+ margin-top: 58px;
+ }
+
+ .doc-content pre {
+ margin-right: -10px;
+ margin-left: -10px;
+ box-shadow: 4px 5px 0 var(--blue);
+ }
+
+ .doc-content img {
+ box-shadow: 5px 5px 0 rgba(31, 31, 29, 0.14);
+ }
+
+ .doc-pager {
+ grid-template-columns: 1fr;
+ }
+}
+
+@media (max-width: 480px) {
+ .doc-sidebar nav {
+ grid-template-columns: 1fr;
+ }
+
+ .doc-sidebar {
+ max-height: 34vh;
+ }
+
+ .doc-main__meta {
+ align-items: flex-start;
+ gap: 16px;
+ flex-direction: column;
+ }
+}
diff --git a/site/src/main.jsx b/site/src/main.jsx
new file mode 100644
index 00000000..b3010648
--- /dev/null
+++ b/site/src/main.jsx
@@ -0,0 +1,10 @@
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import App from './App'
+import './styles/site.css'
+
+createRoot(document.getElementById('root')).render(
+
+
+ ,
+)
diff --git a/site/src/pages/home/HomePage.jsx b/site/src/pages/home/HomePage.jsx
new file mode 100644
index 00000000..ab976a4e
--- /dev/null
+++ b/site/src/pages/home/HomePage.jsx
@@ -0,0 +1,456 @@
+import { useEffect, useRef, useState } from 'react'
+import Icon from '../../components/Icon'
+import appIcon from '../../../../docs/images/app-icon.png'
+import mainSession from '../../../../docs/images/desktop_ui/25_main_session.png'
+import petSettings from '../../../../docs/images/desktop_ui/14_pet_settings_overview.png'
+import computerUse from '../../../../docs/images/desktop_ui/06_settings_computer_use.png'
+import scheduledTask from '../../../../docs/images/desktop_ui/20_scheduled_task.png'
+import skillMarketplace from '../../../../docs/images/desktop_ui/21_skill_marketplace.png'
+import diffReview from '../../../../docs/images/desktop_ui/23_workspace_diff_review.png'
+import browserPreview from '../../../../docs/images/desktop_ui/24_browser_preview.png'
+import dada from '../../../../desktop/src/assets/agent-mascots/agent-mascot-code.png'
+import huhu from '../../../../desktop/src/assets/agent-mascots/agent-mascot-plan.png'
+import bubu from '../../../../desktop/src/assets/agent-mascots/agent-mascot-fix.png'
+import huihui from '../../../../desktop/src/assets/agent-mascots/agent-mascot-build.png'
+
+const DOWNLOAD_URL = 'https://github.com/NanmiCoder/cc-haha/releases/latest'
+const GITHUB_URL = 'https://github.com/NanmiCoder/cc-haha'
+
+const content = {
+ zh: {
+ nav: ['产品旅程', '真实界面', '认识搭档', '文档'],
+ download: '下载桌面端',
+ eyebrow: 'CLAUDE CODE · 现在有了一间工作室',
+ titleA: '把终端智能,',
+ titleB: '带进一间会协作的工作室。',
+ intro: 'Claude Code Haha 把主会话、代码改动、Agent、桌面宠物、远程入口与自动任务收进同一个本地优先的桌面应用。',
+ primary: '下载 macOS / Windows / Linux',
+ secondary: '从快速上手开始',
+ local: '本地优先',
+ providers: '多模型',
+ remote: '跨设备',
+ heroNote: '主会话在这里。右侧工作台只是需要时展开的工具。',
+ orbit: ['会话', '代码', 'Agent', '设备'],
+ journeyEyebrow: 'ONE SESSION, MANY HANDS',
+ journeyTitle: '从一句话开始,工作自然展开。',
+ journeyIntro: '不是把十几个工具摆成菜单,而是让它们围绕同一条会话轨迹工作。',
+ steps: [
+ ['01', '说清目标', '在 Main Session 里描述任务,选择项目、模型与权限模式。'],
+ ['02', '组织协作', '用 Task、SubAgent 与 Agent Team 拆开复杂工作,活动面板持续汇总进度。'],
+ ['03', '看见改变', '代码、Diff、Worktree 和预览都留在同一上下文里,审完再决定是否落地。'],
+ ['04', '把手伸出去', 'Computer Use 操作真实桌面;H5 与 IM 让你离开电脑也能继续。'],
+ ['05', '让它按时回来', '把稳定流程交给定时任务,运行记录仍可回到对应会话复盘。'],
+ ],
+ tourEyebrow: 'REAL PRODUCT, NOT A MOCKUP',
+ tourTitle: '每一个入口,都有真实界面作证。',
+ tourIntro: '切换下面的工作片段。截图来自真实 Claude Code Haha 桌面应用,不用概念图替代产品。',
+ tours: [
+ { id: 'session', label: 'Main Session', kicker: '工作发生的地方', title: '主会话是中心,不是右侧工作台。', body: '项目、对话、输入、模型与上下文在中间完成闭环;代码工作台只在你需要审查改动时出现。', image: mainSession },
+ { id: 'pets', label: '桌面宠物', kicker: '协作也可以有性格', title: '搭搭、弧弧、补补、回回,会跟着任务状态行动。', body: '选择内置伙伴,或用一张角色图生成自己的宠物。它们不只是装饰,也把 Agent 的状态变得一眼可见。', image: petSettings },
+ { id: 'computer', label: 'Computer Use', kicker: '从代码走向真实桌面', title: '让 Agent 看见屏幕、点击、输入与验证。', body: '在设置中完成安装、权限和预授权;敏感动作仍由你决定边界。', image: computerUse },
+ { id: 'schedule', label: '定时任务', kicker: '让流程按时回来', title: '本地调度,运行记录可追溯。', body: '设置频率、模型、目录与通知;桌面应用保持运行时,任务会在独立会话里执行。', image: scheduledTask },
+ { id: 'skills', label: 'Skills & Agents', kicker: '能力可以继续生长', title: '从技能市场发现工作流,也能管理自己的 Agent。', body: '查看来源、安全提示和安装状态;再为 Agent 单独配置模型、工具与系统提示词。', image: skillMarketplace },
+ { id: 'diff', label: '代码审阅', kicker: '改变先被看见', title: 'Changed Files、Diff 与 Worktree 都有明确位置。', body: '逐文件理解改动,保留分支隔离,再由你决定下一步。', image: diffReview },
+ { id: 'preview', label: '浏览器预览', kicker: '验证不必离开会话', title: '在工作台里打开安全的网页预览。', body: '预览本地或公开页面,把看见的结果继续带回当前任务;远程 H5 与 IM 则负责离开电脑后的连接。', image: browserPreview },
+ ],
+ petEyebrow: 'MEET THE CREW',
+ petTitle: '四位搭档,四种工作节奏。',
+ petIntro: '我们不想再用一枚冷冰冰的图标代表所有状态。于是,Claude Code Haha 有了自己的小队。',
+ pets: [
+ ['搭搭 Dada', '搭建', '把想法一块块变成可运行的东西。', dada, '#2eaa91'],
+ ['弧弧 Huhu', '规划', '复杂任务也能画出一条清楚路线。', huhu, '#3577d4'],
+ ['补补 Bubu', '修复', '找到裂缝,验证之后再补好它。', bubu, '#e56645'],
+ ['回回 Huihui', '构建', '新回复一到,就抱着齿轮继续跑。', huihui, '#7657c8'],
+ ],
+ docsEyebrow: 'THE FIELD GUIDE',
+ docsTitle: '不只告诉你“有什么”,还告诉你“怎么走”。',
+ docsIntro: '文档被重新组织成真实任务路径。先完成一次可用体验,再深入架构、边界与维护。',
+ docGroups: [
+ ['第一次打开', '从安装、模型配置到完成第一段会话。', '/desktop/01-quick-start', '打开快速上手'],
+ ['桌面工作台', '主会话、活动面板、代码审阅、宠物与自动任务。', '/desktop/03-features', '认识桌面功能'],
+ ['伸向真实世界', 'Computer Use、H5 和 IM 接入的操作与安全边界。', '/features/computer-use', '配置 Computer Use'],
+ ['理解它如何工作', '本地服务、Agent、记忆、Skills 与项目结构。', '/reference/project-structure', '进入开发者参考'],
+ ],
+ installEyebrow: 'READY WHEN YOU ARE',
+ installTitle: '先把它带回桌面。',
+ installBody: '下载安装包,打开应用,选择项目。你的第一条 Main Session 从这里开始。',
+ copy: '复制',
+ copied: '已复制',
+ footer: '本地优先的 Claude Code 桌面工作室',
+ },
+ en: {
+ nav: ['Journey', 'Real UI', 'Meet the crew', 'Docs'],
+ download: 'Download desktop',
+ eyebrow: 'CLAUDE CODE · NOW HAS A STUDIO',
+ titleA: 'Bring terminal intelligence',
+ titleB: 'into a studio that collaborates.',
+ intro: 'Claude Code Haha brings the main session, code changes, agents, desktop pets, remote access, and scheduled work into one local-first desktop app.',
+ primary: 'Download for macOS / Windows / Linux',
+ secondary: 'Start with the guide',
+ local: 'Local-first',
+ providers: 'Multi-model',
+ remote: 'Cross-device',
+ heroNote: 'This is the Main Session. The right workbench opens only when you need it.',
+ orbit: ['Session', 'Code', 'Agents', 'Devices'],
+ journeyEyebrow: 'ONE SESSION, MANY HANDS',
+ journeyTitle: 'Start with one sentence. Let the work unfold.',
+ journeyIntro: 'Not a menu of disconnected tools—a single session thread where every capability has a role.',
+ steps: [
+ ['01', 'State the goal', 'Describe the task in Main Session, then choose your project, model, and permission mode.'],
+ ['02', 'Organize the crew', 'Use tasks, subagents, and agent teams while the activity panel keeps progress together.'],
+ ['03', 'See the change', 'Code, diffs, worktrees, and previews stay in context until you decide what lands.'],
+ ['04', 'Reach outward', 'Computer Use works with your desktop; H5 and IM keep the session reachable away from it.'],
+ ['05', 'Bring it back on time', 'Schedule stable routines and return to their session history when you need the evidence.'],
+ ],
+ tourEyebrow: 'REAL PRODUCT, NOT A MOCKUP',
+ tourTitle: 'Every doorway is backed by a real screen.',
+ tourIntro: 'Switch between actual workflows captured in Claude Code Haha—not conceptual placeholder art.',
+ tours: [
+ { id: 'session', label: 'Main Session', kicker: 'Where the work happens', title: 'The Main Session is the center—not the right workbench.', body: 'Project, conversation, composer, model, and context form the core loop. The code workbench appears only when review needs it.', image: mainSession },
+ { id: 'pets', label: 'Desktop pets', kicker: 'Collaboration with a pulse', title: 'Dada, Huhu, Bubu, and Huihui move with the work.', body: 'Choose a built-in companion or create your own from a character image. They make agent state visible at a glance.', image: petSettings },
+ { id: 'computer', label: 'Computer Use', kicker: 'Beyond the codebase', title: 'Let an agent see, click, type, and verify on the real desktop.', body: 'Install and authorize it in Settings, while keeping sensitive action boundaries in your hands.', image: computerUse },
+ { id: 'schedule', label: 'Scheduled work', kicker: 'Work that comes back', title: 'Local scheduling with a traceable run history.', body: 'Choose a cadence, model, directory, and notification. Runs happen while the desktop app stays open.', image: scheduledTask },
+ { id: 'skills', label: 'Skills & Agents', kicker: 'A studio that can grow', title: 'Discover workflows in the skill market and shape your own agents.', body: 'Review sources, safety notes, and install state, then give each agent its own model, tools, and system prompt.', image: skillMarketplace },
+ { id: 'diff', label: 'Code review', kicker: 'See before you land', title: 'Changed files, diffs, and worktrees each have a clear place.', body: 'Understand changes file by file, keep branch isolation, and decide the next move yourself.', image: diffReview },
+ { id: 'preview', label: 'Browser preview', kicker: 'Verify without leaving the session', title: 'Open a bounded web preview inside the workbench.', body: 'Preview a local or public page and carry what you see back into the task. H5 and IM keep it reachable away from the desktop.', image: browserPreview },
+ ],
+ petEyebrow: 'MEET THE CREW',
+ petTitle: 'Four companions. Four working rhythms.',
+ petIntro: 'One cold status icon should not represent every kind of work. Claude Code Haha has a crew instead.',
+ pets: [
+ ['Dada', 'Build', 'Turns an idea into something you can run.', dada, '#2eaa91'],
+ ['Huhu', 'Plan', 'Finds a clear route through complicated work.', huhu, '#3577d4'],
+ ['Bubu', 'Fix', 'Finds the crack, proves it, then patches it.', bubu, '#e56645'],
+ ['Huihui', 'Ship', 'Grabs the gear and moves when a reply arrives.', huihui, '#7657c8'],
+ ],
+ docsEyebrow: 'THE FIELD GUIDE',
+ docsTitle: 'Not only what exists—how to get somewhere.',
+ docsIntro: 'The guide follows real user journeys: get to a useful result first, then go deeper into architecture and boundaries.',
+ docGroups: [
+ ['Your first launch', 'Install, choose a model, and finish your first session.', '/en/desktop/01-quick-start', 'Open quick start'],
+ ['The desktop studio', 'Main Session, activity, review, pets, and scheduled work.', '/en/desktop/03-features', 'Explore desktop features'],
+ ['Reach the real world', 'Computer Use, browser access, IM, and their safety boundaries.', '/en/features/computer-use', 'Set up Computer Use'],
+ ['Understand the system', 'Local services, agents, memory, skills, and project structure.', '/en/reference/project-structure', 'Open developer reference'],
+ ],
+ installEyebrow: 'READY WHEN YOU ARE',
+ installTitle: 'Bring it to your desktop.',
+ installBody: 'Install the app, choose a project, and start your first Main Session.',
+ copy: 'Copy',
+ copied: 'Copied',
+ footer: 'A local-first desktop studio for Claude Code',
+ },
+}
+
+function useReveal() {
+ const scope = useRef(null)
+
+ useEffect(() => {
+ const root = scope.current
+ if (!root) return
+
+ const observer = new IntersectionObserver((entries) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) {
+ entry.target.dataset.visible = 'true'
+ observer.unobserve(entry.target)
+ }
+ })
+ }, { threshold: 0.14 })
+
+ root.querySelectorAll('[data-reveal]').forEach((node) => observer.observe(node))
+ return () => observer.disconnect()
+ }, [])
+
+ return scope
+}
+
+function Header({ locale, c }) {
+ const [open, setOpen] = useState(false)
+ const prefix = locale === 'en' ? '/en' : ''
+ const ids = ['journey', 'tour', 'crew', 'guide']
+ const closeMenu = () => setOpen(false)
+
+ return (
+
+ )
+}
+
+function Hero({ locale, c }) {
+ return (
+
+
+
+
{c.eyebrow}
+
{c.titleA} {c.titleB}
+
{c.intro}
+
+
+ {[c.local, c.providers, c.remote].map((item) => {item} )}
+
+
+
+
+
{c.orbit[0]}
+
{c.orbit[1]}
+
{c.orbit[2]}
+
{c.orbit[3]}
+
+
+
+ MAIN SESSION
+ LOCAL · CONNECTED
+
+
+
+
+
+
{c.heroNote}
+
+
+
+
MAIN SESSION · CODE REVIEW · DESKTOP PETS · COMPUTER USE · REMOTE ACCESS · SCHEDULED WORK · MAIN SESSION · CODE REVIEW · DESKTOP PETS · COMPUTER USE · REMOTE ACCESS · SCHEDULED WORK ·
+
+
+ )
+}
+
+function Journey({ c }) {
+ return (
+
+
+
{c.journeyEyebrow}
+
{c.journeyTitle}
+
{c.journeyIntro}
+
+
+ {c.steps.map(([number, title, body], index) => (
+
+ {number}
+
+
+ {index === 0 ? : {['⌘', '∆', '↗', '↻'][index - 1]} }
+
+
+ ))}
+
+
+ )
+}
+
+function ProductTour({ c }) {
+ const [activeId, setActiveId] = useState(c.tours[0].id)
+ const active = c.tours.find((tour) => tour.id === activeId) || c.tours[0]
+
+ useEffect(() => setActiveId(c.tours[0].id), [c])
+
+ const selectAdjacentTab = (event, currentIndex) => {
+ if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return
+
+ event.preventDefault()
+ let nextIndex = currentIndex
+ if (event.key === 'Home') nextIndex = 0
+ if (event.key === 'End') nextIndex = c.tours.length - 1
+ if (event.key === 'ArrowLeft') nextIndex = (currentIndex - 1 + c.tours.length) % c.tours.length
+ if (event.key === 'ArrowRight') nextIndex = (currentIndex + 1) % c.tours.length
+
+ const nextTour = c.tours[nextIndex]
+ setActiveId(nextTour.id)
+ event.currentTarget.parentElement
+ ?.querySelector(`#tour-tab-${nextTour.id}`)
+ ?.focus()
+ }
+
+ return (
+
+
+
+
{c.tourEyebrow}
+
{c.tourTitle}
+
+
{c.tourIntro}
+
+
+ {c.tours.map((tour, index) => (
+ setActiveId(tour.id)}
+ onKeyDown={(event) => selectAdjacentTab(event, index)}
+ role="tab"
+ tabIndex={active.id === tour.id ? 0 : -1}
+ type="button"
+ >
+ 0{c.tours.indexOf(tour) + 1} {tour.label}
+
+ ))}
+
+
+
+
{active.kicker}
+
{active.title}
+
{active.body}
+
LOOK HERE →
+
+
+
+
+
+
+
+
+ )
+}
+
+function Crew({ c }) {
+ return (
+
+
+
{c.petEyebrow}
+
{c.petTitle}
+
{c.petIntro}
+
+
+ {c.pets.map(([name, role, body, image, color], index) => (
+
+ 0{index + 1}
+
+ {role}
+ {name}
+ {body}
+
+ ))}
+
+
+ )
+}
+
+function Guide({ c }) {
+ return (
+
+
+
{c.docsEyebrow}
+
{c.docsTitle}
+
{c.docsIntro}
+
+
+
+ )
+}
+
+function Install({ locale, c }) {
+ const [copied, setCopied] = useState(false)
+ const command = [
+ 'git clone https://github.com/NanmiCoder/cc-haha.git',
+ 'cd cc-haha && bun install',
+ './bin/claude-haha',
+ ].join('\n')
+
+ const copyCommand = async () => {
+ await navigator.clipboard.writeText(command)
+ setCopied(true)
+ window.setTimeout(() => setCopied(false), 1600)
+ }
+
+ return (
+
+ HA!
+
+
{c.installEyebrow}
+
{c.installTitle}
+
{c.installBody}
+
+
+
+
CLI · SOURCE CHECKOUT
+
+ $ git clone https://github.com/NanmiCoder/cc-haha.git
+ $ cd cc-haha && bun install
+ $ ./bin/claude-haha
+
+
{copied ? c.copied : c.copy}
+
Desktop + CLI · same local runtime
+
+
+ )
+}
+
+function Footer({ locale, c }) {
+ return (
+
+ Claude Code Haha
+ {c.footer}
+
+
+ )
+}
+
+export default function HomePage({ locale = 'zh' }) {
+ const c = content[locale]
+ const pageRef = useReveal()
+
+ useEffect(() => {
+ document.documentElement.lang = locale === 'en' ? 'en' : 'zh-CN'
+ document.title = locale === 'en'
+ ? 'Claude Code Haha — A studio for Claude Code'
+ : 'Claude Code Haha — Claude Code 的桌面工作室'
+ }, [locale])
+
+ return (
+
+ )
+}
diff --git a/site/src/styles/site.css b/site/src/styles/site.css
new file mode 100644
index 00000000..f40c1f87
--- /dev/null
+++ b/site/src/styles/site.css
@@ -0,0 +1,1315 @@
+@import url('https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=DM+Sans:opsz,wght@9..40,400;9..40,500;9..40,600;9..40,700&family=Noto+Sans+SC:wght@400;500;600;700;800&display=swap');
+
+:root {
+ color-scheme: light;
+ font-family: 'DM Sans', 'Noto Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
+ --paper: #f4f0e7;
+ --paper-bright: #fffdf7;
+ --ink: #171915;
+ --ink-soft: #62645d;
+ --line: rgba(23, 25, 21, 0.2);
+ --line-strong: rgba(23, 25, 21, 0.62);
+ --blue: #2457e6;
+ --blue-dark: #1539ad;
+ --coral: #ff6542;
+ --green: #16a07d;
+ --yellow: #f5c84c;
+ --shadow: 0 24px 80px rgba(35, 34, 28, 0.14);
+ --radius: 8px;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+html {
+ scroll-behavior: smooth;
+ scroll-padding-top: 90px;
+}
+
+body {
+ margin: 0;
+ min-width: 320px;
+ color: var(--ink);
+ background: var(--paper);
+}
+
+button,
+a {
+ font: inherit;
+}
+
+a {
+ color: inherit;
+ text-decoration: none;
+}
+
+button {
+ color: inherit;
+}
+
+img {
+ display: block;
+ max-width: 100%;
+}
+
+::selection {
+ color: #fff;
+ background: var(--blue);
+}
+
+.page-loading,
+.not-found {
+ display: grid;
+ min-height: 100vh;
+ place-content: center;
+ padding: 32px;
+ text-align: center;
+}
+
+.page-loading,
+.not-found > span {
+ font-family: 'DM Mono', monospace;
+ font-size: 11px;
+ letter-spacing: 0.08em;
+}
+
+.not-found h1 {
+ max-width: 760px;
+ margin: 22px 0 12px;
+ font-size: clamp(48px, 8vw, 96px);
+ line-height: 0.98;
+ letter-spacing: -0.055em;
+}
+
+.not-found p {
+ color: var(--ink-soft);
+}
+
+.not-found a {
+ width: fit-content;
+ margin: 28px auto 0;
+ padding: 12px 16px;
+ color: #fff;
+ border: 1px solid var(--ink);
+ background: var(--blue);
+ box-shadow: 4px 4px 0 var(--ink);
+}
+
+.skip-link {
+ position: fixed;
+ z-index: 999;
+ top: 12px;
+ left: 12px;
+ padding: 10px 14px;
+ color: #fff;
+ background: var(--ink);
+ transform: translateY(-160%);
+}
+
+.skip-link:focus {
+ transform: translateY(0);
+}
+
+.site-header {
+ position: sticky;
+ z-index: 100;
+ top: 0;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ height: 72px;
+ padding: 0 clamp(24px, 4vw, 68px);
+ border-bottom: 1px solid var(--line);
+ background: rgba(244, 240, 231, 0.88);
+ backdrop-filter: blur(18px);
+}
+
+.brand {
+ display: inline-flex;
+ align-items: center;
+ gap: 10px;
+ font-size: 17px;
+ font-weight: 600;
+ letter-spacing: -0.02em;
+}
+
+.brand b {
+ color: var(--coral);
+ font-weight: 800;
+}
+
+.brand-mark {
+ display: grid;
+ width: 36px;
+ height: 36px;
+ place-items: center;
+ border: 1px solid var(--ink);
+ border-radius: 50%;
+ background: var(--paper-bright);
+ box-shadow: 3px 3px 0 var(--ink);
+}
+
+.brand-mark img {
+ width: 25px;
+ height: 25px;
+ object-fit: contain;
+}
+
+.header-nav {
+ display: flex;
+ align-items: center;
+ gap: 28px;
+ font-family: 'DM Mono', 'Noto Sans SC', monospace;
+ font-size: 12px;
+ font-weight: 500;
+}
+
+.header-nav > a:not(.nav-download) {
+ position: relative;
+}
+
+.header-nav > a:not(.nav-download)::after {
+ position: absolute;
+ right: 0;
+ bottom: -6px;
+ left: 0;
+ height: 2px;
+ background: var(--coral);
+ content: '';
+ transform: scaleX(0);
+ transform-origin: right;
+ transition: transform 180ms ease;
+}
+
+.header-nav > a:hover::after {
+ transform: scaleX(1);
+ transform-origin: left;
+}
+
+.nav-download {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 10px 14px;
+ color: white;
+ border: 1px solid var(--ink);
+ background: var(--blue);
+ box-shadow: 3px 3px 0 var(--ink);
+ transition: transform 160ms ease, box-shadow 160ms ease;
+}
+
+.nav-download:hover {
+ box-shadow: 1px 1px 0 var(--ink);
+ transform: translate(2px, 2px);
+}
+
+.menu-button {
+ display: none;
+ padding: 6px;
+ border: 0;
+ background: transparent;
+}
+
+.hero {
+ position: relative;
+ display: grid;
+ min-height: 870px;
+ grid-template-columns: minmax(0, 0.86fr) minmax(520px, 1.14fr);
+ overflow: hidden;
+ border-bottom: 1px solid var(--line-strong);
+}
+
+.hero-grid {
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ background-image:
+ linear-gradient(var(--line) 1px, transparent 1px),
+ linear-gradient(90deg, var(--line) 1px, transparent 1px);
+ background-size: 44px 44px;
+ mask-image: linear-gradient(to right, black, transparent 62%);
+ opacity: 0.38;
+}
+
+.hero-copy {
+ position: relative;
+ z-index: 2;
+ align-self: start;
+ max-width: 760px;
+ padding: 92px 5vw 120px 6vw;
+}
+
+.eyebrow {
+ display: flex;
+ align-items: center;
+ gap: 9px;
+ margin-bottom: 24px;
+ font-family: 'DM Mono', 'Noto Sans SC', monospace;
+ font-size: 12px;
+ font-weight: 500;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.status-dot {
+ width: 9px;
+ height: 9px;
+ border-radius: 50%;
+ background: var(--green);
+ box-shadow: 0 0 0 4px rgba(22, 160, 125, 0.14);
+ animation: pulse 2.4s ease-out infinite;
+}
+
+@keyframes pulse {
+ 50% { box-shadow: 0 0 0 8px rgba(22, 160, 125, 0); }
+}
+
+.hero h1,
+.section h2,
+.install h2 {
+ margin: 0;
+ font-weight: 700;
+ letter-spacing: -0.055em;
+ line-height: 0.99;
+}
+
+.hero h1 {
+ max-width: 780px;
+ font-size: clamp(48px, 5.35vw, 82px);
+}
+
+.hero h1 span,
+.hero h1 em {
+ display: block;
+}
+
+.hero h1 em {
+ position: relative;
+ width: fit-content;
+ margin-top: 10px;
+ color: var(--blue);
+ font-family: Georgia, 'Noto Serif SC', serif;
+ font-weight: 600;
+}
+
+.hero h1 em::after {
+ position: absolute;
+ z-index: -1;
+ right: -8px;
+ bottom: -4px;
+ left: -5px;
+ height: 13px;
+ background: var(--yellow);
+ content: '';
+ opacity: 0.78;
+ transform: rotate(-1deg);
+}
+
+.hero-intro {
+ max-width: 650px;
+ margin: 32px 0 0;
+ color: #474942;
+ font-size: clamp(17px, 1.25vw, 20px);
+ line-height: 1.75;
+}
+
+.hero-actions,
+.install-actions {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 14px;
+ margin-top: 36px;
+}
+
+.button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 10px;
+ min-height: 50px;
+ padding: 0 20px;
+ border: 1px solid var(--ink);
+ font-weight: 700;
+ transition: transform 160ms ease, box-shadow 160ms ease;
+}
+
+.button-primary {
+ color: #fff;
+ background: var(--blue);
+ box-shadow: 5px 5px 0 var(--ink);
+}
+
+.button-paper {
+ background: var(--paper-bright);
+ box-shadow: 5px 5px 0 var(--coral);
+}
+
+.button:hover {
+ box-shadow: 2px 2px 0 var(--ink);
+ transform: translate(3px, 3px);
+}
+
+.button-paper:hover {
+ box-shadow: 2px 2px 0 var(--coral);
+}
+
+.hero-trust {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 20px;
+ margin-top: 30px;
+ color: var(--ink-soft);
+ font-family: 'DM Mono', 'Noto Sans SC', monospace;
+ font-size: 11px;
+}
+
+.hero-trust span {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+}
+
+.hero-stage {
+ position: relative;
+ display: grid;
+ align-items: center;
+ min-height: 870px;
+ padding: 80px 5vw 130px 1.5vw;
+ background: var(--blue);
+}
+
+.hero-stage::before {
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ background:
+ radial-gradient(circle at 15% 20%, rgba(255, 255, 255, 0.16) 0 2px, transparent 3px),
+ linear-gradient(rgba(255, 255, 255, 0.1) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(255, 255, 255, 0.1) 1px, transparent 1px);
+ background-size: auto, 48px 48px, 48px 48px;
+ content: '';
+}
+
+.stage-thread {
+ position: absolute;
+ z-index: 1;
+ inset: 13% 6% 12% 5%;
+ border: 2px dashed rgba(255, 255, 255, 0.62);
+ border-radius: 48% 45% 51% 43%;
+ transform: rotate(-4deg);
+}
+
+.window-card {
+ position: relative;
+ z-index: 3;
+ width: min(860px, 95%);
+ margin: auto;
+ overflow: hidden;
+ border: 2px solid var(--ink);
+ background: var(--paper-bright);
+ box-shadow: 16px 18px 0 rgba(20, 22, 18, 0.92);
+ transform: rotate(1.4deg);
+}
+
+.window-bar {
+ display: grid;
+ align-items: center;
+ height: 38px;
+ padding: 0 12px;
+ grid-template-columns: 1fr auto 1fr;
+ border-bottom: 1px solid var(--ink);
+ background: #ebe7dc;
+ font-family: 'DM Mono', monospace;
+ font-size: 9px;
+ letter-spacing: 0.08em;
+}
+
+.window-bar > span {
+ display: flex;
+ gap: 5px;
+}
+
+.window-bar i {
+ width: 8px;
+ height: 8px;
+ border: 1px solid var(--ink);
+ border-radius: 50%;
+}
+
+.window-bar i:first-child { background: var(--coral); }
+.window-bar i:nth-child(2) { background: var(--yellow); }
+.window-bar i:nth-child(3) { background: var(--green); }
+.window-bar small { justify-self: end; color: #61635d; font-size: 8px; }
+
+.window-card > img {
+ width: 100%;
+ aspect-ratio: 1123 / 768;
+ object-fit: cover;
+}
+
+.mascot-note {
+ position: absolute;
+ z-index: 5;
+ right: 4%;
+ bottom: 74px;
+ display: flex;
+ align-items: center;
+ width: min(360px, 48%);
+ padding: 11px 14px 11px 4px;
+ border: 2px solid var(--ink);
+ background: var(--yellow);
+ box-shadow: 8px 8px 0 var(--ink);
+ transform: rotate(-2.5deg);
+}
+
+.mascot-note::before {
+ position: absolute;
+ top: -10px;
+ right: 38px;
+ width: 70px;
+ height: 18px;
+ background: rgba(255, 253, 247, 0.75);
+ content: '';
+ transform: rotate(4deg);
+}
+
+.mascot-note img {
+ width: 76px;
+ margin-top: -32px;
+}
+
+.mascot-note p {
+ margin: 0;
+ font-size: 12px;
+ font-weight: 700;
+ line-height: 1.5;
+}
+
+.stage-label {
+ position: absolute;
+ z-index: 2;
+ padding: 6px 9px;
+ color: var(--ink);
+ border: 1px solid var(--ink);
+ background: var(--paper-bright);
+ box-shadow: 3px 3px 0 var(--ink);
+ font-family: 'DM Mono', monospace;
+ font-size: 10px;
+ font-weight: 500;
+ text-transform: uppercase;
+}
+
+.stage-label-a { top: 12%; left: 8%; transform: rotate(-5deg); }
+.stage-label-b { top: 13%; right: 8%; transform: rotate(4deg); }
+.stage-label-c { bottom: 13%; left: 7%; transform: rotate(3deg); }
+.stage-label-d { right: 8%; bottom: 12%; transform: rotate(-4deg); }
+
+.hero-marquee {
+ position: absolute;
+ z-index: 8;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ overflow: hidden;
+ padding: 13px 0;
+ color: #fff;
+ border-top: 1px solid var(--ink);
+ background: var(--coral);
+ font-family: 'DM Mono', monospace;
+ font-size: 11px;
+ letter-spacing: 0.08em;
+ white-space: nowrap;
+}
+
+.hero-marquee div {
+ width: max-content;
+ animation: marquee 34s linear infinite;
+}
+
+@keyframes marquee {
+ to { transform: translateX(-50%); }
+}
+
+.section {
+ padding: 120px clamp(24px, 6vw, 110px);
+}
+
+.section-heading {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) minmax(300px, 0.48fr);
+ align-items: end;
+ gap: 8vw;
+ margin-bottom: 72px;
+}
+
+.section-heading .eyebrow,
+.tour-heading .eyebrow,
+.crew-intro .eyebrow,
+.guide-title .eyebrow,
+.install-copy .eyebrow {
+ color: var(--blue);
+}
+
+.section h2,
+.install h2 {
+ max-width: 920px;
+ font-size: clamp(42px, 5.2vw, 76px);
+}
+
+.section-heading > p,
+.tour-heading > p,
+.crew-intro > p,
+.guide-title > p {
+ margin: 0;
+ color: var(--ink-soft);
+ font-size: 17px;
+ line-height: 1.75;
+}
+
+.journey {
+ position: relative;
+ background: var(--paper-bright);
+}
+
+.journey::before {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 37%;
+ width: 1px;
+ background: var(--line);
+ content: '';
+}
+
+.journey-list {
+ max-width: 1180px;
+ padding: 0;
+ margin: 0 auto;
+ list-style: none;
+}
+
+.journey-list li {
+ position: relative;
+ display: grid;
+ min-height: 170px;
+ grid-template-columns: 100px minmax(0, 1fr) 86px;
+ align-items: center;
+ gap: 30px;
+ border-top: 1px solid var(--line-strong);
+}
+
+.journey-list li:last-child {
+ border-bottom: 1px solid var(--line-strong);
+}
+
+.step-number {
+ font-family: 'DM Mono', monospace;
+ font-size: 13px;
+}
+
+.step-copy {
+ display: grid;
+ grid-template-columns: minmax(180px, 0.6fr) minmax(300px, 1fr);
+ align-items: center;
+ gap: 48px;
+}
+
+.step-copy h3 {
+ margin: 0;
+ font-size: clamp(24px, 2.2vw, 34px);
+ letter-spacing: -0.035em;
+}
+
+.step-copy p {
+ max-width: 580px;
+ margin: 0;
+ color: var(--ink-soft);
+ line-height: 1.7;
+}
+
+.step-mark {
+ display: grid;
+ width: 58px;
+ height: 58px;
+ place-items: center;
+ justify-self: end;
+ border: 1px solid var(--ink);
+ border-radius: 50%;
+ background: var(--paper);
+ font-family: 'DM Mono', monospace;
+ font-size: 21px;
+ transition: color 180ms ease, background 180ms ease, transform 180ms ease;
+}
+
+.journey-list li:hover .step-mark {
+ color: #fff;
+ background: var(--coral);
+ transform: rotate(8deg);
+}
+
+.product-tour {
+ color: #fff;
+ border-top: 1px solid var(--ink);
+ border-bottom: 1px solid var(--ink);
+ background: #1c201d;
+}
+
+.tour-heading {
+ display: grid;
+ grid-template-columns: minmax(0, 1.2fr) minmax(280px, 0.5fr);
+ align-items: end;
+ gap: 8vw;
+ margin-bottom: 58px;
+}
+
+.product-tour .eyebrow {
+ color: #89a8ff;
+}
+
+.tour-heading > p {
+ color: #b8bbb4;
+}
+
+.tour-tabs {
+ display: flex;
+ overflow-x: auto;
+ border-top: 1px solid rgba(255, 255, 255, 0.25);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.25);
+}
+
+.tour-tabs button {
+ display: flex;
+ flex: 1 0 auto;
+ align-items: center;
+ gap: 10px;
+ padding: 18px 22px;
+ color: #aeb1aa;
+ border: 0;
+ border-right: 1px solid rgba(255, 255, 255, 0.25);
+ background: transparent;
+ cursor: pointer;
+ font-weight: 600;
+}
+
+.tour-tabs button span {
+ font-family: 'DM Mono', monospace;
+ font-size: 10px;
+}
+
+.tour-tabs button.is-active {
+ color: var(--ink);
+ background: var(--yellow);
+}
+
+.tour-board {
+ display: grid;
+ min-height: 600px;
+ grid-template-columns: minmax(280px, 0.37fr) minmax(0, 1fr);
+ gap: 5vw;
+ align-items: center;
+ padding-top: 68px;
+}
+
+.tour-copy {
+ position: relative;
+ align-self: stretch;
+ padding: 42px 0;
+}
+
+.tour-kicker {
+ margin-bottom: 24px;
+ color: #89a8ff;
+ font-family: 'DM Mono', 'Noto Sans SC', monospace;
+ font-size: 11px;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.tour-copy h3 {
+ margin: 0;
+ font-size: clamp(32px, 3.2vw, 52px);
+ line-height: 1.08;
+ letter-spacing: -0.045em;
+}
+
+.tour-copy p {
+ margin: 26px 0 0;
+ color: #c6c8c3;
+ font-size: 16px;
+ line-height: 1.8;
+}
+
+.tour-scribble {
+ position: absolute;
+ bottom: 18px;
+ color: var(--coral);
+ font-family: 'DM Mono', monospace;
+ font-size: 11px;
+ transform: rotate(-7deg);
+}
+
+.tour-screen {
+ position: relative;
+ padding: 14px;
+ border: 1px solid #000;
+ background: var(--paper);
+ box-shadow: 18px 18px 0 var(--blue);
+ transform: rotate(0.6deg);
+}
+
+.tour-screen img {
+ width: 100%;
+ min-height: 420px;
+ max-height: 610px;
+ border: 1px solid rgba(0, 0, 0, 0.22);
+ object-fit: cover;
+ object-position: top;
+ animation: image-in 300ms ease;
+}
+
+@keyframes image-in {
+ from { opacity: 0; transform: translateY(6px); }
+}
+
+.screen-tape {
+ position: absolute;
+ z-index: 2;
+ width: 85px;
+ height: 22px;
+ background: rgba(245, 200, 76, 0.84);
+}
+
+.screen-tape-one { top: -8px; left: 10%; transform: rotate(-4deg); }
+.screen-tape-two { right: 8%; bottom: -5px; transform: rotate(5deg); }
+
+.crew {
+ position: relative;
+ overflow: hidden;
+ background:
+ radial-gradient(circle at 72% 22%, rgba(255, 101, 66, 0.12), transparent 26%),
+ var(--paper);
+}
+
+.crew::after {
+ position: absolute;
+ top: 44px;
+ right: -44px;
+ width: 180px;
+ height: 180px;
+ border: 28px solid var(--blue);
+ border-radius: 50%;
+ content: '';
+ opacity: 0.9;
+}
+
+.crew-intro {
+ position: relative;
+ z-index: 2;
+ max-width: 820px;
+}
+
+.crew-intro > p {
+ max-width: 640px;
+ margin-top: 26px;
+}
+
+.crew-track {
+ position: relative;
+ z-index: 2;
+ display: grid;
+ max-width: 1260px;
+ margin: 88px auto 0;
+ grid-template-columns: repeat(4, 1fr);
+ border-top: 1px solid var(--ink);
+ border-bottom: 1px solid var(--ink);
+}
+
+.crew-track::before {
+ position: absolute;
+ top: 126px;
+ right: 6%;
+ left: 6%;
+ height: 2px;
+ background: var(--ink);
+ content: '';
+}
+
+.crew-member {
+ position: relative;
+ min-height: 480px;
+ padding: 22px 24px 34px;
+ border-right: 1px solid var(--ink);
+ transition: color 180ms ease, background 180ms ease;
+}
+
+.crew-member:last-child {
+ border-right: 0;
+}
+
+.crew-member:hover {
+ background: var(--pet-accent);
+}
+
+.crew-member:hover p,
+.crew-member:hover .crew-role {
+ color: #fff;
+}
+
+.crew-index {
+ font-family: 'DM Mono', monospace;
+ font-size: 11px;
+}
+
+.pet-portrait {
+ position: relative;
+ z-index: 2;
+ display: grid;
+ height: 230px;
+ place-items: center;
+}
+
+.pet-halo {
+ position: absolute;
+ z-index: -1;
+ width: 132px;
+ height: 132px;
+ border: 2px solid var(--ink);
+ border-radius: 50%;
+ background: var(--pet-accent);
+ box-shadow: 8px 8px 0 var(--ink);
+ transition: transform 220ms ease;
+}
+
+.crew-member:hover .pet-halo {
+ background: var(--paper-bright);
+ transform: rotate(10deg) scale(1.04);
+}
+
+.pet-portrait img {
+ width: 170px;
+ filter: drop-shadow(0 12px 10px rgba(0, 0, 0, 0.18));
+ transition: transform 220ms ease;
+}
+
+.crew-member:hover .pet-portrait img {
+ transform: translateY(-8px) rotate(-3deg);
+}
+
+.crew-role {
+ margin-top: 16px;
+ color: var(--pet-accent);
+ font-family: 'DM Mono', 'Noto Sans SC', monospace;
+ font-size: 11px;
+ font-weight: 700;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.crew-member h3 {
+ margin: 8px 0 12px;
+ font-size: 26px;
+ letter-spacing: -0.03em;
+}
+
+.crew-member p {
+ margin: 0;
+ color: var(--ink-soft);
+ line-height: 1.65;
+}
+
+.guide {
+ display: grid;
+ grid-template-columns: minmax(300px, 0.62fr) minmax(480px, 1fr);
+ gap: 9vw;
+ color: #fff;
+ background: var(--blue);
+}
+
+.guide-title {
+ position: sticky;
+ top: 120px;
+ align-self: start;
+}
+
+.guide-title .eyebrow {
+ color: var(--yellow);
+}
+
+.guide-title > p {
+ margin-top: 30px;
+ color: #c9d4fb;
+}
+
+.guide-stack {
+ border-top: 1px solid rgba(255, 255, 255, 0.5);
+}
+
+.guide-row {
+ display: grid;
+ min-height: 168px;
+ grid-template-columns: 52px minmax(0, 1fr) auto;
+ align-items: center;
+ gap: 24px;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.5);
+ transition: color 180ms ease, background 180ms ease, padding 180ms ease;
+}
+
+.guide-row:hover {
+ padding: 0 18px;
+ color: var(--ink);
+ background: var(--yellow);
+}
+
+.guide-number {
+ align-self: start;
+ padding-top: 38px;
+ font-family: 'DM Mono', monospace;
+ font-size: 11px;
+}
+
+.guide-row-copy {
+ display: grid;
+ gap: 10px;
+}
+
+.guide-row-copy strong {
+ font-size: 26px;
+ letter-spacing: -0.035em;
+}
+
+.guide-row-copy small {
+ max-width: 520px;
+ color: #ccd6f9;
+ font-size: 14px;
+ line-height: 1.6;
+}
+
+.guide-row:hover small {
+ color: #4f514b;
+}
+
+.guide-action {
+ display: flex;
+ align-items: center;
+ gap: 9px;
+ font-family: 'DM Mono', 'Noto Sans SC', monospace;
+ font-size: 11px;
+ white-space: nowrap;
+}
+
+.install {
+ position: relative;
+ display: grid;
+ min-height: 580px;
+ grid-template-columns: minmax(0, 1fr) minmax(380px, 0.72fr);
+ align-items: center;
+ gap: 8vw;
+ overflow: hidden;
+ background: var(--yellow);
+}
+
+.install::before {
+ position: absolute;
+ top: -150px;
+ left: -120px;
+ width: 420px;
+ height: 420px;
+ border: 72px solid var(--coral);
+ border-radius: 50%;
+ content: '';
+}
+
+.install-copy,
+.command-sheet {
+ position: relative;
+ z-index: 2;
+}
+
+.install-copy .eyebrow {
+ color: var(--blue);
+}
+
+.install-copy > p {
+ max-width: 640px;
+ margin: 28px 0 0;
+ font-size: 18px;
+ line-height: 1.7;
+}
+
+.button-ink {
+ color: #fff;
+ background: var(--ink);
+ box-shadow: 5px 5px 0 var(--blue);
+}
+
+.text-link {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ border-bottom: 1px solid var(--ink);
+ font-family: 'DM Mono', monospace;
+ font-size: 12px;
+ font-weight: 600;
+}
+
+.command-sheet {
+ padding: 38px;
+ border: 2px solid var(--ink);
+ background: var(--paper-bright);
+ box-shadow: 16px 16px 0 var(--ink);
+ transform: rotate(2deg);
+}
+
+.command-label {
+ margin-bottom: 28px;
+ font-family: 'DM Mono', monospace;
+ font-size: 10px;
+ letter-spacing: 0.1em;
+}
+
+.command-sheet code {
+ display: block;
+ overflow-x: auto;
+ padding: 22px;
+ color: #fff;
+ background: var(--ink);
+ font-family: 'DM Mono', monospace;
+ font-size: 13px;
+ white-space: nowrap;
+}
+
+.command-sheet code span {
+ color: #68dec2;
+}
+
+.command-sheet button {
+ display: flex;
+ align-items: center;
+ gap: 7px;
+ margin-top: 18px;
+ padding: 8px 0;
+ border: 0;
+ border-bottom: 1px solid var(--ink);
+ background: transparent;
+ cursor: pointer;
+ font-family: 'DM Mono', 'Noto Sans SC', monospace;
+ font-size: 11px;
+}
+
+.command-sheet p {
+ margin: 52px 0 0;
+ color: var(--ink-soft);
+ font-family: 'DM Mono', monospace;
+ font-size: 10px;
+}
+
+.install-mascot {
+ position: absolute;
+ right: 36%;
+ bottom: -24px;
+ z-index: 4;
+}
+
+.install-mascot img {
+ width: 128px;
+}
+
+.install-mascot span {
+ position: absolute;
+ top: -16px;
+ right: -20px;
+ padding: 6px 9px;
+ color: #fff;
+ border: 2px solid var(--ink);
+ background: var(--coral);
+ box-shadow: 3px 3px 0 var(--ink);
+ font-family: 'DM Mono', monospace;
+ font-weight: 700;
+ transform: rotate(8deg);
+}
+
+.footer {
+ display: grid;
+ min-height: 150px;
+ grid-template-columns: 1fr auto 1fr;
+ align-items: center;
+ gap: 28px;
+ padding: 36px clamp(24px, 6vw, 110px);
+ color: #fff;
+ background: var(--ink);
+}
+
+.footer .brand-mark {
+ border-color: #fff;
+ box-shadow: 3px 3px 0 var(--coral);
+}
+
+.footer > p {
+ color: #aeb1aa;
+ font-size: 12px;
+}
+
+.footer-links {
+ display: flex;
+ justify-self: end;
+ gap: 24px;
+ font-family: 'DM Mono', monospace;
+ font-size: 11px;
+}
+
+.footer-links a {
+ display: flex;
+ align-items: center;
+ gap: 7px;
+}
+
+[data-reveal] {
+ opacity: 0;
+ transform: translateY(20px);
+ transition: opacity 560ms ease, transform 560ms cubic-bezier(0.2, 0.8, 0.2, 1);
+}
+
+[data-reveal][data-visible='true'] {
+ opacity: 1;
+ transform: translateY(0);
+}
+
+@media (max-width: 1120px) {
+ .header-nav { gap: 16px; }
+ .header-nav > a:nth-child(-n+2) { display: none; }
+ .hero {
+ min-height: auto;
+ grid-template-columns: 1fr;
+ }
+ .hero-copy {
+ padding-top: 90px;
+ padding-bottom: 85px;
+ }
+ .hero-stage {
+ min-height: 640px;
+ padding: 72px 6vw 130px;
+ }
+ .journey::before { display: none; }
+ .step-copy { grid-template-columns: 0.6fr 1fr; }
+ .crew-track { grid-template-columns: repeat(2, 1fr); }
+ .crew-member:nth-child(2) { border-right: 0; }
+ .crew-member:nth-child(-n+2) { border-bottom: 1px solid var(--ink); }
+ .crew-track::before { display: none; }
+ .guide { gap: 5vw; }
+}
+
+@media (max-width: 820px) {
+ .site-header { height: 64px; }
+ .menu-button { display: block; }
+ .header-nav {
+ position: absolute;
+ top: 64px;
+ right: 0;
+ left: 0;
+ display: none;
+ align-items: stretch;
+ padding: 18px 24px 24px;
+ border-bottom: 1px solid var(--ink);
+ background: var(--paper-bright);
+ flex-direction: column;
+ }
+ .header-nav.is-open { display: flex; }
+ .header-nav > a:nth-child(-n+2) { display: block; }
+ .nav-download { justify-content: center; }
+ .hero-copy { padding: 72px 24px 80px; }
+ .hero h1 { font-size: clamp(44px, 12vw, 66px); }
+ .hero-stage { min-height: 520px; padding: 72px 24px 110px; }
+ .window-card { width: 100%; box-shadow: 9px 10px 0 var(--ink); transform: none; }
+ .mascot-note { right: 28px; bottom: 70px; width: 66%; }
+ .stage-label { display: none; }
+ .section { padding: 88px 24px; }
+ .section-heading,
+ .tour-heading {
+ grid-template-columns: 1fr;
+ gap: 24px;
+ }
+ .section h2,
+ .install h2 { font-size: clamp(40px, 10vw, 60px); }
+ .journey-list li {
+ grid-template-columns: 52px 1fr 48px;
+ gap: 14px;
+ padding: 28px 0;
+ }
+ .step-copy {
+ grid-template-columns: 1fr;
+ gap: 10px;
+ }
+ .step-mark { width: 44px; height: 44px; }
+ .tour-board {
+ min-height: 0;
+ grid-template-columns: 1fr;
+ gap: 30px;
+ }
+ .tour-copy { padding-bottom: 16px; }
+ .tour-scribble { display: none; }
+ .tour-screen { box-shadow: 10px 10px 0 var(--blue); }
+ .tour-screen img { min-height: 0; }
+ .guide {
+ grid-template-columns: 1fr;
+ gap: 58px;
+ }
+ .guide-title { position: static; }
+ .guide-row {
+ min-height: 150px;
+ grid-template-columns: 38px 1fr;
+ }
+ .guide-action {
+ display: none;
+ }
+ .install {
+ grid-template-columns: 1fr;
+ gap: 58px;
+ }
+ .install-mascot { display: none; }
+ .footer {
+ grid-template-columns: 1fr;
+ justify-items: start;
+ }
+ .footer-links { justify-self: start; }
+}
+
+@media (max-width: 560px) {
+ .hero-actions { align-items: stretch; flex-direction: column; }
+ .hero-actions .button { width: 100%; }
+ .hero-stage { min-height: 400px; padding-bottom: 94px; }
+ .window-bar small { display: none; }
+ .window-bar { grid-template-columns: 1fr auto; }
+ .mascot-note { right: 18px; bottom: 58px; width: 82%; }
+ .mascot-note img { width: 62px; }
+ .journey-list li { grid-template-columns: 40px 1fr; }
+ .step-mark { display: none; }
+ .tour-tabs button { padding: 15px 17px; }
+ .crew-track { grid-template-columns: 1fr; }
+ .crew-member {
+ min-height: 430px;
+ border-right: 0;
+ border-bottom: 1px solid var(--ink);
+ }
+ .crew-member:last-child { border-bottom: 0; }
+ .guide-row-copy strong { font-size: 22px; }
+ .command-sheet { padding: 24px; box-shadow: 9px 9px 0 var(--ink); }
+ .footer-links { flex-wrap: wrap; }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ html { scroll-behavior: auto; }
+ *,
+ *::before,
+ *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ scroll-behavior: auto !important;
+ transition-duration: 0.01ms !important;
+ }
+ [data-reveal] {
+ opacity: 1;
+ transform: none;
+ }
+}
diff --git a/site/vite.config.js b/site/vite.config.js
new file mode 100644
index 00000000..09591ec3
--- /dev/null
+++ b/site/vite.config.js
@@ -0,0 +1,75 @@
+import { defineConfig } from 'vite'
+import { promises as fs } from 'node:fs'
+import path from 'node:path'
+
+import { generateDocsManifest, paths } from './scripts/generate-docs-manifest.mjs'
+
+const imageMimeTypes = {
+ '.avif': 'image/avif',
+ '.gif': 'image/gif',
+ '.jpeg': 'image/jpeg',
+ '.jpg': 'image/jpeg',
+ '.png': 'image/png',
+ '.svg': 'image/svg+xml',
+ '.webp': 'image/webp',
+}
+
+function docsManifestPlugin() {
+ return {
+ name: 'claude-code-haha-docs-manifest',
+ async buildStart() {
+ await generateDocsManifest()
+ },
+ configureServer(server) {
+ server.middlewares.use(async (request, response, next) => {
+ const pathname = decodeURIComponent(new URL(request.url, 'http://localhost').pathname)
+ const extension = path.extname(pathname).toLowerCase()
+ if (!imageMimeTypes[extension]) {
+ next()
+ return
+ }
+
+ const sourcePath = path.resolve(paths.docsDir, pathname.replace(/^\/+/, ''))
+ if (!sourcePath.startsWith(`${paths.docsDir}${path.sep}`)) {
+ next()
+ return
+ }
+
+ try {
+ response.statusCode = 200
+ response.setHeader('Content-Type', imageMimeTypes[extension])
+ response.end(await fs.readFile(sourcePath))
+ } catch {
+ next()
+ }
+ })
+
+ const docsGlob = `${paths.docsDir.replaceAll('\\', '/')}/**/*.md`
+ server.watcher.add(docsGlob)
+ server.watcher.on('all', async (event, changedPath) => {
+ if (!changedPath.endsWith('.md') || !changedPath.startsWith(paths.docsDir)) {
+ return
+ }
+
+ if (['add', 'change', 'unlink'].includes(event)) {
+ await generateDocsManifest()
+ }
+ })
+ }
+ }
+}
+
+export default defineConfig({
+ plugins: [docsManifestPlugin()],
+ build: {
+ outDir: 'dist',
+ emptyOutDir: true,
+ sourcemap: true
+ },
+ server: {
+ port: 5173
+ },
+ preview: {
+ port: 4173
+ }
+})