Compare commits

..

No commits in common. "45484ffff2c742f7a1594bbaf231a1849bafa05c" and "d6f0d30f9ae79245fc8ce7ad593e79841cc3f2f1" have entirely different histories.

21 changed files with 134 additions and 251 deletions

View File

@ -23,7 +23,6 @@ package() {
chmod +x ${pkgdir}/opt/mihomo-party/mihomo-party chmod +x ${pkgdir}/opt/mihomo-party/mihomo-party
chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo
chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo-alpha chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo-alpha
chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo-smart
install -Dm755 "${srcdir}/${_pkgname}.sh" "${pkgdir}/usr/bin/${_pkgname}" install -Dm755 "${srcdir}/${_pkgname}.sh" "${pkgdir}/usr/bin/${_pkgname}"
sed -i '3s!/opt/mihomo-party/mihomo-party!mihomo-party!' "${pkgdir}/usr/share/applications/${_pkgname}.desktop" sed -i '3s!/opt/mihomo-party/mihomo-party!mihomo-party!' "${pkgdir}/usr/share/applications/${_pkgname}.desktop"

View File

@ -29,7 +29,6 @@ package() {
cp -r $srcdir/opt/mihomo-party/resources/files ${pkgdir}/opt/mihomo-party/resources/ cp -r $srcdir/opt/mihomo-party/resources/files ${pkgdir}/opt/mihomo-party/resources/
chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo
chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo-alpha chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo-alpha
chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo-smart
install -Dm755 "${srcdir}/${_pkgname}.sh" "${pkgdir}/usr/bin/${_pkgname}" install -Dm755 "${srcdir}/${_pkgname}.sh" "${pkgdir}/usr/bin/${_pkgname}"
install -Dm644 "${_pkgname}.desktop" "${pkgdir}/usr/share/applications/${_pkgname}.desktop" install -Dm644 "${_pkgname}.desktop" "${pkgdir}/usr/share/applications/${_pkgname}.desktop"
install -Dm644 "${pkgdir}/opt/mihomo-party/resources/icon.png" "${pkgdir}/usr/share/icons/hicolor/512x512/apps/${_pkgname}.png" install -Dm644 "${pkgdir}/opt/mihomo-party/resources/icon.png" "${pkgdir}/usr/share/icons/hicolor/512x512/apps/${_pkgname}.png"

View File

@ -39,7 +39,6 @@ package() {
cp -r $srcdir/${_pkgname}-${pkgver}/extra/files ${pkgdir}/opt/mihomo-party/resources/ cp -r $srcdir/${_pkgname}-${pkgver}/extra/files ${pkgdir}/opt/mihomo-party/resources/
chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo
chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo-alpha chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo-alpha
chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo-smart
install -Dm755 "${_pkgname}.sh" "${pkgdir}/usr/bin/${_pkgname}" install -Dm755 "${_pkgname}.sh" "${pkgdir}/usr/bin/${_pkgname}"
install -Dm644 "${_pkgname}.desktop" "${pkgdir}/usr/share/applications/${_pkgname}.desktop" install -Dm644 "${_pkgname}.desktop" "${pkgdir}/usr/share/applications/${_pkgname}.desktop"
install -Dm644 "${pkgdir}/opt/mihomo-party/resources/icon.png" "${pkgdir}/usr/share/icons/hicolor/512x512/apps/${_pkgname}.png" install -Dm644 "${pkgdir}/opt/mihomo-party/resources/icon.png" "${pkgdir}/usr/share/icons/hicolor/512x512/apps/${_pkgname}.png"

View File

@ -41,7 +41,6 @@ package() {
chmod +x ${pkgdir}/opt/mihomo-party/mihomo-party chmod +x ${pkgdir}/opt/mihomo-party/mihomo-party
chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo
chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo-alpha chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo-alpha
chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo-smart
install -Dm755 "${srcdir}/../${_pkgname}.sh" "${pkgdir}/usr/bin/${_pkgname}" install -Dm755 "${srcdir}/../${_pkgname}.sh" "${pkgdir}/usr/bin/${_pkgname}"
sed -i '3s!/opt/mihomo-party/mihomo-party!mihomo-party!' "${pkgdir}/usr/share/applications/${_pkgname}.desktop" sed -i '3s!/opt/mihomo-party/mihomo-party!mihomo-party!' "${pkgdir}/usr/share/applications/${_pkgname}.desktop"

View File

@ -36,7 +36,6 @@ package() {
chmod +x ${pkgdir}/opt/mihomo-party/mihomo-party chmod +x ${pkgdir}/opt/mihomo-party/mihomo-party
chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo
chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo-alpha chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo-alpha
chmod +sx ${pkgdir}/opt/mihomo-party/resources/sidecar/mihomo-smart
install -Dm755 "${srcdir}/../${pkgname}.sh" "${pkgdir}/usr/bin/${pkgname}" install -Dm755 "${srcdir}/../${pkgname}.sh" "${pkgdir}/usr/bin/${pkgname}"
sed -i '3s!/opt/mihomo-party/mihomo-party!mihomo-party!' "${pkgdir}/usr/share/applications/${pkgname}.desktop" sed -i '3s!/opt/mihomo-party/mihomo-party!mihomo-party!' "${pkgdir}/usr/share/applications/${pkgname}.desktop"

View File

@ -13,7 +13,6 @@ fi
chmod 4755 '/opt/mihomo-party/chrome-sandbox' || true chmod 4755 '/opt/mihomo-party/chrome-sandbox' || true
chmod +sx /opt/mihomo-party/resources/sidecar/mihomo chmod +sx /opt/mihomo-party/resources/sidecar/mihomo
chmod +sx /opt/mihomo-party/resources/sidecar/mihomo-alpha chmod +sx /opt/mihomo-party/resources/sidecar/mihomo-alpha
chmod +sx /opt/mihomo-party/resources/sidecar/mihomo-smart
if hash update-mime-database 2>/dev/null; then if hash update-mime-database 2>/dev/null; then
update-mime-database /usr/share/mime || true update-mime-database /usr/share/mime || true

View File

@ -1,24 +1,7 @@
## 1.8.2
**本次更新主要集中在重大内核更新和依赖升级后所产生的 bug 修复解决了自1.7版以后首次安装无法启动的问题,推荐更新**
### 新功能 (Feat)
- 重构 域名嗅探 卡片模块,改为“覆写”逻辑,当开关打开后,使用 嗅探覆写 设置中的配置覆盖订阅原始配置,关闭开关恢复订阅原始配置
- 订阅/覆写卡片可右键呼出菜单
- MacOS 下“轻触(tap)”触控板可进行开关操作(之前必须“按下(click)”)
### 修复 (Fix)
- **因多国语言带来的在 Windows 下首次安装无法启动的问题**
- 1.8.1升级依赖导致的节点圆角显示失效
- DNS 覆写模块的逻辑冲突
- 点击订阅卡片功能区导致的选中订阅问题
- 覆写卡片可以双击编辑
- 因代码不规范导致的控制台警告
- Linux 下没有设置 Smart 内核权限导致的“外部控制监听错误”
## 1.8.1 ## 1.8.1
### 新功能 (Feat) ### 新功能 (Feat)
- 重构 DNS 卡片模块改为“覆写”逻辑当开关打开后使用DNS 设置中的配置覆盖订阅原始配置,关闭开关恢复订阅原始配置 - 重构 DNS 控制模块改为“覆写”逻辑当开关打开后使用DNS 设置中的配置覆盖订阅原始配置,关闭开关恢复订阅原始配置
### 性能提升(Perf) ### 性能提升(Perf)
- 更新依赖,提升页面响应性 - 更新依赖,提升页面响应性

View File

@ -1,6 +1,6 @@
{ {
"name": "mihomo-party", "name": "mihomo-party",
"version": "1.8.2", "version": "1.8.1",
"description": "Mihomo Party", "description": "Mihomo Party",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "mihomo-party-org", "author": "mihomo-party-org",

View File

@ -20,7 +20,23 @@ export async function getControledMihomoConfig(force = false): Promise<Partial<I
export async function patchControledMihomoConfig(patch: Partial<IMihomoConfig>): Promise<void> { export async function patchControledMihomoConfig(patch: Partial<IMihomoConfig>): Promise<void> {
const { useNameserverPolicy, controlDns = true, controlSniff = true } = await getAppConfig() const { useNameserverPolicy, controlDns = true, controlSniff = true } = await getAppConfig()
if (!controlDns) {
delete controledMihomoConfig.dns
delete controledMihomoConfig.hosts
} else {
// 从不接管状态恢复
if (controledMihomoConfig.dns?.ipv6 === undefined) {
controledMihomoConfig.dns = defaultControledMihomoConfig.dns
}
}
if (!controlSniff) {
delete controledMihomoConfig.sniffer
} else {
// 从不接管状态恢复
if (!controledMihomoConfig.sniffer) {
controledMihomoConfig.sniffer = defaultControledMihomoConfig.sniffer
}
}
if (patch.hosts) { if (patch.hosts) {
controledMihomoConfig.hosts = patch.hosts controledMihomoConfig.hosts = patch.hosts
} }
@ -29,37 +45,12 @@ export async function patchControledMihomoConfig(patch: Partial<IMihomoConfig>):
controledMihomoConfig.dns['nameserver-policy'] = patch.dns['nameserver-policy'] controledMihomoConfig.dns['nameserver-policy'] = patch.dns['nameserver-policy']
} }
controledMihomoConfig = deepMerge(controledMihomoConfig, patch) controledMihomoConfig = deepMerge(controledMihomoConfig, patch)
// 覆写开关控制
let configForProfile = { ...controledMihomoConfig }
if (!controlDns) {
delete configForProfile.dns
delete configForProfile.hosts
} else {
if (configForProfile.dns?.ipv6 === undefined) {
configForProfile.dns = defaultControledMihomoConfig.dns
}
}
if (!controlSniff) {
delete configForProfile.sniffer
} else {
if (!configForProfile.sniffer) {
configForProfile.sniffer = defaultControledMihomoConfig.sniffer
}
}
if (!useNameserverPolicy) { if (!useNameserverPolicy) {
delete configForProfile?.dns?.['nameserver-policy'] delete controledMihomoConfig?.dns?.['nameserver-policy']
} }
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
delete configForProfile?.tun?.device delete controledMihomoConfig?.tun?.device
} }
const originalConfig = controledMihomoConfig
controledMihomoConfig = configForProfile
await generateProfile() await generateProfile()
controledMihomoConfig = originalConfig
await writeFile(controledMihomoConfigPath(), yaml.stringify(controledMihomoConfig), 'utf-8') await writeFile(controledMihomoConfigPath(), yaml.stringify(controledMihomoConfig), 'utf-8')
} }

View File

@ -39,7 +39,6 @@ import os from 'os'
import { createWriteStream, existsSync } from 'fs' import { createWriteStream, existsSync } from 'fs'
import { uploadRuntimeConfig } from '../resolve/gistApi' import { uploadRuntimeConfig } from '../resolve/gistApi'
import { startMonitor } from '../resolve/trafficMonitor' import { startMonitor } from '../resolve/trafficMonitor'
import { safeShowErrorBox } from '../utils/init'
import i18next from '../../shared/i18n' import i18next from '../../shared/i18n'
chokidar.watch(path.join(mihomoCoreDir(), 'meta-update'), {}).on('unlinkDir', async () => { chokidar.watch(path.join(mihomoCoreDir(), 'meta-update'), {}).on('unlinkDir', async () => {
@ -47,7 +46,7 @@ chokidar.watch(path.join(mihomoCoreDir(), 'meta-update'), {}).on('unlinkDir', as
await stopCore(true) await stopCore(true)
await startCore() await startCore()
} catch (e) { } catch (e) {
safeShowErrorBox('mihomo.error.coreStartFailed', `${e}`) dialog.showErrorBox(i18next.t('mihomo.error.coreStartFailed'), `${e}`)
} }
}) })
@ -229,7 +228,7 @@ export async function keepCoreAlive(): Promise<void> {
await writeFile(path.join(dataDir(), 'core.pid'), child.pid.toString()) await writeFile(path.join(dataDir(), 'core.pid'), child.pid.toString())
} }
} catch (e) { } catch (e) {
safeShowErrorBox('mihomo.error.coreStartFailed', `${e}`) dialog.showErrorBox(i18next.t('mihomo.error.coreStartFailed'), `${e}`)
} }
} }

