mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-21 16:18:55 +00:00
fix(desktop): preserve UI scale after resize
This commit is contained in:
parent
5f95b251d6
commit
036b659527
3 changed files with 85 additions and 18 deletions
|
|
@ -7173,9 +7173,9 @@ function wireCommonWindowHandlers(win, { zoom = true }: { zoom?: boolean } = {})
|
|||
|
||||
if (zoom) {
|
||||
installZoomShortcuts(win)
|
||||
// Re-apply persisted zoom on show/restore/cross-display move (Windows can
|
||||
// drop webContents zoom after minimize or a monitor-scale change) and on
|
||||
// EVERY full load — not once. The crash-recovery path calls
|
||||
// Re-apply persisted zoom on show/restore/resize/cross-display move
|
||||
// (Chromium can drop webContents zoom after these window transitions) and
|
||||
// on EVERY full load — not once. The crash-recovery path calls
|
||||
// webContents.reload(), which fires did-finish-load again after a `once`
|
||||
// listener is spent, so zoom was silently lost on renderer crash
|
||||
// recovery and any in-place reload/navigation (#46429).
|
||||
|
|
|
|||
|
|
@ -6,16 +6,17 @@
|
|||
|
||||
import assert from 'node:assert/strict'
|
||||
|
||||
import { test } from 'vitest'
|
||||
import { test, vi } from 'vitest'
|
||||
|
||||
import {
|
||||
applyZoomLevel,
|
||||
clampZoomLevel,
|
||||
installZoomReassertOnWindowEvents,
|
||||
percentToZoomLevel,
|
||||
ZOOM_REASSERT_WINDOW_EVENTS,
|
||||
ZOOM_RESIZE_REASSERT_DELAY_MS,
|
||||
ZOOM_STORAGE_KEY,
|
||||
zoomLevelToPercent,
|
||||
zoomReassertWindowEvents,
|
||||
zoomWiringForWindowKind
|
||||
} from './zoom'
|
||||
|
||||
|
|
@ -64,7 +65,7 @@ test('extreme percentages clamp to the level bounds', () => {
|
|||
assert.equal(percentToZoomLevel(1_000_000), 9)
|
||||
})
|
||||
|
||||
test('installZoomReassertOnWindowEvents wires show, restore, and cross-display moves', () => {
|
||||
test('installZoomReassertOnWindowEvents wires show, restore, resize, and cross-display moves on macOS and Windows', () => {
|
||||
const handlers = new Map()
|
||||
|
||||
const win = {
|
||||
|
|
@ -75,15 +76,62 @@ test('installZoomReassertOnWindowEvents wires show, restore, and cross-display m
|
|||
}
|
||||
|
||||
let calls = 0
|
||||
installZoomReassertOnWindowEvents(win, () => {
|
||||
calls += 1
|
||||
})
|
||||
installZoomReassertOnWindowEvents(
|
||||
win,
|
||||
() => {
|
||||
calls += 1
|
||||
},
|
||||
'win32'
|
||||
)
|
||||
|
||||
assert.deepEqual([...handlers.keys()], [...ZOOM_REASSERT_WINDOW_EVENTS])
|
||||
assert.deepEqual([...handlers.keys()], zoomReassertWindowEvents('win32'))
|
||||
handlers.get('show')()
|
||||
handlers.get('restore')()
|
||||
handlers.get('resized')()
|
||||
handlers.get('moved')()
|
||||
assert.equal(calls, 3)
|
||||
assert.equal(calls, 4)
|
||||
})
|
||||
|
||||
test('installZoomReassertOnWindowEvents debounces Linux resize and move events at the trailing edge', () => {
|
||||
vi.useFakeTimers()
|
||||
|
||||
try {
|
||||
const handlers = new Map()
|
||||
let destroyed = false
|
||||
|
||||
const win = {
|
||||
isDestroyed: () => destroyed,
|
||||
on(event, listener) {
|
||||
handlers.set(event, listener)
|
||||
}
|
||||
}
|
||||
|
||||
let calls = 0
|
||||
|
||||
installZoomReassertOnWindowEvents(
|
||||
win,
|
||||
() => {
|
||||
calls += 1
|
||||
},
|
||||
'linux'
|
||||
)
|
||||
|
||||
assert.deepEqual([...handlers.keys()], zoomReassertWindowEvents('linux'))
|
||||
handlers.get('resize')()
|
||||
vi.advanceTimersByTime(ZOOM_RESIZE_REASSERT_DELAY_MS / 2)
|
||||
handlers.get('move')()
|
||||
vi.advanceTimersByTime(ZOOM_RESIZE_REASSERT_DELAY_MS / 2)
|
||||
assert.equal(calls, 0)
|
||||
vi.advanceTimersByTime(ZOOM_RESIZE_REASSERT_DELAY_MS / 2)
|
||||
assert.equal(calls, 1)
|
||||
|
||||
handlers.get('resize')()
|
||||
destroyed = true
|
||||
vi.advanceTimersByTime(ZOOM_RESIZE_REASSERT_DELAY_MS)
|
||||
assert.equal(calls, 1)
|
||||
} finally {
|
||||
vi.useRealTimers()
|
||||
}
|
||||
})
|
||||
|
||||
test('installZoomReassertOnWindowEvents skips destroyed windows', () => {
|
||||
|
|
|
|||
|
|
@ -48,23 +48,42 @@ export function applyZoomLevel(webContents, level) {
|
|||
return clamped
|
||||
}
|
||||
|
||||
// Chromium on Windows can drop webContents zoom when a BrowserWindow is minimized
|
||||
// and restored or crosses onto a monitor with different display scaling. Re-apply
|
||||
// the persisted level after each completed lifecycle transition.
|
||||
export const ZOOM_REASSERT_WINDOW_EVENTS = ['show', 'restore', 'moved']
|
||||
// Chromium can drop webContents zoom when a BrowserWindow is resized, minimized
|
||||
// and restored, or crosses onto a monitor with different display scaling. macOS
|
||||
// and Windows provide trailing `resized`/`moved` events; Linux only provides the
|
||||
// noisy `resize`/`move` pair, so debounce those fallbacks before re-applying the
|
||||
// persisted level.
|
||||
export const ZOOM_RESIZE_REASSERT_DELAY_MS = 100
|
||||
|
||||
export function installZoomReassertOnWindowEvents(win, reassert) {
|
||||
export function zoomReassertWindowEvents(platform = process.platform) {
|
||||
return platform === 'linux' ? ['show', 'restore', 'resize', 'move'] : ['show', 'restore', 'resized', 'moved']
|
||||
}
|
||||
|
||||
export function installZoomReassertOnWindowEvents(win, reassert, platform = process.platform) {
|
||||
if (!win?.on) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const event of ZOOM_REASSERT_WINDOW_EVENTS) {
|
||||
let resizeTimer
|
||||
|
||||
for (const event of zoomReassertWindowEvents(platform)) {
|
||||
win.on(event, () => {
|
||||
if (win.isDestroyed?.()) {
|
||||
return
|
||||
}
|
||||
|
||||
reassert()
|
||||
if (event !== 'resize' && event !== 'move') {
|
||||
reassert()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
clearTimeout(resizeTimer)
|
||||
resizeTimer = setTimeout(() => {
|
||||
if (!win.isDestroyed?.()) {
|
||||
reassert()
|
||||
}
|
||||
}, ZOOM_RESIZE_REASSERT_DELAY_MS)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue