diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index a6f34fe..0000000 --- a/.eslintignore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules -dist -out -.gitignore diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index 63c9779..0000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - extends: [ - 'eslint:recommended', - 'plugin:react/recommended', - 'plugin:react/jsx-runtime', - '@electron-toolkit/eslint-config-ts/recommended', - '@electron-toolkit/eslint-config-prettier' - ] -} diff --git a/eslint.config.cjs b/eslint.config.cjs new file mode 100644 index 0000000..771c36d --- /dev/null +++ b/eslint.config.cjs @@ -0,0 +1,48 @@ +const js = require('@eslint/js') +const react = require('eslint-plugin-react') +const { configs } = require('@electron-toolkit/eslint-config-ts') + +module.exports = [ + { + ignores: ['**/node_modules/**', '**/dist/**', '**/out/**', '**/extra/**'] + }, + + js.configs.recommended, + ...configs.recommended, + + { + files: ['**/*.{js,jsx,ts,tsx}'], + plugins: { + react: react + }, + rules: { + ...react.configs.recommended.rules, + ...react.configs['jsx-runtime'].rules + }, + settings: { + react: { + version: 'detect' + } + }, + languageOptions: { + ...react.configs.recommended.languageOptions + } + }, + + { + files: ['**/*.cjs', '**/*.mjs', '**/tailwind.config.js', '**/postcss.config.js'], + rules: { + '@typescript-eslint/no-require-imports': 'off', + '@typescript-eslint/explicit-function-return-type': 'off' + } + }, + + { + files: ['**/*.{ts,tsx}'], + rules: { + '@typescript-eslint/no-unused-vars': 0, + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-explicit-any': 'warn' + } + } +] diff --git a/scripts/prepare.mjs b/scripts/prepare.mjs index e70f02d..761e2ba 100644 --- a/scripts/prepare.mjs +++ b/scripts/prepare.mjs @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/explicit-function-return-type */ import fs from 'fs' import AdmZip from 'adm-zip' import path from 'path' diff --git a/scripts/telegram.mjs b/scripts/telegram.mjs index 6c884e9..6c1f5f4 100644 --- a/scripts/telegram.mjs +++ b/scripts/telegram.mjs @@ -9,9 +9,7 @@ import { } from './version-utils.mjs' const chat_id = '@MihomoPartyChannel' -const pkg = readFileSync('package.json', 'utf-8') const changelog = readFileSync('changelog.md', 'utf-8') -const { version: packageVersion } = JSON.parse(pkg) // 获取处理后的版本号 const version = getProcessedVersion() diff --git a/scripts/updater.mjs b/scripts/updater.mjs index 8c12d76..8d2014d 100644 --- a/scripts/updater.mjs +++ b/scripts/updater.mjs @@ -7,9 +7,7 @@ import { generateDownloadLinksMarkdown } from './version-utils.mjs' -const pkg = readFileSync('package.json', 'utf-8') let changelog = readFileSync('changelog.md', 'utf-8') -const { version: packageVersion } = JSON.parse(pkg) // 获取处理后的版本号 const version = getProcessedVersion() diff --git a/src/main/config/controledMihomo.ts b/src/main/config/controledMihomo.ts index 0eb2c94..88451f7 100644 --- a/src/main/config/controledMihomo.ts +++ b/src/main/config/controledMihomo.ts @@ -49,7 +49,7 @@ export async function patchControledMihomoConfig(patch: Partial): // 从不接管状态恢复 if (controlDns) { - // 确保DNS配置包含所有必要的默认字段,特别是新增的fallback等 + // 确保 DNS 配置包含所有必要的默认字段,特别是新增的 fallback 等 controledMihomoConfig.dns = deepMerge( defaultControledMihomoConfig.dns || {}, controledMihomoConfig.dns || {} diff --git a/src/main/config/override.ts b/src/main/config/override.ts index 16db1a0..2180d2c 100644 --- a/src/main/config/override.ts +++ b/src/main/config/override.ts @@ -78,7 +78,7 @@ export async function createOverride(item: Partial): Promise { const parts = ruleStr.split(',') @@ -65,7 +65,7 @@ export async function generateProfile(): Promise { controlSniff = true, useNameserverPolicy } = await getAppConfig() - let currentProfile = await overrideProfile(current, await getProfile(current)) + const currentProfile = await overrideProfile(current, await getProfile(current)) let controledMihomoConfig = await getControledMihomoConfig() // 根据开关状态过滤控制配置 @@ -132,7 +132,7 @@ export async function generateProfile(): Promise { } } } catch (error) { - console.error('读取或应用规则文件时出错:', error) + console.error('读取或应用规则文件时出错:', error) } const profile = deepMerge(currentProfile, controledMihomoConfig) diff --git a/src/main/core/manager.ts b/src/main/core/manager.ts index d978554..26ca45e 100644 --- a/src/main/core/manager.ts +++ b/src/main/core/manager.ts @@ -330,8 +330,8 @@ async function cleanupWindowsNamedPipes(): Promise { process.kill(pid, 0) process.kill(pid, 'SIGTERM') await managerLogger.info(`Terminated process ${pid} to free pipe`) - } catch (error: any) { - if (error.code !== 'ESRCH') { + } catch (error: unknown) { + if ((error as { code?: string })?.code !== 'ESRCH') { await managerLogger.warn(`Failed to terminate process ${pid}:`, error) } } @@ -351,8 +351,8 @@ async function cleanupWindowsNamedPipes(): Promise { process.kill(pid, 0) process.kill(pid, 'SIGTERM') await managerLogger.info(`Terminated process ${pid} to free pipe`) - } catch (error: any) { - if (error.code !== 'ESRCH') { + } catch (error: unknown) { + if ((error as { code?: string })?.code !== 'ESRCH') { await managerLogger.warn(`Failed to terminate process ${pid}:`, error) } } @@ -589,8 +589,8 @@ export async function checkAdminPrivileges(): Promise { await execPromise('chcp 65001 >nul 2>&1 && fltmc', { encoding: 'utf8' }) await managerLogger.info('Admin privileges confirmed via fltmc') return true - } catch (fltmcError: any) { - const errorCode = fltmcError?.code || 0 + } catch (fltmcError: unknown) { + const errorCode = (fltmcError as { code?: number })?.code || 0 await managerLogger.debug(`fltmc failed with code ${errorCode}, trying net session as fallback`) try { @@ -598,8 +598,8 @@ export async function checkAdminPrivileges(): Promise { await execPromise('chcp 65001 >nul 2>&1 && net session', { encoding: 'utf8' }) await managerLogger.info('Admin privileges confirmed via net session') return true - } catch (netSessionError: any) { - const netErrorCode = netSessionError?.code || 0 + } catch (netSessionError: unknown) { + const netErrorCode = (netSessionError as { code?: number })?.code || 0 await managerLogger.debug( `Both fltmc and net session failed, no admin privileges. Error codes: fltmc=${errorCode}, net=${netErrorCode}` ) @@ -618,7 +618,8 @@ export async function showTunPermissionDialog(): Promise { const title = i18next.t('tun.permissions.title') || '需要管理员权限' const message = - i18next.t('tun.permissions.message') || '启用TUN模式需要管理员权限,是否现在重启应用获取权限?' + i18next.t('tun.permissions.message') || + '启用 TUN 模式需要管理员权限,是否现在重启应用获取权限?' const confirmText = i18next.t('common.confirm') || '确认' const cancelText = i18next.t('common.cancel') || '取消' @@ -679,7 +680,7 @@ export async function restartAsAdmin(forTun: boolean = true): Promise { await managerLogger.info('Restarting as administrator with command', command) - // 执行PowerShell命令 + // 执行 PowerShell 命令 exec(command, { windowsHide: true }, async (error, _stdout, stderr) => { if (error) { await managerLogger.error('PowerShell execution error', error) @@ -703,7 +704,7 @@ export async function checkMihomoCorePermissions(): Promise { try { if (process.platform === 'win32') { - // Windows权限检查 + // Windows 权限检查 return await checkAdminPrivileges() } @@ -834,7 +835,9 @@ async function checkHighPrivilegeMihomoProcess(): Promise { } } } - } catch (error) {} + } catch (error) { + // ignore + } } if (!foundProcesses) { @@ -851,7 +854,7 @@ async function checkHighPrivilegeMihomoProcess(): Promise { return false } -// TUN模式获取权限 +// TUN 模式获取权限 export async function requestTunPermissions(): Promise { if (process.platform === 'win32') { await restartAsAdmin() @@ -894,7 +897,7 @@ export async function checkAdminRestartForTun(): Promise { await managerLogger.error('Failed to auto-enable TUN after admin restart', error) } } else { - // 检查TUN配置与权限的匹配,但不自动开启 TUN + // 检查 TUN 配置与权限的匹配,但不自动开启 TUN await validateTunPermissionsOnStartup() } } diff --git a/src/main/core/profileUpdater.ts b/src/main/core/profileUpdater.ts index e0d0242..94c3048 100644 --- a/src/main/core/profileUpdater.ts +++ b/src/main/core/profileUpdater.ts @@ -10,7 +10,7 @@ export async function initProfileUpdater(): Promise { for (const item of items.filter((i) => i.id !== current)) { if (item.type === 'remote' && item.autoUpdate && item.interval) { if (typeof item.interval === 'number') { - // 数字间隔使用setInterval + // 数字间隔使用 setInterval intervalPool[item.id] = setInterval( async () => { try { @@ -22,7 +22,7 @@ export async function initProfileUpdater(): Promise { item.interval * 60 * 1000 ) } else if (typeof item.interval === 'string') { - // 字符串间隔使用Cron + // 字符串间隔使用 Cron intervalPool[item.id] = new Cron(item.interval, async () => { try { await addProfileItem(item) diff --git a/src/main/core/subStoreApi.ts b/src/main/core/subStoreApi.ts index f560c74..778f755 100644 --- a/src/main/core/subStoreApi.ts +++ b/src/main/core/subStoreApi.ts @@ -5,13 +5,13 @@ import { getAppConfig } from '../config' export async function subStoreSubs(): Promise { const { useCustomSubStore = false, customSubStoreUrl = '' } = await getAppConfig() const baseUrl = useCustomSubStore ? customSubStoreUrl : `http://127.0.0.1:${subStorePort}` - const res = await chromeRequest.get(`${baseUrl}/api/subs`, { responseType: 'json' }) - return res.data.data as ISubStoreSub[] + const res = await chromeRequest.get<{ data: ISubStoreSub[] }>(`${baseUrl}/api/subs`, { responseType: 'json' }) + return res.data.data } export async function subStoreCollections(): Promise { const { useCustomSubStore = false, customSubStoreUrl = '' } = await getAppConfig() const baseUrl = useCustomSubStore ? customSubStoreUrl : `http://127.0.0.1:${subStorePort}` - const res = await chromeRequest.get(`${baseUrl}/api/collections`, { responseType: 'json' }) - return res.data.data as ISubStoreSub[] + const res = await chromeRequest.get<{ data: ISubStoreSub[] }>(`${baseUrl}/api/collections`, { responseType: 'json' }) + return res.data.data } diff --git a/src/main/index.ts b/src/main/index.ts index e7559ed..fafaf9a 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -231,7 +231,7 @@ app.whenReady().then(async () => { const [startPromise] = await startCore() startPromise.then(async () => { await initProfileUpdater() - await initWebdavBackupScheduler() // 初始化WebDAV定时备份任务 + await initWebdavBackupScheduler() // 初始化 WebDAV 定时备份任务 // 上次是否为了开启 TUN 而重启 await checkAdminRestartForTun() }) diff --git a/src/main/resolve/autoUpdater.ts b/src/main/resolve/autoUpdater.ts index 4d9d9d1..238435f 100644 --- a/src/main/resolve/autoUpdater.ts +++ b/src/main/resolve/autoUpdater.ts @@ -26,7 +26,7 @@ export async function checkUpdate(): Promise { responseType: 'text' } ) - const latest = parse(res.data) as IAppVersion + const latest = parse(res.data as string) as IAppVersion const currentVersion = app.getVersion() if (compareVersions(latest.version, currentVersion) > 0) { return latest @@ -94,7 +94,7 @@ export async function downloadAndInstallUpdate(version: string): Promise { 'Content-Type': 'application/octet-stream' } }) - await writeFile(path.join(dataDir(), file), res.data) + await writeFile(path.join(dataDir(), file), res.data as string | Buffer) } if (file.endsWith('.exe')) { try { @@ -125,7 +125,7 @@ export async function downloadAndInstallUpdate(version: string): Promise { } catch (installerError) { await appLogger.error('Failed to start installer, trying fallback', installerError) - // Fallback: 尝试使用shell.openPath打开安装包 + // Fallback: 尝试使用 shell.openPath 打开安装包 try { await shell.openPath(path.join(dataDir(), file)) await appLogger.info('Opened installer with shell.openPath as fallback') diff --git a/src/main/resolve/backup.ts b/src/main/resolve/backup.ts index 0c68d63..8066801 100644 --- a/src/main/resolve/backup.ts +++ b/src/main/resolve/backup.ts @@ -155,7 +155,7 @@ export async function webdavDelete(filename: string): Promise { } /** - * 初始化WebDAV定时备份任务 + * 初始化 WebDAV 定时备份任务 */ export async function initWebdavBackupScheduler(): Promise { try { @@ -167,7 +167,7 @@ export async function initWebdavBackupScheduler(): Promise { const { webdavBackupCron } = await getAppConfig() - // 如果配置了Cron表达式,则启动定时任务 + // 如果配置了 Cron 表达式,则启动定时任务 if (webdavBackupCron) { backupCronJob = new Cron(webdavBackupCron, async () => { try { @@ -189,7 +189,7 @@ export async function initWebdavBackupScheduler(): Promise { } /** - * 停止WebDAV定时备份任务 + * 停止 WebDAV 定时备份任务 */ export async function stopWebdavBackupScheduler(): Promise { if (backupCronJob) { @@ -200,7 +200,7 @@ export async function stopWebdavBackupScheduler(): Promise { } /** - * 重新初始化WebDAV定时备份任务 + * 重新初始化 WebDAV 定时备份任务 * 先停止现有任务,然后重新启动 */ export async function reinitScheduler(): Promise { diff --git a/src/main/resolve/floatingWindow.ts b/src/main/resolve/floatingWindow.ts index 3c703ea..4a3dec7 100644 --- a/src/main/resolve/floatingWindow.ts +++ b/src/main/resolve/floatingWindow.ts @@ -9,7 +9,7 @@ import { floatingWindowLogger } from '../utils/logger' export let floatingWindow: BrowserWindow | null = null -function logError(message: string, error?: any): void { +function logError(message: string, error?: unknown): void { floatingWindowLogger.log(`FloatingWindow Error: ${message}`, error).catch(() => {}) } diff --git a/src/main/resolve/tray.ts b/src/main/resolve/tray.ts index 5e46041..7a86f75 100644 --- a/src/main/resolve/tray.ts +++ b/src/main/resolve/tray.ts @@ -395,7 +395,7 @@ export async function createTray(): Promise { image.setTemplateImage(true) tray?.setImage(image) }) - // macOS 默认行为: 左键显示窗口, 右键显示菜单 + // macOS 默认行为:左键显示窗口,右键显示菜单 tray?.addListener('click', async () => { if (swapTrayClick) { await updateTrayMenu() @@ -537,7 +537,7 @@ export function updateTrayIconImmediate(sysProxyEnabled: boolean, tunEnabled: bo tray.setImage(iconPath) } } catch (error) { - console.error('更新托盘图标失败:', error) + console.error('更新托盘图标失败:', error) } }) } @@ -560,6 +560,6 @@ export async function updateTrayIcon(): Promise { tray.setImage(iconPath) } } catch (error) { - console.error('更新托盘图标失败:', error) + console.error('更新托盘图标失败:', error) } } diff --git a/src/main/sys/sysproxy.ts b/src/main/sys/sysproxy.ts index 197d84c..c56d08c 100644 --- a/src/main/sys/sysproxy.ts +++ b/src/main/sys/sysproxy.ts @@ -2,7 +2,7 @@ import { triggerAutoProxy, triggerManualProxy } from '@mihomo-party/sysproxy' import { getAppConfig, getControledMihomoConfig } from '../config' import { pacPort, startPacServer, stopPacServer } from '../resolve/server' import { promisify } from 'util' -import { execFile } from 'child_process' +import { exec, execFile } from 'child_process' import path from 'path' import { resourcesFilesDir } from '../utils/dirs' import { net } from 'electron' @@ -164,8 +164,6 @@ function isSocketFileExists(): boolean { async function requestSocketRecreation(): Promise { try { // Send SIGUSR1 signal to helper process to recreate socket - const { exec } = require('child_process') - const { promisify } = require('util') const execPromise = promisify(exec) // Use osascript with administrator privileges (same pattern as grantTunPermissions) diff --git a/src/main/utils/chromeRequest.ts b/src/main/utils/chromeRequest.ts index be42cba..b7ba326 100644 --- a/src/main/utils/chromeRequest.ts +++ b/src/main/utils/chromeRequest.ts @@ -17,7 +17,7 @@ export interface RequestOptions { maxRedirects?: number } -export interface Response { +export interface Response { data: T status: number statusText: string @@ -29,7 +29,7 @@ export interface Response { * Make HTTP request using Chromium's network stack (via electron.net) * This provides better compatibility, HTTP/2 support, and system certificate integration */ -export async function request( +export async function request( url: string, options: RequestOptions = {} ): Promise> { @@ -45,7 +45,7 @@ export async function request( } = options return new Promise((resolve, reject) => { - let sessionToUse = session.defaultSession + let sessionToUse: Electron.Session | undefined = session.defaultSession let tempPartition: string | null = null // Set up proxy if specified @@ -64,7 +64,7 @@ export async function request( if (tempPartition) { // Note: Electron doesn't provide session.destroy(), but temporary sessions // will be garbage collected when no longer referenced - sessionToUse = null as any + sessionToUse = undefined } } @@ -125,7 +125,7 @@ export async function request( if (timeoutId) clearTimeout(timeoutId) const buffer = Buffer.concat(chunks) - let data: any + let data: unknown try { switch (responseType) { @@ -141,25 +141,25 @@ export async function request( } resolve({ - data, + data: data as T, status: statusCode, statusText: statusMessage, headers: responseHeaders, url: url }) - } catch (error) { - reject(new Error(`Failed to parse response: ${error}`)) + } catch (error: unknown) { + reject(new Error(`Failed to parse response: ${String(error)}`)) } }) - res.on('error', (error) => { + res.on('error', (error: unknown) => { cleanup() if (timeoutId) clearTimeout(timeoutId) reject(error) }) }) - req.on('error', (error) => { + req.on('error', (error: unknown) => { cleanup() if (timeoutId) clearTimeout(timeoutId) reject(error) @@ -182,9 +182,9 @@ export async function request( req.end() }) - .catch((error) => { + .catch((error: unknown) => { cleanup() - reject(new Error(`Failed to setup proxy: ${error}`)) + reject(new Error(`Failed to setup proxy: ${String(error)}`)) }) }) } @@ -192,7 +192,7 @@ export async function request( /** * Convenience method for GET requests */ -export const get = ( +export const get = ( url: string, options?: Omit ): Promise> => request(url, { ...options, method: 'GET' }) @@ -200,9 +200,9 @@ export const get = ( /** * Convenience method for POST requests */ -export const post = ( +export const post = ( url: string, - data: any, + data: unknown, options?: Omit ): Promise> => { const body = typeof data === 'string' ? data : JSON.stringify(data) @@ -216,9 +216,9 @@ export const post = ( /** * Convenience method for PUT requests */ -export const put = ( +export const put = ( url: string, - data: any, + data: unknown, options?: Omit ): Promise> => { const body = typeof data === 'string' ? data : JSON.stringify(data) @@ -232,7 +232,7 @@ export const put = ( /** * Convenience method for DELETE requests */ -export const del = ( +export const del = ( url: string, options?: Omit ): Promise> => request(url, { ...options, method: 'DELETE' }) @@ -240,9 +240,9 @@ export const del = ( /** * Convenience method for PATCH requests */ -export const patch = ( +export const patch = ( url: string, - data: any, + data: unknown, options?: Omit ): Promise> => { const body = typeof data === 'string' ? data : JSON.stringify(data) diff --git a/src/main/utils/github.ts b/src/main/utils/github.ts index ee320d7..de2c527 100644 --- a/src/main/utils/github.ts +++ b/src/main/utils/github.ts @@ -42,7 +42,7 @@ const PLATFORM_MAP: Record = { const versionCache = new Map() /** - * 获取GitHub仓库的标签列表(带缓存) + * 获取 GitHub 仓库的标签列表(带缓存) * @param owner 仓库所有者 * @param repo 仓库名称 * @param forceRefresh 是否强制刷新缓存 @@ -109,8 +109,8 @@ export function clearVersionCache(owner: string, repo: string): void { } /** - * 下载GitHub Release资产 - * @param url 下载URL + * 下载 GitHub Release 资产 + * @param url 下载 URL * @param outputPath 输出路径 */ async function downloadGitHubAsset(url: string, outputPath: string): Promise { @@ -133,7 +133,7 @@ async function downloadGitHubAsset(url: string, outputPath: string): Promise { @@ -141,9 +141,9 @@ export async function installMihomoCore(version: string): Promise { console.log(`[GitHub] Installing mihomo core version ${version}`) const plat = platform() - let arch = process.arch + const arch = process.arch - // 映射平台和架构到GitHub Release文件名 + // 映射平台和架构到 GitHub Release 文件名 const key = `${plat}-${arch}` const name = PLATFORM_MAP[key] @@ -185,7 +185,7 @@ export async function installMihomoCore(version: string): Promise { throw new Error(`Executable file not found in zip: ${exeFile}`) } } else { - // 处理.gz文件 + // 处理.gz 文件 console.log(`[GitHub] Extracting GZ file ${tempZip}`) const readStream = createReadStream(tempZip) const writeStream = createWriteStream(targetPath) diff --git a/src/main/utils/init.ts b/src/main/utils/init.ts index 4c6d91c..22401eb 100644 --- a/src/main/utils/init.ts +++ b/src/main/utils/init.ts @@ -84,7 +84,7 @@ async function fixDataDirPermissions(): Promise { } } -// 比较修改geodata文件修改时间 +// 比较修改 geodata 文件修改时间 async function isSourceNewer(sourcePath: string, targetPath: string): Promise { try { const sourceStats = await stat(sourcePath) diff --git a/src/main/utils/ipc.ts b/src/main/utils/ipc.ts index db7c168..bd357c9 100644 --- a/src/main/utils/ipc.ts +++ b/src/main/utils/ipc.ts @@ -147,7 +147,7 @@ function ipcErrorWrapper( // eslint-disable-next-line @typescript-eslint/no-e } } -// GitHub版本管理相关IPC处理程序 +// GitHub 版本管理相关 IPC 处理程序 export async function fetchMihomoTags( forceRefresh = false ): Promise<{ name: string; zipball_url: string; tarball_url: string }[]> { @@ -367,20 +367,20 @@ export function registerIpcMainHandlers(): void { ipcMain.emit('updateTrayMenu') }) - // 注册获取Mihomo标签的IPC处理程序 + // 注册获取 Mihomo 标签的 IPC 处理程序 ipcMain.handle('fetchMihomoTags', (_e, forceRefresh) => ipcErrorWrapper(fetchMihomoTags)(forceRefresh) ) - // 注册安装特定版本Mihomo核心的IPC处理程序 + // 注册安装特定版本 Mihomo 核心的 IPC 处理程序 ipcMain.handle('installSpecificMihomoCore', (_e, version) => ipcErrorWrapper(installSpecificMihomoCore)(version) ) - // 注册清除版本缓存的IPC处理程序 + // 注册清除版本缓存的 IPC 处理程序 ipcMain.handle('clearMihomoVersionCache', () => ipcErrorWrapper(clearMihomoVersionCache)()) - // 规则相关IPC处理程序 + // 规则相关 IPC 处理程序 ipcMain.handle('getRuleStr', (_e, id) => ipcErrorWrapper(getRuleStr)(id)) ipcMain.handle('setRuleStr', (_e, id, str) => ipcErrorWrapper(setRuleStr)(id, str)) } diff --git a/src/main/utils/logger.ts b/src/main/utils/logger.ts index 6259301..2b9de3b 100644 --- a/src/main/utils/logger.ts +++ b/src/main/utils/logger.ts @@ -14,13 +14,13 @@ class Logger { return new Date().toISOString() } - private formatLogMessage(level: LogLevel, message: string, error?: any): string { + private formatLogMessage(level: LogLevel, message: string, error?: unknown): string { const timestamp = this.formatTimestamp() - const errorStr = error ? `: ${error}` : '' + const errorStr = error ? `: ${String(error)}` : '' return `[${timestamp}] [${level.toUpperCase()}] [${this.moduleName}] ${message}${errorStr}\n` } - private async writeToFile(level: LogLevel, message: string, error?: any): Promise { + private async writeToFile(level: LogLevel, message: string, error?: unknown): Promise { try { const appLogPath = logPath() const logMessage = this.formatLogMessage(level, message, error) @@ -35,7 +35,7 @@ class Logger { } } - private logToConsole(level: LogLevel, message: string, error?: any): void { + private logToConsole(level: LogLevel, message: string, error?: unknown): void { const prefix = `[${this.moduleName}] ${message}` switch (level) { @@ -54,28 +54,28 @@ class Logger { } } - async debug(message: string, error?: any): Promise { + async debug(message: string, error?: unknown): Promise { await this.writeToFile('debug', message, error) this.logToConsole('debug', message, error) } - async info(message: string, error?: any): Promise { + async info(message: string, error?: unknown): Promise { await this.writeToFile('info', message, error) this.logToConsole('info', message, error) } - async warn(message: string, error?: any): Promise { + async warn(message: string, error?: unknown): Promise { await this.writeToFile('warn', message, error) this.logToConsole('warn', message, error) } - async error(message: string, error?: any): Promise { + async error(message: string, error?: unknown): Promise { await this.writeToFile('error', message, error) this.logToConsole('error', message, error) } // 兼容原有的 logFloatingWindow 函数签名 - async log(message: string, error?: any): Promise { + async log(message: string, error?: unknown): Promise { if (error) { await this.error(message, error) } else { diff --git a/src/renderer/src/components/base/mihomo-icon.tsx b/src/renderer/src/components/base/mihomo-icon.tsx index 2c99ac8..4ba94c6 100644 --- a/src/renderer/src/components/base/mihomo-icon.tsx +++ b/src/renderer/src/components/base/mihomo-icon.tsx @@ -1,7 +1,7 @@ import React from 'react' -import { GenIcon } from 'react-icons' +import { GenIcon, IconBaseProps } from 'react-icons' -function MihomoIcon(props: any): React.JSX.Element { +function MihomoIcon(props: IconBaseProps): React.JSX.Element { return GenIcon({ tag: 'svg', attr: { viewBox: '0 0 58 61.53' }, diff --git a/src/renderer/src/components/connections/connection-table.tsx b/src/renderer/src/components/connections/connection-table.tsx index ac0d1ab..1c71e93 100644 --- a/src/renderer/src/components/connections/connection-table.tsx +++ b/src/renderer/src/components/connections/connection-table.tsx @@ -332,7 +332,7 @@ const ConnectionTable: React.FC = ({ const handleSort = useCallback( (columnKey: string) => { let newDirection: 'asc' | 'desc' = 'asc' - let newColumn = columnKey + const newColumn = columnKey if (sortColumn === columnKey) { newDirection = sortDirection === 'asc' ? 'desc' : 'asc' diff --git a/src/renderer/src/components/profiles/edit-info-modal.tsx b/src/renderer/src/components/profiles/edit-info-modal.tsx index 446a4b8..53f1ae4 100644 --- a/src/renderer/src/components/profiles/edit-info-modal.tsx +++ b/src/renderer/src/components/profiles/edit-info-modal.tsx @@ -140,7 +140,7 @@ const EditInfoModal: React.FC = (props) => { value={values.interval?.toString() ?? ''} onValueChange={(v) => { // 输入限制 - if (/^[\d\s*\-,\/]*$/.test(v)) { + if (/^[\d\s*\-,/]*$/.test(v)) { // 纯数字 if (/^\d+$/.test(v)) { setValues({ ...values, interval: parseInt(v, 10) || 0 }) diff --git a/src/renderer/src/components/profiles/edit-rules-modal.tsx b/src/renderer/src/components/profiles/edit-rules-modal.tsx index 2919668..6acebfe 100644 --- a/src/renderer/src/components/profiles/edit-rules-modal.tsx +++ b/src/renderer/src/components/profiles/edit-rules-modal.tsx @@ -411,17 +411,16 @@ interface RuleListItemProps { onRemove: (index: number) => void } -const RuleListItem = memo( - ({ - rule, - originalIndex, - isDeleted, - isPrependOrAppend, - rulesLength, - onMoveUp, - onMoveDown, - onRemove - }) => { +const RuleListItemBase: React.FC = ({ + rule, + originalIndex, + isDeleted, + isPrependOrAppend, + rulesLength, + onMoveUp, + onMoveDown, + onRemove +}) => { let bgColorClass = 'bg-content2' let textStyleClass = '' @@ -496,17 +495,19 @@ const RuleListItem = memo( ) - }, - (prevProps, nextProps) => { - return ( - prevProps.rule === nextProps.rule && - prevProps.originalIndex === nextProps.originalIndex && - prevProps.isDeleted === nextProps.isDeleted && - prevProps.isPrependOrAppend === nextProps.isPrependOrAppend && - prevProps.rulesLength === nextProps.rulesLength - ) - } -) +} + +const RuleListItem = memo(RuleListItemBase, (prevProps, nextProps) => { + return ( + prevProps.rule === nextProps.rule && + prevProps.originalIndex === nextProps.originalIndex && + prevProps.isDeleted === nextProps.isDeleted && + prevProps.isPrependOrAppend === nextProps.isPrependOrAppend && + prevProps.rulesLength === nextProps.rulesLength + ) +}) + +RuleListItem.displayName = 'RuleListItem' const EditRulesModal: React.FC = (props) => { const { id, onClose } = props @@ -563,7 +564,7 @@ const EditRulesModal: React.FC = (props) => { const content = await getProfileStr(id) setProfileContent(content) - const parsed = yaml.load(content) as any + const parsed = yaml.load(content) as Record | undefined let initialRules: RuleItem[] = [] if (parsed && parsed.rules && Array.isArray(parsed.rules)) { @@ -593,11 +594,19 @@ const EditRulesModal: React.FC = (props) => { // 添加代理组和代理名称 if (Array.isArray(parsed['proxy-groups'])) { - groups.push(...parsed['proxy-groups'].map((group: any) => group?.name).filter(Boolean)) + groups.push( + ...((parsed['proxy-groups'] as Array>) + .map((group) => (group && typeof group['name'] === 'string' ? (group['name'] as string) : '')) + .filter(Boolean) as string[]) + ) } if (Array.isArray(parsed['proxies'])) { - groups.push(...parsed['proxies'].map((proxy: any) => proxy?.name).filter(Boolean)) + groups.push( + ...((parsed['proxies'] as Array>) + .map((proxy) => (proxy && typeof proxy['name'] === 'string' ? (proxy['name'] as string) : '')) + .filter(Boolean) as string[]) + ) } // 预置出站 https://wiki.metacubex.one/config/proxies/built-in/ @@ -710,7 +719,7 @@ const EditRulesModal: React.FC = (props) => { } } catch (ruleError) { // 规则文件读取失败 - console.debug('规则文件读取失败:', ruleError) + console.debug('规则文件读取失败:', ruleError) setRules(initialRules) // 清空规则标记 setPrependRules(new Set()) diff --git a/src/renderer/src/components/proxies/proxy-item.tsx b/src/renderer/src/components/proxies/proxy-item.tsx index ed54203..083cba2 100644 --- a/src/renderer/src/components/proxies/proxy-item.tsx +++ b/src/renderer/src/components/proxies/proxy-item.tsx @@ -22,174 +22,170 @@ function delayColor(delay: number): 'primary' | 'success' | 'warning' | 'danger' return 'warning' } -const ProxyItem: React.FC = React.memo( - (props) => { - const { t } = useTranslation() - const { - mutateProxies, - proxyDisplayMode, - group, - proxy, - selected, - onSelect, - onProxyDelay, - isGroupTesting = false - } = props +const ProxyItemBase: React.FC = (props) => { + const { t } = useTranslation() + const { + mutateProxies, + proxyDisplayMode, + group, + proxy, + selected, + onSelect, + onProxyDelay, + isGroupTesting = false + } = props - const delay = useMemo(() => { - if (proxy.history.length > 0) { - return proxy.history[proxy.history.length - 1].delay - } - return -1 - }, [proxy.history]) + const delay = useMemo(() => { + if (proxy.history.length > 0) { + return proxy.history[proxy.history.length - 1].delay + } + return -1 + }, [proxy.history]) - const [loading, setLoading] = useState(false) + const [loading, setLoading] = useState(false) - const isLoading = loading || isGroupTesting + const isLoading = loading || isGroupTesting - const delayText = useMemo(() => { - if (delay === -1) return t('proxies.delay.test') - if (delay === 0) return t('proxies.delay.timeout') - return delay.toString() - }, [delay, t]) + const delayText = useMemo(() => { + if (delay === -1) return t('proxies.delay.test') + if (delay === 0) return t('proxies.delay.timeout') + return delay.toString() + }, [delay, t]) - const onDelay = useCallback((): void => { - setLoading(true) - onProxyDelay(proxy.name, group.testUrl).finally(() => { - mutateProxies() - setLoading(false) - }) - }, [proxy.name, group.testUrl, onProxyDelay, mutateProxies]) + const onDelay = useCallback((): void => { + setLoading(true) + onProxyDelay(proxy.name, group.testUrl).finally(() => { + mutateProxies() + setLoading(false) + }) + }, [proxy.name, group.testUrl, onProxyDelay, mutateProxies]) - const fixed = useMemo( - () => group.fixed && group.fixed === proxy.name, - [group.fixed, proxy.name] - ) + const fixed = useMemo(() => group.fixed && group.fixed === proxy.name, [group.fixed, proxy.name]) - return ( - onSelect(group.name, proxy.name)} - isPressable - fullWidth - shadow="sm" - className={`${ - fixed - ? 'bg-secondary/30 border-r-2 border-r-secondary border-l-2 border-l-secondary' - : selected - ? 'bg-primary/30 border-r-2 border-r-primary border-l-2 border-l-primary' - : 'bg-content2' - }`} - radius="sm" - > - - {proxyDisplayMode === 'full' ? ( -
-
-
-
- {proxy.name} -
-
- {fixed && ( - - )} -
-
-
-
- {proxy.type} -
- {['tfo', 'udp', 'xudp', 'mptcp', 'smux'].map( - (protocol) => - proxy[protocol as keyof IMihomoProxy] && ( -
- {protocol} -
- ) - )} -
- -
-
- ) : ( + return ( + onSelect(group.name, proxy.name)} + isPressable + fullWidth + shadow="sm" + className={`${ + fixed + ? 'bg-secondary/30 border-r-2 border-r-secondary border-l-2 border-l-secondary' + : selected + ? 'bg-primary/30 border-r-2 border-r-primary border-l-2 border-l-primary' + : 'bg-content2' + }`} + radius="sm" + > + + {proxyDisplayMode === 'full' ? ( +
{proxy.name}
-
- {fixed && ( - - )} + {fixed && ( + )} +
+
+
+
+ {proxy.type} +
+ {['tfo', 'udp', 'xudp', 'mptcp', 'smux'].map( + (protocol) => + proxy[protocol as keyof IMihomoProxy] && ( +
+ {protocol} +
+ ) + )} +
+ +
+
+ ) : ( +
+
+
+ {proxy.name}
- )} - - - ) - }, - (prevProps, nextProps) => { - // 必要时重新渲染 - return ( - prevProps.proxy.name === nextProps.proxy.name && - prevProps.proxy.history === nextProps.proxy.history && - prevProps.selected === nextProps.selected && - prevProps.proxyDisplayMode === nextProps.proxyDisplayMode && - prevProps.group.fixed === nextProps.group.fixed && - prevProps.isGroupTesting === nextProps.isGroupTesting - ) - } -) +
+ {fixed && ( + + )} + +
+
+ )} + + + ) +} + +const ProxyItem = React.memo(ProxyItemBase, (prevProps, nextProps) => { + // 必要时重新渲染 + return ( + prevProps.proxy.name === nextProps.proxy.name && + prevProps.proxy.history === nextProps.proxy.history && + prevProps.selected === nextProps.selected && + prevProps.proxyDisplayMode === nextProps.proxyDisplayMode && + prevProps.group.fixed === nextProps.group.fixed && + prevProps.isGroupTesting === nextProps.isGroupTesting + ) +}) ProxyItem.displayName = 'ProxyItem' diff --git a/src/renderer/src/components/settings/general-config.tsx b/src/renderer/src/components/settings/general-config.tsx index 545c446..b4d1ddc 100644 --- a/src/renderer/src/components/settings/general-config.tsx +++ b/src/renderer/src/components/settings/general-config.tsx @@ -208,7 +208,7 @@ const GeneralConfig: React.FC = () => { type="number" value={autoQuitWithoutCoreDelay.toString()} onValueChange={async (v: string) => { - let num = parseInt(v) + const num = parseInt(v) await patchAppConfig({ autoQuitWithoutCoreDelay: num }) }} onBlur={async (e) => { diff --git a/src/renderer/src/components/settings/mihomo-config.tsx b/src/renderer/src/components/settings/mihomo-config.tsx index 9c9b04a..7dd42f8 100644 --- a/src/renderer/src/components/settings/mihomo-config.tsx +++ b/src/renderer/src/components/settings/mihomo-config.tsx @@ -59,7 +59,7 @@ const MihomoConfig: React.FC = () => { type="number" value={(subscriptionTimeout / 1000)?.toString()} onValueChange={async (v: string) => { - let num = parseInt(v) + const num = parseInt(v) await patchAppConfig({ subscriptionTimeout: num * 1000 }) }} onBlur={async (e) => { diff --git a/src/renderer/src/hooks/use-profile-config.tsx b/src/renderer/src/hooks/use-profile-config.tsx index 4d9a2be..355d695 100644 --- a/src/renderer/src/hooks/use-profile-config.tsx +++ b/src/renderer/src/hooks/use-profile-config.tsx @@ -1,6 +1,5 @@ import React, { createContext, ReactNode, useContext } from 'react' import { showError } from '@renderer/utils/error-display' -import { toast } from '@renderer/components/base/toast' import useSWR from 'swr' import { addProfileItem as add, @@ -104,7 +103,7 @@ export const ProfileConfigProvider: React.FC<{ children: ReactNode }> = ({ child // 异步执行后台切换,不阻塞 UI await pendingTask.current } catch (e) { - const errorMsg = (e as any)?.message || String(e) + const errorMsg = (e as { message?: string })?.message || String(e) // 处理 IPC 超时错误 if (errorMsg.includes('reply was never sent')) { setTimeout(() => mutateProfileConfig(), 1000) diff --git a/src/renderer/src/locales/zh-CN.json b/src/renderer/src/locales/zh-CN.json index e92f6a3..e7fe1be 100644 --- a/src/renderer/src/locales/zh-CN.json +++ b/src/renderer/src/locales/zh-CN.json @@ -52,7 +52,7 @@ "settings.darkMode": "深色模式", "settings.lightMode": "浅色模式", "settings.autoStart": "开机自启", - "settings.autoStart.permissions": "正在以管理员权限配置计划任务,请在UAC对话框中点击'是'...", + "settings.autoStart.permissions": "正在以管理员权限配置计划任务,请在 UAC 对话框中点击'是'...", "settings.autoCheckUpdate": "自动检查更新", "settings.silentStart": "静默启动", "settings.autoQuitWithoutCore": "自动进入轻量模式", @@ -104,9 +104,9 @@ "settings.webui.secret": "密钥", "settings.webui.noSecret": "无密钥", "settings.webui.panelName": "面板名称", - "settings.webui.panelUrl": "面板URL", + "settings.webui.panelUrl": "面板 URL", "settings.webui.panelNamePlaceholder": "输入面板名称", - "settings.webui.panelUrlPlaceholder": "输入面板URL", + "settings.webui.panelUrlPlaceholder": "输入面板 URL", "settings.webui.variableHint": "可用变量", "settings.webui.addPanel": "添加面板", "settings.webui.panels": "面板列表", @@ -169,7 +169,7 @@ "mihomo.smartCoreStrategyStickySession": "粘性会话", "mihomo.smartCoreStrategyRoundRobin": "轮询", "mihomo.smartCoreUseLightGBMTooltip": "使用预训练的通用模型,可快速提升节点选择效果,但可能不适合您的特定网络环境", - "mihomo.smartCoreCollectDataTooltip": "收集您的网络使用数据,可用于训练更适合您的网络环境的自定义模型(如果您不懂如何训练模型,请关闭)", + "mihomo.smartCoreCollectDataTooltip": "收集您的网络使用数据,可用于训练更适合您的网络环境的自定义模型 (如果您不懂如何训练模型,请关闭)", "mihomo.smartCollectorSize": "数据收集文件大小(MB)", "mihomo.smartCollectorSizeTooltip": "限制数据收集文件大小,默认为 100MB", "mihomo.mixedPort": "混合端口", @@ -280,7 +280,7 @@ "sider.cards.override": "覆写", "sider.cards.connections": "连接", "sider.cards.core": "内核设置", - "sider.cards.dns": "DNS覆写", + "sider.cards.dns": "DNS 覆写", "sider.cards.sniff": "嗅探覆写", "sider.cards.logs": "日志", "sider.cards.substore": "Sub-Store", @@ -363,28 +363,28 @@ "sysproxy.pacEditor.title": "编辑 PAC 脚本", "sysproxy.bypass.title": "代理绕过", "sysproxy.bypass.addDefault": "添加默认代理绕过", - "sysproxy.bypass.placeholder": "例: *.baidu.com", + "sysproxy.bypass.placeholder": "例:*.baidu.com", "tun.title": "虚拟网卡", "tun.firewall.title": "重设防火墙", "tun.firewall.reset": "重设防火墙", "tun.core.title": "手动授权内核", "tun.core.auth": "手动授权内核", - "tun.dns.autoSet": "自动设置系统DNS", + "tun.dns.autoSet": "自动设置系统 DNS", "tun.stack.title": "Tun 模式堆栈", "tun.device.title": "Tun 网卡名称", "tun.strictRoute": "严格路由", "tun.autoRoute": "自动设置全局路由", - "tun.autoRedirect": "自动设置TCP重定向", + "tun.autoRedirect": "自动设置 TCP 重定向", "tun.autoDetectInterface": "自动选择流量出口接口", "tun.dnsHijack": "DNS 劫持", "tun.excludeAddress.title": "排除自定义网段", - "tun.excludeAddress.placeholder": "例: 172.20.0.0/16", + "tun.excludeAddress.placeholder": "例:172.20.0.0/16", "tun.notifications.coreAuthSuccess": "内核授权成功", "tun.notifications.firewallResetSuccess": "防火墙重设成功", "tun.permissions.title": "需要管理员权限", - "tun.permissions.message": "启用TUN模式需要管理员权限,是否现在重启应用获取权限?", + "tun.permissions.message": "启用 TUN 模式需要管理员权限,是否现在重启应用获取权限?", "tun.permissions.failed": "权限授权失败", - "tun.permissions.restarting": "正在以管理员权限重启应用,请在UAC对话框中点击'是'...", + "tun.permissions.restarting": "正在以管理员权限重启应用,请在 UAC 对话框中点击'是'...", "dns.title": "DNS 设置", "dns.enable": "启用 DNS", "dns.enhancedMode.title": "域名映射模式", @@ -456,8 +456,8 @@ "profiles.editInfo.intervalPlaceholder": "例如:30 或 '0 * * * *'", "profiles.editInfo.intervalInvalid": "不合法", "profiles.editInfo.intervalMinutes": "以分钟为单位的定时间隔", - "profiles.editInfo.intervalCron": "有效的Cron表达式", - "profiles.editInfo.intervalHint": "请输入数字或合法的Cron表达式(如:0 * * * *)", + "profiles.editInfo.intervalCron": "有效的 Cron 表达式", + "profiles.editInfo.intervalHint": "请输入数字或合法的 Cron 表达式(如:0 * * * *)", "profiles.editInfo.fixedInterval": "固定更新间隔", "profiles.editInfo.autoUpdate": "自动更新", "profiles.editInfo.override.title": "覆写", @@ -486,8 +486,8 @@ "profiles.editRules.noRules": "暂无规则", "profiles.editRules.noMatchingRules": "没有匹配的规则", "profiles.editRules.saveError": "保存规则时出错", - "profiles.editRules.noResolve": "跳过DNS解析 (no-resolve)", - "profiles.editRules.src": "匹配源IP (src)", + "profiles.editRules.noResolve": "跳过 DNS 解析 (no-resolve)", + "profiles.editRules.src": "匹配源 IP (src)", "profiles.openFile": "打开文件", "profiles.home": "主页", "profiles.notification.importSuccess": "订阅导入成功", @@ -513,8 +513,8 @@ "override.actions.newJs": "新建 JavaScript", "override.defaultContent.yaml": "# https://mihomo.party/docs/guide/override/yaml", "override.defaultContent.js": "// https://mihomo.party/docs/guide/override/javascript\nfunction main(config) {\n return config\n}", - "override.newFile.yaml": "新建YAML", - "override.newFile.js": "新建JS", + "override.newFile.yaml": "新建 YAML", + "override.newFile.js": "新建 JS", "override.editInfo.title": "编辑信息", "override.editInfo.name": "名称", "override.editInfo.url": "地址", @@ -552,22 +552,22 @@ "connections.detail.sniffHost": "嗅探主机", "connections.detail.processName": "进程名", "connections.detail.processPath": "进程路径", - "connections.detail.sourceIP": "来源IP", - "connections.detail.sourceGeoIP": "来源GeoIP", - "connections.detail.sourceASN": "来源ASN", - "connections.detail.destinationIP": "目标IP", - "connections.detail.destinationGeoIP": "目标GeoIP", - "connections.detail.destinationASN": "目标ASN", + "connections.detail.sourceIP": "来源 IP", + "connections.detail.sourceGeoIP": "来源 GeoIP", + "connections.detail.sourceASN": "来源 ASN", + "connections.detail.destinationIP": "目标 IP", + "connections.detail.destinationGeoIP": "目标 GeoIP", + "connections.detail.destinationASN": "目标 ASN", "connections.detail.sourcePort": "来源端口", "connections.detail.destinationPort": "目标端口", - "connections.detail.inboundIP": "入站IP", + "connections.detail.inboundIP": "入站 IP", "connections.detail.inboundPort": "入站端口", "connections.detail.copyRule": "复制规则", "connections.detail.inboundName": "入站名称", "connections.detail.inboundUser": "入站用户", "connections.detail.dscp": "DSCP", "connections.detail.remoteDestination": "远程目标", - "connections.detail.dnsMode": "DNS模式", + "connections.detail.dnsMode": "DNS 模式", "connections.detail.specialProxy": "特殊代理", "connections.detail.specialRules": "特殊规则", "connections.detail.close": "关闭", diff --git a/src/renderer/src/locales/zh-TW.json b/src/renderer/src/locales/zh-TW.json index def326a..e3a5939 100644 --- a/src/renderer/src/locales/zh-TW.json +++ b/src/renderer/src/locales/zh-TW.json @@ -52,7 +52,7 @@ "settings.darkMode": "深色模式", "settings.lightMode": "淺色模式", "settings.autoStart": "開機自啟", - "settings.autoStart.permissions": "正在以管理員權限配置計畫任務,請在UAC對話框中點擊'是'...", + "settings.autoStart.permissions": "正在以管理員權限配置計畫任務,請在 UAC 對話框中點擊'是'...", "settings.autoCheckUpdate": "自動檢查更新", "settings.silentStart": "靜默啟動", "settings.autoQuitWithoutCore": "自動進入輕量模式", @@ -104,9 +104,9 @@ "settings.webui.secret": "密鑰", "settings.webui.noSecret": "無密鑰", "settings.webui.panelName": "面板名稱", - "settings.webui.panelUrl": "面板URL", + "settings.webui.panelUrl": "面板 URL", "settings.webui.panelNamePlaceholder": "輸入面板名稱", - "settings.webui.panelUrlPlaceholder": "輸入面板URL", + "settings.webui.panelUrlPlaceholder": "輸入面板 URL", "settings.webui.variableHint": "可用變量", "settings.webui.addPanel": "添加面板", "settings.webui.panels": "面板列表", @@ -169,7 +169,7 @@ "mihomo.smartCoreStrategyStickySession": "粘性會話", "mihomo.smartCoreStrategyRoundRobin": "輪詢", "mihomo.smartCoreUseLightGBMTooltip": "使用預訓練的通用模型,可快速提升節點選擇效果,但可能不適合您的特定網絡環境", - "mihomo.smartCoreCollectDataTooltip": "收集您的網絡使用數據,可用於訓練更适合您的網絡環境的自定義模型(如果您不懂如何訓練模型,請關閉)", + "mihomo.smartCoreCollectDataTooltip": "收集您的網絡使用數據,可用於訓練更适合您的網絡環境的自定義模型 (如果您不懂如何訓練模型,請關閉)", "mihomo.smartCollectorSize": "數據收集文件大小(MB)", "mihomo.smartCollectorSizeTooltip": "限制數據收集文件大小,默認為 100MB", "mihomo.mixedPort": "混合埠", @@ -280,7 +280,7 @@ "sider.cards.override": "覆寫", "sider.cards.connections": "連接", "sider.cards.core": "內核設置", - "sider.cards.dns": "DNS覆寫", + "sider.cards.dns": "DNS 覆寫", "sider.cards.sniff": "嗅探覆寫", "sider.cards.logs": "日誌", "sider.cards.substore": "Sub-Store", @@ -363,28 +363,28 @@ "sysproxy.pacEditor.title": "編輯 PAC 腳本", "sysproxy.bypass.title": "代理繞過", "sysproxy.bypass.addDefault": "添加默認代理繞過", - "sysproxy.bypass.placeholder": "例: *.baidu.com", + "sysproxy.bypass.placeholder": "例:*.baidu.com", "tun.title": "虛擬網卡", "tun.firewall.title": "重設防火牆", "tun.firewall.reset": "重設防火牆", "tun.core.title": "手動授權內核", "tun.core.auth": "手動授權內核", - "tun.dns.autoSet": "自動設置系統DNS", + "tun.dns.autoSet": "自動設置系統 DNS", "tun.stack.title": "Tun 模式堆棧", "tun.device.title": "Tun 網卡名稱", "tun.strictRoute": "嚴格路由", "tun.autoRoute": "自動設置全局路由", - "tun.autoRedirect": "自動設置TCP重定向", + "tun.autoRedirect": "自動設置 TCP 重定向", "tun.autoDetectInterface": "自動選擇流量出口接口", "tun.dnsHijack": "DNS 劫持", "tun.excludeAddress.title": "排除自定義網段", - "tun.excludeAddress.placeholder": "例: 172.20.0.0/16", + "tun.excludeAddress.placeholder": "例:172.20.0.0/16", "tun.notifications.coreAuthSuccess": "內核授權成功", "tun.notifications.firewallResetSuccess": "防火牆重設成功", "tun.permissions.title": "需要系統管理員權限", - "tun.permissions.message": "啟用TUN模式需要系統管理員權限,是否現在重啟應用獲取權限?", + "tun.permissions.message": "啟用 TUN 模式需要系統管理員權限,是否現在重啟應用獲取權限?", "tun.permissions.failed": "權限授權失敗", - "tun.permissions.restarting": "正在以系統管理員權限重啟應用,請在UAC對話框中點擊'是'...", + "tun.permissions.restarting": "正在以系統管理員權限重啟應用,請在 UAC 對話框中點擊'是'...", "dns.title": "DNS 設置", "dns.enable": "啟用 DNS", "dns.enhancedMode.title": "域名映射模式", @@ -456,8 +456,8 @@ "profiles.editInfo.intervalPlaceholder": "例如:30 或 '0 * * * *'", "profiles.editInfo.intervalInvalid": "不合法", "profiles.editInfo.intervalMinutes": "以分鐘為單位的定時間隔", - "profiles.editInfo.intervalCron": "有效的Cron表達式", - "profiles.editInfo.intervalHint": "請輸入數字或合法的Cron表達式(如:0 * * * *)", + "profiles.editInfo.intervalCron": "有效的 Cron 表達式", + "profiles.editInfo.intervalHint": "請輸入數字或合法的 Cron 表達式(如:0 * * * *)", "profiles.editInfo.fixedInterval": "固定更新間隔", "profiles.editInfo.autoUpdate": "自動更新", "profiles.editInfo.override.title": "覆寫", @@ -486,8 +486,8 @@ "profiles.editRules.noRules": "暫無規則", "profiles.editRules.noMatchingRules": "沒有匹配的規則", "profiles.editRules.saveError": "保存規則時出錯", - "profiles.editRules.noResolve": "跳過DNS解析 (no-resolve)", - "profiles.editRules.src": "匹配源IP (src)", + "profiles.editRules.noResolve": "跳過 DNS 解析 (no-resolve)", + "profiles.editRules.src": "匹配源 IP (src)", "profiles.openFile": "打開檔案", "profiles.home": "主頁", "profiles.notification.importSuccess": "訂閱匯入成功", @@ -513,8 +513,8 @@ "override.actions.newJs": "新建 JavaScript", "override.defaultContent.yaml": "# https://mihomo.party/docs/guide/override/yaml", "override.defaultContent.js": "// https://mihomo.party/docs/guide/override/javascript\nfunction main(config) {\n return config\n}", - "override.newFile.yaml": "新建YAML", - "override.newFile.js": "新建JS", + "override.newFile.yaml": "新建 YAML", + "override.newFile.js": "新建 JS", "override.editInfo.title": "編輯信息", "override.editInfo.name": "名稱", "override.editInfo.url": "地址", @@ -552,22 +552,22 @@ "connections.detail.sniffHost": "嗅探主機", "connections.detail.processName": "進程名", "connections.detail.processPath": "進程路徑", - "connections.detail.sourceIP": "來源IP", - "connections.detail.sourceGeoIP": "來源GeoIP", - "connections.detail.sourceASN": "來源ASN", - "connections.detail.destinationIP": "目標IP", - "connections.detail.destinationGeoIP": "目標GeoIP", - "connections.detail.destinationASN": "目標ASN", + "connections.detail.sourceIP": "來源 IP", + "connections.detail.sourceGeoIP": "來源 GeoIP", + "connections.detail.sourceASN": "來源 ASN", + "connections.detail.destinationIP": "目標 IP", + "connections.detail.destinationGeoIP": "目標 GeoIP", + "connections.detail.destinationASN": "目標 ASN", "connections.detail.sourcePort": "來源埠", "connections.detail.destinationPort": "目標埠", - "connections.detail.inboundIP": "入站IP", + "connections.detail.inboundIP": "入站 IP", "connections.detail.inboundPort": "入站埠", "connections.detail.copyRule": "複製規則", "connections.detail.inboundName": "入站名稱", "connections.detail.inboundUser": "入站用戶", "connections.detail.dscp": "DSCP", "connections.detail.remoteDestination": "遠程目標", - "connections.detail.dnsMode": "DNS模式", + "connections.detail.dnsMode": "DNS 模式", "connections.detail.specialProxy": "特殊代理", "connections.detail.specialRules": "特殊規則", "connections.detail.close": "關閉", @@ -625,7 +625,7 @@ "guide.sysproxy.title": "系統代理", "guide.sysproxy.description": "匯入訂閱之後,內核已經開始運行並監聽指定埠,此时您已經可以通過指定代理埠来使用代理了,如果您要使大部分應用自動使用該埠的代理,您還需要打開系統代理開關", "guide.sysproxySetting.title": "系統代理設置", - "guide.sysproxySetting.description": "在此您可以進行系統代理相關設置,選擇代理模式,如果某些 Windows 應用不遵循系統代理,还可以使用\"UWP 工具\"解除本地回環限制,對於\"手動代理模式\"和\"PAC 代理模式\"的區別,请自行Google", + "guide.sysproxySetting.description": "在此您可以進行系統代理相關設置,選擇代理模式,如果某些 Windows 應用不遵循系統代理,还可以使用\"UWP 工具\"解除本地回環限制,對於\"手動代理模式\"和\"PAC 代理模式\"的區別,请自行 Google", "guide.tun.title": "虛擬網卡", "guide.tun.description": "虛擬網卡,即同類軟件中常見的\"Tun 模式\",對於某些不遵循系統代理的應用程式,您可以打開虛擬網卡以讓內核接管所有流量", "guide.tunSetting.title": "虛擬網卡設置", diff --git a/src/renderer/src/pages/dns.tsx b/src/renderer/src/pages/dns.tsx index a3a5656..0f6467b 100644 --- a/src/renderer/src/pages/dns.tsx +++ b/src/renderer/src/pages/dns.tsx @@ -146,7 +146,7 @@ const DNS: React.FC = () => { await restartCore() } } catch (e) { - showErrorSync(e, 'DNS配置保存失败') + showErrorSync(e, 'DNS 配置保存失败') } } diff --git a/src/renderer/src/pages/mihomo.tsx b/src/renderer/src/pages/mihomo.tsx index 30b0e8a..afb1387 100644 --- a/src/renderer/src/pages/mihomo.tsx +++ b/src/renderer/src/pages/mihomo.tsx @@ -130,7 +130,7 @@ const Mihomo: React.FC = () => { const [searchTerm, setSearchTerm] = useState('') const [refreshing, setRefreshing] = useState(false) - // WebUI管理状态 + // WebUI 管理状态 const [isWebUIModalOpen, setIsWebUIModalOpen] = useState(false) const [allPanels, setAllPanels] = useState([]) const [editingPanel, setEditingPanel] = useState(null) @@ -150,10 +150,10 @@ const Mihomo: React.FC = () => { const { host, port } = parseController() - // 生成随机端口(范围1024-65535) + // 生成随机端口 (范围 1024-65535) const generateRandomPort = () => Math.floor(Math.random() * (65535 - 1024 + 1)) + 1024 - // 默认WebUI面板选项 + // 默认 WebUI 面板选项 const defaultWebUIPanels: WebUIPanel[] = [ { id: 'metacubexd', @@ -185,14 +185,14 @@ const Mihomo: React.FC = () => { } }, []) - // 保存面板列表到localStorage + // 保存面板列表到 localStorage useEffect(() => { if (allPanels.length > 0) { localStorage.setItem('webui-panels', JSON.stringify(allPanels)) } }, [allPanels]) - // 在URL输入框光标处插入或替换变量 + // 在 URL 输入框光标处插入或替换变量 const insertVariableAtCursor = (variable: string) => { if (!urlInputRef.current) return @@ -216,7 +216,7 @@ const Mihomo: React.FC = () => { }, 0) } - // 打开WebUI面板 + // 打开 WebUI 面板 const openWebUI = (panel: WebUIPanel) => { const url = panel.url.replace('%host', host).replace('%port', port).replace('%secret', secret) window.open(url, '_blank') @@ -274,7 +274,7 @@ const Mihomo: React.FC = () => { setAllPanels(defaultWebUIPanels) } - // 用于高亮显示URL中的变量 + // 用于高亮显示 URL 中的变量 const HighlightedUrl: React.FC<{ url: string }> = ({ url }) => { const parts = url.split(/(%host|%port|%secret)/g) @@ -314,7 +314,7 @@ const Mihomo: React.FC = () => { await restartCore() } - const handleConfigChangeWithRestart = async (key: string, value: any) => { + const handleConfigChangeWithRestart = async (key: string, value: unknown) => { try { await patchAppConfig({ [key]: value }) await restartCore() @@ -328,14 +328,14 @@ const Mihomo: React.FC = () => { } } - // 获取GitHub标签列表(带缓存) + // 获取 GitHub 标签列表(带缓存) const fetchTags = async (forceRefresh = false) => { setLoadingTags(true) try { const data = await fetchMihomoTags(forceRefresh) setTags(Array.isArray(data) ? data : []) - } catch (error) { - console.error('Failed to fetch tags:', error) + } catch (error: unknown) { + console.error('Failed to fetch tags:', String(error)) setTags([]) toast.error(t('mihomo.error.fetchTagsFailed')) } finally { @@ -538,7 +538,7 @@ const Mihomo: React.FC = () => { | 'mihomo-alpha' | 'mihomo-smart' | 'mihomo-specific' - // 如果切换到特定版本但没有设置specificVersion,则打开选择模态框 + // 如果切换到特定版本但没有设置 specificVersion,则打开选择模态框 if (selectedCore === 'mihomo-specific' && !specificVersion) { handleOpenModal() } else { diff --git a/src/renderer/src/utils/init.ts b/src/renderer/src/utils/init.ts index 97b9412..c8d3994 100644 --- a/src/renderer/src/utils/init.ts +++ b/src/renderer/src/utils/init.ts @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ - import { getPlatform, getVersion } from './ipc' // const originError = console.error // const originWarn = console.warn diff --git a/src/renderer/src/utils/ipc.ts b/src/renderer/src/utils/ipc.ts index 0957d4e..a67e8f7 100644 --- a/src/renderer/src/utils/ipc.ts +++ b/src/renderer/src/utils/ipc.ts @@ -9,7 +9,7 @@ function ipcErrorWrapper(response: any): any { } } -// GitHub版本管理相关IPC调用 +// GitHub 版本管理相关 IPC 调用 export async function fetchMihomoTags( forceRefresh = false ): Promise<{ name: string; zipball_url: string; tarball_url: string }[]> { diff --git a/tsconfig.web.json b/tsconfig.web.json index b7ddbba..d2f6bbf 100644 --- a/tsconfig.web.json +++ b/tsconfig.web.json @@ -11,10 +11,10 @@ "compilerOptions": { "composite": true, "jsx": "react-jsx", - "baseUrl": ".", + "moduleResolution": "bundler", "paths": { "@renderer/*": [ - "src/renderer/src/*" + "./src/renderer/src/*" ] } }