feat: i18n for website

This commit is contained in:
Simon
2025-10-22 22:35:25 +08:00
parent fedeb57f48
commit cd84269427
34 changed files with 1753 additions and 407 deletions

View File

@@ -8,25 +8,38 @@
href="https://img.alicdn.com/imgextra/i3/O1CN01uo9VaC20mamWNM2Zn_!!6000000006892-55-tps-64-64.svg" href="https://img.alicdn.com/imgextra/i3/O1CN01uo9VaC20mamWNM2Zn_!!6000000006892-55-tps-64-64.svg"
/> />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PageAgent - 网页内的交互助手</title> <title>PageAgent - AI-powered UI Agent in Your Webpage</title>
<meta <meta
name="description" name="description"
content="PageAgent.js:一行 CDN 引入,为任何网站添加智能 UI Agent。现代网页 AI 自动化,极简集成,开发者专用。" content="PageAgent.js: One-line CDN import, add intelligent UI Agent to any website. Modern web AI automation with minimal integration."
/> />
<meta <meta
name="keywords" name="keywords"
content="PageAgent, AI Agent, Web Automation, UI 自动化, 前端, CDN, JavaScript, React, Vite" content="PageAgent, AI Agent, Web Automation, UI Automation, Frontend, CDN, JavaScript, React, Vite, LLM"
/> />
<meta name="theme-color" content="#58c0fc" /> <meta name="theme-color" content="#58c0fc" />
<meta name="color-scheme" content="light dark" /> <meta name="color-scheme" content="light dark" />
<meta name="author" content="PageAgent.js Team" /> <meta name="author" content="PageAgent.js Team" />
<meta property="og:title" content="PageAgent.js" /> <meta property="og:title" content="PageAgent.js - AI-powered UI Agent" />
<meta property="og:description" content="一行 CDN 引入,为任何网站添加智能 UI Agent。" /> <meta
property="og:description"
content="One-line CDN import, add intelligent UI Agent to any website."
/>
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:locale" content="zh_CN" /> <meta property="og:locale" content="en_US" />
<meta property="og:locale:alternate" content="zh_CN" />
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
<script type="module" src="./pages/main.tsx"></script> <script type="module" src="./pages/main.tsx"></script>
<script>
// Dynamically update html lang attribute based on i18n detection
const updateHtmlLang = () => {
const lang = localStorage.getItem('i18nextLng') || navigator.language || 'zh-CN'
document.documentElement.lang = lang
}
updateHtmlLang()
window.addEventListener('storage', updateHtmlLang)
</script>
</body> </body>
</html> </html>

102
package-lock.json generated
View File

