diff --git a/changelog.md b/changelog.md index a9f1be5..e0bfe76 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,7 @@ ### New Features - Windows 允许添加 `noadmin` 参数以普通权限启动程序 +- 支持在 Windows 任务栏显示网速信息 ### Bug Fixes diff --git a/scripts/prepare.mjs b/scripts/prepare.mjs index f778a8e..09db6f4 100644 --- a/scripts/prepare.mjs +++ b/scripts/prepare.mjs @@ -271,6 +271,28 @@ const resolveRunner = () => file: 'mihomo-party-run.exe', downloadURL: `https://github.com/mihomo-party-org/mihomo-party-run/releases/download/${arch}/mihomo-party-run.exe` }) + +const resolveMonitor = async () => { + const tempDir = path.join(TEMP_DIR, 'TrafficMonitor') + const tempZip = path.join(tempDir, `${arch}.zip`) + if (!fs.existsSync(tempDir)) { + fs.mkdirSync(tempDir, { recursive: true }) + } + await downloadFile( + `https://github.com/mihomo-party-org/mihomo-party-run/releases/download/monitor/${arch}.zip`, + tempZip + ) + const zip = new AdmZip(tempZip) + const resDir = path.join(cwd, 'extra', 'files') + const targetPath = path.join(resDir, 'TrafficMonitor') + if (fs.existsSync(targetPath)) { + fs.rmSync(targetPath, { recursive: true }) + } + zip.extractAllTo(resDir, true) + + console.log(`[INFO]: TrafficMonitor finished`) +} + const resolve7zip = () => resolveResource({ file: '7za.exe', @@ -355,6 +377,12 @@ const tasks = [ retry: 5, winOnly: true }, + { + name: 'monitor', + func: resolveMonitor, + retry: 5, + winOnly: true + }, { name: 'substore', func: resolveSubstore, diff --git a/src/main/core/manager.ts b/src/main/core/manager.ts index 21e0041..fe00308 100644 --- a/src/main/core/manager.ts +++ b/src/main/core/manager.ts @@ -34,6 +34,7 @@ import { mainWindow } from '..' import path from 'path' import { existsSync } from 'fs' import { uploadRuntimeConfig } from '../resolve/gistApi' +import { startMonitor } from '../resolve/trafficMonitor' chokidar.watch(path.join(mihomoCoreDir(), 'meta-update'), {}).on('unlinkDir', async () => { try { @@ -193,6 +194,7 @@ export async function keepCoreAlive(): Promise { export async function quitWithoutCore(): Promise { await keepCoreAlive() + await startMonitor(true) app.exit() } diff --git a/src/main/resolve/trafficMonitor.ts b/src/main/resolve/trafficMonitor.ts new file mode 100644 index 0000000..77c121c --- /dev/null +++ b/src/main/resolve/trafficMonitor.ts @@ -0,0 +1,27 @@ +import { ChildProcess, spawn } from 'child_process' +import { getAppConfig } from '../config' +import { resourcesFilesDir } from '../utils/dirs' +import path from 'path' + +let child: ChildProcess + +export async function startMonitor(detached = false): Promise { + if (process.platform !== 'win32') return + await stopMonitor() + const { showTraffic = true } = await getAppConfig() + if (!showTraffic) return + child = spawn(path.join(resourcesFilesDir(), 'TrafficMonitor/TrafficMonitor.exe'), [], { + cwd: path.join(resourcesFilesDir(), 'TrafficMonitor'), + detached: detached, + stdio: detached ? 'ignore' : undefined + }) + if (detached) { + child.unref() + } +} + +async function stopMonitor(): Promise { + if (child) { + child.kill('SIGINT') + } +} diff --git a/src/main/utils/init.ts b/src/main/utils/init.ts index b71eea6..4fb758d 100644 --- a/src/main/utils/init.ts +++ b/src/main/utils/init.ts @@ -35,6 +35,7 @@ import { } from '../config' import { app } from 'electron' import { startSSIDCheck } from '../sys/ssid' +import { startMonitor } from '../resolve/trafficMonitor' async function initDirs(): Promise { if (!existsSync(dataDir())) { @@ -223,6 +224,7 @@ export async function init(): Promise { await cleanup() await startPacServer() await startSubStoreServer() + await startMonitor() const { sysProxy } = await getAppConfig() await triggerSysProxy(sysProxy.enable) await startSSIDCheck() diff --git a/src/main/utils/ipc.ts b/src/main/utils/ipc.ts index 68dbf14..16e49d0 100644 --- a/src/main/utils/ipc.ts +++ b/src/main/utils/ipc.ts @@ -80,6 +80,7 @@ import path from 'path' import v8 from 'v8' import { getGistUrl } from '../resolve/gistApi' import { getImageDataURL } from './image' +import { startMonitor } from '../resolve/trafficMonitor' function ipcErrorWrapper( // eslint-disable-next-line @typescript-eslint/no-explicit-any fn: (...args: any[]) => Promise // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -161,6 +162,7 @@ export function registerIpcMainHandlers(): void { 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('isEncryptionAvailable', isEncryptionAvailable) ipcMain.handle('encryptString', (_e, str) => encryptString(str)) diff --git a/src/renderer/src/components/settings/general-config.tsx b/src/renderer/src/components/settings/general-config.tsx index d617ae6..99e2160 100644 --- a/src/renderer/src/components/settings/general-config.tsx +++ b/src/renderer/src/components/settings/general-config.tsx @@ -15,7 +15,7 @@ import { importThemes, relaunchApp, resolveThemes, - restartCore, + startMonitor, writeTheme } from '@renderer/utils/ipc' import { useAppConfig } from '@renderer/hooks/use-app-config' @@ -176,15 +176,30 @@ const GeneralConfig: React.FC = () => { {platform !== 'linux' && ( - - { - await patchAppConfig({ proxyInTray: v }) - }} - /> - + <> + + { + await patchAppConfig({ proxyInTray: v }) + }} + /> + + + { + await patchAppConfig({ showTraffic: v }) + await startMonitor() + }} + /> + + )} {platform === 'darwin' && ( <> @@ -197,16 +212,6 @@ const GeneralConfig: React.FC = () => { }} /> - - { - await patchAppConfig({ showTraffic: v }) - await restartCore() - }} - /> - )} diff --git a/src/renderer/src/utils/ipc.ts b/src/renderer/src/utils/ipc.ts index 34ed97d..5ef8569 100644 --- a/src/renderer/src/utils/ipc.ts +++ b/src/renderer/src/utils/ipc.ts @@ -191,6 +191,10 @@ 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)) }