fix: bound management terminal logs
This commit is contained in:
@@ -15,9 +15,9 @@ type TerminalQuickCommand = { label: string; command: string; hint: string };
|
||||
const terminalBridgeResultPollMs = 1000;
|
||||
const terminalBridgeResultPollAttempts = 30;
|
||||
const liveLogHistoryWindow = 100;
|
||||
const terminalHistoryWindow = 150;
|
||||
const terminalHistoryWindow = 10000;
|
||||
const maxLogEntries = 500;
|
||||
const maxTerminalLines = 600;
|
||||
const maxTerminalLines = 10000;
|
||||
const terminalQuickCommandCatalog: Record<string, TerminalQuickCommand[]> = {
|
||||
"game.scum": [
|
||||
{ label: "查询玩家", command: "#ListPlayers", hint: "在线玩家列表" },
|
||||
@@ -209,6 +209,9 @@ export function ServerManagementTerminalDrawer({ open, serverId, serverName, plu
|
||||
const [commandHistory, setCommandHistory] = useState<string[]>([]);
|
||||
const [historyIndex, setHistoryIndex] = useState<number | null>(null);
|
||||
const [result, setResult] = useState<{ status: "pending" | "succeeded" | "failed"; label: string } | null>(null);
|
||||
const [followLatest, setFollowLatest] = useState(true);
|
||||
const outputRef = useRef<HTMLDivElement>(null);
|
||||
const followLatestRef = useRef(true);
|
||||
const quickCommands = useMemo(() => terminalQuickCommandsForPlugin(pluginId), [pluginId]);
|
||||
const supportsCommands = quickCommands.length > 0;
|
||||
const terminalStreams = useMemo(() => streams.status === "ready" ? terminalRelevantStreams(streams.data) : [], [streams]);
|
||||
@@ -235,10 +238,21 @@ export function ServerManagementTerminalDrawer({ open, serverId, serverName, plu
|
||||
setPending(false);
|
||||
setResult(null);
|
||||
setHistoryIndex(null);
|
||||
followLatestRef.current = true;
|
||||
setFollowLatest(true);
|
||||
setLines([terminalSystemLine("info", supportsCommands ? "连接平台日志推送,先补最近历史再实时追加。" : "该插件暂未声明可用的管理终端命令通道。", "SYSTEM")]);
|
||||
void loadStreams();
|
||||
}, [loadStreams, open, supportsCommands]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open || !followLatestRef.current) return undefined;
|
||||
const frame = window.requestAnimationFrame(() => {
|
||||
const output = outputRef.current;
|
||||
if (output) output.scrollTop = output.scrollHeight;
|
||||
});
|
||||
return () => window.cancelAnimationFrame(frame);
|
||||
}, [lines, open]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return undefined;
|
||||
let ready = false;
|
||||
@@ -298,6 +312,14 @@ export function ServerManagementTerminalDrawer({ open, serverId, serverName, plu
|
||||
setLines([]);
|
||||
}
|
||||
|
||||
function handleTerminalScroll() {
|
||||
const output = outputRef.current;
|
||||
if (!output) return;
|
||||
const nextFollowLatest = output.scrollHeight - output.clientHeight - output.scrollTop <= 24;
|
||||
followLatestRef.current = nextFollowLatest;
|
||||
setFollowLatest(nextFollowLatest);
|
||||
}
|
||||
|
||||
async function submitCommand(event: FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
if (!supportsCommands || !canManage || pending || !command.trim()) return;
|
||||
@@ -347,14 +369,14 @@ export function ServerManagementTerminalDrawer({ open, serverId, serverName, plu
|
||||
<div className="terminal-output-topbar">
|
||||
<div>
|
||||
<strong>{serverName}</strong>
|
||||
<span>最近历史 + SSE 实时推送 · {streams.status === "ready" ? `${terminalStreams.length} 个日志源` : streams.status === "loading" ? "读取日志源" : "日志源异常"}</span>
|
||||
<span>最近历史 + SSE 实时推送 · {streams.status === "ready" ? `${terminalStreams.length} 个日志源` : streams.status === "loading" ? "读取日志源" : "日志源异常"} · {followLatest ? "自动置底" : "已解锁滚动"}</span>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" className="terminal-output-action" onClick={clearTerminalBuffer}><Trash2 size={14} /><span>清屏</span></button>
|
||||
<button type="button" className="terminal-output-action" aria-label="关闭管理终端" onClick={onClose}><X size={14} /><span>关闭</span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="terminal-output" role="log" aria-live="polite">
|
||||
<div ref={outputRef} className="terminal-output" role="log" aria-live="polite" onScroll={handleTerminalScroll}>
|
||||
{streams.status === "error" && <div className="terminal-line terminal-line-error"><time>{new Date().toLocaleTimeString()}</time><span className="terminal-stream">LOGS</span><span className="terminal-text">{streams.reason}</span></div>}
|
||||
{streams.status === "ready" && streams.data.length === 0 && <div className="terminal-line terminal-line-warn"><time>{new Date().toLocaleTimeString()}</time><span className="terminal-stream">LOGS</span><span className="terminal-text">暂无日志源。需要 Run 上报或历史日志回填后,这里才会持续追加。</span></div>}
|
||||
{lines.map((line) => <div key={line.id} className={`terminal-line terminal-line-${line.tone}`}><time>{line.at}</time><span className="terminal-stream">{line.streamKey || line.level || "LOG"}</span><span className="terminal-text">{line.text}</span></div>)}
|
||||
|
||||
Reference in New Issue
Block a user