From d9c6307cb924b2012dc6d1d5823567c529d94226 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sat, 14 Mar 2026 18:10:21 +0800 Subject: [PATCH 1/5] Add json editor --- v2rayN/Directory.Packages.props | 2 + v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 36 +++++++++++++++ v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 12 +++++ v2rayN/ServiceLib/Resx/ResUI.fr.resx | 12 +++++ v2rayN/ServiceLib/Resx/ResUI.hu.resx | 12 +++++ v2rayN/ServiceLib/Resx/ResUI.resx | 12 +++++ v2rayN/ServiceLib/Resx/ResUI.ru.resx | 12 +++++ v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 12 +++++ v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 12 +++++ v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml | 23 ++++++++++ .../v2rayN.Desktop/Views/JsonEditor.axaml.cs | 46 +++++++++++++++++++ v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj | 2 + 12 files changed, 193 insertions(+) create mode 100644 v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml create mode 100644 v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml.cs diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props index 839b3384..89db7035 100644 --- a/v2rayN/Directory.Packages.props +++ b/v2rayN/Directory.Packages.props @@ -9,6 +9,7 @@ + @@ -25,6 +26,7 @@ + diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 5a1ea759..c75715ae 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -924,6 +924,42 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Copy 的本地化字符串。 + /// + public static string menuEditCopy { + get { + return ResourceManager.GetString("menuEditCopy", resourceCulture); + } + } + + /// + /// 查找类似 Format 的本地化字符串。 + /// + public static string menuEditFormat { + get { + return ResourceManager.GetString("menuEditFormat", resourceCulture); + } + } + + /// + /// 查找类似 Paste 的本地化字符串。 + /// + public static string menuEditPaste { + get { + return ResourceManager.GetString("menuEditPaste", resourceCulture); + } + } + + /// + /// 查找类似 Select all 的本地化字符串。 + /// + public static string menuEditSelectAll { + get { + return ResourceManager.GetString("menuEditSelectAll", resourceCulture); + } + } + /// /// 查找类似 Edit 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index 1dd09e85..30f663e8 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1668,4 +1668,16 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Group by Region + + کپی + + + انتخاب همه + + + Paste + + + Format + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx index c873db43..061d6dad 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fr.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx @@ -1665,4 +1665,16 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Group by Region + + Copier + + + Tout sélect + + + Paste + + + Format + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index f59a652b..4d652587 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1668,4 +1668,16 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Group by Region + + Másolás + + + Összes kijelölése + + + Paste + + + Format + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index f299a877..787e7bd9 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1668,4 +1668,16 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Group by Region + + Copy + + + Select all + + + Paste + + + Format + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 957e6052..5e49c289 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1668,4 +1668,16 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Group by Region + + Скопировать + + + Выбрать все + + + Paste + + + Format + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index d655c30d..707609d0 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1665,4 +1665,16 @@ 按地区分组 + + 复制 + + + 全选 + + + 粘贴 + + + 格式化 + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index 65b1dbfb..4e417308 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1665,4 +1665,16 @@ 按區域分組 + + 複製 + + + 全選 + + + Paste + + + Format + \ No newline at end of file diff --git a/v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml b/v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml new file mode 100644 index 00000000..cad0021d --- /dev/null +++ b/v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml.cs b/v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml.cs new file mode 100644 index 00000000..1ed6203b --- /dev/null +++ b/v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml.cs @@ -0,0 +1,46 @@ +using System.Text.Json; +using AvaloniaEdit.TextMate; +using TextMateSharp.Grammars; + +namespace v2rayN.Desktop.Views; + +public partial class JsonEditor : UserControl +{ + private static readonly JsonSerializerOptions SIndentedOptions = new() { WriteIndented = true }; + + public JsonEditor() + { + InitializeComponent(); + var currentTheme = Application.Current?.ActualThemeVariant; + var themeName = currentTheme == ThemeVariant.Dark ? ThemeName.DarkPlus : + currentTheme == ThemeVariant.Light ? ThemeName.LightPlus : + ThemeName.DarkPlus; + var jsonRegistryOptions = new RegistryOptions(themeName); + var grammarScopeName = jsonRegistryOptions.GetScopeByLanguageId(jsonRegistryOptions.GetLanguageByExtension(".json").Id); + Editor.InstallTextMate(jsonRegistryOptions).SetGrammar(grammarScopeName); + Editor.TextArea.TextView.Options.EnableHyperlinks = false; + } + + public string Text + { + get => Editor.Text; + set => Editor.Text = value; + } + + private void FormatJson_Click(object? sender, RoutedEventArgs e) + { + try + { + var obj = JsonUtils.ParseJson(Editor.Text); + Editor.Text = JsonUtils.Serialize(obj, SIndentedOptions); + } + catch + { + // ignored + } + } + + private void Copy_Click(object? sender, RoutedEventArgs e) => Editor.Copy(); + private void Paste_Click(object? sender, RoutedEventArgs e) => Editor.Paste(); + private void SelectAll_Click(object? sender, RoutedEventArgs e) => Editor.SelectAll(); +} diff --git a/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj b/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj index 6212ce12..baaa36dd 100644 --- a/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj +++ b/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj @@ -15,6 +15,7 @@ + @@ -28,6 +29,7 @@ true + From 51a6fd507a2e04871cdc749879aa951f321ad861 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sat, 14 Mar 2026 18:13:41 +0800 Subject: [PATCH 2/5] Replace JsonEditor with TextBox --- .../Views/DNSSettingWindow.axaml | 22 ++++--------------- .../Views/FullConfigTemplateWindow.axaml | 22 ++++--------------- v2rayN/v2rayN.Desktop/Views/MsgView.axaml | 4 ++-- 3 files changed, 10 insertions(+), 38 deletions(-) diff --git a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml index 705e79e2..3335785b 100644 --- a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml @@ -3,6 +3,7 @@ xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:local="using:v2rayN.Desktop.Views" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib" @@ -399,12 +400,7 @@ BorderBrush="Gray" BorderThickness="1" Header="HTTP/SOCKS"> - + @@ -473,12 +469,7 @@ BorderBrush="Gray" BorderThickness="1" Header="HTTP/SOCKS"> - + @@ -488,12 +479,7 @@ BorderBrush="Gray" BorderThickness="1" Header="{x:Static resx:ResUI.TbSettingsTunMode}"> - + diff --git a/v2rayN/v2rayN.Desktop/Views/FullConfigTemplateWindow.axaml b/v2rayN/v2rayN.Desktop/Views/FullConfigTemplateWindow.axaml index 63f29ed0..1a2b482c 100644 --- a/v2rayN/v2rayN.Desktop/Views/FullConfigTemplateWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/FullConfigTemplateWindow.axaml @@ -5,6 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" + xmlns:views="clr-namespace:v2rayN.Desktop.Views" xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib" Title="{x:Static resx:ResUI.menuFullConfigTemplate}" Width="900" @@ -94,12 +95,7 @@ BorderBrush="Gray" BorderThickness="1" Header="xray config template json"> - + @@ -166,12 +162,7 @@ BorderBrush="Gray" BorderThickness="1" Header="sing-box config template json"> - + @@ -181,12 +172,7 @@ BorderBrush="Gray" BorderThickness="1" Header="sing-box tun config template json"> - + diff --git a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml index 90dcfeb7..aabe80fa 100644 --- a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml +++ b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml @@ -79,8 +79,8 @@ + Header="{x:Static resx:ResUI.menuMsgViewSelectAll}" + InputGesture="Ctrl+A" /> Date: Sat, 14 Mar 2026 18:19:02 +0800 Subject: [PATCH 3/5] Replace JsonEditor with TextBox --- .../v2rayN.Desktop/Views/AddServerWindow.axaml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml index 477eca5d..e9ded8aa 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml @@ -5,6 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" + xmlns:views="clr-namespace:v2rayN.Desktop.Views" xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib" Title="{x:Static resx:ResUI.menuServers}" Width="900" @@ -658,16 +659,13 @@ Margin="{StaticResource Margin4}" VerticalAlignment="Center" Text="{x:Static resx:ResUI.TransportExtraTip}" /> - + VerticalAlignment="Center" /> @@ -749,13 +747,13 @@ - + HorizontalAlignment="Stretch" + VerticalAlignment="Center" /> From 868efa6574f980ae9fb261c264b86566e8d77776 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sat, 14 Mar 2026 18:50:54 +0800 Subject: [PATCH 4/5] Remove TextMateSharp.Grammars --- v2rayN/Directory.Packages.props | 2 - .../v2rayN.Desktop/Views/JsonEditor.axaml.cs | 49 +++++++++++++++---- v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj | 2 - 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props index 89db7035..839b3384 100644 --- a/v2rayN/Directory.Packages.props +++ b/v2rayN/Directory.Packages.props @@ -9,7 +9,6 @@ - @@ -26,7 +25,6 @@ - diff --git a/v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml.cs b/v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml.cs index 1ed6203b..f577c85e 100644 --- a/v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml.cs @@ -1,6 +1,7 @@ using System.Text.Json; -using AvaloniaEdit.TextMate; -using TextMateSharp.Grammars; +using System.Xml; +using AvaloniaEdit.Highlighting; +using AvaloniaEdit.Highlighting.Xshd; namespace v2rayN.Desktop.Views; @@ -8,19 +9,49 @@ public partial class JsonEditor : UserControl { private static readonly JsonSerializerOptions SIndentedOptions = new() { WriteIndented = true }; + private static readonly Lazy SHighlightingDark = + new(() => BuildHighlighting(dark: true), isThreadSafe: true); + + private static readonly Lazy SHighlightingLight = + new(() => BuildHighlighting(dark: false), isThreadSafe: true); + public JsonEditor() { InitializeComponent(); - var currentTheme = Application.Current?.ActualThemeVariant; - var themeName = currentTheme == ThemeVariant.Dark ? ThemeName.DarkPlus : - currentTheme == ThemeVariant.Light ? ThemeName.LightPlus : - ThemeName.DarkPlus; - var jsonRegistryOptions = new RegistryOptions(themeName); - var grammarScopeName = jsonRegistryOptions.GetScopeByLanguageId(jsonRegistryOptions.GetLanguageByExtension(".json").Id); - Editor.InstallTextMate(jsonRegistryOptions).SetGrammar(grammarScopeName); + var isDark = Application.Current?.ActualThemeVariant != ThemeVariant.Light; + Editor.SyntaxHighlighting = isDark ? SHighlightingDark.Value : SHighlightingLight.Value; Editor.TextArea.TextView.Options.EnableHyperlinks = false; } + private static IHighlightingDefinition BuildHighlighting(bool dark) + { + var keyColor = dark ? "#9CDCFE" : "#0451A5"; + var strColor = dark ? "#CE9178" : "#A31515"; + var numColor = dark ? "#B5CEA8" : "#098658"; + var kwColor = dark ? "#569CD6" : "#0000FF"; + var xshd = $""" + + + + + + + + "([^"\\]|\\.)*"(?=\s*:) + "([^"\\]|\\.)*" + -?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)? + + true + false + null + + + + """; + using var reader = XmlReader.Create(new StringReader(xshd)); + return HighlightingLoader.Load(reader, HighlightingManager.Instance); + } + public string Text { get => Editor.Text; diff --git a/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj b/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj index baaa36dd..6212ce12 100644 --- a/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj +++ b/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj @@ -15,7 +15,6 @@ - @@ -29,7 +28,6 @@ true - From 2b0b09cd1a29d97e9cf0e4bc859d050c474ae225 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sat, 14 Mar 2026 19:27:32 +0800 Subject: [PATCH 5/5] Fix two way bind --- .../v2rayN.Desktop/Views/JsonEditor.axaml.cs | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml.cs b/v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml.cs index f577c85e..98620126 100644 --- a/v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml.cs @@ -15,12 +15,37 @@ public partial class JsonEditor : UserControl private static readonly Lazy SHighlightingLight = new(() => BuildHighlighting(dark: false), isThreadSafe: true); + public static readonly StyledProperty TextProperty = + AvaloniaProperty.Register(nameof(Text), defaultValue: string.Empty); + + public string Text + { + get => GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + public JsonEditor() { InitializeComponent(); var isDark = Application.Current?.ActualThemeVariant != ThemeVariant.Light; Editor.SyntaxHighlighting = isDark ? SHighlightingDark.Value : SHighlightingLight.Value; Editor.TextArea.TextView.Options.EnableHyperlinks = false; + + Editor.TextChanged += (_, _) => + { + if (Text != Editor.Text) + { + SetCurrentValue(TextProperty, Editor.Text); + } + }; + + this.GetObservable(TextProperty).Subscribe(text => + { + if (Editor.Text != text) + { + Editor.Text = text ?? string.Empty; + } + }); } private static IHighlightingDefinition BuildHighlighting(bool dark) @@ -52,12 +77,6 @@ public partial class JsonEditor : UserControl return HighlightingLoader.Load(reader, HighlightingManager.Instance); } - public string Text - { - get => Editor.Text; - set => Editor.Text = value; - } - private void FormatJson_Click(object? sender, RoutedEventArgs e) { try