Compare commits

..

30 Commits

Author SHA1 Message Date
2dust
5d6c5da9d9 up 7.15.0 2025-09-28 19:12:58 +08:00
2dust
ade2db3903 Code clean 2025-09-28 19:12:17 +08:00
Wydy
7f07279a4c Update pac (#7991) 2025-09-28 19:08:29 +08:00
2dust
b25d4d57bd Fix ProfilesSelectWindow 2025-09-27 19:46:31 +08:00
2dust
46edd8f9a4 Bug fix 2025-09-27 18:07:20 +08:00
JieXu
ebb95b5ee8 Update MsgView.axaml.cs (#8042) 2025-09-27 17:02:49 +08:00
2dust
dc4611a258 Adjust qrcode width 2025-09-26 20:36:27 +08:00
2dust
03d5b7a05b Bug fix 2025-09-26 17:11:48 +08:00
2dust
a652fd879b Added simple highlight function to the message view 2025-09-26 15:29:46 +08:00
2dust
326bf334e7 Optimize and improve MsgView 2025-09-26 15:07:33 +08:00
JieXu
21a773f400 Update MsgView.axaml.cs Plan C (#8035)
* Add avaloniaEdit for test

* Adjust avaloniaEdit

* Optimize and improve message function

* Update build-linux.yml

* Update MsgView.axaml

* Update MsgView.axaml.cs

---------

Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
2025-09-26 13:55:35 +08:00
2dust
d86003df55 Optimize and improve the Subject 2025-09-25 10:56:10 +08:00
2dust
faff8e4ea2 Remove secret data from mihomo configuration 2025-09-24 18:41:00 +08:00
2dust
6b85aa0b03 Remove Splat.NLog package 2025-09-24 10:57:23 +08:00
2dust
671678724b Optimization and improvement, using event subscribers 2025-09-24 10:57:06 +08:00
2dust
e96a4818c4 Optimization and improvement 2025-09-23 15:31:19 +08:00
2dust
0377e7ce19 Optimization and improvement, using event subscribers 2025-09-23 14:27:42 +08:00
2dust
6929886b3e Optimization and improvement, using event subscribers 2025-09-23 12:08:43 +08:00
2dust
721d70c8c7 Update Directory.Packages.props 2025-09-23 11:39:57 +08:00
2dust
27b45aee83 Optimization and improvement, using event subscribers 2025-09-23 11:39:55 +08:00
2dust
18ac76e683 up 7.14.12 2025-09-21 14:50:01 +08:00
2dust
3e1e23a524 Update Directory.Packages.props 2025-09-21 14:48:54 +08:00
2dust
534c7ab444 Optimize and improve QR code display 2025-09-21 14:35:49 +08:00
2dust
c2c13ad318 Create v2rayN.slnx
https://github.com/2dust/v2rayN/pull/7969
2025-09-21 12:12:24 +08:00
2dust
3a21596d95 Fix node domain resolving in TUN mode
https://github.com/2dust/v2rayN/pull/7989
2025-09-21 12:05:06 +08:00
2dust
ef30d389dc up 7.14.11 2025-09-20 14:06:55 +08:00
2dust
bf8783fed7 Update CheckUpdateViewModel.cs 2025-09-20 14:06:41 +08:00
DHR60
4e042295d2 Add global fakeip and fakeip filter (#7919) 2025-09-13 14:55:30 +08:00
2dust
33d9c5db6c up GlobalUsings 2025-09-13 14:46:35 +08:00
DHR60
cb182125f6 Fix (#7946)
https://github.com/2dust/v2rayN/pull/7937
2025-09-13 11:13:09 +08:00
83 changed files with 966 additions and 595 deletions

View File

@@ -22,7 +22,7 @@ jobs:
matrix: matrix:
configuration: [Release] configuration: [Release]
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
steps: steps:
- name: Checkout - name: Checkout

View File

@@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>7.14.10</Version> <Version>7.15.0</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

View File

@@ -5,6 +5,7 @@
<CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled> <CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.3.0" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.6" /> <PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.6" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.6" /> <PackageVersion Include="Avalonia.Desktop" Version="11.3.6" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.6" /> <PackageVersion Include="Avalonia.Diagnostics" Version="11.3.6" />
@@ -18,9 +19,10 @@
<PackageVersion Include="ReactiveUI" Version="20.4.1" /> <PackageVersion Include="ReactiveUI" Version="20.4.1" />
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" /> <PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageVersion Include="ReactiveUI.WPF" Version="20.4.1" /> <PackageVersion Include="ReactiveUI.WPF" Version="20.4.1" />
<PackageVersion Include="Semi.Avalonia" Version="11.2.1.9" /> <PackageVersion Include="Semi.Avalonia" Version="11.2.1.10" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.9" /> <PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" />
<PackageVersion Include="Splat.NLog" Version="16.2.1" /> <PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.10" />
<PackageVersion Include="NLog" Version="6.0.4" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" /> <PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<PackageVersion Include="TaskScheduler" Version="2.12.2" /> <PackageVersion Include="TaskScheduler" Version="2.12.2" />
<PackageVersion Include="WebDav.Client" Version="2.9.0" /> <PackageVersion Include="WebDav.Client" Version="2.9.0" />

View File

@@ -1,4 +1,5 @@
using QRCoder; using QRCoder;
using QRCoder.Exceptions;
using SkiaSharp; using SkiaSharp;
using ZXing.SkiaSharp; using ZXing.SkiaSharp;
@@ -8,11 +9,46 @@ public class QRCodeUtils
{ {
public static byte[]? GenQRCode(string? url) public static byte[]? GenQRCode(string? url)
{ {
if (url.IsNullOrEmpty())
{
return null;
}
using QRCodeGenerator qrGenerator = new(); using QRCodeGenerator qrGenerator = new();
using var qrCodeData = qrGenerator.CreateQrCode(url ?? string.Empty, QRCodeGenerator.ECCLevel.Q); DataTooLongException? lastDtle = null;
var levels = new[]
{
QRCodeGenerator.ECCLevel.H,
QRCodeGenerator.ECCLevel.Q,
QRCodeGenerator.ECCLevel.M,
QRCodeGenerator.ECCLevel.L
};
foreach (var level in levels)
{
try
{
using var qrCodeData = qrGenerator.CreateQrCode(url, level);
using PngByteQRCode qrCode = new(qrCodeData); using PngByteQRCode qrCode = new(qrCodeData);
return qrCode.GetGraphic(20); return qrCode.GetGraphic(20);
} }
catch (DataTooLongException ex)
{
lastDtle = ex;
continue;
}
catch
{
throw;
}
}
if (lastDtle != null)
{
throw lastDtle;
}
return null;
}
public static string? ParseBarcode(string? fileName) public static string? ParseBarcode(string? fileName)
{ {

View File

@@ -12,7 +12,6 @@ public enum EViewAction
ProfilesFocus, ProfilesFocus,
ShareSub, ShareSub,
ShareServer, ShareServer,
ShowHideWindow,
ScanScreenTask, ScanScreenTask,
ScanImageTask, ScanImageTask,
BrowseServer, BrowseServer,

View File

@@ -0,0 +1,32 @@
using System.Reactive;
namespace ServiceLib.Events;
public static class AppEvents
{
public static readonly EventChannel<Unit> ReloadRequested = new();
public static readonly EventChannel<bool?> ShowHideWindowRequested = new();
public static readonly EventChannel<Unit> AddServerViaScanRequested = new();
public static readonly EventChannel<Unit> AddServerViaClipboardRequested = new();
public static readonly EventChannel<bool> SubscriptionsUpdateRequested = new();
public static readonly EventChannel<Unit> ProfilesRefreshRequested = new();
public static readonly EventChannel<Unit> SubscriptionsRefreshRequested = new();
public static readonly EventChannel<Unit> ProxiesReloadRequested = new();
public static readonly EventChannel<ServerSpeedItem> DispatcherStatisticsRequested = new();
public static readonly EventChannel<string> SendSnackMsgRequested = new();
public static readonly EventChannel<string> SendMsgViewRequested = new();
public static readonly EventChannel<Unit> AppExitRequested = new();
public static readonly EventChannel<bool> ShutdownRequested = new();
public static readonly EventChannel<Unit> AdjustMainLvColWidthRequested = new();
public static readonly EventChannel<string> SetDefaultServerRequested = new();
public static readonly EventChannel<Unit> RoutingsMenuRefreshRequested = new();
public static readonly EventChannel<Unit> TestServerRequested = new();
public static readonly EventChannel<Unit> InboundDisplayRequested = new();
public static readonly EventChannel<ESysProxyType> SysProxyChangeRequested = new();
}

View File

@@ -0,0 +1,29 @@
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
namespace ServiceLib.Events;
public sealed class EventChannel<T>
{
private readonly ISubject<T> _subject = Subject.Synchronize(new Subject<T>());
public IObservable<T> AsObservable()
{
return _subject.AsObservable();
}
public void Publish(T value)
{
_subject.OnNext(value);
}
public void Publish()
{
if (typeof(T) != typeof(Unit))
{
throw new InvalidOperationException("Publish() without value is only valid for EventChannel<Unit>.");
}
_subject.OnNext((T)(object)Unit.Default);
}
}

View File

@@ -40,6 +40,7 @@ public class Global
public const string ProxySetLinuxShellFileName = NamespaceSample + "proxy_set_linux_sh"; public const string ProxySetLinuxShellFileName = NamespaceSample + "proxy_set_linux_sh";
public const string KillAsSudoOSXShellFileName = NamespaceSample + "kill_as_sudo_osx_sh"; public const string KillAsSudoOSXShellFileName = NamespaceSample + "kill_as_sudo_osx_sh";
public const string KillAsSudoLinuxShellFileName = NamespaceSample + "kill_as_sudo_linux_sh"; public const string KillAsSudoLinuxShellFileName = NamespaceSample + "kill_as_sudo_linux_sh";
public const string SingboxFakeIPFilterFileName = NamespaceSample + "singbox_fakeip_filter";
public const string DefaultSecurity = "auto"; public const string DefaultSecurity = "auto";
public const string DefaultNetwork = "tcp"; public const string DefaultNetwork = "tcp";
@@ -448,6 +449,14 @@ public class Global
"none" "none"
]; ];
public static readonly Dictionary<string, string> LogLevelColors = new()
{
{ "debug", "#6C757D" },
{ "info", "#2ECC71" },
{ "warning", "#FFA500" },
{ "error", "#E74C3C" },
};
public static readonly List<string> InboundTags = public static readonly List<string> InboundTags =
[ [
"socks", "socks",
@@ -597,6 +606,7 @@ public class Global
{ "cloudflare-dns.com", new List<string> { "104.16.249.249", "104.16.248.249", "2606:4700::6810:f8f9", "2606:4700::6810:f9f9" } }, { "cloudflare-dns.com", new List<string> { "104.16.249.249", "104.16.248.249", "2606:4700::6810:f8f9", "2606:4700::6810:f9f9" } },
{ "dns.cloudflare.com", new List<string> { "104.16.132.229", "104.16.133.229", "2606:4700::6810:84e5", "2606:4700::6810:85e5" } }, { "dns.cloudflare.com", new List<string> { "104.16.132.229", "104.16.133.229", "2606:4700::6810:84e5", "2606:4700::6810:85e5" } },
{ "dot.pub", new List<string> { "1.12.12.12", "120.53.53.53" } }, { "dot.pub", new List<string> { "1.12.12.12", "120.53.53.53" } },
{ "doh.pub", new List<string> { "1.12.12.12", "120.53.53.53" } },
{ "dns.quad9.net", new List<string> { "9.9.9.9", "149.112.112.112", "2620:fe::fe", "2620:fe::9" } }, { "dns.quad9.net", new List<string> { "9.9.9.9", "149.112.112.112", "2620:fe::fe", "2620:fe::9" } },
{ "dns.yandex.net", new List<string> { "77.88.8.8", "77.88.8.1", "2a02:6b8::feed:0ff", "2a02:6b8:0:1::feed:0ff" } }, { "dns.yandex.net", new List<string> { "77.88.8.8", "77.88.8.1", "2a02:6b8::feed:0ff", "2a02:6b8:0:1::feed:0ff" } },
{ "dns.sb", new List<string> { "185.222.222.222", "2a09::" } }, { "dns.sb", new List<string> { "185.222.222.222", "2a09::" } },

View File

@@ -1,6 +1,7 @@
global using ServiceLib.Base; global using ServiceLib.Base;
global using ServiceLib.Common; global using ServiceLib.Common;
global using ServiceLib.Enums; global using ServiceLib.Enums;
global using ServiceLib.Events;
global using ServiceLib.Handler; global using ServiceLib.Handler;
global using ServiceLib.Helper; global using ServiceLib.Helper;
global using ServiceLib.Manager; global using ServiceLib.Manager;

View File

@@ -1,21 +0,0 @@
using System.Reactive;
using System.Reactive.Subjects;
namespace ServiceLib.Handler;
public static class AppEvents
{
public static readonly Subject<Unit> ProfilesRefreshRequested = new();
public static readonly Subject<string> SendSnackMsgRequested = new();
public static readonly Subject<string> SendMsgViewRequested = new();
public static readonly Subject<Unit> AppExitRequested = new();
public static readonly Subject<bool> ShutdownRequested = new();
public static readonly Subject<Unit> AdjustMainLvColWidthRequested = new();
public static readonly Subject<ServerSpeedItem> DispatcherStatisticsRequested = new();
}

View File

@@ -113,6 +113,10 @@ public static class ConfigHandler
config.ConstItem ??= new ConstItem(); config.ConstItem ??= new ConstItem();
config.SimpleDNSItem ??= InitBuiltinSimpleDNS(); config.SimpleDNSItem ??= InitBuiltinSimpleDNS();
if (config.SimpleDNSItem.GlobalFakeIp is null)
{
config.SimpleDNSItem.GlobalFakeIp = true;
}
config.SpeedTestItem ??= new(); config.SpeedTestItem ??= new();
if (config.SpeedTestItem.SpeedTestTimeout < 10) if (config.SpeedTestItem.SpeedTestTimeout < 10)
@@ -1210,11 +1214,11 @@ public static class ConfigHandler
CoreType = ECoreType.sing_box, CoreType = ECoreType.sing_box,
ConfigType = EConfigType.SOCKS, ConfigType = EConfigType.SOCKS,
Address = Global.Loopback, Address = Global.Loopback,
Sni = node.Address, //Tun2SocksAddress SpiderX = node.Address, // Tun2SocksAddress
Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks) Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks)
}; };
} }
else if ((node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0)) else if (node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0)
{ {
var preCoreType = config.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray; var preCoreType = config.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
itemSocks = new ProfileItem() itemSocks = new ProfileItem()
@@ -2221,6 +2225,7 @@ public static class ConfigHandler
UseSystemHosts = false, UseSystemHosts = false,
AddCommonHosts = true, AddCommonHosts = true,
FakeIP = false, FakeIP = false,
GlobalFakeIp = true,
BlockBindingQuery = true, BlockBindingQuery = true,
DirectDNS = Global.DomainDirectDNSAddress.FirstOrDefault(), DirectDNS = Global.DomainDirectDNSAddress.FirstOrDefault(),
RemoteDNS = Global.DomainRemoteDNSAddress.FirstOrDefault(), RemoteDNS = Global.DomainRemoteDNSAddress.FirstOrDefault(),

View File

@@ -1,5 +1,3 @@
using System.Reactive;
namespace ServiceLib.Manager; namespace ServiceLib.Manager;
public sealed class AppManager public sealed class AppManager
@@ -96,7 +94,7 @@ public sealed class AppManager
Logging.SaveLog("AppExitAsync Begin"); Logging.SaveLog("AppExitAsync Begin");
await SysProxyHandler.UpdateSysProxy(_config, true); await SysProxyHandler.UpdateSysProxy(_config, true);
AppEvents.AppExitRequested.OnNext(Unit.Default); AppEvents.AppExitRequested.Publish();
await Task.Delay(50); //Wait for AppExitRequested to be processed await Task.Delay(50); //Wait for AppExitRequested to be processed
await ConfigHandler.SaveConfig(_config); await ConfigHandler.SaveConfig(_config);
@@ -119,7 +117,13 @@ public sealed class AppManager
public void Shutdown(bool byUser) public void Shutdown(bool byUser)
{ {
AppEvents.ShutdownRequested.OnNext(byUser); AppEvents.ShutdownRequested.Publish(byUser);
}
public async Task RebootAsAdmin()
{
ProcUtils.RebootAsAdmin();
await AppManager.Instance.AppExitAsync(true);
} }
#endregion App #endregion App

View File

@@ -11,7 +11,7 @@ public class NoticeManager
{ {
return; return;
} }
AppEvents.SendSnackMsgRequested.OnNext(content); AppEvents.SendSnackMsgRequested.Publish(content);
} }
public void SendMessage(string? content) public void SendMessage(string? content)
@@ -20,7 +20,7 @@ public class NoticeManager
{ {
return; return;
} }
AppEvents.SendMsgViewRequested.OnNext(content); AppEvents.SendMsgViewRequested.Publish(content);
} }
public void SendMessageEx(string? content) public void SendMessageEx(string? content)

View File

@@ -260,6 +260,7 @@ public class SimpleDNSItem
public bool? UseSystemHosts { get; set; } public bool? UseSystemHosts { get; set; }
public bool? AddCommonHosts { get; set; } public bool? AddCommonHosts { get; set; }
public bool? FakeIP { get; set; } public bool? FakeIP { get; set; }
public bool? GlobalFakeIp { get; set; }
public bool? BlockBindingQuery { get; set; } public bool? BlockBindingQuery { get; set; }
public string? DirectDNS { get; set; } public string? DirectDNS { get; set; }
public string? RemoteDNS { get; set; } public string? RemoteDNS { get; set; }

View File

@@ -2301,15 +2301,6 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Apply to Proxy Domains Only 的本地化字符串。
/// </summary>
public static string TbApplyProxyDomainsOnly {
get {
return ResourceManager.GetString("TbApplyProxyDomainsOnly", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Auto refresh 的本地化字符串。 /// 查找类似 Auto refresh 的本地化字符串。
/// </summary> /// </summary>
@@ -2526,6 +2517,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Applies globally by default, with built-in FakeIP filtering (sing-box only). 的本地化字符串。
/// </summary>
public static string TbFakeIPTips {
get {
return ResourceManager.GetString("TbFakeIPTips", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Fingerprint 的本地化字符串。 /// 查找类似 Fingerprint 的本地化字符串。
/// </summary> /// </summary>

View File

@@ -1455,9 +1455,6 @@
<data name="TbDNSHostsConfig" xml:space="preserve"> <data name="TbDNSHostsConfig" xml:space="preserve">
<value>DNS Hosts: ("domain1 ip1 ip2" per line)</value> <value>DNS Hosts: ("domain1 ip1 ip2" per line)</value>
</data> </data>
<data name="TbApplyProxyDomainsOnly" xml:space="preserve">
<value>Apply to Proxy Domains Only</value>
</data>
<data name="ThBasicDNSSettings" xml:space="preserve"> <data name="ThBasicDNSSettings" xml:space="preserve">
<value>Basic DNS Settings</value> <value>Basic DNS Settings</value>
</data> </data>
@@ -1515,4 +1512,7 @@
<data name="TbSelectProfile" xml:space="preserve"> <data name="TbSelectProfile" xml:space="preserve">
<value>Select Profile</value> <value>Select Profile</value>
</data> </data>
<data name="TbFakeIPTips" xml:space="preserve">
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
</data>
</root> </root>

View File

@@ -1455,9 +1455,6 @@
<data name="TbDNSHostsConfig" xml:space="preserve"> <data name="TbDNSHostsConfig" xml:space="preserve">
<value>DNS Hosts: ("domain1 ip1 ip2" per line)</value> <value>DNS Hosts: ("domain1 ip1 ip2" per line)</value>
</data> </data>
<data name="TbApplyProxyDomainsOnly" xml:space="preserve">
<value>Apply to Proxy Domains Only</value>
</data>
<data name="ThBasicDNSSettings" xml:space="preserve"> <data name="ThBasicDNSSettings" xml:space="preserve">
<value>Basic DNS Settings</value> <value>Basic DNS Settings</value>
</data> </data>
@@ -1515,4 +1512,7 @@
<data name="TbSelectProfile" xml:space="preserve"> <data name="TbSelectProfile" xml:space="preserve">
<value>Select Profile</value> <value>Select Profile</value>
</data> </data>
<data name="TbFakeIPTips" xml:space="preserve">
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
</data>
</root> </root>

View File

@@ -1455,9 +1455,6 @@
<data name="TbDNSHostsConfig" xml:space="preserve"> <data name="TbDNSHostsConfig" xml:space="preserve">
<value>DNS Hosts: ("domain1 ip1 ip2" per line)</value> <value>DNS Hosts: ("domain1 ip1 ip2" per line)</value>
</data> </data>
<data name="TbApplyProxyDomainsOnly" xml:space="preserve">
<value>Apply to Proxy Domains Only</value>
</data>
<data name="ThBasicDNSSettings" xml:space="preserve"> <data name="ThBasicDNSSettings" xml:space="preserve">
<value>Basic DNS Settings</value> <value>Basic DNS Settings</value>
</data> </data>
@@ -1515,4 +1512,7 @@
<data name="TbSelectProfile" xml:space="preserve"> <data name="TbSelectProfile" xml:space="preserve">
<value>Select Profile</value> <value>Select Profile</value>
</data> </data>
<data name="TbFakeIPTips" xml:space="preserve">
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
</data>
</root> </root>

View File

@@ -1455,9 +1455,6 @@
<data name="TbDNSHostsConfig" xml:space="preserve"> <data name="TbDNSHostsConfig" xml:space="preserve">
<value>DNS hosts: (каждая строка в формате "domain1 ip1 ip2")</value> <value>DNS hosts: (каждая строка в формате "domain1 ip1 ip2")</value>
</data> </data>
<data name="TbApplyProxyDomainsOnly" xml:space="preserve">
<value>Применять только к доменам через прокси</value>
</data>
<data name="ThBasicDNSSettings" xml:space="preserve"> <data name="ThBasicDNSSettings" xml:space="preserve">
<value>Базовые настройки DNS</value> <value>Базовые настройки DNS</value>
</data> </data>
@@ -1515,4 +1512,7 @@
<data name="TbSelectProfile" xml:space="preserve"> <data name="TbSelectProfile" xml:space="preserve">
<value>Select Profile</value> <value>Select Profile</value>
</data> </data>
<data name="TbFakeIPTips" xml:space="preserve">
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
</data>
</root> </root>

View File

@@ -1452,9 +1452,6 @@
<data name="TbDNSHostsConfig" xml:space="preserve"> <data name="TbDNSHostsConfig" xml:space="preserve">
<value>DNS Hosts“域名1 ip1 ip2” 一行一个)</value> <value>DNS Hosts“域名1 ip1 ip2” 一行一个)</value>
</data> </data>
<data name="TbApplyProxyDomainsOnly" xml:space="preserve">
<value>仅对代理域名生效</value>
</data>
<data name="ThBasicDNSSettings" xml:space="preserve"> <data name="ThBasicDNSSettings" xml:space="preserve">
<value>DNS 基础设置</value> <value>DNS 基础设置</value>
</data> </data>
@@ -1512,4 +1509,7 @@
<data name="TbSelectProfile" xml:space="preserve"> <data name="TbSelectProfile" xml:space="preserve">
<value>选择配置文件</value> <value>选择配置文件</value>
</data> </data>
<data name="TbFakeIPTips" xml:space="preserve">
<value>默认全局生效,内置 FakeIP 过滤,仅在 sing-box 中生效</value>
</data>
</root> </root>

View File

@@ -1452,9 +1452,6 @@
<data name="TbDNSHostsConfig" xml:space="preserve"> <data name="TbDNSHostsConfig" xml:space="preserve">
<value>DNS Hosts: ("domain1 ip1 ip2" per line)</value> <value>DNS Hosts: ("domain1 ip1 ip2" per line)</value>
</data> </data>
<data name="TbApplyProxyDomainsOnly" xml:space="preserve">
<value>Apply to Proxy Domains Only</value>
</data>
<data name="ThBasicDNSSettings" xml:space="preserve"> <data name="ThBasicDNSSettings" xml:space="preserve">
<value>Basic DNS Settings</value> <value>Basic DNS Settings</value>
</data> </data>
@@ -1512,4 +1509,7 @@
<data name="TbSelectProfile" xml:space="preserve"> <data name="TbSelectProfile" xml:space="preserve">
<value>Select Profile</value> <value>Select Profile</value>
</data> </data>
<data name="TbFakeIPTips" xml:space="preserve">
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
</data>
</root> </root>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,92 @@
{
"domain": [
"amobile.music.tc.qq.com",
"api-jooxtt.sanook.com",
"api.joox.com",
"aqqmusic.tc.qq.com",
"dl.stream.qqmusic.qq.com",
"ff.dorado.sdo.com",
"heartbeat.belkin.com",
"isure.stream.qqmusic.qq.com",
"joox.com",
"lens.l.google.com",
"localhost.ptlogin2.qq.com",
"localhost.sec.qq.com",
"mesu.apple.com",
"mobileoc.music.tc.qq.com",
"music.taihe.com",
"musicapi.taihe.com",
"na.b.g-tun.com",
"proxy.golang.org",
"ps.res.netease.com",
"shark007.net",
"songsearch.kugou.com",
"static.adtidy.org",
"streamoc.music.tc.qq.com",
"swcdn.apple.com",
"swdist.apple.com",
"swdownload.apple.com",
"swquery.apple.com",
"swscan.apple.com",
"trackercdn.kugou.com",
"xnotify.xboxlive.com"
],
"domain_keyword": [
"ntp",
"stun",
"time"
],
"domain_regex": [
"^[^.]+$",
"^[^.]+\\.[^.]+\\.xboxlive\\.com$",
"^localhost\\.[^.]+\\.weixin\\.qq\\.com$",
"^mijia\\scloud$",
"^xbox\\.[^.]+\\.microsoft\\.com$",
"^xbox\\.[^.]+\\.[^.]+\\.microsoft\\.com$"
],
"domain_suffix": [
"126.net",
"3gppnetwork.org",
"battle.net",
"battlenet.com.cn",
"cdn.nintendo.net",
"cmbchina.com",
"cmbimg.com",
"ff14.sdo.com",
"ffxiv.com",
"finalfantasyxiv.com",
"gcloudcs.com",
"home.arpa",
"invalid",
"kuwo.cn",
"lan",
"linksys.com",
"linksyssmartwifi.com",
"local",
"localdomain",
"localhost",
"market.xiaomi.com",
"mcdn.bilivideo.cn",
"media.dssott.com",
"msftconnecttest.com",
"msftncsi.com",
"music.163.com",
"music.migu.cn",
"n0808.com",
"nflxvideo.net",
"oray.com",
"orayimg.com",
"router.asus.com",
"sandai.net",
"square-enix.com",
"srv.nintendo.net",
"steamcontent.com",
"uu.163.com",
"wargaming.net",
"wggames.cn",
"wotgame.cn",
"wowsgame.cn",
"xiami.com",
"y.qq.com"
]
}

View File

@@ -11,7 +11,7 @@
</PackageReference> </PackageReference>
<PackageReference Include="ReactiveUI.Fody" /> <PackageReference Include="ReactiveUI.Fody" />
<PackageReference Include="sqlite-net-pcl" /> <PackageReference Include="sqlite-net-pcl" />
<PackageReference Include="Splat.NLog" /> <PackageReference Include="NLog" />
<PackageReference Include="WebDav.Client" /> <PackageReference Include="WebDav.Client" />
<PackageReference Include="YamlDotNet" /> <PackageReference Include="YamlDotNet" />
<PackageReference Include="QRCoder" /> <PackageReference Include="QRCoder" />
@@ -44,6 +44,7 @@
<EmbeddedResource Include="Sample\tun_singbox_inbound" /> <EmbeddedResource Include="Sample\tun_singbox_inbound" />
<EmbeddedResource Include="Sample\tun_singbox_rules" /> <EmbeddedResource Include="Sample\tun_singbox_rules" />
<EmbeddedResource Include="Sample\linux_autostart_config" /> <EmbeddedResource Include="Sample\linux_autostart_config" />
<EmbeddedResource Include="Sample\singbox_fakeip_filter" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -79,6 +79,7 @@ public class CoreConfigClashService
//external-controller //external-controller
fileContent["external-controller"] = $"{Global.Loopback}:{AppManager.Instance.StatePort2}"; fileContent["external-controller"] = $"{Global.Loopback}:{AppManager.Instance.StatePort2}";
fileContent.Remove("secret");
//allow-lan //allow-lan
if (_config.Inbound.First().AllowLANConn) if (_config.Inbound.First().AllowLANConn)
{ {

View File

@@ -33,17 +33,17 @@ public partial class CoreConfigSingboxService
lastRule.Ip?.Contains("0.0.0.0/0") == true); lastRule.Ip?.Contains("0.0.0.0/0") == true);
} }
singboxConfig.dns.final = useDirectDns ? Global.SingboxDirectDNSTag : Global.SingboxRemoteDNSTag; singboxConfig.dns.final = useDirectDns ? Global.SingboxDirectDNSTag : Global.SingboxRemoteDNSTag;
if ((!useDirectDns) && simpleDNSItem.FakeIP == true && simpleDNSItem.GlobalFakeIp == false)
// Tun2SocksAddress
if (node != null && Utils.IsDomain(node.Address))
{ {
singboxConfig.dns.rules ??= new List<Rule4Sbox>(); singboxConfig.dns.rules.Add(new()
singboxConfig.dns.rules.Insert(0, new Rule4Sbox
{ {
server = Global.SingboxOutboundResolverTag, server = Global.SingboxFakeDNSTag,
domain = [node.Address], query_type = new List<int> { 1, 28 }, // A and AAAA
rewrite_ttl = 1,
}); });
} }
await GenOutboundDnsRule(node, singboxConfig, Global.SingboxOutboundResolverTag);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -187,6 +187,28 @@ public partial class CoreConfigSingboxService
}); });
} }
if (simpleDNSItem.FakeIP == true && simpleDNSItem.GlobalFakeIp == true)
{
var fakeipFilterRule = JsonUtils.Deserialize<Rule4Sbox>(EmbedUtils.GetEmbedText(Global.SingboxFakeIPFilterFileName));
fakeipFilterRule.invert = true;
var rule4Fake = new Rule4Sbox
{
server = Global.SingboxFakeDNSTag,
type = "logical",
mode = "and",
rewrite_ttl = 1,
rules = new List<Rule4Sbox>
{
new() {
query_type = new List<int> { 1, 28 }, // A and AAAA
},
fakeipFilterRule,
}
};
singboxConfig.dns.rules.Add(rule4Fake);
}
var routing = await ConfigHandler.GetDefaultRouting(_config); var routing = await ConfigHandler.GetDefaultRouting(_config);
if (routing == null) if (routing == null)
return 0; return 0;
@@ -266,10 +288,12 @@ public partial class CoreConfigSingboxService
} }
else else
{ {
if (simpleDNSItem.FakeIP == true) if (simpleDNSItem.FakeIP == true && simpleDNSItem.GlobalFakeIp == false)
{ {
var rule4Fake = JsonUtils.DeepCopy(rule); var rule4Fake = JsonUtils.DeepCopy(rule);
rule4Fake.server = Global.SingboxFakeDNSTag; rule4Fake.server = Global.SingboxFakeDNSTag;
rule4Fake.query_type = new List<int> { 1, 28 }; // A and AAAA
rule4Fake.rewrite_ttl = 1;
singboxConfig.dns.rules.Add(rule4Fake); singboxConfig.dns.rules.Add(rule4Fake);
} }
rule.server = Global.SingboxRemoteDNSTag; rule.server = Global.SingboxRemoteDNSTag;
@@ -313,16 +337,7 @@ public partial class CoreConfigSingboxService
await GenDnsDomainsLegacyCompatible(singboxConfig, item); await GenDnsDomainsLegacyCompatible(singboxConfig, item);
} }
// Tun2SocksAddress await GenOutboundDnsRule(node, singboxConfig, Global.SingboxFinalResolverTag);
if (node != null && Utils.IsDomain(node.Address))
{
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
singboxConfig.dns.rules.Insert(0, new Rule4Sbox
{
server = Global.SingboxFinalResolverTag,
domain = [node.Address],
});
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -392,6 +407,37 @@ public partial class CoreConfigSingboxService
return await Task.FromResult(0); return await Task.FromResult(0);
} }
private async Task<int> GenOutboundDnsRule(ProfileItem? node, SingboxConfig singboxConfig, string? server)
{
if (node == null)
{
return 0;
}
var domain = string.Empty;
if (Utils.IsDomain(node.Address)) // normal outbound
{
domain = node.Address;
}
else if (node.Address == Global.Loopback && node.SpiderX.IsNotEmpty() && Utils.IsDomain(node.SpiderX)) // Tun2SocksAddress
{
domain = node.SpiderX;
}
if (domain.IsNullOrEmpty())
{
return 0;
}
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
singboxConfig.dns.rules.Insert(0, new Rule4Sbox
{
server = server,
domain = [domain],
});
return await Task.FromResult(0);
}
private static Server4Sbox? ParseDnsAddress(string address) private static Server4Sbox? ParseDnsAddress(string address)
{ {
var addressFirst = address?.Split(address.Contains(',') ? ',' : ';').FirstOrDefault()?.Trim(); var addressFirst = address?.Split(address.Contains(',') ? ',' : ';').FirstOrDefault()?.Trim();

View File

@@ -72,11 +72,6 @@ public partial class CoreConfigSingboxService
} }
var hostsDomains = new List<string>(); var hostsDomains = new List<string>();
var systemHostsMap = Utils.GetSystemHosts();
foreach (var kvp in systemHostsMap)
{
hostsDomains.Add(kvp.Key);
}
var dnsItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box); var dnsItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
if (dnsItem == null || dnsItem.Enabled == false) if (dnsItem == null || dnsItem.Enabled == false)
{ {
@@ -89,12 +84,23 @@ public partial class CoreConfigSingboxService
hostsDomains.Add(kvp.Key); hostsDomains.Add(kvp.Key);
} }
} }
if (simpleDNSItem.UseSystemHosts == true)
{
var systemHostsMap = Utils.GetSystemHosts();
foreach (var kvp in systemHostsMap)
{
hostsDomains.Add(kvp.Key);
} }
}
}
if (hostsDomains.Count > 0)
{
singboxConfig.route.rules.Add(new() singboxConfig.route.rules.Add(new()
{ {
action = "resolve", action = "resolve",
domain = hostsDomains, domain = hostsDomains,
}); });
}
singboxConfig.route.rules.Add(new() singboxConfig.route.rules.Add(new()
{ {

View File

@@ -2,11 +2,9 @@ using System.Reactive;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using DynamicData;
using DynamicData.Binding; using DynamicData.Binding;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.Fody.Helpers; using ReactiveUI.Fody.Helpers;
using Splat;
namespace ServiceLib.ViewModels; namespace ServiceLib.ViewModels;
@@ -38,7 +36,7 @@ public class CheckUpdateViewModel : MyReactiveObject
this.WhenAnyValue( this.WhenAnyValue(
x => x.EnableCheckPreReleaseUpdate, x => x.EnableCheckPreReleaseUpdate,
y => y == true) y => y == true)
.Subscribe(c => { _config.CheckUpdateItem.CheckPreReleaseUpdate = EnableCheckPreReleaseUpdate; }); .Subscribe(c => _config.CheckUpdateItem.CheckPreReleaseUpdate = EnableCheckPreReleaseUpdate);
RefreshCheckUpdateItems(); RefreshCheckUpdateItems();
} }
@@ -158,11 +156,8 @@ public class CheckUpdateViewModel : MyReactiveObject
UpdatedPlusPlus(_geo, ""); UpdatedPlusPlus(_geo, "");
} }
} }
await (new UpdateService()).UpdateGeoFileAll(_config, _updateUI) await new UpdateService().UpdateGeoFileAll(_config, _updateUI)
.ContinueWith(t => .ContinueWith(t => UpdatedPlusPlus(_geo, ""));
{
UpdatedPlusPlus(_geo, "");
});
} }
private async Task CheckUpdateN(bool preRelease) private async Task CheckUpdateN(bool preRelease)
@@ -176,11 +171,8 @@ public class CheckUpdateViewModel : MyReactiveObject
UpdatedPlusPlus(_v2rayN, msg); UpdatedPlusPlus(_v2rayN, msg);
} }
} }
await (new UpdateService()).CheckUpdateGuiN(_config, _updateUI, preRelease) await new UpdateService().CheckUpdateGuiN(_config, _updateUI, preRelease)
.ContinueWith(t => .ContinueWith(t => UpdatedPlusPlus(_v2rayN, ""));
{
UpdatedPlusPlus(_v2rayN, "");
});
} }
private async Task CheckUpdateCore(CheckUpdateModel model, bool preRelease) private async Task CheckUpdateCore(CheckUpdateModel model, bool preRelease)
@@ -196,11 +188,8 @@ public class CheckUpdateViewModel : MyReactiveObject
} }
} }
var type = (ECoreType)Enum.Parse(typeof(ECoreType), model.CoreType); var type = (ECoreType)Enum.Parse(typeof(ECoreType), model.CoreType);
await (new UpdateService()).CheckUpdateCore(type, _config, _updateUI, preRelease) await new UpdateService().CheckUpdateCore(type, _config, _updateUI, preRelease)
.ContinueWith(t => .ContinueWith(t => UpdatedPlusPlus(model.CoreType, ""));
{
UpdatedPlusPlus(model.CoreType, "");
});
} }
private async Task UpdateFinished() private async Task UpdateFinished()
@@ -234,11 +223,11 @@ public class CheckUpdateViewModel : MyReactiveObject
{ {
if (blReload) if (blReload)
{ {
Locator.Current.GetService<MainWindowViewModel>()?.Reload(); AppEvents.ReloadRequested.Publish();
} }
else else
{ {
Locator.Current.GetService<MainWindowViewModel>()?.CloseCore(); await CoreManager.Instance.CoreStop();
} }
} }
@@ -311,7 +300,7 @@ public class CheckUpdateViewModel : MyReactiveObject
if (Utils.IsNonWindows()) if (Utils.IsNonWindows())
{ {
var filesList = (new DirectoryInfo(toPath)).GetFiles().Select(u => u.FullName).ToList(); var filesList = new DirectoryInfo(toPath).GetFiles().Select(u => u.FullName).ToList();
foreach (var file in filesList) foreach (var file in filesList)
{ {
await Utils.SetLinuxChmod(Path.Combine(toPath, item.CoreType.ToLower())); await Utils.SetLinuxChmod(Path.Combine(toPath, item.CoreType.ToLower()));

View File

@@ -69,6 +69,8 @@ public class ClashProxiesViewModel : MyReactiveObject
SortingSelected = _config.ClashUIItem.ProxiesSorting; SortingSelected = _config.ClashUIItem.ProxiesSorting;
RuleModeSelected = (int)_config.ClashUIItem.RuleMode; RuleModeSelected = (int)_config.ClashUIItem.RuleMode;
#region WhenAnyValue && ReactiveCommand
this.WhenAnyValue( this.WhenAnyValue(
x => x.SelectedGroup, x => x.SelectedGroup,
y => y != null && y.Name.IsNotEmpty()) y => y != null && y.Name.IsNotEmpty())
@@ -89,6 +91,17 @@ public class ClashProxiesViewModel : MyReactiveObject
y => y == true) y => y == true)
.Subscribe(c => { _config.ClashUIItem.ProxiesAutoRefresh = AutoRefresh; }); .Subscribe(c => { _config.ClashUIItem.ProxiesAutoRefresh = AutoRefresh; });
#endregion WhenAnyValue && ReactiveCommand
#region AppEvents
AppEvents.ProxiesReloadRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await ProxiesReload());
#endregion AppEvents
_ = Init(); _ = Init();
} }

