chore: ensure ESLint passes and format code

This commit is contained in:
Memory 2026-01-25 15:28:03 +08:00 committed by GitHub
parent e9c72ce448
commit 842e7f1002
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 106 additions and 51 deletions

View File

@ -336,9 +336,11 @@ function getSysproxyNodeName() {
switch (platform) { switch (platform) {
case 'win32': case 'win32':
if (arch === 'x64') return isWin7Build ? 'sysproxy.win32-x64-msvc-win7.node' : 'sysproxy.win32-x64-msvc.node' if (arch === 'x64')
return isWin7Build ? 'sysproxy.win32-x64-msvc-win7.node' : 'sysproxy.win32-x64-msvc.node'
if (arch === 'arm64') return 'sysproxy.win32-arm64-msvc.node' if (arch === 'arm64') return 'sysproxy.win32-arm64-msvc.node'
if (arch === 'ia32') return isWin7Build ? 'sysproxy.win32-ia32-msvc-win7.node' : 'sysproxy.win32-ia32-msvc.node' if (arch === 'ia32')
return isWin7Build ? 'sysproxy.win32-ia32-msvc-win7.node' : 'sysproxy.win32-ia32-msvc.node'
break break
case 'darwin': case 'darwin':
if (arch === 'x64') return 'sysproxy.darwin-x64.node' if (arch === 'x64') return 'sysproxy.darwin-x64.node'

View File

@ -37,7 +37,10 @@ export async function getControledMihomoConfig(force = false): Promise<Partial<I
// 清理端口字段中的 NaN 值,恢复为默认值 // 清理端口字段中的 NaN 值,恢复为默认值
const portFields = ['mixed-port', 'socks-port', 'port', 'redir-port', 'tproxy-port'] as const const portFields = ['mixed-port', 'socks-port', 'port', 'redir-port', 'tproxy-port'] as const
for (const field of portFields) { for (const field of portFields) {
if (typeof controledMihomoConfig[field] !== 'number' || Number.isNaN(controledMihomoConfig[field])) { if (
typeof controledMihomoConfig[field] !== 'number' ||
Number.isNaN(controledMihomoConfig[field])
) {
controledMihomoConfig[field] = defaultControledMihomoConfig[field] controledMihomoConfig[field] = defaultControledMihomoConfig[field]
} }
} }

View File

@ -67,8 +67,7 @@ export async function changeCurrentProfile(id: string): Promise<void> {
// 使用队列确保 profile 切换串行执行,避免竞态条件 // 使用队列确保 profile 切换串行执行,避免竞态条件
let taskError: unknown = null let taskError: unknown = null
changeProfileQueue = changeProfileQueue changeProfileQueue = changeProfileQueue
.catch(() => { .catch(() => {})
})
.then(async () => { .then(async () => {
const { current } = await getProfileConfig() const { current } = await getProfileConfig()
if (current === id) return if (current === id) return
@ -221,7 +220,7 @@ export async function createProfile(item: Partial<IProfileItem>): Promise<IProfi
const newItem: IProfileItem = { const newItem: IProfileItem = {
id, id,
name: item.name || (item.type === 'remote' ? 'Remote File' : 'Local File'), name: item.name || (item.type === 'remote' ? 'Remote File' : 'Local File'),
type: item.type!, type: item.type || 'local',
url: item.url, url: item.url,
substore: item.substore || false, substore: item.substore || false,
interval: item.interval || 0, interval: item.interval || 0,
@ -255,18 +254,18 @@ export async function createProfile(item: Partial<IProfileItem>): Promise<IProfi
substore: newItem.substore || false substore: newItem.substore || false
} }
const fetchSub = (useProxy: boolean, timeout: number) => const fetchSub = (useProxy: boolean, timeout: number) =>
fetchAndValidateSubscription({ ...baseOptions, useProxy, timeout }) fetchAndValidateSubscription({ ...baseOptions, useProxy, timeout })
let result: FetchResult let result: FetchResult
if (newItem.useProxy || newItem.substore) { if (newItem.useProxy || newItem.substore) {
result = await fetchSub(newItem.useProxy!, userItemTimeoutMs) result = await fetchSub(Boolean(newItem.useProxy), userItemTimeoutMs)
} else { } else {
try { try {
result = await fetchSub(false, userItemTimeoutMs) result = await fetchSub(false, userItemTimeoutMs)
} catch (directError) { } catch (directError) {
try { try {
// smart fallback // smart fallback
result = await fetchSub(true, subscriptionTimeout) result = await fetchSub(true, subscriptionTimeout)
} catch { } catch {
throw directError throw directError

View File

@ -44,11 +44,7 @@ async function getOriginDNS(): Promise<void> {
async function setDNS(dns: string): Promise<void> { async function setDNS(dns: string): Promise<void> {
const service = await getDefaultService() const service = await getDefaultService()
try { try {
await axios.post( await axios.post('http://localhost/dns', { service, dns }, { socketPath: helperSocketPath })
'http://localhost/dns',
{ service, dns },
{ socketPath: helperSocketPath }
)
} catch { } catch {
// fallback to osascript if helper not available // fallback to osascript if helper not available
const shell = `networksetup -setdnsservers "${service}" ${dns}` const shell = `networksetup -setdnsservers "${service}" ${dns}`

View File

@ -135,10 +135,7 @@ interface CoreConfig {
// 准备核心配置 // 准备核心配置
async function prepareCore(detached: boolean, skipStop = false): Promise<CoreConfig> { async function prepareCore(detached: boolean, skipStop = false): Promise<CoreConfig> {
const [appConfig, mihomoConfig] = await Promise.all([ const [appConfig, mihomoConfig] = await Promise.all([getAppConfig(), getControledMihomoConfig()])
getAppConfig(),
getControledMihomoConfig()
])
const { const {
core = 'mihomo', core = 'mihomo',
@ -215,7 +212,10 @@ function spawnCoreProcess(config: CoreConfig): ChildProcess {
}) })
if (process.platform === 'win32' && proc.pid) { if (process.platform === 'win32' && proc.pid) {
os.setPriority(proc.pid, os.constants.priority[cpuPriority as keyof typeof os.constants.priority]) os.setPriority(
proc.pid,
os.constants.priority[cpuPriority as keyof typeof os.constants.priority]
)
} }
if (!detached) { if (!detached) {
@ -294,7 +294,10 @@ function setupCoreListeners(
new Promise((innerResolve) => { new Promise((innerResolve) => {
proc.stdout?.on('data', async (innerData) => { proc.stdout?.on('data', async (innerData) => {
if ( if (
innerData.toString().toLowerCase().includes('start initial compatible provider default') innerData
.toString()
.toLowerCase()
.includes('start initial compatible provider default')
) { ) {
try { try {
mainWindow?.webContents.send('groupsUpdated') mainWindow?.webContents.send('groupsUpdated')

View File

@ -191,7 +191,9 @@ async function checkHighPrivilegeMihomoProcess(): Promise<boolean> {
for (const executable of mihomoExecutables) { for (const executable of mihomoExecutables) {
try { try {
const { stdout } = await execPromise(`ps aux | grep ${executable} | grep -v grep`) const { stdout } = await execPromise(`ps aux | grep ${executable} | grep -v grep`)
const lines = stdout.split('\n').filter((line) => line.trim() && line.includes(executable)) const lines = stdout
.split('\n')
.filter((line) => line.trim() && line.includes(executable))
if (lines.length > 0) { if (lines.length > 0) {
foundProcesses = true foundProcesses = true
@ -337,7 +339,9 @@ export async function showErrorDialog(title: string, message: string): Promise<v
}) })
} }
export async function validateTunPermissionsOnStartup(_restartCore: () => Promise<void>): Promise<void> { export async function validateTunPermissionsOnStartup(
_restartCore: () => Promise<void>
): Promise<void> {
const { getControledMihomoConfig } = await import('../config') const { getControledMihomoConfig } = await import('../config')
const { tun } = await getControledMihomoConfig() const { tun } = await getControledMihomoConfig()
@ -349,7 +353,9 @@ export async function validateTunPermissionsOnStartup(_restartCore: () => Promis
if (!hasPermissions) { if (!hasPermissions) {
// 启动时没有权限,静默禁用 TUN不弹窗打扰用户 // 启动时没有权限,静默禁用 TUN不弹窗打扰用户
managerLogger.warn('TUN is enabled but insufficient permissions detected, auto-disabling TUN...') managerLogger.warn(
'TUN is enabled but insufficient permissions detected, auto-disabling TUN...'
)
await patchControledMihomoConfig({ tun: { enable: false } }) await patchControledMihomoConfig({ tun: { enable: false } })
const { mainWindow } = await import('../index') const { mainWindow } = await import('../index')

View File

@ -114,9 +114,7 @@ async function disableSysProxy(): Promise<void> {
await stopPacServer() await stopPacServer()
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
await helperRequest(() => await helperRequest(() => axios.get('http://localhost/off', { socketPath: helperSocketPath }))
axios.get('http://localhost/off', { socketPath: helperSocketPath })
)
} else { } else {
// Windows / Linux 直接使用 sysproxy-rs // Windows / Linux 直接使用 sysproxy-rs
try { try {

View File

@ -1,8 +1,8 @@
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
import { spawnSync } from 'child_process'
import plist from 'plist' import plist from 'plist'
import { findBestAppPath, isIOSApp } from './icon' import { findBestAppPath, isIOSApp } from './icon'
import { spawnSync } from 'child_process'
export async function getAppName(appPath: string): Promise<string> { export async function getAppName(appPath: string): Promise<string> {
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
@ -20,7 +20,7 @@ export async function getAppName(appPath: string): Promise<string> {
try { try {
const appName = getLocalizedAppName(targetPath) const appName = getLocalizedAppName(targetPath)
if (appName) return appName if (appName) return appName
} catch (err) { } catch {
// ignore // ignore
} }
@ -33,7 +33,7 @@ export async function getAppName(appPath: string): Promise<string> {
} else { } else {
// ignore // ignore
} }
} catch (err) { } catch {
// ignore // ignore
} }
} }

View File

@ -74,8 +74,7 @@ export async function request<T = unknown>(
} }
} }
const cleanup = (): void => { const cleanup = (): void => {}
}
setupProxy() setupProxy()
.then(() => { .then(() => {

View File

@ -1,4 +1,4 @@
import { createWriteStream, createReadStream , existsSync, rmSync } from 'fs' import { createWriteStream, createReadStream, existsSync, rmSync } from 'fs'
import { writeFile } from 'fs/promises' import { writeFile } from 'fs/promises'
import { execSync } from 'child_process' import { execSync } from 'child_process'
import { platform } from 'os' import { platform } from 'os'

View File

@ -190,7 +190,10 @@ async function initFiles(): Promise<void> {
} catch (error: unknown) { } catch (error: unknown) {
const code = (error as NodeJS.ErrnoException).code const code = (error as NodeJS.ErrnoException).code
// 文件被占用或权限问题,如果目标已存在则跳过 // 文件被占用或权限问题,如果目标已存在则跳过
if ((code === 'EPERM' || code === 'EBUSY' || code === 'EACCES') && existsSync(targetPath)) { if (
(code === 'EPERM' || code === 'EBUSY' || code === 'EACCES') &&
existsSync(targetPath)
) {
await initLogger.warn(`Skipping ${file}: file is in use or permission denied`) await initLogger.warn(`Skipping ${file}: file is in use or permission denied`)
return return
} }
@ -318,7 +321,11 @@ async function migrateMihomoConfig(): Promise<void> {
config['skip-auth-prefixes'][0] === '127.0.0.1/32' && config['skip-auth-prefixes'][0] === '127.0.0.1/32' &&
!config['skip-auth-prefixes'].includes('::1/128') !config['skip-auth-prefixes'].includes('::1/128')
) { ) {
patches['skip-auth-prefixes'] = ['127.0.0.1/32', '::1/128', ...config['skip-auth-prefixes'].slice(1)] patches['skip-auth-prefixes'] = [
'127.0.0.1/32',
'::1/128',
...config['skip-auth-prefixes'].slice(1)
]
} }
// 其他默认值 // 其他默认值

View File

@ -12,7 +12,12 @@ export let mainWindow: BrowserWindow | null = null
let quitTimeout: NodeJS.Timeout | null = null let quitTimeout: NodeJS.Timeout | null = null
export async function createWindow(): Promise<void> { export async function createWindow(): Promise<void> {
const { useWindowFrame = false, silentStart = false, autoQuitWithoutCore = false, autoQuitWithoutCoreDelay = 60 } = await getAppConfig() const {
useWindowFrame = false,
silentStart = false,
autoQuitWithoutCore = false,
autoQuitWithoutCoreDelay = 60
} = await getAppConfig()
const mainWindowState = windowStateKeeper({ const mainWindowState = windowStateKeeper({
defaultWidth: 800, defaultWidth: 800,
defaultHeight: 600, defaultHeight: 600,
@ -47,7 +52,11 @@ export async function createWindow(): Promise<void> {
}) })
mainWindowState.manage(mainWindow) mainWindowState.manage(mainWindow)
setupWindowEvents(mainWindow, mainWindowState, { silentStart, autoQuitWithoutCore, autoQuitWithoutCoreDelay }) setupWindowEvents(mainWindow, mainWindowState, {
silentStart,
autoQuitWithoutCore,
autoQuitWithoutCoreDelay
})
if (is.dev) { if (is.dev) {
mainWindow.webContents.openDevTools() mainWindow.webContents.openDevTools()

View File

@ -34,8 +34,10 @@ function getBindingName() {
const win7 = isWindows7() const win7 = isWindows7()
switch (platform) { switch (platform) {
case 'win32': case 'win32':
if (arch === 'x64') return win7 ? 'sysproxy.win32-x64-msvc-win7.node' : 'sysproxy.win32-x64-msvc.node' if (arch === 'x64')
if (arch === 'ia32') return win7 ? 'sysproxy.win32-ia32-msvc-win7.node' : 'sysproxy.win32-ia32-msvc.node' return win7 ? 'sysproxy.win32-x64-msvc-win7.node' : 'sysproxy.win32-x64-msvc.node'
if (arch === 'ia32')
return win7 ? 'sysproxy.win32-ia32-msvc-win7.node' : 'sysproxy.win32-ia32-msvc.node'
if (arch === 'arm64') return 'sysproxy.win32-arm64-msvc.node' if (arch === 'arm64') return 'sysproxy.win32-arm64-msvc.node'
break break
case 'darwin': case 'darwin':

View File

@ -169,4 +169,4 @@ const ConnectionItem = memo(ConnectionItemComponent, (prevProps, nextProps) => {
) )
}) })
export default ConnectionItem export default ConnectionItem

View File

@ -126,7 +126,9 @@ const ProxyProvider: React.FC = () => {
<Button <Button
isIconOnly isIconOnly
title={ title={
provider.vehicleType === 'File' ? t('common.editor.edit') : t('common.viewer.view') provider.vehicleType === 'File'
? t('common.editor.edit')
: t('common.viewer.view')
} }
className="ml-2" className="ml-2"
size="sm" size="sm"

View File

@ -138,7 +138,9 @@ const RuleProvider: React.FC = () => {
<Button <Button
isIconOnly isIconOnly
title={ title={
provider.vehicleType === 'File' ? t('common.editor.edit') : t('common.viewer.view') provider.vehicleType === 'File'
? t('common.editor.edit')
: t('common.viewer.view')
} }
className="ml-2" className="ml-2"
size="sm" size="sm"

View File

@ -53,12 +53,14 @@ const OverrideConfigContextWrapper: React.FC<{ children: ReactNode }> = ({ child
) )
const setOverrideConfig = useCallback( const setOverrideConfig = useCallback(
(cfg: IOverrideConfig) => withErrorHandling(() => set(cfg), 'common.error.saveOverrideConfigFailed')(), (cfg: IOverrideConfig) =>
withErrorHandling(() => set(cfg), 'common.error.saveOverrideConfigFailed')(),
[withErrorHandling] [withErrorHandling]
) )
const addOverrideItem = useCallback( const addOverrideItem = useCallback(
(item: Partial<IOverrideItem>) => withErrorHandling(() => add(item), 'common.error.addOverrideFailed')(), (item: Partial<IOverrideItem>) =>
withErrorHandling(() => add(item), 'common.error.addOverrideFailed')(),
[withErrorHandling] [withErrorHandling]
) )
@ -68,7 +70,8 @@ const OverrideConfigContextWrapper: React.FC<{ children: ReactNode }> = ({ child
) )
const updateOverrideItem = useCallback( const updateOverrideItem = useCallback(
(item: IOverrideItem) => withErrorHandling(() => update(item), 'common.error.updateOverrideFailed')(), (item: IOverrideItem) =>
withErrorHandling(() => update(item), 'common.error.updateOverrideFailed')(),
[withErrorHandling] [withErrorHandling]
) )

View File

@ -1,7 +1,25 @@
import BasePage from '@renderer/components/base/base-page' import BasePage from '@renderer/components/base/base-page'
import { mihomoCloseAllConnections, mihomoCloseConnection, getIconDataURL, getAppName } from '@renderer/utils/ipc' import {
mihomoCloseAllConnections,
mihomoCloseConnection,
getIconDataURL,
getAppName
} from '@renderer/utils/ipc'
import { Key, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { Key, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Badge, Button, Divider, Input, Select, SelectItem, Tab, Tabs , Dropdown, DropdownTrigger, DropdownMenu, DropdownItem } from '@heroui/react' import {
Badge,
Button,
Divider,
Input,
Select,
SelectItem,
Tab,
Tabs,
Dropdown,
DropdownTrigger,
DropdownMenu,
DropdownItem
} from '@heroui/react'
import { calcTraffic } from '@renderer/utils/calc' import { calcTraffic } from '@renderer/utils/calc'
import ConnectionItem from '@renderer/components/connections/connection-item' import ConnectionItem from '@renderer/components/connections/connection-item'
import ConnectionTable from '@renderer/components/connections/connection-table' import ConnectionTable from '@renderer/components/connections/connection-table'
@ -239,7 +257,7 @@ const Connections: React.FC = () => {
: `data:image/png;base64,${rawBase64}` : `data:image/png;base64,${rawBase64}`
let processedDataURL = fullDataURL let processedDataURL = fullDataURL
if (platform != 'darwin') { if (platform !== 'darwin') {
processedDataURL = await cropAndPadTransparent(fullDataURL) processedDataURL = await cropAndPadTransparent(fullDataURL)
} }

View File

@ -78,4 +78,4 @@ function clearHalfIconCache(): void {
} catch { } catch {
// ignore // ignore
} }
} }

View File

@ -13,7 +13,10 @@ export async function cropAndPadTransparent(
const canvas = document.createElement('canvas') const canvas = document.createElement('canvas')
canvas.width = img.width canvas.width = img.width
canvas.height = img.height canvas.height = img.height
const ctx = canvas.getContext('2d')! const ctx = canvas.getContext('2d')
if (!ctx) {
throw new Error('Failed to get 2D context')
}
ctx.clearRect(0, 0, canvas.width, canvas.height) ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.drawImage(img, 0, 0) ctx.drawImage(img, 0, 0)
@ -59,7 +62,10 @@ export async function cropAndPadTransparent(
const outCanvas = document.createElement('canvas') const outCanvas = document.createElement('canvas')
outCanvas.width = finalSize outCanvas.width = finalSize
outCanvas.height = finalSize outCanvas.height = finalSize
const outCtx = outCanvas.getContext('2d')! const outCtx = outCanvas.getContext('2d')
if (!outCtx) {
throw new Error('Failed to get 2D context')
}
outCtx.clearRect(0, 0, finalSize, finalSize) outCtx.clearRect(0, 0, finalSize, finalSize)
outCtx.drawImage( outCtx.drawImage(
canvas, canvas,
@ -74,4 +80,4 @@ export async function cropAndPadTransparent(
) )
return outCanvas.toDataURL('image/png') return outCanvas.toDataURL('image/png')
} }