Implement debugger for packaging scripts

This commit is contained in:
Pat Hartl 2025-12-10 21:46:46 -06:00
parent 17fa802c19
commit ce183c8eb9
4 changed files with 80 additions and 30 deletions

View file

@ -239,6 +239,11 @@ namespace LANCommander.SDK.PowerShell
}
finally
{
await DebugAsync(async dbg =>
{
await dbg.EndAsync(DebugContext);
});
Context.Dispose();
}
}

View file

@ -51,4 +51,8 @@
.ant-layout-footer {
text-align: center;
}
.hidden {
display: none;
}

View file

@ -1,17 +1,21 @@
@using LANCommander.SDK.PowerShell
@using LANCommander.SDK.PowerShell.Rpc
@using LANCommander.SDK.Services
@using LANCommander.Server.Services.PowerShell
@using XtermBlazor
@using LogLevel = Microsoft.Extensions.Logging.LogLevel
@inherits RpcComponentBase<IScriptDebuggerClient, IScriptDebuggerHub>
@inject ScriptDebugger
@inject ScriptDebugger ScriptDebugger
@inject ScriptClient ScriptClient
@inject GameService GameService
<div class="terminal @(Visible ? "" : "hidden")">
<Xterm @ref="Terminal" Options="_options" OnFirstRender="@OnFirstRender" Addons="@Addons" />
<div class="terminal @(Active ? "" : "hidden")">
<Xterm @ref="Terminal" Id="@Id.ToString()" Options="_options" OnFirstRender="@OnFirstRender" Addons="@Addons" />
</div>
@code {
private bool Visible { get; set; } = false;
[Parameter] public Guid Id { get; set; }
[Parameter] public bool Active { get; set; }
[Parameter] public EventCallback<bool> ActiveChanged { get; set; }
private Xterm Terminal;
private TaskCompletionSource<string> InputTaskCompletionSource;
@ -27,8 +31,6 @@
"addon-fit"
};
protected override string HubUrl => "/RPC/ScriptDebugger";
private async Task OnFirstRender()
{
await Terminal.Addon("addon-fit").InvokeVoidAsync("fit");
@ -39,24 +41,39 @@
return await Terminal.Addon("readline").InvokeAsync<string>("read", "> ");
}
async Task SetActive(bool active)
{
Active = active;
if (ActiveChanged.HasDelegate)
await ActiveChanged.InvokeAsync(Active);
}
public async Task DebugPackagingScript(Guid gameId)
{
await Hub.DebugPackageScript(gameId);
ScriptDebugger.OnBreak = Break;
ScriptDebugger.OnStart = Start;
ScriptDebugger.OnEnd = End;
ScriptDebugger.OnOutput = Output;
ScriptClient.Debug = true;
await GameService.PackageAsync(gameId);
}
public async Task Start(IScriptDebugContext context)
{
Terminal?.Clear();
Visible = true;
await SetActive(true);
await InvokeAsync(StateHasChanged);
await Terminal.Focus();
await Terminal.Addon("addon-fit").InvokeVoidAsync("fit");
}
public async Task End(IScriptDebugContext context)
{
Visible = false;
await SetActive(false);
await InvokeAsync(StateHasChanged);
}
@ -78,27 +95,34 @@
public async Task Output(IScriptDebugContext context, LogLevel level, string message)
{
switch (level)
try
{
case LogLevel.Error:
await Terminal.WriteLine($"\x1b[0;31m{message}");
break;
switch (level)
{
case LogLevel.Error:
await Terminal.WriteLine($"\x1b[0;31m{message}");
break;
case LogLevel.Warning:
await Terminal.WriteLine($"\x1b[0;33m{message}");
break;
case LogLevel.Warning:
await Terminal.WriteLine($"\x1b[0;33m{message}");
break;
case LogLevel.Debug:
await Terminal.WriteLine($"\x1b[0;37m{message}");
break;
case LogLevel.Debug:
await Terminal.WriteLine($"\x1b[0;37m{message}");
break;
case LogLevel.Trace:
await Terminal.WriteLine($"\x1b[0;36m{message}");
break;
case LogLevel.Trace:
await Terminal.WriteLine($"\x1b[0;36m{message}");
break;
case LogLevel.Information:
await Terminal.WriteLine($"\x1b[0;37m{message}");
break;
case LogLevel.Information:
await Terminal.WriteLine($"\x1b[0;37m{message}");
break;
}
}
catch (Exception ex)
{
}
}
}

View file

@ -39,13 +39,15 @@
@if (Options.GameId.HasValue && Options.GameId != Guid.Empty && IsDebuggable)
{
<Tooltip Title="Debug">
<Button Icon="@IconType.Outline.CaretRight" Type="@ButtonType.Text" OnClick="() => _console.DebugPackagingScript(Options.GameId.Value)" />
<Button Icon="@IconType.Outline.CaretRight" Type="@ButtonType.Text" OnClick="Debug" Disabled="@_debugging" />
</Tooltip>
}
</FormItem>
<FormItem>
<MonacoCodeEditor @ref="Editor" @bind-Value="context.Contents" Id="@($"editor-{Id}")" ConstructionOptions="EditorConstructionOptions" OnSave="Save" />
<PowerShellConsole @ref="_console" Id="Id" @bind-Active="_debugging" />
</FormItem>
<FormItem Label="Name" Required Rules=@(new[] { new FormValidationRule { Required = true } })>
@ -67,7 +69,6 @@
<TextArea @bind-Value="context.Description" MaxLength=500 ShowCount />
</FormItem>
<PowerShellConsole @ref="_console" />
</Form>
<RegToPowerShell @ref="RegToPowerShell" OnParsed="(text) => InsertText(text)" />
@ -81,6 +82,8 @@
PowerShellConsole _console;
IEnumerable<Snippet> Snippets { get; set; }
bool _debugging;
Archive? _archive;
StandaloneEditorConstructionOptions EditorConstructionOptions(StandaloneCodeEditor editor)
@ -172,6 +175,20 @@
return false;
}
}
async Task Debug()
{
if (!IsDebuggable)
return;
var saved = await Save();
if (!saved)
return;
if (Options.GameId.HasValue)
await _console.DebugPackagingScript(Options.GameId.Value);
}
async void BrowseForPath()
{