View File

@@ -1,8 +1,8 @@
using System.Reactive; using System.Reactive;
using System.Reactive.Concurrency; using System.Reactive.Concurrency;
using System.Reactive.Linq;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.Fody.Helpers; using ReactiveUI.Fody.Helpers;
using Splat;
namespace ServiceLib.ViewModels; namespace ServiceLib.ViewModels;
@@ -184,7 +184,7 @@ public class MainWindowViewModel : MyReactiveObject
}); });
RebootAsAdminCmd = ReactiveCommand.CreateFromTask(async () => RebootAsAdminCmd = ReactiveCommand.CreateFromTask(async () =>
{ {
await RebootAsAdmin(); await AppManager.Instance.RebootAsAdmin();
}); });
ClearServerStatisticsCmd = ReactiveCommand.CreateFromTask(async () => ClearServerStatisticsCmd = ReactiveCommand.CreateFromTask(async () =>
{ {
@@ -217,6 +217,30 @@ public class MainWindowViewModel : MyReactiveObject
#endregion WhenAnyValue && ReactiveCommand #endregion WhenAnyValue && ReactiveCommand
#region AppEvents
AppEvents.ReloadRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await Reload());
AppEvents.AddServerViaScanRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await AddServerViaScanAsync());
AppEvents.AddServerViaClipboardRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await AddServerViaClipboardAsync(null));
AppEvents.SubscriptionsUpdateRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async blProxy => await UpdateSubscriptionProcess("", blProxy));
#endregion AppEvents
_ = Init(); _ = Init();
} }
@@ -224,7 +248,7 @@ public class MainWindowViewModel : MyReactiveObject
{ {
_config.UiItem.ShowInTaskbar = true; _config.UiItem.ShowInTaskbar = true;
await ConfigHandler.InitBuiltinRouting(_config); //await ConfigHandler.InitBuiltinRouting(_config);
await ConfigHandler.InitBuiltinDNS(_config); await ConfigHandler.InitBuiltinDNS(_config);
await ConfigHandler.InitBuiltinFullConfigTemplate(_config); await ConfigHandler.InitBuiltinFullConfigTemplate(_config);
await ProfileExManager.Instance.Init(); await ProfileExManager.Instance.Init();
@@ -240,7 +264,6 @@ public class MainWindowViewModel : MyReactiveObject
BlReloadEnabled = true; BlReloadEnabled = true;
await Reload(); await Reload();
await AutoHideStartup(); await AutoHideStartup();
Locator.Current.GetService<StatusBarViewModel>()?.RefreshRoutingsMenu();
} }
#endregion Init #endregion Init
@@ -269,7 +292,7 @@ public class MainWindowViewModel : MyReactiveObject
} }
if (_config.UiItem.EnableAutoAdjustMainLvColWidth) if (_config.UiItem.EnableAutoAdjustMainLvColWidth)
{ {
AppEvents.AdjustMainLvColWidthRequested.OnNext(Unit.Default); AppEvents.AdjustMainLvColWidthRequested.Publish();
} }
} }
} }
@@ -280,12 +303,7 @@ public class MainWindowViewModel : MyReactiveObject
{ {
return; return;
} }
AppEvents.DispatcherStatisticsRequested.OnNext(update); AppEvents.DispatcherStatisticsRequested.Publish(update);
}
public void ShowHideWindow(bool? blShow)
{
_updateView?.Invoke(EViewAction.ShowHideWindow, blShow);
} }
#endregion Actions #endregion Actions
@@ -294,14 +312,14 @@ public class MainWindowViewModel : MyReactiveObject
private async Task RefreshServers() private async Task RefreshServers()
{ {
AppEvents.ProfilesRefreshRequested.OnNext(Unit.Default); AppEvents.ProfilesRefreshRequested.Publish();
await Task.Delay(200); await Task.Delay(200);
} }
private void RefreshSubscriptions() private void RefreshSubscriptions()
{ {
Locator.Current.GetService<ProfilesViewModel>()?.RefreshSubscriptions(); AppEvents.SubscriptionsRefreshRequested.Publish();
} }
#endregion Servers && Groups #endregion Servers && Groups
@@ -433,7 +451,7 @@ public class MainWindowViewModel : MyReactiveObject
var ret = await _updateView?.Invoke(EViewAction.OptionSettingWindow, null); var ret = await _updateView?.Invoke(EViewAction.OptionSettingWindow, null);
if (ret == true) if (ret == true)
{ {
Locator.Current.GetService<StatusBarViewModel>()?.InboundDisplayStatus(); AppEvents.InboundDisplayRequested.Publish();
await Reload(); await Reload();
} }
} }
@@ -444,7 +462,7 @@ public class MainWindowViewModel : MyReactiveObject
if (ret == true) if (ret == true)
{ {
await ConfigHandler.InitBuiltinRouting(_config); await ConfigHandler.InitBuiltinRouting(_config);
Locator.Current.GetService<StatusBarViewModel>()?.RefreshRoutingsMenu(); AppEvents.RoutingsMenuRefreshRequested.Publish();
await Reload(); await Reload();
} }
} }
@@ -467,12 +485,6 @@ public class MainWindowViewModel : MyReactiveObject
} }
} }
public async Task RebootAsAdmin()
{
ProcUtils.RebootAsAdmin();
await AppManager.Instance.AppExitAsync(true);
}
private async Task ClearServerStatistics() private async Task ClearServerStatistics()
{ {
await StatisticsManager.Instance.ClearAllServerStatistics(); await StatisticsManager.Instance.ClearAllServerStatistics();
@@ -518,9 +530,15 @@ public class MainWindowViewModel : MyReactiveObject
await SysProxyHandler.UpdateSysProxy(_config, false); await SysProxyHandler.UpdateSysProxy(_config, false);
await Task.Delay(1000); await Task.Delay(1000);
}); });
Locator.Current.GetService<StatusBarViewModel>()?.TestServerAvailability(); AppEvents.TestServerRequested.Publish();
RxApp.MainThreadScheduler.Schedule(() => _ = ReloadResult()); var showClashUI = _config.IsRunningCore(ECoreType.sing_box);
if (showClashUI)
{
AppEvents.ProxiesReloadRequested.Publish();
}
RxApp.MainThreadScheduler.Schedule(() => ReloadResult(showClashUI));
BlReloadEnabled = true; BlReloadEnabled = true;
if (_hasNextReloadJob) if (_hasNextReloadJob)
@@ -530,19 +548,11 @@ public class MainWindowViewModel : MyReactiveObject
} }
} }
public async Task ReloadResult() private void ReloadResult(bool showClashUI)
{ {
// BlReloadEnabled = true; // BlReloadEnabled = true;
//Locator.Current.GetService<StatusBarViewModel>()?.ChangeSystemProxyAsync(_config.systemProxyItem.sysProxyType, false); ShowClashUI = showClashUI;
ShowClashUI = _config.IsRunningCore(ECoreType.sing_box); TabMainSelectedIndex = showClashUI ? TabMainSelectedIndex : 0;
if (ShowClashUI)
{
Locator.Current.GetService<ClashProxiesViewModel>()?.ProxiesReload();
}
else
{
TabMainSelectedIndex = 0;
}
} }
private async Task LoadCore() private async Task LoadCore()
@@ -551,17 +561,11 @@ public class MainWindowViewModel : MyReactiveObject
await CoreManager.Instance.LoadCore(node); await CoreManager.Instance.LoadCore(node);
} }
public async Task CloseCore()
{
await ConfigHandler.SaveConfig(_config);
await CoreManager.Instance.CoreStop();
}
private async Task AutoHideStartup() private async Task AutoHideStartup()
{ {
if (_config.UiItem.AutoHideStartup) if (_config.UiItem.AutoHideStartup)
{ {
ShowHideWindow(false); AppEvents.ShowHideWindowRequested.Publish(false);
} }
await Task.CompletedTask; await Task.CompletedTask;
} }
@@ -574,7 +578,7 @@ public class MainWindowViewModel : MyReactiveObject
{ {
await ConfigHandler.ApplyRegionalPreset(_config, type); await ConfigHandler.ApplyRegionalPreset(_config, type);
await ConfigHandler.InitRouting(_config); await ConfigHandler.InitRouting(_config);
Locator.Current.GetService<StatusBarViewModel>()?.RefreshRoutingsMenu(); AppEvents.RoutingsMenuRefreshRequested.Publish();
await ConfigHandler.SaveConfig(_config); await ConfigHandler.SaveConfig(_config);
await new UpdateService().UpdateGeoFileAll(_config, UpdateTaskHandler); await new UpdateService().UpdateGeoFileAll(_config, UpdateTaskHandler);

View File

@@ -1,5 +1,6 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.Fody.Helpers; using ReactiveUI.Fody.Helpers;
@@ -9,9 +10,9 @@ namespace ServiceLib.ViewModels;
public class MsgViewModel : MyReactiveObject public class MsgViewModel : MyReactiveObject
{ {
private readonly ConcurrentQueue<string> _queueMsg = new(); private readonly ConcurrentQueue<string> _queueMsg = new();
private readonly int _numMaxMsg = 500; private volatile bool _lastMsgFilterNotAvailable;
private bool _lastMsgFilterNotAvailable; private int _showLock = 0; // 0 = unlocked, 1 = locked
private bool _blLockShow = false; public int NumMaxMsg { get; } = 500;
[Reactive] [Reactive]
public string MsgFilter { get; set; } public string MsgFilter { get; set; }
@@ -33,46 +34,52 @@ public class MsgViewModel : MyReactiveObject
this.WhenAnyValue( this.WhenAnyValue(
x => x.AutoRefresh, x => x.AutoRefresh,
y => y == true) y => y == true)
.Subscribe(c => { _config.MsgUIItem.AutoRefresh = AutoRefresh; }); .Subscribe(c => _config.MsgUIItem.AutoRefresh = AutoRefresh);
AppEvents.SendMsgViewRequested AppEvents.SendMsgViewRequested
.AsObservable() .AsObservable()
//.ObserveOn(RxApp.MainThreadScheduler) //.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async content => await AppendQueueMsg(content)); .Subscribe(content => _ = AppendQueueMsg(content));
} }
private async Task AppendQueueMsg(string msg) private async Task AppendQueueMsg(string msg)
{ {
//if (msg == Global.CommandClearMsg)
//{
// ClearMsg();
// return;
//}
if (AutoRefresh == false) if (AutoRefresh == false)
{ {
return; return;
} }
_ = EnqueueQueueMsg(msg);
if (_blLockShow) EnqueueQueueMsg(msg);
{
return;
}
if (!_config.UiItem.ShowInTaskbar) if (!_config.UiItem.ShowInTaskbar)
{ {
return; return;
} }
_blLockShow = true; if (Interlocked.CompareExchange(ref _showLock, 1, 0) != 0)
{
await Task.Delay(500); return;
var txt = string.Join("", _queueMsg.ToArray());
await _updateView?.Invoke(EViewAction.DispatcherShowMsg, txt);
_blLockShow = false;
} }
private async Task EnqueueQueueMsg(string msg) try
{
await Task.Delay(500).ConfigureAwait(false);
var sb = new StringBuilder();
while (_queueMsg.TryDequeue(out var line))
{
sb.Append(line);
}
await _updateView?.Invoke(EViewAction.DispatcherShowMsg, sb.ToString());
}
finally
{
Interlocked.Exchange(ref _showLock, 0);
}
}
private void EnqueueQueueMsg(string msg)
{ {
//filter msg //filter msg
if (MsgFilter.IsNotEmpty() && !_lastMsgFilterNotAvailable) if (MsgFilter.IsNotEmpty() && !_lastMsgFilterNotAvailable)
@@ -91,26 +98,17 @@ public class MsgViewModel : MyReactiveObject
} }
} }
//Enqueue
if (_queueMsg.Count > _numMaxMsg)
{
for (int k = 0; k < _queueMsg.Count - _numMaxMsg; k++)
{
_queueMsg.TryDequeue(out _);
}
}
_queueMsg.Enqueue(msg); _queueMsg.Enqueue(msg);
if (!msg.EndsWith(Environment.NewLine)) if (!msg.EndsWith(Environment.NewLine))
{ {
_queueMsg.Enqueue(Environment.NewLine); _queueMsg.Enqueue(Environment.NewLine);
} }
await Task.CompletedTask;
} }
public void ClearMsg() //public void ClearMsg()
{ //{
_queueMsg.Clear(); // _queueMsg.Clear();
} //}
private void DoMsgFilter() private void DoMsgFilter()
{ {

View File

@@ -6,7 +6,6 @@ using DynamicData;
using DynamicData.Binding; using DynamicData.Binding;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.Fody.Helpers; using ReactiveUI.Fody.Helpers;
using Splat;
namespace ServiceLib.ViewModels; namespace ServiceLib.ViewModels;
@@ -240,11 +239,21 @@ public class ProfilesViewModel : MyReactiveObject
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await RefreshServersBiz()); .Subscribe(async _ => await RefreshServersBiz());
AppEvents.SubscriptionsRefreshRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await RefreshSubscriptions());
AppEvents.DispatcherStatisticsRequested AppEvents.DispatcherStatisticsRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async result => await UpdateStatistics(result)); .Subscribe(async result => await UpdateStatistics(result));
AppEvents.SetDefaultServerRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async indexId => await SetDefaultServer(indexId));
#endregion AppEvents #endregion AppEvents
_ = Init(); _ = Init();
@@ -266,7 +275,7 @@ public class ProfilesViewModel : MyReactiveObject
private void Reload() private void Reload()
{ {
Locator.Current.GetService<MainWindowViewModel>()?.Reload(); AppEvents.ReloadRequested.Publish();
} }
public async Task SetSpeedTestResult(SpeedTestResult result) public async Task SetSpeedTestResult(SpeedTestResult result)
@@ -352,7 +361,7 @@ public class ProfilesViewModel : MyReactiveObject
public async Task RefreshServers() public async Task RefreshServers()
{ {
AppEvents.ProfilesRefreshRequested.OnNext(Unit.Default); AppEvents.ProfilesRefreshRequested.Publish();
await Task.Delay(200); await Task.Delay(200);
} }
@@ -380,7 +389,7 @@ public class ProfilesViewModel : MyReactiveObject
await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null); await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null);
} }
public async Task RefreshSubscriptions() private async Task RefreshSubscriptions()
{ {
SubItems.Clear(); SubItems.Clear();
@@ -565,7 +574,7 @@ public class ProfilesViewModel : MyReactiveObject
await SetDefaultServer(SelectedProfile.IndexId); await SetDefaultServer(SelectedProfile.IndexId);
} }
public async Task SetDefaultServer(string? indexId) private async Task SetDefaultServer(string? indexId)
{ {
if (indexId.IsNullOrEmpty()) if (indexId.IsNullOrEmpty())
{ {

View File

@@ -5,12 +5,14 @@ using System.Text;
using DynamicData.Binding; using DynamicData.Binding;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.Fody.Helpers; using ReactiveUI.Fody.Helpers;
using Splat;
namespace ServiceLib.ViewModels; namespace ServiceLib.ViewModels;
public class StatusBarViewModel : MyReactiveObject public class StatusBarViewModel : MyReactiveObject
{ {
private static readonly Lazy<StatusBarViewModel> _instance = new(() => new(null));
public static StatusBarViewModel Instance => _instance.Value;
#region ObservableCollection #region ObservableCollection
public IObservableCollection<RoutingItem> RoutingItems { get; } = new ObservableCollectionExtended<RoutingItem>(); public IObservableCollection<RoutingItem> RoutingItems { get; } = new ObservableCollectionExtended<RoutingItem>();
@@ -146,17 +148,17 @@ public class StatusBarViewModel : MyReactiveObject
NotifyLeftClickCmd = ReactiveCommand.CreateFromTask(async () => NotifyLeftClickCmd = ReactiveCommand.CreateFromTask(async () =>
{ {
Locator.Current.GetService<MainWindowViewModel>()?.ShowHideWindow(null); AppEvents.ShowHideWindowRequested.Publish(null);
await Task.CompletedTask; await Task.CompletedTask;
}); });
ShowWindowCmd = ReactiveCommand.CreateFromTask(async () => ShowWindowCmd = ReactiveCommand.CreateFromTask(async () =>
{ {
Locator.Current.GetService<MainWindowViewModel>()?.ShowHideWindow(true); AppEvents.ShowHideWindowRequested.Publish(true);
await Task.CompletedTask; await Task.CompletedTask;
}); });
HideWindowCmd = ReactiveCommand.CreateFromTask(async () => HideWindowCmd = ReactiveCommand.CreateFromTask(async () =>
{ {
Locator.Current.GetService<MainWindowViewModel>()?.ShowHideWindow(false); AppEvents.ShowHideWindowRequested.Publish(false);
await Task.CompletedTask; await Task.CompletedTask;
}); });
@@ -209,6 +211,26 @@ public class StatusBarViewModel : MyReactiveObject
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async result => await UpdateStatistics(result)); .Subscribe(async result => await UpdateStatistics(result));
AppEvents.RoutingsMenuRefreshRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await RefreshRoutingsMenu());
AppEvents.TestServerRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await TestServerAvailability());
AppEvents.InboundDisplayRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await InboundDisplayStatus());
AppEvents.SysProxyChangeRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async result => await SetListenerType(result));
#endregion AppEvents #endregion AppEvents
_ = Init(); _ = Init();
@@ -216,6 +238,7 @@ public class StatusBarViewModel : MyReactiveObject
private async Task Init() private async Task Init()
{ {
await ConfigHandler.InitBuiltinRouting(_config);
await RefreshRoutingsMenu(); await RefreshRoutingsMenu();
await InboundDisplayStatus(); await InboundDisplayStatus();
await ChangeSystemProxyAsync(_config.SystemProxyItem.SysProxyType, true); await ChangeSystemProxyAsync(_config.SystemProxyItem.SysProxyType, true);
@@ -252,23 +275,20 @@ public class StatusBarViewModel : MyReactiveObject
private async Task AddServerViaClipboard() private async Task AddServerViaClipboard()
{ {
var service = Locator.Current.GetService<MainWindowViewModel>(); AppEvents.AddServerViaClipboardRequested.Publish();
if (service != null) await Task.Delay(1000);
await service.AddServerViaClipboardAsync(null);
} }
private async Task AddServerViaScan() private async Task AddServerViaScan()
{ {
var service = Locator.Current.GetService<MainWindowViewModel>(); AppEvents.AddServerViaScanRequested.Publish();
if (service != null) await Task.Delay(1000);
await service.AddServerViaScanAsync();
} }
private async Task UpdateSubscriptionProcess(bool blProxy) private async Task UpdateSubscriptionProcess(bool blProxy)
{ {
var service = Locator.Current.GetService<MainWindowViewModel>(); AppEvents.SubscriptionsUpdateRequested.Publish(blProxy);
if (service != null) await Task.Delay(1000);
await service.UpdateSubscriptionProcess("", blProxy);
} }
private async Task RefreshServersBiz() private async Task RefreshServersBiz()
@@ -329,7 +349,7 @@ public class StatusBarViewModel : MyReactiveObject
{ {
return; return;
} }
Locator.Current.GetService<ProfilesViewModel>()?.SetDefaultServer(SelectedServer.ID); AppEvents.SetDefaultServerRequested.Publish(SelectedServer.ID);
} }
public async Task TestServerAvailability() public async Task TestServerAvailability()
@@ -364,7 +384,7 @@ public class StatusBarViewModel : MyReactiveObject
#region System proxy and Routings #region System proxy and Routings
public async Task SetListenerType(ESysProxyType type) private async Task SetListenerType(ESysProxyType type)
{ {
if (_config.SystemProxyItem.SysProxyType == type) if (_config.SystemProxyItem.SysProxyType == type)
{ {
@@ -393,7 +413,7 @@ public class StatusBarViewModel : MyReactiveObject
} }
} }
public async Task RefreshRoutingsMenu() private async Task RefreshRoutingsMenu()
{ {
RoutingItems.Clear(); RoutingItems.Clear();
@@ -430,7 +450,7 @@ public class StatusBarViewModel : MyReactiveObject
if (await ConfigHandler.SetDefaultRouting(_config, item) == 0) if (await ConfigHandler.SetDefaultRouting(_config, item) == 0)
{ {
NoticeManager.Instance.SendMessageEx(ResUI.TipChangeRouting); NoticeManager.Instance.SendMessageEx(ResUI.TipChangeRouting);
Locator.Current.GetService<MainWindowViewModel>()?.Reload(); AppEvents.ReloadRequested.Publish();
_updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null); _updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null);
} }
} }
@@ -463,7 +483,7 @@ public class StatusBarViewModel : MyReactiveObject
if (Utils.IsWindows()) if (Utils.IsWindows())
{ {
_config.TunModeItem.EnableTun = false; _config.TunModeItem.EnableTun = false;
Locator.Current.GetService<MainWindowViewModel>()?.RebootAsAdmin(); await AppManager.Instance.RebootAsAdmin();
return; return;
} }
else else
@@ -477,7 +497,7 @@ public class StatusBarViewModel : MyReactiveObject
} }
} }
await ConfigHandler.SaveConfig(_config); await ConfigHandler.SaveConfig(_config);
Locator.Current.GetService<MainWindowViewModel>()?.Reload(); AppEvents.ReloadRequested.Publish();
} }
private bool AllowEnableTun() private bool AllowEnableTun()
@@ -501,7 +521,7 @@ public class StatusBarViewModel : MyReactiveObject
#region UI #region UI
public async Task InboundDisplayStatus() private async Task InboundDisplayStatus()
{ {
StringBuilder sb = new(); StringBuilder sb = new();
sb.Append($"[{EInboundProtocol.mixed}:{AppManager.Instance.GetLocalPort(EInboundProtocol.socks)}"); sb.Append($"[{EInboundProtocol.mixed}:{AppManager.Instance.GetLocalPort(EInboundProtocol.socks)}");

View File

@@ -11,6 +11,7 @@
RequestedThemeVariant="Default"> RequestedThemeVariant="Default">
<Application.Styles> <Application.Styles>
<semi:SemiTheme /> <semi:SemiTheme />
<semi:AvaloniaEditSemiTheme />
<StyleInclude Source="Assets/GlobalStyles.axaml" /> <StyleInclude Source="Assets/GlobalStyles.axaml" />
<StyleInclude Source="avares://Semi.Avalonia.DataGrid/Index.axaml" /> <StyleInclude Source="avares://Semi.Avalonia.DataGrid/Index.axaml" />
<dialogHost:DialogHostStyles /> <dialogHost:DialogHostStyles />

View File

@@ -1,9 +1,6 @@
using Avalonia; using Avalonia;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using ServiceLib.Manager;
using Splat;
using v2rayN.Desktop.Common;
using v2rayN.Desktop.Views; using v2rayN.Desktop.Views;
namespace v2rayN.Desktop; namespace v2rayN.Desktop;
@@ -17,9 +14,7 @@ public partial class App : Application
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
var ViewModel = new StatusBarViewModel(null); DataContext = StatusBarViewModel.Instance;
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel));
DataContext = ViewModel;
} }
public override void OnFrameworkInitializationCompleted() public override void OnFrameworkInitializationCompleted()
@@ -58,16 +53,8 @@ public partial class App : Application
{ {
if (desktop.MainWindow != null) if (desktop.MainWindow != null)
{ {
var clipboardData = await AvaUtils.GetClipboardData(desktop.MainWindow); AppEvents.AddServerViaClipboardRequested.Publish();
if (clipboardData.IsNullOrEmpty()) await Task.Delay(1000);
{
return;
}
var service = Locator.Current.GetService<MainWindowViewModel>();
if (service != null)
{
_ = service.AddServerViaClipboardAsync(clipboardData);
}
} }
} }
} }

