diff --git a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua index 66064dcb..a2d726f0 100644 --- a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua +++ b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua @@ -12973,20 +12973,32 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::PROVIDERVALIDATION::T818893091"] = -- Are you sure you want to delete the chat '{0}' in the workspace '{1}'? UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T1016188706"] = "Are you sure you want to delete the chat '{0}' in the workspace '{1}'?" +-- Copy Chat +UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T1192756314"] = "Copy Chat" + -- Unnamed workspace UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T1307384014"] = "Unnamed workspace" +-- Copy +UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T1703884388"] = "Copy" + -- Delete Chat UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T2244038752"] = "Delete Chat" +-- Please enter a chat name. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T2301651387"] = "Please enter a chat name." + -- Are you sure you want to delete the temporary chat '{0}'? UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T3043761007"] = "Are you sure you want to delete the temporary chat '{0}'?" -- Unnamed chat UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T3310482275"] = "Unnamed chat" +-- Please enter a name for the copy of your chat '{0}': +UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T3323676840"] = "Please enter a name for the copy of your chat '{0}':" + -- Copy of {0} UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T3365678931"] = "Copy of {0}" --- Empty chat -UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T4019509364"] = "Empty chat" +-- Chat Name +UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T3891063690"] = "Chat Name" diff --git a/app/MindWork AI Studio/Components/Workspaces.razor.cs b/app/MindWork AI Studio/Components/Workspaces.razor.cs index 73f85291..801b197c 100644 --- a/app/MindWork AI Studio/Components/Workspaces.razor.cs +++ b/app/MindWork AI Studio/Components/Workspaces.razor.cs @@ -819,7 +819,9 @@ public partial class Workspaces : MSGComponentBase } var sourceChat = isCopyOfOpenChat ? openChat! : chat; - var copy = await WorkspaceBehaviour.CopyChatAsync(sourceChat); + var copy = await WorkspaceBehaviour.CopyChatAsync(this.DialogService, sourceChat); + if (copy is null) + return; await this.LoadTreeItemsAsync(startPrefetch: false); this.CurrentChatThread = copy; diff --git a/app/MindWork AI Studio/Tools/WorkspaceBehaviour.cs b/app/MindWork AI Studio/Tools/WorkspaceBehaviour.cs index 519cff7c..6a9c14da 100644 --- a/app/MindWork AI Studio/Tools/WorkspaceBehaviour.cs +++ b/app/MindWork AI Studio/Tools/WorkspaceBehaviour.cs @@ -789,23 +789,46 @@ public static class WorkspaceBehaviour } /// - /// Copies a chat into a new chat of the same workspace, including its managed transcript files. + /// Copies a chat into a new chat of the same workspace, including its managed transcript files, + /// after asking the user for the name of the copy. /// + /// Used to ask for the name. /// The chat to copy. Its own files and state stay untouched. - /// The persisted copy. + /// The persisted copy. Null when the user canceled the question, in which case nothing was copied. /// + /// This is the one place that asks for the name of a copy, so every way of copying a chat + /// suggests the same name and words the question the same way.

+ /// /// The copy is written before it is returned, so the caller may open it right away. Runtime-only - /// state of the source is not part of the copy: it is rebuilt when the copy gets loaded. + /// state of the source is not part of the copy: it is rebuilt when the copy gets loaded. When + /// the copy cannot be written, nothing of it stays behind and the error reaches the caller. ///
- public static async Task CopyChatAsync(ChatThread sourceChat) + public static async Task CopyChatAsync(IDialogService dialogService, ChatThread sourceChat) { + var sourceName = string.IsNullOrWhiteSpace(sourceChat.Name) ? TB("Unnamed chat") : sourceChat.Name; + var dialogParameters = new DialogParameters + { + { x => x.Message, string.Format(TB("Please enter a name for the copy of your chat '{0}':"), sourceName) }, + { x => x.InputHeaderText, TB("Chat Name") }, + { x => x.UserInput, string.Format(TB("Copy of {0}"), sourceName) }, + { x => x.ConfirmText, TB("Copy") }, + { x => x.ConfirmColor, Color.Info }, + { x => x.AllowEmptyInput, false }, + { x => x.EmptyInputErrorMessage, TB("Please enter a chat name.") }, + }; + + var dialogReference = await dialogService.ShowAsync(TB("Copy Chat"), dialogParameters, Dialogs.DialogOptions.FULLSCREEN); + var dialogResult = await dialogReference.Result; + if (dialogResult is null || dialogResult.Canceled) + return null; + var serializedChat = JsonSerializer.Serialize(sourceChat, JSON_OPTIONS); var copiedChat = JsonSerializer.Deserialize(serializedChat, JSON_OPTIONS) ?? throw new InvalidOperationException("The chat could not be copied."); copiedChat = copiedChat with { ChatId = Guid.NewGuid(), - Name = string.Format(TB("Copy of {0}"), string.IsNullOrWhiteSpace(sourceChat.Name) ? TB("Empty chat") : sourceChat.Name), + Name = (dialogResult.Data as string)!, }; var targetDirectory = GetChatDirectory(copiedChat.WorkspaceId, copiedChat.ChatId);