Compare commits

..

17 commits

Author SHA1 Message Date
DHR60
5851785156
Merge 2a7095aa1e into 29a5abf4d6 2025-09-12 03:36:08 +00:00
DHR60
2a7095aa1e Avoid duplicate tags 2025-09-12 11:35:47 +08:00
DHR60
531bdc522d Add group in traffic splitting support 2025-09-11 17:18:53 +08:00
DHR60
6ff6f58129 Add PolicyGroup include other Group support 2025-09-11 16:24:20 +08:00
DHR60
f8fb492bc5 Add fallback support 2025-09-11 15:51:45 +08:00
DHR60
73c2a203de Fix 2025-09-11 14:50:58 +08:00
DHR60
00ed1edf92 Add Proxy Chain support 2025-09-11 14:29:14 +08:00
DHR60
76f39ccac4 Adjust UI 2025-09-11 13:49:10 +08:00
DHR60
5b073927d2 Add generate policy group 2025-09-11 13:23:38 +08:00
DHR60
5452341b60 Add Policy Group support 2025-09-11 13:00:12 +08:00
DHR60
d8a658037d Rename 2025-09-11 12:59:13 +08:00
DHR60
91eb1846fb Exclude specific profile types from selection 2025-09-11 12:24:05 +08:00
DHR60
e1bb6abfdf Fix right click not working 2025-09-11 12:14:18 +08:00
DHR60
1b9e5eca3e avalonia 2025-09-11 12:10:59 +08:00
DHR60
d0c177643c VM and wpf 2025-09-11 11:58:03 +08:00
DHR60
352490c165 Multi Profile 2025-09-10 23:44:32 +08:00
DHR60
2d29a4596a Add global fakeip and fakeip filter 2025-09-10 20:54:12 +08:00
14 changed files with 42 additions and 167 deletions

View file

