From 759cbc7acbb15f29d78f44fb1fcd016815325a5d Mon Sep 17 00:00:00 2001 From: Simon Sickle <51972200+simonsickle@users.noreply.github.com> Date: Thu, 17 Jul 2025 04:09:08 -0400 Subject: [PATCH] Add wakelock feature to prevent system sleep while Goose is working (#3321) Signed-off-by: Simon Sickle <51972200+simonsickle@users.noreply.github.com> --- .../settings/app/AppSettingsSection.tsx | 30 +++++++++++++ ui/desktop/src/main.ts | 42 +++++++++++++++---- ui/desktop/src/preload.ts | 4 ++ ui/desktop/src/utils/settings.ts | 2 + 4 files changed, 71 insertions(+), 7 deletions(-) diff --git a/ui/desktop/src/components/settings/app/AppSettingsSection.tsx b/ui/desktop/src/components/settings/app/AppSettingsSection.tsx index 6cb99cd2..eb75e2c0 100644 --- a/ui/desktop/src/components/settings/app/AppSettingsSection.tsx +++ b/ui/desktop/src/components/settings/app/AppSettingsSection.tsx @@ -19,6 +19,7 @@ export default function AppSettingsSection({ scrollToSection }: AppSettingsSecti const [menuBarIconEnabled, setMenuBarIconEnabled] = useState(true); const [dockIconEnabled, setDockIconEnabled] = useState(true); const [quitConfirmationEnabled, setQuitConfirmationEnabled] = useState(true); + const [wakelockEnabled, setWakelockEnabled] = useState(true); const [isMacOS, setIsMacOS] = useState(false); const [isDockSwitchDisabled, setIsDockSwitchDisabled] = useState(false); const [showNotificationModal, setShowNotificationModal] = useState(false); @@ -147,6 +148,10 @@ export default function AppSettingsSection({ scrollToSection }: AppSettingsSecti setQuitConfirmationEnabled(enabled); }); + window.electron.getWakelockState().then((enabled) => { + setWakelockEnabled(enabled); + }); + if (isMacOS) { window.electron.getDockIconState().then((enabled) => { setDockIconEnabled(enabled); @@ -202,6 +207,14 @@ export default function AppSettingsSection({ scrollToSection }: AppSettingsSecti } }; + const handleWakelockToggle = async () => { + const newState = !wakelockEnabled; + const success = await window.electron.setWakelock(newState); + if (success) { + setWakelockEnabled(newState); + } + }; + const handleShowPricingToggle = (checked: boolean) => { setShowPricing(checked); localStorage.setItem('show_pricing', String(checked)); @@ -299,6 +312,23 @@ export default function AppSettingsSection({ scrollToSection }: AppSettingsSecti + {/* Prevent Sleep */} +
+
+

Prevent Sleep

+

+ Keep your computer awake while Goose is running a task (screen can still lock) +

+
+
+ +
+
+ {/* Cost Tracking */} {COST_TRACKING_ENABLED && (
diff --git a/ui/desktop/src/main.ts b/ui/desktop/src/main.ts index edde8c4d..f3745ac4 100644 --- a/ui/desktop/src/main.ts +++ b/ui/desktop/src/main.ts @@ -494,6 +494,9 @@ let appConfig = { let windowCounter = 0; const windowMap = new Map(); +// Track power save blocker ID globally +let powerSaveBlockerId: number | null = null; + interface RecipeConfig { id: string; title: string; @@ -1090,6 +1093,36 @@ ipcMain.handle('get-quit-confirmation-state', () => { } }); +// Handle wakelock setting +ipcMain.handle('set-wakelock', async (_event, enable: boolean) => { + try { + const settings = loadSettings(); + settings.enableWakelock = enable; + saveSettings(settings); + + // Stop any existing power save blocker when disabling the setting + if (!enable && powerSaveBlockerId !== null) { + powerSaveBlocker.stop(powerSaveBlockerId); + powerSaveBlockerId = null; + } + + return true; + } catch (error) { + console.error('Error setting wakelock:', error); + return false; + } +}); + +ipcMain.handle('get-wakelock-state', () => { + try { + const settings = loadSettings(); + return settings.enableWakelock ?? false; + } catch (error) { + console.error('Error getting wakelock state:', error); + return false; + } +}); + // Add file/directory selection handler ipcMain.handle('select-file-or-directory', async () => { const result = (await dialog.showOpenDialog({ @@ -1889,24 +1922,19 @@ app.whenReady().then(async () => { } }); - let powerSaveBlockerId: number | null = null; - ipcMain.handle('start-power-save-blocker', () => { - log.info('Starting power save blocker...'); if (powerSaveBlockerId === null) { - powerSaveBlockerId = powerSaveBlocker.start('prevent-display-sleep'); - log.info('Started power save blocker'); + powerSaveBlockerId = powerSaveBlocker.start('prevent-app-suspension'); return true; } + return false; }); ipcMain.handle('stop-power-save-blocker', () => { - log.info('Stopping power save blocker...'); if (powerSaveBlockerId !== null) { powerSaveBlocker.stop(powerSaveBlockerId); powerSaveBlockerId = null; - log.info('Stopped power save blocker'); return true; } return false; diff --git a/ui/desktop/src/preload.ts b/ui/desktop/src/preload.ts index 091c2be9..04ac73fa 100644 --- a/ui/desktop/src/preload.ts +++ b/ui/desktop/src/preload.ts @@ -83,6 +83,8 @@ type ElectronAPI = { setSchedulingEngine: (engine: string) => Promise; setQuitConfirmation: (show: boolean) => Promise; getQuitConfirmationState: () => Promise; + setWakelock: (enable: boolean) => Promise; + getWakelockState: () => Promise; openNotificationsSettings: () => Promise; on: ( channel: string, @@ -176,6 +178,8 @@ const electronAPI: ElectronAPI = { setSchedulingEngine: (engine: string) => ipcRenderer.invoke('set-scheduling-engine', engine), setQuitConfirmation: (show: boolean) => ipcRenderer.invoke('set-quit-confirmation', show), getQuitConfirmationState: () => ipcRenderer.invoke('get-quit-confirmation-state'), + setWakelock: (enable: boolean) => ipcRenderer.invoke('set-wakelock', enable), + getWakelockState: () => ipcRenderer.invoke('get-wakelock-state'), openNotificationsSettings: () => ipcRenderer.invoke('open-notifications-settings'), on: ( channel: string, diff --git a/ui/desktop/src/utils/settings.ts b/ui/desktop/src/utils/settings.ts index bda74a5b..0b956950 100644 --- a/ui/desktop/src/utils/settings.ts +++ b/ui/desktop/src/utils/settings.ts @@ -16,6 +16,7 @@ export interface Settings { showDockIcon: boolean; schedulingEngine: SchedulingEngine; showQuitConfirmation: boolean; + enableWakelock: boolean; } // Constants @@ -30,6 +31,7 @@ const defaultSettings: Settings = { showDockIcon: true, schedulingEngine: 'builtin-cron', showQuitConfirmation: true, + enableWakelock: false, }; // Settings management