Compare commits
13 Commits
EXT_v0.1.0-b.2
...
v1.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
| da440f63c3 | |||
| 622fbff790 | |||
| 68c944c3a4 | |||
| 6cd3e3726c | |||
| d11a2593b3 | |||
| 5e936c35d7 | |||
| 97cdb1e1cd | |||
| 7470c1987b | |||
| b767f10a85 | |||
| 8a03391c95 | |||
| 5cfaa292d3 | |||
| 4e87940127 | |||
| 5d77990187 |
+3
-3
@@ -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 安装
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Generated
+19
-22
@@ -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
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "root",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"type": "module",
|
||||
"workspaces": [
|
||||
"packages/page-controller",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,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",
|
||||
|
||||
@@ -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,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,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,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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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 页面国际化
|
||||
@@ -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
|
||||
@@ -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: '为视障用户、老年用户提供自然语言交互,对接屏幕阅读器或语音助理,让软件人人可用。',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 内的标题 */
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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'}
|
||||
|
||||
Reference in New Issue
Block a user