View File

@@ -1,7 +1,6 @@
using Avalonia; using Avalonia;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using ServiceLib.Manager;
namespace v2rayN.Desktop.Base; namespace v2rayN.Desktop.Base;

View File

@@ -0,0 +1,129 @@
using Avalonia.Media;
using AvaloniaEdit;
using AvaloniaEdit.Document;
using AvaloniaEdit.Rendering;
namespace v2rayN.Desktop.Common;
public class KeywordColorizer : DocumentColorizingTransformer
{
private readonly string[] _keywords;
private readonly Dictionary<string, IBrush> _brushMap;
public KeywordColorizer(IDictionary<string, IBrush> keywordBrushMap)
{
if (keywordBrushMap == null || keywordBrushMap.Count == 0)
{
throw new ArgumentException("keywordBrushMap must not be null or empty", nameof(keywordBrushMap));
}
_brushMap = new Dictionary<string, IBrush>(StringComparer.OrdinalIgnoreCase);
foreach (var kvp in keywordBrushMap)
{
if (string.IsNullOrEmpty(kvp.Key) || kvp.Value == null)
{
continue;
}
if (!_brushMap.ContainsKey(kvp.Key))
{
_brushMap[kvp.Key] = kvp.Value;
}
}
if (_brushMap.Count == 0)
{
throw new ArgumentException("keywordBrushMap must contain at least one non-empty key with a non-null brush", nameof(keywordBrushMap));
}
_keywords = _brushMap.Keys.ToArray();
}
protected override void ColorizeLine(DocumentLine line)
{
var text = CurrentContext.Document.GetText(line);
if (string.IsNullOrEmpty(text))
{
return;
}
foreach (var kw in _keywords)
{
if (string.IsNullOrEmpty(kw))
{
continue;
}
var searchStart = 0;
while (true)
{
var idx = text.IndexOf(kw, searchStart, StringComparison.OrdinalIgnoreCase);
if (idx < 0)
{
break;
}
var kwEndIndex = idx + kw.Length;
if (IsWordCharBefore(text, idx) || IsWordCharAfter(text, kwEndIndex))
{
searchStart = idx + Math.Max(1, kw.Length);
continue;
}
var start = line.Offset + idx;
var end = start + kw.Length;
if (_brushMap.TryGetValue(kw, out var brush) && brush != null)
{
ChangeLinePart(start, end, element => element.TextRunProperties.SetForegroundBrush(brush));
}
searchStart = idx + Math.Max(1, kw.Length);
}
}
}
private static bool IsWordCharBefore(string text, int idx)
{
if (idx <= 0)
{
return false;
}
var c = text[idx - 1];
return char.IsLetterOrDigit(c) || c == '_';
}
private static bool IsWordCharAfter(string text, int idx)
{
if (idx >= text.Length)
{
return false;
}
var c = text[idx];
return char.IsLetterOrDigit(c) || c == '_';
}
}
public static class TextEditorKeywordHighlighter
{
public static void Attach(TextEditor editor, IDictionary<string, IBrush> keywordBrushMap)
{
ArgumentNullException.ThrowIfNull(editor);
if (keywordBrushMap == null || keywordBrushMap.Count == 0)
{
return;
}
if (editor.TextArea?.TextView?.LineTransformers?.OfType<KeywordColorizer>().Any() == true)
{
return;
}
var colorizer = new KeywordColorizer(keywordBrushMap);
editor.TextArea.TextView.LineTransformers.Add(colorizer);
editor.TextArea.TextView.InvalidateVisual();
}
}

