[RegistryPreview] Adopt Monaco Editor (#35122)

This commit is contained in:
Davide Giacometti
2024-10-24 21:55:51 +02:00
committed by GitHub
parent b0be69a1b7
commit f9127b63a5
135 changed files with 456 additions and 133 deletions

View File

@@ -0,0 +1,64 @@
<!doctype HTML>
<html>
<head>
<!-- Set browser to Edge-->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<!-- Set charset -->
<meta charset="utf-8" />
<!-- Title (normally not displayed)-->
<title>Registry Preview Editor</title>
<style>
/* Fits content to window size */
html, body {
padding: 0;
}
#container, .monaco-editor {
position: fixed;
height: 100%;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
.overflowingContentWidgets {
/*Hides alert box */
display: none !important
}
</style>
</head>
<body>
<!-- Container for the editor -->
<div id="container"></div>
<!-- Script -->
<script src="http://PowerToysLocalMonaco/monacoSRC/min/vs/loader.js"></script>
<script type="module">
import { registerAdditionalLanguages } from 'http://PowerToysLocalMonaco/monacoSpecialLanguages.js';
import { customTokenColors } from 'http://PowerToysLocalMonaco/customTokenColors.js';
require.config({ paths: { vs: 'http://PowerToysLocalMonaco/monacoSRC/min/vs' } });
require(['vs/editor/editor.main'], async function () {
await registerAdditionalLanguages(monaco)
const editor = monaco.editor.create(document.getElementById('container'), {
automaticLayout: true,
language: 'reg',
minimap: { enabled: false },
});
window.onresize = () => {
editor.layout();
};
editor.getModel().onDidChangeContent((event) => {
window.chrome.webview.postMessage({ 'id': 'contentChanged', 'content': editor.getValue() });
});
editor.focus();
window.editor = editor;
});
</script>
</body>
</html>

View File

@@ -0,0 +1,35 @@
<UserControl
x:Class="RegistryPreviewUILib.MonacoEditorControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Microsoft.UI.Xaml.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:RegistryPreviewUILib"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters"
mc:Ignorable="d">
<UserControl.Resources>
<tkconverters:BoolToVisibilityConverter
x:Key="BoolToInvertedVisibilityConverter"
FalseValue="Visible"
TrueValue="Collapsed" />
</UserControl.Resources>
<Grid>
<ProgressRing
Width="48"
Height="48"
Visibility="{x:Bind IsLoading, Mode=OneWay}" />
<Border
BorderBrush="{ThemeResource SurfaceStrokeColorDefaultBrush}"
BorderThickness="1"
Visibility="{x:Bind IsLoading, Mode=OneWay, Converter={StaticResource BoolToInvertedVisibilityConverter}}">
<controls:WebView2
x:Name="Browser"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Loaded="Browser_Loaded" />
</Border>
</Grid>
</UserControl>

View File

@@ -0,0 +1,169 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.Reflection;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using System.Timers;
using System.Web;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.Web.WebView2.Core;
using Windows.UI;
namespace RegistryPreviewUILib
{
[INotifyPropertyChanged]
public sealed partial class MonacoEditorControl : UserControl, IDisposable
{
private readonly Timer _textChangedThrottle;
private bool _textChangedThrottled;
public string Text { get; private set; }
[ObservableProperty]
private bool _isLoading;
public event EventHandler TextChanged;
public MonacoEditorControl()
{
InitializeComponent();
Environment.SetEnvironmentVariable("WEBVIEW2_USER_DATA_FOLDER", MonacoHelper.TempFolderPath, EnvironmentVariableTarget.Process);
_textChangedThrottle = new Timer(250);
_textChangedThrottle.Elapsed += OnTextChangedThrottleElapsed;
_textChangedThrottle.AutoReset = false;
ActualThemeChanged += OnActualThemeChanged;
}
public async Task SetTextAsync(string text)
{
Text = text;
if (!IsLoading)
{
var encodedText = HttpUtility.JavaScriptStringEncode(text);
await Browser.CoreWebView2.ExecuteScriptAsync($"editor.setValue('{encodedText}')");
}
}
private async void OnActualThemeChanged(FrameworkElement sender, object args)
{
await SetThemeAsync();
}
private async void Browser_Loaded(object sender, RoutedEventArgs e)
{
IsLoading = true;
await Browser.EnsureCoreWebView2Async();
Browser.DefaultBackgroundColor = Color.FromArgb(0, 0, 0, 0);
Browser.CoreWebView2.NavigationCompleted += CoreWebView2_NavigationCompleted;
Browser.CoreWebView2.PermissionRequested += CoreWebView2_PermissionRequested;
Browser.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false;
Browser.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;
Browser.CoreWebView2.Settings.AreHostObjectsAllowed = false;
Browser.CoreWebView2.Settings.IsGeneralAutofillEnabled = false;
Browser.CoreWebView2.Settings.IsPasswordAutosaveEnabled = false;
Browser.CoreWebView2.Settings.IsScriptEnabled = true;
Browser.CoreWebView2.Settings.IsWebMessageEnabled = true;
#if DEBUG
Browser.CoreWebView2.Settings.AreDevToolsEnabled = true;
#else
Browser.CoreWebView2.Settings.AreDevToolsEnabled = false;
#endif
Browser.CoreWebView2.SetVirtualHostNameToFolderMapping(
MonacoHelper.VirtualHostName,
MonacoHelper.MonacoDirectory,
CoreWebView2HostResourceAccessKind.Allow);
var assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty;
var index = Path.GetFullPath(Path.Combine(assemblyDir, "Assets", "RegistryPreview", "index.html"));
Browser.CoreWebView2.Navigate(index);
}
private void CoreWebView2_PermissionRequested(CoreWebView2 sender, CoreWebView2PermissionRequestedEventArgs args)
{
if (args.PermissionKind == CoreWebView2PermissionKind.ClipboardRead)
{
// Hide the permission request dialog
args.State = CoreWebView2PermissionState.Allow;
args.Handled = true;
}
}
private async void CoreWebView2_NavigationCompleted(CoreWebView2 sender, CoreWebView2NavigationCompletedEventArgs args)
{
await SetThemeAsync();
IsLoading = false;
await SetTextAsync(Text);
Browser.CoreWebView2.WebMessageReceived += CoreWebView2_WebMessageReceived;
Browser.Focus(FocusState.Programmatic);
}
private void CoreWebView2_WebMessageReceived(CoreWebView2 sender, CoreWebView2WebMessageReceivedEventArgs args)
{
var json = JsonNode.Parse(args.WebMessageAsJson);
if (json == null)
{
return;
}
var id = json["id"];
if (id == null || !id.ToString().Equals("contentChanged", StringComparison.OrdinalIgnoreCase))
{
return;
}
var content = json["content"].ToString();
if (content == null)
{
return;
}
Text = content;
ThrottleTextChanged();
}
private async Task SetThemeAsync()
{
var theme = Application.Current.RequestedTheme == ApplicationTheme.Light ? "vs" : "vs-dark";
await Browser.CoreWebView2.ExecuteScriptAsync($"monaco.editor.setTheme('{theme}')");
}
private void OnTextChangedThrottleElapsed(object sender, ElapsedEventArgs e)
{
if (_textChangedThrottled)
{
_textChangedThrottled = false;
TextChanged?.Invoke(this, EventArgs.Empty);
_textChangedThrottle.Start();
}
}
private void ThrottleTextChanged()
{
if (_textChangedThrottle.Enabled)
{
_textChangedThrottled = true;
return;
}
TextChanged?.Invoke(this, EventArgs.Empty);
_textChangedThrottle.Start();
}
public void Dispose()
{
_textChangedThrottle?.Dispose();
}
}
}

View File

@@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.Reflection;
namespace RegistryPreviewUILib
{
public static class MonacoHelper
{
/// <summary>
/// Name of the virtual host
/// </summary>
public const string VirtualHostName = "PowerToysLocalMonaco";
public static string TempFolderPath { get; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), @"AppData\LocalLow\Microsoft\PowerToys\RegistryPreview-Temp");
private static string _monacoDirectory;
public static string GetRuntimeMonacoDirectory()
{
string codeBase = Assembly.GetExecutingAssembly().Location;
string path = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(codeBase) ?? string.Empty, "Assets", "Monaco"));
if (Path.Exists(path))
{
return path;
}
else
{
// We're likely in WinUI3Apps directory and need to go back to the base directory.
return Path.GetFullPath(Path.Combine(Path.GetDirectoryName(codeBase) ?? string.Empty, "..", "Assets", "Monaco"));
}
}
public static string MonacoDirectory
{
get
{
if (string.IsNullOrEmpty(_monacoDirectory))
{
_monacoDirectory = GetRuntimeMonacoDirectory();
}
return _monacoDirectory;
}
}
}
}

View File

@@ -8,9 +8,9 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
using CommunityToolkit.WinUI.UI.Controls;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Data;
@@ -21,6 +21,8 @@ namespace RegistryPreviewUILib
{
public sealed partial class RegistryPreviewMainPage : Page
{
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
/// <summary>
/// Event that is will prevent the app from closing if the "save file" flag is active
/// </summary>
@@ -40,43 +42,25 @@ namespace RegistryPreviewUILib
resourceLoader.GetString("YesNoCancelDialogSecondaryButtonText"),
resourceLoader.GetString("YesNoCancelDialogCloseButtonText"));
}
// Check to see if the textbox's context menu is open
if (textBox.ContextFlyout != null && textBox.ContextFlyout.IsOpen)
{
textBox.ContextFlyout.Hide();
// if true, the app will not close yet
args.Handled = true;
// HACK: To fix https://github.com/microsoft/PowerToys/issues/28820, wait a bit for the close animation of the flyout to run before closing the application.
// This might be called many times if the flyout still hasn't been closed, as Window_Closed will be called again by App.Current.Exit
DispatcherQueue.TryEnqueue(async () =>
{
await Task.Delay(100);
Application.Current.Exit();
});
return;
}
}
/// <summary>
/// Event that gets fired after the visual tree has been fully loaded; the app opens the reg file from here so it can show a message box successfully
/// </summary>
private void GridPreview_Loaded(object sender, RoutedEventArgs e)
private async void GridPreview_Loaded(object sender, RoutedEventArgs e)
{
// static flag to track whether the Visual Tree is ready - if the main Grid has been loaded, the tree is ready.
visualTreeReady = true;
// Check to see if the REG file was opened and parsed successfully
if (OpenRegistryFile(_appFileName) == false)
if (await OpenRegistryFile(_appFileName) == false)
{
if (File.Exists(_appFileName))
{
// Allow Refresh and Edit to be enabled because a broken Reg file might be fixable
UpdateToolBarAndUI(false, true, true);
_updateWindowTitleFunction(resourceLoader.GetString("InvalidRegistryFileTitle"));
textBox.TextChanged += TextBox_TextChanged;
MonacoEditor.TextChanged += MonacoEditor_TextChanged;
return;
}
else
@@ -87,10 +71,10 @@ namespace RegistryPreviewUILib
}
else
{
textBox.TextChanged += TextBox_TextChanged;
MonacoEditor.TextChanged += MonacoEditor_TextChanged;
}
textBox.Focus(FocusState.Programmatic);
MonacoEditor.Focus(FocusState.Programmatic);
}
/// <summary>
@@ -153,16 +137,15 @@ namespace RegistryPreviewUILib
if (storageFile != null)
{
// mute the TextChanged handler to make for clean UI
textBox.TextChanged -= TextBox_TextChanged;
MonacoEditor.TextChanged -= MonacoEditor_TextChanged;
_appFileName = storageFile.Path;
UpdateToolBarAndUI(OpenRegistryFile(_appFileName));
UpdateToolBarAndUI(await OpenRegistryFile(_appFileName));
// disable the Save button as it's a new file
saveButton.IsEnabled = false;
// Restore the event handler as we're loaded
textBox.TextChanged += TextBox_TextChanged;
MonacoEditor.TextChanged += MonacoEditor_TextChanged;
}
}
@@ -177,7 +160,7 @@ namespace RegistryPreviewUILib
/// <summary>
/// Uses a picker to save out a copy of the current reg file
/// </summary>
private void SaveAsButton_Click(object sender, RoutedEventArgs e)
private async void SaveAsButton_Click(object sender, RoutedEventArgs e)
{
// Save out a new REG file and then open it - we have to use the direct Win32 method because FileOpenPicker crashes when it's
// called while running as admin
@@ -195,24 +178,24 @@ namespace RegistryPreviewUILib
_appFileName = filename;
SaveFile();
UpdateToolBarAndUI(OpenRegistryFile(_appFileName));
UpdateToolBarAndUI(await OpenRegistryFile(_appFileName));
}
/// <summary>
/// Reloads the current REG file from storage
/// </summary>
private void RefreshButton_Click(object sender, RoutedEventArgs e)
private async void RefreshButton_Click(object sender, RoutedEventArgs e)
{
// mute the TextChanged handler to make for clean UI
textBox.TextChanged -= TextBox_TextChanged;
MonacoEditor.TextChanged -= MonacoEditor_TextChanged;
// reload the current Registry file and update the toolbar accordingly.
UpdateToolBarAndUI(OpenRegistryFile(_appFileName), true, true);
UpdateToolBarAndUI(await OpenRegistryFile(_appFileName), true, true);
saveButton.IsEnabled = false;
// restore the TextChanged handler
textBox.TextChanged += TextBox_TextChanged;
MonacoEditor.TextChanged += MonacoEditor_TextChanged;
}
/// <summary>
@@ -364,12 +347,15 @@ namespace RegistryPreviewUILib
}
/// <summary>
/// When the text in textBox changes, reload treeView and possibly dataGrid and reset the save button
/// When the text in editor changes, reload treeView and possibly dataGrid and reset the save button
/// </summary>
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
private void MonacoEditor_TextChanged(object sender, EventArgs e)
{
RefreshRegistryFile();
saveButton.IsEnabled = true;
_dispatcherQueue.TryEnqueue(() =>
{
RefreshRegistryFile();
saveButton.IsEnabled = true;
});
}
}
}

