LANCommander/LANCommander.Server/UI/Components/ScriptEditorDialog.razor
Pat Hartl 120e14d40c Add Tools section to server app
Allows users to upload additional tools and tie them to games. This can be useful for software like map editors, trainers, etc.
2026-02-08 02:24:53 -06:00

256 lines
7.7 KiB
Text

@using LANCommander.SDK.Enums
@inherits FeedbackComponent<ScriptEditorOptions, Script>
@inject ScriptService ScriptService
@inject ArchiveService ArchiveService
@inject ServerService ServerService
@inject ToolService ToolService
@inject ModalService ModalService
@inject IMessageService MessageService
@inject ILogger<ScriptEditorDialog> Logger
<Form @ref="@Form" Model="@Script" Layout="@FormLayout.Vertical">
<FormItem>
@foreach (var group in Snippets.Select(s => s.Group).Distinct())
{
<Dropdown>
<Overlay>
<Menu>
@foreach (var snippet in Snippets.Where(s => s.Group == group))
{
<MenuItem OnClick="() => InsertSnippet(snippet)">
@snippet.Name
</MenuItem>
}
</Menu>
</Overlay>
<ChildContent>
<Button Type="@ButtonType.Primary">@group</Button>
</ChildContent>
</Dropdown>
}
@if (_archive != null)
{
<Button Icon="@IconType.Outline.FolderOpen" OnClick="BrowseForPath" Type="@ButtonType.Text">Browse</Button>
}
<Button Icon="@IconType.Outline.Build" OnClick="() => RegToPowerShell.Open()" Type="@ButtonType.Text">Import .reg</Button>
@if (Options.GameId.HasValue && Options.GameId != Guid.Empty && IsDebuggable)
{
<Tooltip Title="Debug">
<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 } })>
<Input @bind-Value="@context.Name" />
</FormItem>
<FormItem Label="Type">
<Select @bind-Value="context.Type" TItem="ScriptType" TItemValue="ScriptType" DataSource="Enum.GetValues<ScriptType>().Where(st => Options.AllowedTypes == null || Options.AllowedTypes.Contains(st))">
<LabelTemplate Context="Value">@Value.GetDisplayName()</LabelTemplate>
<ItemTemplate Context="Value">@Value.GetDisplayName()</ItemTemplate>
</Select>
</FormItem>
<FormItem>
<Checkbox @bind-Checked="context.RequiresAdmin">Requires Admin</Checkbox>
</FormItem>
<FormItem Label="Description">
<TextArea @bind-Value="context.Description" MaxLength=500 ShowCount />
</FormItem>
</Form>
<RegToPowerShell @ref="RegToPowerShell" OnParsed="(text) => InsertText(text)" />
@code {
Guid Id = Guid.NewGuid();
Form<Script> Form;
MonacoCodeEditor? Editor;
RegToPowerShell RegToPowerShell;
PowerShellConsole _console;
IEnumerable<Snippet> Snippets { get; set; }
bool _debugging;
Archive? _archive;
StandaloneEditorConstructionOptions EditorConstructionOptions(StandaloneCodeEditor editor)
{
return new StandaloneEditorConstructionOptions
{
AutomaticLayout = true,
Language = "powershell",
Value = Script.Contents,
Theme = "vs-dark",
};
}
Script Script = new();
bool IsDebuggable =>
Script.Type == ScriptType.Package;
protected override async Task OnInitializedAsync()
{
if (Options.ScriptId != Guid.Empty)
Script = await ScriptService.GetAsync(Options.ScriptId);
else if (Options.GameId != Guid.Empty)
{
Script = new Script
{
GameId = Options.GameId
};
_archive = await ArchiveService.GetLatestArchiveAsync(a => a.GameId == Options.GameId);
}
else if (Options.RedistributableId != Guid.Empty)
{
Script = new Script()
{
RedistributableId = Options.RedistributableId
};
_archive = await ArchiveService.GetLatestArchiveAsync(a => a.RedistributableId == Options.RedistributableId);
}
else if (Options.ServerId != Guid.Empty)
{
Script = new Script()
{
ServerId = Options.ServerId
};
var server = await ServerService.GetAsync(Options.ServerId.Value);
_archive = await ArchiveService.GetLatestArchiveAsync(a => a.GameId == server.GameId);
}
else if (Options.ToolId != Guid.Empty)
{
Script = new Script()
{
ToolId = Options.ToolId
};
_archive = await ArchiveService.GetLatestArchiveAsync(a => a.ToolId == Options.ToolId);
}
Snippets = ScriptService.GetSnippets();
}
public override async Task OnFeedbackOkAsync(ModalClosingEventArgs args)
{
var success = await Save();
if (success)
await base.OkCancelRefWithResult!.OnOk(Script);
else
args.Reject();
}
async Task<bool> Save()
{
try
{
if (Form.Validate())
{
if (Script.Id == Guid.Empty)
Script = await ScriptService.AddAsync(Script);
else
Script = await ScriptService.UpdateAsync(Script);
MessageService.Success("Script saved!");
return true;
}
return false;
}
catch (Exception ex)
{
MessageService.Error("Script could not be saved!");
Logger.LogError(ex, "Script could not be saved!");
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()
{
var modalOptions = new ModalOptions()
{
Title = "Choose Reference",
Maximizable = false,
DefaultMaximized = true,
Closable = true,
OkText = "Insert File Path"
};
var browserOptions = new FilePickerOptions()
{
ArchiveId = _archive?.Id ?? Guid.Empty,
Select = true,
Multiple = false
};
var modalRef = await ModalService.CreateModalAsync<FilePickerDialog, FilePickerOptions, IEnumerable<IFileManagerEntry>>(modalOptions, browserOptions);
modalRef.OnOk = (results) =>
{
var path = results.FirstOrDefault().Path;
InsertText($"$InstallDirectory\\{path.Replace('/', '\\')}");
StateHasChanged();
return Task.CompletedTask;
};
}
async Task InsertText(string text)
{
var line = await Editor.GetPosition();
var range = new BlazorMonaco.Range(line.LineNumber, 1, line.LineNumber, 1);
var currentSelections = await Editor.GetSelections();
await Editor.ExecuteEdits("ScriptEditor", new List<IdentifiedSingleEditOperation>()
{
new IdentifiedSingleEditOperation
{
Range = range,
Text = text,
ForceMoveMarkers = true
}
}, currentSelections);
}
async Task InsertSnippet(Snippet snippet)
{
await InsertText(snippet.Content);
}
}