View File

@@ -1,8 +1,10 @@
global using ServiceLib; global using ServiceLib;
global using ServiceLib.Base; global using ServiceLib.Base;
global using ServiceLib.Common; global using ServiceLib.Common;
global using ServiceLib.Enums; global using ServiceLib.Enums;
global using ServiceLib.Events;
global using ServiceLib.Handler; global using ServiceLib.Handler;
global using ServiceLib.Manager;
global using ServiceLib.Models; global using ServiceLib.Models;
global using ServiceLib.Resx; global using ServiceLib.Resx;
global using ServiceLib.ViewModels; global using ServiceLib.ViewModels;

View File

@@ -1,6 +1,5 @@
using Avalonia; using Avalonia;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using ServiceLib.Manager;
using v2rayN.Desktop.Common; using v2rayN.Desktop.Common;
namespace v2rayN.Desktop; namespace v2rayN.Desktop;

View File

@@ -5,10 +5,10 @@ using Avalonia.Controls.Notifications;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling; using Avalonia.Styling;
using AvaloniaEdit;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.Fody.Helpers; using ReactiveUI.Fody.Helpers;
using Semi.Avalonia; using Semi.Avalonia;
using ServiceLib.Manager;
namespace v2rayN.Desktop.ViewModels; namespace v2rayN.Desktop.ViewModels;
@@ -113,7 +113,8 @@ public class ThemeSettingViewModel : MyReactiveObject
x.OfType<ContextMenu>(), x.OfType<ContextMenu>(),
x.OfType<DataGridRow>(), x.OfType<DataGridRow>(),
x.OfType<ListBoxItem>(), x.OfType<ListBoxItem>(),
x.OfType<HeaderedContentControl>() x.OfType<HeaderedContentControl>(),
x.OfType<TextEditor>()
)); ));
style.Add(new Setter() style.Add(new Setter()
{ {
@@ -154,7 +155,8 @@ public class ThemeSettingViewModel : MyReactiveObject
x.OfType<DataGridRow>(), x.OfType<DataGridRow>(),
x.OfType<ListBoxItem>(), x.OfType<ListBoxItem>(),
x.OfType<HeaderedContentControl>(), x.OfType<HeaderedContentControl>(),
x.OfType<WindowNotificationManager>() x.OfType<WindowNotificationManager>(),
x.OfType<TextEditor>()
)); ));
style.Add(new Setter() style.Add(new Setter()
{ {

View File

@@ -2,7 +2,6 @@ using System.Reactive.Disposables;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
using v2rayN.Desktop.Base; using v2rayN.Desktop.Base;
namespace v2rayN.Desktop.Views; namespace v2rayN.Desktop.Views;

View File

@@ -3,7 +3,6 @@ using Avalonia.Input;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using DynamicData; using DynamicData;
using ReactiveUI; using ReactiveUI;
using Splat;
namespace v2rayN.Desktop.Views; namespace v2rayN.Desktop.Views;
@@ -13,7 +12,6 @@ public partial class ClashProxiesView : ReactiveUserControl<ClashProxiesViewMode
{ {
InitializeComponent(); InitializeComponent();
ViewModel = new ClashProxiesViewModel(UpdateViewHandler); ViewModel = new ClashProxiesViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ClashProxiesViewModel));
lstProxyDetails.DoubleTapped += LstProxyDetails_DoubleTapped; lstProxyDetails.DoubleTapped += LstProxyDetails_DoubleTapped;
this.KeyDown += ClashProxiesView_KeyDown; this.KeyDown += ClashProxiesView_KeyDown;

View File

@@ -229,7 +229,7 @@
Grid.Column="2" Grid.Column="2"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbApplyProxyDomainsOnly}" Text="{x:Static resx:ResUI.TbFakeIPTips}"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<TextBlock <TextBlock

View File

@@ -2,7 +2,6 @@ using System.Reactive.Disposables;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
using v2rayN.Desktop.Base; using v2rayN.Desktop.Base;
namespace v2rayN.Desktop.Views; namespace v2rayN.Desktop.Views;

View File

@@ -1,7 +1,6 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
using v2rayN.Desktop.Base; using v2rayN.Desktop.Base;
namespace v2rayN.Desktop.Views; namespace v2rayN.Desktop.Views;

View File

@@ -10,8 +10,6 @@ using Avalonia.Threading;
using DialogHostAvalonia; using DialogHostAvalonia;
using MsBox.Avalonia.Enums; using MsBox.Avalonia.Enums;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
using Splat;
using v2rayN.Desktop.Base; using v2rayN.Desktop.Base;
using v2rayN.Desktop.Common; using v2rayN.Desktop.Common;
using v2rayN.Desktop.Manager; using v2rayN.Desktop.Manager;
@@ -21,7 +19,7 @@ namespace v2rayN.Desktop.Views;
public partial class MainWindow : WindowBase<MainWindowViewModel> public partial class MainWindow : WindowBase<MainWindowViewModel>
{ {
private static Config _config; private static Config _config;
private WindowNotificationManager? _manager; private readonly WindowNotificationManager? _manager;
private CheckUpdateView? _checkUpdateView; private CheckUpdateView? _checkUpdateView;
private BackupAndRestoreView? _backupAndRestoreView; private BackupAndRestoreView? _backupAndRestoreView;
private bool _blCloseByUser = false; private bool _blCloseByUser = false;
@@ -41,7 +39,6 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
menuClose.Click += MenuClose_Click; menuClose.Click += MenuClose_Click;
ViewModel = new MainWindowViewModel(UpdateViewHandler); ViewModel = new MainWindowViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
switch (_config.UiItem.MainGirdOrientation) switch (_config.UiItem.MainGirdOrientation)
{ {
@@ -154,6 +151,12 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(content => Shutdown(content)) .Subscribe(content => Shutdown(content))
.DisposeWith(disposables); .DisposeWith(disposables);
AppEvents.ShowHideWindowRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(blShow => ShowHideWindow(blShow))
.DisposeWith(disposables);
}); });
if (Utils.IsWindows()) if (Utils.IsWindows())
@@ -222,12 +225,6 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
case EViewAction.SubSettingWindow: case EViewAction.SubSettingWindow:
return await new SubSettingWindow().ShowDialog<bool>(this); return await new SubSettingWindow().ShowDialog<bool>(this);
case EViewAction.ShowHideWindow:
Dispatcher.UIThread.Post(() =>
ShowHideWindow((bool?)obj),
DispatcherPriority.Default);
break;
case EViewAction.ScanScreenTask: case EViewAction.ScanScreenTask:
await ScanScreenTaskAsync(); await ScanScreenTaskAsync();
break; break;
@@ -237,11 +234,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
break; break;
case EViewAction.AddServerViaClipboard: case EViewAction.AddServerViaClipboard:
var clipboardData = await AvaUtils.GetClipboardData(this); await AddServerViaClipboardAsync();
if (clipboardData.IsNotEmpty() && ViewModel != null)
{
await ViewModel.AddServerViaClipboardAsync(clipboardData);
}
break; break;
} }
@@ -260,7 +253,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
case EGlobalHotkey.SystemProxySet: case EGlobalHotkey.SystemProxySet:
case EGlobalHotkey.SystemProxyUnchanged: case EGlobalHotkey.SystemProxyUnchanged:
case EGlobalHotkey.SystemProxyPac: case EGlobalHotkey.SystemProxyPac:
Locator.Current.GetService<StatusBarViewModel>()?.SetListenerType((ESysProxyType)((int)e - 1)); AppEvents.SysProxyChangeRequested.Publish((ESysProxyType)((int)e - 1));
break; break;
} }
} }
@@ -296,11 +289,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
switch (e.Key) switch (e.Key)
{ {
case Key.V: case Key.V:
var clipboardData = await AvaUtils.GetClipboardData(this); await AddServerViaClipboardAsync();
if (clipboardData.IsNotEmpty() && ViewModel != null)
{
await ViewModel.AddServerViaClipboardAsync(clipboardData);
}
break; break;
case Key.S: case Key.S:
@@ -327,6 +316,15 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
ProcUtils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe")); ProcUtils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe"));
} }
public async Task AddServerViaClipboardAsync()
{
var clipboardData = await AvaUtils.GetClipboardData(this);
if (clipboardData.IsNotEmpty() && ViewModel != null)
{
await ViewModel.AddServerViaClipboardAsync(clipboardData);
}
}
public async Task ScanScreenTaskAsync() public async Task ScanScreenTaskAsync()
{ {
//ShowHideWindow(false); //ShowHideWindow(false);

View File

@@ -2,6 +2,7 @@
x:Class="v2rayN.Desktop.Views.MsgView" x:Class="v2rayN.Desktop.Views.MsgView"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:avaloniaEdit="clr-namespace:AvaloniaEdit;assembly=AvaloniaEdit"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 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:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
@@ -70,16 +71,17 @@
Theme="{DynamicResource SimpleToggleSwitch}" /> Theme="{DynamicResource SimpleToggleSwitch}" />
</WrapPanel> </WrapPanel>
<ScrollViewer x:Name="msgScrollViewer" VerticalScrollBarVisibility="Auto"> <avaloniaEdit:TextEditor
<SelectableTextBlock
Name="txtMsg" Name="txtMsg"
Margin="{StaticResource Margin8}" Margin="{StaticResource Margin8}"
VerticalAlignment="Stretch" IsReadOnly="True"
Classes="TextArea" VerticalScrollBarVisibility="Auto"
TextAlignment="Left" WordWrap="True">
TextWrapping="Wrap"> <avaloniaEdit:TextEditor.Options>
<SelectableTextBlock.ContextMenu> <avaloniaEdit:TextEditorOptions AllowScrollBelowDocument="False"/>
<ContextMenu> </avaloniaEdit:TextEditor.Options>
<avaloniaEdit:TextEditor.ContextFlyout>
<MenuFlyout>
<MenuItem <MenuItem
x:Name="menuMsgViewSelectAll" x:Name="menuMsgViewSelectAll"
Click="menuMsgViewSelectAll_Click" Click="menuMsgViewSelectAll_Click"
@@ -96,9 +98,9 @@
x:Name="menuMsgViewClear" x:Name="menuMsgViewClear"
Click="menuMsgViewClear_Click" Click="menuMsgViewClear_Click"
Header="{x:Static resx:ResUI.menuMsgViewClear}" /> Header="{x:Static resx:ResUI.menuMsgViewClear}" />
</ContextMenu> </MenuFlyout>
</SelectableTextBlock.ContextMenu> </avaloniaEdit:TextEditor.ContextFlyout>
</SelectableTextBlock> </avaloniaEdit:TextEditor>
</ScrollViewer>
</DockPanel> </DockPanel>
</UserControl> </UserControl>

View File

@@ -1,6 +1,6 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia.Threading; using Avalonia.Threading;
using ReactiveUI; using ReactiveUI;
@@ -10,13 +10,12 @@ namespace v2rayN.Desktop.Views;
public partial class MsgView : ReactiveUserControl<MsgViewModel> public partial class MsgView : ReactiveUserControl<MsgViewModel>
{ {
private readonly ScrollViewer _scrollViewer; //private const int KeepLines = 30;
public MsgView() public MsgView()
{ {
InitializeComponent(); InitializeComponent();
_scrollViewer = this.FindControl<ScrollViewer>("msgScrollViewer"); txtMsg.TextArea.TextView.Options.EnableHyperlinks = false;
ViewModel = new MsgViewModel(UpdateViewHandler); ViewModel = new MsgViewModel(UpdateViewHandler);
this.WhenActivated(disposables => this.WhenActivated(disposables =>
@@ -24,6 +23,11 @@ public partial class MsgView : ReactiveUserControl<MsgViewModel>
this.Bind(ViewModel, vm => vm.MsgFilter, v => v.cmbMsgFilter.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.MsgFilter, v => v.cmbMsgFilter.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
}); });
TextEditorKeywordHighlighter.Attach(txtMsg, Global.LogLevelColors.ToDictionary(
kv => kv.Key,
kv => (IBrush)new SolidColorBrush(Color.Parse(kv.Value))
));
} }
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj) private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
@@ -34,8 +38,7 @@ public partial class MsgView : ReactiveUserControl<MsgViewModel>
if (obj is null) if (obj is null)
return false; return false;
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() => ShowMsg(obj),
ShowMsg(obj),
DispatcherPriority.ApplicationIdle); DispatcherPriority.ApplicationIdle);
break; break;
} }
@@ -44,23 +47,37 @@ public partial class MsgView : ReactiveUserControl<MsgViewModel>
private void ShowMsg(object msg) private void ShowMsg(object msg)
{ {
txtMsg.Text = msg.ToString(); //var lineCount = txtMsg.LineCount;
//if (lineCount > ViewModel?.NumMaxMsg)
//{
// var cutLine = txtMsg.Document.GetLineByNumber(lineCount - KeepLines);
// txtMsg.Document.Remove(0, cutLine.Offset);
//}
if (txtMsg.LineCount > ViewModel?.NumMaxMsg)
{
ClearMsg();
}
txtMsg.AppendText(msg.ToString());
if (togScrollToEnd.IsChecked ?? true) if (togScrollToEnd.IsChecked ?? true)
{ {
_scrollViewer?.ScrollToEnd(); txtMsg.ScrollToEnd();
} }
} }
public void ClearMsg() public void ClearMsg()
{ {
ViewModel?.ClearMsg(); txtMsg.Clear();
txtMsg.Text = ""; txtMsg.AppendText("----- Message cleared -----\n");
} }
private void menuMsgViewSelectAll_Click(object? sender, RoutedEventArgs e) private void menuMsgViewSelectAll_Click(object? sender, RoutedEventArgs e)
{ {
txtMsg.Focus(); Dispatcher.UIThread.Post(() =>
{
txtMsg.TextArea.Focus();
txtMsg.SelectAll(); txtMsg.SelectAll();
}, DispatcherPriority.Render);
} }
private async void menuMsgViewCopy_Click(object? sender, RoutedEventArgs e) private async void menuMsgViewCopy_Click(object? sender, RoutedEventArgs e)

