Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/COMPONENT_PATTERNS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const emit = defineEmits<{...}>()
- `CreateTaskModal.vue` — 表单弹窗组件:收集 name / repoUrl / tokenBudget 创建 Agent,Vue `<Transition>` 原生过渡动画
- `SwarmConfirmModal.vue` — 确认弹窗组件:类型化图标 + 双按钮确认/取消,配合 useConfirm composable
- `SwarmToast.vue` — Toast 通知组件:类型化图标 + 进度条 + 自动消失,配合 useToast composable
- `AgentDetail.vue` — 聊天式多轮对话组件:Header + Info + PR 审阅 + 文件变更 + 聊天消息区(input/output/think 支持 **Markdown 渲染 + 代码语法高亮**,基于 `marked` + `highlight.js` + `dompurify` XSS 过滤;tool_call/mcp/tool_result 为可折叠气泡保持等宽纯文本,默认收起,点击展开查看完整内容)+ `<textarea>` 输入框(Enter 发送 / Shift+Enter 换行,自动增高)。Info 区任务指令从 `logs` 倒序查找最近 `input` 类型条目渲染,不再依赖 `agent.instruction`;用 `line-clamp-2` 限高最多 2 行(末尾自动 `…`),完整原文由 `:title` tooltip 展示,避免发长消息时把 Info 区撑大挤掉下方聊天面板。日期字段通过 new Date(string).toLocaleTimeString() 显示。滚动行为:通过 `scroll` 事件监听跟踪用户是否在底部 50px 范围内;当新消息到达或内容流式追加时,若用户之前处于底部则即时滚至底部,若用户已主动上滚则保持当前浏览位置。可折叠消息展开/收起后同样会跟进滚动。**输入框草稿缓存**:通过 `useSwarmStore` 的 `draftInputs` 在组件挂载时恢复、卸载时保存、切换 Agent 时自动迁移草稿内容,避免切视图丢失未发送的输入。所有消息气泡均带 `animate-message-enter` 进入动画(fade-in + slide-up)。PR 审阅区「打回」按钮带 tooltip 说明语义——清空审阅状态、切回就绪态等待用户发新指令,**不会自动让 Agent 修改**(避免历史文案「Agent 继续修改」的误导)。Reviewer 行额外支持 `failed` 状态:自动审阅多次失败(kimi 启动失败 / 卡死等)后用 amber AlertCircle 图标 + 「失败 (×N)」标签 + 截断展示 failureReason,鼠标悬停看完整原因;语义跟「已拒绝」不同——审阅没跑通而非内容被拒,需要人工处置
- `AgentDetail.vue` — 聊天式多轮对话组件:Header + Info + PR 审阅 + 文件变更 + 聊天消息区(input/output/think 支持 **Markdown 渲染 + 代码语法高亮**,基于 `marked` + `highlight.js` + `dompurify` XSS 过滤;tool_call/mcp/tool_result 为可折叠气泡保持等宽纯文本,默认收起,点击展开查看完整内容)+ `<textarea>` 输入框(Enter 发送 / Shift+Enter 换行,自动增高)。Info 区任务指令从 `logs` 倒序查找最近 `input` 类型条目渲染,不再依赖 `agent.instruction`;用 `line-clamp-2` 限高最多 2 行(末尾自动 `…`),完整原文由 `:title` tooltip 展示,避免发长消息时把 Info 区撑大挤掉下方聊天面板。日期字段通过 new Date(string).toLocaleTimeString() 显示。滚动行为:通过 `scroll` 事件监听跟踪用户是否在底部 50px 范围内;当新消息到达或内容流式追加时,若用户之前处于底部则即时滚至底部,若用户已主动上滚则保持当前浏览位置。可折叠消息展开/收起后同样会跟进滚动。**滚动到底部按钮**:当用户向上滚动离开底部 50px 以上时,聊天区右下角显示浮动下箭头按钮(白色圆形 + 阴影 + 淡入淡出动画),点击后平滑滚动至最新消息;处于底部或空对话时自动隐藏。**输入框草稿缓存**:通过 `useSwarmStore` 的 `draftInputs` 在组件挂载时恢复、卸载时保存、切换 Agent 时自动迁移草稿内容,避免切视图丢失未发送的输入。所有消息气泡均带 `animate-message-enter` 进入动画(fade-in + slide-up)。PR 审阅区「打回」按钮带 tooltip 说明语义——清空审阅状态、切回就绪态等待用户发新指令,**不会自动让 Agent 修改**(避免历史文案「Agent 继续修改」的误导)。Reviewer 行额外支持 `failed` 状态:自动审阅多次失败(kimi 启动失败 / 卡死等)后用 amber AlertCircle 图标 + 「失败 (×N)」标签 + 截断展示 failureReason,鼠标悬停看完整原因;语义跟「已拒绝」不同——审阅没跑通而非内容被拒,需要人工处置
- `TaskCard.vue` — 卡片组件:Agent 状态 + Token 进度 + 审阅徽章;状态枚举含 `orphan`(已失效,表示本地缓存中存在但引擎 restore 列表中无对应 ID 的 agent);启动/停止/重启/删除按钮均按 `engineReady` prop 禁用,引擎 restore 完成前置灰;删除操作通过 `useConfirm()` 调用自定义确认弹窗
- `AnalyticsPanel.vue` — 数据展示型组件:状态分布、Token 排行、任务列表;列表项通过 `getLastInput(task.logs)` 展示最后一条 input 指令摘要,与 `AgentDetail.vue` 的 Info 区指令展示逻辑一致
- `SettingsPanel.vue` — 纯信息展示型设置面板,使用 lucide 图标 + code 标签展示命令指引
Expand Down
2 changes: 1 addition & 1 deletion docs/FRONTEND.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Vue 3 + TypeScript + Vite + Tailwind CSS + lucide-vue-next + Tauri v2 + @tauri-a
- `components/SwarmConfirmModal.vue` — 确认弹窗组件:支持 danger/warning/info 类型,配合 useConfirm 使用
- `components/SwarmToast.vue` — Toast 通知组件:支持 error/success/info/warning,自动消失
- `App.vue` — 主入口:布局框架 + 视图路由(dashboard/agent-detail/analytics)+ 全局事件处理(如文件 diff 查看)
- `components/AgentDetail.vue` — Agent 详情:`<textarea>` 指令输入(Enter 发送 / Shift+Enter 换行,自动增高)+ 日志流(含 think / tool_call / mcp / tool_result 可折叠气泡,默认收起,点击展开查看完整内容,智能滚动:通过 `scroll` 事件跟踪用户位置,若用户之前在底部 50px 内则即时跟进,已上滚则保持阅读位置)+ PR 审阅 + 文件变更(点击通过 engine 获取 diff)。**聊天消息气泡(input / output / think)支持 Markdown 渲染 + 代码语法高亮**,基于 `marked` + `highlight.js` + `dompurify`(XSS 过滤)。输入框草稿通过 `useSwarmStore.draftInputs` 做内存级缓存,切视图/切 Agent 时不丢失未发送内容。所有消息气泡带 `animate-message-enter` 进入动画。「打回」按钮 tooltip 说明:只切回就绪态、不自动触发修改。Reviewer 行支持 `failed` 状态展示(amber 配色 + 失败原因)。Info 区「任务指令」用 `line-clamp-2` 限高,长消息不会撑大挤掉聊天面板
- `components/AgentDetail.vue` — Agent 详情:`<textarea>` 指令输入(Enter 发送 / Shift+Enter 换行,自动增高)+ 日志流(含 think / tool_call / mcp / tool_result 可折叠气泡,默认收起,点击展开查看完整内容,智能滚动:通过 `scroll` 事件跟踪用户位置,若用户之前在底部 50px 内则即时跟进,已上滚则保持阅读位置)+ PR 审阅 + 文件变更(点击通过 engine 获取 diff)。**聊天消息气泡(input / output / think)支持 Markdown 渲染 + 代码语法高亮**,基于 `marked` + `highlight.js` + `dompurify`(XSS 过滤)。**滚动到底部按钮**:用户上滚离开底部后,聊天区右下角显示浮动下箭头按钮(白色圆形 + 阴影 + 淡入淡出动画),点击平滑滚动至底部;空对话或已处于底部时自动隐藏。输入框草稿通过 `useSwarmStore.draftInputs` 做内存级缓存,切视图/切 Agent 时不丢失未发送内容。所有消息气泡带 `animate-message-enter` 进入动画。「打回」按钮 tooltip 说明:只切回就绪态、不自动触发修改。Reviewer 行支持 `failed` 状态展示(amber 配色 + 失败原因)。Info 区「任务指令」用 `line-clamp-2` 限高,长消息不会撑大挤掉聊天面板
- `composables/useConfirm.ts` — 全局确认弹窗状态管理(命令式 API)
- `composables/useToast.ts` — 全局 Toast 通知状态管理(命令式 API)
- `components/SettingsPanel.vue` — 系统设置(GitHub Token + Kimi CLI 安装指引)
Expand Down
4 changes: 2 additions & 2 deletions docs/STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
| Agent Engine 进程管理 | ✅ | Rust 后台 spawn Node.js Agent Engine,stdin/stdout 管道通信;生产环境用预编译 `dist/index.js`,开发环境 fallback 到 tsx;Windows 主动探测 `node.exe` 路径(nvm-windows 兼容) | `src-tauri/src/lib.rs`, `agent-engine/src/index.ts` |
| Kimi CLI 接入 | ✅ | `sendInstruction` 调用 `kimi --print --quiet`,实时 stdout 流式捕获,可取消 | `src/store/useSwarmStore.ts` |
| Token 预算控制 | ✅ | sendInstruction 前检查预算;process-output 中按输出行长度估算并累加;耗尽时自动 kill 进程 | `src/store/useSwarmStore.ts` |
| Agent 多轮对话交互 | ✅ | 聊天式气泡 UI,支持 input/output/system/error 消息类型;ready/stopped/completed 状态下可持续对话;working 状态显示执行中指示器;**日志已分流**(system/error 技术日志带组件前缀+颜色走终端 stderr,input/output 及关键状态变更进 UI);**stop-agent 已修复**(前端乐观更新 + await IPC) | `src/components/AgentDetail.vue`, `src/store/useSwarmStore.ts`, `agent-engine/src/agent.ts` |
| Agent 多轮对话交互 | ✅ | 聊天式气泡 UI,支持 input/output/system/error 消息类型;ready/stopped/completed 状态下可持续对话;working 状态显示执行中指示器;**日志已分流**(system/error 技术日志带组件前缀+颜色走终端 stderr,input/output 及关键状态变更进 UI);**stop-agent 已修复**(前端乐观更新 + await IPC);**滚动到底部按钮**(用户上滚后显示浮动下箭头,点击平滑滚回底部) | `src/components/AgentDetail.vue`, `src/store/useSwarmStore.ts`, `agent-engine/src/agent.ts` |
| Agent 自动提交审阅 | ✅ | Agent 执行完指令后检测到文件变更自动 `git add/commit/push` 并创建 PR;pre-commit 失败时将完整执行日志全量回传修复(最多 3 轮);PR 创建后自动轮询 GitHub Actions CI(30s 间隔),CI 失败时自动获取日志并修复重新提交(最多 3 轮);**Kimi CLI 自动修复设 120s 超时保护**;无 GitHub Token 时降级为 Mock PR | `agent-engine/src/agent.ts` |

