import { useMemo, useState, useEffect } from 'react' import { Brain, Sparkles } from 'lucide-react' import type { AgentStatus } from '../lib/types' interface AgentThoughtProps { logs: Array<{ line: string; timestamp: string }> agentStatus: AgentStatus } const IDLE_TIMEOUT = 30000 // 30 seconds /** * Determines if a log line is an agent "thought" (narrative text) * vs. tool mechanics that should be hidden */ function isAgentThought(line: string): boolean { const trimmed = line.trim() // Skip tool mechanics if (/^\[Tool:/.test(trimmed)) return false if (/^\s*Input:\s*\{/.test(trimmed)) return false if (/^\[(Done|Error)\]/.test(trimmed)) return false if (/^\[Error\]/.test(trimmed)) return false if (/^Output:/.test(trimmed)) return false // Skip JSON and very short lines if (/^[[{]/.test(trimmed)) return false if (trimmed.length < 10) return false // Skip lines that are just paths or technical output if (/^[A-Za-z]:\\/.test(trimmed)) return false if (/^\/[a-z]/.test(trimmed)) return false // Keep narrative text (looks like a sentence, relaxed filter) return trimmed.length > 10 } /** * Extracts the latest agent thought from logs */ function getLatestThought(logs: Array<{ line: string; timestamp: string }>): string | null { // Search from most recent for (let i = logs.length - 1; i >= 0; i--) { if (isAgentThought(logs[i].line)) { return logs[i].line.trim() } } return null } export function AgentThought({ logs, agentStatus }: AgentThoughtProps) { const thought = useMemo(() => getLatestThought(logs), [logs]) const [displayedThought, setDisplayedThought] = useState(null) const [textVisible, setTextVisible] = useState(true) const [isVisible, setIsVisible] = useState(false) // Get last log timestamp for idle detection const lastLogTimestamp = logs.length > 0 ? new Date(logs[logs.length - 1].timestamp).getTime() : 0 // Determine if component should be visible const shouldShow = useMemo(() => { if (!thought) return false if (agentStatus === 'running') return true if (agentStatus === 'paused') { return Date.now() - lastLogTimestamp < IDLE_TIMEOUT } return false }, [thought, agentStatus, lastLogTimestamp]) // Animate text changes using CSS transitions useEffect(() => { if (thought !== displayedThought && thought) { // Fade out setTextVisible(false) // After fade out, update text and fade in const timeout = setTimeout(() => { setDisplayedThought(thought) setTextVisible(true) }, 150) // Match transition duration return () => clearTimeout(timeout) } }, [thought, displayedThought]) // Handle visibility transitions useEffect(() => { if (shouldShow) { setIsVisible(true) } else { // Delay hiding to allow exit animation const timeout = setTimeout(() => setIsVisible(false), 300) return () => clearTimeout(timeout) } }, [shouldShow]) if (!isVisible || !displayedThought) return null const isRunning = agentStatus === 'running' return (
{/* Brain Icon with subtle glow */}
{isRunning && ( )}
{/* Thought text with fade transition + shimmer effect when running */}

{displayedThought?.replace(/:$/, '')}

{/* Subtle running indicator bar */} {isRunning && (
)}
) }