Compare commits

..

5 Commits

Author SHA1 Message Date
ezequielnick
e35fa316fc fix: autoupdater admin privileges issue 2025-08-27 19:53:21 +08:00
ezequielnick
4bea1b93cc 1.8.5 Released 2025-08-27 14:15:34 +08:00
ezequielnick
7089b3ca5b chore: add smart core settings tootips 2025-08-27 14:11:29 +08:00
Memory
add4196dc5
fix: clean up outdated sub-store.bundle.js 2025-08-27 12:59:39 +08:00
Memory
78ec7f9822
opt: tray icon updating 2025-08-27 12:59:28 +08:00
17 changed files with 180 additions and 41 deletions

View File

@ -17,7 +17,7 @@
<img width='90%' src="./images/preview.jpg"> <img width='90%' src="./images/preview.jpg">
</div> </div>
### 本项目“[狗狗加速](https://party.dginv.click/#/register?code=ARdo0mXx)”赞助 ### 本项目认证稳定机场推荐:“[狗狗加速](https://party.dginv.click/#/register?code=ARdo0mXx)”
##### [狗狗加速 —— 技术流机场 Doggygo VPN](https://party.dginv.click/#/register?code=ARdo0mXx) ##### [狗狗加速 —— 技术流机场 Doggygo VPN](https://party.dginv.click/#/register?code=ARdo0mXx)
- 高性能海外机场,稳定首选,海外团队,无跑路风险 - 高性能海外机场,稳定首选,海外团队,无跑路风险
@ -30,10 +30,11 @@
### 特性 ### 特性
- [x] 一键 Smart Core 规则覆写,基于 AI 模型自动选择最优节点 详细介绍请看 [这里](https://mihomo.party/docs/guide/smart-core)
- [x] 开箱即用,无需服务模式的 Tun - [x] 开箱即用,无需服务模式的 Tun
- [x] 多种配色主题可选UI 焕然一新 - [x] 多种配色主题可选UI 焕然一新
- [x] 支持大部分 Mihomo 常用配置修改 - [x] 支持大部分 Mihomo(Clash Meta) 常用配置修改
- [x] 内置稳定版和预览版 Mihomo 内核 - [x] 内置 Smart内核 与 Mihomo(Clash Meta) 内核
- [x] 通过 WebDAV 一键备份和恢复配置 - [x] 通过 WebDAV 一键备份和恢复配置
- [x] 强大的覆写功能,任意修订配置文件 - [x] 强大的覆写功能,任意修订配置文件
- [x] 深度集成 Sub-Store轻松管理订阅 - [x] 深度集成 Sub-Store轻松管理订阅

View File

@ -1,6 +1,9 @@
## 1.8.5 ## 1.8.5
**本次更新增加了大家期待已久的托盘图标根据代理状态实时变化颜色,系统代理为蓝色,虚拟网卡为绿色,双开为红色;完善 Smart 规则写,当原有规则中有 url-test 和 load-balance 组时,自动替换为 Smart 规则组;当没有 url-test 和 load-balance 时,增加 全局 Smart 规则组,接管全部流量** **🎉 本次更新增加了大家期待已久的托盘图标根据代理状态变化颜色,系统代理为蓝色,虚拟网卡为绿色,双开为红色
⚡ 完善 Smart 规则写,当原有规则中有 url-test 和 load-balance 组时,自动替换为 Smart 规则组;当没有 url-test 和 load-balance 时,增加 全局 Smart 规则组,接管全部流量**
#### 新增 Smart Core 详细文档: [点击查看](https://mihomo.party/docs/guide/smart-core)
### 新功能 (Feat) ### 新功能 (Feat)
- 支持 cron 表达式以自定义订阅更新频率(#766) - 支持 cron 表达式以自定义订阅更新频率(#766)
@ -44,13 +47,4 @@
### 修复 (Fix) ### 修复 (Fix)
- 修复 DNS/嗅探覆写开关逻辑,修改设置不再会直接写入运行时配置,增加了“仅保存”按钮 - 修复 DNS/嗅探覆写开关逻辑,修改设置不再会直接写入运行时配置,增加了“仅保存”按钮
- 悬浮窗改为纯矩形,修复 windows 下兼容性问题带来的白边 - 悬浮窗改为纯矩形,修复 windows 下兼容性问题带来的白边
## 1.8.2
**本次更新主要集中在重大内核更新和依赖升级后所产生的 bug 修复解决了自1.7版以后首次安装无法启动的问题,推荐更新**
### 新功能 (Feat)
- 重构 域名嗅探 卡片模块,改为“覆写”逻辑,当开关打开后,使用 嗅探覆写 设置中的配置覆盖订阅原始配置,关闭开关恢复订阅原始配置
- 订阅/覆写卡片可右键呼出菜单
- MacOS 下“轻触(tap)”触控板可进行开关操作(之前必须“按下(click)”)

View File

@ -1,6 +1,6 @@
{ {
"name": "mihomo-party", "name": "mihomo-party",
"version": "1.8.5-dev", "version": "1.8.5",
"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

@ -375,9 +375,7 @@ export const TunStatus = async (): Promise<boolean> => {
return config?.tun?.enable === true return config?.tun?.enable === true
} }
export async function getTrayIconStatus(): Promise<'white' | 'blue' | 'green' | 'red'> { export function calculateTrayIconStatus(sysProxyEnabled: boolean, tunEnabled: boolean): 'white' | 'blue' | 'green' | 'red' {
const [sysProxyEnabled, tunEnabled] = await Promise.all([SysProxyStatus(), TunStatus()])
if (sysProxyEnabled && tunEnabled) { if (sysProxyEnabled && tunEnabled) {
return 'red' // 系统代理 + TUN 同时启用(警告状态) return 'red' // 系统代理 + TUN 同时启用(警告状态)
} else if (sysProxyEnabled) { } else if (sysProxyEnabled) {
@ -387,4 +385,9 @@ export async function getTrayIconStatus(): Promise<'white' | 'blue' | 'green' |
} else { } else {
return 'white' // 全关 return 'white' // 全关
} }
}
export async function getTrayIconStatus(): Promise<'white' | 'blue' | 'green' | 'red'> {
const [sysProxyEnabled, tunEnabled] = await Promise.all([SysProxyStatus(), TunStatus()])
return calculateTrayIconStatus(sysProxyEnabled, tunEnabled)
} }

View File

@ -9,6 +9,8 @@ import { existsSync } from 'fs'
import os from 'os' import os from 'os'
import { exec, execSync, spawn } from 'child_process' import { exec, execSync, spawn } from 'child_process'
import { promisify } from 'util' import { promisify } from 'util'
import { appLogger } from '../utils/logger'
import { checkAdminPrivileges } from '../core/manager'
export async function checkUpdate(): Promise<IAppVersion | undefined> { export async function checkUpdate(): Promise<IAppVersion | undefined> {
const { 'mixed-port': mixedPort = 7890 } = await getControledMihomoConfig() const { 'mixed-port': mixedPort = 7890 } = await getControledMihomoConfig()
@ -95,10 +97,45 @@ export async function downloadAndInstallUpdate(version: string): Promise<void> {
await writeFile(path.join(dataDir(), file), res.data) await writeFile(path.join(dataDir(), file), res.data)
} }
if (file.endsWith('.exe')) { if (file.endsWith('.exe')) {
spawn(path.join(dataDir(), file), ['/S', '--force-run'], { try {
detached: true, const installerPath = path.join(dataDir(), file)
stdio: 'ignore' const isAdmin = await checkAdminPrivileges()
}).unref()
if (isAdmin) {
await appLogger.info('Running installer with existing admin privileges')
spawn(installerPath, ['/S', '--force-run'], {
detached: true,
stdio: 'ignore'
}).unref()
} else {
// 提升权限安装
const escapedPath = installerPath.replace(/'/g, "''")
const args = ['/S', '--force-run']
const argsString = args.map(arg => arg.replace(/'/g, "''")).join("', '")
const command = `powershell -Command "Start-Process -FilePath '${escapedPath}' -ArgumentList '${argsString}' -Verb RunAs -WindowStyle Hidden"`
await appLogger.info('Starting installer with elevated privileges')
const execPromise = promisify(exec)
await execPromise(command, { windowsHide: true })
await appLogger.info('Installer started successfully with elevation')
}
} catch (installerError) {
await appLogger.error('Failed to start installer, trying fallback', installerError)
// Fallback: 尝试使用shell.openPath打开安装包
try {
await shell.openPath(path.join(dataDir(), file))
await appLogger.info('Opened installer with shell.openPath as fallback')
} catch (fallbackError) {
await appLogger.error('Fallback method also failed', fallbackError)
const installerErrorMessage = installerError instanceof Error ? installerError.message : String(installerError)
const fallbackErrorMessage = fallbackError instanceof Error ? fallbackError.message : String(fallbackError)
throw new Error(`Failed to execute installer: ${installerErrorMessage}. Fallback also failed: ${fallbackErrorMessage}`)
}
}
} }
if (file.endsWith('.7z')) { if (file.endsWith('.7z')) {
await copyFile(path.join(resourcesFilesDir(), '7za.exe'), path.join(dataDir(), '7za.exe')) await copyFile(path.join(resourcesFilesDir(), '7za.exe'), path.join(dataDir(), '7za.exe'))

View File

@ -15,7 +15,7 @@ import pngIconBlue from '../../../resources/icon_blue.png?asset'
import pngIconRed from '../../../resources/icon_red.png?asset' import pngIconRed from '../../../resources/icon_red.png?asset'
import pngIconGreen from '../../../resources/icon_green.png?asset' import pngIconGreen from '../../../resources/icon_green.png?asset'
import templateIcon from '../../../resources/iconTemplate.png?asset' import templateIcon from '../../../resources/iconTemplate.png?asset'
import { mihomoChangeProxy, mihomoCloseAllConnections, mihomoGroups, patchMihomoConfig, getTrayIconStatus } from '../core/mihomoApi' import { mihomoChangeProxy, mihomoCloseAllConnections, mihomoGroups, patchMihomoConfig, getTrayIconStatus, calculateTrayIconStatus } from '../core/mihomoApi'
import { mainWindow, showMainWindow, triggerMainWindow } from '..' import { mainWindow, showMainWindow, triggerMainWindow } from '..'
import { app, clipboard, ipcMain, Menu, nativeImage, shell, Tray } from 'electron' import { app, clipboard, ipcMain, Menu, nativeImage, shell, Tray } from 'electron'
import { dataDir, logDir, mihomoCoreDir, mihomoWorkDir } from '../utils/dirs' import { dataDir, logDir, mihomoCoreDir, mihomoWorkDir } from '../utils/dirs'
@ -458,6 +458,27 @@ const getIconPaths = () => {
} }
} }
export function updateTrayIconImmediate(sysProxyEnabled: boolean, tunEnabled: boolean): void {
if (!tray) return
const status = calculateTrayIconStatus(sysProxyEnabled, tunEnabled)
const iconPaths = getIconPaths()
const iconPath = iconPaths[status]
try {
if (process.platform === 'darwin') {
const icon = nativeImage.createFromPath(iconPath).resize({ height: 16 })
tray.setImage(icon)
} else if (process.platform === 'win32') {
tray.setImage(iconPath)
} else if (process.platform === 'linux') {
tray.setImage(iconPath)
}
} catch (error) {
console.error('更新托盘图标失败:', error)
}
}
export async function updateTrayIcon(): Promise<void> { export async function updateTrayIcon(): Promise<void> {
if (!tray) return if (!tray) return

View File

@ -22,7 +22,7 @@ import {
defaultProfileConfig defaultProfileConfig
} from './template' } from './template'
import yaml from 'yaml' import yaml from 'yaml'
import { mkdir, writeFile, rm, readdir, cp, stat } from 'fs/promises' import { mkdir, writeFile, rm, readdir, cp, stat, rename } from 'fs/promises'
import { existsSync } from 'fs' import { existsSync } from 'fs'
import { exec } from 'child_process' import { exec } from 'child_process'
import { promisify } from 'util' import { promisify } from 'util'
@ -218,6 +218,19 @@ async function cleanup(): Promise<void> {
} }
} }
async function migrateSubStoreFiles(): Promise<void> {
const oldJsPath = path.join(mihomoWorkDir(), 'sub-store.bundle.js')
const newCjsPath = path.join(mihomoWorkDir(), 'sub-store.bundle.cjs')
if (existsSync(oldJsPath) && !existsSync(newCjsPath)) {
try {
await rename(oldJsPath, newCjsPath)
} catch (error) {
await initLogger.error('Failed to rename sub-store.bundle.js to sub-store.bundle.cjs', error)
}
}
}
async function migration(): Promise<void> { async function migration(): Promise<void> {
const { const {
siderOrder = [ siderOrder = [
@ -334,6 +347,7 @@ export async function initBasic(): Promise<void> {
await initDirs() await initDirs()
await initConfig() await initConfig()
await migration() await migration()
await migrateSubStoreFiles()
await initFiles() await initFiles()
await cleanup() await cleanup()
} }

View File

@ -84,7 +84,7 @@ import {
import { getRuntimeConfig, getRuntimeConfigStr } from '../core/factory' import { getRuntimeConfig, getRuntimeConfigStr } from '../core/factory'
import { listWebdavBackups, webdavBackup, webdavDelete, webdavRestore } from '../resolve/backup' import { listWebdavBackups, webdavBackup, webdavDelete, webdavRestore } from '../resolve/backup'
import { getInterfaces } from '../sys/interface' import { getInterfaces } from '../sys/interface'
import { closeTrayIcon, copyEnv, showTrayIcon, updateTrayIcon } from '../resolve/tray' import { closeTrayIcon, copyEnv, showTrayIcon, updateTrayIcon, updateTrayIconImmediate } from '../resolve/tray'
import { registerShortcut } from '../resolve/shortcut' import { registerShortcut } from '../resolve/shortcut'
import { closeMainWindow, mainWindow, showMainWindow, triggerMainWindow } from '..' import { closeMainWindow, mainWindow, showMainWindow, triggerMainWindow } from '..'
import { import {
@ -260,6 +260,9 @@ export function registerIpcMainHandlers(): void {
ipcMain.handle('showTrayIcon', () => ipcErrorWrapper(showTrayIcon)()) ipcMain.handle('showTrayIcon', () => ipcErrorWrapper(showTrayIcon)())
ipcMain.handle('closeTrayIcon', () => ipcErrorWrapper(closeTrayIcon)()) ipcMain.handle('closeTrayIcon', () => ipcErrorWrapper(closeTrayIcon)())
ipcMain.handle('updateTrayIcon', () => ipcErrorWrapper(updateTrayIcon)()) ipcMain.handle('updateTrayIcon', () => ipcErrorWrapper(updateTrayIcon)())
ipcMain.handle('updateTrayIconImmediate', (_e, sysProxyEnabled, tunEnabled) => {
updateTrayIconImmediate(sysProxyEnabled, tunEnabled)
})
ipcMain.handle('showMainWindow', showMainWindow) ipcMain.handle('showMainWindow', showMainWindow)
ipcMain.handle('closeMainWindow', closeMainWindow) ipcMain.handle('closeMainWindow', closeMainWindow)
ipcMain.handle('triggerMainWindow', triggerMainWindow) ipcMain.handle('triggerMainWindow', triggerMainWindow)

View File

@ -2,7 +2,8 @@ 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 { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { useAppConfig } from '@renderer/hooks/use-app-config' import { useAppConfig } from '@renderer/hooks/use-app-config'
import { triggerSysProxy, updateTrayIcon } from '@renderer/utils/ipc' import { triggerSysProxy, updateTrayIcon, updateTrayIconImmediate } from '@renderer/utils/ipc'
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
import { AiOutlineGlobal } from 'react-icons/ai' import { AiOutlineGlobal } from 'react-icons/ai'
import React from 'react' import React from 'react'
import { useSortable } from '@dnd-kit/sortable' import { useSortable } from '@dnd-kit/sortable'
@ -20,7 +21,9 @@ const SysproxySwitcher: React.FC<Props> = (props) => {
const navigate = useNavigate() const navigate = useNavigate()
const match = location.pathname.includes('/sysproxy') const match = location.pathname.includes('/sysproxy')
const { appConfig, patchAppConfig } = useAppConfig() const { appConfig, patchAppConfig } = useAppConfig()
const { controledMihomoConfig } = useControledMihomoConfig()
const { sysProxy, sysproxyCardStatus = 'col-span-1' } = appConfig || {} const { sysProxy, sysproxyCardStatus = 'col-span-1' } = appConfig || {}
const { tun } = controledMihomoConfig || {}
const { enable } = sysProxy || {} const { enable } = sysProxy || {}
const { const {
attributes, attributes,
@ -35,8 +38,11 @@ const SysproxySwitcher: React.FC<Props> = (props) => {
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 (enable: boolean): Promise<void> => { const onChange = async (enable: boolean): Promise<void> => {
const previousState = !enable const previousState = !enable
const tunEnabled = tun?.enable ?? false
// 立即更新图标
updateTrayIconImmediate(enable, tunEnabled)
// 立即更新UI
try { try {
await patchAppConfig({ sysProxy: { enable } }) await patchAppConfig({ sysProxy: { enable } })
await triggerSysProxy(enable) await triggerSysProxy(enable)
@ -46,6 +52,8 @@ const SysproxySwitcher: React.FC<Props> = (props) => {
await updateTrayIcon() await updateTrayIcon()
} catch (e) { } catch (e) {
await patchAppConfig({ sysProxy: { enable: previousState } }) await patchAppConfig({ sysProxy: { enable: previousState } })
// 回滚图标
updateTrayIconImmediate(previousState, tunEnabled)
alert(e) alert(e)
} }
} }

View File

@ -3,7 +3,7 @@ import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-c
import BorderSwitch from '@renderer/components/base/border-swtich' import BorderSwitch from '@renderer/components/base/border-swtich'
import { TbDeviceIpadHorizontalBolt } from 'react-icons/tb' import { TbDeviceIpadHorizontalBolt } from 'react-icons/tb'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { restartCore, updateTrayIcon } from '@renderer/utils/ipc' import { restartCore, updateTrayIcon, updateTrayIconImmediate } from '@renderer/utils/ipc'
import { useSortable } from '@dnd-kit/sortable' import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities' import { CSS } from '@dnd-kit/utilities'
import React from 'react' import React from 'react'
@ -22,6 +22,7 @@ const TunSwitcher: React.FC<Props> = (props) => {
const match = location.pathname.includes('/tun') || false const match = location.pathname.includes('/tun') || false
const { appConfig } = useAppConfig() const { appConfig } = useAppConfig()
const { tunCardStatus = 'col-span-1' } = appConfig || {} const { tunCardStatus = 'col-span-1' } = appConfig || {}
const sysProxyEnabled = appConfig?.sysProxy?.enable ?? false
const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig() const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig()
const { tun } = controledMihomoConfig || {} const { tun } = controledMihomoConfig || {}
const { enable } = tun || {} const { enable } = tun || {}
@ -37,6 +38,7 @@ const TunSwitcher: React.FC<Props> = (props) => {
}) })
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 (enable: boolean): Promise<void> => { const onChange = async (enable: boolean): Promise<void> => {
updateTrayIconImmediate(sysProxyEnabled, enable)
if (enable) { if (enable) {
try { try {
// 检查内核权限 // 检查内核权限
@ -54,9 +56,11 @@ const TunSwitcher: React.FC<Props> = (props) => {
} catch (error) { } catch (error) {
console.error('Failed to restart as admin:', error) console.error('Failed to restart as admin:', error)
await window.electron.ipcRenderer.invoke('showErrorDialog', t('tun.permissions.failed'), String(error)) await window.electron.ipcRenderer.invoke('showErrorDialog', t('tun.permissions.failed'), String(error))
updateTrayIconImmediate(sysProxyEnabled, false)
return return
} }
} else { } else {
updateTrayIconImmediate(sysProxyEnabled, false)
return return
} }
} else { } else {
@ -66,6 +70,7 @@ const TunSwitcher: React.FC<Props> = (props) => {
} catch (error) { } catch (error) {
console.warn('Permission grant failed:', error) console.warn('Permission grant failed:', error)
await window.electron.ipcRenderer.invoke('showErrorDialog', t('tun.permissions.failed'), String(error)) await window.electron.ipcRenderer.invoke('showErrorDialog', t('tun.permissions.failed'), String(error))
updateTrayIconImmediate(sysProxyEnabled, false)
return return
} }
} }

View File

@ -55,17 +55,18 @@ const UpdaterModal: React.FC<Props> = (props) => {
</Button> </Button>
</ModalHeader> </ModalHeader>
<ModalBody className="h-full"> <ModalBody className="h-full">
<ReactMarkdown <div className="markdown-body select-text">
className="markdown-body select-text" <ReactMarkdown
components={{ components={{
a: ({ ...props }) => <a target="_blank" className="text-primary" {...props} />, a: ({ ...props }) => <a target="_blank" className="text-primary" {...props} />,
code: ({ children }) => <Code size="sm">{children}</Code>, code: ({ children }) => <Code size="sm">{children}</Code>,
h3: ({ ...props }) => <h3 className="text-lg font-bold" {...props} />, h3: ({ ...props }) => <h3 className="text-lg font-bold" {...props} />,
li: ({ children }) => <li className="list-disc list-inside">{children}</li> li: ({ children }) => <li className="list-disc list-inside">{children}</li>
}} }}
> >
{changelog} {changelog}
</ReactMarkdown> </ReactMarkdown>
</div>
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<Button size="sm" variant="light" onPress={onClose}> <Button size="sm" variant="light" onPress={onClose}>

View File

@ -127,6 +127,8 @@
"mihomo.smartCoreStrategy": "Strategy Mode", "mihomo.smartCoreStrategy": "Strategy Mode",
"mihomo.smartCoreStrategyStickySession": "Sticky Sessions", "mihomo.smartCoreStrategyStickySession": "Sticky Sessions",
"mihomo.smartCoreStrategyRoundRobin": "Round Robin", "mihomo.smartCoreStrategyRoundRobin": "Round Robin",
"mihomo.smartCoreUseLightGBMTooltip": "Use pre-trained general model for quick node selection improvement, but may not suit your specific network environment",
"mihomo.smartCoreCollectDataTooltip": "Collect your network usage data for training custom models better suited to your network environment (turn off if you don't know how to train models)",
"mihomo.mixedPort": "Mixed Port", "mihomo.mixedPort": "Mixed Port",
"mihomo.confirm": "Confirm", "mihomo.confirm": "Confirm",
"mihomo.socksPort": "Socks Port", "mihomo.socksPort": "Socks Port",

View File

@ -115,6 +115,17 @@
"mihomo.selectCoreVersion": "انتخاب نسخه هسته", "mihomo.selectCoreVersion": "انتخاب نسخه هسته",
"mihomo.stableVersion": "نسخه پایدار", "mihomo.stableVersion": "نسخه پایدار",
"mihomo.alphaVersion": "نسخه آلفا", "mihomo.alphaVersion": "نسخه آلفا",
"mihomo.smartVersion": "Smart",
"mihomo.enableSmartCore": "فعال‌سازی Smart Core",
"mihomo.enableSmartOverride": "استفاده از بازنویسی خودکار Smart",
"mihomo.smartOverrideTooltip": "از اسکریپت بازنویسی هوشمند داخلی Party برای جایگزینی url-test و load-balance با گروه قواعد Smart استفاده کنید؛ اگر گروه‌های قواعد فوق وجود ندارند، تمام گره‌ها را از اشتراک استخراج کرده و قواعد پیش‌فرض را جایگزین کنید، برای کاربرانی که نمی‌خواهند دردسر باشند مناسب است، قابلیت با یک کلیک فعال می‌شود؛ اگر از حالت جهانی استفاده می‌کنید، لطفاً گره با نام 'Smart Group' را انتخاب کنید",
"mihomo.smartCoreUseLightGBM": "استفاده از LightGBM",
"mihomo.smartCoreCollectData": "جمع‌آوری داده‌ها",
"mihomo.smartCoreStrategy": "حالت استراتژی",
"mihomo.smartCoreStrategyStickySession": "نشست‌های چسبنده",
"mihomo.smartCoreStrategyRoundRobin": "نوبتی دایره‌ای",
"mihomo.smartCoreUseLightGBMTooltip": "از مدل عمومی پیش‌آموزش دیده برای بهبود سریع انتخاب گره استفاده کنید، اما ممکن است برای محیط شبکه خاص شما مناسب نباشد",
"mihomo.smartCoreCollectDataTooltip": "داده‌های استفاده شبکه شما را برای آموزش مدل‌های سفارشی متناسب با محیط شبکه شما جمع‌آوری کنید (اگر نمی‌دانید چگونه مدل‌ها را آموزش دهید، آن را خاموش کنید)",
"mihomo.mixedPort": "پورت ترکیبی", "mihomo.mixedPort": "پورت ترکیبی",
"mihomo.confirm": "تایید", "mihomo.confirm": "تایید",
"mihomo.socksPort": "پورت Socks", "mihomo.socksPort": "پورت Socks",

View File

@ -115,6 +115,17 @@
"mihomo.selectCoreVersion": "Выберите версию ядра", "mihomo.selectCoreVersion": "Выберите версию ядра",
"mihomo.stableVersion": "Стабильная", "mihomo.stableVersion": "Стабильная",
"mihomo.alphaVersion": "Альфа", "mihomo.alphaVersion": "Альфа",
"mihomo.smartVersion": "Smart",
"mihomo.enableSmartCore": "Включить Smart ядро",
"mihomo.enableSmartOverride": "Использовать автоматическое Smart переопределение правил",
"mihomo.smartOverrideTooltip": "Использовать встроенный скрипт умного переопределения в Party для замены url-test и load-balance на Smart группы правил; если указанные группы правил отсутствуют, извлечь все узлы из подписки и заменить правила по умолчанию, подходит для пользователей, которые не хотят заморачиваться, функция действует одним кликом; если используется глобальный режим, выберите узел с именем 'Smart Group'",
"mihomo.smartCoreUseLightGBM": "Использовать LightGBM",
"mihomo.smartCoreCollectData": "Собирать данные",
"mihomo.smartCoreStrategy": "Режим стратегии",
"mihomo.smartCoreStrategyStickySession": "Липкие сессии",
"mihomo.smartCoreStrategyRoundRobin": "Круговой опрос",
"mihomo.smartCoreUseLightGBMTooltip": "Использовать предварительно обученную универсальную модель для быстрого улучшения выбора узлов, но может не подходить для вашей специфической сетевой среды",
"mihomo.smartCoreCollectDataTooltip": "Собирать данные о вашем сетевом использовании для обучения пользовательских моделей, более подходящих для вашей сетевой среды (отключите, если не знаете, как обучать модели)",
"mihomo.mixedPort": "Смешанный порт", "mihomo.mixedPort": "Смешанный порт",
"mihomo.confirm": "Подтвердить", "mihomo.confirm": "Подтвердить",
"mihomo.socksPort": "Порт Socks", "mihomo.socksPort": "Порт Socks",

View File

@ -127,6 +127,8 @@
"mihomo.smartCoreStrategy": "策略模式", "mihomo.smartCoreStrategy": "策略模式",
"mihomo.smartCoreStrategyStickySession": "粘性会话", "mihomo.smartCoreStrategyStickySession": "粘性会话",
"mihomo.smartCoreStrategyRoundRobin": "轮询", "mihomo.smartCoreStrategyRoundRobin": "轮询",
"mihomo.smartCoreUseLightGBMTooltip": "使用预训练的通用模型,可快速提升节点选择效果,但可能不适合您的特定网络环境",
"mihomo.smartCoreCollectDataTooltip": "收集您的网络使用数据,可用于训练更适合您的网络环境的自定义模型(如果您不懂如何训练模型,请关闭)",
"mihomo.mixedPort": "混合端口", "mihomo.mixedPort": "混合端口",
"mihomo.confirm": "确认", "mihomo.confirm": "确认",
"mihomo.socksPort": "Socks 端口", "mihomo.socksPort": "Socks 端口",

View File

@ -232,7 +232,18 @@ const Mihomo: React.FC = () => {
{enableSmartCore && core === 'mihomo-smart' && ( {enableSmartCore && core === 'mihomo-smart' && (
<> <>
<SettingItem <SettingItem
title={t('mihomo.smartCoreUseLightGBM')} title={
<div className="flex items-center gap-2">
<span>{t('mihomo.smartCoreUseLightGBM')}</span>
<Tooltip
content={t('mihomo.smartCoreUseLightGBMTooltip')}
placement="top"
className="max-w-xs"
>
<IoMdInformationCircleOutline className="text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 cursor-help" />
</Tooltip>
</div>
}
divider divider
> >
<Switch <Switch
@ -247,7 +258,18 @@ const Mihomo: React.FC = () => {
</SettingItem> </SettingItem>
<SettingItem <SettingItem
title={t('mihomo.smartCoreCollectData')} title={
<div className="flex items-center gap-2">
<span>{t('mihomo.smartCoreCollectData')}</span>
<Tooltip
content={t('mihomo.smartCoreCollectDataTooltip')}
placement="top"
className="max-w-xs"
>
<IoMdInformationCircleOutline className="text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 cursor-help" />
</Tooltip>
</div>
}
divider divider
> >
<Switch <Switch

View File

@ -406,6 +406,10 @@ export async function updateTrayIcon(): Promise<void> {
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('updateTrayIcon')) return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('updateTrayIcon'))
} }
export function updateTrayIconImmediate(sysProxyEnabled: boolean, tunEnabled: boolean): void {
window.electron.ipcRenderer.invoke('updateTrayIconImmediate', sysProxyEnabled, tunEnabled)
}
export async function showMainWindow(): Promise<void> { export async function showMainWindow(): Promise<void> {
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('showMainWindow')) return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('showMainWindow'))
} }