Improve power save blocker mechanism (#3698)

This commit is contained in:
Zane
2025-07-31 12:17:22 -07:00
committed by GitHub
parent c9ae9ba1ce
commit 113dce2999
2 changed files with 125 additions and 24 deletions
+53 -9
View File
@@ -49,6 +49,7 @@ export const useChatEngine = ({
const [sessionOutputTokens, setSessionOutputTokens] = useState<number>(0); const [sessionOutputTokens, setSessionOutputTokens] = useState<number>(0);
const [localInputTokens, setLocalInputTokens] = useState<number>(0); const [localInputTokens, setLocalInputTokens] = useState<number>(0);
const [localOutputTokens, setLocalOutputTokens] = useState<number>(0); const [localOutputTokens, setLocalOutputTokens] = useState<number>(0);
const [powerSaveTimeoutId, setPowerSaveTimeoutId] = useState<number | null>(null);
// Store message in global history when it's added (if enabled) // Store message in global history when it's added (if enabled)
const storeMessageInHistory = useCallback( const storeMessageInHistory = useCallback(
@@ -63,6 +64,20 @@ export const useChatEngine = ({
[enableLocalStorage] [enableLocalStorage]
); );
const stopPowerSaveBlocker = useCallback(() => {
try {
window.electron.stopPowerSaveBlocker();
} catch (error) {
console.error('Failed to stop power save blocker:', error);
}
// Clear timeout if it exists
if (powerSaveTimeoutId) {
window.clearTimeout(powerSaveTimeoutId);
setPowerSaveTimeoutId(null);
}
}, [powerSaveTimeoutId]);
const { const {
messages, messages,
append: originalAppend, append: originalAppend,
@@ -84,7 +99,7 @@ export const useChatEngine = ({
initialMessages: chat.messages, initialMessages: chat.messages,
body: { session_id: chat.id, session_working_dir: window.appConfig.get('GOOSE_WORKING_DIR') }, body: { session_id: chat.id, session_working_dir: window.appConfig.get('GOOSE_WORKING_DIR') },
onFinish: async (_message, _reason) => { onFinish: async (_message, _reason) => {
window.electron.stopPowerSaveBlocker(); stopPowerSaveBlocker();
const timeSinceLastInteraction = Date.now() - lastInteractionTime; const timeSinceLastInteraction = Date.now() - lastInteractionTime;
window.electron.logInfo('last interaction:' + lastInteractionTime); window.electron.logInfo('last interaction:' + lastInteractionTime);
@@ -110,6 +125,8 @@ export const useChatEngine = ({
onMessageStreamFinish?.(); onMessageStreamFinish?.();
}, },
onError: (error) => { onError: (error) => {
stopPowerSaveBlocker();
console.log( console.log(
'CHAT ENGINE RECEIVED ERROR FROM MESSAGE STREAM:', 'CHAT ENGINE RECEIVED ERROR FROM MESSAGE STREAM:',
JSON.stringify( JSON.stringify(
@@ -206,40 +223,67 @@ export const useChatEngine = ({
} }
}, [sessionMetadata]); }, [sessionMetadata]);
useEffect(() => {
return () => {
if (powerSaveTimeoutId) {
window.clearTimeout(powerSaveTimeoutId);
}
try {
window.electron.stopPowerSaveBlocker();
} catch (error) {
console.error('Failed to stop power save blocker during cleanup:', error);
}
};
}, [powerSaveTimeoutId]);
// Handle submit // Handle submit
const handleSubmit = useCallback( const handleSubmit = useCallback(
(combinedTextFromInput: string, onSummaryReset?: () => void) => { (combinedTextFromInput: string, onSummaryReset?: () => void) => {
if (combinedTextFromInput.trim()) { if (combinedTextFromInput.trim()) {
window.electron.startPowerSaveBlocker(); try {
window.electron.startPowerSaveBlocker();
} catch (error) {
console.error('Failed to start power save blocker:', error);
}
setLastInteractionTime(Date.now()); setLastInteractionTime(Date.now());
// Set a timeout to automatically stop the power save blocker after 15 minutes
const timeoutId = window.setTimeout(
() => {
console.warn('Power save blocker timeout - stopping automatically after 15 minutes');
stopPowerSaveBlocker();
},
15 * 60 * 1000
);
setPowerSaveTimeoutId(timeoutId);
const userMessage = createUserMessage(combinedTextFromInput.trim()); const userMessage = createUserMessage(combinedTextFromInput.trim());
if (onSummaryReset) { if (onSummaryReset) {
onSummaryReset(); onSummaryReset();
setTimeout(() => { window.setTimeout(() => {
append(userMessage); append(userMessage);
// Call onMessageSent after the message is sent
onMessageSent?.(); onMessageSent?.();
}, 150); }, 150);
} else { } else {
append(userMessage); append(userMessage);
// Call onMessageSent after the message is sent
onMessageSent?.(); onMessageSent?.();
} }
} else { } else {
// If nothing was actually submitted (e.g. empty input and no images pasted) // If nothing was actually submitted (e.g. empty input and no images pasted)
window.electron.stopPowerSaveBlocker(); stopPowerSaveBlocker();
} }
}, },
[append, onMessageSent] [append, onMessageSent, stopPowerSaveBlocker]
); );
// Handle stopping the message stream // Handle stopping the message stream
const onStopGoose = useCallback(() => { const onStopGoose = useCallback(() => {
stop(); stop();
setLastInteractionTime(Date.now()); setLastInteractionTime(Date.now());
window.electron.stopPowerSaveBlocker(); stopPowerSaveBlocker();
// Handle stopping the message stream // Handle stopping the message stream
const lastMessage = messages[messages.length - 1]; const lastMessage = messages[messages.length - 1];
@@ -330,7 +374,7 @@ export const useChatEngine = ({
setMessages([...messages, responseMessage]); setMessages([...messages, responseMessage]);
} }
} }
}, [stop, messages, _setInput, setMessages, enableLocalStorage]); }, [stop, messages, _setInput, setMessages, stopPowerSaveBlocker, enableLocalStorage]);
const filteredMessages = useMemo(() => { const filteredMessages = useMemo(() => {
return [...ancestorMessages, ...messages].filter((message) => message.display ?? true); return [...ancestorMessages, ...messages].filter((message) => message.display ?? true);
+72 -15
View File
@@ -503,8 +503,8 @@ let appConfig = {
let windowCounter = 0; let windowCounter = 0;
const windowMap = new Map<number, BrowserWindow>(); const windowMap = new Map<number, BrowserWindow>();
// Track power save blocker ID globally // Track power save blockers per window
let powerSaveBlockerId: number | null = null; const windowPowerSaveBlockers = new Map<number, number>(); // windowId -> blockerId
const createChat = async ( const createChat = async (
app: App, app: App,
@@ -838,6 +838,23 @@ const createChat = async (
// Handle window closure // Handle window closure
mainWindow.on('closed', () => { mainWindow.on('closed', () => {
windowMap.delete(windowId); windowMap.delete(windowId);
if (windowPowerSaveBlockers.has(windowId)) {
const blockerId = windowPowerSaveBlockers.get(windowId)!;
try {
powerSaveBlocker.stop(blockerId);
console.log(
`[Main] Stopped power save blocker ${blockerId} for closing window ${windowId}`
);
} catch (error) {
console.error(
`[Main] Failed to stop power save blocker ${blockerId} for window ${windowId}:`,
error
);
}
windowPowerSaveBlockers.delete(windowId);
}
if (goosedProcess && typeof goosedProcess === 'object' && 'kill' in goosedProcess) { if (goosedProcess && typeof goosedProcess === 'object' && 'kill' in goosedProcess) {
goosedProcess.kill(); goosedProcess.kill();
} }
@@ -1220,10 +1237,22 @@ ipcMain.handle('set-wakelock', async (_event, enable: boolean) => {
settings.enableWakelock = enable; settings.enableWakelock = enable;
saveSettings(settings); saveSettings(settings);
// Stop any existing power save blocker when disabling the setting // Stop all existing power save blockers when disabling the setting
if (!enable && powerSaveBlockerId !== null) { if (!enable) {
powerSaveBlocker.stop(powerSaveBlockerId); for (const [windowId, blockerId] of windowPowerSaveBlockers.entries()) {
powerSaveBlockerId = null; try {
powerSaveBlocker.stop(blockerId);
console.log(
`[Main] Stopped power save blocker ${blockerId} for window ${windowId} due to wakelock setting disabled`
);
} catch (error) {
console.error(
`[Main] Failed to stop power save blocker ${blockerId} for window ${windowId}:`,
error
);
}
}
windowPowerSaveBlockers.clear();
} }
return true; return true;
@@ -2026,21 +2055,36 @@ app.whenReady().then(async () => {
} }
}); });
ipcMain.handle('start-power-save-blocker', () => { ipcMain.handle('start-power-save-blocker', (event) => {
if (powerSaveBlockerId === null) { const window = BrowserWindow.fromWebContents(event.sender);
powerSaveBlockerId = powerSaveBlocker.start('prevent-app-suspension'); const windowId = window?.id;
if (windowId && !windowPowerSaveBlockers.has(windowId)) {
const blockerId = powerSaveBlocker.start('prevent-app-suspension');
windowPowerSaveBlockers.set(windowId, blockerId);
console.log(`[Main] Started power save blocker ${blockerId} for window ${windowId}`);
return true; return true;
} }
if (windowId && windowPowerSaveBlockers.has(windowId)) {
console.log(`[Main] Power save blocker already active for window ${windowId}`);
}
return false; return false;
}); });
ipcMain.handle('stop-power-save-blocker', () => { ipcMain.handle('stop-power-save-blocker', (event) => {
if (powerSaveBlockerId !== null) { const window = BrowserWindow.fromWebContents(event.sender);
powerSaveBlocker.stop(powerSaveBlockerId); const windowId = window?.id;
powerSaveBlockerId = null;
if (windowId && windowPowerSaveBlockers.has(windowId)) {
const blockerId = windowPowerSaveBlockers.get(windowId)!;
powerSaveBlocker.stop(blockerId);
windowPowerSaveBlockers.delete(windowId);
console.log(`[Main] Stopped power save blocker ${blockerId} for window ${windowId}`);
return true; return true;
} }
return false; return false;
}); });
@@ -2153,11 +2197,24 @@ async function getAllowList(): Promise<string[]> {
} }
app.on('will-quit', async () => { app.on('will-quit', async () => {
for (const [windowId, blockerId] of windowPowerSaveBlockers.entries()) {
try {
powerSaveBlocker.stop(blockerId);
console.log(
`[Main] Stopped power save blocker ${blockerId} for window ${windowId} during app quit`
);
} catch (error) {
console.error(
`[Main] Failed to stop power save blocker ${blockerId} for window ${windowId}:`,
error
);
}
}
windowPowerSaveBlockers.clear();
// Unregister all shortcuts when quitting // Unregister all shortcuts when quitting
globalShortcut.unregisterAll(); globalShortcut.unregisterAll();
// Clean up the temp directory on app quit
console.log('[Main] App "will-quit". Cleaning up temporary image directory...');
try { try {
await fs.access(gooseTempDir); // Check if directory exists to avoid error on fs.rm if it doesn't await fs.access(gooseTempDir); // Check if directory exists to avoid error on fs.rm if it doesn't