mirror of
				https://github.com/2dust/v2rayN.git
				synced 2025-10-30 12:02:53 +00:00 
			
		
		
		
	Add Detour Feature
This commit is contained in:
		
							parent
							
								
									9a0e340472
								
							
						
					
					
						commit
						0519ca4eff
					
				
					 15 changed files with 369 additions and 215 deletions
				
			
		|  | @ -13,4 +13,6 @@ public class CustomConfigItem | ||||||
|     public ECoreType CoreType { get; set; } |     public ECoreType CoreType { get; set; } | ||||||
|     public string? Config { get; set; } |     public string? Config { get; set; } | ||||||
|     public string? TunConfig { get; set; } |     public string? TunConfig { get; set; } | ||||||
|  |     public bool? AddProxyOnly { get; set; } = false; | ||||||
|  |     public string? ProxyDetour { get; set; } | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										74
									
								
								v2rayN/ServiceLib/Resx/ResUI.Designer.cs
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										74
									
								
								v2rayN/ServiceLib/Resx/ResUI.Designer.cs
									
									
									
										generated
									
									
									
								
							|  | @ -2247,6 +2247,15 @@ namespace ServiceLib.Resx { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
|  |         /// <summary> | ||||||
|  |         ///   查找类似 Do Not Add Non-Proxy Protocol Outbound 的本地化字符串。 | ||||||
|  |         /// </summary> | ||||||
|  |         public static string TbAddProxyProtocolOutboundOnly { | ||||||
|  |             get { | ||||||
|  |                 return ResourceManager.GetString("TbAddProxyProtocolOutboundOnly", resourceCulture); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         ///   查找类似 Address 的本地化字符串。 |         ///   查找类似 Address 的本地化字符串。 | ||||||
|         /// </summary> |         /// </summary> | ||||||
|  | @ -2382,24 +2391,6 @@ namespace ServiceLib.Resx { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         /// <summary> |  | ||||||
|         ///   查找类似 Enable Custom DNS 的本地化字符串。 |  | ||||||
|         /// </summary> |  | ||||||
|         public static string TbCustomDNSEnable { |  | ||||||
|             get { |  | ||||||
|                 return ResourceManager.GetString("TbCustomDNSEnable", resourceCulture); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         /// <summary> |  | ||||||
|         ///   查找类似 Custom DNS Enabled, This Page's Settings Invalid 的本地化字符串。 |  | ||||||
|         /// </summary> |  | ||||||
|         public static string TbCustomDNSEnabledPageInvalid { |  | ||||||
|             get { |  | ||||||
|                 return ResourceManager.GetString("TbCustomDNSEnabledPageInvalid", resourceCulture); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         ///   查找类似 Enable Custom Config 的本地化字符串。 |         ///   查找类似 Enable Custom Config 的本地化字符串。 | ||||||
|         /// </summary> |         /// </summary> | ||||||
|  | @ -2418,6 +2409,24 @@ namespace ServiceLib.Resx { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
|  |         /// <summary> | ||||||
|  |         ///   查找类似 Enable Custom DNS 的本地化字符串。 | ||||||
|  |         /// </summary> | ||||||
|  |         public static string TbCustomDNSEnable { | ||||||
|  |             get { | ||||||
|  |                 return ResourceManager.GetString("TbCustomDNSEnable", resourceCulture); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         ///   查找类似 Custom DNS Enabled, This Page's Settings Invalid 的本地化字符串。 | ||||||
|  |         /// </summary> | ||||||
|  |         public static string TbCustomDNSEnabledPageInvalid { | ||||||
|  |             get { | ||||||
|  |                 return ResourceManager.GetString("TbCustomDNSEnabledPageInvalid", resourceCulture); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         ///   查找类似 Display GUI 的本地化字符串。 |         ///   查找类似 Display GUI 的本地化字符串。 | ||||||
|         /// </summary> |         /// </summary> | ||||||
|  | @ -2761,7 +2770,7 @@ namespace ServiceLib.Resx { | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         ///   查找类似 Override Outbound Config, routing.balancers and routing.rules.outboundTag Only 的本地化字符串。 |         ///   查找类似 Add Outbound Config Only, routing.balancers and routing.rules.outboundTag 的本地化字符串。 | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         public static string TbRayCustomConfigDesc { |         public static string TbRayCustomConfigDesc { | ||||||
|             get { |             get { | ||||||
|  | @ -2895,6 +2904,15 @@ namespace ServiceLib.Resx { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
|  |         /// <summary> | ||||||
|  |         ///   查找类似 Add Outbound and Endpoint Config Only 的本地化字符串。 | ||||||
|  |         /// </summary> | ||||||
|  |         public static string TbSBCustomConfigDesc { | ||||||
|  |             get { | ||||||
|  |                 return ResourceManager.GetString("TbSBCustomConfigDesc", resourceCulture); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         ///   查找类似 sing-box Direct Resolution Strategy 的本地化字符串。 |         ///   查找类似 sing-box Direct Resolution Strategy 的本地化字符串。 | ||||||
|         /// </summary> |         /// </summary> | ||||||
|  | @ -2958,15 +2976,6 @@ namespace ServiceLib.Resx { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         /// <summary> |  | ||||||
|         ///   查找类似 Override Outbound And Endpoint Config Only 的本地化字符串。 |  | ||||||
|         /// </summary> |  | ||||||
|         public static string TbSBCustomConfigDesc { |  | ||||||
|             get { |  | ||||||
|                 return ResourceManager.GetString("TbSBCustomConfigDesc", resourceCulture); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         ///   查找类似 Encryption method (security) 的本地化字符串。 |         ///   查找类似 Encryption method (security) 的本地化字符串。 | ||||||
|         /// </summary> |         /// </summary> | ||||||
|  | @ -3750,6 +3759,15 @@ namespace ServiceLib.Resx { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
|  |         /// <summary> | ||||||
|  |         ///   查找类似 Set Upstream Proxy Tag 的本地化字符串。 | ||||||
|  |         /// </summary> | ||||||
|  |         public static string TbSetUpstreamProxyDetour { | ||||||
|  |             get { | ||||||
|  |                 return ResourceManager.GetString("TbSetUpstreamProxyDetour", resourceCulture); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         ///   查找类似 Short Id 的本地化字符串。 |         ///   查找类似 Short Id 的本地化字符串。 | ||||||
|         /// </summary> |         /// </summary> | ||||||
|  |  | ||||||
|  | @ -1483,9 +1483,15 @@ | ||||||
|     <value>sing-box Custom Config</value> |     <value>sing-box Custom Config</value> | ||||||
|   </data> |   </data> | ||||||
|   <data name="TbRayCustomConfigDesc" xml:space="preserve"> |   <data name="TbRayCustomConfigDesc" xml:space="preserve"> | ||||||
|     <value>Override Outbound Config, routing.balancers and routing.rules.outboundTag Only</value> |     <value>Add Outbound Config Only, routing.balancers and routing.rules.outboundTag</value> | ||||||
|   </data> |   </data> | ||||||
|   <data name="TbSBCustomConfigDesc" xml:space="preserve"> |   <data name="TbSBCustomConfigDesc" xml:space="preserve"> | ||||||
|     <value>Override Outbound And Endpoint Config Only</value> |     <value>Add Outbound and Endpoint Config Only</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve"> | ||||||
|  |     <value>Do Not Add Non-Proxy Protocol Outbound</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSetUpstreamProxyDetour" xml:space="preserve"> | ||||||
|  |     <value>Set Upstream Proxy Tag</value> | ||||||
|   </data> |   </data> | ||||||
| </root> | </root> | ||||||
|  | @ -1483,9 +1483,15 @@ | ||||||
|     <value>sing-box Custom Config</value> |     <value>sing-box Custom Config</value> | ||||||
|   </data> |   </data> | ||||||
|   <data name="TbRayCustomConfigDesc" xml:space="preserve"> |   <data name="TbRayCustomConfigDesc" xml:space="preserve"> | ||||||
|     <value>Override Outbound Config, routing.balancers and routing.rules.outboundTag Only</value> |     <value>Add Outbound Config Only, routing.balancers and routing.rules.outboundTag</value> | ||||||
|   </data> |   </data> | ||||||
|   <data name="TbSBCustomConfigDesc" xml:space="preserve"> |   <data name="TbSBCustomConfigDesc" xml:space="preserve"> | ||||||
|     <value>Override Outbound And Endpoint Config Only</value> |     <value>Add Outbound and Endpoint Config Only</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve"> | ||||||
|  |     <value>Do Not Add Non-Proxy Protocol Outbound</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSetUpstreamProxyDetour" xml:space="preserve"> | ||||||
|  |     <value>Set Upstream Proxy Tag</value> | ||||||
|   </data> |   </data> | ||||||
| </root> | </root> | ||||||
|  | @ -1483,9 +1483,15 @@ | ||||||
|     <value>sing-box Custom Config</value> |     <value>sing-box Custom Config</value> | ||||||
|   </data> |   </data> | ||||||
|   <data name="TbRayCustomConfigDesc" xml:space="preserve"> |   <data name="TbRayCustomConfigDesc" xml:space="preserve"> | ||||||
|     <value>Override Outbound Config, routing.balancers and routing.rules.outboundTag Only</value> |     <value>Add Outbound Config Only, routing.balancers and routing.rules.outboundTag</value> | ||||||
|   </data> |   </data> | ||||||
|   <data name="TbSBCustomConfigDesc" xml:space="preserve"> |   <data name="TbSBCustomConfigDesc" xml:space="preserve"> | ||||||
|     <value>Override Outbound And Endpoint Config Only</value> |     <value>Add Outbound and Endpoint Config Only</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve"> | ||||||
|  |     <value>Do Not Add Non-Proxy Protocol Outbound</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSetUpstreamProxyDetour" xml:space="preserve"> | ||||||
|  |     <value>Set Upstream Proxy Tag</value> | ||||||
|   </data> |   </data> | ||||||
| </root> | </root> | ||||||
|  | @ -1483,9 +1483,15 @@ | ||||||
|     <value>sing-box Custom Config</value> |     <value>sing-box Custom Config</value> | ||||||
|   </data> |   </data> | ||||||
|   <data name="TbRayCustomConfigDesc" xml:space="preserve"> |   <data name="TbRayCustomConfigDesc" xml:space="preserve"> | ||||||
|     <value>Override Outbound Config, routing.balancers and routing.rules.outboundTag Only</value> |     <value>Add Outbound Config Only, routing.balancers and routing.rules.outboundTag</value> | ||||||
|   </data> |   </data> | ||||||
|   <data name="TbSBCustomConfigDesc" xml:space="preserve"> |   <data name="TbSBCustomConfigDesc" xml:space="preserve"> | ||||||
|     <value>Override Outbound And Endpoint Config Only</value> |     <value>Add Outbound and Endpoint Config Only</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve"> | ||||||
|  |     <value>Do Not Add Non-Proxy Protocol Outbound</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSetUpstreamProxyDetour" xml:space="preserve"> | ||||||
|  |     <value>Set Upstream Proxy Tag</value> | ||||||
|   </data> |   </data> | ||||||
| </root> | </root> | ||||||
|  | @ -1480,9 +1480,15 @@ | ||||||
|     <value>sing-box 自定义配置</value> |     <value>sing-box 自定义配置</value> | ||||||
|   </data> |   </data> | ||||||
|   <data name="TbRayCustomConfigDesc" xml:space="preserve"> |   <data name="TbRayCustomConfigDesc" xml:space="preserve"> | ||||||
|     <value>仅覆盖出站配置,routing.balancers 和 routing.rules.outboundTag</value> |     <value>仅添加出站配置,routing.balancers 和 routing.rules.outboundTag</value> | ||||||
|   </data> |   </data> | ||||||
|   <data name="TbSBCustomConfigDesc" xml:space="preserve"> |   <data name="TbSBCustomConfigDesc" xml:space="preserve"> | ||||||
|     <value>仅覆盖出站和端点配置</value> |     <value>仅添加出站和端点配置</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve"> | ||||||
|  |     <value>不添加非代理协议出站</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSetUpstreamProxyDetour" xml:space="preserve"> | ||||||
|  |     <value>设置上游代理 tag</value> | ||||||
|   </data> |   </data> | ||||||
| </root> | </root> | ||||||
|  | @ -1480,9 +1480,15 @@ | ||||||
|     <value>sing-box Custom Config</value> |     <value>sing-box Custom Config</value> | ||||||
|   </data> |   </data> | ||||||
|   <data name="TbRayCustomConfigDesc" xml:space="preserve"> |   <data name="TbRayCustomConfigDesc" xml:space="preserve"> | ||||||
|     <value>Override Outbound Config, routing.balancers and routing.rules.outboundTag Only</value> |     <value>Add Outbound Config Only, routing.balancers and routing.rules.outboundTag</value> | ||||||
|   </data> |   </data> | ||||||
|   <data name="TbSBCustomConfigDesc" xml:space="preserve"> |   <data name="TbSBCustomConfigDesc" xml:space="preserve"> | ||||||
|     <value>Override Outbound And Endpoint Config Only</value> |     <value>Add Outbound and Endpoint Config Only</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve"> | ||||||
|  |     <value>Do Not Add Non-Proxy Protocol Outbound</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSetUpstreamProxyDetour" xml:space="preserve"> | ||||||
|  |     <value>Set Upstream Proxy Tag</value> | ||||||
|   </data> |   </data> | ||||||
| </root> | </root> | ||||||
|  | @ -86,43 +86,7 @@ public class CoreConfigSingboxService | ||||||
|             ret.Success = true; |             ret.Success = true; | ||||||
| 
 | 
 | ||||||
|             var customConfig = await AppHandler.Instance.GetCustomConfigItem(ECoreType.sing_box); |             var customConfig = await AppHandler.Instance.GetCustomConfigItem(ECoreType.sing_box); | ||||||
|             var customConfigItem = customConfig.Config; |             ret.Data = await ApplyCustomConfig(customConfig, singboxConfig); | ||||||
|             if (_config.TunModeItem.EnableTun) |  | ||||||
|             { |  | ||||||
|                 customConfigItem = customConfig.TunConfig; |  | ||||||
|             } |  | ||||||
|             if (customConfig.Enabled && (!customConfigItem.IsNullOrEmpty())) |  | ||||||
|             { |  | ||||||
|                 var customConfigNode = JsonNode.Parse(customConfigItem); |  | ||||||
|                 if (customConfigNode != null) |  | ||||||
|                 { |  | ||||||
|                     if (customConfigNode["outbounds"] is JsonArray customOutboundsNode) |  | ||||||
|                     { |  | ||||||
|                         if (JsonNode.Parse(JsonUtils.Serialize(singboxConfig.outbounds)) is JsonArray newOutbounds) |  | ||||||
|                         { |  | ||||||
|                             foreach (var outbound in newOutbounds) |  | ||||||
|                             { |  | ||||||
|                                 customOutboundsNode.Add(outbound?.DeepClone()); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     if (customConfigNode["endpoints"] is JsonArray customEndpointsNode) |  | ||||||
|                     { |  | ||||||
|                         if (singboxConfig.endpoints != null && JsonNode.Parse(JsonUtils.Serialize(singboxConfig.endpoints)) is JsonArray newEndpoints) |  | ||||||
|                         { |  | ||||||
|                             foreach (var endpoint in newEndpoints) |  | ||||||
|                             { |  | ||||||
|                                 customEndpointsNode.Add(endpoint?.DeepClone()); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     ret.Data = JsonUtils.Serialize(customConfigNode); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 ret.Data = JsonUtils.Serialize(singboxConfig); |  | ||||||
|             } |  | ||||||
|             return ret; |             return ret; | ||||||
|         } |         } | ||||||
|         catch (Exception ex) |         catch (Exception ex) | ||||||
|  | @ -476,43 +440,7 @@ public class CoreConfigSingboxService | ||||||
|             ret.Success = true; |             ret.Success = true; | ||||||
| 
 | 
 | ||||||
|             var customConfig = await AppHandler.Instance.GetCustomConfigItem(ECoreType.sing_box); |             var customConfig = await AppHandler.Instance.GetCustomConfigItem(ECoreType.sing_box); | ||||||
|             var customConfigItem = customConfig.Config; |             ret.Data = await ApplyCustomConfig(customConfig, singboxConfig); | ||||||
|             if (_config.TunModeItem.EnableTun) |  | ||||||
|             { |  | ||||||
|                 customConfigItem = customConfig.TunConfig; |  | ||||||
|             } |  | ||||||
|             if (customConfig.Enabled && (!customConfigItem.IsNullOrEmpty())) |  | ||||||
|             { |  | ||||||
|                 var customConfigNode = JsonNode.Parse(customConfigItem); |  | ||||||
|                 if (customConfigNode != null) |  | ||||||
|                 { |  | ||||||
|                     if (customConfigNode["outbounds"] is JsonArray customOutboundsNode) |  | ||||||
|                     { |  | ||||||
|                         if (JsonNode.Parse(JsonUtils.Serialize(singboxConfig.outbounds)) is JsonArray newOutbounds) |  | ||||||
|                         { |  | ||||||
|                             foreach (var outbound in newOutbounds) |  | ||||||
|                             { |  | ||||||
|                                 customOutboundsNode.Add(outbound?.DeepClone()); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     if (customConfigNode["endpoints"] is JsonArray customEndpointsNode) |  | ||||||
|                     { |  | ||||||
|                         if (singboxConfig.endpoints != null && JsonNode.Parse(JsonUtils.Serialize(singboxConfig.endpoints)) is JsonArray newEndpoints) |  | ||||||
|                         { |  | ||||||
|                             foreach (var endpoint in newEndpoints) |  | ||||||
|                             { |  | ||||||
|                                 customEndpointsNode.Add(endpoint?.DeepClone()); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     ret.Data = JsonUtils.Serialize(customConfigNode); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 ret.Data = JsonUtils.Serialize(singboxConfig); |  | ||||||
|             } |  | ||||||
|             return ret; |             return ret; | ||||||
|         } |         } | ||||||
|         catch (Exception ex) |         catch (Exception ex) | ||||||
|  | @ -2277,5 +2205,61 @@ public class CoreConfigSingboxService | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private async Task<string> ApplyCustomConfig(CustomConfigItem customConfig, SingboxConfig singboxConfig) | ||||||
|  |     { | ||||||
|  |         var customConfigItem = customConfig.Config; | ||||||
|  |         if (_config.TunModeItem.EnableTun) | ||||||
|  |         { | ||||||
|  |             customConfigItem = customConfig.TunConfig; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!customConfig.Enabled || customConfigItem.IsNullOrEmpty()) | ||||||
|  |         { | ||||||
|  |             return JsonUtils.Serialize(singboxConfig); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         var customConfigNode = JsonNode.Parse(customConfigItem); | ||||||
|  |         if (customConfigNode == null) | ||||||
|  |         { | ||||||
|  |             return JsonUtils.Serialize(singboxConfig); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Process outbounds | ||||||
|  |         var customOutboundsNode = customConfigNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray(); | ||||||
|  |         foreach (var outbound in singboxConfig.outbounds) | ||||||
|  |         { | ||||||
|  |             if (outbound.type.ToLower() is "direct" or "block") | ||||||
|  |             { | ||||||
|  |                 if (customConfig.AddProxyOnly == true) | ||||||
|  |                 { | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else if (outbound.detour.IsNullOrEmpty() && (!customConfig.ProxyDetour.IsNullOrEmpty())) | ||||||
|  |             { | ||||||
|  |                 outbound.detour = customConfig.ProxyDetour; | ||||||
|  |             } | ||||||
|  |             customOutboundsNode.Add(JsonUtils.DeepCopy(outbound)); | ||||||
|  |         } | ||||||
|  |         customConfigNode["outbounds"] = customOutboundsNode; | ||||||
|  | 
 | ||||||
|  |         // Process endpoints | ||||||
|  |         if (singboxConfig.endpoints != null && singboxConfig.endpoints.Count > 0) | ||||||
|  |         { | ||||||
|  |             var customEndpointsNode = customConfigNode["endpoints"] is JsonArray endpoints ? endpoints : new JsonArray(); | ||||||
|  |             foreach (var endpoint in singboxConfig.endpoints) | ||||||
|  |             { | ||||||
|  |                 if (endpoint.detour.IsNullOrEmpty() && (!customConfig.ProxyDetour.IsNullOrEmpty())) | ||||||
|  |                 { | ||||||
|  |                     endpoint.detour = customConfig.ProxyDetour; | ||||||
|  |                 } | ||||||
|  |                 customEndpointsNode.Add(JsonUtils.DeepCopy(endpoint)); | ||||||
|  |             } | ||||||
|  |             customConfigNode["endpoints"] = customEndpointsNode; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return await Task.FromResult(JsonUtils.Serialize(customConfigNode)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     #endregion private gen function |     #endregion private gen function | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -71,28 +71,7 @@ public class CoreConfigV2rayService | ||||||
|             ret.Success = true; |             ret.Success = true; | ||||||
| 
 | 
 | ||||||
|             var customConfig = await AppHandler.Instance.GetCustomConfigItem(ECoreType.Xray); |             var customConfig = await AppHandler.Instance.GetCustomConfigItem(ECoreType.Xray); | ||||||
|             if (customConfig.Enabled && (!customConfig.Config.IsNullOrEmpty())) |             ret.Data = await ApplyCustomConfig(customConfig, v2rayConfig); | ||||||
|             { |  | ||||||
|                 var customConfigNode = JsonNode.Parse(customConfig.Config); |  | ||||||
|                 if (customConfigNode != null) |  | ||||||
|                 { |  | ||||||
|                     if (customConfigNode["outbounds"] is JsonArray customOutboundsNode) |  | ||||||
|                     { |  | ||||||
|                         if (JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.outbounds)) is JsonArray newOutbounds) |  | ||||||
|                         { |  | ||||||
|                             foreach (var outbound in newOutbounds) |  | ||||||
|                             { |  | ||||||
|                                 customOutboundsNode.Add(outbound?.DeepClone()); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     ret.Data = JsonUtils.Serialize(customConfigNode); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 ret.Data = JsonUtils.Serialize(v2rayConfig); |  | ||||||
|             } |  | ||||||
|             return ret; |             return ret; | ||||||
|         } |         } | ||||||
|         catch (Exception ex) |         catch (Exception ex) | ||||||
|  | @ -223,64 +202,7 @@ public class CoreConfigV2rayService | ||||||
|             ret.Success = true; |             ret.Success = true; | ||||||
| 
 | 
 | ||||||
|             var customConfig = await AppHandler.Instance.GetCustomConfigItem(ECoreType.Xray); |             var customConfig = await AppHandler.Instance.GetCustomConfigItem(ECoreType.Xray); | ||||||
|             if (customConfig.Enabled && (!customConfig.Config.IsNullOrEmpty())) |             ret.Data = await ApplyCustomConfig(customConfig, v2rayConfig, true); | ||||||
|             { |  | ||||||
|                 var customConfigNode = JsonNode.Parse(customConfig.Config); |  | ||||||
|                 if (customConfigNode != null) |  | ||||||
|                 { |  | ||||||
|                     var rulesNode = customConfigNode["routing"]?["rules"]; |  | ||||||
|                     if (rulesNode != null) |  | ||||||
|                     { |  | ||||||
|                         foreach (var rule in rulesNode.AsArray()) |  | ||||||
|                         { |  | ||||||
|                             if (rule["outboundTag"]?.GetValue<string>() == Global.ProxyTag) |  | ||||||
|                             { |  | ||||||
|                                 rule["outboundTag"] = null; |  | ||||||
|                                 rule["balancerTag"] = balancer.tag; |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                      |  | ||||||
|                     // Ensure routing node exists |  | ||||||
|                     if (customConfigNode["routing"] == null) |  | ||||||
|                     { |  | ||||||
|                         customConfigNode["routing"] = new JsonObject(); |  | ||||||
|                     } |  | ||||||
|                      |  | ||||||
|                     // Handle balancers - append instead of override |  | ||||||
|                     if (customConfigNode["routing"]["balancers"] is JsonArray customBalancersNode) |  | ||||||
|                     { |  | ||||||
|                         if (JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.routing.balancers)) is JsonArray newBalancers) |  | ||||||
|                         { |  | ||||||
|                             foreach (var balancerNode in newBalancers) |  | ||||||
|                             { |  | ||||||
|                                 customBalancersNode.Add(balancerNode?.DeepClone()); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     else |  | ||||||
|                     { |  | ||||||
|                         customConfigNode["routing"]["balancers"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.routing.balancers)); |  | ||||||
|                     } |  | ||||||
|                      |  | ||||||
|                     // append outbounds instead of override |  | ||||||
|                     if (customConfigNode["outbounds"] is JsonArray customOutboundsNode) |  | ||||||
|                     { |  | ||||||
|                         if (JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.outbounds)) is JsonArray newOutbounds) |  | ||||||
|                         { |  | ||||||
|                             foreach (var outbound in newOutbounds) |  | ||||||
|                             { |  | ||||||
|                                 customOutboundsNode.Add(outbound?.DeepClone()); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     ret.Data = JsonUtils.Serialize(customConfigNode); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 ret.Data = JsonUtils.Serialize(v2rayConfig); |  | ||||||
|             } |  | ||||||
|             return ret; |             return ret; | ||||||
|         } |         } | ||||||
|         catch (Exception ex) |         catch (Exception ex) | ||||||
|  | @ -1921,5 +1843,83 @@ public class CoreConfigV2rayService | ||||||
|         return await Task.FromResult(0); |         return await Task.FromResult(0); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private async Task<string> ApplyCustomConfig(CustomConfigItem customConfig, V2rayConfig v2rayConfig, bool handleBalancerAndRules = false) | ||||||
|  |     { | ||||||
|  |         if (!customConfig.Enabled || customConfig.Config.IsNullOrEmpty()) | ||||||
|  |         { | ||||||
|  |             return JsonUtils.Serialize(v2rayConfig); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         var customConfigNode = JsonNode.Parse(customConfig.Config); | ||||||
|  |         if (customConfigNode == null) | ||||||
|  |         { | ||||||
|  |             return JsonUtils.Serialize(v2rayConfig); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Handle balancer and rules modifications (for multiple load scenarios) | ||||||
|  |         if (handleBalancerAndRules && v2rayConfig.routing?.balancers?.Count > 0) | ||||||
|  |         { | ||||||
|  |             var balancer = v2rayConfig.routing.balancers.First(); | ||||||
|  | 
 | ||||||
|  |             // Modify existing rules in custom config | ||||||
|  |             var rulesNode = customConfigNode["routing"]?["rules"]; | ||||||
|  |             if (rulesNode != null) | ||||||
|  |             { | ||||||
|  |                 foreach (var rule in rulesNode.AsArray()) | ||||||
|  |                 { | ||||||
|  |                     if (rule["outboundTag"]?.GetValue<string>() == Global.ProxyTag) | ||||||
|  |                     { | ||||||
|  |                         rule.AsObject().Remove("outboundTag"); | ||||||
|  |                         rule["balancerTag"] = balancer.tag; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // Ensure routing node exists | ||||||
|  |             if (customConfigNode["routing"] == null) | ||||||
|  |             { | ||||||
|  |                 customConfigNode["routing"] = new JsonObject(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // Handle balancers - append instead of override | ||||||
|  |             if (customConfigNode["routing"]["balancers"] is JsonArray customBalancersNode) | ||||||
|  |             { | ||||||
|  |                 if (JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.routing.balancers)) is JsonArray newBalancers) | ||||||
|  |                 { | ||||||
|  |                     foreach (var balancerNode in newBalancers) | ||||||
|  |                     { | ||||||
|  |                         customBalancersNode.Add(balancerNode?.DeepClone()); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 customConfigNode["routing"]["balancers"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.routing.balancers)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Handle outbounds - append instead of override | ||||||
|  |         var customOutboundsNode = customConfigNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray(); | ||||||
|  |         foreach (var outbound in v2rayConfig.outbounds) | ||||||
|  |         { | ||||||
|  |             if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom") | ||||||
|  |             { | ||||||
|  |                 if (customConfig.AddProxyOnly == true) | ||||||
|  |                 { | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else if ((outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() == true) && (!customConfig.ProxyDetour.IsNullOrEmpty())) | ||||||
|  |             { | ||||||
|  |                 outbound.streamSettings ??= new StreamSettings4Ray(); | ||||||
|  |                 outbound.streamSettings.sockopt ??= new Sockopt4Ray(); | ||||||
|  |                 outbound.streamSettings.sockopt.dialerProxy = customConfig.ProxyDetour; | ||||||
|  |             } | ||||||
|  |             customOutboundsNode.Add(JsonUtils.DeepCopy(outbound)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return await Task.FromResult(JsonUtils.Serialize(customConfigNode)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     #endregion private gen function |     #endregion private gen function | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -22,6 +22,18 @@ public class CustomConfigViewModel : MyReactiveObject | ||||||
|     [Reactive] |     [Reactive] | ||||||
|     public string CustomTunConfig4Singbox { get; set; } |     public string CustomTunConfig4Singbox { get; set; } | ||||||
| 
 | 
 | ||||||
|  |     [Reactive] | ||||||
|  |     public bool AddProxyOnly4Ray { get; set; } | ||||||
|  | 
 | ||||||
|  |     [Reactive] | ||||||
|  |     public bool AddProxyOnly4Singbox { get; set; } | ||||||
|  | 
 | ||||||
|  |     [Reactive] | ||||||
|  |     public string ProxyDetour4Ray { get; set; } | ||||||
|  | 
 | ||||||
|  |     [Reactive] | ||||||
|  |     public string ProxyDetour4Singbox { get; set; } | ||||||
|  | 
 | ||||||
|     public ReactiveCommand<Unit, Unit> SaveCmd { get; } |     public ReactiveCommand<Unit, Unit> SaveCmd { get; } | ||||||
|     #endregion Reactive |     #endregion Reactive | ||||||
| 
 | 
 | ||||||
|  | @ -41,11 +53,15 @@ public class CustomConfigViewModel : MyReactiveObject | ||||||
|         var item = await AppHandler.Instance.GetCustomConfigItem(ECoreType.Xray); |         var item = await AppHandler.Instance.GetCustomConfigItem(ECoreType.Xray); | ||||||
|         EnableCustomConfig4Ray = item?.Enabled ?? false; |         EnableCustomConfig4Ray = item?.Enabled ?? false; | ||||||
|         CustomConfig4Ray = item?.Config ?? string.Empty; |         CustomConfig4Ray = item?.Config ?? string.Empty; | ||||||
|  |         AddProxyOnly4Ray = item?.AddProxyOnly ?? false; | ||||||
|  |         ProxyDetour4Ray = item?.ProxyDetour ?? string.Empty; | ||||||
| 
 | 
 | ||||||
|         var item2 = await AppHandler.Instance.GetCustomConfigItem(ECoreType.sing_box); |         var item2 = await AppHandler.Instance.GetCustomConfigItem(ECoreType.sing_box); | ||||||
|         EnableCustomConfig4Singbox = item2?.Enabled ?? false; |         EnableCustomConfig4Singbox = item2?.Enabled ?? false; | ||||||
|         CustomConfig4Singbox = item2?.Config ?? string.Empty; |         CustomConfig4Singbox = item2?.Config ?? string.Empty; | ||||||
|         CustomTunConfig4Singbox = item2?.TunConfig ?? string.Empty; |         CustomTunConfig4Singbox = item2?.TunConfig ?? string.Empty; | ||||||
|  |         AddProxyOnly4Singbox = item2?.AddProxyOnly ?? false; | ||||||
|  |         ProxyDetour4Singbox = item2?.ProxyDetour ?? string.Empty; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private async Task SaveSettingAsync() |     private async Task SaveSettingAsync() | ||||||
|  | @ -66,10 +82,10 @@ public class CustomConfigViewModel : MyReactiveObject | ||||||
|         item.Enabled = EnableCustomConfig4Ray; |         item.Enabled = EnableCustomConfig4Ray; | ||||||
|         item.Config = null; |         item.Config = null; | ||||||
| 
 | 
 | ||||||
|         if (CustomConfig4Ray.IsNotEmpty()) |  | ||||||
|         { |  | ||||||
|         item.Config = CustomConfig4Ray; |         item.Config = CustomConfig4Ray; | ||||||
|         } | 
 | ||||||
|  |         item.AddProxyOnly = AddProxyOnly4Ray; | ||||||
|  |         item.ProxyDetour = ProxyDetour4Ray; | ||||||
| 
 | 
 | ||||||
|         await ConfigHandler.SaveCustomConfigItem(_config, item); |         await ConfigHandler.SaveCustomConfigItem(_config, item); | ||||||
|         return true; |         return true; | ||||||
|  | @ -82,25 +98,13 @@ public class CustomConfigViewModel : MyReactiveObject | ||||||
|         item.Config = null; |         item.Config = null; | ||||||
|         item.TunConfig = null; |         item.TunConfig = null; | ||||||
| 
 | 
 | ||||||
|         var hasChanges = false; |  | ||||||
| 
 |  | ||||||
|         if (CustomConfig4Singbox.IsNotEmpty()) |  | ||||||
|         { |  | ||||||
|         item.Config = CustomConfig4Singbox; |         item.Config = CustomConfig4Singbox; | ||||||
|             hasChanges = true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (CustomTunConfig4Singbox.IsNotEmpty()) |  | ||||||
|         { |  | ||||||
|         item.TunConfig = CustomTunConfig4Singbox; |         item.TunConfig = CustomTunConfig4Singbox; | ||||||
|             hasChanges = true; |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         if (hasChanges) |         item.AddProxyOnly = AddProxyOnly4Singbox; | ||||||
|         { |         item.ProxyDetour = ProxyDetour4Singbox; | ||||||
|  | 
 | ||||||
|         await ConfigHandler.SaveCustomConfigItem(_config, item); |         await ConfigHandler.SaveCustomConfigItem(_config, item); | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -59,6 +59,30 @@ | ||||||
|                         </StackPanel> |                         </StackPanel> | ||||||
|                     </Grid> |                     </Grid> | ||||||
| 
 | 
 | ||||||
|  |                     <WrapPanel DockPanel.Dock="Bottom" Orientation="Horizontal"> | ||||||
|  |                         <StackPanel Orientation="Horizontal"> | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Text="{x:Static resx:ResUI.TbAddProxyProtocolOutboundOnly}" /> | ||||||
|  |                             <ToggleSwitch | ||||||
|  |                                 x:Name="togAddProxyProtocolOutboundOnly4Ray" | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 HorizontalAlignment="Left" /> | ||||||
|  |                         </StackPanel> | ||||||
|  | 
 | ||||||
|  |                         <StackPanel Orientation="Horizontal"> | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Text="{x:Static resx:ResUI.TbSetUpstreamProxyDetour}" /> | ||||||
|  |                             <TextBox | ||||||
|  |                                 x:Name="txtProxyDetour4Ray" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin4}" /> | ||||||
|  |                         </StackPanel> | ||||||
|  |                     </WrapPanel> | ||||||
|  | 
 | ||||||
|                     <HeaderedContentControl |                     <HeaderedContentControl | ||||||
|                         Margin="{StaticResource Margin4}" |                         Margin="{StaticResource Margin4}" | ||||||
|                         BorderBrush="Gray" |                         BorderBrush="Gray" | ||||||
|  | @ -97,6 +121,30 @@ | ||||||
|                         </StackPanel> |                         </StackPanel> | ||||||
|                     </Grid> |                     </Grid> | ||||||
| 
 | 
 | ||||||
|  |                     <WrapPanel DockPanel.Dock="Bottom" Orientation="Horizontal"> | ||||||
|  |                         <StackPanel Orientation="Horizontal"> | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Text="{x:Static resx:ResUI.TbAddProxyProtocolOutboundOnly}" /> | ||||||
|  |                             <ToggleSwitch | ||||||
|  |                                 x:Name="togAddProxyProtocolOutboundOnly4Singbox" | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 HorizontalAlignment="Left" /> | ||||||
|  |                         </StackPanel> | ||||||
|  | 
 | ||||||
|  |                         <StackPanel Orientation="Horizontal"> | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Text="{x:Static resx:ResUI.TbSetUpstreamProxyDetour}" /> | ||||||
|  |                             <TextBox | ||||||
|  |                                 x:Name="txtProxyDetour4Singbox" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin4}" /> | ||||||
|  |                         </StackPanel> | ||||||
|  |                     </WrapPanel> | ||||||
|  | 
 | ||||||
|                     <Grid Margin="{StaticResource Margin4}" ColumnDefinitions="*,10,*"> |                     <Grid Margin="{StaticResource Margin4}" ColumnDefinitions="*,10,*"> | ||||||
| 
 | 
 | ||||||
|                         <HeaderedContentControl |                         <HeaderedContentControl | ||||||
|  |  | ||||||
|  | @ -21,9 +21,13 @@ public partial class CustomConfigWindow : WindowBase<CustomConfigViewModel> | ||||||
|         { |         { | ||||||
|             this.Bind(ViewModel, vm => vm.EnableCustomConfig4Ray, v => v.rayCustomConfigEnable.IsChecked).DisposeWith(disposables); |             this.Bind(ViewModel, vm => vm.EnableCustomConfig4Ray, v => v.rayCustomConfigEnable.IsChecked).DisposeWith(disposables); | ||||||
|             this.Bind(ViewModel, vm => vm.CustomConfig4Ray, v => v.rayCustomConfig.Text).DisposeWith(disposables); |             this.Bind(ViewModel, vm => vm.CustomConfig4Ray, v => v.rayCustomConfig.Text).DisposeWith(disposables); | ||||||
|  |             this.Bind(ViewModel, vm => vm.AddProxyOnly4Ray, v => v.togAddProxyProtocolOutboundOnly4Ray.IsChecked).DisposeWith(disposables); | ||||||
|  |             this.Bind(ViewModel, vm => vm.ProxyDetour4Ray, v => v.txtProxyDetour4Ray.Text).DisposeWith(disposables); | ||||||
|             this.Bind(ViewModel, vm => vm.EnableCustomConfig4Singbox, v => v.sbCustomConfigEnable.IsChecked).DisposeWith(disposables); |             this.Bind(ViewModel, vm => vm.EnableCustomConfig4Singbox, v => v.sbCustomConfigEnable.IsChecked).DisposeWith(disposables); | ||||||
|             this.Bind(ViewModel, vm => vm.CustomConfig4Singbox, v => v.sbCustomConfig.Text).DisposeWith(disposables); |             this.Bind(ViewModel, vm => vm.CustomConfig4Singbox, v => v.sbCustomConfig.Text).DisposeWith(disposables); | ||||||
|             this.Bind(ViewModel, vm => vm.CustomTunConfig4Singbox, v => v.sbCustomTunConfig.Text).DisposeWith(disposables); |             this.Bind(ViewModel, vm => vm.CustomTunConfig4Singbox, v => v.sbCustomTunConfig.Text).DisposeWith(disposables); | ||||||
|  |             this.Bind(ViewModel, vm => vm.AddProxyOnly4Singbox, v => v.togAddProxyProtocolOutboundOnly4Singbox.IsChecked).DisposeWith(disposables); | ||||||
|  |             this.Bind(ViewModel, vm => vm.ProxyDetour4Singbox, v => v.txtProxyDetour4Singbox.Text).DisposeWith(disposables); | ||||||
| 
 | 
 | ||||||
|             this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); |             this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|  | @ -71,6 +71,33 @@ | ||||||
|                         </StackPanel> |                         </StackPanel> | ||||||
|                     </Grid> |                     </Grid> | ||||||
| 
 | 
 | ||||||
|  |                     <WrapPanel DockPanel.Dock="Bottom" Orientation="Horizontal"> | ||||||
|  |                         <StackPanel Orientation="Horizontal"> | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                                 Text="{x:Static resx:ResUI.TbAddProxyProtocolOutboundOnly}" /> | ||||||
|  |                             <ToggleButton | ||||||
|  |                                 x:Name="togAddProxyProtocolOutboundOnly4Ray" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 HorizontalAlignment="Left" /> | ||||||
|  |                         </StackPanel> | ||||||
|  | 
 | ||||||
|  |                         <StackPanel Orientation="Horizontal"> | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                                 Text="{x:Static resx:ResUI.TbSetUpstreamProxyDetour}" /> | ||||||
|  |                             <TextBox | ||||||
|  |                                 x:Name="txtProxyDetour4Ray" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 Style="{StaticResource DefTextBox}" /> | ||||||
|  |                         </StackPanel> | ||||||
|  |                     </WrapPanel> | ||||||
|  | 
 | ||||||
|                     <TextBox |                     <TextBox | ||||||
|                         x:Name="rayCustomConfig" |                         x:Name="rayCustomConfig" | ||||||
|                         Margin="{StaticResource Margin8}" |                         Margin="{StaticResource Margin8}" | ||||||
|  | @ -113,6 +140,33 @@ | ||||||
|                         </StackPanel> |                         </StackPanel> | ||||||
|                     </Grid> |                     </Grid> | ||||||
| 
 | 
 | ||||||
|  |                     <WrapPanel DockPanel.Dock="Bottom" Orientation="Horizontal"> | ||||||
|  |                         <StackPanel Orientation="Horizontal"> | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                                 Text="{x:Static resx:ResUI.TbAddProxyProtocolOutboundOnly}" /> | ||||||
|  |                             <ToggleButton | ||||||
|  |                                 x:Name="togAddProxyProtocolOutboundOnly4Singbox" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 HorizontalAlignment="Left" /> | ||||||
|  |                         </StackPanel> | ||||||
|  | 
 | ||||||
|  |                         <StackPanel Orientation="Horizontal"> | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                                 Text="{x:Static resx:ResUI.TbSetUpstreamProxyDetour}" /> | ||||||
|  |                             <TextBox | ||||||
|  |                                 x:Name="txtProxyDetour4Singbox" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 Style="{StaticResource DefTextBox}" /> | ||||||
|  |                         </StackPanel> | ||||||
|  |                     </WrapPanel> | ||||||
|  | 
 | ||||||
|                     <Grid Margin="{StaticResource Margin8}"> |                     <Grid Margin="{StaticResource Margin8}"> | ||||||
|                         <Grid.ColumnDefinitions> |                         <Grid.ColumnDefinitions> | ||||||
|                             <ColumnDefinition Width="1*" /> |                             <ColumnDefinition Width="1*" /> | ||||||
|  |  | ||||||
|  | @ -21,9 +21,13 @@ public partial class CustomConfigWindow | ||||||
|         { |         { | ||||||
|             this.Bind(ViewModel, vm => vm.EnableCustomConfig4Ray, v => v.rayCustomConfigEnable.IsChecked).DisposeWith(disposables); |             this.Bind(ViewModel, vm => vm.EnableCustomConfig4Ray, v => v.rayCustomConfigEnable.IsChecked).DisposeWith(disposables); | ||||||
|             this.Bind(ViewModel, vm => vm.CustomConfig4Ray, v => v.rayCustomConfig.Text).DisposeWith(disposables); |             this.Bind(ViewModel, vm => vm.CustomConfig4Ray, v => v.rayCustomConfig.Text).DisposeWith(disposables); | ||||||
|  |             this.Bind(ViewModel, vm => vm.AddProxyOnly4Ray, v => v.togAddProxyProtocolOutboundOnly4Ray.IsChecked).DisposeWith(disposables); | ||||||
|  |             this.Bind(ViewModel, vm => vm.ProxyDetour4Ray, v => v.txtProxyDetour4Ray.Text).DisposeWith(disposables); | ||||||
|             this.Bind(ViewModel, vm => vm.EnableCustomConfig4Singbox, v => v.sbCustomConfigEnable.IsChecked).DisposeWith(disposables); |             this.Bind(ViewModel, vm => vm.EnableCustomConfig4Singbox, v => v.sbCustomConfigEnable.IsChecked).DisposeWith(disposables); | ||||||
|             this.Bind(ViewModel, vm => vm.CustomConfig4Singbox, v => v.sbCustomConfig.Text).DisposeWith(disposables); |             this.Bind(ViewModel, vm => vm.CustomConfig4Singbox, v => v.sbCustomConfig.Text).DisposeWith(disposables); | ||||||
|             this.Bind(ViewModel, vm => vm.CustomTunConfig4Singbox, v => v.sbCustomTunConfig.Text).DisposeWith(disposables); |             this.Bind(ViewModel, vm => vm.CustomTunConfig4Singbox, v => v.sbCustomTunConfig.Text).DisposeWith(disposables); | ||||||
|  |             this.Bind(ViewModel, vm => vm.AddProxyOnly4Singbox, v => v.togAddProxyProtocolOutboundOnly4Singbox.IsChecked).DisposeWith(disposables); | ||||||
|  |             this.Bind(ViewModel, vm => vm.ProxyDetour4Singbox, v => v.txtProxyDetour4Singbox.Text).DisposeWith(disposables); | ||||||
| 
 | 
 | ||||||
|             this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); |             this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 DHR60
						DHR60