fix(settings): coalesce appearance option previews

Closes #9914
This commit is contained in:
t8y2 committed 2026-09-22 14:26:52 +08:00
1 parent f468399fbd
commit e42e0711a9
4 files changed
+146 -18

No files matched your search

@@ -0,0 +1,52 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { createDelayedPreview } from "../delayedPreview";
describe("createDelayedPreview", () => {
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
vi.useRealTimers();
});
it("coalesces rapid pointer previews to the latest value", () => {
const apply = vi.fn();
const preview = createDelayedPreview(apply, 80);
preview.schedule("first");
preview.schedule("second");
preview.schedule("last");
vi.advanceTimersByTime(79);
expect(apply).not.toHaveBeenCalled();
vi.advanceTimersByTime(1);
expect(apply).toHaveBeenCalledTimes(1);
expect(apply).toHaveBeenCalledWith("last");
});
it("cancels pending work before an immediate keyboard or selection preview", () => {
const apply = vi.fn();
const preview = createDelayedPreview(apply, 80);
preview.schedule("pointer");
preview.runNow("keyboard");
vi.runAllTimers();
expect(apply).toHaveBeenCalledTimes(1);
expect(apply).toHaveBeenCalledWith("keyboard");
});
it("does not apply a preview after cleanup", () => {
const apply = vi.fn();
const preview = createDelayedPreview(apply, 80);
preview.schedule("stale");
preview.cancel();
vi.runAllTimers();
expect(apply).not.toHaveBeenCalled();
});
});
@@ -0,0 +1,30 @@
export interface DelayedPreview<T> {
schedule(value: T): void;
runNow(value: T): void;
cancel(): void;
}
export function createDelayedPreview<T>(apply: (value: T) => void, delayMs: number): DelayedPreview<T> {
let timer: ReturnType<typeof setTimeout> | undefined;
function cancel() {
if (timer === undefined) return;
clearTimeout(timer);
timer = undefined;
}
return {
schedule(value) {
cancel();
timer = setTimeout(() => {
timer = undefined;
apply(value);
}, delayMs);
},
runNow(value) {
cancel();
apply(value);
},
cancel,
};
}