perf(layout): track panel resizing at a stepped cadence

This commit is contained in:
dengjie zhang authored and GitHub committed 2026-09-18 01:48:27 +08:00
1 parent a78fc48a7b
commit 8eba12ea2f
17 files changed
+705 -34

No files matched your search

+69
View File
@@ -0,0 +1,69 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
const fs = {
exists: vi.fn(),
readTextFile: vi.fn(),
};
vi.mock("@/lib/backend/tauriRuntime", () => ({ isTauriRuntime: () => true }));
vi.mock("@tauri-apps/api/path", () => ({
homeDir: () => Promise.resolve("/home/tester"),
join: (_home: string, ...parts: string[]) => Promise.resolve(["/home/tester", ...parts].join("/")),
}));
vi.mock("@tauri-apps/plugin-fs", () => ({
exists: (path: string) => fs.exists(path),
readTextFile: (path: string) => fs.readTextFile(path),
}));
async function loadFresh() {
vi.resetModules();
const module = await import("../uiTuning");
await module.loadUiTuning();
return module;
}
describe("uiTuning", () => {
beforeEach(() => {
fs.exists.mockReset();
fs.readTextFile.mockReset();
});
it("uses the defaults when ~/.dbx/ui-tuning.json is absent", async () => {
fs.exists.mockResolvedValue(false);
const { uiTuning, DEFAULT_UI_TUNING } = await loadFresh();
expect(uiTuning.value.panelResizeTrackEveryFrames).toBe(DEFAULT_UI_TUNING.panelResizeTrackEveryFrames);
});
it("clamps out-of-range values from the tuning file", async () => {
fs.exists.mockResolvedValue(true);
fs.readTextFile.mockResolvedValue(JSON.stringify({ panelResizeTrackEveryFrames: 99 }));
const { uiTuning } = await loadFresh();
expect(uiTuning.value.panelResizeTrackEveryFrames).toBe(10);
});
it("falls back to defaults on malformed json", async () => {
fs.exists.mockResolvedValue(true);
fs.readTextFile.mockResolvedValue("{ not json");
const { uiTuning, DEFAULT_UI_TUNING } = await loadFresh();
expect(uiTuning.value.panelResizeTrackEveryFrames).toBe(DEFAULT_UI_TUNING.panelResizeTrackEveryFrames);
});
it("accepts a valid in-range value", async () => {
fs.exists.mockResolvedValue(true);
fs.readTextFile.mockResolvedValue(JSON.stringify({ panelResizeTrackEveryFrames: 2 }));
const { uiTuning } = await loadFresh();
expect(uiTuning.value.panelResizeTrackEveryFrames).toBe(2);
expect(fs.exists).toHaveBeenCalledWith("/home/tester/.dbx/ui-tuning.json");
expect(fs.readTextFile).toHaveBeenCalledWith("/home/tester/.dbx/ui-tuning.json");
});
it.each(["exists", "readTextFile"] as const)("keeps defaults when %s access is denied", async (command) => {
fs.exists.mockResolvedValue(true);
fs[command].mockRejectedValue(new Error("path not allowed"));
const { uiTuning, DEFAULT_UI_TUNING, loadUiTuning } = await loadFresh();
expect(uiTuning.value).toEqual(DEFAULT_UI_TUNING);
await loadUiTuning();
expect(fs.exists).toHaveBeenCalledTimes(1);
expect(fs.readTextFile).toHaveBeenCalledTimes(command === "exists" ? 0 : 1);
});
});
@@ -0,0 +1,56 @@
import { ref } from "vue";
/**
* Global "a panel divider is being dragged" state.
*
* Resizing side panels or splitter panes changes the width of the CodeMirror
* editor and the DataGrid on every frame. Both subtrees react to that with
* expensive per-frame work (CodeMirror's requestMeasure walking every visible
* line, ResizeObserver chains reading clientWidth/scrollWidth, which forces
* synchronous layout for the whole document). Heavy components subscribe to
* this flag to freeze measurement while a drag is in flight and re-sync once
* it finishes, turning hundreds of forced reflows into a single relayout.
*/
const isPanelResizing = ref(false);
const pendingAfterResize = new Set<() => void>();
function setPanelResizingResyncClass(resizing: boolean) {
if (typeof document === "undefined") return;
document.body.classList.toggle("dbx-is-resizing", resizing);
}
/** Marks a drag as in flight. Idempotent: safe to call on every move event. */
export function beginPanelResize() {
if (isPanelResizing.value) return;
isPanelResizing.value = true;
setPanelResizingResyncClass(true);
}
/** Ends the current drag (if any) and flushes registered re-sync callbacks. */
export function endPanelResize() {
if (!isPanelResizing.value) return;
isPanelResizing.value = false;
setPanelResizingResyncClass(false);
const callbacks = [...pendingAfterResize];
pendingAfterResize.clear();
for (const callback of callbacks) {
try {
callback();
} catch {
// A failed re-sync must not block the other subscribers.
}
}
}
/**
* Registers a measurement callback to run when the in-flight drag ends.
* Returns whether the caller should skip its own work right now — i.e.
* `true` means "a drag is active, your callback was queued".
*/
export function deferUntilPanelResizeEnd(callback: () => void): boolean {
if (!isPanelResizing.value) return false;
pendingAfterResize.add(callback);
return true;
}
export { isPanelResizing };
+57
View File
@@ -0,0 +1,57 @@
import { readonly, ref } from "vue";
import { isTauriRuntime } from "@/lib/backend/tauriRuntime";
/**
* Runtime UI tuning knobs, loaded once from `~/.dbx/ui-tuning.json` at
* startup (desktop runtime only; web builds keep the defaults).
*
* These are power-user performance dials, deliberately not part of the
* settings UI: edit the JSON file and restart the app to apply.
*/
export interface UiTuning {
/**
* While a panel divider is dragged, expensive subtrees (CodeMirror editor
* box, DataGrid canvas) are pinned to an explicit pixel size and only
* re-measured every N animation frames. 1 = update on every frame (full
* tracking), 5 ≈ 12fps stepping, 10 ≈ 6fps. Clamped to 1..10.
*/
panelResizeTrackEveryFrames: number;
}
export const DEFAULT_UI_TUNING: UiTuning = {
panelResizeTrackEveryFrames: 5,
};
const MAX_TRACK_EVERY_FRAMES = 10;
const state = ref<UiTuning>({ ...DEFAULT_UI_TUNING });
export const uiTuning = readonly(state);
function sanitizeFrames(value: unknown): number {
const parsed = Number(value);
if (!Number.isFinite(parsed)) return DEFAULT_UI_TUNING.panelResizeTrackEveryFrames;
return Math.min(MAX_TRACK_EVERY_FRAMES, Math.max(1, Math.round(parsed)));
}
let loading: Promise<void> | null = null;
/** Reads `~/.dbx/ui-tuning.json` once; safe to call repeatedly. */
export function loadUiTuning(): Promise<void> {
loading ??= (async () => {
if (!isTauriRuntime()) return;
try {
const { homeDir, join } = await import("@tauri-apps/api/path");
const { exists, readTextFile } = await import("@tauri-apps/plugin-fs");
const file = await join(await homeDir(), ".dbx", "ui-tuning.json");
if (!(await exists(file))) return;
const parsed = JSON.parse(await readTextFile(file)) as Partial<UiTuning>;
state.value = {
panelResizeTrackEveryFrames: sanitizeFrames(parsed.panelResizeTrackEveryFrames),
};
console.info("[ui-tuning] loaded", file, state.value);
} catch (error) {
console.info("[ui-tuning] using defaults (could not read ~/.dbx/ui-tuning.json)", error);
}
})();
return loading;
}