- 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.
22 KiB
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.
Pain Points Solved
| Pain Point | Current State | Terminal Dashboard Solution |
|---|---|---|
| Scattered terminals | Multiple terminal windows | Unified tmux-style grid layout |
| No context linkage | Can't associate terminal output with issues | Association highlight provider |
| Panel overload | Fixed layout wastes space | Floating panels (mutually exclusive) |
| Missing tools | Switch between apps | Integrated issues, queue, inspector, scheduler |
| Limited workspace | Can't see code and terminals together | Resizable three-column layout |
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 (/terminal-dashboard)
Layout:
+--------------------------------------------------------------------------+
| Dashboard Toolbar (panel toggles, layout presets, fullscreen) |
+--------------------------------------------------------------------------+
| +----------------+-------------------------------------------+------------+ |
| | Session | Terminal Grid (tmux-style) | File | |
| | Group Tree | +----------+ +----------+ | Sidebar | |
| | (resizable) | | Term 1 | | Term 2 | | (resizable)| |
| | | +----------+ +----------+ | | |
| | | +----------+ +----------+ | | |
| | | | Term 3 | | Term 4 | | | |
| | | +----------+ +----------+ | | |
| +----------------+-------------------------------------------+------------+ |
+--------------------------------------------------------------------------+
| [Floating Panel: Issues+Queue OR Inspector OR Execution OR Scheduler] |
+--------------------------------------------------------------------------+
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 (
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}
))}
{/* 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 |
|---|---|
| Three-Column Layout | Resizable panes using Allotment: Session tree (left), Terminal grid (center), File sidebar (right) |
| Terminal Grid | Tmux-style split panes with layout presets (single, split-h, split-v, grid-2x2) |
| Session Group Tree | Hierarchical view of CLI sessions with grouping by tags |
| Floating Panels | Mutually exclusive overlay panels (Issues+Queue, Inspector, Execution Monitor, Scheduler) |
| Association Highlight | Cross-panel linking between terminals, issues, and queue items |
| Layout Presets | Quick layout buttons: single pane, horizontal split, vertical split, 2x2 grid |
| Launch CLI | Config modal for creating new CLI sessions with tool, model, and settings |
| Fullscreen Mode | Immersive mode hides app chrome (header + sidebar) |
| Feature Flags | Panel visibility controlled by feature flags (queue, inspector, execution monitor) |
Component Hierarchy
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
Props API
TerminalDashboardPage
| Prop | Type | Default | Description |
|---|---|---|---|
| - | - | - | This page component accepts no props (state managed via hooks and Zustand stores) |
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
type PanelId = 'issues' | 'queue' | 'inspector' | 'execution' | 'scheduler';
Usage Examples
Basic Terminal Dashboard
import { TerminalDashboardPage } from '@/pages/TerminalDashboardPage'
// The terminal dashboard is automatically rendered at /terminal-dashboard
// No props needed - layout state managed internally
Using FloatingPanel Component
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
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 (
Terminal Layout Presets
px-3 py-1.5 text-xs rounded transition-colors ${ layout === preset ? 'bg-primary text-primary-foreground' : 'border hover:bg-accent' }}
>
{preset.replace('-', ' ').toUpperCase()}
))}
<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 (
Floating Panels
px-3 py-1.5 text-xs rounded transition-colors ${ activePanel === panel.id ? 'bg-primary text-primary-foreground' : 'border hover:bg-accent' }}
>
{panel.title}
))}
<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 (
{/* 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
| Preset | Layout |
|---|---|
| Single | One terminal pane |
| Split-H | Two panes side by side |
| Split-V | Two panes stacked vertically |
| Grid-2x2 | Four panes in 2x2 grid |
Panel Types
| 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) | - |
Accessibility
-
Keyboard Navigation:
- Tab - Navigate through toolbar buttons
- Enter/Space - Activate toolbar buttons
- Escape - Close floating panels
- F11 - Toggle fullscreen mode
-
ARIA Attributes:
aria-labelon toolbar buttonsaria-expandedon sidebar togglesaria-hiddenon inactive floating panelsrole="dialog"on floating panels
-
Screen Reader Support:
- Panel state announced when toggled
- Layout changes announced
- Focus management when panels open/close
Related Links
- Orchestrator - Visual workflow editor
- Sessions - Session management
- Issue Hub - Issues, queue, discovery
- Explorer - File explorer
- Queue - Queue management standalone page