import React, { useEffect, useRef, useState } from 'react'; import { formatTime } from '../utils/format.js'; export function LogsPanel() { const [entries, setEntries] = useState([]); const [paused, setPaused] = useState(false); const [filter, setFilter] = useState('all'); const containerRef = useRef(null); const pausedRef = useRef(false); useEffect(() => { pausedRef.current = paused; }, [paused]); useEffect(() => { const source = new EventSource('/api/logs/stream'); source.onmessage = (event) => { if (pausedRef.current) return; try { const entry = JSON.parse(event.data); setEntries((prev) => { const next = [...prev, entry]; if (next.length > 500) next.splice(0, next.length - 500); return next; }); } catch {} }; source.onerror = () => { // EventSource сам делает реконнект }; return () => source.close(); }, []); useEffect(() => { if (paused || !containerRef.current) return; containerRef.current.scrollTop = containerRef.current.scrollHeight; }, [entries, paused]); const filtered = entries.filter((entry) => filter === 'all' || entry.level === filter); return (
5

Логи sing-box

{filtered.length === 0 &&

Логов пока нет.

} {filtered.map((entry, index) => (

{formatTime(entry.ts)} {entry.level} {entry.line}

))}
); }