LANCommander/LANCommander.UI/Components/MonacoCodeEditor/MonacoCodeEditor.cs
Pat Hartl 885032547b Improve script editor with context-aware completions, validation, and UX
- Filter variable completions and hover by script type so only relevant variables appear (e.g. $AllocatedKey only in KeyChange scripts)
- Add inline validation warnings for package scripts missing New-Package and for variables used outside their valid script type
- Extend debug console to support tools and redistributables, add stop button and elapsed time display
- Disable minimap, enable bracket pair colorization and word wrap
- Add snippet insertion via Monaco snippet controller with selection replacement support
- Add script templates that auto-populate on new script creation
- Add Variables dropdown in editor toolbar based on current script type
- Change Add Script button to type-selection dropdown, auto-populate script name from type, remove Type field from editor dialog
- Move Requires Admin checkbox to toolbar row, move toolbar and editor outside Form to prevent dropdown clicks from closing the modal
- Set MaskClosable=false on script editor modals so Monaco autocomplete clicks don't dismiss the dialog
2026-05-10 19:33:48 -05:00

95 lines
No EOL
2.6 KiB
C#

using BlazorMonaco.Editor;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace LANCommander.UI.Components;
public class MonacoCodeEditor : StandaloneCodeEditor
{
private static bool _completionsRegistered;
private IJSObjectReference? _module;
private string? _previousScriptType;
[Inject]
private IJSRuntime JSRuntime { get; set; } = default!;
[Parameter]
public string Value { get; set; }
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
[Parameter]
public EventCallback OnSave { get; set; }
[Parameter]
public string? ScriptType { get; set; }
protected override void OnInitialized()
{
OnDidChangeModelContent = Microsoft.AspNetCore.Components.EventCallback.Factory.Create<ModelContentChangedEvent>(this, OnChanged);
OnDidInit = Microsoft.AspNetCore.Components.EventCallback.Factory.Create(this, OnInit);
base.OnInitialized();
}
protected override async Task OnParametersSetAsync()
{
if (_module != null && ScriptType != _previousScriptType)
{
_previousScriptType = ScriptType;
await _module.InvokeVoidAsync("setScriptType", ScriptType);
await RunValidationAsync();
}
await base.OnParametersSetAsync();
}
private async Task OnInit()
{
await AddCommand((int)BlazorMonaco.KeyMod.CtrlCmd | (int)BlazorMonaco.KeyCode.KeyS, async (editor) =>
{
if (OnSave.HasDelegate)
await OnSave.InvokeAsync();
}, null);
_module = await JSRuntime.InvokeAsync<IJSObjectReference>(
"import", "./_content/LANCommander.UI/bundle.js");
if (!_completionsRegistered)
{
_completionsRegistered = true;
await _module.InvokeVoidAsync("registerPowerShellCompletions");
}
if (ScriptType != null)
{
_previousScriptType = ScriptType;
await _module.InvokeVoidAsync("setScriptType", ScriptType);
}
}
private async Task OnChanged(ModelContentChangedEvent e)
{
Value = await GetValue();
if (ValueChanged.HasDelegate)
await ValueChanged.InvokeAsync(Value);
await RunValidationAsync();
}
public async Task InsertSnippetAsync(string snippetText)
{
if (_module != null)
await _module.InvokeVoidAsync("insertSnippet", snippetText);
}
private async Task RunValidationAsync()
{
if (_module == null)
return;
await _module.InvokeVoidAsync("validateScript");
}
}