refactor(runs): extract useRunLogs hook from RunCenter

RunCenter had three useEffects juggling log-chunk loading, tailing, and
older-chunk fetches. Move the whole lifecycle into useRunLogs, which
uses a single effect + AbortController to scope every IPC call to the
current run and pauses polling while document.hidden.
This commit is contained in:
ZacharyZhang-NY committed 2026-04-16 23:32:02 -04:00
1 parent 0c9d8269c1
commit 933fcf27d0
2 files changed
+143 -137

No files matched your search

+4 -137
View File
@@ -2,10 +2,9 @@ import { useEffect, useMemo, useState } from "react";
import type { AgentRecord, RunRecord, TaskRecord, WorkspaceRecord } from "@shared/types";
import { EmptyState } from "../ui";
import { useT } from "../../i18n";
import { LOG_CHUNK_BYTES, initialLogState, isActiveStatus } from "./run-helpers";
import type { RunLogState } from "./run-helpers";
import { RunQueue } from "./RunQueue";
import { RunDetail } from "./RunDetail";
import { useRunLogs } from "../../hooks/useRunLogs";
interface RunCenterProps {
runs: RunRecord[];
@@ -33,8 +32,6 @@ export function RunCenter({
const t = useT();
const [statusFilter, setStatusFilter] = useState<"all" | RunRecord["status"]>("all");
const [search, setSearch] = useState("");
const [logState, setLogState] = useState<RunLogState>(initialLogState);
const [logReloadKey, setLogReloadKey] = useState(0);
const agentMap = useMemo(() => new Map(agents.map((agent) => [agent.id, agent])), [agents]);
const taskMap = useMemo(() => new Map(tasks.map((task) => [task.id, task])), [tasks]);
@@ -69,6 +66,8 @@ export function RunCenter({
});
}, [agentMap, runs, search, statusFilter, taskMap, workspaceMap]);
const { logState, reload: reloadLogs, loadOlder: loadOlderLogs } = useRunLogs(selectedRun);
useEffect(() => {
if (selectedRunId && !runs.some((run) => run.id === selectedRunId)) {
const fallbackRun = runs[0];
@@ -78,138 +77,6 @@ export function RunCenter({
}
}, [onSelectRun, runs, selectedRunId]);
useEffect(() => {
let cancelled = false;
async function loadInitialChunk() {
if (!selectedRun) {
setLogState(initialLogState);
return;
}
setLogState((current) => ({ ...current, loading: true, error: null }));
const metaResult = await window.agentCompany.getRunLogChunk({
companyId: selectedRun.companyId,
runId: selectedRun.id,
offset: 0,
limit: 1024,
});
if (!metaResult.ok) {
if (!cancelled) {
setLogState((current) => ({
...current,
loading: false,
error: metaResult.error.message,
}));
}
return;
}
const totalBytes = metaResult.data.totalBytes;
const startOffset = Math.max(0, totalBytes - LOG_CHUNK_BYTES);
const chunkResult =
startOffset === 0 && totalBytes <= LOG_CHUNK_BYTES
? metaResult
: await window.agentCompany.getRunLogChunk({
companyId: selectedRun.companyId,
runId: selectedRun.id,
offset: startOffset,
limit: LOG_CHUNK_BYTES,
});
if (!chunkResult.ok) {
if (!cancelled) {
setLogState((current) => ({
...current,
loading: false,
error: chunkResult.error.message,
}));
}
return;
}
if (!cancelled) {
setLogState({
...chunkResult.data,
loading: false,
error: null,
});
}
}
void loadInitialChunk();
return () => {
cancelled = true;
};
}, [logReloadKey, selectedRun]);
useEffect(() => {
if (!selectedRun || !isActiveStatus(selectedRun.status)) return;
let cancelled = false;
const tick = async () => {
if (document.visibilityState === "hidden") return;
const latest = await window.agentCompany.getRunLogChunk({
companyId: selectedRun.companyId,
runId: selectedRun.id,
offset: Math.max(0, logState.totalBytes - LOG_CHUNK_BYTES),
limit: LOG_CHUNK_BYTES,
});
if (!latest.ok || cancelled) {
return;
}
setLogState({
...latest.data,
loading: false,
error: null,
});
};
const intervalId = window.setInterval(tick, 2500);
return () => {
cancelled = true;
window.clearInterval(intervalId);
};
}, [logState.totalBytes, selectedRun]);
async function loadOlderLogs() {
if (!selectedRun || logState.offset <= 0) {
return;
}
const nextOffset = Math.max(0, logState.offset - LOG_CHUNK_BYTES);
setLogState((current) => ({ ...current, loading: true, error: null }));
const result = await window.agentCompany.getRunLogChunk({
companyId: selectedRun.companyId,
runId: selectedRun.id,
offset: nextOffset,
limit: logState.offset - nextOffset,
});
if (!result.ok) {
setLogState((current) => ({
...current,
loading: false,
error: result.error.message,
}));
return;
}
setLogState({
...result.data,
content: `${result.data.content}${logState.content}`,
nextOffset: logState.nextOffset,
eof: logState.eof,
loading: false,
error: null,
});
}
if (runs.length === 0) {
return (
<EmptyState
@@ -245,7 +112,7 @@ export function RunCenter({
onOpenConsole={onOpenConsole}
onCancelRun={onCancelRun}
onOpenLogPath={onOpenLogPath}
onReloadLog={() => setLogReloadKey((current) => current + 1)}
onReloadLog={reloadLogs}
onLoadOlderLogs={() => void loadOlderLogs()}
t={t}
/>