diff --git a/app/MindWork AI Studio/Components/Blocks/Workspaces.razor.cs b/app/MindWork AI Studio/Components/Blocks/Workspaces.razor.cs index 63c77a5c..2b4fb82d 100644 --- a/app/MindWork AI Studio/Components/Blocks/Workspaces.razor.cs +++ b/app/MindWork AI Studio/Components/Blocks/Workspaces.razor.cs @@ -248,28 +248,32 @@ public partial class Workspaces : ComponentBase return null; } - private async Task DeleteChat(string? chatPath) + public async Task DeleteChat(string? chatPath, bool askForConfirmation = true, bool unloadChat = true) { var chat = await this.LoadChat(chatPath, false); if (chat is null) return; - - var workspaceName = await this.LoadWorkspaceName(chat.WorkspaceId); - var dialogParameters = new DialogParameters + + if (askForConfirmation) { - { "Message", (chat.WorkspaceId == Guid.Empty) switch - { - true => $"Are you sure you want to delete the temporary chat '{chat.Name}'?", - false => $"Are you sure you want to delete the chat '{chat.Name}' in the workspace '{workspaceName}'?", - } - }, - }; - - var dialogReference = await this.DialogService.ShowAsync("Delete Chat", dialogParameters, DialogOptions.FULLSCREEN); - var dialogResult = await dialogReference.Result; - if (dialogResult.Canceled) - return; - + var workspaceName = await this.LoadWorkspaceName(chat.WorkspaceId); + var dialogParameters = new DialogParameters + { + { + "Message", (chat.WorkspaceId == Guid.Empty) switch + { + true => $"Are you sure you want to delete the temporary chat '{chat.Name}'?", + false => $"Are you sure you want to delete the chat '{chat.Name}' in the workspace '{workspaceName}'?", + } + }, + }; + + var dialogReference = await this.DialogService.ShowAsync("Delete Chat", dialogParameters, DialogOptions.FULLSCREEN); + var dialogResult = await dialogReference.Result; + if (dialogResult.Canceled) + return; + } + string chatDirectory; if (chat.WorkspaceId == Guid.Empty) chatDirectory = Path.Join(SettingsManager.DataDirectory, "tempChats", chat.ChatId.ToString()); @@ -279,7 +283,7 @@ public partial class Workspaces : ComponentBase Directory.Delete(chatDirectory, true); await this.LoadTreeItems(); - if(this.CurrentChatThread?.ChatId == chat.ChatId) + if(unloadChat && this.CurrentChatThread?.ChatId == chat.ChatId) { this.CurrentChatThread = null; await this.CurrentChatThreadChanged.InvokeAsync(this.CurrentChatThread); diff --git a/app/MindWork AI Studio/Components/CommonDialogs/WorkspaceSelectionDialog.razor b/app/MindWork AI Studio/Components/CommonDialogs/WorkspaceSelectionDialog.razor new file mode 100644 index 00000000..3927b6c9 --- /dev/null +++ b/app/MindWork AI Studio/Components/CommonDialogs/WorkspaceSelectionDialog.razor @@ -0,0 +1,15 @@ + + + @this.Message + + @foreach (var (workspaceName, workspaceId) in this.workspaces) + { + + } + + + + Cancel + @this.ConfirmText + + \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/CommonDialogs/WorkspaceSelectionDialog.razor.cs b/app/MindWork AI Studio/Components/CommonDialogs/WorkspaceSelectionDialog.razor.cs new file mode 100644 index 00000000..821d3d67 --- /dev/null +++ b/app/MindWork AI Studio/Components/CommonDialogs/WorkspaceSelectionDialog.razor.cs @@ -0,0 +1,60 @@ +using System.Text; + +using AIStudio.Settings; + +using Microsoft.AspNetCore.Components; + +namespace AIStudio.Components.CommonDialogs; + +public partial class WorkspaceSelectionDialog : ComponentBase +{ + [CascadingParameter] + private MudDialogInstance MudDialog { get; set; } = null!; + + [Parameter] + public string Message { get; set; } = string.Empty; + + [Parameter] + public Guid SelectedWorkspace { get; set; } = Guid.Empty; + + [Parameter] + public string ConfirmText { get; set; } = "OK"; + + private readonly Dictionary workspaces = new(); + private object? selectedWorkspace; + + #region Overrides of ComponentBase + + protected override async Task OnInitializedAsync() + { + this.selectedWorkspace = this.SelectedWorkspace; + + // Get the workspace root directory: + var workspaceDirectories = Path.Join(SettingsManager.DataDirectory, "workspaces"); + if(!Directory.Exists(workspaceDirectories)) + { + await base.OnInitializedAsync(); + return; + } + + // Enumerate the workspace directories: + foreach (var workspaceDirPath in Directory.EnumerateDirectories(workspaceDirectories)) + { + // Read the `name` file: + var workspaceNamePath = Path.Join(workspaceDirPath, "name"); + var workspaceName = await File.ReadAllTextAsync(workspaceNamePath, Encoding.UTF8); + + // Add the workspace to the list: + this.workspaces.Add(workspaceName, Guid.Parse(Path.GetFileName(workspaceDirPath))); + } + + this.StateHasChanged(); + await base.OnInitializedAsync(); + } + + #endregion + + private void Cancel() => this.MudDialog.Cancel(); + + private void Confirm() => this.MudDialog.Close(DialogResult.Ok(this.selectedWorkspace is Guid workspaceId ? workspaceId : default)); +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/Pages/Chat.razor b/app/MindWork AI Studio/Components/Pages/Chat.razor index 6f005b61..a9f1aea1 100644 --- a/app/MindWork AI Studio/Components/Pages/Chat.razor +++ b/app/MindWork AI Studio/Components/Pages/Chat.razor @@ -48,7 +48,7 @@ - + diff --git a/app/MindWork AI Studio/Components/Pages/Chat.razor.cs b/app/MindWork AI Studio/Components/Pages/Chat.razor.cs index 7fc9d05a..17a9173d 100644 --- a/app/MindWork AI Studio/Components/Pages/Chat.razor.cs +++ b/app/MindWork AI Studio/Components/Pages/Chat.razor.cs @@ -62,7 +62,7 @@ public partial class Chat : ComponentBase, IAsyncDisposable private string InputLabel => this.IsProviderSelected ? $"Your Prompt (use selected instance '{this.selectedProvider.InstanceName}', provider '{this.selectedProvider.UsedProvider.ToName()}')" : "Select a provider first"; - private bool CanThreadBeSaved => this.IsProviderSelected && this.chatThread is not null && this.chatThread.Blocks.Count > 0; + private bool CanThreadBeSaved => this.chatThread is not null && this.chatThread.Blocks.Count > 0; private async Task SendMessage() { @@ -229,6 +229,46 @@ public partial class Chat : ComponentBase, IAsyncDisposable await this.inputField.Clear(); } + private async Task MoveChatToWorkspace() + { + if(this.chatThread is null) + return; + + if(this.workspaces is null) + return; + + var dialogParameters = new DialogParameters + { + { "Message", "Please select the workspace where you want to move the chat to." }, + { "SelectedWorkspace", this.chatThread?.WorkspaceId }, + { "ConfirmText", "Move chat" }, + }; + + var dialogReference = await this.DialogService.ShowAsync("Move Chat to Workspace", dialogParameters, DialogOptions.FULLSCREEN); + var dialogResult = await dialogReference.Result; + if (dialogResult.Canceled) + return; + + var workspaceId = dialogResult.Data is Guid id ? id : default; + if (workspaceId == Guid.Empty) + return; + + // Delete the chat from the current workspace or the temporary storage: + if (this.chatThread!.WorkspaceId == Guid.Empty) + { + // Case: The chat is stored in the temporary storage: + await this.workspaces.DeleteChat(Path.Join(SettingsManager.DataDirectory, "tempChats", this.chatThread.ChatId.ToString()), askForConfirmation: false, unloadChat: false); + } + else + { + // Case: The chat is stored in a workspace. + await this.workspaces.DeleteChat(Path.Join(SettingsManager.DataDirectory, "workspaces", this.chatThread.WorkspaceId.ToString(), this.chatThread.ChatId.ToString()), askForConfirmation: false, unloadChat: false); + } + + this.chatThread!.WorkspaceId = workspaceId; + await this.SaveThread(); + } + #region Implementation of IAsyncDisposable public async ValueTask DisposeAsync()