diff --git a/Cargo.lock b/Cargo.lock
index 080d959d1..c18416c52 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1079,7 +1079,7 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]]
name = "clash-verge"
-version = "2.5.4"
+version = "2.5.5"
dependencies = [
"aes-gcm",
"anyhow",
diff --git a/Changelog.md b/Changelog.md
index ec0d50ca4..fc1ca120a 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,106 +1,14 @@
-## v2.5.4
+## v2.5.5
🐞 修复问题
-- 修复首页代理组和节点下拉菜单:再次点击无法收起或误选、小窗口显示不全、切换模式后意外展开
-- 修复系统代理切换失败后,开关状态显示错误的问题
-- 修复代理守卫失败后反复重试且没有提示的问题
-- 修复未开启系统代理时,启动仍提示代理错误的问题
-- 修复恢复备份后未按备份设置关闭系统代理的问题
-- 修复服务模式下操作超时后,后续操作报错或状态异常的问题
-- 修复部分节点显示「Unknown」或无法测试延迟的问题
-- 修复 `PASS-RULE` 节点被错误隐藏的问题
-- 修复重启内核后未恢复上次所选节点的问题
-- 修复部分代理组和首页图标无法显示、页面报错的问题
-- 修复同时修改端口、TUN 等设置时,部分修改丢失的问题
-- 修复 TUN 模式下 IPv6 Fake-IP 默认地址范围错误的问题
-- 修复切换节点时应用可能卡住的问题
-- 修复订阅文件被误删、删除或排序后列表异常的问题
-- 修复订阅自动更新失败无提示、重启后更新时间延后及状态误报的问题
-- 修复订阅编辑器因节点名称为空而崩溃、因格式错误而丢失内容的问题
-- 修复 DNS 编辑器空内容报错、初始值覆盖已有设置的问题
-- 修复关闭自动检查更新后,手动检查更新无效的问题
-- 修复应用冷启动或从轻量模式打开界面时, 系统代理被重复设置的问题
-
-**🖥️ Windows**
-
-- 修复 Windows 管理员运行时,服务权限校验失败后无法继续使用代理的问题
-- 修复 Windows 便携版或 Scoop 安装后服务内核无法启动的问题
-- 修复 Windows 系统代理设置偶发失败的问题
-- 修复 Windows 关机被应用阻止的问题
-- 修复 Windows 服务模式下 TUN 使用 system/mixed 堆栈时无法接管流量的问题,防火墙放行规则改为自动添加
-- 修复 Windows 配置中 GEO 规则较多时,首次启动内核失败的问题
-
-**🍎 macOS**
-
-- 修复 macOS 内核更新失败后无法启动的问题
-- 修复 macOS 需要管理员授权或开机自启时,系统代理设置失败的问题
-- 修复 macOS 切换显示器后托盘速率样式不同步的问题
-
-**🐧 Linux**
-
-- 修复 Linux 安装或卸载服务时授权失败的问题
-- 修复 Fedora 等启用 SELinux 的系统更新后服务无法启动的问题
-
-**🍎/🐧 macOS/Linux**
-
-- 修复 macOS/Linux 系统代理绕过规则未正确生效的问题
-- 修复 macOS/Linux 因旧版服务缓存权限异常而无法记住节点的问题
-
-
-
-
- ✨ 新增功能
-
-- 新增混合代理端口自动避让,被占用时临时使用其他端口
-- 新增系统代理和 TUN 权限引导,可直接安装服务并恢复原设置
-- 新增服务模式多用户隔离,支持旧版服务升级迁移
-- 新增手动取消节点固定功能
-- 新增代理组标题栏拖动调整,可交换组名和工具栏的位置
-- 新增前置规则与后置规则互相移动功能
-- 新增 DNS 覆写保护:订阅含自定义 DNS 时先确认,订阅更新后自动关闭覆写
-- 新增代理页排查提示,方便检查订阅和内核状态
-- 新增订阅更新频率提醒,间隔不足 24 小时时提示
-- 新增启动失败提示,显示原因并保存启动日志
-- 新增 TUN「Mips」堆栈选项,需配合 mihomo v1.19.31 及以上内核
-
-**🍎 macOS**
-
-- 新增 macOS Liquid Glass 风格图标
-- 新增 macOS 多用户启动保护,避免多个实例冲突
-
-**🐧 Linux**
-
-- 新增 Linux 系统主题跟随,自动切换深浅色外观
+- 修复 DNS 覆写:空内容报错、初始值覆盖已有设置、已填写字段残留原配置、空字段在高级编辑器中缺失
🚀 优化改进
-- 优化首页节点菜单关闭时的刷新性能
-- 优化 TUN 配置优先级:界面设置优先,其他选项仍可通过订阅、覆写和脚本调整
-- 优化 DNS 覆写:仅覆盖已填写的字段和已开启的开关,留空或关闭的项保留订阅设置
-- 优化系统代理失败后的恢复流程,保持系统设置和界面状态一致
-- 优化错误提示,支持复制完整原因,托盘和快捷键操作失败时也会提醒
-- 优化代理列表刷新、排序和页面切换速度
-- 优化首页节点操作,显示节点固定状态
-- 优化外部链接检查,仅允许打开 HTTP/HTTPS 网页
-- 优化更新说明排版,支持表格和提示框
-- 优化搜索框聚焦、界面字体和拖动排序动画
-- 优化后台连接和托盘刷新,降低资源占用
-- 优化解锁检测,支持同时检测多个项目并逐项显示结果
-- 优化日志记录,减少重复信息
-- 移除便携模式,统一使用系统应用数据目录
-- 优化繁体中文、俄语翻译及语言名称显示
-
-**🖥️ Windows**
-
-- 优化 Windows 自动更新,跳过安装语言选择
-
-**🐧 Linux**
-
-- 优化 Linux 系统代理设置,减少额外命令依赖,并明确提示缺少的命令
diff --git a/docs/Changelog.history.md b/docs/Changelog.history.md
index bd8026425..5a35fd3da 100644
--- a/docs/Changelog.history.md
+++ b/docs/Changelog.history.md
@@ -1,3 +1,97 @@
+## v2.5.4
+
+- 修复首页代理组和节点下拉菜单:再次点击无法收起或误选、小窗口显示不全、切换模式后意外展开
+- 修复系统代理切换失败后,开关状态显示错误的问题
+- 修复代理守卫失败后反复重试且没有提示的问题
+- 修复未开启系统代理时,启动仍提示代理错误的问题
+- 修复恢复备份后未按备份设置关闭系统代理的问题
+- 修复服务模式下操作超时后,后续操作报错或状态异常的问题
+- 修复部分节点显示「Unknown」或无法测试延迟的问题
+- 修复 `PASS-RULE` 节点被错误隐藏的问题
+- 修复重启内核后未恢复上次所选节点的问题
+- 修复部分代理组和首页图标无法显示、页面报错的问题
+- 修复同时修改端口、TUN 等设置时,部分修改丢失的问题
+- 修复 TUN 模式下 IPv6 Fake-IP 默认地址范围错误的问题
+- 修复切换节点时应用可能卡住的问题
+- 修复订阅文件被误删、删除或排序后列表异常的问题
+- 修复订阅自动更新失败无提示、重启后更新时间延后及状态误报的问题
+- 修复订阅编辑器因节点名称为空而崩溃、因格式错误而丢失内容的问题
+- 修复 DNS 编辑器空内容报错、初始值覆盖已有设置的问题
+- 修复关闭自动检查更新后,手动检查更新无效的问题
+- 修复应用冷启动或从轻量模式打开界面时, 系统代理被重复设置的问题
+
+**🖥️ Windows**
+
+- 修复 Windows 管理员运行时,服务权限校验失败后无法继续使用代理的问题
+- 修复 Windows 便携版或 Scoop 安装后服务内核无法启动的问题
+- 修复 Windows 系统代理设置偶发失败的问题
+- 修复 Windows 关机被应用阻止的问题
+- 修复 Windows 服务模式下 TUN 使用 system/mixed 堆栈时无法接管流量的问题,防火墙放行规则改为自动添加
+- 修复 Windows 配置中 GEO 规则较多时,首次启动内核失败的问题
+
+**🍎 macOS**
+
+- 修复 macOS 内核更新失败后无法启动的问题
+- 修复 macOS 需要管理员授权或开机自启时,系统代理设置失败的问题
+- 修复 macOS 切换显示器后托盘速率样式不同步的问题
+
+**🐧 Linux**
+
+- 修复 Linux 安装或卸载服务时授权失败的问题
+- 修复 Fedora 等启用 SELinux 的系统更新后服务无法启动的问题
+
+**🍎/🐧 macOS/Linux**
+
+- 修复 macOS/Linux 系统代理绕过规则未正确生效的问题
+- 修复 macOS/Linux 因旧版服务缓存权限异常而无法记住节点的问题
+
+- 新增混合代理端口自动避让,被占用时临时使用其他端口
+- 新增系统代理和 TUN 权限引导,可直接安装服务并恢复原设置
+- 新增服务模式多用户隔离,支持旧版服务升级迁移
+- 新增手动取消节点固定功能
+- 新增代理组标题栏拖动调整,可交换组名和工具栏的位置
+- 新增前置规则与后置规则互相移动功能
+- 新增 DNS 覆写保护:订阅含自定义 DNS 时先确认,订阅更新后自动关闭覆写
+- 新增代理页排查提示,方便检查订阅和内核状态
+- 新增订阅更新频率提醒,间隔不足 24 小时时提示
+- 新增启动失败提示,显示原因并保存启动日志
+- 新增 TUN「Mips」堆栈选项,需配合 mihomo v1.19.31 及以上内核
+
+**🍎 macOS**
+
+- 新增 macOS Liquid Glass 风格图标
+- 新增 macOS 多用户启动保护,避免多个实例冲突
+
+**🐧 Linux**
+
+- 新增 Linux 系统主题跟随,自动切换深浅色外观
+
+- 优化首页节点菜单关闭时的刷新性能
+- 优化 TUN 配置优先级:界面设置优先,其他选项仍可通过订阅、覆写和脚本调整
+- 优化 DNS 覆写:仅覆盖已填写的字段和已开启的开关,留空或关闭的项保留订阅设置
+- 优化系统代理失败后的恢复流程,保持系统设置和界面状态一致
+- 优化错误提示,支持复制完整原因,托盘和快捷键操作失败时也会提醒
+- 优化代理列表刷新、排序和页面切换速度
+- 优化首页节点操作,显示节点固定状态
+- 优化外部链接检查,仅允许打开 HTTP/HTTPS 网页
+- 优化更新说明排版,支持表格和提示框
+- 优化搜索框聚焦、界面字体和拖动排序动画
+- 优化后台连接和托盘刷新,降低资源占用
+- 优化解锁检测,支持同时检测多个项目并逐项显示结果
+- 优化日志记录,减少重复信息
+- 移除便携模式,统一使用系统应用数据目录
+- 优化繁体中文、俄语翻译及语言名称显示
+
+**🖥️ Windows**
+
+- 优化 Windows 自动更新,跳过安装语言选择
+
+**🐧 Linux**
+
+- 优化 Linux 系统代理设置,减少额外命令依赖,并明确提示缺少的命令
+
+
+
## v2.5.2
### 🐞 修复问题
diff --git a/package.json b/package.json
index 890c85174..bc24d1485 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "clash-verge",
- "version": "2.5.4",
+ "version": "2.5.5",
"license": "GPL-3.0-only",
"scripts": {
"prepare": "husky || true",
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 5f114a521..4d3fc6fd3 100755
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clash-verge"
-version = "2.5.4"
+version = "2.5.5"
description = "clash verge"
authors = ["zzzgydi", "Tunglies", "wonfen", "MystiPanda"]
license = "GPL-3.0-only"
diff --git a/src-tauri/src/enhance/mod.rs b/src-tauri/src/enhance/mod.rs
index c507164e3..c62aaf4d8 100644
--- a/src-tauri/src/enhance/mod.rs
+++ b/src-tauri/src/enhance/mod.rs
@@ -708,17 +708,9 @@ fn is_set(value: &Value) -> bool {
}
}
-/// Overlay set fields onto base; maps merge per key, lists are leaves.
+/// Configured DNS fields replace the whole value, including maps and lists.
fn merge_set_fields(mut base: Mapping, overlay: Mapping) -> Mapping {
for (key, value) in overlay {
- let value = match (base.get_mut(&key), value) {
- (Some(Value::Mapping(existing)), Value::Mapping(patch)) => {
- *existing = merge_set_fields(std::mem::take(existing), patch);
- continue;
- }
- (_, Value::Mapping(patch)) => Value::Mapping(merge_set_fields(Mapping::new(), patch)),
- (_, value) => value,
- };
if is_set(&value) {
base.insert(key, value);
}
@@ -734,39 +726,45 @@ fn take_mapping(config: &mut Mapping, key: &str) -> Mapping {
.unwrap_or_default()
}
-/// Returns the merged config and whether the page set `dns.ipv6`.
-async fn apply_dns_settings(mut config: Mapping, enable_dns_settings: bool) -> (Mapping, bool) {
+fn merge_dns_config(mut config: Mapping, mut dns_config: Mapping) -> (Mapping, bool) {
let mut owns_ipv6 = false;
+ if let Some(Value::Mapping(hosts)) = dns_config.remove("hosts")
+ && !hosts.is_empty()
+ {
+ config.insert("hosts".into(), hosts.into());
+ logging!(debug, Type::Core, "apply hosts configuration");
+ }
+
+ // Legacy layout: no `dns` root.
+ let dns_override = match dns_config.remove("dns") {
+ Some(Value::Mapping(dns)) => Some(dns),
+ Some(_) => None,
+ None => Some(dns_config),
+ };
+ if let Some(dns_override) = dns_override {
+ owns_ipv6 = dns_override.get("ipv6").is_some_and(is_set);
+ let mut dns = merge_set_fields(take_mapping(&mut config, "dns"), dns_override);
+ ensure_fake_ip_range6(&mut dns);
+ config.insert("dns".into(), dns.into());
+ logging!(debug, Type::Core, "apply dns_config.yaml");
+ }
+ (config, owns_ipv6)
+}
+
+/// Returns the merged config and whether the page set `dns.ipv6`.
+async fn apply_dns_settings(config: Mapping, enable_dns_settings: bool) -> (Mapping, bool) {
if enable_dns_settings && let Ok(app_dir) = dirs::app_home_dir() {
let dns_path = app_dir.join(constants::files::DNS_CONFIG);
if dns_path.exists()
&& let Ok(dns_yaml) = fs::read_to_string(&dns_path).await
- && let Ok(mut dns_config) = serde_yaml_ng::from_str::(&dns_yaml)
+ && let Ok(dns_config) = serde_yaml_ng::from_str::(&dns_yaml)
{
- if let Some(Value::Mapping(hosts)) = dns_config.remove("hosts") {
- let hosts = merge_set_fields(take_mapping(&mut config, "hosts"), hosts);
- config.insert("hosts".into(), hosts.into());
- logging!(debug, Type::Core, "apply hosts configuration");
- }
-
- // Legacy layout: no `dns` root.
- let dns_override = match dns_config.remove("dns") {
- Some(Value::Mapping(dns)) => Some(dns),
- Some(_) => None,
- None => Some(dns_config),
- };
- if let Some(dns_override) = dns_override {
- owns_ipv6 = dns_override.get("ipv6").is_some_and(is_set);
- let mut dns = merge_set_fields(take_mapping(&mut config, "dns"), dns_override);
- ensure_fake_ip_range6(&mut dns);
- config.insert("dns".into(), dns.into());
- logging!(debug, Type::Core, "apply dns_config.yaml");
- }
+ return merge_dns_config(config, dns_config);
}
}
- (config, owns_ipv6)
+ (config, false)
}
/// Returns the enhanced profile, its original keys, script logs, and DNS override decision.
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index 84b3c1916..c060624c0 100755
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -1,5 +1,5 @@
{
- "version": "2.5.4",
+ "version": "2.5.5",
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"bundle": {
"active": true,
diff --git a/src/components/setting/mods/dns-viewer.tsx b/src/components/setting/mods/dns-viewer.tsx
index 5ffe6e101..c4cd75951 100644
--- a/src/components/setting/mods/dns-viewer.tsx
+++ b/src/components/setting/mods/dns-viewer.tsx
@@ -346,6 +346,7 @@ export function DnsViewer({ ref }: { ref?: Ref }) {
'fake-ip-filter': parseList(values.fakeIpFilter),
'default-nameserver': parseList(values.defaultNameserver),
nameserver: parseList(values.nameserver),
+ 'nameserver-policy': parseNameserverPolicy(values.nameserverPolicy),
'direct-nameserver-follow-policy': values.directNameserverFollowPolicy,
'fallback-filter': {
geoip: values.fallbackGeoip,
@@ -359,11 +360,6 @@ export function DnsViewer({ ref }: { ref?: Ref }) {
'direct-nameserver': parseList(values.directNameserver),
}
- const policy = parseNameserverPolicy(values.nameserverPolicy)
- if (Object.keys(policy).length > 0) {
- dnsConfig['nameserver-policy'] = policy
- }
-
return dnsConfig
}, [values])
@@ -375,10 +371,7 @@ export function DnsViewer({ ref }: { ref?: Ref }) {
config.dns = dnsConfig
}
- const hosts = parseHosts(values.hosts)
- if (Object.keys(hosts).length > 0) {
- config.hosts = hosts
- }
+ config.hosts = parseHosts(values.hosts)
setYamlContent(yaml.dump(config, { forceQuotes: true }))
}, [generateDnsConfig, values.hosts])
@@ -507,10 +500,7 @@ export function DnsViewer({ ref }: { ref?: Ref }) {
config.dns = dnsConfig
}
- const hosts = parseHosts(values.hosts)
- if (Object.keys(hosts).length > 0) {
- config.hosts = hosts
- }
+ config.hosts = parseHosts(values.hosts)
} else {
const parsedConfig = yaml.load(yamlContent)
if (typeof parsedConfig !== 'object' || parsedConfig === null) {