## 质量约束
Expand Down Expand Up @@ -63,4 +63,4 @@

---

*Last updated: 2026-05-16*
*Last updated: 2026-05-28*
19 changes: 17 additions & 2 deletions kimi-code-swarm/src/components/AgentDetail.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { ref, computed, watch, nextTick, onMounted, onUnmounted } from 'vue'
import {
ArrowLeft, Send, Terminal, AlertCircle, CheckCircle, Play,
ArrowLeft, ArrowDown, Send, Terminal, AlertCircle, CheckCircle, Play,
GitPullRequest, GitMerge, RotateCcw, Square, Clock, XCircle, FileCode,
User, Bot, Loader2, Brain, Wrench, Server,
ChevronDown, ChevronRight
Expand Down Expand Up @@ -57,9 +57,11 @@
// This fixes the race where scrollHeight grows but scrollTop stays stale,
// causing isNearBottom() to falsely return false after DOM update.
const userWasNearBottom = ref(true)
const showScrollToBottom = ref(false)

function onScroll() {
userWasNearBottom.value = isNearBottom()
showScrollToBottom.value = !isNearBottom() && props.agent.logs.length > 0
}

// Collapsible logs: expanded by default for none; stored as Set of log ids
Expand Down Expand Up @@ -144,6 +146,7 @@
el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' })
} else {
el.scrollTop = el.scrollHeight
showScrollToBottom.value = false
}
}

