import { useCallback, useEffect, useRef, useState } from 'react'; import { httpClient } from '@/app/infra/http/HttpClient'; import { useTranslation } from 'react-i18next'; import { PluginLogEntry } from '@/app/infra/entities/plugin'; import { Button } from '@/components/ui/button'; import { Switch } from '@/components/ui/switch'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { RefreshCw } from 'lucide-react'; const LEVEL_OPTIONS = ['ALL', 'DEBUG', 'INFO', 'WARNING', 'ERROR'] as const; function levelClassName(level: string): string { switch (level) { case 'ERROR': case 'CRITICAL': return 'text-red-500'; case 'WARNING': return 'text-amber-500'; case 'DEBUG': return 'text-gray-400 dark:text-gray-500'; default: return 'text-gray-700 dark:text-gray-300'; } } export default function MCPLogs({ serverName }: { serverName: string }) { const { t } = useTranslation(); const [logs, setLogs] = useState([]); const [isLoading, setIsLoading] = useState(false); const [level, setLevel] = useState('ALL'); const [autoRefresh, setAutoRefresh] = useState(true); const scrollRef = useRef(null); const atBottomRef = useRef(true); const fetchLogs = useCallback(() => { setIsLoading(true); httpClient .getMcpServerLogs(serverName, 500, level === 'ALL' ? undefined : level) .then((res) => { setLogs(res.logs ?? []); }) .catch(() => { setLogs([]); }) .finally(() => { setIsLoading(false); }); }, [serverName, level]); useEffect(() => { fetchLogs(); }, [fetchLogs]); // Auto-refresh poll loop. useEffect(() => { if (!autoRefresh) return; const timer = setInterval(fetchLogs, 3000); return () => clearInterval(timer); }, [autoRefresh, fetchLogs]); // Keep view pinned to bottom when the user is already at the bottom. useEffect(() => { const el = scrollRef.current; if (el && atBottomRef.current) { el.scrollTop = el.scrollHeight; } }, [logs]); function handleScroll() { const el = scrollRef.current; if (!el) return; atBottomRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < 40; } return (
{logs.length === 0 ? (
{t('mcp.logsEmpty')}
) : ( logs.map((entry, idx) => (
{entry.text}
)) )}
); }