@ -9,31 +9,6 @@ public class JsonUtils
{ {
private static readonly string _tag = "JsonUtils"; private static readonly string _tag = "JsonUtils";
private static readonly JsonSerializerOptions _defaultDeserializeOptions = new()
{
PropertyNameCaseInsensitive = true,
ReadCommentHandling = JsonCommentHandling.Skip
};
private static readonly JsonSerializerOptions _defaultSerializeOptions = new()
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
private static readonly JsonSerializerOptions _nullValueSerializeOptions = new()
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
private static readonly JsonDocumentOptions _defaultDocumentOptions = new()
{
CommentHandling = JsonCommentHandling.Skip
};
/// <summary> /// <summary>
/// DeepCopy /// DeepCopy
/// </summary> /// </summary>
@ -59,7 +34,11 @@ public class JsonUtils
{ {
return default; return default;
} }
return JsonSerializer.Deserialize<T>(strJson, _defaultDeserializeOptions); var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
return JsonSerializer.Deserialize<T>(strJson, options);
} }
catch catch
{ {
@ -80,7 +59,7 @@ public class JsonUtils
{ {
return null; return null;
} }
return JsonNode.Parse(strJson, nodeOptions: null, _defaultDocumentOptions); return JsonNode.Parse(strJson);
} }
catch catch
{ {
@ -105,7 +84,12 @@ public class JsonUtils
{ {
return result; return result;
} }
var options = nullValue ? _nullValueSerializeOptions : _defaultSerializeOptions; var options = new JsonSerializerOptions
{
WriteIndented = indented,
DefaultIgnoreCondition = nullValue ? JsonIgnoreCondition.Never : JsonIgnoreCondition.WhenWritingNull,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
result = JsonSerializer.Serialize(obj, options); result = JsonSerializer.Serialize(obj, options);
} }
catch (Exception ex) catch (Exception ex)

View file

@ -331,32 +331,6 @@ public class Utils
.ToList(); .ToList();
} }
public static Dictionary<string, List<string>> ParseHostsToDictionary(string hostsContent)
{
var userHostsMap = hostsContent
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(line => line.Trim())
// skip full-line comments
.Where(line => !string.IsNullOrWhiteSpace(line) && !line.StartsWith("#"))
// strip inline comments (truncate at '#')
.Select(line =>
{
var index = line.IndexOf('#');
return index >= 0 ? line.Substring(0, index).Trim() : line;
})
// ensure line still contains valid parts
.Where(line => !string.IsNullOrWhiteSpace(line) && line.Contains(' '))
.Select(line => line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries))
.Where(parts => parts.Length >= 2)
.GroupBy(parts => parts[0])
.ToDictionary(
group => group.Key,
group => group.SelectMany(parts => parts.Skip(1)).ToList()
);
return userHostsMap;
}
#endregion #endregion
#region #region
@ -883,55 +857,6 @@ public class Utils
return false; return false;
} }
public static bool IsPackagedInstall()
{
try
{
if (IsWindows() || IsOSX())
{
return false;
}
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("APPIMAGE")))
{
return true;
}
var exePath = GetExePath();
var baseDir = string.IsNullOrEmpty(exePath) ? StartupPath() : Path.GetDirectoryName(exePath) ?? "";
var p = baseDir.Replace('\\', '/');
if (string.IsNullOrEmpty(p))
{
return false;
}
if (p.Contains("/.mount_", StringComparison.Ordinal))
{
return true;
}
if (p.StartsWith("/opt/v2rayN", StringComparison.OrdinalIgnoreCase))
{
return true;
}
if (p.StartsWith("/usr/lib/v2rayN", StringComparison.OrdinalIgnoreCase))
{
return true;
}
if (p.StartsWith("/usr/share/v2rayN", StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
catch
{
}
return false;
}
private static async Task<string?> GetLinuxUserId() private static async Task<string?> GetLinuxUserId()
{ {
var arg = new List<string>() { "-c", "id -u" }; var arg = new List<string>() { "-c", "id -u" };

View file

@ -4,7 +4,7 @@ public class ClashFmt : BaseFmt
{ {
public static ProfileItem? ResolveFull(string strData, string? subRemarks) public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{ {
if (Contains(strData, "external-controller", "-port", "proxies")) if (Contains(strData, "port", "socks-port", "proxies"))
{ {
var fileName = WriteAllText(strData, "yaml"); var fileName = WriteAllText(strData, "yaml");

View file

@ -103,7 +103,17 @@ public partial class CoreConfigSingboxService
if (!simpleDNSItem.Hosts.IsNullOrEmpty()) if (!simpleDNSItem.Hosts.IsNullOrEmpty())
{ {
var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts); var userHostsMap = simpleDNSItem.Hosts
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(line => line.Trim())
.Where(line => !string.IsNullOrWhiteSpace(line) && line.Contains(' '))
.Select(line => line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries))
.Where(parts => parts.Length >= 2)
.GroupBy(parts => parts[0])
.ToDictionary(
group => group.Key,
group => group.SelectMany(parts => parts.Skip(1)).ToList()
);
foreach (var kvp in userHostsMap) foreach (var kvp in userHostsMap)
{ {

View file

@ -71,31 +71,6 @@ public partial class CoreConfigSingboxService
}); });
} }
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);
if (dnsItem == null || dnsItem.Enabled == false)
{
var simpleDNSItem = _config.SimpleDNSItem;
if (!simpleDNSItem.Hosts.IsNullOrEmpty())
{
var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts);
foreach (var kvp in userHostsMap)
{
hostsDomains.Add(kvp.Key);
}
}
}
singboxConfig.route.rules.Add(new()
{
action = "resolve",
domain = hostsDomains,
});
singboxConfig.route.rules.Add(new() singboxConfig.route.rules.Add(new()
{ {
outbound = Global.DirectTag, outbound = Global.DirectTag,

View file

@ -261,7 +261,17 @@ public partial class CoreConfigV2rayService
if (!simpleDNSItem.Hosts.IsNullOrEmpty()) if (!simpleDNSItem.Hosts.IsNullOrEmpty())
{ {
var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts); var userHostsMap = simpleDNSItem.Hosts
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(line => line.Trim())
.Where(line => !string.IsNullOrWhiteSpace(line) && line.Contains(' '))
.Select(line => line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries))
.Where(parts => parts.Length >= 2)
.GroupBy(parts => parts[0])
.ToDictionary(
group => group.Key,
group => group.SelectMany(parts => parts.Skip(1)).ToList()
);
foreach (var kvp in userHostsMap) foreach (var kvp in userHostsMap)
{ {

View file

@ -39,30 +39,26 @@ public class AddGroupServerViewModel : MyReactiveObject
_config = AppManager.Instance.Config; _config = AppManager.Instance.Config;
_updateView = updateView; _updateView = updateView;
var canEditRemove = this.WhenAnyValue(
x => x.SelectedChild,
SelectedChild => SelectedChild != null && !SelectedChild.Remarks.IsNullOrEmpty());
RemoveCmd = ReactiveCommand.CreateFromTask(async () => RemoveCmd = ReactiveCommand.CreateFromTask(async () =>
{ {
await ChildRemoveAsync(); await ChildRemoveAsync();
}, canEditRemove); });
MoveTopCmd = ReactiveCommand.CreateFromTask(async () => MoveTopCmd = ReactiveCommand.CreateFromTask(async () =>
{ {
await MoveServer(EMove.Top); await MoveServer(EMove.Top);
}, canEditRemove); });
MoveUpCmd = ReactiveCommand.CreateFromTask(async () => MoveUpCmd = ReactiveCommand.CreateFromTask(async () =>
{ {
await MoveServer(EMove.Up); await MoveServer(EMove.Up);
}, canEditRemove); });
MoveDownCmd = ReactiveCommand.CreateFromTask(async () => MoveDownCmd = ReactiveCommand.CreateFromTask(async () =>
{ {
await MoveServer(EMove.Down); await MoveServer(EMove.Down);
}, canEditRemove); });
MoveBottomCmd = ReactiveCommand.CreateFromTask(async () => MoveBottomCmd = ReactiveCommand.CreateFromTask(async () =>
{ {
await MoveServer(EMove.Bottom); await MoveServer(EMove.Bottom);
}, canEditRemove); });
SaveCmd = ReactiveCommand.CreateFromTask(async () => SaveCmd = ReactiveCommand.CreateFromTask(async () =>
{ {
await SaveServerAsync(); await SaveServerAsync();

View file

@ -63,16 +63,6 @@ public class CheckUpdateViewModel : MyReactiveObject
private CheckUpdateModel GetCheckUpdateModel(string coreType) private CheckUpdateModel GetCheckUpdateModel(string coreType)
{ {
if (coreType == _v2rayN && Utils.IsPackagedInstall())
{
return new()
{
IsSelected = false,
CoreType = coreType,
Remarks = ResUI.menuCheckUpdate + " (Not Support)",
};
}
return new() return new()
{ {
IsSelected = _config.CheckUpdateItem.SelectedCoreTypes?.Contains(coreType) ?? true, IsSelected = _config.CheckUpdateItem.SelectedCoreTypes?.Contains(coreType) ?? true,
@ -114,11 +104,6 @@ public class CheckUpdateViewModel : MyReactiveObject
} }
else if (item.CoreType == _v2rayN) else if (item.CoreType == _v2rayN)
{ {
if (Utils.IsPackagedInstall())
{
await UpdateView(_v2rayN, "Not Support");
continue;
}
await CheckUpdateN(EnableCheckPreReleaseUpdate); await CheckUpdateN(EnableCheckPreReleaseUpdate);
} }
else if (item.CoreType == ECoreType.Xray.ToString()) else if (item.CoreType == ECoreType.Xray.ToString())

View file

@ -400,7 +400,7 @@
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
Watermark="1000-2000,3000,4000" /> Watermark="1000:2000,3000:4000" />
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"
Grid.Column="2" Grid.Column="2"

View file

@ -176,7 +176,6 @@
<DataGrid <DataGrid
x:Name="lstRules" x:Name="lstRules"
AutoGenerateColumns="False" AutoGenerateColumns="False"
Background="Transparent"
BorderThickness="1" BorderThickness="1"
CanUserResizeColumns="True" CanUserResizeColumns="True"
GridLinesVisibility="All" GridLinesVisibility="All"

View file

@ -92,7 +92,6 @@
<DataGrid <DataGrid
x:Name="lstRoutings" x:Name="lstRoutings"
AutoGenerateColumns="False" AutoGenerateColumns="False"
Background="Transparent"
BorderThickness="1" BorderThickness="1"
CanUserResizeColumns="True" CanUserResizeColumns="True"
GridLinesVisibility="All" GridLinesVisibility="All"

View file

@ -16,7 +16,6 @@ public partial class AddGroupServerWindow
this.Loaded += Window_Loaded; this.Loaded += Window_Loaded;
this.PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown; this.PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown;
lstChild.SelectionChanged += LstChild_SelectionChanged; lstChild.SelectionChanged += LstChild_SelectionChanged;
menuSelectAllChild.Click += MenuSelectAllChild_Click;
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler); ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
@ -139,9 +138,4 @@ public partial class AddGroupServerWindow
ViewModel.SelectedChildren = lstChild.SelectedItems.Cast<ProfileItem>().ToList(); ViewModel.SelectedChildren = lstChild.SelectedItems.Cast<ProfileItem>().ToList();
} }
} }
private void MenuSelectAllChild_Click(object sender, RoutedEventArgs e)
{
lstChild.SelectAll();
}
} }

View file

@ -538,7 +538,7 @@
Width="400" Width="400"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
materialDesign:HintAssist.Hint="1000-2000,3000,4000" materialDesign:HintAssist.Hint="1000:2000,3000:4000"
Style="{StaticResource DefTextBox}" /> Style="{StaticResource DefTextBox}" />
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"

View file

@ -122,8 +122,6 @@ public partial class RoutingRuleSettingWindow
private void RoutingRuleSettingWindow_PreviewKeyDown(object sender, KeyEventArgs e) private void RoutingRuleSettingWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{ {
if (!lstRules.IsKeyboardFocusWithin)
return;
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
{ {
if (e.Key == Key.A) if (e.Key == Key.A)