diff --git a/frontend/components/settings/system-logs/ansi-log-viewer.tsx b/frontend/components/settings/system-logs/ansi-log-viewer.tsx new file mode 100644 index 00000000..27ef37a6 --- /dev/null +++ b/frontend/components/settings/system-logs/ansi-log-viewer.tsx @@ -0,0 +1,103 @@ +"use client" + +import { useEffect, useRef } from "react" +import { Terminal } from "@xterm/xterm" +import { FitAddon } from "@xterm/addon-fit" +import { WebLinksAddon } from "@xterm/addon-web-links" +import "@xterm/xterm/css/xterm.css" + +interface AnsiLogViewerProps { + content: string + className?: string +} + +export function AnsiLogViewer({ content, className }: AnsiLogViewerProps) { + const terminalRef = useRef(null) + const xtermRef = useRef(null) + const fitAddonRef = useRef(null) + + useEffect(() => { + if (!terminalRef.current) return + + // 创建 Terminal 实例 + const terminal = new Terminal({ + fontSize: 12, + fontFamily: 'Menlo, Monaco, "Courier New", monospace', + theme: { + background: "#1e1e1e", + foreground: "#d4d4d4", + }, + rows: 30, + scrollback: 10000, + convertEol: true, + disableStdin: true, // 只读模式 + cursorBlink: false, + }) + + // 添加插件 + const fitAddon = new FitAddon() + terminal.loadAddon(fitAddon) + terminal.loadAddon(new WebLinksAddon()) + + // 挂载到 DOM + terminal.open(terminalRef.current) + fitAddon.fit() + + // 保存引用 + xtermRef.current = terminal + fitAddonRef.current = fitAddon + + // 监听窗口大小变化 + const handleResize = () => fitAddon.fit() + window.addEventListener("resize", handleResize) + + return () => { + window.removeEventListener("resize", handleResize) + terminal.dispose() + } + }, []) + + // 更新日志内容 + useEffect(() => { + const terminal = xtermRef.current + if (!terminal || !content) return + + // 清空终端 + terminal.clear() + + // 写入新内容 + terminal.write(content.replace(/\n/g, "\r\n")) // 转换换行符 + + // 滚动到底部 + terminal.scrollToBottom() + }, [content]) + + // 监听主题变化(可选) + useEffect(() => { + const terminal = xtermRef.current + if (!terminal) return + + // 根据系统主题切换颜色 + const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches + terminal.options.theme = isDark + ? { + background: "#1e1e1e", + foreground: "#d4d4d4", + } + : { + background: "#ffffff", + foreground: "#000000", + } + }, []) + + return ( +
+ ) +} diff --git a/frontend/components/settings/system-logs/index.ts b/frontend/components/settings/system-logs/index.ts index 3d62a8a6..5a822d61 100644 --- a/frontend/components/settings/system-logs/index.ts +++ b/frontend/components/settings/system-logs/index.ts @@ -1 +1,2 @@ export { SystemLogsView } from "./system-logs-view" +export { AnsiLogViewer } from "./ansi-log-viewer" diff --git a/frontend/components/settings/system-logs/system-logs-view.tsx b/frontend/components/settings/system-logs/system-logs-view.tsx index 4a75cd82..203b0d83 100644 --- a/frontend/components/settings/system-logs/system-logs-view.tsx +++ b/frontend/components/settings/system-logs/system-logs-view.tsx @@ -1,19 +1,17 @@ "use client" -import { useEffect, useMemo, useRef, useState } from "react" -import Editor, { type Monaco } from "@monaco-editor/react" -import { useColorTheme } from "@/hooks/use-color-theme" +import { useEffect, useMemo, useState } from "react" import { useTranslations } from "next-intl" import { Card, CardContent } from "@/components/ui/card" import { useSystemLogs, useLogFiles } from "@/hooks/use-system-logs" import { LogToolbar } from "./log-toolbar" +import { AnsiLogViewer } from "./ansi-log-viewer" const DEFAULT_FILE = "xingrin.log" const DEFAULT_LINES = 500 export function SystemLogsView() { - const { currentTheme } = useColorTheme() const t = useTranslations("settings.systemLogs") // 状态管理 @@ -41,20 +39,6 @@ export function SystemLogsView() { const content = useMemo(() => logsData?.content ?? "", [logsData?.content]) - const editorRef = useRef(null) - - // 自动滚动到底部 - useEffect(() => { - const editor = editorRef.current - if (!editor) return - - const model = editor.getModel?.() - if (!model) return - - const lastLine = model.getLineCount?.() ?? 1 - editor.revealLine?.(lastLine) - }, [content]) - return ( @@ -67,28 +51,14 @@ export function SystemLogsView() { onLinesChange={setLines} onAutoRefreshChange={setAutoRefresh} /> -
- { - editorRef.current = editor - }} - options={{ - readOnly: true, - minimap: { enabled: false }, - fontSize: 12, - lineNumbers: "off", - scrollBeyondLastLine: false, - automaticLayout: true, - folding: false, - wordWrap: "off", - renderLineHighlight: "none", - padding: { top: 12, bottom: 12 }, - }} - /> +
+ {content ? ( + + ) : ( +
+ {t("noContent")} +
+ )}