fix(agent): 切回看过的会话直接铺出历史,长内容不再从顶滚到底
- 事件流按会话缓存最近 30 条会话的已读事件,切回时直接用缓存渲染,再从最后一个 seq 之后补读 - 消息区跟随:一次多出一屏以上直接跳到底,流式小段增长才平滑跟随 (原来只有第一次 ResizeObserver 回调算首屏,事件补读回来时整段平滑滚动) Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
1 parent
9f499ac7c2
commit
93a5341688
3 files changed
+78
-10
No files matched your search
@@ -284,20 +284,49 @@ export function foldEvents(events: AgentRunEvent[]): StreamItem[] {
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* 最近看过的会话的事件(模块级,最多 30 条会话):切回看过的会话时直接用已读到的事件铺出来,
|
||||
* 再从最后一个 seq 之后补读;不再先空一帧、再整段补读回来。
|
||||
*/
|
||||
const EVENT_CACHE_MAX = 30;
|
||||
const eventCache = new Map<string, AgentRunEvent[]>();
|
||||
|
||||
export function cachedRunEvents(key: string): AgentRunEvent[] {
|
||||
return eventCache.get(key) ?? [];
|
||||
}
|
||||
|
||||
export function cacheRunEvents(key: string, events: AgentRunEvent[]): void {
|
||||
if (!key || !events.length) return;
|
||||
eventCache.delete(key);
|
||||
eventCache.set(key, events);
|
||||
while (eventCache.size > EVENT_CACHE_MAX) {
|
||||
const oldest = eventCache.keys().next().value;
|
||||
if (oldest === undefined) break;
|
||||
eventCache.delete(oldest);
|
||||
}
|
||||
}
|
||||
|
||||
const lastSeqOf = (events: AgentRunEvent[]) => events[events.length - 1]?.seq ?? -1;
|
||||
|
||||
export function useAgentStream(runId: string | undefined, active: boolean): AgentStreamState {
|
||||
const [events, setEvents] = useState<AgentRunEvent[]>([]);
|
||||
// 演示数据与真实会话分开缓存
|
||||
const cacheKey = runId ? `${agentMockEnabled() ? "mock" : "live"}:${runId}` : "";
|
||||
const [events, setEvents] = useState<AgentRunEvent[]>(() => cachedRunEvents(cacheKey));
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [connected, setConnected] = useState<AgentStreamState["connected"]>("idle");
|
||||
const [nonce, setNonce] = useState(0);
|
||||
const lastSeq = useRef(-1);
|
||||
const lastSeq = useRef(lastSeqOf(events));
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: 换 run 时重置
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: 换 run 时重置(有缓存就从缓存接着读)
|
||||
useEffect(() => {
|
||||
setEvents([]);
|
||||
const cached = cachedRunEvents(cacheKey);
|
||||
setEvents(cached);
|
||||
setError(null);
|
||||
lastSeq.current = -1;
|
||||
lastSeq.current = lastSeqOf(cached);
|
||||
}, [runId]);
|
||||
|
||||
useEffect(() => cacheRunEvents(cacheKey, events), [cacheKey, events]);
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: nonce 变化 = 手动重试,重新建立补读
|
||||
useEffect(() => {
|
||||
if (!runId) return;
|
||||
|
||||
Reference in new issue
Block a user