mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-01 15:03:57 +08:00
feat: add queue management and terminal dashboard documentation in Chinese
- Introduced comprehensive documentation for the queue management feature, detailing its pain points, core functionalities, and component structure. - Added terminal dashboard documentation, highlighting its layout, core features, and usage examples. - Created an index page in Chinese for Claude Code Workflow, summarizing its purpose and core features, along with quick links to installation and guides.
This commit is contained in:
607
docs/zh-CN/features/dashboard.md
Normal file
607
docs/zh-CN/features/dashboard.md
Normal file
@@ -0,0 +1,607 @@
|
||||
# 仪表板
|
||||
|
||||
## 一句话概述
|
||||
**仪表板通过直观的基于小部件的界面,提供项目工作流状态、统计信息和最近活动的概览。**
|
||||
|
||||
---
|
||||
|
||||
## 解决的痛点
|
||||
|
||||
| 痛点 | 当前状态 | 仪表板解决方案 |
|
||||
|------|----------|----------------|
|
||||
| **项目可见性不足** | 无法查看整体项目健康状况 | 带有技术栈和开发索引的项目信息横幅 |
|
||||
| **指标分散** | 统计信息分布在多个位置 | 集中式统计数据,带有迷你趋势图 |
|
||||
| **工作流状态未知** | 难以跟踪会话进度 | 带有状态细分的饼图 |
|
||||
| **最近工作丢失** | 无法快速访问活动会话 | 带有任务详情的会话轮播 |
|
||||
| **索引状态不明确** | 不知道代码是否已索引 | 实时索引状态指示器 |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
**位置**: `ccw/frontend/src/pages/HomePage.tsx`
|
||||
|
||||
**用途**: 仪表板主页,提供项目概览、统计信息、工作流状态和最近活动监控。
|
||||
|
||||
**访问**: 导航 → 仪表板(默认首页,路径为 `/`)
|
||||
|
||||
**布局**:
|
||||
```
|
||||
+--------------------------------------------------------------------------+
|
||||
| 仪表板头部(标题 + 刷新) |
|
||||
+--------------------------------------------------------------------------+
|
||||
| WorkflowTaskWidget(组合卡片) |
|
||||
| +--------------------------------------------------------------------+ |
|
||||
| | 项目信息横幅(可展开) | |
|
||||
| | - 项目名称、描述、技术栈徽章 | |
|
||||
| | - 快速统计(功能、bug修复、增强) | |
|
||||
| | - 索引状态指示器 | |
|
||||
| +----------------------------------+---------------------------------+ |
|
||||
| | 统计部分 | 工作流状态 | 任务详情(轮播) | |
|
||||
| | - 6 个迷你卡片 | - 饼图 | - 会话导航 | |
|
||||
| | - 迷你趋势图 | - 图例 | - 任务列表(2 列) | |
|
||||
| +----------------+-----------------+-------------------------------+ |
|
||||
+--------------------------------------------------------------------------+
|
||||
| RecentSessionsWidget |
|
||||
| +--------------------------------------------------------------------+ |
|
||||
| | 标签页:所有任务 | 工作流 | 轻量任务 | |
|
||||
| | +---------------+---------------+-------------------------------+ | |
|
||||
| | | 带有状态、进度、标签、时间的任务卡片 | | |
|
||||
| | +---------------------------------------------------------------+ | |
|
||||
+--------------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 实时演示
|
||||
|
||||
:::demo DashboardOverview
|
||||
# dashboard-overview.tsx
|
||||
/**
|
||||
* 仪表板概览演示
|
||||
* 显示带有小部件的主仪表板布局
|
||||
*/
|
||||
export function DashboardOverview() {
|
||||
return (
|
||||
<div className="space-y-6 p-6 bg-background min-h-[600px]">
|
||||
{/* 头部 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold">仪表板</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
项目概览和活动监控
|
||||
</p>
|
||||
</div>
|
||||
<button className="px-3 py-1.5 text-sm border rounded-md hover:bg-accent">
|
||||
刷新
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 工作流统计小部件 */}
|
||||
<div className="border rounded-lg overflow-hidden">
|
||||
<div className="p-4 border-b bg-muted/30">
|
||||
<h2 className="font-semibold">项目概览与统计</h2>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div className="space-y-3">
|
||||
<div className="text-xs font-medium text-muted-foreground">统计数据</div>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{[
|
||||
{ label: '活动会话', value: '12', color: 'text-blue-500' },
|
||||
{ label: '总任务', value: '48', color: 'text-green-500' },
|
||||
{ label: '已完成', value: '35', color: 'text-emerald-500' },
|
||||
{ label: '待处理', value: '8', color: 'text-amber-500' },
|
||||
].map((stat, i) => (
|
||||
<div key={i} className="p-2 bg-muted/50 rounded">
|
||||
<div className={`text-lg font-bold ${stat.color}`}>{stat.value}</div>
|
||||
<div className="text-xs text-muted-foreground truncate">{stat.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="text-xs font-medium text-muted-foreground">工作流状态</div>
|
||||
<div className="flex items-center justify-center h-24">
|
||||
<div className="relative w-20 h-20">
|
||||
<svg className="w-full h-full -rotate-90" viewBox="0 0 36 36">
|
||||
<circle cx="18" cy="18" r="15" fill="none" stroke="currentColor" strokeWidth="3" className="text-muted opacity-20"/>
|
||||
<circle cx="18" cy="18" r="15" fill="none" stroke="currentColor" strokeWidth="3" className="text-blue-500" strokeDasharray="70 100"/>
|
||||
</svg>
|
||||
<div className="absolute inset-0 flex items-center justify-center text-xs font-bold">70%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs text-center space-y-1">
|
||||
<div className="flex items-center justify-center gap-1">
|
||||
<div className="w-2 h-2 rounded-full bg-blue-500"/>
|
||||
<span>已完成:70%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="text-xs font-medium text-muted-foreground">最近会话</div>
|
||||
<div className="p-3 bg-accent/20 rounded border">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-medium">功能:身份验证流程</span>
|
||||
<span className="text-xs px-2 py-0.5 rounded-full bg-green-500/20 text-green-600">运行中</span>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<div className="w-3 h-3 rounded bg-green-500"/>
|
||||
<span>实现登录表单</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<div className="w-3 h-3 rounded bg-amber-500"/>
|
||||
<span>添加 OAuth 提供商</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<div className="w-3 h-3 rounded bg-muted"/>
|
||||
<span className="text-muted-foreground">测试流程</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 最近会话小部件 */}
|
||||
<div className="border rounded-lg overflow-hidden">
|
||||
<div className="border-b bg-muted/30">
|
||||
<div className="flex gap-1 p-2">
|
||||
{['所有任务', '工作流', '轻量任务'].map((tab, i) => (
|
||||
<button
|
||||
key={tab}
|
||||
className={`px-3 py-1.5 text-xs rounded-md transition-colors ${
|
||||
i === 0 ? 'bg-background text-foreground' : 'text-muted-foreground hover:bg-foreground/5'
|
||||
}`}
|
||||
>
|
||||
{tab}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
{[
|
||||
{ name: '重构 UI 组件', status: '进行中', progress: 65 },
|
||||
{ name: '修复登录 Bug', status: '待处理', progress: 0 },
|
||||
{ name: '添加深色模式', status: '已完成', progress: 100 },
|
||||
].map((task, i) => (
|
||||
<div key={i} className="p-3 bg-muted/30 rounded border cursor-pointer hover:border-primary/30">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-xs font-medium line-clamp-1">{task.name}</span>
|
||||
<span className={`text-xs px-1.5 py-0.5 rounded ${
|
||||
task.status === '已完成' ? 'bg-green-500/20 text-green-600' :
|
||||
task.status === '进行中' ? 'bg-blue-500/20 text-blue-600' :
|
||||
'bg-gray-500/20 text-gray-600'
|
||||
}`}>{task.status}</span>
|
||||
</div>
|
||||
{task.progress > 0 && task.progress < 100 && (
|
||||
<div className="w-full h-1.5 bg-muted rounded-full overflow-hidden">
|
||||
<div className="h-full bg-blue-500 rounded-full" style={{ width: `${task.progress}%` }}/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 核心功能
|
||||
|
||||
| 功能 | 描述 |
|
||||
|------|------|
|
||||
| **项目信息横幅** | 可展开的横幅,显示项目名称、描述、技术栈(语言、框架、架构)、开发索引(功能/bug修复/增强)和实时索引状态 |
|
||||
| **统计部分** | 6 个迷你统计卡片(活动会话、总任务、已完成任务、待处理任务、失败任务、今日活动),带有 7 天迷你趋势图 |
|
||||
| **工作流状态饼图** | 环形图显示会话状态细分(已完成、进行中、计划中、已暂停、已归档),附带百分比 |
|
||||
| **会话轮播** | 自动轮播(5秒间隔)的会话卡片,带有任务列表、进度条和手动导航箭头 |
|
||||
| **最近会话小部件** | 所有任务类型的标签页视图,带有筛选、状态徽章和进度指示器 |
|
||||
| **实时更新** | 统计数据每 60 秒自动刷新,索引状态每 30 秒刷新 |
|
||||
|
||||
---
|
||||
|
||||
## 组件层次结构
|
||||
|
||||
```
|
||||
HomePage
|
||||
├── DashboardHeader
|
||||
│ ├── 标题
|
||||
│ └── 刷新操作按钮
|
||||
├── WorkflowTaskWidget
|
||||
│ ├── ProjectInfoBanner(可展开)
|
||||
│ │ ├── 项目名称和描述
|
||||
│ │ ├── 技术栈徽章
|
||||
│ │ ├── 快速统计卡片
|
||||
│ │ ├── 索引状态指示器
|
||||
│ │ ├── 架构部分
|
||||
│ │ ├── 关键组件
|
||||
│ │ └── 设计模式
|
||||
│ ├── 统计部分
|
||||
│ │ └── MiniStatCard(6 个卡片,带迷你趋势图)
|
||||
│ ├── WorkflowStatusChart
|
||||
│ │ └── 饼图与图例
|
||||
│ └── SessionCarousel
|
||||
│ ├── 导航箭头
|
||||
│ └── 会话卡片(任务列表)
|
||||
└── RecentSessionsWidget
|
||||
├── 标签导航(全部 | 工作流 | 轻量任务)
|
||||
├── 任务网格
|
||||
│ └── TaskItemCard
|
||||
└── 加载/空状态
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Props API
|
||||
|
||||
### HomePage 组件
|
||||
|
||||
| Prop | 类型 | 默认值 | 描述 |
|
||||
|------|------|--------|------|
|
||||
| - | - | - | 此页面组件不接受任何 props(数据通过 hooks 获取) |
|
||||
|
||||
### WorkflowTaskWidget
|
||||
|
||||
| Prop | 类型 | 默认值 | 描述 |
|
||||
|------|------|--------|------|
|
||||
| `className` | `string` | `undefined` | 用于样式的额外 CSS 类 |
|
||||
|
||||
### RecentSessionsWidget
|
||||
|
||||
| Prop | 类型 | 默认值 | 描述 |
|
||||
|------|------|--------|------|
|
||||
| `className` | `string` | `undefined` | 用于样式的额外 CSS 类 |
|
||||
| `maxItems` | `number` | `6` | 要显示的最大项目数量 |
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础仪表板
|
||||
|
||||
```tsx
|
||||
import { HomePage } from '@/pages/HomePage'
|
||||
|
||||
// 仪表板在根路由 (/) 自动渲染
|
||||
// 不需要 props - 数据通过 hooks 获取
|
||||
```
|
||||
|
||||
### 嵌入 WorkflowTaskWidget
|
||||
|
||||
```tsx
|
||||
import { WorkflowTaskWidget } from '@/components/dashboard/widgets/WorkflowTaskWidget'
|
||||
|
||||
function CustomDashboard() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<WorkflowTaskWidget />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 自定义最近会话小部件
|
||||
|
||||
```tsx
|
||||
import { RecentSessionsWidget } from '@/components/dashboard/widgets/RecentSessionsWidget'
|
||||
|
||||
function ActivityFeed() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<RecentSessionsWidget maxItems={10} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 状态管理
|
||||
|
||||
### 本地状态
|
||||
|
||||
| 状态 | 类型 | 描述 |
|
||||
|------|------|------|
|
||||
| `hasError` | `boolean` | 关键错误的错误跟踪 |
|
||||
| `projectExpanded` | `boolean` | 项目信息横幅展开状态 |
|
||||
| `currentSessionIndex` | `number` | 轮播中活动会话的索引 |
|
||||
| `activeTab` | `'all' \| 'workflow' \| 'lite'` | 最近会话小部件筛选标签页 |
|
||||
|
||||
### Store 选择器(Zustand)
|
||||
|
||||
| Store | 选择器 | 用途 |
|
||||
|-------|--------|------|
|
||||
| `appStore` | `selectIsImmersiveMode` | 检查沉浸模式是否激活 |
|
||||
|
||||
### 自定义 Hooks(数据获取)
|
||||
|
||||
| Hook | 描述 | 重新获取间隔 |
|
||||
|------|-------------|--------------|
|
||||
| `useWorkflowStatusCounts` | 会话状态分布数据 | - |
|
||||
| `useDashboardStats` | 带迷你趋势图的统计数据 | 60 秒 |
|
||||
| `useProjectOverview` | 项目信息和技术栈 | - |
|
||||
| `useIndexStatus` | 实时索引状态 | 30 秒 |
|
||||
| `useSessions` | 活动会话数据 | - |
|
||||
| `useLiteTasks` | 最近小部件的轻量任务数据 | - |
|
||||
|
||||
---
|
||||
|
||||
## 交互演示
|
||||
|
||||
### 统计卡片演示
|
||||
|
||||
:::demo MiniStatCards
|
||||
# mini-stat-cards.tsx
|
||||
/**
|
||||
* 迷你统计卡片演示
|
||||
* 带有迷你趋势图的独立统计卡片
|
||||
*/
|
||||
export function MiniStatCards() {
|
||||
const stats = [
|
||||
{ label: '活动会话', value: 12, trend: [8, 10, 9, 11, 10, 12, 12], color: 'blue' },
|
||||
{ label: '总任务', value: 48, trend: [40, 42, 45, 44, 46, 47, 48], color: 'green' },
|
||||
{ label: '已完成', value: 35, trend: [25, 28, 30, 32, 33, 34, 35], color: 'emerald' },
|
||||
{ label: '待处理', value: 8, trend: [12, 10, 11, 9, 8, 7, 8], color: 'amber' },
|
||||
{ label: '失败', value: 5, trend: [3, 4, 3, 5, 4, 5, 5], color: 'red' },
|
||||
{ label: '今日活动', value: 23, trend: [5, 10, 15, 18, 20, 22, 23], color: 'purple' },
|
||||
]
|
||||
|
||||
const colorMap = {
|
||||
blue: 'text-blue-500 bg-blue-500/10',
|
||||
green: 'text-green-500 bg-green-500/10',
|
||||
emerald: 'text-emerald-500 bg-emerald-500/10',
|
||||
amber: 'text-amber-500 bg-amber-500/10',
|
||||
red: 'text-red-500 bg-red-500/10',
|
||||
purple: 'text-purple-500 bg-purple-500/10',
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-6 bg-background">
|
||||
<h3 className="text-sm font-semibold mb-4">带迷你趋势图的统计</h3>
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||
{stats.map((stat, i) => (
|
||||
<div key={i} className="p-4 border rounded-lg bg-card">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-xs text-muted-foreground">{stat.label}</span>
|
||||
<div className={`w-2 h-2 rounded-full ${colorMap[stat.color].split(' ')[1]}`}/>
|
||||
</div>
|
||||
<div className={`text-2xl font-bold ${colorMap[stat.color].split(' ')[0]}`}>{stat.value}</div>
|
||||
<div className="mt-2 h-8 flex items-end gap-0.5">
|
||||
{stat.trend.map((v, j) => (
|
||||
<div
|
||||
key={j}
|
||||
className="flex-1 rounded-t"
|
||||
style={{
|
||||
height: `${(v / Math.max(...stat.trend)) * 100}%`,
|
||||
backgroundColor: v === stat.value ? 'currentColor' : 'rgba(59, 130, 246, 0.3)',
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
:::
|
||||
|
||||
### 项目信息横幅演示
|
||||
|
||||
:::demo ProjectInfoBanner
|
||||
# project-info-banner.tsx
|
||||
/**
|
||||
* 项目信息横幅演示
|
||||
* 带有技术栈的可展开项目信息
|
||||
*/
|
||||
export function ProjectInfoBanner() {
|
||||
const [expanded, setExpanded] = React.useState(false)
|
||||
|
||||
return (
|
||||
<div className="p-6 bg-background">
|
||||
<h3 className="text-sm font-semibold mb-4">项目信息横幅</h3>
|
||||
<div className="border rounded-lg overflow-hidden">
|
||||
{/* 横幅头部 */}
|
||||
<div className="p-4 bg-muted/30 flex items-center justify-between">
|
||||
<div>
|
||||
<h4 className="font-semibold">我的项目</h4>
|
||||
<p className="text-sm text-muted-foreground">使用 React 构建的现代化 Web 应用</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
className="p-2 rounded-md hover:bg-accent"
|
||||
>
|
||||
<svg className={`w-5 h-5 transition-transform ${expanded ? 'rotate-180' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 技术栈徽章 */}
|
||||
<div className="px-4 pb-3 flex flex-wrap gap-2">
|
||||
{['TypeScript', 'React', 'Vite', 'Tailwind CSS', 'Zustand'].map((tech) => (
|
||||
<span key={tech} className="px-2 py-1 text-xs rounded-full bg-primary/10 text-primary">
|
||||
{tech}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 展开内容 */}
|
||||
{expanded && (
|
||||
<div className="p-4 border-t bg-muted/20 space-y-4">
|
||||
<div>
|
||||
<h5 className="text-xs font-semibold mb-2">架构</h5>
|
||||
<div className="text-sm text-muted-foreground space-y-1">
|
||||
<div>• 基于组件的 UI 架构</div>
|
||||
<div>• 集中式状态管理</div>
|
||||
<div>• RESTful API 集成</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h5 className="text-xs font-semibold mb-2">关键组件</h5>
|
||||
<div className="grid grid-cols-2 gap-2 text-sm">
|
||||
{['会话管理器', '仪表板', '任务调度器', '分析'].map((comp) => (
|
||||
<div key={comp} className="flex items-center gap-2">
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-primary"/>
|
||||
{comp}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
:::
|
||||
|
||||
### 会话轮播演示
|
||||
|
||||
:::demo SessionCarousel
|
||||
# session-carousel.tsx
|
||||
/**
|
||||
* 会话轮播演示
|
||||
* 带有导航的自动轮播会话卡片
|
||||
*/
|
||||
export function SessionCarousel() {
|
||||
const [currentIndex, setCurrentIndex] = React.useState(0)
|
||||
const sessions = [
|
||||
{
|
||||
name: '功能:用户身份验证',
|
||||
status: 'running',
|
||||
tasks: [
|
||||
{ name: '实现登录表单', status: 'completed' },
|
||||
{ name: '添加 OAuth 提供商', status: 'in-progress' },
|
||||
{ name: '创建会话管理', status: 'pending' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Bug 修复:内存泄漏',
|
||||
status: 'running',
|
||||
tasks: [
|
||||
{ name: '识别泄漏源', status: 'completed' },
|
||||
{ name: '修复清理处理器', status: 'in-progress' },
|
||||
{ name: '添加单元测试', status: 'pending' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: '重构:API 层',
|
||||
status: 'planning',
|
||||
tasks: [
|
||||
{ name: '设计新接口', status: 'pending' },
|
||||
{ name: '迁移现有端点', status: 'pending' },
|
||||
{ name: '更新文档', status: 'pending' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const statusColors = {
|
||||
completed: 'bg-green-500',
|
||||
'in-progress': 'bg-amber-500',
|
||||
pending: 'bg-muted',
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
setCurrentIndex((i) => (i + 1) % sessions.length)
|
||||
}, 5000)
|
||||
return () => clearInterval(timer)
|
||||
}, [sessions.length])
|
||||
|
||||
return (
|
||||
<div className="p-6 bg-background">
|
||||
<h3 className="text-sm font-semibold mb-4">会话轮播(每 5 秒自动轮播)</h3>
|
||||
<div className="border rounded-lg p-4 bg-card">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-sm font-medium">会话 {currentIndex + 1} / {sessions.length}</span>
|
||||
<div className="flex gap-1">
|
||||
{sessions.map((_, i) => (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => setCurrentIndex(i)}
|
||||
className={`w-2 h-2 rounded-full transition-colors ${
|
||||
i === currentIndex ? 'bg-primary' : 'bg-muted-foreground/30'
|
||||
}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-4 bg-accent/20 rounded border">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="font-medium">{sessions[currentIndex].name}</span>
|
||||
<span className={`text-xs px-2 py-1 rounded-full ${
|
||||
sessions[currentIndex].status === 'running' ? 'bg-green-500/20 text-green-600' : 'bg-blue-500/20 text-blue-600'
|
||||
}`}>
|
||||
{sessions[currentIndex].status === 'running' ? '运行中' : sessions[currentIndex].status === 'planning' ? '计划中' : sessions[currentIndex].status}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
{sessions[currentIndex].tasks.map((task, i) => (
|
||||
<div key={i} className="flex items-center gap-2 text-sm">
|
||||
<div className={`w-3 h-3 rounded ${statusColors[task.status]}`}/>
|
||||
<span className={task.status === 'pending' ? 'text-muted-foreground' : ''}>{task.name}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between mt-3">
|
||||
<button
|
||||
onClick={() => setCurrentIndex((i) => (i - 1 + sessions.length) % sessions.length)}
|
||||
className="px-3 py-1.5 text-sm border rounded-md hover:bg-accent"
|
||||
>
|
||||
← 上一页
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setCurrentIndex((i) => (i + 1) % sessions.length)}
|
||||
className="px-3 py-1.5 text-sm border rounded-md hover:bg-accent"
|
||||
>
|
||||
下一页 →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 可访问性
|
||||
|
||||
- **键盘导航**:
|
||||
- <kbd>Tab</kbd> - 在交互元素之间导航
|
||||
- <kbd>Enter</kbd>/<kbd>Space</kbd> - 激活按钮和卡片
|
||||
- <kbd>方向键</kbd> - 导航轮播会话
|
||||
|
||||
- **ARIA 属性**:
|
||||
- 导航按钮上的 `aria-label`
|
||||
- 可展开部分的 `aria-expanded`
|
||||
- 实时更新的 `aria-live` 区域
|
||||
|
||||
- **屏幕阅读器支持**:
|
||||
- 所有图表都有文字描述
|
||||
- 状态指示器包含文字标签
|
||||
- 导航被正确宣布
|
||||
|
||||
---
|
||||
|
||||
## 相关链接
|
||||
|
||||
- [会话](/features/sessions) - 查看和管理所有会话
|
||||
- [终端仪表板](/features/terminal) - 终端优先监控界面
|
||||
- [队列](/features/queue) - 问题执行队列管理
|
||||
- [内存](/features/memory) - 持久化内存管理
|
||||
- [设置](/features/settings) - 全局应用设置
|
||||
283
docs/zh-CN/features/queue.md
Normal file
283
docs/zh-CN/features/queue.md
Normal file
@@ -0,0 +1,283 @@
|
||||
# 队列管理
|
||||
|
||||
## 一句话概述
|
||||
**队列管理页面提供对问题执行队列的集中控制,配备调度器控件、状态监控和会话池管理。**
|
||||
|
||||
---
|
||||
|
||||
## 解决的痛点
|
||||
|
||||
| 痛点 | 当前状态 | 队列解决方案 |
|
||||
|------|----------|--------------|
|
||||
| **执行无序** | 没有统一的任务队列 | 带分组项目的集中化队列 |
|
||||
| **调度器状态未知** | 不知道调度器是否在运行 | 实时状态指示器(空闲/运行/暂停) |
|
||||
| **无执行控制** | 无法启动/停止队列处理 | 带确认的开始/暂停/停止控件 |
|
||||
| **并发限制** | 同时运行太多会话 | 可配置的最大并发会话数 |
|
||||
| **无可见性** | 不知道队列中有什么 | 统计卡片 + 带状态跟踪的项目列表 |
|
||||
| **资源浪费** | 空闲会话消耗资源 | 带超时配置的会话池概览 |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
**位置**: `ccw/frontend/src/pages/QueuePage.tsx`(旧版),`ccw/frontend/src/components/terminal-dashboard/QueuePanel.tsx`(当前)
|
||||
|
||||
**用途**: 查看和管理问题执行队列,配备调度器控件、进度跟踪和会话池管理。
|
||||
|
||||
**访问方式**: 导航 → 问题 → 队列标签页 或 终端仪表板 → 队列浮动面板
|
||||
|
||||
**布局**:
|
||||
```
|
||||
+--------------------------------------------------------------------------+
|
||||
| 队列面板标题栏 |
|
||||
+--------------------------------------------------------------------------+
|
||||
| 调度器状态栏 |
|
||||
| +----------------+ +-------------+ +-------------------------------+ |
|
||||
| | 状态徽章 | | 进度 | | 并发数 (2/2) | |
|
||||
| +----------------+ +-------------+ +-------------------------------+ |
|
||||
+--------------------------------------------------------------------------+
|
||||
| 调度器控件 |
|
||||
| +--------+ +--------+ +--------+ +-----------+ |
|
||||
| | 开始 | | 暂停 | | 停止 | | 配置 | |
|
||||
| +--------+ +--------+ +--------+ +-----------+ |
|
||||
+--------------------------------------------------------------------------+
|
||||
| 队列项目列表 |
|
||||
| +---------------------------------------------------------------------+ |
|
||||
| | QueueItemRow(状态、issue_id、session_key、操作) | |
|
||||
| | - 状态图标(待处理/执行中/已完成/被阻塞/失败) | |
|
||||
| | - Issue ID / 项目 ID 显示 | |
|
||||
| | - 会话绑定信息 | |
|
||||
| | - 进度指示器(对于执行中的项目) | |
|
||||
| +---------------------------------------------------------------------+ |
|
||||
| | [更多队列项目...] | |
|
||||
| +---------------------------------------------------------------------+ |
|
||||
+--------------------------------------------------------------------------+
|
||||
| 会话池概览(可选) |
|
||||
| +--------------------------------------------------------------------------+
|
||||
| | 活动会话 | 空闲会话 | 总会话数 |
|
||||
| +--------------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 核心功能
|
||||
|
||||
| 功能 | 描述 |
|
||||
|------|------|
|
||||
| **调度器状态** | 实时状态指示器(空闲/运行/暂停),带视觉徽章 |
|
||||
| **进度跟踪** | 显示队列总体完成百分比的进度条 |
|
||||
| **开始/暂停/停止控件** | 控制队列执行,停止操作时带确认对话框 |
|
||||
| **并发显示** | 显示当前活动会话数与最大并发会话数 |
|
||||
| **队列项目列表** | 所有队列项目的可滚动列表,附带状态、Issue ID 和会话绑定 |
|
||||
| **状态图标** | 项目状态的视觉指示器(待处理/执行中/已完成/被阻塞/失败) |
|
||||
| **会话池** | 活动会话、空闲会话和总会话数的概览 |
|
||||
| **配置面板** | 调整最大并发会话数和超时设置 |
|
||||
| **空状态** | 队列为空时的友好消息,附带添加项目的说明 |
|
||||
|
||||
---
|
||||
|
||||
## 组件层次结构
|
||||
|
||||
```
|
||||
QueuePage(旧版)/ QueuePanel(当前)
|
||||
├── QueuePanelHeader
|
||||
│ ├── 标题
|
||||
│ └── 标签切换器(队列 | 编排器)
|
||||
├── SchedulerBar(内联在 QueueListColumn 中)
|
||||
│ ├── 状态徽章
|
||||
│ ├── 进度 + 并发数
|
||||
│ └── 控制按钮(播放/暂停/停止)
|
||||
├── QueueItemsList
|
||||
│ └── QueueItemRow(重复)
|
||||
│ ├── 状态图标
|
||||
│ ├── Issue ID / 项目 ID
|
||||
│ ├── 会话绑定
|
||||
│ └── 进度(对于执行中的项目)
|
||||
└── SchedulerPanel(独立)
|
||||
├── 状态部分
|
||||
├── 进度条
|
||||
├── 控制按钮
|
||||
├── 配置部分
|
||||
│ ├── 最大并发会话数
|
||||
│ ├── 会话空闲超时
|
||||
│ └── 恢复键绑定超时
|
||||
└── 会话池概览
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Props API
|
||||
|
||||
### QueuePanel
|
||||
|
||||
| Prop | 类型 | 默认值 | 描述 |
|
||||
|------|------|--------|------|
|
||||
| `embedded` | `boolean` | `false` | 面板是否嵌入在另一个组件中 |
|
||||
|
||||
### SchedulerPanel
|
||||
|
||||
| Prop | 类型 | 默认值 | 描述 |
|
||||
|------|------|--------|------|
|
||||
| - | - | - | 此组件不接受任何 props(所有数据来自 Zustand store) |
|
||||
|
||||
### QueueListColumn
|
||||
|
||||
| Prop | 类型 | 默认值 | 描述 |
|
||||
|------|------|--------|------|
|
||||
| - | - | - | 此组件不接受任何 props(所有数据来自 Zustand store) |
|
||||
|
||||
---
|
||||
|
||||
## 状态管理
|
||||
|
||||
### Zustand Stores
|
||||
|
||||
| Store | 选择器 | 用途 |
|
||||
|-------|--------|------|
|
||||
| `queueSchedulerStore` | `selectQueueSchedulerStatus` | 当前调度器状态(空闲/运行/暂停) |
|
||||
| `queueSchedulerStore` | `selectSchedulerProgress` | 队列总体完成百分比 |
|
||||
| `queueSchedulerStore` | `selectQueueItems` | 所有队列项目的列表 |
|
||||
| `queueSchedulerStore` | `selectCurrentConcurrency` | 活动会话计数 |
|
||||
| `queueSchedulerStore` | `selectSchedulerConfig` | 调度器配置 |
|
||||
| `queueSchedulerStore` | `selectSessionPool` | 会话池概览 |
|
||||
| `queueSchedulerStore` | `selectSchedulerError` | 错误消息(如果有) |
|
||||
| `issueQueueIntegrationStore` | `selectAssociationChain` | 用于高亮显示的当前关联链 |
|
||||
| `queueExecutionStore` | `selectByQueueItem` | 队列项目的执行数据 |
|
||||
|
||||
### 队列项目状态
|
||||
|
||||
```typescript
|
||||
type QueueItemStatus =
|
||||
| 'pending' // 等待执行
|
||||
| 'executing' // 正在处理中
|
||||
| 'completed' // 成功完成
|
||||
| 'blocked' // 被依赖项阻塞
|
||||
| 'failed'; // 失败并报错
|
||||
```
|
||||
|
||||
### 调度器状态
|
||||
|
||||
```typescript
|
||||
type QueueSchedulerStatus =
|
||||
| 'idle' // 无项目或已停止
|
||||
| 'running' // 活动处理项目
|
||||
| 'paused'; // 临时暂停
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基本队列面板
|
||||
|
||||
```tsx
|
||||
import { QueuePanel } from '@/components/terminal-dashboard/QueuePanel'
|
||||
|
||||
function QueueSection() {
|
||||
return <QueuePanel />
|
||||
}
|
||||
```
|
||||
|
||||
### 独立调度器面板
|
||||
|
||||
```tsx
|
||||
import { SchedulerPanel } from '@/components/terminal-dashboard/SchedulerPanel'
|
||||
|
||||
function SchedulerControls() {
|
||||
return <SchedulerPanel />
|
||||
}
|
||||
```
|
||||
|
||||
### 嵌入式队列列表列
|
||||
|
||||
```tsx
|
||||
import { QueueListColumn } from '@/components/terminal-dashboard/QueueListColumn'
|
||||
|
||||
function EmbeddedQueue() {
|
||||
return <QueueListColumn />
|
||||
}
|
||||
```
|
||||
|
||||
### 队列 Store 操作
|
||||
|
||||
```tsx
|
||||
import { useQueueSchedulerStore } from '@/stores/queueSchedulerStore'
|
||||
|
||||
function QueueActions() {
|
||||
const startQueue = useQueueSchedulerStore((s) => s.startQueue)
|
||||
const pauseQueue = useQueueSchedulerStore((s) => s.pauseQueue)
|
||||
const stopQueue = useQueueSchedulerStore((s) => s.stopQueue)
|
||||
const updateConfig = useQueueSchedulerStore((s) => s.updateConfig)
|
||||
|
||||
const handleStart = () => startQueue()
|
||||
const handlePause = () => pauseQueue()
|
||||
const handleStop = () => stopQueue()
|
||||
const handleConfig = (config) => updateConfig(config)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button onClick={handleStart}>开始</button>
|
||||
<button onClick={handlePause}>暂停</button>
|
||||
<button onClick={handleStop}>停止</button>
|
||||
<button onClick={() => handleConfig({ maxConcurrentSessions: 4 })}>
|
||||
设置最大值为 4
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 配置
|
||||
|
||||
### 调度器配置
|
||||
|
||||
| 设置 | 类型 | 默认值 | 描述 |
|
||||
|------|------|--------|------|
|
||||
| `maxConcurrentSessions` | `number` | `2` | 同时运行的最大会话数 |
|
||||
| `sessionIdleTimeoutMs` | `number` | `60000` | 空闲会话超时时间(毫秒) |
|
||||
| `resumeKeySessionBindingTimeoutMs` | `number` | `300000` | 恢复键绑定超时时间(毫秒) |
|
||||
|
||||
### 队列项目结构
|
||||
|
||||
```typescript
|
||||
interface QueueItem {
|
||||
item_id: string;
|
||||
issue_id?: string;
|
||||
sessionKey?: string;
|
||||
status: QueueItemStatus;
|
||||
execution_order: number;
|
||||
created_at?: number;
|
||||
updated_at?: number;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 可访问性
|
||||
|
||||
- **键盘导航**:
|
||||
- <kbd>Tab</kbd> - 在队列项目和控件之间导航
|
||||
- <kbd>Enter</kbd>/<kbd>Space</kbd> - 激活按钮
|
||||
- <kbd>Escape</kbd> - 关闭对话框
|
||||
|
||||
- **ARIA 属性**:
|
||||
- 控制按钮上的 `aria-label`
|
||||
- 状态更新的 `aria-live` 区域
|
||||
- 当前队列项目的 `aria-current`
|
||||
- 队列项目列表上的 `role="list"`
|
||||
|
||||
- **屏幕阅读器支持**:
|
||||
- 状态变化被宣布
|
||||
- 进度更新被朗读
|
||||
- 错误消息被宣布
|
||||
|
||||
---
|
||||
|
||||
## 相关链接
|
||||
|
||||
- [问题中心](/features/issue-hub) - 统一的问题、队列和发现管理
|
||||
- [终端仪表板](/features/terminal) - 带集成队列面板的终端优先工作空间
|
||||
- [发现](/features/discovery) - 发现会话跟踪
|
||||
- [会话](/features/sessions) - 会话管理和详情
|
||||
612
docs/zh-CN/features/terminal.md
Normal file
612
docs/zh-CN/features/terminal.md
Normal file
@@ -0,0 +1,612 @@
|
||||
# 终端仪表板
|
||||
|
||||
## 一句话概述
|
||||
**终端仪表板提供以终端为首的工作空间,具有可调整大小的窗格、浮动面板和用于会话监控与编排的集成工具。**
|
||||
|
||||
---
|
||||
|
||||
## 解决的痛点
|
||||
|
||||
| 痛点 | 当前状态 | 终端仪表板解决方案 |
|
||||
|------|----------|---------------------|
|
||||
| **终端分散** | 多个终端窗口 | 统一的 tmux 风格网格布局 |
|
||||
| **无上下文关联** | 无法将终端输出与问题关联 | 关联高亮提供程序 |
|
||||
| **面板过多** | 固定布局浪费空间 | 浮动面板(互斥) |
|
||||
| **缺少工具** | 在应用间切换 | 集成问题、队列、检查器、调度器 |
|
||||
| **工作空间有限** | 无法同时查看代码和终端 | 可调整大小的三列布局 |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
**位置**: `ccw/frontend/src/pages/TerminalDashboardPage.tsx`
|
||||
|
||||
**用途**: 用于多终端会话管理的终端优先布局,配备集成工具和可调整大小的面板。
|
||||
|
||||
**访问方式**: 导航 → 终端仪表板 (`/terminal-dashboard`)
|
||||
|
||||
**布局**:
|
||||
```
|
||||
+--------------------------------------------------------------------------+
|
||||
| 仪表板工具栏(面板切换、布局预设、全屏) |
|
||||
+--------------------------------------------------------------------------+
|
||||
| +----------------+-------------------------------------------+------------+ |
|
||||
| | 会话 | 终端网格(tmux 风格) | 文件 | |
|
||||
| | 分组树 | +----------+ +----------+ | 侧边栏 | |
|
||||
| | (可调整大小) | | 终端 1 | | 终端 2 | |(可调整大小)| |
|
||||
| | | +----------+ +----------+ | | |
|
||||
| | | +----------+ +----------+ | | |
|
||||
| | | | 终端 3 | | 终端 4 | | | |
|
||||
| | | +----------+ +----------+ | | |
|
||||
| +----------------+-------------------------------------------+------------+ |
|
||||
+--------------------------------------------------------------------------+
|
||||
| [浮动面板: 问题+队列 或 检查器 或 执行 或 调度器] |
|
||||
+--------------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 实时演示
|
||||
|
||||
:::demo TerminalDashboardOverview
|
||||
# terminal-dashboard-overview.tsx
|
||||
/**
|
||||
* Terminal Dashboard Overview Demo
|
||||
* Shows the three-column layout with resizable panes and toolbar
|
||||
*/
|
||||
export function TerminalDashboardOverview() {
|
||||
const [fileSidebarOpen, setFileSidebarOpen] = React.useState(true)
|
||||
const [sessionSidebarOpen, setSessionSidebarOpen] = React.useState(true)
|
||||
const [activePanel, setActivePanel] = React.useState(null)
|
||||
|
||||
return (
|
||||
<div className="h-[600px] flex flex-col bg-background">
|
||||
{/* Toolbar */}
|
||||
<div className="flex items-center justify-between px-3 py-2 border-b bg-muted/30">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm font-medium">Terminal Dashboard</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
{['Sessions', 'Files', 'Issues', 'Queue', 'Inspector', 'Scheduler'].map((item) => (
|
||||
<button
|
||||
key={item}
|
||||
onClick={() => {
|
||||
if (item === 'Sessions') setSessionSidebarOpen(!sessionSidebarOpen)
|
||||
else if (item === 'Files') setFileSidebarOpen(!fileSidebarOpen)
|
||||
else setActivePanel(activePanel === item.toLowerCase() ? null : item.toLowerCase())
|
||||
}}
|
||||
className={`px-2 py-1 text-xs rounded transition-colors ${
|
||||
(item === 'Sessions' && sessionSidebarOpen) ||
|
||||
(item === 'Files' && fileSidebarOpen) ||
|
||||
activePanel === item.toLowerCase()
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: 'hover:bg-accent'
|
||||
}`}
|
||||
>
|
||||
{item}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Layout */}
|
||||
<div className="flex-1 flex min-h-0">
|
||||
{/* Session Sidebar */}
|
||||
{sessionSidebarOpen && (
|
||||
<div className="w-60 border-r flex flex-col">
|
||||
<div className="px-3 py-2 text-xs font-semibold border-b bg-muted/30">
|
||||
Session Groups
|
||||
</div>
|
||||
<div className="flex-1 p-2 space-y-1 text-sm overflow-auto">
|
||||
{['Active Sessions', 'Completed', 'Archived'].map((group) => (
|
||||
<div key={group}>
|
||||
<div className="flex items-center gap-1 px-2 py-1 rounded hover:bg-accent cursor-pointer">
|
||||
<span className="text-xs">▼</span>
|
||||
<span>{group}</span>
|
||||
</div>
|
||||
<div className="ml-4 space-y-0.5">
|
||||
<div className="px-2 py-1 text-xs text-muted-foreground hover:bg-accent rounded cursor-pointer">
|
||||
Session 1
|
||||
</div>
|
||||
<div className="px-2 py-1 text-xs text-muted-foreground hover:bg-accent rounded cursor-pointer">
|
||||
Session 2
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Terminal Grid */}
|
||||
<div className="flex-1 bg-muted/20 p-2">
|
||||
<div className="grid grid-cols-2 grid-rows-2 gap-2 h-full">
|
||||
{[1, 2, 3, 4].map((i) => (
|
||||
<div key={i} className="bg-background border rounded p-3 font-mono text-xs">
|
||||
<div className="text-green-500 mb-2">$ Terminal {i}</div>
|
||||
<div className="text-muted-foreground">
|
||||
<div>Working directory: /project</div>
|
||||
<div>Type a command to begin...</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* File Sidebar */}
|
||||
{fileSidebarOpen && (
|
||||
<div className="w-64 border-l flex flex-col">
|
||||
<div className="px-3 py-2 text-xs font-semibold border-b bg-muted/30">
|
||||
Project Files
|
||||
</div>
|
||||
<div className="flex-1 p-2 text-sm overflow-auto">
|
||||
<div className="space-y-1">
|
||||
{['src', 'docs', 'tests', 'package.json', 'README.md'].map((item) => (
|
||||
<div key={item} className="px-2 py-1 rounded hover:bg-accent cursor-pointer flex items-center gap-2">
|
||||
<span className="text-xs text-muted-foreground">📁</span>
|
||||
{item}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Floating Panel */}
|
||||
{activePanel && (
|
||||
<div className="absolute top-12 right-4 w-80 bg-background border rounded-lg shadow-lg">
|
||||
<div className="flex items-center justify-between px-3 py-2 border-b">
|
||||
<span className="text-sm font-medium capitalize">{activePanel} Panel</span>
|
||||
<button onClick={() => setActivePanel(null)} className="text-xs hover:bg-accent px-2 py-1 rounded">
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-4 text-sm text-muted-foreground">
|
||||
{activePanel} content placeholder
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 核心功能
|
||||
|
||||
| 功能 | 描述 |
|
||||
|------|------|
|
||||
| **三列布局** | 使用 Allotment 的可调整大小窗格:会话树(左)、终端网格(中)、文件侧边栏(右) |
|
||||
| **终端网格** | Tmux 风格的分割窗格,带布局预设(单格、水平分割、垂直分割、2x2 网格) |
|
||||
| **会话分组树** | CLI 会话的分层视图,按标签分组 |
|
||||
| **浮动面板** | 互斥的叠加面板(问题+队列、检查器、执行监控器、调度器) |
|
||||
| **关联高亮** | 终端、问题和队列项之间的跨面板链接 |
|
||||
| **布局预设** | 快速布局按钮:单格窗格、水平分割、垂直分割、2x2 网格 |
|
||||
| **启动 CLI** | 用于创建新 CLI 会话的配置模态框,可选择工具、模型和设置 |
|
||||
| **全屏模式** | 沉浸模式隐藏应用框架(标题栏 + 侧边栏) |
|
||||
| **功能标志** | 面板可见性由功能标志控制(队列、检查器、执行监控器) |
|
||||
|
||||
---
|
||||
|
||||
## 组件层次结构
|
||||
|
||||
```
|
||||
TerminalDashboardPage
|
||||
├── AssociationHighlightProvider(上下文)
|
||||
├── DashboardToolbar
|
||||
│ ├── 布局预设按钮(单格 | 水平分割 | 垂直分割 | 2x2 网格)
|
||||
│ ├── 面板切换(会话 | 文件 | 问题 | 队列 | 检查器 | 执行 | 调度器)
|
||||
│ ├── 全屏切换
|
||||
│ └── 启动 CLI 按钮
|
||||
├── Allotment(三列布局)
|
||||
│ ├── SessionGroupTree
|
||||
│ │ └── 会话分组项(可折叠)
|
||||
│ ├── TerminalGrid
|
||||
│ │ ├── GridGroupRenderer(递归)
|
||||
│ │ └── TerminalPane
|
||||
│ └── FileSidebarPanel
|
||||
│ └── 文件树视图
|
||||
└── FloatingPanel(多个,互斥)
|
||||
├── 问题+队列(分割面板)
|
||||
│ ├── IssuePanel
|
||||
│ └── QueueListColumn
|
||||
├── QueuePanel(功能标志)
|
||||
├── InspectorContent(功能标志)
|
||||
├── ExecutionMonitorPanel(功能标志)
|
||||
└── SchedulerPanel
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Props API
|
||||
|
||||
### TerminalDashboardPage
|
||||
|
||||
| Prop | 类型 | 默认值 | 描述 |
|
||||
|------|------|--------|------|
|
||||
| - | - | - | 此页面组件不接受任何 props(状态通过 hooks 和 Zustand stores 管理) |
|
||||
|
||||
### DashboardToolbar
|
||||
|
||||
| Prop | 类型 | 默认值 | 描述 |
|
||||
|------|------|--------|------|
|
||||
| `activePanel` | `PanelId \| null` | `null` | 当前活动的浮动面板 |
|
||||
| `onTogglePanel` | `(panelId: PanelId) => void` | - | 切换面板可见性的回调 |
|
||||
| `isFileSidebarOpen` | `boolean` | `true` | 文件侧边栏可见性状态 |
|
||||
| `onToggleFileSidebar` | `() => void` | - | 切换文件侧边栏回调 |
|
||||
| `isSessionSidebarOpen` | `boolean` | `true` | 会话侧边栏可见性状态 |
|
||||
| `onToggleSessionSidebar` | `() => void` | - | 切换会话侧边栏回调 |
|
||||
| `isFullscreen` | `boolean` | `false` | 全屏模式状态 |
|
||||
| `onToggleFullscreen` | `() => void` | - | 切换全屏回调 |
|
||||
|
||||
### FloatingPanel
|
||||
|
||||
| Prop | 类型 | 默认值 | 描述 |
|
||||
|------|------|--------|------|
|
||||
| `isOpen` | `boolean` | `false` | 面板打开状态 |
|
||||
| `onClose` | `() => void` | - | 关闭回调 |
|
||||
| `title` | `string` | - | 面板标题 |
|
||||
| `side` | `'left' \| 'right'` | `'left'` | 面板侧边 |
|
||||
| `width` | `number` | `400` | 面板宽度(像素) |
|
||||
| `children` | `ReactNode` | - | 面板内容 |
|
||||
|
||||
---
|
||||
|
||||
## 状态管理
|
||||
|
||||
### 本地状态
|
||||
|
||||
| 状态 | 类型 | 描述 |
|
||||
|------|------|------|
|
||||
| `activePanel` | `PanelId \| null` | 当前活动的浮动面板(互斥) |
|
||||
| `isFileSidebarOpen` | `boolean` | 文件侧边栏可见性 |
|
||||
| `isSessionSidebarOpen` | `boolean` | 会话侧边栏可见性 |
|
||||
|
||||
### Zustand Stores
|
||||
|
||||
| Store | 选择器 | 用途 |
|
||||
|-------|--------|------|
|
||||
| `workflowStore` | `selectProjectPath` | 文件侧边栏的当前项目路径 |
|
||||
| `appStore` | `selectIsImmersiveMode` | 全屏模式状态 |
|
||||
| `configStore` | `featureFlags` | 功能标志配置 |
|
||||
| `terminalGridStore` | 网格布局和焦点窗格状态 |
|
||||
| `executionMonitorStore` | 活动执行计数 |
|
||||
| `queueSchedulerStore` | 调度器状态和设置 |
|
||||
|
||||
### 面板 ID 类型
|
||||
|
||||
```typescript
|
||||
type PanelId = 'issues' | 'queue' | 'inspector' | 'execution' | 'scheduler';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基本终端仪表板
|
||||
|
||||
```tsx
|
||||
import { TerminalDashboardPage } from '@/pages/TerminalDashboardPage'
|
||||
|
||||
// 终端仪表板自动在 /terminal-dashboard 渲染
|
||||
// 不需要 props - 布局状态内部管理
|
||||
```
|
||||
|
||||
### 使用 FloatingPanel 组件
|
||||
|
||||
```tsx
|
||||
import { FloatingPanel } from '@/components/terminal-dashboard/FloatingPanel'
|
||||
import { IssuePanel } from '@/components/terminal-dashboard/IssuePanel'
|
||||
|
||||
function CustomLayout() {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<FloatingPanel
|
||||
isOpen={isOpen}
|
||||
onClose={() => setIsOpen(false)}
|
||||
title="问题"
|
||||
side="left"
|
||||
width={700}
|
||||
>
|
||||
<IssuePanel />
|
||||
</FloatingPanel>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 交互演示
|
||||
|
||||
### 布局预设演示
|
||||
|
||||
:::demo TerminalLayoutPresets
|
||||
# terminal-layout-presets.tsx
|
||||
/**
|
||||
* Terminal Layout Presets Demo
|
||||
* Interactive layout preset buttons
|
||||
*/
|
||||
export function TerminalLayoutPresets() {
|
||||
const [layout, setLayout] = React.useState('grid-2x2')
|
||||
|
||||
const layouts = {
|
||||
single: 'grid-cols-1 grid-rows-1',
|
||||
'split-h': 'grid-cols-2 grid-rows-1',
|
||||
'split-v': 'grid-cols-1 grid-rows-2',
|
||||
'grid-2x2': 'grid-cols-2 grid-rows-2',
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-6 bg-background space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-sm font-semibold">Terminal Layout Presets</h3>
|
||||
<div className="flex gap-2">
|
||||
{Object.keys(layouts).map((preset) => (
|
||||
<button
|
||||
key={preset}
|
||||
onClick={() => setLayout(preset)}
|
||||
className={`px-3 py-1.5 text-xs rounded transition-colors ${
|
||||
layout === preset
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: 'border hover:bg-accent'
|
||||
}`}
|
||||
>
|
||||
{preset.replace('-', ' ').toUpperCase()}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`grid gap-2 h-64 ${layouts[layout]}`}>
|
||||
{Array.from({ length: layout === 'single' ? 1 : layout.includes('2x') ? 4 : 2 }).map((_, i) => (
|
||||
<div key={i} className="bg-muted/20 border rounded p-4 font-mono text-xs">
|
||||
<div className="text-green-500">$ Terminal {i + 1}</div>
|
||||
<div className="text-muted-foreground mt-1">Ready for input...</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
:::
|
||||
|
||||
### 浮动面板演示
|
||||
|
||||
:::demo FloatingPanelsDemo
|
||||
# floating-panels-demo.tsx
|
||||
/**
|
||||
* Floating Panels Demo
|
||||
* Mutually exclusive overlay panels
|
||||
*/
|
||||
export function FloatingPanelsDemo() {
|
||||
const [activePanel, setActivePanel] = React.useState(null)
|
||||
|
||||
const panels = [
|
||||
{ id: 'issues', title: 'Issues + Queue', side: 'left', width: 700 },
|
||||
{ id: 'queue', title: 'Queue', side: 'right', width: 400 },
|
||||
{ id: 'inspector', title: 'Inspector', side: 'right', width: 360 },
|
||||
{ id: 'execution', title: 'Execution Monitor', side: 'right', width: 380 },
|
||||
{ id: 'scheduler', title: 'Scheduler', side: 'right', width: 340 },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="relative h-[500px] p-6 bg-background border rounded-lg">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="text-sm font-semibold">Floating Panels</h3>
|
||||
<div className="flex gap-2">
|
||||
{panels.map((panel) => (
|
||||
<button
|
||||
key={panel.id}
|
||||
onClick={() => setActivePanel(activePanel === panel.id ? null : panel.id)}
|
||||
className={`px-3 py-1.5 text-xs rounded transition-colors ${
|
||||
activePanel === panel.id
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: 'border hover:bg-accent'
|
||||
}`}
|
||||
>
|
||||
{panel.title}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="h-[380px] bg-muted/20 border rounded flex items-center justify-center">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{activePanel ? `"${panels.find((p) => p.id === activePanel)?.title}" panel is open` : 'Click a button to open a floating panel'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Floating Panel Overlay */}
|
||||
{activePanel && (
|
||||
<div
|
||||
className={`absolute top-16 border rounded-lg shadow-lg bg-background ${
|
||||
panels.find((p) => p.id === activePanel)?.side === 'left' ? 'left-6' : 'right-6'
|
||||
}`}
|
||||
style={{ width: panels.find((p) => p.id === activePanel)?.width }}
|
||||
>
|
||||
<div className="flex items-center justify-between px-3 py-2 border-b">
|
||||
<span className="text-sm font-medium">{panels.find((p) => p.id === activePanel)?.title}</span>
|
||||
<button
|
||||
onClick={() => setActivePanel(null)}
|
||||
className="text-xs hover:bg-accent px-2 py-1 rounded"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-4 text-sm text-muted-foreground">
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 rounded-full bg-blue-500"/>
|
||||
<span>Item 1</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 rounded-full bg-green-500"/>
|
||||
<span>Item 2</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 rounded-full bg-amber-500"/>
|
||||
<span>Item 3</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
:::
|
||||
|
||||
### 可调整大小窗格演示
|
||||
|
||||
:::demo ResizablePanesDemo
|
||||
# resizable-panes-demo.tsx
|
||||
/**
|
||||
* Resizable Panes Demo
|
||||
* Simulates the Allotment resizable split behavior
|
||||
*/
|
||||
export function ResizablePanesDemo() {
|
||||
const [leftWidth, setLeftWidth] = React.useState(240)
|
||||
const [rightWidth, setRightWidth] = React.useState(280)
|
||||
const [isDragging, setIsDragging] = React.useState(null)
|
||||
|
||||
const handleDragStart = (side) => (e) => {
|
||||
setIsDragging(side)
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
const handleMouseMove = (e) => {
|
||||
if (isDragging === 'left') {
|
||||
setLeftWidth(Math.max(180, Math.min(320, e.clientX)))
|
||||
} else if (isDragging === 'right') {
|
||||
setRightWidth(Math.max(200, Math.min(400, window.innerWidth - e.clientX)))
|
||||
}
|
||||
}
|
||||
|
||||
const handleMouseUp = () => setIsDragging(null)
|
||||
|
||||
if (isDragging) {
|
||||
window.addEventListener('mousemove', handleMouseMove)
|
||||
window.addEventListener('mouseup', handleMouseUp)
|
||||
return () => {
|
||||
window.removeEventListener('mousemove', handleMouseMove)
|
||||
window.removeEventListener('mouseup', handleMouseUp)
|
||||
}
|
||||
}
|
||||
}, [isDragging])
|
||||
|
||||
return (
|
||||
<div className="h-[400px] flex bg-background border rounded-lg overflow-hidden">
|
||||
{/* Left Sidebar */}
|
||||
<div style={{ width: leftWidth }} className="border-r flex flex-col">
|
||||
<div className="px-3 py-2 text-xs font-semibold border-b bg-muted/30">
|
||||
Session Groups
|
||||
</div>
|
||||
<div className="flex-1 p-2 text-sm space-y-1">
|
||||
{['Active Sessions', 'Completed'].map((g) => (
|
||||
<div key={g} className="px-2 py-1 hover:bg-accent rounded cursor-pointer">{g}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Left Drag Handle */}
|
||||
<div
|
||||
onMouseDown={handleDragStart('left')}
|
||||
className={`w-1 bg-border hover:bg-primary cursor-col-resize transition-colors ${
|
||||
isDragging === 'left' ? 'bg-primary' : ''
|
||||
}`}
|
||||
/>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="flex-1 bg-muted/20 flex items-center justify-center">
|
||||
<span className="text-sm text-muted-foreground">Terminal Grid Area</span>
|
||||
</div>
|
||||
|
||||
{/* Right Drag Handle */}
|
||||
<div
|
||||
onMouseDown={handleDragStart('right')}
|
||||
className={`w-1 bg-border hover:bg-primary cursor-col-resize transition-colors ${
|
||||
isDragging === 'right' ? 'bg-primary' : ''
|
||||
}`}
|
||||
/>
|
||||
|
||||
{/* Right Sidebar */}
|
||||
<div style={{ width: rightWidth }} className="border-l flex flex-col">
|
||||
<div className="px-3 py-2 text-xs font-semibold border-b bg-muted/30">
|
||||
Project Files
|
||||
</div>
|
||||
<div className="flex-1 p-2 text-sm space-y-1">
|
||||
{['src/', 'docs/', 'tests/'].map((f) => (
|
||||
<div key={f} className="px-2 py-1 hover:bg-accent rounded cursor-pointer">{f}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 配置
|
||||
|
||||
### 功能标志
|
||||
|
||||
| 标志 | 控制 |
|
||||
|------|------|
|
||||
| `dashboardQueuePanelEnabled` | 队列面板可见性 |
|
||||
| `dashboardInspectorEnabled` | 检查器面板可见性 |
|
||||
| `dashboardExecutionMonitorEnabled` | 执行监控器面板可见性 |
|
||||
|
||||
### 布局预设
|
||||
|
||||
| 预设 | 布局 |
|
||||
|------|------|
|
||||
| **单格** | 一个终端窗格 |
|
||||
| **水平分割** | 两个窗格并排 |
|
||||
| **垂直分割** | 两个窗格垂直堆叠 |
|
||||
| **2x2 网格** | 2x2 网格中的四个窗格 |
|
||||
|
||||
### 面板类型
|
||||
|
||||
| 面板 | 内容 | 位置 | 功能标志 |
|
||||
|------|------|------|----------|
|
||||
| **问题+队列** | 组合的问题面板 + 队列列表列 | 左侧(叠加) | - |
|
||||
| **队列** | 完整的队列管理面板 | 右侧(叠加) | `dashboardQueuePanelEnabled` |
|
||||
| **检查器** | 关联链检查器 | 右侧(叠加) | `dashboardInspectorEnabled` |
|
||||
| **执行监控器** | 实时执行跟踪 | 右侧(叠加) | `dashboardExecutionMonitorEnabled` |
|
||||
| **调度器** | 队列调度器控制 | 右侧(叠加) | - |
|
||||
|
||||
---
|
||||
|
||||
## 可访问性
|
||||
|
||||
- **键盘导航**:
|
||||
- <kbd>Tab</kbd> - 在工具栏按钮之间导航
|
||||
- <kbd>Enter</kbd>/<kbd>Space</kbd> - 激活工具栏按钮
|
||||
- <kbd>Escape</kbd> - 关闭浮动面板
|
||||
- <kbd>F11</kbd> - 切换全屏模式
|
||||
|
||||
- **ARIA 属性**:
|
||||
- 工具栏按钮上的 `aria-label`
|
||||
- 侧边栏切换上的 `aria-expanded`
|
||||
- 非活动浮动面板上的 `aria-hidden`
|
||||
- 浮动面板上的 `role="dialog"`
|
||||
|
||||
- **屏幕阅读器支持**:
|
||||
- 切换面板时宣布面板状态
|
||||
- 布局更改被宣布
|
||||
- 面板打开/关闭时的焦点管理
|
||||
|
||||
---
|
||||
|
||||
## 相关链接
|
||||
|
||||
- [编排器](/features/orchestrator) - 可视化工作流编辑器
|
||||
- [会话](/features/sessions) - 会话管理
|
||||
- [问题中心](/features/issue-hub) - 问题、队列、发现
|
||||
- [资源管理器](/features/explorer) - 文件资源管理器
|
||||
- [队列](/features/queue) - 队列管理独立页面
|
||||
Reference in New Issue
Block a user