Batch stream deltas, window long transcripts, lazy-load syntax highlighting, and refine activity/composer interactions. Add title refresh retries plus tests for streaming, windowing, code blocks, and live activity behavior.
69 lines
1.9 KiB
TypeScript
69 lines
1.9 KiB
TypeScript
import { useCallback, useEffect, useRef } from "react";
|
|
|
|
import type { ChatSummary } from "@/lib/types";
|
|
|
|
const TITLE_REFRESH_RETRY_DELAYS_MS = [1_000, 3_000, 7_000] as const;
|
|
|
|
function hasGeneratedTitle(session: ChatSummary | null): boolean {
|
|
return !!session?.title?.trim();
|
|
}
|
|
|
|
/**
|
|
* The server generates WebUI titles after the main turn has already ended.
|
|
* Refresh once immediately, then retry lightly for untitled sessions so the
|
|
* async title appears even if the websocket metadata notification is delayed.
|
|
*/
|
|
export function useDeferredTitleRefresh(
|
|
activeSession: ChatSummary | null,
|
|
refresh: () => Promise<void>,
|
|
retryDelaysMs: readonly number[] = TITLE_REFRESH_RETRY_DELAYS_MS,
|
|
): () => void {
|
|
const activeSessionRef = useRef(activeSession);
|
|
const timersRef = useRef<ReturnType<typeof setTimeout>[]>([]);
|
|
activeSessionRef.current = activeSession;
|
|
|
|
const clearTimers = useCallback(() => {
|
|
for (const timer of timersRef.current) {
|
|
clearTimeout(timer);
|
|
}
|
|
timersRef.current = [];
|
|
}, []);
|
|
|
|
useEffect(() => clearTimers, [clearTimers]);
|
|
|
|
useEffect(() => {
|
|
clearTimers();
|
|
}, [activeSession?.key, clearTimers]);
|
|
|
|
useEffect(() => {
|
|
if (hasGeneratedTitle(activeSession)) {
|
|
clearTimers();
|
|
}
|
|
}, [activeSession, clearTimers]);
|
|
|
|
return useCallback(() => {
|
|
void refresh();
|
|
|
|
const sessionAtTurnEnd = activeSessionRef.current;
|
|
if (!sessionAtTurnEnd || hasGeneratedTitle(sessionAtTurnEnd)) {
|
|
return;
|
|
}
|
|
|
|
clearTimers();
|
|
for (const delayMs of retryDelaysMs) {
|
|
const timer = setTimeout(() => {
|
|
const latest = activeSessionRef.current;
|
|
if (
|
|
!latest ||
|
|
latest.key !== sessionAtTurnEnd.key ||
|
|
hasGeneratedTitle(latest)
|
|
) {
|
|
return;
|
|
}
|
|
void refresh();
|
|
}, delayMs);
|
|
timersRef.current.push(timer);
|
|
}
|
|
}, [clearTimers, refresh, retryDelaysMs]);
|
|
}
|