Expand Down Expand Up @@ -382,7 +385,7 @@
<!-- Chat Area -->
<div
ref="scrollRef"
class="flex-1 overflow-y-auto scrollbar-thin rounded-xl bg-gray-50/50 border border-gray-200 p-4 space-y-4 min-h-0"
class="flex-1 overflow-y-auto scrollbar-thin rounded-xl bg-gray-50/50 border border-gray-200 p-4 space-y-4 min-h-0 relative"
>
<template v-if="agent.logs.length === 0">
<div class="h-full flex flex-col items-center justify-center text-gray-400">
Expand All @@ -396,7 +399,7 @@
<div v-if="log.type === 'input'" class="flex justify-end animate-message-enter">
<div class="flex items-end gap-2 max-w-[85%] min-w-0">
<div class="bg-swarm-600 text-white rounded-2xl rounded-tr-sm px-4 py-2.5 shadow-sm">
<div class="markdown-content markdown-content-inverted" v-html="renderMarkdown(log.content)" />

Check warning on line 402 in kimi-code-swarm/src/components/AgentDetail.vue

View workflow job for this annotation

GitHub Actions / check

'v-html' directive can lead to XSS attack
<div class="flex items-center justify-end gap-2 mt-1">
<span v-if="log.tokens" class="text-[10px] opacity-60">{{ log.tokens }} tokens</span>
<span class="text-[10px] opacity-60">{{ new Date(log.timestamp).toLocaleTimeString() }}</span>
Expand All @@ -415,7 +418,7 @@
<Bot class="w-3.5 h-3.5 text-blue-600" />
</div>
<div class="bg-white text-gray-800 rounded-2xl rounded-tl-sm px-4 py-2.5 shadow-sm border border-gray-100">
<div class="markdown-content" v-html="renderMarkdown(log.content)" />

