← back to Letsbegin

app/live/page.tsx

945 lines

'use client'

import { useState, useEffect, useRef } from 'react'
import {
  Activity,
  Cpu,
  MemoryStick,
  Terminal,
  GitBranch,
  CheckCircle2,
  Circle,
  Loader2,
  XCircle,
  RefreshCw,
  Zap,
  Clock,
  Server,
  ChevronDown,
  ChevronRight,
  AlertTriangle,
  Play,
  Square,
  Info,
  Folder,
  FileText,
  Layers,
  ArrowLeft
} from 'lucide-react'
import Link from 'next/link'

interface ClaudeProcess {
  pid: number
  cpu: string
  memory: string
  terminal: string
  uptime: string
  command: string
  type: 'interactive' | 'ralph' | 'mcp' | 'unknown'
  project?: string
  status: string
}

interface StoryDetail {
  id: string
  title: string
  status: 'done' | 'running' | 'pending' | 'fail'
  time?: number
}

interface RalphStatus {
  running: boolean
  project: string
  projectPath: string
  branch: string
  currentStory: string
  progress: string
  completedStories: string[]
  pendingStories: string[]
  runningStory: string | null
  logs: string[]
  elapsed: number
  storyDetails: StoryDetail[]
}

interface LiveData {
  timestamp: string
  claudeProcesses: ClaudeProcess[]
  ralphStatus: RalphStatus | null
  systemStats: {
    loadAvg: number[]
    memUsed: number
    memTotal: number
    uptime: string
  }
  totalProcesses: number
  activeRalph: boolean
}

interface RalphProject {
  project: string
  projectPath: string
  pid: number
  cpu: string
  memory: string
  status: 'running' | 'completed' | 'failed' | 'idle'
  currentStory: string
  startTime: string
  progress: string
}

interface ProcessDetails {
  status: string
  message: string
  processInfo: {
    pid: number
    state: string
    cpu: number
    memory: number
    elapsed: string
    command: string
  } | null
  detailedInfo: {
    cwd?: string
    cmdline?: string
    openFiles?: number
    threads?: number
    VmRSS?: string
    VmSize?: string
    VmPeak?: string
    ralphStatus?: {
      running: boolean
      currentStory: string
      progress: string
      lastLog: string[]
    }
    stuckIndicators?: string[]
  }
  stateDescription?: string
}

function formatElapsed(seconds: number): string {
  const hrs = Math.floor(seconds / 3600)
  const mins = Math.floor((seconds % 3600) / 60)
  const secs = seconds % 60
  if (hrs > 0) return `${hrs}h ${mins}m`
  if (mins > 0) return `${mins}m ${secs}s`
  return `${secs}s`
}

function ProcessTypeIcon({ type }: { type: ClaudeProcess['type'] }) {
  switch (type) {
    case 'interactive':
      return <Terminal className="w-4 h-4 text-blue-400" />
    case 'ralph':
      return <Zap className="w-4 h-4 text-yellow-400" />
    case 'mcp':
      return <Server className="w-4 h-4 text-purple-400" />
    default:
      return <Circle className="w-4 h-4 text-gray-400" />
  }
}

function StoryStatusIcon({ status }: { status: StoryDetail['status'] }) {
  switch (status) {
    case 'done':
      return <CheckCircle2 className="w-5 h-5 text-green-500" />
    case 'running':
      return <Loader2 className="w-5 h-5 text-yellow-500 animate-spin" />
    case 'fail':
      return <XCircle className="w-5 h-5 text-red-500" />
    default:
      return <Circle className="w-5 h-5 text-gray-500" />
  }
}

function StateIndicator({ state }: { state: string }) {
  const colors: Record<string, string> = {
    'R': 'bg-green-500',
    'S': 'bg-blue-500',
    'D': 'bg-red-500',
    'T': 'bg-yellow-500',
    'Z': 'bg-gray-500',
  }
  return (
    <span className={`inline-block w-2 h-2 rounded-full ${colors[state] || 'bg-gray-400'}`} />
  )
}

