diff --git a/ChatHistorySessionListMigrationPlan.md b/ChatHistorySessionListMigrationPlan.md new file mode 100644 index 0000000..0b0337c --- /dev/null +++ b/ChatHistorySessionListMigrationPlan.md @@ -0,0 +1,223 @@ +# ChatHistory 会话列表重构迁移计划 + +## 参考来源 +- **源文件**: `ClawX/src/components/layout/Sidebar.tsx:280-335` +- **目标文件**: `zn-ai/src/pages/home/ChatHistory.vue` + +## 源实现思路分析 + +`Sidebar.tsx:280-335` 的历史会话列表核心设计: + +1. **时间分桶(Time Buckets)** + - 将会话按最后活动时间划分为:`today`、`yesterday`、`withinWeek`、`withinTwoWeeks`、`withinMonth`、`older`。 + - 通过 `getSessionBucket(activityMs, nowMs)` 函数计算每个会话所属的分组。 + - 会话先按时间降序排序,再归入对应 bucket。 + +2. **分组标签渲染** + - 每个 bucket 顶部显示一个 11px 的灰色小标签(如 "Today" / "Yesterday" / "Older")。 + - 空 bucket 直接跳过不渲染。 + +3. **会话项结构** + - 外层 `group relative flex items-center` 包裹。 + - 左侧显示 **agent 名称标签**(圆角小 pill),右侧显示 **会话标题**(`truncate` 截断)。 + - 当前选中的会话高亮:背景色 + 加粗文字。 + - hover 时背景变深。 + +4. **删除交互(悬停显隐)** + - 删除按钮默认 `opacity-0`,hover 整个会话项时通过 `group-hover:opacity-100` 显现。 + - 按钮绝对定位在右侧,避免挤压标题空间。 + +5. **点击行为** + - 点击整个会话行:切换会话 + 导航到聊天页。 + - 删除按钮单独拦截 `stopPropagation`。 + +--- + +## 迁移目标 + +将上述时间分桶、分组标签、悬停删除、紧凑列表布局迁移到 `ChatHistory.vue`,替换当前简单平铺的 `groups` 列表,同时保留 zn-ai 项目现有风格与功能。 + +--- + +## 实现步骤 + +### 1. 引入时间分桶工具函数 + +在 ` +``` + +### 4. 流式消息与 Indicator 支持 + +在 Store 的 WebSocket `onMessage` 回调中: + +1. **首次收到内容**:创建 `streamingMessage`(AI 角色、初始内容)。 +2. **后续收到增量**:`streamingMessage.messageContent += data.content`。 +3. **收到 finish**:将 `streamingMessage` 推入 `messages[]`,然后清空 `streamingMessage`,结束 `sending`。 +4. **异常/超时**:清空 `streamingMessage` 并设置 `error`。 + +**新增 `ChatTypingIndicator.vue`**(参考 ClawX `TypingIndicator`): +- 左侧 AI 头像,右侧三个跳动的圆点,用于 `sending && !streamingMessage` 时占位。 + +### 5. 生命周期与页面级整合(`home/index.vue`) + +在 `home/index.vue` 中补充 Store 生命周期绑定: + +```ts +import { useChatStore } from '@store/chat'; + +const chatStore = useChatStore(); + +onMounted(() => { + chatStore.initConnection(); +}); + +onBeforeUnmount(() => { + chatStore.resetSession(); +}); +``` + +> 当用户从首页切到其他页面时,调用 `resetSession()` 关闭 WebSocket、清理定时器,避免后台继续接收消息。 + +### 6. 历史消息加载与空会话清理 + +- **选择历史会话**:`ChatHistory.vue` 的 `@select-chat` 事件触发 `chatStore.loadHistory(conversationId)`。 +- **新建对话**:`@new-chat` 触发 `chatStore.createSession()`,然后清空 `messages`。 +- **切页清理**:Store 中实现 `cleanupEmptySession()`,如果当前会话没有任何消息且不是历史来源,则自动重置 `conversationId`,避免产生幽灵会话。 + +### 7. 样式与兼容性保持 + +- **颜色主题**:继续使用 zn-ai 现有的 `#2B7FFF` 蓝色高亮、`#E5E8EE` 边框色。 +- **头像布局**:左右头像顺序不变(AI 左、用户右)。 +- **输入框样式**:`ChatInput` 保留现有的圆角、阴影、发送按钮样式。 +- **引导页**:保留现有的 `TaskCenter` 和欢迎文案。 + +--- + +## 涉及文件 + +| 新建/修改 | 文件路径 | 说明 | +|---|---|---| +| 新建 | `zn-ai/src/store/chat.ts` | Pinia Chat Store,承载状态与 WebSocket 逻辑 | +| 新建 | `zn-ai/src/pages/home/components/chat/ChatMessage.vue` | 单条消息渲染(聚合现有原子组件) | +| 新建 | `zn-ai/src/pages/home/components/chat/ChatEmpty.vue` | 空会话欢迎页 | +| 新建 | `zn-ai/src/pages/home/components/chat/ChatErrorBar.vue` | 底部全局错误提示条 | +| 新建 | `zn-ai/src/pages/home/components/chat/ChatTypingIndicator.vue` | 发送中跳动圆点 | +| 升级 | `zn-ai/src/pages/home/components/ChatInputArea.vue` | 增加 `sending` 态与 `stop` 事件 | +| 重构 | `zn-ai/src/pages/home/ChatBox.vue` | 大幅瘦身,仅负责拼装子组件 | +| 调整 | `zn-ai/src/pages/home/index.vue` | 引入 Store 生命周期、事件透传 | + +--- + +## 验收标准 + +- [ ] `ChatBox.vue` 代码量从 800+ 行降至 150 行以内,不再包含 WebSocket 细节。 +- [ ] 新建 `chat.ts` Pinia Store 能正常初始化 WebSocket、发送消息、接收回复。 +- [ ] 用户发送消息后,输入框清空,列表底部出现 `ChatTypingIndicator`;收到首包内容后切换为 `ChatMessage` 流式渲染。 +- [ ] 收到完成标识后,流式消息固化到消息列表,状态恢复正常。 +- [ ] 发生超时或错误时,底部出现 `ChatErrorBar`,可点击清除。 +- [ ] 切换历史会话时,通过 Store 加载历史消息并正确渲染。 +- [ ] 新建对话时,列表回到空状态(`ChatEmpty`)。 +- [ ] 离开首页时 WebSocket 被正确关闭,无后台消息泄漏。 diff --git a/SidebarToggleMigrationPlan.md b/SidebarToggleMigrationPlan.md new file mode 100644 index 0000000..75854f0 --- /dev/null +++ b/SidebarToggleMigrationPlan.md @@ -0,0 +1,126 @@ +# ChatHistory 侧边栏折叠功能迁移计划 + +## 参考来源 +- **源文件**: `ClawX/src/components/layout/Sidebar.tsx:234-247` +- **目标文件**: `zn-ai/src/pages/home/ChatHistory.vue:6` + +## 源实现思路分析 + +`Sidebar.tsx:234-247` 的核心实现逻辑: + +1. **状态驱动**: 通过 `useSettingsStore` 获取 `sidebarCollapsed` 布尔值与 `setSidebarCollapsed` setter。 +2. **图标切换**: 点击按钮时,根据当前状态切换图标: + - 展开状态 → 显示 `PanelLeftClose`(提示可收起) + - 收起状态 → 显示 `PanelLeft`(提示可展开) +3. **样式过渡**: 外层 `aside` 通过 `transition-all duration-300` 配合条件类名 `w-16` / `w-64` 实现宽度动画。 +4. **内容显隐**: 内部文字/列表在收起时隐藏(`!sidebarCollapsed && ...`),只保留图标按钮可点。 + +## 迁移目标 + +将上述折叠/展开交互迁移到 `ChatHistory.vue`,使其第 6 行的 `RiSideBarLine` 图标具备切换侧边栏宽度的能力,并保持与现有 `zn-ai` 项目风格一致。 + +## 实现步骤 + +### 1. 状态定义(ChatHistory.vue 本地状态) + +在 `