Files
TrueGrowth/specs/003-chat-drawer/research.md

5.0 KiB
Raw Blame History

Research: 对话抽屉 (Chat Drawer)

Date: 2025-12-03 Feature: 001-chat-drawer

技术决策

1. 抽屉组件实现方案

Decision: 使用 CSS Transform + Transition 实现抽屉动画

Rationale:

  • CSS Transform 利用 GPU 加速,性能最优
  • 现有项目已有类似动画模式unified-toolbar 的响应式切换)
  • 简单直接,易于维护

Alternatives Considered:

  • Framer Motion功能强大但增加依赖体积
  • React Spring学习曲线较高
  • TDesign Drawer与画板布局集成复杂

2. 流式响应实现方案

Decision: 使用 Fetch API + ReadableStream 处理 SSE (Server-Sent Events)

Rationale:

  • 原生支持,无需额外依赖
  • 与现有 Gemini API 集成模式一致
  • 可以逐字符/逐词渲染,提供最佳用户体验

Alternatives Considered:

  • WebSocket需要服务端支持过于复杂
  • 轮询:用户体验差,延迟高
  • 第三方库(如 openai-streams增加依赖

3. 对话存储方案

Decision: IndexedDB via localforage独立 store

Rationale:

  • 项目已使用 localforagetask-queue 模块)
  • IndexedDB 支持大容量存储50MB+
  • 异步 API 不阻塞主线程
  • 支持复杂查询和索引

Alternatives Considered:

  • localStorage5MB 限制,同步 API
  • 纯 IndexedDBAPI 复杂localforage 封装更友好
  • 云端存储:需要后端支持,超出当前范围

4. 会话状态管理方案

Decision: React Context + useReducer + RxJS Subject

Rationale:

  • 与现有 DrawnixContext 模式一致
  • RxJS 用于跨组件事件通信(参考 task-queue-service
  • useReducer 管理复杂状态转换

Alternatives Considered:

  • Redux/Zustand增加依赖当前规模不需要
  • 纯 Context复杂状态难以管理
  • Jotai/Recoil团队不熟悉

5. 附件处理方案

Decision: FileReader + Base64 编码存储小文件,大文件使用 Blob URL

Rationale:

  • 小文件(<5MBBase64 存储便于 IndexedDB 持久化
  • 大文件 Blob URL 避免内存问题
  • 与现有 media-cache-service 模式一致

Alternatives Considered:

  • 全部 Base64大文件性能差
  • 全部 Blob刷新后丢失
  • 云存储:需要后端

6. 响应式布局方案

Decision: CSS Media Query + ResizeObserver + isMobile 状态

Rationale:

  • 与现有 unified-toolbar 响应式模式一致
  • 结合 CSS 和 JS 实现精确控制
  • 复用现有 isMobile 检测逻辑

Alternatives Considered:

  • 纯 CSS复杂状态难以处理
  • 纯 JS性能开销大

7. AI API 集成方案

Decision: 复用现有 gemini-api 工具,扩展支持聊天接口

Rationale:

  • API Key 管理已在 settings-manager 中实现
  • 减少重复代码
  • 统一的错误处理和重试逻辑

Alternatives Considered:

  • 新建独立 API 模块:重复工作
  • 使用第三方 SDK增加依赖

最佳实践

React 组件结构

// 组件拆分策略 - 确保每个文件 <500 行
// ChatDrawer.tsx: 主容器,状态管理 (~200行)
// MessageList.tsx: 消息列表渲染 (~150行)
// MessageItem.tsx: 单条消息 (~100行)
// MessageInput.tsx: 输入框 (~150行)
// SessionList.tsx: 会话列表 (~100行)
// SessionItem.tsx: 单个会话 (~80行)
// AttachmentPreview.tsx: 附件预览 (~100行)
// StreamingText.tsx: 流式文本 (~80行)

性能优化

// 1. 消息列表虚拟化(长列表场景)
// 使用 react-window 或自定义实现

// 2. 消息组件记忆化
const MessageItem = React.memo(({ message }) => {...});

// 3. 存储操作防抖
const debouncedSave = useMemo(
  () => debounce(saveToStorage, 500),
  []
);

// 4. 流式渲染优化
// 使用 requestAnimationFrame 批量更新

错误处理

// 统一错误边界
class ChatErrorBoundary extends React.Component {
  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }

  render() {
    if (this.state.hasError) {
      return <ChatErrorFallback error={this.state.error} />;
    }
    return this.props.children;
  }
}

// API 错误处理
try {
  await sendMessage(content);
} catch (error) {
  if (error.name === 'AbortError') {
    // 用户取消
  } else if (error.status === 429) {
    // 限流
  } else {
    // 通用错误
  }
}

技术风险

Risk Impact Mitigation
AI API 响应慢 用户体验差 流式显示 + 加载状态 + 超时处理
IndexedDB 存储满 数据丢失 存储限额监控 + 自动清理旧数据
长对话性能 卡顿 虚拟滚动 + 消息分页
移动端键盘遮挡 输入困难 监听 visualViewport + 自动滚动
网络断开 消息发送失败 离线检测 + 队列重试

参考实现

  • 现有 Dialog 系统: packages/drawnix/src/components/dialog/dialog.tsx
  • Task Queue UI: packages/drawnix/src/components/task-queue/
  • 存储服务: packages/drawnix/src/services/storage-service.ts
  • Settings Manager: packages/drawnix/src/utils/settings-manager.ts
  • Gemini API: packages/drawnix/src/utils/gemini-api/