🐛 fix(window): 修复 mac 窗口恢复越界

This commit is contained in:
DurianPankek
2026-07-09 15:25:48 +08:00
parent 097826508a
commit 099443fddc
4 changed files with 104 additions and 8 deletions

View File

@@ -420,7 +420,8 @@ describe('tool center menu entries', () => {
});
it('clamps normal runtime window bounds back into the visible screen after display changes', () => {
expect(appSource).toContain('const readCurrentVisibleViewport = () => ({');
expect(appSource).toContain('const readCurrentVisibleViewport = () => resolveWailsWindowVisibleViewport(');
expect(appSource).toContain('{ useMonitorLocalOrigin: isMacLikePlatform() }');
expect(appSource).toContain('const repairRuntimeWindowBounds = async () => {');
expect(appSource).toContain('const nextBounds = resolveVisibleStartupWindowBounds(currentBounds, readCurrentVisibleViewport());');
expect(appSource).toContain("void emitWindowDiagnostic('adjust:runtime-window-bounds'");

View File

@@ -36,7 +36,7 @@ import {
useStore,
} from './store';
import { GlobalProxyConfig, SavedConnection, SecurityUpdateIssue, SecurityUpdateStatus } from './types';
import { blurToFilter, normalizeBlurForPlatform, normalizeOpacityForPlatform, isWindowsPlatform, resolveAppearanceValues } from './utils/appearance';
import { blurToFilter, normalizeBlurForPlatform, normalizeOpacityForPlatform, isMacLikePlatform, isWindowsPlatform, resolveAppearanceValues } from './utils/appearance';
import { buildFontFamilyOptions, DEFAULT_MONO_FONT_FAMILY, DEFAULT_UI_FONT_FAMILY, getLinuxCJKFontInstallHint, matchFontFamilyOption, resolveMonoFontFamily, resolveUIFontFamily, sanitizeFontFamilyInput, type FontFamilyOption, type InstalledFontFamily } from './utils/fontFamilies';
import {
DENSITY_OPTIONS,
@@ -129,6 +129,7 @@ import {
} from './utils/shortcuts';
import { resolveTitleBarToggleIconKey, resolveWindowsScaleCheckDelayMs, shouldApplyWindowsScaleFix, shouldResetWebViewZoomForScaleFix, shouldToggleMaximisedWindowForScaleFix, type WindowScaleFixReason, type WindowsScaleCheckTrigger } from './utils/windowStateUi';
import { resolveVisibleStartupWindowBounds } from './utils/windowRestoreBounds';
import { resolveWailsWindowVisibleViewport } from './utils/wailsWindowViewport';
import {
SIDEBAR_UTILITY_ITEM_KEYS,
resolveAIEntryPlacement,
@@ -378,12 +379,11 @@ const detectNavigatorPlatform = (): string => {
return navigator.userAgent || '';
};
const readCurrentVisibleViewport = () => ({
availWidth: window.screen?.availWidth || window.innerWidth || 0,
availHeight: window.screen?.availHeight || window.innerHeight || 0,
availLeft: (window.screen as Screen & { availLeft?: number })?.availLeft || 0,
availTop: (window.screen as Screen & { availTop?: number })?.availTop || 0,
});
const readCurrentVisibleViewport = () => resolveWailsWindowVisibleViewport(
window.screen as Screen & { availLeft?: number; availTop?: number },
{ innerWidth: window.innerWidth, innerHeight: window.innerHeight },
{ useMonitorLocalOrigin: isMacLikePlatform() },
);
const getSystemThemeMode = (): 'light' | 'dark' => {
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {

View File

@@ -0,0 +1,43 @@
import { describe, expect, it } from 'vitest';
import { resolveWailsWindowVisibleViewport } from './wailsWindowViewport';
describe('wailsWindowViewport', () => {
it('keeps browser work-area offsets for platforms that use absolute screen coordinates', () => {
expect(resolveWailsWindowVisibleViewport(
{ availWidth: 1728, availHeight: 1040, availLeft: -1728, availTop: 40 },
{ innerWidth: 1440, innerHeight: 900 },
)).toEqual({
availWidth: 1728,
availHeight: 1040,
availLeft: -1728,
availTop: 40,
});
});
it('uses current-monitor local origin for macOS Wails window positioning', () => {
expect(resolveWailsWindowVisibleViewport(
{ availWidth: 1728, availHeight: 1040, availLeft: -1728, availTop: 40 },
{ innerWidth: 1440, innerHeight: 900 },
{ useMonitorLocalOrigin: true },
)).toEqual({
availWidth: 1728,
availHeight: 1040,
availLeft: 0,
availTop: 0,
});
});
it('falls back to window inner size when screen size is unavailable', () => {
expect(resolveWailsWindowVisibleViewport(
null,
{ innerWidth: 1280, innerHeight: 720 },
{ useMonitorLocalOrigin: true },
)).toEqual({
availWidth: 1280,
availHeight: 720,
availLeft: 0,
availTop: 0,
});
});
});

View File

@@ -0,0 +1,52 @@
export type WailsWindowVisibleViewport = {
availWidth: number;
availHeight: number;
availLeft?: number;
availTop?: number;
};
type ScreenLike = {
availWidth?: number;
availHeight?: number;
availLeft?: number;
availTop?: number;
} | null | undefined;
type ViewportFallback = {
innerWidth?: number;
innerHeight?: number;
};
const toFiniteInteger = (value: unknown, fallback = 0): number => {
const next = Math.trunc(Number(value));
return Number.isFinite(next) ? next : fallback;
};
/**
* 解析 Wails 主窗口恢复使用的可见区域。
* Wails 的 WindowSetPosition 在 macOS 上接收当前 monitor 可见区域内的局部坐标,
* 因此 macOS 不能把浏览器 screen.availLeft/availTop 的全局屏幕偏移继续传下去。
*/
export const resolveWailsWindowVisibleViewport = (
screenLike: ScreenLike,
fallback: ViewportFallback,
options?: { useMonitorLocalOrigin?: boolean },
): WailsWindowVisibleViewport => {
const availWidth = toFiniteInteger(
screenLike?.availWidth,
toFiniteInteger(fallback.innerWidth),
);
const availHeight = toFiniteInteger(
screenLike?.availHeight,
toFiniteInteger(fallback.innerHeight),
);
const useMonitorLocalOrigin = options?.useMonitorLocalOrigin === true;
return {
availWidth,
availHeight,
// macOS 修复点Wails 会自己把局部坐标映射到当前 NSScreen.visibleFrame。
availLeft: useMonitorLocalOrigin ? 0 : toFiniteInteger(screenLike?.availLeft),
availTop: useMonitorLocalOrigin ? 0 : toFiniteInteger(screenLike?.availTop),
};
};