mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
refactor(desktop): drop keep-awake statusbar toggle; persist in main
Keep-awake lives only in Settings → Advanced now. Remove the statusbar quick-toggle (+ its Sun icon, store toggle helper, and keepAwakeOn/Off strings across locales). Since the statusbar was what eagerly loaded the store at boot, move persistence to the main process (keep-awake.json, re-applied on app ready — same pattern as translucency), so a cold launch restores the blocker without the renderer opening Settings.
This commit is contained in:
parent
fc8e96b200
commit
3ef5249558
9 changed files with 32 additions and 42 deletions
|
|
@ -105,8 +105,8 @@ import {
|
|||
} from './hardening'
|
||||
import { createLinkTitleWindow, guardLinkTitleSession, readLinkTitleWindowTitle } from './link-title-window'
|
||||
import { ensureMainWindow } from './main-window-lifecycle'
|
||||
import { createKeepAwake } from './power-save'
|
||||
import { serializeJsonBody, setJsonRequestHeaders } from './oauth-net-request'
|
||||
import { createKeepAwake } from './power-save'
|
||||
import { decideProfileDeleteAction, profileNameFromDeleteRequest, resolveRouteProfile } from './profile-delete-routing'
|
||||
import {
|
||||
buildSessionWindowUrl,
|
||||
|
|
@ -8556,11 +8556,31 @@ ipcMain.on('hermes:translucency', (_event, payload) => {
|
|||
}
|
||||
})
|
||||
|
||||
// Keep-awake: the renderer owns the preference; main holds the one blocker.
|
||||
// Keep-awake: hold the machine awake for long/overnight runs. Main owns the one
|
||||
// blocker and its persisted state so a cold launch restores it (applied on
|
||||
// ready — powerSaveBlocker needs the app ready). The renderer toggles it from
|
||||
// Settings → Advanced over IPC. See store/keep-awake.
|
||||
const KEEP_AWAKE_CONFIG_PATH = path.join(app.getPath('userData'), 'keep-awake.json')
|
||||
const keepAwake = createKeepAwake(powerSaveBlocker)
|
||||
|
||||
function readPersistedKeepAwake() {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(KEEP_AWAKE_CONFIG_PATH, 'utf8')).on === true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
ipcMain.on('hermes:keep-awake', (_event, on) => {
|
||||
keepAwake.set(Boolean(on))
|
||||
const enabled = Boolean(on)
|
||||
keepAwake.set(enabled)
|
||||
|
||||
try {
|
||||
fs.mkdirSync(path.dirname(KEEP_AWAKE_CONFIG_PATH), { recursive: true })
|
||||
fs.writeFileSync(KEEP_AWAKE_CONFIG_PATH, JSON.stringify({ on: enabled }, null, 2), 'utf8')
|
||||
} catch (error) {
|
||||
rememberLog(`[keep-awake] write failed: ${error.message}`)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('hermes:openExternal', (_event, url) => {
|
||||
|
|
@ -9563,6 +9583,7 @@ app.whenReady().then(() => {
|
|||
ensureWslWindowsFonts()
|
||||
configureSpellChecker()
|
||||
registerPowerResumeListeners()
|
||||
keepAwake.set(readPersistedKeepAwake())
|
||||
createWindow()
|
||||
|
||||
// Win/Linux cold start: the launching hermes:// URL is in our own argv.
|
||||
|
|
|
|||
|
|
@ -9,12 +9,11 @@ import { GatewayMenuPanel } from '@/app/shell/gateway-menu-panel'
|
|||
import { Codicon } from '@/components/ui/codicon'
|
||||
import { GlyphSpinner } from '@/components/ui/glyph-spinner'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { Activity, AlertCircle, Clock, Command, FolderOpen, Hash, Loader2, Sun, Terminal } from '@/lib/icons'
|
||||
import { Activity, AlertCircle, Clock, Command, FolderOpen, Hash, Loader2, Terminal } from '@/lib/icons'
|
||||
import type { RuntimeReadinessResult } from '@/lib/runtime-readiness'
|
||||
import { contextBarLabel, LiveDuration, usageContextLabel } from '@/lib/statusbar'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { copyFilePath, revealFile } from '@/store/file-actions'
|
||||
import { $keepAwake, toggleKeepAwake } from '@/store/keep-awake'
|
||||
import { revealFileInTree } from '@/store/layout'
|
||||
import { $activeGatewayProfile } from '@/store/profile'
|
||||
import { $projectTree, projectNameForCwd } from '@/store/projects'
|
||||
|
|
@ -91,7 +90,6 @@ export function useStatusbarItems({
|
|||
const primaryActiveSessionId = useStore($activeSessionId)
|
||||
const activeGatewayProfile = useStore($activeGatewayProfile)
|
||||
const terminalTakeover = useStore($terminalTakeover)
|
||||
const keepAwake = useStore($keepAwake)
|
||||
const primaryBusy = useStore($busy)
|
||||
const currentCwd = useStore($currentCwd)
|
||||
// Derive the workspace's project name from the already-cached project tree
|
||||
|
|
@ -453,14 +451,6 @@ export function useStatusbarItems({
|
|||
title: terminalTakeover ? copy.hideTerminal : copy.showTerminal,
|
||||
variant: 'action'
|
||||
},
|
||||
{
|
||||
className: `w-7 justify-center px-0${keepAwake ? ' bg-accent/55 text-foreground' : ''}`,
|
||||
icon: <Sun className="size-3.5" />,
|
||||
id: 'keep-awake',
|
||||
onSelect: () => toggleKeepAwake(),
|
||||
title: keepAwake ? copy.keepAwakeOn : copy.keepAwakeOff,
|
||||
variant: 'action'
|
||||
},
|
||||
clientVersionItem,
|
||||
...(backendVersionItem ? [backendVersionItem] : [])
|
||||
],
|
||||
|
|
@ -475,7 +465,6 @@ export function useStatusbarItems({
|
|||
contextUsage,
|
||||
copy,
|
||||
currentUsage,
|
||||
keepAwake,
|
||||
requestGateway,
|
||||
sessionStartedAt,
|
||||
gatewayState,
|
||||
|
|
|
|||
|
|
@ -2176,8 +2176,6 @@ export const en: Translations = {
|
|||
openCommandCenter: 'Open Command Center',
|
||||
showTerminal: 'Show terminal',
|
||||
hideTerminal: 'Hide terminal',
|
||||
keepAwakeOn: 'Keeping awake — click to allow sleep',
|
||||
keepAwakeOff: 'Keep computer awake',
|
||||
gateway: 'Gateway',
|
||||
gatewayReady: 'ready',
|
||||
gatewayNeedsSetup: 'needs setup',
|
||||
|
|
|
|||
|
|
@ -2102,8 +2102,6 @@ export const ja = defineLocale({
|
|||
openCommandCenter: 'コマンドセンターを開く',
|
||||
showTerminal: 'ターミナルを表示',
|
||||
hideTerminal: 'ターミナルを非表示',
|
||||
keepAwakeOn: 'スリープ抑止中 — クリックで解除',
|
||||
keepAwakeOff: 'コンピューターをスリープさせない',
|
||||
gateway: 'ゲートウェイ',
|
||||
gatewayReady: '準備完了',
|
||||
gatewayNeedsSetup: '設定が必要',
|
||||
|
|
|
|||
|
|
@ -1804,8 +1804,6 @@ export interface Translations {
|
|||
openCommandCenter: string
|
||||
showTerminal: string
|
||||
hideTerminal: string
|
||||
keepAwakeOn: string
|
||||
keepAwakeOff: string
|
||||
gateway: string
|
||||
gatewayReady: string
|
||||
gatewayNeedsSetup: string
|
||||
|
|
|
|||
|
|
@ -2035,8 +2035,6 @@ export const zhHant = defineLocale({
|
|||
openCommandCenter: '開啟命令中心',
|
||||
showTerminal: '顯示終端機',
|
||||
hideTerminal: '隱藏終端機',
|
||||
keepAwakeOn: '保持喚醒中 — 點擊以允許睡眠',
|
||||
keepAwakeOff: '保持電腦喚醒',
|
||||
gateway: '閘道',
|
||||
gatewayReady: '就緒',
|
||||
gatewayNeedsSetup: '需要設定',
|
||||
|
|
|
|||
|
|
@ -2346,8 +2346,6 @@ export const zh: Translations = {
|
|||
openCommandCenter: '打开命令中心',
|
||||
showTerminal: '显示终端',
|
||||
hideTerminal: '隐藏终端',
|
||||
keepAwakeOn: '保持唤醒中 — 点击以允许休眠',
|
||||
keepAwakeOff: '保持电脑唤醒',
|
||||
gateway: '网关',
|
||||
gatewayReady: '就绪',
|
||||
gatewayNeedsSetup: '需要设置',
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|||
|
||||
import { storedBoolean } from '@/lib/storage'
|
||||
|
||||
import { $keepAwake, setKeepAwake, toggleKeepAwake } from './keep-awake'
|
||||
import { $keepAwake, setKeepAwake } from './keep-awake'
|
||||
|
||||
const KEY = 'hermes.desktop.keepAwake.v1'
|
||||
const desktopWindow = window as unknown as { hermesDesktop?: Window['hermesDesktop'] }
|
||||
|
|
@ -30,11 +30,4 @@ describe('keep-awake store', () => {
|
|||
expect(storedBoolean(KEY, true)).toBe(false)
|
||||
expect(setKeepAwakeBridge).toHaveBeenLastCalledWith(false)
|
||||
})
|
||||
|
||||
it('toggles the current value', () => {
|
||||
toggleKeepAwake()
|
||||
expect($keepAwake.get()).toBe(true)
|
||||
toggleKeepAwake()
|
||||
expect($keepAwake.get()).toBe(false)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
/**
|
||||
* Keep-awake — stop the machine sleeping during long, unattended runs.
|
||||
*
|
||||
* A device-local preference (each computer keeps its own), off by default. The
|
||||
* renderer owns the value and persists it; the main process holds the actual
|
||||
* power-save blocker (see electron/power-save.ts) and re-reads this on every
|
||||
* window load via the subscribe below. Linux/web builds without the bridge just
|
||||
* no-op.
|
||||
* A device-local preference (each computer keeps its own), off by default. This
|
||||
* atom backs the Settings → Advanced toggle and mirrors changes to the main
|
||||
* process, which owns the real power-save blocker AND its own persisted copy —
|
||||
* so a cold launch restores the blocker without the renderer visiting Settings
|
||||
* (see electron/main.ts + electron/power-save.ts). Linux/web without the bridge
|
||||
* just no-op.
|
||||
*/
|
||||
|
||||
import { atom } from 'nanostores'
|
||||
|
|
@ -20,10 +21,6 @@ export function setKeepAwake(on: boolean): void {
|
|||
$keepAwake.set(on)
|
||||
}
|
||||
|
||||
export function toggleKeepAwake(): void {
|
||||
$keepAwake.set(!$keepAwake.get())
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
$keepAwake.subscribe(on => {
|
||||
persistBoolean(KEY, on)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue