import { useState, useEffect, useRef, useMemo, useCallback } from "react"; import { useAPI } from "../contexts/APIProvider"; import { usePersistentState } from "../hooks/usePersistentState"; import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels"; import { RiTextWrap, RiAlignJustify, RiFontSize, RiMenuSearchLine, RiMenuSearchFill, RiCloseCircleFill, } from "react-icons/ri"; import { useTheme } from "../contexts/ThemeProvider"; const LogViewer = () => { const { proxyLogs, upstreamLogs } = useAPI(); const { isNarrow } = useTheme(); const direction = isNarrow ? "vertical" : "horizontal"; return ( ); }; interface LogPanelProps { id: string; title: string; logData: string; } export const LogPanel = ({ id, title, logData }: LogPanelProps) => { const [filterRegex, setFilterRegex] = useState(""); const [fontSize, setFontSize] = usePersistentState<"xxs" | "xs" | "small" | "normal">( `logPanel-${id}-fontSize`, "normal" ); const [wrapText, setTextWrap] = usePersistentState(`logPanel-${id}-wrapText`, false); const [showFilter, setShowFilter] = usePersistentState(`logPanel-${id}-showFilter`, false); const textWrapClass = useMemo(() => { return wrapText ? "whitespace-pre-wrap" : "whitespace-pre"; }, [wrapText]); const toggleFontSize = useCallback(() => { setFontSize((prev) => { switch (prev) { case "xxs": return "xs"; case "xs": return "small"; case "small": return "normal"; case "normal": return "xxs"; } }); }, []); const toggleWrapText = useCallback(() => { setTextWrap((prev) => !prev); }, []); const toggleFilter = useCallback(() => { if (showFilter) { setShowFilter(false); setFilterRegex(""); // Clear filter when closing } else { setShowFilter(true); } }, [filterRegex, setFilterRegex, showFilter]); const fontSizeClass = useMemo(() => { switch (fontSize) { case "xxs": return "text-[0.5rem]"; // 0.5rem (8px) case "xs": return "text-[0.75rem]"; // 0.75rem (12px) case "small": return "text-[0.875rem]"; // 0.875rem (14px) case "normal": return "text-base"; // 1rem (16px) } }, [fontSize]); const filteredLogs = useMemo(() => { if (!filterRegex) return logData; try { const regex = new RegExp(filterRegex, "i"); const lines = logData.split("\n"); const filtered = lines.filter((line) => regex.test(line)); return filtered.join("\n"); } catch (e) { return logData; // Return unfiltered if regex is invalid } }, [logData, filterRegex]); // auto scroll to bottom const preTagRef = useRef(null); useEffect(() => { if (!preTagRef.current) return; preTagRef.current.scrollTop = preTagRef.current.scrollHeight; }, [filteredLogs]); return (

{title}

{/* Filtering Options - Full width on mobile, normal on desktop */} {showFilter && (
setFilterRegex(e.target.value)} />
)}
          {filteredLogs}
        
); }; export default LogViewer;