View File

@ -18,31 +18,12 @@ import { initProfileUpdater } from './core/profileUpdater'
import { existsSync, writeFileSync } from 'fs' import { existsSync, writeFileSync } from 'fs'
import { exePath, taskDir } from './utils/dirs' import { exePath, taskDir } from './utils/dirs'
import path from 'path' import path from 'path'
import iconv from 'iconv-lite'
import { startMonitor } from './resolve/trafficMonitor' import { startMonitor } from './resolve/trafficMonitor'
import { showFloatingWindow } from './resolve/floatingWindow' import { showFloatingWindow } from './resolve/floatingWindow'
import iconv from 'iconv-lite'
import { initI18n } from '../shared/i18n' import { initI18n } from '../shared/i18n'
import i18next from 'i18next' import i18next from 'i18next'
// 错误处理
function showSafeErrorBox(titleKey: string, message: string): void {
let title: string
try {
title = i18next.t(titleKey)
if (!title || title === titleKey) throw new Error('Translation not ready')
} catch {
const isZh = app.getLocale().startsWith('zh')
const fallbacks: Record<string, { zh: string; en: string }> = {
'common.error.initFailed': { zh: '应用初始化失败', en: 'Application initialization failed' },
'mihomo.error.coreStartFailed': { zh: '内核启动出错', en: 'Core start failed' },
'profiles.error.importFailed': { zh: '配置导入失败', en: 'Profile import failed' },
'common.error.adminRequired': { zh: '需要管理员权限', en: 'Administrator privileges required' }
}
title = fallbacks[titleKey] ? (isZh ? fallbacks[titleKey].zh : fallbacks[titleKey].en) : (isZh ? '错误' : 'Error')
}
dialog.showErrorBox(title, message)
}
async function fixUserDataPermissions(): Promise<void> { async function fixUserDataPermissions(): Promise<void> {
if (process.platform !== 'darwin') return if (process.platform !== 'darwin') return
@ -69,7 +50,6 @@ async function fixUserDataPermissions(): Promise<void> {
let quitTimeout: NodeJS.Timeout | null = null let quitTimeout: NodeJS.Timeout | null = null
export let mainWindow: BrowserWindow | null = null export let mainWindow: BrowserWindow | null = null
// Windows 管理员权限检查(仅在生产模式下)
if (process.platform === 'win32' && !is.dev && !process.argv.includes('noadmin')) { if (process.platform === 'win32' && !is.dev && !process.argv.includes('noadmin')) {
try { try {
createElevateTask() createElevateTask()
@ -94,7 +74,10 @@ if (process.platform === 'win32' && !is.dev && !process.argv.includes('noadmin')
} catch { } catch {
// ignore // ignore
} }
showSafeErrorBox('common.error.adminRequired', `${createErrorStr}\n${eStr}`) dialog.showErrorBox(
i18next.t('common.error.adminRequired'),
`${i18next.t('common.error.adminRequired')}\n${createErrorStr}\n${eStr}`
)
} finally { } finally {
app.exit() app.exit()
} }
@ -188,9 +171,6 @@ app.whenReady().then(async () => {
electronApp.setAppUserModelId('party.mihomo.app') electronApp.setAppUserModelId('party.mihomo.app')
try { try {
// 首先等待初始化完成,确保配置文件和目录都已创建
await initPromise
const appConfig = await getAppConfig() const appConfig = await getAppConfig()
// 如果配置中没有语言设置,则使用系统语言 // 如果配置中没有语言设置,则使用系统语言
if (!appConfig.language) { if (!appConfig.language) {
@ -199,8 +179,9 @@ app.whenReady().then(async () => {
appConfig.language = systemLanguage appConfig.language = systemLanguage
} }
await initI18n({ lng: appConfig.language }) await initI18n({ lng: appConfig.language })
await initPromise
} catch (e) { } catch (e) {
showSafeErrorBox('common.error.initFailed', `${e}`) dialog.showErrorBox(i18next.t('common.error.initFailed'), `${e}`)
app.quit() app.quit()
} }
try { try {
@ -209,7 +190,7 @@ app.whenReady().then(async () => {
await initProfileUpdater() await initProfileUpdater()
}) })
} catch (e) { } catch (e) {
showSafeErrorBox('mihomo.error.coreStartFailed', `${e}`) dialog.showErrorBox(i18next.t('mihomo.error.coreStartFailed'), `${e}`)
} }
try { try {
await startMonitor() await startMonitor()
@ -261,7 +242,7 @@ async function handleDeepLink(url: string): Promise<void> {
new Notification({ title: i18next.t('profiles.notification.importSuccess') }).show() new Notification({ title: i18next.t('profiles.notification.importSuccess') }).show()
break break
} catch (e) { } catch (e) {
showSafeErrorBox('profiles.error.importFailed', `${url}\n${e}`) dialog.showErrorBox(i18next.t('profiles.error.importFailed'), `${url}\n${e}`)
} }
} }
} }

View File

@ -39,25 +39,8 @@ import {
patchAppConfig, patchAppConfig,
patchControledMihomoConfig patchControledMihomoConfig
} from '../config' } from '../config'
import { app, dialog } from 'electron' import { app } from 'electron'
import { startSSIDCheck } from '../sys/ssid' import { startSSIDCheck } from '../sys/ssid'
import i18next from '../../shared/i18n'
// 安全错误处理
export function safeShowErrorBox(titleKey: string, message: string): void {
let title: string
try {
title = i18next.t(titleKey)
if (!title || title === titleKey) throw new Error('Translation not ready')
} catch {
const isZh = process.env.LANG?.startsWith('zh') || process.env.LC_ALL?.startsWith('zh')
const fallbacks: Record<string, { zh: string; en: string }> = {
'mihomo.error.coreStartFailed': { zh: '内核启动出错', en: 'Core start failed' }
}
title = fallbacks[titleKey] ? (isZh ? fallbacks[titleKey].zh : fallbacks[titleKey].en) : (isZh ? '错误' : 'Error')
}
dialog.showErrorBox(title, message)
}
async function fixDataDirPermissions(): Promise<void> { async function fixDataDirPermissions(): Promise<void> {
if (process.platform !== 'darwin') return if (process.platform !== 'darwin') return
@ -85,48 +68,47 @@ async function fixDataDirPermissions(): Promise<void> {
async function initDirs(): Promise<void> { async function initDirs(): Promise<void> {
await fixDataDirPermissions() await fixDataDirPermissions()
// 按依赖顺序创建目录 if (!existsSync(dataDir())) {
const dirsToCreate = [ await mkdir(dataDir())
dataDir(), }
themesDir(), if (!existsSync(themesDir())) {
profilesDir(), await mkdir(themesDir())
overrideDir(), }
mihomoWorkDir(), if (!existsSync(profilesDir())) {
logDir(), await mkdir(profilesDir())
mihomoTestDir(), }
subStoreDir() if (!existsSync(overrideDir())) {
] await mkdir(overrideDir())
}
for (const dir of dirsToCreate) { if (!existsSync(mihomoWorkDir())) {
try { await mkdir(mihomoWorkDir())
if (!existsSync(dir)) { }
await mkdir(dir, { recursive: true }) if (!existsSync(logDir())) {
} await mkdir(logDir())
} catch (error) { }
console.error(`Failed to create directory ${dir}:`, error) if (!existsSync(mihomoTestDir())) {
throw new Error(`Failed to create directory ${dir}: ${error}`) await mkdir(mihomoTestDir())
} }
if (!existsSync(subStoreDir())) {
await mkdir(subStoreDir())
} }
} }
async function initConfig(): Promise<void> { async function initConfig(): Promise<void> {
const configs = [ if (!existsSync(appConfigPath())) {
{ path: appConfigPath(), content: defaultConfig, name: 'app config' }, await writeFile(appConfigPath(), yaml.stringify(defaultConfig))
{ path: profileConfigPath(), content: defaultProfileConfig, name: 'profile config' }, }
{ path: overrideConfigPath(), content: defaultOverrideConfig, name: 'override config' }, if (!existsSync(profileConfigPath())) {
{ path: profilePath('default'), content: defaultProfile, name: 'default profile' }, await writeFile(profileConfigPath(), yaml.stringify(defaultProfileConfig))
{ path: controledMihomoConfigPath(), content: defaultControledMihomoConfig, name: 'mihomo config' } }
] if (!existsSync(overrideConfigPath())) {
await writeFile(overrideConfigPath(), yaml.stringify(defaultOverrideConfig))
for (const config of configs) { }
try { if (!existsSync(profilePath('default'))) {
if (!existsSync(config.path)) { await writeFile(profilePath('default'), yaml.stringify(defaultProfile))
await writeFile(config.path, yaml.stringify(config.content)) }
} if (!existsSync(controledMihomoConfigPath())) {
} catch (error) { await writeFile(controledMihomoConfigPath(), yaml.stringify(defaultControledMihomoConfig))
console.error(`Failed to create ${config.name} at ${config.path}:`, error)
throw new Error(`Failed to create ${config.name}: ${error}`)
}
} }
} }
@ -135,30 +117,13 @@ async function initFiles(): Promise<void> {
const targetPath = path.join(mihomoWorkDir(), file) const targetPath = path.join(mihomoWorkDir(), file)
const testTargetPath = path.join(mihomoTestDir(), file) const testTargetPath = path.join(mihomoTestDir(), file)
const sourcePath = path.join(resourcesFilesDir(), file) const sourcePath = path.join(resourcesFilesDir(), file)
if (!existsSync(targetPath) && existsSync(sourcePath)) {
try { await cp(sourcePath, targetPath, { recursive: true })
if (!existsSync(targetPath) && existsSync(sourcePath)) { }
await cp(sourcePath, targetPath, { recursive: true }) if (!existsSync(testTargetPath) && existsSync(sourcePath)) {
} await cp(sourcePath, testTargetPath, { recursive: true })
if (!existsSync(testTargetPath) && existsSync(sourcePath)) {
await cp(sourcePath, testTargetPath, { recursive: true })
}
} catch (error) {
console.error(`Failed to copy ${file}:`, error)
if (['country.mmdb', 'geoip.dat', 'geosite.dat'].includes(file)) {
throw new Error(`Failed to copy critical file ${file}: ${error}`)
}
} }
} }
// 确保工作目录存在
if (!existsSync(mihomoWorkDir())) {
await mkdir(mihomoWorkDir(), { recursive: true })
}
if (!existsSync(mihomoTestDir())) {
await mkdir(mihomoTestDir(), { recursive: true })
}
await Promise.all([ await Promise.all([
copy('country.mmdb'), copy('country.mmdb'),
copy('geoip.metadb'), copy('geoip.metadb'),

View File

@ -10,7 +10,7 @@ import {
} from '@heroui/react' } from '@heroui/react'
import { IoMdMore, IoMdRefresh } from 'react-icons/io' import { IoMdMore, IoMdRefresh } from 'react-icons/io'
import dayjs from '@renderer/utils/dayjs' import dayjs from '@renderer/utils/dayjs'
import React, { Key, useMemo, useState } from 'react' import React, { Key, useEffect, useMemo, useState } from 'react'
import EditFileModal from './edit-file-modal' import EditFileModal from './edit-file-modal'
import EditInfoModal from './edit-info-modal' import EditInfoModal from './edit-info-modal'
import { useSortable } from '@dnd-kit/sortable' import { useSortable } from '@dnd-kit/sortable'
@ -54,7 +54,7 @@ const OverrideItem: React.FC<Props> = (props) => {
id: info.id id: info.id
}) })
const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null
const [dropdownOpen, setDropdownOpen] = useState(false) const [disableOpen, setDisableOpen] = useState(false)
const menuItems: MenuItem[] = useMemo(() => { const menuItems: MenuItem[] = useMemo(() => {
const list = [ const list = [
{ {
@ -124,13 +124,17 @@ const OverrideItem: React.FC<Props> = (props) => {
} }
} }
useEffect(() => {
if (isDragging) {
const handleContextMenu = (e: React.MouseEvent) => { setTimeout(() => {
e.preventDefault() setDisableOpen(true)
e.stopPropagation() }, 200)
setDropdownOpen(true) } else {
} setTimeout(() => {
setDisableOpen(false)
}, 200)
}
}, [isDragging])
return ( return (
<div <div
@ -160,21 +164,13 @@ const OverrideItem: React.FC<Props> = (props) => {
<Card <Card
as="div" as="div"
fullWidth fullWidth
className="cursor-pointer" isPressable
onContextMenu={handleContextMenu} onPress={() => {
onDoubleClick={(e) => { if (disableOpen) return
if ((e.target as Element)?.closest('button, [role="menu"], [role="menuitem"]')) {
return
}
setOpenFileEditor(true) setOpenFileEditor(true)
}} }}
> >
<div <div ref={setNodeRef} {...attributes} {...listeners} className="h-full w-full">
ref={setNodeRef}
{...attributes}
{...listeners}
className="h-full w-full"
>
<CardBody> <CardBody>
<div className="flex justify-between h-[32px]"> <div className="flex justify-between h-[32px]">
<h3 <h3
@ -210,10 +206,7 @@ const OverrideItem: React.FC<Props> = (props) => {
</Button> </Button>
)} )}
<Dropdown <Dropdown>
isOpen={dropdownOpen}
onOpenChange={setDropdownOpen}
>
<DropdownTrigger> <DropdownTrigger>
<Button isIconOnly size="sm" variant="light" color="default"> <Button isIconOnly size="sm" variant="light" color="default">
<IoMdMore color="default" className={`text-[24px]`} /> <IoMdMore color="default" className={`text-[24px]`} />

View File

@ -4,7 +4,7 @@ import SettingItem from '../base/base-setting-item'
import { Button, Input, Select, SelectItem, Switch, Tooltip } from '@heroui/react' import { Button, Input, Select, SelectItem, Switch, Tooltip } from '@heroui/react'
import { useAppConfig } from '@renderer/hooks/use-app-config' import { useAppConfig } from '@renderer/hooks/use-app-config'
import debounce from '@renderer/utils/debounce' import debounce from '@renderer/utils/debounce'
import { getGistUrl, restartCore } from '@renderer/utils/ipc' import { getGistUrl, patchControledMihomoConfig, restartCore } from '@renderer/utils/ipc'
import { MdDeleteForever } from 'react-icons/md' import { MdDeleteForever } from 'react-icons/md'
import { BiCopy } from 'react-icons/bi' import { BiCopy } from 'react-icons/bi'
import { IoIosHelpCircle } from 'react-icons/io' import { IoIosHelpCircle } from 'react-icons/io'
@ -16,6 +16,7 @@ const MihomoConfig: React.FC = () => {
const { appConfig, patchAppConfig } = useAppConfig() const { appConfig, patchAppConfig } = useAppConfig()
const { const {
diffWorkDir = false, diffWorkDir = false,
controlSniff = true,
delayTestConcurrency, delayTestConcurrency,
delayTestTimeout, delayTestTimeout,
githubToken = '', githubToken = '',
@ -192,7 +193,21 @@ const MihomoConfig: React.FC = () => {
/> />
</SettingItem> </SettingItem>
<SettingItem title={t('mihomo.controlSniff')} divider>
<Switch
size="sm"
isSelected={controlSniff}
onValueChange={async (v) => {
try {
await patchAppConfig({ controlSniff: v })
await patchControledMihomoConfig({})
await restartCore()
} catch (e) {
alert(e)
}
}}
/>
</SettingItem>
<SettingItem title={t('mihomo.autoCloseConnection')} divider> <SettingItem title={t('mihomo.autoCloseConnection')} divider>
<Switch <Switch
size="sm" size="sm"

View File

@ -2,7 +2,7 @@ import { Button, Card, CardBody, CardFooter, Tooltip } from '@heroui/react'
import BorderSwitch from '@renderer/components/base/border-swtich' import BorderSwitch from '@renderer/components/base/border-swtich'
import { RiScan2Fill } from 'react-icons/ri' import { RiScan2Fill } from 'react-icons/ri'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { restartCore } from '@renderer/utils/ipc' import { patchMihomoConfig } from '@renderer/utils/ipc'
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config' import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
import { useSortable } from '@dnd-kit/sortable' import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities' import { CSS } from '@dnd-kit/utilities'
@ -15,13 +15,15 @@ interface Props {
} }
const SniffCard: React.FC<Props> = (props) => { const SniffCard: React.FC<Props> = (props) => {
const { t } = useTranslation() const { t } = useTranslation()
const { appConfig, patchAppConfig } = useAppConfig() const { appConfig } = useAppConfig()
const { iconOnly } = props const { iconOnly } = props
const { sniffCardStatus = 'col-span-1', controlSniff = true } = appConfig || {} const { sniffCardStatus = 'col-span-1', controlSniff = true } = appConfig || {}
const location = useLocation() const location = useLocation()
const navigate = useNavigate() const navigate = useNavigate()
const match = location.pathname.includes('/sniffer') const match = location.pathname.includes('/sniffer')
const { patchControledMihomoConfig } = useControledMihomoConfig() const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig()
const { sniffer } = controledMihomoConfig || {}
const { enable } = sniffer || {}
const { const {
attributes, attributes,
listeners, listeners,
@ -33,19 +35,14 @@ const SniffCard: React.FC<Props> = (props) => {
id: 'sniff' id: 'sniff'
}) })
const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null
const onChange = async (controlSniff: boolean): Promise<void> => { const onChange = async (enable: boolean): Promise<void> => {
try { await patchControledMihomoConfig({ sniffer: { enable } })
await patchAppConfig({ controlSniff }) await patchMihomoConfig({ sniffer: { enable } })
await patchControledMihomoConfig({})
await restartCore()
} catch (e) {
alert(e)
}
} }
if (iconOnly) { if (iconOnly) {
return ( return (
<div className={`${sniffCardStatus} flex justify-center`}> <div className={`${sniffCardStatus} ${!controlSniff ? 'hidden' : ''} flex justify-center`}>
<Tooltip content={t('sider.cards.sniff')} placement="right"> <Tooltip content={t('sider.cards.sniff')} placement="right">
<Button <Button
size="sm" size="sm"
@ -71,7 +68,7 @@ const SniffCard: React.FC<Props> = (props) => {
transition, transition,
zIndex: isDragging ? 'calc(infinity)' : undefined zIndex: isDragging ? 'calc(infinity)' : undefined
}} }}
className={`${sniffCardStatus} sniff-card`} className={`${sniffCardStatus} ${!controlSniff ? 'hidden' : ''} sniff-card`}
> >
<Card <Card
fullWidth fullWidth
@ -94,8 +91,8 @@ const SniffCard: React.FC<Props> = (props) => {
/> />
</Button> </Button>
<BorderSwitch <BorderSwitch
isShowBorder={match && controlSniff} isShowBorder={match && enable}
isSelected={controlSniff} isSelected={enable}
onValueChange={onChange} onValueChange={onChange}
/> />
</div> </div>

View File

@ -216,7 +216,7 @@
"sider.cards.connections": "Connections", "sider.cards.connections": "Connections",
"sider.cards.core": "Core Settings", "sider.cards.core": "Core Settings",
"sider.cards.dns": "DNS Override", "sider.cards.dns": "DNS Override",
"sider.cards.sniff": "Sniff OVRD", "sider.cards.sniff": "Sniffing",
"sider.cards.logs": "Logs", "sider.cards.logs": "Logs",
"sider.cards.substore": "Sub-Store", "sider.cards.substore": "Sub-Store",
"sider.cards.config": "Runtime Config", "sider.cards.config": "Runtime Config",
@ -269,7 +269,6 @@
"proxies.search.placeholder": "Search Proxies", "proxies.search.placeholder": "Search Proxies",
"proxies.locate": "Locate Current Proxy", "proxies.locate": "Locate Current Proxy",
"sniffer.title": "Domain Sniffing Settings", "sniffer.title": "Domain Sniffing Settings",
"sniffer.enable": "Enable Domain Sniffing",
"sniffer.parsePureIP": "Sniff Unmapped IP Addresses", "sniffer.parsePureIP": "Sniff Unmapped IP Addresses",
"sniffer.forceDNSMapping": "Sniff Real IP Mappings", "sniffer.forceDNSMapping": "Sniff Real IP Mappings",
"sniffer.overrideDestination": "Override Connection Address", "sniffer.overrideDestination": "Override Connection Address",
@ -285,7 +284,6 @@
"sniffer.skipDstAddress.placeholder": "Example: 1.1.1.1/32", "sniffer.skipDstAddress.placeholder": "Example: 1.1.1.1/32",
"sniffer.skipSrcAddress.title": "Skip Source Address Sniffing", "sniffer.skipSrcAddress.title": "Skip Source Address Sniffing",
"sniffer.skipSrcAddress.placeholder": "Example: 192.168.1.1/24", "sniffer.skipSrcAddress.placeholder": "Example: 192.168.1.1/24",
"sniffer.saveOnly": "Save Only",
"sysproxy.title": "System Proxy", "sysproxy.title": "System Proxy",
"sysproxy.host.title": "Proxy Host", "sysproxy.host.title": "Proxy Host",
"sysproxy.host.placeholder": "Default 127.0.0.1, do not modify unless necessary", "sysproxy.host.placeholder": "Default 127.0.0.1, do not modify unless necessary",
@ -344,7 +342,6 @@
"dns.customHosts.list": "Hosts List", "dns.customHosts.list": "Hosts List",
"dns.customHosts.domainPlaceholder": "Domain", "dns.customHosts.domainPlaceholder": "Domain",
"dns.customHosts.valuePlaceholder": "Domain or IP", "dns.customHosts.valuePlaceholder": "Domain or IP",
"dns.saveOnly": "Save Only",
"profiles.title": "Profile Management", "profiles.title": "Profile Management",
"profiles.updateAll": "Update All Profiles", "profiles.updateAll": "Update All Profiles",
"profiles.useProxy": "Proxy", "profiles.useProxy": "Proxy",

View File

@ -207,7 +207,7 @@
"sider.cards.connections": "اتصالات", "sider.cards.connections": "اتصالات",
"sider.cards.core": "تنظیمات هسته", "sider.cards.core": "تنظیمات هسته",
"sider.cards.dns": "بازنویسی دی‌ان‌اس", "sider.cards.dns": "بازنویسی دی‌ان‌اس",
"sider.cards.sniff": "لغو بو کشیدن", "sider.cards.sniff": "تشخیص دامنه",
"sider.cards.logs": "گزارش‌ها", "sider.cards.logs": "گزارش‌ها",
"sider.cards.substore": "ساب استور", "sider.cards.substore": "ساب استور",
"sider.cards.config": "پیکربندی اجرا", "sider.cards.config": "پیکربندی اجرا",
@ -260,7 +260,6 @@
"proxies.search.placeholder": "جستجوی پراکسی‌ها", "proxies.search.placeholder": "جستجوی پراکسی‌ها",
"proxies.locate": "یافتن پراکسی فعلی", "proxies.locate": "یافتن پراکسی فعلی",
"sniffer.title": "تنظیمات تشخیص دامنه", "sniffer.title": "تنظیمات تشخیص دامنه",
"sniffer.enable": "فعال کردن قابلیت شنود دامنه",
"sniffer.parsePureIP": "تشخیص آدرس‌های IP بدون نگاشت", "sniffer.parsePureIP": "تشخیص آدرس‌های IP بدون نگاشت",
"sniffer.forceDNSMapping": "تشخیص نگاشت‌های IP واقعی", "sniffer.forceDNSMapping": "تشخیص نگاشت‌های IP واقعی",
"sniffer.overrideDestination": "جایگزینی آدرس اتصال", "sniffer.overrideDestination": "جایگزینی آدرس اتصال",
@ -276,7 +275,6 @@
"sniffer.skipDstAddress.placeholder": "مثال: 1.1.1.1/32", "sniffer.skipDstAddress.placeholder": "مثال: 1.1.1.1/32",
"sniffer.skipSrcAddress.title": "رد کردن تشخیص آدرس مبدا", "sniffer.skipSrcAddress.title": "رد کردن تشخیص آدرس مبدا",
"sniffer.skipSrcAddress.placeholder": "مثال: 192.168.1.1/24", "sniffer.skipSrcAddress.placeholder": "مثال: 192.168.1.1/24",
"sniffer.saveOnly": "فقط ذخیره",
"sysproxy.title": "پراکسی سیستم", "sysproxy.title": "پراکسی سیستم",
"sysproxy.host.title": "میزبان پراکسی", "sysproxy.host.title": "میزبان پراکسی",
"sysproxy.host.placeholder": "پیش‌فرض 127.0.0.1، در صورت عدم نیاز تغییر ندهید", "sysproxy.host.placeholder": "پیش‌فرض 127.0.0.1، در صورت عدم نیاز تغییر ندهید",
@ -335,7 +333,6 @@
"dns.customHosts.list": "لیست Hosts", "dns.customHosts.list": "لیست Hosts",
"dns.customHosts.domainPlaceholder": "دامنه", "dns.customHosts.domainPlaceholder": "دامنه",
"dns.customHosts.valuePlaceholder": "دامنه یا IP", "dns.customHosts.valuePlaceholder": "دامنه یا IP",
"dns.saveOnly": "فقط ذخیره",
"profiles.title": "مدیریت پروفایل", "profiles.title": "مدیریت پروفایل",
"profiles.updateAll": "به‌روزرسانی همه پروفایل‌ها", "profiles.updateAll": "به‌روزرسانی همه پروفایل‌ها",
"profiles.useProxy": "پراکسی", "profiles.useProxy": "پراکسی",

View File

@ -207,7 +207,7 @@
"sider.cards.connections": "Подключения", "sider.cards.connections": "Подключения",
"sider.cards.core": "Настройки ядра", "sider.cards.core": "Настройки ядра",
"sider.cards.dns": "Переопределение DNS", "sider.cards.dns": "Переопределение DNS",
"sider.cards.sniff": "переопределение сниффинга", "sider.cards.sniff": "Анализ трафика",
"sider.cards.logs": "Журналы", "sider.cards.logs": "Журналы",
"sider.cards.substore": "Sub-Store", "sider.cards.substore": "Sub-Store",
"sider.cards.config": "Конфигурация", "sider.cards.config": "Конфигурация",
@ -260,7 +260,6 @@
"proxies.search.placeholder": "Поиск прокси", "proxies.search.placeholder": "Поиск прокси",
"proxies.locate": "Найти текущий прокси", "proxies.locate": "Найти текущий прокси",
"sniffer.title": "Настройки анализа доменов", "sniffer.title": "Настройки анализа доменов",
"sniffer.enable": "Включить анализ домена",
"sniffer.parsePureIP": "Анализировать немаппированные IP-адреса", "sniffer.parsePureIP": "Анализировать немаппированные IP-адреса",
"sniffer.forceDNSMapping": "Анализировать реальные IP-маппинги", "sniffer.forceDNSMapping": "Анализировать реальные IP-маппинги",
"sniffer.overrideDestination": "Переопределить адрес подключения", "sniffer.overrideDestination": "Переопределить адрес подключения",
@ -276,7 +275,6 @@
"sniffer.skipDstAddress.placeholder": "Пример: 1.1.1.1/32", "sniffer.skipDstAddress.placeholder": "Пример: 1.1.1.1/32",
"sniffer.skipSrcAddress.title": "Пропустить анализ исходных адресов", "sniffer.skipSrcAddress.title": "Пропустить анализ исходных адресов",
"sniffer.skipSrcAddress.placeholder": "Пример: 192.168.1.1/24", "sniffer.skipSrcAddress.placeholder": "Пример: 192.168.1.1/24",
"sniffer.saveOnly": "Только сохранить",
"sysproxy.title": "Системный прокси", "sysproxy.title": "Системный прокси",
"sysproxy.host.title": "Хост прокси", "sysproxy.host.title": "Хост прокси",
"sysproxy.host.placeholder": "По умолчанию 127.0.0.1, не изменяйте без необходимости", "sysproxy.host.placeholder": "По умолчанию 127.0.0.1, не изменяйте без необходимости",
@ -335,7 +333,6 @@
"dns.customHosts.list": "Список Hosts", "dns.customHosts.list": "Список Hosts",
"dns.customHosts.domainPlaceholder": "Домен", "dns.customHosts.domainPlaceholder": "Домен",
"dns.customHosts.valuePlaceholder": "Домен или IP", "dns.customHosts.valuePlaceholder": "Домен или IP",
"dns.saveOnly": "Только сохранить",
"profiles.title": "Управление профилями", "profiles.title": "Управление профилями",
"profiles.updateAll": "Обновить все профили", "profiles.updateAll": "Обновить все профили",
"profiles.useProxy": "Прокси", "profiles.useProxy": "Прокси",

View File

@ -216,7 +216,7 @@
"sider.cards.connections": "连接", "sider.cards.connections": "连接",
"sider.cards.core": "内核设置", "sider.cards.core": "内核设置",
"sider.cards.dns": "DNS覆写", "sider.cards.dns": "DNS覆写",
"sider.cards.sniff": "嗅探覆写", "sider.cards.sniff": "域名嗅探",
"sider.cards.logs": "日志", "sider.cards.logs": "日志",
"sider.cards.substore": "Sub-Store", "sider.cards.substore": "Sub-Store",
"sider.cards.config": "运行时配置", "sider.cards.config": "运行时配置",
@ -269,7 +269,6 @@
"proxies.search.placeholder": "搜索节点", "proxies.search.placeholder": "搜索节点",
"proxies.locate": "定位到当前节点", "proxies.locate": "定位到当前节点",
"sniffer.title": "域名嗅探设置", "sniffer.title": "域名嗅探设置",
"sniffer.enable": "启用域名嗅探",
"sniffer.parsePureIP": "对未映射 IP 地址嗅探", "sniffer.parsePureIP": "对未映射 IP 地址嗅探",
"sniffer.forceDNSMapping": "对真实 IP 映射嗅探", "sniffer.forceDNSMapping": "对真实 IP 映射嗅探",
"sniffer.overrideDestination": "覆盖连接地址", "sniffer.overrideDestination": "覆盖连接地址",
@ -285,7 +284,6 @@
"sniffer.skipDstAddress.placeholder": "例1.1.1.1/32", "sniffer.skipDstAddress.placeholder": "例1.1.1.1/32",
"sniffer.skipSrcAddress.title": "跳过来源地址嗅探", "sniffer.skipSrcAddress.title": "跳过来源地址嗅探",
"sniffer.skipSrcAddress.placeholder": "例192.168.1.1/24", "sniffer.skipSrcAddress.placeholder": "例192.168.1.1/24",
"sniffer.saveOnly": "仅保存",
"sysproxy.title": "系统代理", "sysproxy.title": "系统代理",
"sysproxy.host.title": "代理主机", "sysproxy.host.title": "代理主机",
"sysproxy.host.placeholder": "默认 127.0.0.1 若无特殊需求请勿修改", "sysproxy.host.placeholder": "默认 127.0.0.1 若无特殊需求请勿修改",
@ -344,7 +342,6 @@
"dns.customHosts.list": "Hosts 列表", "dns.customHosts.list": "Hosts 列表",
"dns.customHosts.domainPlaceholder": "域名", "dns.customHosts.domainPlaceholder": "域名",
"dns.customHosts.valuePlaceholder": "域名或 IP", "dns.customHosts.valuePlaceholder": "域名或 IP",
"dns.saveOnly": "仅保存",
"profiles.title": "订阅管理", "profiles.title": "订阅管理",
"profiles.updateAll": "更新全部订阅", "profiles.updateAll": "更新全部订阅",
"profiles.useProxy": "代理", "profiles.useProxy": "代理",

View File

@ -13,7 +13,7 @@ const DNS: React.FC = () => {
const { t } = useTranslation() const { t } = useTranslation()
const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig() const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig()
const { appConfig, patchAppConfig } = useAppConfig() const { appConfig, patchAppConfig } = useAppConfig()
const { nameserverPolicy, useNameserverPolicy, controlDns = true } = appConfig || {} const { nameserverPolicy, useNameserverPolicy } = appConfig || {}
const { dns, hosts } = controledMihomoConfig || {} const { dns, hosts } = controledMihomoConfig || {}
const { const {
enable = true, enable = true,
@ -128,10 +128,8 @@ const DNS: React.FC = () => {
try { try {
setChanged(false) setChanged(false)
await patchControledMihomoConfig(patch) await patchControledMihomoConfig(patch)
if (controlDns) { await patchMihomoConfig(patch)
await patchMihomoConfig(patch) await restartCore()
await restartCore()
}
} catch (e) { } catch (e) {
alert(e) alert(e)
} }
@ -177,7 +175,7 @@ const DNS: React.FC = () => {
onSave(result) onSave(result)
}} }}
> >
{controlDns ? t('common.save') : t('dns.saveOnly')} {t('common.save')}
</Button> </Button>
) )
} }

View File

@ -3,8 +3,7 @@ import BasePage from '@renderer/components/base/base-page'
import SettingCard from '@renderer/components/base/base-setting-card' import SettingCard from '@renderer/components/base/base-setting-card'
import SettingItem from '@renderer/components/base/base-setting-item' import SettingItem from '@renderer/components/base/base-setting-item'
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config' import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
import { useAppConfig } from '@renderer/hooks/use-app-config' import { restartCore } from '@renderer/utils/ipc'
import { restartCore, patchMihomoConfig } from '@renderer/utils/ipc'
import React, { ReactNode, useState } from 'react' import React, { ReactNode, useState } from 'react'
import { MdDeleteForever } from 'react-icons/md' import { MdDeleteForever } from 'react-icons/md'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -12,11 +11,8 @@ import { useTranslation } from 'react-i18next'
const Sniffer: React.FC = () => { const Sniffer: React.FC = () => {
const { t } = useTranslation() const { t } = useTranslation()
const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig() const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig()
const { appConfig } = useAppConfig()
const { controlSniff = true } = appConfig || {}
const { sniffer } = controledMihomoConfig || {} const { sniffer } = controledMihomoConfig || {}
const { const {
enable = true,
'parse-pure-ip': parsePureIP = true, 'parse-pure-ip': parsePureIP = true,
'force-dns-mapping': forceDNSMapping = true, 'force-dns-mapping': forceDNSMapping = true,
'override-destination': overrideDestination = false, 'override-destination': overrideDestination = false,
@ -45,7 +41,6 @@ const Sniffer: React.FC = () => {
} = sniffer || {} } = sniffer || {}
const [changed, setChanged] = useState(false) const [changed, setChanged] = useState(false)
const [values, originSetValues] = useState({ const [values, originSetValues] = useState({
enable,
parsePureIP, parsePureIP,
forceDNSMapping, forceDNSMapping,
overrideDestination, overrideDestination,
@ -64,11 +59,7 @@ const Sniffer: React.FC = () => {
try { try {
setChanged(false) setChanged(false)
await patchControledMihomoConfig(patch) await patchControledMihomoConfig(patch)
await restartCore()
if (controlSniff) {
await patchMihomoConfig(patch)
await restartCore()
}
} catch (e) { } catch (e) {
alert(e) alert(e)
} }
@ -139,34 +130,22 @@ const Sniffer: React.FC = () => {
onPress={() => onPress={() =>
onSave({ onSave({
sniffer: { sniffer: {
enable: values.enable,
'parse-pure-ip': values.parsePureIP, 'parse-pure-ip': values.parsePureIP,
'force-dns-mapping': values.forceDNSMapping, 'force-dns-mapping': values.forceDNSMapping,
'override-destination': values.overrideDestination, 'override-destination': values.overrideDestination,
sniff: values.sniff, sniff: values.sniff,
'skip-domain': values.skipDomain, 'skip-domain': values.skipDomain,
'force-domain': values.forceDomain, 'force-domain': values.forceDomain
'skip-dst-address': values.skipDstAddress,
'skip-src-address': values.skipSrcAddress
} }
}) })
} }
> >
{controlSniff ? t('common.save') : t('sniffer.saveOnly')} {t('common.save')}
</Button> </Button>
) )
} }
> >
<SettingCard> <SettingCard>
<SettingItem title={t('sniffer.enable')} divider>
<Switch
size="sm"
isSelected={values.enable}
onValueChange={(v) => {
setValues({ ...values, enable: v })
}}
/>
</SettingItem>
<SettingItem title={t('sniffer.overrideDestination')} divider> <SettingItem title={t('sniffer.overrideDestination')} divider>
<Switch <Switch
size="sm" size="sm"