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: 2 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ PR 创建时,Store 自动生成 `ReviewEntry[]`,包含所有其他 Agent 作
- `components/AgentDashboard.vue` — Agent 卡片网格(最多 5 个)
- `components/AgentDetail.vue` — Agent 详情(多轮对话聊天 UI + 日志 + PR 审阅)
- `components/AnalyticsPanel.vue` — 数据可视化(只读聚合)
- `composables/useToast.ts` — 全局 Toast 通知(Store 错误/状态反馈)
- `composables/useConfirm.ts` — 可复用确认弹窗(危险操作确认)
- `src-tauri/` —— 唯一有权 spawn 进程的 Rust 后端
- `save_api_key` / `get_api_key` / `delete_api_key` — OS Keyring 操作
- `verify_api_key` — Kimi CLI 存在性检测(拒绝 fallback),启动引擎时注入 `KIMI_API_KEY`
Expand Down
6 changes: 5 additions & 1 deletion docs/COMPONENT_PATTERNS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ const emit = defineEmits<{...}>()
- `AgentDetail.vue` — 聊天式多轮对话组件:Header + Info + PR 审阅 + 文件变更 + 聊天消息区(input/output/system/error 气泡)+ 输入框。日期字段通过 new Date(string).toLocaleTimeString() 显示
- `AnalyticsPanel.vue` — 数据展示型组件:状态分布、Token 排行、任务列表
- `SettingsPanel.vue` — 纯信息展示型设置面板,使用 lucide 图标 + code 标签展示命令指引
- `CreateTaskModal.vue` — 表单弹窗组件:新建 Agent 的输入表单,使用 Vue `<Transition>` 原生过渡,避免手动管理过渡状态
- `SwarmToast.vue` — 全局 Toast 通知容器(右上角堆叠,自动消失)
- `SwarmConfirmModal.vue` — 全局确认弹窗(danger/warning/info 三种类型,替代原生 confirm)

## 视觉风格

Expand All @@ -44,7 +47,8 @@ const emit = defineEmits<{...}>()

## 交互规范

- **危险操作须确认**:删除 Agent 等不可逆操作须通过 `confirm()` 弹窗提示用户,明确告知影响范围(如工作目录将被一并删除)。即使 `workspace` 字段为空,也应根据命名规则(`E:/workspace/{agentId}`)推断并显示路径
- **危险操作须确认**:删除 Agent 等不可逆操作须通过 `useConfirm()` 弹窗提示用户,明确告知影响范围(如工作目录将被一并删除)。即使 `workspace` 字段为空,也应根据命名规则(`E:/workspace/{agentId}`)推断并显示路径
- **错误反馈用 Toast**:关键路径错误(如创建/启动/停止 Agent 失败)使用 `useToast()` 替代原生 `alert()`,避免阻断用户操作

## E2E 测试标识

Expand Down
10 changes: 8 additions & 2 deletions docs/FRONTEND.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,18 @@ Vue 3 + TypeScript + Vite + Tailwind CSS + lucide-vue-next + Tauri v2 + @tauri-a
- `types/index.ts` — AgentTask / LogEntry / CommandCenterStats;修改前检查上下游依赖
- `api/github.ts` — GitHub API 封装(PR 创建/合并/查询)
- `api/ipc.ts` — Tauri IPC 适配层
- `composables/useToast.ts` — 全局 Toast 通知(error/success/info/warning,右上角堆叠自动消失)
- `composables/useConfirm.ts` — 可复用确认弹窗(danger/warning/info 三种类型)
- `components/LoginView.vue` — API Key 登录页(验证 + keyring 存储)
- `components/AgentDashboard.vue` — Agent 卡片网格(最多 5 个,点击进入详情)
- `App.vue` — 主入口:布局框架 + 视图路由(dashboard/agent-detail/analytics)+ 全局事件处理(如文件 diff 查看)
- `App.vue` — 主入口:布局框架 + 视图路由(dashboard/agent-detail/analytics)+ 全局事件处理(如文件 diff 查看)+ 全局 Toast/ConfirmModal 挂载
- `components/AgentDetail.vue` — Agent 详情:指令输入 + 日志流 + PR 审阅 + 文件变更(点击通过 engine 获取 diff)
- `components/SettingsPanel.vue` — 系统设置(GitHub Token + Kimi CLI 安装指引)
- `components/AnalyticsPanel.vue` — 监控分析:状态分布、Token 排行、活跃/审阅任务
- `components/TaskCard.vue` — Agent 卡片:状态 + Token 进度 + 审阅徽章
- `components/CreateTaskModal.vue` — 新建 Agent 弹窗(名称/仓库/指令/Token 预算表单,Vue Transition 过渡)
- `components/SwarmToast.vue` — 全局 Toast 容器(动画进入/退出 + 进度条)
- `components/SwarmConfirmModal.vue` — 全局确认弹窗(带图标、动画、类型化样式)

