Files
codeaashu-claude-code/web/lib/performance/streaming-optimizer.ts
ashutoshpythoncs@gmail.com b564857c0b claude-code
2026-03-31 18:58:05 +05:30

67 lines
1.8 KiB
TypeScript

/**
* Batches streaming token updates via requestAnimationFrame to avoid
* per-token re-renders. Flushes accumulated text on each animation frame
* rather than on every chunk, keeping the UI smooth at 60fps during streaming.
*/
export class StreamingOptimizer {
private buffer = "";
private rafId: number | null = null;
private onFlush: (text: string) => void;
private lastFlushTime = 0;
/** Max ms to wait before forcing a flush regardless of rAF timing */
private readonly maxDelay: number;
constructor(onFlush: (text: string) => void, maxDelay = 50) {
this.onFlush = onFlush;
this.maxDelay = maxDelay;
}
push(chunk: string): void {
this.buffer += chunk;
if (this.rafId !== null) return; // rAF already scheduled
const now = performance.now();
const timeSinceLast = now - this.lastFlushTime;
if (timeSinceLast >= this.maxDelay) {
// Flush is overdue — do it synchronously to avoid latency buildup
this.flush();
} else {
this.rafId = requestAnimationFrame(() => {
this.rafId = null;
this.flush();
});
}
}
flush(): void {
if (!this.buffer) return;
const text = this.buffer;
this.buffer = "";
this.lastFlushTime = performance.now();
this.onFlush(text);
}
destroy(): void {
if (this.rafId !== null) {
cancelAnimationFrame(this.rafId);
this.rafId = null;
}
// Flush any remaining buffered text
this.flush();
}
}
/**
* Hook-friendly factory that returns a stable optimizer instance
* tied to a callback ref so the callback can change without recreating
* the optimizer (avoids stale closure issues).
*/
export function createStreamingOptimizer(
onFlush: (accumulated: string) => void,
maxDelay = 50
): StreamingOptimizer {
return new StreamingOptimizer(onFlush, maxDelay);
}