@@ -32,10 +32,13 @@
"eslint-plugin-react-x": "^2.0.6", "eslint-plugin-react-x": "^2.0.6",
"globals": "^16.4.0", "globals": "^16.4.0",
"husky": "^9.1.7", "husky": "^9.1.7",
"i18next": "^25.6.0",
"i18next-browser-languagedetector": "^8.2.0",
"lint-staged": "^16.2.4", "lint-staged": "^16.2.4",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"react": "^19.2.0", "react": "^19.2.0",
"react-dom": "^19.2.0", "react-dom": "^19.2.0",
"react-i18next": "^16.1.4",
"tailwindcss": "^4.1.14", "tailwindcss": "^4.1.14",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"typescript-eslint": "^8.46.0", "typescript-eslint": "^8.46.0",
@@ -277,6 +280,16 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/@babel/runtime": {
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
"integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/template": { "node_modules/@babel/template": {
"version": "7.27.2", "version": "7.27.2",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
@@ -4553,6 +4566,16 @@
"hermes-estree": "0.25.1" "hermes-estree": "0.25.1"
} }
}, },
"node_modules/html-parse-stringify": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
"integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
"dev": true,
"license": "MIT",
"dependencies": {
"void-elements": "3.1.0"
}
},
"node_modules/husky": { "node_modules/husky": {
"version": "9.1.7", "version": "9.1.7",
"resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
@@ -4569,6 +4592,48 @@
"url": "https://github.com/sponsors/typicode" "url": "https://github.com/sponsors/typicode"
} }
}, },
"node_modules/i18next": {
"version": "25.6.0",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.6.0.tgz",
"integrity": "sha512-tTn8fLrwBYtnclpL5aPXK/tAYBLWVvoHM1zdfXoRNLcI+RvtMsoZRV98ePlaW3khHYKuNh/Q65W/+NVFUeIwVw==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://locize.com"
},
{
"type": "individual",
"url": "https://locize.com/i18next.html"
},
{
"type": "individual",
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
}
],
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.27.6"
},
"peerDependencies": {
"typescript": "^5"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/i18next-browser-languagedetector": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.2.0.tgz",
"integrity": "sha512-P+3zEKLnOF0qmiesW383vsLdtQVyKtCNA9cjSoKCppTKPQVfKd2W8hbVo5ZhNJKDqeM7BOcvNoKJOjpHh4Js9g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.23.2"
}
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -5874,6 +5939,33 @@
"react": "^19.2.0" "react": "^19.2.0"
} }
}, },
"node_modules/react-i18next": {
"version": "16.1.4",
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.1.4.tgz",
"integrity": "sha512-0UUKZDHjKnLk6dfbYXEZ9CVqLMpNiul+dHbPVQo2z2t1GkdirkeHXb/TtdsNuv+nyNOTDl1Jp6F6uwf9M3DMcg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.27.6",
"html-parse-stringify": "^3.0.1"
},
"peerDependencies": {
"i18next": ">= 25.5.2",
"react": ">= 16.8.0",
"typescript": "^5"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
},
"typescript": {
"optional": true
}
}
},
"node_modules/regexparam": { "node_modules/regexparam": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/regexparam/-/regexparam-3.0.0.tgz", "resolved": "https://registry.npmjs.org/regexparam/-/regexparam-3.0.0.tgz",
@@ -6738,6 +6830,16 @@
"url": "https://github.com/sponsors/jonschlinkert" "url": "https://github.com/sponsors/jonschlinkert"
} }
}, },
"node_modules/void-elements": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
"integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/vscode-uri": { "node_modules/vscode-uri": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz",

View File

@@ -74,10 +74,13 @@
"eslint-plugin-react-x": "^2.0.6", "eslint-plugin-react-x": "^2.0.6",
"globals": "^16.4.0", "globals": "^16.4.0",
"husky": "^9.1.7", "husky": "^9.1.7",
"i18next": "^25.6.0",
"i18next-browser-languagedetector": "^8.2.0",
"lint-staged": "^16.2.4", "lint-staged": "^16.2.4",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"react": "^19.2.0", "react": "^19.2.0",
"react-dom": "^19.2.0", "react-dom": "^19.2.0",
"react-i18next": "^16.1.4",
"tailwindcss": "^4.1.14", "tailwindcss": "^4.1.14",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"typescript-eslint": "^8.46.0", "typescript-eslint": "^8.46.0",

View File

@@ -1,4 +1,8 @@
import { useTranslation } from 'react-i18next'
export default function BetaNotice() { export default function BetaNotice() {
const { t } = useTranslation('common')
return ( return (
<div className="bg-orange-50 dark:bg-orange-900/20 border border-orange-200 dark:border-orange-800 rounded-lg p-4 mb-8"> <div className="bg-orange-50 dark:bg-orange-900/20 border border-orange-200 dark:border-orange-800 rounded-lg p-4 mb-8">
<div className="flex items-start"> <div className="flex items-start">
@@ -7,11 +11,9 @@ export default function BetaNotice() {
</div> </div>
<div className="ml-3"> <div className="ml-3">
<h3 className="text-sm font-medium text-orange-800 dark:text-orange-200 mb-1"> <h3 className="text-sm font-medium text-orange-800 dark:text-orange-200 mb-1">
Beta {t('beta_notice.title')}
</h3> </h3>
<p className="text-sm text-orange-700 dark:text-orange-300"> <p className="text-sm text-orange-700 dark:text-orange-300">{t('beta_notice.content')}</p>
</p>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,4 +1,5 @@
import { ReactNode } from 'react' import { ReactNode } from 'react'
import { useTranslation } from 'react-i18next'
import { Link, useLocation } from 'wouter' import { Link, useLocation } from 'wouter'
interface DocsLayoutProps { interface DocsLayoutProps {
@@ -15,39 +16,40 @@ interface NavSection {
items: NavItem[] items: NavItem[]
} }
export default function DocsLayout({ children }: DocsLayoutProps) {
const { t } = useTranslation('common')
const [location] = useLocation()
const navigationSections: NavSection[] = [ const navigationSections: NavSection[] = [
{ {
title: 'Introduction', title: t('nav.introduction'),
items: [ items: [
{ title: 'Overview', path: '/docs/introduction/overview' }, { title: t('nav.overview'), path: '/docs/introduction/overview' },
{ title: 'Quick Start', path: '/docs/introduction/quick-start' }, { title: t('nav.quick_start'), path: '/docs/introduction/quick-start' },
{ title: '使用限制', path: '/docs/introduction/limitations' }, { title: t('nav.limitations'), path: '/docs/introduction/limitations' },
], ],
}, },
{ {
title: 'Features', title: t('nav.features'),
items: [ items: [
{ title: '模型接入', path: '/docs/features/model-integration' }, { title: t('nav.model_integration'), path: '/docs/features/model-integration' },
{ title: '自定义工具', path: '/docs/features/custom-tools' }, { title: t('nav.custom_tools'), path: '/docs/features/custom-tools' },
{ title: '知识库注入', path: '/docs/features/knowledge-injection' }, { title: t('nav.knowledge_injection'), path: '/docs/features/knowledge-injection' },
{ title: '安全与权限', path: '/docs/features/security-permissions' }, { title: t('nav.security_permissions'), path: '/docs/features/security-permissions' },
{ title: '数据脱敏', path: '/docs/features/data-masking' }, { title: t('nav.data_masking'), path: '/docs/features/data-masking' },
], ],
}, },
{ {
title: 'Integration', title: t('nav.integration'),
items: [ items: [
{ title: 'CDN 引入', path: '/docs/integration/cdn-setup' }, { title: t('nav.cdn_setup'), path: '/docs/integration/cdn-setup' },
{ title: '配置选项', path: '/docs/integration/configuration' }, { title: t('nav.configuration'), path: '/docs/integration/configuration' },
{ title: '最佳实践', path: '/docs/integration/best-practices' }, { title: t('nav.best_practices'), path: '/docs/integration/best-practices' },
{ title: '接入第三方 Agent', path: '/docs/integration/third-party-agent' }, { title: t('nav.third_party_agent'), path: '/docs/integration/third-party-agent' },
], ],
}, },
] ]
export default function DocsLayout({ children }: DocsLayoutProps) {
const [location] = useLocation()
return ( return (
<div className="max-w-7xl mx-auto px-6 py-8"> <div className="max-w-7xl mx-auto px-6 py-8">
<div className="flex gap-8"> <div className="flex gap-8">
@@ -57,7 +59,9 @@ export default function DocsLayout({ children }: DocsLayoutProps) {
<nav className="space-y-8" role="navigation" aria-label="文档章节"> <nav className="space-y-8" role="navigation" aria-label="文档章节">
{navigationSections.map((section) => ( {navigationSections.map((section) => (
<section key={section.title}> <section key={section.title}>
<h3 className="font-semibold uppercase tracking-wider mb-3">{section.title}</h3> <h3 className="font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider mb-3">
{section.title}
</h3>
<ul className="space-y-2" role="list"> <ul className="space-y-2" role="list">
{section.items.map((item) => { {section.items.map((item) => {
const isActive = location === item.path const isActive = location === item.path
@@ -68,7 +72,7 @@ export default function DocsLayout({ children }: DocsLayoutProps) {
className={`block px-3 py-2 rounded-lg transition-colors duration-200 ${ className={`block px-3 py-2 rounded-lg transition-colors duration-200 ${
isActive isActive
? 'bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 font-medium' ? 'bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 font-medium'
: ' hover:text-foreground hover:bg-gray-100 dark:hover:bg-gray-800' : 'text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-800'
}`} }`}
aria-current={isActive ? 'page' : undefined} aria-current={isActive ? 'page' : undefined}
> >

View File

@@ -1,4 +1,8 @@
import { useTranslation } from 'react-i18next'
export default function Footer() { export default function Footer() {
const { t } = useTranslation('common')
return ( return (
<footer <footer
className="bg-gray-50 dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700" className="bg-gray-50 dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700"
@@ -6,14 +10,14 @@ export default function Footer() {
> >
<div className="max-w-7xl mx-auto px-6 py-6"> <div className="max-w-7xl mx-auto px-6 py-6">
<div className="flex flex-col md:flex-row justify-between items-center space-y-4 md:space-y-0"> <div className="flex flex-col md:flex-row justify-between items-center space-y-4 md:space-y-0">
<p className="text-foreground/80 text-sm">© 2025 page-agent. All rights reserved.</p> <p className="text-gray-600 dark:text-gray-300 text-sm">{t('footer.copyright')}</p>
<div className="flex items-center space-x-6"> <div className="flex items-center space-x-6">
<a <a
href="https://github.com/alibaba/page-agent" href="https://github.com/alibaba/page-agent"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="text-foreground/80 hover:text-foreground transition-colors duration-200" className="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors duration-200"
aria-label="访问 GitHub 仓库" aria-label={t('footer.github_label')}
> >
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true"> <svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" /> <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />

View File

@@ -1,7 +1,13 @@
import { useTranslation } from 'react-i18next'
import { Link } from 'wouter' import { Link } from 'wouter'
import LanguageSwitcher from './LanguageSwitcher'
import ThemeSwitcher from './ThemeSwitcher'
export default function Header() { export default function Header() {
const { t } = useTranslation('common')
return ( return (
<>
<header <header
className="relative z-50 bg-white/80 dark:bg-gray-900/80 backdrop-blur-md border-b border-gray-200 dark:border-gray-700" className="relative z-50 bg-white/80 dark:bg-gray-900/80 backdrop-blur-md border-b border-gray-200 dark:border-gray-700"
role="banner" role="banner"
@@ -9,46 +15,69 @@ export default function Header() {
<div className="max-w-7xl mx-auto px-6 py-4"> <div className="max-w-7xl mx-auto px-6 py-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
{/* Logo */} {/* Logo */}
<Link href="/" className="flex items-center space-x-3 group" aria-label="page-agent 首页"> <Link
href="/"
className="flex items-center space-x-3 group"
aria-label={t('header.logo_alt')}
>
<div className="w-10 h-10 bg-gradient-to-br from-blue-500 to-purple-600 rounded-xl flex items-center justify-center group-hover:scale-110 transition-transform duration-200"> <div className="w-10 h-10 bg-gradient-to-br from-blue-500 to-purple-600 rounded-xl flex items-center justify-center group-hover:scale-110 transition-transform duration-200">
<span className="text-white font-bold text-2xl lg:text-2xl" aria-hidden="true"> <span className="text-white font-bold text-2xl lg:text-2xl" aria-hidden="true">
P P
</span> </span>
</div> </div>
<div> <div>
<span className="text-xl font-bold text-foreground">page-agent</span> <span className="text-xl font-bold text-gray-900 dark:text-white">page-agent</span>
<p className="text-xs text-foreground/80">UI Agent in your webpage</p> <p className="text-xs text-gray-600 dark:text-gray-300">{t('header.slogan')}</p>
</div> </div>
</Link> </Link>
{/* Navigation */} {/* Navigation */}
<nav <nav
className="hidden md:flex items-center space-x-8" className="hidden md:flex items-center space-x-6"
role="navigation" role="navigation"
aria-label="主导航" aria-label={t('header.nav_docs')}
> >
<Link <Link
href="/docs/introduction/overview" href="/docs/introduction/overview"
className="text-foreground/80 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-200" className="flex items-center gap-1.5 text-gray-600 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-200"
> >
<svg
className="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
/>
</svg>
{t('header.nav_docs')}
</Link> </Link>
<a <a
href="https://github.com/alibaba/page-agent" href="https://github.com/alibaba/page-agent"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="text-foreground/80 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-200" className="flex items-center gap-1.5 text-gray-600 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-200"
aria-label="查看源码(在新窗口打开)" aria-label={t('header.nav_source')}
> >
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
</svg>
{t('header.nav_source')}
</a> </a>
<ThemeSwitcher />
<LanguageSwitcher />
</nav> </nav>
{/* Mobile menu button */} {/* Mobile menu button */}
<button <button
type="button" type="button"
className="md:hidden p-2 rounded-lg text-foreground/80 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors duration-200" className="md:hidden p-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors duration-200"
aria-label="打开移动端菜单" aria-label={t('header.mobile_menu')}
aria-expanded="false" aria-expanded="false"
aria-controls="mobile-menu" aria-controls="mobile-menu"
> >
@@ -70,5 +99,6 @@ export default function Header() {
</div> </div>
</div> </div>
</header> </header>
</>
) )
} }

View File

@@ -3,19 +3,43 @@
word-break: break-word; word-break: break-word;
overflow-wrap: break-word; overflow-wrap: break-word;
font-family: monospace; font-family: monospace;
color: #171717;
}
:global(.dark) .syntax {
color: #e0e0e0;
} }
.keyword { .keyword {
color: #d73a49; color: #d73a49;
font-weight: 600; font-weight: 600;
} }
:global(.dark) .keyword {
color: #ff6b6b;
}
.string { .string {
color: #1d6eca; color: #1d6eca;
} }
:global(.dark) .string {
color: #4fc3f7;
}
.number { .number {
color: #00c583; color: #00c583;
} }
:global(.dark) .number {
color: #66bb6a;
}
.comment { .comment {
color: #6a737d; color: #6a737d;
font-style: italic; font-style: italic;
} }
:global(.dark) .comment {
color: #9e9e9e;
}

View File

@@ -46,19 +46,19 @@ function highlightSyntax(code: string): string {
/^'([^'\\]|\\.)*'$/.test(token) || /^'([^'\\]|\\.)*'$/.test(token) ||
/^`([^`\\]|\\.)*`$/.test(token) /^`([^`\\]|\\.)*`$/.test(token)
) { ) {
return `<span style="color: #1d6eca;">${token}</span>` return `<span class="${styles.string}">${token}</span>`
} }
if (/^\b\d+\.?\d*\b$/.test(token)) { if (/^\b\d+\.?\d*\b$/.test(token)) {
return `<span style="color: #00c583;">${token}</span>` return `<span class="${styles.number}">${token}</span>`
} }
if (/^\/\/.*$/.test(token)) { if (/^\/\/.*$/.test(token)) {
return `<span style="color: #6a737d; font-style: italic;">${token}</span>` return `<span class="${styles.comment}">${token}</span>`
} }
if (/^\/\*[\s\S]*?\*\/$/.test(token)) { if (/^\/\*[\s\S]*?\*\/$/.test(token)) {
return `<span style="color: #6a737d; font-style: italic;">${token}</span>` return `<span class="${styles.comment}">${token}</span>`
} }
if (new RegExp(`\\b(?:${keywords})\\b`).test(token)) { if (new RegExp(`\\b(?:${keywords})\\b`).test(token)) {
return `<span style="color: #d73a49; font-weight: 600;">${token}</span>` return `<span class="${styles.keyword}">${token}</span>`
} }
return token return token
}) })

View File

@@ -0,0 +1,115 @@
import { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
export default function LanguageSwitcher() {
const { i18n, t } = useTranslation('common')
const [isOpen, setIsOpen] = useState(false)
const dropdownRef = useRef<HTMLDivElement>(null)
const currentLang = i18n.language
const languages = [
{ code: 'zh-CN', label: '中文' },
{ code: 'en-US', label: 'English' },
]
const currentLanguage = languages.find((lang) => lang.code === currentLang) || languages[0]
const handleLanguageChange = (langCode: string) => {
i18n.changeLanguage(langCode)
setIsOpen(false)
}
// Close dropdown when clicking outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsOpen(false)
}
}
if (isOpen) {
document.addEventListener('mousedown', handleClickOutside)
}
return () => {
document.removeEventListener('mousedown', handleClickOutside)
}
}, [isOpen])
return (
<div className="relative" ref={dropdownRef}>
<button
onClick={() => setIsOpen(!isOpen)}
className="flex items-center gap-2 px-3 py-1.5 rounded-lg bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors text-sm font-medium border border-gray-200 dark:border-gray-700 text-gray-700 dark:text-gray-300"
aria-label={t('language.switch_label')}
aria-expanded={isOpen}
aria-haspopup="true"
>
<svg
className="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129"
/>
</svg>
<span>{currentLanguage.label}</span>
<svg
className={`w-4 h-4 transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
{isOpen && (
<div
className="absolute right-0 mt-2 w-40 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 py-1 z-50"
role="menu"
aria-orientation="vertical"
>
{languages.map((lang) => (
<button
key={lang.code}
onClick={() => handleLanguageChange(lang.code)}
className={`w-full flex items-center gap-2 px-4 py-2 text-sm text-left hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors ${
currentLang === lang.code
? 'bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-300'
: 'text-gray-700 dark:text-gray-300'
}`}
role="menuitem"
>
<span>{lang.label}</span>
{currentLang === lang.code && (
<svg
className="w-4 h-4 ml-auto"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M5 13l4 4L19 7"
/>
</svg>
)}
</button>
))}
</div>
)}
</div>
)
}

View File

@@ -0,0 +1,133 @@
import { useEffect, useState } from 'react'
type Theme = 'light' | 'dark'
export default function ThemeSwitcher() {
const [theme, setTheme] = useState<Theme>(() => {
// 初始化时读取保存的主题
if (typeof window !== 'undefined') {
const savedTheme = localStorage.getItem('theme') as Theme | null
if (savedTheme === 'light' || savedTheme === 'dark') {
return savedTheme
}
// 默认跟随系统
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
return 'light'
})
useEffect(() => {
// 应用主题
if (theme === 'dark') {
document.documentElement.classList.add('dark')
document.documentElement.style.colorScheme = 'dark'
} else {
document.documentElement.classList.remove('dark')
document.documentElement.style.colorScheme = 'light'
}
// 保存到 localStorage
localStorage.setItem('theme', theme)
}, [theme])
// 监听系统主题变化
useEffect(() => {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
const handleSystemThemeChange = (e: MediaQueryListEvent) => {
// 只有在用户未手动设置时才自动跟随系统
const savedTheme = localStorage.getItem('theme')
if (!savedTheme) {
setTheme(e.matches ? 'dark' : 'light')
}
}
mediaQuery.addEventListener('change', handleSystemThemeChange)
return () => mediaQuery.removeEventListener('change', handleSystemThemeChange)
}, [])
const toggleTheme = () => {
setTheme((prev) => (prev === 'light' ? 'dark' : 'light'))
}
return (
<button
onClick={toggleTheme}
className="relative inline-flex items-center h-8 w-16 rounded-full transition-colors duration-300 ease-in-out focus:outline-none"
style={{
backgroundColor: theme === 'dark' ? '#1e293b' : '#e0f2fe',
}}
aria-label={theme === 'light' ? '切换到深色模式' : '切换到浅色模式'}
role="switch"
aria-checked={theme === 'dark'}
>
{/* 滑块 */}
<span
className="inline-block h-6 w-6 transform rounded-full transition-all duration-300 ease-in-out shadow-md"
style={{
backgroundColor: theme === 'dark' ? '#475569' : '#fbbf24',
transform: theme === 'dark' ? 'translateX(2.25rem)' : 'translateX(0.25rem)',
}}
>
{/* 图标 */}
<span className="flex items-center justify-center h-full w-full">
{theme === 'light' ? (
// 太阳图标
<svg
className="w-4 h-4 text-white"
fill="currentColor"
viewBox="0 0 20 20"
aria-hidden="true"
>
<path
fillRule="evenodd"
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
clipRule="evenodd"
/>
</svg>
) : (
// 月亮图标
<svg
className="w-4 h-4 text-slate-200"
fill="currentColor"
viewBox="0 0 20 20"
aria-hidden="true"
>
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
</svg>
)}
</span>
</span>
{/* 背景装饰 */}
<span
className="absolute inset-0 flex items-center justify-between px-2 pointer-events-none"
aria-hidden="true"
>
{/* 左侧太阳(浅色模式时显示) */}
<span
className={`transition-opacity duration-300 ${
theme === 'light' ? 'opacity-0' : 'opacity-40'
}`}
>
<svg className="w-4 h-4 text-sky-400" fill="currentColor" viewBox="0 0 20 20">
<path
fillRule="evenodd"
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
clipRule="evenodd"
/>
</svg>
</span>
{/* 右侧月亮(深色模式时显示) */}
<span
className={`transition-opacity duration-300 ${
theme === 'dark' ? 'opacity-0' : 'opacity-40'
}`}
>
<svg className="w-4 h-4 text-slate-400" fill="currentColor" viewBox="0 0 20 20">
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
</svg>
</span>
</span>
</button>
)
}

View File

@@ -1,21 +1,23 @@
import BetaNotice from '@pages/components/BetaNotice' import BetaNotice from '@pages/components/BetaNotice'
import CodeEditor from '@pages/components/CodeEditor' import CodeEditor from '@pages/components/CodeEditor'
import { useTranslation } from 'react-i18next'
export default function CustomTools() { export default function CustomTools() {
const { t } = useTranslation('docs')
return ( return (
<div> <div>
<h1 className="text-4xl font-bold mb-6"></h1> <h1 className="text-4xl font-bold mb-6">{t('custom_tools.title')}</h1>
<p className="text-xl text-foreground/80 mb-8 leading-relaxed"> <p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
AI Agent 使 Zod AI {t('custom_tools.subtitle')}
</p> </p>
<div className="space-y-8"> <div className="space-y-8">
<section> <section>
<h2 className="text-2xl font-bold mb-4"></h2> <h2 className="text-2xl font-bold mb-4">{t('custom_tools.registration')}</h2>
<p className="text-foreground/80 mb-4"> <p className="text-gray-600 dark:text-gray-300 mb-4">
namedescriptioninput schema execute {t('custom_tools.registration_desc')}
</p> </p>
<CodeEditor <CodeEditor
@@ -49,13 +51,12 @@ const pageAgent = new PageAgent({customTools})
</section> </section>
<section> <section>
<h2 className="text-2xl font-bold mb-4"></h2> <h2 className="text-2xl font-bold mb-4">{t('custom_tools.page_filter')}</h2>
<BetaNotice /> <BetaNotice />
<p className="text-foreground/80 mb-4"> <p className="text-gray-600 dark:text-gray-300 mb-4">
<code className="bg-gray-200 dark:bg-gray-700 px-2 py-1 rounded">pageFilter</code>{' '} {t('custom_tools.page_filter_desc')}
</p> </p>
<CodeEditor <CodeEditor
@@ -82,16 +83,16 @@ const pageAgent = new PageAgent({customTools})
</section> </section>
<section> <section>
<h2 className="text-2xl font-bold mb-4"></h2> <h2 className="text-2xl font-bold mb-4">{t('custom_tools.best_practices')}</h2>
<div className="space-y-4"> <div className="space-y-4">
<div className="p-4 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg"> <div className="p-4 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg">
<h3 className="text-lg font-semibold text-yellow-900 dark:text-yellow-300 mb-2"> <h3 className="text-lg font-semibold text-yellow-900 dark:text-yellow-300 mb-2">
{t('custom_tools.bp_performance')}
</h3> </h3>
<ul className="text-foreground/80 space-y-1 text-sm"> <ul className="text-gray-600 dark:text-gray-300 space-y-1 text-sm">
<li> 使 pageFilter </li> <li>{t('custom_tools.bp_1')}</li>
<li> execute </li> <li>{t('custom_tools.bp_2')}</li>
<li> </li> <li>{t('custom_tools.bp_3')}</li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@@ -8,7 +8,7 @@ export default function DataMasking() {
<BetaNotice /> <BetaNotice />
<p className="text-xl text-foreground/80 mb-6 leading-relaxed"> <p className="text-xl text-gray-600 dark:text-gray-300 mb-6 leading-relaxed">
AI AI
</p> </p>
@@ -19,14 +19,18 @@ export default function DataMasking() {
<h3 className="text-lg font-semibold mb-2 text-blue-900 dark:text-blue-300"> <h3 className="text-lg font-semibold mb-2 text-blue-900 dark:text-blue-300">
🔒 🔒
</h3> </h3>
<p className="text-foreground/80"></p> <p className="text-gray-600 dark:text-gray-300">
</p>
</div> </div>
<div className="p-4 bg-purple-50 dark:bg-purple-900/20 rounded-lg"> <div className="p-4 bg-purple-50 dark:bg-purple-900/20 rounded-lg">
<h3 className="text-lg font-semibold mb-2 text-purple-900 dark:text-purple-300"> <h3 className="text-lg font-semibold mb-2 text-purple-900 dark:text-purple-300">
</h3> </h3>
<p className="text-foreground/80"></p> <p className="text-gray-600 dark:text-gray-300">
</p>
</div> </div>
</div> </div>

View File

@@ -8,7 +8,7 @@ export default function KnowledgeInjection() {
<BetaNotice /> <BetaNotice />
<p className="text-xl text-foreground/80 mb-8 leading-relaxed"> <p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
AI AI
</p> </p>
@@ -20,8 +20,10 @@ export default function KnowledgeInjection() {
<h3 className="text-xl font-semibold mb-3 text-purple-900 dark:text-purple-300"> <h3 className="text-xl font-semibold mb-3 text-purple-900 dark:text-purple-300">
🎯 🎯
</h3> </h3>
<p className="text-foreground/80 mb-4"> AI </p> <p className="text-gray-600 dark:text-gray-300 mb-4">
<ul className="list-disc list-inside space-y-2 text-foreground/70"> AI
</p>
<ul className="list-disc list-inside space-y-2 text-gray-500 dark:text-gray-400">
<li> AI </li> <li> AI </li>
<li></li> <li></li>
<li></li> <li></li>
@@ -57,13 +59,13 @@ const pageAgent = new PageAgent({
<h3 className="text-xl font-semibold mb-3 text-blue-900 dark:text-blue-300"> <h3 className="text-xl font-semibold mb-3 text-blue-900 dark:text-blue-300">
<EFBFBD> <EFBFBD>
</h3> </h3>
<p className="text-foreground/80 mb-4"> <p className="text-gray-600 dark:text-gray-300 mb-4">
AI AI
</p> </p>
<div className="grid md:grid-cols-2 gap-4"> <div className="grid md:grid-cols-2 gap-4">
<div className="space-y-2"> <div className="space-y-2">
<h4 className="font-semibold text-blue-800 dark:text-blue-200"></h4> <h4 className="font-semibold text-blue-800 dark:text-blue-200"></h4>
<ul className="list-disc list-inside text-sm text-foreground/70 space-y-1"> <ul className="list-disc list-inside text-sm text-gray-500 dark:text-gray-400 space-y-1">
<li></li> <li></li>
<li></li> <li></li>
<li></li> <li></li>
@@ -71,7 +73,7 @@ const pageAgent = new PageAgent({
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<h4 className="font-semibold text-blue-800 dark:text-blue-200"></h4> <h4 className="font-semibold text-blue-800 dark:text-blue-200"></h4>
<ul className="list-disc list-inside text-sm text-foreground/70 space-y-1"> <ul className="list-disc list-inside text-sm text-gray-500 dark:text-gray-400 space-y-1">
<li></li> <li></li>
<li></li> <li></li>
<li></li> <li></li>
@@ -111,21 +113,25 @@ pageAgent.knowledge.setAppKnowledge(\`
<h3 className="text-xl font-semibold mb-3 text-green-900 dark:text-green-300"> <h3 className="text-xl font-semibold mb-3 text-green-900 dark:text-green-300">
📄 📄
</h3> </h3>
<p className="text-foreground/80 mb-4"> <p className="text-gray-600 dark:text-gray-300 mb-4">
AI AI
</p> </p>
<div className="grid md:grid-cols-3 gap-4"> <div className="grid md:grid-cols-3 gap-4">
<div className="space-y-2"> <div className="space-y-2">
<h4 className="font-semibold text-green-800 dark:text-green-200"></h4> <h4 className="font-semibold text-green-800 dark:text-green-200"></h4>
<p className="text-sm text-foreground/70"></p> <p className="text-sm text-gray-500 dark:text-gray-400"></p>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<h4 className="font-semibold text-green-800 dark:text-green-200"></h4> <h4 className="font-semibold text-green-800 dark:text-green-200"></h4>
<p className="text-sm text-foreground/70"></p> <p className="text-sm text-gray-500 dark:text-gray-400">
</p>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<h4 className="font-semibold text-green-800 dark:text-green-200"></h4> <h4 className="font-semibold text-green-800 dark:text-green-200"></h4>
<p className="text-sm text-foreground/70"></p> <p className="text-sm text-gray-500 dark:text-gray-400">
</p>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,69 +1,78 @@
import BetaNotice from '@pages/components/BetaNotice' import BetaNotice from '@pages/components/BetaNotice'
import CodeEditor from '@pages/components/CodeEditor' import CodeEditor from '@pages/components/CodeEditor'
import { useTranslation } from 'react-i18next'
export default function ModelIntegration() { export default function ModelIntegration() {
const { t } = useTranslation('docs')
return ( return (
<div> <div>
<h1 className="text-4xl font-bold mb-6"></h1> <h1 className="text-4xl font-bold mb-6">{t('model_integration.title')}</h1>
<p className="text-xl text-gray-600 dark:text-gray-300 mb-6 leading-relaxed"> <p className="text-xl text-gray-600 dark:text-gray-300 mb-6 leading-relaxed">
OpenAI tool call {t('model_integration.subtitle')}
</p> </p>
<h2 className="text-2xl font-bold mb-3"></h2> <h2 className="text-2xl font-bold mb-3">{t('model_integration.recommended')}</h2>
<div className="grid md:grid-cols-3 gap-4 mb-6"> <div className="grid md:grid-cols-3 gap-4 mb-6">
<div className="p-4 bg-green-50 dark:bg-green-900/20 rounded-lg"> <div className="p-4 bg-green-50 dark:bg-green-900/20 rounded-lg">
<h3 className="text-lg font-semibold mb-2 text-green-900 dark:text-green-300"> <h3 className="text-lg font-semibold mb-2 text-green-900 dark:text-green-300">
gpt-4.1-mini {t('model_integration.model_gpt4_title')}
</h3> </h3>
<p className="text-sm text-foreground/80 mb-2"> </p> <p className="text-sm text-gray-600 dark:text-gray-300 mb-2">
<ul className="text-sm text-foreground/70 space-y-1"> {t('model_integration.model_gpt4_badge')}
<li> </li> </p>
<li> </li> <ul className="text-sm text-gray-500 dark:text-gray-400 space-y-1">
<li> </li> <li>{t('model_integration.model_gpt4_1')}</li>
<li>{t('model_integration.model_gpt4_2')}</li>
<li>{t('model_integration.model_gpt4_3')}</li>
</ul> </ul>
</div> </div>
<div className="p-4 bg-purple-50 dark:bg-purple-900/20 rounded-lg"> <div className="p-4 bg-purple-50 dark:bg-purple-900/20 rounded-lg">
<h3 className="text-lg font-semibold mb-2 text-purple-900 dark:text-purple-300"> <h3 className="text-lg font-semibold mb-2 text-purple-900 dark:text-purple-300">
💰 DeepSeek-3.2 {t('model_integration.model_deepseek_title')}
</h3> </h3>
<p className="text-sm text-foreground/80 mb-2"></p> <p className="text-sm text-gray-600 dark:text-gray-300 mb-2">
<ul className="text-sm text-foreground/70 space-y-1"> {t('model_integration.model_deepseek_badge')}
<li> </li> </p>
<li> ToolCall </li> <ul className="text-sm text-gray-500 dark:text-gray-400 space-y-1">
<li> DeepSeek</li> <li>{t('model_integration.model_deepseek_1')}</li>
<li>{t('model_integration.model_deepseek_2')}</li>
<li>{t('model_integration.model_deepseek_3')}</li>
</ul> </ul>
</div> </div>
<div className="p-4 bg-orange-50 dark:bg-orange-900/20 rounded-lg"> <div className="p-4 bg-orange-50 dark:bg-orange-900/20 rounded-lg">
<h3 className="text-lg font-semibold mb-2 text-orange-900 dark:text-orange-300"> <h3 className="text-lg font-semibold mb-2 text-orange-900 dark:text-orange-300">
🛡 qwen3 {t('model_integration.model_qwen_title')}
</h3> </h3>
<p className="text-sm text-foreground/80 mb-2"></p> <p className="text-sm text-gray-600 dark:text-gray-300 mb-2">
<ul className="text-sm text-foreground/70 space-y-1"> {t('model_integration.model_qwen_badge')}
<li> </li> </p>
<li> ToolCall </li> <ul className="text-sm text-gray-500 dark:text-gray-400 space-y-1">
<li> <li>{t('model_integration.model_qwen_1')}</li>
<strong></strong> <li>{t('model_integration.model_qwen_2')}</li>
</li> <li>{t('model_integration.model_qwen_3')}</li>
</ul> </ul>
</div> </div>
<div className="p-4 bg-orange-50 dark:bg-orange-900/20 rounded-lg"> <div className="p-4 bg-orange-50 dark:bg-orange-900/20 rounded-lg">
<h3 className="text-lg font-semibold mb-2 text-orange-900 dark:text-orange-300"> <h3 className="text-lg font-semibold mb-2 text-orange-900 dark:text-orange-300">
gemini-2.5-flash {t('model_integration.model_gemini_title')}
</h3> </h3>
<p className="text-sm text-foreground/80 mb-2"></p> <p className="text-sm text-gray-600 dark:text-gray-300 mb-2">
{t('model_integration.model_gemini_badge')}
</p>
</div> </div>
</div> </div>
<h2 className="text-2xl font-bold mb-3"></h2> <h2 className="text-2xl font-bold mb-3">{t('model_integration.available')}</h2>
<div className="p-4 bg-emerald-50 dark:bg-emerald-900/20 rounded-lg mb-6"> <div className="p-4 bg-emerald-50 dark:bg-emerald-900/20 rounded-lg mb-6">
<h3 className="text-lg font-semibold mb-3 text-emerald-900 dark:text-emerald-300"> <h3 className="text-lg font-semibold mb-3 text-emerald-900 dark:text-emerald-300">
{t('model_integration.available_verified')}
</h3> </h3>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
<span className="inline-flex items-center rounded-full bg-emerald-100 dark:bg-emerald-900/40 text-emerald-900 dark:text-emerald-200 px-3 py-1 text-sm"> <span className="inline-flex items-center rounded-full bg-emerald-100 dark:bg-emerald-900/40 text-emerald-900 dark:text-emerald-200 px-3 py-1 text-sm">
@@ -87,20 +96,17 @@ export default function ModelIntegration() {
</div> </div>
</div> </div>
<h2 className="text-2xl font-bold mb-3"></h2> <h2 className="text-2xl font-bold mb-3">{t('model_integration.tips')}</h2>
<div className="p-4 bg-red-50 dark:bg-red-900/20 rounded-lg mb-6"> <div className="p-4 bg-red-50 dark:bg-red-900/20 rounded-lg mb-6">
<ul className="text-sm text-foreground/80 space-y-1 list-disc pl-5"> <ul className="text-sm text-gray-600 dark:text-gray-300 space-y-1 list-disc pl-5">
<li>reasoning GPT-5</li> <li>{t('model_integration.tip_1')}</li>
<li> <li>{t('model_integration.tip_2')}</li>
json schema openAI tool call <li>{t('model_integration.tip_3')}</li>
temperature
</li>
<li>nano </li>
</ul> </ul>
</div> </div>
<h2 className="text-2xl font-bold mb-3"></h2> <h2 className="text-2xl font-bold mb-3">{t('model_integration.configuration')}</h2>
<CodeEditor <CodeEditor
code={` code={`

View File

@@ -7,7 +7,7 @@ export default function SecurityPermissions() {
<BetaNotice /> <BetaNotice />
<p className="text-xl text-foreground/80 mb-8 leading-relaxed"> <p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
page-agent AI page-agent AI
</p> </p>
@@ -19,13 +19,15 @@ export default function SecurityPermissions() {
<h3 className="text-lg font-semibold text-red-900 dark:text-red-300"> <h3 className="text-lg font-semibold text-red-900 dark:text-red-300">
🚫 🚫
</h3> </h3>
<p className="text-foreground/80"> AI </p> <p className="text-gray-600 dark:text-gray-300">
AI
</p>
</div> </div>
<div className="p-4 bg-green-50 dark:bg-green-900/20 rounded-lg"> <div className="p-4 bg-green-50 dark:bg-green-900/20 rounded-lg">
<h3 className="text-lg font-semibold text-green-900 dark:text-green-300"> <h3 className="text-lg font-semibold text-green-900 dark:text-green-300">
</h3> </h3>
<p className="text-foreground/80"> AI </p> <p className="text-gray-600 dark:text-gray-300"> AI </p>
</div> </div>
</div> </div>
</section> </section>
@@ -37,13 +39,13 @@ export default function SecurityPermissions() {
<h3 className="text-lg font-semibold text-red-900 dark:text-red-300"> <h3 className="text-lg font-semibold text-red-900 dark:text-red-300">
🚫 URL 🚫 URL
</h3> </h3>
<p className="text-foreground/80"> AI 访</p> <p className="text-gray-600 dark:text-gray-300"> AI 访</p>
</div> </div>
<div className="p-4 bg-green-50 dark:bg-green-900/20 rounded-lg"> <div className="p-4 bg-green-50 dark:bg-green-900/20 rounded-lg">
<h3 className="text-lg font-semibold text-green-900 dark:text-green-300"> <h3 className="text-lg font-semibold text-green-900 dark:text-green-300">
URL URL
</h3> </h3>
<p className="text-foreground/80"> AI 访</p> <p className="text-gray-600 dark:text-gray-300"> AI 访</p>
</div> </div>
</div> </div>
</section> </section>
@@ -54,17 +56,21 @@ export default function SecurityPermissions() {
<h3 className="text-lg font-semibold mb-2 text-yellow-900 dark:text-yellow-300"> <h3 className="text-lg font-semibold mb-2 text-yellow-900 dark:text-yellow-300">
</h3> </h3>
<p className="text-foreground/80 mb-3"> <p className="text-gray-600 dark:text-gray-300 mb-3">
AI AI
</p> </p>
<div className="space-y-2"> <div className="space-y-2">
<div className="pl-3 border-l-2 border-red-400"> <div className="pl-3 border-l-2 border-red-400">
<p className="font-medium text-red-700 dark:text-red-300"></p> <p className="font-medium text-red-700 dark:text-red-300"></p>
<p className="text-sm text-foreground/70"></p> <p className="text-sm text-gray-500 dark:text-gray-400">
</p>
</div> </div>
<div className="pl-3 border-l-2 border-orange-400"> <div className="pl-3 border-l-2 border-orange-400">
<p className="font-medium text-orange-700 dark:text-orange-300"></p> <p className="font-medium text-orange-700 dark:text-orange-300"></p>
<p className="text-sm text-foreground/70"></p> <p className="text-sm text-gray-500 dark:text-gray-400">
</p>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -19,7 +19,9 @@ export default function BestPractices() {
<h3 className="text-lg font-semibold mb-2 text-green-900 dark:text-green-300"> <h3 className="text-lg font-semibold mb-2 text-green-900 dark:text-green-300">
API API
</h3> </h3>
<p className="text-foreground/80 mb-3"> AI </p> <p className="text-gray-600 dark:text-gray-300 mb-3">
AI
</p>
<CodeEditor <CodeEditor
code={`// 推荐:合并操作 code={`// 推荐:合并操作
@@ -36,7 +38,9 @@ await pageAgent.execute('点击提交按钮');`}
<h3 className="text-lg font-semibold mb-2 text-blue-900 dark:text-blue-300"> <h3 className="text-lg font-semibold mb-2 text-blue-900 dark:text-blue-300">
🎯 🎯
</h3> </h3>
<p className="text-foreground/80">使</p> <p className="text-gray-600 dark:text-gray-300">
使
</p>
</div> </div>
</div> </div>
@@ -45,12 +49,12 @@ await pageAgent.execute('点击提交按钮');`}
<div className="space-y-3 mb-6"> <div className="space-y-3 mb-6">
<div className="p-3 bg-red-50 dark:bg-red-900/20 rounded-lg border-l-4 border-red-500"> <div className="p-3 bg-red-50 dark:bg-red-900/20 rounded-lg border-l-4 border-red-500">
<h3 className="font-semibold mb-1 text-red-900 dark:text-red-300"></h3> <h3 className="font-semibold mb-1 text-red-900 dark:text-red-300"></h3>
<p className="text-foreground/80"></p> <p className="text-gray-600 dark:text-gray-300"></p>
</div> </div>
<div className="p-3 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg border-l-4 border-yellow-500"> <div className="p-3 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg border-l-4 border-yellow-500">
<h3 className="font-semibold mb-1 text-yellow-900 dark:text-yellow-300"></h3> <h3 className="font-semibold mb-1 text-yellow-900 dark:text-yellow-300"></h3>
<p className="text-foreground/80"></p> <p className="text-gray-600 dark:text-gray-300"></p>
</div> </div>
</div> </div>

View File

@@ -27,7 +27,7 @@ export default function CdnSetup() {
<h3 className="text-lg font-semibold mb-2 text-yellow-900 dark:text-yellow-300"> <h3 className="text-lg font-semibold mb-2 text-yellow-900 dark:text-yellow-300">
</h3> </h3>
<ul className="text-foreground/80 space-y-1"> <ul className="text-gray-600 dark:text-gray-300 space-y-1">
<li> 使</li> <li> 使</li>
<li> HTTPS 使</li> <li> HTTPS 使</li>
<li> CSP </li> <li> CSP </li>

View File

@@ -4,14 +4,14 @@ export default function ThirdPartyAgentPage() {
return ( return (
<div> <div>
<h1 className="text-4xl font-bold mb-6"> Agent</h1> <h1 className="text-4xl font-bold mb-6"> Agent</h1>
<p className="mb-6 leading-relaxed"> <p className="mb-6 leading-relaxed text-gray-600 dark:text-gray-300">
pageAgent Agent Agent pageAgent Agent Agent
</p> </p>
<div className="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-4 mb-6"> <div className="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-4 mb-6">
<h3 className="text-lg font-semibold mb-2 text-blue-900 dark:text-blue-300">💡 </h3> <h3 className="text-lg font-semibold mb-2 text-blue-900 dark:text-blue-300">💡 </h3>
<p className="text-blue-800 dark:text-blue-200"> <p className="text-blue-800 dark:text-blue-200">
Agent "嘴巴""眼睛""手" "嘴巴""眼睛""手"
</p> </p>
</div> </div>
@@ -49,25 +49,25 @@ const pageAgentTool = {
<h2 className="text-2xl font-bold mb-4"></h2> <h2 className="text-2xl font-bold mb-4"></h2>
<div className="grid md:grid-cols-2 gap-4 mb-6"> <div className="grid md:grid-cols-2 gap-4 mb-6">
<div className="bg-gradient-to-br from-blue-50 to-purple-50 dark:from-gray-800 dark:to-gray-700 p-4 rounded-lg"> <div className="bg-gradient-to-br from-blue-50 to-purple-50 dark:from-gray-800 dark:to-gray-700 p-4 rounded-lg">
<h4 className="font-semibold mb-2">🤖 </h4> <h4 className="font-semibold mb-2 text-gray-900 dark:text-white">🤖 </h4>
<p className="text-sm text-gray-600 dark:text-gray-300"> <p className="text-sm text-gray-600 dark:text-gray-300">
"帮我提交工单" "帮我提交工单"
</p> </p>
</div> </div>
<div className="bg-gradient-to-br from-green-50 to-blue-50 dark:from-gray-800 dark:to-gray-700 p-4 rounded-lg"> <div className="bg-gradient-to-br from-green-50 to-blue-50 dark:from-gray-800 dark:to-gray-700 p-4 rounded-lg">
<h4 className="font-semibold mb-2">📋 </h4> <h4 className="font-semibold mb-2 text-gray-900 dark:text-white">📋 </h4>
<p className="text-sm text-gray-600 dark:text-gray-300"> <p className="text-sm text-gray-600 dark:text-gray-300">
"完成客户入职" "完成客户入职"
</p> </p>
</div> </div>
<div className="bg-gradient-to-br from-purple-50 to-pink-50 dark:from-gray-800 dark:to-gray-700 p-4 rounded-lg"> <div className="bg-gradient-to-br from-purple-50 to-pink-50 dark:from-gray-800 dark:to-gray-700 p-4 rounded-lg">
<h4 className="font-semibold mb-2">🎯 </h4> <h4 className="font-semibold mb-2 text-gray-900 dark:text-white">🎯 </h4>
<p className="text-sm text-gray-600 dark:text-gray-300"> <p className="text-sm text-gray-600 dark:text-gray-300">
"预订会议室" "预订会议室"
</p> </p>
</div> </div>
<div className="bg-gradient-to-br from-orange-50 to-red-50 dark:from-gray-800 dark:to-gray-700 p-4 rounded-lg"> <div className="bg-gradient-to-br from-orange-50 to-red-50 dark:from-gray-800 dark:to-gray-700 p-4 rounded-lg">
<h4 className="font-semibold mb-2">🔧 </h4> <h4 className="font-semibold mb-2 text-gray-900 dark:text-white">🔧 </h4>
<p className="text-sm text-gray-600 dark:text-gray-300"> <p className="text-sm text-gray-600 dark:text-gray-300">
"重启服务器" "重启服务器"
</p> </p>
@@ -77,12 +77,12 @@ const pageAgentTool = {
<h2 className="text-2xl font-bold mb-4"></h2> <h2 className="text-2xl font-bold mb-4"></h2>
<div className="space-y-4 mb-6"> <div className="space-y-4 mb-6">
<div className="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg"> <div className="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
<h3 className="text-lg font-semibold mb-2"></h3> <h3 className="text-lg font-semibold mb-2 text-gray-900 dark:text-white"></h3>
<CodeEditor code={`// @TODO`} language="javascript" /> <CodeEditor code={`// @TODO`} language="javascript" />
</div> </div>
<div className="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg"> <div className="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
<h3 className="text-lg font-semibold mb-2"></h3> <h3 className="text-lg font-semibold mb-2 text-gray-900 dark:text-white"></h3>
<CodeEditor code={`// @TODO`} language="javascript" /> <CodeEditor code={`// @TODO`} language="javascript" />
</div> </div>
</div> </div>
@@ -99,8 +99,10 @@ const pageAgentTool = {
</div> </div>
<div className="bg-gradient-to-r from-green-50 to-blue-50 dark:from-green-900/20 dark:to-blue-900/20 p-4 rounded-lg"> <div className="bg-gradient-to-r from-green-50 to-blue-50 dark:from-green-900/20 dark:to-blue-900/20 p-4 rounded-lg">
<h3 className="text-lg font-semibold mb-2">🎉 </h3> <h3 className="text-lg font-semibold mb-2 text-gray-900 dark:text-white">🎉 </h3>
<p className="mb-3"> Agent </p> <p className="mb-3 text-gray-700 dark:text-gray-300">
Agent
</p>
<a <a
href="/docs/integration/configuration" href="/docs/integration/configuration"
className="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors duration-200" className="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors duration-200"

View File

@@ -1,153 +1,141 @@
import { useTranslation } from 'react-i18next'
export default function LimitationsPage() { export default function LimitationsPage() {
const { t } = useTranslation('docs')
return ( return (
<div className="max-w-4xl mx-auto"> <div className="max-w-4xl mx-auto">
<div className="mb-8"> <div className="mb-8">
<h1 className="text-4xl font-bold mb-4 text-gray-900 dark:text-white">使</h1> <h1 className="text-4xl font-bold mb-4 text-gray-900 dark:text-white">
<p className="text-xl text-gray-600 dark:text-gray-300"> {t('limitations.title')}
page-agent </h1>
</p> <p className="text-xl text-gray-600 dark:text-gray-300">{t('limitations.subtitle')}</p>
</div> </div>
<div className="prose prose-lg dark:prose-invert max-w-none"> <div className="prose prose-lg dark:prose-invert max-w-none">
<h2 className="text-2xl font-bold mb-3"></h2> <h2 className="text-2xl font-bold mb-3">{t('limitations.page_support')}</h2>
<div className="bg-blue-50 dark:bg-blue-900/20 border-l-4 border-blue-400 p-4 mb-6"> <div className="bg-blue-50 dark:bg-blue-900/20 border-l-4 border-blue-400 p-4 mb-6">
<h3 className="font-semibold text-blue-800 dark:text-blue-200 mb-2"></h3> <h3 className="font-semibold text-blue-800 dark:text-blue-200 mb-2">
{t('limitations.spa_limit_title')}
</h3>
<ul className="text-blue-700 dark:text-blue-300 space-y-2"> <ul className="text-blue-700 dark:text-blue-300 space-y-2">
<li> <li>{t('limitations.spa_limit_1')}</li>
<strong>SPA</strong> <li>{t('limitations.spa_limit_2')}</li>
</li> <li>{t('limitations.spa_limit_3')}</li>
<li>
<strong></strong>
</li>
<li>
<strong></strong> page-agent
</li>
</ul> </ul>
</div> </div>
<h2 className="text-2xl font-bold mb-3"></h2> <h2 className="text-2xl font-bold mb-3">{t('limitations.interaction_limits')}</h2>
<div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-6 mb-6"> <div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-6 mb-6">
<h3 className="font-semibold mb-4"></h3> <h3 className="font-semibold mb-4">{t('limitations.supported_ops')}</h3>
<div className="grid md:grid-cols-2 gap-4 mb-6"> <div className="grid md:grid-cols-2 gap-4 mb-6">
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center text-green-600 dark:text-green-400"> <div className="flex items-center text-green-600 dark:text-green-400">
<span className="mr-2"></span> <span className="mr-2"></span>
<span></span> <span>{t('limitations.op_click')}</span>
</div> </div>
<div className="flex items-center text-green-600 dark:text-green-400"> <div className="flex items-center text-green-600 dark:text-green-400">
<span className="mr-2"></span> <span className="mr-2"></span>
<span></span> <span>{t('limitations.op_input')}</span>
</div> </div>
<div className="flex items-center text-green-600 dark:text-green-400"> <div className="flex items-center text-green-600 dark:text-green-400">
<span className="mr-2"></span> <span className="mr-2"></span>
<span></span> <span>{t('limitations.op_scroll')}</span>
</div> </div>
<div className="flex items-center text-green-600 dark:text-green-400"> <div className="flex items-center text-green-600 dark:text-green-400">
<span className="mr-2"></span> <span className="mr-2"></span>
<span></span> <span>{t('limitations.op_submit')}</span>
</div> </div>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center text-green-600 dark:text-green-400"> <div className="flex items-center text-green-600 dark:text-green-400">
<span className="mr-2"></span> <span className="mr-2"></span>
<span></span> <span>{t('limitations.op_select')}</span>
</div> </div>
<div className="flex items-center text-green-600 dark:text-green-400"> <div className="flex items-center text-green-600 dark:text-green-400">
<span className="mr-2"></span> <span className="mr-2"></span>
<span></span> <span>{t('limitations.op_focus')}</span>
</div> </div>
</div> </div>
</div> </div>
<h3 className="font-semibold mb-4"></h3> <h3 className="font-semibold mb-4">{t('limitations.unsupported_ops')}</h3>
<div className="grid md:grid-cols-2 gap-4"> <div className="grid md:grid-cols-2 gap-4">
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center text-red-600 dark:text-red-400"> <div className="flex items-center text-red-600 dark:text-red-400">
<span className="mr-2"></span> <span className="mr-2"></span>
<span>hover</span> <span>{t('limitations.op_hover')}</span>
</div> </div>
<div className="flex items-center text-red-600 dark:text-red-400"> <div className="flex items-center text-red-600 dark:text-red-400">
<span className="mr-2"></span> <span className="mr-2"></span>
<span></span> <span>{t('limitations.op_drag')}</span>
</div> </div>
<div className="flex items-center text-red-600 dark:text-red-400"> <div className="flex items-center text-red-600 dark:text-red-400">
<span className="mr-2"></span> <span className="mr-2"></span>
<span></span> <span>{t('limitations.op_context')}</span>
</div> </div>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center text-red-600 dark:text-red-400"> <div className="flex items-center text-red-600 dark:text-red-400">
<span className="mr-2"></span> <span className="mr-2"></span>
<span></span> <span>{t('limitations.op_draw')}</span>
</div> </div>
<div className="flex items-center text-red-600 dark:text-red-400"> <div className="flex items-center text-red-600 dark:text-red-400">
<span className="mr-2"></span> <span className="mr-2"></span>
<span></span> <span>{t('limitations.op_keyboard')}</span>
</div> </div>
<div className="flex items-center text-red-600 dark:text-red-400"> <div className="flex items-center text-red-600 dark:text-red-400">
<span className="mr-2"></span> <span className="mr-2"></span>
<span></span> <span>{t('limitations.op_position')}</span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<h2 className="text-2xl font-bold mb-3"></h2> <h2 className="text-2xl font-bold mb-3">{t('limitations.understanding_limits')}</h2>
<div className="bg-red-50 dark:bg-red-900/20 border-l-4 border-red-400 p-4 mb-6"> <div className="bg-red-50 dark:bg-red-900/20 border-l-4 border-red-400 p-4 mb-6">
<h3 className="font-semibold text-red-800 dark:text-red-200 mb-2"></h3> <h3 className="font-semibold text-red-800 dark:text-red-200 mb-2">
<p className="text-red-700 dark:text-red-300 mb-3"> {t('limitations.no_vision_title')}
page-agent DOM <strong></strong> </h3>
<p className="text-red-700 dark:text-red-300 mb-3">{t('limitations.no_vision_desc')}</p>
</p>
<ul className="text-red-700 dark:text-red-300 space-y-1"> <ul className="text-red-700 dark:text-red-300 space-y-1">
<li> <li>{t('limitations.no_vision_1')}</li>
<strong></strong> <li>{t('limitations.no_vision_2')}</li>
</li> <li>{t('limitations.no_vision_3')}</li>
<li> <li>{t('limitations.no_vision_4')}</li>
<strong>Canvas </strong> Canvas
</li>
<li>
<strong>WebGL 3D </strong> 3D
</li>
<li>
<strong>SVG </strong> SVG
</li>
</ul> </ul>
</div> </div>
<h2 className="text-2xl font-bold mb-3"></h2> <h2 className="text-2xl font-bold mb-3">{t('limitations.website_requirements')}</h2>
<div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-6 mb-6"> <div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-6 mb-6">
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
<h3 className="font-semibold mb-2"></h3> <h3 className="font-semibold mb-2">{t('limitations.req_semantic_title')}</h3>
<p className="text-gray-600 dark:text-gray-300"> <p className="text-gray-600 dark:text-gray-300">
DOM {t('limitations.req_semantic_desc')}
accessibility AI
</p> </p>
</div> </div>
<div> <div>
<h3 className="font-semibold mb-2">UI/UX</h3> <h3 className="font-semibold mb-2">{t('limitations.req_ux_title')}</h3>
<p className="text-gray-600 dark:text-gray-300"> <p className="text-gray-600 dark:text-gray-300">{t('limitations.req_ux_desc')}</p>
AI
</p>
</div> </div>
<div> <div>
<h3 className="font-semibold mb-2"></h3> <h3 className="font-semibold mb-2">{t('limitations.req_env_title')}</h3>
<p className="text-gray-600 dark:text-gray-300">modern browser</p> <p className="text-gray-600 dark:text-gray-300">{t('limitations.req_env_desc')}</p>
</div> </div>
</div> </div>
</div> </div>
<h2></h2> <h2>{t('limitations.future')}</h2>
<div className="bg-green-50 dark:bg-green-900/20 border-l-4 border-green-400 p-4"> <div className="bg-green-50 dark:bg-green-900/20 border-l-4 border-green-400 p-4">
<h3 className="font-semibold text-green-800 dark:text-green-200 mb-2"></h3> <h3 className="font-semibold text-green-800 dark:text-green-200 mb-2">
{t('limitations.future_title')}
</h3>
<ul className="text-green-700 dark:text-green-300 space-y-1"> <ul className="text-green-700 dark:text-green-300 space-y-1">
<li> </li> <li>{t('limitations.future_1')}</li>
<li> </li> <li>{t('limitations.future_2')}</li>
<li> </li> <li>{t('limitations.future_3')}</li>
<li> </li> <li>{t('limitations.future_4')}</li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@@ -1,75 +1,117 @@
import { useTranslation } from 'react-i18next'
export default function Overview() { export default function Overview() {
const { t } = useTranslation('docs')
return ( return (
<article> <article>
{/* 头图 */} {/* 头图 */}
<figure className="mb-8 rounded-xl overflow-hidden"> <figure className="mb-8 rounded-xl overflow-hidden">
<img <img
src="https://img.alicdn.com/imgextra/i1/O1CN01RY0Wvh26ATVeDIX7v_!!6000000007621-0-tps-1672-512.jpg" src="https://img.alicdn.com/imgextra/i1/O1CN01RY0Wvh26ATVeDIX7v_!!6000000007621-0-tps-1672-512.jpg"
alt="page-agent 概览图示" alt="page-agent"
className="w-full h-64 object-cover" className="w-full h-64 object-cover"
/> />
</figure> </figure>
<h1 className="text-4xl font-bold mb-6">Overview</h1> <div className="mb-8">
<h1 className="text-4xl font-bold mb-4">{t('overview.title')}</h1>
<p className="text-xl text-foreground/80 mb-8 leading-relaxed"> <p className="text-xl text-gray-600 dark:text-gray-300 mb-4 leading-relaxed">
page-agent Web技术的 UI Agent AI {t('overview.subtitle')}
</p> </p>
<section> {/* Status Badges */}
<h2 className="text-2xl font-bold mb-4"> page-agent</h2> <div className="flex flex-wrap gap-2 items-center">
<a
href="https://www.npmjs.com/package/page-agent"
target="_blank"
rel="noopener noreferrer"
>
<img src="https://badge.fury.io/js/page-agent.svg" alt="npm version" />
</a>
<a href="https://opensource.org/licenses/MIT" target="_blank" rel="noopener noreferrer">
<img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="MIT License" />
</a>
<a href="http://www.typescriptlang.org/" target="_blank" rel="noopener noreferrer">
<img
src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg"
alt="TypeScript"
/>
</a>
<a
href="https://www.npmjs.com/package/page-agent"
target="_blank"
rel="noopener noreferrer"
>
<img src="https://img.shields.io/npm/dt/page-agent.svg" alt="Downloads" />
</a>
<a
href="https://bundlephobia.com/package/page-agent"
target="_blank"
rel="noopener noreferrer"
>
<img src="https://img.shields.io/bundlephobia/minzip/page-agent" alt="Bundle Size" />
</a>
<a href="https://github.com/alibaba/page-agent" target="_blank" rel="noopener noreferrer">
<img
src="https://img.shields.io/github/stars/alibaba/page-agent.svg"
alt="GitHub stars"
/>
</a>
</div>
</div>
<p className="text-foreground/80 mb-8 leading-relaxed "> <section>
page-agent <strong> UI Agent</strong> <h2 className="text-2xl font-bold mb-4">{t('overview.what_is')}</h2>
page-agent <strong></strong>
Agent开发者 Agent <p className="text-gray-600 dark:text-gray-300 mb-8 leading-relaxed ">
{t('overview.what_is_desc')}
</p> </p>
</section> </section>
<section> <section>
<h2 className="text-2xl font-bold mb-3"></h2> <h2 className="text-2xl font-bold mb-3">{t('overview.features_title')}</h2>
<div className="grid md:grid-cols-2 gap-4 mb-8" role="list"> <div className="grid md:grid-cols-2 gap-4 mb-8" role="list">
<div className="p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg"> <div className="p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
<h3 className="text-lg font-semibold mb-2 text-blue-900 dark:text-blue-300"> <h3 className="text-lg font-semibold mb-2 text-blue-900 dark:text-blue-300">
🧠 DOM {t('overview.feature_dom.title')}
</h3> </h3>
<p className=""> DOM </p> <p className="text-gray-700 dark:text-gray-300">{t('overview.feature_dom.desc')}</p>
</div> </div>
<div className="p-4 bg-purple-50 dark:bg-purple-900/20 rounded-lg"> <div className="p-4 bg-purple-50 dark:bg-purple-900/20 rounded-lg">
<h3 className="text-lg font-semibold mb-2 text-purple-900 dark:text-purple-300"> <h3 className="text-lg font-semibold mb-2 text-purple-900 dark:text-purple-300">
🔒 {t('overview.feature_secure.title')}
</h3> </h3>
<p className=""> <p className="text-gray-700 dark:text-gray-300">{t('overview.feature_secure.desc')}</p>
AI
</p>
</div> </div>
<div className="p-4 bg-green-50 dark:bg-green-900/20 rounded-lg"> <div className="p-4 bg-green-50 dark:bg-green-900/20 rounded-lg">
<h3 className="text-lg font-semibold mb-2 text-green-900 dark:text-green-300"> <h3 className="text-lg font-semibold mb-2 text-green-900 dark:text-green-300">
{t('overview.feature_backend.title')}
</h3> </h3>
<p className="">CDN NPM LLM </p> <p className="text-gray-700 dark:text-gray-300">{t('overview.feature_backend.desc')}</p>
</div> </div>
<div className="p-4 bg-orange-50 dark:bg-orange-900/20 rounded-lg"> <div className="p-4 bg-orange-50 dark:bg-orange-900/20 rounded-lg">
<h3 className="text-lg font-semibold mb-2 text-orange-900 dark:text-orange-300"> <h3 className="text-lg font-semibold mb-2 text-orange-900 dark:text-orange-300">
{t('overview.feature_accessible.title')}
</h3> </h3>
<p className=""> B端系统</p> <p className="text-gray-700 dark:text-gray-300">
{t('overview.feature_accessible.desc')}
</p>
</div> </div>
</div> </div>
<h2 className="text-2xl font-bold mb-4"> browser-use </h2> <h2 className="text-2xl font-bold mb-4">{t('overview.vs_browser_use')}</h2>
<div className="overflow-x-auto mb-8"> <div className="overflow-x-auto mb-8">
<table className="w-full border-collapse border border-gray-300 dark:border-gray-600"> <table className="w-full border-collapse border border-gray-300 dark:border-gray-600">
<thead> <thead>
<tr className="bg-gray-50 dark:bg-gray-800"> <tr className="bg-gray-50 dark:bg-gray-800">
<th className="border border-gray-300 dark:border-gray-600 px-4 py-3 text-left"> <th className="border border-gray-300 dark:border-gray-600 px-4 py-3 text-left">
{t('overview.table_feature')}
</th> </th>
<th className="border border-gray-300 dark:border-gray-600 px-4 py-3 text-left"> <th className="border border-gray-300 dark:border-gray-600 px-4 py-3 text-left">
page-agent page-agent
@@ -82,103 +124,88 @@ export default function Overview() {
<tbody> <tbody>
<tr> <tr>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3 font-medium"> <td className="border border-gray-300 dark:border-gray-600 px-4 py-3 font-medium">
{t('overview.table_deployment')}
</td> </td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3"> <td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
{t('overview.table_deployment_pa')}
</td> </td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3"></td>
</tr>
<tr>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3 font-medium">
</td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3"></td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3"> <td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
{t('overview.table_deployment_bu')}
</td> </td>
</tr> </tr>
<tr> <tr>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3 font-medium"> <td className="border border-gray-300 dark:border-gray-600 px-4 py-3 font-medium">
{t('overview.table_scope')}
</td> </td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3"> <td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
{t('overview.table_scope_pa')}
</td> </td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3"> <td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
/Agent {t('overview.table_scope_bu')}
</td> </td>
</tr> </tr>
<tr> <tr>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3 font-medium"> <td className="border border-gray-300 dark:border-gray-600 px-4 py-3 font-medium">
使 {t('overview.table_user')}
</td> </td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3"> <td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
{t('overview.table_user_pa')}
</td> </td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3"> <td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
{t('overview.table_user_bu')}
</td>
</tr>
<tr>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3 font-medium">
{t('overview.table_scenario')}
</td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
{t('overview.table_scenario_pa')}
</td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
{t('overview.table_scenario_bu')}
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<h2 className="text-2xl font-bold mb-4"></h2> <h2 className="text-2xl font-bold mb-4">{t('overview.use_cases_title')}</h2>
<ul className="space-y-4 mb-8"> <ul className="space-y-4 mb-8">
<li className="flex items-start space-x-3"> <li className="flex items-start space-x-3">
<span className="w-6 h-6 bg-blue-500 text-white rounded-full flex items-center justify-center font-bold mt-0.5"> <span className="w-6 h-6 min-w-6 bg-blue-500 text-white rounded-full flex items-center justify-center font-bold text-sm mt-0.5 flex-shrink-0">
1 1
</span> </span>
<div className=""> <div className="text-gray-700 dark:text-gray-300">
<strong></strong> <strong>{t('overview.use_case1_title')}</strong> {t('overview.use_case1_desc')}
Agent"请先点击设置按钮然后点击..."
</div> </div>
</li> </li>
<li className="flex items-start space-x-3"> <li className="flex items-start space-x-3">
<span className="w-6 h-6 bg-green-500 text-white rounded-full flex items-center justify-center font-bold mt-0.5"> <span className="w-6 h-6 min-w-6 bg-green-500 text-white rounded-full flex items-center justify-center font-bold text-sm mt-0.5 flex-shrink-0">
2 2
</span> </span>
<div className=""> <div className="text-gray-700 dark:text-gray-300">
<strong>/</strong> <strong>{t('overview.use_case2_title')}</strong> {t('overview.use_case2_desc')}
Agent B
</div> </div>
</li> </li>
<li className="flex items-start space-x-3"> <li className="flex items-start space-x-3">
<span className="w-6 h-6 bg-purple-500 text-white rounded-full flex items-center justify-center font-bold mt-0.5"> <span className="w-6 h-6 min-w-6 bg-purple-500 text-white rounded-full flex items-center justify-center font-bold text-sm mt-0.5 flex-shrink-0">
3 3
</span> </span>
<div className=""> <div className="text-gray-700 dark:text-gray-300">
<strong></strong> <strong>{t('overview.use_case3_title')}</strong> {t('overview.use_case3_desc')}
AI演示"如何提交报销申请"
</div> </div>
</li> </li>
<li className="flex items-start space-x-3"> <li className="flex items-start space-x-3">
<span className="w-6 h-6 bg-orange-500 text-white rounded-full flex items-center justify-center font-bold mt-0.5"> <span className="w-6 h-6 min-w-6 bg-orange-500 text-white rounded-full flex items-center justify-center font-bold text-sm mt-0.5 flex-shrink-0">
4 4
</span> </span>
<div className=""> <div className="text-gray-700 dark:text-gray-300">
<strong></strong> <strong>{t('overview.use_case4_title')}</strong> {t('overview.use_case4_desc')}
</div> </div>
</li> </li>
</ul> </ul>
<div className="bg-gradient-to-r from-blue-50 to-purple-50 dark:from-blue-900/20 dark:to-purple-900/20 p-4 rounded-lg">
<h3 className="text-lg font-semibold mb-2">🚀 使</h3>
<p className="mb-3 ">
AI
</p>
<a
href="/docs/introduction/quick-start"
className="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors duration-200"
role="button"
>
</a>
</div>
</section> </section>
</article> </article>
) )

View File

@@ -1,23 +1,26 @@
import BetaNotice from '@pages/components/BetaNotice' import BetaNotice from '@pages/components/BetaNotice'
import CodeEditor from '@pages/components/CodeEditor' import CodeEditor from '@pages/components/CodeEditor'
import { useTranslation } from 'react-i18next'
export default function QuickStart() { export default function QuickStart() {
const { t } = useTranslation('docs')
return ( return (
<div> <div>
<h1 className="text-4xl font-bold mb-6">Quick Start</h1> <h1 className="text-4xl font-bold mb-6">{t('quick_start.title')}</h1>
<p className=" mb-6 leading-relaxed"> page-agent </p> <p className=" mb-6 leading-relaxed">{t('quick_start.subtitle')}</p>
<h2 className="text-2xl font-bold mb-3"></h2> <h2 className="text-2xl font-bold mb-3">{t('quick_start.installation')}</h2>
<div className="space-y-4 mb-6"> <div className="space-y-4 mb-6">
<div className="p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg"> <div className="p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
<h3 className="text-lg font-semibold mb-2 text-blue-900 dark:text-blue-300"> <h3 className="text-lg font-semibold mb-2 text-blue-900 dark:text-blue-300">
1. {t('quick_start.step1_title')}
</h3> </h3>
<div className="space-y-3"> <div className="space-y-3">
<div> <div>
<p className="text-sm font-medium mb-2">CDN </p> <p className="text-sm font-medium mb-2">{t('quick_start.step1_cdn')}</p>
<CodeEditor <CodeEditor
code={`// 仅供测试使用 code={`// 仅供测试使用
<script src="https://hwcxiuzfylggtcktqgij.supabase.co/storage/v1/object/public/demo-public/v0.0.2/page-agent.js" crossorigin="true" type="text/javascript"></script>`} <script src="https://hwcxiuzfylggtcktqgij.supabase.co/storage/v1/object/public/demo-public/v0.0.2/page-agent.js" crossorigin="true" type="text/javascript"></script>`}
@@ -25,7 +28,7 @@ export default function QuickStart() {
/> />
</div> </div>
<div> <div>
<p className="text-sm font-medium mb-2">NPM </p> <p className="text-sm font-medium mb-2">{t('quick_start.step1_npm')}</p>
<CodeEditor <CodeEditor
code={`// npm install page-agent code={`// npm install page-agent
import PageAgent from 'page-agent'`} import PageAgent from 'page-agent'`}
@@ -37,7 +40,7 @@ import PageAgent from 'page-agent'`}
<div className="p-4 bg-green-50 dark:bg-green-900/20 rounded-lg"> <div className="p-4 bg-green-50 dark:bg-green-900/20 rounded-lg">
<h3 className="text-lg font-semibold mb-2 text-green-900 dark:text-green-300"> <h3 className="text-lg font-semibold mb-2 text-green-900 dark:text-green-300">
2. {t('quick_start.step2_title')}
</h3> </h3>
<CodeEditor <CodeEditor
code={`// 仅供测试使用,生产环境需要配置 LLM 接入点,本工具不提供 LLM 服务 code={`// 仅供测试使用,生产环境需要配置 LLM 接入点,本工具不提供 LLM 服务
@@ -60,7 +63,7 @@ const agent = new PageAgent({
<div className="p-4 bg-purple-50 dark:bg-purple-900/20 rounded-lg"> <div className="p-4 bg-purple-50 dark:bg-purple-900/20 rounded-lg">
<h3 className="text-lg font-semibold mb-2 text-purple-900 dark:text-purple-300"> <h3 className="text-lg font-semibold mb-2 text-purple-900 dark:text-purple-300">
3. 使 {t('quick_start.step3_title')}
</h3> </h3>
<CodeEditor <CodeEditor
code={`// 程序化执行自然语言指令 code={`// 程序化执行自然语言指令

74
pages/i18n/README.md Normal file
View File

@@ -0,0 +1,74 @@
# 国际化配置说明
本项目使用 `react-i18next` 实现国际化支持。
## 目录结构
```
pages/i18n/
├── config.ts # i18next 配置和初始化
├── types.ts # TypeScript 类型声明
├── locales/
│ ├── zh-CN/ # 中文翻译
│ │ ├── common.json # 通用组件Header, Footer等
│ │ ├── home.json # 首页
│ │ └── docs.json # 文档页(待完善)
│ └── en-US/ # 英文翻译
│ ├── common.json
│ ├── home.json
│ └── docs.json
└── README.md # 本文件
```
## 使用方法
### 在组件中使用
```tsx
import { useTranslation } from 'react-i18next'
function MyComponent() {
const { t } = useTranslation('common') // 指定命名空间
return <h1>{t('header.nav_docs')}</h1>
}
```
### 使用多个命名空间
```tsx
const { t } = useTranslation(['home', 'common'])
// 使用时指定命名空间
t('home:hero.title')
t('common:header.nav_docs')
```
## 语言切换
用户可以通过以下方式切换语言:
1. **自动检测**:首次访问根据浏览器语言自动选择
2. **手动切换**:点击页面右上角的语言切换按钮
3. **持久化**:语言选择保存在 `localStorage` 中,刷新后保持
## 添加新翻译
1. 在对应的 JSON 文件中添加翻译条目(如 `zh-CN/home.json`
2. 在对应的英文文件中添加翻译(如 `en-US/home.json`
3. 在组件中使用 `t('namespace:key')` 获取翻译
## TypeScript 支持
`types.ts` 文件提供了类型声明,使得翻译 key 具有:
- 自动补全
- 编译期类型检查
- 防止拼写错误
## 待完成
- [ ] 文档页翻译(`docs.json`
- [ ] DocsLayout 导航结构国际化
- [ ] 404 页面国际化

45
pages/i18n/config.ts Normal file
View File

@@ -0,0 +1,45 @@
import i18n from 'i18next'
import LanguageDetector from 'i18next-browser-languagedetector'
import { initReactI18next } from 'react-i18next'
import commonEn from './locales/en-US/common'
import docsEn from './locales/en-US/docs'
import homeEn from './locales/en-US/home'
import commonZh from './locales/zh-CN/common'
import docsZh from './locales/zh-CN/docs'
import homeZh from './locales/zh-CN/home'
const resources = {
'zh-CN': {
common: commonZh,
home: homeZh,
docs: docsZh,
},
'en-US': {
common: commonEn,
home: homeEn,
docs: docsEn,
},
}
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources,
fallbackLng: 'en-US',
defaultNS: 'common',
// 语言检测配置
detection: {
// localStorage 优先(用户手动选择),其次检测浏览器语言
order: ['localStorage', 'navigator'],
caches: ['localStorage'],
},
interpolation: {
escapeValue: false, // React 已经做了 XSS 防护
},
})
export default i18n

View File

@@ -0,0 +1,40 @@
export default {
header: {
logo_alt: 'page-agent home',
slogan: 'GUI Agent in your webpage',
nav_docs: 'Docs',
nav_source: 'GitHub',
mobile_menu: 'Open mobile menu',
},
footer: {
copyright: '© 2025 page-agent. All rights reserved.',
github_label: 'Visit GitHub repository',
},
beta_notice: {
title: 'Beta Stage',
content:
'Current features are incomplete and the API may change at any time. Please do not use in production environments before the official release.',
},
language: {
zh: '中文',
en: 'English',
switch_label: 'Switch language',
},
nav: {
introduction: 'Introduction',
features: 'Features',
integration: 'Integration',
overview: 'Overview',
quick_start: 'Quick Start',
limitations: 'Limitations',
model_integration: 'Model Integration',
custom_tools: 'Custom Tools',
knowledge_injection: 'Knowledge Injection',
security_permissions: 'Security & Permissions',
data_masking: 'Data Masking',
cdn_setup: 'CDN Setup',
configuration: 'Configuration',
best_practices: 'Best Practices',
third_party_agent: 'Third-party Agent',
},
}

View File

@@ -0,0 +1,162 @@
export default {
overview: {
title: 'Overview',
subtitle:
'page-agent is a pure web-based GUI Agent. Simple integration gives your website an AI operator.',
what_is: 'What is page-agent?',
what_is_desc:
'page-agent is an embedded GUI Agent. Unlike traditional browser automation tools, page-agent targets web developers, not scrapers or generic agent builders. Integrate it into your site to let users interact with pages through natural language.',
features_title: 'Core Features',
feature_dom: {
title: '🧠 Smart DOM Analysis',
desc: 'DOM-based analysis with high-intensity dehydration. No visual recognition needed—pure text for precise operations.',
},
feature_secure: {
title: '🔒 Secure & Controllable',
desc: 'Supports operation allowlists, data masking protection. Inject custom knowledge to make AI work by your rules.',
},
feature_backend: {
title: '⚡ Zero Backend',
desc: 'CDN or NPM import with custom LLM endpoints.',
},
feature_accessible: {
title: '♿ Accessible Intelligence',
desc: 'Provides natural language interface for complex B2B systems and admin panels. Makes software easy for everyone.',
},
vs_browser_use: 'vs. browser-use',
table_feature: 'Feature',
table_deployment: 'Deployment',
table_deployment_pa: 'Embedded component',
table_deployment_bu: 'External tool',
table_scope: 'Scope',
table_scope_pa: 'Current page',
table_scope_bu: 'Entire browser',
table_user: 'Target Users',
table_user_pa: 'Web developers',
table_user_bu: 'Scraper/Agent developers',
table_scenario: 'Use Case',
table_scenario_pa: 'UX enhancement',
table_scenario_bu: 'Automation tasks',
use_cases_title: 'Use Cases',
use_case1_title: 'Connect Support Bots:',
use_case1_desc:
"Turn your support assistant into a full agent. Customer service bots no longer just say 'Please click the settings button then click...'—they operate for users directly.",
use_case2_title: 'Modernize Legacy Apps:',
use_case2_desc:
'One line of code transforms old apps into agents. Product experts help users navigate complex B2B software. Reduce support costs and improve satisfaction.',
use_case3_title: 'Interactive Training:',
use_case3_desc:
"Demonstrate workflows in real-time. Let AI show the complete process of 'how to submit an expense report.'",
use_case4_title: 'Accessibility:',
use_case4_desc:
'Provide natural language interaction for visually impaired and elderly users. Connect screen readers or voice assistants to make software accessible to everyone.',
get_started_title: '🚀 Get Started',
get_started_desc:
'Ready to add an AI operator to your website? Check our quick start guide for integration in minutes.',
get_started_button: 'Quick Start →',
},
quick_start: {
title: 'Quick Start',
subtitle: 'Integrate page-agent in minutes.',
installation: 'Installation Steps',
step1_title: '1. Import Options',
step1_cdn: 'CDN Import',
step1_npm: 'NPM Install',
step2_title: '2. Initialize Configuration',
step3_title: '3. Start Using',
},
limitations: {
title: 'Limitations',
subtitle: "Understand page-agent's current capabilities and technical constraints",
page_support: 'Page Support Limitations',
spa_limit_title: 'Single Page Application Limits',
spa_limit_1: '• SPA only: Currently operates within a single page',
spa_limit_2: '• Multi-page relay in design: Cannot execute continuous tasks across pages yet',
spa_limit_3: '• Requires integration: Cannot operate on sites without page-agent',
interaction_limits: 'Interaction Limitations',
supported_ops: 'Supported Operations',
op_click: 'Click',
op_input: 'Text input',
op_scroll: 'Scroll',
op_submit: 'Form submit',
op_select: 'Select',
op_focus: 'Focus',
unsupported_ops: 'Unsupported Operations',
op_hover: 'Mouse hover',
op_drag: 'Drag & drop',
op_context: 'Right-click menu',
op_draw: 'Drawing',
op_keyboard: 'Keyboard shortcuts',
op_position: 'Position-based control',
understanding_limits: 'Understanding Limitations',
no_vision_title: 'No Visual Recognition',
no_vision_desc:
'page-agent operates based on DOM structure with no visual recognition. Cannot understand:',
no_vision_1: '• Image content: Cannot recognize text, icons, or visual elements in images',
no_vision_2: '• Canvas: Cannot understand graphics drawn on Canvas',
no_vision_3: '• WebGL 3D: Cannot operate elements in 3D scenes',
no_vision_4: '• SVG graphics: Cannot understand visual content and paths in SVG',
website_requirements: 'Website Requirements',
req_semantic_title: 'Semantic & Usability',
req_semantic_desc:
'All operations rely on semantic tags and attributes. Poor semantic structure or lack of accessibility features may affect AI understanding accuracy.',
req_ux_title: 'UI/UX',
req_ux_desc:
'Counter-intuitive interaction rules, visual-only operation hints, complex mouse interactions, or rapidly appearing/disappearing elements can affect AI understanding and operation.',
req_env_title: 'Environment',
req_env_desc: 'modern browser',
future: 'Future Plans',
future_title: 'Coming Soon',
future_1: '• Multi-page relay capabilities',
future_2: '• Richer mouse interaction support',
future_3: '• Basic visual understanding',
future_4: '• Smarter error recovery',
},
model_integration: {
title: 'Model Integration',
subtitle:
'Supports OpenAI-compatible models with tool call support, including public cloud services and private deployments.',
recommended: 'Recommended Models',
model_gpt4_title: '⚡ gpt-4.1-mini',
model_gpt4_badge: 'Evaluation Baseline ✅',
model_gpt4_1: '• Cost-effective',
model_gpt4_2: '• Fast',
model_gpt4_3: '• High success rate',
model_deepseek_title: '💰 DeepSeek-3.2',
model_deepseek_badge: 'Economical',
model_deepseek_1: '• Much cheaper than similar models',
model_deepseek_2: '• ToolCall may error but usually auto-recovers',
model_deepseek_3: "• This site's free demo uses DeepSeek",
model_qwen_title: '🛡️ qwen3',
model_qwen_badge: 'Secure & Compliant',
model_qwen_1: '• Controllable, decent results, reasonable price',
model_qwen_2: '• ToolCall may error but usually auto-recovers',
model_qwen_3: '• Best for scenarios with detailed steps',
model_gemini_title: '⚡ gemini-2.5-flash',
model_gemini_badge: 'Highly efficient, high success rate, reasonable price',
available: 'Available Models',
available_verified: '✅ Verified Working',
tips: 'Tips',
tip_1: 'Reasoning models (like GPT-5) are slower with no advantage',
tip_2:
"Non-OpenAI models don't guarantee JSON schema compliance—tool call may error but usually recovers. Higher temperature recommended",
tip_3: 'Small/nano models perform poorly',
configuration: 'Configuration',
},
custom_tools: {
title: 'Custom Tools',
subtitle:
'Extend AI Agent capabilities by registering custom tools. Use Zod to define strict input schemas for safe business logic calls.',
registration: 'Tool Registration',
registration_desc:
'Each custom tool requires four core properties: name, description, input schema, and execute function.',
page_filter: 'Page Filter',
page_filter_desc:
'Control tool visibility on specific pages via the pageFilter property to enhance security and UX.',
best_practices: 'Best Practices',
bp_performance: '⚡ Performance Optimization',
bp_1: '• Use pageFilter to reduce unnecessary tool loading',
bp_2: '• Implement appropriate caching in execute functions',
bp_3: '• Avoid long-running sync operations in tools',
},
}

View File

@@ -0,0 +1,78 @@
export default {
hero: {
badge: 'GUI Agent in your webpage',
title_line1: 'An AI Operator',
title_line2: 'Living in Your Web Apps',
subtitle_emoji: '🪄 One line of CDN',
subtitle_main: ' adds intelligent GUI Agents to your website.',
subtitle_detail: 'Users give natural language commands, AI handles the rest.',
tab_try: '🚀 Try It Now',
tab_other: '🌐 Try on Other Sites',
input_placeholder: 'Describe what you want AI to do...',
execute_button: 'Run',
default_task: 'Navigate to docs, find data masking section, and summarize in markdown',
},
try_other: {
step1_title: 'Step 1:',
step1_content: 'Show your bookmarks bar',
step2_title: 'Step 2:',
step2_content: 'Drag this button to your bookmarks',
step3_title: 'Step 3:',
step3_content: 'Click the bookmark on any site to activate',
notice_title: '⚠️ Heads Up',
notice_items: {
item1: 'Demo only—link may expire without notice',
item2: 'This free demo uses DeepSeek API (see their terms and privacy policy)',
item3: 'Some sites block script injection (CSP policies)',
item4: 'Works on single-page apps only—reload required after navigation',
item5: 'Text-only understanding—no image recognition or drag-and-drop',
item6_prefix: 'Full limitations in',
item6_link: 'Docs',
},
},
benefits: {
no_backend: 'Zero Backend',
private_model: 'Your Own Models',
data_masking: 'Built-in Privacy',
open_source: 'MIT Open Source',
},
features: {
section_title: 'Why PageAgent',
dom_understanding: {
title: 'Smart DOM Analysis',
desc: 'Analyzes page structure without screenshots or vision models. Pure text understanding for fast, accurate automation.',
},
secure_integration: {
title: 'Secure by Design',
desc: 'Control what AI can access with allowlists, data masking, and custom knowledge injection. Your rules, your data.',
},
zero_backend: {
title: 'Zero Backend Setup',
desc: 'Just drop in a script tag. Works with any LLM provider—OpenAI, Anthropic, or your own models.',
},
accessible: {
title: 'Natural Language UI',
desc: 'Transform complex admin panels into chat interfaces. Make powerful tools accessible to everyone, not just experts.',
},
},
use_cases: {
section_title: 'Where It Shines',
section_subtitle: 'From simple forms to complex workflows, AI understands and executes',
case1: {
title: 'Supercharge Support Bots',
desc: 'Stop telling users where to click—let AI do it for them. Turn your chatbot from a guide into an operator that actually completes tasks.',
},
case2: {
title: 'Modernize Legacy Apps',
desc: 'Add AI superpowers to old software without rebuilding. One script tag transforms complex enterprise tools into chat-driven interfaces.',
},
case3: {
title: 'Interactive Walkthroughs',
desc: "Show, don't tell. Let AI demonstrate workflows in real-time—perfect for onboarding or training new users on complex systems.",
},
case4: {
title: 'Accessibility First',
desc: 'Make web apps accessible through natural language. Perfect for screen readers, voice control, or users who find traditional interfaces challenging.',
},
},
}

View File

@@ -0,0 +1,39 @@
export default {
header: {
logo_alt: 'page-agent 首页',
slogan: 'GUI Agent in your webpage',
nav_docs: '文档',
nav_source: 'GitHub',
mobile_menu: '打开移动端菜单',
},
footer: {
copyright: '© 2025 page-agent. All rights reserved.',
github_label: '访问 GitHub 仓库',
},
beta_notice: {
title: 'Beta 阶段',
content: '当前功能未完成,接口可能随时变更。正式版本发布前请勿用于生产环境。',
},
language: {
zh: '中文',
en: 'English',
switch_label: '切换语言',
},
nav: {
introduction: '介绍',
features: '功能特性',
integration: '集成指南',
overview: '概览',
quick_start: '快速开始',
limitations: '使用限制',
model_integration: '模型接入',
custom_tools: '自定义工具',
knowledge_injection: '知识库注入',
security_permissions: '安全与权限',
data_masking: '数据脱敏',
cdn_setup: 'CDN 引入',
configuration: '配置选项',
best_practices: '最佳实践',
third_party_agent: '接入第三方 Agent',
},
}

View File

@@ -0,0 +1,158 @@
export default {
overview: {
title: 'Overview',
subtitle: 'page-agent 是一个完全基于Web技术的 GUI Agent简单引入让你的网站拥有 AI 操作员。',
what_is: '什么是 page-agent',
what_is_desc:
'page-agent 是一个页面内嵌式 GUI Agent。与传统的浏览器自动化工具不同page-agent 面向网站开发者而非爬虫或通用Agent开发者将 Agent 集成到你的网站中,让用户可以通过自然语言与页面进行交互。',
features_title: '核心特性',
feature_dom: {
title: '🧠 智能 DOM 理解',
desc: '基于 DOM 分析,高强度脱水。无需视觉识别,纯文本实现精准操作。',
},
feature_secure: {
title: '🔒 安全可控',
desc: '支持操作黑白名单、数据脱敏保护。注入自定义知识库,让 AI 按你的规则工作。',
},
feature_backend: {
title: '⚡ 零后端部署',
desc: 'CDN 或 NPM 引入,自定义 LLM 接入点。',
},
feature_accessible: {
title: '♿ 普惠智能',
desc: '为复杂 B端系统、管理后台提供自然语言入口。让每个用户都能轻松上手。',
},
vs_browser_use: '与 browser-use 的区别',
table_feature: '特性',
table_deployment: '部署方式',
table_deployment_pa: '页面内嵌组件',
table_deployment_bu: '外部工具',
table_scope: '操作范围',
table_scope_pa: '当前页面',
table_scope_bu: '整个浏览器',
table_user: '目标用户',
table_user_pa: '网站开发者',
table_user_bu: '爬虫/Agent 开发者',
table_scenario: '使用场景',
table_scenario_pa: '用户体验增强',
table_scenario_bu: '自动化任务',
use_cases_title: '应用场景',
use_case1_title: '对接答疑机器人:',
use_case1_desc:
'把你的答疑助手变成全能Agent。客服机器人不再只说「请先点击设置按钮然后点击...」,而是直接帮用户现场操作。',
use_case2_title: '交互升级/智能化改造:',
use_case2_desc:
'一行代码老应用变身Agent产品专家帮用户操作复杂 B 端软件。降低人工支持成本,提高用户满意度。',
use_case3_title: '产品教学:',
use_case3_desc:
'向用户演示交互过程边做边教。例如让AI演示「如何提交报销申请」的完整操作流程。',
use_case4_title: '无障碍支持:',
use_case4_desc:
'为视障用户、老年用户提供自然语言交互,对接屏幕阅读器或语音助理,让软件人人可用。',
get_started_title: '🚀 开始使用',
get_started_desc:
'准备好为你的网站添加 AI 操作员了吗?查看我们的快速开始指南,几分钟内完成集成。',
get_started_button: '快速开始 →',
},
quick_start: {
title: 'Quick Start',
subtitle: '几分钟内完成 page-agent 的集成。',
installation: '安装步骤',
step1_title: '1. 引入方式',
step1_cdn: 'CDN 引入',
step1_npm: 'NPM 安装',
step2_title: '2. 初始化配置',
step3_title: '3. 开始使用',
},
limitations: {
title: '使用限制',
subtitle: '了解 page-agent 当前的功能边界和技术限制',
page_support: '页面支持限制',
spa_limit_title: '单页应用限制',
spa_limit_1: '• 仅支持单页应用SPA目前只能在单个页面内进行操作',
spa_limit_2: '• 多页接力功能正在设计中:暂时无法跨页面执行连续任务',
spa_limit_3: '• 无法操作未接入该能力的网站:需要目标网站主动集成 page-agent',
interaction_limits: '交互行为限制',
supported_ops: '支持的操作',
op_click: '点击操作',
op_input: '文本输入',
op_scroll: '页面滚动',
op_submit: '表单提交',
op_select: '选择操作',
op_focus: '焦点切换',
unsupported_ops: '不支持的操作',
op_hover: '鼠标悬停hover',
op_drag: '拖拽操作',
op_context: '右键菜单',
op_draw: '图形绘制',
op_keyboard: '键盘快捷键',
op_position: '基于点击区域或鼠标位置的控制',
understanding_limits: '网页理解限制',
no_vision_title: '无视觉能力',
no_vision_desc: 'page-agent 基于 DOM 结构进行理解和操作,没有视觉识别能力,无法理解以下内容:',
no_vision_1: '• 图片内容:无法识别图片中的文字、图标或视觉元素',
no_vision_2: '• Canvas 画布:无法理解 Canvas 中绘制的图形和内容',
no_vision_3: '• WebGL 3D 内容:无法操作 3D 场景中的元素',
no_vision_4: '• SVG 图形:无法理解 SVG 中的视觉内容和路径',
website_requirements: '被操作网站要求',
req_semantic_title: '语义化和易用性',
req_semantic_desc:
'所有操作都基于 DOM 元素的语义化标签和属性。如果页面结构不够语义化,或者没有任何 accessibility 特性,可能影响 AI 的理解准确性。',
req_ux_title: 'UI/UX',
req_ux_desc:
'反常识的交互规则、基于视觉的操作提示、复杂的鼠标交互、快速出现快速消失的元素等,都会影响 AI 的理解和操作。',
req_env_title: '环境要求',
req_env_desc: 'modern browser',
future: '未来规划',
future_title: '即将支持',
future_1: '• 多页面接力操作能力',
future_2: '• 更丰富的鼠标交互支持',
future_3: '• 基础的视觉理解能力',
future_4: '• 更智能的错误恢复机制',
},
model_integration: {
title: '模型接入',
subtitle: '当前支持符合 OpenAI 接口规范且支持 tool call 的模型,包括公有云服务和私有部署方案。',
recommended: '推荐模型',
model_gpt4_title: '⚡ gpt-4.1-mini',
model_gpt4_badge: '评估基准 ✅',
model_gpt4_1: '• 性价比高',
model_gpt4_2: '• 速度快',
model_gpt4_3: '• 成功率高',
model_deepseek_title: '💰 DeepSeek-3.2',
model_deepseek_badge: '经济实惠',
model_deepseek_1: '• 价格远低于同等级其他模型',
model_deepseek_2: '• ToolCall 有出错率,通常能够自动修复',
model_deepseek_3: '• 本网站提供的免费试用为 DeepSeek',
model_qwen_title: '🛡️ qwen3',
model_qwen_badge: '安全合规',
model_qwen_1: '• 可控、效果尚可,价格合理',
model_qwen_2: '• ToolCall 有出错率,通常能够自动修复',
model_qwen_3: '• 适合能给出详细步骤的场景',
model_gemini_title: '⚡ gemini-2.5-flash',
model_gemini_badge: '极其高效,成功率高,价格合理',
available: '可用模型',
available_verified: '✅ 已验证可用',
tips: '提示',
tip_1: 'reasoning 模型(如 GPT-5速度偏慢没有必要',
tip_2:
'不保证 json schema 的模型openAI 以外的几乎所有模型tool call 有概率出错,通常能自动修复,建议 temperature 设置高一些',
tip_3: '小模型、nano 模型,效果不佳',
configuration: '配置方式',
},
custom_tools: {
title: '自定义工具',
subtitle:
'通过注册自定义工具,扩展 AI Agent 的能力边界。使用 Zod 定义严格的输入接口,让 AI 安全调用你的业务逻辑。',
registration: '工具注册',
registration_desc:
'每个自定义工具需要定义四个核心属性name、description、input schema 和 execute 函数。',
page_filter: '页面过滤器',
page_filter_desc: '通过 pageFilter 属性控制工具在哪些页面可见,提升安全性和用户体验。',
best_practices: '最佳实践',
bp_performance: '⚡ 性能优化',
bp_1: '• 使用 pageFilter 减少不必要的工具加载',
bp_2: '• 在 execute 函数中实现适当的缓存机制',
bp_3: '• 避免在工具中执行耗时的同步操作',
},
}

View File

@@ -0,0 +1,78 @@
export default {
hero: {
badge: 'GUI Agent in your webpage',
title_line1: '让你的 Web 应用',
title_line2: '拥有 AI 操作员',
subtitle_emoji: '🪄 一行 CDN 引入',
subtitle_main: ',为你的网站添加智能 UI Agent。',
subtitle_detail: '用户/答疑机器人给出文字指示AI 帮你操作页面。',
tab_try: '🚀 立即尝试',
tab_other: '🌐 其他网页尝试',
input_placeholder: '输入您想要 AI 执行的任务...',
execute_button: '执行',
default_task: '进入文档页,打开数据脱敏相关的文档,帮我总结成 markdown',
},
try_other: {
step1_title: '步骤 1:',
step1_content: '显示收藏夹栏',
step2_title: '步骤 2:',
step2_content: '拖拽下面按钮到收藏夹栏',
step3_title: '步骤 3:',
step3_content: '在其他网站点击收藏夹中的按钮即可使用',
notice_title: '⚠️ 注意',
notice_items: {
item1: '仅做技术评估,链接定期失效',
item2: '使用 DeepSeek 模型,参考 DeepSeek 用户协议和隐私政策',
item3: '部分网站屏蔽了链接嵌入,将无反应',
item4: '仅支持单页应用,页面跳转后需要重新注入',
item5: '仅识别文本,不识别图像,不支持拖拽等复杂交互',
item6_prefix: '详细使用限制参照',
item6_link: '《文档》',
},
},
benefits: {
no_backend: '无需后端',
private_model: '支持私有模型',
data_masking: '无痛脱敏',
open_source: 'MIT 开源',
},
features: {
section_title: '核心特性',
dom_understanding: {
title: '智能 DOM 理解',
desc: '基于 DOM 分析,高强度脱水。无需视觉识别,纯文本实现精准操作。',
},
secure_integration: {
title: '安全可控集成',
desc: '支持操作黑白名单、数据脱敏保护。注入自定义知识库,让 AI 按你的规则工作。',
},
zero_backend: {
title: '零后端部署',
desc: 'CDN 直接引入,自定义 LLM 接入点。从 OpenAI 到 qwen3完全由你掌控。',
},
accessible: {
title: '普惠智能交互',
desc: '为复杂 B端系统、管理后台提供自然语言入口。让每个用户都能轻松上手。',
},
},
use_cases: {
section_title: '应用场景',
section_subtitle: '从简单的表单填写到复杂的业务流程AI 都能理解并执行',
case1: {
title: '对接答疑机器人',
desc: '把你的答疑助手变成全能Agent。客服机器人不再只说「请先点击设置按钮然后点击...」,而是直接帮用户现场操作。',
},
case2: {
title: '交互升级/智能化改造',
desc: '一行代码老应用变身Agent产品专家帮用户操作复杂 B 端软件。降低人工支持成本,提高用户满意度。',
},
case3: {
title: '产品教学',
desc: '向用户演示交互过程边做边教。例如让AI演示「如何提交报销申请」的完整操作流程。',
},
case4: {
title: '无障碍支持',
desc: '为视障用户、老年用户提供自然语言交互,对接屏幕阅读器或语音助理,让软件人人可用。',
},
},
}

16
pages/i18n/types.ts Normal file
View File

@@ -0,0 +1,16 @@
import 'react-i18next'
import type commonZh from './locales/zh-CN/common'
import type docsZh from './locales/zh-CN/docs'
import type homeZh from './locales/zh-CN/home'
declare module 'react-i18next' {
interface CustomTypeOptions {
defaultNS: 'common'
resources: {
common: typeof commonZh
home: typeof homeZh
docs: typeof docsZh
}
}
}

View File

@@ -1,5 +1,8 @@
@import 'tailwindcss'; @import 'tailwindcss';
/* 启用 class-based dark mode for Tailwind v4 */
@variant dark (.dark &);
:root { :root {
--background: #ffffff; --background: #ffffff;
--foreground: #171717; --foreground: #171717;
@@ -8,12 +11,21 @@
--theme-color-2: rgb(189, 69, 251); --theme-color-2: rgb(189, 69, 251);
} }
@media (prefers-color-scheme: dark) { /* class-based dark mode - 应用到 html.dark */
:root { html.dark,
:root.dark {
--background: #0a0a0a; --background: #0a0a0a;
--foreground: #ededed; --foreground: #ededed;
} }
/* 同时支持系统偏好 */
/* @media (prefers-color-scheme: dark) {
html:not(.light),
:root:not(.light) {
--background: #0a0a0a;
--foreground: #ededed;
} }
} */
/* 添加 Tailwind 自定义颜色 */ /* 添加 Tailwind 自定义颜色 */
@theme { @theme {
@@ -21,13 +33,162 @@
--color-foreground: var(--foreground); --color-foreground: var(--foreground);
} }
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
}
body { body {
background: var(--background); background: var(--background);
color: var(--foreground); color: var(--foreground);
font-family: Arial, Helvetica, sans-serif; font-family: Arial, Helvetica, sans-serif;
} }
/* 确保文档页面标题在暗色模式下可见 - 只针对 prose 内的标题 */
.prose h1,
.prose h2,
.prose h3,
.prose h4,
.prose h5,
.prose h6 {
color: rgba(23, 23, 23, 0.85);
}
.dark .prose h1,
.dark .prose h2,
.dark .prose h3,
.dark .prose h4,
.dark .prose h5,
.dark .prose h6 {
color: rgba(255, 255, 255, 0.9);
}
table,
th,
td {
color: #171717;
}
.dark table,
.dark th,
.dark td {
color: #ededed;
}
/* 文档页深色模式优化 */
.dark .prose {
color: rgba(255, 255, 255, 0.7);
}
/* @media (prefers-color-scheme: dark) {
:root:not(.light) .prose {
color: rgba(255, 255, 255, 0.7);
}
} */
.dark .dark\:prose-invert {
--tw-prose-body: rgba(255, 255, 255, 0.7);
--tw-prose-headings: rgba(255, 255, 255, 0.95);
--tw-prose-lead: rgba(255, 255, 255, 0.7);
--tw-prose-links: rgba(147, 197, 253, 0.9);
--tw-prose-bold: rgba(255, 255, 255, 0.9);
--tw-prose-counters: rgba(255, 255, 255, 0.6);
--tw-prose-bullets: rgba(255, 255, 255, 0.5);
--tw-prose-hr: rgba(255, 255, 255, 0.2);
--tw-prose-quotes: rgba(255, 255, 255, 0.8);
--tw-prose-quote-borders: rgba(255, 255, 255, 0.3);
--tw-prose-captions: rgba(255, 255, 255, 0.6);
--tw-prose-code: rgba(255, 255, 255, 0.9);
--tw-prose-pre-code: rgba(255, 255, 255, 0.95);
--tw-prose-pre-bg: rgba(0, 0, 0, 0.5);
--tw-prose-th-borders: rgba(255, 255, 255, 0.3);
--tw-prose-td-borders: rgba(255, 255, 255, 0.2);
}
/* @media (prefers-color-scheme: dark) {
:root:not(.light) .dark\:prose-invert {
--tw-prose-body: rgba(255, 255, 255, 0.7);
--tw-prose-headings: rgba(255, 255, 255, 0.95);
--tw-prose-lead: rgba(255, 255, 255, 0.7);
--tw-prose-links: rgba(147, 197, 253, 0.9);
--tw-prose-bold: rgba(255, 255, 255, 0.9);
--tw-prose-counters: rgba(255, 255, 255, 0.6);
--tw-prose-bullets: rgba(255, 255, 255, 0.5);
--tw-prose-hr: rgba(255, 255, 255, 0.2);
--tw-prose-quotes: rgba(255, 255, 255, 0.8);
--tw-prose-quote-borders: rgba(255, 255, 255, 0.3);
--tw-prose-captions: rgba(255, 255, 255, 0.6);
--tw-prose-code: rgba(255, 255, 255, 0.9);
--tw-prose-pre-code: rgba(255, 255, 255, 0.95);
--tw-prose-pre-bg: rgba(0, 0, 0, 0.5);
--tw-prose-th-borders: rgba(255, 255, 255, 0.3);
--tw-prose-td-borders: rgba(255, 255, 255, 0.2);
}
} */
/* 标题更清晰 */
.dark .prose h1,
.dark .prose h2,
.dark .prose h3,
.dark .prose h4 {
color: rgba(255, 255, 255, 0.95);
}
/* @media (prefers-color-scheme: dark) {
:root:not(.light) .prose h1,
:root:not(.light) .prose h2,
:root:not(.light) .prose h3,
:root:not(.light) .prose h4 {
color: rgba(255, 255, 255, 0.95);
}
} */
/* 链接更清晰 */
.dark .prose a {
color: rgba(147, 197, 253, 0.9);
}
/* @media (prefers-color-scheme: dark) {
:root:not(.light) .prose a {
color: rgba(147, 197, 253, 0.9);
}
} */
/* 代码块背景更黑 */
.dark .prose pre {
background-color: rgba(0, 0, 0, 0.6);
}
/* @media (prefers-color-scheme: dark) {
:root:not(.light) .prose pre {
background-color: rgba(0, 0, 0, 0.6);
}
} */
/* 表格样式 */
.dark .prose table {
color: rgba(255, 255, 255, 0.7);
}
/* @media (prefers-color-scheme: dark) {
:root:not(.light) .prose table {
color: rgba(255, 255, 255, 0.7);
}
} */
.dark .prose thead {
color: rgba(255, 255, 255, 0.9);
border-bottom-color: rgba(255, 255, 255, 0.3);
}
/* @media (prefers-color-scheme: dark) {
:root:not(.light) .prose thead {
color: rgba(255, 255, 255, 0.9);
border-bottom-color: rgba(255, 255, 255, 0.3);
}
} */
.dark .prose tbody tr {
border-bottom-color: rgba(255, 255, 255, 0.2);
}
/* @media (prefers-color-scheme: dark) {
:root:not(.light) .prose tbody tr {
border-bottom-color: rgba(255, 255, 255, 0.2);
}
} */

View File

@@ -2,6 +2,8 @@ import { createRoot } from 'react-dom/client'
import { Route, Router, Switch } from 'wouter' import { Route, Router, Switch } from 'wouter'
import { useHashLocation } from 'wouter/use-hash-location' import { useHashLocation } from 'wouter/use-hash-location'
import './i18n/config'
import './i18n/types'
import { default as PagesRouter } from './router.tsx' import { default as PagesRouter } from './router.tsx'
import { default as TestPagesRouter } from './test-pages/router.tsx' import { default as TestPagesRouter } from './test-pages/router.tsx'

View File

@@ -1,5 +1,6 @@
/* eslint-disable react-dom/no-dangerously-set-innerhtml */ /* eslint-disable react-dom/no-dangerously-set-innerhtml */
import { useState } from 'react' import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Link, useSearchParams } from 'wouter' import { Link, useSearchParams } from 'wouter'
import { PageAgent } from '@/PageAgent.js' import { PageAgent } from '@/PageAgent.js'
@@ -25,7 +26,14 @@ const injectionA = `
` `
export default function HomePage() { export default function HomePage() {
const [task, setTask] = useState('进入文档页,打开数据脱敏相关的文档,帮我总结成 markdown') const { t, i18n } = useTranslation(['home', 'common'])
const [task, setTask] = useState(() => t('home:hero.default_task'))
// Update task when language changes
const defaultTask = t('home:hero.default_task')
useEffect(() => {
setTask(defaultTask)
}, [defaultTask])
const [params, setParams] = useSearchParams() const [params, setParams] = useSearchParams()
const isOther = params.has('try_other') const isOther = params.has('try_other')
@@ -79,25 +87,25 @@ export default function HomePage() {
className="w-2 h-2 bg-blue-500 rounded-full mr-2 animate-pulse" className="w-2 h-2 bg-blue-500 rounded-full mr-2 animate-pulse"
aria-hidden="true" aria-hidden="true"
></span> ></span>
UI Agent in your webpage {t('home:hero.badge')}
</div> </div>
<h1 <h1
id="hero-heading" id="hero-heading"
className="text-5xl lg:text-7xl font-bold mb-8 bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent" className="text-5xl lg:text-7xl font-bold mb-8 bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent"
> >
Web {t('home:hero.title_line1')}
<br /> <br />
AI {t('home:hero.title_line2')}
</h1> </h1>
<p className="text-xl lg:text-2xl text-gray-600 dark:text-gray-300 mb-12 max-w-4xl mx-auto leading-relaxed"> <p className="text-xl lg:text-2xl text-gray-600 dark:text-gray-300 mb-12 max-w-4xl mx-auto leading-relaxed">
<span className="bg-gradient-to-r from-blue-500 to-purple-500 bg-clip-text text-transparent font-bold"> <span className="bg-gradient-to-r from-blue-500 to-purple-500 bg-clip-text text-transparent font-bold">
🪄 CDN {t('home:hero.subtitle_emoji')}
</span> </span>
UI Agent {t('home:hero.subtitle_main')}
<br /> <br />
/AI {t('home:hero.subtitle_detail')}
</p> </p>
{/* Try It Now Section - Tab Card */} {/* Try It Now Section - Tab Card */}
@@ -114,7 +122,7 @@ export default function HomePage() {
: 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-700' : 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-700'
}`} }`}
> >
🚀 {t('home:hero.tab_try')}
</button> </button>
<button <button
onClick={() => setActiveTab('other')} onClick={() => setActiveTab('other')}
@@ -124,7 +132,7 @@ export default function HomePage() {
: 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-700' : 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-700'
}`} }`}
> >
🌐 {t('home:hero.tab_other')}
</button> </button>
</div> </div>
@@ -136,7 +144,7 @@ export default function HomePage() {
<input <input
value={task} value={task}
onChange={(e) => setTask(e.target.value)} onChange={(e) => setTask(e.target.value)}
placeholder="输入您想要 AI 执行的任务..." placeholder={t('home:hero.input_placeholder')}
className="w-full px-4 py-3 pr-20 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-500 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none text-sm mb-0" className="w-full px-4 py-3 pr-20 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-500 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none text-sm mb-0"
data-page-agent-not-interactive data-page-agent-not-interactive
/> />
@@ -147,7 +155,7 @@ export default function HomePage() {
className="absolute right-2 top-2 px-5 py-1.5 bg-gradient-to-r from-blue-600 to-purple-600 text-white font-medium rounded-md hover:shadow-md transform hover:scale-105 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none text-sm" className="absolute right-2 top-2 px-5 py-1.5 bg-gradient-to-r from-blue-600 to-purple-600 text-white font-medium rounded-md hover:shadow-md transform hover:scale-105 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none text-sm"
data-page-agent-not-interactive data-page-agent-not-interactive
> >
{t('home:hero.execute_button')}
</button> </button>
</div> </div>
</div> </div>
@@ -160,7 +168,10 @@ export default function HomePage() {
{/* Keyboard Shortcut Hint */} {/* Keyboard Shortcut Hint */}
<div className="bg-blue-50 dark:bg-gray-700 p-4 rounded-lg"> <div className="bg-blue-50 dark:bg-gray-700 p-4 rounded-lg">
<p className="text-gray-700 dark:text-gray-300 text-sm mb-3"> <p className="text-gray-700 dark:text-gray-300 text-sm mb-3">
<span className="font-semibold"> 1:</span> <span className="font-semibold">
{t('home:try_other.step1_title')}
</span>{' '}
{t('home:try_other.step1_content')}
</p> </p>
<div className="flex items-center justify-center gap-2"> <div className="flex items-center justify-center gap-2">
<kbd className="px-2 py-1 bg-white dark:bg-gray-600 border border-gray-300 dark:border-gray-500 rounded text-xs font-mono"> <kbd className="px-2 py-1 bg-white dark:bg-gray-600 border border-gray-300 dark:border-gray-500 rounded text-xs font-mono">
@@ -176,8 +187,10 @@ export default function HomePage() {
{/* Draggable Bookmarklet */} {/* Draggable Bookmarklet */}
<div className="bg-green-50 dark:bg-gray-700 p-4 rounded-lg"> <div className="bg-green-50 dark:bg-gray-700 p-4 rounded-lg">
<p className="text-gray-700 dark:text-gray-300 text-sm mb-3"> <p className="text-gray-700 dark:text-gray-300 text-sm mb-3">
<span className="font-semibold"> 2:</span>{' '} <span className="font-semibold">
{t('home:try_other.step2_title')}
</span>{' '}
{t('home:try_other.step2_content')}
</p> </p>
<div <div
className="flex items-center justify-center gap-2 text-gray-500 dark:text-gray-400" className="flex items-center justify-center gap-2 text-gray-500 dark:text-gray-400"
@@ -188,8 +201,10 @@ export default function HomePage() {
{/* Usage Instructions */} {/* Usage Instructions */}
<div className="bg-purple-50 dark:bg-gray-700 p-4 rounded-lg"> <div className="bg-purple-50 dark:bg-gray-700 p-4 rounded-lg">
<p className="text-gray-700 dark:text-gray-300 text-sm"> <p className="text-gray-700 dark:text-gray-300 text-sm">
<span className="font-semibold"> 3:</span>{' '} <span className="font-semibold">
使 {t('home:try_other.step3_title')}
</span>{' '}
{t('home:try_other.step3_content')}
</p> </p>
</div> </div>
</div> </div>
@@ -197,37 +212,37 @@ export default function HomePage() {
{/* 右侧:注意事项 */} {/* 右侧:注意事项 */}
<div className="bg-yellow-50 dark:bg-gray-700 p-4 rounded-lg"> <div className="bg-yellow-50 dark:bg-gray-700 p-4 rounded-lg">
<h4 className="font-semibold text-gray-900 dark:text-white mb-3 text-sm"> <h4 className="font-semibold text-gray-900 dark:text-white mb-3 text-sm">
{t('home:try_other.notice_title')}
</h4> </h4>
<ul className="space-y-2 text-sm text-gray-700 dark:text-gray-300"> <ul className="space-y-2 text-sm text-gray-700 dark:text-gray-300">
<li className="flex items-start text-left"> <li className="flex items-start text-left">
<span className="w-1.5 h-1.5 bg-yellow-500 rounded-full mt-2 mr-2 flex-shrink-0 "></span> <span className="w-1.5 h-1.5 bg-yellow-500 rounded-full mt-2 mr-2 flex-shrink-0 "></span>
{t('home:try_other.notice_items.item1')}
</li> </li>
<li className="flex items-start text-left"> <li className="flex items-start text-left">
<span className="w-1.5 h-1.5 bg-yellow-500 rounded-full mt-2 mr-2 flex-shrink-0 "></span> <span className="w-1.5 h-1.5 bg-yellow-500 rounded-full mt-2 mr-2 flex-shrink-0 "></span>
使 DeepSeek DeepSeek {t('home:try_other.notice_items.item2')}
</li> </li>
<li className="flex items-start text-left"> <li className="flex items-start text-left">
<span className="w-1.5 h-1.5 bg-yellow-500 rounded-full mt-2 mr-2 flex-shrink-0 "></span> <span className="w-1.5 h-1.5 bg-yellow-500 rounded-full mt-2 mr-2 flex-shrink-0 "></span>
{t('home:try_other.notice_items.item3')}
</li> </li>
<li className="flex items-start text-left"> <li className="flex items-start text-left">
<span className="w-1.5 h-1.5 bg-yellow-500 rounded-full mt-2 mr-2 flex-shrink-0 "></span> <span className="w-1.5 h-1.5 bg-yellow-500 rounded-full mt-2 mr-2 flex-shrink-0 "></span>
{t('home:try_other.notice_items.item4')}
</li> </li>
<li className="flex items-start text-left"> <li className="flex items-start text-left">
<span className="w-1.5 h-1.5 bg-yellow-500 rounded-full mt-2 mr-2 flex-shrink-0 "></span> <span className="w-1.5 h-1.5 bg-yellow-500 rounded-full mt-2 mr-2 flex-shrink-0 "></span>
{t('home:try_other.notice_items.item5')}
</li> </li>
<li className="flex items-start text-left"> <li className="flex items-start text-left">
<span className="w-1.5 h-1.5 bg-yellow-500 rounded-full mt-2 mr-2 flex-shrink-0 "></span> <span className="w-1.5 h-1.5 bg-yellow-500 rounded-full mt-2 mr-2 flex-shrink-0 "></span>
使{' '} {t('home:try_other.notice_items.item6_prefix')}{' '}
<Link <Link
href="/docs/introduction/limitations" href="/docs/introduction/limitations"
className="text-blue-600 dark:text-blue-400 hover:underline" className="text-blue-600 dark:text-blue-400 hover:underline"
> >
{t('home:try_other.notice_items.item6_link')}
</Link> </Link>
</li> </li>
</ul> </ul>
@@ -248,28 +263,28 @@ export default function HomePage() {
className="w-2 h-2 bg-green-500 rounded-full mr-2" className="w-2 h-2 bg-green-500 rounded-full mr-2"
aria-hidden="true" aria-hidden="true"
></span> ></span>
{t('home:benefits.no_backend')}
</li> </li>
<li className="flex items-center"> <li className="flex items-center">
<span <span
className="w-2 h-2 bg-green-500 rounded-full mr-2" className="w-2 h-2 bg-green-500 rounded-full mr-2"
aria-hidden="true" aria-hidden="true"
></span> ></span>
{t('home:benefits.private_model')}
</li> </li>
<li className="flex items-center"> <li className="flex items-center">
<span <span
className="w-2 h-2 bg-green-500 rounded-full mr-2" className="w-2 h-2 bg-green-500 rounded-full mr-2"
aria-hidden="true" aria-hidden="true"
></span> ></span>
{t('home:benefits.data_masking')}
</li> </li>
<li className="flex items-center"> <li className="flex items-center">
<span <span
className="w-2 h-2 bg-green-500 rounded-full mr-2" className="w-2 h-2 bg-green-500 rounded-full mr-2"
aria-hidden="true" aria-hidden="true"
></span> ></span>
DOM {t('home:benefits.open_source')}
</li> </li>
</ul> </ul>
</div> </div>
@@ -295,10 +310,10 @@ export default function HomePage() {
<span className="text-white text-xl">🧠</span> <span className="text-white text-xl">🧠</span>
</div> </div>
<h3 className="text-xl font-bold mb-4 text-gray-900 dark:text-white"> <h3 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">
DOM {t('home:features.dom_understanding.title')}
</h3> </h3>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed"> <p className="text-gray-600 dark:text-gray-300 leading-relaxed">
DOM {t('home:features.dom_understanding.desc')}
</p> </p>
</article> </article>
@@ -314,10 +329,10 @@ export default function HomePage() {
<span className="text-white text-xl">🔒</span> <span className="text-white text-xl">🔒</span>
</div> </div>
<h3 className="text-xl font-bold mb-4 text-gray-900 dark:text-white"> <h3 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">
{t('home:features.secure_integration.title')}
</h3> </h3>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed"> <p className="text-gray-600 dark:text-gray-300 leading-relaxed">
AI {t('home:features.secure_integration.desc')}
</p> </p>
</article> </article>
@@ -332,9 +347,11 @@ export default function HomePage() {
> >
<span className="text-white text-xl"></span> <span className="text-white text-xl"></span>
</div> </div>
<h3 className="text-xl font-bold mb-4 text-gray-900 dark:text-white"></h3> <h3 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">
{t('home:features.zero_backend.title')}
</h3>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed"> <p className="text-gray-600 dark:text-gray-300 leading-relaxed">
CDN LLM OpenAI qwen3 {t('home:features.zero_backend.desc')}
</p> </p>
</article> </article>
@@ -350,10 +367,10 @@ export default function HomePage() {
<span className="text-white text-xl"></span> <span className="text-white text-xl"></span>
</div> </div>
<h3 className="text-xl font-bold mb-4 text-gray-900 dark:text-white"> <h3 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">
{t('home:features.accessible.title')}
</h3> </h3>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed"> <p className="text-gray-600 dark:text-gray-300 leading-relaxed">
B端系统 {t('home:features.accessible.desc')}
</p> </p>
</article> </article>
</div> </div>
@@ -368,27 +385,27 @@ export default function HomePage() {
id="use-cases-heading" id="use-cases-heading"
className="text-4xl lg:text-5xl mb-6 text-gray-900 dark:text-white" className="text-4xl lg:text-5xl mb-6 text-gray-900 dark:text-white"
> >
{t('home:use_cases.section_title')}
</h2> </h2>
<p className="text-xl text-gray-600 dark:text-gray-300 max-w-3xl mx-auto"> <p className="text-xl text-gray-600 dark:text-gray-300 max-w-3xl mx-auto">
AI {t('home:use_cases.section_subtitle')}
</p> </p>
</div> </div>
<div className="grid lg:grid-cols-2 gap-12" role="list"> <div className="grid lg:grid-cols-2 gap-12" role="list">
{/* Use Case 1 */} {/* Use Case 1 */}
<div className="bg-gradient-to-br from-blue-100 to-purple-100 dark:from-gray-700 dark:to-gray-800 p-8 rounded-2xl"> <div className="bg-gradient-to-br from-blue-100 to-purple-100 dark:from-gray-700 dark:to-gray-800 p-8 rounded-2xl">
<div className="flex items-start space-x-4 h-20"> <div className="flex items-start space-x-4">
<div className="w-10 h-10 bg-blue-500 rounded-lg flex items-center justify-center flex-shrink-0"> <div className="w-10 h-10 bg-blue-500 rounded-lg flex items-center justify-center flex-shrink-0">
<span className="text-white font-bold">1</span> <span className="text-white font-bold">1</span>
</div> </div>
<div> <div>
<h3 className="text-xl font-bold mb-2 text-gray-900 dark:text-white"> <h3 className="text-xl font-bold mb-2 text-gray-900 dark:text-white">
{t('home:use_cases.case1.title')}
</h3> </h3>
<p className="text-gray-600 dark:text-gray-300"> <p className="text-gray-600 dark:text-gray-300">
Agent"请先点击设置按钮然后点击..." {t('home:use_cases.case1.desc')}
</p> </p>
</div> </div>
</div> </div>
@@ -396,17 +413,16 @@ export default function HomePage() {
{/* Use Case 2 */} {/* Use Case 2 */}
<div className="bg-gradient-to-br from-green-100 to-blue-100 dark:from-gray-700 dark:to-gray-800 p-8 rounded-2xl"> <div className="bg-gradient-to-br from-green-100 to-blue-100 dark:from-gray-700 dark:to-gray-800 p-8 rounded-2xl">
<div className="flex items-start space-x-4 h-20"> <div className="flex items-start space-x-4">
<div className="w-10 h-10 bg-green-500 rounded-lg flex items-center justify-center flex-shrink-0"> <div className="w-10 h-10 bg-green-500 rounded-lg flex items-center justify-center flex-shrink-0">
<span className="text-white font-bold">2</span> <span className="text-white font-bold">2</span>
</div> </div>
<div> <div>
<h3 className="text-xl font-bold mb-2 text-gray-900 dark:text-white"> <h3 className="text-xl font-bold mb-2 text-gray-900 dark:text-white">
/ {t('home:use_cases.case2.title')}
</h3> </h3>
<p className="text-gray-600 dark:text-gray-300"> <p className="text-gray-600 dark:text-gray-300">
Agent B {t('home:use_cases.case2.desc')}
</p> </p>
</div> </div>
</div> </div>
@@ -414,16 +430,16 @@ export default function HomePage() {
{/* Use Case 3 */} {/* Use Case 3 */}
<div className="bg-gradient-to-br from-purple-100 to-pink-100 dark:from-gray-700 dark:to-gray-800 p-8 rounded-2xl"> <div className="bg-gradient-to-br from-purple-100 to-pink-100 dark:from-gray-700 dark:to-gray-800 p-8 rounded-2xl">
<div className="flex items-start space-x-4 h-20"> <div className="flex items-start space-x-4">
<div className="w-10 h-10 bg-purple-500 rounded-lg flex items-center justify-center flex-shrink-0"> <div className="w-10 h-10 bg-purple-500 rounded-lg flex items-center justify-center flex-shrink-0">
<span className="text-white font-bold">3</span> <span className="text-white font-bold">3</span>
</div> </div>
<div> <div>
<h3 className="text-xl font-bold mb-2 text-gray-900 dark:text-white"> <h3 className="text-xl font-bold mb-2 text-gray-900 dark:text-white">
{t('home:use_cases.case3.title')}
</h3> </h3>
<p className="text-gray-600 dark:text-gray-300"> <p className="text-gray-600 dark:text-gray-300">
AI演示"如何提交报销申请" {t('home:use_cases.case3.desc')}
</p> </p>
</div> </div>
</div> </div>
@@ -431,16 +447,16 @@ export default function HomePage() {
{/* Use Case 4 */} {/* Use Case 4 */}
<div className="bg-gradient-to-br from-orange-100 to-red-100 dark:from-gray-700 dark:to-gray-800 p-8 rounded-2xl"> <div className="bg-gradient-to-br from-orange-100 to-red-100 dark:from-gray-700 dark:to-gray-800 p-8 rounded-2xl">
<div className="flex items-start space-x-4 h-20"> <div className="flex items-start space-x-4">
<div className="w-10 h-10 bg-orange-500 rounded-lg flex items-center justify-center flex-shrink-0"> <div className="w-10 h-10 bg-orange-500 rounded-lg flex items-center justify-center flex-shrink-0">
<span className="text-white font-bold">4</span> <span className="text-white font-bold">4</span>
</div> </div>
<div> <div>
<h3 className="text-xl font-bold mb-2 text-gray-900 dark:text-white"> <h3 className="text-xl font-bold mb-2 text-gray-900 dark:text-white">
{t('home:use_cases.case4.title')}
</h3> </h3>
<p className="text-gray-600 dark:text-gray-300"> <p className="text-gray-600 dark:text-gray-300">
{t('home:use_cases.case4.desc')}
</p> </p>
</div> </div>
</div> </div>