fix: phase-lock tool-call spinners across parallel calls

Each live ToolExecutionComponent advanced its spinner glyph from its own
per-instance start time, so concurrent tool spinners showed different
frames and read as janky. Derive the glyph from a single shared monotonic
clock (sharedSpinnerFrame) so every live block animates in lockstep; the
per-instance interval still drives requestRender. The unused
#lastSpinnerAdvanceAt anchor is removed; the todo-strike counter is
untouched.
This commit is contained in:
metaphorics
2026-06-14 07:38:37 +09:00
parent 11622e6190
commit b85f599917
3 changed files with 33 additions and 22 deletions
@@ -2,6 +2,10 @@ import { afterEach, beforeEach, describe, expect, it } from "bun:test";
import * as fs from "node:fs/promises";
import * as os from "node:os";
import * as path from "node:path";
import {
SPINNER_GLYPH_ADVANCE_MS,
sharedSpinnerFrame,
} from "@oh-my-pi/pi-coding-agent/modes/components/tool-execution";
import { getThemeByName } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
import { getConfigRootDir, getCustomThemesDir, setAgentDir } from "@oh-my-pi/pi-utils";
@@ -87,4 +91,16 @@ describe("theme symbols.spinnerFrames", () => {
expect(status.length).toBeGreaterThan(1);
expect(status).not.toContain("A");
});
it("derives live tool spinner frames from a shared clock", () => {
const frameCount = 4;
const now = SPINNER_GLYPH_ADVANCE_MS * 3 + 12;
expect(sharedSpinnerFrame(frameCount, now)).toBe(sharedSpinnerFrame(frameCount, now));
expect(sharedSpinnerFrame(frameCount, now + SPINNER_GLYPH_ADVANCE_MS)).toBe(
(sharedSpinnerFrame(frameCount, now) + 1) % frameCount,
);
expect(sharedSpinnerFrame(frameCount, SPINNER_GLYPH_ADVANCE_MS * frameCount)).toBe(0);
expect(sharedSpinnerFrame(0, now)).toBe(0);
});
});