Compare commits

...

13 Commits

Author SHA1 Message Date
Simon da440f63c3 chore(version): bump version to 1.0.1 2026-01-30 16:59:11 +08:00
Simon 622fbff790 docs: simplify layout; rm useless CDN-setup 2026-01-30 16:54:31 +08:00
Simon 68c944c3a4 docs: remove i18n dep 2026-01-30 16:47:23 +08:00
Simon 6cd3e3726c docs: typography 2026-01-30 16:34:27 +08:00
Simon d11a2593b3 docs: pretty chrome extension entry 2026-01-30 15:19:54 +08:00
Simon 5e936c35d7 docs: simplify i18n 2026-01-30 15:13:31 +08:00
Simon 97cdb1e1cd docs: Add Custom UI doc 2026-01-30 14:58:33 +08:00
Simon 7470c1987b chore(ext): update extension version 2026-01-29 22:34:15 +08:00
Simon b767f10a85 feat(ext): extending execute api 2026-01-29 22:26:31 +08:00
Simon 8a03391c95 fix(core): change emitActivity to #emitActivity 2026-01-29 21:15:24 +08:00
Simon 5cfaa292d3 feat(ext): add agent heart beat check 2026-01-29 19:21:57 +08:00
Simon 4e87940127 fix(ext): update message handlers
> If multiple listeners are registered for onMessage,
> only the first listener to respond, reject, or throw
> an error will affect the sender; all other listeners
> will run, but their results will be ignored.
2026-01-29 18:37:42 +08:00
Simon 5d77990187 feat(docs): add Chrome extension documentation 2026-01-28 23:27:11 +08:00
58 changed files with 1425 additions and 1238 deletions
+3 -3
View File
@@ -40,7 +40,7 @@
```html
<script
src="https://registry.npmmirror.com/page-agent/1.0.0/files/dist/iife/page-agent.demo.js"
src="https://registry.npmmirror.com/page-agent/1.0.1/files/dist/iife/page-agent.demo.js"
crossorigin="true"
></script>
```
@@ -50,8 +50,8 @@
| Mirrors | URL |
| ------- | ---------------------------------------------------------------------------------- |
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.0.0/dist/iife/page-agent.demo.js |
| China | https://registry.npmmirror.com/page-agent/1.0.0/files/dist/iife/page-agent.demo.js |
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.0.1/dist/iife/page-agent.demo.js |
| China | https://registry.npmmirror.com/page-agent/1.0.1/files/dist/iife/page-agent.demo.js |
### NPM 安装
+3 -3
View File
@@ -40,7 +40,7 @@ Fastest way to try PageAgent with our free Demo LLM:
```html
<script
src="https://cdn.jsdelivr.net/npm/page-agent@1.0.0/dist/iife/page-agent.demo.js"
src="https://cdn.jsdelivr.net/npm/page-agent@1.0.1/dist/iife/page-agent.demo.js"
crossorigin="true"
></script>
```
@@ -50,8 +50,8 @@ Fastest way to try PageAgent with our free Demo LLM:
| Mirrors | URL |
| ------- | ---------------------------------------------------------------------------------- |
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.0.0/dist/iife/page-agent.demo.js |
| China | https://registry.npmmirror.com/page-agent/1.0.0/files/dist/iife/page-agent.demo.js |
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.0.1/dist/iife/page-agent.demo.js |
| China | https://registry.npmmirror.com/page-agent/1.0.1/files/dist/iife/page-agent.demo.js |
### NPM Installation
+19 -22
View File
@@ -1,12 +1,12 @@
{
"name": "root",
"version": "1.0.0",
"version": "1.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "root",
"version": "1.0.0",
"version": "1.0.1",
"license": "MIT",
"workspaces": [
"packages/page-controller",
@@ -11322,24 +11322,24 @@
},
"packages/core": {
"name": "@page-agent/core",
"version": "1.0.0",
"version": "1.0.1",
"license": "MIT",
"dependencies": {
"@page-agent/llms": "1.0.0",
"@page-agent/page-controller": "1.0.0",
"@page-agent/llms": "1.0.1",
"@page-agent/page-controller": "1.0.1",
"chalk": "^5.6.2",
"zod": "^4.3.5"
}
},
"packages/extension": {
"name": "@page-agent/ext",
"version": "0.1.0-b.2",
"version": "0.1.0-b4",
"hasInstallScript": true,
"dependencies": {
"@page-agent/core": "1.0.0",
"@page-agent/llms": "1.0.0",
"@page-agent/page-controller": "1.0.0",
"@page-agent/ui": "1.0.0",
"@page-agent/core": "1.0.1",
"@page-agent/llms": "1.0.1",
"@page-agent/page-controller": "1.0.1",
"@page-agent/ui": "1.0.1",
"@radix-ui/react-hover-card": "^1.1.15",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-label": "^2.1.8",
@@ -11377,7 +11377,7 @@
},
"packages/llms": {
"name": "@page-agent/llms",
"version": "1.0.0",
"version": "1.0.1",
"license": "MIT",
"dependencies": {
"chalk": "^5.6.2",
@@ -11385,20 +11385,20 @@
}
},
"packages/page-agent": {
"version": "1.0.0",
"version": "1.0.1",
"license": "MIT",
"dependencies": {
"@page-agent/core": "1.0.0",
"@page-agent/llms": "1.0.0",
"@page-agent/page-controller": "1.0.0",
"@page-agent/ui": "1.0.0",
"@page-agent/core": "1.0.1",
"@page-agent/llms": "1.0.1",
"@page-agent/page-controller": "1.0.1",
"@page-agent/ui": "1.0.1",
"chalk": "^5.6.2",
"zod": "^4.3.5"
}
},
"packages/page-controller": {
"name": "@page-agent/page-controller",
"version": "1.0.0",
"version": "1.0.1",
"license": "MIT",
"dependencies": {
"ai-motion": "^0.4.8"
@@ -11406,12 +11406,12 @@
},
"packages/ui": {
"name": "@page-agent/ui",
"version": "1.0.0",
"version": "1.0.1",
"license": "MIT"
},
"packages/website": {
"name": "@page-agent/website",
"version": "1.0.0",
"version": "1.0.1",
"dependencies": {
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-separator": "^1.1.8",
@@ -11433,11 +11433,8 @@
"@types/react": "^19.2.8",
"@types/react-dom": "^19.2.1",
"@vitejs/plugin-react-swc": "^4.1.0",
"i18next": "^25.7.4",
"i18next-browser-languagedetector": "^8.2.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-i18next": "^16.5.3",
"tailwindcss": "^4.1.14",
"tw-animate-css": "^1.4.0",
"wouter": "^3.9.0"
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "root",
"private": true,
"version": "1.0.0",
"version": "1.0.1",
"type": "module",
"workspaces": [
"packages/page-controller",
+3 -3
View File
@@ -1,7 +1,7 @@
{
"name": "@page-agent/core",
"private": false,
"version": "1.0.0",
"version": "1.0.1",
"type": "module",
"main": "./dist/esm/page-agent-core.js",
"module": "./dist/esm/page-agent-core.js",
@@ -45,7 +45,7 @@
"dependencies": {
"chalk": "^5.6.2",
"zod": "^4.3.5",
"@page-agent/llms": "1.0.0",
"@page-agent/page-controller": "1.0.0"
"@page-agent/llms": "1.0.1",
"@page-agent/page-controller": "1.0.1"
}
}
+7 -7
View File
@@ -95,7 +95,7 @@ export class PageAgentCore extends EventTarget {
// Listen to LLM retry events
this.#llm.addEventListener('retry', (e) => {
const { attempt, maxAttempts } = (e as CustomEvent).detail
this.emitActivity({ type: 'retrying', attempt, maxAttempts })
this.#emitActivity({ type: 'retrying', attempt, maxAttempts })
// Also push to history for panel rendering
this.history.push({
type: 'retry',
@@ -109,7 +109,7 @@ export class PageAgentCore extends EventTarget {
const error = (e as CustomEvent).detail.error as Error | InvokeError
if ((error as any)?.rawError?.name === 'AbortError') return
const message = String(error)
this.emitActivity({ type: 'error', message })
this.#emitActivity({ type: 'error', message })
// Also push to history for panel rendering
this.history.push({
type: 'error',
@@ -153,7 +153,7 @@ export class PageAgentCore extends EventTarget {
* Emit activity event - for transient UI feedback
* @param activity - Current agent activity
*/
emitActivity(activity: AgentActivity): void {
#emitActivity(activity: AgentActivity): void {
this.dispatchEvent(new CustomEvent('activity', { detail: activity }))
}
@@ -224,7 +224,7 @@ export class PageAgentCore extends EventTarget {
// Thinking
console.log(chalk.blue('Thinking...'))
this.emitActivity({ type: 'thinking' })
this.#emitActivity({ type: 'thinking' })
// invoke LLM
@@ -303,7 +303,7 @@ export class PageAgentCore extends EventTarget {
} catch (error: unknown) {
console.error('Task failed', error)
const errorMessage = String(error)
this.emitActivity({ type: 'error', message: errorMessage })
this.#emitActivity({ type: 'error', message: errorMessage })
this.#onDone(false)
const result: ExecutionResult = {
success: false,
@@ -376,7 +376,7 @@ export class PageAgentCore extends EventTarget {
console.log(chalk.blue.bold(`Executing tool: ${toolName}`), toolInput)
// Emit executing activity
this.emitActivity({ type: 'executing', tool: toolName, input: toolInput })
this.#emitActivity({ type: 'executing', tool: toolName, input: toolInput })
const startTime = Date.now()
@@ -387,7 +387,7 @@ export class PageAgentCore extends EventTarget {
console.log(chalk.green.bold(`Tool (${toolName}) executed for ${duration}ms`), result)
// Emit executed activity
this.emitActivity({
this.#emitActivity({
type: 'executed',
tool: toolName,
input: toolInput,
+5 -5
View File
@@ -1,7 +1,7 @@
{
"name": "@page-agent/ext",
"private": true,
"version": "0.1.0-b.2",
"version": "0.1.0-b4",
"type": "module",
"scripts": {
"dev": "wxt",
@@ -27,10 +27,10 @@
"wxt": "^0.20.13"
},
"dependencies": {
"@page-agent/core": "1.0.0",
"@page-agent/llms": "1.0.0",
"@page-agent/page-controller": "1.0.0",
"@page-agent/ui": "1.0.0",
"@page-agent/core": "1.0.1",
"@page-agent/llms": "1.0.1",
"@page-agent/page-controller": "1.0.1",
"@page-agent/ui": "1.0.1",
"@radix-ui/react-hover-card": "^1.1.15",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-label": "^2.1.8",
@@ -5,12 +5,26 @@ import { TabsController } from './TabsController'
import SYSTEM_PROMPT from './system_prompt.md?raw'
import { createTabTools } from './tabTools'
/**
* MultiPageAgent
* - use with extension
* - can be used from a side panel or a content script
*/
export class MultiPageAgent extends PageAgentCore {
constructor(config: Omit<PageAgentConfig, 'pageController'>) {
const tabsController = new TabsController()
const pageController = new RemotePageController(tabsController)
const customTools = createTabTools(tabsController)
/**
* When the agent is in side-panel and user closed the side-panel.
* There is no chance for isAgentRunning to be set false.
* (unload event doesn't work well in side panel.)
* (I'm trying not to use long-lived connection because the lifecycle of a sw is hard to predict.)
* This heartbeat mechanism acts as a backup.
*/
let heartBeatInterval: null | number = null
super({
...config,
pageController: pageController as any,
@@ -20,18 +34,34 @@ export class MultiPageAgent extends PageAgentCore {
onBeforeTask: async (agent) => {
await tabsController.init(agent.task)
heartBeatInterval = window.setInterval(() => {
chrome.storage.local.set({
agentHeartbeat: Date.now(),
})
}, 1_000)
await chrome.storage.local.set({
isAgentRunning: true,
})
},
onAfterTask: async () => {
if (heartBeatInterval) {
window.clearInterval(heartBeatInterval)
heartBeatInterval = null
}
await chrome.storage.local.set({
isAgentRunning: false,
})
},
onDispose: () => {
if (heartBeatInterval) {
window.clearInterval(heartBeatInterval)
heartBeatInterval = null
}
chrome.storage.local.set({
isAgentRunning: false,
})
@@ -7,12 +7,12 @@ export function handlePageControlMessage(
message: { type: 'PAGE_CONTROL'; action: string; payload: any; targetTabId: number },
sender: chrome.runtime.MessageSender,
sendResponse: (response: unknown) => void
): boolean {
): true | undefined {
const { action, payload, targetTabId } = message
if (action === 'get_my_tab_id') {
sendResponse({ tabId: sender.tab?.id || null })
return false
return
}
chrome.tabs
@@ -21,17 +21,14 @@ export function initPageController() {
}
intervalID = window.setInterval(async () => {
const agentHeartbeat = (await chrome.storage.local.get('agentHeartbeat')).agentHeartbeat
const now = Date.now()
const agentInTouch = typeof agentHeartbeat === 'number' && now - agentHeartbeat < 2_000
const isAgentRunning = (await chrome.storage.local.get('isAgentRunning')).isAgentRunning
const currentTabId = (await chrome.storage.local.get('currentTabId')).currentTabId
const shouldShowMask = isAgentRunning && currentTabId === (await myTabIdPromise)
// console.log('[RemotePageController] polling:', {
// isAgentRunning,
// currentTabId,
// myTabId: await myTabIdPromise,
// shouldShowMask,
// })
const shouldShowMask = isAgentRunning && agentInTouch && currentTabId === (await myTabIdPromise)
if (shouldShowMask) {
const pc = getPC()
@@ -45,20 +42,20 @@ export function initPageController() {
}
}
if (!isAgentRunning) {
if (!isAgentRunning && agentInTouch) {
if (pageController) {
pageController.dispose()
pageController = null
}
}
}, 1_000)
}, 500)
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
chrome.runtime.onMessage.addListener((message, sender, sendResponse): true | undefined => {
if (message.type !== 'PAGE_CONTROL') {
sendResponse({
success: false,
error: `[RemotePageController.ContentScript]: Invalid message type: ${message.type}`,
})
// sendResponse({
// success: false,
// error: `[RemotePageController.ContentScript]: Invalid message type: ${message.type}`,
// })
return
}
@@ -7,7 +7,7 @@ export function handleTabControlMessage(
message: { type: 'TAB_CONTROL'; action: TabAction; payload: any },
sender: chrome.runtime.MessageSender,
sendResponse: (response: unknown) => void
): boolean {
): true | undefined {
const { action, payload } = message
switch (action as TabAction) {
@@ -102,6 +102,6 @@ export function handleTabControlMessage(
default:
sendResponse({ error: `Unknown action: ${action}` })
return false
return
}
}
@@ -39,9 +39,11 @@ export class TabsController extends EventTarget {
await this.updateCurrentTabId(this.currentTabId)
const tabChangeHandler = (message: any) => {
if (message.type !== 'TAB_CHANGE')
throw new Error(`[TabsController]: Invalid message type: ${message.type}`)
const tabChangeHandler = (message: any): void => {
if (message.type !== 'TAB_CHANGE') {
// throw new Error(`[TabsController]: Invalid message type: ${message.type}`)
return
}
if (message.action === 'created') {
const tab = message.payload.tab as chrome.tabs.Tab
@@ -1,7 +1,7 @@
import { handlePageControlMessage } from '@/agent/RemotePageController.background'
import { handleTabControlMessage } from '@/agent/TabsController.background'
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
chrome.runtime.onMessage.addListener((message, sender, sendResponse): true | undefined => {
if (message.type === 'TAB_CONTROL') {
return handleTabControlMessage(message, sender, sendResponse)
} else if (message.type === 'PAGE_CONTROL') {
@@ -54,6 +54,7 @@ async function exposeAgentToPage() {
switch (action) {
case 'execute': {
// singleton check
if (multiPageAgent && multiPageAgent.status === 'running') {
window.postMessage(
{
@@ -70,8 +71,64 @@ async function exposeAgentToPage() {
try {
const { task, llmConfig } = payload
// create when used
multiPageAgent = new MultiPageAgent(llmConfig)
// events
multiPageAgent.addEventListener('statuschange', (event) => {
if (!multiPageAgent) return
window.postMessage(
{
channel: 'PAGE_AGENT_EXT_RESPONSE',
id,
action: 'status_change_event',
payload: multiPageAgent.status,
},
'*'
)
})
multiPageAgent.addEventListener('activity', (event) => {
if (!multiPageAgent) return
window.postMessage(
{
channel: 'PAGE_AGENT_EXT_RESPONSE',
id,
action: 'activity_event',
payload: (event as CustomEvent).detail,
},
'*'
)
})
multiPageAgent.addEventListener('historychange', (event) => {
if (!multiPageAgent) return
window.postMessage(
{
channel: 'PAGE_AGENT_EXT_RESPONSE',
id,
action: 'history_change_event',
payload: multiPageAgent.history,
},
'*'
)
})
multiPageAgent.addEventListener('dispose', () => {
window.postMessage(
{
channel: 'PAGE_AGENT_EXT_RESPONSE',
id,
action: 'dispose_event',
},
'*'
)
})
// result
const result = await multiPageAgent.execute(task)
window.postMessage(
@@ -1,5 +1,19 @@
import type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from '@page-agent/core'
import type { LLMConfig } from '@page-agent/llms'
export interface ExecuteHooks {
onStatusChange?: (status: AgentStatus) => void
onActivity?: (activity: AgentActivity) => void
onHistoryUpdate?: (history: HistoricalEvent[]) => void
onDispose?: () => void
}
export type Execute = (
task: string,
llmConfig: LLMConfig,
hooks?: ExecuteHooks
) => Promise<ExecutionResult>
export default defineUnlistedScript(() => {
const w = window as any
@@ -9,7 +23,7 @@ export default defineUnlistedScript(() => {
return _lastId
}
w.execute = async (task: string, llmConfig: LLMConfig) => {
w.execute = async (task: string, llmConfig: LLMConfig, hooks?: ExecuteHooks) => {
if (typeof task !== 'string') throw new Error('Task must be a string')
if (task.trim().length === 0) throw new Error('Task cannot be empty')
if (!llmConfig) throw new Error('LLM config is required')
@@ -19,14 +33,39 @@ export default defineUnlistedScript(() => {
const id = getId()
const promise = new Promise((resolve, reject) => {
const promise = new Promise<ExecutionResult>((resolve, reject) => {
function handleMessage(e: MessageEvent) {
const data = e.data
if (typeof data !== 'object' || data === null) return
if (data.channel !== 'PAGE_AGENT_EXT_RESPONSE') return
if (data.action !== 'execute_result') return
if (data.id !== id) return
// events
if (data.action === 'status_change_event' && hooks?.onStatusChange) {
hooks.onStatusChange(data.payload)
return
}
if (data.action === 'activity_event' && hooks?.onActivity) {
hooks.onActivity(data.payload)
return
}
if (data.action === 'history_change_event' && hooks?.onHistoryUpdate) {
hooks.onHistoryUpdate(data.payload)
return
}
if (data.action === 'dispose_event' && hooks?.onDispose) {
hooks.onDispose()
return
}
// result
if (data.action !== 'execute_result') return
window.removeEventListener('message', handleMessage)
if (data.error) {
@@ -109,7 +109,7 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
<div className="flex flex-col gap-1.5">
<label className="text-xs text-muted-foreground">Model</label>
<Input
placeholder="gpt-4o"
placeholder="gpt-5.2"
value={model}
onChange={(e) => setModel(e.target.value)}
className="text-xs h-8"
@@ -249,10 +249,10 @@ function StepCard({ event }: { event: AgentStepEvent }) {
)}
</p>
<p className="text-[11px] text-muted-foreground/70 grid grid-cols-[auto_1fr] gap-1.5">
<div className=""></div>
<div className="wrap-anywhere break-all line-clamp-1 hover:line-clamp-3">
<span className=""></span>
<span className="wrap-anywhere break-all line-clamp-1 hover:line-clamp-3">
{event.action.output}
</div>
</span>
</p>
</div>
</div>
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@page-agent/llms",
"version": "1.0.0",
"version": "1.0.1",
"type": "module",
"main": "./dist/lib/page-agent-llms.js",
"module": "./dist/lib/page-agent-llms.js",
+5 -5
View File
@@ -1,7 +1,7 @@
{
"name": "page-agent",
"private": false,
"version": "1.0.0",
"version": "1.0.1",
"type": "module",
"main": "./dist/esm/page-agent.js",
"module": "./dist/esm/page-agent.js",
@@ -46,9 +46,9 @@
"dependencies": {
"chalk": "^5.6.2",
"zod": "^4.3.5",
"@page-agent/llms": "1.0.0",
"@page-agent/page-controller": "1.0.0",
"@page-agent/core": "1.0.0",
"@page-agent/ui": "1.0.0"
"@page-agent/llms": "1.0.1",
"@page-agent/page-controller": "1.0.1",
"@page-agent/core": "1.0.1",
"@page-agent/ui": "1.0.1"
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@page-agent/page-controller",
"version": "1.0.0",
"version": "1.0.1",
"type": "module",
"main": "./dist/lib/page-controller.js",
"module": "./dist/lib/page-controller.js",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@page-agent/ui",
"version": "1.0.0",
"version": "1.0.1",
"type": "module",
"main": "./dist/lib/page-agent-ui.js",
"module": "./dist/lib/page-agent-ui.js",
+1 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@page-agent/website",
"private": true,
"version": "1.0.0",
"version": "1.0.1",
"type": "module",
"scripts": {
"dev": "vite --host 0.0.0.0",
@@ -14,11 +14,8 @@
"@types/react": "^19.2.8",
"@types/react-dom": "^19.2.1",
"@vitejs/plugin-react-swc": "^4.1.0",
"i18next": "^25.7.4",
"i18next-browser-languagedetector": "^8.2.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-i18next": "^16.5.3",
"tailwindcss": "^4.1.14",
"tw-animate-css": "^1.4.0",
"wouter": "^3.9.0"
@@ -1,7 +1,7 @@
import { useTranslation } from 'react-i18next'
import { useLanguage } from '@/i18n/context'
export default function BetaNotice() {
const { t } = useTranslation('common')
const { isZh } = useLanguage()
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">
@@ -11,9 +11,13 @@ export default function BetaNotice() {
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-orange-800 dark:text-orange-200 mb-1">
{t('beta_notice.title')}
{isZh ? 'Beta 阶段' : 'Beta Stage'}
</h3>
<p className="text-sm text-orange-700 dark:text-orange-300">{t('beta_notice.content')}</p>
<p className="text-sm text-orange-700 dark:text-orange-300">
{isZh
? '当前功能未完成,接口可能随时变更。正式版本发布前请勿用于生产环境。'
: 'Current features are incomplete and the API may change at any time. Please do not use in production environments before the official release.'}
</p>
</div>
</div>
</div>
+7 -4
View File
@@ -1,8 +1,9 @@
import { useTranslation } from 'react-i18next'
import { siGithub } from 'simple-icons'
import { useLanguage } from '@/i18n/context'
export default function Footer() {
const { t } = useTranslation('common')
const { isZh } = useLanguage()
return (
<footer
@@ -11,14 +12,16 @@ export default function Footer() {
>
<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">
<p className="text-gray-600 dark:text-gray-300 text-sm">{t('footer.copyright')}</p>
<p className="text-gray-600 dark:text-gray-300 text-sm">
© 2026 page-agent. All rights reserved.
</p>
<div className="flex items-center space-x-6">
<a
href="https://github.com/alibaba/page-agent"
target="_blank"
rel="noopener noreferrer"
className="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors duration-200"
aria-label={t('footer.github_label')}
aria-label={isZh ? '访问 GitHub 仓库' : 'Visit GitHub repository'}
>
<svg
role="img"
+15 -14
View File
@@ -1,15 +1,16 @@
import { BookOpen, Menu, X } from 'lucide-react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { siGithub } from 'simple-icons'
import { Link } from 'wouter'
import { useLanguage } from '@/i18n/context'
import LanguageSwitcher from './LanguageSwitcher'
import ThemeSwitcher from './ThemeSwitcher'
import { HyperText } from './ui/hyper-text'
export default function Header() {
const { t } = useTranslation('common')
const { isZh } = useLanguage()
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
return (
@@ -24,7 +25,7 @@ export default function Header() {
<Link
href="~/"
className="flex items-center gap-2 sm:gap-3 group shrink-0"
aria-label={t('header.logo_alt')}
aria-label={isZh ? 'page-agent 首页' : 'page-agent home'}
onClick={() => setMobileMenuOpen(false)}
>
<img
@@ -43,7 +44,7 @@ export default function Header() {
animateOnHover={true}
aria-hidden="true"
>
{t('header.slogan')}
GUI Agent in your webpage
</HyperText>
</div>
</Link>
@@ -57,7 +58,7 @@ export default function Header() {
<Link
href="~/docs/introduction/overview"
className="p-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-200 shrink-0"
aria-label={t('header.nav_docs')}
aria-label={isZh ? '文档' : 'Docs'}
>
<BookOpen className="w-5 h-5" />
</Link>
@@ -66,7 +67,7 @@ export default function Header() {
target="_blank"
rel="noopener noreferrer"
className="p-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-200 shrink-0"
aria-label={t('header.nav_source')}
aria-label="GitHub"
>
<svg
role="img"
@@ -83,7 +84,7 @@ export default function Header() {
<nav
className="hidden md:flex items-center space-x-6"
role="navigation"
aria-label={t('header.nav_docs')}
aria-label={isZh ? '文档' : 'Docs'}
>
<span className="text-xs font-mono text-gray-400 dark:text-gray-500 tabular-nums before:content-['v']">
{import.meta.env.VERSION}
@@ -93,14 +94,14 @@ export default function Header() {
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"
>
<BookOpen className="w-4 h-4" />
{t('header.nav_docs')}
{isZh ? '文档' : 'Docs'}
</Link>
<a
href="https://github.com/alibaba/page-agent"
target="_blank"
rel="noopener noreferrer"
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={t('header.nav_source')}
aria-label="GitHub"
>
<svg
role="img"
@@ -110,7 +111,7 @@ export default function Header() {
>
<path d={siGithub.path} />
</svg>
{t('header.nav_source')}
GitHub
</a>
<ThemeSwitcher />
<LanguageSwitcher />
@@ -120,7 +121,7 @@ export default function Header() {
<button
type="button"
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 shrink-0"
aria-label={t('header.mobile_menu')}
aria-label={isZh ? '打开导航栏' : 'Open navigation'}
aria-expanded={mobileMenuOpen}
aria-controls="mobile-menu"
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
@@ -142,14 +143,14 @@ export default function Header() {
onClick={() => setMobileMenuOpen(false)}
>
<BookOpen className="w-5 h-5" />
{t('header.nav_docs')}
{isZh ? '文档' : 'Docs'}
</Link>
<a
href="https://github.com/alibaba/page-agent"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2 px-3 py-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-200"
aria-label={t('header.nav_source')}
aria-label="GitHub"
>
<svg
role="img"
@@ -159,7 +160,7 @@ export default function Header() {
>
<path d={siGithub.path} />
</svg>
{t('header.nav_source')}
GitHub
</a>
<div className="flex items-center gap-3 px-3 py-2">
<ThemeSwitcher />
@@ -3,6 +3,8 @@
word-break: break-word;
overflow-wrap: break-word;
font-family: monospace;
font-size: 13px;
line-height: 1;
color: #171717;
}
@@ -1,22 +1,21 @@
import { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useLanguage } from '@/i18n/context'
export default function LanguageSwitcher() {
const { i18n, t } = useTranslation('common')
const { language, isZh, setLanguage } = useLanguage()
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' },
{ code: 'zh-CN' as const, label: '中文' },
{ code: 'en-US' as const, label: 'English' },
]
const currentLanguage = languages.find((lang) => lang.code === currentLang) || languages[0]
const currentLanguage = languages.find((lang) => lang.code === language) || languages[0]
const handleLanguageChange = (langCode: string) => {
i18n.changeLanguage(langCode)
const handleLanguageChange = (langCode: 'zh-CN' | 'en-US') => {
setLanguage(langCode)
setIsOpen(false)
}
@@ -42,7 +41,7 @@ export default function LanguageSwitcher() {
<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-label={isZh ? '切换语言' : 'Switch language'}
aria-expanded={isOpen}
aria-haspopup="true"
>
@@ -83,14 +82,14 @@ export default function LanguageSwitcher() {
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
language === 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 && (
{language === lang.code && (
<svg
className="w-4 h-4 ml-auto"
fill="none"
+2 -2
View File
@@ -1,8 +1,8 @@
// Demo build (auto-init with demo LLM, for quick testing)
export const CDN_DEMO_URL =
'https://cdn.jsdelivr.net/npm/page-agent@1.0.0/dist/iife/page-agent.demo.js'
'https://cdn.jsdelivr.net/npm/page-agent@1.0.1/dist/iife/page-agent.demo.js'
export const CDN_DEMO_CN_URL =
'https://registry.npmmirror.com/page-agent/1.0.0/files/dist/iife/page-agent.demo.js'
'https://registry.npmmirror.com/page-agent/1.0.1/files/dist/iife/page-agent.demo.js'
// Demo LLM for website testing
export const DEMO_MODEL = 'PAGE-AGENT-FREE-TESTING-RANDOM'
-73
View File
@@ -1,73 +0,0 @@
# 国际化配置说明
本项目使用 `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
View File
@@ -1,45 +0,0 @@
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
+35
View File
@@ -0,0 +1,35 @@
import { ReactNode, createContext, use, useState } from 'react'
type Lang = 'en-US' | 'zh-CN'
const LanguageContext = createContext<{
language: Lang
isZh: boolean
setLanguage: (lang: Lang) => void
} | null>(null)
export function LanguageProvider({ children }: { children: ReactNode }) {
const [language, setLang] = useState<Lang>(() => {
const stored = localStorage.getItem('language') as Lang
if (stored === 'zh-CN' || stored === 'en-US') return stored
return navigator.language.startsWith('zh') ? 'zh-CN' : 'en-US'
})
const setLanguage = (lang: Lang) => {
setLang(lang)
localStorage.setItem('language', lang)
}
return (
<LanguageContext value={{ language, isZh: language === 'zh-CN', setLanguage }}>
{children}
</LanguageContext>
)
}
// eslint-disable-next-line react-refresh/only-export-components
export function useLanguage() {
const ctx = use(LanguageContext)
if (!ctx) throw new Error('useLanguage must be used within LanguageProvider')
return ctx
}
@@ -1,42 +0,0 @@
export default {
header: {
logo_alt: 'page-agent home',
slogan: 'GUI Agent in your webpage',
nav_docs: 'Docs',
nav_source: 'GitHub',
mobile_menu: 'Open navigation',
},
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',
advanced: 'Advanced',
overview: 'Overview',
quick_start: 'Quick Start',
limitations: 'Limitations',
models: 'Models',
custom_tools: 'Custom Tools',
knowledge_injection: 'Instructions',
data_masking: 'Data Masking',
cdn_setup: 'CDN Setup',
best_practices: 'Best Practices',
third_party_agent: 'Third-party Agent',
security_permissions: 'Security & Permissions',
page_agent: 'PageAgent',
page_agent_core: 'PageAgentCore',
},
}
@@ -1,157 +0,0 @@
export default {
overview: {
title: 'Overview',
subtitle:
'page-agent is a purely web-based GUI Agent. Gives your website an AI operator in simple steps.',
what_is: 'What is page-agent?',
what_is_desc:
'page-agent is an embedded GUI Agent. Unlike traditional browser automation tools, page-agent is built for web developers and web applications first. 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 fast and 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',
},
models: {
title: 'Models',
subtitle:
'Supports models that comply with OpenAI API specification and support tool calls, including public cloud services and private deployments.',
recommendation_logic: 'Recommended: Lightweight models with strong ToolCall capabilities.',
recommended: 'Recommended Models',
available: 'Tested Models',
available_verified: 'Verified Working Models',
tips: 'Tips',
tip_1: 'Reasoning models (like GPT-5) are slower with no advantage',
tip_2:
'Models with weaker ToolCall capabilities may return incorrect formats. Common errors usually auto-recover. Higher temperature recommended',
tip_3:
'Small models or those unable to handle complex tool definitions typically perform poorly',
security: '🔐 Production Authentication',
security_warning: '⚠️ Never commit real LLM API Keys to your frontend code',
security_desc:
'In production environments, to hide the real LLM API Keys, we recommend the following architecture:',
security_backend_proxy: 'Backend Proxy Pattern',
security_backend_desc:
'Set up a backend LLM proxy endpoint that uses the same authentication method as other APIs in your website, such as:',
security_method_1: '• Session/Cookie-based authentication',
security_method_2: '• OIDC (OpenID Connect) single sign-on',
security_method_3: '• Temporary Access Key or JWT Token',
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',
},
}
@@ -1,82 +0,0 @@
export default {
hero: {
badge: 'GUI Agent in your webpage',
title_line1: 'The AI Operator',
title_line2: 'Living in Your Web App',
subtitle_emoji: '🪄One line of code',
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:
'Goto docs in navigation bar, find Quick-Start 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',
cdn_label: 'CDN Source',
cdn_international: 'International',
cdn_china: 'China Mirror',
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: 'Pure Front-end Solution',
private_model: 'Your Own Models',
data_masking: 'Built-in Privacy',
open_source: 'MIT Open Source',
},
features: {
section_title: 'Why PageAgent',
in_page: {
title: 'In-page Solution',
desc: 'Runs entirely within your page. No browser extensions, no headless browsers, and no backend required.',
},
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. 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.',
},
},
}
@@ -1,41 +0,0 @@
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: '集成指南',
advanced: '高级',
overview: '概览',
quick_start: '快速开始',
limitations: '使用限制',
models: '模型',
custom_tools: '自定义工具',
knowledge_injection: '知识注入',
data_masking: '数据脱敏',
cdn_setup: 'CDN 引入',
best_practices: '最佳实践',
third_party_agent: '接入第三方 Agent',
security_permissions: '安全与权限',
page_agent: 'PageAgent',
page_agent_core: 'PageAgentCore',
},
}
@@ -1,152 +0,0 @@
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: '• 更智能的错误恢复机制',
},
models: {
title: '模型',
subtitle: '当前支持符合 OpenAI 接口规范且支持 tool call 的模型,包括公有云服务和私有部署方案。',
recommendation_logic: '推荐使用 ToolCall 能力强的轻量级模型。',
recommended: '推荐模型',
available: '已测试模型',
available_verified: '已验证可用模型',
tips: '提示',
tip_1: 'reasoning 模型(如 GPT-5),速度偏慢,没有必要',
tip_2:
'ToolCall 能力较弱的模型可能返回错误的格式,常见错误能够自动恢复,建议设置较高的 temperature',
tip_3: '小模型或者无法适应复杂 Tool 定义的模型,通常效果不佳',
security: '🔐 生产环境鉴权建议',
security_warning: '⚠️ 永远不要把真实的 LLM API Key 发布到前端代码',
security_desc: '在实际应用中,为了隐藏真实的 LLM API Key,建议采用以下架构:',
security_backend_proxy: '后端代理转发',
security_backend_desc:
'在后端搭建一个 LLM 流量转发接口,该接口使用与你网站上其他接口相同的鉴权方式,例如:',
security_method_1: '• Session/Cookie 会话认证',
security_method_2: '• OIDC (OpenID Connect) 单点登录',
security_method_3: '• 临时 Access Key 或 JWT Token',
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: '• 避免在工具中执行耗时的同步操作',
},
}
@@ -1,81 +0,0 @@
export default {
hero: {
badge: 'GUI Agent in your webpage',
title_line1: '让你的 Web 应用',
title_line2: '拥有 AI 操作员',
subtitle_emoji: '🪄一行代码',
subtitle_main: ',为你的网站添加 GUI 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: '拖拽下面按钮到收藏夹栏',
cdn_label: 'CDN 源',
cdn_international: '国际',
cdn_china: '国内镜像',
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: '核心特性',
in_page: {
title: '纯页面内方案',
desc: '完全运行在你的页面内。不需要浏览器插件、不需要无头浏览器,不需要后端。',
},
secure_integration: {
title: '安全可控集成',
desc: '支持操作黑白名单、数据脱敏保护。注入自定义知识库,让 AI 按你的规则工作。',
},
zero_backend: {
title: '零后端部署',
desc: '前端脚本引入,自定义 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
View File
@@ -1,16 +0,0 @@
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
}
}
}
+37 -1
View File
@@ -71,7 +71,43 @@ html.dark,
body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
font-family:
system-ui,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
'Noto Sans',
'Liberation Sans',
sans-serif,
'Apple Color Emoji',
'Segoe UI Emoji';
}
/* 文档正文排版优化 */
.prose {
letter-spacing: 0.01em;
font-weight: 380;
}
.prose p {
line-height: 1.6;
}
/* 标题使用中等字重(相对细体更重,但比默认 bold 更轻) */
.prose h1,
.prose h2,
.prose h3,
.prose h4,
.prose h5,
.prose h6 {
font-weight: 480;
}
/* strong/b 也用中等字重 */
.prose strong,
.prose b {
font-weight: 480;
}
/* 确保文档页面标题在暗色模式下可见 - 只针对 prose 内的标题 */
+6 -5
View File
@@ -2,14 +2,15 @@ import { createRoot } from 'react-dom/client'
import { Router } from 'wouter'
import { useHashLocation } from 'wouter/use-hash-location'
import './i18n/config'
import './i18n/types'
import { LanguageProvider } from './i18n/context'
import { default as PagesRouter } from './router'
import './index.css'
createRoot(document.getElementById('root')!).render(
<Router hook={useHashLocation}>
<PagesRouter />
</Router>
<LanguageProvider>
<Router hook={useHashLocation}>
<PagesRouter />
</Router>
</LanguageProvider>
)
+102 -54
View File
@@ -1,7 +1,6 @@
/* eslint-disable react-dom/no-dangerously-set-innerhtml */
import { Bot, Box, MessageSquare, PlayCircle, Shield, Sparkles, Users, Zap } from 'lucide-react'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Link, useSearchParams } from 'wouter'
import Footer from '../components/Footer'
@@ -18,6 +17,7 @@ import {
DEMO_BASE_URL,
DEMO_MODEL,
} from '../constants'
import { useLanguage } from '../i18n/context'
function getInjection(useCN?: boolean) {
const cdn = useCN ? CDN_DEMO_CN_URL : CDN_DEMO_URL
@@ -40,11 +40,15 @@ function getInjection(useCN?: boolean) {
}
export default function HomePage() {
const { t, i18n } = useTranslation(['home', 'common'])
const [task, setTask] = useState(() => t('home:hero.default_task'))
const { language, isZh } = useLanguage()
const defaultTask = isZh
? '从导航栏中进入文档页,打开"快速开始"相关的文档,帮我总结成 markdown'
: 'Goto docs in navigation bar, find Quick-Start section, and summarize in markdown'
const [task, setTask] = useState(() => defaultTask)
// Update task when language changes
const defaultTask = t('home:hero.default_task')
useEffect(() => {
setTask(defaultTask)
}, [defaultTask])
@@ -66,7 +70,7 @@ export default function HomePage() {
win.pageAgent = new PageAgent({
// 把 react 根元素排除掉,挂了很多冒泡时间导致假阳
interactiveBlacklist: [document.getElementById('root')!],
language: i18n.language as any,
language: language,
instructions: {
system: 'You are a helpful assistant on PageAgent website.',
@@ -131,7 +135,7 @@ export default function HomePage() {
aria-hidden="true"
></span>
<AnimatedGradientText colorFrom="#3b82f6" colorTo="#8b5cf6">
{t('home:hero.badge')}
GUI Agent in your webpage
</AnimatedGradientText>
</div>
@@ -139,20 +143,24 @@ export default function HomePage() {
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')}
{isZh ? '让你的 Web 应用' : 'The AI Operator'}
<br />
{t('home:hero.title_line2')}
{isZh ? '拥有 AI 操作员' : 'Living in Your Web App'}
</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')}
{isZh ? '🪄一行代码' : '🪄One line of code'}
</span>
</Highlighter>
{t('home:hero.subtitle_main')}
{isZh
? ',为你的网站添加 GUI Agent。'
: ' adds intelligent GUI Agents to your website.'}
<br />
{t('home:hero.subtitle_detail')}
{isZh
? '用户/答疑机器人给出文字指示,AI 帮你操作页面。'
: 'Users give natural language commands, AI handles the rest.'}
</p>
{/* Try It Now Section - Tab Card */}
@@ -173,7 +181,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'
}`}
>
{t('home:hero.tab_try')}
{isZh ? '🚀 立即尝试' : '🚀 Try It Now'}
</button>
<button
onClick={() => setActiveTab('other')}
@@ -183,7 +191,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'
}`}
>
{t('home:hero.tab_other')}
{isZh ? '🌐 其他网页尝试' : '🌐 Try on Other Sites'}
</button>
</div>
@@ -195,7 +203,11 @@ export default function HomePage() {
<input
value={task}
onChange={(e) => setTask(e.target.value)}
placeholder={t('home:hero.input_placeholder')}
placeholder={
isZh
? '输入您想要 AI 执行的任务...'
: 'Describe what you want AI to do...'
}
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
/>
@@ -206,7 +218,7 @@ export default function HomePage() {
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')}
{isZh ? '执行' : 'Run'}
</button>
</div>
</div>
@@ -220,15 +232,17 @@ export default function HomePage() {
<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')}
{isZh ? '步骤 1:' : 'Step 1:'}
</span>{' '}
{t('home:try_other.step1_content')}
{isZh ? '显示收藏夹栏' : 'Show your bookmarks bar'}
</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>
<span className="text-gray-500 dark:text-gray-400">
{isZh ? '或' : 'or'}
</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>
@@ -239,9 +253,11 @@ export default function HomePage() {
<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')}
{isZh ? '步骤 2:' : 'Step 2:'}
</span>{' '}
{t('home:try_other.step2_content')}
{isZh
? '拖拽下面按钮到收藏夹栏'
: 'Drag this button to your bookmarks'}
</p>
<div className="flex items-center justify-center gap-3">
<select
@@ -252,9 +268,11 @@ export default function HomePage() {
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')}
{isZh ? '国际' : 'International'}
</option>
<option value="china">
{isZh ? '国内镜像' : 'China Mirror'}
</option>
<option value="china">{t('home:try_other.cdn_china')}</option>
</select>
<div
dangerouslySetInnerHTML={{
@@ -268,9 +286,11 @@ export default function HomePage() {
<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')}
{isZh ? '步骤 3:' : 'Step 3:'}
</span>{' '}
{t('home:try_other.step3_content')}
{isZh
? '在其他网站点击收藏夹中的按钮即可使用'
: 'Click the bookmark on any site to activate'}
</p>
</div>
</div>
@@ -278,37 +298,47 @@ export default function HomePage() {
{/* 右侧:注意事项 */}
<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')}
{isZh ? '⚠️ 注意' : '⚠️ Heads Up'}
</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')}
{isZh
? '仅做技术评估,链接定期失效'
: 'Demo only—link may expire without notice'}
</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')}
{isZh
? '使用 DeepSeek 模型,参考 DeepSeek 用户协议和隐私政策'
: 'This free demo uses DeepSeek API (see their terms and privacy policy)'}
</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')}
{isZh
? '部分网站屏蔽了链接嵌入,将无反应'
: 'Some sites block script injection (CSP policies)'}
</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')}
{isZh
? '仅支持单页应用,页面跳转后需要重新注入'
: 'Works on single-page apps only—reload required after navigation'}
</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')}
{isZh
? '仅识别文本,不识别图像,不支持拖拽等复杂交互'
: 'Text-only understanding—no image recognition or drag-and-drop'}
</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')}{' '}
{isZh ? '详细使用限制参照' : 'Full limitations in'}{' '}
<Link
href="/docs/introduction/limitations"
className="text-blue-600 dark:text-blue-400 hover:underline"
>
{t('home:try_other.notice_items.item6_link')}
{isZh ? '《文档》' : 'Docs'}
</Link>
</li>
</ul>
@@ -329,28 +359,28 @@ export default function HomePage() {
className="w-2 h-2 bg-green-500 rounded-full mr-2"
aria-hidden="true"
></span>
{t('home:benefits.no_backend')}
{isZh ? '纯前端方案' : 'Pure Front-end Solution'}
</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')}
{isZh ? '支持私有模型' : 'Your Own Models'}
</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')}
{isZh ? '无痛脱敏' : 'Built-in Privacy'}
</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')}
{isZh ? 'MIT 开源' : 'MIT Open Source'}
</li>
</ul>
</div>
@@ -376,10 +406,12 @@ export default function HomePage() {
<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')}
{isZh ? '纯页面内方案' : 'In-page Solution'}
</h3>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
{t('home:features.in_page.desc')}
{isZh
? '完全运行在你的页面内。不需要浏览器插件、不需要无头浏览器,不需要后端。'
: 'Runs entirely within your page. No browser extensions, no headless browsers, and no backend required.'}
</p>
</article>
@@ -395,10 +427,12 @@ export default function HomePage() {
<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')}
{isZh ? '零后端部署' : 'Zero Backend Setup'}
</h3>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
{t('home:features.zero_backend.desc')}
{isZh
? '前端脚本引入,自定义 LLM 接入点。从 OpenAI 到 qwen3,完全由你掌控。'
: 'Just drop in a script. Works with any LLM provider—OpenAI, Anthropic, or your own models.'}
</p>
</article>
@@ -414,10 +448,12 @@ export default function HomePage() {
<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')}
{isZh ? '普惠智能交互' : 'Natural Language UI'}
</h3>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
{t('home:features.accessible.desc')}
{isZh
? '为复杂 B端系统、管理后台提供自然语言入口。让每个用户都能轻松上手。'
: 'Transform complex admin panels into chat interfaces. Make powerful tools accessible to everyone, not just experts.'}
</p>
</article>
@@ -433,10 +469,12 @@ export default function HomePage() {
<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')}
{isZh ? '安全可控集成' : 'Secure by Design'}
</h3>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
{t('home:features.secure_integration.desc')}
{isZh
? '支持操作黑白名单、数据脱敏保护。注入自定义知识库,让 AI 按你的规则工作。'
: 'Control what AI can access with allowlists, data masking, and custom knowledge injection. Your rules, your data.'}
</p>
</article>
</div>
@@ -451,10 +489,12 @@ export default function HomePage() {
className="text-4xl lg:text-5xl mb-6"
colors={{ first: '#3b82f6', second: '#8b5cf6' }}
>
{t('home:use_cases.section_title')}
{isZh ? '应用场景' : 'Where It Shines'}
</SparklesText>
<p className="text-xl text-gray-600 dark:text-gray-300 max-w-3xl mx-auto">
{t('home:use_cases.section_subtitle')}
{isZh
? '从简单的表单填写到复杂的业务流程,AI 都能理解并执行'
: 'From simple forms to complex workflows, AI understands and executes'}
</p>
</div>
@@ -468,10 +508,12 @@ export default function HomePage() {
<div>
<h3 className="text-xl font-bold mb-2 text-gray-900 dark:text-white">
{t('home:use_cases.case1.title')}
{isZh ? '对接答疑机器人' : 'Supercharge Support Bots'}
</h3>
<p className="text-gray-600 dark:text-gray-300">
{t('home:use_cases.case1.desc')}
{isZh
? '把你的答疑助手变成全能Agent。客服机器人不再只说「请先点击设置按钮然后点击...」,而是直接帮用户现场操作。'
: '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.'}
</p>
</div>
</div>
@@ -485,10 +527,12 @@ export default function HomePage() {
</div>
<div>
<h3 className="text-xl font-bold mb-2 text-gray-900 dark:text-white">
{t('home:use_cases.case2.title')}
{isZh ? '交互升级/智能化改造' : 'Modernize Legacy Apps'}
</h3>
<p className="text-gray-600 dark:text-gray-300">
{t('home:use_cases.case2.desc')}
{isZh
? '一行代码,老应用变身Agent,产品专家帮用户操作复杂 B 端软件。降低人工支持成本,提高用户满意度。'
: 'Add AI superpowers to old software without rebuilding. One script tag transforms complex enterprise tools into chat-driven interfaces.'}
</p>
</div>
</div>
@@ -502,10 +546,12 @@ export default function HomePage() {
</div>
<div>
<h3 className="text-xl font-bold mb-2 text-gray-900 dark:text-white">
{t('home:use_cases.case3.title')}
{isZh ? '产品教学' : 'Interactive Walkthroughs'}
</h3>
<p className="text-gray-600 dark:text-gray-300">
{t('home:use_cases.case3.desc')}
{isZh
? '向用户演示交互过程,边做边教。例如让AI演示「如何提交报销申请」的完整操作流程。'
: "Show, don't tell. Let AI demonstrate workflows in real-time—perfect for onboarding or training new users on complex systems."}
</p>
</div>
</div>
@@ -519,10 +565,12 @@ export default function HomePage() {
</div>
<div>
<h3 className="text-xl font-bold mb-2 text-gray-900 dark:text-white">
{t('home:use_cases.case4.title')}
{isZh ? '无障碍支持' : 'Accessibility First'}
</h3>
<p className="text-gray-600 dark:text-gray-300">
{t('home:use_cases.case4.desc')}
{isZh
? '为视障用户、老年用户提供自然语言交互,对接屏幕阅读器或语音助理,让软件人人可用。'
: 'Make web apps accessible through natural language. Perfect for screen readers, voice control, or users who find traditional interfaces challenging.'}
</p>
</div>
</div>
+57 -33
View File
@@ -1,7 +1,10 @@
import { ReactNode } from 'react'
import { useTranslation } from 'react-i18next'
import { siGooglechrome } from 'simple-icons'
import { Link, useLocation } from 'wouter'
import { SparklesText } from '@/components/ui/sparkles-text'
import { useLanguage } from '@/i18n/context'
interface DocsLayoutProps {
children: ReactNode
}
@@ -17,44 +20,46 @@ interface NavSection {
}
export default function DocsLayout({ children }: DocsLayoutProps) {
const { t } = useTranslation('common')
const { isZh } = useLanguage()
const [location] = useLocation()
const navigationSections: NavSection[] = [
{
title: t('nav.introduction'),
title: isZh ? '介绍' : 'Introduction',
items: [
{ title: t('nav.overview'), path: '/introduction/overview' },
{ title: t('nav.quick_start'), path: '/introduction/quick-start' },
{ title: t('nav.limitations'), path: '/introduction/limitations' },
],
},
{
title: t('nav.features'),
items: [
{ title: t('nav.models'), path: '/features/models' },
{ title: t('nav.custom_tools'), path: '/features/custom-tools' },
{ title: t('nav.knowledge_injection'), path: '/features/custom-instructions' },
{ title: t('nav.data_masking'), path: '/features/data-masking' },
],
},
{
title: t('nav.integration'),
items: [
{ title: t('nav.third_party_agent'), path: '/integration/third-party-agent' },
{ title: t('nav.cdn_setup'), path: '/integration/cdn-setup' },
{ title: isZh ? '概览' : 'Overview', path: '/introduction/overview' },
{ title: isZh ? '快速开始' : 'Quick Start', path: '/introduction/quick-start' },
{ title: isZh ? '使用限制' : 'Limitations', path: '/introduction/limitations' },
{
title: '🚧 ' + t('nav.security_permissions'),
title: '🚧 ' + (isZh ? '最佳实践' : 'Best Practices'),
path: '/integration/best-practices',
},
],
},
{
title: isZh ? '功能特性' : 'Features',
items: [
{ title: isZh ? '模型' : 'Models', path: '/features/models' },
{ title: isZh ? '自定义工具' : 'Custom Tools', path: '/features/custom-tools' },
{ title: isZh ? '知识注入' : 'Instructions', path: '/features/custom-instructions' },
{ title: isZh ? '数据脱敏' : 'Data Masking', path: '/features/data-masking' },
{ title: isZh ? 'Chrome 扩展' : 'Chrome Extension', path: '/features/chrome-extension' },
{
title: isZh ? '接入第三方 Agent' : 'Third-party Agent',
path: '/integration/third-party-agent',
},
],
},
{
title: isZh ? '高级' : 'Advanced',
items: [
{ title: 'PageAgent', path: '/advanced/page-agent' },
{ title: 'PageAgentCore', path: '/advanced/page-agent-core' },
{ title: isZh ? '自定义 UI' : 'Custom UI', path: '/advanced/custom-ui' },
{
title: '🚧 ' + (isZh ? '安全与权限' : 'Security & Permissions'),
path: '/integration/security-permissions',
},
{ title: '🚧 ' + t('nav.best_practices'), path: '/integration/best-practices' },
],
},
{
title: t('nav.advanced'),
items: [
{ title: t('nav.page_agent'), path: '/advanced/page-agent' },
{ title: t('nav.page_agent_core'), path: '/advanced/page-agent-core' },
],
},
]
@@ -74,6 +79,7 @@ export default function DocsLayout({ children }: DocsLayoutProps) {
<ul className="space-y-2" role="list">
{section.items.map((item) => {
const isActive = location === item.path
const isChromeExtension = item.path === '/features/chrome-extension'
return (
<li key={item.path}>
<Link
@@ -85,7 +91,25 @@ export default function DocsLayout({ children }: DocsLayoutProps) {
}`}
aria-current={isActive ? 'page' : undefined}
>
{item.title}
{isChromeExtension ? (
<span className="flex items-center gap-1.5">
<svg
className="w-3.5 h-3.5 shrink-0"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path d={siGooglechrome.path} fill="currentColor" />
</svg>
<SparklesText
className="text-[length:inherit] font-[inherit] font-medium"
sparklesCount={3}
>
{item.title}
</SparklesText>
</span>
) : (
item.title
)}
</Link>
</li>
)
@@ -99,7 +123,7 @@ export default function DocsLayout({ children }: DocsLayoutProps) {
{/* 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>
<div className="prose dark:prose-invert max-w-none">{children}</div>
</main>
</div>
</div>
@@ -0,0 +1,272 @@
import CodeEditor from '@/components/CodeEditor'
import { APIDivider, APIReference } from '@/components/ui/api-reference'
import { useLanguage } from '@/i18n/context'
export default function CustomUIDocs() {
const { isZh } = useLanguage()
return (
<div>
<h1 className="text-4xl font-bold mb-6">{isZh ? '自定义 UI' : 'Custom UI'}</h1>
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
{isZh
? 'PageAgent 的核心逻辑(PageAgentCore)和 UI 完全解耦,通过事件通讯。你可以用自己的 UI 替换内置 Panel。'
: 'PageAgent core logic (PageAgentCore) is fully decoupled from UI through events. You can replace the built-in Panel with your own UI.'}
</p>
{/* Architecture */}
<section className="mb-10">
<h2 className="text-2xl font-semibold mb-4">{isZh ? '架构' : 'Architecture'}</h2>
<p className="text-gray-600 dark:text-gray-400 mb-4">
{isZh
? 'PageAgent 由三个独立模块组成,可自由组合:'
: 'PageAgent consists of three independent modules that can be freely combined:'}
</p>
<ul className="list-disc list-inside text-gray-600 dark:text-gray-400 space-y-2 mb-4">
<li>
<strong>PageAgentCore</strong> -{' '}
{isZh ? '核心 Agent 逻辑,不包含 UI' : 'Core agent logic, no UI'}
</li>
<li>
<strong>PageController</strong> -{' '}
{isZh ? 'DOM 操作和视觉反馈' : 'DOM operations and visual feedback'}
</li>
<li>
<strong>UI (Panel)</strong> -{' '}
{isZh
? '用户界面,可替换为自定义实现'
: 'User interface, replaceable with custom implementation'}
</li>
</ul>
</section>
<APIDivider title={isZh ? '事件系统' : 'Event System'} />
{/* Two Event Streams */}
<section className="mb-10">
<h2 className="text-2xl font-semibold mb-4">{isZh ? '两个事件流' : 'Two Event Streams'}</h2>
<p className="text-gray-600 dark:text-gray-400 mb-4">
{isZh
? 'PageAgentCore 提供两种不同性质的事件流,方便 UI 渲染:'
: 'PageAgentCore provides two distinct event streams for UI rendering:'}
</p>
{/* Comparison Table */}
<div className="overflow-x-auto mb-6">
<table className="w-full border-collapse border border-gray-300 dark:border-gray-600">
<thead>
<tr className="bg-gray-100 dark:bg-gray-800">
<th className="border border-gray-300 dark:border-gray-600 px-4 py-2 text-left"></th>
<th className="border border-gray-300 dark:border-gray-600 px-4 py-2 text-left">
Historical Events
</th>
<th className="border border-gray-300 dark:border-gray-600 px-4 py-2 text-left">
Activity Events
</th>
</tr>
</thead>
<tbody>
<tr>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-2">
{isZh ? '事件名' : 'Event Name'}
</td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-2">
<code>historychange</code>
</td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-2">
<code>activity</code>
</td>
</tr>
<tr>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-2">
{isZh ? '持久性' : 'Persistence'}
</td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-2">
{isZh ? '持久化到 agent.history' : 'Persisted in agent.history'}
</td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-2">
{isZh ? '瞬态' : 'Transient'}
</td>
</tr>
<tr>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-2">
{isZh ? '传给 LLM' : 'Sent to LLM'}
</td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-2">
{isZh ? '是' : 'Yes'}
</td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-2">
{isZh ? '否' : 'No'}
</td>
</tr>
<tr>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-2">
{isZh ? '用途' : 'Purpose'}
</td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-2">
{isZh ? '构成 Agent 记忆,显示历史步骤' : 'Forms agent memory, displays history'}
</td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-2">
{isZh
? '实时 UI 反馈(如 loading 状态)'
: 'Real-time UI feedback (e.g., loading state)'}
</td>
</tr>
</tbody>
</table>
</div>
</section>
{/* All Events */}
<section className="mb-10">
<h2 className="text-2xl font-semibold mb-4">{isZh ? '所有事件' : 'All Events'}</h2>
<APIReference
properties={[
{
name: 'statuschange',
type: 'Event',
description: isZh
? 'Agent 状态变化 (idle → running → completed/error)'
: 'Agent status changes (idle → running → completed/error)',
},
{
name: 'historychange',
type: 'Event',
description: isZh
? '历史事件更新,读取 agent.history 获取完整历史'
: 'History updated, read agent.history for full history',
},
{
name: 'activity',
type: 'CustomEvent<AgentActivity>',
description: isZh
? '实时活动反馈:thinking, executing, executed, retrying, error'
: 'Real-time activity: thinking, executing, executed, retrying, error',
},
{
name: 'dispose',
type: 'Event',
description: isZh ? 'Agent 被销毁' : 'Agent is disposed',
},
]}
/>
</section>
{/* HistoricalEvent Types */}
<section className="mb-10">
<h2 className="text-2xl font-semibold mb-4">HistoricalEvent</h2>
<p className="text-gray-600 dark:text-gray-400 mb-4">
{isZh ? 'agent.history 数组中的事件类型:' : 'Event types in agent.history array:'}
</p>
<CodeEditor
language="typescript"
code={`type HistoricalEvent =
| { type: 'step'; stepIndex: number; reflection: AgentReflection; action: Action }
| { type: 'observation'; content: string }
| { type: 'user_takeover' }
| { type: 'retry'; message: string; attempt: number; maxAttempts: number }
| { type: 'error'; message: string }`}
/>
</section>
{/* AgentActivity Types */}
<section className="mb-10">
<h2 className="text-2xl font-semibold mb-4">AgentActivity</h2>
<p className="text-gray-600 dark:text-gray-400 mb-4">
{isZh ? 'activity 事件的 detail 类型:' : 'The detail type of activity events:'}
</p>
<CodeEditor
language="typescript"
code={`type AgentActivity =
| { type: 'thinking' }
| { type: 'executing'; tool: string; input: unknown }
| { type: 'executed'; tool: string; input: unknown; output: string; duration: number }
| { type: 'retrying'; attempt: number; maxAttempts: number }
| { type: 'error'; message: string }`}
/>
</section>
<APIDivider title={isZh ? 'React 示例' : 'React Example'} />
{/* React Hooks Example */}
<section className="mb-10">
<h2 className="text-2xl font-semibold mb-4">
{isZh ? '使用 React Hooks' : 'Using React Hooks'}
</h2>
<p className="text-gray-600 dark:text-gray-400 mb-4">
{isZh ? '监听事件并更新 React 状态:' : 'Listen to events and update React state:'}
</p>
<CodeEditor
language="tsx"
code={`function useAgent(agent: PageAgentCore) {
const [status, setStatus] = useState(agent.status)
const [history, setHistory] = useState(agent.history)
const [activity, setActivity] = useState<AgentActivity | null>(null)
useEffect(() => {
const onStatus = () => setStatus(agent.status)
const onHistory = () => setHistory([...agent.history])
const onActivity = (e: Event) => setActivity((e as CustomEvent).detail)
agent.addEventListener('statuschange', onStatus)
agent.addEventListener('historychange', onHistory)
agent.addEventListener('activity', onActivity)
return () => {
agent.removeEventListener('statuschange', onStatus)
agent.removeEventListener('historychange', onHistory)
agent.removeEventListener('activity', onActivity)
}
}, [agent])
return { status, history, activity }
}`}
/>
</section>
<APIDivider title={isZh ? '完整组装示例' : 'Complete Assembly Example'} />
{/* Assembly Example */}
<section className="mb-10">
<h2 className="text-2xl font-semibold mb-4">
{isZh ? '组装 Core + Controller + 自定义 UI' : 'Assembling Core + Controller + Custom UI'}
</h2>
<p className="text-gray-600 dark:text-gray-400 mb-4">
{isZh
? '参考内置 PageAgent 的实现方式,用自定义 UI 替换 Panel'
: 'Following the built-in PageAgent pattern, replace Panel with custom UI:'}
</p>
<CodeEditor
language="typescript"
code={`import { PageAgentCore } from '@page-agent/core'
import { PageController } from '@page-agent/page-controller'
// 1. Create PageController
const pageController = new PageController({ enableMask: true })
// 2. Create PageAgentCore with controller
const agent = new PageAgentCore({
pageController,
baseURL: 'https://api.openai.com/v1',
apiKey: 'your-api-key',
model: 'gpt-5.2',
})
// 3. Mount your custom UI
const root = createRoot(document.getElementById('my-ui')!)
root.render(<MyAgentUI agent={agent} />)
// 4. Handle user input (optional)
agent.onAskUser = async (question) => window.prompt(question) || ''
// 5. Execute task
await agent.execute('Fill the form with test data')
// 6. Cleanup
agent.dispose()`}
/>
</section>
</div>
)
}
@@ -1,11 +1,9 @@
import { useTranslation } from 'react-i18next'
import CodeEditor from '@/components/CodeEditor'
import { APIDivider, APIReference, TypeRef } from '@/components/ui/api-reference'
import { useLanguage } from '@/i18n/context'
export default function PageAgentCoreDocs() {
const { i18n } = useTranslation()
const isZh = i18n.language === 'zh-CN'
const { isZh } = useLanguage()
return (
<div>
@@ -385,13 +383,6 @@ const result = await agent.execute('Fill in the form with test data')`}
? '向历史流推送一个观察事件,会在下一步时被 LLM 看到'
: 'Push an observation to history stream, will be seen by LLM in next step',
},
{
name: 'emitActivity(activity: AgentActivity)',
type: 'void',
description: isZh
? '发出活动事件用于 UI 反馈'
: 'Emit activity event for UI feedback',
},
{
name: 'dispose(reason?: string)',
type: 'void',
@@ -1,12 +1,11 @@
import { useTranslation } from 'react-i18next'
import { Link } from 'wouter'
import CodeEditor from '@/components/CodeEditor'
import { APIReference, TypeRef } from '@/components/ui/api-reference'
import { useLanguage } from '@/i18n/context'
export default function PageAgentDocs() {
const { i18n } = useTranslation()
const isZh = i18n.language === 'zh-CN'
const { isZh } = useLanguage()
return (
<div>
@@ -193,9 +192,7 @@ agent.panel.dispose()`}
<table className="w-full text-sm">
<thead>
<tr className="bg-gray-50 dark:bg-gray-800/50">
<th className="px-4 py-3 text-left font-medium text-gray-600 dark:text-gray-300">
{isZh ? '特性' : 'Feature'}
</th>
<th className="px-4 py-3 text-left font-medium text-gray-600 dark:text-gray-300"></th>
<th className="px-4 py-3 text-center font-medium text-gray-600 dark:text-gray-300">
PageAgent
</th>
@@ -0,0 +1,309 @@
import { siGithub } from 'simple-icons'
import BetaNotice from '@/components/BetaNotice'
import CodeEditor from '@/components/CodeEditor'
import { useLanguage } from '@/i18n/context'
export default function ChromeExtension() {
const { isZh } = useLanguage()
return (
<div>
<h1 className="text-4xl font-bold mb-6">{isZh ? 'Chrome 扩展' : 'Chrome Extension'}</h1>
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
{isZh
? '可选的 Chrome 扩展,解锁多页任务和第三方 API 集成。'
: 'Optional Chrome extension that unlocks multi-page tasks and third-party API integration.'}
</p>
<BetaNotice />
<div className="space-y-8 mt-8">
{/* Hero Section */}
<section className="p-6 bg-linear-to-r from-blue-50 to-purple-50 dark:from-blue-900/20 dark:to-purple-900/20 rounded-xl">
<div className="flex items-start gap-4">
<div>
<p className="text-gray-600 dark:text-gray-300">
{isZh
? '解锁多页任务!借助 Chrome 扩展,Agent 可以跨标签页和页面导航,突破单页限制。'
: 'Unlock multi-page tasks! With the Chrome extension, your agent can navigate across tabs and pages, breaking the single-page limitation.'}
</p>
</div>
</div>
</section>
{/* Features */}
<section>
<h2 className="text-2xl font-bold mb-4">{isZh ? '核心特性' : 'Key Features'}</h2>
<div className="grid md:grid-cols-2 gap-4">
<div className="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
<h3 className="font-semibold mb-2">🔓 {isZh ? '多页任务' : 'Multi-Page Tasks'}</h3>
<p className="text-gray-600 dark:text-gray-300 text-sm">
{isZh
? '跨多个页面和标签页执行任务,不再局限于单页操作。'
: 'Execute tasks across multiple pages and tabs. No longer limited to single-page operations.'}
</p>
</div>
<div className="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
<h3 className="font-semibold mb-2">
🔌 {isZh ? '开放第三方接口' : 'Third-Party API'}
</h3>
<p className="text-gray-600 dark:text-gray-300 text-sm">
{isZh
? '用户授权后,你的网页、本地 Agent 或云端 Agent 都能通过扩展操作用户浏览器!'
: 'After user authorization, your webpage, local agent, or cloud agent can control the browser through the extension.'}
</p>
</div>
</div>
</section>
{/* Download */}
<section>
<h2 className="text-2xl font-bold mb-4">{isZh ? '下载测试版' : 'Download Beta'}</h2>
<p className="text-gray-600 dark:text-gray-300 mb-4">
{isZh
? '扩展目前处于 Beta 阶段,请从 GitHub Releases 下载最新版本。'
: 'The extension is currently in beta. Download the latest version from GitHub Releases.'}
</p>
<a
href="https://github.com/alibaba/page-agent/releases"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors"
>
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d={siGithub.path} />
</svg>
{isZh ? '前往 GitHub Releases 下载' : 'Download from GitHub Releases'}
</a>
</section>
{/* Third-party Integration */}
<section>
<h2 className="text-2xl font-bold mb-4">
{isZh ? '第三方接入' : 'Third-Party Integration'}
</h2>
<p className="text-gray-600 dark:text-gray-300 mb-4">
{isZh
? '用户授权后,外部应用可以调用扩展 API 来控制浏览器。'
: 'After user authorization, external applications can call the extension API to control the browser.'}
</p>
{/* Auth Flow */}
<h3 className="text-xl font-semibold mb-3">{isZh ? '授权流程' : 'Authorization Flow'}</h3>
<p className="text-gray-600 dark:text-gray-300 mb-4">
{isZh
? '扩展使用基于 Token 的授权机制,扩展端和页面端必须持有匹配的 Token。'
: 'The extension uses a token-based authorization mechanism. Both extension and page must have matching tokens.'}
</p>
<CodeEditor
code={
isZh
? `// 1. 用户安装扩展并在扩展设置中配置 auth token
// 2. 你的页面读取相同的 token 并存入 localStorage
// 3. Token 匹配后,扩展会暴露 window.execute() 和 window.dispose()
// ⚠️ 请在扩展弹窗中查看你的 auth token,然后填入下方
localStorage.setItem('PageAgentExtUserAuthToken', '<从扩展中获取的-token>')`
: `// 1. User installs extension and sets an auth token in extension settings
// 2. Your page reads the same token and stores it in localStorage
// 3. After token match, extension exposes window.execute() and window.dispose()
// ⚠️ Check your extension popup for the auth token
localStorage.setItem('PageAgentExtUserAuthToken', '<your-token-from-extension>')`
}
language="javascript"
/>
</section>
{/* API Reference */}
<section>
<h2 className="text-2xl font-bold mb-4">{isZh ? 'API 参考' : 'API Reference'}</h2>
<h3 className="text-xl font-semibold mb-3">window.execute(task, llmConfig, hooks?)</h3>
<p className="text-gray-600 dark:text-gray-300 mb-4">
{isZh
? '使用 LLM 配置执行任务。返回一个 Promise,在任务完成时 resolve。可选的 hooks 参数用于监听任务执行过程中的事件。'
: 'Execute a task with LLM configuration. Returns a Promise that resolves when the task completes. Optional hooks parameter for listening to events during task execution.'}
</p>
<CodeEditor
code={
isZh
? `// 使用 LLM 配置和 hooks 执行任务
const result = await window.execute(
'在 GitHub 上搜索 "page-agent" 并打开第一个结果',
{
baseURL: 'https://api.openai.com/v1',
apiKey: 'your-api-key',
model: 'gpt-5-2'
},
{
onStatusChange: status => console.log('状态变化:', status),
onActivity: activity => console.log('活动:', activity),
onHistoryUpdate: history => console.log('历史更新:', history),
onDispose: () => console.log('已停止')
}
)
console.log(result) // 任务执行结果`
: `// Execute a task with LLM configuration and hooks
const result = await window.execute(
'Search for "page-agent" on GitHub and open the first result',
{
baseURL: 'https://api.openai.com/v1',
apiKey: 'your-api-key',
model: 'gpt-5-2'
},
{
onStatusChange: status => console.log('Status change:', status),
onActivity: activity => console.log('Activity:', activity),
onHistoryUpdate: history => console.log('History update:', history),
onDispose: () => console.log('Disposed')
}
)
console.log(result) // Task execution result`
}
language="javascript"
/>
<h3 className="text-xl font-semibold mt-6 mb-3">window.dispose()</h3>
<p className="text-gray-600 dark:text-gray-300 mb-4">
{isZh
? '停止当前正在运行的任务。停止后 Agent 可以重新使用。'
: 'Stop the current running task. The agent can be reused after disposal.'}
</p>
<CodeEditor
code={
isZh
? `// 停止当前任务
window.dispose()`
: `// Stop current task execution
window.dispose()`
}
language="javascript"
/>
</section>
{/* LLM Config */}
<section>
<h2 className="text-2xl font-bold mb-4">{isZh ? 'LLM 配置' : 'LLM Configuration'}</h2>
<CodeEditor
code={
isZh
? `interface LLMConfig {
baseURL: string // LLM API 端点
apiKey: string // API 密钥
model: string // 模型名称
}`
: `interface LLMConfig {
baseURL: string // LLM API endpoint
apiKey: string // API key
model: string // Model name
}`
}
language="typescript"
/>
</section>
{/* Execute Hooks */}
<section>
<h2 className="text-2xl font-bold mb-4">{isZh ? 'Execute Hooks' : 'Execute Hooks'}</h2>
<p className="text-gray-600 dark:text-gray-300 mb-4">
{isZh
? '通过 hooks 参数,你可以监听任务执行过程中的各种事件,实现实时更新 UI、日志记录等功能。'
: 'With hooks parameter, you can listen to various events during task execution for real-time UI updates, logging, and more.'}
</p>
<CodeEditor
code={
isZh
? `interface ExecuteHooks {
// Agent 状态变化时调用(idle, running, error, completed 等)
onStatusChange?: (status: AgentStatus) => void
// Agent 执行活动时调用(如点击、输入、导航等操作)
onActivity?: (activity: AgentActivity) => void
// 历史记录更新时调用(包含完整的事件历史)
onHistoryUpdate?: (history: HistoricalEvent[]) => void
// Agent 被停止时调用
onDispose?: () => void
}`
: `interface ExecuteHooks {
// Called when agent status changes (idle, running, error, completed, etc.)
onStatusChange?: (status: AgentStatus) => void
// Called when agent performs an activity (click, input, navigation, etc.)
onActivity?: (activity: AgentActivity) => void
// Called when history is updated (contains full event history)
onHistoryUpdate?: (history: HistoricalEvent[]) => void
// Called when agent is disposed
onDispose?: () => void
}`
}
language="typescript"
/>
</section>
{/* Security Notice */}
<section 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">
{isZh ? '安全须知' : 'Security Notes'}
</h3>
<ul className="text-gray-600 dark:text-gray-300 space-y-1 text-sm">
<li>
{' '}
{isZh
? '用户必须在扩展设置中显式授权每个域名'
: 'Users must explicitly authorize each domain in extension settings'}
</li>
<li>
{' '}
{isZh
? '生产环境建议使用后端代理 LLM API Key'
: 'Consider using backend proxy for LLM API keys in production'}
</li>
</ul>
</section>
{/* Integration Guide */}
<section>
<h2 className="text-2xl font-bold mb-4">
{isZh
? '将 MultiPageAgent 融入你自己的插件'
: 'Integrate MultiPageAgent into Your Extension'}
</h2>
<p className="text-gray-600 dark:text-gray-300 mb-4">
{isZh
? '你可以将 MultiPageAgent 集成到自己的浏览器扩展中,实现跨页面的 AI 自动化能力。'
: 'You can integrate MultiPageAgent into your own browser extension for cross-page AI automation capabilities.'}
</p>
<p className="text-gray-600 dark:text-gray-300 mb-4">TODO</p>
<p className="text-gray-600 dark:text-gray-300 mb-4">
{isZh ? '参考源码实现:' : 'Reference implementation:'}
</p>
<a
href="https://github.com/alibaba/page-agent/blob/main/packages/extension/src/entrypoints/background.ts"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
>
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d={siGithub.path} />
</svg>
packages/extension/src/entrypoints/background.ts
</a>
</section>
</div>
</div>
)
}
@@ -1,10 +1,8 @@
import { useTranslation } from 'react-i18next'
import CodeEditor from '@/components/CodeEditor'
import { useLanguage } from '@/i18n/context'
export default function Instructions() {
const { i18n } = useTranslation()
const isZh = i18n.language === 'zh-CN'
const { isZh } = useLanguage()
return (
<div>
@@ -1,24 +1,27 @@
import { useTranslation } from 'react-i18next'
import BetaNotice from '@/components/BetaNotice'
import CodeEditor from '@/components/CodeEditor'
import { useLanguage } from '@/i18n/context'
export default function CustomTools() {
const { t } = useTranslation('docs')
const { isZh } = useLanguage()
return (
<div>
<h1 className="text-4xl font-bold mb-6">{t('custom_tools.title')}</h1>
<h1 className="text-4xl font-bold mb-6">{isZh ? '自定义工具' : 'Custom Tools'}</h1>
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
{t('custom_tools.subtitle')}
{isZh
? '通过注册自定义工具,扩展 AI Agent 的能力边界。使用 Zod 定义严格的输入接口,让 AI 安全调用你的业务逻辑。'
: 'Extend AI Agent capabilities by registering custom tools. Use Zod to define strict input schemas for safe business logic calls.'}
</p>
<div className="space-y-8">
<section>
<h2 className="text-2xl font-bold mb-4">{t('custom_tools.registration')}</h2>
<h2 className="text-2xl font-bold mb-4">{isZh ? '工具注册' : 'Tool Registration'}</h2>
<p className="text-gray-600 dark:text-gray-300 mb-4">
{t('custom_tools.registration_desc')}
{isZh
? '每个自定义工具需要定义四个核心属性:name、description、input schema 和 execute 函数。'
: 'Each custom tool requires four core properties: name, description, input schema, and execute function.'}
</p>
<CodeEditor
@@ -27,22 +30,22 @@ 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
},
})
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
scroll: null, // never scroll
}
const pageAgent = new PageAgent({customTools})
@@ -52,12 +55,14 @@ const pageAgent = new PageAgent({customTools})
</section>
<section>
<h2 className="text-2xl font-bold mb-4">{t('custom_tools.page_filter')}</h2>
<h2 className="text-2xl font-bold mb-4">{isZh ? '页面过滤器' : 'Page Filter'}</h2>
<BetaNotice />
<p className="text-gray-600 dark:text-gray-300 mb-4">
{t('custom_tools.page_filter_desc')}
{isZh
? '通过 pageFilter 属性控制工具在哪些页面可见,提升安全性和用户体验。'
: 'Control tool visibility on specific pages via the pageFilter property to enhance security and UX.'}
</p>
<CodeEditor
@@ -84,16 +89,28 @@ const pageAgent = new PageAgent({customTools})
</section>
<section>
<h2 className="text-2xl font-bold mb-4">{t('custom_tools.best_practices')}</h2>
<h2 className="text-2xl font-bold mb-4">{isZh ? '最佳实践' : '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')}
{isZh ? '⚡ 性能优化' : '⚡ Performance Optimization'}
</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>
<li>
{isZh
? '• 使用 pageFilter 减少不必要的工具加载'
: '• Use pageFilter to reduce unnecessary tool loading'}
</li>
<li>
{isZh
? '• 在 execute 函数中实现适当的缓存机制'
: '• Implement appropriate caching in execute functions'}
</li>
<li>
{isZh
? '• 避免在工具中执行耗时的同步操作'
: '• Avoid long-running sync operations in tools'}
</li>
</ul>
</div>
</div>
@@ -1,10 +1,8 @@
import { useTranslation } from 'react-i18next'
import CodeEditor from '@/components/CodeEditor'
import { useLanguage } from '@/i18n/context'
export default function DataMasking() {
const { i18n } = useTranslation()
const isZh = i18n.language === 'zh-CN'
const { isZh } = useLanguage()
return (
<div>
@@ -1,6 +1,5 @@
import { useTranslation } from 'react-i18next'
import CodeEditor from '@/components/CodeEditor'
import { useLanguage } from '@/i18n/context'
// Recommended models: lightweight with excellent tool call capabilities
const MODELS = {
@@ -28,19 +27,25 @@ const MODELS = {
}
export default function Models() {
const { t } = useTranslation('docs')
const { isZh } = useLanguage()
const allModels = [...MODELS.recommended, ...MODELS.verified]
return (
<div className="max-w-4xl">
<h1 className="text-4xl font-bold mb-4">{t('models.title')}</h1>
<p className="text-lg text-gray-600 dark:text-gray-400 mb-8">{t('models.subtitle')}</p>
<h1 className="text-4xl font-bold mb-4">{isZh ? '模型' : 'Models'}</h1>
<p className="text-lg text-gray-600 dark:text-gray-400 mb-8">
{isZh
? '当前支持符合 OpenAI 接口规范且支持 tool call 的模型,包括公有云服务和私有部署方案。'
: 'Supports models that comply with OpenAI API specification and support tool calls, including public cloud services and private deployments.'}
</p>
{/* Models Section */}
<section className="mb-10">
<h2 className="text-2xl font-semibold mb-3">{t('models.available')}</h2>
<h2 className="text-2xl font-semibold mb-3">{isZh ? '已测试模型' : 'Tested Models'}</h2>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
{t('models.recommendation_logic')}
{isZh
? '推荐使用 ToolCall 能力强的轻量级模型。'
: 'Recommended: Lightweight models with strong ToolCall capabilities.'}
</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">
@@ -67,41 +72,59 @@ export default function Models() {
{/* Tips Section */}
<section className="mb-10">
<h2 className="text-2xl font-semibold mb-4">{t('models.tips')}</h2>
<h2 className="text-2xl font-semibold mb-4">{isZh ? '提示' : '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('models.tip_2')}</li>
<li>{t('models.tip_3')}</li>
<li>
{isZh
? 'ToolCall 能力较弱的模型可能返回错误的格式,常见错误能够自动恢复,建议设置较高的 temperature'
: 'Models with weaker ToolCall capabilities may return incorrect formats. Common errors usually auto-recover. Higher temperature recommended'}
</li>
<li>
{isZh
? '小模型或者无法适应复杂 Tool 定义的模型,通常效果不佳'
: 'Small models or those unable to handle complex tool definitions typically perform poorly'}
</li>
</ul>
</div>
</section>
{/* Security Section */}
<section className="mb-10">
<h2 className="text-2xl font-semibold mb-4">{t('models.security')}</h2>
<h2 className="text-2xl font-semibold mb-4">
{isZh ? '🔐 生产环境鉴权建议' : '🔐 Production Authentication'}
</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('models.security_warning')}
{isZh
? '⚠️ 永远不要把真实的 LLM API Key 发布到前端代码'
: '⚠️ Never commit real LLM API Keys to your frontend code'}
</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('models.security_backend_proxy')}
{isZh ? '后端代理转发' : 'Backend Proxy Pattern'}
</h3>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-3">
{t('models.security_backend_desc')}
{isZh
? '在后端搭建一个 LLM 流量转发接口,该接口使用与你网站上其他接口相同的鉴权方式,例如:'
: 'Set up a backend LLM proxy endpoint that uses the same authentication method as other APIs in your website, such as:'}
</p>
<ul className="text-sm text-gray-600 dark:text-gray-400 space-y-1">
<li>{t('models.security_method_1')}</li>
<li>{t('models.security_method_2')}</li>
<li>{t('models.security_method_3')}</li>
<li>{isZh ? '• Session/Cookie 会话认证' : '• Session/Cookie-based authentication'}</li>
<li>
{isZh ? '• OIDC (OpenID Connect) 单点登录' : '• OIDC (OpenID Connect) single sign-on'}
</li>
<li>
{isZh ? '• 临时 Access Key 或 JWT Token' : '• Temporary Access Key or JWT Token'}
</li>
</ul>
</div>
</section>
{/* Configuration Section */}
<section className="mb-10">
<h2 className="text-2xl font-semibold mb-4">{t('models.configuration')}</h2>
<h2 className="text-2xl font-semibold mb-4">{isZh ? '配置方式' : 'Configuration'}</h2>
<CodeEditor
code={`// OpenAI-compatible services (e.g., Alibaba Bailian)
const pageAgent = new PageAgent({
+22 -19
View File
@@ -3,17 +3,17 @@ import { Route, Switch } from 'wouter'
import Header from '../../components/Header'
import DocsLayout from './Layout'
import CustomUIDocs from './advanced/custom-ui/page'
import PageAgentCoreDocs from './advanced/page-agent-core/page'
// Advanced
import PageAgentDocs from './advanced/page-agent/page'
import Instructions from './features/custom-instructions/page'
// Features
import ChromeExtension from './features/chrome-extension/page'
import Instructions from './features/custom-instructions/page'
import CustomTools from './features/custom-tools/page'
import DataMasking from './features/data-masking/page'
import Models from './features/models/page'
import BestPractices from './integration/best-practices/page'
// Integration
import CdnSetup from './integration/cdn-setup/page'
import SecurityPermissions from './integration/security-permissions/page'
import ThirdPartyAgent from './integration/third-party-agent/page'
import Limitations from './introduction/limitations/page'
@@ -73,11 +73,26 @@ export default function DocsRouter() {
<Models />
</DocsPage>
</Route>
{/* Integration */}
<Route path="/integration/cdn-setup">
<Route path="/features/chrome-extension">
<DocsPage>
<CdnSetup />
<ChromeExtension />
</DocsPage>
</Route>
{/* Advanced */}
<Route path="/advanced/page-agent">
<DocsPage>
<PageAgentDocs />
</DocsPage>
</Route>
<Route path="/advanced/page-agent-core">
<DocsPage>
<PageAgentCoreDocs />
</DocsPage>
</Route>
<Route path="/advanced/custom-ui">
<DocsPage>
<CustomUIDocs />
</DocsPage>
</Route>
<Route path="/integration/security-permissions">
@@ -96,18 +111,6 @@ export default function DocsRouter() {
</DocsPage>
</Route>
{/* Advanced */}
<Route path="/advanced/page-agent">
<DocsPage>
<PageAgentDocs />
</DocsPage>
</Route>
<Route path="/advanced/page-agent-core">
<DocsPage>
<PageAgentCoreDocs />
</DocsPage>
</Route>
{/* Default redirect or 404 */}
<Route path="/docs">
<DocsPage>
@@ -1,65 +1,19 @@
import BetaNotice from '@/components/BetaNotice'
import CodeEditor from '@/components/CodeEditor'
import { useLanguage } from '@/i18n/context'
export default function BestPractices() {
const { isZh } = useLanguage()
return (
<div>
<h1 className="text-4xl font-bold mb-6"></h1>
<h1 className="text-4xl font-bold mb-6">{isZh ? '最佳实践' : 'Best Practices'}</h1>
<BetaNotice />
<p className="text-xl text-gray-600 dark:text-gray-300 mb-6 leading-relaxed">
使 page-agent
{isZh
? '使用 page-agent 的最佳实践和常见问题解决方案。'
: 'Best practices and common solutions for using 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>
)
@@ -1,68 +0,0 @@
import { useTranslation } from 'react-i18next'
import CodeEditor from '@/components/CodeEditor'
import { CDN_DEMO_CN_URL, CDN_DEMO_URL } from '@/constants'
export default function CdnSetup() {
const { i18n } = useTranslation()
const isZh = i18n.language === 'zh-CN'
return (
<div className="space-y-10">
<header>
<h1 className="text-4xl font-bold mb-4">{isZh ? 'CDN 引入' : 'CDN Setup'}</h1>
<p className="text-lg text-gray-600 dark:text-gray-300 leading-relaxed">
{isZh
? 'CDN 提供两种构建版本:Demo 版用于快速体验,标准版用法与 NPM 一致。'
: 'CDN provides two builds: Demo for quick testing, standard build with usage identical to NPM.'}
</p>
</header>
{/* Demo Build Section */}
<section>
<h2 className="text-2xl font-bold mb-3">{isZh ? '🚀 快速体验' : '🚀 Quick Try'}</h2>
<p className="text-gray-600 dark:text-gray-300 mb-3">
{isZh
? '自动初始化并使用内置 Demo LLM,无需任何配置:'
: 'Auto-initializes with built-in demo LLM, no configuration needed:'}
</p>
<CodeEditor
className="mb-3"
code={`<script src="${CDN_DEMO_URL}" crossorigin="true"></script>`}
/>
<div className="bg-yellow-50 dark:bg-yellow-900/20 px-4 py-3 rounded-lg mb-4 text-sm">
<span className="text-yellow-800 dark:text-yellow-200">
{' '}
{isZh
? '仅用于技术评估,Demo 模型有速率限制。'
: 'For evaluation only. Demo model has rate limits.'}
</span>
</div>
<table className="w-full border-collapse text-sm">
<thead>
<tr className="border-b border-gray-200 dark:border-gray-700">
<th className="text-left py-2 px-3 font-semibold w-28">
{isZh ? '位置' : 'Location'}
</th>
<th className="text-left py-2 px-3 font-semibold">URL</th>
</tr>
</thead>
<tbody>
<tr className="border-b border-gray-100 dark:border-gray-800">
<td className="py-2 px-3">{isZh ? '全球' : 'Global'}</td>
<td className="py-2 px-3 font-mono text-xs break-all">{CDN_DEMO_URL}</td>
</tr>
<tr>
<td className="py-2 px-3">{isZh ? '中国' : 'China'}</td>
<td className="py-2 px-3 font-mono text-xs break-all">{CDN_DEMO_CN_URL}</td>
</tr>
</tbody>
</table>
</section>
</div>
)
}
@@ -1,10 +1,8 @@
import { useTranslation } from 'react-i18next'
import BetaNotice from '@/components/BetaNotice'
import { useLanguage } from '@/i18n/context'
export default function SecurityPermissions() {
const { i18n } = useTranslation()
const isZh = i18n.language === 'zh-CN'
const { isZh } = useLanguage()
return (
<div>
@@ -1,21 +1,32 @@
import CodeEditor from '@/components/CodeEditor'
import { useLanguage } from '@/i18n/context'
export default function ThirdPartyAgentPage() {
const { isZh } = useLanguage()
return (
<div>
<h1 className="text-4xl font-bold mb-6"> Agent</h1>
<h1 className="text-4xl font-bold mb-6">
{isZh ? '接入第三方 Agent' : 'Third-party Agent Integration'}
</h1>
<p className="mb-6 leading-relaxed text-gray-600 dark:text-gray-300">
pageAgent Agent Agent
{isZh
? '将 pageAgent 作为工具接入你的答疑助手或 Agent 系统,成为你 Agent 的眼和手。'
: 'Integrate pageAgent as a tool in your support assistant or Agent system, becoming the eyes and hands of your 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>
<h3 className="text-lg font-semibold mb-2 text-blue-900 dark:text-blue-300">
{isZh ? '💡 核心价值' : '💡 Core Value'}
</h3>
<p className="text-blue-800 dark:text-blue-200">
"嘴巴""眼睛""手"
{isZh
? '让你的答疑机器人不再只是"嘴巴",而是拥有"眼睛"和"手"的完整智能体。'
: 'Transform your support bot from just a "mouth" into a complete intelligent agent with "eyes" and "hands".'}
</p>
</div>
<h2 className="text-2xl font-bold mb-4"></h2>
<h2 className="text-2xl font-bold mb-4">{isZh ? '集成方式' : 'Integration Method'}</h2>
<div className="space-y-4 mb-6">
<div className="p-4 bg-green-50 dark:bg-green-900/20 rounded-lg">
@@ -23,14 +34,14 @@ export default function ThirdPartyAgentPage() {
1. Function Calling
</h3>
<CodeEditor
code={`// 定义工具
code={`// ${isZh ? '定义工具' : 'Define tool'}
const pageAgentTool = {
name: "page_agent",
description: "执行网页操作",
description: "${isZh ? '执行网页操作' : 'Execute web page operations'}",
parameters: {
type: "object",
properties: {
instruction: { type: "string", description: "操作指令" }
instruction: { type: "string", description: "${isZh ? '操作指令' : 'Operation instruction'}" }
},
required: ["instruction"]
},
@@ -40,48 +51,74 @@ const pageAgentTool = {
}
}
// 注册到你的 agent 中`}
// ${isZh ? '注册到你的 agent 中' : 'Register to your agent'}`}
language="javascript"
/>
</div>
</div>
<h2 className="text-2xl font-bold mb-4"></h2>
<h2 className="text-2xl font-bold mb-4">{isZh ? '应用场景' : 'Use Cases'}</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>
<h4 className="font-semibold mb-2 text-gray-900 dark:text-white">
{isZh ? '🤖 智能客服系统' : '🤖 Smart Customer Service'}
</h4>
<p className="text-sm text-gray-600 dark:text-gray-300">
"帮我提交工单"
{isZh
? '客服机器人帮用户直接操作系统,如"帮我提交工单"'
: 'Support bots directly operate systems for users, e.g., "Help me submit a ticket"'}
</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>
<h4 className="font-semibold mb-2 text-gray-900 dark:text-white">
{isZh ? '📋 业务流程助手' : '📋 Business Process Assistant'}
</h4>
<p className="text-sm text-gray-600 dark:text-gray-300">
"完成客户入职"
{isZh
? '引导新员工完成复杂流程,如"完成客户入职"'
: 'Guide new employees through complex processes, e.g., "Complete customer onboarding"'}
</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>
<h4 className="font-semibold mb-2 text-gray-900 dark:text-white">
{isZh ? '🎯 个人效率助手' : '🎯 Personal Productivity Assistant'}
</h4>
<p className="text-sm text-gray-600 dark:text-gray-300">
"预订会议室"
{isZh
? '跨网站帮你完成任务,如"预订会议室"'
: 'Complete tasks across websites, e.g., "Book a meeting room"'}
</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>
<h4 className="font-semibold mb-2 text-gray-900 dark:text-white">
{isZh ? '🔧 运维自动化' : '🔧 DevOps Automation'}
</h4>
<p className="text-sm text-gray-600 dark:text-gray-300">
"重启服务器"
{isZh
? '通过自然语言操作管理后台,如"重启服务器"'
: 'Operate admin panels via natural language, e.g., "Restart server"'}
</p>
</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">
{isZh ? '⚠️ 注意事项' : '⚠️ Notes'}
</h3>
<ul className="text-yellow-800 dark:text-yellow-200 space-y-1 text-sm">
<li> </li>
<li> </li>
<li> </li>
<li>
{' '}
{isZh
? '确保目标网站允许自动化操作'
: 'Ensure target website allows automated operations'}
</li>
<li> {isZh ? '实现适当的频率限制' : 'Implement appropriate rate limiting'}</li>
<li>
{' '}
{isZh
? '敏感操作建议要求人工确认'
: 'Recommend human confirmation for sensitive operations'}
</li>
</ul>
</div>
</div>
@@ -1,141 +1,193 @@
import { useTranslation } from 'react-i18next'
import { useLanguage } from '@/i18n/context'
export default function LimitationsPage() {
const { t } = useTranslation('docs')
const { isZh } = useLanguage()
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')}
{isZh ? '使用限制' : 'Limitations'}
</h1>
<p className="text-xl text-gray-600 dark:text-gray-300">{t('limitations.subtitle')}</p>
<p className="text-xl text-gray-600 dark:text-gray-300">
{isZh
? '了解 page-agent 当前的功能边界和技术限制'
: "Understand page-agent's current capabilities and technical constraints"}
</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>
<h2 className="text-2xl font-bold mb-3">
{isZh ? '页面支持限制' : 'Page Support Limitations'}
</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')}
{isZh ? '单页应用限制' : 'Single Page Application Limits'}
</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>
<li>
{isZh
? '• 仅支持单页应用(SPA):目前只能在单个页面内进行操作'
: '• SPA only: Currently operates within a single page'}
</li>
<li>
{isZh
? '• 多页接力功能正在设计中:暂时无法跨页面执行连续任务'
: '• Multi-page relay in design: Cannot execute continuous tasks across pages yet'}
</li>
<li>
{isZh
? '• 无法操作未接入该能力的网站:需要目标网站主动集成 page-agent'
: '• Requires integration: Cannot operate on sites without page-agent'}
</li>
</ul>
</div>
<h2 className="text-2xl font-bold mb-3">{t('limitations.interaction_limits')}</h2>
<h2 className="text-2xl font-bold mb-3">
{isZh ? '交互行为限制' : 'Interaction Limitations'}
</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>
<h3 className="font-semibold mb-4">{isZh ? '支持的操作' : 'Supported Operations'}</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>
<span>{isZh ? '点击操作' : '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>
<span>{isZh ? '文本输入' : 'Text 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>
<span>{isZh ? '页面滚动' : '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>
<span>{isZh ? '表单提交' : 'Form 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>
<span>{isZh ? '选择操作' : '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>
<span>{isZh ? '焦点切换' : 'Focus'}</span>
</div>
</div>
</div>
<h3 className="font-semibold mb-4">{t('limitations.unsupported_ops')}</h3>
<h3 className="font-semibold mb-4">{isZh ? '不支持的操作' : 'Unsupported Operations'}</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>
<span>{isZh ? '鼠标悬停(hover' : 'Mouse 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>
<span>{isZh ? '拖拽操作' : 'Drag & drop'}</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>
<span>{isZh ? '右键菜单' : 'Right-click menu'}</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>
<span>{isZh ? '图形绘制' : 'Drawing'}</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>
<span>{isZh ? '键盘快捷键' : 'Keyboard shortcuts'}</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>
<span>{isZh ? '基于点击区域或鼠标位置的控制' : 'Position-based control'}</span>
</div>
</div>
</div>
</div>
<h2 className="text-2xl font-bold mb-3">{t('limitations.understanding_limits')}</h2>
<h2 className="text-2xl font-bold mb-3">
{isZh ? '网页理解限制' : 'Understanding Limitations'}
</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')}
{isZh ? '无视觉能力' : 'No Visual Recognition'}
</h3>
<p className="text-red-700 dark:text-red-300 mb-3">{t('limitations.no_vision_desc')}</p>
<p className="text-red-700 dark:text-red-300 mb-3">
{isZh
? 'page-agent 基于 DOM 结构进行理解和操作,没有视觉识别能力,无法理解以下内容:'
: 'page-agent operates based on DOM structure with no visual recognition. Cannot understand:'}
</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>
<li>
{isZh
? '• 图片内容:无法识别图片中的文字、图标或视觉元素'
: '• Image content: Cannot recognize text, icons, or visual elements in images'}
</li>
<li>
{isZh
? '• Canvas 画布:无法理解 Canvas 中绘制的图形和内容'
: '• Canvas: Cannot understand graphics drawn on Canvas'}
</li>
<li>
{isZh
? '• WebGL 3D 内容:无法操作 3D 场景中的元素'
: '• WebGL 3D: Cannot operate elements in 3D scenes'}
</li>
<li>
{isZh
? '• SVG 图形:无法理解 SVG 中的视觉内容和路径'
: '• SVG graphics: Cannot understand visual content and paths in SVG'}
</li>
</ul>
</div>
<h2 className="text-2xl font-bold mb-3">{t('limitations.website_requirements')}</h2>
<h2 className="text-2xl font-bold mb-3">
{isZh ? '被操作网站要求' : '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>
<h3 className="font-semibold mb-2">
{isZh ? '语义化和易用性' : 'Semantic & Usability'}
</h3>
<p className="text-gray-600 dark:text-gray-300">
{t('limitations.req_semantic_desc')}
{isZh
? '所有操作都基于 DOM 元素的语义化标签和属性。如果页面结构不够语义化,或者没有任何 accessibility 特性,可能影响 AI 的理解准确性。'
: 'All operations rely on semantic tags and attributes. Poor semantic structure or lack of accessibility features may affect AI understanding accuracy.'}
</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>
<h3 className="font-semibold mb-2">{isZh ? 'UI/UX' : 'UI/UX'}</h3>
<p className="text-gray-600 dark:text-gray-300">
{isZh
? '反常识的交互规则、基于视觉的操作提示、复杂的鼠标交互、快速出现快速消失的元素等,都会影响 AI 的理解和操作。'
: 'Counter-intuitive interaction rules, visual-only operation hints, complex mouse interactions, or rapidly appearing/disappearing elements can affect AI understanding and operation.'}
</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>
<h3 className="font-semibold mb-2">{isZh ? '环境要求' : 'Environment'}</h3>
<p className="text-gray-600 dark:text-gray-300">modern browser</p>
</div>
</div>
</div>
<h2>{t('limitations.future')}</h2>
<h2>{isZh ? '未来规划' : 'Future Plans'}</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')}
{isZh ? '即将支持' : 'Coming Soon'}
</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>
<li>{isZh ? '• 多页面接力操作能力' : '• Multi-page relay capabilities'}</li>
<li>{isZh ? '• 更丰富的鼠标交互支持' : '• Richer mouse interaction support'}</li>
<li>{isZh ? '• 基础的视觉理解能力' : '• Basic visual understanding'}</li>
<li>{isZh ? '• 更智能的错误恢复机制' : '• Smarter error recovery'}</li>
</ul>
</div>
</div>
@@ -1,23 +1,16 @@
import { useTranslation } from 'react-i18next'
import { useLanguage } from '@/i18n/context'
export default function Overview() {
const { t } = useTranslation('docs')
const { isZh } = useLanguage()
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>
<h1 className="text-4xl font-bold mb-4">Overview</h1>
<p className="text-xl text-gray-600 dark:text-gray-300 mb-4 leading-relaxed">
{t('overview.subtitle')}
{isZh
? 'page-agent 是一个完全基于 Web 技术的 GUI Agent,简单几步,让你的网站拥有 AI 操作员。'
: 'page-agent is a purely web-based GUI Agent. Gives your website an AI operator in simple steps.'}
</p>
{/* Status Badges */}
@@ -55,57 +48,75 @@ export default function Overview() {
</div>
<section>
<h2 className="text-2xl font-bold mb-4">{t('overview.what_is')}</h2>
<h2 className="text-2xl font-bold mb-4">
{isZh ? '什么是 page-agent' : 'What is page-agent?'}
</h2>
<p className="text-gray-600 dark:text-gray-300 mb-8 leading-relaxed ">
{t('overview.what_is_desc')}
{isZh
? 'page-agent 是一个页面内嵌式 GUI Agent。与传统的浏览器自动化工具不同,page-agent 面向网站开发者,而非爬虫或Agent客户端开发者;将 Agent 集成到你的网站中,让用户可以通过自然语言与页面进行交互。'
: 'page-agent is an embedded GUI Agent. Unlike traditional browser automation tools, page-agent is built for web developers and web applications first. Integrate it into your site to let users interact with pages through natural language.'}
</p>
</section>
<section>
<h2 className="text-2xl font-bold mb-3">{t('overview.features_title')}</h2>
<h2 className="text-2xl font-bold mb-3">{isZh ? '核心特性' : 'Core Features'}</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')}
{isZh ? '🧠 智能 DOM 理解' : '🧠 Smart DOM Analysis'}
</h3>
<p className="text-gray-700 dark:text-gray-300">{t('overview.feature_dom.desc')}</p>
<p className="text-gray-700 dark:text-gray-300">
{isZh
? '基于 DOM 分析,高强度脱水。无需视觉识别,纯文本实现精准操作。'
: 'DOM-based analysis with high-intensity dehydration. No visual recognition needed. Pure text for fast and precise operations.'}
</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')}
{isZh ? '🔒 安全可控' : '🔒 Secure & Controllable'}
</h3>
<p className="text-gray-700 dark:text-gray-300">{t('overview.feature_secure.desc')}</p>
<p className="text-gray-700 dark:text-gray-300">
{isZh
? '支持操作黑白名单、数据脱敏保护。注入自定义知识库,让 AI 按你的规则工作。'
: 'Supports operation allowlists, data masking protection. Inject custom knowledge to make AI work by your rules.'}
</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')}
{isZh ? '⚡ 零后端部署' : '⚡ Zero Backend'}
</h3>
<p className="text-gray-700 dark:text-gray-300">{t('overview.feature_backend.desc')}</p>
<p className="text-gray-700 dark:text-gray-300">
{isZh
? 'CDN 或 NPM 引入,自定义 LLM 接入点。'
: 'CDN or NPM import with custom LLM endpoints.'}
</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')}
{isZh ? '♿ 普惠智能' : '♿ Accessible Intelligence'}
</h3>
<p className="text-gray-700 dark:text-gray-300">
{t('overview.feature_accessible.desc')}
{isZh
? '为复杂 B端系统、管理后台提供自然语言入口。让每个用户都能轻松上手。'
: 'Provides natural language interface for complex B2B systems and admin panels. Makes software easy for everyone.'}
</p>
</div>
</div>
<h2 className="text-2xl font-bold mb-4">{t('overview.vs_browser_use')}</h2>
<h2 className="text-2xl font-bold mb-4">
{isZh ? '与 browser-use 的区别' : '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"></th>
<th className="border border-gray-300 dark:border-gray-600 px-4 py-3 text-left">
page-agent
</th>
@@ -117,53 +128,53 @@ export default function Overview() {
<tbody>
<tr>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3 font-medium">
{t('overview.table_deployment')}
{isZh ? '部署方式' : 'Deployment'}
</td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
{t('overview.table_deployment_pa')}
{isZh ? '页面内嵌组件' : 'Embedded component'}
</td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
{t('overview.table_deployment_bu')}
{isZh ? '外部工具' : 'External tool'}
</td>
</tr>
<tr>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3 font-medium">
{t('overview.table_scope')}
{isZh ? '操作范围' : 'Scope'}
</td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
{t('overview.table_scope_pa')}
{isZh ? '当前页面' : 'Current page'}
</td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
{t('overview.table_scope_bu')}
{isZh ? '整个浏览器' : 'Entire browser'}
</td>
</tr>
<tr>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3 font-medium">
{t('overview.table_user')}
{isZh ? '目标用户' : 'Target Users'}
</td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
{t('overview.table_user_pa')}
{isZh ? '网站开发者' : 'Web developers'}
</td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
{t('overview.table_user_bu')}
{isZh ? '爬虫/Agent 开发者' : 'Scraper/Agent developers'}
</td>
</tr>
<tr>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3 font-medium">
{t('overview.table_scenario')}
{isZh ? '使用场景' : 'Use Case'}
</td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
{t('overview.table_scenario_pa')}
{isZh ? '用户体验增强' : 'UX enhancement'}
</td>
<td className="border border-gray-300 dark:border-gray-600 px-4 py-3">
{t('overview.table_scenario_bu')}
{isZh ? '自动化任务' : 'Automation tasks'}
</td>
</tr>
</tbody>
</table>
</div>
<h2 className="text-2xl font-bold mb-4">{t('overview.use_cases_title')}</h2>
<h2 className="text-2xl font-bold mb-4">{isZh ? '应用场景' : 'Use Cases'}</h2>
<ul className="space-y-4 mb-8">
<li className="flex items-start space-x-3">
@@ -171,7 +182,10 @@ export default function Overview() {
1
</span>
<div className="text-gray-700 dark:text-gray-300">
<strong>{t('overview.use_case1_title')}</strong> {t('overview.use_case1_desc')}
<strong>{isZh ? '对接答疑机器人:' : 'Connect Support Bots:'}</strong>{' '}
{isZh
? '把你的答疑助手变成全能Agent。客服机器人不再只说「请先点击设置按钮然后点击...」,而是直接帮用户现场操作。'
: "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."}
</div>
</li>
<li className="flex items-start space-x-3">
@@ -179,7 +193,10 @@ export default function Overview() {
2
</span>
<div className="text-gray-700 dark:text-gray-300">
<strong>{t('overview.use_case2_title')}</strong> {t('overview.use_case2_desc')}
<strong>{isZh ? '交互升级/智能化改造:' : 'Modernize Legacy Apps:'}</strong>{' '}
{isZh
? '一行代码,老应用变身Agent,产品专家帮用户操作复杂 B 端软件。降低人工支持成本,提高用户满意度。'
: 'One line of code transforms old apps into agents. Product experts help users navigate complex B2B software. Reduce support costs and improve satisfaction.'}
</div>
</li>
<li className="flex items-start space-x-3">
@@ -187,7 +204,10 @@ export default function Overview() {
3
</span>
<div className="text-gray-700 dark:text-gray-300">
<strong>{t('overview.use_case3_title')}</strong> {t('overview.use_case3_desc')}
<strong>{isZh ? '产品教学:' : 'Interactive Training:'}</strong>{' '}
{isZh
? '向用户演示交互过程,边做边教。例如让AI演示「如何提交报销申请」的完整操作流程。'
: "Demonstrate workflows in real-time. Let AI show the complete process of 'how to submit an expense report.'"}
</div>
</li>
<li className="flex items-start space-x-3">
@@ -195,7 +215,10 @@ export default function Overview() {
4
</span>
<div className="text-gray-700 dark:text-gray-300">
<strong>{t('overview.use_case4_title')}</strong> {t('overview.use_case4_desc')}
<strong>{isZh ? '无障碍支持:' : 'Accessibility:'}</strong>{' '}
{isZh
? '为视障用户、老年用户提供自然语言交互,对接屏幕阅读器或语音助理,让软件人人可用。'
: 'Provide natural language interaction for visually impaired and elderly users. Connect screen readers or voice assistants to make software accessible to everyone.'}
</div>
</li>
</ul>
@@ -1,19 +1,19 @@
import { useTranslation } from 'react-i18next'
import CodeEditor from '@/components/CodeEditor'
import { CDN_DEMO_CN_URL, CDN_DEMO_URL } from '@/constants'
import { useLanguage } from '@/i18n/context'
export default function QuickStart() {
const { t, i18n } = useTranslation('docs')
const isZh = i18n.language === 'zh-CN'
const { isZh } = useLanguage()
return (
<div>
<h1 className="text-4xl font-bold mb-6">{t('quick_start.title')}</h1>
<h1 className="text-4xl font-bold mb-6">Quick Start</h1>
<p className=" mb-6 leading-relaxed">{t('quick_start.subtitle')}</p>
<p className=" mb-6 leading-relaxed">
{isZh ? '几分钟内完成 page-agent 的集成。' : 'Integrate page-agent in minutes.'}
</p>
<h2 className="text-2xl font-bold mb-3">{t('quick_start.installation')}</h2>
<h2 className="text-2xl font-bold mb-3">{isZh ? '安装步骤' : 'Installation Steps'}</h2>
<div className="space-y-4 mb-6">
{/* Demo CDN - One Line */}
@@ -27,7 +27,7 @@ export default function QuickStart() {
</span>
</div>
<CodeEditor
code={`<script src="${isZh ? CDN_DEMO_CN_URL : CDN_DEMO_URL}" crossorigin="true"></script>`}
code={`<script src="DEMO_CDN_URL" crossorigin="true"></script>`}
language="html"
/>
<table className="w-full border-collapse text-sm">
@@ -67,7 +67,7 @@ import { PageAgent } from 'page-agent'`}
<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.step2_title')}
{isZh ? '2. 初始化配置' : '2. Initialize Configuration'}
</h3>
<CodeEditor
code={`const agent = new PageAgent({
@@ -82,7 +82,7 @@ import { PageAgent } from 'page-agent'`}
<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('quick_start.step3_title')}
{isZh ? '3. 开始使用' : '3. Start Using'}
</h3>
<CodeEditor
code={`// ${isZh ? '程序化执行自然语言指令' : 'Execute natural language instructions programmatically'}