Skip to content

Commit 7bff089

Browse files
committed
feat: aggressive always-on-top with background guard and event-driven reclaim
1 parent c4174d2 commit 7bff089

2 files changed

Lines changed: 49 additions & 5 deletions

File tree

src/main/main-window.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ export function createWindow(): void {
3434
mainWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true })
3535
app.dock?.show()
3636
mainWindow.setContentProtection(true)
37+
38+
// Reclaim top position when other apps steal it
39+
mainWindow.on('always-on-top-changed', (_event, isAlwaysOnTop) => {
40+
if (!isAlwaysOnTop && mainWindow.isVisible() && !mainWindow.isDestroyed()) {
41+
// Only re-set the flag; avoid moveTop() to not disturb other window focus
42+
mainWindow.setAlwaysOnTop(true, 'screen-saver', 1)
43+
}
44+
})
3745
})
3846

3947
mainWindow.webContents.setWindowOpenHandler((details) => {

src/main/shortcuts.ts

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,46 @@ let conversationMessages: ModelMessage[] = []
7575
let recentScreenshots: string[] = [] // 最近截图,水平预览 (限5张)
7676
let hasAppendSeparator = false
7777

78-
const FRONT_REASSERT_DURATION = 5000
79-
const FRONT_REASSERT_INTERVAL = 150
80-
const FRONT_RELATIVE_LEVEL = 10
78+
const FRONT_REASSERT_DURATION = 8000
79+
const FRONT_REASSERT_INTERVAL = 100
80+
const FRONT_RELATIVE_LEVEL = 100
81+
const BACKGROUND_GUARD_INTERVAL = 2000
8182
let frontReassertTimer: NodeJS.Timeout | null = null
83+
let backgroundGuardTimer: NodeJS.Timeout | null = null
8284

83-
function applyTopMost(win: BrowserWindow) {
85+
/**
86+
* Reassert always-on-top. `aggressive` also calls moveTop() which
87+
* brings the window above everything — only use on explicit user actions
88+
* (show, screenshot, etc.) to avoid disturbing interaction with other apps.
89+
*/
90+
function applyTopMost(win: BrowserWindow, aggressive = true) {
8491
if (!win || win.isDestroyed()) return
8592
win.setAlwaysOnTop(true, 'screen-saver', FRONT_RELATIVE_LEVEL)
86-
win.moveTop()
93+
if (aggressive) win.moveTop()
94+
}
95+
96+
/**
97+
* Start a persistent low-frequency background guard that continuously
98+
* re-asserts always-on-top while the window is visible.
99+
* Uses the non-aggressive variant so it won't steal focus or
100+
* interfere with the user's interaction with other windows.
101+
*/
102+
function startBackgroundGuard(window: BrowserWindow) {
103+
if (backgroundGuardTimer) return // already running
104+
backgroundGuardTimer = setInterval(() => {
105+
if (!window || window.isDestroyed() || !window.isVisible()) {
106+
stopBackgroundGuard()
107+
return
108+
}
109+
applyTopMost(window, false)
110+
}, BACKGROUND_GUARD_INTERVAL)
111+
}
112+
113+
function stopBackgroundGuard() {
114+
if (backgroundGuardTimer) {
115+
clearInterval(backgroundGuardTimer)
116+
backgroundGuardTimer = null
117+
}
87118
}
88119

89120
function keepWindowInFront(window: BrowserWindow) {
@@ -102,6 +133,7 @@ function keepWindowInFront(window: BrowserWindow) {
102133

103134
if (!reassert()) return
104135

136+
// Aggressive burst: rapid reasserts for a short period
105137
frontReassertTimer = setInterval(() => {
106138
const shouldStop = Date.now() - start > FRONT_REASSERT_DURATION
107139
if (shouldStop || !reassert()) {
@@ -111,6 +143,9 @@ function keepWindowInFront(window: BrowserWindow) {
111143
}
112144
}
113145
}, FRONT_REASSERT_INTERVAL)
146+
147+
// Ensure background guard is running for persistent protection
148+
startBackgroundGuard(window)
114149
}
115150

116151
function abortCurrentStream(reason: AbortReason) {
@@ -124,6 +159,7 @@ const callbacks: Record<string, () => void> = {
124159
const mainWindow = global.mainWindow
125160
if (!mainWindow || mainWindow.isDestroyed()) return
126161
if (mainWindow.isVisible()) {
162+
stopBackgroundGuard()
127163
mainWindow.hide()
128164
} else {
129165
// 重新显示时不断重申置顶属性,抵消其他前台软件持续抢占

0 commit comments

Comments
 (0)