diff --git a/docs/superpowers/specs/2026-03-17-browser-hand-ui-design.md b/docs/superpowers/specs/2026-03-17-browser-hand-ui-design.md new file mode 100644 index 0000000..930117a --- /dev/null +++ b/docs/superpowers/specs/2026-03-17-browser-hand-ui-design.md @@ -0,0 +1,547 @@ +# Browser Hand UI 组件设计文档 + +> **日期**: 2026-03-17 +> **状态**: Draft +> **作者**: Claude Code + +## 一、概述 + +Browser Hand 是 ZCLAW 的浏览器自动化能力,基于 Fantoccini (Rust WebDriver) 实现。本设计文档描述其前端 UI 组件架构和集成方案。 + +### 设计目标 + +1. **双层触发机制**: 前端任务模板 + Agent 脚本驱动 +2. **实时可视化**: 执行状态 + 截图预览 +3. **完整工具集**: 支持所有浏览器自动化功能 +4. **标准集成**: 作为 Hand 集成到现有 HandsPanel + +### 范围 + +- 前端 UI 组件 +- 状态管理 Store +- 任务模板系统 +- 与现有 HandsPanel 集成 + +--- + +## 二、架构设计 + +### 2.1 组件结构 + +``` +desktop/src/ +├── components/ +│ ├── HandsPanel.tsx # 现有,添加 Browser Hand 特殊处理 +│ └── BrowserHand/ +│ ├── index.ts # 模块导出 +│ ├── BrowserHandCard.tsx # Browser Hand 专用卡片 +│ ├── TaskTemplateModal.tsx # 任务模板选择模态框 +│ ├── TaskRunner.tsx # 任务执行状态展示 +│ ├── ScreenshotPreview.tsx # 截图预览组件 +│ └── templates/ +│ ├── index.ts # 模板注册 +│ ├── types.ts # 模板类型定义 +│ ├── basic.ts # 基础操作模板 +│ ├── scraping.ts # 数据采集模板 +│ └── automation.ts # 自动化流程模板 +├── lib/ +│ ├── browser-client.ts # 已完成 - Tauri API 封装 +│ └── browser-templates.ts # 模板执行引擎 +└── store/ + └── browserHandStore.ts # Browser Hand 状态管理 +``` + +### 2.2 数据流 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 前端触发流程 │ +├─────────────────────────────────────────────────────────────────┤ +│ HandsPanel │ +│ ↓ 检测 Browser Hand │ +│ BrowserHandCard │ +│ ↓ 点击"执行任务" │ +│ TaskTemplateModal │ +│ ↓ 选择模板 + 填参数 │ +│ browserHandStore.executeTemplate() │ +│ ↓ 调用 │ +│ Tauri Commands (browser_*) │ +│ ↓ WebDriver Protocol │ +│ Fantoccini → ChromeDriver │ +└─────────────────────────────────────────────────────────────────┘ + +┌─────────────────────────────────────────────────────────────────┐ +│ Agent 触发流程 │ +├─────────────────────────────────────────────────────────────────┤ +│ 用户对话 │ +│ ↓ 理解意图 │ +│ LLM 生成脚本 │ +│ ↓ Agent 调用 │ +│ browserHandStore.executeScript() │ +│ ↓ 调用 │ +│ Tauri Commands (browser_execute_script) │ +│ ↓ WebDriver Protocol │ +│ Fantoccini → ChromeDriver │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 2.3 与现有系统集成 + +```typescript +// HandsPanel.tsx 修改 +import { BrowserHandCard } from './BrowserHand'; + +function HandsPanel() { + // 检测是否为 Browser Hand + const isBrowserHand = (hand: Hand) => hand.id === 'browser' || hand.name === 'Browser'; + + return ( +
+ {hands.map((hand) => + isBrowserHand(hand) ? ( + + ) : ( + + ) + )} +
+ ); +} +``` + +--- + +## 三、状态管理 + +### 3.1 Store 结构 + +```typescript +// store/browserHandStore.ts + +interface BrowserSession { + id: string; + name: string; + currentUrl: string | null; + title: string | null; + status: 'connecting' | 'connected' | 'active' | 'idle' | 'error'; + createdAt: string; + lastActivity: string; +} + +interface BrowserLog { + id: string; + timestamp: string; + level: 'info' | 'warn' | 'error' | 'action'; + message: string; + details?: Record; +} + +interface ExecutionState { + isRunning: boolean; + currentAction: string | null; + currentUrl: string | null; + lastScreenshot: string | null; + progress: number; // 0-100 + startTime: string | null; +} + +interface RecentTask { + id: string; + templateId: string; + templateName: string; + params: Record; + status: 'success' | 'failed' | 'cancelled'; + executedAt: string; + duration: number; + result?: unknown; +} + +interface BrowserHandState { + // 会话管理 + sessions: BrowserSession[]; + activeSessionId: string | null; + + // 执行状态 + execution: ExecutionState; + + // 日志 + logs: BrowserLog[]; + + // 模板 + templates: TaskTemplate[]; + recentTasks: RecentTask[]; + + // UI 状态 + isTemplateModalOpen: boolean; + isLoading: boolean; + error: string | null; +} + +interface BrowserHandActions { + // 会话管理 + createSession: (options?: SessionOptions) => Promise; + closeSession: (sessionId: string) => Promise; + listSessions: () => Promise; + + // 模板执行 + executeTemplate: (templateId: string, params: Record) => Promise; + executeScript: (script: string, args?: unknown[]) => Promise; + + // 状态更新 + updateExecutionState: (state: Partial) => void; + addLog: (log: Omit) => void; + + // 截图 + takeScreenshot: () => Promise; + + // UI 控制 + openTemplateModal: () => void; + closeTemplateModal: () => void; + clearError: () => void; +} +``` + +### 3.2 Store 实现要点 + +```typescript +// 使用 Zustand 创建 store +export const useBrowserHandStore = create((set, get) => ({ + // 初始状态 + sessions: [], + activeSessionId: null, + execution: { + isRunning: false, + currentAction: null, + currentUrl: null, + lastScreenshot: null, + progress: 0, + startTime: null, + }, + logs: [], + templates: BUILTIN_TEMPLATES, + recentTasks: [], + isTemplateModalOpen: false, + isLoading: false, + error: null, + + // Actions 实现见详细设计 +})); +``` + +--- + +## 四、任务模板系统 + +### 4.1 模板类型定义 + +```typescript +// components/BrowserHand/templates/types.ts + +interface TaskTemplateParam { + key: string; + label: string; + type: 'text' | 'url' | 'number' | 'select' | 'textarea' | 'json'; + required: boolean; + default?: unknown; + placeholder?: string; + options?: { value: string; label: string }[]; // for select type + description?: string; +} + +interface TaskTemplate { + id: string; + name: string; + description: string; + category: 'basic' | 'scraping' | 'automation'; + icon: string; + params: TaskTemplateParam[]; + execute: (params: Record, context: ExecutionContext) => Promise; +} + +interface ExecutionContext { + browser: Browser; // browser-client.ts 中的 Browser 类 + onProgress: (action: string, progress: number) => void; + onLog: (level: BrowserLog['level'], message: string, details?: Record) => void; +} +``` + +### 4.2 内置模板列表 + +#### 基础操作类 (Basic) + +| ID | 名称 | 参数 | 说明 | +|----|------|------|------| +| `basic_navigate_screenshot` | 打开网页并截图 | `url` | 访问指定 URL 并截图 | +| `basic_fill_form` | 填写表单 | `url`, `fields[]` | 填写并可选提交表单 | +| `basic_click_navigate` | 点击导航 | `url`, `selector` | 点击指定元素 | + +#### 数据采集类 (Scraping) + +| ID | 名称 | 参数 | 说明 | +|----|------|------|------| +| `scrape_text` | 抓取页面文本 | `url`, `selectors[]` | 提取多个选择器的文本 | +| `scrape_list` | 提取列表数据 | `url`, `itemSelector`, `fieldMappings` | 批量提取结构化数据 | +| `scrape_images` | 下载图片 | `url`, `imageSelector`, `savePath` | 批量下载图片 | + +#### 自动化流程类 (Automation) + +| ID | 名称 | 参数 | 说明 | +|----|------|------|------| +| `auto_login_action` | 登录并操作 | `loginUrl`, `credentials`, `actions[]` | 登录后执行操作序列 | +| `auto_multi_page` | 多页面导航 | `urls[]`, `actions[]` | 遍历多个页面执行操作 | +| `auto_monitor` | 定时监控 | `url`, `checkCondition`, `interval` | 周期性检查页面状态 | + +### 4.3 模板执行引擎 + +```typescript +// lib/browser-templates.ts + +export async function executeTemplate( + template: TaskTemplate, + params: Record, + browser: Browser, + onProgress: (action: string, progress: number) => void, + onLog: (level: BrowserLog['level'], message: string) => void +): Promise { + const context: ExecutionContext = { + browser, + onProgress, + onLog: (level, message, details) => { + onLog(level, message); + console.log(`[BrowserHand] ${level}: ${message}`, details); + }, + }; + + // 验证参数 + validateParams(template.params, params); + + // 执行模板 + onProgress('启动浏览器', 0); + try { + const result = await template.execute(params, context); + onProgress('完成', 100); + return result; + } catch (error) { + onLog('error', `执行失败: ${error}`); + throw error; + } +} +``` + +--- + +## 五、UI 组件设计 + +### 5.1 BrowserHandCard + +Browser Hand 的专用卡片组件,显示实时状态和截图预览。 + +``` +┌────────────────────────────────────────────────────────────┐ +│ 🌐 Browser Hand [●] 就绪/运行中 │ +├────────────────────────────────────────────────────────────┤ +│ 浏览器自动化能力 - 支持网页操作、数据采集、自动化流程 │ +├────────────────────────────────────────────────────────────┤ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ 截 图 预 览 │ │ +│ │ (最近一次截图) │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ 当前: https://example.com/page │ +│ 操作: 正在填写表单... ████████░░ 80% │ +├────────────────────────────────────────────────────────────┤ +│ [📋 执行任务] [📸 截图] [🔄 刷新] [⚙️ 设置] │ +└────────────────────────────────────────────────────────────┘ +``` + +**Props**: +```typescript +interface BrowserHandCardProps { + hand: Hand; + onOpenSettings?: () => void; +} +``` + +**状态显示**: +- `idle`: 显示"就绪",截图区域显示占位符 +- `running`: 显示进度条、当前操作、最新截图 +- `error`: 显示错误信息、重试按钮 + +### 5.2 TaskTemplateModal + +任务模板选择和参数填写模态框。 + +``` +┌────────────────────────────────────────────────────────────┐ +│ 选择任务模板 ✕ │ +├────────────────────────────────────────────────────────────┤ +│ [基础操作] [数据采集] [自动化流程] │ +├────────────────────────────────────────────────────────────┤ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ 📸 打开截图 │ │ 📝 填写表单 │ │ 🖱️ 点击导航 │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ ┌──────────────┐ ┌──────────────┐ │ +│ │ 📄 抓取文本 │ │ 📊 提取列表 │ │ +│ └──────────────┘ └──────────────┘ │ +├────────────────────────────────────────────────────────────┤ +│ 已选: 📸 打开网页并截图 │ +│ │ +│ 网页地址 * │ +│ ┌────────────────────────────────────────────────────────┐│ +│ │ https://example.com ││ +│ └────────────────────────────────────────────────────────┘│ +├────────────────────────────────────────────────────────────┤ +│ [取消] [▶ 执行任务] │ +└────────────────────────────────────────────────────────────┘ +``` + +**Props**: +```typescript +interface TaskTemplateModalProps { + isOpen: boolean; + onClose: () => void; + onSelect: (template: TaskTemplate, params: Record) => void; +} +``` + +### 5.3 TaskRunner + +任务执行中的状态展示组件(可嵌入 BrowserHandCard 或独立显示)。 + +``` +┌────────────────────────────────────────────────────────────┐ +│ 执行中: 打开网页并截图 │ +├────────────────────────────────────────────────────────────┤ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ 实 时 截 图 │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ │ +│ 当前 URL: https://example.com/page │ +│ 操作: 等待页面加载... │ +│ │ +│ ████████████████████░░░░░░░░░░ 65% │ +│ │ +│ ┌─ 操作日志 ─────────────────────────────────────────────┐│ +│ │ 10:23:01 [info] 创建浏览器会话 ││ +│ │ 10:23:02 [info] 导航到 https://example.com ││ +│ │ 10:23:05 [action] 等待页面加载 ││ +│ └────────────────────────────────────────────────────────┘│ +├────────────────────────────────────────────────────────────┤ +│ [⏹ 停止] [📋 查看结果] │ +└────────────────────────────────────────────────────────────┘ +``` + +### 5.4 ScreenshotPreview + +截图预览组件,支持缩放和全屏查看。 + +```typescript +interface ScreenshotPreviewProps { + base64: string | null; + isLoading?: boolean; + onRefresh?: () => void; + onClick?: () => void; // 点击放大 +} +``` + +--- + +## 六、错误处理 + +### 6.1 错误类型 + +```typescript +enum BrowserHandErrorType { + WEBDRIVER_NOT_AVAILABLE = 'webdriver_not_available', + SESSION_CREATION_FAILED = 'session_creation_failed', + NAVIGATION_FAILED = 'navigation_failed', + ELEMENT_NOT_FOUND = 'element_not_found', + TIMEOUT = 'timeout', + SCRIPT_ERROR = 'script_error', + PERMISSION_DENIED = 'permission_denied', +} +``` + +### 6.2 错误处理策略 + +| 错误类型 | 处理方式 | +|---------|---------| +| WEBDRIVER_NOT_AVAILABLE | 显示安装指引,提供下载链接 | +| SESSION_CREATION_FAILED | 重试按钮,显示详细错误 | +| NAVIGATION_FAILED | 显示 URL,提供重试 | +| ELEMENT_NOT_FOUND | 显示选择器,建议检查页面 | +| TIMEOUT | 提供增加超时时间选项 | +| SCRIPT_ERROR | 显示脚本错误位置和消息 | + +--- + +## 七、测试策略 + +### 7.1 单元测试 + +- `browserHandStore` 状态管理测试 +- 模板参数验证测试 +- 错误处理测试 + +### 7.2 集成测试 + +- 模板执行流程测试(mock Tauri commands) +- UI 组件渲染测试 +- 与 HandsPanel 集成测试 + +### 7.3 E2E 测试 + +- 完整任务执行流程 +- 多模板顺序执行 +- 错误恢复流程 + +--- + +## 八、实现优先级 + +### Phase 1: 核心框架 +1. `browserHandStore` 状态管理 +2. `BrowserHandCard` 基础组件 +3. 集成到 `HandsPanel` + +### Phase 2: 模板系统 +4. 模板类型定义和注册 +5. `TaskTemplateModal` 组件 +6. 基础操作类模板实现 + +### Phase 3: 完整功能 +7. 数据采集类模板 +8. 自动化流程类模板 +9. `TaskRunner` 实时状态展示 +10. 截图预览功能 + +--- + +## 九、依赖 + +- `browser-client.ts` (已完成) +- Tauri browser commands (已完成) +- Fantoccini WebDriver (已完成) +- Zustand (已有) +- Lucide React icons (已有) +- Tailwind CSS (已有) + +--- + +## 十、风险与缓解 + +| 风险 | 影响 | 缓解措施 | +|------|------|---------| +| WebDriver 未安装 | 无法使用 | 提供安装检测和指引 | +| 大量截图消耗内存 | 性能下降 | 限制截图历史数量,压缩图片 | +| 脚本执行安全 | 潜在危险操作 | Agent 层审批机制 | +| 复杂模板难以调试 | 用户体验差 | 详细日志,步骤高亮 | + +--- + +## 十一、未来扩展 + +- [ ] 录制回放功能 +- [ ] 模板自定义创建 +- [ ] 多浏览器并行 +- [ ] Chrome DevTools Protocol 直接集成 +- [ ] WebMCP 支持 (Chrome 146+)