import React, { useMemo, useState, useCallback, useRef, useEffect, } from "react"; import { Virtuoso } from "react-virtuoso"; import { useTranslation } from "react-i18next"; import { useLocalStorage } from "foxact/use-local-storage"; import { Play, Pause, Trash2 } from "lucide-react"; import { LogLevel } from "@/hooks/use-log-data"; import { useClashInfo } from "@/hooks/use-clash"; import { useEnableLog } from "@/services/states"; import { BaseEmpty } from "@/components/base/base-empty"; import LogItem from "@/components/log/log-item"; import { BaseSearchBox } from "@/components/base/base-search-box"; import { SearchState } from "@/components/base/base-search-box"; import { useGlobalLogData, clearGlobalLogs, changeLogLevel, toggleLogEnabled, } from "@/services/global-log-service"; import { cn } from "@root/lib/utils"; import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { SidebarTrigger } from "@/components/ui/sidebar"; const LogPage = () => { const { t } = useTranslation(); const [enableLog, setEnableLog] = useEnableLog(); const { clashInfo } = useClashInfo(); const [logLevel, setLogLevel] = useLocalStorage( "log:log-level", "info", ); const [match, setMatch] = useState(() => (_: string) => true); const logData = useGlobalLogData(logLevel); const [searchState, setSearchState] = useState(); const scrollContainerRef = useRef(null); const [isScrolled, setIsScrolled] = useState(false); useEffect(() => { const scrollContainer = scrollContainerRef.current; const handleScroll = () => { if (scrollContainer) setIsScrolled(scrollContainer.scrollTop > 5); }; scrollContainer?.addEventListener("scroll", handleScroll); return () => scrollContainer?.removeEventListener("scroll", handleScroll); }, []); const filterLogs = useMemo(() => { return logData ? logData.filter((data) => { const searchText = `${data.time || ""} ${data.type} ${data.payload}`.toLowerCase(); return logLevel === "all" ? match(searchText) : data.type.toLowerCase() === logLevel && match(searchText); }) : []; }, [logData, logLevel, match]); const handleLogLevelChange = (newLevel: LogLevel) => { setLogLevel(newLevel); if (clashInfo) { const { server = "", secret = "" } = clashInfo; changeLogLevel(newLevel, server, secret); } }; const handleToggleLog = () => { if (clashInfo) { const { server = "", secret = "" } = clashInfo; toggleLogEnabled(server, secret); setEnableLog(!enableLog); } }; const handleSearch = useCallback( (matcher: (content: string) => boolean, state: SearchState) => { setMatch(() => matcher); setSearchState(state); }, [], ); return (

{t("Logs")}

{enableLog && ( )}
{filterLogs.length > 0 ? ( ( )} followOutput={"smooth"} className="w-full h-full" /> ) : ( )}
); }; export default LogPage;