refactor: normalize website structure
This commit is contained in:
524
packages/website/src/pages/Home.tsx
Normal file
524
packages/website/src/pages/Home.tsx
Normal file
@@ -0,0 +1,524 @@
|
||||
/* eslint-disable react-dom/no-dangerously-set-innerhtml */
|
||||
import { Bot, Box, MessageSquare, PlayCircle, Shield, Sparkles, Users, Zap } from 'lucide-react'
|
||||
import { PageAgent } from 'page-agent'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Link, useSearchParams } from 'wouter'
|
||||
|
||||
import Footer from '../components/Footer'
|
||||
import Header from '../components/Header'
|
||||
import { AnimatedGradientText } from '../components/ui/animated-gradient-text'
|
||||
import { Highlighter } from '../components/ui/highlighter'
|
||||
import { NeonGradientCard } from '../components/ui/neon-gradient-card'
|
||||
import { Particles } from '../components/ui/particles'
|
||||
import { SparklesText } from '../components/ui/sparkles-text'
|
||||
import { CDN_CN_URL, CDN_URL } from '../constants'
|
||||
|
||||
function getInjection(useCN?: boolean) {
|
||||
const cdn = useCN ? CDN_CN_URL : CDN_URL
|
||||
|
||||
const injection = encodeURI(
|
||||
`javascript:(function(){var s=document.createElement('script');s.src=\`${cdn}?t=\${Math.random()}\`;s.setAttribute('crossorigin', true);s.type="text/javascript";s.onload=()=>console.log('PageAgent script loaded!');document.body.appendChild(s);})();`
|
||||
)
|
||||
|
||||
return `
|
||||
<a
|
||||
href=${injection}
|
||||
class="inline-flex items-center text-xs px-3 py-2 bg-blue-500 text-white font-medium rounded-lg hover:shadow-md transform hover:scale-105 transition-all duration-200 cursor-move border-2 border-dashed border-green-300"
|
||||
draggable="true"
|
||||
onclick="return false;"
|
||||
title="Drag me to your bookmarks bar!"
|
||||
>
|
||||
✨PageAgent
|
||||
</a>
|
||||
`
|
||||
}
|
||||
|
||||
export default function HomePage() {
|
||||
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 isOther = params.has('try_other')
|
||||
|
||||
const [activeTab, setActiveTab] = useState<'try' | 'other'>(isOther ? 'other' : 'try')
|
||||
const [cdnSource, setCdnSource] = useState<'international' | 'china'>('international')
|
||||
|
||||
const handleExecute = async () => {
|
||||
if (!task.trim()) return
|
||||
|
||||
let pageAgent: PageAgent
|
||||
const win = window as any
|
||||
|
||||
if (win.pageAgent && !win.pageAgent.disposed) {
|
||||
pageAgent = win.pageAgent
|
||||
} else {
|
||||
pageAgent = new PageAgent({
|
||||
// 把 react 根元素排除掉,挂了很多冒泡时间导致假阳
|
||||
interactiveBlacklist: [document.getElementById('root')!],
|
||||
language: i18n.language as any,
|
||||
|
||||
instructions: {
|
||||
system: 'You are a helpful assistant on PageAgent website.',
|
||||
getPageInstructions: (url) => {
|
||||
const hint = url.includes('page-agent') ? 'This is PageAgent demo page.' : undefined
|
||||
console.log('[instructions] getPageInstructions:', url, '->', hint)
|
||||
return hint
|
||||
},
|
||||
},
|
||||
|
||||
// experimentalScriptExecutionTool: true,
|
||||
|
||||
// testing server
|
||||
// @note: rate limit. prompt limit.
|
||||
// model: DEMO_MODEL,
|
||||
// baseURL: DEMO_BASE_URL,
|
||||
// apiKey: DEMO_API_KEY,
|
||||
})
|
||||
win.pageAgent = pageAgent
|
||||
}
|
||||
|
||||
const result = await pageAgent.execute(task)
|
||||
|
||||
console.log(result)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-linear-to-br from-blue-50 to-purple-50 dark:from-gray-900 dark:to-gray-800">
|
||||
<Header />
|
||||
|
||||
{/* Hero Section */}
|
||||
<main id="main-content">
|
||||
<section className="relative px-6 py-22 lg:py-28" aria-labelledby="hero-heading">
|
||||
<div className="max-w-7xl mx-auto text-center">
|
||||
{/* Background Pattern + Particles */}
|
||||
<div className="absolute inset-0 opacity-30" aria-hidden="true">
|
||||
<div className="absolute inset-0 bg-linear-to-r from-blue-400/20 to-purple-400/20 rounded-3xl transform rotate-1"></div>
|
||||
<div className="absolute inset-0 bg-linear-to-l from-purple-400/20 to-blue-400/20 rounded-3xl transform -rotate-1"></div>
|
||||
</div>
|
||||
<Particles
|
||||
className="absolute inset-0"
|
||||
quantity={80}
|
||||
staticity={30}
|
||||
ease={80}
|
||||
color="#6366f1"
|
||||
/>
|
||||
|
||||
<div className="relative z-10">
|
||||
<div className="inline-flex items-center px-4 py-2 mb-8 text-sm font-medium bg-white/90 dark:bg-gray-800/90 rounded-full shadow-lg border border-gray-200 dark:border-gray-700">
|
||||
<span
|
||||
className="w-2 h-2 bg-blue-500 rounded-full mr-2 animate-pulse"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<AnimatedGradientText colorFrom="#3b82f6" colorTo="#8b5cf6">
|
||||
{t('home:hero.badge')}
|
||||
</AnimatedGradientText>
|
||||
</div>
|
||||
|
||||
<h1
|
||||
id="hero-heading"
|
||||
className="text-5xl lg:text-7xl font-bold mb-8 bg-linear-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent pb-1"
|
||||
>
|
||||
{t('home:hero.title_line1')}
|
||||
<br />
|
||||
{t('home:hero.title_line2')}
|
||||
</h1>
|
||||
|
||||
<p className="text-xl lg:text-2xl text-gray-600 dark:text-gray-300 mb-12 max-w-4xl mx-auto leading-relaxed">
|
||||
<Highlighter action="underline" color="#8b5cf6" strokeWidth={2}>
|
||||
<span className="bg-linear-to-r from-blue-500 to-purple-500 bg-clip-text text-transparent font-bold">
|
||||
{t('home:hero.subtitle_emoji')}
|
||||
</span>
|
||||
</Highlighter>
|
||||
{t('home:hero.subtitle_main')}
|
||||
<br />
|
||||
{t('home:hero.subtitle_detail')}
|
||||
</p>
|
||||
|
||||
{/* Try It Now Section - Tab Card */}
|
||||
<div className="mb-12">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<NeonGradientCard
|
||||
borderSize={2}
|
||||
borderRadius={20}
|
||||
neonColors={{ firstColor: '#ff00aa', secondColor: '#00FFF1' }}
|
||||
>
|
||||
{/* Tab Headers */}
|
||||
<div className="flex border-b border-gray-200 dark:border-gray-700">
|
||||
<button
|
||||
onClick={() => setActiveTab('try')}
|
||||
className={`flex-1 px-4 py-4 text-lg font-medium transition-colors duration-200 rounded-tl-2xl ${
|
||||
activeTab === 'try'
|
||||
? 'bg-linear-to-r from-blue-50 to-purple-50 dark:from-blue-900/30 dark:to-purple-900/30 text-blue-700 dark:text-blue-300 border-b-2 border-blue-500'
|
||||
: '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
|
||||
onClick={() => setActiveTab('other')}
|
||||
className={`flex-1 px-4 py-4 text-lg font-medium transition-colors duration-200 rounded-tr-2xl ${
|
||||
activeTab === 'other'
|
||||
? 'bg-linear-to-r from-green-50 to-blue-50 dark:from-green-900/30 dark:to-blue-900/30 text-green-700 dark:text-green-300 border-b-2 border-green-500'
|
||||
: '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>
|
||||
</div>
|
||||
|
||||
{/* Tab Content */}
|
||||
<div className="p-4">
|
||||
{activeTab === 'try' && (
|
||||
<div className="space-y-4">
|
||||
<div className="relative">
|
||||
<input
|
||||
value={task}
|
||||
onChange={(e) => setTask(e.target.value)}
|
||||
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"
|
||||
data-page-agent-not-interactive
|
||||
/>
|
||||
<button
|
||||
onClick={handleExecute}
|
||||
// disabled
|
||||
// disabled={!task.trim()}
|
||||
className="absolute right-2 top-2 px-5 py-1.5 bg-linear-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
|
||||
>
|
||||
{t('home:hero.execute_button')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'other' && (
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
{/* 左侧:操作步骤 */}
|
||||
<div className="space-y-4">
|
||||
{/* Keyboard Shortcut Hint */}
|
||||
<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">
|
||||
<span className="font-semibold">
|
||||
{t('home:try_other.step1_title')}
|
||||
</span>{' '}
|
||||
{t('home:try_other.step1_content')}
|
||||
</p>
|
||||
<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">
|
||||
Ctrl + Shift + B
|
||||
</kbd>
|
||||
<span className="text-gray-500 dark:text-gray-400">或</span>
|
||||
<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">
|
||||
⌘ + Shift + B
|
||||
</kbd>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Draggable Bookmarklet */}
|
||||
<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">
|
||||
<span className="font-semibold">
|
||||
{t('home:try_other.step2_title')}
|
||||
</span>{' '}
|
||||
{t('home:try_other.step2_content')}
|
||||
</p>
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<select
|
||||
value={cdnSource}
|
||||
onChange={(e) =>
|
||||
setCdnSource(e.target.value as 'international' | 'china')
|
||||
}
|
||||
className="px-2 py-1.5 text-xs border border-gray-300 dark:border-gray-500 rounded bg-white dark:bg-gray-600 text-gray-700 dark:text-gray-200"
|
||||
>
|
||||
<option value="international">
|
||||
{t('home:try_other.cdn_international')}
|
||||
</option>
|
||||
<option value="china">{t('home:try_other.cdn_china')}</option>
|
||||
</select>
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: getInjection(cdnSource === 'china'),
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Usage Instructions */}
|
||||
<div className="bg-purple-50 dark:bg-gray-700 p-4 rounded-lg">
|
||||
<p className="text-gray-700 dark:text-gray-300 text-sm">
|
||||
<span className="font-semibold">
|
||||
{t('home:try_other.step3_title')}
|
||||
</span>{' '}
|
||||
{t('home:try_other.step3_content')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 右侧:注意事项 */}
|
||||
<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">
|
||||
{t('home:try_other.notice_title')}
|
||||
</h4>
|
||||
<ul className="space-y-2 text-sm text-gray-700 dark:text-gray-300">
|
||||
<li className="flex items-start text-left">
|
||||
<span className="w-1.5 h-1.5 bg-yellow-500 rounded-full mt-2 mr-2 shrink-0 "></span>
|
||||
{t('home:try_other.notice_items.item1')}
|
||||
</li>
|
||||
<li className="flex items-start text-left">
|
||||
<span className="w-1.5 h-1.5 bg-yellow-500 rounded-full mt-2 mr-2 shrink-0 "></span>
|
||||
{t('home:try_other.notice_items.item2')}
|
||||
</li>
|
||||
<li className="flex items-start text-left">
|
||||
<span className="w-1.5 h-1.5 bg-yellow-500 rounded-full mt-2 mr-2 shrink-0 "></span>
|
||||
{t('home:try_other.notice_items.item3')}
|
||||
</li>
|
||||
<li className="flex items-start text-left">
|
||||
<span className="w-1.5 h-1.5 bg-yellow-500 rounded-full mt-2 mr-2 shrink-0 "></span>
|
||||
{t('home:try_other.notice_items.item4')}
|
||||
</li>
|
||||
<li className="flex items-start text-left">
|
||||
<span className="w-1.5 h-1.5 bg-yellow-500 rounded-full mt-2 mr-2 shrink-0 "></span>
|
||||
{t('home:try_other.notice_items.item5')}
|
||||
</li>
|
||||
<li className="flex items-start text-left">
|
||||
<span className="w-1.5 h-1.5 bg-yellow-500 rounded-full mt-2 mr-2 shrink-0 "></span>
|
||||
{t('home:try_other.notice_items.item6_prefix')}{' '}
|
||||
<Link
|
||||
href="/docs/introduction/limitations"
|
||||
className="text-blue-600 dark:text-blue-400 hover:underline"
|
||||
>
|
||||
{t('home:try_other.notice_items.item6_link')}
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</NeonGradientCard>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul
|
||||
className="flex flex-wrap justify-center gap-6 text-sm text-gray-500 dark:text-gray-400"
|
||||
role="list"
|
||||
>
|
||||
<li className="flex items-center">
|
||||
<span
|
||||
className="w-2 h-2 bg-green-500 rounded-full mr-2"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
{t('home:benefits.no_backend')}
|
||||
</li>
|
||||
<li className="flex items-center">
|
||||
<span
|
||||
className="w-2 h-2 bg-green-500 rounded-full mr-2"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
{t('home:benefits.private_model')}
|
||||
</li>
|
||||
<li className="flex items-center">
|
||||
<span
|
||||
className="w-2 h-2 bg-green-500 rounded-full mr-2"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
{t('home:benefits.data_masking')}
|
||||
</li>
|
||||
<li className="flex items-center">
|
||||
<span
|
||||
className="w-2 h-2 bg-green-500 rounded-full mr-2"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
{t('home:benefits.open_source')}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Features Section */}
|
||||
<section
|
||||
className="px-6 py-20 bg-white/50 dark:bg-gray-800/50 backdrop-blur-sm"
|
||||
aria-labelledby="features-heading"
|
||||
>
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-8" role="list">
|
||||
{/* Feature 1 */}
|
||||
<article
|
||||
className="group p-8 bg-linear-to-br from-blue-100 to-purple-100 dark:from-gray-700 dark:to-gray-800 rounded-2xl shadow-lg hover:shadow-xl transition-all duration-300 border border-gray-100 dark:border-gray-700"
|
||||
role="listitem"
|
||||
>
|
||||
<div
|
||||
className="w-14 h-14 bg-linear-to-br from-blue-500 to-blue-600 rounded-xl flex items-center justify-center mb-6 group-hover:scale-110 group-hover:rotate-3 transition-all duration-300 shadow-lg"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<Box className="w-7 h-7 text-white" strokeWidth={2.5} />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">
|
||||
{t('home:features.in_page.title')}
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
|
||||
{t('home:features.in_page.desc')}
|
||||
</p>
|
||||
</article>
|
||||
|
||||
{/* Feature 2 */}
|
||||
<article
|
||||
className="group p-8 bg-linear-to-br from-purple-100 to-pink-100 dark:from-gray-700 dark:to-gray-800 rounded-2xl shadow-lg hover:shadow-xl transition-all duration-300 border border-gray-100 dark:border-gray-700"
|
||||
role="listitem"
|
||||
>
|
||||
<div
|
||||
className="w-14 h-14 bg-linear-to-br from-green-500 to-green-600 rounded-xl flex items-center justify-center mb-6 group-hover:scale-110 group-hover:rotate-3 transition-all duration-300 shadow-lg"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<Zap className="w-7 h-7 text-white fill-white" strokeWidth={2.5} />
|
||||
</div>
|
||||
<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">
|
||||
{t('home:features.zero_backend.desc')}
|
||||
</p>
|
||||
</article>
|
||||
|
||||
{/* Feature 3 */}
|
||||
<article
|
||||
className="group p-8 bg-linear-to-br from-orange-100 to-red-100 dark:from-gray-700 dark:to-gray-800 rounded-2xl shadow-lg hover:shadow-xl transition-all duration-300 border border-gray-100 dark:border-gray-700"
|
||||
role="listitem"
|
||||
>
|
||||
<div
|
||||
className="w-14 h-14 bg-linear-to-br from-purple-500 to-purple-600 rounded-xl flex items-center justify-center mb-6 group-hover:scale-110 group-hover:rotate-3 transition-all duration-300 shadow-lg"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<MessageSquare className="w-7 h-7 text-white" strokeWidth={2.5} />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">
|
||||
{t('home:features.accessible.title')}
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
|
||||
{t('home:features.accessible.desc')}
|
||||
</p>
|
||||
</article>
|
||||
|
||||
{/* Feature 4 */}
|
||||
<article
|
||||
className="group p-8 bg-linear-to-br from-green-100 to-blue-100 dark:from-gray-700 dark:to-gray-800 rounded-2xl shadow-lg hover:shadow-xl transition-all duration-300 border border-gray-100 dark:border-gray-700"
|
||||
role="listitem"
|
||||
>
|
||||
<div
|
||||
className="w-14 h-14 bg-linear-to-br from-orange-500 to-orange-600 rounded-xl flex items-center justify-center mb-6 group-hover:scale-110 group-hover:rotate-3 transition-all duration-300 shadow-lg"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<Shield className="w-7 h-7 text-white" strokeWidth={2.5} />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">
|
||||
{t('home:features.secure_integration.title')}
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
|
||||
{t('home:features.secure_integration.desc')}
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Use Cases Section */}
|
||||
<section className="px-6 py-20" aria-labelledby="use-cases-heading">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="text-center mb-16">
|
||||
<SparklesText
|
||||
className="text-4xl lg:text-5xl mb-6"
|
||||
colors={{ first: '#3b82f6', second: '#8b5cf6' }}
|
||||
>
|
||||
{t('home:use_cases.section_title')}
|
||||
</SparklesText>
|
||||
<p className="text-xl text-gray-600 dark:text-gray-300 max-w-3xl mx-auto">
|
||||
{t('home:use_cases.section_subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid lg:grid-cols-2 gap-12" role="list">
|
||||
{/* Use Case 1 */}
|
||||
<div className="group bg-linear-to-br from-blue-100 to-purple-100 dark:from-gray-700 dark:to-gray-800 p-8 rounded-2xl hover:shadow-xl transition-all duration-300">
|
||||
<div className="flex items-start space-x-4">
|
||||
<div className="w-12 h-12 bg-linear-to-br from-blue-500 to-purple-500 rounded-xl flex items-center justify-center shrink-0 group-hover:scale-110 transition-transform duration-300 shadow-md">
|
||||
<Bot className="w-6 h-6 text-white" strokeWidth={2.5} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-xl font-bold mb-2 text-gray-900 dark:text-white">
|
||||
{t('home:use_cases.case1.title')}
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
{t('home:use_cases.case1.desc')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Use Case 2 */}
|
||||
<div className="group bg-linear-to-br from-green-100 to-blue-100 dark:from-gray-700 dark:to-gray-800 p-8 rounded-2xl hover:shadow-xl transition-all duration-300">
|
||||
<div className="flex items-start space-x-4">
|
||||
<div className="w-12 h-12 bg-linear-to-br from-green-500 to-blue-500 rounded-xl flex items-center justify-center shrink-0 group-hover:scale-110 transition-transform duration-300 shadow-md">
|
||||
<Sparkles className="w-6 h-6 text-white" strokeWidth={2.5} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold mb-2 text-gray-900 dark:text-white">
|
||||
{t('home:use_cases.case2.title')}
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
{t('home:use_cases.case2.desc')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Use Case 3 */}
|
||||
<div className="group bg-linear-to-br from-purple-100 to-pink-100 dark:from-gray-700 dark:to-gray-800 p-8 rounded-2xl hover:shadow-xl transition-all duration-300">
|
||||
<div className="flex items-start space-x-4">
|
||||
<div className="w-12 h-12 bg-linear-to-br from-purple-500 to-pink-500 rounded-xl flex items-center justify-center shrink-0 group-hover:scale-110 transition-transform duration-300 shadow-md">
|
||||
<PlayCircle className="w-6 h-6 text-white fill-white/30" strokeWidth={2.5} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold mb-2 text-gray-900 dark:text-white">
|
||||
{t('home:use_cases.case3.title')}
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
{t('home:use_cases.case3.desc')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Use Case 4 */}
|
||||
<div className="group bg-linear-to-br from-orange-100 to-red-100 dark:from-gray-700 dark:to-gray-800 p-8 rounded-2xl hover:shadow-xl transition-all duration-300">
|
||||
<div className="flex items-start space-x-4">
|
||||
<div className="w-12 h-12 bg-linear-to-br from-orange-500 to-red-500 rounded-xl flex items-center justify-center shrink-0 group-hover:scale-110 transition-transform duration-300 shadow-md">
|
||||
<Users className="w-6 h-6 text-white" strokeWidth={2.5} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold mb-2 text-gray-900 dark:text-white">
|
||||
{t('home:use_cases.case4.title')}
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
{t('home:use_cases.case4.desc')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
98
packages/website/src/pages/docs/Layout.tsx
Normal file
98
packages/website/src/pages/docs/Layout.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import { ReactNode } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Link, useLocation } from 'wouter'
|
||||
|
||||
interface DocsLayoutProps {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
interface NavItem {
|
||||
title: string
|
||||
path: string
|
||||
}
|
||||
|
||||
interface NavSection {
|
||||
title: string
|
||||
items: NavItem[]
|
||||
}
|
||||
|
||||
export default function DocsLayout({ children }: DocsLayoutProps) {
|
||||
const { t } = useTranslation('common')
|
||||
const [location] = useLocation()
|
||||
|
||||
const navigationSections: NavSection[] = [
|
||||
{
|
||||
title: t('nav.introduction'),
|
||||
items: [
|
||||
{ title: t('nav.overview'), path: '/docs/introduction/overview' },
|
||||
{ title: t('nav.quick_start'), path: '/docs/introduction/quick-start' },
|
||||
{ title: t('nav.limitations'), path: '/docs/introduction/limitations' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: t('nav.features'),
|
||||
items: [
|
||||
{ title: t('nav.model_integration'), path: '/docs/features/model-integration' },
|
||||
{ title: t('nav.custom_tools'), path: '/docs/features/custom-tools' },
|
||||
{ title: t('nav.knowledge_injection'), path: '/docs/features/custom-instructions' },
|
||||
{ title: t('nav.security_permissions'), path: '/docs/features/security-permissions' },
|
||||
{ title: t('nav.data_masking'), path: '/docs/features/data-masking' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: t('nav.integration'),
|
||||
items: [
|
||||
{ title: t('nav.cdn_setup'), path: '/docs/integration/cdn-setup' },
|
||||
{ title: t('nav.configuration'), path: '/docs/integration/configuration' },
|
||||
{ title: t('nav.best_practices'), path: '/docs/integration/best-practices' },
|
||||
{ title: t('nav.third_party_agent'), path: '/docs/integration/third-party-agent' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="max-w-7xl mx-auto px-6 py-8 overflow-x-auto">
|
||||
<div className="flex gap-8 min-w-[900px]">
|
||||
{/* Sidebar */}
|
||||
<aside className="w-64 shrink-0" role="complementary" aria-label="文档导航">
|
||||
<div className="sticky top-8">
|
||||
<nav className="space-y-8" role="navigation" aria-label="文档章节">
|
||||
{navigationSections.map((section) => (
|
||||
<section key={section.title}>
|
||||
<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">
|
||||
{section.items.map((item) => {
|
||||
const isActive = location === item.path
|
||||
return (
|
||||
<li key={item.path}>
|
||||
<Link
|
||||
href={item.path}
|
||||
className={`block px-3 py-2 rounded-lg transition-colors duration-200 ${
|
||||
isActive
|
||||
? 'bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 font-medium'
|
||||
: '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}
|
||||
>
|
||||
{item.title}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</section>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="flex-1 min-w-0" id="main-content" role="main">
|
||||
<div className="prose prose-lg dark:prose-invert max-w-none">{children}</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import CodeEditor from '@/components/CodeEditor'
|
||||
|
||||
export default function Instructions() {
|
||||
const { i18n } = useTranslation()
|
||||
const isZh = i18n.language === 'zh-CN'
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold mb-6">{isZh ? '知识注入' : 'Instructions'}</h1>
|
||||
|
||||
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
|
||||
{isZh
|
||||
? '通过 instructions 配置,为 AI 注入系统级指导和页面级上下文,让它更好地理解你的业务场景。'
|
||||
: 'Use the instructions config to inject system-level directives and page-specific context, helping the AI better understand your application.'}
|
||||
</p>
|
||||
|
||||
{/* System Instructions */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-3xl font-bold mb-6">
|
||||
{isZh ? '系统级指导 (System Instructions)' : 'System Instructions'}
|
||||
</h2>
|
||||
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-6">
|
||||
{isZh
|
||||
? '全局提示词,应用于所有任务。定义 AI 的角色、工作风格和行为边界。'
|
||||
: "Global directives applied to all tasks. Define the AI's role, working style, and behavioral boundaries."}
|
||||
</p>
|
||||
|
||||
<CodeEditor
|
||||
className="mb-6"
|
||||
code={`const agent = new PageAgent({
|
||||
// ...other config
|
||||
instructions: {
|
||||
system: \`
|
||||
You are a professional e-commerce assistant.
|
||||
|
||||
Guidelines:
|
||||
- Always confirm before submitting orders
|
||||
- Double-check prices and quantities
|
||||
- Report errors immediately instead of retrying blindly
|
||||
\`
|
||||
}
|
||||
})`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Page Instructions */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-3xl font-bold mb-6">
|
||||
{isZh ? '页面级指导 (Page Instructions)' : 'Page Instructions'}
|
||||
</h2>
|
||||
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-6">
|
||||
{isZh
|
||||
? '动态回调函数,在每个 step 执行前调用,根据当前页面 URL 返回特定提示词。适用于为不同页面提供针对性的操作引导。'
|
||||
: 'A dynamic callback invoked before each step. Returns page-specific instructions based on the current URL. Useful for providing targeted guidance on different pages.'}
|
||||
</p>
|
||||
|
||||
<CodeEditor
|
||||
className="mb-6"
|
||||
code={`const agent = new PageAgent({
|
||||
// ...other config
|
||||
instructions: {
|
||||
system: 'You are an order management assistant.',
|
||||
|
||||
getPageInstructions: (url) => {
|
||||
if (url.includes('/checkout')) {
|
||||
return \`
|
||||
This is the checkout page.
|
||||
- Verify shipping address before proceeding
|
||||
- Check if any discounts are applied
|
||||
- Confirm the total amount with the user
|
||||
\`
|
||||
}
|
||||
|
||||
if (url.includes('/products')) {
|
||||
return \`
|
||||
This is the product listing page.
|
||||
- Use filters to narrow down search results
|
||||
- Check stock availability before adding to cart
|
||||
\`
|
||||
}
|
||||
|
||||
return undefined // No special instructions for other pages
|
||||
}
|
||||
}
|
||||
})`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* How It Works */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-3xl font-bold mb-6">{isZh ? '工作原理' : 'How It Works'}</h2>
|
||||
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||
{isZh
|
||||
? '在每个执行步骤之前,page-agent 会将 instructions 拼接到用户提示词中:'
|
||||
: 'Before each execution step, page-agent prepends the instructions to the user prompt:'}
|
||||
</p>
|
||||
|
||||
<CodeEditor
|
||||
language="xml"
|
||||
className="mb-6"
|
||||
code={`<instructions>
|
||||
<system_instructions>
|
||||
You are a professional e-commerce assistant.
|
||||
...
|
||||
</system_instructions>
|
||||
<page_instructions>
|
||||
This is the checkout page.
|
||||
...
|
||||
</page_instructions>
|
||||
</instructions>
|
||||
|
||||
<!-- followed by agent state, history, and browser state -->`}
|
||||
/>
|
||||
|
||||
<ul className="list-disc list-inside space-y-2 text-gray-600 dark:text-gray-400">
|
||||
<li>
|
||||
{isZh
|
||||
? '如果 system 为空,则不输出 <system_instructions> 标签'
|
||||
: 'If system is empty, the <system_instructions> tag is omitted'}
|
||||
</li>
|
||||
<li>
|
||||
{isZh
|
||||
? '如果 getPageInstructions 返回空值,则不输出 <page_instructions> 标签'
|
||||
: 'If getPageInstructions returns empty, the <page_instructions> tag is omitted'}
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
104
packages/website/src/pages/docs/features/custom-tools/page.tsx
Normal file
104
packages/website/src/pages/docs/features/custom-tools/page.tsx
Normal file
@@ -0,0 +1,104 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import BetaNotice from '@/components/BetaNotice'
|
||||
import CodeEditor from '@/components/CodeEditor'
|
||||
|
||||
export default function CustomTools() {
|
||||
const { t } = useTranslation('docs')
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold mb-6">{t('custom_tools.title')}</h1>
|
||||
|
||||
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
|
||||
{t('custom_tools.subtitle')}
|
||||
</p>
|
||||
|
||||
<div className="space-y-8">
|
||||
<section>
|
||||
<h2 className="text-2xl font-bold mb-4">{t('custom_tools.registration')}</h2>
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||
{t('custom_tools.registration_desc')}
|
||||
</p>
|
||||
|
||||
<CodeEditor
|
||||
code={`import zod from 'zod'
|
||||
import { PageAgent, tool } from 'page-agent'
|
||||
|
||||
// override internal tool
|
||||
const customTools = {
|
||||
ask_user: tool({
|
||||
description:
|
||||
'Ask the user or parent model a question and wait for their answer. Use this if you need more information or clarification.',
|
||||
inputSchema: zod.object({
|
||||
question: zod.string(),
|
||||
}),
|
||||
execute: async function (this: PageAgent, input) {
|
||||
const answer = await do_some_thing(input.question)
|
||||
return "✅ Received user answer: " + answer
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// remove internal tool
|
||||
const customTools = {
|
||||
ask_user: null // never ask user questions
|
||||
}
|
||||
|
||||
const pageAgent = new PageAgent({customTools})
|
||||
`}
|
||||
language="javascript"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-bold mb-4">{t('custom_tools.page_filter')}</h2>
|
||||
|
||||
<BetaNotice />
|
||||
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||
{t('custom_tools.page_filter_desc')}
|
||||
</p>
|
||||
|
||||
<CodeEditor
|
||||
code={`pageAgent.registerTool({
|
||||
name: 'approveOrder',
|
||||
description: '审批订单',
|
||||
input: z.object({
|
||||
orderId: z.string(),
|
||||
approved: z.boolean()
|
||||
}),
|
||||
execute: async (params) => {
|
||||
// 审批逻辑
|
||||
},
|
||||
// 可选:页面过滤器
|
||||
pageFilter: {
|
||||
// 只在订单管理页面显示
|
||||
include: ['/admin/orders', '/admin/orders/*'],
|
||||
// 排除特定页面
|
||||
exclude: ['/admin/orders/archived']
|
||||
}
|
||||
})`}
|
||||
language="javascript"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-bold mb-4">{t('custom_tools.best_practices')}</h2>
|
||||
<div className="space-y-4">
|
||||
<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">
|
||||
{t('custom_tools.bp_performance')}
|
||||
</h3>
|
||||
<ul className="text-gray-600 dark:text-gray-300 space-y-1 text-sm">
|
||||
<li>{t('custom_tools.bp_1')}</li>
|
||||
<li>{t('custom_tools.bp_2')}</li>
|
||||
<li>{t('custom_tools.bp_3')}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import CodeEditor from '@/components/CodeEditor'
|
||||
|
||||
export default function DataMasking() {
|
||||
const { i18n } = useTranslation()
|
||||
const isZh = i18n.language === 'zh-CN'
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold mb-6">{isZh ? '数据脱敏' : 'Data Masking'}</h1>
|
||||
|
||||
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
|
||||
{isZh
|
||||
? '使用 transformPageContent 钩子在页面内容发送给 LLM 之前进行处理,可用于检查清洗效果、修改页面信息、隐藏敏感数据等。'
|
||||
: 'Use the transformPageContent hook to process page content before sending to LLM. Useful for inspecting extraction results, modifying page info, and masking sensitive data.'}
|
||||
</p>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-3xl font-bold mb-6">{isZh ? '接口定义' : 'API Definition'}</h2>
|
||||
|
||||
<CodeEditor
|
||||
className="mb-6"
|
||||
code={`interface PageAgentConfig {
|
||||
/**
|
||||
* Transform page content before sending to LLM.
|
||||
* Called after DOM extraction and simplification.
|
||||
*/
|
||||
transformPageContent?: (content: string) => Promise<string> | string
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-3xl font-bold mb-6">
|
||||
{isZh ? '常用脱敏规则' : 'Common Masking Patterns'}
|
||||
</h2>
|
||||
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-6">
|
||||
{isZh
|
||||
? '以下示例展示了如何脱敏常见的敏感信息:'
|
||||
: 'The following example shows how to mask common sensitive data:'}
|
||||
</p>
|
||||
|
||||
<CodeEditor
|
||||
code={`const agent = new PageAgent({
|
||||
transformPageContent: async (content) => {
|
||||
// China phone number (11 digits starting with 1)
|
||||
content = content.replace(/\\b(1[3-9]\\d)(\\d{4})(\\d{4})\\b/g, '$1****$3')
|
||||
|
||||
// Email address
|
||||
content = content.replace(
|
||||
/\\b([a-zA-Z0-9._%+-])[^@]*(@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})\\b/g,
|
||||
'$1***$2'
|
||||
)
|
||||
|
||||
// China ID card number (18 digits)
|
||||
content = content.replace(
|
||||
/\\b(\\d{6})(19|20\\d{2})(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])(\\d{3}[\\dXx])\\b/g,
|
||||
'$1********$5'
|
||||
)
|
||||
|
||||
// Bank card number (16-19 digits)
|
||||
content = content.replace(/\\b(\\d{4})\\d{8,11}(\\d{4})\\b/g, '$1********$2')
|
||||
|
||||
return content
|
||||
}
|
||||
})`}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import CodeEditor from '@/components/CodeEditor'
|
||||
|
||||
// Recommended models: lightweight with excellent tool call capabilities
|
||||
const MODELS = {
|
||||
recommended: ['gpt-4.1-mini', 'claude-haiku-4.5', 'gemini-3-flash', 'deepseek-3.2', 'gpt-5.2'],
|
||||
verified: [
|
||||
'qwen-3-max',
|
||||
'gpt-4.1',
|
||||
'gpt-5',
|
||||
'gpt-5-mini',
|
||||
'gpt-5.1',
|
||||
'grok-4',
|
||||
'grok-code-fast',
|
||||
'claude-sonnet-3.5',
|
||||
'claude-sonnet-4.5',
|
||||
'claude-opus-4.5',
|
||||
'gemini-2.5',
|
||||
'gemini-3-pro',
|
||||
],
|
||||
}
|
||||
|
||||
export default function ModelIntegration() {
|
||||
const { t } = useTranslation('docs')
|
||||
const allModels = [...MODELS.recommended, ...MODELS.verified]
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl">
|
||||
<h1 className="text-4xl font-bold mb-4">{t('model_integration.title')}</h1>
|
||||
<p className="text-lg text-gray-600 dark:text-gray-400 mb-8">
|
||||
{t('model_integration.subtitle')}
|
||||
</p>
|
||||
|
||||
{/* Models Section */}
|
||||
<section className="mb-10">
|
||||
<h2 className="text-2xl font-semibold mb-3">{t('model_integration.available')}</h2>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
||||
{t('model_integration.recommendation_logic')}
|
||||
</p>
|
||||
<div className="bg-linear-to-br from-emerald-50 to-cyan-50 dark:from-emerald-950/30 dark:to-cyan-950/30 rounded-xl p-6 border border-emerald-200/50 dark:border-emerald-800/50">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{allModels.map((model) => {
|
||||
const isRecommended = MODELS.recommended.includes(model)
|
||||
return (
|
||||
<div
|
||||
key={model}
|
||||
className={`px-3 py-1.5 rounded-md text-sm font-medium font-mono transition-colors ${
|
||||
isRecommended
|
||||
? 'bg-emerald-500 text-white shadow-sm'
|
||||
: 'bg-white/80 dark:bg-gray-800/80 text-gray-800 dark:text-gray-200 border border-gray-300 dark:border-gray-600'
|
||||
}`}
|
||||
>
|
||||
{model}
|
||||
{isRecommended && <span className="ml-1">⭐</span>}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400 mt-5">⭐ baseline models</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Tips Section */}
|
||||
<section className="mb-10">
|
||||
<h2 className="text-2xl font-semibold mb-4">{t('model_integration.tips')}</h2>
|
||||
<div className="p-4 bg-blue-50 dark:bg-blue-950/20 rounded-lg border border-blue-200 dark:border-blue-800">
|
||||
<ul className="text-sm text-gray-700 dark:text-gray-300 space-y-2 list-disc pl-5">
|
||||
<li>{t('model_integration.tip_2')}</li>
|
||||
<li>{t('model_integration.tip_3')}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Security Section */}
|
||||
<section className="mb-10">
|
||||
<h2 className="text-2xl font-semibold mb-4">{t('model_integration.security')}</h2>
|
||||
<div className="bg-yellow-50 dark:bg-yellow-950/20 border-l-4 border-yellow-500 p-5 rounded-r-lg mb-4">
|
||||
<p className="text-sm font-semibold text-yellow-900 dark:text-yellow-200">
|
||||
{t('model_integration.security_warning')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-gray-50 dark:bg-gray-900/30 rounded-lg p-5 border border-gray-200 dark:border-gray-800">
|
||||
<h3 className="font-semibold text-gray-900 dark:text-gray-100 mb-2">
|
||||
{t('model_integration.security_backend_proxy')}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-3">
|
||||
{t('model_integration.security_backend_desc')}
|
||||
</p>
|
||||
<ul className="text-sm text-gray-600 dark:text-gray-400 space-y-1">
|
||||
<li>{t('model_integration.security_method_1')}</li>
|
||||
<li>{t('model_integration.security_method_2')}</li>
|
||||
<li>{t('model_integration.security_method_3')}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Configuration Section */}
|
||||
<section className="mb-10">
|
||||
<h2 className="text-2xl font-semibold mb-4">{t('model_integration.configuration')}</h2>
|
||||
<CodeEditor
|
||||
code={`// OpenAI-compatible services (e.g., Alibaba Bailian)
|
||||
const pageAgent = new PageAgent({
|
||||
baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
||||
apiKey: 'your-api-key',
|
||||
model: 'qwen-plus'
|
||||
});
|
||||
|
||||
// Self-hosted models (e.g., Ollama)
|
||||
const pageAgent = new PageAgent({
|
||||
baseURL: 'http://localhost:11434/v1',
|
||||
apiKey: 'N/A', // Ollama typically accepts any value
|
||||
model: 'qwen3:latest'
|
||||
});
|
||||
|
||||
// Free testing endpoint
|
||||
// Note: Rate-limited, content-filtered, subject to change. Replace with your own.
|
||||
// Note: Uses official DeepSeek-chat (3.2). See DeepSeek website for terms & privacy.
|
||||
const DEMO_MODEL = 'PAGE-AGENT-FREE-TESTING-RANDOM'
|
||||
const DEMO_BASE_URL = 'https://hwcxiuzfylggtcktqgij.supabase.co/functions/v1/llm-testing-proxy'
|
||||
const DEMO_API_KEY = 'PAGE-AGENT-FREE-TESTING-RANDOM'
|
||||
`}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import BetaNotice from '@/components/BetaNotice'
|
||||
|
||||
export default function SecurityPermissions() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold mb-6">安全与权限</h1>
|
||||
|
||||
<BetaNotice />
|
||||
|
||||
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
|
||||
page-agent 提供四种安全机制,确保 AI 操作在可控范围内进行。
|
||||
</p>
|
||||
|
||||
<div className="space-y-6">
|
||||
<section>
|
||||
<h2 className="text-2xl font-bold mb-3">元素操作黑白名单</h2>
|
||||
<div className="space-y-3">
|
||||
<div className="p-4 bg-red-50 dark:bg-red-900/20 rounded-lg">
|
||||
<h3 className="text-lg font-semibold text-red-900 dark:text-red-300">
|
||||
🚫 操作黑名单
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
禁止 AI 操作敏感元素,如删除按钮、支付按钮等。
|
||||
</p>
|
||||
</div>
|
||||
<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>
|
||||
<p className="text-gray-600 dark:text-gray-300">明确定义 AI 可以操作的元素范围。</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-bold mb-3">URL 黑白名单</h2>
|
||||
<div className="space-y-3">
|
||||
<div className="p-4 bg-red-50 dark:bg-red-900/20 rounded-lg">
|
||||
<h3 className="text-lg font-semibold text-red-900 dark:text-red-300">
|
||||
🚫 URL 黑名单
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">禁止 AI 访问敏感页面和危险链接。</p>
|
||||
</div>
|
||||
<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">
|
||||
✅ URL 白名单
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">限制 AI 只能访问预定义的安全页面。</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-bold mb-3">Instruction 安全约束</h2>
|
||||
<div className="p-4 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg">
|
||||
<h3 className="text-lg font-semibold mb-2 text-yellow-900 dark:text-yellow-300">
|
||||
⚠️ 高危操作控制
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-3">
|
||||
在 AI 指令中明确列举高危操作,通过两种策略进行控制:
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
<div className="pl-3 border-l-2 border-red-400">
|
||||
<p className="font-medium text-red-700 dark:text-red-300">完全禁止操作</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
对极高风险操作明确禁止执行
|
||||
</p>
|
||||
</div>
|
||||
<div className="pl-3 border-l-2 border-orange-400">
|
||||
<p className="font-medium text-orange-700 dark:text-orange-300">需用户确认操作</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
对中等风险操作要求用户明确同意
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import BetaNotice from '@/components/BetaNotice'
|
||||
import CodeEditor from '@/components/CodeEditor'
|
||||
|
||||
export default function BestPractices() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold mb-6">最佳实践</h1>
|
||||
|
||||
<BetaNotice />
|
||||
|
||||
<p className="text-xl text-gray-600 dark:text-gray-300 mb-6 leading-relaxed">
|
||||
使用 page-agent 的最佳实践和常见问题解决方案。
|
||||
</p>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-3">性能优化</h2>
|
||||
|
||||
<div className="space-y-4 mb-6">
|
||||
<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">
|
||||
⚡ 减少 API 调用
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-3">
|
||||
合并多个操作指令,减少与 AI 模型的交互次数。
|
||||
</p>
|
||||
|
||||
<CodeEditor
|
||||
code={`// 推荐:合并操作
|
||||
await pageAgent.execute('填写表单:姓名张三,邮箱test@example.com,然后提交');
|
||||
|
||||
// 不推荐:分别操作
|
||||
await pageAgent.execute('填写姓名张三');
|
||||
await pageAgent.execute('填写邮箱test@example.com');
|
||||
await pageAgent.execute('点击提交按钮');`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
使用具体、明确的元素描述,提高操作成功率。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-3">安全建议</h2>
|
||||
|
||||
<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">
|
||||
<h3 className="font-semibold mb-1 text-red-900 dark:text-red-300">重要操作保护</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">对删除、支付等敏感操作设置黑名单保护。</p>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<p className="text-gray-600 dark:text-gray-300">启用数据脱敏功能,保护用户隐私信息。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-3">调试技巧</h2>
|
||||
|
||||
<CodeEditor code={`// TODO`} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import BetaNotice from '@/components/BetaNotice'
|
||||
import CodeEditor from '@/components/CodeEditor'
|
||||
import { CDN_CN_URL, CDN_URL } from '@/constants'
|
||||
|
||||
export default function CdnSetup() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold mb-6">CDN 引入</h1>
|
||||
|
||||
<p className="text-xl text-gray-600 dark:text-gray-300 mb-6 leading-relaxed">
|
||||
通过 CDN 快速集成 page-agent,无需复杂的构建配置。
|
||||
</p>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-3">快速开始</h2>
|
||||
|
||||
<CodeEditor
|
||||
className="mb-8"
|
||||
code={`
|
||||
// CDN: \t${CDN_URL}
|
||||
// Mirror: \t${CDN_CN_URL}
|
||||
<script
|
||||
src="${CDN_URL}"
|
||||
crossorigin="true"
|
||||
type="text/javascript"
|
||||
></script>
|
||||
|
||||
<script>
|
||||
// window.pageAgent
|
||||
</script>`}
|
||||
/>
|
||||
|
||||
<div className="bg-yellow-50 dark:bg-yellow-900/20 p-4 rounded-lg">
|
||||
<h3 className="text-lg font-semibold mb-2 text-yellow-900 dark:text-yellow-300">
|
||||
⚠️ 注意事项
|
||||
</h3>
|
||||
<ul className="text-gray-600 dark:text-gray-300 space-y-1">
|
||||
<li>• 生产环境建议使用固定版本号</li>
|
||||
<li>• 确保 HTTPS 环境下使用</li>
|
||||
<li>• 配置 CSP 策略允许脚本执行</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import CodeEditor from '@/components/CodeEditor'
|
||||
|
||||
export default function Configuration() {
|
||||
const { i18n } = useTranslation()
|
||||
const isZh = i18n.language === 'zh-CN'
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold mb-6">{isZh ? '配置选项' : 'Configuration'}</h1>
|
||||
|
||||
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
|
||||
{isZh
|
||||
? 'PageAgent 的完整配置接口定义。'
|
||||
: 'Complete configuration interface for PageAgent.'}
|
||||
</p>
|
||||
|
||||
<CodeEditor
|
||||
className="mb-8"
|
||||
language="typescript"
|
||||
code={`type PageAgentConfig = LLMConfig & AgentConfig & PageControllerConfig
|
||||
|
||||
// ============ LLM Configuration ============
|
||||
|
||||
interface LLMConfig {
|
||||
baseURL?: string
|
||||
apiKey?: string
|
||||
model?: string
|
||||
temperature?: number
|
||||
maxRetries?: number
|
||||
|
||||
/**
|
||||
* Custom fetch function for LLM API requests.
|
||||
* Use this to customize headers, credentials, proxy, etc.
|
||||
*/
|
||||
customFetch?: typeof globalThis.fetch
|
||||
}
|
||||
|
||||
// ============ Agent Configuration ============
|
||||
|
||||
interface AgentConfig {
|
||||
language?: 'en-US' | 'zh-CN'
|
||||
|
||||
/** Custom tools to extend or override built-in tools */
|
||||
customTools?: Record<string, PageAgentTool | null>
|
||||
|
||||
/** Instructions to guide the agent's behavior */
|
||||
instructions?: {
|
||||
/** Global system-level instructions, applied to all tasks */
|
||||
system?: string
|
||||
|
||||
/** Dynamic page-level instructions callback */
|
||||
getPageInstructions?: (url: string) => string | undefined | null
|
||||
}
|
||||
|
||||
// Lifecycle hooks
|
||||
onBeforeStep?: (stepCnt: number) => Promise<void> | void
|
||||
onAfterStep?: (stepCnt: number, history: AgentHistory[]) => Promise<void> | void
|
||||
onBeforeTask?: () => Promise<void> | void
|
||||
onAfterTask?: (result: ExecutionResult) => Promise<void> | void
|
||||
onDispose?: (reason?: string) => void
|
||||
|
||||
/**
|
||||
* Transform page content before sending to LLM.
|
||||
* Use cases: inspect extraction results, modify page info, mask sensitive data.
|
||||
*/
|
||||
transformPageContent?: (content: string) => Promise<string> | string
|
||||
|
||||
/** @experimental Enable JavaScript execution tool */
|
||||
experimentalScriptExecutionTool?: boolean
|
||||
}
|
||||
|
||||
// ============ PageController Configuration ============
|
||||
|
||||
interface PageControllerConfig {
|
||||
/** Elements to exclude from interaction */
|
||||
interactiveBlacklist?: (Element | (() => Element))[]
|
||||
|
||||
/** Elements to force include for interaction */
|
||||
interactiveWhitelist?: (Element | (() => Element))[]
|
||||
|
||||
/** Additional attributes to include in DOM extraction */
|
||||
include_attributes?: string[]
|
||||
|
||||
/** Highlight overlay opacity (0-1) */
|
||||
highlightOpacity?: number
|
||||
|
||||
/** Highlight label opacity (0-1) */
|
||||
highlightLabelOpacity?: number
|
||||
|
||||
/** Viewport expansion in pixels (-1 for full page) */
|
||||
viewportExpansion?: number
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import CodeEditor from '@/components/CodeEditor'
|
||||
|
||||
export default function ThirdPartyAgentPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold mb-6">接入第三方 Agent</h1>
|
||||
<p className="mb-6 leading-relaxed text-gray-600 dark:text-gray-300">
|
||||
将 pageAgent 作为工具接入你的答疑助手或 Agent 系统,成为你 Agent 的眼和手。
|
||||
</p>
|
||||
|
||||
<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>
|
||||
<p className="text-blue-800 dark:text-blue-200">
|
||||
让你的答疑机器人不再只是"嘴巴",而是拥有"眼睛"和"手"的完整智能体。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-4">集成方式</h2>
|
||||
|
||||
<div className="space-y-4 mb-6">
|
||||
<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">
|
||||
1. Function Calling
|
||||
</h3>
|
||||
<CodeEditor
|
||||
code={`// 定义工具
|
||||
const pageAgentTool = {
|
||||
name: "page_agent",
|
||||
description: "执行网页操作",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
instruction: { type: "string", description: "操作指令" }
|
||||
},
|
||||
required: ["instruction"]
|
||||
},
|
||||
execute: async (params) => {
|
||||
const result = await pageAgent.execute(params.instruction)
|
||||
return { success: result.success, message: result.message }
|
||||
}
|
||||
}
|
||||
|
||||
// 注册到你的 agent 中`}
|
||||
language="javascript"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-4">应用场景</h2>
|
||||
<div className="grid md:grid-cols-2 gap-4 mb-6">
|
||||
<div className="bg-linear-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 text-gray-900 dark:text-white">🤖 智能客服系统</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">
|
||||
客服机器人帮用户直接操作系统,如"帮我提交工单"
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-linear-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 text-gray-900 dark:text-white">📋 业务流程助手</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">
|
||||
引导新员工完成复杂流程,如"完成客户入职"
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-linear-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 text-gray-900 dark:text-white">🎯 个人效率助手</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">
|
||||
跨网站帮你完成任务,如"预订会议室"
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-linear-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 text-gray-900 dark:text-white">🔧 运维自动化</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">
|
||||
通过自然语言操作管理后台,如"重启服务器"
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-4">最佳实践</h2>
|
||||
<div className="space-y-4 mb-6">
|
||||
<div className="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||
<h3 className="text-lg font-semibold mb-2 text-gray-900 dark:text-white">错误处理</h3>
|
||||
<CodeEditor code={`// @TODO`} language="javascript" />
|
||||
</div>
|
||||
|
||||
<div className="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||
<h3 className="text-lg font-semibold mb-2 text-gray-900 dark:text-white">权限控制</h3>
|
||||
<CodeEditor code={`// @TODO`} language="javascript" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4 mb-6">
|
||||
<h3 className="text-lg font-semibold text-yellow-900 dark:text-yellow-100 mb-2">
|
||||
⚠️ 注意事项
|
||||
</h3>
|
||||
<ul className="text-yellow-800 dark:text-yellow-200 space-y-1 text-sm">
|
||||
<li>• 确保目标网站允许自动化操作</li>
|
||||
<li>• 实现适当的频率限制</li>
|
||||
<li>• 敏感操作建议要求人工确认</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="bg-linear-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 text-gray-900 dark:text-white">🎉 开始集成</h3>
|
||||
<p className="mb-3 text-gray-700 dark:text-gray-300">
|
||||
通过这种方式,你的 Agent 系统就能真正成为用户的智能助手。
|
||||
</p>
|
||||
<a
|
||||
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"
|
||||
>
|
||||
查看配置选项 →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export default function LimitationsPage() {
|
||||
const { t } = useTranslation('docs')
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-4xl font-bold mb-4 text-gray-900 dark:text-white">
|
||||
{t('limitations.title')}
|
||||
</h1>
|
||||
<p className="text-xl text-gray-600 dark:text-gray-300">{t('limitations.subtitle')}</p>
|
||||
</div>
|
||||
|
||||
<div className="prose prose-lg dark:prose-invert max-w-none">
|
||||
<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">
|
||||
<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">
|
||||
<li>{t('limitations.spa_limit_1')}</li>
|
||||
<li>{t('limitations.spa_limit_2')}</li>
|
||||
<li>{t('limitations.spa_limit_3')}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<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">
|
||||
<h3 className="font-semibold mb-4">{t('limitations.supported_ops')}</h3>
|
||||
<div className="grid md:grid-cols-2 gap-4 mb-6">
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center text-green-600 dark:text-green-400">
|
||||
<span className="mr-2">✅</span>
|
||||
<span>{t('limitations.op_click')}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-green-600 dark:text-green-400">
|
||||
<span className="mr-2">✅</span>
|
||||
<span>{t('limitations.op_input')}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-green-600 dark:text-green-400">
|
||||
<span className="mr-2">✅</span>
|
||||
<span>{t('limitations.op_scroll')}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-green-600 dark:text-green-400">
|
||||
<span className="mr-2">✅</span>
|
||||
<span>{t('limitations.op_submit')}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center text-green-600 dark:text-green-400">
|
||||
<span className="mr-2">✅</span>
|
||||
<span>{t('limitations.op_select')}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-green-600 dark:text-green-400">
|
||||
<span className="mr-2">✅</span>
|
||||
<span>{t('limitations.op_focus')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 className="font-semibold mb-4">{t('limitations.unsupported_ops')}</h3>
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center text-red-600 dark:text-red-400">
|
||||
<span className="mr-2">❌</span>
|
||||
<span>{t('limitations.op_hover')}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-red-600 dark:text-red-400">
|
||||
<span className="mr-2">❌</span>
|
||||
<span>{t('limitations.op_drag')}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-red-600 dark:text-red-400">
|
||||
<span className="mr-2">❌</span>
|
||||
<span>{t('limitations.op_context')}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center text-red-600 dark:text-red-400">
|
||||
<span className="mr-2">❌</span>
|
||||
<span>{t('limitations.op_draw')}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-red-600 dark:text-red-400">
|
||||
<span className="mr-2">❌</span>
|
||||
<span>{t('limitations.op_keyboard')}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-red-600 dark:text-red-400">
|
||||
<span className="mr-2">❌</span>
|
||||
<span>{t('limitations.op_position')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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">
|
||||
<h3 className="font-semibold text-red-800 dark:text-red-200 mb-2">
|
||||
{t('limitations.no_vision_title')}
|
||||
</h3>
|
||||
<p className="text-red-700 dark:text-red-300 mb-3">{t('limitations.no_vision_desc')}</p>
|
||||
<ul className="text-red-700 dark:text-red-300 space-y-1">
|
||||
<li>{t('limitations.no_vision_1')}</li>
|
||||
<li>{t('limitations.no_vision_2')}</li>
|
||||
<li>{t('limitations.no_vision_3')}</li>
|
||||
<li>{t('limitations.no_vision_4')}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<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="space-y-4">
|
||||
<div>
|
||||
<h3 className="font-semibold mb-2">{t('limitations.req_semantic_title')}</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
{t('limitations.req_semantic_desc')}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold mb-2">{t('limitations.req_ux_title')}</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">{t('limitations.req_ux_desc')}</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold mb-2">{t('limitations.req_env_title')}</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">{t('limitations.req_env_desc')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>{t('limitations.future')}</h2>
|
||||
<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">
|
||||
{t('limitations.future_title')}
|
||||
</h3>
|
||||
<ul className="text-green-700 dark:text-green-300 space-y-1">
|
||||
<li>{t('limitations.future_1')}</li>
|
||||
<li>{t('limitations.future_2')}</li>
|
||||
<li>{t('limitations.future_3')}</li>
|
||||
<li>{t('limitations.future_4')}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
212
packages/website/src/pages/docs/introduction/overview/page.tsx
Normal file
212
packages/website/src/pages/docs/introduction/overview/page.tsx
Normal file
@@ -0,0 +1,212 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export default function Overview() {
|
||||
const { t } = useTranslation('docs')
|
||||
|
||||
return (
|
||||
<article>
|
||||
{/* 头图 */}
|
||||
<figure className="mb-8 rounded-xl overflow-hidden">
|
||||
<img
|
||||
src="https://img.alicdn.com/imgextra/i3/O1CN014xLeic1gnF2iWdzjJ_!!6000000004186-49-tps-1280-353.webp"
|
||||
alt="page-agent"
|
||||
className="w-full h-64 object-cover"
|
||||
/>
|
||||
</figure>
|
||||
|
||||
<div className="mb-8">
|
||||
<h1 className="text-4xl font-bold mb-4">{t('overview.title')}</h1>
|
||||
<p className="text-xl text-gray-600 dark:text-gray-300 mb-4 leading-relaxed">
|
||||
{t('overview.subtitle')}
|
||||
</p>
|
||||
|
||||
{/* Status Badges */}
|
||||
<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>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-bold mb-4">{t('overview.what_is')}</h2>
|
||||
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-8 leading-relaxed ">
|
||||
{t('overview.what_is_desc')}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<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="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">
|
||||
{t('overview.feature_dom.title')}
|
||||
</h3>
|
||||
<p className="text-gray-700 dark:text-gray-300">{t('overview.feature_dom.desc')}</p>
|
||||
</div>
|
||||
|
||||
<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">
|
||||
{t('overview.feature_secure.title')}
|
||||
</h3>
|
||||
<p className="text-gray-700 dark:text-gray-300">{t('overview.feature_secure.desc')}</p>
|
||||
</div>
|
||||
|
||||
<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">
|
||||
{t('overview.feature_backend.title')}
|
||||
</h3>
|
||||
<p className="text-gray-700 dark:text-gray-300">{t('overview.feature_backend.desc')}</p>
|
||||
</div>
|
||||
|
||||
<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">
|
||||
{t('overview.feature_accessible.title')}
|
||||
</h3>
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
{t('overview.feature_accessible.desc')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-4">{t('overview.vs_browser_use')}</h2>
|
||||
|
||||
<div className="overflow-x-auto mb-8">
|
||||
<table className="w-full border-collapse border border-gray-300 dark:border-gray-600">
|
||||
<thead>
|
||||
<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">
|
||||
{t('overview.table_feature')}
|
||||
</th>
|
||||
<th className="border border-gray-300 dark:border-gray-600 px-4 py-3 text-left">
|
||||
page-agent
|
||||
</th>
|
||||
<th className="border border-gray-300 dark:border-gray-600 px-4 py-3 text-left">
|
||||
browser-use
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3 font-medium">
|
||||
{t('overview.table_deployment')}
|
||||
</td>
|
||||
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
|
||||
{t('overview.table_deployment_pa')}
|
||||
</td>
|
||||
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
|
||||
{t('overview.table_deployment_bu')}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3 font-medium">
|
||||
{t('overview.table_scope')}
|
||||
</td>
|
||||
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
|
||||
{t('overview.table_scope_pa')}
|
||||
</td>
|
||||
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
|
||||
{t('overview.table_scope_bu')}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3 font-medium">
|
||||
{t('overview.table_user')}
|
||||
</td>
|
||||
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
|
||||
{t('overview.table_user_pa')}
|
||||
</td>
|
||||
<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>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-4">{t('overview.use_cases_title')}</h2>
|
||||
|
||||
<ul className="space-y-4 mb-8">
|
||||
<li className="flex items-start space-x-3">
|
||||
<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 shrink-0">
|
||||
1
|
||||
</span>
|
||||
<div className="text-gray-700 dark:text-gray-300">
|
||||
<strong>{t('overview.use_case1_title')}</strong> {t('overview.use_case1_desc')}
|
||||
</div>
|
||||
</li>
|
||||
<li className="flex items-start space-x-3">
|
||||
<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 shrink-0">
|
||||
2
|
||||
</span>
|
||||
<div className="text-gray-700 dark:text-gray-300">
|
||||
<strong>{t('overview.use_case2_title')}</strong> {t('overview.use_case2_desc')}
|
||||
</div>
|
||||
</li>
|
||||
<li className="flex items-start space-x-3">
|
||||
<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 shrink-0">
|
||||
3
|
||||
</span>
|
||||
<div className="text-gray-700 dark:text-gray-300">
|
||||
<strong>{t('overview.use_case3_title')}</strong> {t('overview.use_case3_desc')}
|
||||
</div>
|
||||
</li>
|
||||
<li className="flex items-start space-x-3">
|
||||
<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 shrink-0">
|
||||
4
|
||||
</span>
|
||||
<div className="text-gray-700 dark:text-gray-300">
|
||||
<strong>{t('overview.use_case4_title')}</strong> {t('overview.use_case4_desc')}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</article>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import BetaNotice from '@/components/BetaNotice'
|
||||
import CodeEditor from '@/components/CodeEditor'
|
||||
import { CDN_CN_URL, CDN_URL } from '@/constants'
|
||||
|
||||
export default function QuickStart() {
|
||||
const { t } = useTranslation('docs')
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold mb-6">{t('quick_start.title')}</h1>
|
||||
|
||||
<p className=" mb-6 leading-relaxed">{t('quick_start.subtitle')}</p>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-3">{t('quick_start.installation')}</h2>
|
||||
|
||||
<div className="space-y-4 mb-6">
|
||||
<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">
|
||||
{t('quick_start.step1_title')}
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<p className="text-sm font-medium mb-2">{t('quick_start.step1_cdn')}</p>
|
||||
<CodeEditor
|
||||
code={`// CDN: \t${CDN_URL}
|
||||
// Mirror: \t${CDN_CN_URL}
|
||||
<script src="${CDN_URL}" crossorigin="true" type="text/javascript"></script>`}
|
||||
language="html"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium mb-2">{t('quick_start.step1_npm')}</p>
|
||||
<CodeEditor
|
||||
code={`// npm install page-agent
|
||||
import PageAgent from 'page-agent'`}
|
||||
language="bash"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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">
|
||||
{t('quick_start.step2_title')}
|
||||
</h3>
|
||||
<CodeEditor
|
||||
code={`// 仅供测试使用,生产环境需要配置 LLM 接入点,本工具不提供 LLM 服务
|
||||
// 测试接口
|
||||
// @note: 限流,限制 prompt 内容,限制来源,随时变更,请替换成你自己的
|
||||
// @note: 使用 DeepSeek-chat(3.2) 官方版本,使用协议和隐私策略见 DeepSeek 网站
|
||||
const DEMO_MODEL = 'PAGE-AGENT-FREE-TESTING-RANDOM'
|
||||
const DEMO_BASE_URL = 'https://hwcxiuzfylggtcktqgij.supabase.co/functions/v1/llm-testing-proxy'
|
||||
const DEMO_API_KEY = 'PAGE-AGENT-FREE-TESTING-RANDOM'
|
||||
|
||||
const agent = new PageAgent({
|
||||
model: DEMO_MODEL,
|
||||
baseURL: DEMO_BASE_URL,
|
||||
apiKey: DEMO_API_KEY,
|
||||
language: 'zh-CN'
|
||||
})`}
|
||||
language="javascript"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<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">
|
||||
{t('quick_start.step3_title')}
|
||||
</h3>
|
||||
<CodeEditor
|
||||
code={`// 程序化执行自然语言指令
|
||||
await pageAgent.execute('点击提交按钮,然后填写用户名为张三');
|
||||
|
||||
// 或者显示对话框让用户输入指令
|
||||
pageAgent.panel.show()
|
||||
`}
|
||||
language="javascript"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user