Compare commits

..

10 Commits

Author SHA1 Message Date
ezequielnick
f7b9eb2113 refactor: rewrite DNS control module with override logic 2025-08-04 13:20:24 +08:00
ezequielnick
d8aeb63584 feat: optimize and improve subscription switching 2025-08-04 13:17:43 +08:00
ezequielnick
f153217372 chore: Upgrade tailwindcss 2025-08-04 09:18:58 +08:00
ezequielnick
bc54d87389 chore: update deps 2025-08-03 20:15:40 +08:00
ezequielnick
266953e525 chore: remove default policy-priority 2025-08-03 10:18:21 +08:00
ezequielnick
36c4b6f0d1 fix: smart gruop "MATCH" 2025-08-03 09:56:37 +08:00
ezequielnick
3d137fde4d fix: smart core prepare 2025-08-02 22:36:20 +08:00
ezequielnick
7397467138 fix: smart core version fetch logic 2025-08-02 21:54:42 +08:00
ezequielnick
b8e885df6d 1.8.0 Smart Core Test Released 2025-08-02 21:25:40 +08:00
ezequielnick
e25f1fd912 feat: add smart core & one-click smart 2025-08-02 21:09:01 +08:00
14 changed files with 68 additions and 47 deletions

View File

@ -8,7 +8,5 @@
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>

View File