## 环境准备

Expand Down Expand Up @@ -105,7 +110,8 @@ PR CI 中额外运行 `check-test-sync`:若 `src/` 新增代码文件,`tests
| PR 审阅面板 | ✅ 真实 | 含审阅者列表、进度、门控;白色简约 UI |
| SettingsPanel(Token 配置) | ✅ 真实 | localStorage 持久化;白色简约 UI |
| 监控分析页 | ✅ 真实 | 状态分布、Token 排行、活跃/审阅任务;白色简约 UI |
| 删除 Agent 确认 | ✅ 真实 | `confirm()` 弹窗提示,明确告知工作目录将被一并删除;未启动的 Agent 根据命名规则推断路径显示 |
| 删除 Agent 确认 | ✅ 真实 | `useConfirm()` 自定义弹窗,替代原生 confirm,明确告知工作目录将被一并删除;未启动的 Agent 根据命名规则推断路径显示 |
| Toast 通知 | ✅ 真实 | `useToast()` 全局提示(错误/成功/信息/警告),替代原生 alert,不阻断操作 |
| Agent 状态持久化 | ✅ 真实 | tauri-plugin-store 自动保存/恢复 |

文档同步检测被阻断时,不直接告知需要更新哪个文档——Agent 需回顾本次会话已读文档,或查阅 AGENTS.md 地图自行定位关联文档。
8 changes: 8 additions & 0 deletions kimi-code-swarm/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import AgentDetail from './components/AgentDetail.vue'
import CreateTaskModal from './components/CreateTaskModal.vue'
import SettingsPanel from './components/SettingsPanel.vue'
import AnalyticsPanel from './components/AnalyticsPanel.vue'
import SwarmToast from './components/SwarmToast.vue'
import SwarmConfirmModal from './components/SwarmConfirmModal.vue'
import { useSwarmStore } from './store/useSwarmStore'

const store = useSwarmStore()
Expand Down Expand Up @@ -195,5 +197,11 @@ function handleLogout() {
@close="store.setIsCreateModalOpen(false)"
@create="store.createAgent"
/>

<!-- Global Toast -->
<SwarmToast />

<!-- Global Confirm Modal -->
<SwarmConfirmModal />
</div>
</template>
95 changes: 75 additions & 20 deletions kimi-code-swarm/src/components/AgentDashboard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,44 @@ const completedCount = computed(() => props.agents.filter((a) => a.status === 'c
const totalTokens = computed(() => props.agents.reduce((sum, a) => sum + a.tokenUsed, 0))
const totalBudget = computed(() => props.agents.reduce((sum, a) => sum + a.tokenBudget, 0))

const statCards = [
{ label: 'Agent 总数', value: () => `${props.agents.length} / ${props.maxAgents}`, sub: () => '最多同时运行 5 个', icon: Users, color: 'bg-swarm-600' },
{ label: '活跃 Agent', value: () => activeCount.value.toString(), sub: () => '正在工作中', icon: Activity, color: 'bg-amber-500' },
{ label: '已完成', value: () => completedCount.value.toString(), sub: () => '累计交付', icon: CheckCircle, color: 'bg-emerald-500' },
{ label: 'Token 消耗', value: () => `${(totalTokens.value / 1000).toFixed(1)}K`, sub: () => `预算 ${(totalBudget.value / 1000).toFixed(0)}K`, icon: Coins, color: 'bg-blue-500' },
]
const statCards = computed(() => [
{
label: 'Agent 总数',
value: `${props.agents.length} / ${props.maxAgents}`,
sub: '最多同时运行 5 个',
icon: Users,
gradient: 'from-swarm-500 to-emerald-400',
accent: 'bg-swarm-500',
progress: props.maxAgents > 0 ? (props.agents.length / props.maxAgents) * 100 : 0,
},
{
label: '活跃 Agent',
value: activeCount.value.toString(),
sub: '正在工作中',
icon: Activity,
gradient: 'from-amber-500 to-orange-400',
accent: 'bg-amber-500',
progress: props.agents.length > 0 ? (activeCount.value / Math.max(props.agents.length, 1)) * 100 : 0,
},
{
label: '已完成',
value: completedCount.value.toString(),
sub: '累计交付',
icon: CheckCircle,
gradient: 'from-emerald-500 to-teal-400',
accent: 'bg-emerald-500',
progress: props.agents.length > 0 ? (completedCount.value / Math.max(props.agents.length, 1)) * 100 : 0,
},
{
label: 'Token 消耗',
value: `${(totalTokens.value / 1000).toFixed(1)}K`,
sub: `预算 ${(totalBudget.value / 1000).toFixed(0)}K`,
icon: Coins,
gradient: 'from-blue-500 to-indigo-400',
accent: 'bg-blue-500',
progress: totalBudget.value > 0 ? (totalTokens.value / totalBudget.value) * 100 : 0,
},
])
</script>

<template>
Expand All @@ -38,15 +70,36 @@ const statCards = [
<div
v-for="stat in statCards"
:key="stat.label"
class="bg-white border border-gray-200 rounded-xl p-4 flex items-center gap-4 shadow-sm"
class="group relative bg-white border border-gray-200 rounded-2xl p-5 flex items-center gap-4 shadow-sm hover:shadow-lg hover:-translate-y-0.5 transition-all duration-300 overflow-hidden"
>
<div :class="['w-12 h-12 rounded-xl flex items-center justify-center', stat.color]">
<component :is="stat.icon" class="w-6 h-6 text-white" />
<!-- Background gradient decoration -->
<div
class="absolute -right-6 -top-6 w-24 h-24 rounded-full opacity-20 blur-2xl transition-opacity duration-300 group-hover:opacity-40 bg-gradient-to-br"
:class="stat.gradient"
/>

<!-- Icon -->
<div
class="relative w-12 h-12 rounded-xl flex items-center justify-center shadow-sm bg-gradient-to-br text-white shrink-0"
:class="stat.gradient"
>
<component :is="stat.icon" class="w-6 h-6" />
</div>

<!-- Content -->
<div class="relative flex-1 min-w-0">
<p class="text-xs text-gray-400 font-medium mb-0.5">{{ stat.label }}</p>
<p class="text-2xl font-bold text-gray-900 tracking-tight">{{ stat.value }}</p>
<p class="text-[11px] text-gray-400 mt-0.5">{{ stat.sub }}</p>
</div>
<div>
<p class="text-xs text-gray-400 font-medium">{{ stat.label }}</p>
<p class="text-2xl font-bold text-gray-900">{{ stat.value() }}</p>
<p class="text-xs text-gray-400">{{ stat.sub() }}</p>

<!-- Bottom progress accent -->
<div class="absolute bottom-0 left-0 right-0 h-0.5 bg-gray-100">
<div
class="h-full rounded-full transition-all duration-700 ease-out"
:class="stat.accent"
:style="{ width: `${Math.min(stat.progress, 100)}%` }"
/>
</div>
</div>
</div>
Expand All @@ -59,19 +112,21 @@ const statCards = [
{{ agents.length }} / {{ maxAgents }}
</span>
</div>
<div class="flex items-center gap-2 text-xs text-gray-400">
<span class="flex items-center gap-1"><span class="w-2 h-2 rounded-full bg-amber-500" /> 工作中</span>
<span class="flex items-center gap-1"><span class="w-2 h-2 rounded-full bg-purple-500" /> 待审阅</span>
<span class="flex items-center gap-1"><span class="w-2 h-2 rounded-full bg-emerald-500" /> 已完成</span>
<div class="flex items-center gap-3 text-xs text-gray-400">
<span class="flex items-center gap-1.5"><span class="w-2 h-2 rounded-full bg-amber-500" /> 工作中</span>
<span class="flex items-center gap-1.5"><span class="w-2 h-2 rounded-full bg-purple-500" /> 待审阅</span>
<span class="flex items-center gap-1.5"><span class="w-2 h-2 rounded-full bg-emerald-500" /> 已完成</span>
</div>
</div>

<!-- Agents Grid -->
<div class="flex-1 overflow-y-auto scrollbar-thin">
<div v-if="agents.length === 0" class="h-full flex flex-col items-center justify-center text-gray-400">
<Bot class="w-12 h-12 mb-3 opacity-40" />
<p class="text-sm">还没有 Agent</p>
<p class="text-xs mt-1">点击右上角按钮创建你的第一个 Agent</p>
<div class="w-20 h-20 rounded-2xl bg-gray-100 flex items-center justify-center mb-4">
<Bot class="w-10 h-10 text-gray-300" />
</div>
<p class="text-sm font-medium text-gray-500">还没有 Agent</p>
<p class="text-xs mt-1.5 text-gray-400">点击右上角按钮创建你的第一个 Agent</p>
</div>

<div v-else class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-4 pb-4">
Expand Down
Loading
Loading