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:
catlog22
2026-03-01 10:52:46 +08:00
parent a753327acc
commit 2fb93d20e0
34 changed files with 6908 additions and 257 deletions

View File

@@ -1,7 +1,6 @@
# Dashboard
## One-Liner
**The Dashboard provides an at-a-glance overview of your project's workflow status, statistics, and recent activity through an intuitive widget-based interface.**
---
@@ -18,16 +17,15 @@
---
## Page Overview
## Overview
**Location**: `ccw/frontend/src/pages/HomePage.tsx`
**Purpose**: Dashboard home page providing project overview, statistics, workflow status, and recent activity monitoring.
**Access**: Navigation → Dashboard (default home page)
### Layout
**Access**: Navigation → Dashboard (default home page at `/`)
**Layout**:
```
+--------------------------------------------------------------------------+
| Dashboard Header (title + refresh) |
@@ -55,6 +53,149 @@
---
## Live Demo
:::demo DashboardOverview
# dashboard-overview.tsx
/**
* Dashboard Overview Demo
* Shows the main dashboard layout with widgets
*/
export function DashboardOverview() {
return (
<div className="space-y-6 p-6 bg-background min-h-[600px]">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold">Dashboard</h1>
<p className="text-sm text-muted-foreground">
Project overview and activity monitoring
</p>
</div>
<button className="px-3 py-1.5 text-sm border rounded-md hover:bg-accent">
Refresh
</button>
</div>
{/* Workflow Stats Widget */}
<div className="border rounded-lg overflow-hidden">
<div className="p-4 border-b bg-muted/30">
<h2 className="font-semibold">Project Overview & Statistics</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">Statistics</div>
<div className="grid grid-cols-2 gap-2">
{[
{ label: 'Active Sessions', value: '12', color: 'text-blue-500' },
{ label: 'Total Tasks', value: '48', color: 'text-green-500' },
{ label: 'Completed', value: '35', color: 'text-emerald-500' },
{ label: 'Pending', 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">Workflow Status</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>Completed: 70%</span>
</div>
</div>
</div>
<div className="space-y-3">
<div className="text-xs font-medium text-muted-foreground">Recent Session</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">Feature: Auth Flow</span>
<span className="text-xs px-2 py-0.5 rounded-full bg-green-500/20 text-green-600">Running</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>Implement login</span>
</div>
<div className="flex items-center gap-2 text-xs">
<div className="w-3 h-3 rounded bg-amber-500"/>
<span>Add 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">Test flow</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{/* Recent Sessions Widget */}
<div className="border rounded-lg overflow-hidden">
<div className="border-b bg-muted/30">
<div className="flex gap-1 p-2">
{['All Tasks', 'Workflow', 'Lite Tasks'].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: 'Refactor UI Components', status: 'In Progress', progress: 65 },
{ name: 'Fix Login Bug', status: 'Pending', progress: 0 },
{ name: 'Add Dark Mode', status: 'Completed', 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 === 'Completed' ? 'bg-green-500/20 text-green-600' :
task.status === 'In Progress' ? '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>
)
}
:::
---
## Core Features
| Feature | Description |
@@ -68,85 +209,399 @@
---
## Usage Guide
## Component Hierarchy
### Basic Workflow
1. **View Project Overview**: Check the project info banner for tech stack and development index
2. **Monitor Statistics**: Review mini stat cards for current project metrics and trends
3. **Track Workflow Status**: View pie chart for session status distribution
4. **Browse Active Sessions**: Use session carousel to see task details and progress
5. **Access Recent Work**: Switch between All Tasks/Workflow/Lite Tasks tabs to find specific sessions
### Key Interactions
| Interaction | How to Use |
|-------------|------------|
| **Expand Project Details** | Click the chevron button in the project banner to show architecture, components, patterns |
| **Navigate Sessions** | Click arrow buttons or wait for auto-rotation (5s interval) in the carousel |
| **View Session Details** | Click on any session card to navigate to session detail page |
| **Filter Recent Tasks** | Click tab buttons to filter by task type (All/Workflow/Lite) |
| **Refresh Dashboard** | Click the refresh button in the header to reload all data |
### Index Status Indicator
| Status | Icon | Meaning |
|--------|------|---------|
| **Building** | Pulsing blue dot | Code index is being built/updated |
| **Completed** | Green dot | Index is up-to-date |
| **Idle** | Gray dot | Index status is unknown/idle |
| **Failed** | Red dot | Index build failed |
```
HomePage
├── DashboardHeader
│ ├── Title
│ └── Refresh Action Button
├── WorkflowTaskWidget
│ ├── ProjectInfoBanner (expandable)
│ │ ├── Project Name & Description
│ │ ├── Tech Stack Badges
│ │ ├── Quick Stats Cards
│ │ ├── Index Status Indicator
│ │ ├── Architecture Section
│ │ ├── Key Components
│ │ └── Design Patterns
│ ├── Stats Section
│ │ └── MiniStatCard (6 cards with Sparkline)
│ ├── WorkflowStatusChart
│ │ └── Pie Chart with Legend
│ └── SessionCarousel
│ ├── Navigation Arrows
│ └── Session Cards (Task List)
└── RecentSessionsWidget
├── Tab Navigation (All | Workflow | Lite)
├── Task Grid
│ └── TaskItemCard
└── Loading/Empty States
```
---
## Components Reference
## Props API
### Main Components
### HomePage Component
| Component | Location | Purpose |
|-----------|----------|---------|
| `DashboardHeader` | `@/components/dashboard/DashboardHeader.tsx` | Page header with title and refresh action |
| `WorkflowTaskWidget` | `@/components/dashboard/widgets/WorkflowTaskWidget.tsx` | Combined widget with project info, stats, workflow status, and session carousel |
| `RecentSessionsWidget` | `@/components/dashboard/widgets/RecentSessionsWidget.tsx` | Recent sessions across workflow and lite tasks |
| `MiniStatCard` | (internal to WorkflowTaskWidget) | Individual stat card with optional sparkline |
| `HomeEmptyState` | (internal to WorkflowTaskWidget) | Empty state display when no sessions exist |
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| - | - | - | This page component accepts no props (data fetched via hooks) |
### State Management
### WorkflowTaskWidget
- **Local state**:
- `hasError` - Error tracking for critical failures
- `projectExpanded` - Project info banner expansion state
- `currentSessionIndex` - Active session in carousel
- `activeTab` - Recent sessions widget filter tab
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `className` | `string` | `undefined` | Additional CSS classes for styling |
- **Custom hooks**:
- `useWorkflowStatusCounts` - Session status distribution data
- `useDashboardStats` - Statistics with auto-refresh (60s)
- `useProjectOverview` - Project information and tech stack
- `useIndexStatus` - Real-time index status (30s refresh)
- `useSessions` - Active sessions data
- `useLiteTasks` - Lite tasks data for recent widget
### RecentSessionsWidget
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `className` | `string` | `undefined` | Additional CSS classes |
| `maxItems` | `number` | `6` | Maximum number of items to display |
---
## Configuration
## Usage Examples
No configuration required. The dashboard automatically fetches data from the backend.
### Basic Dashboard
### Auto-Refresh Intervals
```tsx
import { HomePage } from '@/pages/HomePage'
| Data Type | Interval |
|-----------|----------|
| Dashboard stats | 60 seconds |
| Index status | 30 seconds |
| Discovery sessions | 3 seconds (on discovery page) |
// The dashboard is automatically rendered at the root route (/)
// No props needed - data is fetched via hooks
```
### Embedding WorkflowTaskWidget
```tsx
import { WorkflowTaskWidget } from '@/components/dashboard/widgets/WorkflowTaskWidget'
function CustomDashboard() {
return (
<div className="p-6">
<WorkflowTaskWidget />
</div>
)
}
```
### Custom Recent Sessions Widget
```tsx
import { RecentSessionsWidget } from '@/components/dashboard/widgets/RecentSessionsWidget'
function ActivityFeed() {
return (
<div className="p-6">
<RecentSessionsWidget maxItems={10} />
</div>
)
}
```
---
## State Management
### Local State
| State | Type | Description |
|-------|------|-------------|
| `hasError` | `boolean` | Error tracking for critical failures |
| `projectExpanded` | `boolean` | Project info banner expansion state |
| `currentSessionIndex` | `number` | Active session index in carousel |
| `activeTab` | `'all' \| 'workflow' \| 'lite'` | Recent sessions widget filter tab |
### Store Selectors (Zustand)
| Store | Selector | Purpose |
|-------|----------|---------|
| `appStore` | `selectIsImmersiveMode` | Check if immersive mode is active |
### Custom Hooks (Data Fetching)
| Hook | Description | Refetch Interval |
|------|-------------|------------------|
| `useWorkflowStatusCounts` | Session status distribution data | - |
| `useDashboardStats` | Statistics with sparkline data | 60 seconds |
| `useProjectOverview` | Project information and tech stack | - |
| `useIndexStatus` | Real-time index status | 30 seconds |
| `useSessions` | Active sessions data | - |
| `useLiteTasks` | Lite tasks data for recent widget | - |
---
## Interactive Demos
### Statistics Cards Demo
:::demo MiniStatCards
# mini-stat-cards.tsx
/**
* Mini Stat Cards Demo
* Individual statistics cards with sparkline trends
*/
export function MiniStatCards() {
const stats = [
{ label: 'Active Sessions', value: 12, trend: [8, 10, 9, 11, 10, 12, 12], color: 'blue' },
{ label: 'Total Tasks', value: 48, trend: [40, 42, 45, 44, 46, 47, 48], color: 'green' },
{ label: 'Completed', value: 35, trend: [25, 28, 30, 32, 33, 34, 35], color: 'emerald' },
{ label: 'Pending', value: 8, trend: [12, 10, 11, 9, 8, 7, 8], color: 'amber' },
{ label: 'Failed', value: 5, trend: [3, 4, 3, 5, 4, 5, 5], color: 'red' },
{ label: 'Today Activity', 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">Statistics with Sparklines</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>
)
}
:::
### Project Info Banner Demo
:::demo ProjectInfoBanner
# project-info-banner.tsx
/**
* Project Info Banner Demo
* Expandable project information with tech stack
*/
export function ProjectInfoBanner() {
const [expanded, setExpanded] = React.useState(false)
return (
<div className="p-6 bg-background">
<h3 className="text-sm font-semibold mb-4">Project Info Banner</h3>
<div className="border rounded-lg overflow-hidden">
{/* Banner Header */}
<div className="p-4 bg-muted/30 flex items-center justify-between">
<div>
<h4 className="font-semibold">My Awesome Project</h4>
<p className="text-sm text-muted-foreground">A modern web application built with React</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>
{/* Tech Stack Badges */}
<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 Content */}
{expanded && (
<div className="p-4 border-t bg-muted/20 space-y-4">
<div>
<h5 className="text-xs font-semibold mb-2">Architecture</h5>
<div className="text-sm text-muted-foreground space-y-1">
<div>• Component-based UI architecture</div>
<div>• Centralized state management</div>
<div>• RESTful API integration</div>
</div>
</div>
<div>
<h5 className="text-xs font-semibold mb-2">Key Components</h5>
<div className="grid grid-cols-2 gap-2 text-sm">
{['Session Manager', 'Dashboard', 'Task Scheduler', 'Analytics'].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>
)
}
:::
### Session Carousel Demo
:::demo SessionCarousel
# session-carousel.tsx
/**
* Session Carousel Demo
* Auto-rotating session cards with navigation
*/
export function SessionCarousel() {
const [currentIndex, setCurrentIndex] = React.useState(0)
const sessions = [
{
name: 'Feature: User Authentication',
status: 'running',
tasks: [
{ name: 'Implement login form', status: 'completed' },
{ name: 'Add OAuth provider', status: 'in-progress' },
{ name: 'Create session management', status: 'pending' },
],
},
{
name: 'Bug Fix: Memory Leak',
status: 'running',
tasks: [
{ name: 'Identify leak source', status: 'completed' },
{ name: 'Fix cleanup handlers', status: 'in-progress' },
{ name: 'Add unit tests', status: 'pending' },
],
},
{
name: 'Refactor: API Layer',
status: 'planning',
tasks: [
{ name: 'Design new interface', status: 'pending' },
{ name: 'Migrate existing endpoints', status: 'pending' },
{ name: 'Update documentation', 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">Session Carousel (auto-rotates every 5s)</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">Session {currentIndex + 1} of {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}
</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"
>
← Previous
</button>
<button
onClick={() => setCurrentIndex((i) => (i + 1) % sessions.length)}
className="px-3 py-1.5 text-sm border rounded-md hover:bg-accent"
>
Next →
</button>
</div>
</div>
</div>
)
}
:::
---
## Accessibility
- **Keyboard Navigation**:
- <kbd>Tab</kbd> - Navigate through interactive elements
- <kbd>Enter</kbd>/<kbd>Space</kbd> - Activate buttons and cards
- <kbd>Arrow</kbd> keys - Navigate carousel sessions
- **ARIA Attributes**:
- `aria-label` on navigation buttons
- `aria-expanded` on expandable sections
- `aria-live` regions for real-time updates
- **Screen Reader Support**:
- All charts have text descriptions
- Status indicators include text labels
- Navigation is announced properly
---
## Related Links
- [Sessions](/features/sessions) - View and manage all sessions
- [Terminal Dashboard](/features/terminal) - Terminal-first monitoring interface
- [Queue](/features/queue) - Issue execution queue management
- [Discovery](/features/discovery) - Discovery session tracking
- [Memory](/features/memory) - Persistent memory management
- [System Settings](/features/system-settings) - Global application settings
- [Settings](/features/settings) - Global application settings

View File

@@ -1,8 +1,7 @@
# Queue
# Queue Management
## One-Liner
**The Queue page manages issue execution queues, displaying grouped tasks and solutions with conflict detection and merge capabilities.**
**The Queue Management page provides centralized control over issue execution queues with scheduler controls, status monitoring, and session pool management.**
---
@@ -11,153 +10,627 @@
| Pain Point | Current State | Queue Solution |
|------------|---------------|----------------|
| **Disorganized execution** | No unified task queue | Centralized queue with grouped items |
| **Unknown queue status** | Can't tell if queue is ready | Status indicator with conflicts warning |
| **Manual queue management** | No way to control execution | Activate/deactivate/delete with actions |
| **Duplicate handling** | Confusing duplicate items | Merge queues functionality |
| **No visibility** | Don't know what's queued | Stats cards with items/groups/tasks/solutions counts |
| **Unknown scheduler status** | Can't tell if scheduler is running | Real-time status indicator (idle/running/paused) |
| **No execution control** | Can't start/stop queue processing | Start/Pause/Stop controls with confirmation |
| **Concurrency limits** | Too many simultaneous sessions | Configurable max concurrent sessions |
| **No visibility** | Don't know what's queued | Stats cards + item list with status tracking |
| **Resource waste** | Idle sessions consuming resources | Session pool overview with timeout config |
---
## Page Overview
## Overview
**Location**: `ccw/frontend/src/pages/QueuePage.tsx`
**Location**: `ccw/frontend/src/pages/QueuePage.tsx` (legacy), `ccw/frontend/src/components/terminal-dashboard/QueuePanel.tsx` (current)
**Purpose**: View and manage issue execution queues with stats, conflict detection, and queue operations.
**Purpose**: View and manage issue execution queues with scheduler controls, progress tracking, and session pool management.
**Access**: Navigation → Issues → Queue tab OR directly via `/queue` route
### Layout
**Access**: Navigation → Issues → Queue tab OR Terminal Dashboard → Queue floating panel
**Layout**:
```
+--------------------------------------------------------------------------+
| Queue Title [Refresh] |
| Queue Panel Header |
+--------------------------------------------------------------------------+
| Stats Cards |
| +-------------+ +-------------+ +-------------+ +-------------+ |
| | Total Items | | Groups | | Tasks | | Solutions | |
| +-------------+ +-------------+ +-------------+ +-------------+ |
| Scheduler Status Bar |
| +----------------+ +-------------+ +-------------------------------+ |
| | Status Badge | | Progress | | Concurrency (2/2) | |
| +----------------+ +-------------+ +-------------------------------+ |
+--------------------------------------------------------------------------+
| Conflicts Warning (when conflicts exist) |
| Scheduler Controls |
| +--------+ +--------+ +--------+ +-----------+ |
| | Start | | Pause | | Stop | | Config | |
| +--------+ +--------+ +--------+ +-----------+ |
+--------------------------------------------------------------------------+
| Queue Cards (1-2 columns) |
| Queue Items List |
| +---------------------------------------------------------------------+ |
| | QueueCard | |
| | - Queue info | |
| | - Grouped items preview | |
| | - Action buttons (Activate/Deactivate/Delete/Merge) | |
| | QueueItemRow (status, issue_id, session_key, actions) | |
| | - Status icon (pending/executing/completed/blocked/failed) | |
| | - Issue ID / Item ID display | |
| | - Session binding info | |
| | - Progress indicator (for executing items) | |
| +---------------------------------------------------------------------+ |
| | [More queue items...] | |
| +---------------------------------------------------------------------+ |
+--------------------------------------------------------------------------+
| Status Footer (Ready/Pending indicator) |
+--------------------------------------------------------------------------+
| Session Pool Overview (optional) |
| +--------------------------------------------------------------------------+
| | Active Sessions | Idle Sessions | Total Sessions |
| +--------------------------------------------------------------------------+
```
---
## Live Demo
:::demo QueueManagementDemo
# queue-management-demo.tsx
/**
* Queue Management Demo
* Shows scheduler controls and queue items list
*/
export function QueueManagementDemo() {
const [schedulerStatus, setSchedulerStatus] = React.useState('idle')
const [progress, setProgress] = React.useState(0)
const queueItems = [
{ id: '1', status: 'completed', issueId: 'ISSUE-1', sessionKey: 'session-1' },
{ id: '2', status: 'executing', issueId: 'ISSUE-2', sessionKey: 'session-2' },
{ id: '3', status: 'pending', issueId: 'ISSUE-3', sessionKey: null },
{ id: '4', status: 'pending', issueId: 'ISSUE-4', sessionKey: null },
{ id: '5', status: 'blocked', issueId: 'ISSUE-5', sessionKey: null },
]
const statusConfig = {
idle: { label: 'Idle', color: 'bg-gray-500/20 text-gray-600 border-gray-500' },
running: { label: 'Running', color: 'bg-green-500/20 text-green-600 border-green-500' },
paused: { label: 'Paused', color: 'bg-amber-500/20 text-amber-600 border-amber-500' },
}
const itemStatusConfig = {
completed: { icon: '✓', color: 'text-green-500', label: 'Completed' },
executing: { icon: '▶', color: 'text-blue-500', label: 'Executing' },
pending: { icon: '○', color: 'text-gray-400', label: 'Pending' },
blocked: { icon: '✕', color: 'text-red-500', label: 'Blocked' },
failed: { icon: '!', color: 'text-red-500', label: 'Failed' },
}
// Simulate progress when running
React.useEffect(() => {
if (schedulerStatus === 'running') {
const interval = setInterval(() => {
setProgress((p) => (p >= 100 ? 0 : p + 10))
}, 500)
return () => clearInterval(interval)
}
}, [schedulerStatus])
const handleStart = () => {
if (schedulerStatus === 'idle' || schedulerStatus === 'paused') {
setSchedulerStatus('running')
}
}
const handlePause = () => {
if (schedulerStatus === 'running') {
setSchedulerStatus('paused')
}
}
const handleStop = () => {
setSchedulerStatus('idle')
setProgress(0)
}
const currentConfig = statusConfig[schedulerStatus]
return (
<div className="p-6 bg-background space-y-6">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h3 className="text-lg font-semibold">Queue Management</h3>
<p className="text-sm text-muted-foreground">Manage issue execution queue</p>
</div>
</div>
{/* Scheduler Status Bar */}
<div className="border rounded-lg p-4 space-y-4">
<div className="flex items-center justify-between">
<span className={`px-3 py-1 rounded text-xs font-medium border ${currentConfig.color}`}>
{currentConfig.label}
</span>
<div className="flex items-center gap-4 text-sm text-muted-foreground">
<span>{queueItems.filter((i) => i.status === 'completed').length}/{queueItems.length} items</span>
<span>2/2 concurrent</span>
</div>
</div>
{/* Progress Bar */}
{schedulerStatus === 'running' && (
<div className="space-y-1">
<div className="h-2 bg-muted rounded-full overflow-hidden">
<div
className="h-full bg-green-500 rounded-full transition-all"
style={{ width: `${progress}%` }}
/>
</div>
<span className="text-xs text-muted-foreground">{progress}% complete</span>
</div>
)}
{/* Scheduler Controls */}
<div className="flex items-center gap-2">
{(schedulerStatus === 'idle' || schedulerStatus === 'paused') && (
<button
onClick={handleStart}
className="px-4 py-2 text-sm bg-green-500 text-white rounded hover:bg-green-600 flex items-center gap-2"
>
<span>▶</span> Start
</button>
)}
{schedulerStatus === 'running' && (
<button
onClick={handlePause}
className="px-4 py-2 text-sm bg-amber-500 text-white rounded hover:bg-amber-600 flex items-center gap-2"
>
<span>⏸</span> Pause
</button>
)}
{schedulerStatus !== 'idle' && (
<button
onClick={handleStop}
className="px-4 py-2 text-sm bg-red-500 text-white rounded hover:bg-red-600 flex items-center gap-2"
>
<span>⬛</span> Stop
</button>
)}
<button className="px-4 py-2 text-sm border rounded hover:bg-accent">
Config
</button>
</div>
</div>
{/* Queue Items List */}
<div className="border rounded-lg">
<div className="px-4 py-3 border-b bg-muted/30">
<h4 className="text-sm font-semibold">Queue Items</h4>
</div>
<div className="divide-y max-h-80 overflow-auto">
{queueItems.map((item) => {
const config = itemStatusConfig[item.status]
return (
<div key={item.id} className="px-4 py-3 flex items-center gap-4 hover:bg-accent/50">
<span className={`text-lg ${config.color}`}>{config.icon}</span>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<span className="text-sm font-medium">{item.issueId}</span>
<span className={`text-xs px-2 py-0.5 rounded-full ${config.color} bg-opacity-10`}>
{config.label}
</span>
</div>
{item.sessionKey && (
<div className="text-xs text-muted-foreground mt-1">
Session: {item.sessionKey}
</div>
)}
</div>
{item.status === 'executing' && (
<div className="w-20 h-1.5 bg-muted rounded-full overflow-hidden">
<div className="h-full bg-blue-500 animate-pulse" style={{ width: '60%' }}/>
</div>
)}
</div>
)
})}
</div>
</div>
</div>
)
}
:::
---
## Core Features
| Feature | Description |
|---------|-------------|
| **Stats Cards** | Four metric cards showing total items, groups, tasks, and solutions counts |
| **Conflicts Warning** | Banner alert when conflicts exist, showing count and description |
| **Queue Card** | Displays queue information with grouped items preview and action buttons |
| **Activate/Deactivate** | Toggle queue active state for execution control |
| **Delete Queue** | Remove queue with confirmation dialog |
| **Merge Queues** | Combine multiple queues (if multiple exist) |
| **Status Footer** | Visual indicator showing if queue is ready (active) or pending (inactive/conflicts) |
| **Loading State** | Skeleton placeholders during data fetch |
| **Empty State** | Friendly message when no queue exists |
| **Scheduler Status** | Real-time status indicator (idle/running/paused) with visual badge |
| **Progress Tracking** | Progress bar showing overall queue completion percentage |
| **Start/Pause/Stop Controls** | Control queue execution with confirmation dialog for stop action |
| **Concurrency Display** | Shows current active sessions vs max concurrent sessions |
| **Queue Items List** | Scrollable list of all queue items with status, issue ID, and session binding |
| **Status Icons** | Visual indicators for item status (pending/executing/completed/blocked/failed) |
| **Session Pool** | Overview of active, idle, and total sessions in the pool |
| **Config Panel** | Adjust max concurrent sessions and timeout settings |
| **Empty State** | Friendly message when queue is empty with instructions to add items |
---
## Usage Guide
## Component Hierarchy
### Basic Workflow
1. **Check Queue Status**: Review stats cards and status footer to understand queue state
2. **Review Conflicts**: If conflicts warning is shown, resolve conflicts before activation
3. **Activate Queue**: Click "Activate" to enable queue for execution (only if no conflicts)
4. **Deactivate Queue**: Click "Deactivate" to pause execution
5. **Delete Queue**: Click "Delete" to remove the queue (requires confirmation)
6. **Merge Queues**: Use merge action to combine multiple queues when applicable
### Key Interactions
| Interaction | How to Use |
|-------------|------------|
| **Refresh queue** | Click the refresh button to reload queue data |
| **Activate queue** | Click the Activate button on the queue card |
| **Deactivate queue** | Click the Deactivate button to pause the queue |
| **Delete queue** | Click the Delete button, confirm in the dialog |
| **Merge queues** | Select source and target queues, click merge |
| **View status** | Check the status footer for ready/pending indication |
### Queue Status
| Status | Condition | Appearance |
|--------|-----------|------------|
| **Ready/Active** | Total items > 0 AND no conflicts | Green badge with checkmark |
| **Pending/Inactive** | No items OR conflicts exist | Gray/amber badge with clock icon |
### Conflict Resolution
When conflicts are detected:
1. A warning banner appears showing conflict count
2. Queue cannot be activated until conflicts are resolved
3. Status footer shows "Pending" state
4. Resolve conflicts through the Issues panel or related workflows
```
QueuePage (legacy) / QueuePanel (current)
├── QueuePanelHeader
│ ├── Title
│ └── Tab Switcher (Queue | Orchestrator)
├── SchedulerBar (inline in QueueListColumn)
│ ├── Status Badge
│ ├── Progress + Concurrency
│ └── Control Buttons (Play/Pause/Stop)
├── QueueItemsList
│ └── QueueItemRow (repeating)
│ ├── Status Icon
│ ├── Issue ID / Item ID
│ ├── Session Binding
│ └── Progress (for executing items)
└── SchedulerPanel (standalone)
├── Status Section
├── Progress Bar
├── Control Buttons
├── Config Section
│ ├── Max Concurrent Sessions
│ ├── Session Idle Timeout
│ └── Resume Key Binding Timeout
└── Session Pool Overview
```
---
## Components Reference
## Props API
### Main Components
### QueuePanel
| Component | Location | Purpose |
|-----------|----------|---------|
| `QueuePage` | `@/pages/QueuePage.tsx` | Main queue management page |
| `QueueCard` | `@/components/issue/queue/QueueCard.tsx` | Queue display with actions |
| `QueuePageSkeleton` | (internal to QueuePage) | Loading placeholder |
| `QueueEmptyState` | (internal to QueuePage) | Empty state display |
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `embedded` | `boolean` | `false` | Whether panel is embedded in another component |
### State Management
### SchedulerPanel
- **Local state**:
- None (all data from hooks)
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| - | - | - | This component accepts no props (all data from Zustand store) |
- **Custom hooks**:
- `useIssueQueue` - Queue data fetching
- `useQueueMutations` - Queue operations (activate, deactivate, delete, merge)
### QueueListColumn
### Mutation States
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| - | - | - | This component accepts no props (all data from Zustand store) |
| State | Loading Indicator |
|-------|------------------|
| `isActivating` | Disable activate button during activation |
| `isDeactivating` | Disable deactivate button during deactivation |
| `isDeleting` | Disable delete button during deletion |
| `isMerging` | Disable merge button during merge operation |
---
## State Management
### Zustand Stores
| Store | Selector | Purpose |
|-------|----------|---------|
| `queueSchedulerStore` | `selectQueueSchedulerStatus` | Current scheduler status (idle/running/paused) |
| `queueSchedulerStore` | `selectSchedulerProgress` | Overall queue completion percentage |
| `queueSchedulerStore` | `selectQueueItems` | List of all queue items |
| `queueSchedulerStore` | `selectCurrentConcurrency` | Active sessions count |
| `queueSchedulerStore` | `selectSchedulerConfig` | Scheduler configuration |
| `queueSchedulerStore` | `selectSessionPool` | Session pool overview |
| `queueSchedulerStore` | `selectSchedulerError` | Error message if any |
| `issueQueueIntegrationStore` | `selectAssociationChain` | Current association chain for highlighting |
| `queueExecutionStore` | `selectByQueueItem` | Execution data for queue item |
### Queue Item Status
```typescript
type QueueItemStatus =
| 'pending' // Waiting to be executed
| 'executing' // Currently being processed
| 'completed' // Finished successfully
| 'blocked' // Blocked by dependency
| 'failed'; // Failed with error
```
### Scheduler Status
```typescript
type QueueSchedulerStatus =
| 'idle' // No items or stopped
| 'running' // Actively processing items
| 'paused'; // Temporarily paused
```
---
## Usage Examples
### Basic Queue Panel
```tsx
import { QueuePanel } from '@/components/terminal-dashboard/QueuePanel'
function QueueSection() {
return <QueuePanel />
}
```
### Standalone Scheduler Panel
```tsx
import { SchedulerPanel } from '@/components/terminal-dashboard/SchedulerPanel'
function SchedulerControls() {
return <SchedulerPanel />
}
```
### Queue List Column (Embedded)
```tsx
import { QueueListColumn } from '@/components/terminal-dashboard/QueueListColumn'
function EmbeddedQueue() {
return <QueueListColumn />
}
```
### Queue Store Actions
```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}>Start</button>
<button onClick={handlePause}>Pause</button>
<button onClick={handleStop}>Stop</button>
<button onClick={() => handleConfig({ maxConcurrentSessions: 4 })}>
Set Max 4
</button>
</div>
)
}
```
---
## Interactive Demos
### Queue Item Status Demo
:::demo QueueItemStatusDemo
# queue-item-status-demo.tsx
/**
* Queue Item Status Demo
* Shows all possible queue item states
*/
export function QueueItemStatusDemo() {
const itemStates = [
{ status: 'pending', issueId: 'ISSUE-101', sessionKey: null },
{ status: 'executing', issueId: 'ISSUE-102', sessionKey: 'cli-session-abc' },
{ status: 'completed', issueId: 'ISSUE-103', sessionKey: 'cli-session-def' },
{ status: 'blocked', issueId: 'ISSUE-104', sessionKey: null },
{ status: 'failed', issueId: 'ISSUE-105', sessionKey: 'cli-session-ghi' },
]
const statusConfig = {
pending: { icon: '○', color: 'text-gray-400', bg: 'bg-gray-500/10', label: 'Pending' },
executing: { icon: '▶', color: 'text-blue-500', bg: 'bg-blue-500/10', label: 'Executing' },
completed: { icon: '✓', color: 'text-green-500', bg: 'bg-green-500/10', label: 'Completed' },
blocked: { icon: '✕', color: 'text-red-500', bg: 'bg-red-500/10', label: 'Blocked' },
failed: { icon: '!', color: 'text-red-500', bg: 'bg-red-500/10', label: 'Failed' },
}
return (
<div className="p-6 bg-background space-y-4">
<h3 className="text-sm font-semibold">Queue Item Status States</h3>
<div className="space-y-2">
{itemStates.map((item) => {
const config = statusConfig[item.status]
return (
<div key={item.status} className="border rounded-lg p-4 flex items-center gap-4">
<span className={`text-2xl ${config.color}`}>{config.icon}</span>
<div className="flex-1">
<div className="flex items-center gap-2">
<span className="font-medium">{item.issueId}</span>
<span className={`text-xs px-2 py-0.5 rounded-full ${config.bg} ${config.color}`}>
{config.label}
</span>
</div>
{item.sessionKey && (
<div className="text-sm text-muted-foreground mt-1">
Bound session: <code className="text-xs bg-muted px-1 rounded">{item.sessionKey}</code>
</div>
)}
</div>
{item.status === 'executing' && (
<div className="flex items-center gap-2">
<div className="w-24 h-2 bg-muted rounded-full overflow-hidden">
<div className="h-full bg-blue-500 animate-pulse" style={{ width: '60%' }}/>
</div>
<span className="text-xs text-muted-foreground">60%</span>
</div>
)}
</div>
)
})}
</div>
</div>
)
}
:::
### Scheduler Config Demo
:::demo SchedulerConfigDemo
# scheduler-config-demo.tsx
/**
* Scheduler Config Demo
* Interactive configuration panel
*/
export function SchedulerConfigDemo() {
const [config, setConfig] = React.useState({
maxConcurrentSessions: 2,
sessionIdleTimeoutMs: 60000,
resumeKeySessionBindingTimeoutMs: 300000,
})
const formatMs = (ms) => {
if (ms >= 60000) return `${ms / 60000}m`
if (ms >= 1000) return `${ms / 1000}s`
return `${ms}ms`
}
return (
<div className="p-6 bg-background space-y-6 max-w-md">
<div className="flex items-center justify-between">
<h3 className="text-sm font-semibold">Scheduler Configuration</h3>
<button className="px-3 py-1.5 text-xs bg-primary text-primary-foreground rounded hover:opacity-90">
Save
</button>
</div>
<div className="space-y-4">
{/* Max Concurrent Sessions */}
<div className="space-y-2">
<label className="text-sm font-medium">Max Concurrent Sessions</label>
<div className="flex items-center gap-3">
<input
type="range"
min="1"
max="8"
value={config.maxConcurrentSessions}
onChange={(e) => setConfig({ ...config, maxConcurrentSessions: parseInt(e.target.value) })}
className="flex-1"
/>
<span className="text-sm font-medium w-8 text-center">{config.maxConcurrentSessions}</span>
</div>
<p className="text-xs text-muted-foreground">
Maximum number of sessions to run simultaneously
</p>
</div>
{/* Session Idle Timeout */}
<div className="space-y-2">
<label className="text-sm font-medium">Session Idle Timeout</label>
<div className="flex items-center gap-3">
<input
type="range"
min="10000"
max="300000"
step="10000"
value={config.sessionIdleTimeoutMs}
onChange={(e) => setConfig({ ...config, sessionIdleTimeoutMs: parseInt(e.target.value) })}
className="flex-1"
/>
<span className="text-sm font-medium w-12 text-right">{formatMs(config.sessionIdleTimeoutMs)}</span>
</div>
<p className="text-xs text-muted-foreground">
Time before idle session is terminated
</p>
</div>
{/* Resume Key Binding Timeout */}
<div className="space-y-2">
<label className="text-sm font-medium">Resume Key Binding Timeout</label>
<div className="flex items-center gap-3">
<input
type="range"
min="60000"
max="600000"
step="60000"
value={config.resumeKeySessionBindingTimeoutMs}
onChange={(e) => setConfig({ ...config, resumeKeySessionBindingTimeoutMs: parseInt(e.target.value) })}
className="flex-1"
/>
<span className="text-sm font-medium w-12 text-right">{formatMs(config.resumeKeySessionBindingTimeoutMs)}</span>
</div>
<p className="text-xs text-muted-foreground">
Time to preserve resume key session binding
</p>
</div>
</div>
{/* Current Config Display */}
<div className="border rounded-lg p-4 bg-muted/30">
<h4 className="text-xs font-semibold mb-3">Current Configuration</h4>
<dl className="space-y-2 text-sm">
<div className="flex justify-between">
<dt className="text-muted-foreground">Max Concurrent</dt>
<dd className="font-medium">{config.maxConcurrentSessions} sessions</dd>
</div>
<div className="flex justify-between">
<dt className="text-muted-foreground">Idle Timeout</dt>
<dd className="font-medium">{formatMs(config.sessionIdleTimeoutMs)}</dd>
</div>
<div className="flex justify-between">
<dt className="text-muted-foreground">Binding Timeout</dt>
<dd className="font-medium">{formatMs(config.resumeKeySessionBindingTimeoutMs)}</dd>
</div>
</dl>
</div>
</div>
)
}
:::
---
## Configuration
No configuration required. Queue data is automatically fetched from the backend.
### Scheduler Config
### Queue Data Structure
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `maxConcurrentSessions` | `number` | `2` | Maximum sessions running simultaneously |
| `sessionIdleTimeoutMs` | `number` | `60000` | Idle session timeout in milliseconds |
| `resumeKeySessionBindingTimeoutMs` | `number` | `300000` | Resume key binding timeout in milliseconds |
### Queue Item Structure
```typescript
interface QueueData {
tasks: Task[];
solutions: Solution[];
conflicts: Conflict[];
grouped_items: Record<string, GroupedItem>;
interface QueueItem {
item_id: string;
issue_id?: string;
sessionKey?: string;
status: QueueItemStatus;
execution_order: number;
created_at?: number;
updated_at?: number;
}
```
---
## Accessibility
- **Keyboard Navigation**:
- <kbd>Tab</kbd> - Navigate through queue items and controls
- <kbd>Enter</kbd>/<kbd>Space</kbd> - Activate buttons
- <kbd>Escape</kbd> - Close dialogs
- **ARIA Attributes**:
- `aria-label` on control buttons
- `aria-live` regions for status updates
- `aria-current` for active queue item
- `role="list"` on queue items list
- **Screen Reader Support**:
- Status changes announced
- Progress updates spoken
- Error messages announced
---
## Related Links
- [Issue Hub](/features/issue-hub) - Unified issues, queue, and discovery management
- [Terminal Dashboard](/features/terminal) - Terminal-first workspace with integrated queue panel
- [Discovery](/features/discovery) - Discovery session tracking
- [Issues Panel](/features/issue-hub) - Issue list and GitHub sync
- [Sessions](/features/sessions) - Session management and details

View File

@@ -1,7 +1,6 @@
# Terminal Dashboard
## One-Liner
**The Terminal Dashboard provides a terminal-first workspace with resizable panes, floating panels, and integrated tools for session monitoring and orchestration.**
---
@@ -18,16 +17,15 @@
---
## Page Overview
## Overview
**Location**: `ccw/frontend/src/pages/TerminalDashboardPage.tsx`
**Purpose**: Terminal-first layout for multi-terminal session management with integrated tools and resizable panels.
**Access**: Navigation → Terminal Dashboard
### Layout
**Access**: Navigation → Terminal Dashboard (`/terminal-dashboard`)
**Layout**:
```
+--------------------------------------------------------------------------+
| Dashboard Toolbar (panel toggles, layout presets, fullscreen) |
@@ -48,6 +46,134 @@
---
## Live Demo
:::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>
)
}
:::
---
## Core Features
| Feature | Description |
@@ -64,38 +190,396 @@
---
## Usage Guide
## Component Hierarchy
### Basic Workflow
```
TerminalDashboardPage
├── AssociationHighlightProvider (context)
├── DashboardToolbar
│ ├── Layout Preset Buttons (Single | Split-H | Split-V | Grid-2x2)
│ ├── Panel Toggles (Sessions | Files | Issues | Queue | Inspector | Execution | Scheduler)
│ ├── Fullscreen Toggle
│ └── Launch CLI Button
├── Allotment (Three-Column Layout)
│ ├── SessionGroupTree
│ │ └── Session Group Items (collapsible)
│ ├── TerminalGrid
│ │ ├── GridGroupRenderer (recursive)
│ │ └── TerminalPane
│ └── FileSidebarPanel
│ └── File Tree View
└── FloatingPanel (multiple, mutually exclusive)
├── Issues+Queue (split panel)
│ ├── IssuePanel
│ └── QueueListColumn
├── QueuePanel (feature flag)
├── InspectorContent (feature flag)
├── ExecutionMonitorPanel (feature flag)
└── SchedulerPanel
```
1. **Launch CLI Session**: Click "Launch CLI" button, configure options (tool, model, shell, working directory)
2. **Arrange Terminals**: Use layout presets or manually split panes
3. **Navigate Sessions**: Browse session groups in the left tree
4. **Toggle Panels**: Click toolbar buttons to show/hide floating panels
5. **Inspect Issues**: Open Issues panel to view associated issues
6. **Monitor Execution**: Use Execution Monitor panel for real-time tracking
---
### Key Interactions
## Props API
| Interaction | How to Use |
|-------------|------------|
| **Launch CLI** | Click "Launch CLI" button, configure session in modal |
| **Toggle sidebar** | Click Sessions/Files button in toolbar |
| **Open floating panel** | Click Issues/Queue/Inspector/Execution/Scheduler button |
| **Close floating panel** | Click X on panel or toggle button again |
| **Resize panes** | Drag the divider between panes |
| **Change layout** | Click layout preset buttons (single/split/grid) |
| **Fullscreen mode** | Click fullscreen button to hide app chrome |
### TerminalDashboardPage
### Panel Types
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| - | - | - | This page component accepts no props (state managed via hooks and Zustand stores) |
| Panel | Content | Position |
|-------|---------|----------|
| **Issues+Queue** | Combined Issues panel + Queue list column | Left (overlay) |
| **Queue** | Full queue management panel | Right (overlay, feature flag) |
| **Inspector** | Association chain inspector | Right (overlay, feature flag) |
| **Execution Monitor** | Real-time execution tracking | Right (overlay, feature flag) |
| **Scheduler** | Queue scheduler controls | Right (overlay) |
### DashboardToolbar
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `activePanel` | `PanelId \| null` | `null` | Currently active floating panel |
| `onTogglePanel` | `(panelId: PanelId) => void` | - | Callback to toggle panel visibility |
| `isFileSidebarOpen` | `boolean` | `true` | File sidebar visibility state |
| `onToggleFileSidebar` | `() => void` | - | Toggle file sidebar callback |
| `isSessionSidebarOpen` | `boolean` | `true` | Session sidebar visibility state |
| `onToggleSessionSidebar` | `() => void` | - | Toggle session sidebar callback |
| `isFullscreen` | `boolean` | `false` | Fullscreen mode state |
| `onToggleFullscreen` | `() => void` | - | Toggle fullscreen callback |
### FloatingPanel
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `isOpen` | `boolean` | `false` | Panel open state |
| `onClose` | `() => void` | - | Close callback |
| `title` | `string` | - | Panel title |
| `side` | `'left' \| 'right'` | `'left'` | Panel side |
| `width` | `number` | `400` | Panel width in pixels |
| `children` | `ReactNode` | - | Panel content |
---
## State Management
### Local State
| State | Type | Description |
|-------|------|-------------|
| `activePanel` | `PanelId \| null` | Currently active floating panel (mutually exclusive) |
| `isFileSidebarOpen` | `boolean` | File sidebar visibility |
| `isSessionSidebarOpen` | `boolean` | Session sidebar visibility |
### Zustand Stores
| Store | Selector | Purpose |
|-------|----------|---------|
| `workflowStore` | `selectProjectPath` | Current project path for file sidebar |
| `appStore` | `selectIsImmersiveMode` | Fullscreen mode state |
| `configStore` | `featureFlags` | Feature flag configuration |
| `terminalGridStore` | Grid layout and focused pane state |
| `executionMonitorStore` | Active execution count |
| `queueSchedulerStore` | Scheduler status and settings |
### Panel ID Type
```typescript
type PanelId = 'issues' | 'queue' | 'inspector' | 'execution' | 'scheduler';
```
---
## Usage Examples
### Basic Terminal Dashboard
```tsx
import { TerminalDashboardPage } from '@/pages/TerminalDashboardPage'
// The terminal dashboard is automatically rendered at /terminal-dashboard
// No props needed - layout state managed internally
```
### Using FloatingPanel Component
```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="Issues"
side="left"
width={700}
>
<IssuePanel />
</FloatingPanel>
)
}
```
### Panel Toggle Pattern
```tsx
import { useState, useCallback } from 'react'
function usePanelToggle() {
const [activePanel, setActivePanel] = useState<string | null>(null)
const togglePanel = useCallback((panelId: string) => {
setActivePanel((prev) => (prev === panelId ? null : panelId))
}, [])
const closePanel = useCallback(() => {
setActivePanel(null)
}, [])
return { activePanel, togglePanel, closePanel }
}
```
---
## Interactive Demos
### Layout Presets Demo
:::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>
)
}
:::
### Floating Panels Demo
:::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>
)
}
:::
### Resizable Panes Demo
:::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>
)
}
:::
---
## Configuration
### Feature Flags
| Flag | Controls |
|------|----------|
| `dashboardQueuePanelEnabled` | Queue panel visibility |
| `dashboardInspectorEnabled` | Inspector panel visibility |
| `dashboardExecutionMonitorEnabled` | Execution Monitor panel visibility |
### Layout Presets
@@ -106,59 +590,36 @@
| **Split-V** | Two panes stacked vertically |
| **Grid-2x2** | Four panes in 2x2 grid |
---
### Panel Types
## Components Reference
### Main Components
| Component | Location | Purpose |
|-----------|----------|---------|
| `TerminalDashboardPage` | `@/pages/TerminalDashboardPage.tsx` | Main terminal dashboard page |
| `DashboardToolbar` | `@/components/terminal-dashboard/DashboardToolbar.tsx` | Top toolbar with panel toggles |
| `SessionGroupTree` | `@/components/terminal-dashboard/SessionGroupTree.tsx` | Session tree navigation |
| `TerminalGrid` | `@/components/terminal-dashboard/TerminalGrid.tsx` | Tmux-style terminal panes |
| `FileSidebarPanel` | `@/components/terminal-dashboard/FileSidebarPanel.tsx` | File explorer sidebar |
| `FloatingPanel` | `@/components/terminal-dashboard/FloatingPanel.tsx` | Overlay panel wrapper |
| `IssuePanel` | `@/components/terminal-dashboard/IssuePanel.tsx` | Issues list panel |
| `QueuePanel` | `@/components/terminal-dashboard/QueuePanel.tsx` | Queue management panel |
| `QueueListColumn` | `@/components/terminal-dashboard/QueueListColumn.tsx` | Queue list (compact) |
| `SchedulerPanel` | `@/components/terminal-dashboard/SchedulerPanel.tsx` | Queue scheduler controls |
| `InspectorContent` | `@/components/terminal-dashboard/BottomInspector.tsx` | Association inspector |
| `ExecutionMonitorPanel` | `@/components/terminal-dashboard/ExecutionMonitorPanel.tsx` | Execution tracking |
### State Management
- **Local state** (TerminalDashboardPage):
- `activePanel`: PanelId | null
- `isFileSidebarOpen`: boolean
- `isSessionSidebarOpen`: boolean
- **Zustand stores**:
- `useWorkflowStore` - Project path
- `useAppStore` - Immersive mode state
- `useConfigStore` - Feature flags
- `useTerminalGridStore` - Terminal grid layout and focused pane
- `useExecutionMonitorStore` - Active execution count
- `useQueueSchedulerStore` - Scheduler status
| Panel | Content | Position | Feature Flag |
|-------|---------|----------|--------------|
| **Issues+Queue** | Combined Issues panel + Queue list column | Left (overlay) | - |
| **Queue** | Full queue management panel | Right (overlay) | `dashboardQueuePanelEnabled` |
| **Inspector** | Association chain inspector | Right (overlay) | `dashboardInspectorEnabled` |
| **Execution Monitor** | Real-time execution tracking | Right (overlay) | `dashboardExecutionMonitorEnabled` |
| **Scheduler** | Queue scheduler controls | Right (overlay) | - |
---
## Configuration
## Accessibility
### Panel IDs
- **Keyboard Navigation**:
- <kbd>Tab</kbd> - Navigate through toolbar buttons
- <kbd>Enter</kbd>/<kbd>Space</kbd> - Activate toolbar buttons
- <kbd>Escape</kbd> - Close floating panels
- <kbd>F11</kbd> - Toggle fullscreen mode
```typescript
type PanelId = 'issues' | 'queue' | 'inspector' | 'execution' | 'scheduler';
```
- **ARIA Attributes**:
- `aria-label` on toolbar buttons
- `aria-expanded` on sidebar toggles
- `aria-hidden` on inactive floating panels
- `role="dialog"` on floating panels
### Feature Flags
| Flag | Controls |
|------|----------|
| `dashboardQueuePanelEnabled` | Queue panel visibility |
| `dashboardInspectorEnabled` | Inspector panel visibility |
| `dashboardExecutionMonitorEnabled` | Execution Monitor panel visibility |
- **Screen Reader Support**:
- Panel state announced when toggled
- Layout changes announced
- Focus management when panels open/close
---
@@ -168,3 +629,4 @@ type PanelId = 'issues' | 'queue' | 'inspector' | 'execution' | 'scheduler';
- [Sessions](/features/sessions) - Session management
- [Issue Hub](/features/issue-hub) - Issues, queue, discovery
- [Explorer](/features/explorer) - File explorer
- [Queue](/features/queue) - Queue management standalone page