export default function LiveDashboard() {
  const [data, setData] = useState<LiveData | null>(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [autoRefresh, setAutoRefresh] = useState(true)
  const [selectedPid, setSelectedPid] = useState<number | null>(null)
  const [processDetails, setProcessDetails] = useState<ProcessDetails | null>(null)
  const [checkingProcess, setCheckingProcess] = useState(false)
  const [restartingProcess, setRestartingProcess] = useState(false)
  const [showProcessTable, setShowProcessTable] = useState(true)
  const [allProjects, setAllProjects] = useState<RalphProject[]>([])
  const [bottomBarExpanded, setBottomBarExpanded] = useState(true)
  const logsEndRef = useRef<HTMLDivElement>(null)

  const fetchProjects = async () => {
    try {
      const res = await fetch('/api/processes')
      if (!res.ok) return
      const json = await res.json()
      setAllProjects(json.processes || [])
    } catch (err) {
      // Silent fail for projects bar
    }
  }

  const fetchData = async () => {
    try {
      const res = await fetch('/api/live')
      if (!res.ok) throw new Error('Failed to fetch')
      const json = await res.json()
      setData(json)
      setError(null)
    } catch (err) {
      setError('Failed to fetch live data')
    } finally {
      setLoading(false)
    }
  }

  const checkProcess = async (pid: number) => {
    setCheckingProcess(true)
    try {
      const res = await fetch('/api/live/check', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ pid })
      })
      const json = await res.json()
      setProcessDetails(json)
    } catch (err) {
      setProcessDetails({
        status: 'error',
        message: 'Failed to check process',
        processInfo: null,
        detailedInfo: {}
      })
    } finally {
      setCheckingProcess(false)
    }
  }

  const restartProcess = async (pid: number) => {
    setRestartingProcess(true)
    try {
      const res = await fetch('/api/live/check', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ pid, action: 'restart' })
      })
      const json = await res.json()
      setProcessDetails(json)
      // Refresh the main data after restart
      setTimeout(fetchData, 2000)
    } catch (err) {
      setProcessDetails({
        status: 'error',
        message: 'Failed to restart process',
        processInfo: null,
        detailedInfo: {}
      })
    } finally {
      setRestartingProcess(false)
    }
  }

  useEffect(() => {
    fetchData()
    fetchProjects()
    if (autoRefresh) {
      const interval = setInterval(() => {
        fetchData()
        fetchProjects()
      }, 3000)
      return () => clearInterval(interval)
    }
  }, [autoRefresh])

  useEffect(() => {
    if (selectedPid) {
      checkProcess(selectedPid)
    }
  }, [selectedPid])

  useEffect(() => {
    logsEndRef.current?.scrollIntoView({ behavior: 'smooth' })
  }, [data?.ralphStatus?.logs])

  if (loading && !data) {
    return (
      <div className="min-h-screen bg-gray-950 flex items-center justify-center">
        <Loader2 className="w-8 h-8 text-blue-500 animate-spin" />
      </div>
    )
  }

  return (
    <div className="min-h-screen bg-gray-950 text-gray-100 p-6">
      {/* Header */}
      <header className="mb-8">
        <div className="flex items-center justify-between">
          <div className="flex items-center gap-3">
            <Link
              href="/"
              className="p-2 rounded-lg hover:bg-gray-800 transition-colors"
            >
              <ArrowLeft className="w-5 h-5 text-gray-400" />
            </Link>
            <Activity className="w-8 h-8 text-blue-500" />
            <h1 className="text-2xl font-bold">Claude Live Dashboard</h1>
            <span className="px-2 py-1 text-xs rounded-full bg-green-500/20 text-green-400 flex items-center gap-1">
              <span className="w-2 h-2 rounded-full bg-green-500 animate-pulse" />
              Live
            </span>
          </div>
          <div className="flex items-center gap-4">
            <button
              onClick={() => setAutoRefresh(!autoRefresh)}
              className={`px-3 py-1.5 rounded-lg text-sm flex items-center gap-2 transition-colors ${
                autoRefresh
                  ? 'bg-blue-500/20 text-blue-400 border border-blue-500/30'
                  : 'bg-gray-800 text-gray-400 border border-gray-700'
              }`}
            >
              <RefreshCw className={`w-4 h-4 ${autoRefresh ? 'animate-spin' : ''}`} />
              Auto-refresh {autoRefresh ? 'ON' : 'OFF'}
            </button>
            <span className="text-sm text-gray-500">
              {data?.timestamp ? new Date(data.timestamp).toLocaleTimeString() : ''}
            </span>
          </div>
        </div>
      </header>

      {error && (
        <div className="mb-6 p-4 rounded-lg bg-red-500/20 border border-red-500/30 text-red-400">
          {error}
        </div>
      )}

      {/* System Stats */}
      <div className="grid grid-cols-4 gap-4 mb-8">
        <div className="p-4 rounded-xl bg-gray-900 border border-gray-800">
          <div className="flex items-center gap-2 text-gray-400 text-sm mb-2">
            <Cpu className="w-4 h-4" />
            CPU Load
          </div>
          <div className="text-2xl font-bold">
            {data?.systemStats?.loadAvg?.[0]?.toFixed(2) || '0.00'}
          </div>
          <div className="text-xs text-gray-500 mt-1">
            5m: {data?.systemStats?.loadAvg?.[1]?.toFixed(2)} | 15m: {data?.systemStats?.loadAvg?.[2]?.toFixed(2)}
          </div>
        </div>

        <div className="p-4 rounded-xl bg-gray-900 border border-gray-800">
          <div className="flex items-center gap-2 text-gray-400 text-sm mb-2">
            <MemoryStick className="w-4 h-4" />
            Memory
          </div>
          <div className="text-2xl font-bold">
            {data?.systemStats?.memUsed || 0} MB
          </div>
          <div className="text-xs text-gray-500 mt-1">
            of {data?.systemStats?.memTotal || 0} MB ({Math.round((data?.systemStats?.memUsed || 0) / (data?.systemStats?.memTotal || 1) * 100)}%)
          </div>
        </div>

        <div className="p-4 rounded-xl bg-gray-900 border border-gray-800">
          <div className="flex items-center gap-2 text-gray-400 text-sm mb-2">
            <Terminal className="w-4 h-4" />
            Claude Processes
          </div>
          <div className="text-2xl font-bold text-blue-400">
            {data?.totalProcesses || 0}
          </div>
          <div className="text-xs text-gray-500 mt-1">
            Active instances
          </div>
        </div>

        <div className="p-4 rounded-xl bg-gray-900 border border-gray-800">
          <div className="flex items-center gap-2 text-gray-400 text-sm mb-2">
            <Clock className="w-4 h-4" />
            System Uptime
          </div>
          <div className="text-lg font-bold">
            {data?.systemStats?.uptime || 'Unknown'}
          </div>
        </div>
      </div>

      {/* Collapsible Claude Processes Table */}
      <div className="mb-8 rounded-xl bg-gray-900 border border-gray-800 overflow-hidden">
        <button
          onClick={() => setShowProcessTable(!showProcessTable)}
          className="w-full px-4 py-3 border-b border-gray-800 flex items-center gap-2 hover:bg-gray-800/50 transition-colors"
        >
          {showProcessTable ? (
            <ChevronDown className="w-5 h-5 text-gray-400" />
          ) : (
            <ChevronRight className="w-5 h-5 text-gray-400" />
          )}
          <Terminal className="w-5 h-5 text-blue-400" />
          <h2 className="font-semibold">Claude Processes</h2>
          <span className="ml-auto px-2 py-0.5 text-xs rounded-full bg-blue-500/20 text-blue-400">
            {data?.claudeProcesses?.length || 0} running
          </span>
        </button>

        {showProcessTable && (
          <div className="overflow-x-auto">
            <table className="w-full text-sm">
              <thead className="bg-gray-800/50">
                <tr>
                  <th className="px-4 py-2 text-left text-gray-400 font-medium">Select</th>
                  <th className="px-4 py-2 text-left text-gray-400 font-medium">PID</th>
                  <th className="px-4 py-2 text-left text-gray-400 font-medium">Type</th>
                  <th className="px-4 py-2 text-left text-gray-400 font-medium">Terminal</th>
                  <th className="px-4 py-2 text-left text-gray-400 font-medium">CPU %</th>
                  <th className="px-4 py-2 text-left text-gray-400 font-medium">Mem %</th>
                  <th className="px-4 py-2 text-left text-gray-400 font-medium">Uptime</th>
                  <th className="px-4 py-2 text-left text-gray-400 font-medium">Command</th>
                </tr>
              </thead>
              <tbody className="divide-y divide-gray-800">
                {data?.claudeProcesses?.map((proc) => (
                  <tr
                    key={proc.pid}
                    className={`hover:bg-gray-800/50 transition-colors cursor-pointer ${
                      selectedPid === proc.pid ? 'bg-blue-500/10 border-l-2 border-blue-500' : ''
                    }`}
                    onClick={() => setSelectedPid(proc.pid)}
                  >
                    <td className="px-4 py-3">
                      <input
                        type="radio"
                        name="selectedProcess"
                        checked={selectedPid === proc.pid}
                        onChange={() => setSelectedPid(proc.pid)}
                        className="w-4 h-4 text-blue-500 bg-gray-800 border-gray-600 focus:ring-blue-500"
                      />
                    </td>
                    <td className="px-4 py-3 font-mono text-gray-300">{proc.pid}</td>
                    <td className="px-4 py-3">
                      <span className={`inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-xs ${
                        proc.type === 'interactive' ? 'bg-blue-500/20 text-blue-400' :
                        proc.type === 'ralph' ? 'bg-yellow-500/20 text-yellow-400' :
                        proc.type === 'mcp' ? 'bg-purple-500/20 text-purple-400' :
                        'bg-gray-500/20 text-gray-400'
                      }`}>
                        <ProcessTypeIcon type={proc.type} />
                        {proc.type}
                      </span>
                    </td>
                    <td className="px-4 py-3 text-gray-400">{proc.terminal}</td>
                    <td className="px-4 py-3 text-gray-300">{proc.cpu}</td>
                    <td className="px-4 py-3 text-gray-300">{proc.memory}</td>
                    <td className="px-4 py-3 text-gray-400">{proc.uptime}</td>
                    <td className="px-4 py-3 text-gray-500 truncate max-w-xs">{proc.command}</td>
                  </tr>
                ))}
                {(!data?.claudeProcesses || data.claudeProcesses.length === 0) && (
                  <tr>
                    <td colSpan={8} className="px-4 py-8 text-center text-gray-500">
                      No Claude processes running
                    </td>
                  </tr>
                )}
              </tbody>
            </table>
          </div>
        )}
      </div>

      {/* Selected Process Details */}
      {selectedPid && (
        <div className="mb-8 rounded-xl bg-gray-900 border border-gray-800 overflow-hidden">
          <div className="px-4 py-3 border-b border-gray-800 flex items-center justify-between">
            <div className="flex items-center gap-2">
              <Info className="w-5 h-5 text-blue-400" />
              <h2 className="font-semibold">Process Details - PID {selectedPid}</h2>
            </div>
            <div className="flex items-center gap-2">
              <button
                onClick={() => checkProcess(selectedPid)}
                disabled={checkingProcess}
                className="px-3 py-1.5 rounded-lg text-sm flex items-center gap-2 bg-blue-500/20 text-blue-400 border border-blue-500/30 hover:bg-blue-500/30 transition-colors disabled:opacity-50"
              >
                {checkingProcess ? (
                  <Loader2 className="w-4 h-4 animate-spin" />
                ) : (
                  <RefreshCw className="w-4 h-4" />
                )}
                Check Status
              </button>
              <button
                onClick={() => restartProcess(selectedPid)}
                disabled={restartingProcess}
                className="px-3 py-1.5 rounded-lg text-sm flex items-center gap-2 bg-red-500/20 text-red-400 border border-red-500/30 hover:bg-red-500/30 transition-colors disabled:opacity-50"
              >
                {restartingProcess ? (
                  <Loader2 className="w-4 h-4 animate-spin" />
                ) : (
                  <Square className="w-4 h-4" />
                )}
                Kill & Restart
              </button>
              <button
                onClick={() => {
                  setSelectedPid(null)
                  setProcessDetails(null)
                }}
                className="px-3 py-1.5 rounded-lg text-sm bg-gray-800 text-gray-400 hover:bg-gray-700 transition-colors"
              >
                Close
              </button>
            </div>
          </div>

          {checkingProcess && !processDetails ? (
            <div className="px-4 py-8 flex items-center justify-center">
              <Loader2 className="w-6 h-6 text-blue-500 animate-spin" />
            </div>
          ) : processDetails ? (
            <div className="p-4">
              {/* Status Banner */}
              <div className={`mb-4 p-4 rounded-lg border ${
                processDetails.status === 'running' ? 'bg-green-500/10 border-green-500/30' :
                processDetails.status === 'stuck' ? 'bg-yellow-500/10 border-yellow-500/30' :
                processDetails.status === 'dead' ? 'bg-red-500/10 border-red-500/30' :
                processDetails.status === 'killed' || processDetails.status === 'restarted' ? 'bg-blue-500/10 border-blue-500/30' :
                'bg-gray-500/10 border-gray-500/30'
              }`}>
                <div className="flex items-center gap-2">
                  {processDetails.status === 'running' && <CheckCircle2 className="w-5 h-5 text-green-500" />}
                  {processDetails.status === 'stuck' && <AlertTriangle className="w-5 h-5 text-yellow-500" />}
                  {processDetails.status === 'dead' && <XCircle className="w-5 h-5 text-red-500" />}
                  {(processDetails.status === 'killed' || processDetails.status === 'restarted') && <CheckCircle2 className="w-5 h-5 text-blue-500" />}
                  <span className={`font-medium ${
                    processDetails.status === 'running' ? 'text-green-400' :
                    processDetails.status === 'stuck' ? 'text-yellow-400' :
                    processDetails.status === 'dead' ? 'text-red-400' :
                    'text-blue-400'
                  }`}>
                    {processDetails.message}
                  </span>
                </div>
                {processDetails.stateDescription && (
                  <div className="mt-2 text-sm text-gray-400">
                    State: {processDetails.stateDescription}
                  </div>
                )}
              </div>

              {/* Stuck Indicators */}
              {processDetails.detailedInfo?.stuckIndicators && processDetails.detailedInfo.stuckIndicators.length > 0 && (
                <div className="mb-4 p-4 rounded-lg bg-yellow-500/10 border border-yellow-500/30">
                  <div className="flex items-center gap-2 mb-2">
                    <AlertTriangle className="w-5 h-5 text-yellow-500" />
                    <span className="font-medium text-yellow-400">Potential Issues Detected</span>
                  </div>
                  <ul className="list-disc list-inside text-sm text-yellow-300">
                    {processDetails.detailedInfo.stuckIndicators.map((indicator, i) => (
                      <li key={i}>{indicator}</li>
                    ))}
                  </ul>
                </div>
              )}

              {/* Process Info Grid */}
              {processDetails.processInfo && (
                <div className="grid grid-cols-3 gap-4 mb-4">
                  <div className="p-3 rounded-lg bg-gray-800/50">
                    <div className="text-xs text-gray-500 mb-1">State</div>
                    <div className="flex items-center gap-2">
                      <StateIndicator state={processDetails.processInfo.state} />
                      <span className="font-mono text-gray-300">{processDetails.processInfo.state}</span>
                    </div>
                  </div>
                  <div className="p-3 rounded-lg bg-gray-800/50">
                    <div className="text-xs text-gray-500 mb-1">CPU</div>
                    <div className="font-mono text-gray-300">{processDetails.processInfo.cpu}%</div>
                  </div>
                  <div className="p-3 rounded-lg bg-gray-800/50">
                    <div className="text-xs text-gray-500 mb-1">Memory</div>
                    <div className="font-mono text-gray-300">{processDetails.processInfo.memory}%</div>
                  </div>
                  <div className="p-3 rounded-lg bg-gray-800/50">
                    <div className="text-xs text-gray-500 mb-1">Elapsed</div>
                    <div className="font-mono text-gray-300">{processDetails.processInfo.elapsed}</div>
                  </div>
                  <div className="p-3 rounded-lg bg-gray-800/50">
                    <div className="text-xs text-gray-500 mb-1">Open Files</div>
                    <div className="font-mono text-gray-300">{processDetails.detailedInfo?.openFiles || 'N/A'}</div>
                  </div>
                  <div className="p-3 rounded-lg bg-gray-800/50">
                    <div className="text-xs text-gray-500 mb-1">Threads</div>
                    <div className="font-mono text-gray-300">{processDetails.detailedInfo?.threads || 'N/A'}</div>
                  </div>
                </div>
              )}

              {/* Memory Details */}
              {(processDetails.detailedInfo?.VmRSS || processDetails.detailedInfo?.VmSize) && (
                <div className="mb-4 p-4 rounded-lg bg-gray-800/50">
                  <div className="flex items-center gap-2 mb-3">
                    <MemoryStick className="w-4 h-4 text-purple-400" />
                    <span className="font-medium text-gray-300">Memory Details</span>
                  </div>
                  <div className="grid grid-cols-3 gap-4 text-sm">
                    <div>
                      <div className="text-gray-500">Resident (VmRSS)</div>
                      <div className="font-mono text-gray-300">{processDetails.detailedInfo.VmRSS || 'N/A'}</div>
                    </div>
                    <div>
                      <div className="text-gray-500">Virtual (VmSize)</div>
                      <div className="font-mono text-gray-300">{processDetails.detailedInfo.VmSize || 'N/A'}</div>
                    </div>
                    <div>
                      <div className="text-gray-500">Peak (VmPeak)</div>
                      <div className="font-mono text-gray-300">{processDetails.detailedInfo.VmPeak || 'N/A'}</div>
                    </div>
                  </div>
                </div>
              )}

              {/* Working Directory */}
              {processDetails.detailedInfo?.cwd && (
                <div className="mb-4 p-4 rounded-lg bg-gray-800/50">
                  <div className="flex items-center gap-2 mb-2">
                    <Folder className="w-4 h-4 text-blue-400" />
                    <span className="font-medium text-gray-300">Working Directory</span>
                  </div>
                  <code className="text-sm text-gray-400 break-all">{processDetails.detailedInfo.cwd}</code>
                </div>
              )}

              {/* Command Line */}
              {processDetails.detailedInfo?.cmdline && (
                <div className="mb-4 p-4 rounded-lg bg-gray-800/50">
                  <div className="flex items-center gap-2 mb-2">
                    <FileText className="w-4 h-4 text-green-400" />
                    <span className="font-medium text-gray-300">Command Line</span>
                  </div>
                  <code className="text-sm text-gray-400 break-all">{processDetails.detailedInfo.cmdline}</code>
                </div>
              )}

              {/* Ralph Status (if applicable) */}
              {processDetails.detailedInfo?.ralphStatus && (
                <div className="p-4 rounded-lg bg-yellow-500/10 border border-yellow-500/30">
                  <div className="flex items-center gap-2 mb-3">
                    <Zap className="w-4 h-4 text-yellow-400" />
                    <span className="font-medium text-yellow-400">Ralph Automation Status</span>
                  </div>
                  <div className="grid grid-cols-3 gap-4 text-sm mb-3">
                    <div>
                      <div className="text-gray-500">Running</div>
                      <div className={processDetails.detailedInfo.ralphStatus.running ? 'text-green-400' : 'text-gray-400'}>
                        {processDetails.detailedInfo.ralphStatus.running ? 'Yes' : 'No'}
                      </div>
                    </div>
                    <div>
                      <div className="text-gray-500">Current Story</div>
                      <div className="text-gray-300">{processDetails.detailedInfo.ralphStatus.currentStory || 'N/A'}</div>
                    </div>
                    <div>
                      <div className="text-gray-500">Progress</div>
                      <div className="text-gray-300">{processDetails.detailedInfo.ralphStatus.progress}</div>
                    </div>
                  </div>
                  {processDetails.detailedInfo.ralphStatus.lastLog && processDetails.detailedInfo.ralphStatus.lastLog.length > 0 && (
                    <div>
                      <div className="text-gray-500 text-sm mb-1">Recent Logs</div>
                      <div className="bg-black/30 rounded p-2 font-mono text-xs">
                        {processDetails.detailedInfo.ralphStatus.lastLog.map((log, i) => (
                          <div key={i} className="text-gray-400">{log}</div>
                        ))}
                      </div>
                    </div>
                  )}
                </div>
              )}
            </div>
          ) : (
            <div className="px-4 py-8 text-center text-gray-500">
              Click "Check Status" to load process details
            </div>
          )}
        </div>
      )}

      {/* Ralph Status */}
      <div className="grid grid-cols-1 gap-6">
        <div className="rounded-xl bg-gray-900 border border-gray-800 overflow-hidden">
          <div className="px-4 py-3 border-b border-gray-800 flex items-center gap-2">
            <Zap className="w-5 h-5 text-yellow-400" />
            <h2 className="font-semibold">Ralph Automation</h2>
            {data?.ralphStatus?.runningStory && (
              <span className="ml-auto px-2 py-0.5 text-xs rounded-full bg-yellow-500/20 text-yellow-400 flex items-center gap-1">
                <Loader2 className="w-3 h-3 animate-spin" />
                Running
              </span>
            )}
          </div>

          {data?.ralphStatus ? (
            <div>
              {/* Project Info */}
              <div className="px-4 py-3 border-b border-gray-800 bg-gray-800/30">
                <div className="flex items-center gap-2 mb-2">
                  <GitBranch className="w-4 h-4 text-purple-400" />
                  <span className="font-medium text-purple-400">{data.ralphStatus.project}</span>
                </div>
                <div className="text-xs text-gray-500 font-mono truncate">
                  {data.ralphStatus.branch}
                </div>
                <div className="flex items-center gap-4 mt-2 text-sm">
                  <span className="text-gray-400">
                    Progress: <span className="text-green-400 font-bold">{data.ralphStatus.progress}</span>
                  </span>
                  <span className="text-gray-400">
                    Elapsed: <span className="text-blue-400">{formatElapsed(data.ralphStatus.elapsed)}</span>
                  </span>
                </div>
              </div>

              {/* Story List */}
              <div className="divide-y divide-gray-800 max-h-[300px] overflow-y-auto">
                {data.ralphStatus.storyDetails.map((story) => (
                  <div
                    key={story.id}
                    className={`px-4 py-2 flex items-center gap-3 ${
                      story.status === 'running' ? 'bg-yellow-500/10' : ''
                    }`}
                  >
                    <StoryStatusIcon status={story.status} />
                    <div className="flex-1 min-w-0">
                      <div className="flex items-center gap-2">
                        <span className="font-mono text-sm text-gray-300">{story.id}</span>
                        {story.status === 'running' && (
                          <span className="text-xs text-yellow-400">← Current</span>
                        )}
                      </div>
                      <div className="text-xs text-gray-500 truncate">{story.title}</div>
                    </div>
                    {story.time && (
                      <span className="text-xs text-gray-500">{formatElapsed(story.time)}</span>
                    )}
                  </div>
                ))}
              </div>

              {/* Logs - Detailed View */}
              <div className="border-t border-gray-800">
                <div className="px-4 py-2 text-xs text-gray-500 bg-gray-800/30 flex items-center justify-between">
                  <div className="flex items-center gap-2">
                    <span>Live Logs</span>
                    <span className="flex items-center gap-1 px-2 py-0.5 rounded-full bg-green-500/20 text-green-400">
                      <span className="w-1.5 h-1.5 rounded-full bg-green-500 animate-pulse" />
                      LIVE
                    </span>
                  </div>
                  <span className="text-gray-600">{data.ralphStatus.logs.length} lines</span>
                </div>
                <div className="px-4 py-2 min-h-[300px] max-h-[500px] overflow-y-auto bg-black/50 font-mono text-xs">
                  {data.ralphStatus.logs.map((log, i) => {
                    const isSuccess = log.includes('✓') || log.includes('Completed') || log.includes('PASS') || log.includes('success')
                    const isError = log.includes('Error') || log.includes('Failed') || log.includes('FAIL') || log.includes('❌')
                    const isWarning = log.includes('Warning') || log.includes('⚠')
                    const isRunning = log.includes('Running') || log.includes('Starting') || log.includes('...')
                    const isFile = log.includes('.ts') || log.includes('.tsx') || log.includes('.js')
                    const isCommand = log.startsWith('$') || log.startsWith('npm') || log.startsWith('git')
                    const isStory = log.includes('US-') || log.includes('Story')

                    return (
                      <div key={i} className={`py-0.5 leading-relaxed ${
                        isSuccess ? 'text-green-400' :
                        isError ? 'text-red-400 font-medium' :
                        isWarning ? 'text-yellow-400' :
                        isStory ? 'text-purple-400 font-medium' :
                        isRunning ? 'text-blue-400' :
                        isFile ? 'text-cyan-400' :
                        isCommand ? 'text-orange-400' :
                        'text-gray-400'
                      }`}>
                        {log}
                      </div>
                    )
                  })}
                  {data.ralphStatus.running && (
                    <div className="flex items-center gap-2 mt-2 text-blue-400">
                      <span className="inline-block w-2 h-3 bg-blue-400 animate-pulse" />
                      <span className="animate-pulse">Processing...</span>
                    </div>
                  )}
                  <div ref={logsEndRef} />
                </div>
                {/* Legend */}
                <div className="px-4 py-2 border-t border-gray-800 bg-gray-900/50 flex items-center gap-3 text-xs text-gray-500">
                  <span>Legend:</span>
                  <span className="text-green-400">● Success</span>
                  <span className="text-red-400">● Error</span>
                  <span className="text-yellow-400">● Warning</span>
                  <span className="text-purple-400">● Story</span>
                  <span className="text-blue-400">● Running</span>
                  <span className="text-cyan-400">● Files</span>
                </div>
              </div>
            </div>
          ) : (
            <div className="px-4 py-8 text-center text-gray-500">
              No Ralph automation running
            </div>
          )}
        </div>
      </div>

      {/* Spacer for fixed bottom bar */}
      <div className={bottomBarExpanded ? "h-48" : "h-12"} />

      {/* Fixed Bottom Bar - Collapsible All Processes */}
      <div className="fixed bottom-0 left-0 right-0 bg-gray-900/95 backdrop-blur-sm border-t border-gray-700 z-50">
        {/* Header - Always visible */}
        <button
          onClick={() => setBottomBarExpanded(!bottomBarExpanded)}
          className="w-full px-4 py-2 flex items-center justify-between hover:bg-gray-800/50 transition-colors"
        >
          <div className="flex items-center gap-2 text-xs text-gray-400">
            <Layers className="w-4 h-4" />
            <span className="font-medium">All Recent Processes</span>
            <span className="text-gray-600">
              ({allProjects.length} projects, {data?.claudeProcesses?.length || 0} claude)
            </span>
            {allProjects.some(p => p.status === 'running') && (
              <span className="flex items-center gap-1 text-yellow-400">
                <Loader2 className="w-3 h-3 animate-spin" />
                <span>Active</span>
              </span>
            )}
          </div>
          <div className="flex items-center gap-2">
            {bottomBarExpanded ? (
              <ChevronDown className="w-4 h-4 text-gray-400" />
            ) : (
              <ChevronRight className="w-4 h-4 text-gray-400" />
            )}
          </div>
        </button>

        {/* Expanded Content */}
        {bottomBarExpanded && (
          <div className="px-4 pb-3 max-h-[300px] overflow-y-auto">
            {/* Ralphy Boy Projects */}
            <div className="mb-3">
              <div className="text-xs text-gray-500 mb-2 flex items-center gap-2">
                <Zap className="w-3 h-3" />
                Ralphy Boy Projects
              </div>
              <div className="flex gap-2 overflow-x-auto pb-2">
                {allProjects.length === 0 ? (
                  <div className="text-sm text-gray-600">No projects tracked</div>
                ) : (
                  allProjects.map((proj) => (
                    <div
                      key={proj.project}
                      className={`flex-shrink-0 px-3 py-2 rounded-lg border transition-all ${
                        proj.status === 'running'
                          ? 'bg-yellow-900/30 border-yellow-600/50 shadow-lg shadow-yellow-900/20'
                          : proj.status === 'completed'
                          ? 'bg-green-900/20 border-green-700/40'
                          : proj.status === 'failed'
                          ? 'bg-red-900/20 border-red-700/40'
                          : 'bg-gray-800/50 border-gray-700/50'
                      }`}
                    >
                      <div className="flex items-center gap-2 mb-1">
                        {proj.status === 'running' ? (
                          <Loader2 className="w-4 h-4 text-yellow-400 animate-spin" />
                        ) : proj.status === 'completed' ? (
                          <CheckCircle2 className="w-4 h-4 text-green-400" />
                        ) : proj.status === 'failed' ? (
                          <XCircle className="w-4 h-4 text-red-400" />
                        ) : (
                          <Circle className="w-4 h-4 text-gray-500" />
                        )}
                        <span className="font-medium text-sm text-white truncate max-w-[120px]">
                          {proj.project}
                        </span>
                      </div>
                      <div className="flex items-center gap-2 text-xs">
                        <span className={`font-mono ${
                          proj.status === 'running' ? 'text-yellow-300' :
                          proj.status === 'completed' ? 'text-green-300' :
                          proj.status === 'failed' ? 'text-red-300' : 'text-gray-400'
                        }`}>
                          {proj.progress}
                        </span>
                        {proj.status === 'running' && proj.currentStory && (
                          <span className="text-blue-300 truncate max-w-[80px]">{proj.currentStory}</span>
                        )}
                      </div>
                    </div>
                  ))
                )}
              </div>
            </div>

            {/* Claude Processes */}
            <div>
              <div className="text-xs text-gray-500 mb-2 flex items-center gap-2">
                <Terminal className="w-3 h-3" />
                Claude Processes
              </div>
              <div className="flex gap-2 overflow-x-auto pb-2">
                {!data?.claudeProcesses?.length ? (
                  <div className="text-sm text-gray-600">No claude processes</div>
                ) : (
                  data.claudeProcesses.slice(0, 10).map((proc) => (
                    <div
                      key={proc.pid}
                      onClick={() => setSelectedPid(proc.pid)}
                      className={`flex-shrink-0 px-3 py-2 rounded-lg border cursor-pointer transition-all hover:border-blue-500/50 ${
                        proc.type === 'ralph'
                          ? 'bg-yellow-900/20 border-yellow-700/40'
                          : proc.type === 'interactive'
                          ? 'bg-blue-900/20 border-blue-700/40'
                          : proc.type === 'mcp'
                          ? 'bg-purple-900/20 border-purple-700/40'
                          : 'bg-gray-800/50 border-gray-700/50'
                      } ${selectedPid === proc.pid ? 'ring-2 ring-blue-500' : ''}`}
                    >
                      <div className="flex items-center gap-2 mb-1">
                        <ProcessTypeIcon type={proc.type} />
                        <span className="font-medium text-sm text-white truncate max-w-[100px]">
                          {proc.project || proc.type}
                        </span>
                      </div>
                      <div className="flex items-center gap-2 text-xs text-gray-400">
                        <span>PID: {proc.pid}</span>
                        <span className="text-gray-600">|</span>
                        <span>{proc.cpu}% CPU</span>
                        <span className="text-gray-600">|</span>
                        <span>{proc.uptime}</span>
                      </div>
                    </div>
                  ))
                )}
              </div>
            </div>
          </div>
        )}
      </div>
    </div>
  )
}