mirror of
https://github.com/codeaashu/claude-code.git
synced 2026-04-08 22:28:48 +03:00
claude-code
This commit is contained in:
66
web/lib/performance/streaming-optimizer.ts
Normal file
66
web/lib/performance/streaming-optimizer.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
Reference in New Issue
Block a user