mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-05-07 04:22:48 +08:00
🐛 fix(window): 修正启动窗口恢复到不可见区域
- 启动恢复普通窗口时先校验持久化 bounds 是否仍与可视区域相交 - 完全掉出可视区域时自动回正并回写新的窗口位置到 store - 补充窗口恢复 helper 回归测试并验证前端构建通过 Fixes #384
This commit is contained in:
@@ -70,6 +70,7 @@ import {
|
||||
normalizeShortcutCombo,
|
||||
} from './utils/shortcuts';
|
||||
import { resolveTitleBarToggleIconKey, shouldToggleMaximisedWindowForScaleFix } from './utils/windowStateUi';
|
||||
import { resolveVisibleStartupWindowBounds } from './utils/windowRestoreBounds';
|
||||
import {
|
||||
SIDEBAR_UTILITY_ITEM_KEYS,
|
||||
resolveAIEntryPlacement,
|
||||
@@ -522,8 +523,26 @@ function App() {
|
||||
const bounds = state.windowBounds;
|
||||
if (!bounds || bounds.width < 400 || bounds.height < 300) return;
|
||||
try {
|
||||
WindowSetSize(bounds.width, bounds.height);
|
||||
WindowSetPosition(bounds.x, bounds.y);
|
||||
const nextBounds = resolveVisibleStartupWindowBounds(bounds, {
|
||||
availWidth: window.screen?.availWidth || 0,
|
||||
availHeight: window.screen?.availHeight || 0,
|
||||
availLeft: (window.screen as Screen & { availLeft?: number })?.availLeft || 0,
|
||||
availTop: (window.screen as Screen & { availTop?: number })?.availTop || 0,
|
||||
});
|
||||
if (
|
||||
nextBounds.x !== bounds.x ||
|
||||
nextBounds.y !== bounds.y ||
|
||||
nextBounds.width !== bounds.width ||
|
||||
nextBounds.height !== bounds.height
|
||||
) {
|
||||
void emitWindowDiagnostic('adjust:startup-window-bounds', {
|
||||
from: bounds,
|
||||
to: nextBounds,
|
||||
});
|
||||
state.setWindowBounds(nextBounds);
|
||||
}
|
||||
WindowSetSize(nextBounds.width, nextBounds.height);
|
||||
WindowSetPosition(nextBounds.x, nextBounds.y);
|
||||
} catch (e) {
|
||||
console.warn('Failed to restore window bounds', e);
|
||||
}
|
||||
|
||||
26
frontend/src/utils/windowRestoreBounds.test.ts
Normal file
26
frontend/src/utils/windowRestoreBounds.test.ts
Normal 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 });
|
||||
});
|
||||
});
|
||||
47
frontend/src/utils/windowRestoreBounds.ts
Normal file
47
frontend/src/utils/windowRestoreBounds.ts
Normal 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)),
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user