# ZCLAW UI/UX 全面优化计划 ## 背景 ZCLAW 是基于 ZCLAW (Rust Agent OS) 的 AI Agent 桌面客户端。经过 Phase 1-8 的功能开发,系统已达到 93% API 覆盖率和 100% UI 组件完成度。现在需要对前端设计元素进行全面优化,提升用户操作效率与视觉体验。 ## 当前状态分析 ### 代码结构 - **框架**: React 18 + TypeScript + Tauri - **样式**: Tailwind CSS v4.2.1 - **状态管理**: Zustand (gatewayStore, chatStore, teamStore) - **图标**: lucide-react - **组件数量**: 37 个组件 ### 现有设计特点 - **主色调**: Orange (#f97316) 作为品牌色 - **布局**: 三栏式 (Sidebar w-64 | Main flex-1 | RightPanel w-80) - **暗黑模式**: 部分支持 (部分组件有 `dark:` 变体) - **动画**: 基础 CSS 动画 (animate-pulse, animate-spin) --- ## 发现的问题 ### 1. 设计系统问题 (CRITICAL) | 问题 | 位置 | 影响 | |------|------|------| | 无 UI 组件库 | 全局 | 一致性差,重复代码 | | 颜色系统不完整 | index.css | 品牌识别度低 | | 暗黑模式不完整 | 多个组件 | 用户体验不一致 | | **使用 Emoji 作为 UI 图标** | ChatArea, App.tsx | 不专业,违反设计规范 | ### 2. 可访问性问题 (CRITICAL) | 问题 | 位置 | 影响 | |------|------|------| | 缺少 aria-label | 图标按钮 | 屏幕阅读器无法识别 | | 焦点状态不明显 | 多个交互元素 | 键盘导航困难 | | 颜色对比度不足 | text-gray-400 类 | 可读性差 | ### 3. 交互与动画 (HIGH) | 问题 | 位置 | 影响 | |------|------|------| | 无过渡动画库 | 全局 | 交互生硬 | | 悬停状态不一致 | 多个组件 | 用户困惑 | | 缺少骨架屏 | 数据加载 | 感知性能差 | ### 4. 视觉层次 (MEDIUM) | 问题 | 位置 | 影响 | |------|------|------| | 卡片阴影过轻 | shadow-sm | 层次不明显 | | 间距不一致 | padding/margin | 视觉混乱 | | 字体过小 | text-xs | 可读性差 | ### 5. 用户体验 (MEDIUM) | 问题 | 位置 | 影响 | |------|------|------| | 空状态设计简陋 | 多个列表 | 缺乏引导 | | 错误提示不明显 | 表单等 | 用户困惑 | | 加载反馈不足 | API 调用 | 用户焦虑 | --- ## 用户确认的优化方向 | 决策项 | 选择 | |--------|------| | 动画库 | ✅ 引入 Framer Motion | | 暗黑模式 | ✅ 完整支持所有组件 | | 组件库 | ✅ 创建 Button, Card, Input 等基础组件 | --- ## 优化方案 ### Phase 1: 设计系统建立 (P0) #### 1.1 CSS 变量系统 **文件**: `desktop/src/index.css` ```css :root { /* Brand Colors */ --color-primary: #f97316; --color-primary-hover: #ea580c; --color-primary-light: #fff7ed; /* Semantic Colors */ --color-success: #22c55e; --color-warning: #eab308; --color-error: #ef4444; --color-info: #3b82f6; /* Neutral Colors */ --color-bg: #ffffff; --color-bg-secondary: #f9fafb; --color-border: #e5e7eb; --color-text: #111827; --color-text-secondary: #6b7280; --color-text-muted: #9ca3af; /* Spacing Scale */ --space-xs: 4px; --space-sm: 8px; --space-md: 16px; --space-lg: 24px; --space-xl: 32px; /* Border Radius */ --radius-sm: 6px; --radius-md: 8px; --radius-lg: 12px; --radius-xl: 16px; --radius-full: 9999px; /* Shadows */ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1); --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1); /* Transitions */ --transition-fast: 150ms ease; --transition-normal: 200ms ease; --transition-slow: 300ms ease; } .dark { --color-bg: #0f172a; --color-bg-secondary: #1e293b; --color-border: #334155; --color-text: #f1f5f9; --color-text-secondary: #94a3b8; --color-text-muted: #64748b; } ``` #### 1.2 统一 Tailwind 配置 **文件**: `desktop/tailwind.config.js` (新建) ```javascript export default { theme: { extend: { colors: { primary: { DEFAULT: 'var(--color-primary)', hover: 'var(--color-primary-hover)', light: 'var(--color-primary-light)', }, }, boxShadow: { 'card': 'var(--shadow-md)', 'hover': 'var(--shadow-lg)', }, transitionDuration: { 'fast': '150ms', 'normal': '200ms', }, }, }, } ``` ### Phase 2: 基础组件库 (P0) #### 2.1 Button 组件 **文件**: `desktop/src/components/ui/Button.tsx` ```tsx import { forwardRef } from 'react'; import { motion } from 'framer-motion'; import { cn } from '../../lib/utils'; interface ButtonProps extends React.ButtonHTMLAttributes { variant?: 'primary' | 'secondary' | 'ghost' | 'danger'; size?: 'sm' | 'md' | 'lg'; loading?: boolean; } export const Button = forwardRef( ({ className, variant = 'primary', size = 'md', loading, children, disabled, ...props }, ref) => { return ( {loading && } {children} ); } ); const variants = { primary: 'bg-primary text-white hover:bg-primary-hover', secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200', ghost: 'text-gray-600 hover:bg-gray-100', danger: 'bg-red-500 text-white hover:bg-red-600', }; const sizes = { sm: 'px-3 py-1.5 text-xs', md: 'px-4 py-2 text-sm', lg: 'px-6 py-3 text-base', }; ``` #### 2.2 Card 组件 **文件**: `desktop/src/components/ui/Card.tsx` ```tsx import { motion } from 'framer-motion'; import { cn } from '../../lib/utils'; interface CardProps { children: React.ReactNode; className?: string; hoverable?: boolean; onClick?: () => void; } export function Card({ children, className, hoverable, onClick }: CardProps) { const Component = hoverable ? motion.div : 'div'; return ( {children} ); } ``` #### 2.3 cn() 工具函数 **文件**: `desktop/src/lib/utils.ts` ```typescript import { clsx, type ClassValue } from 'clsx'; import { twMerge } from 'tailwind-merge'; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } ``` **原则**: 使用 SVG 图标 (lucide-react),不使用 emoji 作为 UI 元素 | 文件 | 当前 | 修改为 | |------|------|--------| | `ChatArea.tsx:79` | `🤖` | `` | | `ChatArea.tsx:279` | `🦞` (消息气泡) | `` (自定义图标) | | `App.tsx:79` | `🤖` | `` | | `App.tsx:98` | `👥` | `` | | `Sidebar.tsx:98` | 用户头像 emoji | 首字母 + 渐变背景 | ### Phase 3: 可访问性改进 (P0) #### 3.1 添加 aria-label **关键文件**: - `Sidebar.tsx` - 设置按钮、tab 切换 - `ChatArea.tsx` - 发送按钮、附件按钮、模型选择器 - `RightPanel.tsx` - tab 切换、刷新按钮 - `SettingsLayout.tsx` - 返回按钮、菜单项 ```tsx // 示例修复 ``` #### 3.2 焦点状态增强 ```css /* 全局焦点样式 */ :focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; } /* 按钮焦点 */ .btn:focus-visible { ring: 2px; ring-color: var(--color-primary); ring-offset: 2px; } ``` #### 3.3 颜色对比度修复 | 当前 | 对比度 | 修复 | |------|--------|------| | `text-gray-400` on white | 3.1:1 (FAIL) | 改为 `text-gray-500` (#6b7280) 4.6:1 | | `text-gray-300` on white | 2.9:1 (FAIL) | 改为 `text-gray-600` (#4b5563) 7.0:1 | ### Phase 4: 交互与动画增强 (P1) #### 4.1 Framer Motion 动画系统 **新文件**: `desktop/src/lib/animations.ts` ```typescript import { Variants } from 'framer-motion'; // 页面切换动画 export const pageVariants: Variants = { initial: { opacity: 0, y: 10 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -10 }, }; // 列表项交错动画 export const listItemVariants: Variants = { hidden: { opacity: 0, x: -10 }, visible: { opacity: 1, x: 0 }, }; // 按钮点击动画 export const buttonTap = { scale: 0.98 }; // 卡片悬停动画 export const cardHover = { y: -2, boxShadow: '0 10px 25px -5px rgb(0 0 0 / 0.1)' }; // 默认过渡配置 export const defaultTransition = { duration: 0.2, ease: [0.4, 0, 0.2, 1], }; ``` **使用示例**: ```tsx import { motion, AnimatePresence } from 'framer-motion'; import { pageVariants, defaultTransition } from '../lib/animations'; // 页面切换 {content} ``` **新文件**: `desktop/src/components/ui/Skeleton.tsx` ```tsx export function Skeleton({ className }: { className?: string }) { return (
); } export function CardSkeleton() { return (
); } ``` #### 4.3 Toast 通知系统 **新文件**: `desktop/src/components/ui/Toast.tsx` 用于替代 `alert()` 和内联错误消息。 ### Phase 5: 视觉层次优化 (P1) #### 5.1 卡片阴影增强 ```diff - className="rounded-xl border border-gray-200 bg-white p-4 shadow-sm" + className="rounded-xl border border-gray-200 bg-white p-4 shadow-sm hover:shadow-md transition-shadow duration-200" ``` #### 5.2 间距标准化 | 区域 | 当前 | 标准 | |------|------|------| | 页面内边距 | 不一致 | `p-6` (24px) | | 卡片内边距 | `p-3`/`p-4` | `p-4` (16px) | | 列表项间距 | `space-y-1`/`space-y-2` | `space-y-2` (8px) | | 区域间距 | `space-y-3`/`space-y-4` | `space-y-4` (16px) | #### 5.3 字体大小优化 | 类型 | 当前 | 建议 | |------|------|------| | 正文 | `text-xs` (12px) | `text-sm` (14px) | | 小标签 | `text-[10px]` | `text-xs` (12px) | | 标题 | `text-lg` | 保持 | ### Phase 6: 用户体验改进 (P2) #### 6.1 空状态设计 **新组件**: `EmptyState.tsx` ```tsx interface EmptyStateProps { icon: React.ReactNode; title: string; description: string; action?: React.ReactNode; } export function EmptyState({ icon, title, description, action }: EmptyStateProps) { return (
{icon}

{title}

{description}

{action}
); } ``` #### 6.2 错误状态设计 - 使用红色边框和背景 - 显示错误图标 - 提供重试按钮 #### 6.3 加载状态改进 - 按钮加载时禁用并显示 spinner - 列表加载时显示骨架屏 - 全局加载时显示顶部进度条 --- ## 实施顺序 ### 阶段 1: 基础设施 (2-3 天) 1. 安装依赖: `framer-motion clsx tailwind-merge` 2. 创建 CSS 变量系统 (含完整暗黑模式) 3. 创建 tailwind.config.js 4. 创建 utils.ts (cn 函数) 5. 创建 animations.ts (Framer Motion 预设) ### 阶段 2: 基础组件库 (2-3 天) 1. 创建 Button 组件 (含变体: primary, secondary, ghost) 2. 创建 Card 组件 (含悬停动画) 3. 创建 Input 组件 (含焦点状态) 4. 创建 Badge 组件 5. 创建 Skeleton 组件 6. 创建 EmptyState 组件 7. 创建 Toast 组件 ### 阶段 3: 可访问性修复 (1-2 天) 1. 添加 aria-label 到所有图标按钮 2. 修复颜色对比度问题 3. 增强焦点状态 4. 添加键盘导航支持 ### 阶段 4: 视觉优化 (2-3 天) 1. 移除所有 emoji 图标,替换为 SVG 2. 使用基础组件库重构现有组件 3. 标准化间距 4. 增强卡片阴影和悬停效果 5. 完善暗黑模式支持 ### 阶段 5: 交互增强 (2-3 天) 1. 添加 Framer Motion 页面切换动画 2. 添加列表项动画 (stagger) 3. 添加按钮点击反馈 4. 实现骨架屏加载 5. 集成 Toast 通知系统 ### 阶段 6: 组件重构 (2-3 天) 1. 重构 Sidebar 组件 (使用新组件库 + 动画) 2. 重构 ChatArea 组件 3. 重构 RightPanel 组件 4. 重构 SettingsLayout 组件 5. 完整暗黑模式测试 --- ## 关键文件清单 ### 需要修改的文件 | 文件 | 修改内容 | |------|----------| | `desktop/src/index.css` | 添加 CSS 变量系统 | | `desktop/src/components/Sidebar.tsx` | aria-label, 动画, emoji 移除 | | `desktop/src/components/ChatArea.tsx` | aria-label, emoji 移除, 空状态 | | `desktop/src/components/RightPanel.tsx` | aria-label, 间距标准化 | | `desktop/src/components/Settings/SettingsLayout.tsx` | aria-label, 间距标准化 | | `desktop/src/App.tsx` | emoji 移除, 空状态组件 | ### 需要新建的文件 | 文件 | 用途 | |------|------| | `desktop/tailwind.config.js` | Tailwind 自定义配置 | | `desktop/src/components/ui/Button.tsx` | 按钮组件 | | `desktop/src/components/ui/Card.tsx` | 卡片组件 | | `desktop/src/components/ui/Input.tsx` | 输入框组件 | | `desktop/src/components/ui/Badge.tsx` | 标签组件 | | `desktop/src/components/ui/Skeleton.tsx` | 骨架屏组件 | | `desktop/src/components/ui/EmptyState.tsx` | 空状态组件 | | `desktop/src/components/ui/Toast.tsx` | Toast 通知组件 | | `desktop/src/lib/utils.ts` | cn() 工具函数 | | `desktop/src/lib/animations.ts` | Framer Motion 动画预设 | ### 需要安装的依赖 ```bash cd desktop pnpm add framer-motion clsx tailwind-merge ``` --- ## 验证方法 ### 1. 视觉验证 ```bash pnpm tauri:dev ``` - 检查所有页面在浅色/深色模式下的显示 - 检查所有交互元素的悬停状态 - 检查所有空状态的显示 ### 2. 可访问性验证 - 使用 Chrome DevTools Lighthouse 进行可访问性审计 - 使用 Tab 键进行键盘导航测试 - 使用屏幕阅读器测试 (NVDA/JAWS) ### 3. 类型检查 ```bash cd desktop && pnpm tsc --noEmit ``` ### 4. 构建验证 ```bash pnpm build ``` --- ## 预期成果 1. **设计系统**: 完整的 CSS 变量和 Tailwind 配置 2. **组件库**: 7 个可复用 UI 组件 (Button, Card, Input, Badge, Skeleton, EmptyState, Toast) 3. **可访问性**: Lighthouse 可访问性分数 > 90 4. **暗黑模式**: 所有组件完整支持暗黑模式 5. **动画系统**: Framer Motion 动画预设和页面过渡 6. **视觉一致性**: 所有组件遵循统一设计规范 7. **无 Emoji**: 所有 UI 图标使用 SVG --- *计划创建: 2026-03-15* *预计完成: 11-17 个工作日*