perf(app): keep warm workspace switching responsive (#3447)
* 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
This commit is contained in:
@@ -52,6 +52,36 @@ describe("focusWithRetries", () => {
|
||||
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;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
interface FocusWithRetriesOptions {
|
||||
focus: () => void;
|
||||
isFocused: () => boolean;
|
||||
deferInitialAttempt?: boolean;
|
||||
timeoutMs?: number;
|
||||
onSuccess?: () => void;
|
||||
onTimeout?: () => void;
|
||||
@@ -9,6 +10,7 @@ interface FocusWithRetriesOptions {
|
||||
export function focusWithRetries({
|
||||
focus,
|
||||
isFocused,
|
||||
deferInitialAttempt = false,
|
||||
timeoutMs = 1500,
|
||||
onSuccess,
|
||||
onTimeout,
|
||||
@@ -40,7 +42,11 @@ export function focusWithRetries({
|
||||
});
|
||||
};
|
||||
|
||||
tick();
|
||||
if (deferInitialAttempt) {
|
||||
requestAnimationFrame(tick);
|
||||
} else {
|
||||
tick();
|
||||
}
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
|
||||
Reference in New Issue
Block a user