AI-Studio/app/MindWork AI Studio/Components/Workspaces.razor.cs

614 lines
23 KiB
C#
Raw Normal View History

2026-03-05 17:37:18 +00:00
using System.Text;
using System.Text.Json;
using AIStudio.Chat;
2024-08-21 06:30:01 +00:00
using AIStudio.Dialogs;
using AIStudio.Settings;
using Microsoft.AspNetCore.Components;
2024-08-21 06:30:01 +00:00
using DialogOptions = AIStudio.Dialogs.DialogOptions;
2024-08-21 06:30:01 +00:00
namespace AIStudio.Components;
2025-04-27 07:06:05 +00:00
public partial class Workspaces : MSGComponentBase
{
[Inject]
private IDialogService DialogService { get; init; } = null!;
2024-09-01 18:10:03 +00:00
[Inject]
private ILogger<Workspaces> Logger { get; init; } = null!;
[Parameter]
public ChatThread? CurrentChatThread { get; set; }
[Parameter]
public EventCallback<ChatThread> CurrentChatThreadChanged { get; set; }
2024-11-02 21:53:02 +00:00
[Parameter]
public bool ExpandRootNodes { get; set; } = true;
private const Placement WORKSPACE_ITEM_TOOLTIP_PLACEMENT = Placement.Bottom;
2026-03-05 17:37:18 +00:00
private readonly SemaphoreSlim treeLoadingSemaphore = new(1, 1);
private readonly List<TreeItemData<ITreeItem>> treeItems = [];
private readonly HashSet<Guid> loadingWorkspaceChatLists = [];
2024-07-24 13:17:45 +00:00
2026-03-05 17:37:18 +00:00
private CancellationTokenSource? prefetchCancellationTokenSource;
private bool isInitialLoading = true;
private bool isDisposed;
#region Overrides of ComponentBase
protected override async Task OnInitializedAsync()
{
2025-04-27 14:13:15 +00:00
await base.OnInitializedAsync();
2026-03-05 17:37:18 +00:00
_ = this.LoadTreeItemsAsync(startPrefetch: true);
}
#endregion
2026-03-05 17:37:18 +00:00
private async Task LoadTreeItemsAsync(bool startPrefetch = true, bool forceReload = false)
{
await this.treeLoadingSemaphore.WaitAsync();
try
{
if (this.isDisposed)
return;
if (forceReload)
await WorkspaceBehaviour.ForceReloadWorkspaceTreeAsync();
var snapshot = await WorkspaceBehaviour.GetOrLoadWorkspaceTreeShellAsync();
this.BuildTreeItems(snapshot);
this.isInitialLoading = false;
}
finally
{
this.treeLoadingSemaphore.Release();
}
await this.SafeStateHasChanged();
if (startPrefetch)
await this.StartPrefetchAsync();
}
private void BuildTreeItems(WorkspaceTreeCacheSnapshot snapshot)
{
this.treeItems.Clear();
2026-03-05 17:37:18 +00:00
var workspaceChildren = new List<TreeItemData<ITreeItem>>();
foreach (var workspace in snapshot.Workspaces)
workspaceChildren.Add(this.CreateWorkspaceTreeItem(workspace));
workspaceChildren.Add(new TreeItemData<ITreeItem>
{
Expandable = false,
Value = new TreeButton(WorkspaceBranch.WORKSPACES, 1, T("Add workspace"), Icons.Material.Filled.LibraryAdd, this.AddWorkspaceAsync),
});
this.treeItems.Add(new TreeItemData<ITreeItem>
{
2024-11-02 21:53:02 +00:00
Expanded = this.ExpandRootNodes,
Expandable = true,
2024-07-24 13:17:45 +00:00
Value = new TreeItemData
{
Depth = 0,
Branch = WorkspaceBranch.WORKSPACES,
2025-04-27 07:06:05 +00:00
Text = T("Workspaces"),
2024-07-24 13:17:45 +00:00
Icon = Icons.Material.Filled.Folder,
Expandable = true,
Path = "root",
2026-03-05 17:37:18 +00:00
Children = workspaceChildren,
2024-07-24 13:17:45 +00:00
},
});
this.treeItems.Add(new TreeItemData<ITreeItem>
{
Expandable = false,
Value = new TreeDivider(),
});
2026-03-05 17:37:18 +00:00
var temporaryChatsChildren = new List<TreeItemData<ITreeItem>>();
foreach (var temporaryChat in snapshot.TemporaryChats.OrderByDescending(x => x.LastEditTime))
temporaryChatsChildren.Add(CreateChatTreeItem(temporaryChat, WorkspaceBranch.TEMPORARY_CHATS, depth: 1, icon: Icons.Material.Filled.Timer));
this.treeItems.Add(new TreeItemData<ITreeItem>
{
2024-11-02 21:53:02 +00:00
Expanded = this.ExpandRootNodes,
Expandable = true,
2024-07-24 13:17:45 +00:00
Value = new TreeItemData
{
Depth = 0,
Branch = WorkspaceBranch.TEMPORARY_CHATS,
2025-04-27 07:06:05 +00:00
Text = T("Disappearing Chats"),
2024-07-24 13:17:45 +00:00
Icon = Icons.Material.Filled.Timer,
Expandable = true,
Path = "temp",
2026-03-05 17:37:18 +00:00
Children = temporaryChatsChildren,
2024-07-24 13:17:45 +00:00
},
});
}
2026-03-05 17:37:18 +00:00
private TreeItemData<ITreeItem> CreateWorkspaceTreeItem(WorkspaceTreeWorkspace workspace)
{
2026-03-05 17:37:18 +00:00
var children = new List<TreeItemData<ITreeItem>>();
if (workspace.ChatsLoaded)
{
foreach (var workspaceChat in workspace.Chats.OrderByDescending(x => x.LastEditTime))
children.Add(CreateChatTreeItem(workspaceChat, WorkspaceBranch.WORKSPACES, depth: 2, icon: Icons.Material.Filled.Chat));
}
else if (this.loadingWorkspaceChatLists.Contains(workspace.WorkspaceId))
children.AddRange(this.CreateLoadingRows(workspace.WorkspacePath));
children.Add(new TreeItemData<ITreeItem>
{
Expandable = false,
Value = new TreeButton(WorkspaceBranch.WORKSPACES, 2, T("Add chat"), Icons.Material.Filled.AddComment, () => this.AddChatAsync(workspace.WorkspacePath)),
});
return new TreeItemData<ITreeItem>
{
Expandable = true,
Value = new TreeItemData
{
2026-03-05 17:37:18 +00:00
Type = TreeItemType.WORKSPACE,
Depth = 1,
2026-03-05 17:37:18 +00:00
Branch = WorkspaceBranch.WORKSPACES,
Text = workspace.Name,
Icon = Icons.Material.Filled.Description,
Expandable = true,
Path = workspace.WorkspacePath,
Children = children,
},
};
}
private IReadOnlyCollection<TreeItemData<ITreeItem>> CreateLoadingRows(string workspacePath)
{
return
[
this.CreateLoadingTreeItem(workspacePath, "loading_1"),
this.CreateLoadingTreeItem(workspacePath, "loading_2"),
this.CreateLoadingTreeItem(workspacePath, "loading_3"),
];
}
private TreeItemData<ITreeItem> CreateLoadingTreeItem(string workspacePath, string suffix)
{
return new TreeItemData<ITreeItem>
{
Expandable = false,
Value = new TreeItemData
{
Type = TreeItemType.LOADING,
Depth = 2,
Branch = WorkspaceBranch.WORKSPACES,
Text = T("Loading chats..."),
Icon = Icons.Material.Filled.HourglassTop,
Expandable = false,
2026-03-05 17:37:18 +00:00
Path = Path.Join(workspacePath, suffix),
},
};
}
private static TreeItemData<ITreeItem> CreateChatTreeItem(WorkspaceTreeChat chat, WorkspaceBranch branch, int depth, string icon)
{
return new TreeItemData<ITreeItem>
{
Expandable = false,
2026-03-05 17:37:18 +00:00
Value = new TreeItemData
{
Type = TreeItemType.CHAT,
Depth = depth,
Branch = branch,
Text = chat.Name,
Icon = icon,
Expandable = false,
Path = chat.ChatPath,
LastEditTime = chat.LastEditTime,
},
};
}
2026-03-05 17:37:18 +00:00
private async Task SafeStateHasChanged()
{
2026-03-05 17:37:18 +00:00
if (this.isDisposed)
return;
2026-03-05 17:37:18 +00:00
await this.InvokeAsync(this.StateHasChanged);
}
2026-03-05 17:37:18 +00:00
private async Task StartPrefetchAsync()
{
if (this.prefetchCancellationTokenSource is not null)
{
await this.prefetchCancellationTokenSource.CancelAsync();
this.prefetchCancellationTokenSource.Dispose();
}
2026-03-05 17:37:18 +00:00
this.prefetchCancellationTokenSource = new CancellationTokenSource();
await this.PrefetchWorkspaceChatsAsync(this.prefetchCancellationTokenSource.Token);
}
private async Task PrefetchWorkspaceChatsAsync(CancellationToken cancellationToken)
{
try
{
2026-03-05 17:37:18 +00:00
await WorkspaceBehaviour.TryPrefetchRemainingChatsAsync(async _ =>
2025-08-28 14:44:32 +00:00
{
2026-03-05 17:37:18 +00:00
if (this.isDisposed || cancellationToken.IsCancellationRequested)
return;
await this.LoadTreeItemsAsync(startPrefetch: false);
}, cancellationToken);
}
2026-03-05 17:37:18 +00:00
catch (OperationCanceledException)
2024-07-24 13:17:45 +00:00
{
2026-03-05 17:37:18 +00:00
// Expected when the component is hidden or disposed.
}
catch (Exception ex)
{
this.Logger.LogWarning(ex, "Failed while prefetching workspace chats.");
}
}
private async Task OnWorkspaceClicked(TreeItemData treeItem)
{
if (treeItem.Type is not TreeItemType.WORKSPACE)
return;
if (!Guid.TryParse(Path.GetFileName(treeItem.Path), out var workspaceId))
return;
await this.EnsureWorkspaceChatsLoadedAsync(workspaceId);
}
2026-03-05 17:37:18 +00:00
private async Task EnsureWorkspaceChatsLoadedAsync(Guid workspaceId)
{
2026-03-05 17:37:18 +00:00
var snapshot = await WorkspaceBehaviour.GetOrLoadWorkspaceTreeShellAsync();
var hasWorkspace = false;
var chatsLoaded = false;
2026-03-05 17:37:18 +00:00
foreach (var workspace in snapshot.Workspaces)
{
2026-03-05 17:37:18 +00:00
if (workspace.WorkspaceId != workspaceId)
continue;
hasWorkspace = true;
chatsLoaded = workspace.ChatsLoaded;
break;
}
2026-03-05 17:37:18 +00:00
if (!hasWorkspace || chatsLoaded || !this.loadingWorkspaceChatLists.Add(workspaceId))
return;
await this.LoadTreeItemsAsync(startPrefetch: false);
try
{
2026-03-05 17:37:18 +00:00
await WorkspaceBehaviour.GetWorkspaceChatsAsync(workspaceId);
}
finally
2024-07-24 13:17:45 +00:00
{
2026-03-05 17:37:18 +00:00
this.loadingWorkspaceChatLists.Remove(workspaceId);
}
await this.LoadTreeItemsAsync(startPrefetch: false);
}
public async Task ForceRefreshFromDiskAsync()
{
if (this.prefetchCancellationTokenSource is not null)
{
await this.prefetchCancellationTokenSource.CancelAsync();
this.prefetchCancellationTokenSource.Dispose();
this.prefetchCancellationTokenSource = null;
}
2024-07-24 13:17:45 +00:00
2026-03-05 17:37:18 +00:00
this.loadingWorkspaceChatLists.Clear();
this.isInitialLoading = true;
await this.SafeStateHasChanged();
await this.LoadTreeItemsAsync(startPrefetch: true, forceReload: true);
}
2026-03-05 17:37:18 +00:00
public async Task StoreChatAsync(ChatThread chat, bool reloadTreeItems = false)
{
2026-03-05 17:37:18 +00:00
await WorkspaceBehaviour.StoreChatAsync(chat);
2026-03-05 17:37:18 +00:00
if (reloadTreeItems)
this.loadingWorkspaceChatLists.Clear();
2024-10-28 14:41:00 +00:00
2026-03-05 17:37:18 +00:00
await this.LoadTreeItemsAsync(startPrefetch: false);
}
2026-03-05 17:37:18 +00:00
private async Task<ChatThread?> LoadChatAsync(string? chatPath, bool switchToChat)
{
2026-03-05 17:37:18 +00:00
if (string.IsNullOrWhiteSpace(chatPath))
return null;
2026-03-05 17:37:18 +00:00
if (!Directory.Exists(chatPath))
return null;
if (switchToChat && await MessageBus.INSTANCE.SendMessageUseFirstResult<bool, bool>(this, Event.HAS_CHAT_UNSAVED_CHANGES))
{
var dialogParameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Message, T("Are you sure you want to load another chat? All unsaved changes will be lost.") },
};
2026-03-05 17:37:18 +00:00
2025-04-27 07:06:05 +00:00
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Load Chat"), dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
2024-07-24 13:17:45 +00:00
if (dialogResult is null || dialogResult.Canceled)
return null;
}
try
{
var chatData = await File.ReadAllTextAsync(Path.Join(chatPath, "thread.json"), Encoding.UTF8);
var chat = JsonSerializer.Deserialize<ChatThread>(chatData, WorkspaceBehaviour.JSON_OPTIONS);
if (switchToChat)
{
this.CurrentChatThread = chat;
await this.CurrentChatThreadChanged.InvokeAsync(this.CurrentChatThread);
2025-01-02 12:16:47 +00:00
await MessageBus.INSTANCE.SendMessage<bool>(this, Event.WORKSPACE_LOADED_CHAT_CHANGED);
}
return chat;
}
catch (Exception e)
{
2024-09-01 18:10:03 +00:00
this.Logger.LogError($"Failed to load chat from '{chatPath}': {e.Message}");
}
return null;
}
2026-03-05 17:37:18 +00:00
public async Task DeleteChatAsync(string? chatPath, bool askForConfirmation = true, bool unloadChat = true)
{
2026-03-05 17:37:18 +00:00
var chat = await this.LoadChatAsync(chatPath, false);
if (chat is null)
return;
if (askForConfirmation)
{
2026-03-05 17:37:18 +00:00
var workspaceName = await WorkspaceBehaviour.LoadWorkspaceNameAsync(chat.WorkspaceId);
var dialogParameters = new DialogParameters<ConfirmDialog>
{
{
x => x.Message, (chat.WorkspaceId == Guid.Empty) switch
{
2025-04-27 07:06:05 +00:00
true => string.Format(T("Are you sure you want to delete the temporary chat '{0}'?"), chat.Name),
false => string.Format(T("Are you sure you want to delete the chat '{0}' in the workspace '{1}'?"), chat.Name, workspaceName),
}
},
};
2025-04-27 07:06:05 +00:00
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Chat"), dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
2024-07-24 13:17:45 +00:00
if (dialogResult is null || dialogResult.Canceled)
return;
}
2026-03-05 17:37:18 +00:00
await WorkspaceBehaviour.DeleteChatAsync(this.DialogService, chat.WorkspaceId, chat.ChatId, askForConfirmation: false);
await this.LoadTreeItemsAsync(startPrefetch: false);
2026-03-05 17:37:18 +00:00
if (unloadChat && this.CurrentChatThread?.ChatId == chat.ChatId)
{
this.CurrentChatThread = null;
await this.CurrentChatThreadChanged.InvokeAsync(this.CurrentChatThread);
2025-01-02 12:16:47 +00:00
await MessageBus.INSTANCE.SendMessage<bool>(this, Event.WORKSPACE_LOADED_CHAT_CHANGED);
}
}
2026-03-05 17:37:18 +00:00
private async Task RenameChatAsync(string? chatPath)
{
2026-03-05 17:37:18 +00:00
var chat = await this.LoadChatAsync(chatPath, false);
if (chat is null)
return;
var dialogParameters = new DialogParameters<SingleInputDialog>
{
{ x => x.Message, string.Format(T("Please enter a new or edit the name for your chat '{0}':"), chat.Name) },
{ x => x.InputHeaderText, T("Chat Name") },
{ x => x.UserInput, chat.Name },
{ x => x.ConfirmText, T("Rename") },
{ x => x.ConfirmColor, Color.Info },
{ x => x.AllowEmptyInput, false },
{ x => x.EmptyInputErrorMessage, T("Please enter a chat name.") },
};
2025-04-27 07:06:05 +00:00
var dialogReference = await this.DialogService.ShowAsync<SingleInputDialog>(T("Rename Chat"), dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
2024-07-24 13:17:45 +00:00
if (dialogResult is null || dialogResult.Canceled)
return;
chat.Name = (dialogResult.Data as string)!;
2026-03-05 17:37:18 +00:00
if (this.CurrentChatThread?.ChatId == chat.ChatId)
{
this.CurrentChatThread.Name = chat.Name;
await this.CurrentChatThreadChanged.InvokeAsync(this.CurrentChatThread);
await MessageBus.INSTANCE.SendMessage<bool>(this, Event.WORKSPACE_LOADED_CHAT_CHANGED);
}
2026-03-05 17:37:18 +00:00
await WorkspaceBehaviour.StoreChatAsync(chat);
await this.LoadTreeItemsAsync(startPrefetch: false);
}
2026-03-05 17:37:18 +00:00
private async Task RenameWorkspaceAsync(string? workspacePath)
{
2026-03-05 17:37:18 +00:00
if (workspacePath is null)
return;
var workspaceId = Guid.Parse(Path.GetFileName(workspacePath));
2026-03-05 17:37:18 +00:00
var workspaceName = await WorkspaceBehaviour.LoadWorkspaceNameAsync(workspaceId);
var dialogParameters = new DialogParameters<SingleInputDialog>
{
{ x => x.Message, string.Format(T("Please enter a new or edit the name for your workspace '{0}':"), workspaceName) },
{ x => x.InputHeaderText, T("Workspace Name") },
{ x => x.UserInput, workspaceName },
{ x => x.ConfirmText, T("Rename") },
{ x => x.ConfirmColor, Color.Info },
{ x => x.AllowEmptyInput, false },
{ x => x.EmptyInputErrorMessage, T("Please enter a workspace name.") },
};
2025-04-27 07:06:05 +00:00
var dialogReference = await this.DialogService.ShowAsync<SingleInputDialog>(T("Rename Workspace"), dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
2024-07-24 13:17:45 +00:00
if (dialogResult is null || dialogResult.Canceled)
return;
var alteredWorkspaceName = (dialogResult.Data as string)!;
var workspaceNamePath = Path.Join(workspacePath, "name");
await File.WriteAllTextAsync(workspaceNamePath, alteredWorkspaceName, Encoding.UTF8);
2026-03-05 17:37:18 +00:00
await WorkspaceBehaviour.UpdateWorkspaceNameInCacheAsync(workspaceId, alteredWorkspaceName);
await this.LoadTreeItemsAsync(startPrefetch: false);
}
2026-03-05 17:37:18 +00:00
private async Task AddWorkspaceAsync()
{
var dialogParameters = new DialogParameters<SingleInputDialog>
{
{ x => x.Message, T("Please name your workspace:") },
{ x => x.InputHeaderText, T("Workspace Name") },
{ x => x.UserInput, string.Empty },
{ x => x.ConfirmText, T("Add workspace") },
{ x => x.ConfirmColor, Color.Info },
{ x => x.AllowEmptyInput, false },
{ x => x.EmptyInputErrorMessage, T("Please enter a workspace name.") },
};
2025-04-27 07:06:05 +00:00
var dialogReference = await this.DialogService.ShowAsync<SingleInputDialog>(T("Add Workspace"), dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
2024-07-24 13:17:45 +00:00
if (dialogResult is null || dialogResult.Canceled)
return;
var workspaceId = Guid.NewGuid();
var workspacePath = Path.Join(SettingsManager.DataDirectory, "workspaces", workspaceId.ToString());
Directory.CreateDirectory(workspacePath);
2026-03-05 17:37:18 +00:00
var workspaceName = (dialogResult.Data as string)!;
var workspaceNamePath = Path.Join(workspacePath, "name");
2026-03-05 17:37:18 +00:00
await File.WriteAllTextAsync(workspaceNamePath, workspaceName, Encoding.UTF8);
await WorkspaceBehaviour.AddWorkspaceToCacheAsync(workspaceId, workspacePath, workspaceName);
2026-03-05 17:37:18 +00:00
await this.LoadTreeItemsAsync(startPrefetch: false);
}
2024-10-28 14:41:00 +00:00
2026-03-05 17:37:18 +00:00
private async Task DeleteWorkspaceAsync(string? workspacePath)
{
2026-03-05 17:37:18 +00:00
if (workspacePath is null)
return;
var workspaceId = Guid.Parse(Path.GetFileName(workspacePath));
2026-03-05 17:37:18 +00:00
var workspaceName = await WorkspaceBehaviour.LoadWorkspaceNameAsync(workspaceId);
var chatCount = Directory.EnumerateDirectories(workspacePath).Count();
var dialogParameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Message, string.Format(T("Are you sure you want to delete the workspace '{0}'? This will also delete {1} chat(s) in this workspace."), workspaceName, chatCount) },
};
2025-04-27 07:06:05 +00:00
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Workspace"), dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
2024-07-24 13:17:45 +00:00
if (dialogResult is null || dialogResult.Canceled)
return;
Directory.Delete(workspacePath, true);
2026-03-05 17:37:18 +00:00
await WorkspaceBehaviour.RemoveWorkspaceFromCacheAsync(workspaceId);
await this.LoadTreeItemsAsync(startPrefetch: false);
}
2026-03-05 17:37:18 +00:00
private async Task MoveChatAsync(string? chatPath)
{
2026-03-05 17:37:18 +00:00
var chat = await this.LoadChatAsync(chatPath, false);
if (chat is null)
return;
var dialogParameters = new DialogParameters<WorkspaceSelectionDialog>
{
{ x => x.Message, T("Please select the workspace where you want to move the chat to.") },
{ x => x.SelectedWorkspace, chat.WorkspaceId },
{ x => x.ConfirmText, T("Move chat") },
};
2025-04-27 07:06:05 +00:00
var dialogReference = await this.DialogService.ShowAsync<WorkspaceSelectionDialog>(T("Move Chat to Workspace"), dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
2024-07-24 13:17:45 +00:00
if (dialogResult is null || dialogResult.Canceled)
return;
2025-04-27 07:06:05 +00:00
var workspaceId = dialogResult.Data is Guid id ? id : Guid.Empty;
if (workspaceId == Guid.Empty)
return;
2026-03-05 17:37:18 +00:00
await WorkspaceBehaviour.DeleteChatAsync(this.DialogService, chat.WorkspaceId, chat.ChatId, askForConfirmation: false);
chat.WorkspaceId = workspaceId;
if (this.CurrentChatThread?.ChatId == chat.ChatId)
{
this.CurrentChatThread = chat;
await this.CurrentChatThreadChanged.InvokeAsync(this.CurrentChatThread);
2025-01-02 12:16:47 +00:00
await MessageBus.INSTANCE.SendMessage<bool>(this, Event.WORKSPACE_LOADED_CHAT_CHANGED);
}
2026-03-05 17:37:18 +00:00
await WorkspaceBehaviour.StoreChatAsync(chat);
await this.LoadTreeItemsAsync(startPrefetch: false);
}
2026-03-05 17:37:18 +00:00
private async Task AddChatAsync(string workspacePath)
{
if (await MessageBus.INSTANCE.SendMessageUseFirstResult<bool, bool>(this, Event.HAS_CHAT_UNSAVED_CHANGES))
{
var dialogParameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Message, T("Are you sure you want to create a another chat? All unsaved changes will be lost.") },
};
2025-04-27 07:06:05 +00:00
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Create Chat"), dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
2024-07-24 13:17:45 +00:00
if (dialogResult is null || dialogResult.Canceled)
return;
}
var workspaceId = Guid.Parse(Path.GetFileName(workspacePath));
var chat = new ChatThread
{
WorkspaceId = workspaceId,
ChatId = Guid.NewGuid(),
Name = string.Empty,
SystemPrompt = SystemPrompts.DEFAULT,
Blocks = [],
};
var chatPath = Path.Join(workspacePath, chat.ChatId.ToString());
2026-03-05 17:37:18 +00:00
await WorkspaceBehaviour.StoreChatAsync(chat);
await this.LoadChatAsync(chatPath, switchToChat: true);
await this.LoadTreeItemsAsync(startPrefetch: false);
}
2025-04-27 14:13:15 +00:00
#region Overrides of MSGComponentBase
protected override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
{
switch (triggeredEvent)
{
case Event.PLUGINS_RELOADED:
2026-03-05 17:37:18 +00:00
await this.ForceRefreshFromDiskAsync();
2025-04-27 14:13:15 +00:00
break;
}
}
2026-03-05 17:37:18 +00:00
protected override void DisposeResources()
{
this.isDisposed = true;
this.prefetchCancellationTokenSource?.Cancel();
this.prefetchCancellationTokenSource?.Dispose();
this.prefetchCancellationTokenSource = null;
base.DisposeResources();
}
2025-04-27 14:13:15 +00:00
#endregion
}