View File

@@ -2,7 +2,6 @@ using System.Reactive.Disposables;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
using v2rayN.Desktop.Base; using v2rayN.Desktop.Base;
namespace v2rayN.Desktop.Views; namespace v2rayN.Desktop.Views;

View File

@@ -3,14 +3,13 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.ReactiveUI;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager; using v2rayN.Desktop.Base;
namespace v2rayN.Desktop.Views; namespace v2rayN.Desktop.Views;
public partial class ProfilesSelectWindow : ReactiveWindow<ProfilesSelectViewModel> public partial class ProfilesSelectWindow : WindowBase<ProfilesSelectViewModel>
{ {
private static Config _config; private static Config _config;

View File

@@ -8,8 +8,6 @@ using Avalonia.Threading;
using DialogHostAvalonia; using DialogHostAvalonia;
using MsBox.Avalonia.Enums; using MsBox.Avalonia.Enums;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
using Splat;
using v2rayN.Desktop.Common; using v2rayN.Desktop.Common;
namespace v2rayN.Desktop.Views; namespace v2rayN.Desktop.Views;
@@ -49,7 +47,6 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
//} //}
ViewModel = new ProfilesViewModel(UpdateViewHandler); ViewModel = new ProfilesViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ProfilesViewModel));
this.WhenActivated(disposables => this.WhenActivated(disposables =>
{ {

View File

@@ -4,19 +4,25 @@
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: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"
d:DesignHeight="480" xmlns:sys="clr-namespace:System;assembly=netstandard"
d:DesignWidth="400" d:DesignHeight="600"
d:DesignWidth="600"
mc:Ignorable="d"> mc:Ignorable="d">
<UserControl.Resources>
<sys:Double x:Key="QrcodeWidth">400</sys:Double>
</UserControl.Resources>
<Grid Margin="32" RowDefinitions="Auto,Auto"> <Grid Margin="32" RowDefinitions="Auto,Auto">
<Image <Image
Name="imgQrcode" Name="imgQrcode"
Width="300" Width="{StaticResource QrcodeWidth}"
Height="300" /> Height="{StaticResource QrcodeWidth}" />
<TextBox <TextBox
x:Name="txtContent" x:Name="txtContent"
Grid.Row="1" Grid.Row="1"
Width="300" Width="{StaticResource QrcodeWidth}"
MaxHeight="100" MaxHeight="100"
Margin="{StaticResource MarginTb8}" Margin="{StaticResource MarginTb8}"
VerticalAlignment="Center" VerticalAlignment="Center"

View File

@@ -22,10 +22,18 @@ public partial class QrcodeView : UserControl
} }
private Bitmap? GetQRCode(string? url) private Bitmap? GetQRCode(string? url)
{
try
{ {
var bytes = QRCodeUtils.GenQRCode(url); var bytes = QRCodeUtils.GenQRCode(url);
return ByteToBitmap(bytes); return ByteToBitmap(bytes);
} }
catch (Exception ex)
{
Logging.SaveLog("GetQRCode", ex);
return null;
}
}
private Bitmap? ByteToBitmap(byte[]? bytes) private Bitmap? ByteToBitmap(byte[]? bytes)
{ {

View File

@@ -6,8 +6,6 @@ using Avalonia.ReactiveUI;
using Avalonia.Threading; using Avalonia.Threading;
using DialogHostAvalonia; using DialogHostAvalonia;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
using Splat;
using v2rayN.Desktop.Common; using v2rayN.Desktop.Common;
namespace v2rayN.Desktop.Views; namespace v2rayN.Desktop.Views;
@@ -21,9 +19,8 @@ public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
InitializeComponent(); InitializeComponent();
_config = AppManager.Instance.Config; _config = AppManager.Instance.Config;
//ViewModel = new StatusBarViewModel(UpdateViewHandler);
//Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel)); ViewModel = StatusBarViewModel.Instance;
ViewModel = Locator.Current.GetService<StatusBarViewModel>();
ViewModel?.InitUpdateView(UpdateViewHandler); ViewModel?.InitUpdateView(UpdateViewHandler);
txtRunningServerDisplay.Tapped += TxtRunningServerDisplay_Tapped; txtRunningServerDisplay.Tapped += TxtRunningServerDisplay_Tapped;

View File

@@ -2,7 +2,6 @@ using Avalonia.Controls;
using Avalonia.Threading; using Avalonia.Threading;
using CliWrap.Buffered; using CliWrap.Buffered;
using DialogHostAvalonia; using DialogHostAvalonia;
using ServiceLib.Manager;
namespace v2rayN.Desktop.Views; namespace v2rayN.Desktop.Views;

View File

@@ -9,6 +9,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia.AvaloniaEdit" />
<PackageReference Include="Avalonia.Controls.DataGrid"> <PackageReference Include="Avalonia.Controls.DataGrid">
<TreatAsUsed>true</TreatAsUsed> <TreatAsUsed>true</TreatAsUsed>
</PackageReference> </PackageReference>
@@ -17,6 +18,7 @@
<PackageReference Include="Avalonia.ReactiveUI" /> <PackageReference Include="Avalonia.ReactiveUI" />
<PackageReference Include="MessageBox.Avalonia" /> <PackageReference Include="MessageBox.Avalonia" />
<PackageReference Include="Semi.Avalonia" /> <PackageReference Include="Semi.Avalonia" />
<PackageReference Include="Semi.Avalonia.AvaloniaEdit" />
<PackageReference Include="Semi.Avalonia.DataGrid"> <PackageReference Include="Semi.Avalonia.DataGrid">
<TreatAsUsed>true</TreatAsUsed> <TreatAsUsed>true</TreatAsUsed>
</PackageReference> </PackageReference>

24
v2rayN/v2rayN.slnx Normal file
View File

@@ -0,0 +1,24 @@
<Solution>
<Folder Name="/GitHub Action/">
<File Path="../.github/workflows/build-all.yml" />
<File Path="../.github/workflows/build-linux.yml" />
<File Path="../.github/workflows/build-osx.yml" />
<File Path="../.github/workflows/build-windows-desktop.yml" />
<File Path="../.github/workflows/build-windows.yml" />
<File Path="../.github/workflows/winget-publish.yml" />
<File Path="../package-appimage.sh" />
<File Path="../package-debian.sh" />
<File Path="../package-osx.sh" />
<File Path="../package-release-zip.sh" />
<File Path="../pkg2appimage.yml" />
</Folder>
<Folder Name="/Solution Files/">
<File Path="Directory.Build.props" />
<File Path="Directory.Packages.props" />
</Folder>
<Project Path="AmazTool/AmazTool.csproj" />
<Project Path="GlobalHotKeys/src/GlobalHotKeys/GlobalHotKeys.csproj" />
<Project Path="ServiceLib/ServiceLib.csproj" />
<Project Path="v2rayN.Desktop/v2rayN.Desktop.csproj" />
<Project Path="v2rayN/v2rayN.csproj" />
</Solution>

View File

@@ -1,7 +1,6 @@
using System.Diagnostics; using System.Diagnostics;
using System.Windows; using System.Windows;
using System.Windows.Threading; using System.Windows.Threading;
using ServiceLib.Manager;
namespace v2rayN; namespace v2rayN;

View File

@@ -1,6 +1,5 @@
using System.Windows; using System.Windows;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
namespace v2rayN.Base; namespace v2rayN.Base;

View File

@@ -20,8 +20,9 @@ public class QRCodeUtils
var qrCodeImage = ServiceLib.Common.QRCodeUtils.GenQRCode(strContent); var qrCodeImage = ServiceLib.Common.QRCodeUtils.GenQRCode(strContent);
return qrCodeImage is null ? null : ByteToImage(qrCodeImage); return qrCodeImage is null ? null : ByteToImage(qrCodeImage);
} }
catch catch (Exception ex)
{ {
Logging.SaveLog("GetQRCode", ex);
return null; return null;
} }
} }
@@ -32,8 +33,8 @@ public class QRCodeUtils
{ {
GetDpi(window, out var dpiX, out var dpiY); GetDpi(window, out var dpiX, out var dpiY);
var left = (int)(SystemParameters.WorkArea.Left); var left = (int)SystemParameters.WorkArea.Left;
var top = (int)(SystemParameters.WorkArea.Top); var top = (int)SystemParameters.WorkArea.Top;
var width = (int)(SystemParameters.WorkArea.Width / dpiX); var width = (int)(SystemParameters.WorkArea.Width / dpiX);
var height = (int)(SystemParameters.WorkArea.Height / dpiY); var height = (int)(SystemParameters.WorkArea.Height / dpiY);

View File

@@ -1,5 +1,4 @@
using System.Windows.Media; using System.Windows.Media;
using ServiceLib.Manager;
namespace v2rayN.Converters; namespace v2rayN.Converters;

View File

@@ -1,8 +1,10 @@
global using ServiceLib; global using ServiceLib;
global using ServiceLib.Base; global using ServiceLib.Base;
global using ServiceLib.Common; global using ServiceLib.Common;
global using ServiceLib.Enums; global using ServiceLib.Enums;
global using ServiceLib.Events;
global using ServiceLib.Handler; global using ServiceLib.Handler;
global using ServiceLib.Manager;
global using ServiceLib.Models; global using ServiceLib.Models;
global using ServiceLib.Resx; global using ServiceLib.Resx;
global using ServiceLib.ViewModels; global using ServiceLib.ViewModels;

View File

@@ -4,7 +4,6 @@ using System.Text;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Interop; using System.Windows.Interop;
using ServiceLib.Manager;
namespace v2rayN.Manager; namespace v2rayN.Manager;

View File

@@ -9,7 +9,6 @@ using MaterialDesignColors.ColorManipulation;
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.Fody.Helpers; using ReactiveUI.Fody.Helpers;
using ServiceLib.Manager;
namespace v2rayN.ViewModels; namespace v2rayN.ViewModels;

View File

@@ -1,7 +1,6 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Windows; using System.Windows;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
namespace v2rayN.Views; namespace v2rayN.Views;

View File

@@ -2,7 +2,6 @@ using System.Reactive.Disposables;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
namespace v2rayN.Views; namespace v2rayN.Views;

View File

@@ -1,7 +1,6 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Windows.Input; using System.Windows.Input;
using ReactiveUI; using ReactiveUI;
using Splat;
namespace v2rayN.Views; namespace v2rayN.Views;
@@ -14,7 +13,6 @@ public partial class ClashProxiesView
{ {
InitializeComponent(); InitializeComponent();
ViewModel = new ClashProxiesViewModel(UpdateViewHandler); ViewModel = new ClashProxiesViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ClashProxiesViewModel));
lstProxyDetails.PreviewMouseDoubleClick += lstProxyDetails_PreviewMouseDoubleClick; lstProxyDetails.PreviewMouseDoubleClick += lstProxyDetails_PreviewMouseDoubleClick;
this.WhenActivated(disposables => this.WhenActivated(disposables =>

View File

@@ -281,7 +281,7 @@
Margin="{StaticResource Margin8}" Margin="{StaticResource Margin8}"
VerticalAlignment="Center" VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}" Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbApplyProxyDomainsOnly}" Text="{x:Static resx:ResUI.TbFakeIPTips}"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<TextBlock <TextBlock

View File

@@ -1,7 +1,6 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Windows; using System.Windows;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
namespace v2rayN.Views; namespace v2rayN.Views;

View File

@@ -1,7 +1,6 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Windows; using System.Windows;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
namespace v2rayN.Views; namespace v2rayN.Views;

View File

@@ -4,7 +4,6 @@ using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
using v2rayN.Manager; using v2rayN.Manager;
namespace v2rayN.Views; namespace v2rayN.Views;

View File

@@ -6,11 +6,8 @@ using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Interop; using System.Windows.Interop;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Threading;
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
using Splat;
using v2rayN.Manager; using v2rayN.Manager;
namespace v2rayN.Views; namespace v2rayN.Views;
@@ -38,7 +35,6 @@ public partial class MainWindow
menuBackupAndRestore.Click += MenuBackupAndRestore_Click; menuBackupAndRestore.Click += MenuBackupAndRestore_Click;
ViewModel = new MainWindowViewModel(UpdateViewHandler); ViewModel = new MainWindowViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
switch (_config.UiItem.MainGirdOrientation) switch (_config.UiItem.MainGirdOrientation)
{ {
@@ -151,6 +147,12 @@ public partial class MainWindow
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(content => Shutdown(content)) .Subscribe(content => Shutdown(content))
.DisposeWith(disposables); .DisposeWith(disposables);
AppEvents.ShowHideWindowRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(blShow => ShowHideWindow(blShow))
.DisposeWith(disposables);
}); });
this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}"; this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
@@ -211,13 +213,6 @@ public partial class MainWindow
case EViewAction.SubSettingWindow: case EViewAction.SubSettingWindow:
return (new SubSettingWindow().ShowDialog() ?? false); return (new SubSettingWindow().ShowDialog() ?? false);
case EViewAction.ShowHideWindow:
Application.Current?.Dispatcher.Invoke((() =>
{
ShowHideWindow((bool?)obj);
}), DispatcherPriority.Normal);
break;
case EViewAction.ScanScreenTask: case EViewAction.ScanScreenTask:
await ScanScreenTaskAsync(); await ScanScreenTaskAsync();
break; break;
@@ -227,11 +222,7 @@ public partial class MainWindow
break; break;
case EViewAction.AddServerViaClipboard: case EViewAction.AddServerViaClipboard:
var clipboardData = WindowsUtils.GetClipboardData(); await AddServerViaClipboardAsync();
if (clipboardData.IsNotEmpty())
{
ViewModel?.AddServerViaClipboardAsync(clipboardData);
}
break; break;
} }
@@ -250,7 +241,7 @@ public partial class MainWindow
case EGlobalHotkey.SystemProxySet: case EGlobalHotkey.SystemProxySet:
case EGlobalHotkey.SystemProxyUnchanged: case EGlobalHotkey.SystemProxyUnchanged:
case EGlobalHotkey.SystemProxyPac: case EGlobalHotkey.SystemProxyPac:
Locator.Current.GetService<StatusBarViewModel>()?.SetListenerType((ESysProxyType)((int)e - 1)); AppEvents.SysProxyChangeRequested.Publish((ESysProxyType)((int)e - 1));
break; break;
} }
} }
@@ -284,16 +275,7 @@ public partial class MainWindow
{ {
return; return;
} }
AddServerViaClipboardAsync().ContinueWith(_ => { });
var clipboardData = WindowsUtils.GetClipboardData();
if (clipboardData.IsNotEmpty())
{
var service = Locator.Current.GetService<MainWindowViewModel>();
if (service != null)
{
_ = service.AddServerViaClipboardAsync(clipboardData);
}
}
break; break;
@@ -327,6 +309,15 @@ public partial class MainWindow
ProcUtils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe")); ProcUtils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe"));
} }
public async Task AddServerViaClipboardAsync()
{
var clipboardData = WindowsUtils.GetClipboardData();
if (clipboardData.IsNotEmpty() && ViewModel != null)
{
await ViewModel.AddServerViaClipboardAsync(clipboardData);
}
}
private async Task ScanScreenTaskAsync() private async Task ScanScreenTaskAsync()
{ {
ShowHideWindow(false); ShowHideWindow(false);

View File

@@ -47,19 +47,22 @@ public partial class MsgView
private void ShowMsg(object msg) private void ShowMsg(object msg)
{ {
txtMsg.BeginChange(); if (txtMsg.LineCount > ViewModel?.NumMaxMsg)
txtMsg.Text = msg.ToString(); {
ClearMsg();
}
txtMsg.AppendText(msg.ToString());
if (togScrollToEnd.IsChecked ?? true) if (togScrollToEnd.IsChecked ?? true)
{ {
txtMsg.ScrollToEnd(); txtMsg.ScrollToEnd();
} }
txtMsg.EndChange();
} }
public void ClearMsg() public void ClearMsg()
{ {
ViewModel?.ClearMsg();
txtMsg.Clear(); txtMsg.Clear();
txtMsg.AppendText("----- Message cleared -----\n");
} }
private void menuMsgViewSelectAll_Click(object sender, System.Windows.RoutedEventArgs e) private void menuMsgViewSelectAll_Click(object sender, System.Windows.RoutedEventArgs e)

View File

@@ -4,7 +4,6 @@ using System.Reactive.Disposables;
using System.Windows; using System.Windows;
using System.Windows.Media; using System.Windows.Media;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
namespace v2rayN.Views; namespace v2rayN.Views;

View File

@@ -4,7 +4,6 @@ using System.Windows.Controls;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
using System.Windows.Input; using System.Windows.Input;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
using v2rayN.Base; using v2rayN.Base;
namespace v2rayN.Views; namespace v2rayN.Views;

View File

@@ -8,8 +8,6 @@ using System.Windows.Media;
using System.Windows.Threading; using System.Windows.Threading;
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
using Splat;
using v2rayN.Base; using v2rayN.Base;
using Point = System.Windows.Point; using Point = System.Windows.Point;
@@ -43,7 +41,6 @@ public partial class ProfilesView
} }
ViewModel = new ProfilesViewModel(UpdateViewHandler); ViewModel = new ProfilesViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ProfilesViewModel));
this.WhenActivated(disposables => this.WhenActivated(disposables =>
{ {

View File

@@ -1,4 +1,4 @@
<UserControl <UserControl
x:Class="v2rayN.Views.QrcodeView" x:Class="v2rayN.Views.QrcodeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -6,28 +6,33 @@
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" 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:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
d:DesignHeight="300" xmlns:sys="clr-namespace:System;assembly=mscorlib"
d:DesignWidth="300" d:DesignHeight="600"
d:DesignWidth="600"
Style="{StaticResource ViewGlobal}" Style="{StaticResource ViewGlobal}"
mc:Ignorable="d"> mc:Ignorable="d">
<UserControl.Resources>
<sys:Double x:Key="QrcodeWidth">400</sys:Double>
</UserControl.Resources>
<Grid Margin="32"> <Grid Margin="32">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="60" /> <RowDefinition Height="80" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Image <Image
x:Name="imgQrcode" x:Name="imgQrcode"
Grid.Row="0" Grid.Row="0"
Width="300" Width="{StaticResource QrcodeWidth}"
Height="300" Height="{StaticResource QrcodeWidth}"
Stretch="UniformToFill" /> Stretch="UniformToFill" />
<TextBox <TextBox
x:Name="txtContent" x:Name="txtContent"
Grid.Row="1" Grid.Row="1"
Width="300" Width="{StaticResource QrcodeWidth}"
Margin="0,8" Margin="0,8"
VerticalAlignment="Center" VerticalAlignment="Center"
IsReadOnly="True" IsReadOnly="True"

View File

@@ -1,7 +1,6 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Windows; using System.Windows;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
namespace v2rayN.Views; namespace v2rayN.Views;

View File

@@ -2,7 +2,6 @@ using System.Reactive.Disposables;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
namespace v2rayN.Views; namespace v2rayN.Views;

View File

@@ -2,7 +2,6 @@ using System.Reactive.Disposables;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
namespace v2rayN.Views; namespace v2rayN.Views;

View File

@@ -3,8 +3,6 @@ using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Threading; using System.Windows.Threading;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
using Splat;
using v2rayN.Manager; using v2rayN.Manager;
namespace v2rayN.Views; namespace v2rayN.Views;
@@ -17,8 +15,8 @@ public partial class StatusBarView
{ {
InitializeComponent(); InitializeComponent();
_config = AppManager.Instance.Config; _config = AppManager.Instance.Config;
ViewModel = new StatusBarViewModel(UpdateViewHandler); ViewModel = StatusBarViewModel.Instance;
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel)); ViewModel?.InitUpdateView(UpdateViewHandler);
menuExit.Click += menuExit_Click; menuExit.Click += menuExit_Click;
txtRunningServerDisplay.PreviewMouseDown += txtRunningInfoDisplay_MouseDoubleClick; txtRunningServerDisplay.PreviewMouseDown += txtRunningInfoDisplay_MouseDoubleClick;

View File

@@ -1,7 +1,6 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Windows; using System.Windows;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
namespace v2rayN.Views; namespace v2rayN.Views;

View File

@@ -4,7 +4,6 @@ using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
using ReactiveUI; using ReactiveUI;
using ServiceLib.Manager;
namespace v2rayN.Views; namespace v2rayN.Views;