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
|
// Execution Monitor Panel
|
||||||
// ========================================
|
// ========================================
|
||||||
// Panel for monitoring workflow executions in Terminal Dashboard.
|
// Panel for monitoring workflow executions and orchestration plans in Terminal Dashboard.
|
||||||
// Displays execution progress, step list, and control buttons.
|
// Displays execution progress, step list, control buttons, and active orchestration plans.
|
||||||
|
|
||||||
|
import { useMemo } from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import {
|
import {
|
||||||
Play,
|
Play,
|
||||||
@@ -15,6 +16,7 @@ import {
|
|||||||
Loader2,
|
Loader2,
|
||||||
Clock,
|
Clock,
|
||||||
Terminal,
|
Terminal,
|
||||||
|
Bot,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { Button } from '@/components/ui/Button';
|
import { Button } from '@/components/ui/Button';
|
||||||
@@ -25,7 +27,10 @@ import {
|
|||||||
selectCurrentExecution,
|
selectCurrentExecution,
|
||||||
selectActiveExecutions,
|
selectActiveExecutions,
|
||||||
} from '@/stores/executionMonitorStore';
|
} from '@/stores/executionMonitorStore';
|
||||||
|
import { useOrchestratorStore, selectActivePlans } from '@/stores';
|
||||||
import type { ExecutionStatus, StepInfo } from '@/stores/executionMonitorStore';
|
import type { ExecutionStatus, StepInfo } from '@/stores/executionMonitorStore';
|
||||||
|
import type { OrchestrationRunState } from '@/stores/orchestratorStore';
|
||||||
|
import type { OrchestrationStatus } from '@/types/orchestrator';
|
||||||
|
|
||||||
// ========== Status Config ==========
|
// ========== 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' },
|
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 ==========
|
// ========== Step Status Icon ==========
|
||||||
|
|
||||||
function StepStatusIcon({ status }: { status: ExecutionStatus }) {
|
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 ==========
|
// ========== Main Component ==========
|
||||||
|
|
||||||
export function ExecutionMonitorPanel() {
|
export function ExecutionMonitorPanel() {
|
||||||
@@ -110,10 +181,17 @@ export function ExecutionMonitorPanel() {
|
|||||||
const stopExecution = useExecutionMonitorStore((s) => s.stopExecution);
|
const stopExecution = useExecutionMonitorStore((s) => s.stopExecution);
|
||||||
const clearExecution = useExecutionMonitorStore((s) => s.clearExecution);
|
const clearExecution = useExecutionMonitorStore((s) => s.clearExecution);
|
||||||
|
|
||||||
const executions = Object.values(activeExecutions);
|
// Orchestration plans
|
||||||
const hasExecutions = executions.length > 0;
|
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 (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center h-full text-muted-foreground p-8">
|
<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" />
|
<Terminal className="w-10 h-10 mb-3 opacity-30" />
|
||||||
@@ -129,6 +207,29 @@ export function ExecutionMonitorPanel() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full">
|
<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) */}
|
{/* Execution selector (if multiple) */}
|
||||||
{executions.length > 1 && (
|
{executions.length > 1 && (
|
||||||
<div className="border-b border-border p-2 shrink-0">
|
<div className="border-b border-border p-2 shrink-0">
|
||||||
@@ -277,6 +378,8 @@ export function ExecutionMonitorPanel() {
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
// Terminal Dashboard Page (V2)
|
// Terminal Dashboard Page (V2)
|
||||||
// ========================================
|
// ========================================
|
||||||
// Terminal-first layout with fixed session sidebar + floating panels + right file sidebar.
|
// 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)
|
// Main area: TerminalGrid (tmux-style split panes)
|
||||||
// Right sidebar: FileSidebarPanel (file tree, resizable)
|
// Right sidebar: FileSidebarPanel (file tree, resizable)
|
||||||
// Top: DashboardToolbar with panel toggles and layout presets
|
// 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)
|
// Fullscreen mode: Uses global isImmersiveMode to hide app chrome (Header + Sidebar)
|
||||||
|
|
||||||
import { useState, useCallback } from 'react';
|
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 { TerminalGrid } from '@/components/terminal-dashboard/TerminalGrid';
|
||||||
import { FloatingPanel } from '@/components/terminal-dashboard/FloatingPanel';
|
import { FloatingPanel } from '@/components/terminal-dashboard/FloatingPanel';
|
||||||
import { SessionGroupTree } from '@/components/terminal-dashboard/SessionGroupTree';
|
import { SessionGroupTree } from '@/components/terminal-dashboard/SessionGroupTree';
|
||||||
import { AgentList } from '@/components/terminal-dashboard/AgentList';
|
|
||||||
import { IssuePanel } from '@/components/terminal-dashboard/IssuePanel';
|
import { IssuePanel } from '@/components/terminal-dashboard/IssuePanel';
|
||||||
import { QueuePanel } from '@/components/terminal-dashboard/QueuePanel';
|
import { QueuePanel } from '@/components/terminal-dashboard/QueuePanel';
|
||||||
import { InspectorContent } from '@/components/terminal-dashboard/BottomInspector';
|
import { InspectorContent } from '@/components/terminal-dashboard/BottomInspector';
|
||||||
@@ -74,9 +73,6 @@ export function TerminalDashboardPage() {
|
|||||||
<div className="flex-1 min-h-0 overflow-y-auto">
|
<div className="flex-1 min-h-0 overflow-y-auto">
|
||||||
<SessionGroupTree />
|
<SessionGroupTree />
|
||||||
</div>
|
</div>
|
||||||
<div className="shrink-0">
|
|
||||||
<AgentList />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Allotment.Pane>
|
</Allotment.Pane>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user