@page "/" @using BlazorMonaco @using BlazorMonaco.Editor @using BlazorMonaco.Languages @using CommunityToolkit.Maui.Storage @using Inspectron.PrintServer.TemplateEditor.Services @inject IJSRuntime JSRuntime
@FileDisplayName@(HasUnsavedChanges ? " *" : "")
@if (!string.IsNullOrEmpty(_loadedJsonFile)) { @_loadedJsonFile }
Editor
Preview
@code { private StandaloneCodeEditor _editor = null!; private string _editorContent = ""; private string? _loadedJsonFile; private readonly TemplateCompletionProvider _completionProvider = new(); private string? _currentFilePath; private string _savedContent = ""; private bool HasUnsavedChanges => _editorContent != _savedContent; private string FileDisplayName => _currentFilePath is not null ? Path.GetFileName(_currentFilePath) : "Untitled"; private StandaloneEditorConstructionOptions EditorConstructionOptions(StandaloneCodeEditor editor) { return new StandaloneEditorConstructionOptions { AutomaticLayout = true, Language = "receipt-xml", Theme = "receipt-dark" }; } private async Task OnEditorInitAsync() { // Register custom language and theme await JSRuntime.InvokeVoidAsync("receiptLanguage.register"); await JSRuntime.InvokeVoidAsync("receiptLanguage.defineTheme"); // Apply the theme now that it's defined await BlazorMonaco.Editor.Global.SetTheme(JSRuntime, "receipt-dark"); // Wire up content accessor for the completion provider _completionProvider.SetEditorContentAccessor(() => _editorContent); // Register completion provider await BlazorMonaco.Languages.Global.RegisterCompletionItemProvider( JSRuntime, "receipt-xml", _completionProvider.ProvideCompletionItems, _completionProvider.ResolveCompletionItem); } private async Task OnContentChanged(ModelContentChangedEvent e) { _editorContent = await _editor.GetValue(); } private async Task NewFileAsync() { _currentFilePath = null; _savedContent = ""; _editorContent = ""; await _editor.SetValue(""); } private async Task OpenFileAsync() { try { var result = await FilePicker.Default.PickAsync(new PickOptions { PickerTitle = "Select an XML template file", FileTypes = new FilePickerFileType(new Dictionary> { { DevicePlatform.WinUI, new[] { ".xml" } }, { DevicePlatform.macOS, new[] { "xml" } }, { DevicePlatform.iOS, new[] { "public.xml" } }, { DevicePlatform.Android, new[] { "text/xml" } }, }) }); if (result is null) return; var content = await File.ReadAllTextAsync(result.FullPath); _currentFilePath = result.FullPath; _savedContent = content; _editorContent = content; await _editor.SetValue(content); } catch (Exception) { // User cancelled or file read failed } } private async Task SaveFileAsync() { if (_currentFilePath is not null) { await File.WriteAllTextAsync(_currentFilePath, _editorContent); _savedContent = _editorContent; } else { await SaveAsAsync(); } } private async Task SaveAsAsync() { try { using var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(_editorContent)); var result = await FileSaver.Default.SaveAsync("template.xml", stream, CancellationToken.None); if (result.IsSuccessful) { _currentFilePath = result.FilePath; _savedContent = _editorContent; } } catch (Exception) { // User cancelled or save failed } } private async Task LoadJsonAsync() { try { var result = await FilePicker.Default.PickAsync(new PickOptions { PickerTitle = "Select a JSON data file", FileTypes = new FilePickerFileType(new Dictionary> { { DevicePlatform.WinUI, new[] { ".json" } }, { DevicePlatform.macOS, new[] { "json" } }, { DevicePlatform.iOS, new[] { "public.json" } }, { DevicePlatform.Android, new[] { "application/json" } }, }) }); if (result is null) return; using var stream = await result.OpenReadAsync(); using var reader = new StreamReader(stream); var json = await reader.ReadToEndAsync(); var paths = JsonPathExtractor.Extract(json); _completionProvider.SetJsonPaths(paths); _loadedJsonFile = result.FileName; StateHasChanged(); } catch (Exception) { // User cancelled or file read failed } } }