mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-28 09:23:08 +08:00
refactor(terminal-dashboard): move agent list to execution monitor panel
- Remove AgentList component from left sidebar - Integrate orchestration plans display into ExecutionMonitorPanel - Execution Monitor now shows both workflow executions and orchestration plans - Cleaner sidebar with only session tree
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
// ========================================
|
||||
// Execution Monitor Panel
|
||||
// ========================================
|
||||
// Panel for monitoring workflow executions in Terminal Dashboard.
|
||||
// Displays execution progress, step list, and control buttons.
|
||||
// Panel for monitoring workflow executions and orchestration plans in Terminal Dashboard.
|
||||
// Displays execution progress, step list, control buttons, and active orchestration plans.
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import {
|
||||
Play,
|
||||
@@ -15,6 +16,7 @@ import {
|
||||
Loader2,
|
||||
Clock,
|
||||
Terminal,
|
||||
Bot,
|
||||
} from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
@@ -25,7 +27,10 @@ import {
|
||||
selectCurrentExecution,
|
||||
selectActiveExecutions,
|
||||
} from '@/stores/executionMonitorStore';
|
||||
import { useOrchestratorStore, selectActivePlans } from '@/stores';
|
||||
import type { ExecutionStatus, StepInfo } from '@/stores/executionMonitorStore';
|
||||
import type { OrchestrationRunState } from '@/stores/orchestratorStore';
|
||||
import type { OrchestrationStatus } from '@/types/orchestrator';
|
||||
|
||||
// ========== Status Config ==========
|
||||
|
||||
@@ -38,6 +43,18 @@ const statusConfig: Record<ExecutionStatus, { label: string; color: string; bgCo
|
||||
cancelled: { label: 'Cancelled', color: 'text-muted-foreground', bgColor: 'bg-muted' },
|
||||
};
|
||||
|
||||
const ORCHESTRATION_STATUS_CONFIG: Record<
|
||||
OrchestrationStatus,
|
||||
{ variant: 'default' | 'info' | 'success' | 'destructive' | 'secondary' | 'warning'; messageId: string }
|
||||
> = {
|
||||
running: { variant: 'info', messageId: 'terminalDashboard.agentList.statusRunning' },
|
||||
completed: { variant: 'success', messageId: 'terminalDashboard.agentList.statusCompleted' },
|
||||
failed: { variant: 'destructive', messageId: 'terminalDashboard.agentList.statusFailed' },
|
||||
paused: { variant: 'warning', messageId: 'terminalDashboard.agentList.statusPaused' },
|
||||
pending: { variant: 'secondary', messageId: 'terminalDashboard.agentList.statusPending' },
|
||||
cancelled: { variant: 'secondary', messageId: 'terminalDashboard.agentList.statusPending' },
|
||||
};
|
||||
|
||||
// ========== Step Status Icon ==========
|
||||
|
||||
function StepStatusIcon({ status }: { status: ExecutionStatus }) {
|
||||
@@ -97,6 +114,60 @@ function StepListItem({ step, isCurrent }: StepListItemProps) {
|
||||
);
|
||||
}
|
||||
|
||||
// ========== Orchestration Plan Item ==========
|
||||
|
||||
function OrchestrationPlanItem({
|
||||
runState,
|
||||
}: {
|
||||
runState: OrchestrationRunState;
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
const { plan, status, stepStatuses } = runState;
|
||||
|
||||
const totalSteps = plan.steps.length;
|
||||
const completedSteps = useMemo(
|
||||
() =>
|
||||
Object.values(stepStatuses).filter(
|
||||
(s) => s.status === 'completed' || s.status === 'skipped'
|
||||
).length,
|
||||
[stepStatuses]
|
||||
);
|
||||
|
||||
const config = ORCHESTRATION_STATUS_CONFIG[status] ?? ORCHESTRATION_STATUS_CONFIG.pending;
|
||||
const isRunning = status === 'running';
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center gap-2 px-3 py-2 rounded-md',
|
||||
'hover:bg-muted/30 transition-colors'
|
||||
)}
|
||||
>
|
||||
<div className="shrink-0">
|
||||
{isRunning ? (
|
||||
<Loader2 className="w-4 h-4 text-primary animate-spin" />
|
||||
) : (
|
||||
<Bot className="w-4 h-4 text-muted-foreground" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium truncate">{plan.name}</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{formatMessage(
|
||||
{ id: 'terminalDashboard.agentList.stepLabel' },
|
||||
{ current: completedSteps, total: totalSteps }
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Badge variant={config.variant} className="text-xs px-2 py-0 shrink-0">
|
||||
{formatMessage({ id: config.messageId })}
|
||||
</Badge>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ========== Main Component ==========
|
||||
|
||||
export function ExecutionMonitorPanel() {
|
||||
@@ -110,10 +181,17 @@ export function ExecutionMonitorPanel() {
|
||||
const stopExecution = useExecutionMonitorStore((s) => s.stopExecution);
|
||||
const clearExecution = useExecutionMonitorStore((s) => s.clearExecution);
|
||||
|
||||
const executions = Object.values(activeExecutions);
|
||||
const hasExecutions = executions.length > 0;
|
||||
// Orchestration plans
|
||||
const activePlans = useOrchestratorStore(selectActivePlans);
|
||||
const planEntries = useMemo(
|
||||
() => Object.entries(activePlans),
|
||||
[activePlans]
|
||||
);
|
||||
|
||||
if (!hasExecutions) {
|
||||
const executions = Object.values(activeExecutions);
|
||||
const hasContent = executions.length > 0 || planEntries.length > 0;
|
||||
|
||||
if (!hasContent) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-full text-muted-foreground p-8">
|
||||
<Terminal className="w-10 h-10 mb-3 opacity-30" />
|
||||
@@ -129,6 +207,29 @@ export function ExecutionMonitorPanel() {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
{/* Orchestration Plans Section */}
|
||||
{planEntries.length > 0 && (
|
||||
<div className="border-b border-border shrink-0">
|
||||
<div className="flex items-center gap-2 px-4 py-2 bg-muted/20">
|
||||
<Bot className="w-4 h-4 text-muted-foreground" />
|
||||
<h3 className="text-xs font-semibold text-muted-foreground uppercase tracking-wide">
|
||||
{formatMessage({ id: 'terminalDashboard.agentList.title' })}
|
||||
</h3>
|
||||
<Badge variant="secondary" className="text-[10px] px-1.5 py-0 ml-auto">
|
||||
{planEntries.length}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="divide-y divide-border/50">
|
||||
{planEntries.map(([planId, runState]) => (
|
||||
<OrchestrationPlanItem key={planId} runState={runState} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Workflow Executions Section */}
|
||||
{executions.length > 0 && (
|
||||
<>
|
||||
{/* Execution selector (if multiple) */}
|
||||
{executions.length > 1 && (
|
||||
<div className="border-b border-border p-2 shrink-0">
|
||||
@@ -277,6 +378,8 @@ export function ExecutionMonitorPanel() {
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
// Terminal Dashboard Page (V2)
|
||||
// ========================================
|
||||
// Terminal-first layout with fixed session sidebar + floating panels + right file sidebar.
|
||||
// Left sidebar: SessionGroupTree + AgentList (always visible)
|
||||
// Left sidebar: SessionGroupTree (always visible)
|
||||
// Main area: TerminalGrid (tmux-style split panes)
|
||||
// Right sidebar: FileSidebarPanel (file tree, resizable)
|
||||
// Top: DashboardToolbar with panel toggles and layout presets
|
||||
// Floating panels: Issues, Queue, Inspector (overlay, mutually exclusive)
|
||||
// Floating panels: Issues, Queue, Inspector, Execution Monitor (overlay, mutually exclusive)
|
||||
// Fullscreen mode: Uses global isImmersiveMode to hide app chrome (Header + Sidebar)
|
||||
|
||||
import { useState, useCallback } from 'react';
|
||||
@@ -18,7 +18,6 @@ import { DashboardToolbar, type PanelId } from '@/components/terminal-dashboard/
|
||||
import { TerminalGrid } from '@/components/terminal-dashboard/TerminalGrid';
|
||||
import { FloatingPanel } from '@/components/terminal-dashboard/FloatingPanel';
|
||||
import { SessionGroupTree } from '@/components/terminal-dashboard/SessionGroupTree';
|
||||
import { AgentList } from '@/components/terminal-dashboard/AgentList';
|
||||
import { IssuePanel } from '@/components/terminal-dashboard/IssuePanel';
|
||||
import { QueuePanel } from '@/components/terminal-dashboard/QueuePanel';
|
||||
import { InspectorContent } from '@/components/terminal-dashboard/BottomInspector';
|
||||
@@ -74,9 +73,6 @@ export function TerminalDashboardPage() {
|
||||
<div className="flex-1 min-h-0 overflow-y-auto">
|
||||
<SessionGroupTree />
|
||||
</div>
|
||||
<div className="shrink-0">
|
||||
<AgentList />
|
||||
</div>
|
||||
</div>
|
||||
</Allotment.Pane>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user