fix format and lint

This commit is contained in:
xishang0128 2025-12-25 15:32:32 +08:00
parent b7d6ea8e7a
commit 54bb819e28
No known key found for this signature in database
GPG Key ID: 02DAB1BEA331D06C
39 changed files with 387 additions and 352 deletions

View File

@ -1,4 +0,0 @@
node_modules
dist
out
.gitignore

View File

@ -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'
]
}

48
eslint.config.cjs Normal file
View File

@ -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'
}
}
]

View File

@ -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'

View File

@ -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()

View File

@ -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()

View File

@ -49,7 +49,7 @@ export async function patchControledMihomoConfig(patch: Partial<IMihomoConfig>):
// 从不接管状态恢复
if (controlDns) {
// 确保DNS配置包含所有必要的默认字段特别是新增的fallback等
// 确保 DNS 配置包含所有必要的默认字段,特别是新增的 fallback
controledMihomoConfig.dns = deepMerge(
defaultControledMihomoConfig.dns || {},
controledMihomoConfig.dns || {}

View File

@ -78,7 +78,7 @@ export async function createOverride(item: Partial<IOverrideItem>): Promise<IOve
},
responseType: 'text'
})
const data = res.data
const data = res.data as string
await setOverride(id, newItem.ext, data)
break
}

View File

@ -418,7 +418,9 @@ export async function convertMrsRuleset(filePath: string, behavior: string): Pro
} catch (error) {
try {
await unlink(tempFilePath)
} catch {}
} catch {
// ignore
}
throw error
}
}

View File

