Make the workspace optional when building a chat launcher

This commit is contained in:
Thorsten Sommer committed 2026-09-14 09:20:55 +02:00
1 parent 3548b1d44d
commit d25bdb35b7
6 files changed
+27 -27

No files matched your search

@@ -23,7 +23,7 @@
</MudField> </MudField>
<MudJustifiedText Typo="Typo.body2" Class="mb-3"> <MudJustifiedText Typo="Typo.body2" Class="mb-3">
@(this.createChatLauncher @(this.createChatLauncher
? T("The direct chat launcher tile has no input form of its own. It opens a new chat right away, in the workspace you name below and with the provider, profile, chat template, and data sources you select there.") ? T("The direct chat launcher tile has no input form of its own. It opens a new chat right away, with the provider, profile, chat template, and data sources you select below. Name a workspace for that chat, or leave the workspace empty to open a disappearing chat.")
: T("The assistant asks users for input through a form and builds its own prompt from it.")) : T("The assistant asks users for input through a form and builds its own prompt from it."))
</MudJustifiedText> </MudJustifiedText>
@if (this.createChatLauncher) @if (this.createChatLauncher)
@@ -39,8 +39,7 @@
@bind-ProfileId="@this.launcherProfileId" @bind-ProfileId="@this.launcherProfileId"
@bind-ChatTemplateId="@this.launcherChatTemplateId" @bind-ChatTemplateId="@this.launcherChatTemplateId"
@bind-DataSourceIds="@this.launcherDataSourceIds" @bind-DataSourceIds="@this.launcherDataSourceIds"
@bind-ToolIds="@this.launcherToolIds" @bind-ToolIds="@this.launcherToolIds"/>
ValidateWorkspaceName="@this.ValidateLauncherWorkspaceName"/>
</MudPaper> </MudPaper>
} }
@@ -375,14 +375,6 @@ public partial class AssistantBuilder : AssistantBaseCore<NoSettingsPanel>
return null; return null;
} }
private string? ValidateLauncherWorkspaceName(string workspaceName)
{
if (this.createChatLauncher && string.IsNullOrWhiteSpace(workspaceName))
return T("Please select or enter a workspace name for the chat launcher.");
return null;
}
private async Task GenerateAssistantSpec() private async Task GenerateAssistantSpec()
{ {
await this.Form!.Validate(); await this.Form!.Validate();
@@ -589,7 +581,8 @@ public partial class AssistantBuilder : AssistantBaseCore<NoSettingsPanel>
// The description stays required for both kinds of assistant. Users who only want a tile // The description stays required for both kinds of assistant. Users who only want a tile
// usually flip the switch before typing anything, so the Builder offers a starting point they // usually flip the switch before typing anything, so the Builder offers a starting point they
// can edit or replace. The workspace is picked after that, hence the suggestion is refreshed // can edit or replace. The workspace is picked after that, hence the suggestion is refreshed
// whenever the workspace changes: // whenever the workspace changes — including when it is cleared again, which turns the tile
// into one that opens a disappearing chat:
// //
private void SuggestLauncherDescription() private void SuggestLauncherDescription()
{ {
@@ -597,8 +590,9 @@ public partial class AssistantBuilder : AssistantBaseCore<NoSettingsPanel>
return; return;
var suggestion = T("Create a tile that opens a preconfigured chat directly, without an input form of its own."); var suggestion = T("Create a tile that opens a preconfigured chat directly, without an input form of its own.");
if (!string.IsNullOrWhiteSpace(this.launcherWorkspaceName)) suggestion = string.IsNullOrWhiteSpace(this.launcherWorkspaceName)
suggestion = $"{suggestion} {string.Format(T("Workspace: {0}"), this.launcherWorkspaceName.Trim())}"; ? $"{suggestion} {T("The chat belongs to no workspace and disappears again.")}"
: $"{suggestion} {string.Format(T("Workspace: {0}"), this.launcherWorkspaceName.Trim())}";
this.assistantDescription = suggestion; this.assistantDescription = suggestion;
this.descriptionSuggestion = suggestion; this.descriptionSuggestion = suggestion;
@@ -10,7 +10,15 @@
</MudSelect> </MudSelect>
} }
<MudTextField T="string" Text="@this.WorkspaceName" TextChanged="@this.SetWorkspaceName" Validation="@this.ValidateWorkspaceName" AdornmentIcon="@Icons.Material.Filled.CreateNewFolder" Adornment="Adornment.Start" IconSize="Size.Small" Label="@T("Workspace name")" HelperText="@T("Choose an existing workspace or enter a name that should be created when the launcher is opened.")" Variant="Variant.Outlined" Margin="Margin.Dense" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/> <MudTextField T="string" Text="@this.WorkspaceName" TextChanged="@this.SetWorkspaceName" AdornmentIcon="@(this.OpensTemporaryChat ? Icons.Material.Filled.Timer : Icons.Material.Filled.CreateNewFolder)" Adornment="Adornment.Start" IconSize="Size.Small" Label="@T("Workspace name (Optional)")" HelperText="@T("Choose an existing workspace or enter a name that should be created when the launcher is opened.")" Variant="Variant.Outlined" Margin="Margin.Dense" Class="mb-1" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
@* The tile behaves differently with and without a workspace, so the form says which of the two is
chosen right now instead of only explaining that both are possible. *@
<MudText Typo="Typo.body2" Class="mud-text-secondary mb-3">
@(this.OpensTemporaryChat
? T("Without a workspace, the tile opens a disappearing chat: it belongs to no workspace and is deleted according to your workspace maintenance settings.")
: T("The tile opens its chat in this workspace and creates the workspace when it does not exist yet."))
</MudText>
<MudSelect T="string" Value="@this.ProviderId" ValueChanged="@this.SetProviderId" Label="@T("Chat provider")" Variant="Variant.Outlined" Margin="Margin.Dense" Class="mb-3 rounded-lg" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.SmartToy"> <MudSelect T="string" Value="@this.ProviderId" ValueChanged="@this.SetProviderId" Label="@T("Chat provider")" Variant="Variant.Outlined" Margin="Margin.Dense" Class="mb-3 rounded-lg" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.SmartToy">
<MudSelectItem T="string" Value="@string.Empty">@T("Use chat default")</MudSelectItem> <MudSelectItem T="string" Value="@string.Empty">@T("Use chat default")</MudSelectItem>
@foreach (var provider in this.SettingsManager.GetConfidentProviders(Components.CHAT)) @foreach (var provider in this.SettingsManager.GetConfidentProviders(Components.CHAT))
@@ -3,8 +3,9 @@ using Microsoft.AspNetCore.Components;
namespace AIStudio.Components; namespace AIStudio.Components;
/// <summary> /// <summary>
/// The selection a direct chat launcher needs: the workspace its chat is created in, and the /// The selection a direct chat launcher needs: the workspace its chat is created in — or no
/// provider, profile, chat template, and data sources that chat starts with. /// workspace, for a disappearing chat — and the provider, profile, chat template, and data sources
/// that chat starts with.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// The Assistant Builder uses this form to describe a launcher it is about to generate, while the /// The Assistant Builder uses this form to describe a launcher it is about to generate, while the
@@ -15,7 +16,8 @@ public partial class DirectChatLauncherForm : MSGComponentBase
{ {
/// <summary> /// <summary>
/// The name of the workspace the launcher opens its chat in. The workspace is created when it /// The name of the workspace the launcher opens its chat in. The workspace is created when it
/// does not exist yet, hence this is a free-text field and not a workspace ID. /// does not exist yet, hence this is a free-text field and not a workspace ID. An empty name is
/// a choice of its own: the launcher then opens a disappearing chat.
/// </summary> /// </summary>
[Parameter] [Parameter]
public string WorkspaceName { get; set; } = string.Empty; public string WorkspaceName { get; set; } = string.Empty;
@@ -75,11 +77,9 @@ public partial class DirectChatLauncherForm : MSGComponentBase
public EventCallback<HashSet<string>> ToolIdsChanged { get; set; } public EventCallback<HashSet<string>> ToolIdsChanged { get; set; }
/// <summary> /// <summary>
/// Validates the workspace name. The hosts differ here: the Builder requires a name only while /// Whether the launcher currently describes a chat without a workspace.
/// its launcher switch is on, whereas the settings dialog always requires one.
/// </summary> /// </summary>
[Parameter] private bool OpensTemporaryChat => string.IsNullOrWhiteSpace(this.WorkspaceName);
public Func<string, string?>? ValidateWorkspaceName { get; set; }
private IReadOnlyList<WorkspaceTreeWorkspace> availableWorkspaces = []; private IReadOnlyList<WorkspaceTreeWorkspace> availableWorkspaces = [];
@@ -32,8 +32,7 @@
@bind-ProfileId="@this.profileId" @bind-ProfileId="@this.profileId"
@bind-ChatTemplateId="@this.chatTemplateId" @bind-ChatTemplateId="@this.chatTemplateId"
@bind-DataSourceIds="@this.dataSourceIds" @bind-DataSourceIds="@this.dataSourceIds"
@bind-ToolIds="@this.toolIds" @bind-ToolIds="@this.toolIds"/>
ValidateWorkspaceName="@this.ValidateWorkspaceName"/>
</MudPaper> </MudPaper>
</MudForm> </MudForm>
@@ -152,7 +152,9 @@ public partial class DirectChatLauncherSettingsDialog : MSGComponentBase
// //
// An empty selection means "use the chat defaults" and is left out of the plugin, whereas // An empty selection means "use the chat defaults" and is left out of the plugin, whereas
// the empty GUID explicitly selects no profile or no chat template: // the empty GUID explicitly selects no profile or no chat template. Clearing the workspace
// name is a change of its own: the tile then opens a disappearing chat, and the writer
// switches the launch behavior accordingly.
// //
return new( return new(
this.workspaceName.Trim(), this.workspaceName.Trim(),
@@ -248,8 +250,6 @@ public partial class DirectChatLauncherSettingsDialog : MSGComponentBase
private string? ValidateDescription(string value) => string.IsNullOrWhiteSpace(value) ? T("Please provide a description for this tile.") : null; private string? ValidateDescription(string value) => string.IsNullOrWhiteSpace(value) ? T("Please provide a description for this tile.") : null;
private string? ValidateWorkspaceName(string value) => string.IsNullOrWhiteSpace(value) ? T("Please select or enter a workspace name for this tile.") : null;
private void Cancel() => this.MudDialog.Cancel(); private void Cancel() => this.MudDialog.Cancel();
private static Guid? ParseOptionalGuid(string value) => Guid.TryParse(value, out var parsed) ? parsed : null; private static Guid? ParseOptionalGuid(string value) => Guid.TryParse(value, out var parsed) ? parsed : null;