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
+