Files
zclaw_openfang/docs/superpowers/specs/2026-03-17-browser-hand-ui-design.md
iven 69c874ed59 docs(spec): add Browser Hand UI design specification
Design for browser automation UI component integrated into HandsPanel:
- Dual trigger mechanism: frontend templates + agent scripts
- Real-time status + screenshot preview
- Complete template system (basic, scraping, automation)
- Task runner with progress tracking

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 08:14:41 +08:00

21 KiB
Raw Blame History

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 与现有系统集成

// HandsPanel.tsx 修改
import { BrowserHandCard } from './BrowserHand';

function HandsPanel() {
  // 检测是否为 Browser Hand
  const isBrowserHand = (hand: Hand) => hand.id === 'browser' || hand.name === 'Browser';

  return (
    <div className="grid gap-3">
      {hands.map((hand) =>
        isBrowserHand(hand) ? (
          <BrowserHandCard key={hand.id} hand={hand} />
        ) : (
          <HandCard key={hand.id} hand={hand} {...props} />
        )
      )}
    </div>
  );
}

三、状态管理

3.1 Store 结构

// 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<string, unknown>;
}

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<string, unknown>;
  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<string>;
  closeSession: (sessionId: string) => Promise<void>;
  listSessions: () => Promise<void>;

  // 模板执行
  executeTemplate: (templateId: string, params: Record<string, unknown>) => Promise<unknown>;
  executeScript: (script: string, args?: unknown[]) => Promise<unknown>;

  // 状态更新
  updateExecutionState: (state: Partial<ExecutionState>) => void;
  addLog: (log: Omit<BrowserLog, 'id' | 'timestamp'>) => void;

  // 截图
  takeScreenshot: () => Promise<string>;

  // UI 控制
  openTemplateModal: () => void;
  closeTemplateModal: () => void;
  clearError: () => void;
}

3.2 Store 实现要点

// 使用 Zustand 创建 store
export const useBrowserHandStore = create<BrowserHandState & BrowserHandActions>((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 模板类型定义

// 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<string, unknown>, context: ExecutionContext) => Promise<unknown>;
}

interface ExecutionContext {
  browser: Browser; // browser-client.ts 中的 Browser 类
  onProgress: (action: string, progress: number) => void;
  onLog: (level: BrowserLog['level'], message: string, details?: Record<string, unknown>) => 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 模板执行引擎

// lib/browser-templates.ts

export async function executeTemplate(
  template: TaskTemplate,
  params: Record<string, unknown>,
  browser: Browser,
  onProgress: (action: string, progress: number) => void,
  onLog: (level: BrowserLog['level'], message: string) => void
): Promise<unknown> {
  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:

interface BrowserHandCardProps {
  hand: Hand;
  onOpenSettings?: () => void;
}

状态显示:

  • idle: 显示"就绪",截图区域显示占位符
  • running: 显示进度条、当前操作、最新截图
  • error: 显示错误信息、重试按钮

5.2 TaskTemplateModal

任务模板选择和参数填写模态框。

┌────────────────────────────────────────────────────────────┐
│ 选择任务模板                                           ✕   │
├────────────────────────────────────────────────────────────┤
│ [基础操作] [数据采集] [自动化流程]                          │
├────────────────────────────────────────────────────────────┤
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐        │
│ │ 📸 打开截图   │ │ 📝 填写表单   │ │ 🖱️ 点击导航   │        │
│ └──────────────┘ └──────────────┘ └──────────────┘        │
│ ┌──────────────┐ ┌──────────────┐                         │
│ │ 📄 抓取文本   │ │ 📊 提取列表   │                         │
│ └──────────────┘ └──────────────┘                         │
├────────────────────────────────────────────────────────────┤
│ 已选: 📸 打开网页并截图                                     │
│                                                            │
│ 网页地址 *                                                  │
│ ┌────────────────────────────────────────────────────────┐│
│ │ https://example.com                                    ││
│ └────────────────────────────────────────────────────────┘│
├────────────────────────────────────────────────────────────┤
│                                    [取消] [▶ 执行任务]     │
└────────────────────────────────────────────────────────────┘

Props:

interface TaskTemplateModalProps {
  isOpen: boolean;
  onClose: () => void;
  onSelect: (template: TaskTemplate, params: Record<string, unknown>) => 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

截图预览组件,支持缩放和全屏查看。

interface ScreenshotPreviewProps {
  base64: string | null;
  isLoading?: boolean;
  onRefresh?: () => void;
  onClick?: () => void; // 点击放大
}

六、错误处理

6.1 错误类型

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: 模板系统

  1. 模板类型定义和注册
  2. TaskTemplateModal 组件
  3. 基础操作类模板实现

Phase 3: 完整功能

  1. 数据采集类模板
  2. 自动化流程类模板
  3. TaskRunner 实时状态展示
  4. 截图预览功能

九、依赖

  • browser-client.ts (已完成)
  • Tauri browser commands (已完成)
  • Fantoccini WebDriver (已完成)
  • Zustand (已有)
  • Lucide React icons (已有)
  • Tailwind CSS (已有)

十、风险与缓解

风险 影响 缓解措施
WebDriver 未安装 无法使用 提供安装检测和指引
大量截图消耗内存 性能下降 限制截图历史数量,压缩图片
脚本执行安全 潜在危险操作 Agent 层审批机制
复杂模板难以调试 用户体验差 详细日志,步骤高亮

十一、未来扩展

  • 录制回放功能
  • 模板自定义创建
  • 多浏览器并行
  • Chrome DevTools Protocol 直接集成
  • WebMCP 支持 (Chrome 146+)