mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-03 02:27:36 +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>
|
</MudSelectItem>
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
<MudButton Variant="Variant.Outlined" Size="Size.Small" Disabled="@this.IsSpecificationDownloadDisabled" Href="@this.selectedERIVersion.SpecificationURL()" Target="_blank">
|
<MudButton Variant="Variant.Outlined" Size="Size.Small" Disabled="@this.IsSpecificationSavingDisabled" OnClick="@this.SaveSpecificationAsync">
|
||||||
<MudIcon Icon="@Icons.Material.Filled.Link" Class="mr-2"/> @T("Download specification")
|
<MudIcon Icon="@Icons.Material.Filled.Save" Class="mr-2"/> @T("Save specification")
|
||||||
</MudButton>
|
</MudButton>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using AIStudio.Dialogs;
|
|||||||
using AIStudio.Dialogs.Settings;
|
using AIStudio.Dialogs.Settings;
|
||||||
using AIStudio.Settings.DataModel;
|
using AIStudio.Settings.DataModel;
|
||||||
using AIStudio.Tools.AssistantSessions;
|
using AIStudio.Tools.AssistantSessions;
|
||||||
|
using AIStudio.Tools.Rust;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
@@ -375,9 +376,43 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
private bool IsERIInputDisabled => this.IsNoneERIServerSelected || this.IsProcessing;
|
private bool IsERIInputDisabled => this.IsNoneERIServerSelected || this.IsProcessing;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether the selected ERI specification cannot be downloaded.
|
/// Gets whether the selected ERI specification cannot be saved.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Gets whether the generated-code target directory selection should be disabled.
|
/// 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);
|
using var reader = new StreamReader(fileStream);
|
||||||
return await reader.ReadToEndAsync();
|
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");
|
var text = await new WebRootFiles().ReadTextAsync("changelog/does-not-exist.md");
|
||||||
Assert.That(text, Is.Empty);
|
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