Improved local plugin handling (share, import, delete) (#900)
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Sync Flatpak repo (push) Blocked by required conditions
Build and Release / Collect Flatpak artifacts (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions

This commit is contained in:
Thorsten Sommer authored and GitHub committed 2026-08-09 19:01:58 +02:00
1 parent 0eb747b386
commit 6e143aafaa
47 files changed
+3108 -1480

No files matched your search

@@ -29,7 +29,7 @@ public partial class AssistantPluginEditorDialog : MSGComponentBase
private IMudDialogInstance MudDialog { get; set; } = null!;
[Inject]
private AssistantPluginInstallService AssistantPluginInstallService { get; init; } = null!;
private PluginInstallService PluginInstallService { get; init; } = null!;
[Parameter]
public Guid PluginId { get; set; }
@@ -105,7 +105,7 @@ public partial class AssistantPluginEditorDialog : MSGComponentBase
try
{
var editedLua = await this.codeEditor.GetCodeAsync();
var result = await this.AssistantPluginInstallService.UpdateInstalledAssistantAsync(this.plugin, editedLua, CancellationToken.None);
var result = await this.PluginInstallService.UpdateInstalledAssistantAsync(this.plugin, editedLua, CancellationToken.None);
if (!result.Success)
{
LOGGER.LogError($"Failed to update assistant plugin '{result.PluginName}' ({result.PluginId}) in '{result.PluginDirectory}' with issue '{result.Issue}'.");
@@ -23,7 +23,7 @@ public partial class AssistantPluginRevisionDialog : MSGComponentBase
private AssistantPluginGenerationService AssistantPluginGenerationService { get; init; } = null!;
[Inject]
private AssistantPluginInstallService AssistantPluginInstallService { get; init; } = null!;
private PluginInstallService PluginInstallService { get; init; } = null!;
[Inject]
private AssistantPluginAuditService AssistantPluginAuditService { get; init; } = null!;
@@ -144,7 +144,7 @@ public partial class AssistantPluginRevisionDialog : MSGComponentBase
if (this.availablePlugin is null)
return;
this.revisionCheckResult = await this.AssistantPluginInstallService.CheckInstalledAssistantUpdateAsync(this.availablePlugin, this.revisedLua, CancellationToken.None);
this.revisionCheckResult = await this.PluginInstallService.CheckInstalledAssistantUpdateAsync(this.availablePlugin, this.revisedLua, CancellationToken.None);
if (this.revisionCheckResult.Success)
return;
@@ -168,7 +168,7 @@ public partial class AssistantPluginRevisionDialog : MSGComponentBase
try
{
var result = await this.AssistantPluginInstallService.UpdateInstalledAssistantAsync(this.availablePlugin, this.revisedLua, CancellationToken.None);
var result = await this.PluginInstallService.UpdateInstalledAssistantAsync(this.availablePlugin, this.revisedLua, CancellationToken.None);
if (!result.Success)
{
LOGGER.LogError($"Failed to revise assistant plugin '{result.PluginName}' ({result.PluginId}) in '{result.PluginDirectory}' with issue '{result.Issue}'.");
@@ -0,0 +1,42 @@
@inherits MSGComponentBase
<MudDialog>
<DialogContent>
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
@(string.Format(T("Do you really want to delete the configuration plugin '{0}'? This permanently deletes its local plugin files."), this.PluginName))
</MudJustifiedText>
@if (this.Consequences.Count > 0)
{
<MudJustifiedText Typo="Typo.body1" Class="mb-1">
@T("This also removes everything the configuration plugin had set up:")
</MudJustifiedText>
<MudList T="string" Class="mb-3">
@foreach (var consequence in this.Consequences)
{
<MudListItem T="string" Icon="@Icons.Material.Filled.RemoveCircleOutline" IconColor="Color.Error">
@consequence
</MudListItem>
}
</MudList>
}
else
{
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
@T("The configuration plugin is not running, so we cannot tell what it had set up. Anything it configured will be removed as well.")
</MudJustifiedText>
}
<MudJustifiedText Typo="Typo.body2">
@T("You can install the plugin again later, but any changes you made to its settings are lost.")
</MudJustifiedText>
</DialogContent>
<DialogActions>
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">
@T("No")
</MudButton>
<MudButton OnClick="@this.Confirm" Variant="Variant.Filled" Color="Color.Error">
@T("Yes, delete it")
</MudButton>
</DialogActions>
</MudDialog>
@@ -0,0 +1,69 @@
using AIStudio.Components;
using AIStudio.Tools.Services;
using Microsoft.AspNetCore.Components;
namespace AIStudio.Dialogs;
/// <summary>
/// Asks the user whether a local configuration plugin may be deleted, and shows what the deletion
/// takes with it.
/// </summary>
public partial class ConfigurationPluginDeleteDialog : MSGComponentBase
{
[CascadingParameter]
private IMudDialogInstance MudDialog { get; set; } = null!;
/// <summary>
/// The name of the configuration plugin about to be deleted.
/// </summary>
[Parameter]
public string PluginName { get; set; } = string.Empty;
/// <summary>
/// What the deletion removes besides the plugin directory.
/// </summary>
[Parameter]
public ConfigurationPluginDeleteSummary Summary { get; set; } = ConfigurationPluginDeleteSummary.EMPTY;
private List<string> Consequences => this.BuildConsequences();
/// <summary>
/// Turns the summary into the lines shown to the user. Only what is actually affected is listed,
/// so the dialog stays short for a configuration plugin that just locks a single setting.
/// </summary>
private List<string> BuildConsequences()
{
var consequences = new List<string>();
var summary = this.Summary;
Add(summary.LlmProviders, this.T("{0} LLM provider"), this.T("{0} LLM providers"));
Add(summary.TranscriptionProviders, this.T("{0} transcription provider"), this.T("{0} transcription providers"));
Add(summary.EmbeddingProviders, this.T("{0} embedding provider"), this.T("{0} embedding providers"));
Add(summary.ChatTemplates, this.T("{0} chat template"), this.T("{0} chat templates"));
Add(summary.Profiles, this.T("{0} profile"), this.T("{0} profiles"));
Add(summary.DocumentAnalysisPolicies, this.T("{0} document analysis policy"), this.T("{0} document analysis policies"));
Add(summary.MandatoryInfos, this.T("{0} mandatory information"), this.T("{0} mandatory informations"));
Add(summary.Introductions, this.T("{0} introduction on the welcome page"), this.T("{0} introductions on the welcome page"));
// Data sources are called out separately: removing them also deletes their credentials from
// the operating system's keychain, which the user cannot undo by reinstalling the plugin.
Add(summary.DataSources,
this.T("{0} data source, including its credentials in your operating system's keychain"),
this.T("{0} data sources, including their credentials in your operating system's keychain"));
Add(summary.LockedSettings, this.T("{0} setting returns to its default value"), this.T("{0} settings return to their default values"));
return consequences;
void Add(int count, string singular, string plural)
{
if (count > 0)
consequences.Add(string.Format(count == 1 ? singular : plural, count));
}
}
private void Cancel() => this.MudDialog.Cancel();
private void Confirm() => this.MudDialog.Close(DialogResult.Ok(true));
}
@@ -35,6 +35,51 @@
}
</MudPaper>
@if (this.Preview.ConfigurationSummary is { HasAnyContent: true } configurationSummary)
{
<MudAlert Severity="Severity.Warning" Variant="Variant.Outlined" Class="mb-3">
@T("A configuration takes effect right after the installation and has no on/off switch. Please check what it sets up:")
</MudAlert>
@if (configurationSummary.Destinations.Count > 0)
{
<MudSimpleTable Dense="@true" Striped="@true" Class="mb-3">
<thead>
<tr>
<th>@T("Sends data to")</th>
<th>@T("Name")</th>
<th>@T("Destination")</th>
</tr>
</thead>
<tbody>
@foreach (var destination in configurationSummary.Destinations)
{
<tr>
<td>@this.DestinationTypeLabel(destination.Type)</td>
<td>@destination.Name</td>
<td><strong>@destination.Endpoint</strong></td>
</tr>
}
</tbody>
</MudSimpleTable>
}
@if (this.ConfigurationContents.Count > 0)
{
<MudText Typo="Typo.body2" Class="mb-1">
@T("It also brings:")
</MudText>
<MudList T="string" Class="mb-3">
@foreach (var content in this.ConfigurationContents)
{
<MudListItem T="string" Icon="@Icons.Material.Filled.AddCircleOutline">
@content
</MudListItem>
}
</MudList>
}
}
@if (!string.IsNullOrWhiteSpace(this.Preview.Plugin.DeprecationMessage))
{
<MudAlert Severity="Severity.Warning" Variant="Variant.Outlined" Class="mb-3">
@@ -41,6 +41,48 @@ public partial class PluginImportDialog : MSGComponentBase
? string.Join(", ", this.Preview.Plugin.Authors)
: this.T("Unknown");
/// <summary>
/// Names the kind of a destination a configuration plugin brings.
/// </summary>
private string DestinationTypeLabel(PluginConfigurationObjectType objectType) => objectType switch
{
PluginConfigurationObjectType.LLM_PROVIDER => this.T("LLM provider"),
PluginConfigurationObjectType.EMBEDDING_PROVIDER => this.T("Embedding provider"),
PluginConfigurationObjectType.TRANSCRIPTION_PROVIDER => this.T("Transcription provider"),
PluginConfigurationObjectType.DATA_SOURCE => this.T("Data source"),
_ => this.T("Unknown"),
};
/// <summary>
/// Everything a configuration plugin brings besides its providers and data sources. Only what is
/// actually there gets listed, so the dialog stays short for a small configuration.
/// </summary>
private List<string> ConfigurationContents
{
get
{
var contents = new List<string>();
if (this.Preview.ConfigurationSummary is not { } summary)
return contents;
Add(summary.DeclaredSettings, this.T("{0} setting it takes control of"), this.T("{0} settings it takes control of"));
Add(summary.ChatTemplates, this.T("{0} chat template"), this.T("{0} chat templates"));
Add(summary.Profiles, this.T("{0} profile"), this.T("{0} profiles"));
Add(summary.DocumentAnalysisPolicies, this.T("{0} document analysis policy"), this.T("{0} document analysis policies"));
Add(summary.MandatoryInfos, this.T("{0} mandatory information you have to accept before using AI Studio"), this.T("{0} mandatory information you have to accept before using AI Studio"));
Add(summary.Introductions, this.T("{0} introduction on the welcome page"), this.T("{0} introductions on the welcome page"));
return contents;
void Add(int count, string singular, string plural)
{
if (count > 0)
contents.Add(string.Format(count == 1 ? singular : plural, count));
}
}
}
private void Cancel() => this.MudDialog.Cancel();
private void Confirm() => this.MudDialog.Close(DialogResult.Ok(true));