mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-02-02 07:23:35 +00:00
refactor: compact Progress card and merge agent thought into it
- Redesign ProgressDashboard from tall stacked layout to compact inline: title/badge left, passing/total right, progress bar with percentage below - Absorb AgentThought functionality directly into ProgressDashboard, showing the agent's current thought below the progress bar - Remove standalone AgentThought usage from App.tsx (component now unused) - Pass logs/agentStatus to ProgressDashboard in single-agent mode only Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,7 +13,6 @@ import { SetupWizard } from './components/SetupWizard'
|
|||||||
import { AddFeatureForm } from './components/AddFeatureForm'
|
import { AddFeatureForm } from './components/AddFeatureForm'
|
||||||
import { FeatureModal } from './components/FeatureModal'
|
import { FeatureModal } from './components/FeatureModal'
|
||||||
import { DebugLogViewer, type TabType } from './components/DebugLogViewer'
|
import { DebugLogViewer, type TabType } from './components/DebugLogViewer'
|
||||||
import { AgentThought } from './components/AgentThought'
|
|
||||||
import { AgentMissionControl } from './components/AgentMissionControl'
|
import { AgentMissionControl } from './components/AgentMissionControl'
|
||||||
import { CelebrationOverlay } from './components/CelebrationOverlay'
|
import { CelebrationOverlay } from './components/CelebrationOverlay'
|
||||||
import { AssistantFAB } from './components/AssistantFAB'
|
import { AssistantFAB } from './components/AssistantFAB'
|
||||||
@@ -390,6 +389,8 @@ function App() {
|
|||||||
total={progress.total}
|
total={progress.total}
|
||||||
percentage={progress.percentage}
|
percentage={progress.percentage}
|
||||||
isConnected={wsState.isConnected}
|
isConnected={wsState.isConnected}
|
||||||
|
logs={wsState.activeAgents.length === 0 ? wsState.logs : undefined}
|
||||||
|
agentStatus={wsState.activeAgents.length === 0 ? wsState.agentStatus : undefined}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Agent Mission Control - shows orchestrator status and active agents in parallel mode */}
|
{/* Agent Mission Control - shows orchestrator status and active agents in parallel mode */}
|
||||||
@@ -400,13 +401,6 @@ function App() {
|
|||||||
getAgentLogs={wsState.getAgentLogs}
|
getAgentLogs={wsState.getAgentLogs}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Agent Thought - shows latest agent narrative (single agent mode) */}
|
|
||||||
{wsState.activeAgents.length === 0 && (
|
|
||||||
<AgentThought
|
|
||||||
logs={wsState.logs}
|
|
||||||
agentStatus={wsState.agentStatus}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Initializing Features State - show when agent is running but no features yet */}
|
{/* Initializing Features State - show when agent is running but no features yet */}
|
||||||
{features &&
|
{features &&
|
||||||
|
|||||||
@@ -1,12 +1,40 @@
|
|||||||
import { Wifi, WifiOff } from 'lucide-react'
|
import { useMemo, useState, useEffect } from 'react'
|
||||||
|
import { Wifi, WifiOff, Brain, Sparkles } from 'lucide-react'
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
import type { AgentStatus } from '../lib/types'
|
||||||
|
|
||||||
interface ProgressDashboardProps {
|
interface ProgressDashboardProps {
|
||||||
passing: number
|
passing: number
|
||||||
total: number
|
total: number
|
||||||
percentage: number
|
percentage: number
|
||||||
isConnected: boolean
|
isConnected: boolean
|
||||||
|
logs?: Array<{ line: string; timestamp: string }>
|
||||||
|
agentStatus?: AgentStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
const IDLE_TIMEOUT = 30000
|
||||||
|
|
||||||
|
function isAgentThought(line: string): boolean {
|
||||||
|
const trimmed = line.trim()
|
||||||
|
if (/^\[Tool:/.test(trimmed)) return false
|
||||||
|
if (/^\s*Input:\s*\{/.test(trimmed)) return false
|
||||||
|
if (/^\[(Done|Error)\]/.test(trimmed)) return false
|
||||||
|
if (/^Output:/.test(trimmed)) return false
|
||||||
|
if (/^[[{]/.test(trimmed)) return false
|
||||||
|
if (trimmed.length < 10) return false
|
||||||
|
if (/^[A-Za-z]:\\/.test(trimmed)) return false
|
||||||
|
if (/^\/[a-z]/.test(trimmed)) return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLatestThought(logs: Array<{ line: string; timestamp: string }>): string | null {
|
||||||
|
for (let i = logs.length - 1; i >= 0; i--) {
|
||||||
|
if (isAgentThought(logs[i].line)) {
|
||||||
|
return logs[i].line.trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ProgressDashboard({
|
export function ProgressDashboard({
|
||||||
@@ -14,67 +42,109 @@ export function ProgressDashboard({
|
|||||||
total,
|
total,
|
||||||
percentage,
|
percentage,
|
||||||
isConnected,
|
isConnected,
|
||||||
|
logs = [],
|
||||||
|
agentStatus,
|
||||||
}: ProgressDashboardProps) {
|
}: ProgressDashboardProps) {
|
||||||
|
const thought = useMemo(() => getLatestThought(logs), [logs])
|
||||||
|
const [displayedThought, setDisplayedThought] = useState<string | null>(null)
|
||||||
|
const [textVisible, setTextVisible] = useState(true)
|
||||||
|
|
||||||
|
const lastLogTimestamp = logs.length > 0
|
||||||
|
? new Date(logs[logs.length - 1].timestamp).getTime()
|
||||||
|
: 0
|
||||||
|
|
||||||
|
const showThought = useMemo(() => {
|
||||||
|
if (!thought) return false
|
||||||
|
if (agentStatus === 'running') return true
|
||||||
|
if (agentStatus === 'paused') {
|
||||||
|
return Date.now() - lastLogTimestamp < IDLE_TIMEOUT
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}, [thought, agentStatus, lastLogTimestamp])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (thought !== displayedThought && thought) {
|
||||||
|
setTextVisible(false)
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
setDisplayedThought(thought)
|
||||||
|
setTextVisible(true)
|
||||||
|
}, 150)
|
||||||
|
return () => clearTimeout(timeout)
|
||||||
|
}
|
||||||
|
}, [thought, displayedThought])
|
||||||
|
|
||||||
|
const isRunning = agentStatus === 'running'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="flex-row items-center justify-between space-y-0 pb-4">
|
<CardHeader className="flex-row items-center justify-between space-y-0 pb-0">
|
||||||
<CardTitle className="text-xl uppercase tracking-wide">
|
<div className="flex items-center gap-3">
|
||||||
Progress
|
<CardTitle className="text-xl uppercase tracking-wide">
|
||||||
</CardTitle>
|
Progress
|
||||||
<Badge variant={isConnected ? 'default' : 'destructive'} className="gap-1">
|
</CardTitle>
|
||||||
{isConnected ? (
|
<Badge variant={isConnected ? 'default' : 'destructive'} className="gap-1">
|
||||||
<>
|
{isConnected ? (
|
||||||
<Wifi size={14} />
|
<>
|
||||||
Live
|
<Wifi size={14} />
|
||||||
</>
|
Live
|
||||||
) : (
|
</>
|
||||||
<>
|
) : (
|
||||||
<WifiOff size={14} />
|
<>
|
||||||
Offline
|
<WifiOff size={14} />
|
||||||
</>
|
Offline
|
||||||
)}
|
</>
|
||||||
</Badge>
|
)}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-baseline gap-1">
|
||||||
|
<span className="font-mono text-lg font-bold text-primary">
|
||||||
|
{passing}
|
||||||
|
</span>
|
||||||
|
<span className="text-sm text-muted-foreground">/</span>
|
||||||
|
<span className="font-mono text-lg font-bold">
|
||||||
|
{total}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|
||||||
<CardContent>
|
<CardContent className="pt-3 pb-3">
|
||||||
{/* Large Percentage */}
|
<div className="flex items-center gap-4">
|
||||||
<div className="text-center mb-6">
|
{/* Progress Bar */}
|
||||||
<span className="inline-flex items-baseline">
|
<div className="h-2.5 bg-muted rounded-full overflow-hidden flex-1">
|
||||||
<span className="text-6xl font-bold tabular-nums">
|
<div
|
||||||
{percentage.toFixed(1)}
|
className="h-full bg-primary rounded-full transition-all duration-500 ease-out"
|
||||||
</span>
|
style={{ width: `${percentage}%` }}
|
||||||
<span className="text-3xl font-semibold text-muted-foreground">
|
/>
|
||||||
%
|
</div>
|
||||||
</span>
|
{/* Percentage */}
|
||||||
|
<span className="text-sm font-bold tabular-nums text-muted-foreground w-12 text-right">
|
||||||
|
{percentage.toFixed(1)}%
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Progress Bar */}
|
{/* Agent Thought */}
|
||||||
<div className="h-3 bg-muted rounded-full overflow-hidden mb-6">
|
<div
|
||||||
<div
|
className={`
|
||||||
className="h-full bg-primary rounded-full transition-all duration-500 ease-out"
|
transition-all duration-300 ease-out overflow-hidden
|
||||||
style={{ width: `${percentage}%` }}
|
${showThought && displayedThought ? 'opacity-100 max-h-10 mt-3' : 'opacity-0 max-h-0 mt-0'}
|
||||||
/>
|
`}
|
||||||
</div>
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
{/* Stats */}
|
<div className="relative shrink-0">
|
||||||
<div className="flex justify-center gap-8 text-center">
|
<Brain size={16} className="text-primary" strokeWidth={2.5} />
|
||||||
<div>
|
{isRunning && (
|
||||||
<span className="font-mono text-3xl font-bold text-primary">
|
<Sparkles size={8} className="absolute -top-1 -right-1 text-yellow-500 animate-pulse" />
|
||||||
{passing}
|
)}
|
||||||
</span>
|
</div>
|
||||||
<span className="block text-sm text-muted-foreground uppercase">
|
<p
|
||||||
Passing
|
className="font-mono text-sm truncate text-muted-foreground transition-all duration-150 ease-out"
|
||||||
</span>
|
style={{
|
||||||
</div>
|
opacity: textVisible ? 1 : 0,
|
||||||
<div className="text-4xl text-muted-foreground">/</div>
|
transform: textVisible ? 'translateY(0)' : 'translateY(-4px)',
|
||||||
<div>
|
}}
|
||||||
<span className="font-mono text-3xl font-bold">
|
>
|
||||||
{total}
|
{displayedThought?.replace(/:$/, '')}
|
||||||
</span>
|
</p>
|
||||||
<span className="block text-sm text-muted-foreground uppercase">
|
|
||||||
Total
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
Reference in New Issue
Block a user