mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-25 02:32:47 +00:00
* perf(app): reduce warm workspace switch work Retained workspaces avoided mounts but pane activity still rerendered the full composer, message input, and agent-control presentation. Keep the activity contract intact while moving focus-sensitive behavior behind small context consumers, and add a Playwright benchmark that measures rapid retained workspace switching on one browser clock. * perf(app): accelerate retained workspace switching Avoid repeated retained-stream layout reconciliation and unrelated workspace structure recomputation. Use the lightweight route transition while the host is already focused, while preserving targeted root POP_TO for returns from app-wide routes where it prevents duplicate hidden decks. * perf(app): defer composer focus during workspace switches * perf(app): retain recently used workspaces longer Keep up to ten workspace screens warm so normal multi-workspace navigation does not repeatedly rebuild recently used timelines. Inactive screens expire after ten minutes, while the active screen remains protected and capacity eviction stays least-recently-used. * fix(app): reconcile retained timeline geometry * fix(app): refresh retained reading position
130 lines
3.1 KiB
TypeScript
130 lines
3.1 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { focusWithRetries } from "./web-focus";
|
|
|
|
describe("focusWithRetries", () => {
|
|
let frameQueue: FrameRequestCallback[] = [];
|
|
let originalRequestAnimationFrame: typeof requestAnimationFrame | undefined;
|
|
|
|
beforeEach(() => {
|
|
frameQueue = [];
|
|
originalRequestAnimationFrame = globalThis.requestAnimationFrame;
|
|
globalThis.requestAnimationFrame = vi.fn((callback: FrameRequestCallback) => {
|
|
frameQueue.push(callback);
|
|
return frameQueue.length;
|
|
}) as typeof requestAnimationFrame;
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (originalRequestAnimationFrame) {
|
|
globalThis.requestAnimationFrame = originalRequestAnimationFrame;
|
|
return;
|
|
}
|
|
|
|
delete (globalThis as { requestAnimationFrame?: typeof requestAnimationFrame })
|
|
.requestAnimationFrame;
|
|
});
|
|
|
|
function flushAnimationFrames(count: number): void {
|
|
for (let index = 0; index < count; index += 1) {
|
|
const callbacks = frameQueue;
|
|
frameQueue = [];
|
|
for (const callback of callbacks) {
|
|
callback(index);
|
|
}
|
|
}
|
|
}
|
|
|
|
it("tries to focus immediately before waiting for animation frames", () => {
|
|
let focused = false;
|
|
const focus = vi.fn(() => {
|
|
focused = true;
|
|
});
|
|
const onSuccess = vi.fn();
|
|
|
|
focusWithRetries({
|
|
focus,
|
|
isFocused: () => focused,
|
|
onSuccess,
|
|
});
|
|
|
|
expect(focus).toHaveBeenCalledTimes(1);
|
|
expect(onSuccess).toHaveBeenCalledTimes(1);
|
|
expect(frameQueue).toHaveLength(0);
|
|
});
|
|
|
|
it("can defer the first focus attempt until the next animation frame", () => {
|
|
const focus = vi.fn();
|
|
|
|
focusWithRetries({
|
|
focus,
|
|
isFocused: () => true,
|
|
deferInitialAttempt: true,
|
|
});
|
|
|
|
expect(focus).not.toHaveBeenCalled();
|
|
|
|
flushAnimationFrames(1);
|
|
|
|
expect(focus).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("cancels a deferred focus before its first attempt", () => {
|
|
const focus = vi.fn();
|
|
const cancel = focusWithRetries({
|
|
focus,
|
|
isFocused: () => false,
|
|
deferInitialAttempt: true,
|
|
});
|
|
|
|
cancel();
|
|
flushAnimationFrames(1);
|
|
|
|
expect(focus).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("keeps retrying on later animation frames until focus succeeds", () => {
|
|
let focused = false;
|
|
let attempts = 0;
|
|
const focus = vi.fn(() => {
|
|
attempts += 1;
|
|
if (attempts >= 3) {
|
|
focused = true;
|
|
}
|
|
});
|
|
const onSuccess = vi.fn();
|
|
|
|
focusWithRetries({
|
|
focus,
|
|
isFocused: () => focused,
|
|
onSuccess,
|
|
});
|
|
|
|
expect(focus).toHaveBeenCalledTimes(1);
|
|
expect(onSuccess).not.toHaveBeenCalled();
|
|
|
|
flushAnimationFrames(2);
|
|
expect(focus).toHaveBeenCalledTimes(2);
|
|
expect(onSuccess).not.toHaveBeenCalled();
|
|
|
|
flushAnimationFrames(2);
|
|
expect(focus).toHaveBeenCalledTimes(3);
|
|
expect(onSuccess).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("stops retrying after cancellation", () => {
|
|
const focus = vi.fn();
|
|
|
|
const cancel = focusWithRetries({
|
|
focus,
|
|
isFocused: () => false,
|
|
});
|
|
|
|
expect(focus).toHaveBeenCalledTimes(1);
|
|
|
|
cancel();
|
|
flushAnimationFrames(4);
|
|
|
|
expect(focus).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|