Reza Bakhshi Laktasaraei 2025-06-07 15:02:33 +03:30
commit ca8cbc3a16
56 changed files with 951 additions and 595 deletions

View file

@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found. the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.> <one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author> Copyright (C) 2019-Present 2dust
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode: notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author> v2rayN Copyright (C) 2019-Present 2dust
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details. under certain conditions; type `show c' for details.

View file

@ -1,16 +1,18 @@
# v2rayN # v2rayN
A GUI client for Windows, Linux and macOS, support [Xray](https://github.com/XTLS/Xray-core) and [sing-box](https://github.com/SagerNet/sing-box/releases) and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
A GUI client for Windows, Linux and macOS, support [Xray](https://github.com/XTLS/Xray-core)
and [sing-box](https://github.com/SagerNet/sing-box)
and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/2dust/v2rayN)](https://github.com/2dust/v2rayN/commits/master) [![GitHub commit activity](https://img.shields.io/github/commit-activity/m/2dust/v2rayN)](https://github.com/2dust/v2rayN/commits/master)
[![CodeFactor](https://www.codefactor.io/repository/github/2dust/v2rayn/badge)](https://www.codefactor.io/repository/github/2dust/v2rayn) [![CodeFactor](https://www.codefactor.io/repository/github/2dust/v2rayn/badge)](https://www.codefactor.io/repository/github/2dust/v2rayn)
[![GitHub Releases](https://img.shields.io/github/downloads/2dust/v2rayN/latest/total?logo=github)](https://github.com/2dust/v2rayN/releases) [![GitHub Releases](https://img.shields.io/github/downloads/2dust/v2rayN/latest/total?logo=github)](https://github.com/2dust/v2rayN/releases)
[![Chat on Telegram](https://img.shields.io/badge/Chat%20on-Telegram-brightgreen.svg)](https://t.me/v2rayn) [![Chat on Telegram](https://img.shields.io/badge/Chat%20on-Telegram-brightgreen.svg)](https://t.me/v2rayn)
## How to use ## How to use
Read the [Wiki](https://github.com/2dust/v2rayN/wiki) for details. Read the [Wiki](https://github.com/2dust/v2rayN/wiki) for details.
## Telegram Channel ## Telegram Channel
[github_2dust](https://t.me/github_2dust) [github_2dust](https://t.me/github_2dust)

View file

@ -5,21 +5,83 @@ internal static class Program
[STAThread] [STAThread]
private static void Main(string[] args) private static void Main(string[] args)
{ {
if (args.Length == 0) try
{ {
Console.WriteLine(Resx.Resource.Guidelines); // If no arguments are provided, display usage guidelines and exit
Thread.Sleep(5000); if (args.Length == 0)
return; {
} ShowHelp();
return;
}
var argData = Uri.UnescapeDataString(string.Join(" ", args)); // Log all arguments for debugging purposes
if (argData.Equals("rebootas")) foreach (var arg in args)
{
Console.WriteLine(arg);
}
// Parse command based on first argument
switch (args[0].ToLowerInvariant())
{
case "rebootas":
// Handle application restart
HandleRebootAsync();
break;
case "help":
case "--help":
case "-h":
case "/?":
// Display help information
ShowHelp();
break;
default:
// Default behavior: handle as upgrade data
// Maintain backward compatibility with existing usage pattern
var argData = Uri.UnescapeDataString(string.Join(" ", args));
HandleUpgrade(argData);
break;
}
}
catch (Exception ex)
{ {
Thread.Sleep(1000); // Global exception handling
Utils.StartV2RayN(); Console.WriteLine($"An error occurred: {ex.Message}");
return; Console.WriteLine("Press any key to exit...");
Console.ReadKey();
} }
}
UpgradeApp.Upgrade(argData); /// <summary>
/// Display help information and usage guidelines
/// </summary>
private static void ShowHelp()
{
Console.WriteLine(Resx.Resource.Guidelines);
Console.WriteLine("Available commands:");
Console.WriteLine(" rebootas - Restart the application");
Console.WriteLine(" help - Display this help information");
Thread.Sleep(5000);
}
/// <summary>
/// Handle application restart
/// </summary>
private static void HandleRebootAsync()
{
Console.WriteLine("Restarting application...");
Thread.Sleep(1000);
Utils.StartV2RayN();
}
/// <summary>
/// Handle application upgrade with the provided data
/// </summary>
/// <param name="upgradeData">Data for the upgrade process</param>
private static void HandleUpgrade(string upgradeData)
{
Console.WriteLine("Upgrading application...");
UpgradeApp.Upgrade(upgradeData);
} }
} }

View file

@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>7.11.3</Version> <Version>7.12.5</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

View file

@ -5,10 +5,10 @@
<CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled> <CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.2.8" /> <PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.0" />
<PackageVersion Include="Avalonia.Desktop" Version="11.2.8" /> <PackageVersion Include="Avalonia.Desktop" Version="11.3.0" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.2.8" /> <PackageVersion Include="Avalonia.Diagnostics" Version="11.3.0" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.2.8" /> <PackageVersion Include="Avalonia.ReactiveUI" Version="11.3.0" />
<PackageVersion Include="CliWrap" Version="3.8.2" /> <PackageVersion Include="CliWrap" Version="3.8.2" />
<PackageVersion Include="Downloader" Version="3.3.4" /> <PackageVersion Include="Downloader" Version="3.3.4" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" /> <PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" />
@ -18,8 +18,8 @@
<PackageVersion Include="ReactiveUI" Version="20.2.45" /> <PackageVersion Include="ReactiveUI" Version="20.2.45" />
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" /> <PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageVersion Include="ReactiveUI.WPF" Version="20.2.45" /> <PackageVersion Include="ReactiveUI.WPF" Version="20.2.45" />
<PackageVersion Include="Semi.Avalonia" Version="11.2.1.6" /> <PackageVersion Include="Semi.Avalonia" Version="11.2.1.7" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.6" /> <PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.7" />
<PackageVersion Include="Splat.NLog" Version="15.3.1" /> <PackageVersion Include="Splat.NLog" Version="15.3.1" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" /> <PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<PackageVersion Include="TaskScheduler" Version="2.12.1" /> <PackageVersion Include="TaskScheduler" Version="2.12.1" />

View file

@ -20,6 +20,7 @@ public enum EViewAction
BrowseServer, BrowseServer,
ImportRulesFromFile, ImportRulesFromFile,
InitSettingFont, InitSettingFont,
PasswordInput,
SubEditWindow, SubEditWindow,
RoutingRuleSettingWindow, RoutingRuleSettingWindow,
RoutingRuleDetailsWindow, RoutingRuleDetailsWindow,

View file

@ -8,9 +8,7 @@ public class Global
public const string GithubUrl = "https://github.com"; public const string GithubUrl = "https://github.com";
public const string GithubApiUrl = "https://api.github.com/repos"; public const string GithubApiUrl = "https://api.github.com/repos";
public const string GeoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/{0}.dat"; public const string GeoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/{0}.dat";
public const string SpeedPingTestUrl = @"https://www.google.com/generate_204";
public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-{0}/{1}.srs"; public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-{0}/{1}.srs";
public const string IPAPIUrl = "https://api.ip.sb/geoip";
public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw="; public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw=";
public const string ConfigFileName = "guiNConfig.json"; public const string ConfigFileName = "guiNConfig.json";
@ -519,5 +517,15 @@ public class Global
@"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb" @"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb"
]; ];
public static readonly List<string> IPAPIUrls =
[
@"https://speed.cloudflare.com/meta",
@"https://api.ip.sb/geoip",
@"https://api-ipv4.ip.sb/geoip",
@"https://api-ipv6.ip.sb/geoip",
@"https://api.ipapi.is",
@""
];
#endregion const #endregion const
} }

View file

@ -9,7 +9,6 @@ public sealed class AppHandler
private int? _statePort; private int? _statePort;
private int? _statePort2; private int? _statePort2;
private Job? _processJob; private Job? _processJob;
private bool? _isAdministrator;
public static AppHandler Instance => _instance.Value; public static AppHandler Instance => _instance.Value;
public Config Config => _config; public Config Config => _config;
@ -31,14 +30,7 @@ public sealed class AppHandler
} }
} }
public bool IsAdministrator public string LinuxSudoPwd { get; set; }
{
get
{
_isAdministrator ??= Utils.IsAdministrator();
return _isAdministrator.Value;
}
}
#endregion Property #endregion Property

View file

@ -122,7 +122,7 @@ public class ConfigHandler
} }
if (config.SpeedTestItem.SpeedPingTestUrl.IsNullOrEmpty()) if (config.SpeedTestItem.SpeedPingTestUrl.IsNullOrEmpty())
{ {
config.SpeedTestItem.SpeedPingTestUrl = Global.SpeedPingTestUrl; config.SpeedTestItem.SpeedPingTestUrl = Global.SpeedPingTestUrls.First();
} }
if (config.SpeedTestItem.MixedConcurrencyCount < 1) if (config.SpeedTestItem.MixedConcurrencyCount < 1)
{ {
@ -799,8 +799,11 @@ public class ConfigHandler
{ {
return -1; return -1;
} }
var lstServerStat = (config.GuiItem.EnableStatistics ? StatisticsHandler.Instance.ServerStat : null) ?? [];
var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs(); var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs();
var lstProfile = (from t in lstModel var lstProfile = (from t in lstModel
join t2 in lstServerStat on t.IndexId equals t2.IndexId into t2b
from t22 in t2b.DefaultIfEmpty()
join t3 in lstProfileExs on t.IndexId equals t3.IndexId into t3b join t3 in lstProfileExs on t.IndexId equals t3.IndexId into t3b
from t33 in t3b.DefaultIfEmpty() from t33 in t3b.DefaultIfEmpty()
select new ProfileItemModel select new ProfileItemModel
@ -815,7 +818,11 @@ public class ConfigHandler
StreamSecurity = t.StreamSecurity, StreamSecurity = t.StreamSecurity,
Delay = t33?.Delay ?? 0, Delay = t33?.Delay ?? 0,
Speed = t33?.Speed ?? 0, Speed = t33?.Speed ?? 0,
Sort = t33?.Sort ?? 0 Sort = t33?.Sort ?? 0,
TodayDown = (t22?.TodayDown ?? 0).ToString("D16"),
TodayUp = (t22?.TodayUp ?? 0).ToString("D16"),
TotalDown = (t22?.TotalDown ?? 0).ToString("D16"),
TotalUp = (t22?.TotalUp ?? 0).ToString("D16"),
}).ToList(); }).ToList();
Enum.TryParse(colName, true, out EServerColName name); Enum.TryParse(colName, true, out EServerColName name);
@ -833,6 +840,10 @@ public class ConfigHandler
EServerColName.DelayVal => lstProfile.OrderBy(t => t.Delay).ToList(), EServerColName.DelayVal => lstProfile.OrderBy(t => t.Delay).ToList(),
EServerColName.SpeedVal => lstProfile.OrderBy(t => t.Speed).ToList(), EServerColName.SpeedVal => lstProfile.OrderBy(t => t.Speed).ToList(),
EServerColName.SubRemarks => lstProfile.OrderBy(t => t.Subid).ToList(), EServerColName.SubRemarks => lstProfile.OrderBy(t => t.Subid).ToList(),
EServerColName.TodayDown => lstProfile.OrderBy(t => t.TodayDown).ToList(),
EServerColName.TodayUp => lstProfile.OrderBy(t => t.TodayUp).ToList(),
EServerColName.TotalDown => lstProfile.OrderBy(t => t.TotalDown).ToList(),
EServerColName.TotalUp => lstProfile.OrderBy(t => t.TotalUp).ToList(),
_ => lstProfile _ => lstProfile
}; };
} }
@ -849,6 +860,10 @@ public class ConfigHandler
EServerColName.DelayVal => lstProfile.OrderByDescending(t => t.Delay).ToList(), EServerColName.DelayVal => lstProfile.OrderByDescending(t => t.Delay).ToList(),
EServerColName.SpeedVal => lstProfile.OrderByDescending(t => t.Speed).ToList(), EServerColName.SpeedVal => lstProfile.OrderByDescending(t => t.Speed).ToList(),
EServerColName.SubRemarks => lstProfile.OrderByDescending(t => t.Subid).ToList(), EServerColName.SubRemarks => lstProfile.OrderByDescending(t => t.Subid).ToList(),
EServerColName.TodayDown => lstProfile.OrderByDescending(t => t.TodayDown).ToList(),
EServerColName.TodayUp => lstProfile.OrderByDescending(t => t.TodayUp).ToList(),
EServerColName.TotalDown => lstProfile.OrderByDescending(t => t.TotalDown).ToList(),
EServerColName.TotalUp => lstProfile.OrderByDescending(t => t.TotalUp).ToList(),
_ => lstProfile _ => lstProfile
}; };
} }

View file

@ -0,0 +1,42 @@
namespace ServiceLib.Handler;
public class ConnectionHandler
{
private static readonly Lazy<ConnectionHandler> _instance = new(() => new());
public static ConnectionHandler Instance => _instance.Value;
public async Task<string> RunAvailabilityCheck()
{
var downloadHandle = new DownloadService();
var time = await downloadHandle.RunAvailabilityCheck(null);
var ip = time > 0 ? await GetIPInfo(downloadHandle) ?? Global.None : Global.None;
return string.Format(ResUI.TestMeOutput, time, ip);
}
private async Task<string?> GetIPInfo(DownloadService downloadHandle)
{
var url = AppHandler.Instance.Config.SpeedTestItem.IPAPIUrl;
if (url.IsNullOrEmpty())
{
return null;
}
var result = await downloadHandle.TryDownloadString(url, true, "");
if (result == null)
{
return null;
}
var ipInfo = JsonUtils.Deserialize<IPAPIInfo>(result);
if (ipInfo == null)
{
return null;
}
var ip = ipInfo.ip ?? ipInfo.clientIp ?? ipInfo.ip_addr ?? ipInfo.query;
var country = ipInfo.country_code ?? ipInfo.country ?? ipInfo.countryCode ?? ipInfo.location?.country_code;
return $"({country ?? "unknown"}) {ip}";
}
}

View file

@ -0,0 +1,126 @@
using System.Diagnostics;
using System.Text;
using CliWrap;
namespace ServiceLib.Handler;
public class CoreAdminHandler
{
private static readonly Lazy<CoreAdminHandler> _instance = new(() => new());
public static CoreAdminHandler Instance => _instance.Value;
private Config _config;
private Action<bool, string>? _updateFunc;
private int _linuxSudoPid = -1;
public async Task Init(Config config, Action<bool, string> updateFunc)
{
if (_config != null)
{
return;
}
_config = config;
_updateFunc = updateFunc;
}
private void UpdateFunc(bool notify, string msg)
{
_updateFunc?.Invoke(notify, msg);
}
public async Task<Process?> RunProcessAsLinuxSudo(string fileName, CoreInfo coreInfo, string configPath)
{
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
Process proc = new()
{
StartInfo = new()
{
FileName = shFilePath,
Arguments = "",
WorkingDirectory = Utils.GetBinConfigPath(),
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
StandardInputEncoding = Encoding.UTF8,
StandardOutputEncoding = Encoding.UTF8,
StandardErrorEncoding = Encoding.UTF8,
}
};
proc.OutputDataReceived += (sender, e) =>
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
proc.ErrorDataReceived += (sender, e) =>
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
proc.Start();
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync();
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(AppHandler.Instance.LinuxSudoPwd);
await Task.Delay(100);
if (proc is null or { HasExited: true })
{
throw new Exception(ResUI.FailedToRunCore);
}
_linuxSudoPid = proc.Id;
return proc;
}
public async Task KillProcessAsLinuxSudo()
{
if (_linuxSudoPid < 0)
{
return;
}
var cmdLine = $"pkill -P {_linuxSudoPid} ; kill {_linuxSudoPid}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh");
await Cli.Wrap(shFilePath)
.WithStandardInputPipe(PipeSource.FromString(AppHandler.Instance.LinuxSudoPwd))
.ExecuteAsync();
_linuxSudoPid = -1;
}
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
{
var shFilePath = Utils.GetBinConfigPath(fileName);
File.Delete(shFilePath);
var sb = new StringBuilder();
sb.AppendLine("#!/bin/sh");
if (Utils.IsAdministrator())
{
sb.AppendLine($"{cmdLine}");
}
else
{
sb.AppendLine($"sudo -S {cmdLine}");
}
await File.WriteAllTextAsync(shFilePath, sb.ToString());
await Utils.SetLinuxChmod(shFilePath);
return shFilePath;
}
}

View file

@ -13,7 +13,7 @@ public class CoreHandler
private Config _config; private Config _config;
private Process? _process; private Process? _process;
private Process? _processPre; private Process? _processPre;
private int _linuxSudoPid = -1; private bool _linuxSudo = false;
private Action<bool, string>? _updateFunc; private Action<bool, string>? _updateFunc;
private const string _tag = "CoreHandler"; private const string _tag = "CoreHandler";
@ -155,23 +155,23 @@ public class CoreHandler
{ {
try try
{ {
if (_linuxSudo)
{
await CoreAdminHandler.Instance.KillProcessAsLinuxSudo();
_linuxSudo = false;
}
if (_process != null) if (_process != null)
{ {
await ProcUtils.ProcessKill(_process, true); await ProcUtils.ProcessKill(_process, Utils.IsWindows());
_process = null; _process = null;
} }
if (_processPre != null) if (_processPre != null)
{ {
await ProcUtils.ProcessKill(_processPre, true); await ProcUtils.ProcessKill(_processPre, Utils.IsWindows());
_processPre = null; _processPre = null;
} }
if (_linuxSudoPid > 0)
{
await KillProcessAsLinuxSudo();
}
_linuxSudoPid = -1;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -225,15 +225,6 @@ public class CoreHandler
_updateFunc?.Invoke(notify, msg); _updateFunc?.Invoke(notify, msg);
} }
private bool IsNeedSudo(ECoreType eCoreType)
{
return _config.TunModeItem.EnableTun
&& eCoreType == ECoreType.sing_box
&& (Utils.IsNonWindows())
//&& _config.TunModeItem.LinuxSudoPwd.IsNotEmpty()
;
}
#endregion Private #endregion Private
#region Process #region Process
@ -249,69 +240,17 @@ public class CoreHandler
try try
{ {
Process proc = new() if (mayNeedSudo
&& _config.TunModeItem.EnableTun
&& coreInfo.CoreType == ECoreType.sing_box
&& Utils.IsNonWindows())
{ {
StartInfo = new() _linuxSudo = true;
{ await CoreAdminHandler.Instance.Init(_config, _updateFunc);
FileName = fileName, return await CoreAdminHandler.Instance.RunProcessAsLinuxSudo(fileName, coreInfo, configPath);
Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath),
WorkingDirectory = Utils.GetBinConfigPath(),
UseShellExecute = false,
RedirectStandardOutput = displayLog,
RedirectStandardError = displayLog,
CreateNoWindow = true,
StandardOutputEncoding = displayLog ? Encoding.UTF8 : null,
StandardErrorEncoding = displayLog ? Encoding.UTF8 : null,
}
};
var isNeedSudo = mayNeedSudo && IsNeedSudo(coreInfo.CoreType);
if (isNeedSudo)
{
await RunProcessAsLinuxSudo(proc, fileName, coreInfo, configPath);
} }
if (displayLog) return await RunProcessNormal(fileName, coreInfo, configPath, displayLog);
{
proc.OutputDataReceived += (sender, e) =>
{
if (e.Data.IsNullOrEmpty())
return;
UpdateFunc(false, e.Data + Environment.NewLine);
};
proc.ErrorDataReceived += (sender, e) =>
{
if (e.Data.IsNullOrEmpty())
return;
UpdateFunc(false, e.Data + Environment.NewLine);
};
}
proc.Start();
if (isNeedSudo && _config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
{
var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd);
}
if (isNeedSudo)
_linuxSudoPid = proc.Id;
if (displayLog)
{
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
}
await Task.Delay(500);
AppHandler.Instance.AddProcess(proc.Handle);
if (proc is null or { HasExited: true })
{
throw new Exception(ResUI.FailedToRunCore);
}
return proc;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -321,89 +260,57 @@ public class CoreHandler
} }
} }
#endregion Process private async Task<Process?> RunProcessNormal(string fileName, CoreInfo? coreInfo, string configPath, bool displayLog)
#region Linux
private async Task RunProcessAsLinuxSudo(Process proc, string fileName, CoreInfo coreInfo, string configPath)
{ {
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
proc.StartInfo.FileName = shFilePath;
proc.StartInfo.Arguments = "";
proc.StartInfo.WorkingDirectory = "";
if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
{
proc.StartInfo.StandardInputEncoding = Encoding.UTF8;
proc.StartInfo.RedirectStandardInput = true;
}
}
private async Task KillProcessAsLinuxSudo()
{
var cmdLine = $"kill {_linuxSudoPid}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh");
Process proc = new() Process proc = new()
{ {
StartInfo = new() StartInfo = new()
{ {
FileName = shFilePath, FileName = fileName,
Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath),
WorkingDirectory = Utils.GetBinConfigPath(),
UseShellExecute = false, UseShellExecute = false,
RedirectStandardOutput = displayLog,
RedirectStandardError = displayLog,
CreateNoWindow = true, CreateNoWindow = true,
StandardInputEncoding = Encoding.UTF8, StandardOutputEncoding = displayLog ? Encoding.UTF8 : null,
RedirectStandardInput = true StandardErrorEncoding = displayLog ? Encoding.UTF8 : null,
} }
}; };
if (displayLog)
{
proc.OutputDataReceived += (sender, e) =>
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
proc.ErrorDataReceived += (sender, e) =>
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
}
proc.Start(); proc.Start();
if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty()) if (displayLog)
{ {
try proc.BeginOutputReadLine();
{ proc.BeginErrorReadLine();
var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd);
}
catch (Exception)
{
// ignored
}
} }
var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10)); await Task.Delay(100);
await proc.WaitForExitAsync(timeout.Token); AppHandler.Instance.AddProcess(proc.Handle);
await Task.Delay(3000); if (proc is null or { HasExited: true })
{
throw new Exception(ResUI.FailedToRunCore);
}
return proc;
} }
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName) #endregion Process
{
//Shell scripts
var shFilePath = Utils.GetBinConfigPath(AppHandler.Instance.IsAdministrator ? "root_" + fileName : fileName);
File.Delete(shFilePath);
var sb = new StringBuilder();
sb.AppendLine("#!/bin/sh");
if (AppHandler.Instance.IsAdministrator)
{
sb.AppendLine($"{cmdLine}");
}
else if (_config.TunModeItem.LinuxSudoPwd.IsNullOrEmpty())
{
sb.AppendLine($"pkexec {cmdLine}");
}
else
{
sb.AppendLine($"sudo -S {cmdLine}");
}
await File.WriteAllTextAsync(shFilePath, sb.ToString());
await Utils.SetLinuxChmod(shFilePath);
Logging.SaveLog(shFilePath);
return shFilePath;
}
#endregion Linux
} }

View file

@ -6,9 +6,9 @@ public class VmessFmt : BaseFmt
{ {
msg = ResUI.ConfigurationFormatIncorrect; msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem? item; ProfileItem? item;
if (str.IndexOf('?') > 0 && str.IndexOf('&') > 0) if (str.IndexOf('@') > 0)
{ {
item = ResolveStdVmess(str); item = ResolveStdVmess(str) ?? ResolveVmess(str, out msg);
} }
else else
{ {

View file

@ -147,7 +147,6 @@ public class TunModeItem
public int Mtu { get; set; } public int Mtu { get; set; }
public bool EnableExInbound { get; set; } public bool EnableExInbound { get; set; }
public bool EnableIPv6Address { get; set; } public bool EnableIPv6Address { get; set; }
public string? LinuxSudoPwd { get; set; }
} }
[Serializable] [Serializable]
@ -157,6 +156,7 @@ public class SpeedTestItem
public string SpeedTestUrl { get; set; } public string SpeedTestUrl { get; set; }
public string SpeedPingTestUrl { get; set; } public string SpeedPingTestUrl { get; set; }
public int MixedConcurrencyCount { get; set; } public int MixedConcurrencyCount { get; set; }
public string IPAPIUrl { get; set; }
} }
[Serializable] [Serializable]

View file

@ -3,10 +3,17 @@ namespace ServiceLib.Models;
internal class IPAPIInfo internal class IPAPIInfo
{ {
public string? ip { get; set; } public string? ip { get; set; }
public string? city { get; set; } public string? clientIp { get; set; }
public string? region { get; set; } public string? ip_addr { get; set; }
public string? region_code { get; set; } public string? query { get; set; }
public string? country { get; set; } public string? country { get; set; }
public string? country_name { get; set; } public string? country_name { get; set; }
public string? country_code { get; set; } public string? country_code { get; set; }
public string? countryCode { get; set; }
public LocationInfo? location { get; set; }
}
public class LocationInfo
{
public string? country_code { get; set; }
} }

View file

@ -3201,6 +3201,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Current connection info test URL 的本地化字符串。
/// </summary>
public static string TbSettingsIPAPIUrl {
get {
return ResourceManager.GetString("TbSettingsIPAPIUrl", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Keep older entries when de-duplicating 的本地化字符串。 /// 查找类似 Keep older entries when de-duplicating 的本地化字符串。
/// </summary> /// </summary>
@ -3247,7 +3256,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 The password is encrypted and stored only in local files 的本地化字符串。 /// 查找类似 The password you entered cannot be verified, so make sure you enter it correctly. If the application does not work properly due to an incorrect input, please restart the application. The password will not be stored and you will need to enter it again after each restart. 的本地化字符串。
/// </summary> /// </summary>
public static string TbSettingsLinuxSudoPasswordTip { public static string TbSettingsLinuxSudoPasswordTip {
get { get {

View file

@ -1339,7 +1339,7 @@
<value>رمز عبور sudo سیستم</value> <value>رمز عبور sudo سیستم</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>رمز عبور رمزگذاری شده و فقط در فایل های محلی ذخیره می شود.</value> <value>رمز عبوری که وارد کرده اید تایید نمی شود، بنابراین مطمئن شوید که آن را به درستی وارد کرده اید. اگر برنامه به دلیل ورودی نادرست به درستی کار نمی کند، لطفاً برنامه را مجدداً راه اندازی کنید. رمز عبور ذخیره نمی شود و پس از هر بار راه اندازی مجدد باید آن را دوباره وارد کنید.</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
<value>لطفاً ابتدا رمز عبور sudo را در تنظیمات حالت Tun تنظیم کنید</value> <value>لطفاً ابتدا رمز عبور sudo را در تنظیمات حالت Tun تنظیم کنید</value>
@ -1416,4 +1416,7 @@
<data name="menuExportConfig" xml:space="preserve"> <data name="menuExportConfig" xml:space="preserve">
<value>صادر کردن سرور</value> <value>صادر کردن سرور</value>
</data> </data>
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
<value>URL آزمایش اطلاعات اتصال فعلی</value>
</data>
</root> </root>

View file

@ -1339,7 +1339,7 @@
<value>Rendszer sudo jelszó</value> <value>Rendszer sudo jelszó</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>A jelszó titkosítva és csak a helyi fájlokban tárolva.</value> <value>The password you entered cannot be verified, so make sure you enter it correctly. If the application does not work properly due to an incorrect input, please restart the application. The password will not be stored and you will need to enter it again after each restart.</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
<value>Kérlek, először állítsd be a sudo jelszót a Tun módban</value> <value>Kérlek, először állítsd be a sudo jelszót a Tun módban</value>
@ -1416,4 +1416,7 @@
<data name="menuExportConfig" xml:space="preserve"> <data name="menuExportConfig" xml:space="preserve">
<value>Export server</value> <value>Export server</value>
</data> </data>
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
<value>Current connection info test URL</value>
</data>
</root> </root>

View file

@ -1339,7 +1339,7 @@
<value>System sudo password</value> <value>System sudo password</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>The password is encrypted and stored only in local files</value> <value>The password you entered cannot be verified, so make sure you enter it correctly. If the application does not work properly due to an incorrect input, please restart the application. The password will not be stored and you will need to enter it again after each restart.</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
<value>Please set the sudo password in Tun mode settings first</value> <value>Please set the sudo password in Tun mode settings first</value>
@ -1416,4 +1416,7 @@
<data name="menuExportConfig" xml:space="preserve"> <data name="menuExportConfig" xml:space="preserve">
<value>Export Configuration</value> <value>Export Configuration</value>
</data> </data>
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
<value>Current connection info test URL</value>
</data>
</root> </root>

View file

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
Version 2.0 Version 2.0
The primary goals of this format is to allow a simple XML format The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes various data types are done through the TypeConverter classes
associated with the data types. associated with the data types.
Example: Example:
... ado.net/XML headers & schema ... ... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader> <resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader> <resheader name="version">2.0</resheader>
@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment> <comment>This is a comment</comment>
</data> </data>
There are any number of "resheader" rows that contain simple There are any number of "resheader" rows that contain simple
name/value pairs. name/value pairs.
Each data row contains a name, and value. The row also contains a Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture. text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the Classes that don't support this are serialized and stored with the
mimetype set. mimetype set.
The mimetype is used for serialized objects, and tells the The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly: extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below. read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64 mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64 mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64 mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter : using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
--> -->
@ -118,16 +118,16 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="BatchExportURLSuccessfully" xml:space="preserve"> <data name="BatchExportURLSuccessfully" xml:space="preserve">
<value>Экспортирование URL в буфер обмена успешно завершено</value> <value>Ссылка успешно скопирована в буфер обмена</value>
</data> </data>
<data name="CheckServerSettings" xml:space="preserve"> <data name="CheckServerSettings" xml:space="preserve">
<value>Пожалуйста, сначала проверьте настройки сервера</value> <value>Сначала проверьте настройки сервера</value>
</data> </data>
<data name="ConfigurationFormatIncorrect" xml:space="preserve"> <data name="ConfigurationFormatIncorrect" xml:space="preserve">
<value>Недопустимый формат конфигурации</value> <value>Недопустимый формат конфигурации</value>
</data> </data>
<data name="CustomServerTips" xml:space="preserve"> <data name="CustomServerTips" xml:space="preserve">
<value>Обратите внимание, что пользовательская конфигурация полностью зависит от вашей собственной конфигурации и работает не со всеми настройками. Если вы хотите использовать системный прокси, пожалуйста, измените порт прослушивания вручную.</value> <value>Обратите внимание, что пользовательская конфигурация полностью зависит от вашей собственной конфигурации и работает не со всеми настройками. Если вы хотите использовать системный прокси, то измените порт прослушивания вручную</value>
</data> </data>
<data name="Downloading" xml:space="preserve"> <data name="Downloading" xml:space="preserve">
<value>Загрузка...</value> <value>Загрузка...</value>
@ -136,13 +136,13 @@
<value>Скорость загрузки</value> <value>Скорость загрузки</value>
</data> </data>
<data name="DownloadYesNo" xml:space="preserve"> <data name="DownloadYesNo" xml:space="preserve">
<value>Хотите загрузить? {0}</value> <value>Хотите загрузить {0}?</value>
</data> </data>
<data name="FailedConversionConfiguration" xml:space="preserve"> <data name="FailedConversionConfiguration" xml:space="preserve">
<value>Не удалось преобразовать файл конфигурации</value> <value>Не удалось преобразовать файл конфигурации</value>
</data> </data>
<data name="FailedGenDefaultConfiguration" xml:space="preserve"> <data name="FailedGenDefaultConfiguration" xml:space="preserve">
<value>Не удалось создать файл конфигурации по умолчаниюe</value> <value>Не удалось создать файл конфигурации по умолчанию</value>
</data> </data>
<data name="FailedGetDefaultConfiguration" xml:space="preserve"> <data name="FailedGetDefaultConfiguration" xml:space="preserve">
<value>Не удалось получить конфигурацию по умолчанию</value> <value>Не удалось получить конфигурацию по умолчанию</value>
@ -154,37 +154,37 @@
<value>Не удалось прочитать файл конфигурации</value> <value>Не удалось прочитать файл конфигурации</value>
</data> </data>
<data name="FillCorrectServerPort" xml:space="preserve"> <data name="FillCorrectServerPort" xml:space="preserve">
<value>Пожалуйста, укажите порт сервера в правильном формате</value> <value>Укажите порт сервера в правильном формате</value>
</data> </data>
<data name="FillLocalListeningPort" xml:space="preserve"> <data name="FillLocalListeningPort" xml:space="preserve">
<value>Пожалуйста, укажите локальный порт прослушивания</value> <value>Введите локальный порт для прослушивания</value>
</data> </data>
<data name="FillPassword" xml:space="preserve"> <data name="FillPassword" xml:space="preserve">
<value>Пожалуйста, введите пароль</value> <value>Введите пароль</value>
</data> </data>
<data name="FillServerAddress" xml:space="preserve"> <data name="FillServerAddress" xml:space="preserve">
<value>Пожалуйста, заполните адрес сервера</value> <value>Введите адрес сервера</value>
</data> </data>
<data name="FillUUID" xml:space="preserve"> <data name="FillUUID" xml:space="preserve">
<value>Пожалуйста, заполните идентификатор пользователя</value> <value>Введите идентификатор пользователя</value>
</data> </data>
<data name="Incorrectconfiguration" xml:space="preserve"> <data name="Incorrectconfiguration" xml:space="preserve">
<value>Некорректная конфигурация, пожалуйста, проверьте</value> <value>Некорректная конфигурация. Проверьте</value>
</data> </data>
<data name="InitialConfiguration" xml:space="preserve"> <data name="InitialConfiguration" xml:space="preserve">
<value>Исходная конфигурация</value> <value>Исходная конфигурация</value>
</data> </data>
<data name="IsLatestCore" xml:space="preserve"> <data name="IsLatestCore" xml:space="preserve">
<value>{0} {1} является последней версией.</value> <value>{0} {1} является последней версией</value>
</data> </data>
<data name="IsLatestN" xml:space="preserve"> <data name="IsLatestN" xml:space="preserve">
<value>{0} {1} является последней версией.</value> <value>{0} {1} является последней версией</value>
</data> </data>
<data name="LvAddress" xml:space="preserve"> <data name="LvAddress" xml:space="preserve">
<value>Адрес</value> <value>Адрес</value>
</data> </data>
<data name="LvEncryptionMethod" xml:space="preserve"> <data name="LvEncryptionMethod" xml:space="preserve">
<value>Безопасность</value> <value>Метод шифрования</value>
</data> </data>
<data name="LvPort" xml:space="preserve"> <data name="LvPort" xml:space="preserve">
<value>Порт</value> <value>Порт</value>
@ -199,7 +199,7 @@
<value>Загружено трафика сегодня</value> <value>Загружено трафика сегодня</value>
</data> </data>
<data name="LvTodayUploadDataAmount" xml:space="preserve"> <data name="LvTodayUploadDataAmount" xml:space="preserve">
<value>Отдано трафика сегодня</value> <value>Отправлено сегодня</value>
</data> </data>
<data name="LvTotalDownloadDataAmount" xml:space="preserve"> <data name="LvTotalDownloadDataAmount" xml:space="preserve">
<value>Всего загружено</value> <value>Всего загружено</value>
@ -214,13 +214,13 @@
<value>Ядро успешно загружено</value> <value>Ядро успешно загружено</value>
</data> </data>
<data name="MsgFailedImportSubscription" xml:space="preserve"> <data name="MsgFailedImportSubscription" xml:space="preserve">
<value>Не удалось импортировать содержимое подписки</value> <value>Не удалось импортировать подписку</value>
</data> </data>
<data name="MsgGetSubscriptionSuccessfully" xml:space="preserve"> <data name="MsgGetSubscriptionSuccessfully" xml:space="preserve">
<value>Содержимое подписки успешно импортировано</value> <value>Содержимое подписки успешно импортировано</value>
</data> </data>
<data name="MsgNoValidSubscription" xml:space="preserve"> <data name="MsgNoValidSubscription" xml:space="preserve">
<value>Нет установлены подписки</value> <value>Нет установленных подписок</value>
</data> </data>
<data name="MsgParsingSuccessfully" xml:space="preserve"> <data name="MsgParsingSuccessfully" xml:space="preserve">
<value>Парсинг {0} прошел успешно</value> <value>Парсинг {0} прошел успешно</value>
@ -235,7 +235,7 @@
<value>Некорректное содержимое подписки</value> <value>Некорректное содержимое подписки</value>
</data> </data>
<data name="MsgUnpacking" xml:space="preserve"> <data name="MsgUnpacking" xml:space="preserve">
<value>распаковывается...</value> <value>Распаковка…</value>
</data> </data>
<data name="MsgUpdateSubscriptionEnd" xml:space="preserve"> <data name="MsgUpdateSubscriptionEnd" xml:space="preserve">
<value>Обновление подписки закончено</value> <value>Обновление подписки закончено</value>
@ -244,37 +244,37 @@
<value>Обновление подписки начинается</value> <value>Обновление подписки начинается</value>
</data> </data>
<data name="MsgUpdateV2rayCoreSuccessfully" xml:space="preserve"> <data name="MsgUpdateV2rayCoreSuccessfully" xml:space="preserve">
<value>Успешное обновление ядра</value> <value>Ядро успешно обновлено</value>
</data> </data>
<data name="MsgUpdateV2rayCoreSuccessfullyMore" xml:space="preserve"> <data name="MsgUpdateV2rayCoreSuccessfullyMore" xml:space="preserve">
<value>Успешное обновление ядра! Перезапуск службы...</value> <value>Успешное обновление ядра! Перезапуск службы...</value>
</data> </data>
<data name="NonvmessOrssProtocol" xml:space="preserve"> <data name="NonvmessOrssProtocol" xml:space="preserve">
<value>Не является протоколом Vmess или SS</value> <value>Не является протоколом VMess или Shadowsocks</value>
</data> </data>
<data name="NotFoundCore" xml:space="preserve"> <data name="NotFoundCore" xml:space="preserve">
<value>Файл Core (имя файла: {1}) не найден в папке ({0}), загрузите и поместите его в папку, адрес загрузки: {2}</value> <value>Файл ядра ({1}) не найден в папке {0}. Скачайте по адресу {2} и поместите его туда</value>
</data> </data>
<data name="NoValidQRcodeFound" xml:space="preserve"> <data name="NoValidQRcodeFound" xml:space="preserve">
<value>Сканирование завершено, не найден корректный QR код</value> <value>Сканирование завершено, не найден корректный QR код</value>
</data> </data>
<data name="OperationFailed" xml:space="preserve"> <data name="OperationFailed" xml:space="preserve">
<value> операция безуспешна, проверьте и попробуйте ещё раз</value> <value>Операция безуспешна, проверьте и попробуйте ещё раз</value>
</data> </data>
<data name="PleaseFillRemarks" xml:space="preserve"> <data name="PleaseFillRemarks" xml:space="preserve">
<value>Пожалуйста, заполните примечания</value> <value>Введите примечания</value>
</data> </data>
<data name="PleaseSelectEncryption" xml:space="preserve"> <data name="PleaseSelectEncryption" xml:space="preserve">
<value>Пожалуйста, выберите метод шифрования</value> <value>Выберите метод шифрования</value>
</data> </data>
<data name="PleaseSelectProtocol" xml:space="preserve"> <data name="PleaseSelectProtocol" xml:space="preserve">
<value>Пожалуйста, выберите протокол</value> <value>Выберите протокол</value>
</data> </data>
<data name="PleaseSelectServer" xml:space="preserve"> <data name="PleaseSelectServer" xml:space="preserve">
<value>Сначала выберите сервер</value> <value>Сначала выберите сервер</value>
</data> </data>
<data name="RemoveDuplicateServerResult" xml:space="preserve"> <data name="RemoveDuplicateServerResult" xml:space="preserve">
<value>Удаление дублей завершено. Старая: {0}, Новая: {1}.</value> <value>Удаление дублей завершено. Старая: {0}, Новая: {1}</value>
</data> </data>
<data name="RemoveServer" xml:space="preserve"> <data name="RemoveServer" xml:space="preserve">
<value>Вы уверены, что хотите удалить сервер?</value> <value>Вы уверены, что хотите удалить сервер?</value>
@ -286,16 +286,16 @@
<value>Запуск сервиса ({0})...</value> <value>Запуск сервиса ({0})...</value>
</data> </data>
<data name="SuccessfulConfiguration" xml:space="preserve"> <data name="SuccessfulConfiguration" xml:space="preserve">
<value>Конфигурация успешна {0}</value> <value>Конфигурация выполнена успешно {0}</value>
</data> </data>
<data name="SuccessfullyImportedCustomServer" xml:space="preserve"> <data name="SuccessfullyImportedCustomServer" xml:space="preserve">
<value>Пользовательский сервер конфигурации успешно импортирован.</value> <value>Пользовательская конфигурация сервера успешно импортирована</value>
</data> </data>
<data name="SuccessfullyImportedServerViaClipboard" xml:space="preserve"> <data name="SuccessfullyImportedServerViaClipboard" xml:space="preserve">
<value>{0} серверов импортировано из буфера обмена.</value> <value>{0} серверов импортировано из буфера обмена</value>
</data> </data>
<data name="SuccessfullyImportedServerViaScan" xml:space="preserve"> <data name="SuccessfullyImportedServerViaScan" xml:space="preserve">
<value>Сканирование URL-адреса импорта успешна.</value> <value>Сканирование URL-адреса импорта прошло успешно</value>
</data> </data>
<data name="TestMeOutput" xml:space="preserve"> <data name="TestMeOutput" xml:space="preserve">
<value>Задержка текущего сервера: {0} мс, {1}</value> <value>Задержка текущего сервера: {0} мс, {1}</value>
@ -310,7 +310,7 @@
<value>Вы уверены, что хотите удалить правила?</value> <value>Вы уверены, что хотите удалить правила?</value>
</data> </data>
<data name="RoutingRuleDetailRequiredTips" xml:space="preserve"> <data name="RoutingRuleDetailRequiredTips" xml:space="preserve">
<value>{0}, Один из обязательных..</value> <value>{0}: одно из обязательных полей</value>
</data> </data>
<data name="LvRemarks" xml:space="preserve"> <data name="LvRemarks" xml:space="preserve">
<value>Примечания</value> <value>Примечания</value>
@ -322,13 +322,13 @@
<value>Количество</value> <value>Количество</value>
</data> </data>
<data name="MsgNeedUrl" xml:space="preserve"> <data name="MsgNeedUrl" xml:space="preserve">
<value>Пожалуйста, заполните адрес (Url)</value> <value>Введите URL-адрес</value>
</data> </data>
<data name="AddBatchRoutingRulesYesNo" xml:space="preserve"> <data name="AddBatchRoutingRulesYesNo" xml:space="preserve">
<value>Вы хотите добавить правила? Выберите «Да», чтобы добавить, выберите иное, чтобы заменить</value> <value>Хотите добавить правила? Выберите «Да» для добавления или «Нет» для замены</value>
</data> </data>
<data name="MsgDownloadGeoFileSuccessfully" xml:space="preserve"> <data name="MsgDownloadGeoFileSuccessfully" xml:space="preserve">
<value>Загрузка GeoFile: {0} успешна</value> <value>Загрузка GeoFile {0} прошла успешно</value>
</data> </data>
<data name="MsgInformationTitle" xml:space="preserve"> <data name="MsgInformationTitle" xml:space="preserve">
<value>Информация</value> <value>Информация</value>
@ -337,49 +337,49 @@
<value>Пользовательская иконка</value> <value>Пользовательская иконка</value>
</data> </data>
<data name="FillCorrectDNSText" xml:space="preserve"> <data name="FillCorrectDNSText" xml:space="preserve">
<value>Пожалуйста, заполните правильный пользовательский DNS</value> <value>Введите корректный пользовательский DNS</value>
</data> </data>
<data name="TransportPathTip1" xml:space="preserve"> <data name="TransportPathTip1" xml:space="preserve">
<value>*ws путь</value> <value>*WebSocket-путь</value>
</data> </data>
<data name="TransportPathTip2" xml:space="preserve"> <data name="TransportPathTip2" xml:space="preserve">
<value>*h2 путь</value> <value>*HTTP2-путь</value>
</data> </data>
<data name="TransportPathTip3" xml:space="preserve"> <data name="TransportPathTip3" xml:space="preserve">
<value>*QUIC ключ/Kcp seed</value> <value>*QUIC-ключ / KCP-seed</value>
</data> </data>
<data name="TransportPathTip4" xml:space="preserve"> <data name="TransportPathTip4" xml:space="preserve">
<value>*grpc serviceName</value> <value>Имя сервиса *gRPC</value>
</data> </data>
<data name="TransportRequestHostTip1" xml:space="preserve"> <data name="TransportRequestHostTip1" xml:space="preserve">
<value>*http хосты разделенные запятыми (,)</value> <value>*http-хосты, разделённые запятыми (,)</value>
</data> </data>
<data name="TransportRequestHostTip2" xml:space="preserve"> <data name="TransportRequestHostTip2" xml:space="preserve">
<value>*ws хост</value> <value>*WebSocket-хост</value>
</data> </data>
<data name="TransportRequestHostTip3" xml:space="preserve"> <data name="TransportRequestHostTip3" xml:space="preserve">
<value>*h2 хосты разделенные запятыми (,)</value> <value>*HTTP2-хосты, разделённые запятыми (,)</value>
</data> </data>
<data name="TransportRequestHostTip4" xml:space="preserve"> <data name="TransportRequestHostTip4" xml:space="preserve">
<value>* безопасность QUIC</value> <value>Безопасность *QUIC</value>
</data> </data>
<data name="TransportHeaderTypeTip1" xml:space="preserve"> <data name="TransportHeaderTypeTip1" xml:space="preserve">
<value>* тип TCP камуфляжа</value> <value>Тип *TCP-камуфляжа</value>
</data> </data>
<data name="TransportHeaderTypeTip2" xml:space="preserve"> <data name="TransportHeaderTypeTip2" xml:space="preserve">
<value>* тип KCP камуфляжа</value> <value>Тип *KCP-камуфляжа</value>
</data> </data>
<data name="TransportHeaderTypeTip3" xml:space="preserve"> <data name="TransportHeaderTypeTip3" xml:space="preserve">
<value>* тип QUIC камуфляжа</value> <value>Тип *QUIC-камуфляжа</value>
</data> </data>
<data name="TransportHeaderTypeTip4" xml:space="preserve"> <data name="TransportHeaderTypeTip4" xml:space="preserve">
<value>* режим grpc</value> <value>Режим *gRPC</value>
</data> </data>
<data name="LvTLS" xml:space="preserve"> <data name="LvTLS" xml:space="preserve">
<value>TLS</value> <value>TLS</value>
</data> </data>
<data name="TransportPathTip5" xml:space="preserve"> <data name="TransportPathTip5" xml:space="preserve">
<value>*Kcp seed</value> <value>*KCP-seed</value>
</data> </data>
<data name="RegisterGlobalHotkeyFailed" xml:space="preserve"> <data name="RegisterGlobalHotkeyFailed" xml:space="preserve">
<value>Не удалось зарегистрировать глобальную горячую клавишу {0}, причина: {1}</value> <value>Не удалось зарегистрировать глобальную горячую клавишу {0}, причина: {1}</value>
@ -391,10 +391,10 @@
<value>Разгруппировано</value> <value>Разгруппировано</value>
</data> </data>
<data name="AllGroupServers" xml:space="preserve"> <data name="AllGroupServers" xml:space="preserve">
<value>Все сервера</value> <value>Все серверы</value>
</data> </data>
<data name="FillServerAddressCustom" xml:space="preserve"> <data name="FillServerAddressCustom" xml:space="preserve">
<value>Пожалуйста, просмотрите, чтобы импортировать конфигурацию сервера</value> <value>Выберите файл конфигурации сервера для импорта</value>
</data> </data>
<data name="Speedtesting" xml:space="preserve"> <data name="Speedtesting" xml:space="preserve">
<value>Тестирование...</value> <value>Тестирование...</value>
@ -436,7 +436,7 @@
<value>Настройки маршрутизации</value> <value>Настройки маршрутизации</value>
</data> </data>
<data name="menuServers" xml:space="preserve"> <data name="menuServers" xml:space="preserve">
<value>Сервера</value> <value>Серверы</value>
</data> </data>
<data name="menuSetting" xml:space="preserve"> <data name="menuSetting" xml:space="preserve">
<value>Настройки</value> <value>Настройки</value>
@ -445,7 +445,7 @@
<value>Обновить текущую подписку без прокси</value> <value>Обновить текущую подписку без прокси</value>
</data> </data>
<data name="menuSubGroupUpdateViaProxy" xml:space="preserve"> <data name="menuSubGroupUpdateViaProxy" xml:space="preserve">
<value>Обновить текущую подписку с прокси</value> <value>Обновить подписку через прокси</value>
</data> </data>
<data name="menuSubscription" xml:space="preserve"> <data name="menuSubscription" xml:space="preserve">
<value>Группа подписки</value> <value>Группа подписки</value>
@ -571,7 +571,7 @@
<value>Сортировка</value> <value>Сортировка</value>
</data> </data>
<data name="LvUserAgent" xml:space="preserve"> <data name="LvUserAgent" xml:space="preserve">
<value>User Agent</value> <value>Заголовок User-Agent</value>
</data> </data>
<data name="TbCancel" xml:space="preserve"> <data name="TbCancel" xml:space="preserve">
<value>Отмена</value> <value>Отмена</value>
@ -628,7 +628,7 @@
<value>TLS</value> <value>TLS</value>
</data> </data>
<data name="TipNetwork" xml:space="preserve"> <data name="TipNetwork" xml:space="preserve">
<value>* По-умолчанию TCP</value> <value>*По-умолчанию TCP</value>
</data> </data>
<data name="TbCoreType" xml:space="preserve"> <data name="TbCoreType" xml:space="preserve">
<value>Ядро</value> <value>Ядро</value>
@ -658,10 +658,10 @@
<value>Шифрования</value> <value>Шифрования</value>
</data> </data>
<data name="TbPreSocksPort" xml:space="preserve"> <data name="TbPreSocksPort" xml:space="preserve">
<value>txtPreSocksPort</value> <value>Порт SOCKS</value>
</data> </data>
<data name="TipPreSocksPort" xml:space="preserve"> <data name="TipPreSocksPort" xml:space="preserve">
<value>* После установки этого значения служба socks будет запущена с использованием Xray/sing-box(Tun) для обеспечения таких функций, как отображение скорости</value> <value>* После установки этого значения служба SOCKS будет запущена с использованием Xray/sing-box(TUN) для обеспечения таких функций, как отображение скорости</value>
</data> </data>
<data name="TbBrowse" xml:space="preserve"> <data name="TbBrowse" xml:space="preserve">
<value>Просмотр</value> <value>Просмотр</value>
@ -697,7 +697,7 @@
<value>Разрешить небезопасные</value> <value>Разрешить небезопасные</value>
</data> </data>
<data name="TbSettingsDomainStrategy4Freedom" xml:space="preserve"> <data name="TbSettingsDomainStrategy4Freedom" xml:space="preserve">
<value>Outbound Freedom domainStrategy</value> <value>«Freedom»: стратегия обработки доменов исходящего трафика</value>
</data> </data>
<data name="TbSettingsEnableAutoAdjustMainLvColWidth" xml:space="preserve"> <data name="TbSettingsEnableAutoAdjustMainLvColWidth" xml:space="preserve">
<value>Автоматически регулировать ширину столбца после обновления подписки</value> <value>Автоматически регулировать ширину столбца после обновления подписки</value>
@ -712,7 +712,7 @@
<value>Исключение. Не используйте прокси-сервер для адресов, начинающихся с (,), используйте точку с запятой (;)</value> <value>Исключение. Не используйте прокси-сервер для адресов, начинающихся с (,), используйте точку с запятой (;)</value>
</data> </data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve"> <data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>Display real-time speed</value> <value>Показывать скорость в реальном времени</value>
</data> </data>
<data name="TbSettingsKeepOlderDedupl" xml:space="preserve"> <data name="TbSettingsKeepOlderDedupl" xml:space="preserve">
<value>Сохранить старые при удалении дублей</value> <value>Сохранить старые при удалении дублей</value>
@ -721,10 +721,10 @@
<value>Записывать логи</value> <value>Записывать логи</value>
</data> </data>
<data name="TbSettingsLogLevel" xml:space="preserve"> <data name="TbSettingsLogLevel" xml:space="preserve">
<value>Уровень логгирования</value> <value>Уровень записи логов</value>
</data> </data>
<data name="TbSettingsMuxEnabled" xml:space="preserve"> <data name="TbSettingsMuxEnabled" xml:space="preserve">
<value>Включите мультиплексирование Mux</value> <value>Включить мультиплексирование Mux</value>
</data> </data>
<data name="TbSettingsN" xml:space="preserve"> <data name="TbSettingsN" xml:space="preserve">
<value>Настройки v2rayN</value> <value>Настройки v2rayN</value>
@ -733,16 +733,16 @@
<value>Пароль аутентификации</value> <value>Пароль аутентификации</value>
</data> </data>
<data name="TbSettingsRemoteDNS" xml:space="preserve"> <data name="TbSettingsRemoteDNS" xml:space="preserve">
<value>Пользовательский DNS (если несколько делите с запятыми (,))</value> <value>Пользовательский DNS (если несколько, то делите запятыми (,))</value>
</data> </data>
<data name="TbSettingsSetUWP" xml:space="preserve"> <data name="TbSettingsSetUWP" xml:space="preserve">
<value>Set Win10 UWP Loopback</value> <value>Разрешить loopback для приложений UWP (Win10)</value>
</data> </data>
<data name="TbSettingsSniffingEnabled" xml:space="preserve"> <data name="TbSettingsSniffingEnabled" xml:space="preserve">
<value>Включить сниффинг</value> <value>Включить сниффинг</value>
</data> </data>
<data name="TbSettingsSocksPort" xml:space="preserve"> <data name="TbSettingsSocksPort" xml:space="preserve">
<value>Mixed Port</value> <value>Смешанный порт</value>
</data> </data>
<data name="TbSettingsStartBoot" xml:space="preserve"> <data name="TbSettingsStartBoot" xml:space="preserve">
<value>Автозапуск</value> <value>Автозапуск</value>
@ -751,7 +751,7 @@
<value>Включить статистику (требуется перезагрузка)</value> <value>Включить статистику (требуется перезагрузка)</value>
</data> </data>
<data name="TbSettingsSubConvert" xml:space="preserve"> <data name="TbSettingsSubConvert" xml:space="preserve">
<value>URL-адрес конверсии подписки</value> <value>URL конвертации подписок</value>
</data> </data>
<data name="TbSettingsSystemproxy" xml:space="preserve"> <data name="TbSettingsSystemproxy" xml:space="preserve">
<value>Настройки системного прокси</value> <value>Настройки системного прокси</value>
@ -766,7 +766,7 @@
<value>Включить UDP</value> <value>Включить UDP</value>
</data> </data>
<data name="TbSettingsUser" xml:space="preserve"> <data name="TbSettingsUser" xml:space="preserve">
<value>Auth user</value> <value>Имя пользователя (логин)</value>
</data> </data>
<data name="TbClearSystemProxy" xml:space="preserve"> <data name="TbClearSystemProxy" xml:space="preserve">
<value>Очистить системный прокси</value> <value>Очистить системный прокси</value>
@ -816,6 +816,9 @@
<data name="menuMoveUp" xml:space="preserve"> <data name="menuMoveUp" xml:space="preserve">
<value>Вверх (U)</value> <value>Вверх (U)</value>
</data> </data>
<data name="menuMoveTo" xml:space="preserve">
<value>Переместить вверх/вниз</value>
</data>
<data name="MsgFilterTitle" xml:space="preserve"> <data name="MsgFilterTitle" xml:space="preserve">
<value>Фильтр, поддерживает regex</value> <value>Фильтр, поддерживает regex</value>
</data> </data>
@ -850,13 +853,13 @@
<value>2.Прямой домен или IP</value> <value>2.Прямой домен или IP</value>
</data> </data>
<data name="TbRoutingTabProxy" xml:space="preserve"> <data name="TbRoutingTabProxy" xml:space="preserve">
<value>1.Прокси-домен или IP</value> <value>1.Прокси домен или IP</value>
</data> </data>
<data name="TbRoutingTabRuleList" xml:space="preserve"> <data name="TbRoutingTabRuleList" xml:space="preserve">
<value>Предустановленный список наборов правил</value> <value>Предустановленный список наборов правил</value>
</data> </data>
<data name="TbRoutingTips" xml:space="preserve"> <data name="TbRoutingTips" xml:space="preserve">
<value>* Установите правила, разделенные запятыми (,); Запятая в регулярке заменена на &lt;COMMA&gt;</value> <value>*Разделяйте правила запятыми (,). Литерал «,» — &lt;COMMA&gt;. Префикс # — отключить правило</value>
</data> </data>
<data name="menuImportRulesFromClipboard" xml:space="preserve"> <data name="menuImportRulesFromClipboard" xml:space="preserve">
<value>Импорт правил из буфера обмена</value> <value>Импорт правил из буфера обмена</value>
@ -883,16 +886,16 @@
<value>Удалить правила (Delete)</value> <value>Удалить правила (Delete)</value>
</data> </data>
<data name="menuRoutingRuleDetailsSetting" xml:space="preserve"> <data name="menuRoutingRuleDetailsSetting" xml:space="preserve">
<value>RoutingRuleDetailsSetting</value> <value>Детальные настройки правил маршрутизации</value>
</data> </data>
<data name="TbAutoSort" xml:space="preserve"> <data name="TbAutoSort" xml:space="preserve">
<value>Домен и IP автоматически сортируются при сохранении</value> <value>Домен и IP автоматически сортируются при сохранении</value>
</data> </data>
<data name="TbRuleobjectDoc" xml:space="preserve"> <data name="TbRuleobjectDoc" xml:space="preserve">
<value>Ruleobject Doc</value> <value>Документация RuleObject</value>
</data> </data>
<data name="TbDnsObjectDoc" xml:space="preserve"> <data name="TbDnsObjectDoc" xml:space="preserve">
<value>Поддержка DnsObject</value> <value>Поддерживаются DNS-объекты, нажмите для просмотра документации</value>
</data> </data>
<data name="SubUrlTips" xml:space="preserve"> <data name="SubUrlTips" xml:space="preserve">
<value>Необязательное поле</value> <value>Необязательное поле</value>
@ -913,16 +916,16 @@
<value>Тест задержки и скорости всех серверов (Ctrl+E)</value> <value>Тест задержки и скорости всех серверов (Ctrl+E)</value>
</data> </data>
<data name="LvTestDelay" xml:space="preserve"> <data name="LvTestDelay" xml:space="preserve">
<value>Задержка (ms)</value> <value>Задержка (мс)</value>
</data> </data>
<data name="LvTestSpeed" xml:space="preserve"> <data name="LvTestSpeed" xml:space="preserve">
<value>Скорость (M/s)</value> <value>Скорость (МБ/с)</value>
</data> </data>
<data name="FailedToRunCore" xml:space="preserve"> <data name="FailedToRunCore" xml:space="preserve">
<value>Не удалось запустить ядро, посмотрите логи</value> <value>Не удалось запустить ядро, посмотрите логи</value>
</data> </data>
<data name="LvFilter" xml:space="preserve"> <data name="LvFilter" xml:space="preserve">
<value>Remarks regular filter</value> <value>Фильтр примечаний (Regex)</value>
</data> </data>
<data name="TbDisplayLog" xml:space="preserve"> <data name="TbDisplayLog" xml:space="preserve">
<value>Показать логи</value> <value>Показать логи</value>
@ -934,7 +937,7 @@
<value>Новый порт для локальной сети</value> <value>Новый порт для локальной сети</value>
</data> </data>
<data name="TbSettingsTunMode" xml:space="preserve"> <data name="TbSettingsTunMode" xml:space="preserve">
<value>Настройки TunMode</value> <value>Настройки режима TUN</value>
</data> </data>
<data name="menuMoveToGroup" xml:space="preserve"> <data name="menuMoveToGroup" xml:space="preserve">
<value>Перейти в группу</value> <value>Перейти в группу</value>
@ -958,22 +961,22 @@
<value>Тест завершен</value> <value>Тест завершен</value>
</data> </data>
<data name="TbSettingsDefFingerprint" xml:space="preserve"> <data name="TbSettingsDefFingerprint" xml:space="preserve">
<value>TLS отпечаток по умлочанию</value> <value>TLS отпечаток по умолчанию</value>
</data> </data>
<data name="TbSettingsDefUserAgent" xml:space="preserve"> <data name="TbSettingsDefUserAgent" xml:space="preserve">
<value>User-Agent</value> <value>User-Agent</value>
</data> </data>
<data name="TbSettingsDefUserAgentTips" xml:space="preserve"> <data name="TbSettingsDefUserAgentTips" xml:space="preserve">
<value>Этот параметр действителен только для TCP/HTTP и WS</value> <value>Параметр действует только для TCP/HTTP и WebSocket (WS)</value>
</data> </data>
<data name="TbSettingsCurrentFontFamily" xml:space="preserve"> <data name="TbSettingsCurrentFontFamily" xml:space="preserve">
<value>Шрифт (требуется перезагрузка)</value> <value>Шрифт (требуется перезагрузка)</value>
</data> </data>
<data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve"> <data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve">
<value>Скопируйте файл шрифта TTF/TTC в каталог guiFonts, перезапустите настройки</value> <value>Скопируйте файл шрифта TTF/TTC в каталог guiFonts и заново откройте окно настроек</value>
</data> </data>
<data name="TbSettingsSocksPortTip" xml:space="preserve"> <data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>Pac порт = +3; Xray API порт = +4; mihomo API порт = +5;</value> <value>Pac порт = +3,Xray API порт = +4, mihomo API порт = +5</value>
</data> </data>
<data name="TbSettingsStartBootTip" xml:space="preserve"> <data name="TbSettingsStartBootTip" xml:space="preserve">
<value>Установите это с правами администратора</value> <value>Установите это с правами администратора</value>
@ -982,13 +985,10 @@
<value>Размер шрифта</value> <value>Размер шрифта</value>
</data> </data>
<data name="TbSettingsSpeedTestTimeout" xml:space="preserve"> <data name="TbSettingsSpeedTestTimeout" xml:space="preserve">
<value>Таймаут одиночного спидтеста</value> <value>Тайм-аут одиночного тестирования скорости</value>
</data> </data>
<data name="TbSettingsSpeedTestUrl" xml:space="preserve"> <data name="TbSettingsSpeedTestUrl" xml:space="preserve">
<value>URL спидтеста</value> <value>URL для тестирования скорости</value>
</data>
<data name="menuMoveTo" xml:space="preserve">
<value>Move up and down</value>
</data> </data>
<data name="TbPublicKey" xml:space="preserve"> <data name="TbPublicKey" xml:space="preserve">
<value>PublicKey</value> <value>PublicKey</value>
@ -1003,19 +1003,19 @@
<value>Включить аппаратное ускорение (требуется перезагрузка)</value> <value>Включить аппаратное ускорение (требуется перезагрузка)</value>
</data> </data>
<data name="SpeedtestingWait" xml:space="preserve"> <data name="SpeedtestingWait" xml:space="preserve">
<value>Ожидание тестирования (нажмите ESC для отмены)...</value> <value>Ожидание тестирования (нажмите ESC для отмены)</value>
</data> </data>
<data name="TipDisplayLog" xml:space="preserve"> <data name="TipDisplayLog" xml:space="preserve">
<value>Please turn off when there is an abnormal disconnection</value> <value>Отключите при аномальном разрыве соединения</value>
</data> </data>
<data name="MsgSkipSubscriptionUpdate" xml:space="preserve"> <data name="MsgSkipSubscriptionUpdate" xml:space="preserve">
<value>Updates are not enabled, skip this subscription</value> <value>Обновления не включены — подписка пропущена</value>
</data> </data>
<data name="menuRebootAsAdmin" xml:space="preserve"> <data name="menuRebootAsAdmin" xml:space="preserve">
<value>Перезагрузить как администратор</value> <value>Перезагрузить как администратор</value>
</data> </data>
<data name="LvMoreUrl" xml:space="preserve"> <data name="LvMoreUrl" xml:space="preserve">
<value>More URLs, separated by commas; Subscription conversion will be invalid</value> <value>Дополнительные URL через запятую, конвертация подписки недоступна</value>
</data> </data>
<data name="SpeedDisplayText" xml:space="preserve"> <data name="SpeedDisplayText" xml:space="preserve">
<value>{0} : {1}/s↑ | {2}/s↓</value> <value>{0} : {1}/s↑ | {2}/s↓</value>
@ -1024,13 +1024,13 @@
<value>Интервал автоматического обновления в минутах</value> <value>Интервал автоматического обновления в минутах</value>
</data> </data>
<data name="TbSettingsLogEnabledToFile" xml:space="preserve"> <data name="TbSettingsLogEnabledToFile" xml:space="preserve">
<value>Включить логгирование в файл</value> <value>Включить запись логов в файл</value>
</data> </data>
<data name="LvConvertTarget" xml:space="preserve"> <data name="LvConvertTarget" xml:space="preserve">
<value>Преобразовать тип цели</value> <value>Преобразовать тип цели</value>
</data> </data>
<data name="LvConvertTargetTip" xml:space="preserve"> <data name="LvConvertTargetTip" xml:space="preserve">
<value>Если преобразование не требуется, оставьте поле пустым.</value> <value>Если преобразование не требуется, оставьте поле пустым</value>
</data> </data>
<data name="menuDNSSetting" xml:space="preserve"> <data name="menuDNSSetting" xml:space="preserve">
<value>Настройки DNS</value> <value>Настройки DNS</value>
@ -1039,7 +1039,7 @@
<value>Настройки DNS sing-box</value> <value>Настройки DNS sing-box</value>
</data> </data>
<data name="TbDnsSingboxObjectDoc" xml:space="preserve"> <data name="TbDnsSingboxObjectDoc" xml:space="preserve">
<value>Пожалуйста, заполните структуру DNS. Нажмите, чтобы просмотреть документ.</value> <value>Заполните структуру DNS, нажмите, чтобы открыть документ</value>
</data> </data>
<data name="TbSettingDnsImportDefConfig" xml:space="preserve"> <data name="TbSettingDnsImportDefConfig" xml:space="preserve">
<value>Нажмите, чтобы импортировать конфигурацию DNS по умолчанию</value> <value>Нажмите, чтобы импортировать конфигурацию DNS по умолчанию</value>
@ -1048,88 +1048,88 @@
<value>Стратегия домена для sing-box</value> <value>Стратегия домена для sing-box</value>
</data> </data>
<data name="TbSettingsMux4SboxProtocol" xml:space="preserve"> <data name="TbSettingsMux4SboxProtocol" xml:space="preserve">
<value>sing-box Mux Protocol</value> <value>Протокол Mux для sing-box</value>
</data> </data>
<data name="TbRoutingRuleProcess" xml:space="preserve"> <data name="TbRoutingRuleProcess" xml:space="preserve">
<value>Full process name (Tun mode)</value> <value>Полное имя процесса (режим TUN)</value>
</data> </data>
<data name="TbRoutingRuleIP" xml:space="preserve"> <data name="TbRoutingRuleIP" xml:space="preserve">
<value>IP or IP CIDR</value> <value>IP-адрес или сеть CIDR</value>
</data> </data>
<data name="TbRoutingRuleDomain" xml:space="preserve"> <data name="TbRoutingRuleDomain" xml:space="preserve">
<value>Домен</value> <value>Домен</value>
</data> </data>
<data name="menuAddHysteria2Server" xml:space="preserve"> <data name="menuAddHysteria2Server" xml:space="preserve">
<value>Добавить [Hysteria2] сервер</value> <value>Добавить сервер [Hysteria2]</value>
</data> </data>
<data name="TbSettingsHysteriaBandwidth" xml:space="preserve"> <data name="TbSettingsHysteriaBandwidth" xml:space="preserve">
<value>Hysteria Max bandwidth (Up/Dw)</value> <value>Максимальная пропускная способность Hysteria (загрузка/отдача)</value>
</data> </data>
<data name="TbSettingsUseSystemHosts" xml:space="preserve"> <data name="TbSettingsUseSystemHosts" xml:space="preserve">
<value>Использовать системные узлы</value> <value>Использовать системные узлы</value>
</data> </data>
<data name="menuAddTuicServer" xml:space="preserve"> <data name="menuAddTuicServer" xml:space="preserve">
<value>Добавить [TUIC] сервер</value> <value>Добавить сервер [TUIC]</value>
</data> </data>
<data name="TbHeaderType8" xml:space="preserve"> <data name="TbHeaderType8" xml:space="preserve">
<value>Контроль перегрузок</value> <value>Контроль перегрузок</value>
</data> </data>
<data name="LvPrevProfile" xml:space="preserve"> <data name="LvPrevProfile" xml:space="preserve">
<value>Previous proxy remarks</value> <value>Примечания к предыдущему прокси</value>
</data> </data>
<data name="LvNextProfile" xml:space="preserve"> <data name="LvNextProfile" xml:space="preserve">
<value>Next proxy remarks</value> <value>Примечания к следующему прокси</value>
</data> </data>
<data name="LvPrevProfileTip" xml:space="preserve"> <data name="LvPrevProfileTip" xml:space="preserve">
<value>Пожалуйста, убедитесь, что примечание существует и является уникальным.</value> <value>Убедитесь, что примечание существует и является уникальным</value>
</data> </data>
<data name="TbSettingsEnableExInbound" xml:space="preserve"> <data name="TbSettingsEnableExInbound" xml:space="preserve">
<value>Enable additional Inbound</value> <value>Включить дополнительный входящий канал</value>
</data> </data>
<data name="TbSettingsEnableIPv6Address" xml:space="preserve"> <data name="TbSettingsEnableIPv6Address" xml:space="preserve">
<value>Включить IPv6 адреса</value> <value>Включить IPv6 адреса</value>
</data> </data>
<data name="menuAddWireguardServer" xml:space="preserve"> <data name="menuAddWireguardServer" xml:space="preserve">
<value>Добавить [WireGuard] сервер</value> <value>Добавить сервер [WireGuard]</value>
</data> </data>
<data name="TbPrivateKey" xml:space="preserve"> <data name="TbPrivateKey" xml:space="preserve">
<value>PrivateKey</value> <value>Приватный ключ</value>
</data> </data>
<data name="TbReserved" xml:space="preserve"> <data name="TbReserved" xml:space="preserve">
<value>Reserved(2,3,4)</value> <value>Зарезервировано (2, 3, 4)</value>
</data> </data>
<data name="TbLocalAddress" xml:space="preserve"> <data name="TbLocalAddress" xml:space="preserve">
<value>Адрес(Ipv4,Ipv6)</value> <value>Адрес (Ipv4,Ipv6)</value>
</data> </data>
<data name="TbPath7" xml:space="preserve"> <data name="TbPath7" xml:space="preserve">
<value>obfs password</value> <value>Пароль obfs</value>
</data> </data>
<data name="TbRuleMatchingTips" xml:space="preserve"> <data name="TbRuleMatchingTips" xml:space="preserve">
<value>(Domain or IP or ProcName) and Port and Protocol and InboundTag =&gt; OutboundTag</value> <value>(Домен или IP или имя процесса) и порт, и протокол, и InboundTag =&gt; OutboundTag</value>
</data> </data>
<data name="TbAutoScrollToEnd" xml:space="preserve"> <data name="TbAutoScrollToEnd" xml:space="preserve">
<value>Автоматическая прокрутка в конец</value> <value>Автоматическая прокрутка в конец</value>
</data> </data>
<data name="TbSettingsSpeedPingTestUrl" xml:space="preserve"> <data name="TbSettingsSpeedPingTestUrl" xml:space="preserve">
<value>URL-адрес для проверки скорости пинга</value> <value>URL для быстрой проверки реальной задержки</value>
</data> </data>
<data name="TbSettingsEnableUpdateSubOnlyRemarksExist" xml:space="preserve"> <data name="TbSettingsEnableUpdateSubOnlyRemarksExist" xml:space="preserve">
<value>Updating subscription, only determine remarks exists</value> <value>Обновляя подписку, проверять лишь наличие примечаний</value>
</data> </data>
<data name="SpeedtestingStop" xml:space="preserve"> <data name="SpeedtestingStop" xml:space="preserve">
<value>Отмена тестирования...</value> <value>Отмена тестирования...</value>
</data> </data>
<data name="TransportRequestHostTip5" xml:space="preserve"> <data name="TransportRequestHostTip5" xml:space="preserve">
<value>*grpc Authority</value> <value>*gRPC Authority</value>
</data> </data>
<data name="menuAddHttpServer" xml:space="preserve"> <data name="menuAddHttpServer" xml:space="preserve">
<value>Добавить [HTTP] сервер</value> <value>Добавить сервер [HTTP]</value>
</data> </data>
<data name="TbSettingsEnableFragmentTips" xml:space="preserve"> <data name="TbSettingsEnableFragmentTips" xml:space="preserve">
<value>Use Xray and enable non-Tun mode, which conflicts with the group previous proxy</value> <value>Используйте Xray и отключите режим TUN, так как он конфликтует с предыдущим прокси-сервером группы</value>
</data> </data>
<data name="TbSettingsEnableFragment" xml:space="preserve"> <data name="TbSettingsEnableFragment" xml:space="preserve">
<value>Включить фрагмент</value> <value>Включить фрагментацию (Fragment)</value>
</data> </data>
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve"> <data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
<value>Включить файл кэша для sing-box (файлы наборов правил)</value> <value>Включить файл кэша для sing-box (файлы наборов правил)</value>
@ -1138,7 +1138,7 @@
<value>Пользовательский набор правил для sing-box</value> <value>Пользовательский набор правил для sing-box</value>
</data> </data>
<data name="NeedRebootTips" xml:space="preserve"> <data name="NeedRebootTips" xml:space="preserve">
<value>Successful operation. Click the settings menu to reboot the app.</value> <value>Операция успешна. Перезапустите приложение</value>
</data> </data>
<data name="menuOpenTheFileLocation" xml:space="preserve"> <data name="menuOpenTheFileLocation" xml:space="preserve">
<value>Открыть место хранения</value> <value>Открыть место хранения</value>
@ -1147,7 +1147,7 @@
<value>Сортировка</value> <value>Сортировка</value>
</data> </data>
<data name="TbSortingChain" xml:space="preserve"> <data name="TbSortingChain" xml:space="preserve">
<value>Chain</value> <value>Цепочка</value>
</data> </data>
<data name="TbSortingDefault" xml:space="preserve"> <data name="TbSortingDefault" xml:space="preserve">
<value>По умолчанию</value> <value>По умолчанию</value>
@ -1159,7 +1159,7 @@
<value>Скорость загрузки</value> <value>Скорость загрузки</value>
</data> </data>
<data name="TbSortingDownTraffic" xml:space="preserve"> <data name="TbSortingDownTraffic" xml:space="preserve">
<value>Download Traffic</value> <value>Скачанный трафик</value>
</data> </data>
<data name="TbSortingHost" xml:space="preserve"> <data name="TbSortingHost" xml:space="preserve">
<value>Узел</value> <value>Узел</value>
@ -1177,10 +1177,10 @@
<value>Тип</value> <value>Тип</value>
</data> </data>
<data name="TbSortingUpSpeed" xml:space="preserve"> <data name="TbSortingUpSpeed" xml:space="preserve">
<value>Скорость загрузки</value> <value>Скорость отдачи</value>
</data> </data>
<data name="TbSortingUpTraffic" xml:space="preserve"> <data name="TbSortingUpTraffic" xml:space="preserve">
<value>Upload Traffic</value> <value>Отправленный трафик</value>
</data> </data>
<data name="TbConnections" xml:space="preserve"> <data name="TbConnections" xml:space="preserve">
<value>Соединения</value> <value>Соединения</value>
@ -1198,13 +1198,13 @@
<value>Режим правила</value> <value>Режим правила</value>
</data> </data>
<data name="menuModeDirect" xml:space="preserve"> <data name="menuModeDirect" xml:space="preserve">
<value>Direct</value> <value>Прямое соединение</value>
</data> </data>
<data name="menuModeGlobal" xml:space="preserve"> <data name="menuModeGlobal" xml:space="preserve">
<value>Global</value> <value>Глобальный режим</value>
</data> </data>
<data name="menuModeNothing" xml:space="preserve"> <data name="menuModeNothing" xml:space="preserve">
<value>Do not change</value> <value>Не менять</value>
</data> </data>
<data name="menuModeRule" xml:space="preserve"> <data name="menuModeRule" xml:space="preserve">
<value>Правила</value> <value>Правила</value>
@ -1213,13 +1213,13 @@
<value>Тест задержки</value> <value>Тест задержки</value>
</data> </data>
<data name="menuProxiesDelaytestPart" xml:space="preserve"> <data name="menuProxiesDelaytestPart" xml:space="preserve">
<value>Part Node Latency Test</value> <value>Тест задержки выбранных узлов</value>
</data> </data>
<data name="menuProxiesReload" xml:space="preserve"> <data name="menuProxiesReload" xml:space="preserve">
<value>Обновить прокси</value> <value>Обновить прокси</value>
</data> </data>
<data name="menuProxiesSelectActivity" xml:space="preserve"> <data name="menuProxiesSelectActivity" xml:space="preserve">
<value>Активировать узел (Enter)</value> <value>Сделать узел активным (Enter)</value>
</data> </data>
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve"> <data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
<value>Стратегия домена по умолчанию для исходящих</value> <value>Стратегия домена по умолчанию для исходящих</value>
@ -1234,7 +1234,7 @@
<value>Автоматическая регулировка ширины столбца</value> <value>Автоматическая регулировка ширины столбца</value>
</data> </data>
<data name="menuExport2ShareUrlBase64" xml:space="preserve"> <data name="menuExport2ShareUrlBase64" xml:space="preserve">
<value>Export Base64-encoded Share Links to Clipboard</value> <value>Экспорт ссылок в формате Base64 в буфер обмена</value>
</data> </data>
<data name="menuExport2ClientConfigClipboard" xml:space="preserve"> <data name="menuExport2ClientConfigClipboard" xml:space="preserve">
<value>Экспортировать выбранный сервер для полной конфигурации в буфер обмена</value> <value>Экспортировать выбранный сервер для полной конфигурации в буфер обмена</value>
@ -1243,7 +1243,7 @@
<value>Показать или скрыть главное окно</value> <value>Показать или скрыть главное окно</value>
</data> </data>
<data name="TbPreSocksPort4Sub" xml:space="preserve"> <data name="TbPreSocksPort4Sub" xml:space="preserve">
<value>Пользовательская конфигурация порта socks</value> <value>Пользовательская конфигурация порта SOCKS</value>
</data> </data>
<data name="menuBackupAndRestore" xml:space="preserve"> <data name="menuBackupAndRestore" xml:space="preserve">
<value>Резервное копирование и восстановление</value> <value>Резервное копирование и восстановление</value>
@ -1255,28 +1255,28 @@
<value>Восстановить из файла</value> <value>Восстановить из файла</value>
</data> </data>
<data name="menuRemoteBackup" xml:space="preserve"> <data name="menuRemoteBackup" xml:space="preserve">
<value>Backup to remote (WebDAV)</value> <value>Резервное копирование на удалённый сервер (WebDAV)</value>
</data> </data>
<data name="menuRemoteRestore" xml:space="preserve"> <data name="menuRemoteRestore" xml:space="preserve">
<value>Restore from remote (WebDAV)</value> <value>Восстановить с удалённого сервера (WebDAV)</value>
</data> </data>
<data name="menuLocalBackupAndRestore" xml:space="preserve"> <data name="menuLocalBackupAndRestore" xml:space="preserve">
<value>Local</value> <value>Локально</value>
</data> </data>
<data name="menuRemoteBackupAndRestore" xml:space="preserve"> <data name="menuRemoteBackupAndRestore" xml:space="preserve">
<value>Remote (WebDAV)</value> <value>Удалённо (WebDAV)</value>
</data> </data>
<data name="LvWebDavUrl" xml:space="preserve"> <data name="LvWebDavUrl" xml:space="preserve">
<value>WebDav Url</value> <value>URL WebDAV</value>
</data> </data>
<data name="LvWebDavUserName" xml:space="preserve"> <data name="LvWebDavUserName" xml:space="preserve">
<value>WebDav User Name</value> <value>Имя пользователя WebDAV</value>
</data> </data>
<data name="LvWebDavPassword" xml:space="preserve"> <data name="LvWebDavPassword" xml:space="preserve">
<value>WebDav Password</value> <value>Пароль WebDAV</value>
</data> </data>
<data name="LvWebDavCheck" xml:space="preserve"> <data name="LvWebDavCheck" xml:space="preserve">
<value>WebDav Check</value> <value>Проверить WebDAV</value>
</data> </data>
<data name="LvWebDavDirName" xml:space="preserve"> <data name="LvWebDavDirName" xml:space="preserve">
<value>Имя удаленной папки (необязательно)</value> <value>Имя удаленной папки (необязательно)</value>
@ -1285,16 +1285,16 @@
<value>Неверный файл резервной копии</value> <value>Неверный файл резервной копии</value>
</data> </data>
<data name="ConnectionsHostFilterTitle" xml:space="preserve"> <data name="ConnectionsHostFilterTitle" xml:space="preserve">
<value>Host filter</value> <value>Фильтр хостов</value>
</data> </data>
<data name="TipActiveServer" xml:space="preserve"> <data name="TipActiveServer" xml:space="preserve">
<value>Active</value> <value>Активный</value>
</data> </data>
<data name="TbSettingsGeoFilesSource" xml:space="preserve"> <data name="TbSettingsGeoFilesSource" xml:space="preserve">
<value>Источник geo файлов</value> <value>Источник файлов Geo (необязательно)</value>
</data> </data>
<data name="TbSettingsSrsFilesSource" xml:space="preserve"> <data name="TbSettingsSrsFilesSource" xml:space="preserve">
<value>Источник sing-box srs файлов</value> <value>Источник файлов наборов правил sing-box (необязательно)</value>
</data> </data>
<data name="UpgradeAppNotExistTip" xml:space="preserve"> <data name="UpgradeAppNotExistTip" xml:space="preserve">
<value>Программы для обновления не существует</value> <value>Программы для обновления не существует</value>
@ -1315,7 +1315,7 @@
<value>Иран</value> <value>Иран</value>
</data> </data>
<data name="TbSettingsChinaUserTip" xml:space="preserve"> <data name="TbSettingsChinaUserTip" xml:space="preserve">
<value>Используйте Настройки -&gt; Региональные пресеты вместо изменения этого поля</value> <value>Пользователи из Китая могут пропустить этот пункт</value>
</data> </data>
<data name="menuAddServerViaImage" xml:space="preserve"> <data name="menuAddServerViaImage" xml:space="preserve">
<value>Сканировать QR-код с изображения</value> <value>Сканировать QR-код с изображения</value>
@ -1324,7 +1324,7 @@
<value>Неверный адрес (Url)</value> <value>Неверный адрес (Url)</value>
</data> </data>
<data name="InsecureUrlProtocol" xml:space="preserve"> <data name="InsecureUrlProtocol" xml:space="preserve">
<value>Пожалуйста, не используйте небезопасный адрес подписки по протоколу HTTP.</value> <value>Не используйте небезопасный адрес подписки по протоколу HTTP</value>
</data> </data>
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve"> <data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
<value>Установите шрифт в систему и перезапустите настройки</value> <value>Установите шрифт в систему и перезапустите настройки</value>
@ -1333,43 +1333,43 @@
<value>Вы уверены, что хотите выйти?</value> <value>Вы уверены, что хотите выйти?</value>
</data> </data>
<data name="LvMemo" xml:space="preserve"> <data name="LvMemo" xml:space="preserve">
<value>Remarks Memo</value> <value>Заметка (Memo)</value>
</data> </data>
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve"> <data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
<value>System sudo password</value> <value>Пароль sudo системы</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>Пароль зашифрован и хранится только в локальных файлах..</value> <value>Введенный вами пароль не может быть подтвержден, поэтому убедитесь, что вы ввели его правильно. Если приложение не работает должным образом из-за неправильного ввода, то перезапустите его. Пароль не будет сохранен, и вам придется вводить его заново после каждого перезапуска</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
<value>Please set the sudo password in Tun mode settings first</value> <value>Сначала задайте пароль sudo в настройках TUN-режима</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordNotSudoRunApp" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordNotSudoRunApp" xml:space="preserve">
<value>Пожалуйста, не запускайте программу как суперпользователь.</value> <value>Не запускайте программу как суперпользователь</value>
</data> </data>
<data name="TransportHeaderTypeTip5" xml:space="preserve"> <data name="TransportHeaderTypeTip5" xml:space="preserve">
<value>*xhttp mode</value> <value>*XHTTP-режим</value>
</data> </data>
<data name="TransportExtraTip" xml:space="preserve"> <data name="TransportExtraTip" xml:space="preserve">
<value>XHTTP Extra raw JSON, format: { XHTTPObject }</value> <value>Дополнительный XHTTP сырой JSON, формат: { XHTTPObject }</value>
</data> </data>
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve"> <data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>Скрыть в трее при закрытии окна</value> <value>Скрыть в трее при закрытии окна</value>
</data> </data>
<data name="TbSettingsMixedConcurrencyCount" xml:space="preserve"> <data name="TbSettingsMixedConcurrencyCount" xml:space="preserve">
<value>The number of concurrent during multi-test</value> <value>Количество одновременно выполняемых тестов при многоэтапном тестировании</value>
</data> </data>
<data name="TbSettingsExceptionTip2" xml:space="preserve"> <data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>Исключение. Не используйте прокси-сервер для адресов с запятой (,)</value> <value>Исключение. Не используйте прокси-сервер для адресов с запятой (,)</value>
</data> </data>
<data name="TbSettingsDestOverride" xml:space="preserve"> <data name="TbSettingsDestOverride" xml:space="preserve">
<value>Sniffing type</value> <value>Тип сниффинга</value>
</data> </data>
<data name="TbSettingsSecondLocalPortEnabled" xml:space="preserve"> <data name="TbSettingsSecondLocalPortEnabled" xml:space="preserve">
<value>Enable second mixed port</value> <value>Включить второй смешанный порт</value>
</data> </data>
<data name="TbRoutingInboundTagTips" xml:space="preserve"> <data name="TbRoutingInboundTagTips" xml:space="preserve">
<value>socks: local port, socks2: second local port, socks3: LAN port</value> <value>socks: локальный порт, socks2: второй локальный порт, socks3: LAN порт</value>
</data> </data>
<data name="TbSettingsTheme" xml:space="preserve"> <data name="TbSettingsTheme" xml:space="preserve">
<value>Темы</value> <value>Темы</value>
@ -1378,7 +1378,7 @@
<value>Копировать команду прокси в буфер обмена</value> <value>Копировать команду прокси в буфер обмена</value>
</data> </data>
<data name="SpeedtestingTestFailedPart" xml:space="preserve"> <data name="SpeedtestingTestFailedPart" xml:space="preserve">
<value>Starting retesting failed parts, {0} remaining. Press ESC to terminate...</value> <value>Повторное тестирование неудачных элементов, осталось {0}. Нажмите ESC для остановки…</value>
</data> </data>
<data name="menuTestServerResult" xml:space="preserve"> <data name="menuTestServerResult" xml:space="preserve">
<value>По результату теста</value> <value>По результату теста</value>
@ -1387,33 +1387,36 @@
<value>Удалить недействительные по результатам теста</value> <value>Удалить недействительные по результатам теста</value>
</data> </data>
<data name="RemoveInvalidServerResultTip" xml:space="preserve"> <data name="RemoveInvalidServerResultTip" xml:space="preserve">
<value>Удалено {0} недействительных.</value> <value>Удалено {0} недействительных</value>
</data> </data>
<data name="TbPorts7" xml:space="preserve"> <data name="TbPorts7" xml:space="preserve">
<value>Диапазон портов сервера</value> <value>Диапазон портов сервера</value>
</data> </data>
<data name="TbPorts7Tips" xml:space="preserve"> <data name="TbPorts7Tips" xml:space="preserve">
<value>Will cover the port, separate with commas (,)</value> <value>Заменит указанный порт, перечисляйте через запятую (,)</value>
</data> </data>
<data name="menuSetDefaultMultipleServer" xml:space="preserve"> <data name="menuSetDefaultMultipleServer" xml:space="preserve">
<value>Multi-server to custom configuration</value> <value>От мультиконфигурации к пользовательской конфигурации</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayRandom" xml:space="preserve"> <data name="menuSetDefaultMultipleServerXrayRandom" xml:space="preserve">
<value>Multi-server Random by Xray</value> <value>Случайный (Xray)</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayRoundRobin" xml:space="preserve"> <data name="menuSetDefaultMultipleServerXrayRoundRobin" xml:space="preserve">
<value>Multi-server RoundRobin by Xray</value> <value>Круговой (Xray)</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayLeastPing" xml:space="preserve"> <data name="menuSetDefaultMultipleServerXrayLeastPing" xml:space="preserve">
<value>Multi-server LeastPing by Xray</value> <value>Минимальное RTT (минимальное время туда-обратно) (Xray)</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayLeastLoad" xml:space="preserve"> <data name="menuSetDefaultMultipleServerXrayLeastLoad" xml:space="preserve">
<value>Multi-server LeastLoad by Xray</value> <value>Минимальная нагрузка (Xray)</value>
</data> </data>
<data name="menuSetDefaultMultipleServerSingBoxLeastPing" xml:space="preserve"> <data name="menuSetDefaultMultipleServerSingBoxLeastPing" xml:space="preserve">
<value>Multi-server LeastPing by sing-box</value> <value>Минимальное RTT (минимальное время туда-обратно) (sing-box)</value>
</data> </data>
<data name="menuExportConfig" xml:space="preserve"> <data name="menuExportConfig" xml:space="preserve">
<value>Export server</value> <value>Экспортировать конфигурацию</value>
</data> </data>
</root> <data name="TbSettingsIPAPIUrl" xml:space="preserve">
<value>URL для тестирования текущего соединения</value>
</data>
</root>

View file

@ -1071,13 +1071,13 @@
<data name="TbHeaderType8" xml:space="preserve"> <data name="TbHeaderType8" xml:space="preserve">
<value>拥塞控制算法</value> <value>拥塞控制算法</value>
</data> </data>
<data name="LvPrevProfile" xml:space="preserve"> <data name="LvPrevProfile" xml:space="preserve">
<value>前置代理配置文件别名</value> <value>前置代理配置文件别名</value>
</data> </data>
<data name="LvNextProfile" xml:space="preserve"> <data name="LvNextProfile" xml:space="preserve">
<value>落地代理配置文件別名</value> <value>落地代理配置文件別名</value>
</data> </data>
<data name="LvPrevProfileTip" xml:space="preserve"> <data name="LvPrevProfileTip" xml:space="preserve">
<value>请确保配置文件别名存在并唯一</value> <value>请确保配置文件别名存在并唯一</value>
</data> </data>
<data name="TbSettingsEnableExInbound" xml:space="preserve"> <data name="TbSettingsEnableExInbound" xml:space="preserve">
@ -1336,7 +1336,7 @@
<value>系统的 sudo 密码</value> <value>系统的 sudo 密码</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>密码已加密且只存储在本地文件中,无密码则每次都要输入</value> <value>输入的密码无法校验,所以请确保输入正确。如果因为输入错误导致无法正常运行时,请重启本应用。 密码不会存储,每次重启后都需要再次输入。</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
<value>请先在 Tun 模式设置中设置 sudo 密码</value> <value>请先在 Tun 模式设置中设置 sudo 密码</value>
@ -1413,4 +1413,7 @@
<data name="menuExportConfig" xml:space="preserve"> <data name="menuExportConfig" xml:space="preserve">
<value>导出配置文件</value> <value>导出配置文件</value>
</data> </data>
</root> <data name="TbSettingsIPAPIUrl" xml:space="preserve">
<value>当前连接信息测试地址</value>
</data>
</root>

View file

@ -1071,13 +1071,13 @@
<data name="TbHeaderType8" xml:space="preserve"> <data name="TbHeaderType8" xml:space="preserve">
<value>擁塞控制算法</value> <value>擁塞控制算法</value>
</data> </data>
<data name="LvPrevProfile" xml:space="preserve"> <data name="LvPrevProfile" xml:space="preserve">
<value>前置代理設定檔別名</value> <value>前置代理設定檔別名</value>
</data> </data>
<data name="LvNextProfile" xml:space="preserve"> <data name="LvNextProfile" xml:space="preserve">
<value>落地代理設定檔別名</value> <value>落地代理設定檔別名</value>
</data> </data>
<data name="LvPrevProfileTip" xml:space="preserve"> <data name="LvPrevProfileTip" xml:space="preserve">
<value>請確保設定檔別名存在並且唯一</value> <value>請確保設定檔別名存在並且唯一</value>
</data> </data>
<data name="TbSettingsEnableExInbound" xml:space="preserve"> <data name="TbSettingsEnableExInbound" xml:space="preserve">
@ -1336,7 +1336,7 @@
<value>系統的 sudo 密碼</value> <value>系統的 sudo 密碼</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>密碼已加密且只儲存在本機檔案中,無密碼則每次都要輸入</value> <value>輸入的密碼無法校驗,所以請確保輸入正確。如果因為輸入錯誤導致無法正常運作時,請重新啟動本應用。 密碼不會存儲,每次重啟後都需要再次輸入。</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
<value>請先在 Tun 模式設定中設定 sudo 密碼</value> <value>請先在 Tun 模式設定中設定 sudo 密碼</value>
@ -1413,4 +1413,7 @@
<data name="menuExportConfig" xml:space="preserve"> <data name="menuExportConfig" xml:space="preserve">
<value>匯出設定檔</value> <value>匯出設定檔</value>
</data> </data>
</root> <data name="TbSettingsIPAPIUrl" xml:space="preserve">
<value>目前連接資訊測試地址</value>
</data>
</root>

View file

@ -6,11 +6,10 @@
"servers": [ "servers": [
{ {
"address": "1.1.1.1", "address": "1.1.1.1",
"skipFallback": true,
"domains": [ "domains": [
"geosite:geolocation-!cn" "domain:googleapis.cn",
], "domain:gstatic.com"
"expectIPs": [
"geoip:!cn"
] ]
}, },
{ {
@ -23,6 +22,7 @@
"geoip:cn" "geoip:cn"
] ]
}, },
"1.1.1.1",
"8.8.8.8", "8.8.8.8",
"https://dns.google/dns-query" "https://dns.google/dns-query"
] ]

View file

@ -95,6 +95,21 @@ detect_desktop_environment() {
return return
fi fi
if [[ "$XDG_CURRENT_DESKTOP" == *"UKUI"* ]] || [[ "$XDG_SESSION_DESKTOP" == *"ukui"* ]]; then
echo "gnome"
return
fi
if [[ "$XDG_CURRENT_DESKTOP" == *"DDE"* ]] || [[ "$XDG_SESSION_DESKTOP" == *"dde"* ]]; then
echo "gnome"
return
fi
if [[ "$XDG_CURRENT_DESKTOP" == *"MATE"* ]] || [[ "$XDG_SESSION_DESKTOP" == *"mate"* ]]; then
echo "gnome"
return
fi
local KDE_ENVIRONMENTS=("KDE" "plasma") local KDE_ENVIRONMENTS=("KDE" "plasma")
for ENV in "${KDE_ENVIRONMENTS[@]}"; do for ENV in "${KDE_ENVIRONMENTS[@]}"; do
if [ "$XDG_CURRENT_DESKTOP" == "$ENV" ] || [ "$XDG_SESSION_DESKTOP" == "$ENV" ]; then if [ "$XDG_CURRENT_DESKTOP" == "$ENV" ] || [ "$XDG_SESSION_DESKTOP" == "$ENV" ]; then

View file

@ -979,26 +979,6 @@ public class CoreConfigSingboxService
try try
{ {
var dnsOutbound = "dns_out"; var dnsOutbound = "dns_out";
if (!_config.Inbound.First().SniffingEnabled)
{
singboxConfig.route.rules.Add(new()
{
port = [53],
network = ["udp"],
outbound = dnsOutbound
});
}
singboxConfig.route.rules.Insert(0, new()
{
outbound = Global.DirectTag,
clash_mode = ERuleMode.Direct.ToString()
});
singboxConfig.route.rules.Insert(0, new()
{
outbound = Global.ProxyTag,
clash_mode = ERuleMode.Global.ToString()
});
if (_config.TunModeItem.EnableTun) if (_config.TunModeItem.EnableTun)
{ {
@ -1025,6 +1005,27 @@ public class CoreConfigSingboxService
}); });
} }
if (!_config.Inbound.First().SniffingEnabled)
{
singboxConfig.route.rules.Add(new()
{
port = [53],
network = ["udp"],
outbound = dnsOutbound
});
}
singboxConfig.route.rules.Add(new()
{
outbound = Global.DirectTag,
clash_mode = ERuleMode.Direct.ToString()
});
singboxConfig.route.rules.Add(new()
{
outbound = Global.ProxyTag,
clash_mode = ERuleMode.Global.ToString()
});
var routing = await ConfigHandler.GetDefaultRouting(_config); var routing = await ConfigHandler.GetDefaultRouting(_config);
if (routing != null) if (routing != null)
{ {
@ -1047,28 +1048,30 @@ public class CoreConfigSingboxService
private void GenRoutingDirectExe(out List<string> lstDnsExe, out List<string> lstDirectExe) private void GenRoutingDirectExe(out List<string> lstDnsExe, out List<string> lstDirectExe)
{ {
lstDnsExe = new(); var dnsExeSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
lstDirectExe = new(); var directExeSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo();
foreach (var it in coreInfo) var coreInfoResult = CoreInfoHandler.Instance.GetCoreInfo();
foreach (var coreConfig in coreInfoResult)
{ {
if (it.CoreType == ECoreType.v2rayN) if (coreConfig.CoreType == ECoreType.v2rayN)
{ {
continue; continue;
} }
foreach (var it2 in it.CoreExes)
{
if (!lstDnsExe.Contains(it2) && it.CoreType != ECoreType.sing_box)
{
lstDnsExe.Add($"{it2}.exe");
}
if (!lstDirectExe.Contains(it2)) foreach (var baseExeName in coreConfig.CoreExes)
{
if (coreConfig.CoreType != ECoreType.sing_box)
{ {
lstDirectExe.Add($"{it2}.exe"); dnsExeSet.Add(Utils.GetExeName(baseExeName));
} }
directExeSet.Add(Utils.GetExeName(baseExeName));
} }
} }
lstDnsExe = new List<string>(dnsExeSet);
lstDirectExe = new List<string>(directExeSet);
} }
private async Task<int> GenRoutingUserRule(RulesItem item, List<Rule4Sbox> rules) private async Task<int> GenRoutingUserRule(RulesItem item, List<Rule4Sbox> rules)
@ -1087,14 +1090,11 @@ public class CoreConfigSingboxService
if (item.Port.IsNotEmpty()) if (item.Port.IsNotEmpty())
{ {
if (item.Port.Contains("-")) var portRanges = item.Port.Split(',').Where(it => it.Contains('-')).Select(it => it.Replace("-", ":")).ToList();
{ var ports = item.Port.Split(',').Where(it => !it.Contains('-')).Select(it => it.ToInt()).ToList();
rule.port_range = new List<string> { item.Port.Replace("-", ":") };
} rule.port_range = portRanges.Count > 0 ? portRanges : null;
else rule.port = ports.Count > 0 ? ports : null;
{
rule.port = new List<int> { item.Port.ToInt() };
}
} }
if (item.Network.IsNotEmpty()) if (item.Network.IsNotEmpty())
{ {

View file

@ -328,7 +328,7 @@ public class CoreConfigV2rayService
{ {
listen = Global.Loopback, listen = Global.Loopback,
port = port, port = port,
protocol = EInboundProtocol.socks.ToString(), protocol = EInboundProtocol.mixed.ToString(),
}; };
inbound.tag = inbound.protocol + inbound.port.ToString(); inbound.tag = inbound.protocol + inbound.port.ToString();
v2rayConfig.inbounds.Add(inbound); v2rayConfig.inbounds.Add(inbound);
@ -403,7 +403,7 @@ public class CoreConfigV2rayService
tag = $"{EInboundProtocol.socks}{port}", tag = $"{EInboundProtocol.socks}{port}",
listen = Global.Loopback, listen = Global.Loopback,
port = port, port = port,
protocol = EInboundProtocol.socks.ToString(), protocol = EInboundProtocol.mixed.ToString(),
}); });
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
@ -507,7 +507,7 @@ public class CoreConfigV2rayService
} }
inbound.tag = protocol.ToString(); inbound.tag = protocol.ToString();
inbound.port = inItem.LocalPort + (int)protocol; inbound.port = inItem.LocalPort + (int)protocol;
inbound.protocol = EInboundProtocol.socks.ToString(); inbound.protocol = EInboundProtocol.mixed.ToString();
inbound.settings.udp = inItem.UdpEnabled; inbound.settings.udp = inItem.UdpEnabled;
inbound.sniffing.enabled = inItem.SniffingEnabled; inbound.sniffing.enabled = inItem.SniffingEnabled;
inbound.sniffing.destOverride = inItem.DestOverride; inbound.sniffing.destOverride = inItem.DestOverride;
@ -1183,7 +1183,6 @@ public class CoreConfigV2rayService
if (servers != null) if (servers != null)
{ {
var domainList = new List<string>(); var domainList = new List<string>();
string? wireguardEndpointDomain = null;
if (Utils.IsDomain(node.Address)) if (Utils.IsDomain(node.Address))
{ {
domainList.Add(node.Address); domainList.Add(node.Address);
@ -1210,14 +1209,7 @@ public class CoreConfigV2rayService
&& nextNode.ConfigType != EConfigType.TUIC && nextNode.ConfigType != EConfigType.TUIC
&& Utils.IsDomain(nextNode.Address)) && Utils.IsDomain(nextNode.Address))
{ {
if (nextNode.ConfigType == EConfigType.WireGuard) domainList.Add(nextNode.Address);
{
wireguardEndpointDomain = nextNode.Address;
}
else
{
domainList.Add(nextNode.Address);
}
} }
} }
if (domainList.Count > 0) if (domainList.Count > 0)
@ -1230,16 +1222,6 @@ public class CoreConfigV2rayService
}; };
servers.AsArray().Add(JsonUtils.SerializeToNode(dnsServer)); servers.AsArray().Add(JsonUtils.SerializeToNode(dnsServer));
} }
if (wireguardEndpointDomain is not null)
{
var dnsServer = new DnsServer4Ray()
{
address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress,
skipFallback = true,
domains = new() { wireguardEndpointDomain }
};
servers.AsArray().Insert(0, JsonUtils.SerializeToNode(dnsServer));
}
} }
return await Task.FromResult(0); return await Task.FromResult(0);
} }

View file

@ -194,11 +194,11 @@ public class UpdateService
{ {
if (Utils.IsBase64String(result2)) if (Utils.IsBase64String(result2))
{ {
result += Utils.Base64Decode(result2); result += Environment.NewLine + Utils.Base64Decode(result2);
} }
else else
{ {
result += result2; result += Environment.NewLine + result2;
} }
} }
} }
@ -243,21 +243,6 @@ public class UpdateService
_updateFunc?.Invoke(true, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, "geo")); _updateFunc?.Invoke(true, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, "geo"));
} }
public async Task<string> RunAvailabilityCheck()
{
var downloadHandle = new DownloadService();
var time = await downloadHandle.RunAvailabilityCheck(null);
var ip = Global.None;
if (time > 0)
{
var result = await downloadHandle.TryDownloadString(Global.IPAPIUrl, true, Global.IPAPIUrl);
var ipInfo = JsonUtils.Deserialize<IPAPIInfo>(result);
ip = $"({ipInfo?.country_code}) {ipInfo?.ip}";
}
return string.Format(ResUI.TestMeOutput, time, ip);
}
#region CheckUpdate private #region CheckUpdate private
private async Task<RetResult> CheckUpdateAsync(DownloadService downloadHandle, ECoreType type, bool preRelease) private async Task<RetResult> CheckUpdateAsync(DownloadService downloadHandle, ECoreType type, bool preRelease)

View file

@ -70,6 +70,7 @@ public class OptionSettingViewModel : MyReactiveObject
[Reactive] public string GeoFileSourceUrl { get; set; } [Reactive] public string GeoFileSourceUrl { get; set; }
[Reactive] public string SrsFileSourceUrl { get; set; } [Reactive] public string SrsFileSourceUrl { get; set; }
[Reactive] public string RoutingRulesSourceUrl { get; set; } [Reactive] public string RoutingRulesSourceUrl { get; set; }
[Reactive] public string IPAPIUrl { get; set; }
#endregion UI #endregion UI
@ -88,7 +89,6 @@ public class OptionSettingViewModel : MyReactiveObject
[Reactive] public int TunMtu { get; set; } [Reactive] public int TunMtu { get; set; }
[Reactive] public bool TunEnableExInbound { get; set; } [Reactive] public bool TunEnableExInbound { get; set; }
[Reactive] public bool TunEnableIPv6Address { get; set; } [Reactive] public bool TunEnableIPv6Address { get; set; }
[Reactive] public string TunLinuxSudoPassword { get; set; }
#endregion Tun mode #endregion Tun mode
@ -187,6 +187,7 @@ public class OptionSettingViewModel : MyReactiveObject
GeoFileSourceUrl = _config.ConstItem.GeoSourceUrl; GeoFileSourceUrl = _config.ConstItem.GeoSourceUrl;
SrsFileSourceUrl = _config.ConstItem.SrsSourceUrl; SrsFileSourceUrl = _config.ConstItem.SrsSourceUrl;
RoutingRulesSourceUrl = _config.ConstItem.RouteRulesTemplateSourceUrl; RoutingRulesSourceUrl = _config.ConstItem.RouteRulesTemplateSourceUrl;
IPAPIUrl = _config.SpeedTestItem.IPAPIUrl;
#endregion UI #endregion UI
@ -205,7 +206,6 @@ public class OptionSettingViewModel : MyReactiveObject
TunMtu = _config.TunModeItem.Mtu; TunMtu = _config.TunModeItem.Mtu;
TunEnableExInbound = _config.TunModeItem.EnableExInbound; TunEnableExInbound = _config.TunModeItem.EnableExInbound;
TunEnableIPv6Address = _config.TunModeItem.EnableIPv6Address; TunEnableIPv6Address = _config.TunModeItem.EnableIPv6Address;
TunLinuxSudoPassword = _config.TunModeItem.LinuxSudoPwd;
#endregion Tun mode #endregion Tun mode
@ -346,6 +346,7 @@ public class OptionSettingViewModel : MyReactiveObject
_config.ConstItem.GeoSourceUrl = GeoFileSourceUrl; _config.ConstItem.GeoSourceUrl = GeoFileSourceUrl;
_config.ConstItem.SrsSourceUrl = SrsFileSourceUrl; _config.ConstItem.SrsSourceUrl = SrsFileSourceUrl;
_config.ConstItem.RouteRulesTemplateSourceUrl = RoutingRulesSourceUrl; _config.ConstItem.RouteRulesTemplateSourceUrl = RoutingRulesSourceUrl;
_config.SpeedTestItem.IPAPIUrl = IPAPIUrl;
//systemProxy //systemProxy
_config.SystemProxyItem.SystemProxyExceptions = systemProxyExceptions; _config.SystemProxyItem.SystemProxyExceptions = systemProxyExceptions;
@ -358,10 +359,6 @@ public class OptionSettingViewModel : MyReactiveObject
_config.TunModeItem.Mtu = TunMtu; _config.TunModeItem.Mtu = TunMtu;
_config.TunModeItem.EnableExInbound = TunEnableExInbound; _config.TunModeItem.EnableExInbound = TunEnableExInbound;
_config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address; _config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address;
if (TunLinuxSudoPassword != _config.TunModeItem.LinuxSudoPwd)
{
_config.TunModeItem.LinuxSudoPwd = DesUtils.Encrypt(TunLinuxSudoPassword);
}
//coreType //coreType
await SaveCoreType(); await SaveCoreType();

View file

@ -320,7 +320,7 @@ public class StatusBarViewModel : MyReactiveObject
var msg = await Task.Run(async () => var msg = await Task.Run(async () =>
{ {
return await (new UpdateService()).RunAvailabilityCheck(); return await ConnectionHandler.Instance.RunAvailabilityCheck();
}); });
NoticeHandler.Instance.SendMessageEx(msg); NoticeHandler.Instance.SendMessageEx(msg);
@ -436,11 +436,13 @@ public class StatusBarViewModel : MyReactiveObject
Locator.Current.GetService<MainWindowViewModel>()?.RebootAsAdmin(); Locator.Current.GetService<MainWindowViewModel>()?.RebootAsAdmin();
return; return;
} }
else if (Utils.IsOSX()) else
{ {
_config.TunModeItem.EnableTun = false; if (await _updateView?.Invoke(EViewAction.PasswordInput, null) == false)
NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordIsEmpty); {
return; _config.TunModeItem.EnableTun = false;
return;
}
} }
} }
await ConfigHandler.SaveConfig(_config); await ConfigHandler.SaveConfig(_config);
@ -452,15 +454,15 @@ public class StatusBarViewModel : MyReactiveObject
{ {
if (Utils.IsWindows()) if (Utils.IsWindows())
{ {
return AppHandler.Instance.IsAdministrator; return Utils.IsAdministrator();
} }
else if (Utils.IsLinux()) else if (Utils.IsLinux())
{ {
return _config.TunModeItem.LinuxSudoPwd.IsNotEmpty(); return AppHandler.Instance.LinuxSudoPwd.IsNotEmpty();
} }
else if (Utils.IsOSX()) else if (Utils.IsOSX())
{ {
return _config.TunModeItem.LinuxSudoPwd.IsNotEmpty(); return AppHandler.Instance.LinuxSudoPwd.IsNotEmpty();
} }
return false; return false;
} }

View file

@ -43,7 +43,7 @@ public partial class App : Application
{ {
if (e.ExceptionObject != null) if (e.ExceptionObject != null)
{ {
Logging.SaveLog("CurrentDomain_UnhandledException", (Exception)e.ExceptionObject!); Logging.SaveLog("CurrentDomain_UnhandledException", (Exception)e.ExceptionObject);
} }
} }

View file

@ -52,9 +52,16 @@ public partial class ClashConnectionsView : ReactiveUserControl<ClashConnections
private void AutofitColumnWidth() private void AutofitColumnWidth()
{ {
foreach (var it in lstConnections.Columns) try
{ {
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto); foreach (var it in lstConnections.Columns)
{
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
}
}
catch (Exception ex)
{
Logging.SaveLog("ClashConnectionsView", ex);
} }
} }

View file

@ -143,7 +143,7 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
} }
else else
{ {
if (AppHandler.Instance.IsAdministrator) if (Utils.IsAdministrator())
{ {
this.Title = $"{Utils.GetVersion()} - {ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp}"; this.Title = $"{Utils.GetVersion()} - {ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp}";
NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp); NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp);
@ -388,7 +388,7 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
private async void MenuClose_Click(object? sender, RoutedEventArgs e) private async void MenuClose_Click(object? sender, RoutedEventArgs e)
{ {
if (await UI.ShowYesNo(this, ResUI.menuExitTips) == ButtonResult.No) if (await UI.ShowYesNo(this, ResUI.menuExitTips) != ButtonResult.Yes)
{ {
return; return;
} }

View file

@ -343,7 +343,7 @@
<Grid <Grid
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
ColumnDefinitions="Auto,Auto,*" ColumnDefinitions="Auto,Auto,*"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto"> RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<TextBlock <TextBlock
x:Name="tbAutoRun" x:Name="tbAutoRun"
@ -655,6 +655,20 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsChinaUserTip}" Text="{x:Static resx:ResUI.TbSettingsChinaUserTip}"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<TextBlock
Grid.Row="25"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsIPAPIUrl}" />
<ComboBox
x:Name="cmbIPAPIUrl"
Grid.Row="25"
Grid.Column="1"
Width="300"
Margin="{StaticResource Margin4}" />
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</TabItem> </TabItem>
@ -784,28 +798,6 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" /> HorizontalAlignment="Left" />
<TextBlock
x:Name="labLinuxSudoPassword"
Grid.Row="7"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsLinuxSudoPassword}" />
<TextBox
x:Name="txtLinuxSudoPassword"
Grid.Row="7"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
x:Name="labLinuxSudoPasswordTip"
Grid.Row="7"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsLinuxSudoPasswordTip}"
TextWrapping="Wrap" />
</Grid> </Grid>
</TabItem> </TabItem>

View file

@ -92,6 +92,10 @@ public partial class OptionSettingWindow : ReactiveWindow<OptionSettingViewModel
{ {
cmbRoutingRulesSourceUrl.Items.Add(it); cmbRoutingRulesSourceUrl.Items.Add(it);
}); });
Global.IPAPIUrls.ForEach(it =>
{
cmbIPAPIUrl.Items.Add(it);
});
foreach (EGirdOrientation it in Enum.GetValues(typeof(EGirdOrientation))) foreach (EGirdOrientation it in Enum.GetValues(typeof(EGirdOrientation)))
{ {
cmbMainGirdOrientation.Items.Add(it.ToString()); cmbMainGirdOrientation.Items.Add(it.ToString());
@ -143,6 +147,7 @@ public partial class OptionSettingWindow : ReactiveWindow<OptionSettingViewModel
this.Bind(ViewModel, vm => vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SrsFileSourceUrl, v => v.cmbSrsFilesSourceUrl.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SrsFileSourceUrl, v => v.cmbSrsFilesSourceUrl.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RoutingRulesSourceUrl, v => v.cmbRoutingRulesSourceUrl.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.RoutingRulesSourceUrl, v => v.cmbRoutingRulesSourceUrl.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.IPAPIUrl, v => v.cmbIPAPIUrl.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.notProxyLocalAddress, v => v.tognotProxyLocalAddress.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.notProxyLocalAddress, v => v.tognotProxyLocalAddress.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.systemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.systemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.SelectedValue).DisposeWith(disposables);
@ -153,7 +158,6 @@ public partial class OptionSettingWindow : ReactiveWindow<OptionSettingViewModel
this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunEnableExInbound, v => v.togEnableExInbound.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunEnableExInbound, v => v.togEnableExInbound.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunLinuxSudoPassword, v => v.txtLinuxSudoPassword.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.SelectedValue).DisposeWith(disposables);
@ -170,10 +174,6 @@ public partial class OptionSettingWindow : ReactiveWindow<OptionSettingViewModel
{ {
txbSettingsExceptionTip2.IsVisible = false; txbSettingsExceptionTip2.IsVisible = false;
txtLinuxSudoPassword.IsVisible = false;
labLinuxSudoPassword.IsVisible = false;
labLinuxSudoPasswordTip.IsVisible = false;
labHide2TrayWhenClose.IsVisible = false; labHide2TrayWhenClose.IsVisible = false;
togHide2TrayWhenClose.IsVisible = false; togHide2TrayWhenClose.IsVisible = false;
} }

View file

@ -138,7 +138,7 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
break; break;
case EViewAction.ShowYesNo: case EViewAction.ShowYesNo:
if (await UI.ShowYesNo(_window, ResUI.RemoveServer) == ButtonResult.No) if (await UI.ShowYesNo(_window, ResUI.RemoveServer) != ButtonResult.Yes)
{ {
return false; return false;
} }
@ -345,9 +345,16 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
private void AutofitColumnWidth() private void AutofitColumnWidth()
{ {
foreach (var it in lstProfiles.Columns) try
{ {
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto); foreach (var it in lstProfiles.Columns)
{
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
}
}
catch (Exception ex)
{
Logging.SaveLog("ProfilesView", ex);
} }
} }

View file

@ -80,14 +80,14 @@ public partial class RoutingRuleSettingWindow : ReactiveWindow<RoutingRuleSettin
break; break;
case EViewAction.ShowYesNo: case EViewAction.ShowYesNo:
if (await UI.ShowYesNo(this, ResUI.RemoveServer) == ButtonResult.No) if (await UI.ShowYesNo(this, ResUI.RemoveServer) != ButtonResult.Yes)
{ {
return false; return false;
} }
break; break;
case EViewAction.AddBatchRoutingRulesYesNo: case EViewAction.AddBatchRoutingRulesYesNo:
if (await UI.ShowYesNo(this, ResUI.AddBatchRoutingRulesYesNo) == ButtonResult.No) if (await UI.ShowYesNo(this, ResUI.AddBatchRoutingRulesYesNo) != ButtonResult.Yes)
{ {
return false; return false;
} }

View file

@ -68,7 +68,7 @@ public partial class RoutingSettingWindow : ReactiveWindow<RoutingSettingViewMod
break; break;
case EViewAction.ShowYesNo: case EViewAction.ShowYesNo:
if (await UI.ShowYesNo(this, ResUI.RemoveRules) == ButtonResult.No) if (await UI.ShowYesNo(this, ResUI.RemoveRules) != ButtonResult.Yes)
{ {
return false; return false;
} }

View file

@ -4,6 +4,7 @@ using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia.Threading; using Avalonia.Threading;
using DialogHostAvalonia;
using ReactiveUI; using ReactiveUI;
using Splat; using Splat;
using v2rayN.Desktop.Common; using v2rayN.Desktop.Common;
@ -81,6 +82,10 @@ public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
return false; return false;
await AvaUtils.SetClipboardData(this, (string)obj); await AvaUtils.SetClipboardData(this, (string)obj);
break; break;
case EViewAction.PasswordInput:
return await PasswordInputAsync();
break;
} }
return await Task.FromResult(true); return await Task.FromResult(true);
} }
@ -96,6 +101,20 @@ public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
} }
} }
private async Task<bool> PasswordInputAsync()
{
var dialog = new SudoPasswordInputView();
var obj = await DialogHost.Show(dialog);
if (obj == null)
{
togEnableTun.IsChecked = false;
return false;
}
AppHandler.Instance.LinuxSudoPwd = obj.ToString() ?? string.Empty;
return true;
}
private void TxtRunningServerDisplay_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) private void TxtRunningServerDisplay_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
{ {
ViewModel?.TestServerAvailability(); ViewModel?.TestServerAvailability();

View file

@ -45,7 +45,7 @@ public partial class SubSettingWindow : ReactiveWindow<SubSettingViewModel>
break; break;
case EViewAction.ShowYesNo: case EViewAction.ShowYesNo:
if (await UI.ShowYesNo(this, ResUI.RemoveServer) == ButtonResult.No) if (await UI.ShowYesNo(this, ResUI.RemoveServer) != ButtonResult.Yes)
{ {
return false; return false;
} }

View file

@ -0,0 +1,70 @@
<UserControl
x:Class="v2rayN.Desktop.Views.SudoPasswordInputView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
d:DesignHeight="200"
d:DesignWidth="400"
mc:Ignorable="d">
<DockPanel Margin="{StaticResource Margin8}">
<Border
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
DockPanel.Dock="Bottom"
Theme="{DynamicResource CardBorder}">
<StackPanel
Margin="{StaticResource Margin4}"
HorizontalAlignment="Center"
Orientation="Horizontal">
<Button
x:Name="btnSave"
Width="100"
Content="{x:Static resx:ResUI.TbConfirm}"
Cursor="Hand"
IsDefault="True" />
<Button
x:Name="btnCancel"
Width="100"
Margin="{StaticResource MarginLr8}"
Content="{x:Static resx:ResUI.TbCancel}"
Cursor="Hand"
IsCancel="True" />
</StackPanel>
</Border>
<Border
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Theme="{DynamicResource CardBorder}">
<Grid ColumnDefinitions="Auto,400" RowDefinitions="Auto,Auto,Auto">
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsLinuxSudoPassword}" />
<TextBox
x:Name="txtPassword"
Grid.Row="1"
Grid.Column="1"
Margin="{StaticResource Margin4}"
Classes="revealPasswordButton"
Focusable="True" />
<TextBlock
Grid.Row="2"
Grid.Column="1"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsLinuxSudoPasswordTip}"
TextWrapping="Wrap" />
</Grid>
</Border>
</DockPanel>
</UserControl>

View file

@ -0,0 +1,33 @@
using Avalonia.Controls;
using Avalonia.Threading;
using DialogHostAvalonia;
namespace v2rayN.Desktop.Views;
public partial class SudoPasswordInputView : UserControl
{
public SudoPasswordInputView()
{
InitializeComponent();
this.Loaded += (s, e) => txtPassword.Focus();
btnSave.Click += (_, _) =>
{
if (string.IsNullOrEmpty(txtPassword.Text))
{
txtPassword.Focus();
return;
}
Dispatcher.UIThread.Post(() =>
{
DialogHost.Close(null, txtPassword.Text);
});
};
btnCancel.Click += (_, _) =>
{
DialogHost.Close(null);
};
}
}

View file

@ -1,9 +1,9 @@
<Application <Application
x:Class="v2rayN.App" x:Class="v2rayN.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:conv="clr-namespace:v2rayN.Converters" xmlns:conv="clr-namespace:v2rayN.Converters"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
ShutdownMode="OnExplicitShutdown" ShutdownMode="OnExplicitShutdown"
StartupUri="Views/MainWindow.xaml"> StartupUri="Views/MainWindow.xaml">
<Application.Resources> <Application.Resources>

View file

@ -56,7 +56,7 @@ public partial class App : Application
{ {
if (e.ExceptionObject != null) if (e.ExceptionObject != null)
{ {
Logging.SaveLog("CurrentDomain_UnhandledException", (Exception)e.ExceptionObject!); Logging.SaveLog("CurrentDomain_UnhandledException", (Exception)e.ExceptionObject);
} }
} }

View file

@ -55,9 +55,16 @@ public partial class ClashConnectionsView
private void AutofitColumnWidth() private void AutofitColumnWidth()
{ {
foreach (var it in lstConnections.Columns) try
{ {
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto); foreach (var it in lstConnections.Columns)
{
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
}
}
catch (Exception ex)
{
Logging.SaveLog("ClashConnectionsView", ex);
} }
} }

View file

@ -1,14 +1,14 @@
<reactiveui:ReactiveWindow <reactiveui:ReactiveWindow
x:Class="v2rayN.Views.MainWindow" x:Class="v2rayN.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:reactiveui="http://reactiveui.net"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:reactiveui="http://reactiveui.net"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
xmlns:view="clr-namespace:v2rayN.Views" xmlns:view="clr-namespace:v2rayN.Views"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Title="v2rayN" Title="v2rayN"
Width="900" Width="900"
Height="700" Height="700"
@ -43,8 +43,7 @@
Style="{StaticResource MaterialDesignToolBar}" Style="{StaticResource MaterialDesignToolBar}"
KeyboardNavigation.TabNavigation="Continue"> KeyboardNavigation.TabNavigation="Continue">
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}"> <Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
<MenuItem Padding="8,0" <MenuItem Padding="8,0" AutomationProperties.Name="{x:Static resx:ResUI.menuServers}">
AutomationProperties.Name="{x:Static resx:ResUI.menuServers}">
<MenuItem.Header> <MenuItem.Header>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<materialDesign:PackIcon <materialDesign:PackIcon
@ -112,8 +111,7 @@
</Menu> </Menu>
<Separator /> <Separator />
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}"> <Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
<MenuItem Padding="8,0" <MenuItem Padding="8,0" AutomationProperties.Name="{x:Static resx:ResUI.menuSubscription}">
AutomationProperties.Name="{x:Static resx:ResUI.menuSubscription}">
<MenuItem.Header> <MenuItem.Header>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<materialDesign:PackIcon <materialDesign:PackIcon
@ -148,8 +146,7 @@
</Menu> </Menu>
<Separator /> <Separator />
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}"> <Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
<MenuItem Padding="8,0" <MenuItem Padding="8,0" AutomationProperties.Name="{x:Static resx:ResUI.menuSetting}">
AutomationProperties.Name="{x:Static resx:ResUI.menuSetting}">
<MenuItem.Header> <MenuItem.Header>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<materialDesign:PackIcon <materialDesign:PackIcon
@ -215,8 +212,10 @@
</Menu> </Menu>
<Separator /> <Separator />
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}"> <Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
<MenuItem x:Name="menuReload" Padding="8,0" <MenuItem
AutomationProperties.Name="{x:Static resx:ResUI.menuReload}"> x:Name="menuReload"
Padding="8,0"
AutomationProperties.Name="{x:Static resx:ResUI.menuReload}">
<MenuItem.Header> <MenuItem.Header>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<materialDesign:PackIcon <materialDesign:PackIcon
@ -230,8 +229,10 @@
</Menu> </Menu>
<Separator /> <Separator />
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}"> <Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
<MenuItem Name="menuCheckUpdate" Padding="8,0" <MenuItem
AutomationProperties.Name="{x:Static resx:ResUI.menuCheckUpdate}"> Name="menuCheckUpdate"
Padding="8,0"
AutomationProperties.Name="{x:Static resx:ResUI.menuCheckUpdate}">
<MenuItem.Header> <MenuItem.Header>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<materialDesign:PackIcon <materialDesign:PackIcon
@ -245,8 +246,10 @@
</Menu> </Menu>
<Separator /> <Separator />
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}"> <Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
<MenuItem x:Name="menuHelp" Padding="8,0" <MenuItem
AutomationProperties.Name="{x:Static resx:ResUI.menuHelp}"> x:Name="menuHelp"
Padding="8,0"
AutomationProperties.Name="{x:Static resx:ResUI.menuHelp}">
<MenuItem.Header> <MenuItem.Header>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<materialDesign:PackIcon <materialDesign:PackIcon
@ -260,8 +263,10 @@
</Menu> </Menu>
<Separator /> <Separator />
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}"> <Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
<MenuItem x:Name="menuPromotion" Padding="8,0" <MenuItem
AutomationProperties.Name="{x:Static resx:ResUI.menuPromotion}"> x:Name="menuPromotion"
Padding="8,0"
AutomationProperties.Name="{x:Static resx:ResUI.menuPromotion}">
<MenuItem.Header> <MenuItem.Header>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<materialDesign:PackIcon <materialDesign:PackIcon
@ -275,8 +280,10 @@
</Menu> </Menu>
<Separator /> <Separator />
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}"> <Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
<MenuItem x:Name="menuClose" Padding="8,0" <MenuItem
AutomationProperties.Name="{x:Static resx:ResUI.menuClose}"> x:Name="menuClose"
Padding="8,0"
AutomationProperties.Name="{x:Static resx:ResUI.menuClose}">
<MenuItem.Header> <MenuItem.Header>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<materialDesign:PackIcon <materialDesign:PackIcon

View file

@ -132,7 +132,7 @@ public partial class MainWindow
} }
}); });
this.Title = $"{Utils.GetVersion()} - {(AppHandler.Instance.IsAdministrator ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}"; this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
if (!_config.GuiItem.EnableHWA) if (!_config.GuiItem.EnableHWA)
{ {

View file

@ -552,6 +552,7 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
@ -942,6 +943,22 @@
Style="{StaticResource ToolbarTextBlock}" Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbSettingsChinaUserTip}" Text="{x:Static resx:ResUI.TbSettingsChinaUserTip}"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<TextBlock
Grid.Row="25"
Grid.Column="0"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbSettingsIPAPIUrl}" />
<ComboBox
x:Name="cmbIPAPIUrl"
Grid.Row="25"
Grid.Column="1"
Width="300"
Margin="{StaticResource Margin8}"
IsEditable="True"
Style="{StaticResource DefComboBox}" />
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</TabItem> </TabItem>

View file

@ -101,6 +101,10 @@ public partial class OptionSettingWindow
{ {
cmbRoutingRulesSourceUrl.Items.Add(it); cmbRoutingRulesSourceUrl.Items.Add(it);
}); });
Global.IPAPIUrls.ForEach(it =>
{
cmbIPAPIUrl.Items.Add(it);
});
foreach (EGirdOrientation it in Enum.GetValues(typeof(EGirdOrientation))) foreach (EGirdOrientation it in Enum.GetValues(typeof(EGirdOrientation)))
{ {
cmbMainGirdOrientation.Items.Add(it.ToString()); cmbMainGirdOrientation.Items.Add(it.ToString());
@ -162,6 +166,7 @@ public partial class OptionSettingWindow
this.Bind(ViewModel, vm => vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SrsFileSourceUrl, v => v.cmbSrsFilesSourceUrl.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SrsFileSourceUrl, v => v.cmbSrsFilesSourceUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RoutingRulesSourceUrl, v => v.cmbRoutingRulesSourceUrl.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.RoutingRulesSourceUrl, v => v.cmbRoutingRulesSourceUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.IPAPIUrl, v => v.cmbIPAPIUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.notProxyLocalAddress, v => v.tognotProxyLocalAddress.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.notProxyLocalAddress, v => v.tognotProxyLocalAddress.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.systemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.systemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.Text).DisposeWith(disposables);

View file

@ -25,6 +25,7 @@
<ListBox <ListBox
x:Name="lstGroup" x:Name="lstGroup"
MaxHeight="200" MaxHeight="200"
AutomationProperties.Name="{x:Static resx:ResUI.menuSubscription}"
FontSize="{DynamicResource StdFontSize}" FontSize="{DynamicResource StdFontSize}"
ItemContainerStyle="{StaticResource MyChipListBoxItem}" ItemContainerStyle="{StaticResource MyChipListBoxItem}"
Style="{StaticResource MaterialDesignChoiceChipPrimaryOutlineListBox}"> Style="{StaticResource MaterialDesignChoiceChipPrimaryOutlineListBox}">
@ -40,9 +41,9 @@
Width="30" Width="30"
Height="30" Height="30"
Margin="{StaticResource MarginLeftRight4}" Margin="{StaticResource MarginLeftRight4}"
AutomationProperties.Name="{x:Static resx:ResUI.menuSubEdit}"
Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}" Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}"
ToolTip="{x:Static resx:ResUI.menuSubEdit}" ToolTip="{x:Static resx:ResUI.menuSubEdit}">
AutomationProperties.Name="{x:Static resx:ResUI.menuSubEdit}">
<materialDesign:PackIcon VerticalAlignment="Center" Kind="Edit" /> <materialDesign:PackIcon VerticalAlignment="Center" Kind="Edit" />
</Button> </Button>
<Button <Button
@ -50,9 +51,9 @@
Width="30" Width="30"
Height="30" Height="30"
Margin="{StaticResource MarginLeftRight4}" Margin="{StaticResource MarginLeftRight4}"
AutomationProperties.Name="{x:Static resx:ResUI.menuSubAdd}"
Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}" Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}"
ToolTip="{x:Static resx:ResUI.menuSubAdd}" ToolTip="{x:Static resx:ResUI.menuSubAdd}">
AutomationProperties.Name="{x:Static resx:ResUI.menuSubAdd}">
<materialDesign:PackIcon VerticalAlignment="Center" Kind="Plus" /> <materialDesign:PackIcon VerticalAlignment="Center" Kind="Plus" />
</Button> </Button>
@ -61,9 +62,9 @@
Width="30" Width="30"
Height="30" Height="30"
Margin="{StaticResource MarginLeftRight8}" Margin="{StaticResource MarginLeftRight8}"
AutomationProperties.Name="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}"
Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}" Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}"
ToolTip="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}" ToolTip="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}">
AutomationProperties.Name="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}">
<materialDesign:PackIcon VerticalAlignment="Center" Kind="ArrowSplitVertical" /> <materialDesign:PackIcon VerticalAlignment="Center" Kind="ArrowSplitVertical" />
</Button> </Button>
<TextBox <TextBox
@ -73,8 +74,8 @@
VerticalContentAlignment="Center" VerticalContentAlignment="Center"
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.MsgServerTitle}" materialDesign:HintAssist.Hint="{x:Static resx:ResUI.MsgServerTitle}"
materialDesign:TextFieldAssist.HasClearButton="True" materialDesign:TextFieldAssist.HasClearButton="True"
Style="{StaticResource DefTextBox}" AutomationProperties.Name="{x:Static resx:ResUI.MsgServerTitle}"
AutomationProperties.Name="{x:Static resx:ResUI.MsgServerTitle}" /> Style="{StaticResource DefTextBox}" />
</WrapPanel> </WrapPanel>
<DataGrid <DataGrid
x:Name="lstProfiles" x:Name="lstProfiles"

View file

@ -323,9 +323,16 @@ public partial class ProfilesView
private void AutofitColumnWidth() private void AutofitColumnWidth()
{ {
foreach (var it in lstProfiles.Columns) try
{ {
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto); foreach (var it in lstProfiles.Columns)
{
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
}
}
catch (Exception ex)
{
Logging.SaveLog("ProfilesView", ex);
} }
} }

View file

@ -1,11 +1,11 @@
<reactiveui:ReactiveWindow <reactiveui:ReactiveWindow
x:Class="v2rayN.Views.RoutingRuleDetailsWindow" x:Class="v2rayN.Views.RoutingRuleDetailsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:reactiveui="http://reactiveui.net"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:reactiveui="http://reactiveui.net"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib" xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Title="{x:Static resx:ResUI.menuRoutingRuleDetailsSetting}" Title="{x:Static resx:ResUI.menuRoutingRuleDetailsSetting}"

View file

@ -1,11 +1,11 @@
<reactiveui:ReactiveWindow <reactiveui:ReactiveWindow
x:Class="v2rayN.Views.RoutingRuleSettingWindow" x:Class="v2rayN.Views.RoutingRuleSettingWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:reactiveui="http://reactiveui.net"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:reactiveui="http://reactiveui.net"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib" xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Title="{x:Static resx:ResUI.menuRoutingRuleSetting}" Title="{x:Static resx:ResUI.menuRoutingRuleSetting}"

View file

@ -61,7 +61,8 @@
x:Name="togEnableTun" x:Name="togEnableTun"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" AutomationProperties.Name="{x:Static resx:ResUI.TbEnableTunAs}" /> VerticalAlignment="Center"
AutomationProperties.Name="{x:Static resx:ResUI.TbEnableTunAs}" />
</StackPanel> </StackPanel>
<StackPanel <StackPanel
@ -74,9 +75,9 @@
Width="160" Width="160"
Margin="{StaticResource MarginLeftRight8}" Margin="{StaticResource MarginLeftRight8}"
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuSystemproxy}" materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuSystemproxy}"
AutomationProperties.Name="{x:Static resx:ResUI.menuSystemproxy}"
FontSize="{DynamicResource StdFontSize}" FontSize="{DynamicResource StdFontSize}"
Style="{StaticResource MaterialDesignFloatingHintComboBox}" Style="{StaticResource MaterialDesignFloatingHintComboBox}">
AutomationProperties.Name="{x:Static resx:ResUI.menuSystemproxy}">
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxyClear}" /> <ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxyClear}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxySet}" /> <ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxySet}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxyNothing}" /> <ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxyNothing}" />
@ -88,10 +89,11 @@
Width="160" Width="160"
Margin="{StaticResource MarginLeftRight8}" Margin="{StaticResource MarginLeftRight8}"
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuRouting}" materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuRouting}"
AutomationProperties.Name="{x:Static resx:ResUI.menuRouting}"
DisplayMemberPath="Remarks" DisplayMemberPath="Remarks"
FontSize="{DynamicResource StdFontSize}" FontSize="{DynamicResource StdFontSize}"
Style="{StaticResource MaterialDesignFloatingHintComboBox}" Style="{StaticResource MaterialDesignFloatingHintComboBox}">
AutomationProperties.Name="{x:Static resx:ResUI.menuRouting}"/> </ComboBox>
</StackPanel> </StackPanel>
<StackPanel Margin="{StaticResource MarginLeftRight8}" VerticalAlignment="Center"> <StackPanel Margin="{StaticResource MarginLeftRight8}" VerticalAlignment="Center">
@ -109,8 +111,10 @@
ToolTipText="v2rayN"> ToolTipText="v2rayN">
<tb:TaskbarIcon.ContextMenu> <tb:TaskbarIcon.ContextMenu>
<ContextMenu Style="{StaticResource DefContextMenu}"> <ContextMenu Style="{StaticResource DefContextMenu}">
<MenuItem x:Name="menuSystemProxyClear" Height="{StaticResource MenuItemHeight}" <MenuItem
AutomationProperties.Name="{x:Static resx:ResUI.menuSystemProxyClear}"> x:Name="menuSystemProxyClear"
Height="{StaticResource MenuItemHeight}"
AutomationProperties.Name="{x:Static resx:ResUI.menuSystemProxyClear}">
<MenuItem.Header> <MenuItem.Header>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<materialDesign:PackIcon <materialDesign:PackIcon
@ -122,8 +126,10 @@
</StackPanel> </StackPanel>
</MenuItem.Header> </MenuItem.Header>
</MenuItem> </MenuItem>
<MenuItem x:Name="menuSystemProxySet" Height="{StaticResource MenuItemHeight}" <MenuItem
AutomationProperties.Name="{x:Static resx:ResUI.menuSystemProxySet}"> x:Name="menuSystemProxySet"
Height="{StaticResource MenuItemHeight}"
AutomationProperties.Name="{x:Static resx:ResUI.menuSystemProxySet}">
<MenuItem.Header> <MenuItem.Header>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<materialDesign:PackIcon <materialDesign:PackIcon
@ -135,8 +141,10 @@
</StackPanel> </StackPanel>
</MenuItem.Header> </MenuItem.Header>
</MenuItem> </MenuItem>
<MenuItem x:Name="menuSystemProxyNothing" Height="{StaticResource MenuItemHeight}" <MenuItem
AutomationProperties.Name="{x:Static resx:ResUI.menuSystemProxyNothing}"> x:Name="menuSystemProxyNothing"
Height="{StaticResource MenuItemHeight}"
AutomationProperties.Name="{x:Static resx:ResUI.menuSystemProxyNothing}">
<MenuItem.Header> <MenuItem.Header>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<materialDesign:PackIcon <materialDesign:PackIcon
@ -148,8 +156,10 @@
</StackPanel> </StackPanel>
</MenuItem.Header> </MenuItem.Header>
</MenuItem> </MenuItem>
<MenuItem x:Name="menuSystemProxyPac" Height="{StaticResource MenuItemHeight}" <MenuItem
AutomationProperties.Name="{x:Static resx:ResUI.menuSystemProxyPac}"> x:Name="menuSystemProxyPac"
Height="{StaticResource MenuItemHeight}"
AutomationProperties.Name="{x:Static resx:ResUI.menuSystemProxyPac}">
<MenuItem.Header> <MenuItem.Header>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<materialDesign:PackIcon <materialDesign:PackIcon
@ -162,18 +172,21 @@
</MenuItem.Header> </MenuItem.Header>
</MenuItem> </MenuItem>
<Separator x:Name="sepRoutings" /> <Separator x:Name="sepRoutings" />
<MenuItem x:Name="menuRoutings" Height="Auto" <MenuItem
AutomationProperties.Name="{x:Static resx:ResUI.menuRouting}"> x:Name="menuRoutings"
Height="Auto"
AutomationProperties.Name="{x:Static resx:ResUI.menuRouting}">
<MenuItem.Header> <MenuItem.Header>
<DockPanel> <DockPanel>
<ComboBox <ComboBox
x:Name="cmbRoutings" x:Name="cmbRoutings"
MaxWidth="300" MaxWidth="300"
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuRouting}" materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuRouting}"
AutomationProperties.Name="{x:Static resx:ResUI.menuRouting}"
DisplayMemberPath="Remarks" DisplayMemberPath="Remarks"
FontSize="{DynamicResource StdFontSize}" FontSize="{DynamicResource StdFontSize}"
Style="{StaticResource MaterialDesignFilledComboBox}" Style="{StaticResource MaterialDesignFilledComboBox}">
AutomationProperties.Name="{x:Static resx:ResUI.menuRouting}"/> </ComboBox>
</DockPanel> </DockPanel>
</MenuItem.Header> </MenuItem.Header>
</MenuItem> </MenuItem>
@ -184,10 +197,11 @@
x:Name="cmbServers" x:Name="cmbServers"
MaxWidth="300" MaxWidth="300"
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuServers}" materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuServers}"
AutomationProperties.Name="{x:Static resx:ResUI.menuServers}"
DisplayMemberPath="Text" DisplayMemberPath="Text"
FontSize="{DynamicResource StdFontSize}" FontSize="{DynamicResource StdFontSize}"
Style="{StaticResource MaterialDesignFilledComboBox}" Style="{StaticResource MaterialDesignFilledComboBox}">
AutomationProperties.Name="{x:Static resx:ResUI.menuServers}"/> </ComboBox>
</DockPanel> </DockPanel>
</MenuItem.Header> </MenuItem.Header>
</MenuItem> </MenuItem>

View file

@ -1,11 +1,11 @@
<reactiveui:ReactiveWindow <reactiveui:ReactiveWindow
x:Class="v2rayN.Views.SubEditWindow" x:Class="v2rayN.Views.SubEditWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:reactiveui="http://reactiveui.net"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:reactiveui="http://reactiveui.net"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib" xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Title="{x:Static resx:ResUI.menuSubSetting}" Title="{x:Static resx:ResUI.menuSubSetting}"

View file

@ -1,11 +1,11 @@
<reactiveui:ReactiveWindow <reactiveui:ReactiveWindow
x:Class="v2rayN.Views.SubSettingWindow" x:Class="v2rayN.Views.SubSettingWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:reactiveui="http://reactiveui.net"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:reactiveui="http://reactiveui.net"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib" xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Title="{x:Static resx:ResUI.menuSubSetting}" Title="{x:Static resx:ResUI.menuSubSetting}"

View file

@ -1,10 +1,10 @@
<reactiveui:ReactiveUserControl <reactiveui:ReactiveUserControl
x:Class="v2rayN.Views.ThemeSettingView" x:Class="v2rayN.Views.ThemeSettingView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:reactiveui="http://reactiveui.net"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:reactiveui="http://reactiveui.net"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:vms="clr-namespace:v2rayN.ViewModels" xmlns:vms="clr-namespace:v2rayN.ViewModels"
d:DesignHeight="450" d:DesignHeight="450"