const { contextBridge, ipcRenderer, webUtils } = require('electron') contextBridge.exposeInMainWorld('hermesDesktop', { getConnection: () => ipcRenderer.invoke('hermes:connection'), getGatewayWsUrl: () => ipcRenderer.invoke('hermes:gateway:ws-url'), getBootProgress: () => ipcRenderer.invoke('hermes:boot-progress:get'), getConnectionConfig: () => ipcRenderer.invoke('hermes:connection-config:get'), saveConnectionConfig: payload => ipcRenderer.invoke('hermes:connection-config:save', payload), applyConnectionConfig: payload => ipcRenderer.invoke('hermes:connection-config:apply', payload), testConnectionConfig: payload => ipcRenderer.invoke('hermes:connection-config:test', payload), probeConnectionConfig: remoteUrl => ipcRenderer.invoke('hermes:connection-config:probe', remoteUrl), oauthLoginConnectionConfig: remoteUrl => ipcRenderer.invoke('hermes:connection-config:oauth-login', remoteUrl), oauthLogoutConnectionConfig: remoteUrl => ipcRenderer.invoke('hermes:connection-config:oauth-logout', remoteUrl), api: request => ipcRenderer.invoke('hermes:api', request), notify: payload => ipcRenderer.invoke('hermes:notify', payload), requestMicrophoneAccess: () => ipcRenderer.invoke('hermes:requestMicrophoneAccess'), readFileDataUrl: filePath => ipcRenderer.invoke('hermes:readFileDataUrl', filePath), readFileText: filePath => ipcRenderer.invoke('hermes:readFileText', filePath), selectPaths: options => ipcRenderer.invoke('hermes:selectPaths', options), writeClipboard: text => ipcRenderer.invoke('hermes:writeClipboard', text), saveImageFromUrl: url => ipcRenderer.invoke('hermes:saveImageFromUrl', url), saveImageBuffer: (data, ext) => ipcRenderer.invoke('hermes:saveImageBuffer', { data, ext }), saveClipboardImage: () => ipcRenderer.invoke('hermes:saveClipboardImage'), getPathForFile: file => { try { return webUtils.getPathForFile(file) || '' } catch { return '' } }, normalizePreviewTarget: (target, baseDir) => ipcRenderer.invoke('hermes:normalizePreviewTarget', target, baseDir), watchPreviewFile: url => ipcRenderer.invoke('hermes:watchPreviewFile', url), stopPreviewFileWatch: id => ipcRenderer.invoke('hermes:stopPreviewFileWatch', id), setTitleBarTheme: payload => ipcRenderer.send('hermes:titlebar-theme', payload), setPreviewShortcutActive: active => ipcRenderer.send('hermes:previewShortcutActive', Boolean(active)), openExternal: url => ipcRenderer.invoke('hermes:openExternal', url), fetchLinkTitle: url => ipcRenderer.invoke('hermes:fetchLinkTitle', url), settings: { getDefaultProjectDir: () => ipcRenderer.invoke('hermes:setting:defaultProjectDir:get'), setDefaultProjectDir: dir => ipcRenderer.invoke('hermes:setting:defaultProjectDir:set', dir), pickDefaultProjectDir: () => ipcRenderer.invoke('hermes:setting:defaultProjectDir:pick') }, revealLogs: () => ipcRenderer.invoke('hermes:logs:reveal'), getRecentLogs: () => ipcRenderer.invoke('hermes:logs:recent'), readDir: dirPath => ipcRenderer.invoke('hermes:fs:readDir', dirPath), gitRoot: startPath => ipcRenderer.invoke('hermes:fs:gitRoot', startPath), terminal: { dispose: id => ipcRenderer.invoke('hermes:terminal:dispose', id), resize: (id, size) => ipcRenderer.invoke('hermes:terminal:resize', id, size), start: options => ipcRenderer.invoke('hermes:terminal:start', options), write: (id, data) => ipcRenderer.invoke('hermes:terminal:write', id, data), onData: (id, callback) => { const channel = `hermes:terminal:${id}:data` const listener = (_event, payload) => callback(payload) ipcRenderer.on(channel, listener) return () => ipcRenderer.removeListener(channel, listener) }, onExit: (id, callback) => { const channel = `hermes:terminal:${id}:exit` const listener = (_event, payload) => callback(payload) ipcRenderer.on(channel, listener) return () => ipcRenderer.removeListener(channel, listener) } }, onClosePreviewRequested: callback => { const listener = () => callback() ipcRenderer.on('hermes:close-preview-requested', listener) return () => ipcRenderer.removeListener('hermes:close-preview-requested', listener) }, onOpenUpdatesRequested: callback => { const listener = () => callback() ipcRenderer.on('hermes:open-updates', listener) return () => ipcRenderer.removeListener('hermes:open-updates', listener) }, onWindowStateChanged: callback => { const listener = (_event, payload) => callback(payload) ipcRenderer.on('hermes:window-state-changed', listener) return () => ipcRenderer.removeListener('hermes:window-state-changed', listener) }, onPreviewFileChanged: callback => { const listener = (_event, payload) => callback(payload) ipcRenderer.on('hermes:preview-file-changed', listener) return () => ipcRenderer.removeListener('hermes:preview-file-changed', listener) }, onBackendExit: callback => { const listener = (_event, payload) => callback(payload) ipcRenderer.on('hermes:backend-exit', listener) return () => ipcRenderer.removeListener('hermes:backend-exit', listener) }, onPowerResume: callback => { const listener = () => callback() ipcRenderer.on('hermes:power-resume', listener) return () => ipcRenderer.removeListener('hermes:power-resume', listener) }, onBootProgress: callback => { const listener = (_event, payload) => callback(payload) ipcRenderer.on('hermes:boot-progress', listener) return () => ipcRenderer.removeListener('hermes:boot-progress', listener) }, // First-launch bootstrap progress -- emitted by the install.ps1 stage // runner in main.cjs (apps/desktop/electron/bootstrap-runner.cjs). // Renderer's install overlay subscribes to live events and queries the // current snapshot via getBootstrapState() to recover after a devtools // reload mid-bootstrap. getBootstrapState: () => ipcRenderer.invoke('hermes:bootstrap:get'), resetBootstrap: () => ipcRenderer.invoke('hermes:bootstrap:reset'), repairBootstrap: () => ipcRenderer.invoke('hermes:bootstrap:repair'), cancelBootstrap: () => ipcRenderer.invoke('hermes:bootstrap:cancel'), onBootstrapEvent: callback => { const listener = (_event, payload) => callback(payload) ipcRenderer.on('hermes:bootstrap:event', listener) return () => ipcRenderer.removeListener('hermes:bootstrap:event', listener) }, getVersion: () => ipcRenderer.invoke('hermes:version'), updates: { check: () => ipcRenderer.invoke('hermes:updates:check'), apply: opts => ipcRenderer.invoke('hermes:updates:apply', opts), getBranch: () => ipcRenderer.invoke('hermes:updates:branch:get'), setBranch: name => ipcRenderer.invoke('hermes:updates:branch:set', name), onProgress: callback => { const listener = (_event, payload) => callback(payload) ipcRenderer.on('hermes:updates:progress', listener) return () => ipcRenderer.removeListener('hermes:updates:progress', listener) } } })