| 'use client' |
|
|
| import { useEffect, useRef } from 'react' |
| import { X, MonitorPlay, Brain, Globe, Code2, Terminal, GitBranch, Rocket, CheckCircle, XCircle, Loader, FileText, Search, PenTool, HardDrive } from 'lucide-react' |
| import { useAppStore, type ComputerUseStep } from '@/store/useAppStore' |
|
|
| const STEP_ICONS: Record<string, { icon: React.ElementType; color: string; bg: string }> = { |
| thinking: { icon: Brain, color: '#a78bfa', bg: 'rgba(124,58,237,0.12)' }, |
| browsing: { icon: Globe, color: '#22d3ee', bg: 'rgba(34,211,238,0.12)' }, |
| coding: { icon: Code2, color: '#34d399', bg: 'rgba(52,211,153,0.12)' }, |
| terminal: { icon: Terminal, color: '#f59e0b', bg: 'rgba(245,158,11,0.12)' }, |
| executing: { icon: Terminal, color: '#f59e0b', bg: 'rgba(245,158,11,0.12)' }, |
| file: { icon: FileText, color: '#94a3b8', bg: 'rgba(148,163,184,0.1)' }, |
| git: { icon: GitBranch, color: '#60a5fa', bg: 'rgba(96,165,250,0.12)' }, |
| deploy: { icon: Rocket, color: '#f472b6', bg: 'rgba(244,114,182,0.12)' }, |
| complete: { icon: CheckCircle, color: '#22c55e', bg: 'rgba(34,197,94,0.12)' }, |
| error: { icon: XCircle, color: '#ef4444', bg: 'rgba(239,68,68,0.12)' }, |
| reading: { icon: FileText, color: '#94a3b8', bg: 'rgba(148,163,184,0.1)' }, |
| writing: { icon: PenTool, color: '#818cf8', bg: 'rgba(129,140,248,0.12)'}, |
| searching: { icon: Search, color: '#fbbf24', bg: 'rgba(251,191,36,0.12)' }, |
| sandbox: { icon: HardDrive, color: '#fb923c', bg: 'rgba(251,146,60,0.12)' }, |
| } |
|
|
| function StepCard({ step }: { step: ComputerUseStep }) { |
| const def = STEP_ICONS[step.type] || STEP_ICONS.thinking |
| const Icon = def.icon |
|
|
| return ( |
| <div |
| className="computer-use-step animate-fade-in" |
| style={{ borderBottom: '1px solid var(--border)' }} |
| > |
| <div className="computer-use-step-icon" style={{ background: def.bg }}> |
| {step.status === 'running' && step.type !== 'complete' && step.type !== 'error' && step.type !== 'done' ? ( |
| <Loader size={14} style={{ color: def.color, animation: 'spin 1s linear infinite' }} /> |
| ) : ( |
| <Icon size={14} style={{ color: def.color }} /> |
| )} |
| </div> |
| <div className="flex-1 min-w-0"> |
| <div className="flex items-center justify-between gap-2"> |
| <span className="text-xs font-semibold capitalize" style={{ color: def.color }}> |
| {step.type} |
| </span> |
| <span className="text-[10px]" style={{ color: 'var(--text-muted)' }}> |
| {new Date(step.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })} |
| </span> |
| </div> |
| <div className="text-xs mt-0.5" style={{ color: 'var(--text-secondary)' }}> |
| {step.title} |
| </div> |
| {step.detail && ( |
| <div className="text-[11px] mt-1 p-2 rounded-lg" style={{ background: 'var(--surface-3)', color: 'var(--text-muted)' }}> |
| {step.detail} |
| </div> |
| )} |
| {step.data && step.type === 'coding' && (() => { |
| const code = step.data['code'] as string | undefined |
| return code ? ( |
| <pre className="text-[11px] mt-1 p-2 rounded-lg overflow-x-auto" style={{ background: 'var(--surface-3)', color: '#86efac', fontFamily: 'monospace' }}> |
| {code.slice(0, 200)}{code.length > 200 ? '...' : ''} |
| </pre> |
| ) : null |
| })()} |
| </div> |
| </div> |
| ) |
| } |
|
|
| export default function ComputerUsePanel() { |
| const { computerUseSteps, clearComputerUseSteps, setComputerUseOpen, locale } = useAppStore() |
| const bottomRef = useRef<HTMLDivElement>(null) |
|
|
| useEffect(() => { |
| bottomRef.current?.scrollIntoView({ behavior: 'smooth' }) |
| }, [computerUseSteps.length]) |
|
|
| const runningSteps = computerUseSteps.filter(s => s.status === 'running').length |
|
|
| return ( |
| <div className="computer-use-panel flex flex-col" style={{ borderLeft: '1px solid var(--border)', background: 'var(--surface-1)' }}> |
| {/* Header */} |
| <div className="flex items-center justify-between px-4 py-3 shrink-0" |
| style={{ borderBottom: '1px solid var(--border)' }}> |
| <div className="flex items-center gap-2"> |
| <div className="w-7 h-7 rounded-lg flex items-center justify-center" |
| style={{ background: 'rgba(124,58,237,0.12)', border: '1px solid rgba(124,58,237,0.2)' }}> |
| <MonitorPlay size={14} style={{ color: '#a78bfa' }} /> |
| </div> |
| <div> |
| <div className="text-xs font-bold" style={{ color: 'var(--text-primary)' }}> |
| {locale === 'my' ? 'Computer ကြည့်ရန်' : 'Computer Use'} |
| </div> |
| <div className="text-[10px]" style={{ color: 'var(--text-muted)' }}> |
| {locale === 'my' ? 'Agent လုပ်ဆောင်မှုများ' : 'Live agent activity'} |
| </div> |
| </div> |
| </div> |
| <div className="flex items-center gap-2"> |
| {runningSteps > 0 && ( |
| <div className="badge badge-purple text-[10px]"> |
| <Loader size={8} style={{ animation: 'spin 1s linear infinite' }} /> |
| {runningSteps} {locale === 'my' ? 'လုပ်နေသည်' : 'running'} |
| </div> |
| )} |
| <button |
| onClick={clearComputerUseSteps} |
| className="text-[10px] px-2 py-1 rounded-md hover:bg-white/5 transition-colors" |
| style={{ color: 'var(--text-muted)' }} |
| > |
| {locale === 'my' ? 'ရှင်းလင်း' : 'Clear'} |
| </button> |
| <button |
| onClick={() => setComputerUseOpen(false)} |
| className="p-1 rounded-md hover:bg-white/5 transition-colors" |
| style={{ color: 'var(--text-muted)' }} |
| > |
| <X size={14} /> |
| </button> |
| </div> |
| </div> |
| |
| {/* Steps */} |
| <div className="flex-1 overflow-y-auto"> |
| {computerUseSteps.length === 0 ? ( |
| <div className="flex flex-col items-center justify-center h-48 gap-3"> |
| <div className="w-12 h-12 rounded-2xl flex items-center justify-center" |
| style={{ background: 'rgba(124,58,237,0.08)', border: '1px solid rgba(124,58,237,0.15)' }}> |
| <MonitorPlay size={20} style={{ color: '#6d28d9' }} /> |
| </div> |
| <div className="text-center"> |
| <div className="text-xs font-semibold" style={{ color: 'var(--text-muted)' }}> |
| {locale === 'my' ? 'လုပ်ဆောင်မှုမရှိသေးပါ' : 'No activity yet'} |
| </div> |
| <div className="text-[10px] mt-1" style={{ color: 'var(--text-muted)' }}> |
| {locale === 'my' |
| ? 'Chat မှာ task တစ်ခုပေးပြီး agent ၏ computer အသုံးပြုမှုကြည့်ပါ' |
| : 'Send a task in chat to see Manus-style agent activity here'} |
| </div> |
| </div> |
| </div> |
| ) : ( |
| <> |
| {computerUseSteps.map(step => ( |
| <StepCard key={step.id} step={step} /> |
| ))} |
| <div ref={bottomRef} /> |
| </> |
| )} |
| </div> |
|
|
| {} |
| <div className="px-4 py-2.5 shrink-0 flex items-center justify-between" |
| style={{ borderTop: '1px solid var(--border)' }}> |
| <span className="text-[10px]" style={{ color: 'var(--text-muted)' }}> |
| {computerUseSteps.length} {locale === 'my' ? 'အဆင့်' : 'steps'} |
| </span> |
| <span className="text-[10px]" style={{ color: 'var(--text-muted)' }}> |
| {computerUseSteps.filter(s => s.type === 'complete').length} {locale === 'my' ? 'ပြီးဆုံး' : 'completed'} |
| </span> |
| </div> |
| </div> |
| ) |
| } |
|
|