Check warning on line 421 in kimi-code-swarm/src/components/AgentDetail.vue

View workflow job for this annotation

GitHub Actions / check

'v-html' directive can lead to XSS attack
<div class="flex items-center gap-2 mt-1">
<span class="text-[10px] text-gray-400">{{ new Date(log.timestamp).toLocaleTimeString() }}</span>
</div>
Expand All @@ -439,7 +442,7 @@
<ChevronDown v-else class="w-3.5 h-3.5 text-amber-600 shrink-0 ml-2" />
</button>
<div v-if="isLogExpanded(log.id)" class="px-4 pb-2.5">
<div class="markdown-content" v-html="renderMarkdown(log.content)" />

Check warning on line 445 in kimi-code-swarm/src/components/AgentDetail.vue

View workflow job for this annotation

GitHub Actions / check

'v-html' directive can lead to XSS attack
<div class="flex items-center gap-2 mt-1">
<span class="text-[10px] text-amber-400">{{ new Date(log.timestamp).toLocaleTimeString() }}</span>
</div>
Expand Down Expand Up @@ -530,6 +533,18 @@
</div>
</div>
</div>

<!-- Scroll to Bottom Button -->
<button
data-testid="scroll-to-bottom-button"
:class="[
'absolute bottom-4 right-4 w-9 h-9 rounded-full bg-white border border-gray-200 shadow-md flex items-center justify-center text-gray-500 hover:text-swarm-600 hover:border-swarm-200 transition-all duration-200 z-10',
showScrollToBottom ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-2 pointer-events-none'
]"
@click="scrollToBottom(true)"
>
<ArrowDown class="w-4 h-4" />
</button>
</div>

<!-- Input Area -->
Expand Down
Loading