refactor(client): 四处轮询站点迁移到 use-polling,语义逐一保持

- SubagentView useSubagentLive:self-scheduling(上一请求 settle 后才排
  下一次,不重叠)+ 每代 AbortSignal + signal.aborted 拦迟到写入,行为
  与原 disposed 标志等价(teardown 同步置 disposed 并 abort)。
- SideChatView:立即拉取 effect 与 2s 定时拆分——输入变化(含 running
  翻转)仍先拉一次转录,运行中间隔 tick 拉转录+徽章;fetchThread 自带
  的逐次 abort 与卸载 abort 原样保留。
- GitLens:可见时 2s 固定静默 tick,无初始突发(mount/scope 变更的完整
  刷新在别处);refresh 身份变化照旧重启间隔相位。
- ChangesTab:输入变化即拉一次(含隐藏态翻转),可见时 2.5s 固定间隔;
  pull 内部 pollGen 代际守卫不动。

守护 spec 全绿:subagent-live-polling + subagent-jobs-view 13/13、
sidechat-view + sidechat-transcript 29/29、git-view-worktree +
changes-tab + changes-ops 13/13;断言零改动。
This commit is contained in:
Menghuan1918
2026-09-03 20:35:59 +08:00
parent 6ef11ae010
commit 1ec1565b13
4 changed files with 44 additions and 55 deletions
+17 -8
View File
@@ -69,6 +69,7 @@ import {
type SidechatTranscriptRow,
} from './sidechat-transcript.ts'
import { api } from './api.ts'
import { usePolling } from './use-polling.ts'
import { t } from './locales.ts'
import type { SessionScope } from './api.ts'
import type { SidebarTab } from './state.ts'
@@ -493,17 +494,25 @@ export function SideChatView(props: {
}
}, [threadId, fetchInfo])
// Poll while the tab is visible and the thread runs.
// One transcript pull on every input change (attach, visibility flip,
// run-state flip — the last one catches a thread's terminal state once it
// stops running).
useEffect(() => {
if (!visible || threadId === undefined) return
void fetchThread(threadId)
if (!running) return
const timer = window.setInterval(() => {
void fetchThread(threadId)
void fetchInfo(threadId)
}, POLL_MS)
return () => { window.clearInterval(timer) }
}, [visible, threadId, running, fetchThread, fetchInfo])
// `running` is not read here, but re-triggering this pull on run-state
// flips is load-bearing (see above); the badge fetch rides the ticks.
}, [visible, threadId, running, fetchThread])
// Poll while the tab is visible and the thread runs: transcript deltas +
// badge refresh on a fixed cadence. Each pull self-guards (fetchThread
// aborts its predecessor; a late settle keeps the last rows).
const pollTick = useCallback(async (): Promise<void> => {
if (threadId === undefined) return
void fetchThread(threadId)
void fetchInfo(threadId)
}, [threadId, fetchThread, fetchInfo])
usePolling(visible && running && threadId !== undefined, pollTick, { intervalMs: POLL_MS })
useEffect(() => () => { controllerRef.current?.abort() }, [])