- Create docs/README.md as documentation index - Add WORK_SUMMARY_2026-03-16.md for today's work - Move test reports to docs/test-reports/ - Move completed plans to docs/archive/completed-plans/ - Move research reports to docs/archive/research-reports/ - Move technical reference to docs/knowledge-base/ - Move all plans from root plans/ to docs/plans/ New structure: docs/ ├── README.md # Documentation index ├── DEVELOPMENT.md # Development guide ├── OPENVIKING_INTEGRATION.md # OpenViking integration ├── USER_MANUAL.md # User manual ├── ZCLAW_AGENT_INTELLIGENCE_EVOLUTION.md ├── archive/ # Archived documents ├── knowledge-base/ # Technical knowledge ├── plans/ # Execution plans └── test-reports/ # Test reports Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
15 KiB
15 KiB
ZCLAW UI/UX 全面优化计划
背景
ZCLAW 是基于 OpenFang (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
: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 (新建)
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
import { forwardRef } from 'react';
import { motion } from 'framer-motion';
import { cn } from '../../lib/utils';
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
size?: 'sm' | 'md' | 'lg';
loading?: boolean;
}
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant = 'primary', size = 'md', loading, children, disabled, ...props }, ref) => {
return (
<motion.button
ref={ref}
whileTap={{ scale: 0.98 }}
className={cn(
'inline-flex items-center justify-center font-medium rounded-lg transition-colors',
'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2',
'disabled:opacity-50 disabled:cursor-not-allowed',
variants[variant],
sizes[size],
className
)}
disabled={disabled || loading}
{...props}
>
{loading && <Spinner className="mr-2" />}
{children}
</motion.button>
);
}
);
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
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 (
<Component
className={cn(
'rounded-xl border border-gray-200 bg-white p-4 shadow-sm',
'dark:border-gray-700 dark:bg-gray-800',
hoverable && 'cursor-pointer hover:shadow-md transition-shadow duration-200',
className
)}
{...(hoverable && {
whileHover: { y: -2 },
onClick,
})}
>
{children}
</Component>
);
}
2.3 cn() 工具函数
文件: desktop/src/lib/utils.ts
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 |
🤖 |
<BotIcon className="w-10 h-10 text-gray-400" /> |
ChatArea.tsx:279 |
🦞 (消息气泡) |
<ZClawLogo className="w-6 h-6" /> (自定义图标) |
App.tsx:79 |
🤖 |
<BotIcon /> |
App.tsx:98 |
👥 |
<UsersIcon /> |
Sidebar.tsx:98 |
用户头像 emoji | 首字母 + 渐变背景 |
Phase 3: 可访问性改进 (P0)
3.1 添加 aria-label
关键文件:
Sidebar.tsx- 设置按钮、tab 切换ChatArea.tsx- 发送按钮、附件按钮、模型选择器RightPanel.tsx- tab 切换、刷新按钮SettingsLayout.tsx- 返回按钮、菜单项
// 示例修复
<button
onClick={onOpenSettings}
aria-label="打开设置"
className="..."
>
<Settings className="w-4 h-4" />
</button>
3.2 焦点状态增强
/* 全局焦点样式 */
: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
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],
};
使用示例:
import { motion, AnimatePresence } from 'framer-motion';
import { pageVariants, defaultTransition } from '../lib/animations';
// 页面切换
<AnimatePresence mode="wait">
<motion.div
key={view}
variants={pageVariants}
initial="initial"
animate="animate"
exit="exit"
transition={defaultTransition}
>
{content}
</motion.div>
</AnimatePresence>
新文件: desktop/src/components/ui/Skeleton.tsx
export function Skeleton({ className }: { className?: string }) {
return (
<div
className={cn(
"animate-pulse bg-gray-200 dark:bg-gray-700 rounded",
className
)}
/>
);
}
export function CardSkeleton() {
return (
<div className="rounded-xl border border-gray-200 p-4">
<Skeleton className="h-4 w-24 mb-3" />
<Skeleton className="h-3 w-full mb-2" />
<Skeleton className="h-3 w-3/4" />
</div>
);
}
4.3 Toast 通知系统
新文件: desktop/src/components/ui/Toast.tsx
用于替代 alert() 和内联错误消息。
Phase 5: 视觉层次优化 (P1)
5.1 卡片阴影增强
- 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
interface EmptyStateProps {
icon: React.ReactNode;
title: string;
description: string;
action?: React.ReactNode;
}
export function EmptyState({ icon, title, description, action }: EmptyStateProps) {
return (
<div className="flex-1 flex items-center justify-center p-6">
<div className="text-center max-w-sm">
<div className="w-16 h-16 bg-gray-100 dark:bg-gray-800 rounded-full flex items-center justify-center mx-auto mb-4 text-gray-400">
{icon}
</div>
<h3 className="text-base font-semibold text-gray-700 dark:text-gray-300 mb-2">
{title}
</h3>
<p className="text-sm text-gray-500 dark:text-gray-400 mb-4">
{description}
</p>
{action}
</div>
</div>
);
}
6.2 错误状态设计
- 使用红色边框和背景
- 显示错误图标
- 提供重试按钮
6.3 加载状态改进
- 按钮加载时禁用并显示 spinner
- 列表加载时显示骨架屏
- 全局加载时显示顶部进度条
实施顺序
阶段 1: 基础设施 (2-3 天)
- 安装依赖:
framer-motion clsx tailwind-merge - 创建 CSS 变量系统 (含完整暗黑模式)
- 创建 tailwind.config.js
- 创建 utils.ts (cn 函数)
- 创建 animations.ts (Framer Motion 预设)
阶段 2: 基础组件库 (2-3 天)
- 创建 Button 组件 (含变体: primary, secondary, ghost)
- 创建 Card 组件 (含悬停动画)
- 创建 Input 组件 (含焦点状态)
- 创建 Badge 组件
- 创建 Skeleton 组件
- 创建 EmptyState 组件
- 创建 Toast 组件
阶段 3: 可访问性修复 (1-2 天)
- 添加 aria-label 到所有图标按钮
- 修复颜色对比度问题
- 增强焦点状态
- 添加键盘导航支持
阶段 4: 视觉优化 (2-3 天)
- 移除所有 emoji 图标,替换为 SVG
- 使用基础组件库重构现有组件
- 标准化间距
- 增强卡片阴影和悬停效果
- 完善暗黑模式支持
阶段 5: 交互增强 (2-3 天)
- 添加 Framer Motion 页面切换动画
- 添加列表项动画 (stagger)
- 添加按钮点击反馈
- 实现骨架屏加载
- 集成 Toast 通知系统
阶段 6: 组件重构 (2-3 天)
- 重构 Sidebar 组件 (使用新组件库 + 动画)
- 重构 ChatArea 组件
- 重构 RightPanel 组件
- 重构 SettingsLayout 组件
- 完整暗黑模式测试
关键文件清单
需要修改的文件
| 文件 | 修改内容 |
|---|---|
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 动画预设 |
需要安装的依赖
cd desktop
pnpm add framer-motion clsx tailwind-merge
验证方法
1. 视觉验证
pnpm tauri:dev
- 检查所有页面在浅色/深色模式下的显示
- 检查所有交互元素的悬停状态
- 检查所有空状态的显示
2. 可访问性验证
- 使用 Chrome DevTools Lighthouse 进行可访问性审计
- 使用 Tab 键进行键盘导航测试
- 使用屏幕阅读器测试 (NVDA/JAWS)
3. 类型检查
cd desktop && pnpm tsc --noEmit
4. 构建验证
pnpm build
预期成果
- 设计系统: 完整的 CSS 变量和 Tailwind 配置
- 组件库: 7 个可复用 UI 组件 (Button, Card, Input, Badge, Skeleton, EmptyState, Toast)
- 可访问性: Lighthouse 可访问性分数 > 90
- 暗黑模式: 所有组件完整支持暗黑模式
- 动画系统: Framer Motion 动画预设和页面过渡
- 视觉一致性: 所有组件遵循统一设计规范
- 无 Emoji: 所有 UI 图标使用 SVG
计划创建: 2026-03-15 预计完成: 11-17 个工作日