import { JSX } from 'preact'; import { useEffect, useRef, useState } from 'preact/hooks'; import { LogEntry } from '@/types'; import { GM_setValue, GM_getValue } from '$'; import './LogPanel.css'; interface LogPanelProps { logs: LogEntry[]; onClear: () => void; } export function LogPanel({ logs, onClear }: LogPanelProps): JSX.Element { const logContainerRef = useRef(null); // 日志级别过滤状态 - 默认除了debug都开启 const [logLevelFilters, setLogLevelFilters] = useState>(() => { const saved = GM_getValue('logLevelFilters', null); if (saved) { try { return JSON.parse(saved); } catch { // 解析失败时使用默认值 } } return { info: true, success: true, warn: true, error: true, debug: false }; }); // 过滤后的日志 const filteredLogs = logs.filter(log => logLevelFilters[log.level]); // 切换日志级别过滤 const toggleLogLevel = (level: LogEntry['level']) => { const newFilters = { ...logLevelFilters, [level]: !logLevelFilters[level] }; setLogLevelFilters(newFilters); GM_setValue('logLevelFilters', JSON.stringify(newFilters)); }; // 自动滚动到底部 useEffect(() => { if (logContainerRef.current) { logContainerRef.current.scrollTop = logContainerRef.current.scrollHeight; } }, [filteredLogs]); const formatTime = (date: Date): string => { return date.toLocaleTimeString('zh-CN', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit', }); }; const getLogIcon = (level: LogEntry['level']): string => { switch (level) { case 'info': return 'ℹ️'; case 'warn': return '⚠️'; case 'error': return '❌'; case 'success': return '✅'; case 'debug': return '🔍'; default: return 'ℹ️'; } }; return (

执行日志

{filteredLogs.length}/{logs.length} 条日志
{filteredLogs.length === 0 ? (
📝

{logs.length === 0 ? '暂无日志' : '无匹配的日志'}

{logs.length === 0 ? '执行脚本后,日志将在这里显示' : '调整过滤条件以显示更多日志'}
) : (
{filteredLogs.map((log) => (
{getLogIcon(log.level)} {formatTime(log.timestamp)} {log.scriptId && ( [{log.scriptId}] )}
{log.message}
))}
)}
); }