Files
NapCatQQ/napcat.webui/src/components/log_com/realtime.tsx
时瑾 06f6a542f5 refactor: 优化eslint配置,提升代码质量 (#1341)
* feat: 统一并标准化eslint

* lint: napcat.webui

* lint: napcat.webui

* lint: napcat.core

* build: fix

* lint: napcat.webui

* refactor: 重构eslint

* Update README.md
2025-11-03 16:30:45 +08:00

115 lines
3.0 KiB
TypeScript

import { Button } from '@heroui/button';
import type { Selection } from '@react-types/shared';
import { useEffect, useRef, useState } from 'react';
import toast from 'react-hot-toast';
import { IoDownloadOutline } from 'react-icons/io5';
import { colorizeLogLevelWithTag } from '@/utils/terminal';
import WebUIManager, { Log } from '@/controllers/webui_manager';
import type { XTermRef } from '../xterm';
import XTerm from '../xterm';
import LogLevelSelect from './log_level_select';
const RealTimeLogs = () => {
const Xterm = useRef<XTermRef>(null);
const [logLevel, setLogLevel] = useState<Selection>(
new Set(['info', 'warn', 'error'])
);
const [dataArr, setDataArr] = useState<Log[]>([]);
const onDownloadLog = () => {
const logContent = dataArr
.filter((log) => {
if (logLevel === 'all') {
return true;
}
return logLevel.has(log.level);
})
.map((log) => colorizeLogLevelWithTag(log.message, log.level))
.join('\r\n');
const blob = new Blob([logContent], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'napcat.log';
a.click();
URL.revokeObjectURL(url);
};
const writeStream = () => {
try {
const _data = dataArr
.filter((log) => {
if (logLevel === 'all') {
return true;
}
return logLevel.has(log.level);
})
.map((log) => colorizeLogLevelWithTag(log.message, log.level))
.join('\r\n');
Xterm.current?.clear();
Xterm.current?.write(_data);
} catch (error) {
console.error(error);
toast.error('获取实时日志失败');
}
};
useEffect(() => {
writeStream();
}, [logLevel, dataArr]);
useEffect(() => {
const subscribeLogs = () => {
try {
const source = WebUIManager.getRealTimeLogs((data) => {
setDataArr((prev) => {
const newData = [...prev, ...data];
if (newData.length > 1000) {
newData.splice(0, newData.length - 1000);
}
return newData;
});
});
return () => {
source.close();
};
} catch (_error) {
toast.error('获取实时日志失败');
}
};
const close = subscribeLogs();
return () => {
console.log('close');
close?.();
};
}, []);
return (
<>
<title> - NapCat WebUI</title>
<div className='flex items-center gap-2'>
<LogLevelSelect
selectedKeys={logLevel}
onSelectionChange={setLogLevel}
/>
<Button
className='flex-shrink-0'
onPress={onDownloadLog}
startContent={<IoDownloadOutline className='text-lg' />}
>
</Button>
</div>
<div className='flex-1 h-full overflow-hidden'>
<XTerm ref={Xterm} />
</div>
</>
);
};
export default RealTimeLogs;