diff --git a/docs/superpowers/plans/2026-03-21-phase3-core-optimization.md b/docs/superpowers/plans/2026-03-21-phase3-core-optimization.md new file mode 100644 index 0000000..67edcac --- /dev/null +++ b/docs/superpowers/plans/2026-03-21-phase3-core-optimization.md @@ -0,0 +1,397 @@ +# ZCLAW 架构优化 - Phase 3: 核心优化 实施计划 + +> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 实现核心性能优化 - 虚拟滚动、Web Worker 隔离、缓存策略 + +**Architecture:** 三条并行轨道,可独立实施和验证 + +**Tech Stack:** React, react-window, Web Worker, Valtio + +**Spec Reference:** `docs/superpowers/specs/2026-03-21-architecture-optimization-design.md` + +**Duration:** 6 周 (24 人日) + +--- + +## 并行轨道概览 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ Phase 3: 核心优化 (并行) │ +├─────────────────────┬─────────────────────┬─────────────────────────────┤ +│ Track A: Chat │ Track B: Hands │ Track C: Intelligence │ +│ 虚拟滚动优化 │ Web Worker 隔离 │ 缓存策略 ✅ │ +├─────────────────────┼─────────────────────┼─────────────────────────────┤ +│ • react-window │ • browser-worker.ts │ • IntelligenceCache │ +│ • 消息分页 │ • worker-pool.ts │ • TTL + LRU │ +│ • 惰性加载 │ • 安全执行隔离 │ • 命中率统计 │ +│ • 首屏 3x 提升 │ • XSS 防护 │ • 70% 响应提升 │ +└─────────────────────┴─────────────────────┴─────────────────────────────┘ +``` + +**Track C (Intelligence 缓存) 已在 Phase 2.5 完成** + +--- + +## Track A: Chat 虚拟滚动优化 + +### 目标 + +| 指标 | 当前 | 目标 | +|------|------|------| +| 首屏渲染 | ~2s | <500ms | +| 1000+ 消息滚动 | 卡顿 | 60fps | +| 内存占用 | 无限增长 | 限制 100 条 | + +### 文件结构 + +``` +desktop/src/ +├── components/ChatArea/ +│ ├── MessageList.tsx # 修改: 使用虚拟滚动 +│ ├── VirtualMessageList.tsx # 新建: react-window 封装 +│ └── MessageItem.tsx # 新建: 单条消息组件 +└── domains/chat/ + └── hooks.ts # 修改: 添加分页 hooks +``` + +### Task A.1: 安装 react-window + +**Files:** +- Modify: `desktop/package.json` + +- [ ] **Step 1: 安装依赖** + +```bash +cd g:/ZClaw_openfang/desktop && pnpm add react-window @types/react-window +``` + +- [ ] **Step 2: 验证安装** + +```bash +pnpm list react-window +``` + +--- + +### Task A.2: 创建 VirtualMessageList 组件 + +**Files:** +- Create: `desktop/src/components/ChatArea/VirtualMessageList.tsx` +- Create: `desktop/src/components/ChatArea/MessageItem.tsx` + +- [ ] **Step 1: 创建 MessageItem 组件** + +```typescript +// desktop/src/components/ChatArea/MessageItem.tsx +import { memo } from 'react'; +import type { Message } from '@/domains/chat'; + +interface MessageItemProps { + message: Message; + style?: React.CSSProperties; +} + +export const MessageItem = memo(function MessageItem({ message, style }: MessageItemProps) { + return ( +
+
{message.content}
+ {message.streaming && ...} +
+ ); +}); +``` + +- [ ] **Step 2: 创建 VirtualMessageList 组件** + +```typescript +// desktop/src/components/ChatArea/VirtualMessageList.tsx +import { useRef, useCallback, useEffect } from 'react'; +import { FixedSizeList as List } from 'react-window'; +import { MessageItem } from './MessageItem'; +import type { Message } from '@/domains/chat'; + +interface VirtualMessageListProps { + messages: Message[]; + height: number; + width: number; + onScrollToEnd?: () => void; +} + +export function VirtualMessageList({ + messages, + height, + width, + onScrollToEnd, +}: VirtualMessageListProps) { + const listRef = useRef(null); + + // Auto-scroll to bottom when new message arrives + useEffect(() => { + if (listRef.current && messages.length > 0) { + listRef.current.scrollToItem(messages.length - 1, 'end'); + } + }, [messages.length]); + + const Row = useCallback( + ({ index, style }: { index: number; style: React.CSSProperties }) => ( + + ), + [messages] + ); + + return ( + + {Row} + + ); +} +``` + +--- + +### Task A.3: 更新 MessageList 使用虚拟滚动 + +**Files:** +- Modify: `desktop/src/components/ChatArea/MessageList.tsx` + +- [ ] **Step 1: 替换实现** + +将现有的全量渲染替换为 VirtualMessageList。 + +--- + +### Task A.4: 添加消息分页支持 + +**Files:** +- Modify: `desktop/src/domains/chat/store.ts` +- Modify: `desktop/src/domains/chat/hooks.ts` + +- [ ] **Step 1: 添加分页状态** + +在 chatStore 中添加: +```typescript +pageSize: 50, +currentPage: 0, +hasMore: true, +loadMoreMessages: async () => { /* ... */ }, +``` + +--- + +## Track B: Hands Web Worker 隔离 + +### 目标 + +| 指标 | 当前 | 目标 | +|------|------|------| +| 执行安全性 | eval() XSS 风险 | 完全隔离 | +| 错误隔离 | 主线程崩溃 | Worker 隔离 | +| 超时控制 | 无 | 30s 强制终止 | + +### 文件结构 + +``` +desktop/src/ +├── workers/ +│ ├── browser-worker.ts # 新建: 浏览器执行 Worker +│ └── worker-pool.ts # 新建: Worker 池管理 +├── lib/ +│ └── browser-executor.ts # 修改: 使用 Worker +└── domains/hands/ + └── store.ts # 修改: 集成 Worker 执行 +``` + +### Task B.1: 创建 Browser Worker + +**Files:** +- Create: `desktop/src/workers/browser-worker.ts` + +- [ ] **Step 1: 创建 Worker 脚本** + +```typescript +// desktop/src/workers/browser-worker.ts +/// + +const ctx = self as DedicatedWorkerGlobalScope; + +interface ExecuteRequest { + type: 'execute'; + id: string; + script: string; + args: unknown[]; + timeout: number; +} + +interface ExecuteResponse { + type: 'result' | 'error'; + id: string; + result?: unknown; + error?: string; +} + +ctx.onmessage = async (e: MessageEvent) => { + const { type, id, script, args, timeout } = e.data; + + if (type !== 'execute') return; + + const timeoutId = setTimeout(() => { + ctx.postMessage({ + type: 'error', + id, + error: 'Execution timeout', + } as ExecuteResponse); + }, timeout); + + try { + // Safe execution without DOM access + const fn = new Function('args', script); + const result = await fn(args); + clearTimeout(timeoutId); + ctx.postMessage({ type: 'result', id, result } as ExecuteResponse); + } catch (err) { + clearTimeout(timeoutId); + ctx.postMessage({ + type: 'error', + id, + error: err instanceof Error ? err.message : String(err), + } as ExecuteResponse); + } +}; + +ctx.postMessage({ type: 'ready' }); +``` + +--- + +### Task B.2: 创建 Worker Pool + +**Files:** +- Create: `desktop/src/workers/worker-pool.ts` + +- [ ] **Step 1: 创建 Worker 池** + +```typescript +// desktop/src/workers/worker-pool.ts +export class BrowserWorkerPool { + private workers: Worker[] = []; + private available: Worker[] = []; + private maxWorkers: number; + + constructor(maxWorkers = 4) { + this.maxWorkers = maxWorkers; + } + + private createWorker(): Worker { + return new Worker( + new URL('./browser-worker.ts', import.meta.url), + { type: 'module' } + ); + } + + async execute(script: string, args: unknown[], timeout = 30000): Promise { + const worker = this.available.pop() || this.createWorker(); + + return new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => { + worker.terminate(); + reject(new Error('Execution timeout')); + }, timeout); + + const handler = (e: MessageEvent) => { + if (e.data.type === 'result') { + clearTimeout(timeoutId); + worker.removeEventListener('message', handler); + this.available.push(worker); + resolve(e.data.result); + } else if (e.data.type === 'error') { + clearTimeout(timeoutId); + worker.removeEventListener('message', handler); + this.available.push(worker); + reject(new Error(e.data.error)); + } + }; + + worker.addEventListener('message', handler); + worker.postMessage({ + type: 'execute', + id: Date.now().toString(), + script, + args, + timeout, + }); + }); + } + + terminateAll(): void { + this.workers.forEach(w => w.terminate()); + this.workers = []; + this.available = []; + } +} + +export const browserWorkerPool = new BrowserWorkerPool(); +``` + +--- + +### Task B.3: 集成到 Hands Domain + +**Files:** +- Modify: `desktop/src/domains/hands/store.ts` + +- [ ] **Step 1: 使用 Worker 执行** + +在 `triggerHand` action 中使用 `browserWorkerPool.execute()` 替代直接 `eval()`。 + +--- + +## Track C: Intelligence 缓存策略 ✅ 已完成 + +已在 Phase 2.5 实现: + +- `desktop/src/domains/intelligence/cache.ts` - LRU + TTL 缓存 +- `desktop/src/domains/intelligence/store.ts` - 带缓存的 Valtio store +- `desktop/src/domains/intelligence/hooks.ts` - React hooks + +--- + +## 验证清单 + +### Track A 验证 + +- [ ] 首屏渲染 < 500ms +- [ ] 1000+ 消息滚动 60fps +- [ ] 消息正确显示 +- [ ] 流式消息正常更新 + +### Track B 验证 + +- [ ] Worker 正常创建和销毁 +- [ ] 超时正确终止 +- [ ] 错误正确隔离 +- [ ] XSS 攻击被阻止 + +### 集成验证 + +- [ ] 所有 TypeScript 编译通过 +- [ ] 现有测试通过 +- [ ] E2E 核心流程正常 + +--- + +## 提交规范 + +``` +feat(chat): add virtual scrolling with react-window +feat(hands): add Web Worker isolation for browser execution +perf(intelligence): add LRU cache with TTL support +```