mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-01 22:07:20 +00:00
Changed the ERI Server assistant to save its specification through a save dialog
This commit is contained in:
4 files changed
+87
-4
No files matched your search
@@ -116,8 +116,8 @@ else
|
||||
</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
<MudButton Variant="Variant.Outlined" Size="Size.Small" Disabled="@this.IsSpecificationDownloadDisabled" Href="@this.selectedERIVersion.SpecificationURL()" Target="_blank">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Link" Class="mr-2"/> @T("Download specification")
|
||||
<MudButton Variant="Variant.Outlined" Size="Size.Small" Disabled="@this.IsSpecificationSavingDisabled" OnClick="@this.SaveSpecificationAsync">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Save" Class="mr-2"/> @T("Save specification")
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using AIStudio.Dialogs;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
using AIStudio.Tools.Rust;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
@@ -375,9 +376,43 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
||||
private bool IsERIInputDisabled => this.IsNoneERIServerSelected || this.IsProcessing;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the selected ERI specification cannot be downloaded.
|
||||
/// Gets whether the selected ERI specification cannot be saved.
|
||||
/// </summary>
|
||||
private bool IsSpecificationDownloadDisabled => !this.selectedERIVersion.WasSpecificationSelected() || this.IsERIInputDisabled;
|
||||
private bool IsSpecificationSavingDisabled => !this.selectedERIVersion.WasSpecificationSelected() || this.IsERIInputDisabled;
|
||||
|
||||
/// <summary>
|
||||
/// Lets the user save the selected ERI specification, a copy of the one the app carries.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The app server answers the app window only, so a link to the file would fail in the browser
|
||||
/// of the system. The copy also matches the installed version of AI Studio.
|
||||
/// </remarks>
|
||||
private async Task SaveSpecificationAsync()
|
||||
{
|
||||
var specificationPath = this.selectedERIVersion.SpecificationURL();
|
||||
var response = await this.RustService.SaveFile(T("Save the ERI specification"), [FileTypes.JSON], Path.GetFileName(specificationPath));
|
||||
if (response.UserCancelled)
|
||||
return;
|
||||
|
||||
await using var feedback = new FileExportFeedback();
|
||||
feedback.ShowRunning(response.SaveFilePath);
|
||||
try
|
||||
{
|
||||
if (await this.WebRootFiles.CopyToAsync(specificationPath, response.SaveFilePath))
|
||||
{
|
||||
await feedback.SucceededAsync(Icons.Material.Filled.CheckCircle, T("The ERI specification was saved."), response.SaveFilePath, true);
|
||||
return;
|
||||
}
|
||||
|
||||
this.Logger.LogError("The app carries no ERI specification at '{SpecificationPath}'.", specificationPath);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
this.Logger.LogError(exception, "Failed to save the ERI specification {ERIVersion}.", this.selectedERIVersion);
|
||||
}
|
||||
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Cancel, T("The ERI specification could not be saved.")));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the generated-code target directory selection should be disabled.
|
||||
|
||||
@@ -34,4 +34,22 @@ public sealed class WebRootFiles
|
||||
using var reader = new StreamReader(fileStream);
|
||||
return await reader.ReadToEndAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies a file below wwwroot to the given place, such as one the user chose to save it to.
|
||||
/// </summary>
|
||||
/// <param name="path">The path below wwwroot, such as <c>specs/eri/v1.json</c>.</param>
|
||||
/// <param name="destinationPath">Where to write the copy. An existing file there gets replaced.</param>
|
||||
/// <returns>True when the copy was written, false when there is no such file below wwwroot.</returns>
|
||||
public async Task<bool> CopyToAsync(string path, string destinationPath)
|
||||
{
|
||||
var file = this.FileProvider.GetFileInfo(path);
|
||||
if (!file.Exists)
|
||||
return false;
|
||||
|
||||
await using var source = file.CreateReadStream();
|
||||
await using var destination = File.Create(destinationPath);
|
||||
await source.CopyToAsync(destination);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -40,4 +40,34 @@ public sealed class WebRootFilesTests
|
||||
var text = await new WebRootFiles().ReadTextAsync("changelog/does-not-exist.md");
|
||||
Assert.That(text, Is.Empty);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(SpecificationVersions))]
|
||||
public async Task EveryERISpecificationCanBeSaved(ERIVersion version)
|
||||
{
|
||||
var webRootFiles = new WebRootFiles();
|
||||
var destinationPath = Path.Combine(Path.GetTempPath(), $"eri-specification-{Guid.NewGuid():N}.json");
|
||||
try
|
||||
{
|
||||
Assert.That(await webRootFiles.CopyToAsync(version.SpecificationURL(), destinationPath), Is.True);
|
||||
Assert.That(await File.ReadAllTextAsync(destinationPath), Is.EqualTo(await version.ReadSpecification(webRootFiles)));
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(destinationPath);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task AMissingFileIsNotCopied()
|
||||
{
|
||||
var destinationPath = Path.Combine(Path.GetTempPath(), $"eri-specification-{Guid.NewGuid():N}.json");
|
||||
|
||||
var copied = await new WebRootFiles().CopyToAsync("specs/eri/does-not-exist.json", destinationPath);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(copied, Is.False);
|
||||
Assert.That(File.Exists(destinationPath), Is.False);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user