@ -1,21 +1,28 @@
## 1.8.1
### 新功能 (Feat)
- 重构 DNS 控制模块改为“覆写”逻辑当开关打开后使用DNS 设置中的配置覆盖订阅原始配置,关闭开关恢复订阅原始配置
### 性能提升(Perf)
- 更新依赖,提升页面响应性
- 优化订阅切换逻辑,大幅提升切换速度和稳定性
### 修复 (Fix)
- “使用自动 Smart 规则覆写”没有覆盖兜底的 MATCH 规则
- 移除默认的 Smart "policy-priority" 规则
-
## 1.8.0
### 新功能 (Feat)
**重大更新:本次更新增加了 Smart Core可以根据用户使用习惯和节点质量自动选择符合您的最优节点。并内置了“一键开启”适合不想折腾自定义规则的用户
“一键开启”内置 Smart规则的功能在“内核设置”下的“使用自动 Smart 规则覆写”,原理:当开关开启后,自动载入覆写脚本,新增 Smart Group并替换当前配置文件下的默认出站规则为"Smart Group",您的所有代理流量都将从此分组下的节点流出。如果使用“全局模式”请选择名称为"Smart Group"的节点,以使用该功能。**
注意:本功能还在测试中,如遇到问题请发 issue 反馈
## 1.7.7
### 新功能 (Feat)
- Mihomo 内核升级 v1.19.12
- 新增 Webdav 最大备数设置和清理逻辑
### 修复 (Fix)
- 修复 MacOS 下无法启动的问题(重置工作目录权限)
- 尝试修复不同版本 MacOS 下安装软件时候的报错Input/output error
- 部分遗漏的多国语言翻译
## 1.7.6
**此版本修复了 1.7.5 中的几个严重 bug推荐所有人更新**
### 修复 (Fix)
- 修复了内核1.19.8更新后gist同步失效的问题(#780)
- 部分遗漏的多国语言翻译
- MacOS 下启动Error: EACCES: permission denied
- MacOS 系统代理 bypass 不生效
- MacOS 系统代理开启时 500 报错

View File

@ -39,14 +39,12 @@ mac:
target:
- pkg
entitlementsInherit: build/entitlements.mac.plist
hardenedRuntime: true
gatekeeperAssess: false
extendInfo:
- NSCameraUsageDescription: Application requests access to the device's camera.
- NSMicrophoneUsageDescription: Application requests access to the device's microphone.
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
notarize: false
notarize: true
artifactName: ${name}-macos-${version}-${arch}.${ext}
pkg:
allowAnywhere: false

View File

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

View File

@ -4,11 +4,12 @@ import { addOverrideItem, removeOverrideItem, getOverrideItem } from './override
const SMART_OVERRIDE_ID = 'smart-core-override'
/**
* Smart
* Smart
*/
function generateSmartOverrideTemplate(useLightGBM: boolean, collectData: boolean, strategy: string): string {
return `
// 配置会在启用 Smart 内核时自动应用
// Smart 内核覆写配置
// 此配置会在启用 Smart 内核时自动应用
function main(config) {
try {
@ -29,7 +30,7 @@ function main(config) {
config['proxy-groups'] = []
}
// 查找现有的 Smart 代理组并更新
// 查找现有的 Smart 代理组并更新配置
let smartGroupExists = false
for (let i = 0; i < config['proxy-groups'].length; i++) {
const group = config['proxy-groups'][i]
@ -37,9 +38,11 @@ function main(config) {
smartGroupExists = true
console.log('[Smart Override] Found existing smart group:', group.name)
// 确保 Smart 组有正确的配置
if (!group['policy-priority']) {
group['policy-priority'] = '' // policy-priority: <1 means lower priority, >1 means higher priority, the default is 1, pattern support regex and string
}
// 始终使用用户配置的值
group.uselightgbm = ${useLightGBM}
group.collectdata = ${collectData}
group.strategy = '${strategy}'
@ -47,11 +50,11 @@ function main(config) {
}
}
// 如果没有 Smart 组且有可用代理,创建示例组
// 如果没有 Smart 组且有可用代理,创建一个示例组
if (!smartGroupExists && config.proxies && Array.isArray(config.proxies) && config.proxies.length > 0) {
console.log('[Smart Override] Creating new smart group with', config.proxies.length, 'proxies')
// 获取所有代理的名称
// 获取所有代理的名称确保代理对象有name属性
const proxyNames = config.proxies
.filter(proxy => proxy && typeof proxy === 'object' && proxy.name)
.map(proxy => proxy.name)
@ -66,6 +69,7 @@ function main(config) {
strategy: '${strategy}',
proxies: proxyNames
}
// 将 Smart 分组插入到第一个位置
config['proxy-groups'].unshift(smartGroup)
console.log('[Smart Override] Created smart group at first position with proxies:', proxyNames)
} else {
@ -75,11 +79,11 @@ function main(config) {
console.log('[Smart Override] No proxies available, skipping smart group creation')
}
// 处理规则替换
// 处理规则替换:将非 DIRECT 规则替换为 Smart Group
if (config.rules && Array.isArray(config.rules)) {
console.log('[Smart Override] Processing rules, original count:', config.rules.length)
// 收集所有代理组名称
// 收集所有代理组名称,用于智能识别
const proxyGroupNames = new Set()
if (config['proxy-groups'] && Array.isArray(config['proxy-groups'])) {
config['proxy-groups'].forEach(group => {
@ -98,7 +102,7 @@ function main(config) {
'COMPATIBLE'
])
// 添加常见的规则参数,不应该替换
// 添加常见的规则参数,这些不应该替换
const ruleParams = new Set(['no-resolve', 'force-remote-dns', 'prefer-ipv6'])
console.log('[Smart Override] Found', proxyGroupNames.size, 'proxy groups:', Array.from(proxyGroupNames))
@ -119,12 +123,12 @@ function main(config) {
let targetIndex = -1
let targetValue = ''
// 处理 MATCH 规则
// 对于 MATCH 规则目标在第2个位置索引1
if (parts[0] === 'MATCH' && parts.length === 2) {
targetIndex = 1
targetValue = parts[1]
} else if (parts.length >= 3) {
// 处理其他规则
// 对于其他规则从第3个参数开始查找索引2找到第一个不是规则参数的值
for (let i = 2; i < parts.length; i++) {
const part = parts[i]
if (!ruleParams.has(part)) {
@ -136,7 +140,7 @@ function main(config) {
}
if (targetIndex !== -1 && targetValue) {
// 检查是否应该替换
// 检查是否应该替换这个目标
const shouldReplace = !builtinTargets.has(targetValue) &&
(proxyGroupNames.has(targetValue) ||
!ruleParams.has(targetValue))
@ -150,7 +154,7 @@ function main(config) {
}
}
} else if (typeof rule === 'object' && rule !== null) {
// 处理对象格式
// 处理对象格式的规则
let targetField = ''
let targetValue = ''
@ -264,8 +268,10 @@ export async function manageSmartOverride(): Promise<void> {
const { enableSmartCore = true, enableSmartOverride = true, core } = await getAppConfig()
if (enableSmartCore && enableSmartOverride && core === 'mihomo-smart') {
// 启用 Smart 内核且启用覆写时创建或更新覆写配置
await createSmartOverride()
} else {
// 其他情况删除覆写配置
await removeSmartOverride()
}
}

View File

@ -266,6 +266,7 @@ async function checkProfile(): Promise<void> {
console.log('Profile check stdout:', stdout)
console.log('Profile check stderr:', stderr)
// 提取错误信息
const errorLines = stdout
.split('\n')
.filter((line) => line.includes('level=error') || line.includes('error'))
@ -277,6 +278,7 @@ async function checkProfile(): Promise<void> {
})
.filter(line => line.length > 0)
// 如果没有找到错误行,显示完整输出
if (errorLines.length === 0) {
const allLines = stdout.split('\n').filter(line => line.trim().length > 0)
throw new Error(`${i18next.t('mihomo.error.profileCheckFailed')}:\n${allLines.join('\n')}`)

View File

@ -168,7 +168,7 @@ export const mihomoUpgrade = async (): Promise<void> => {
return await instance.post('/upgrade')
}
// Smart 内核 API
// Smart 内核特有的 API
export const mihomoSmartGroupWeights = async (groupName: string): Promise<Record<string, number>> => {
const instance = await getAxios()
return await instance.get(`/group/${encodeURIComponent(groupName)}/weights`)

View File

@ -69,7 +69,7 @@ export function mihomoCoreDir(): string {
export function mihomoCorePath(core: string): string {
const isWin = process.platform === 'win32'
// 处理 Smart 内核
// 处理 Smart 内核的特殊情况
if (core === 'mihomo-smart') {
return path.join(mihomoCoreDir(), `mihomo-smart${isWin ? '.exe' : ''}`)
}

View File

@ -143,7 +143,7 @@ export function registerIpcMainHandlers(): void {
ipcErrorWrapper(mihomoGroupDelay)(group, url)
)
ipcMain.handle('patchMihomoConfig', (_e, patch) => ipcErrorWrapper(patchMihomoConfig)(patch))
// Smart 内核 API
// Smart 内核特有的 API
ipcMain.handle('mihomoSmartGroupWeights', (_e, groupName) =>
ipcErrorWrapper(mihomoSmartGroupWeights)(groupName)
)

View File

@ -39,7 +39,7 @@ const ConnectionItem: React.FC<Props> = (props) => {
<Chip
color={`${info.isActive ? 'primary' : 'danger'}`}
size="sm"
radius="sm"
radius="xs"
variant="dot"
>
{info.metadata.type}({info.metadata.network.toUpperCase()})
@ -65,16 +65,16 @@ const ConnectionItem: React.FC<Props> = (props) => {
<Chip
className="flag-emoji text-ellipsis whitespace-nowrap overflow-hidden"
size="sm"
radius="sm"
radius="xs"
variant="bordered"
>
{info.chains[0]}
</Chip>
<Chip size="sm" radius="sm" variant="bordered">
<Chip size="sm" radius="xs" variant="bordered">
{calcTraffic(info.upload)} {calcTraffic(info.download)}
</Chip>
{info.uploadSpeed !== 0 || info.downloadSpeed !== 0 ? (
<Chip color="primary" size="sm" radius="sm" variant="bordered">
<Chip color="primary" size="sm" radius="xs" variant="bordered">
{calcTraffic(info.uploadSpeed || 0)}/s {calcTraffic(info.downloadSpeed || 0)}
/s
</Chip>

View File

@ -68,7 +68,7 @@ const ProxyItem: React.FC<Props> = (props) => {
? 'bg-primary/30 border-r-2 border-r-primary border-l-2 border-l-primary'
: 'bg-content2'
}`}
radius="sm"
radius="xs"
>
<CardBody className="p-1">
{proxyDisplayMode === 'full' ? (

View File

@ -89,9 +89,11 @@ const Mihomo: React.FC = () => {
await patchAppConfig({ [key]: value })
await restartCore()
} catch (e) {
// 显示详细的错误信息
const errorMessage = e instanceof Error ? e.message : String(e)
console.error('Core restart failed:', errorMessage)
// 如果是配置检查失败,显示详细错误
if (errorMessage.includes('配置检查失败') || errorMessage.includes('Profile Check Failed')) {
await showDetailedError(t('mihomo.error.profileCheckFailed'), errorMessage)
} else {
@ -106,7 +108,7 @@ const Mihomo: React.FC = () => {
<>
{lanOpen && <InterfaceModal onClose={() => setLanOpen(false)} />}
<BasePage title={t('mihomo.title')}>
{/* Smart 内核设置 */}
{/* Smart 内核专用设置区域 */}
<SettingCard>
<div className={`rounded-md border p-2 transition-all duration-200 ${
enableSmartCore
@ -124,15 +126,17 @@ const Mihomo: React.FC = () => {
onValueChange={async (v) => {
await patchAppConfig({ enableSmartCore: v })
if (v && core !== 'mihomo-smart') {
// 启用 Smart 内核时,自动切换到 Smart 内核
await handleConfigChangeWithRestart('core', 'mihomo-smart')
} else if (!v && core === 'mihomo-smart') {
// 禁用 Smart 内核时,切换回普通内核
await handleConfigChangeWithRestart('core', 'mihomo')
}
}}
/>
</SettingItem>
{/* Smart 覆写开关 */}
{/* Smart 覆写开关 - 只在启用 Smart 内核时显示 */}
{enableSmartCore && (
<SettingItem
title={
@ -155,6 +159,7 @@ const Mihomo: React.FC = () => {
color="primary"
onValueChange={async (v) => {
await patchAppConfig({ enableSmartOverride: v })
// 重新生成覆写配置
await restartCore()
}}
/>
@ -214,8 +219,10 @@ const Mihomo: React.FC = () => {
}}
>
{enableSmartCore ? (
// 启用 Smart 内核时,只显示 Smart 版本
<SelectItem key="mihomo-smart">{t(CoreMap['mihomo-smart'])}</SelectItem>
) : (
// 未启用 Smart 内核时,显示普通版本
<>
<SelectItem key="mihomo">{t(CoreMap['mihomo'])}</SelectItem>
<SelectItem key="mihomo-alpha">{t(CoreMap['mihomo-alpha'])}</SelectItem>
@ -224,7 +231,7 @@ const Mihomo: React.FC = () => {
</Select>
</SettingItem>
{/* Smart 内核配置项 */}
{/* Smart 内核专用配置项 */}
{enableSmartCore && core === 'mihomo-smart' && (
<>
<SettingItem
@ -237,6 +244,7 @@ const Mihomo: React.FC = () => {
isSelected={smartCoreUseLightGBM}
onValueChange={async (v) => {
await patchAppConfig({ smartCoreUseLightGBM: v })
// 重新生成覆写配置
await restartCore()
}}
/>
@ -252,6 +260,7 @@ const Mihomo: React.FC = () => {
isSelected={smartCoreCollectData}
onValueChange={async (v) => {
await patchAppConfig({ smartCoreCollectData: v })
// 重新生成覆写配置
await restartCore()
}}
/>
@ -270,6 +279,7 @@ const Mihomo: React.FC = () => {
onSelectionChange={async (v) => {
const strategy = v.currentKey as 'sticky-sessions' | 'round-robin'
await patchAppConfig({ smartCoreStrategy: strategy })
// 重新生成覆写配置
await restartCore()
}}
>

View File

@ -85,7 +85,7 @@ const Profiles: React.FC = () => {
<div>
{sub.tag?.map((tag) => {
return (
<Chip key={tag} size="sm" className="ml-1" radius="sm">
<Chip key={tag} size="sm" className="ml-1" radius="xs">
{tag}
</Chip>
)
@ -108,7 +108,7 @@ const Profiles: React.FC = () => {
<div>
{sub.tag?.map((tag) => {
return (
<Chip key={tag} size="sm" className="ml-1" radius="sm">
<Chip key={tag} size="sm" className="ml-1" radius="xs">
{tag}
</Chip>
)

View File

@ -324,7 +324,7 @@ const Proxies: React.FC = () => {
<Avatar
className="bg-transparent mr-2"
size="sm"
radius="sm"
radius="xs"
src={
groups[index].icon.startsWith('<svg')
? `data:image/svg+xml;utf8,${groups[index].icon}`