View File

@@ -11,7 +11,8 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.UI.Input;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@@ -21,12 +22,14 @@ namespace RegistryPreviewUILib
{
public sealed partial class RegistryPreviewMainPage : Page
{
private static SemaphoreSlim _dialogSemaphore = new(1);
public delegate void UpdateWindowTitleFunction(string title);
/// <summary>
/// Method that opens and processes the passed in file name; expected to be an absolute path and a first time open
/// </summary>
private bool OpenRegistryFile(string filename)
private async Task<bool> OpenRegistryFile(string filename)
{
// clamp to prevent attempts to open a file larger than 10MB
try
@@ -46,7 +49,7 @@ namespace RegistryPreviewUILib
// Disable parts of the UI that can cause trouble when loading
ChangeCursor(gridPreview, true);
textBox.Text = string.Empty;
await MonacoEditor.SetTextAsync(string.Empty);
// clear the treeView and dataGrid no matter what
treeView.RootNodes.Clear();
@@ -55,7 +58,7 @@ namespace RegistryPreviewUILib
// update the current window's title with the current filename
_updateWindowTitleFunction(filename);
// Load in the whole file in one call and plop it all into textBox
// Load in the whole file in one call and plop it all into editor
FileStream fileStream = null;
try
{
@@ -68,15 +71,15 @@ namespace RegistryPreviewUILib
StreamReader streamReader = new StreamReader(fileStream);
string filenameText = streamReader.ReadToEnd();
textBox.Text = filenameText;
await MonacoEditor.SetTextAsync(filenameText);
streamReader.Close();
}
catch
{
// restore TextChanged handler to make for clean UI
textBox.TextChanged += TextBox_TextChanged;
MonacoEditor.TextChanged += MonacoEditor_TextChanged;
// Reset the cursor but leave textBox disabled as no content got loaded
// Reset the cursor but leave editor disabled as no content got loaded
ChangeCursor(gridPreview, false);
return false;
}
@@ -89,8 +92,8 @@ namespace RegistryPreviewUILib
}
}
// now that the file is loaded and in textBox, parse the data
ParseRegistryFile(textBox.Text);
// now that the file is loaded and in editor, parse the data
ParseRegistryFile(MonacoEditor.Text);
// Getting here means that the entire REG file was parsed without incident
// so select the root of the tree and celebrate
@@ -120,8 +123,8 @@ namespace RegistryPreviewUILib
treeView.RootNodes.Clear();
ClearTable();
// the existing text is still in textBox so parse the data again
ParseRegistryFile(textBox.Text);
// the existing text is still in editor so parse the data again
ParseRegistryFile(MonacoEditor.Text);
// check to see if there was a key in treeView before the refresh happened
if (currentNode != null)
@@ -164,7 +167,7 @@ namespace RegistryPreviewUILib
}
/// <summary>
/// Parses the text that is passed in, which should be the same text that's in textBox
/// Parses the text that is passed in, which should be the same text that's in editor
/// </summary>
private bool ParseRegistryFile(string filenameText)
{
@@ -181,10 +184,10 @@ namespace RegistryPreviewUILib
// As we'll be processing the text one line at a time, this string will be the current line
string registryLine;
// Brute force editing: for textBox to show Cr-Lf corrected, we need to strip out the \n's
// Brute force editing: for editor to show Cr-Lf corrected, we need to strip out the \n's
filenameText = filenameText.Replace("\r\n", "\r");
// split apart all of the text in textBox, where one element in the array represents one line
// split apart all of the text in editor, where one element in the array represents one line
string[] registryLines = filenameText.Split("\r");
if (registryLines.Length <= 1)
{
@@ -655,8 +658,8 @@ namespace RegistryPreviewUILib
}
/// <summary>
/// Enable command bar buttons and textBox.
/// Note that writeButton and textBox all update with the same value on purpose
/// Enable command bar buttons
/// Note that writeButton and editor all update with the same value on purpose
/// </summary>
private void UpdateToolBarAndUI(bool enableWrite, bool enableRefresh, bool enableEdit)
{
@@ -776,21 +779,34 @@ namespace RegistryPreviewUILib
/// </summary>
private async void ShowMessageBox(string title, string content, string closeButtonText)
{
ContentDialog contentDialog = new ContentDialog()
if (_dialogSemaphore.CurrentCount == 0)
{
Title = title,
Content = content,
CloseButtonText = closeButtonText,
};
// Use this code to associate the dialog to the appropriate AppWindow by setting
// the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow.
if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8))
{
contentDialog.XamlRoot = this.Content.XamlRoot;
return;
}
await contentDialog.ShowAsync();
try
{
await _dialogSemaphore.WaitAsync();
ContentDialog contentDialog = new ContentDialog()
{
Title = title,
Content = content,
CloseButtonText = closeButtonText,
};
// Use this code to associate the dialog to the appropriate AppWindow by setting
// the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow.
if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8))
{
contentDialog.XamlRoot = this.Content.XamlRoot;
}
await contentDialog.ShowAsync();
}
finally
{
_dialogSemaphore.Release();
}
}
/// <summary>
@@ -894,7 +910,7 @@ namespace RegistryPreviewUILib
}
/// <summary>
/// Wrapper method that saves the current file in place, using the current text in textBox.
/// Wrapper method that saves the current file in place, using the current text in editor.
/// </summary>
private void SaveFile()
{
@@ -914,8 +930,8 @@ namespace RegistryPreviewUILib
fileStream = new FileStream(_appFileName, fileStreamOptions);
StreamWriter streamWriter = new StreamWriter(fileStream, System.Text.Encoding.Unicode);
// if we get here, the file is open and writable so dump the whole contents of textBox
string filenameText = textBox.Text;
// if we get here, the file is open and writable so dump the whole contents of editor
string filenameText = MonacoEditor.Text;
streamWriter.Write(filenameText);
streamWriter.Flush();
streamWriter.Close();

View File

@@ -3,6 +3,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:RegistryPreviewUILib"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:tk7controls="using:CommunityToolkit.WinUI.UI.Controls"
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
@@ -133,28 +134,12 @@
</CommandBar>
</Border>
<TextBox
x:Name="textBox"
x:Uid="textBox"
<local:MonacoEditorControl
x:Name="MonacoEditor"
Grid.Row="1"
Grid.RowSpan="3"
Grid.Column="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AcceptsReturn="True"
CanBeScrollAnchor="False"
CornerRadius="{StaticResource OverlayCornerRadius}"
FontFamily="Cascadia Mono, Consolas, Courier New"
IsSpellCheckEnabled="False"
IsTabStop="True"
IsTextPredictionEnabled="False"
PlaceholderText="{Binding PlaceholderText}"
ScrollViewer.HorizontalScrollBarVisibility="Visible"
ScrollViewer.IsHorizontalRailEnabled="True"
ScrollViewer.IsVerticalRailEnabled="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
TabIndex="0"
TextWrapping="NoWrap" />
IsTabStop="True" />
<Border
Grid.Row="1"

View File

@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
<Import Project="..\..\..\Monaco.props" />
<PropertyGroup>
<OutputType>Library</OutputType>
@@ -39,6 +40,12 @@
</ItemGroup>
<ItemGroup>
<None Remove="MonacoEditorControl.xaml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" />
<PackageReference Include="CommunityToolkit.WinUI.Converters" />
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls.DataGrid" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.Sizers" />
<PackageReference Include="CommunityToolkit.WinUI.Extensions" />
@@ -52,4 +59,10 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Page Update="MonacoEditorControl.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
</Project>

View File

@@ -199,9 +199,6 @@
<data name="SuggestFileName" xml:space="preserve">
<value>New Registry file</value>
</data>
<data name="textBox.PlaceholderText" xml:space="preserve">
<value>Registry file text will appear here</value>
</data>
<data name="titleBarText.ApplicationTitle" xml:space="preserve">
<value>Registry Preview</value>
</data>