Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 31 additions & 15 deletions site/src/pages/AgentsPage/AgentDetail/SmoothText.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,33 @@ function makeText(length: number): string {
return "x".repeat(length);
}

/**
* Prepare an engine for streaming tests by consuming the first-update
* snap (which shows all initial text immediately) so that subsequent
* updates exercise the incremental animation path.
*/
function createStreamingEngine(initialText = ""): SmoothTextEngine {
const engine = new SmoothTextEngine();
engine.update(initialText, true, false);
return engine;
}

describe("SmoothTextEngine", () => {
it("reveals text steadily and reaches full length", () => {
it("snaps to full length on first update (no animation on reconnect)", () => {
const engine = new SmoothTextEngine();
const fullText = makeText(500);

engine.update(fullText, true, false);

// First update should show everything immediately — this
// prevents a "replay" animation when reconnecting to an
// in-progress stream.
expect(engine.visibleLength).toBe(fullText.length);
expect(engine.isCaughtUp).toBe(true);
});

it("reveals text steadily and reaches full length", () => {
const engine = createStreamingEngine();
const fullText = makeText(200);

engine.update(fullText, true, false);
Expand All @@ -32,7 +56,7 @@ describe("SmoothTextEngine", () => {
});

it("accelerates reveal speed when backlog is large", () => {
const engine = new SmoothTextEngine();
const engine = createStreamingEngine();
const fullText = makeText(500);

engine.update(fullText, true, false);
Expand All @@ -52,9 +76,7 @@ describe("SmoothTextEngine", () => {
});

it("caps visual lag when incoming text jumps ahead", () => {
const engine = new SmoothTextEngine();

engine.update(makeText(40), true, false);
const engine = createStreamingEngine(makeText(40));

while (!engine.isCaughtUp) {
engine.tick(16);
Expand All @@ -68,7 +90,7 @@ describe("SmoothTextEngine", () => {
});

it("flushes immediately when streaming ends", () => {
const engine = new SmoothTextEngine();
const engine = createStreamingEngine();
const fullText = makeText(120);

engine.update(fullText, true, false);
Expand Down Expand Up @@ -96,21 +118,15 @@ describe("SmoothTextEngine", () => {
});

it("clamps visible length when content shrinks", () => {
const engine = new SmoothTextEngine();

engine.update(makeText(100), true, false);

while (engine.visibleLength < 50) {
engine.tick(16);
}
const engine = createStreamingEngine(makeText(100));

engine.update(makeText(30), true, false);

expect(engine.visibleLength).toBe(30);
});

it("does not force reveal when budget is below one char", () => {
const engine = new SmoothTextEngine();
const engine = createStreamingEngine();
// With a 1-char backlog, adaptive rate is at floor (~24 cps).
// At 4ms per tick: 24 * 0.004 = 0.096 budget per tick.
// Budget reaches 1.0 after ceil(1 / 0.096) ≈ 11 ticks.
Expand All @@ -134,7 +150,7 @@ describe("SmoothTextEngine", () => {

it("keeps reveal near frame-rate invariant over equal wall time", () => {
const run = (frameMs: number) => {
const engine = new SmoothTextEngine();
const engine = createStreamingEngine();
engine.update(makeText(400), true, false);
for (let t = 0; t < 1000; t += frameMs) {
engine.tick(frameMs);
Expand Down
15 changes: 14 additions & 1 deletion site/src/pages/AgentsPage/AgentDetail/SmoothText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ export class SmoothTextEngine {
private charBudget = 0;
private isStreaming = false;
private bypassSmoothing = false;
// True until the first streaming update has been applied.
// Used to skip the reveal animation for text that already
// existed when the engine was created (e.g. reconnect
// snapshot after a page navigation).
private firstUpdate = true;

private rafId: number | null = null;
private previousTimestamp: number | null = null;
Expand Down Expand Up @@ -150,7 +155,14 @@ export class SmoothTextEngine {
this.charBudget = 0;
}

if (!isStreaming || bypassSmoothing) {
// On the very first update, snap to full length so
// pre-existing text (e.g. a reconnect snapshot) renders
// instantly instead of replaying the reveal animation.
if (this.firstUpdate) {
this.firstUpdate = false;
this.visibleLengthValue = this.fullLength;
this.charBudget = 0;
} else if (!isStreaming || bypassSmoothing) {
this.visibleLengthValue = this.fullLength;
this.charBudget = 0;
this.stopLoop();
Expand Down Expand Up @@ -241,6 +253,7 @@ export class SmoothTextEngine {
this.charBudget = 0;
this.isStreaming = false;
this.bypassSmoothing = false;
this.firstUpdate = true;
}

/**
Expand Down
Loading