Files
AgentCompany/src/main/window-state.ts
T

94 lines
2.7 KiB
TypeScript

import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname } from "node:path";
import type { BrowserWindow, Rectangle } from "electron";
interface StoredWindowState {
bounds: Rectangle;
maximized: boolean;
}
const DEFAULT_BOUNDS: Rectangle = {
width: 1480,
height: 980,
x: 120,
y: 80,
};
function safeParseState(filePath: string): StoredWindowState | null {
// First-run: file absent is normal, not corruption. Return null silently.
if (!existsSync(filePath)) return null;
try {
const raw = readFileSync(filePath, "utf8");
const parsed = JSON.parse(raw) as Partial<StoredWindowState>;
if (!parsed.bounds || typeof parsed.bounds !== "object") {
console.warn(`[window-state] ${filePath} is missing bounds; using default geometry.`);
return null;
}
const { width, height, x, y } = parsed.bounds as Partial<Rectangle>;
if (typeof width !== "number" || !Number.isFinite(width) || typeof height !== "number" || !Number.isFinite(height)) {
console.warn(`[window-state] ${filePath} has non-numeric bounds; using default geometry.`);
return null;
}
return {
bounds: {
width,
height,
x: typeof x === "number" && Number.isFinite(x) ? x : DEFAULT_BOUNDS.x,
y: typeof y === "number" && Number.isFinite(y) ? y : DEFAULT_BOUNDS.y,
},
maximized: parsed.maximized === true,
};
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
console.warn(`[window-state] ${filePath} unreadable (${message}); using default geometry.`);
return null;
}
}
function writeState(filePath: string, state: StoredWindowState) {
mkdirSync(dirname(filePath), { recursive: true });
writeFileSync(filePath, `${JSON.stringify(state, null, 2)}\n`, "utf8");
}
export function loadWindowState(filePath: string) {
return safeParseState(filePath) ?? {
bounds: DEFAULT_BOUNDS,
maximized: false,
};
}
export function attachWindowStatePersistence(win: BrowserWindow, filePath: string) {
let saveTimer: ReturnType<typeof setTimeout> | null = null;
const persist = () => {
if (saveTimer) {
clearTimeout(saveTimer);
}
saveTimer = setTimeout(() => {
saveTimer = null;
writeState(filePath, {
bounds: win.getBounds(),
maximized: win.isMaximized(),
});
}, 250);
};
win.on("resize", persist);
win.on("move", persist);
win.on("maximize", persist);
win.on("unmaximize", persist);
win.on("close", () => {
if (saveTimer) {
clearTimeout(saveTimer);
saveTimer = null;
}
writeState(filePath, {
bounds: win.getBounds(),
maximized: win.isMaximized(),
});
});
}