diff --git a/src/main/utils/ipc.ts b/src/main/utils/ipc.ts index bd357c9..5d33cb7 100644 --- a/src/main/utils/ipc.ts +++ b/src/main/utils/ipc.ts @@ -92,7 +92,8 @@ import { webdavDelete, webdavRestore, exportLocalBackup, - importLocalBackup + importLocalBackup, + reinitScheduler } from '../resolve/backup' import { getInterfaces } from '../sys/interface' import { @@ -122,24 +123,23 @@ import { startMonitor } from '../resolve/trafficMonitor' import { closeFloatingWindow, showContextMenu, showFloatingWindow } from '../resolve/floatingWindow' import i18next from 'i18next' import { addProfileUpdater, removeProfileUpdater } from '../core/profileUpdater' -import { reinitScheduler } from '../resolve/backup' +import { readFile, writeFile } from 'fs/promises' -function ipcErrorWrapper( // eslint-disable-next-line @typescript-eslint/no-explicit-any - fn: (...args: any[]) => Promise // eslint-disable-next-line @typescript-eslint/no-explicit-any -): (...args: any[]) => Promise { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return async (...args: any[]) => { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function wrapAsync Promise>( + fn: T +): (...args: Parameters) => Promise | { invokeError: unknown }> { + return async (...args) => { try { return await fn(...args) } catch (e) { if (e && typeof e === 'object') { if ('message' in e) { return { invokeError: e.message } - } else { - return { invokeError: JSON.stringify(e) } } + return { invokeError: JSON.stringify(e) } } - if (e instanceof Error || typeof e === 'string') { + if (typeof e === 'string') { return { invokeError: e } } return { invokeError: 'Unknown Error' } @@ -147,240 +147,275 @@ function ipcErrorWrapper( // eslint-disable-next-line @typescript-eslint/no-e } } -// GitHub 版本管理相关 IPC 处理程序 -export async function fetchMihomoTags( +async function fetchMihomoTags( forceRefresh = false ): Promise<{ name: string; zipball_url: string; tarball_url: string }[]> { return await getGitHubTags('MetaCubeX', 'mihomo', forceRefresh) } -export async function installSpecificMihomoCore(version: string): Promise { - // 安装完成后清除缓存,以便下次获取最新的标签列表 +async function installSpecificMihomoCore(version: string): Promise { clearVersionCache('MetaCubeX', 'mihomo') return await installMihomoCore(version) } -export async function clearMihomoVersionCache(): Promise { +async function clearMihomoVersionCache(): Promise { clearVersionCache('MetaCubeX', 'mihomo') } -export async function getRuleStr(id: string): Promise { - const { readFile } = await import('fs/promises') - const filePath = rulePath(id) - return await readFile(filePath, 'utf-8') +async function getRuleStr(id: string): Promise { + return await readFile(rulePath(id), 'utf-8') } -export async function setRuleStr(id: string, str: string): Promise { - const { writeFile } = await import('fs/promises') - const filePath = rulePath(id) - await writeFile(filePath, str, 'utf-8') +async function setRuleStr(id: string, str: string): Promise { + await writeFile(rulePath(id), str, 'utf-8') +} + +async function getSmartOverrideContent(): Promise { + try { + const override = await getOverrideItem('smart-core-override') + return override?.file || null + } catch { + return null + } +} + +async function changeLanguage(lng: string): Promise { + await i18next.changeLanguage(lng) + ipcMain.emit('updateTrayMenu') +} + +async function setTitleBarOverlay(overlay: Electron.TitleBarOverlayOptions): Promise { + if (mainWindow && typeof mainWindow.setTitleBarOverlay === 'function') { + mainWindow.setTitleBarOverlay(overlay) + } } export function registerIpcMainHandlers(): void { - ipcMain.handle('mihomoVersion', ipcErrorWrapper(mihomoVersion)) - ipcMain.handle('mihomoCloseConnection', (_e, id) => ipcErrorWrapper(mihomoCloseConnection)(id)) - ipcMain.handle('mihomoCloseAllConnections', ipcErrorWrapper(mihomoCloseAllConnections)) - ipcMain.handle('mihomoRules', ipcErrorWrapper(mihomoRules)) - ipcMain.handle('mihomoProxies', ipcErrorWrapper(mihomoProxies)) - ipcMain.handle('mihomoGroups', ipcErrorWrapper(mihomoGroups)) - ipcMain.handle('mihomoProxyProviders', ipcErrorWrapper(mihomoProxyProviders)) - ipcMain.handle('mihomoUpdateProxyProviders', (_e, name) => - ipcErrorWrapper(mihomoUpdateProxyProviders)(name) + // Mihomo API + ipcMain.handle('mihomoVersion', wrapAsync(mihomoVersion)) + ipcMain.handle('mihomoCloseConnection', (_e, id: string) => wrapAsync(mihomoCloseConnection)(id)) + ipcMain.handle('mihomoCloseAllConnections', wrapAsync(mihomoCloseAllConnections)) + ipcMain.handle('mihomoRules', wrapAsync(mihomoRules)) + ipcMain.handle('mihomoProxies', wrapAsync(mihomoProxies)) + ipcMain.handle('mihomoGroups', wrapAsync(mihomoGroups)) + ipcMain.handle('mihomoProxyProviders', wrapAsync(mihomoProxyProviders)) + ipcMain.handle('mihomoUpdateProxyProviders', (_e, name: string) => + wrapAsync(mihomoUpdateProxyProviders)(name) ) - ipcMain.handle('mihomoRuleProviders', ipcErrorWrapper(mihomoRuleProviders)) - ipcMain.handle('mihomoUpdateRuleProviders', (_e, name) => - ipcErrorWrapper(mihomoUpdateRuleProviders)(name) + ipcMain.handle('mihomoRuleProviders', wrapAsync(mihomoRuleProviders)) + ipcMain.handle('mihomoUpdateRuleProviders', (_e, name: string) => + wrapAsync(mihomoUpdateRuleProviders)(name) ) - ipcMain.handle('mihomoChangeProxy', (_e, group, proxy) => - ipcErrorWrapper(mihomoChangeProxy)(group, proxy) + ipcMain.handle('mihomoChangeProxy', (_e, group: string, proxy: string) => + wrapAsync(mihomoChangeProxy)(group, proxy) ) - ipcMain.handle('mihomoUnfixedProxy', (_e, group) => ipcErrorWrapper(mihomoUnfixedProxy)(group)) - ipcMain.handle('mihomoUpgradeGeo', ipcErrorWrapper(mihomoUpgradeGeo)) - ipcMain.handle('mihomoUpgrade', ipcErrorWrapper(mihomoUpgrade)) - ipcMain.handle('mihomoUpgradeUI', ipcErrorWrapper(mihomoUpgradeUI)) - ipcMain.handle('mihomoUpgradeConfig', ipcErrorWrapper(mihomoUpgradeConfig)) - ipcMain.handle('mihomoProxyDelay', (_e, proxy, url) => - ipcErrorWrapper(mihomoProxyDelay)(proxy, url) + ipcMain.handle('mihomoUnfixedProxy', (_e, group: string) => wrapAsync(mihomoUnfixedProxy)(group)) + ipcMain.handle('mihomoUpgradeGeo', wrapAsync(mihomoUpgradeGeo)) + ipcMain.handle('mihomoUpgrade', wrapAsync(mihomoUpgrade)) + ipcMain.handle('mihomoUpgradeUI', wrapAsync(mihomoUpgradeUI)) + ipcMain.handle('mihomoUpgradeConfig', wrapAsync(mihomoUpgradeConfig)) + ipcMain.handle('mihomoProxyDelay', (_e, proxy: string, url?: string) => + wrapAsync(mihomoProxyDelay)(proxy, url) ) - ipcMain.handle('mihomoGroupDelay', (_e, group, url) => - ipcErrorWrapper(mihomoGroupDelay)(group, url) + ipcMain.handle('mihomoGroupDelay', (_e, group: string, url?: string) => + wrapAsync(mihomoGroupDelay)(group, url) ) - ipcMain.handle('patchMihomoConfig', (_e, patch) => ipcErrorWrapper(patchMihomoConfig)(patch)) - // Smart 内核 API - ipcMain.handle('mihomoSmartGroupWeights', (_e, groupName) => - ipcErrorWrapper(mihomoSmartGroupWeights)(groupName) + ipcMain.handle('patchMihomoConfig', (_e, patch: Partial) => + wrapAsync(patchMihomoConfig)(patch) ) - ipcMain.handle('mihomoSmartFlushCache', (_e, configName) => - ipcErrorWrapper(mihomoSmartFlushCache)(configName) + ipcMain.handle('mihomoSmartGroupWeights', (_e, groupName: string) => + wrapAsync(mihomoSmartGroupWeights)(groupName) ) - ipcMain.handle('checkAutoRun', ipcErrorWrapper(checkAutoRun)) - ipcMain.handle('enableAutoRun', ipcErrorWrapper(enableAutoRun)) - ipcMain.handle('disableAutoRun', ipcErrorWrapper(disableAutoRun)) - ipcMain.handle('getAppConfig', (_e, force) => ipcErrorWrapper(getAppConfig)(force)) - ipcMain.handle('patchAppConfig', (_e, config) => ipcErrorWrapper(patchAppConfig)(config)) - ipcMain.handle('getControledMihomoConfig', (_e, force) => - ipcErrorWrapper(getControledMihomoConfig)(force) - ) - ipcMain.handle('patchControledMihomoConfig', (_e, config) => - ipcErrorWrapper(patchControledMihomoConfig)(config) - ) - ipcMain.handle('getProfileConfig', (_e, force) => ipcErrorWrapper(getProfileConfig)(force)) - ipcMain.handle('setProfileConfig', (_e, config) => ipcErrorWrapper(setProfileConfig)(config)) - ipcMain.handle('getCurrentProfileItem', ipcErrorWrapper(getCurrentProfileItem)) - ipcMain.handle('getProfileItem', (_e, id) => ipcErrorWrapper(getProfileItem)(id)) - ipcMain.handle('getProfileStr', (_e, id) => ipcErrorWrapper(getProfileStr)(id)) - ipcMain.handle('getFileStr', (_e, path) => ipcErrorWrapper(getFileStr)(path)) - ipcMain.handle('setFileStr', (_e, path, str) => ipcErrorWrapper(setFileStr)(path, str)) - ipcMain.handle('convertMrsRuleset', (_e, path, behavior) => - ipcErrorWrapper(convertMrsRuleset)(path, behavior) - ) - ipcMain.handle('setProfileStr', (_e, id, str) => ipcErrorWrapper(setProfileStr)(id, str)) - ipcMain.handle('updateProfileItem', (_e, item) => ipcErrorWrapper(updateProfileItem)(item)) - ipcMain.handle('changeCurrentProfile', (_e, id) => ipcErrorWrapper(changeCurrentProfile)(id)) - ipcMain.handle('addProfileItem', (_e, item) => ipcErrorWrapper(addProfileItem)(item)) - ipcMain.handle('removeProfileItem', (_e, id) => ipcErrorWrapper(removeProfileItem)(id)) - ipcMain.handle('addProfileUpdater', (_e, item) => ipcErrorWrapper(addProfileUpdater)(item)) - ipcMain.handle('removeProfileUpdater', (_e, id) => ipcErrorWrapper(removeProfileUpdater)(id)) - ipcMain.handle('getOverrideConfig', (_e, force) => ipcErrorWrapper(getOverrideConfig)(force)) - ipcMain.handle('setOverrideConfig', (_e, config) => ipcErrorWrapper(setOverrideConfig)(config)) - ipcMain.handle('getOverrideItem', (_e, id) => ipcErrorWrapper(getOverrideItem)(id)) - ipcMain.handle('addOverrideItem', (_e, item) => ipcErrorWrapper(addOverrideItem)(item)) - ipcMain.handle('removeOverrideItem', (_e, id) => ipcErrorWrapper(removeOverrideItem)(id)) - ipcMain.handle('updateOverrideItem', (_e, item) => ipcErrorWrapper(updateOverrideItem)(item)) - ipcMain.handle('getOverride', (_e, id, ext) => ipcErrorWrapper(getOverride)(id, ext)) - ipcMain.handle('setOverride', (_e, id, ext, str) => ipcErrorWrapper(setOverride)(id, ext, str)) - ipcMain.handle('restartCore', ipcErrorWrapper(restartCore)) - ipcMain.handle('startMonitor', (_e, detached) => ipcErrorWrapper(startMonitor)(detached)) - ipcMain.handle('triggerSysProxy', (_e, enable) => ipcErrorWrapper(triggerSysProxy)(enable)) - ipcMain.handle('manualGrantCorePermition', () => ipcErrorWrapper(manualGrantCorePermition)()) - ipcMain.handle('checkAdminPrivileges', () => ipcErrorWrapper(checkAdminPrivileges)()) - ipcMain.handle('restartAsAdmin', () => ipcErrorWrapper(restartAsAdmin)()) - ipcMain.handle('checkMihomoCorePermissions', () => ipcErrorWrapper(checkMihomoCorePermissions)()) - ipcMain.handle('requestTunPermissions', () => ipcErrorWrapper(requestTunPermissions)()) - ipcMain.handle('checkHighPrivilegeCore', () => ipcErrorWrapper(checkHighPrivilegeCore)()) - ipcMain.handle('showTunPermissionDialog', () => ipcErrorWrapper(showTunPermissionDialog)()) - ipcMain.handle('showErrorDialog', (_, title: string, message: string) => - ipcErrorWrapper(showErrorDialog)(title, message) + ipcMain.handle('mihomoSmartFlushCache', (_e, configName?: string) => + wrapAsync(mihomoSmartFlushCache)(configName) ) - ipcMain.handle('checkTunPermissions', () => ipcErrorWrapper(checkTunPermissions)()) - ipcMain.handle('grantTunPermissions', () => ipcErrorWrapper(grantTunPermissions)()) - ipcMain.handle('getFilePath', (_e, ext) => getFilePath(ext)) - ipcMain.handle('readTextFile', (_e, filePath) => ipcErrorWrapper(readTextFile)(filePath)) - ipcMain.handle('getRuntimeConfigStr', ipcErrorWrapper(getRuntimeConfigStr)) - ipcMain.handle('getRuntimeConfig', ipcErrorWrapper(getRuntimeConfig)) - ipcMain.handle('downloadAndInstallUpdate', (_e, version) => - ipcErrorWrapper(downloadAndInstallUpdate)(version) + // AutoRun + ipcMain.handle('checkAutoRun', wrapAsync(checkAutoRun)) + ipcMain.handle('enableAutoRun', wrapAsync(enableAutoRun)) + ipcMain.handle('disableAutoRun', wrapAsync(disableAutoRun)) + + // Config + ipcMain.handle('getAppConfig', (_e, force?: boolean) => wrapAsync(getAppConfig)(force)) + ipcMain.handle('patchAppConfig', (_e, config: Partial) => + wrapAsync(patchAppConfig)(config) + ) + ipcMain.handle('getControledMihomoConfig', (_e, force?: boolean) => + wrapAsync(getControledMihomoConfig)(force) + ) + ipcMain.handle('patchControledMihomoConfig', (_e, config: Partial) => + wrapAsync(patchControledMihomoConfig)(config) + ) + ipcMain.handle('resetAppConfig', () => resetAppConfig()) + + // Profile + ipcMain.handle('getProfileConfig', (_e, force?: boolean) => wrapAsync(getProfileConfig)(force)) + ipcMain.handle('setProfileConfig', (_e, config: IProfileConfig) => + wrapAsync(setProfileConfig)(config) + ) + ipcMain.handle('getCurrentProfileItem', wrapAsync(getCurrentProfileItem)) + ipcMain.handle('getProfileItem', (_e, id?: string) => wrapAsync(getProfileItem)(id)) + ipcMain.handle('getProfileStr', (_e, id: string) => wrapAsync(getProfileStr)(id)) + ipcMain.handle('setProfileStr', (_e, id: string, str: string) => + wrapAsync(setProfileStr)(id, str) + ) + ipcMain.handle('addProfileItem', (_e, item: Partial) => + wrapAsync(addProfileItem)(item) + ) + ipcMain.handle('removeProfileItem', (_e, id: string) => wrapAsync(removeProfileItem)(id)) + ipcMain.handle('updateProfileItem', (_e, item: IProfileItem) => wrapAsync(updateProfileItem)(item)) + ipcMain.handle('changeCurrentProfile', (_e, id: string) => wrapAsync(changeCurrentProfile)(id)) + ipcMain.handle('addProfileUpdater', (_e, item: IProfileItem) => wrapAsync(addProfileUpdater)(item)) + ipcMain.handle('removeProfileUpdater', (_e, id: string) => wrapAsync(removeProfileUpdater)(id)) + + // Override + ipcMain.handle('getOverrideConfig', (_e, force?: boolean) => wrapAsync(getOverrideConfig)(force)) + ipcMain.handle('setOverrideConfig', (_e, config: IOverrideConfig) => + wrapAsync(setOverrideConfig)(config) + ) + ipcMain.handle('getOverrideItem', (_e, id: string) => wrapAsync(getOverrideItem)(id)) + ipcMain.handle('addOverrideItem', (_e, item: Partial) => + wrapAsync(addOverrideItem)(item) + ) + ipcMain.handle('removeOverrideItem', (_e, id: string) => wrapAsync(removeOverrideItem)(id)) + ipcMain.handle('updateOverrideItem', (_e, item: IOverrideItem) => + wrapAsync(updateOverrideItem)(item) + ) + ipcMain.handle('getOverride', (_e, id: string, ext: 'js' | 'yaml' | 'log') => + wrapAsync(getOverride)(id, ext) + ) + ipcMain.handle('setOverride', (_e, id: string, ext: 'js' | 'yaml', str: string) => + wrapAsync(setOverride)(id, ext, str) + ) + + // File + ipcMain.handle('getFileStr', (_e, filePath: string) => wrapAsync(getFileStr)(filePath)) + ipcMain.handle('setFileStr', (_e, filePath: string, str: string) => + wrapAsync(setFileStr)(filePath, str) + ) + ipcMain.handle('convertMrsRuleset', (_e, filePath: string, behavior: string) => + wrapAsync(convertMrsRuleset)(filePath, behavior) + ) + ipcMain.handle('getRuntimeConfig', wrapAsync(getRuntimeConfig)) + ipcMain.handle('getRuntimeConfigStr', wrapAsync(getRuntimeConfigStr)) + ipcMain.handle('getSmartOverrideContent', wrapAsync(getSmartOverrideContent)) + ipcMain.handle('getRuleStr', (_e, id: string) => wrapAsync(getRuleStr)(id)) + ipcMain.handle('setRuleStr', (_e, id: string, str: string) => wrapAsync(setRuleStr)(id, str)) + ipcMain.handle('getFilePath', (_e, ext: string[]) => getFilePath(ext)) + ipcMain.handle('readTextFile', (_e, filePath: string) => wrapAsync(readTextFile)(filePath)) + ipcMain.handle('openFile', (_e, type: 'profile' | 'override', id: string, ext?: 'yaml' | 'js') => + openFile(type, id, ext) + ) + + // Core + ipcMain.handle('restartCore', wrapAsync(restartCore)) + ipcMain.handle('startMonitor', (_e, detached?: boolean) => wrapAsync(startMonitor)(detached)) + ipcMain.handle('quitWithoutCore', wrapAsync(quitWithoutCore)) + + // System + ipcMain.handle('triggerSysProxy', (_e, enable: boolean) => wrapAsync(triggerSysProxy)(enable)) + ipcMain.handle('checkTunPermissions', wrapAsync(checkTunPermissions)) + ipcMain.handle('grantTunPermissions', wrapAsync(grantTunPermissions)) + ipcMain.handle('manualGrantCorePermition', wrapAsync(manualGrantCorePermition)) + ipcMain.handle('checkAdminPrivileges', wrapAsync(checkAdminPrivileges)) + ipcMain.handle('restartAsAdmin', (_e, forTun?: boolean) => wrapAsync(restartAsAdmin)(forTun)) + ipcMain.handle('checkMihomoCorePermissions', wrapAsync(checkMihomoCorePermissions)) + ipcMain.handle('requestTunPermissions', wrapAsync(requestTunPermissions)) + ipcMain.handle('checkHighPrivilegeCore', wrapAsync(checkHighPrivilegeCore)) + ipcMain.handle('showTunPermissionDialog', wrapAsync(showTunPermissionDialog)) + ipcMain.handle('showErrorDialog', (_e, title: string, message: string) => + wrapAsync(showErrorDialog)(title, message) + ) + ipcMain.handle('openUWPTool', wrapAsync(openUWPTool)) + ipcMain.handle('setupFirewall', wrapAsync(setupFirewall)) + ipcMain.handle('getInterfaces', getInterfaces) + ipcMain.handle('setNativeTheme', (_e, theme: 'system' | 'light' | 'dark') => setNativeTheme(theme)) + ipcMain.handle('copyEnv', (_e, type: 'bash' | 'cmd' | 'powershell') => wrapAsync(copyEnv)(type)) + + // Update + ipcMain.handle('checkUpdate', wrapAsync(checkUpdate)) + ipcMain.handle('downloadAndInstallUpdate', (_e, version: string) => + wrapAsync(downloadAndInstallUpdate)(version) ) - ipcMain.handle('checkUpdate', ipcErrorWrapper(checkUpdate)) ipcMain.handle('getVersion', () => app.getVersion()) ipcMain.handle('platform', () => process.platform) - ipcMain.handle('openUWPTool', ipcErrorWrapper(openUWPTool)) - ipcMain.handle('setupFirewall', ipcErrorWrapper(setupFirewall)) - ipcMain.handle('getInterfaces', getInterfaces) - ipcMain.handle('webdavBackup', ipcErrorWrapper(webdavBackup)) - ipcMain.handle('webdavRestore', (_e, filename) => ipcErrorWrapper(webdavRestore)(filename)) - ipcMain.handle('listWebdavBackups', ipcErrorWrapper(listWebdavBackups)) - ipcMain.handle('webdavDelete', (_e, filename) => ipcErrorWrapper(webdavDelete)(filename)) - ipcMain.handle('reinitWebdavBackupScheduler', ipcErrorWrapper(reinitScheduler)) - ipcMain.handle('exportLocalBackup', () => ipcErrorWrapper(exportLocalBackup)()) - ipcMain.handle('importLocalBackup', () => ipcErrorWrapper(importLocalBackup)()) - ipcMain.handle('registerShortcut', (_e, oldShortcut, newShortcut, action) => - ipcErrorWrapper(registerShortcut)(oldShortcut, newShortcut, action) + ipcMain.handle('fetchMihomoTags', (_e, forceRefresh?: boolean) => + wrapAsync(fetchMihomoTags)(forceRefresh) ) - ipcMain.handle('startSubStoreFrontendServer', () => - ipcErrorWrapper(startSubStoreFrontendServer)() + ipcMain.handle('installSpecificMihomoCore', (_e, version: string) => + wrapAsync(installSpecificMihomoCore)(version) ) - ipcMain.handle('stopSubStoreFrontendServer', () => ipcErrorWrapper(stopSubStoreFrontendServer)()) - ipcMain.handle('startSubStoreBackendServer', () => ipcErrorWrapper(startSubStoreBackendServer)()) - ipcMain.handle('stopSubStoreBackendServer', () => ipcErrorWrapper(stopSubStoreBackendServer)()) - ipcMain.handle('downloadSubStore', () => ipcErrorWrapper(downloadSubStore)()) + ipcMain.handle('clearMihomoVersionCache', wrapAsync(clearMihomoVersionCache)) + + // Backup + ipcMain.handle('webdavBackup', wrapAsync(webdavBackup)) + ipcMain.handle('webdavRestore', (_e, filename: string) => wrapAsync(webdavRestore)(filename)) + ipcMain.handle('listWebdavBackups', wrapAsync(listWebdavBackups)) + ipcMain.handle('webdavDelete', (_e, filename: string) => wrapAsync(webdavDelete)(filename)) + ipcMain.handle('reinitWebdavBackupScheduler', wrapAsync(reinitScheduler)) + ipcMain.handle('exportLocalBackup', wrapAsync(exportLocalBackup)) + ipcMain.handle('importLocalBackup', wrapAsync(importLocalBackup)) + + // SubStore + ipcMain.handle('startSubStoreFrontendServer', wrapAsync(startSubStoreFrontendServer)) + ipcMain.handle('stopSubStoreFrontendServer', wrapAsync(stopSubStoreFrontendServer)) + ipcMain.handle('startSubStoreBackendServer', wrapAsync(startSubStoreBackendServer)) + ipcMain.handle('stopSubStoreBackendServer', wrapAsync(stopSubStoreBackendServer)) + ipcMain.handle('downloadSubStore', wrapAsync(downloadSubStore)) ipcMain.handle('subStorePort', () => subStorePort) ipcMain.handle('subStoreFrontendPort', () => subStoreFrontendPort) - ipcMain.handle('subStoreSubs', () => ipcErrorWrapper(subStoreSubs)()) - ipcMain.handle('subStoreCollections', () => ipcErrorWrapper(subStoreCollections)()) - ipcMain.handle('getGistUrl', ipcErrorWrapper(getGistUrl)) - ipcMain.handle('setNativeTheme', (_e, theme) => { - setNativeTheme(theme) - }) - ipcMain.handle('setTitleBarOverlay', (_e, overlay) => - ipcErrorWrapper(async (overlay): Promise => { - if (mainWindow && typeof mainWindow.setTitleBarOverlay === 'function') { - mainWindow.setTitleBarOverlay(overlay) - } - })(overlay) + ipcMain.handle('subStoreSubs', wrapAsync(subStoreSubs)) + ipcMain.handle('subStoreCollections', wrapAsync(subStoreCollections)) + + // Theme + ipcMain.handle('resolveThemes', wrapAsync(resolveThemes)) + ipcMain.handle('fetchThemes', wrapAsync(fetchThemes)) + ipcMain.handle('importThemes', (_e, files: string[]) => wrapAsync(importThemes)(files)) + ipcMain.handle('readTheme', (_e, theme: string) => wrapAsync(readTheme)(theme)) + ipcMain.handle('writeTheme', (_e, theme: string, css: string) => + wrapAsync(writeTheme)(theme, css) ) - ipcMain.handle('setAlwaysOnTop', (_e, alwaysOnTop) => { - mainWindow?.setAlwaysOnTop(alwaysOnTop) - }) - ipcMain.handle('isAlwaysOnTop', () => { - return mainWindow?.isAlwaysOnTop() - }) - ipcMain.handle('showTrayIcon', () => ipcErrorWrapper(showTrayIcon)()) - ipcMain.handle('closeTrayIcon', () => ipcErrorWrapper(closeTrayIcon)()) - ipcMain.handle('updateTrayIcon', () => ipcErrorWrapper(updateTrayIcon)()) - ipcMain.handle('updateTrayIconImmediate', (_e, sysProxyEnabled, tunEnabled) => { + ipcMain.handle('applyTheme', (_e, theme: string) => wrapAsync(applyTheme)(theme)) + + // Tray + ipcMain.handle('showTrayIcon', wrapAsync(showTrayIcon)) + ipcMain.handle('closeTrayIcon', wrapAsync(closeTrayIcon)) + ipcMain.handle('updateTrayIcon', wrapAsync(updateTrayIcon)) + ipcMain.handle('updateTrayIconImmediate', (_e, sysProxyEnabled: boolean, tunEnabled: boolean) => updateTrayIconImmediate(sysProxyEnabled, tunEnabled) - }) + ) + + // Window ipcMain.handle('showMainWindow', showMainWindow) ipcMain.handle('closeMainWindow', closeMainWindow) - ipcMain.handle('triggerMainWindow', (_e, force) => triggerMainWindow(force)) - ipcMain.handle('showFloatingWindow', () => ipcErrorWrapper(showFloatingWindow)()) - ipcMain.handle('closeFloatingWindow', () => ipcErrorWrapper(closeFloatingWindow)()) - ipcMain.handle('showContextMenu', () => ipcErrorWrapper(showContextMenu)()) - ipcMain.handle('openFile', (_e, type, id, ext) => openFile(type, id, ext)) - ipcMain.handle('openDevTools', () => { - mainWindow?.webContents.openDevTools() - }) - ipcMain.handle('createHeapSnapshot', () => { + ipcMain.handle('triggerMainWindow', (_e, force?: boolean) => triggerMainWindow(force)) + ipcMain.handle('showFloatingWindow', wrapAsync(showFloatingWindow)) + ipcMain.handle('closeFloatingWindow', wrapAsync(closeFloatingWindow)) + ipcMain.handle('showContextMenu', wrapAsync(showContextMenu)) + ipcMain.handle('setTitleBarOverlay', (_e, overlay: Electron.TitleBarOverlayOptions) => + wrapAsync(setTitleBarOverlay)(overlay) + ) + ipcMain.handle('setAlwaysOnTop', (_e, alwaysOnTop: boolean) => + mainWindow?.setAlwaysOnTop(alwaysOnTop) + ) + ipcMain.handle('isAlwaysOnTop', () => mainWindow?.isAlwaysOnTop()) + ipcMain.handle('openDevTools', () => mainWindow?.webContents.openDevTools()) + ipcMain.handle('createHeapSnapshot', () => v8.writeHeapSnapshot(path.join(logDir(), `${Date.now()}.heapsnapshot`)) - }) - ipcMain.handle('getImageDataURL', (_e, url) => ipcErrorWrapper(getImageDataURL)(url)) - ipcMain.handle('resolveThemes', () => ipcErrorWrapper(resolveThemes)()) - ipcMain.handle('fetchThemes', () => ipcErrorWrapper(fetchThemes)()) - ipcMain.handle('importThemes', (_e, file) => ipcErrorWrapper(importThemes)(file)) - ipcMain.handle('readTheme', (_e, theme) => ipcErrorWrapper(readTheme)(theme)) - ipcMain.handle('writeTheme', (_e, theme, css) => ipcErrorWrapper(writeTheme)(theme, css)) - ipcMain.handle('applyTheme', (_e, theme) => ipcErrorWrapper(applyTheme)(theme)) - ipcMain.handle('copyEnv', (_e, type) => ipcErrorWrapper(copyEnv)(type)) - ipcMain.handle('getSmartOverrideContent', async () => { - const { getOverrideItem } = await import('../config') - try { - const override = await getOverrideItem('smart-core-override') - return override?.file || null - } catch (error) { - return null - } - }) - ipcMain.handle('resetAppConfig', resetAppConfig) + ) + + // Shortcut + ipcMain.handle('registerShortcut', (_e, oldShortcut: string, newShortcut: string, action: string) => + wrapAsync(registerShortcut)(oldShortcut, newShortcut, action) + ) + + // Misc + ipcMain.handle('getGistUrl', wrapAsync(getGistUrl)) + ipcMain.handle('getImageDataURL', (_e, url: string) => wrapAsync(getImageDataURL)(url)) ipcMain.handle('relaunchApp', () => { app.relaunch() app.quit() }) - ipcMain.handle('quitWithoutCore', ipcErrorWrapper(quitWithoutCore)) ipcMain.handle('quitApp', () => app.quit()) - - // Add language change handler - ipcMain.handle('changeLanguage', async (_e, lng) => { - await i18next.changeLanguage(lng) - // 触发托盘菜单更新 - ipcMain.emit('updateTrayMenu') - }) - - // 注册获取 Mihomo 标签的 IPC 处理程序 - ipcMain.handle('fetchMihomoTags', (_e, forceRefresh) => - ipcErrorWrapper(fetchMihomoTags)(forceRefresh) - ) - - // 注册安装特定版本 Mihomo 核心的 IPC 处理程序 - ipcMain.handle('installSpecificMihomoCore', (_e, version) => - ipcErrorWrapper(installSpecificMihomoCore)(version) - ) - - // 注册清除版本缓存的 IPC 处理程序 - ipcMain.handle('clearMihomoVersionCache', () => ipcErrorWrapper(clearMihomoVersionCache)()) - - // 规则相关 IPC 处理程序 - ipcMain.handle('getRuleStr', (_e, id) => ipcErrorWrapper(getRuleStr)(id)) - ipcMain.handle('setRuleStr', (_e, id, str) => ipcErrorWrapper(setRuleStr)(id, str)) + ipcMain.handle('changeLanguage', (_e, lng: string) => wrapAsync(changeLanguage)(lng)) } diff --git a/src/renderer/src/utils/ipc.ts b/src/renderer/src/utils/ipc.ts index a67e8f7..4fcb6ca 100644 --- a/src/renderer/src/utils/ipc.ts +++ b/src/renderer/src/utils/ipc.ts @@ -1,568 +1,263 @@ import { TitleBarOverlayOptions } from 'electron' -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function ipcErrorWrapper(response: any): any { - if (typeof response === 'object' && 'invokeError' in response) { +function checkIpcError(response: T | { invokeError: unknown }): T { + if (response && typeof response === 'object' && 'invokeError' in response) { throw response.invokeError - } else { - return response } -} - -// GitHub 版本管理相关 IPC 调用 -export async function fetchMihomoTags( - forceRefresh = false -): Promise<{ name: string; zipball_url: string; tarball_url: string }[]> { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('fetchMihomoTags', forceRefresh)) -} - -export async function installSpecificMihomoCore(version: string): Promise { - return ipcErrorWrapper( - await window.electron.ipcRenderer.invoke('installSpecificMihomoCore', version) - ) -} - -export async function clearMihomoVersionCache(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('clearMihomoVersionCache')) -} - -export async function mihomoVersion(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoVersion')) -} - -export async function mihomoCloseConnection(id: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoCloseConnection', id)) -} - -export async function mihomoCloseAllConnections(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoCloseAllConnections')) -} - -export async function mihomoRules(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoRules')) -} - -export async function mihomoProxies(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoProxies')) -} - -export async function mihomoGroups(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoGroups')) -} - -export async function mihomoProxyProviders(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoProxyProviders')) -} - -export async function mihomoUpdateProxyProviders(name: string): Promise { - return ipcErrorWrapper( - await window.electron.ipcRenderer.invoke('mihomoUpdateProxyProviders', name) - ) -} - -export async function mihomoRuleProviders(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoRuleProviders')) -} - -export async function mihomoUpdateRuleProviders(name: string): Promise { - return ipcErrorWrapper( - await window.electron.ipcRenderer.invoke('mihomoUpdateRuleProviders', name) - ) -} - -export async function mihomoChangeProxy(group: string, proxy: string): Promise { - return ipcErrorWrapper( - await window.electron.ipcRenderer.invoke('mihomoChangeProxy', group, proxy) - ) -} - -export async function mihomoUnfixedProxy(group: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoUnfixedProxy', group)) -} - -export async function mihomoUpgradeGeo(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoUpgradeGeo')) -} - -export async function mihomoUpgrade(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoUpgrade')) -} - -export async function mihomoUpgradeUI(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoUpgradeUI')) -} - -export async function mihomoUpgradeConfig(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoUpgradeConfig')) -} - -export async function mihomoProxyDelay(proxy: string, url?: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoProxyDelay', proxy, url)) -} - -export async function mihomoGroupDelay(group: string, url?: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoGroupDelay', group, url)) -} - -export async function mihomoSmartGroupWeights(groupName: string): Promise> { - return ipcErrorWrapper( - await window.electron.ipcRenderer.invoke('mihomoSmartGroupWeights', groupName) - ) -} - -export async function mihomoSmartFlushCache(configName?: string): Promise { - return ipcErrorWrapper( - await window.electron.ipcRenderer.invoke('mihomoSmartFlushCache', configName) - ) -} - -export async function getSmartOverrideContent(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getSmartOverrideContent')) -} - -export async function patchMihomoConfig(patch: Partial): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('patchMihomoConfig', patch)) -} - -export async function checkAutoRun(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('checkAutoRun')) -} - -export async function enableAutoRun(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('enableAutoRun')) -} - -export async function disableAutoRun(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('disableAutoRun')) -} - -export async function getAppConfig(force = false): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getAppConfig', force)) -} - -export async function patchAppConfig(patch: Partial): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('patchAppConfig', patch)) -} - -export async function getControledMihomoConfig(force = false): Promise> { - return ipcErrorWrapper( - await window.electron.ipcRenderer.invoke('getControledMihomoConfig', force) - ) -} - -export async function patchControledMihomoConfig(patch: Partial): Promise { - return ipcErrorWrapper( - await window.electron.ipcRenderer.invoke('patchControledMihomoConfig', patch) - ) -} - -export async function getProfileConfig(force = false): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getProfileConfig', force)) -} - -export async function setProfileConfig(config: IProfileConfig): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setProfileConfig', config)) -} - -export async function getCurrentProfileItem(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getCurrentProfileItem')) -} - -export async function getProfileItem(id: string | undefined): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getProfileItem', id)) -} - -export async function changeCurrentProfile(id: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('changeCurrentProfile', id)) -} - -export async function addProfileItem(item: Partial): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('addProfileItem', item)) -} - -export async function removeProfileItem(id: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('removeProfileItem', id)) -} - -export async function updateProfileItem(item: IProfileItem): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('updateProfileItem', item)) -} - -export async function addProfileUpdater(item: IProfileItem): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('addProfileUpdater', item)) -} - -export async function removeProfileUpdater(id: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('removeProfileUpdater', id)) -} - -export async function getProfileStr(id: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getProfileStr', id)) -} - -export async function getFileStr(id: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getFileStr', id)) -} - -export async function setFileStr(id: string, str: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setFileStr', id, str)) -} - -export async function setProfileStr(id: string, str: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setProfileStr', id, str)) -} - -export async function convertMrsRuleset(path: string, behavior: string): Promise { - return ipcErrorWrapper( - await window.electron.ipcRenderer.invoke('convertMrsRuleset', path, behavior) - ) -} - -export async function getOverrideConfig(force = false): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getOverrideConfig', force)) -} - -export async function setOverrideConfig(config: IOverrideConfig): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setOverrideConfig', config)) -} - -export async function getOverrideItem(id: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getOverrideItem', id)) -} - -export async function addOverrideItem(item: Partial): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('addOverrideItem', item)) -} - -export async function removeOverrideItem(id: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('removeOverrideItem', id)) -} - -export async function updateOverrideItem(item: IOverrideItem): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('updateOverrideItem', item)) -} - -export async function getOverride(id: string, ext: 'js' | 'yaml' | 'log'): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getOverride', id, ext)) -} - -export async function setOverride(id: string, ext: 'js' | 'yaml', str: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setOverride', id, ext, str)) -} - -export async function restartCore(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('restartCore')) -} - -export async function startMonitor(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('startMonitor')) -} - -export async function triggerSysProxy(enable: boolean): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('triggerSysProxy', enable)) -} - -export async function checkTunPermissions(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('checkTunPermissions')) -} - -export async function grantTunPermissions(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('grantTunPermissions')) -} - -export async function manualGrantCorePermition(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('manualGrantCorePermition')) -} - -export async function checkHighPrivilegeCore(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('checkHighPrivilegeCore')) -} - -export async function checkAdminPrivileges(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('checkAdminPrivileges')) -} - -export async function restartAsAdmin(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('restartAsAdmin')) -} - -export async function showTunPermissionDialog(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('showTunPermissionDialog')) -} - -export async function showErrorDialog(title: string, message: string): Promise { - return ipcErrorWrapper( - await window.electron.ipcRenderer.invoke('showErrorDialog', title, message) - ) -} - -export async function getFilePath(ext: string[]): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getFilePath', ext)) -} - -export async function readTextFile(filePath: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('readTextFile', filePath)) -} - -export async function getRuntimeConfigStr(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getRuntimeConfigStr')) -} - -export async function getRuntimeConfig(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getRuntimeConfig')) -} - -export async function checkUpdate(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('checkUpdate')) -} - -export async function downloadAndInstallUpdate(version: string): Promise { - return ipcErrorWrapper( - await window.electron.ipcRenderer.invoke('downloadAndInstallUpdate', version) - ) -} - -export async function getVersion(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getVersion')) -} - -export async function getPlatform(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('platform')) -} - -export async function openUWPTool(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('openUWPTool')) -} - -export async function setupFirewall(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setupFirewall')) -} - -export async function getInterfaces(): Promise> { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getInterfaces')) -} - -export async function webdavBackup(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('webdavBackup')) -} - -export async function webdavRestore(filename: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('webdavRestore', filename)) -} - -export async function listWebdavBackups(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('listWebdavBackups')) -} - -export async function webdavDelete(filename: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('webdavDelete', filename)) -} - -// WebDAV 备份调度器相关 IPC 调用 -export async function reinitWebdavBackupScheduler(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('reinitWebdavBackupScheduler')) -} - -// 本地备份相关 IPC 调用 -export async function exportLocalBackup(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('exportLocalBackup')) -} - -export async function importLocalBackup(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('importLocalBackup')) -} - -export async function setTitleBarOverlay(overlay: TitleBarOverlayOptions): Promise { - try { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setTitleBarOverlay', overlay)) - } catch (error) { - console.debug('setTitleBarOverlay not supported on this platform') - } -} - -export async function setAlwaysOnTop(alwaysOnTop: boolean): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setAlwaysOnTop', alwaysOnTop)) -} - -export async function isAlwaysOnTop(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('isAlwaysOnTop')) -} - -export async function relaunchApp(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('relaunchApp')) -} - -export async function quitWithoutCore(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('quitWithoutCore')) -} - -export async function quitApp(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('quitApp')) -} - -export async function setNativeTheme(theme: 'system' | 'light' | 'dark'): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setNativeTheme', theme)) -} - -export async function getGistUrl(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getGistUrl')) -} - -export async function startSubStoreFrontendServer(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('startSubStoreFrontendServer')) -} - -export async function stopSubStoreFrontendServer(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('stopSubStoreFrontendServer')) -} - -export async function startSubStoreBackendServer(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('startSubStoreBackendServer')) -} - -export async function stopSubStoreBackendServer(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('stopSubStoreBackendServer')) -} -export async function downloadSubStore(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('downloadSubStore')) -} - -export async function subStorePort(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('subStorePort')) -} - -export async function subStoreFrontendPort(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('subStoreFrontendPort')) -} - -export async function subStoreSubs(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('subStoreSubs')) -} - -export async function subStoreCollections(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('subStoreCollections')) -} - -export async function showTrayIcon(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('showTrayIcon')) -} - -export async function closeTrayIcon(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('closeTrayIcon')) -} - -export async function updateTrayIcon(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('updateTrayIcon')) -} - -export function updateTrayIconImmediate(sysProxyEnabled: boolean, tunEnabled: boolean): void { - window.electron.ipcRenderer.invoke('updateTrayIconImmediate', sysProxyEnabled, tunEnabled) -} - -export async function showMainWindow(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('showMainWindow')) -} - -export async function closeMainWindow(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('closeMainWindow')) -} - -export async function triggerMainWindow(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('triggerMainWindow')) -} - -export async function showFloatingWindow(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('showFloatingWindow')) -} - -export async function closeFloatingWindow(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('closeFloatingWindow')) -} - -export async function showContextMenu(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('showContextMenu')) -} - -export async function openFile( + return response as T +} + +async function invoke(channel: string, ...args: unknown[]): Promise { + const response = await window.electron.ipcRenderer.invoke(channel, ...args) + return checkIpcError(response) +} + +// Mihomo API +export const mihomoVersion = (): Promise => invoke('mihomoVersion') +export const mihomoCloseConnection = (id: string): Promise => + invoke('mihomoCloseConnection', id) +export const mihomoCloseAllConnections = (): Promise => invoke('mihomoCloseAllConnections') +export const mihomoRules = (): Promise => invoke('mihomoRules') +export const mihomoProxies = (): Promise => invoke('mihomoProxies') +export const mihomoGroups = (): Promise => invoke('mihomoGroups') +export const mihomoProxyProviders = (): Promise => + invoke('mihomoProxyProviders') +export const mihomoUpdateProxyProviders = (name: string): Promise => + invoke('mihomoUpdateProxyProviders', name) +export const mihomoRuleProviders = (): Promise => + invoke('mihomoRuleProviders') +export const mihomoUpdateRuleProviders = (name: string): Promise => + invoke('mihomoUpdateRuleProviders', name) +export const mihomoChangeProxy = (group: string, proxy: string): Promise => + invoke('mihomoChangeProxy', group, proxy) +export const mihomoUnfixedProxy = (group: string): Promise => + invoke('mihomoUnfixedProxy', group) +export const mihomoUpgradeGeo = (): Promise => invoke('mihomoUpgradeGeo') +export const mihomoUpgrade = (): Promise => invoke('mihomoUpgrade') +export const mihomoUpgradeUI = (): Promise => invoke('mihomoUpgradeUI') +export const mihomoUpgradeConfig = (): Promise => invoke('mihomoUpgradeConfig') +export const mihomoProxyDelay = (proxy: string, url?: string): Promise => + invoke('mihomoProxyDelay', proxy, url) +export const mihomoGroupDelay = (group: string, url?: string): Promise => + invoke('mihomoGroupDelay', group, url) +export const patchMihomoConfig = (patch: Partial): Promise => + invoke('patchMihomoConfig', patch) +export const mihomoSmartGroupWeights = (groupName: string): Promise> => + invoke('mihomoSmartGroupWeights', groupName) +export const mihomoSmartFlushCache = (configName?: string): Promise => + invoke('mihomoSmartFlushCache', configName) +export const getSmartOverrideContent = (): Promise => + invoke('getSmartOverrideContent') + +// AutoRun +export const checkAutoRun = (): Promise => invoke('checkAutoRun') +export const enableAutoRun = (): Promise => invoke('enableAutoRun') +export const disableAutoRun = (): Promise => invoke('disableAutoRun') + +// Config +export const getAppConfig = (force = false): Promise => invoke('getAppConfig', force) +export const patchAppConfig = (patch: Partial): Promise => + invoke('patchAppConfig', patch) +export const getControledMihomoConfig = (force = false): Promise> => + invoke('getControledMihomoConfig', force) +export const patchControledMihomoConfig = (patch: Partial): Promise => + invoke('patchControledMihomoConfig', patch) +export const resetAppConfig = (): Promise => invoke('resetAppConfig') + +// Profile +export const getProfileConfig = (force = false): Promise => + invoke('getProfileConfig', force) +export const setProfileConfig = (config: IProfileConfig): Promise => + invoke('setProfileConfig', config) +export const getCurrentProfileItem = (): Promise => invoke('getCurrentProfileItem') +export const getProfileItem = (id: string | undefined): Promise => + invoke('getProfileItem', id) +export const getProfileStr = (id: string): Promise => invoke('getProfileStr', id) +export const setProfileStr = (id: string, str: string): Promise => + invoke('setProfileStr', id, str) +export const addProfileItem = (item: Partial): Promise => + invoke('addProfileItem', item) +export const removeProfileItem = (id: string): Promise => invoke('removeProfileItem', id) +export const updateProfileItem = (item: IProfileItem): Promise => + invoke('updateProfileItem', item) +export const changeCurrentProfile = (id: string): Promise => + invoke('changeCurrentProfile', id) +export const addProfileUpdater = (item: IProfileItem): Promise => + invoke('addProfileUpdater', item) +export const removeProfileUpdater = (id: string): Promise => + invoke('removeProfileUpdater', id) + +// Override +export const getOverrideConfig = (force = false): Promise => + invoke('getOverrideConfig', force) +export const setOverrideConfig = (config: IOverrideConfig): Promise => + invoke('setOverrideConfig', config) +export const getOverrideItem = (id: string): Promise => + invoke('getOverrideItem', id) +export const addOverrideItem = (item: Partial): Promise => + invoke('addOverrideItem', item) +export const removeOverrideItem = (id: string): Promise => invoke('removeOverrideItem', id) +export const updateOverrideItem = (item: IOverrideItem): Promise => + invoke('updateOverrideItem', item) +export const getOverride = (id: string, ext: 'js' | 'yaml' | 'log'): Promise => + invoke('getOverride', id, ext) +export const setOverride = (id: string, ext: 'js' | 'yaml', str: string): Promise => + invoke('setOverride', id, ext, str) + +// File +export const getFileStr = (path: string): Promise => invoke('getFileStr', path) +export const setFileStr = (path: string, str: string): Promise => + invoke('setFileStr', path, str) +export const convertMrsRuleset = (path: string, behavior: string): Promise => + invoke('convertMrsRuleset', path, behavior) +export const getRuntimeConfig = (): Promise => invoke('getRuntimeConfig') +export const getRuntimeConfigStr = (): Promise => invoke('getRuntimeConfigStr') +export const getRuleStr = (id: string): Promise => invoke('getRuleStr', id) +export const setRuleStr = (id: string, str: string): Promise => + invoke('setRuleStr', id, str) +export const getFilePath = (ext: string[]): Promise => + invoke('getFilePath', ext) +export const readTextFile = (filePath: string): Promise => invoke('readTextFile', filePath) +export const openFile = ( type: 'profile' | 'override', id: string, ext?: 'yaml' | 'js' -): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('openFile', type, id, ext)) -} +): Promise => invoke('openFile', type, id, ext) -export async function openDevTools(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('openDevTools')) -} +// Core +export const restartCore = (): Promise => invoke('restartCore') +export const startMonitor = (): Promise => invoke('startMonitor') +export const quitWithoutCore = (): Promise => invoke('quitWithoutCore') -export async function resetAppConfig(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('resetAppConfig')) -} +// System +export const triggerSysProxy = (enable: boolean): Promise => + invoke('triggerSysProxy', enable) +export const checkTunPermissions = (): Promise => invoke('checkTunPermissions') +export const grantTunPermissions = (): Promise => invoke('grantTunPermissions') +export const manualGrantCorePermition = (): Promise => invoke('manualGrantCorePermition') +export const checkAdminPrivileges = (): Promise => invoke('checkAdminPrivileges') +export const restartAsAdmin = (): Promise => invoke('restartAsAdmin') +export const checkMihomoCorePermissions = (): Promise => + invoke('checkMihomoCorePermissions') +export const checkHighPrivilegeCore = (): Promise => invoke('checkHighPrivilegeCore') +export const showTunPermissionDialog = (): Promise => invoke('showTunPermissionDialog') +export const showErrorDialog = (title: string, message: string): Promise => + invoke('showErrorDialog', title, message) +export const openUWPTool = (): Promise => invoke('openUWPTool') +export const setupFirewall = (): Promise => invoke('setupFirewall') +export const getInterfaces = (): Promise> => + invoke('getInterfaces') +export const setNativeTheme = (theme: 'system' | 'light' | 'dark'): Promise => + invoke('setNativeTheme', theme) +export const copyEnv = (type: 'bash' | 'cmd' | 'powershell'): Promise => + invoke('copyEnv', type) -export async function createHeapSnapshot(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('createHeapSnapshot')) -} +// Update +export const checkUpdate = (): Promise => invoke('checkUpdate') +export const downloadAndInstallUpdate = (version: string): Promise => + invoke('downloadAndInstallUpdate', version) +export const getVersion = (): Promise => invoke('getVersion') +export const getPlatform = (): Promise => invoke('platform') +export const fetchMihomoTags = ( + forceRefresh = false +): Promise<{ name: string; zipball_url: string; tarball_url: string }[]> => + invoke('fetchMihomoTags', forceRefresh) +export const installSpecificMihomoCore = (version: string): Promise => + invoke('installSpecificMihomoCore', version) +export const clearMihomoVersionCache = (): Promise => invoke('clearMihomoVersionCache') -export async function getImageDataURL(url: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getImageDataURL', url)) -} +// Backup +export const webdavBackup = (): Promise => invoke('webdavBackup') +export const webdavRestore = (filename: string): Promise => + invoke('webdavRestore', filename) +export const listWebdavBackups = (): Promise => invoke('listWebdavBackups') +export const webdavDelete = (filename: string): Promise => invoke('webdavDelete', filename) +export const reinitWebdavBackupScheduler = (): Promise => + invoke('reinitWebdavBackupScheduler') +export const exportLocalBackup = (): Promise => invoke('exportLocalBackup') +export const importLocalBackup = (): Promise => invoke('importLocalBackup') -export async function resolveThemes(): Promise<{ key: string; label: string; content: string }[]> { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('resolveThemes')) -} +// SubStore +export const startSubStoreFrontendServer = (): Promise => + invoke('startSubStoreFrontendServer') +export const stopSubStoreFrontendServer = (): Promise => + invoke('stopSubStoreFrontendServer') +export const startSubStoreBackendServer = (): Promise => + invoke('startSubStoreBackendServer') +export const stopSubStoreBackendServer = (): Promise => invoke('stopSubStoreBackendServer') +export const downloadSubStore = (): Promise => invoke('downloadSubStore') +export const subStorePort = (): Promise => invoke('subStorePort') +export const subStoreFrontendPort = (): Promise => invoke('subStoreFrontendPort') +export const subStoreSubs = (): Promise => invoke('subStoreSubs') +export const subStoreCollections = (): Promise => invoke('subStoreCollections') -export async function fetchThemes(): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('fetchThemes')) -} - -export async function importThemes(files: string[]): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('importThemes', files)) -} - -export async function readTheme(theme: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('readTheme', theme)) -} - -export async function writeTheme(theme: string, css: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('writeTheme', theme, css)) -} +// Theme +export const resolveThemes = (): Promise<{ key: string; label: string; content: string }[]> => + invoke('resolveThemes') +export const fetchThemes = (): Promise => invoke('fetchThemes') +export const importThemes = (files: string[]): Promise => invoke('importThemes', files) +export const readTheme = (theme: string): Promise => invoke('readTheme', theme) +export const writeTheme = (theme: string, css: string): Promise => + invoke('writeTheme', theme, css) let applyThemeRunning = false -const waitList: string[] = [] +const applyThemeWaitList: string[] = [] export async function applyTheme(theme: string): Promise { if (applyThemeRunning) { - waitList.push(theme) + applyThemeWaitList.push(theme) return } applyThemeRunning = true try { - return await ipcErrorWrapper(window.electron.ipcRenderer.invoke('applyTheme', theme)) + await invoke('applyTheme', theme) } finally { applyThemeRunning = false - if (waitList.length > 0) { - await applyTheme(waitList.shift() || '') + if (applyThemeWaitList.length > 0) { + const nextTheme = applyThemeWaitList.shift() + if (nextTheme) { + await applyTheme(nextTheme) + } } } } -export async function registerShortcut( +// Tray +export const showTrayIcon = (): Promise => invoke('showTrayIcon') +export const closeTrayIcon = (): Promise => invoke('closeTrayIcon') +export const updateTrayIcon = (): Promise => invoke('updateTrayIcon') +export function updateTrayIconImmediate(sysProxyEnabled: boolean, tunEnabled: boolean): void { + window.electron.ipcRenderer.invoke('updateTrayIconImmediate', sysProxyEnabled, tunEnabled) +} + +// Window +export const showMainWindow = (): Promise => invoke('showMainWindow') +export const closeMainWindow = (): Promise => invoke('closeMainWindow') +export const triggerMainWindow = (): Promise => invoke('triggerMainWindow') +export const showFloatingWindow = (): Promise => invoke('showFloatingWindow') +export const closeFloatingWindow = (): Promise => invoke('closeFloatingWindow') +export const showContextMenu = (): Promise => invoke('showContextMenu') +export async function setTitleBarOverlay(overlay: TitleBarOverlayOptions): Promise { + try { + await invoke('setTitleBarOverlay', overlay) + } catch { + // Not supported on this platform + } +} +export const setAlwaysOnTop = (alwaysOnTop: boolean): Promise => + invoke('setAlwaysOnTop', alwaysOnTop) +export const isAlwaysOnTop = (): Promise => invoke('isAlwaysOnTop') +export const openDevTools = (): Promise => invoke('openDevTools') +export const createHeapSnapshot = (): Promise => invoke('createHeapSnapshot') + +// Shortcut +export const registerShortcut = ( oldShortcut: string, newShortcut: string, action: string -): Promise { - return ipcErrorWrapper( - await window.electron.ipcRenderer.invoke('registerShortcut', oldShortcut, newShortcut, action) - ) -} +): Promise => invoke('registerShortcut', oldShortcut, newShortcut, action) -export async function copyEnv(type: 'bash' | 'cmd' | 'powershell'): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('copyEnv', type)) -} - -export async function getRuleStr(id: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getRuleStr', id)) -} - -export async function setRuleStr(id: string, str: string): Promise { - return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setRuleStr', id, str)) -} +// Misc +export const getGistUrl = (): Promise => invoke('getGistUrl') +export const getImageDataURL = (url: string): Promise => invoke('getImageDataURL', url) +export const relaunchApp = (): Promise => invoke('relaunchApp') +export const quitApp = (): Promise => invoke('quitApp')