Compare commits

..

17 Commits

Author SHA1 Message Date
2dust
3f0bcf7b83 up 7.16.2 2025-11-14 19:46:54 +08:00
2dust
7e712fcdeb Refactor menu layouts in window views 2025-11-14 19:44:09 +08:00
2dust
e634e6dae3 Code clean 2025-11-13 20:31:02 +08:00
2dust
24f8d767b1 Update routing version prefix to V4 2025-11-12 19:34:04 +08:00
2dust
31a8ddef23 Update Directory.Packages.props 2025-11-12 19:33:32 +08:00
MkQtS
30e9e64fd5 Simplify sing-box rules for domain_suffix (#8306)
adapt to new domain_suffix behavior since sing-box 1.9.0
2025-11-12 19:08:57 +08:00
MkQtS
f677934257 Proxy all Google domains (#8287)
* Proxy all Google domains

Default geosite-cn(dat/srs) used by v2rayN contains google@cn, which performs poorly in certain user environments.

* Resolve all Google domains via remote server

* fix typo

* Add google to default geofiles
2025-11-12 19:08:36 +08:00
JieXu
df7ca81837 Update package-osx.sh (#8303) 2025-11-11 19:31:06 +08:00
tt2563
53bd03dea2 更新繁體中文翻譯 (#8302)
Co-authored-by: tes2511 <tes2511@user.user>
2025-11-11 19:30:41 +08:00
JieXu
1f8dd1a52d Update ResUI.fr.resx (#8297) 2025-11-10 19:59:29 +08:00
2dust
d5460d758b up 7.16.1 2025-11-09 15:17:08 +08:00
2dust
6e38357b7d Add macOS Dock visibility option to settings 2025-11-09 14:47:53 +08:00
DHR60
1990850d9a Optimize Cert Pinning (#8282) 2025-11-09 11:20:30 +08:00
2dust
e6cb146671 Refactor UI platform visibility to use ViewModel properties 2025-11-09 11:11:23 +08:00
2dust
4da59cd767 Rename IsOSX to IsMacOS in Utils and usages 2025-11-09 10:52:46 +08:00
2dust
e20c11c1a7 Refactor reload logic with semaphore for concurrency 2025-11-08 20:48:55 +08:00
2dust
a6af95e083 Bug fix
https://github.com/2dust/v2rayN/issues/8276
2025-11-08 20:10:20 +08:00
45 changed files with 363 additions and 286 deletions

View File

@@ -43,6 +43,8 @@ cat >"$PackagePath/v2rayN.app/Contents/Info.plist" <<-EOF
<true/>
<key>NSHighResolutionCapable</key>
<true/>
<key>LSMinimumSystemVersion</key>
<string>12.7</string>
</dict>
</plist>
EOF
@@ -55,4 +57,4 @@ create-dmg \
--hide-extension "v2rayN.app" \
--app-drop-link 500 185 \
"v2rayN-${Arch}.dmg" \
"$PackagePath/v2rayN.app"
"$PackagePath/v2rayN.app"

View File

@@ -458,7 +458,7 @@ download_geo_assets() {
"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-geoip/$f" || true
done
for f in \
geosite-cn.srs geosite-gfw.srs geosite-greatfire.srs \
geosite-cn.srs geosite-gfw.srs geosite-google.srs geosite-greatfire.srs \
geosite-geolocation-cn.srs geosite-category-ads-all.srs geosite-private.srs; do
curl -fsSL -o "$srss_dir/$f" \
"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-geosite/$f" || true

View File

@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<Version>7.16.0</Version>
<Version>7.16.2</Version>
</PropertyGroup>
<PropertyGroup>

View File

@@ -22,7 +22,7 @@
<PackageVersion Include="Semi.Avalonia" Version="11.3.7" />
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7" />
<PackageVersion Include="NLog" Version="6.0.5" />
<PackageVersion Include="NLog" Version="6.0.6" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
<PackageVersion Include="WebDav.Client" Version="2.9.0" />

View File

@@ -998,7 +998,7 @@ public class Utils
public static bool IsLinux() => OperatingSystem.IsLinux();
public static bool IsOSX() => OperatingSystem.IsMacOS();
public static bool IsMacOS() => OperatingSystem.IsMacOS();
public static bool IsNonWindows() => !OperatingSystem.IsWindows();
@@ -1020,7 +1020,7 @@ public class Utils
{
try
{
if (IsWindows() || IsOSX())
if (IsWindows() || IsMacOS())
{
return false;
}

View File

@@ -26,7 +26,7 @@ public static class AutoStartupHandler
await SetTaskLinux();
}
}
else if (Utils.IsOSX())
else if (Utils.IsMacOS())
{
await ClearTaskOSX();

View File

@@ -2080,7 +2080,7 @@ public static class ConfigHandler
/// <returns>0 if successful</returns>
public static async Task<int> InitBuiltinRouting(Config config, bool blImportAdvancedRules = false)
{
var ver = "V3-";
var ver = "V4-";
var items = await AppManager.Instance.RoutingItems();
//TODO Temporary code to be removed later
@@ -2091,7 +2091,7 @@ public static class ConfigHandler
items = await AppManager.Instance.RoutingItems();
}
if (!blImportAdvancedRules && items.Count > 0)
if (!blImportAdvancedRules && items.Count(u => u.Remarks.StartsWith(ver)) > 0)
{
//migrate
//TODO Temporary code to be removed later

View File

@@ -45,7 +45,7 @@ public class Hysteria2Fmt : BaseFmt
}
var dicQuery = new Dictionary<string, string>();
ToUriQueryLite(item, ref dicQuery);
if (item.Path.IsNotEmpty())
{
dicQuery.Add("obfs", "salamander");

View File

@@ -33,7 +33,7 @@ public static class SysProxyHandler
await ProxySettingLinux.SetProxy(Global.Loopback, port, exceptions);
break;
case ESysProxyType.ForcedChange when Utils.IsOSX():
case ESysProxyType.ForcedChange when Utils.IsMacOS():
await ProxySettingOSX.SetProxy(Global.Loopback, port, exceptions);
break;
@@ -45,7 +45,7 @@ public static class SysProxyHandler
await ProxySettingLinux.UnsetProxy();
break;
case ESysProxyType.ForcedClear when Utils.IsOSX():
case ESysProxyType.ForcedClear when Utils.IsMacOS():
await ProxySettingOSX.UnsetProxy();
break;

View File

@@ -48,7 +48,6 @@ public class HttpClientHelper
}
return await httpClient.GetStringAsync(url);
}
public async Task PutAsync(string url, Dictionary<string, string> headers)
{
@@ -72,6 +71,4 @@ public class HttpClientHelper
{
await httpClient.DeleteAsync(url);
}
}

View File

@@ -202,7 +202,7 @@ public class CertPemManager
/// <summary>
/// Get certificate in PEM format from a server with CA pinning validation
/// </summary>
public async Task<(string?, string?)> GetCertPemAsync(string target, string serverName, int timeout = 10)
public async Task<(string?, string?)> GetCertPemAsync(string target, string serverName, int timeout = 4)
{
try
{
@@ -216,7 +216,13 @@ public class CertPemManager
using var ssl = new SslStream(client.GetStream(), false, ValidateServerCertificate);
await ssl.AuthenticateAsClientAsync(serverName);
var sslOptions = new SslClientAuthenticationOptions
{
TargetHost = serverName,
RemoteCertificateValidationCallback = ValidateServerCertificate
};
await ssl.AuthenticateAsClientAsync(sslOptions, cts.Token);
var remote = ssl.RemoteCertificate;
if (remote == null)
@@ -242,7 +248,7 @@ public class CertPemManager
/// <summary>
/// Get certificate chain in PEM format from a server with CA pinning validation
/// </summary>
public async Task<(List<string>, string?)> GetCertChainPemAsync(string target, string serverName, int timeout = 10)
public async Task<(List<string>, string?)> GetCertChainPemAsync(string target, string serverName, int timeout = 4)
{
var pemList = new List<string>();
try
@@ -257,7 +263,13 @@ public class CertPemManager
using var ssl = new SslStream(client.GetStream(), false, ValidateServerCertificate);
await ssl.AuthenticateAsClientAsync(serverName);
var sslOptions = new SslClientAuthenticationOptions
{
TargetHost = serverName,
RemoteCertificateValidationCallback = ValidateServerCertificate
};
await ssl.AuthenticateAsClientAsync(sslOptions, cts.Token);
if (ssl.RemoteCertificate is not X509Certificate2 certChain)
{
@@ -330,10 +342,78 @@ public class CertPemManager
return TrustedCaThumbprints.Contains(rootThumbprint);
}
public string ExportCertToPem(X509Certificate2 cert)
public static string ExportCertToPem(X509Certificate2 cert)
{
var der = cert.Export(X509ContentType.Cert);
var b64 = Convert.ToBase64String(der, Base64FormattingOptions.InsertLineBreaks);
var b64 = Convert.ToBase64String(der);
return $"-----BEGIN CERTIFICATE-----\n{b64}\n-----END CERTIFICATE-----\n";
}
/// <summary>
/// Parse concatenated PEM certificates string into a list of individual certificates
/// Normalizes format: removes line breaks from base64 content for better compatibility
/// </summary>
/// <param name="pemChain">Concatenated PEM certificates string (supports both \r\n and \n line endings)</param>
/// <returns>List of individual PEM certificate strings with normalized format</returns>
public static List<string> ParsePemChain(string pemChain)
{
var certs = new List<string>();
if (string.IsNullOrWhiteSpace(pemChain))
{
return certs;
}
// Normalize line endings (CRLF -> LF) at the beginning
pemChain = pemChain.Replace("\r\n", "\n").Replace("\r", "\n");
const string beginMarker = "-----BEGIN CERTIFICATE-----";
const string endMarker = "-----END CERTIFICATE-----";
var index = 0;
while (index < pemChain.Length)
{
var beginIndex = pemChain.IndexOf(beginMarker, index, StringComparison.Ordinal);
if (beginIndex == -1)
{
break;
}
var endIndex = pemChain.IndexOf(endMarker, beginIndex, StringComparison.Ordinal);
if (endIndex == -1)
{
break;
}
// Extract certificate content
var base64Start = beginIndex + beginMarker.Length;
var base64Content = pemChain.Substring(base64Start, endIndex - base64Start);
// Remove all whitespace from base64 content
base64Content = new string(base64Content.Where(c => !char.IsWhiteSpace(c)).ToArray());
// Reconstruct with clean format: BEGIN marker + base64 (no line breaks) + END marker
var normalizedCert = $"{beginMarker}\n{base64Content}\n{endMarker}\n";
certs.Add(normalizedCert);
// Move to next certificate
index = endIndex + endMarker.Length;
}
return certs;
}
/// <summary>
/// Concatenate a list of PEM certificates into a single string
/// </summary>
/// <param name="pemList">List of individual PEM certificate strings</param>
/// <returns>Concatenated PEM certificates string</returns>
public static string ConcatenatePemChain(IEnumerable<string> pemList)
{
if (pemList == null)
{
return string.Empty;
}
return string.Concat(pemList);
}
}

View File

@@ -67,7 +67,7 @@ public class CoreAdminManager
try
{
var shellFileName = Utils.IsOSX() ? Global.KillAsSudoOSXShellFileName : Global.KillAsSudoLinuxShellFileName;
var shellFileName = Utils.IsMacOS() ? Global.KillAsSudoOSXShellFileName : Global.KillAsSudoLinuxShellFileName;
var shFilePath = await FileUtils.CreateLinuxShellFile("kill_as_sudo.sh", EmbedUtils.GetEmbedText(shellFileName), true);
if (shFilePath.Contains(' '))
{

View File

@@ -2599,8 +2599,10 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Server certificate (PEM format, optional). Entering a certificate will pin it.
///Do not use the &quot;Fetch Certificate&quot; button when &quot;Allow Insecure&quot; is enabled. 的本地化字符串。
/// 查找类似 Server Certificate (PEM format, optional)
///When specified, the certificate will be pinned, and &quot;Allow Insecure&quot; will be disabled.
///
///The &quot;Get Certificate&quot; action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA. 的本地化字符串。
/// </summary>
public static string TbCertPinningTips {
get {
@@ -3850,6 +3852,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 macOS displays this in the Dock (requires restart) 的本地化字符串。
/// </summary>
public static string TbSettingsMacOSShowInDock {
get {
return ResourceManager.GetString("TbSettingsMacOSShowInDock", resourceCulture);
}
}
/// <summary>
/// 查找类似 Main layout orientation (requires restart) 的本地化字符串。
/// </summary>

View File

@@ -1606,8 +1606,10 @@
<value>Certificate Pinning</value>
</data>
<data name="TbCertPinningTips" xml:space="preserve">
<value>Server certificate (PEM format, optional). Entering a certificate will pin it.
Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.</value>
<value>Server Certificate (PEM format, optional)
When specified, the certificate will be pinned, and "Allow Insecure" will be disabled.
The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA.</value>
</data>
<data name="TbFetchCert" xml:space="preserve">
<value>Fetch Certificate</value>
@@ -1630,4 +1632,7 @@ Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.</val
<data name="TbSettingsCustomSystemProxyScriptPath" xml:space="preserve">
<value>Custom system proxy script file path</value>
</data>
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
<value>macOS displays this in the Dock (requires restart)</value>
</data>
</root>

View File

@@ -1603,8 +1603,10 @@
<value>Certificate Pinning</value>
</data>
<data name="TbCertPinningTips" xml:space="preserve">
<value>Certificat serveur (PEM, optionnel). Lajout dun certificat le fixe.
Ne pas utiliser « Obtenir le certificat » si « Autoriser non sécurisé » est activé.</value>
<value>Certificat serveur (format PEM, facultatif)
Si le certificat est défini, il est fixé et loption « Ignorer la vérification » est désactivée.
Si un certificat auto-signé est utilisé ou si le système contient une CA non fiable ou malveillante, laction « Obtenir le certificat » peut échouer.</value>
</data>
<data name="TbFetchCert" xml:space="preserve">
<value>Obtenir le certificat</value>
@@ -1627,4 +1629,7 @@ Ne pas utiliser « Obtenir le certificat » si « Autoriser non sécurisé » es
<data name="TbSettingsCustomSystemProxyScriptPath" xml:space="preserve">
<value>Chemin script proxy système personnalisé</value>
</data>
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
<value>Afficher dans le Dock de macOS (redém. requis)</value>
</data>
</root>

View File

@@ -1606,8 +1606,10 @@
<value>Certificate Pinning</value>
</data>
<data name="TbCertPinningTips" xml:space="preserve">
<value>Server certificate (PEM format, optional). Entering a certificate will pin it.
Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.</value>
<value>Server Certificate (PEM format, optional)
When specified, the certificate will be pinned, and "Allow Insecure" will be disabled.
The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA.</value>
</data>
<data name="TbFetchCert" xml:space="preserve">
<value>Fetch Certificate</value>
@@ -1630,4 +1632,7 @@ Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.</val
<data name="TbSettingsCustomSystemProxyScriptPath" xml:space="preserve">
<value>Custom system proxy script file path</value>
</data>
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
<value>macOS displays this in the Dock (requires restart)</value>
</data>
</root>

View File

@@ -1606,8 +1606,10 @@
<value>Certificate Pinning</value>
</data>
<data name="TbCertPinningTips" xml:space="preserve">
<value>Server certificate (PEM format, optional). Entering a certificate will pin it.
Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.</value>
<value>Server Certificate (PEM format, optional)
When specified, the certificate will be pinned, and "Allow Insecure" will be disabled.
The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA.</value>
</data>
<data name="TbFetchCert" xml:space="preserve">
<value>Fetch Certificate</value>
@@ -1630,4 +1632,7 @@ Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.</val
<data name="TbSettingsCustomSystemProxyScriptPath" xml:space="preserve">
<value>Custom system proxy script file path</value>
</data>
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
<value>macOS displays this in the Dock (requires restart)</value>
</data>
</root>

View File

@@ -1606,8 +1606,10 @@
<value>Certificate Pinning</value>
</data>
<data name="TbCertPinningTips" xml:space="preserve">
<value>Server certificate (PEM format, optional). Entering a certificate will pin it.
Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.</value>
<value>Server Certificate (PEM format, optional)
When specified, the certificate will be pinned, and "Allow Insecure" will be disabled.
The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA.</value>
</data>
<data name="TbFetchCert" xml:space="preserve">
<value>Fetch Certificate</value>
@@ -1630,4 +1632,7 @@ Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.</val
<data name="TbSettingsCustomSystemProxyScriptPath" xml:space="preserve">
<value>Custom system proxy script file path</value>
</data>
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
<value>macOS displays this in the Dock (requires restart)</value>
</data>
</root>

View File

@@ -1603,8 +1603,10 @@
<value>固定证书</value>
</data>
<data name="TbCertPinningTips" xml:space="preserve">
<value>服务器证书PEM 格式,可选)。填入后将固定该证书。
启用“跳过证书验证”时,请勿使用 '获取证书'。</value>
<value>服务器证书PEM 格式,可选)
当指定此证书后,将固定该证书,并禁用“跳过证书验证”选项。
“获取证书”操作可能失败,原因可能是使用了自签证书,或系统中存在不受信任或恶意的 CA。</value>
</data>
<data name="TbFetchCert" xml:space="preserve">
<value>获取证书</value>
@@ -1627,4 +1629,7 @@
<data name="TbSettingsCustomSystemProxyScriptPath" xml:space="preserve">
<value>自定义系统代理脚本文件路径</value>
</data>
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
<value>macOS 在 Dock 栏中显示 (需重启)</value>
</data>
</root>

View File

@@ -1600,26 +1600,28 @@
<value>自動從訂閱分組新增過濾後的配置</value>
</data>
<data name="TbCertPinning" xml:space="preserve">
<value>Certificate Pinning</value>
<value>憑證綁定</value>
</data>
<data name="TbCertPinningTips" xml:space="preserve">
<value>Server certificate (PEM format, optional). Entering a certificate will pin it.
Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.</value>
<value>伺服器憑證PEM 格式,可選)
若已指定,憑證將會被綁定,並且「跳過憑證驗證」將被停用。
若使用自簽憑證,或系統中存在不受信任或惡意的 CA「取得憑證」動作可能會失敗。</value>
</data>
<data name="TbFetchCert" xml:space="preserve">
<value>Fetch Certificate</value>
<value>獲取憑證</value>
</data>
<data name="TbFetchCertChain" xml:space="preserve">
<value>Fetch Certificate Chain</value>
<value>獲取憑證鏈</value>
</data>
<data name="ServerNameMustBeValidDomain" xml:space="preserve">
<value>Please set a valid domain</value>
<value>請設定有效的網域名稱</value>
</data>
<data name="CertNotSet" xml:space="preserve">
<value>Certificate not set</value>
<value>尚未設定憑證</value>
</data>
<data name="CertSet" xml:space="preserve">
<value>Certificate set</value>
<value>已設定憑證</value>
</data>
<data name="TbSettingsCustomSystemProxyPacPath" xml:space="preserve">
<value>自訂 PAC 檔案路徑</value>
@@ -1627,4 +1629,7 @@ Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.</val
<data name="TbSettingsCustomSystemProxyScriptPath" xml:space="preserve">
<value>自訂系統代理程式腳本檔案路徑</value>
</data>
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
<value>macOS 在 Dock 欄顯示 (需重啟)</value>
</data>
</root>

View File

@@ -13,20 +13,19 @@
"api.ip.sb"
]
},
{
"remarks": "Google cn",
"outboundTag": "proxy",
"domain": [
"domain:googleapis.cn",
"domain:gstatic.com"
]
},
{
"remarks": "阻断udp443",
"outboundTag": "block",
"port": "443",
"network": "udp"
},
{
"remarks": "代理Google",
"outboundTag": "proxy",
"domain": [
"geosite:google"
]
},
{
"remarks": "绕过局域网IP",
"outboundTag": "direct",

View File

@@ -1,18 +1,17 @@
[
{
"remarks": "Google cn",
"outboundTag": "proxy",
"domain": [
"domain:googleapis.cn",
"domain:gstatic.com"
]
},
{
"remarks": "阻断udp443",
"outboundTag": "block",
"port": "443",
"network": "udp"
},
{
"remarks": "代理Google",
"outboundTag": "proxy",
"domain": [
"geosite:google"
]
},
{
"remarks": "绕过局域网IP",
"outboundTag": "direct",

View File

@@ -14,9 +14,8 @@
],
"rules": [
{
"domain_suffix": [
"googleapis.cn",
"gstatic.com"
"rule_set": [
"geosite-google"
],
"server": "remote",
"strategy": "prefer_ipv4"

View File

@@ -8,8 +8,7 @@
"address": "1.1.1.1",
"skipFallback": true,
"domains": [
"domain:googleapis.cn",
"domain:gstatic.com"
"geosite:google"
]
},
{

View File

@@ -14,9 +14,8 @@
],
"rules": [
{
"domain_suffix": [
"googleapis.cn",
"gstatic.com"
"rule_set": [
"geosite-google"
],
"server": "remote",
"strategy": "prefer_ipv4"

View File

@@ -61,7 +61,7 @@ public partial class CoreConfigSingboxService
}
var tunInbound = JsonUtils.Deserialize<Inbound4Sbox>(EmbedUtils.GetEmbedText(Global.TunSingboxInboundFileName)) ?? new Inbound4Sbox { };
tunInbound.interface_name = Utils.IsOSX() ? $"utun{new Random().Next(99)}" : "singbox_tun";
tunInbound.interface_name = Utils.IsMacOS() ? $"utun{new Random().Next(99)}" : "singbox_tun";
tunInbound.mtu = _config.TunModeItem.Mtu;
tunInbound.auto_route = _config.TunModeItem.AutoRoute;
tunInbound.strict_route = _config.TunModeItem.StrictRoute;

View File

@@ -261,13 +261,7 @@ public partial class CoreConfigSingboxService
}
if (node.StreamSecurity == Global.StreamSecurity)
{
var certs = node.Cert
?.Split("-----END CERTIFICATE-----", StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.TrimEx())
.Where(s => !s.IsNullOrEmpty())
.Select(s => s + "\n-----END CERTIFICATE-----")
.Select(s => s.Replace("\r\n", "\n"))
.ToList() ?? new();
var certs = CertPemManager.ParsePemChain(node.Cert);
if (certs.Count > 0)
{
tls.certificate = certs;

View File

@@ -316,10 +316,8 @@ public partial class CoreConfigSingboxService
}
else if (domain.StartsWith("domain:"))
{
rule.domain ??= [];
rule.domain_suffix ??= [];
rule.domain?.Add(domain.Substring(7));
rule.domain_suffix?.Add("." + domain.Substring(7));
rule.domain_suffix?.Add(domain.Substring(7));
}
else if (domain.StartsWith("full:"))
{

View File

@@ -245,13 +245,6 @@ public partial class CoreConfigV2rayService
var host = node.RequestHost.TrimEx();
var path = node.Path.TrimEx();
var sni = node.Sni.TrimEx();
var certs = node.Cert
?.Split("-----END CERTIFICATE-----", StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.TrimEx())
.Where(s => !s.IsNullOrEmpty())
.Select(s => s + "\n-----END CERTIFICATE-----")
.Select(s => s.Replace("\r\n", "\n"))
.ToList() ?? new();
var useragent = "";
if (!_config.CoreBasicItem.DefUserAgent.IsNullOrEmpty())
{
@@ -284,6 +277,7 @@ public partial class CoreConfigV2rayService
{
tlsSettings.serverName = Utils.String2List(host)?.First();
}
var certs = CertPemManager.ParsePemChain(node.Cert);
if (certs.Count > 0)
{
var certsettings = new List<CertificateSettings4Ray>();

View File

@@ -17,17 +17,17 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
{
if (args.Success)
{
UpdateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully);
UpdateFunc(true, Utils.UrlEncode(fileName));
_ = UpdateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully);
_ = UpdateFunc(true, Utils.UrlEncode(fileName));
}
else
{
UpdateFunc(false, args.Msg);
_ = UpdateFunc(false, args.Msg);
}
};
downloadHandle.Error += (sender2, args) =>
{
UpdateFunc(false, args.GetException().Message);
_ = UpdateFunc(false, args.GetException().Message);
};
await UpdateFunc(false, string.Format(ResUI.MsgStartUpdating, ECoreType.v2rayN));
@@ -57,26 +57,26 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
{
if (args.Success)
{
UpdateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully);
UpdateFunc(false, ResUI.MsgUnpacking);
_ = UpdateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully);
_ = UpdateFunc(false, ResUI.MsgUnpacking);
try
{
UpdateFunc(true, fileName);
_ = UpdateFunc(true, fileName);
}
catch (Exception ex)
{
UpdateFunc(false, ex.Message);
_ = UpdateFunc(false, ex.Message);
}
}
else
{
UpdateFunc(false, args.Msg);
_ = UpdateFunc(false, args.Msg);
}
};
downloadHandle.Error += (sender2, args) =>
{
UpdateFunc(false, args.GetException().Message);
_ = UpdateFunc(false, args.GetException().Message);
};
await UpdateFunc(false, string.Format(ResUI.MsgStartUpdating, type));
@@ -313,7 +313,7 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
_ => null,
};
}
else if (Utils.IsOSX())
else if (Utils.IsMacOS())
{
return RuntimeInformation.ProcessArchitecture switch
{
@@ -396,6 +396,7 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
}
//append dns items TODO
geoSiteFiles.Add("google");
geoSiteFiles.Add("cn");
geoSiteFiles.Add("geolocation-cn");
geoSiteFiles.Add("category-ads-all");
@@ -438,7 +439,7 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
{
if (args.Success)
{
UpdateFunc(false, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, fileName));
_ = UpdateFunc(false, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, fileName));
try
{
@@ -452,17 +453,17 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
}
catch (Exception ex)
{
UpdateFunc(false, ex.Message);
_ = UpdateFunc(false, ex.Message);
}
}
else
{
UpdateFunc(false, args.Msg);
_ = UpdateFunc(false, args.Msg);
}
};
downloadHandle.Error += (sender2, args) =>
{
UpdateFunc(false, args.GetException().Message);
_ = UpdateFunc(false, args.GetException().Message);
};
await downloadHandle.DownloadFileAsync(url, tmpFileName, true, _timeout);

View File

@@ -2,8 +2,6 @@ namespace ServiceLib.ViewModels;
public class AddServerViewModel : MyReactiveObject
{
private string _certError = string.Empty;
[Reactive]
public ProfileItem SelectedSource { get; set; }
@@ -112,11 +110,11 @@ public class AddServerViewModel : MyReactiveObject
}
}
private void UpdateCertTip()
private void UpdateCertTip(string? errorMessage = null)
{
CertTip = _certError.IsNullOrEmpty()
CertTip = errorMessage.IsNullOrEmpty()
? (Cert.IsNullOrEmpty() ? ResUI.CertNotSet : ResUI.CertSet)
: _certError;
: errorMessage;
}
private async Task FetchCert()
@@ -137,17 +135,16 @@ public class AddServerViewModel : MyReactiveObject
}
if (!Utils.IsDomain(serverName))
{
_certError = ResUI.ServerNameMustBeValidDomain;
UpdateCertTip();
_certError = string.Empty;
UpdateCertTip(ResUI.ServerNameMustBeValidDomain);
return;
}
if (SelectedSource.Port > 0)
{
domain += $":{SelectedSource.Port}";
}
(Cert, _certError) = await CertPemManager.Instance.GetCertPemAsync(domain, serverName);
UpdateCertTip();
string certError;
(Cert, certError) = await CertPemManager.Instance.GetCertPemAsync(domain, serverName);
UpdateCertTip(certError);
}
private async Task FetchCertChain()
@@ -168,17 +165,16 @@ public class AddServerViewModel : MyReactiveObject
}
if (!Utils.IsDomain(serverName))
{
_certError = ResUI.ServerNameMustBeValidDomain;
UpdateCertTip();
_certError = string.Empty;
UpdateCertTip(ResUI.ServerNameMustBeValidDomain);
return;
}
if (SelectedSource.Port > 0)
{
domain += $":{SelectedSource.Port}";
}
(var certs, _certError) = await CertPemManager.Instance.GetCertChainPemAsync(domain, serverName);
UpdateCertTip();
Cert = string.Join("\n", certs);
string certError;
(var certs, certError) = await CertPemManager.Instance.GetCertChainPemAsync(domain, serverName);
Cert = CertPemManager.ConcatenatePemChain(certs);
UpdateCertTip(certError);
}
}

View File

@@ -62,9 +62,9 @@ public class MainWindowViewModel : MyReactiveObject
[Reactive]
public int TabMainSelectedIndex { get; set; }
#endregion Menu
[Reactive] public bool BlIsWindows { get; set; }
private bool _hasNextReloadJob = false;
#endregion Menu
#region Init
@@ -72,6 +72,7 @@ public class MainWindowViewModel : MyReactiveObject
{
_config = AppManager.Instance.Config;
_updateView = updateView;
BlIsWindows = Utils.IsWindows();
#region WhenAnyValue && ReactiveCommand
@@ -268,7 +269,6 @@ public class MainWindowViewModel : MyReactiveObject
}
await RefreshServers();
SetReloadEnabled(true);
await Reload();
}
@@ -514,7 +514,7 @@ public class MainWindowViewModel : MyReactiveObject
{
ProcUtils.ProcessStart("xdg-open", path);
}
else if (Utils.IsOSX())
else if (Utils.IsMacOS())
{
ProcUtils.ProcessStart("open", path);
}
@@ -525,49 +525,59 @@ public class MainWindowViewModel : MyReactiveObject
#region core job
private bool _hasNextReloadJob = false;
private readonly SemaphoreSlim _reloadSemaphore = new(1, 1);
public async Task Reload()
{
//If there are unfinished reload job, marked with next job.
if (!BlReloadEnabled)
if (!await _reloadSemaphore.WaitAsync(0))
{
_hasNextReloadJob = true;
return;
}
SetReloadEnabled(false);
var msgs = await ActionPrecheckManager.Instance.Check(_config.IndexId);
if (msgs.Count > 0)
try
{
foreach (var msg in msgs)
SetReloadEnabled(false);
var msgs = await ActionPrecheckManager.Instance.Check(_config.IndexId);
if (msgs.Count > 0)
{
NoticeManager.Instance.SendMessage(msg);
foreach (var msg in msgs)
{
NoticeManager.Instance.SendMessage(msg);
}
NoticeManager.Instance.Enqueue(Utils.List2String(msgs.Take(10).ToList(), true));
return;
}
NoticeManager.Instance.Enqueue(Utils.List2String(msgs.Take(10).ToList(), true));
await Task.Run(async () =>
{
await LoadCore();
await SysProxyHandler.UpdateSysProxy(_config, false);
await Task.Delay(1000);
});
AppEvents.TestServerRequested.Publish();
var showClashUI = _config.IsRunningCore(ECoreType.sing_box);
if (showClashUI)
{
AppEvents.ProxiesReloadRequested.Publish();
}
ReloadResult(showClashUI);
}
finally
{
SetReloadEnabled(true);
return;
}
await Task.Run(async () =>
{
await LoadCore();
await SysProxyHandler.UpdateSysProxy(_config, false);
await Task.Delay(1000);
});
AppEvents.TestServerRequested.Publish();
var showClashUI = _config.IsRunningCore(ECoreType.sing_box);
if (showClashUI)
{
AppEvents.ProxiesReloadRequested.Publish();
}
ReloadResult(showClashUI);
SetReloadEnabled(true);
if (_hasNextReloadJob)
{
_hasNextReloadJob = false;
await Reload();
_reloadSemaphore.Release();
//If there is a next reload job, execute it.
if (_hasNextReloadJob)
{
_hasNextReloadJob = false;
await Reload();
}
}
}

View File

@@ -50,6 +50,7 @@ public class OptionSettingViewModel : MyReactiveObject
[Reactive] public bool EnableUpdateSubOnlyRemarksExist { get; set; }
[Reactive] public bool AutoHideStartup { get; set; }
[Reactive] public bool Hide2TrayWhenClose { get; set; }
[Reactive] public bool MacOSShowInDock { get; set; }
[Reactive] public bool EnableDragDropSort { get; set; }
[Reactive] public bool DoubleClick2Activate { get; set; }
[Reactive] public int AutoUpdateInterval { get; set; }
@@ -69,6 +70,15 @@ public class OptionSettingViewModel : MyReactiveObject
#endregion UI
#region UI visibility
[Reactive] public bool BlIsWindows { get; set; }
[Reactive] public bool BlIsLinux { get; set; }
[Reactive] public bool BlIsIsMacOS { get; set; }
[Reactive] public bool BlIsNonWindows { get; set; }
#endregion UI visibility
#region System proxy
[Reactive] public bool notProxyLocalAddress { get; set; }
@@ -108,6 +118,10 @@ public class OptionSettingViewModel : MyReactiveObject
{
_config = AppManager.Instance.Config;
_updateView = updateView;
BlIsWindows = Utils.IsWindows();
BlIsLinux = Utils.IsLinux();
BlIsIsMacOS = Utils.IsMacOS();
BlIsNonWindows = Utils.IsNonWindows();
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
{
@@ -169,6 +183,7 @@ public class OptionSettingViewModel : MyReactiveObject
EnableUpdateSubOnlyRemarksExist = _config.UiItem.EnableUpdateSubOnlyRemarksExist;
AutoHideStartup = _config.UiItem.AutoHideStartup;
Hide2TrayWhenClose = _config.UiItem.Hide2TrayWhenClose;
MacOSShowInDock = _config.UiItem.MacOSShowInDock;
EnableDragDropSort = _config.UiItem.EnableDragDropSort;
DoubleClick2Activate = _config.UiItem.DoubleClick2Activate;
AutoUpdateInterval = _config.GuiItem.AutoUpdateInterval;
@@ -330,6 +345,7 @@ public class OptionSettingViewModel : MyReactiveObject
_config.UiItem.EnableUpdateSubOnlyRemarksExist = EnableUpdateSubOnlyRemarksExist;
_config.UiItem.AutoHideStartup = AutoHideStartup;
_config.UiItem.Hide2TrayWhenClose = Hide2TrayWhenClose;
_config.UiItem.MacOSShowInDock = MacOSShowInDock;
_config.GuiItem.AutoUpdateInterval = AutoUpdateInterval;
_config.UiItem.EnableDragDropSort = EnableDragDropSort;
_config.UiItem.DoubleClick2Activate = DoubleClick2Activate;

View File

@@ -504,7 +504,7 @@ public class StatusBarViewModel : MyReactiveObject
{
return AppManager.Instance.LinuxSudoPwd.IsNotEmpty();
}
else if (Utils.IsOSX())
else if (Utils.IsMacOS())
{
return AppManager.Instance.LinuxSudoPwd.IsNotEmpty();
}

View File

@@ -26,7 +26,7 @@ public class WindowBase<TViewModel> : ReactiveWindow<TViewModel> where TViewMode
Height = sizeItem.Height;
var workingArea = (Screens.ScreenFromWindow(this) ?? Screens.Primary).WorkingArea;
var scaling = (Utils.IsOSX() ? null : VisualRoot?.RenderScaling) ?? 1.0;
var scaling = (Utils.IsMacOS() ? null : VisualRoot?.RenderScaling) ?? 1.0;
var x = workingArea.X + ((workingArea.Width - (Width * scaling)) / 2);
var y = workingArea.Y + ((workingArea.Height - (Height * scaling)) / 2);

View File

@@ -800,9 +800,11 @@
<Flyout>
<StackPanel>
<TextBlock
Width="400"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbCertPinningTips}" />
Text="{x:Static resx:ResUI.TbCertPinningTips}"
TextWrapping="Wrap" />
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
<Button
x:Name="btnFetchCert"
@@ -816,13 +818,10 @@
<TextBox
x:Name="txtCert"
Width="400"
MinHeight="100"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
AcceptsReturn="True"
Classes="TextArea"
MinLines="6"
TextWrapping="Wrap" />
TextWrapping="NoWrap" />
</StackPanel>
</Flyout>
</Button.Flyout>

View File

@@ -24,13 +24,8 @@
<DockPanel>
<DockPanel Margin="{StaticResource Margin8}" DockPanel.Dock="Top">
<ContentControl x:Name="conTheme" DockPanel.Dock="Right" />
<Menu Margin="0,1">
<MenuItem Padding="{StaticResource MarginLr8}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{x:Static resx:ResUI.menuServers}" />
</StackPanel>
</MenuItem.Header>
<Menu Margin="{StaticResource Margin4}">
<MenuItem Header="{x:Static resx:ResUI.menuServers}">
<MenuItem x:Name="menuAddServerViaClipboard" Header="{x:Static resx:ResUI.menuAddServerViaClipboard}" />
<MenuItem x:Name="menuAddServerViaScan" Header="{x:Static resx:ResUI.menuAddServerViaScan}" />
<MenuItem x:Name="menuAddServerViaImage" Header="{x:Static resx:ResUI.menuAddServerViaImage}" />
@@ -51,12 +46,7 @@
<MenuItem x:Name="menuAddAnytlsServer" Header="{x:Static resx:ResUI.menuAddAnytlsServer}" />
</MenuItem>
<MenuItem Padding="{StaticResource MarginLr8}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{x:Static resx:ResUI.menuSubscription}" />
</StackPanel>
</MenuItem.Header>
<MenuItem Header="{x:Static resx:ResUI.menuSubscription}">
<MenuItem x:Name="menuSubSetting" Header="{x:Static resx:ResUI.menuSubSetting}" />
<Separator />
<MenuItem x:Name="menuSubUpdate" Header="{x:Static resx:ResUI.menuSubUpdate}" />
@@ -65,20 +55,24 @@
<MenuItem x:Name="menuSubGroupUpdateViaProxy" Header="{x:Static resx:ResUI.menuSubGroupUpdateViaProxy}" />
</MenuItem>
<MenuItem Padding="{StaticResource MarginLr8}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{x:Static resx:ResUI.menuSetting}" />
</StackPanel>
</MenuItem.Header>
<MenuItem Header="{x:Static resx:ResUI.menuSetting}">
<MenuItem x:Name="menuOptionSetting" Header="{x:Static resx:ResUI.menuOptionSetting}" />
<MenuItem x:Name="menuRoutingSetting" Header="{x:Static resx:ResUI.menuRoutingSetting}" />
<MenuItem x:Name="menuDNSSetting" Header="{x:Static resx:ResUI.menuDNSSetting}" />
<MenuItem x:Name="menuFullConfigTemplate" Header="{x:Static resx:ResUI.menuFullConfigTemplate}" />
<MenuItem x:Name="menuGlobalHotkeySetting" Header="{x:Static resx:ResUI.menuGlobalHotkeySetting}" />
<MenuItem
x:Name="menuGlobalHotkeySetting"
Header="{x:Static resx:ResUI.menuGlobalHotkeySetting}"
IsVisible="{Binding BlIsWindows}" />
<Separator />
<MenuItem x:Name="menuRebootAsAdmin" Header="{x:Static resx:ResUI.menuRebootAsAdmin}" />
<MenuItem x:Name="menuSettingsSetUWP" Header="{x:Static resx:ResUI.TbSettingsSetUWP}" />
<MenuItem
x:Name="menuRebootAsAdmin"
Header="{x:Static resx:ResUI.menuRebootAsAdmin}"
IsVisible="{Binding BlIsWindows}" />
<MenuItem
x:Name="menuSettingsSetUWP"
Header="{x:Static resx:ResUI.TbSettingsSetUWP}"
IsVisible="{Binding BlIsWindows}" />
<MenuItem x:Name="menuClearServerStatistics" Header="{x:Static resx:ResUI.menuClearServerStatistics}" />
<Separator />
<MenuItem Header="{x:Static resx:ResUI.menuRegionalPresets}">
@@ -90,15 +84,16 @@
<MenuItem x:Name="menuOpenTheFileLocation" Header="{x:Static resx:ResUI.menuOpenTheFileLocation}" />
</MenuItem>
<MenuItem x:Name="menuReload" Padding="{StaticResource MarginLr8}" Header="{x:Static resx:ResUI.menuReload}" />
<MenuItem x:Name="menuReload" Header="{x:Static resx:ResUI.menuReload}" />
<MenuItem x:Name="menuCheckUpdate" Header="{x:Static resx:ResUI.menuCheckUpdate}" />
<MenuItem x:Name="menuHelp" Header="{x:Static resx:ResUI.menuHelp}">
<MenuItem x:Name="menuCheckUpdate" Header="{x:Static resx:ResUI.menuCheckUpdate}" />
<Separator />
</MenuItem>
<MenuItem x:Name="menuHelp" Padding="{StaticResource MarginLr8}" Header="{x:Static resx:ResUI.menuHelp}" />
<MenuItem x:Name="menuPromotion" Header="{x:Static resx:ResUI.menuPromotion}" />
<MenuItem x:Name="menuPromotion" Padding="{StaticResource MarginLr8}" Header="{x:Static resx:ResUI.menuPromotion}" />
<MenuItem x:Name="menuClose" Padding="{StaticResource MarginLr8}" Header="{x:Static resx:ResUI.menuExit}" />
<MenuItem x:Name="menuClose" Header="{x:Static resx:ResUI.menuExit}" />
</Menu>
</DockPanel>

View File

@@ -161,10 +161,6 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
else
{
Title = $"{Utils.GetVersion()}";
menuRebootAsAdmin.IsVisible = false;
menuSettingsSetUWP.IsVisible = false;
menuGlobalHotkeySetting.IsVisible = false;
}
menuAddServerViaScan.IsVisible = false;

View File

@@ -356,11 +356,11 @@
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
x:Name="tbAutoRunTip"
Grid.Row="1"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
IsVisible="{Binding BlIsWindows}"
Text="{x:Static resx:ResUI.TbSettingsStartBootTip}"
TextWrapping="Wrap" />
@@ -443,25 +443,41 @@
HorizontalAlignment="Left" />
<TextBlock
x:Name="labHide2TrayWhenClose"
Grid.Row="9"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
IsVisible="{Binding BlIsLinux}"
Text="{x:Static resx:ResUI.TbSettingsHide2TrayWhenClose}" />
<ToggleSwitch
x:Name="togHide2TrayWhenClose"
Grid.Row="9"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
HorizontalAlignment="Left"
IsVisible="{Binding BlIsLinux}" />
<TextBlock
x:Name="labHide2TrayWhenCloseTip"
Grid.Row="9"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
IsVisible="{Binding BlIsLinux}"
Text="{x:Static resx:ResUI.TbSettingsHide2TrayWhenCloseTip}" />
<TextBlock
Grid.Row="10"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
IsVisible="{Binding BlIsIsMacOS}"
Text="{x:Static resx:ResUI.TbSettingsMacOSShowInDock}" />
<ToggleSwitch
x:Name="togMacOSShowInDock"
Grid.Row="10"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
IsVisible="{Binding BlIsIsMacOS}"/>
<TextBlock
Grid.Row="11"
@@ -675,8 +691,8 @@
<TabItem Name="tabSystemproxy" Header="{x:Static resx:ResUI.TbSettingsSystemproxy}">
<DockPanel Margin="{StaticResource Margin8}">
<StackPanel
Name="panSystemProxyUnix"
DockPanel.Dock="Bottom"
IsVisible="{Binding BlIsNonWindows}"
Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock
@@ -690,7 +706,7 @@
HorizontalAlignment="Left"
VerticalAlignment="Center"
TextWrapping="Wrap"
Watermark="proxy_set.sh"/>
Watermark="proxy_set.sh" />
<Button
x:Name="btnBrowseCustomSystemProxyScriptPath"
Margin="{StaticResource Margin4}"
@@ -698,8 +714,8 @@
</StackPanel>
</StackPanel>
<StackPanel
Name="panSystemProxyAdvanced"
DockPanel.Dock="Bottom"
IsVisible="{Binding BlIsWindows}"
Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock
@@ -735,7 +751,7 @@
HorizontalAlignment="Left"
VerticalAlignment="Center"
TextWrapping="Wrap"
Watermark="pac.txt"/>
Watermark="pac.txt" />
<Button
x:Name="btnBrowseCustomSystemProxyPacPath"
Margin="{StaticResource Margin4}"
@@ -744,16 +760,16 @@
</StackPanel>
<TextBlock
Name="txbSettingsExceptionTip"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
DockPanel.Dock="Top"
IsVisible="{Binding BlIsWindows}"
Text="{x:Static resx:ResUI.TbSettingsExceptionTip}" />
<TextBlock
Name="txbSettingsExceptionTip2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
DockPanel.Dock="Top"
IsVisible="{Binding BlIsNonWindows}"
Text="{x:Static resx:ResUI.TbSettingsExceptionTip2}" />
<TextBox
x:Name="txtsystemProxyExceptions"

View File

@@ -88,6 +88,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
this.Bind(ViewModel, vm => vm.EnableUpdateSubOnlyRemarksExist, v => v.togEnableUpdateSubOnlyRemarksExist.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoHideStartup, v => v.togAutoHideStartup.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Hide2TrayWhenClose, v => v.togHide2TrayWhenClose.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.MacOSShowInDock, v => v.togMacOSShowInDock.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DoubleClick2Activate, v => v.togDoubleClick2Activate.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoUpdateInterval, v => v.txtautoUpdateInterval.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CurrentFontFamily, v => v.cmbcurrentFontFamily.Text).DisposeWith(disposables);
@@ -125,34 +126,6 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
});
if (Utils.IsWindows())
{
txbSettingsExceptionTip2.IsVisible = false;
labHide2TrayWhenClose.IsVisible = false;
togHide2TrayWhenClose.IsVisible = false;
labHide2TrayWhenCloseTip.IsVisible = false;
panSystemProxyUnix.IsVisible = false;
}
else if (Utils.IsLinux())
{
txbSettingsExceptionTip.IsVisible = false;
panSystemProxyAdvanced.IsVisible = false;
tbAutoRunTip.IsVisible = false;
}
else if (Utils.IsOSX())
{
txbSettingsExceptionTip.IsVisible = false;
panSystemProxyAdvanced.IsVisible = false;
tbAutoRunTip.IsVisible = false;
labHide2TrayWhenClose.IsVisible = false;
togHide2TrayWhenClose.IsVisible = false;
labHide2TrayWhenCloseTip.IsVisible = false;
}
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)

View File

@@ -14,17 +14,12 @@
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<DockPanel>
<StackPanel
Margin="{StaticResource Margin4}"
DockPanel.Dock="Top"
Orientation="Horizontal">
<Menu>
<MenuItem x:Name="menuRuleAdd" Header="{x:Static resx:ResUI.menuRuleAdd}" />
<MenuItem x:Name="menuImportRulesFromFile" Header="{x:Static resx:ResUI.menuImportRulesFromFile}" />
<MenuItem x:Name="menuImportRulesFromClipboard" Header="{x:Static resx:ResUI.menuImportRulesFromClipboard}" />
<MenuItem x:Name="menuImportRulesFromUrl" Header="{x:Static resx:ResUI.menuImportRulesFromUrl}" />
</Menu>
</StackPanel>
<Menu Margin="{StaticResource Margin4}" DockPanel.Dock="Top">
<MenuItem x:Name="menuRuleAdd" Header="{x:Static resx:ResUI.menuRuleAdd}" />
<MenuItem x:Name="menuImportRulesFromFile" Header="{x:Static resx:ResUI.menuImportRulesFromFile}" />
<MenuItem x:Name="menuImportRulesFromClipboard" Header="{x:Static resx:ResUI.menuImportRulesFromClipboard}" />
<MenuItem x:Name="menuImportRulesFromUrl" Header="{x:Static resx:ResUI.menuImportRulesFromUrl}" />
</Menu>
<StackPanel
Margin="{StaticResource Margin4}"

View File

@@ -15,16 +15,10 @@
mc:Ignorable="d">
<DockPanel>
<StackPanel
Margin="{StaticResource Margin4}"
DockPanel.Dock="Top"
Orientation="Horizontal"
Spacing="4">
<Menu>
<MenuItem x:Name="menuRoutingAdvancedAdd2" Header="{x:Static resx:ResUI.menuRoutingAdvancedAdd}" />
<MenuItem x:Name="menuRoutingAdvancedImportRules2" Header="{x:Static resx:ResUI.menuRoutingAdvancedImportRules}" />
</Menu>
</StackPanel>
<Menu Margin="{StaticResource Margin4}" DockPanel.Dock="Top">
<MenuItem x:Name="menuRoutingAdvancedAdd2" Header="{x:Static resx:ResUI.menuRoutingAdvancedAdd}" />
<MenuItem x:Name="menuRoutingAdvancedImportRules2" Header="{x:Static resx:ResUI.menuRoutingAdvancedImportRules}" />
</Menu>
<StackPanel
Margin="{StaticResource Margin4}"

View File

@@ -20,18 +20,13 @@
DisableOpeningAnimation="True"
Identifier="dialogHostSub">
<DockPanel Margin="{StaticResource Margin8}">
<StackPanel
Margin="{StaticResource Margin4}"
DockPanel.Dock="Top"
Orientation="Horizontal">
<Menu>
<MenuItem x:Name="menuSubAdd" Header="{x:Static resx:ResUI.menuSubAdd}" />
<MenuItem x:Name="menuSubDelete" Header="{x:Static resx:ResUI.menuSubDelete}" />
<MenuItem x:Name="menuSubEdit" Header="{x:Static resx:ResUI.menuSubEdit}" />
<MenuItem x:Name="menuSubShare" Header="{x:Static resx:ResUI.menuSubShare}" />
<MenuItem x:Name="menuClose" Header="{x:Static resx:ResUI.menuClose}" />
</Menu>
</StackPanel>
<Menu Margin="{StaticResource Margin4}" DockPanel.Dock="Top">
<MenuItem x:Name="menuSubAdd" Header="{x:Static resx:ResUI.menuSubAdd}" />
<MenuItem x:Name="menuSubDelete" Header="{x:Static resx:ResUI.menuSubDelete}" />
<MenuItem x:Name="menuSubEdit" Header="{x:Static resx:ResUI.menuSubEdit}" />
<MenuItem x:Name="menuSubShare" Header="{x:Static resx:ResUI.menuSubShare}" />
<MenuItem x:Name="menuClose" Header="{x:Static resx:ResUI.menuClose}" />
</Menu>
<DataGrid
x:Name="lstSubscription"
@@ -74,4 +69,4 @@
</DataGrid>
</DockPanel>
</dialogHost:DialogHost>
</Window>
</Window>

View File

@@ -1021,6 +1021,7 @@
Style="{StaticResource MaterialDesignToolForegroundPopupBox}">
<StackPanel>
<TextBlock
Width="400"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
@@ -1029,13 +1030,11 @@
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
<Button
x:Name="btnFetchCert"
Width="100"
Margin="{StaticResource MarginLeftRight4}"
Content="{x:Static resx:ResUI.TbFetchCert}"
Style="{StaticResource DefButton}" />
<Button
x:Name="btnFetchCertChain"
Width="100"
Margin="{StaticResource MarginLeftRight4}"
Content="{x:Static resx:ResUI.TbFetchCertChain}"
Style="{StaticResource DefButton}" />
@@ -1044,15 +1043,16 @@
x:Name="txtCert"
Width="400"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
AcceptsReturn="True"
HorizontalScrollBarVisibility="Auto"
MaxLines="18"
MinLines="6"
Style="{StaticResource MyOutlinedTextBox}"
TextWrapping="Wrap" />
TextWrapping="NoWrap"
VerticalScrollBarVisibility="Auto" />
</StackPanel>
</materialDesign:PopupBox>
</StackPanel>
</Grid>
<Grid
x:Name="gridRealityMore"

View File

@@ -246,23 +246,6 @@
</MenuItem>
</Menu>
<Separator />
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
<MenuItem
Name="menuCheckUpdate"
Padding="{StaticResource MarginLeftRight8}"
AutomationProperties.Name="{x:Static resx:ResUI.menuCheckUpdate}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon
Margin="{StaticResource MarginRight8}"
VerticalAlignment="Center"
Kind="Update" />
<TextBlock Text="{x:Static resx:ResUI.menuCheckUpdate}" />
</StackPanel>
</MenuItem.Header>
</MenuItem>
</Menu>
<Separator />
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
<MenuItem
x:Name="menuHelp"
@@ -277,6 +260,8 @@
<TextBlock Text="{x:Static resx:ResUI.menuHelp}" />
</StackPanel>
</MenuItem.Header>
<MenuItem x:Name="menuCheckUpdate" Header="{x:Static resx:ResUI.menuCheckUpdate}" />
<Separator Margin="-40,5" />
</MenuItem>
</Menu>
<Separator />