🐛 fix(window): 修正启动窗口恢复到不可见区域

- 启动恢复普通窗口时先校验持久化 bounds 是否仍与可视区域相交
- 完全掉出可视区域时自动回正并回写新的窗口位置到 store
- 补充窗口恢复 helper 回归测试并验证前端构建通过

Fixes #384
This commit is contained in:
Syngnat
2026-04-17 18:19:42 +08:00
parent 4fd679ce42
commit 9613b2a8eb
3 changed files with 94 additions and 2 deletions

View File

@@ -0,0 +1,26 @@
import { describe, expect, it } from 'vitest';
import { resolveVisibleStartupWindowBounds } from './windowRestoreBounds';
describe('windowRestoreBounds', () => {
it('keeps existing bounds when the window still overlaps the visible area', () => {
expect(resolveVisibleStartupWindowBounds(
{ width: 1280, height: 820, x: -120, y: 40 },
{ availWidth: 1920, availHeight: 1080, availLeft: 0, availTop: 0 },
)).toEqual({ width: 1280, height: 820, x: -120, y: 40 });
});
it('recenters bounds when the saved window is fully outside the visible area', () => {
expect(resolveVisibleStartupWindowBounds(
{ width: 1280, height: 820, x: 3200, y: 1800 },
{ availWidth: 1920, availHeight: 1080, availLeft: 0, availTop: 0 },
)).toEqual({ width: 1280, height: 820, x: 320, y: 130 });
});
it('recenters bounds when the saved window is fully above and left of the visible area', () => {
expect(resolveVisibleStartupWindowBounds(
{ width: 900, height: 640, x: -1600, y: -900 },
{ availWidth: 1600, availHeight: 900, availLeft: 0, availTop: 0 },
)).toEqual({ width: 900, height: 640, x: 350, y: 130 });
});
});

View File

@@ -0,0 +1,47 @@
export type WindowRestoreBounds = {
width: number;
height: number;
x: number;
y: number;
};
type VisibleViewport = {
availWidth: number;
availHeight: number;
availLeft?: number;
availTop?: number;
};
const MIN_VISIBLE_WIDTH = 160;
const MIN_VISIBLE_HEIGHT = 120;
export const resolveVisibleStartupWindowBounds = (
bounds: WindowRestoreBounds,
viewport: VisibleViewport,
): WindowRestoreBounds => {
const visibleWidth = Math.trunc(Number(viewport.availWidth) || 0);
const visibleHeight = Math.trunc(Number(viewport.availHeight) || 0);
if (visibleWidth <= 0 || visibleHeight <= 0) {
return bounds;
}
const visibleLeft = Math.trunc(Number(viewport.availLeft) || 0);
const visibleTop = Math.trunc(Number(viewport.availTop) || 0);
const visibleRight = visibleLeft + visibleWidth;
const visibleBottom = visibleTop + visibleHeight;
const overlapWidth = Math.min(bounds.x + bounds.width, visibleRight) - Math.max(bounds.x, visibleLeft);
const overlapHeight = Math.min(bounds.y + bounds.height, visibleBottom) - Math.max(bounds.y, visibleTop);
if (
overlapWidth >= Math.min(MIN_VISIBLE_WIDTH, bounds.width) &&
overlapHeight >= Math.min(MIN_VISIBLE_HEIGHT, bounds.height)
) {
return bounds;
}
return {
...bounds,
x: visibleLeft + Math.max(0, Math.trunc((visibleWidth - bounds.width) / 2)),
y: visibleTop + Math.max(0, Math.trunc((visibleHeight - bounds.height) / 2)),
};
};