@ -28,7 +28,7 @@ let runtimeConfig: IMihomoConfig
// 辅助函数:处理带偏移量的规则
function processRulesWithOffset(ruleStrings: string[], currentRules: string[], isAppend = false) {
const normalRules: string[] = []
let rules = [...currentRules]
const rules = [...currentRules]
ruleStrings.forEach((ruleStr) => {
const parts = ruleStr.split(',')
@ -65,7 +65,7 @@ export async function generateProfile(): Promise<void> {
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<void> {
}
}
} catch (error) {
console.error('读取或应用规则文件时出错:', error)
console.error('读取或应用规则文件时出错', error)
}
const profile = deepMerge(currentProfile, controledMihomoConfig)

View File

@ -330,8 +330,8 @@ async function cleanupWindowsNamedPipes(): Promise<void> {
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<void> {
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<boolean> {
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<boolean> {
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<boolean> {
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<void> {
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<boolean> {
try {
if (process.platform === 'win32') {
// Windows权限检查
// Windows 权限检查
return await checkAdminPrivileges()
}
@ -834,7 +835,9 @@ async function checkHighPrivilegeMihomoProcess(): Promise<boolean> {
}
}
}
} catch (error) {}
} catch (error) {
// ignore
}
}
if (!foundProcesses) {
@ -851,7 +854,7 @@ async function checkHighPrivilegeMihomoProcess(): Promise<boolean> {
return false
}
// TUN模式获取权限
// TUN 模式获取权限
export async function requestTunPermissions(): Promise<void> {
if (process.platform === 'win32') {
await restartAsAdmin()
@ -894,7 +897,7 @@ export async function checkAdminRestartForTun(): Promise<void> {
await managerLogger.error('Failed to auto-enable TUN after admin restart', error)
}
} else {
// 检查TUN配置与权限的匹配但不自动开启 TUN
// 检查 TUN 配置与权限的匹配,但不自动开启 TUN
await validateTunPermissionsOnStartup()
}
}

View File

@ -10,7 +10,7 @@ export async function initProfileUpdater(): Promise<void> {
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<void> {
item.interval * 60 * 1000
)
} else if (typeof item.interval === 'string') {
// 字符串间隔使用Cron
// 字符串间隔使用 Cron
intervalPool[item.id] = new Cron(item.interval, async () => {
try {
await addProfileItem(item)

View File

@ -5,13 +5,13 @@ import { getAppConfig } from '../config'
export async function subStoreSubs(): Promise<ISubStoreSub[]> {
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<ISubStoreSub[]> {
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
}

View File

@ -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()
})

View File

@ -26,7 +26,7 @@ export async function checkUpdate(): Promise<IAppVersion | undefined> {
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<void> {
'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<void> {
} 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')

View File

@ -155,7 +155,7 @@ export async function webdavDelete(filename: string): Promise<void> {
}
/**
* WebDAV定时备份任务
* WebDAV
*/
export async function initWebdavBackupScheduler(): Promise<void> {
try {
@ -167,7 +167,7 @@ export async function initWebdavBackupScheduler(): Promise<void> {
const { webdavBackupCron } = await getAppConfig()
// 如果配置了Cron表达式则启动定时任务
// 如果配置了 Cron 表达式,则启动定时任务
if (webdavBackupCron) {
backupCronJob = new Cron(webdavBackupCron, async () => {
try {
@ -189,7 +189,7 @@ export async function initWebdavBackupScheduler(): Promise<void> {
}
/**
* WebDAV定时备份任务
* WebDAV
*/
export async function stopWebdavBackupScheduler(): Promise<void> {
if (backupCronJob) {
@ -200,7 +200,7 @@ export async function stopWebdavBackupScheduler(): Promise<void> {
}
/**
* WebDAV定时备份任务
* WebDAV
*
*/
export async function reinitScheduler(): Promise<void> {

View File

@ -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(() => {})
}

View File

@ -395,7 +395,7 @@ export async function createTray(): Promise<void> {
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<void> {
tray.setImage(iconPath)
}
} catch (error) {
console.error('更新托盘图标失败:', error)
console.error('更新托盘图标失败', error)
}
}

View File

@ -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<void> {
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)

View File

@ -17,7 +17,7 @@ export interface RequestOptions {
maxRedirects?: number
}
export interface Response<T = any> {
export interface Response<T = unknown> {
data: T
status: number
statusText: string
@ -29,7 +29,7 @@ export interface Response<T = any> {
* 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<T = any>(
export async function request<T = unknown>(
url: string,
options: RequestOptions = {}
): Promise<Response<T>> {
@ -45,7 +45,7 @@ export async function request<T = any>(
} = 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<T = any>(
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<T = any>(
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<T = any>(
}
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<T = any>(
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<T = any>(
/**
* Convenience method for GET requests
*/
export const get = <T = any>(
export const get = <T = unknown>(
url: string,
options?: Omit<RequestOptions, 'method' | 'body'>
): Promise<Response<T>> => request<T>(url, { ...options, method: 'GET' })
@ -200,9 +200,9 @@ export const get = <T = any>(
/**
* Convenience method for POST requests
*/
export const post = <T = any>(
export const post = <T = unknown>(
url: string,
data: any,
data: unknown,
options?: Omit<RequestOptions, 'method' | 'body'>
): Promise<Response<T>> => {
const body = typeof data === 'string' ? data : JSON.stringify(data)
@ -216,9 +216,9 @@ export const post = <T = any>(
/**
* Convenience method for PUT requests
*/
export const put = <T = any>(
export const put = <T = unknown>(
url: string,
data: any,
data: unknown,
options?: Omit<RequestOptions, 'method' | 'body'>
): Promise<Response<T>> => {
const body = typeof data === 'string' ? data : JSON.stringify(data)
@ -232,7 +232,7 @@ export const put = <T = any>(
/**
* Convenience method for DELETE requests
*/
export const del = <T = any>(
export const del = <T = unknown>(
url: string,
options?: Omit<RequestOptions, 'method' | 'body'>
): Promise<Response<T>> => request<T>(url, { ...options, method: 'DELETE' })
@ -240,9 +240,9 @@ export const del = <T = any>(
/**
* Convenience method for PATCH requests
*/
export const patch = <T = any>(
export const patch = <T = unknown>(
url: string,
data: any,
data: unknown,
options?: Omit<RequestOptions, 'method' | 'body'>
): Promise<Response<T>> => {
const body = typeof data === 'string' ? data : JSON.stringify(data)

View File

@ -42,7 +42,7 @@ const PLATFORM_MAP: Record<string, string> = {
const versionCache = new Map<string, VersionCache>()
/**
* 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<void> {
@ -133,7 +133,7 @@ async function downloadGitHubAsset(url: string, outputPath: string): Promise<voi
}
/**
* mihomo核心
* mihomo
* @param version
*/
export async function installMihomoCore(version: string): Promise<void> {
@ -141,9 +141,9 @@ export async function installMihomoCore(version: string): Promise<void> {
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<void> {
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)

View File

@ -84,7 +84,7 @@ async function fixDataDirPermissions(): Promise<void> {
}
}
// 比较修改geodata文件修改时间
// 比较修改 geodata 文件修改时间
async function isSourceNewer(sourcePath: string, targetPath: string): Promise<boolean> {
try {
const sourceStats = await stat(sourcePath)

View File

@ -147,7 +147,7 @@ function ipcErrorWrapper<T>( // 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))
}

View File

@ -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<void> {
private async writeToFile(level: LogLevel, message: string, error?: unknown): Promise<void> {
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<void> {
async debug(message: string, error?: unknown): Promise<void> {
await this.writeToFile('debug', message, error)
this.logToConsole('debug', message, error)
}
async info(message: string, error?: any): Promise<void> {
async info(message: string, error?: unknown): Promise<void> {
await this.writeToFile('info', message, error)
this.logToConsole('info', message, error)
}
async warn(message: string, error?: any): Promise<void> {
async warn(message: string, error?: unknown): Promise<void> {
await this.writeToFile('warn', message, error)
this.logToConsole('warn', message, error)
}
async error(message: string, error?: any): Promise<void> {
async error(message: string, error?: unknown): Promise<void> {
await this.writeToFile('error', message, error)
this.logToConsole('error', message, error)
}
// 兼容原有的 logFloatingWindow 函数签名
async log(message: string, error?: any): Promise<void> {
async log(message: string, error?: unknown): Promise<void> {
if (error) {
await this.error(message, error)
} else {

View File

@ -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' },

View File

@ -332,7 +332,7 @@ const ConnectionTable: React.FC<Props> = ({
const handleSort = useCallback(
(columnKey: string) => {
let newDirection: 'asc' | 'desc' = 'asc'
let newColumn = columnKey
const newColumn = columnKey
if (sortColumn === columnKey) {
newDirection = sortDirection === 'asc' ? 'desc' : 'asc'

View File

@ -140,7 +140,7 @@ const EditInfoModal: React.FC<Props> = (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 })

View File

@ -411,8 +411,7 @@ interface RuleListItemProps {
onRemove: (index: number) => void
}
const RuleListItem = memo<RuleListItemProps>(
({
const RuleListItemBase: React.FC<RuleListItemProps> = ({
rule,
originalIndex,
isDeleted,
@ -421,7 +420,7 @@ const RuleListItem = memo<RuleListItemProps>(
onMoveUp,
onMoveDown,
onRemove
}) => {
}) => {
let bgColorClass = 'bg-content2'
let textStyleClass = ''
@ -496,8 +495,9 @@ const RuleListItem = memo<RuleListItemProps>(
</div>
</div>
)
},
(prevProps, nextProps) => {
}
const RuleListItem = memo(RuleListItemBase, (prevProps, nextProps) => {
return (
prevProps.rule === nextProps.rule &&
prevProps.originalIndex === nextProps.originalIndex &&
@ -505,8 +505,9 @@ const RuleListItem = memo<RuleListItemProps>(
prevProps.isPrependOrAppend === nextProps.isPrependOrAppend &&
prevProps.rulesLength === nextProps.rulesLength
)
}
)
})
RuleListItem.displayName = 'RuleListItem'
const EditRulesModal: React.FC<Props> = (props) => {
const { id, onClose } = props
@ -563,7 +564,7 @@ const EditRulesModal: React.FC<Props> = (props) => {
const content = await getProfileStr(id)
setProfileContent(content)
const parsed = yaml.load(content) as any
const parsed = yaml.load(content) as Record<string, unknown> | undefined
let initialRules: RuleItem[] = []
if (parsed && parsed.rules && Array.isArray(parsed.rules)) {
@ -593,11 +594,19 @@ const EditRulesModal: React.FC<Props> = (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<Record<string, unknown>>)
.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<Record<string, unknown>>)
.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> = (props) => {
}
} catch (ruleError) {
// 规则文件读取失败
console.debug('规则文件读取失败:', ruleError)
console.debug('规则文件读取失败', ruleError)
setRules(initialRules)
// 清空规则标记
setPrependRules(new Set())

View File

@ -22,8 +22,7 @@ function delayColor(delay: number): 'primary' | 'success' | 'warning' | 'danger'
return 'warning'
}
const ProxyItem: React.FC<Props> = React.memo(
(props) => {
const ProxyItemBase: React.FC<Props> = (props) => {
const { t } = useTranslation()
const {
mutateProxies,
@ -61,10 +60,7 @@ const ProxyItem: React.FC<Props> = React.memo(
})
}, [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 (
<Card
@ -177,8 +173,9 @@ const ProxyItem: React.FC<Props> = React.memo(
</CardBody>
</Card>
)
},
(prevProps, nextProps) => {
}
const ProxyItem = React.memo(ProxyItemBase, (prevProps, nextProps) => {
// 必要时重新渲染
return (
prevProps.proxy.name === nextProps.proxy.name &&
@ -188,8 +185,7 @@ const ProxyItem: React.FC<Props> = React.memo(
prevProps.group.fixed === nextProps.group.fixed &&
prevProps.isGroupTesting === nextProps.isGroupTesting
)
}
)
})
ProxyItem.displayName = 'ProxyItem'

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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)

View File

@ -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": "关闭",

View File

@ -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": "虛擬網卡設置",

View File

@ -146,7 +146,7 @@ const DNS: React.FC = () => {
await restartCore()
}
} catch (e) {
showErrorSync(e, 'DNS配置保存失败')
showErrorSync(e, 'DNS 配置保存失败')
}
}

View File

@ -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<WebUIPanel[]>([])
const [editingPanel, setEditingPanel] = useState<WebUIPanel | null>(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 {

View File

@ -1,5 +1,3 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { getPlatform, getVersion } from './ipc'
// const originError = console.error
// const originWarn = console.warn

View File

@ -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 }[]> {

View File

@ -11,10 +11,10 @@
"compilerOptions": {
"composite": true,
"jsx": "react-jsx",
"baseUrl": ".",
"moduleResolution": "bundler",
"paths": {
"@renderer/*": [
"src/renderer/src/*"
"./src/renderer/src/*"
]
}
}