Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4807d550a3 | |||
| f9722f0619 | |||
| e1557a5d2e | |||
| 06280c2ba5 | |||
| 645a7ceb52 | |||
| a47c72ce84 | |||
| dcc6cd1cf3 | |||
| b4b9f6ce2d | |||
| 14e31e6ec4 | |||
| 69f2478fd7 | |||
| fe792f1ceb | |||
| d1d27a76de | |||
| 3e7e99420a |
@@ -49,8 +49,8 @@ Fastest way to try PageAgent with our free Demo LLM:
|
|||||||
|
|
||||||
| Mirrors | URL |
|
| Mirrors | URL |
|
||||||
| ------- | ---------------------------------------------------------------------------------- |
|
| ------- | ---------------------------------------------------------------------------------- |
|
||||||
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.4.0/dist/iife/page-agent.demo.js |
|
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.4.2/dist/iife/page-agent.demo.js |
|
||||||
| China | https://registry.npmmirror.com/page-agent/1.4.0/files/dist/iife/page-agent.demo.js |
|
| China | https://registry.npmmirror.com/page-agent/1.4.2/files/dist/iife/page-agent.demo.js |
|
||||||
|
|
||||||
> **⚠️ For technical evaluation only.** This demo CDN uses our free [testing LLM API](https://alibaba.github.io/page-agent/docs/features/models#free-testing-api). By using it, you agree to its [terms](https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md).
|
> **⚠️ For technical evaluation only.** This demo CDN uses our free [testing LLM API](https://alibaba.github.io/page-agent/docs/features/models#free-testing-api). By using it, you agree to its [terms](https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md).
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.4.0] - 2026-02-27
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Update Terms of Use and Privacy Policy
|
||||||
|
- **Robust tool-call validation** - Action inputs are now validated against tool schemas individually, producing clear error messages (e.g. `Invalid input for action "click_element_by_index"`) instead of unreadable union parse errors
|
||||||
|
- **Primitive action input coercion** - Small models that output `{"click_element_by_index": 2}` instead of `{"click_element_by_index": {"index": 2}}` are now auto-corrected using tool schemas
|
||||||
|
- **Qwen model updates** - Added `qwen3.5-plus` as the default free testing model; disabled `enable_thinking` for Qwen models to avoid incompatible responses
|
||||||
|
- **Updated default LLM endpoint** - Migrated demo and extension to a new testing endpoint with legacy endpoint auto-migration
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
- Unified zod imports (`* as z`) across all packages for consistency
|
||||||
|
- Better Zod error formatting with `z.prettifyError()` in LLM client
|
||||||
|
- Exported `InvokeError` and `InvokeErrorType` as values (not just types) from `@page-agent/llms`
|
||||||
|
- Exported `SupportedLanguage` type from `@page-agent/core`
|
||||||
|
|
||||||
|
### Extension v0.1.8
|
||||||
|
|
||||||
|
- **Language setting** - Added language selector (System / English / 中文) in config panel
|
||||||
|
- **UI makeover** - New empty state with breathing glow and typing animation; ai-motion glow overlay while running; refined focus styles
|
||||||
|
- **Testing endpoint notice** - Shows terms of use notice when using the free testing API
|
||||||
|
- **Legacy endpoint migration** - Auto-migrates old Supabase testing endpoint to new endpoint on startup
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [1.3.0] - 2026-02-13
|
## [1.3.0] - 2026-02-13
|
||||||
|
|
||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
|
|||||||
+2
-2
@@ -49,8 +49,8 @@
|
|||||||
|
|
||||||
| Mirrors | URL |
|
| Mirrors | URL |
|
||||||
| ------- | ---------------------------------------------------------------------------------- |
|
| ------- | ---------------------------------------------------------------------------------- |
|
||||||
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.4.0/dist/iife/page-agent.demo.js |
|
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.4.2/dist/iife/page-agent.demo.js |
|
||||||
| China | https://registry.npmmirror.com/page-agent/1.4.0/files/dist/iife/page-agent.demo.js |
|
| China | https://registry.npmmirror.com/page-agent/1.4.2/files/dist/iife/page-agent.demo.js |
|
||||||
|
|
||||||
> **⚠️ 仅用于技术评估。** 该 Demo CDN 使用了免费的[测试 LLM API](https://alibaba.github.io/page-agent/docs/features/models#free-testing-api),使用即表示您同意其[条款](https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md)。
|
> **⚠️ 仅用于技术评估。** 该 Demo CDN 使用了免费的[测试 LLM API](https://alibaba.github.io/page-agent/docs/features/models#free-testing-api),使用即表示您同意其[条款](https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md)。
|
||||||
|
|
||||||
|
|||||||
Generated
+18
-18
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "root",
|
"name": "root",
|
||||||
"version": "1.4.0",
|
"version": "1.4.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "root",
|
"name": "root",
|
||||||
"version": "1.4.0",
|
"version": "1.4.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/page-controller",
|
"packages/page-controller",
|
||||||
@@ -11161,11 +11161,11 @@
|
|||||||
},
|
},
|
||||||
"packages/core": {
|
"packages/core": {
|
||||||
"name": "@page-agent/core",
|
"name": "@page-agent/core",
|
||||||
"version": "1.4.0",
|
"version": "1.4.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@page-agent/llms": "1.4.0",
|
"@page-agent/llms": "1.4.2",
|
||||||
"@page-agent/page-controller": "1.4.0",
|
"@page-agent/page-controller": "1.4.2",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"zod": "^4.3.5"
|
"zod": "^4.3.5"
|
||||||
}
|
}
|
||||||
@@ -11175,10 +11175,10 @@
|
|||||||
"version": "0.1.8",
|
"version": "0.1.8",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@page-agent/core": "1.4.0",
|
"@page-agent/core": "1.4.2",
|
||||||
"@page-agent/llms": "1.4.0",
|
"@page-agent/llms": "1.4.2",
|
||||||
"@page-agent/page-controller": "1.4.0",
|
"@page-agent/page-controller": "1.4.2",
|
||||||
"@page-agent/ui": "1.4.0",
|
"@page-agent/ui": "1.4.2",
|
||||||
"ai-motion": "^0.4.8",
|
"ai-motion": "^0.4.8",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"zod": "^4.3.5"
|
"zod": "^4.3.5"
|
||||||
@@ -11214,7 +11214,7 @@
|
|||||||
},
|
},
|
||||||
"packages/llms": {
|
"packages/llms": {
|
||||||
"name": "@page-agent/llms",
|
"name": "@page-agent/llms",
|
||||||
"version": "1.4.0",
|
"version": "1.4.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
@@ -11222,20 +11222,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/page-agent": {
|
"packages/page-agent": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@page-agent/core": "1.4.0",
|
"@page-agent/core": "1.4.2",
|
||||||
"@page-agent/llms": "1.4.0",
|
"@page-agent/llms": "1.4.2",
|
||||||
"@page-agent/page-controller": "1.4.0",
|
"@page-agent/page-controller": "1.4.2",
|
||||||
"@page-agent/ui": "1.4.0",
|
"@page-agent/ui": "1.4.2",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"zod": "^4.3.5"
|
"zod": "^4.3.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/page-controller": {
|
"packages/page-controller": {
|
||||||
"name": "@page-agent/page-controller",
|
"name": "@page-agent/page-controller",
|
||||||
"version": "1.4.0",
|
"version": "1.4.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ai-motion": "^0.4.8"
|
"ai-motion": "^0.4.8"
|
||||||
@@ -11243,12 +11243,12 @@
|
|||||||
},
|
},
|
||||||
"packages/ui": {
|
"packages/ui": {
|
||||||
"name": "@page-agent/ui",
|
"name": "@page-agent/ui",
|
||||||
"version": "1.4.0",
|
"version": "1.4.2",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"packages/website": {
|
"packages/website": {
|
||||||
"name": "@page-agent/website",
|
"name": "@page-agent/website",
|
||||||
"version": "1.4.0",
|
"version": "1.4.2",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@radix-ui/react-icons": "^1.3.2",
|
"@radix-ui/react-icons": "^1.3.2",
|
||||||
"@radix-ui/react-separator": "^1.1.8",
|
"@radix-ui/react-separator": "^1.1.8",
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "root",
|
"name": "root",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.4.0",
|
"version": "1.4.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/page-controller",
|
"packages/page-controller",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@page-agent/core",
|
"name": "@page-agent/core",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.4.0",
|
"version": "1.4.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/esm/page-agent-core.js",
|
"main": "./dist/esm/page-agent-core.js",
|
||||||
"module": "./dist/esm/page-agent-core.js",
|
"module": "./dist/esm/page-agent-core.js",
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"zod": "^4.3.5",
|
"zod": "^4.3.5",
|
||||||
"@page-agent/llms": "1.4.0",
|
"@page-agent/llms": "1.4.2",
|
||||||
"@page-agent/page-controller": "1.4.0"
|
"@page-agent/page-controller": "1.4.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -342,6 +342,8 @@ export class PageAgentCore extends EventTarget {
|
|||||||
await onAfterTask?.(this, result)
|
await onAfterTask?.(this, result)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await waitFor(0.4) // @TODO: configurable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
AI-powered browser automation. Control web pages with natural language.
|
|
||||||
|
|
||||||
Page Agent Ext — AI-Powered Browser Automation
|
|
||||||
|
|
||||||
🌟 What is Page Agent Ext?
|
|
||||||
|
|
||||||
Page Agent Ext brings AI-powered automation to your browser. Built on the open-source Page Agent framework, it lets you control web pages across multiple tabs using natural language — no scripting required.
|
|
||||||
|
|
||||||
🌟 Key Features:
|
|
||||||
|
|
||||||
- Natural Language Control — Command your browser in plain language, no code needed
|
|
||||||
- Cross-Tab Automation — Seamlessly operate across multiple tabs and pages
|
|
||||||
- Smart HTML Cleaning — Intelligently extracts and simplifies page structure for accurate AI understanding
|
|
||||||
- Bring Your Own LLM — Use OpenAI, Anthropic, or any compatible API with full data control
|
|
||||||
- Privacy-First — Zero data collection; all data flows directly to your chosen LLM provider
|
|
||||||
- Open Source — MIT licensed, built on the Page Agent framework with full transparency
|
|
||||||
|
|
||||||
🌟 How It Works & Privacy:
|
|
||||||
|
|
||||||
Page Agent Ext performs DOM analysis locally in your browser. When you initiate a task, sanitized page structure is sent to the LLM API you configure. Your data is never collected or stored by us.
|
|
||||||
|
|
||||||
- Your API Key — Configure your own LLM API (OpenAI, Anthropic, etc.). Data goes directly to your provider
|
|
||||||
- Test API — A free test endpoint is available for evaluation; we recommend your own key for regular use
|
|
||||||
|
|
||||||
Terms of Use & Privacy: https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md
|
|
||||||
|
|
||||||
🌟 Open Source:
|
|
||||||
|
|
||||||
This project is MIT licensed. Review the code, verify privacy claims, or extend it for your needs:
|
|
||||||
https://github.com/alibaba/page-agent
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
AI 驱动的浏览器自动化助手,用自然语言控制网页。
|
|
||||||
|
|
||||||
Page Agent Ext — AI 驱动的浏览器自动化助手
|
|
||||||
|
|
||||||
🌟 什么是 Page Agent Ext?
|
|
||||||
|
|
||||||
Page Agent Ext 为浏览器带来 AI 自动化能力。基于开源的 Page Agent 框架,你可以用自然语言跨标签页控制网页,无需编写任何脚本。
|
|
||||||
|
|
||||||
🌟 核心特性:
|
|
||||||
|
|
||||||
- 自然语言控制 — 用日常语言指挥浏览器,无需编程
|
|
||||||
- 跨标签页操作 — 在多个标签页之间无缝切换和操控
|
|
||||||
- HTML 智能清洗 — 智能提取和精简页面结构,让 AI 准确理解网页内容
|
|
||||||
- 使用你自己的模型 — 支持 OpenAI、Anthropic 或任何兼容 API,数据完全自主可控
|
|
||||||
- 隐私优先 — 零数据收集,所有数据直接发送到你配置的 LLM 服务商
|
|
||||||
- 开源透明 — MIT 协议,基于 Page Agent 开源框架,代码完全公开
|
|
||||||
|
|
||||||
🌟 工作原理与隐私:
|
|
||||||
|
|
||||||
Page Agent Ext 在浏览器本地进行 DOM 分析。当你发起任务时,经清洗的页面结构会发送到你配置的 LLM API。我们不会收集或存储你的任何数据。
|
|
||||||
|
|
||||||
- 你的 API Key — 配置你自己的 LLM API(OpenAI、Anthropic 等),数据直接发送到你的服务商
|
|
||||||
- 测试 API — 提供免费测试端点供体验,日常使用建议配置自己的 Key
|
|
||||||
|
|
||||||
使用条款与隐私:https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md
|
|
||||||
|
|
||||||
🌟 开源项目:
|
|
||||||
|
|
||||||
MIT 协议开源。查看源码、验证隐私承诺,或按需扩展:
|
|
||||||
https://github.com/alibaba/page-agent
|
|
||||||
@@ -38,10 +38,10 @@
|
|||||||
"wxt": "^0.20.18"
|
"wxt": "^0.20.18"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@page-agent/core": "1.4.0",
|
"@page-agent/core": "1.4.2",
|
||||||
"@page-agent/llms": "1.4.0",
|
"@page-agent/llms": "1.4.2",
|
||||||
"@page-agent/page-controller": "1.4.0",
|
"@page-agent/page-controller": "1.4.2",
|
||||||
"@page-agent/ui": "1.4.0",
|
"@page-agent/ui": "1.4.2",
|
||||||
"ai-motion": "^0.4.8",
|
"ai-motion": "^0.4.8",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"zod": "^4.3.5"
|
"zod": "^4.3.5"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@page-agent/llms",
|
"name": "@page-agent/llms",
|
||||||
"version": "1.4.0",
|
"version": "1.4.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/lib/page-agent-llms.js",
|
"main": "./dist/lib/page-agent-llms.js",
|
||||||
"module": "./dist/lib/page-agent-llms.js",
|
"module": "./dist/lib/page-agent-llms.js",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "page-agent",
|
"name": "page-agent",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.4.0",
|
"version": "1.4.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/esm/page-agent.js",
|
"main": "./dist/esm/page-agent.js",
|
||||||
"module": "./dist/esm/page-agent.js",
|
"module": "./dist/esm/page-agent.js",
|
||||||
@@ -44,10 +44,10 @@
|
|||||||
"postpublish": "node -e \"['README.md','LICENSE'].forEach(f=>{try{require('fs').unlinkSync(f)}catch{}})\""
|
"postpublish": "node -e \"['README.md','LICENSE'].forEach(f=>{try{require('fs').unlinkSync(f)}catch{}})\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@page-agent/core": "1.4.0",
|
"@page-agent/core": "1.4.2",
|
||||||
"@page-agent/llms": "1.4.0",
|
"@page-agent/llms": "1.4.2",
|
||||||
"@page-agent/page-controller": "1.4.0",
|
"@page-agent/page-controller": "1.4.2",
|
||||||
"@page-agent/ui": "1.4.0",
|
"@page-agent/ui": "1.4.2",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"zod": "^4.3.5"
|
"zod": "^4.3.5"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@page-agent/page-controller",
|
"name": "@page-agent/page-controller",
|
||||||
"version": "1.4.0",
|
"version": "1.4.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/lib/page-controller.js",
|
"main": "./dist/lib/page-controller.js",
|
||||||
"module": "./dist/lib/page-controller.js",
|
"module": "./dist/lib/page-controller.js",
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export async function clickElement(element: HTMLElement) {
|
|||||||
// dispatch a click event
|
// dispatch a click event
|
||||||
// element.click()
|
// element.click()
|
||||||
|
|
||||||
await waitFor(0.1) // Wait to ensure click event processing completes
|
await waitFor(0.2) // Wait to ensure click event processing completes
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
@@ -181,11 +181,6 @@ export async function scrollVertically(
|
|||||||
// Element-specific scrolling if element is provided
|
// Element-specific scrolling if element is provided
|
||||||
if (element) {
|
if (element) {
|
||||||
const targetElement = element
|
const targetElement = element
|
||||||
console.log(
|
|
||||||
'[SCROLL DEBUG] Starting direct container scroll for element:',
|
|
||||||
targetElement.tagName
|
|
||||||
)
|
|
||||||
|
|
||||||
let currentElement = targetElement as HTMLElement | null
|
let currentElement = targetElement as HTMLElement | null
|
||||||
let scrollSuccess = false
|
let scrollSuccess = false
|
||||||
let scrolledElement: HTMLElement | null = null
|
let scrolledElement: HTMLElement | null = null
|
||||||
@@ -198,19 +193,6 @@ export async function scrollVertically(
|
|||||||
const hasScrollableY = /(auto|scroll|overlay)/.test(computedStyle.overflowY)
|
const hasScrollableY = /(auto|scroll|overlay)/.test(computedStyle.overflowY)
|
||||||
const canScrollVertically = currentElement.scrollHeight > currentElement.clientHeight
|
const canScrollVertically = currentElement.scrollHeight > currentElement.clientHeight
|
||||||
|
|
||||||
console.log(
|
|
||||||
'[SCROLL DEBUG] Checking element:',
|
|
||||||
currentElement.tagName,
|
|
||||||
'hasScrollableY:',
|
|
||||||
hasScrollableY,
|
|
||||||
'canScrollVertically:',
|
|
||||||
canScrollVertically,
|
|
||||||
'scrollHeight:',
|
|
||||||
currentElement.scrollHeight,
|
|
||||||
'clientHeight:',
|
|
||||||
currentElement.clientHeight
|
|
||||||
)
|
|
||||||
|
|
||||||
if (hasScrollableY && canScrollVertically) {
|
if (hasScrollableY && canScrollVertically) {
|
||||||
const beforeScroll = currentElement.scrollTop
|
const beforeScroll = currentElement.scrollTop
|
||||||
const maxScroll = currentElement.scrollHeight - currentElement.clientHeight
|
const maxScroll = currentElement.scrollHeight - currentElement.clientHeight
|
||||||
@@ -228,27 +210,10 @@ export async function scrollVertically(
|
|||||||
const afterScroll = currentElement.scrollTop
|
const afterScroll = currentElement.scrollTop
|
||||||
const actualScrollDelta = afterScroll - beforeScroll
|
const actualScrollDelta = afterScroll - beforeScroll
|
||||||
|
|
||||||
console.log(
|
|
||||||
'[SCROLL DEBUG] Scroll attempt:',
|
|
||||||
currentElement.tagName,
|
|
||||||
'before:',
|
|
||||||
beforeScroll,
|
|
||||||
'after:',
|
|
||||||
afterScroll,
|
|
||||||
'delta:',
|
|
||||||
actualScrollDelta
|
|
||||||
)
|
|
||||||
|
|
||||||
if (Math.abs(actualScrollDelta) > 0.5) {
|
if (Math.abs(actualScrollDelta) > 0.5) {
|
||||||
scrollSuccess = true
|
scrollSuccess = true
|
||||||
scrolledElement = currentElement
|
scrolledElement = currentElement
|
||||||
scrollDelta = actualScrollDelta
|
scrollDelta = actualScrollDelta
|
||||||
console.log(
|
|
||||||
'[SCROLL DEBUG] Successfully scrolled container:',
|
|
||||||
currentElement.tagName,
|
|
||||||
'delta:',
|
|
||||||
actualScrollDelta
|
|
||||||
)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -344,11 +309,6 @@ export async function scrollHorizontally(
|
|||||||
// Element-specific scrolling if element is provided
|
// Element-specific scrolling if element is provided
|
||||||
if (element) {
|
if (element) {
|
||||||
const targetElement = element
|
const targetElement = element
|
||||||
console.log(
|
|
||||||
'[SCROLL DEBUG] Starting direct container scroll for element:',
|
|
||||||
targetElement.tagName
|
|
||||||
)
|
|
||||||
|
|
||||||
let currentElement = targetElement as HTMLElement | null
|
let currentElement = targetElement as HTMLElement | null
|
||||||
let scrollSuccess = false
|
let scrollSuccess = false
|
||||||
let scrolledElement: HTMLElement | null = null
|
let scrolledElement: HTMLElement | null = null
|
||||||
@@ -361,19 +321,6 @@ export async function scrollHorizontally(
|
|||||||
const hasScrollableX = /(auto|scroll|overlay)/.test(computedStyle.overflowX)
|
const hasScrollableX = /(auto|scroll|overlay)/.test(computedStyle.overflowX)
|
||||||
const canScrollHorizontally = currentElement.scrollWidth > currentElement.clientWidth
|
const canScrollHorizontally = currentElement.scrollWidth > currentElement.clientWidth
|
||||||
|
|
||||||
console.log(
|
|
||||||
'[SCROLL DEBUG] Checking element:',
|
|
||||||
currentElement.tagName,
|
|
||||||
'hasScrollableX:',
|
|
||||||
hasScrollableX,
|
|
||||||
'canScrollHorizontally:',
|
|
||||||
canScrollHorizontally,
|
|
||||||
'scrollWidth:',
|
|
||||||
currentElement.scrollWidth,
|
|
||||||
'clientWidth:',
|
|
||||||
currentElement.clientWidth
|
|
||||||
)
|
|
||||||
|
|
||||||
if (hasScrollableX && canScrollHorizontally) {
|
if (hasScrollableX && canScrollHorizontally) {
|
||||||
const beforeScroll = currentElement.scrollLeft
|
const beforeScroll = currentElement.scrollLeft
|
||||||
const maxScroll = currentElement.scrollWidth - currentElement.clientWidth
|
const maxScroll = currentElement.scrollWidth - currentElement.clientWidth
|
||||||
@@ -391,27 +338,10 @@ export async function scrollHorizontally(
|
|||||||
const afterScroll = currentElement.scrollLeft
|
const afterScroll = currentElement.scrollLeft
|
||||||
const actualScrollDelta = afterScroll - beforeScroll
|
const actualScrollDelta = afterScroll - beforeScroll
|
||||||
|
|
||||||
console.log(
|
|
||||||
'[SCROLL DEBUG] Scroll attempt:',
|
|
||||||
currentElement.tagName,
|
|
||||||
'before:',
|
|
||||||
beforeScroll,
|
|
||||||
'after:',
|
|
||||||
afterScroll,
|
|
||||||
'delta:',
|
|
||||||
actualScrollDelta
|
|
||||||
)
|
|
||||||
|
|
||||||
if (Math.abs(actualScrollDelta) > 0.5) {
|
if (Math.abs(actualScrollDelta) > 0.5) {
|
||||||
scrollSuccess = true
|
scrollSuccess = true
|
||||||
scrolledElement = currentElement
|
scrolledElement = currentElement
|
||||||
scrollDelta = actualScrollDelta
|
scrollDelta = actualScrollDelta
|
||||||
console.log(
|
|
||||||
'[SCROLL DEBUG] Successfully scrolled container:',
|
|
||||||
currentElement.tagName,
|
|
||||||
'delta:',
|
|
||||||
actualScrollDelta
|
|
||||||
)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@page-agent/ui",
|
"name": "@page-agent/ui",
|
||||||
"version": "1.4.0",
|
"version": "1.4.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/lib/page-agent-ui.js",
|
"main": "./dist/lib/page-agent-ui.js",
|
||||||
"module": "./dist/lib/page-agent-ui.js",
|
"module": "./dist/lib/page-agent-ui.js",
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const enUS = {
|
|||||||
close: 'Close',
|
close: 'Close',
|
||||||
expand: 'Expand history',
|
expand: 'Expand history',
|
||||||
collapse: 'Collapse history',
|
collapse: 'Collapse history',
|
||||||
step: 'Step {{number}} · {{time}}{{duration}}',
|
step: 'Step {{number}}',
|
||||||
},
|
},
|
||||||
tools: {
|
tools: {
|
||||||
clicking: 'Clicking element [{{index}}]...',
|
clicking: 'Clicking element [{{index}}]...',
|
||||||
@@ -63,7 +63,7 @@ const zhCN = {
|
|||||||
close: '关闭',
|
close: '关闭',
|
||||||
expand: '展开历史',
|
expand: '展开历史',
|
||||||
collapse: '收起历史',
|
collapse: '收起历史',
|
||||||
step: '步骤 {{number}} · {{time}}{{duration}}',
|
step: '步骤 {{number}}',
|
||||||
},
|
},
|
||||||
tools: {
|
tools: {
|
||||||
clicking: '正在点击元素 [{{index}}]...',
|
clicking: '正在点击元素 [{{index}}]...',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { I18n, type SupportedLanguage } from '../i18n'
|
import { I18n, type SupportedLanguage } from '../i18n'
|
||||||
import { truncate } from '../utils'
|
import { truncate } from '../utils'
|
||||||
import { createCard, createReflectionLines, formatTime } from './cards'
|
import { createCard, createReflectionLines } from './cards'
|
||||||
import type { AgentActivity, PanelAgentAdapter } from './types'
|
import type { AgentActivity, PanelAgentAdapter } from './types'
|
||||||
|
|
||||||
import styles from './Panel.module.css'
|
import styles from './Panel.module.css'
|
||||||
@@ -187,7 +187,6 @@ export class Panel {
|
|||||||
tempCard.innerHTML = createCard({
|
tempCard.innerHTML = createCard({
|
||||||
icon: '❓',
|
icon: '❓',
|
||||||
content: `Question: ${question}`,
|
content: `Question: ${question}`,
|
||||||
meta: formatTime(this.#config.language ?? 'en-US'),
|
|
||||||
type: 'question',
|
type: 'question',
|
||||||
})
|
})
|
||||||
const cardElement = tempCard.firstElementChild as HTMLElement
|
const cardElement = tempCard.firstElementChild as HTMLElement
|
||||||
@@ -591,15 +590,12 @@ export class Panel {
|
|||||||
/** Create cards for a history event */
|
/** Create cards for a history event */
|
||||||
#createHistoryCards(event: PanelAgentAdapter['history'][number]): string[] {
|
#createHistoryCards(event: PanelAgentAdapter['history'][number]): string[] {
|
||||||
const cards: string[] = []
|
const cards: string[] = []
|
||||||
const time = formatTime(this.#config.language ?? 'en-US')
|
|
||||||
const meta =
|
const meta =
|
||||||
event.type === 'step' && event.stepIndex !== undefined
|
event.type === 'step' && event.stepIndex !== undefined
|
||||||
? this.#i18n.t('ui.panel.step', {
|
? this.#i18n.t('ui.panel.step', {
|
||||||
number: (event.stepIndex + 1).toString(),
|
number: (event.stepIndex + 1).toString(),
|
||||||
time,
|
|
||||||
duration: '',
|
|
||||||
})
|
})
|
||||||
: time
|
: undefined
|
||||||
|
|
||||||
if (event.type === 'step') {
|
if (event.type === 'step') {
|
||||||
// Reflection card
|
// Reflection card
|
||||||
@@ -636,7 +632,7 @@ export class Panel {
|
|||||||
/** Create cards for an action */
|
/** Create cards for an action */
|
||||||
#createActionCards(
|
#createActionCards(
|
||||||
action: { name: string; input: unknown; output: string },
|
action: { name: string; input: unknown; output: string },
|
||||||
meta: string
|
meta?: string
|
||||||
): string[] {
|
): string[] {
|
||||||
const cards: string[] = []
|
const cards: string[] = []
|
||||||
|
|
||||||
|
|||||||
@@ -32,16 +32,6 @@ export function createCard({ icon, content, meta, type }: CardOptions): string {
|
|||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Format timestamp for cards */
|
|
||||||
export function formatTime(locale: string = 'en-US'): string {
|
|
||||||
return new Date().toLocaleTimeString(locale, {
|
|
||||||
hour12: false,
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit',
|
|
||||||
second: '2-digit',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Create reflection lines from reflection object */
|
/** Create reflection lines from reflection object */
|
||||||
export function createReflectionLines(reflection: {
|
export function createReflectionLines(reflection: {
|
||||||
evaluation_previous_goal?: string
|
evaluation_previous_goal?: string
|
||||||
|
|||||||
@@ -45,13 +45,24 @@
|
|||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root">
|
||||||
|
<div id="sk">
|
||||||
|
<p class="sk-text" id="sk-text">Loading...</p>
|
||||||
|
<style>
|
||||||
|
#sk{display:flex;align-items:center;justify-content:center;min-height:100vh;background:linear-gradient(135deg,#eff6ff,#f5f3ff)}
|
||||||
|
@media(prefers-color-scheme:dark){#sk{background:linear-gradient(135deg,#111827,#1f2937)}}
|
||||||
|
.sk-text{font:400 14px/1 system-ui,sans-serif;color:#94a3b8;animation:skf 2s ease-in-out infinite}
|
||||||
|
@keyframes skf{0%,100%{opacity:.6}50%{opacity:.3}}
|
||||||
|
</style>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<script type="module" src="./src/main.tsx"></script>
|
<script type="module" src="./src/main.tsx"></script>
|
||||||
<script>
|
<script>
|
||||||
// Dynamically update html lang attribute based on i18n detection
|
|
||||||
const updateHtmlLang = () => {
|
const updateHtmlLang = () => {
|
||||||
const lang = localStorage.getItem('i18nextLng') || navigator.language || 'zh-CN'
|
const lang = localStorage.getItem('i18nextLng') || navigator.language || 'zh-CN'
|
||||||
document.documentElement.lang = lang
|
document.documentElement.lang = lang
|
||||||
|
const el = document.getElementById('sk-text')
|
||||||
|
if (el) el.textContent = lang.startsWith('zh') ? '加载中...' : 'Loading...'
|
||||||
}
|
}
|
||||||
updateHtmlLang()
|
updateHtmlLang()
|
||||||
window.addEventListener('storage', updateHtmlLang)
|
window.addEventListener('storage', updateHtmlLang)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@page-agent/website",
|
"name": "@page-agent/website",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.4.0",
|
"version": "1.4.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host 0.0.0.0",
|
"dev": "vite --host 0.0.0.0",
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// Demo build (auto-init with demo LLM, for quick testing)
|
// Demo build (auto-init with demo LLM, for quick testing)
|
||||||
export const CDN_DEMO_URL =
|
export const CDN_DEMO_URL =
|
||||||
'https://cdn.jsdelivr.net/npm/page-agent@1.4.0/dist/iife/page-agent.demo.js'
|
'https://cdn.jsdelivr.net/npm/page-agent@1.4.2/dist/iife/page-agent.demo.js'
|
||||||
export const CDN_DEMO_CN_URL =
|
export const CDN_DEMO_CN_URL =
|
||||||
'https://registry.npmmirror.com/page-agent/1.4.0/files/dist/iife/page-agent.demo.js'
|
'https://registry.npmmirror.com/page-agent/1.4.2/files/dist/iife/page-agent.demo.js'
|
||||||
|
|
||||||
// Demo LLM for website testing (homepage quick trial uses flash)
|
// Demo LLM for website testing (homepage quick trial uses flash)
|
||||||
export const DEMO_MODEL = 'qwen3.5-flash'
|
export const DEMO_MODEL = 'qwen3.5-flash'
|
||||||
|
|||||||
@@ -1,15 +1,27 @@
|
|||||||
import FeaturesSection from './home/FeaturesSection'
|
import { Suspense, lazy } from 'react'
|
||||||
|
|
||||||
import HeroSection from './home/HeroSection'
|
import HeroSection from './home/HeroSection'
|
||||||
import OneMoreThingSection from './home/OneMoreThingSection'
|
|
||||||
import ScenariosSection from './home/ScenariosSection'
|
const FeaturesSection = lazy(() => import('./home/FeaturesSection'))
|
||||||
|
const ScenariosSection = lazy(() => import('./home/ScenariosSection'))
|
||||||
|
const OneMoreThingSection = lazy(() => import('./home/OneMoreThingSection'))
|
||||||
|
|
||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<HeroSection />
|
<HeroSection />
|
||||||
<FeaturesSection />
|
<Suspense
|
||||||
<ScenariosSection />
|
fallback={
|
||||||
<OneMoreThingSection />
|
<div className="flex items-center justify-center gap-3 py-20 text-gray-400">
|
||||||
|
<div className="w-4 h-4 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" />
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<FeaturesSection />
|
||||||
|
<ScenariosSection />
|
||||||
|
<OneMoreThingSection />
|
||||||
|
</Suspense>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export default function FeaturesSection() {
|
|||||||
color="#ef4444aa"
|
color="#ef4444aa"
|
||||||
strokeWidth={1.5}
|
strokeWidth={1.5}
|
||||||
// multiline={false}
|
// multiline={false}
|
||||||
isView
|
// isView
|
||||||
>
|
>
|
||||||
{cmd}
|
{cmd}
|
||||||
</Highlighter>
|
</Highlighter>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable react-dom/no-dangerously-set-innerhtml */
|
/* eslint-disable react-dom/no-dangerously-set-innerhtml */
|
||||||
import { PageAgent } from 'page-agent'
|
import type { PageAgent as PageAgentType } from 'page-agent'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Link, useSearchParams } from 'wouter'
|
import { Link, useSearchParams } from 'wouter'
|
||||||
|
|
||||||
@@ -16,6 +16,8 @@ import {
|
|||||||
} from '../../constants'
|
} from '../../constants'
|
||||||
import { useLanguage } from '../../i18n/context'
|
import { useLanguage } from '../../i18n/context'
|
||||||
|
|
||||||
|
let pageAgentModule: Promise<typeof import('page-agent')> | null = null
|
||||||
|
|
||||||
function getInjection(useCN?: boolean) {
|
function getInjection(useCN?: boolean) {
|
||||||
const cdn = useCN ? CDN_DEMO_CN_URL : CDN_DEMO_URL
|
const cdn = useCN ? CDN_DEMO_CN_URL : CDN_DEMO_URL
|
||||||
|
|
||||||
@@ -55,13 +57,20 @@ export default function HeroSection() {
|
|||||||
const [activeTab, setActiveTab] = useState<'try' | 'other'>(isOther ? 'other' : 'try')
|
const [activeTab, setActiveTab] = useState<'try' | 'other'>(isOther ? 'other' : 'try')
|
||||||
const [cdnSource, setCdnSource] = useState<'international' | 'china'>('international')
|
const [cdnSource, setCdnSource] = useState<'international' | 'china'>('international')
|
||||||
|
|
||||||
const handleExecute = async () => {
|
const [ready, setReady] = useState(false)
|
||||||
if (!task.trim()) return
|
useEffect(() => {
|
||||||
|
pageAgentModule ??= import('page-agent')
|
||||||
|
pageAgentModule.then(() => setReady(true))
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleExecute = async () => {
|
||||||
|
if (!task.trim() || !ready || !pageAgentModule) return
|
||||||
|
|
||||||
|
const { PageAgent } = await pageAgentModule
|
||||||
const win = window as any
|
const win = window as any
|
||||||
|
|
||||||
if (!win.pageAgent || win.pageAgent.disposed) {
|
if (!win.pageAgent || win.pageAgent.disposed) {
|
||||||
win.pageAgent = new PageAgent({
|
win.pageAgent = new (PageAgent as typeof PageAgentType)({
|
||||||
interactiveBlacklist: [document.getElementById('root')!],
|
interactiveBlacklist: [document.getElementById('root')!],
|
||||||
language: language,
|
language: language,
|
||||||
|
|
||||||
@@ -208,10 +217,21 @@ export default function HeroSection() {
|
|||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={handleExecute}
|
onClick={handleExecute}
|
||||||
|
disabled={!ready}
|
||||||
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"
|
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
|
data-page-agent-not-interactive
|
||||||
>
|
>
|
||||||
{isZh ? '执行' : 'Run'}
|
{ready ? (
|
||||||
|
isZh ? (
|
||||||
|
'执行'
|
||||||
|
) : (
|
||||||
|
'Run'
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<span className="animate-pulse">
|
||||||
|
{isZh ? '准备中...' : 'Preparing...'}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-gray-500 dark:text-gray-400 text-left">
|
<p className="text-xs text-gray-500 dark:text-gray-400 text-left">
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ export default function ScenariosSection() {
|
|||||||
const { isZh } = useLanguage()
|
const { isZh } = useLanguage()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="px-6 py-16" aria-labelledby="scenarios-heading">
|
<section
|
||||||
|
className="px-6 py-16 bg-linear-to-b from-blue-100 to-purple-100 dark:from-blue-950/40 dark:to-gray-800"
|
||||||
|
aria-labelledby="scenarios-heading"
|
||||||
|
>
|
||||||
<div className="max-w-6xl mx-auto">
|
<div className="max-w-6xl mx-auto">
|
||||||
<BlurFade inView>
|
<BlurFade inView>
|
||||||
<div className="text-center mb-12">
|
<div className="text-center mb-12">
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { Suspense, useLayoutEffect } from 'react'
|
import { Suspense, lazy, useEffect, useLayoutEffect } from 'react'
|
||||||
import { Route, Switch, useLocation } from 'wouter'
|
import { Route, Switch, useLocation } from 'wouter'
|
||||||
|
|
||||||
import Footer from './components/Footer'
|
import Footer from './components/Footer'
|
||||||
import Header from './components/Header'
|
import Header from './components/Header'
|
||||||
import HomePage from './pages/Home'
|
import HomePage from './pages/Home'
|
||||||
import DocsPages from './pages/docs/index'
|
|
||||||
|
const docsImport = () => import('./pages/docs/index')
|
||||||
|
const DocsPages = lazy(docsImport)
|
||||||
|
|
||||||
function ScrollToTop() {
|
function ScrollToTop() {
|
||||||
const [pathname] = useLocation()
|
const [pathname] = useLocation()
|
||||||
@@ -15,6 +17,11 @@ function ScrollToTop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Router() {
|
export default function Router() {
|
||||||
|
useEffect(() => {
|
||||||
|
const id = requestIdleCallback(() => docsImport())
|
||||||
|
return () => cancelIdleCallback(id)
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen flex-col">
|
<div className="flex min-h-screen flex-col">
|
||||||
<Header />
|
<Header />
|
||||||
@@ -32,7 +39,16 @@ export default function Router() {
|
|||||||
|
|
||||||
<Route path="/docs" nest>
|
<Route path="/docs" nest>
|
||||||
<div className="flex-1 bg-white dark:bg-gray-900">
|
<div className="flex-1 bg-white dark:bg-gray-900">
|
||||||
<DocsPages />
|
<Suspense
|
||||||
|
fallback={
|
||||||
|
<div className="flex items-center justify-center gap-3 py-20 text-gray-400">
|
||||||
|
<div className="w-4 h-4 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" />
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<DocsPages />
|
||||||
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,11 @@ export default defineConfig(({ mode }) => ({
|
|||||||
if (message.code === 'EVAL') return
|
if (message.code === 'EVAL') return
|
||||||
handler(message)
|
handler(message)
|
||||||
},
|
},
|
||||||
|
output: {
|
||||||
|
manualChunks: {
|
||||||
|
vendor: ['react', 'react-dom', 'wouter'],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
|
|||||||
Reference in New Issue
Block a user