Improved workspace performance (#680)
Build and Release / Read metadata (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg updater) (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) (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 deb updater) (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 updater) (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) (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 deb updater) (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

This commit is contained in:
Thorsten Sommer authored and GitHub committed 2026-03-05 18:37:18 +01:00
1 parent 721d5c9070
commit 906d9ba058
16 files changed
+994 -494

No files matched your search

@@ -56,6 +56,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
private bool autoSaveEnabled;
private string currentWorkspaceName = string.Empty;
private Guid currentWorkspaceId = Guid.Empty;
private Guid currentChatThreadId = Guid.Empty;
private CancellationTokenSource? cancellationTokenSource;
private HashSet<FileAttachment> chatDocumentPaths = [];
@@ -197,8 +198,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
//
if (this.ChatThread is not null)
{
this.currentChatThreadId = this.ChatThread.ChatId;
this.currentWorkspaceId = this.ChatThread.WorkspaceId;
this.currentWorkspaceName = await WorkspaceBehaviour.LoadWorkspaceName(this.ChatThread.WorkspaceId);
this.currentWorkspaceName = await WorkspaceBehaviour.LoadWorkspaceNameAsync(this.ChatThread.WorkspaceId);
this.WorkspaceName(this.currentWorkspaceName);
}
@@ -214,12 +216,12 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
this.mustStoreChat = false;
if(this.Workspaces is not null)
await this.Workspaces.StoreChat(this.ChatThread);
await this.Workspaces.StoreChatAsync(this.ChatThread);
else
await WorkspaceBehaviour.StoreChat(this.ChatThread);
await WorkspaceBehaviour.StoreChatAsync(this.ChatThread);
this.currentWorkspaceId = this.ChatThread.WorkspaceId;
this.currentWorkspaceName = await WorkspaceBehaviour.LoadWorkspaceName(this.ChatThread.WorkspaceId);
this.currentWorkspaceName = await WorkspaceBehaviour.LoadWorkspaceNameAsync(this.ChatThread.WorkspaceId);
this.WorkspaceName(this.currentWorkspaceName);
}
@@ -227,14 +229,14 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
{
this.Logger.LogInformation($"Try to load the chat '{this.loadChat.ChatId}' now.");
this.mustLoadChat = false;
this.ChatThread = await WorkspaceBehaviour.LoadChat(this.loadChat);
this.ChatThread = await WorkspaceBehaviour.LoadChatAsync(this.loadChat);
if(this.ChatThread is not null)
{
await this.ChatThreadChanged.InvokeAsync(this.ChatThread);
this.Logger.LogInformation($"The chat '{this.ChatThread!.ChatId}' with title '{this.ChatThread.Name}' ({this.ChatThread.Blocks.Count} messages) was loaded successfully.");
this.currentWorkspaceName = await WorkspaceBehaviour.LoadWorkspaceName(this.ChatThread.WorkspaceId);
this.currentWorkspaceName = await WorkspaceBehaviour.LoadWorkspaceNameAsync(this.ChatThread.WorkspaceId);
this.WorkspaceName(this.currentWorkspaceName);
await this.SelectProviderWhenLoadingChat();
}
@@ -260,7 +262,49 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
await base.OnAfterRenderAsync(firstRender);
}
protected override async Task OnParametersSetAsync()
{
await this.SyncWorkspaceHeaderWithChatThreadAsync();
await base.OnParametersSetAsync();
}
#endregion
private async Task SyncWorkspaceHeaderWithChatThreadAsync()
{
if (this.ChatThread is null)
{
if (this.currentChatThreadId != Guid.Empty || this.currentWorkspaceId != Guid.Empty || !string.IsNullOrWhiteSpace(this.currentWorkspaceName))
{
this.currentChatThreadId = Guid.Empty;
this.currentWorkspaceId = Guid.Empty;
this.currentWorkspaceName = string.Empty;
this.WorkspaceName(this.currentWorkspaceName);
}
return;
}
// Guard: If ChatThread ID and WorkspaceId haven't changed, skip entirely.
// Using ID-based comparison instead of name-based to correctly handle
// temporary chats where the workspace name is always empty.
if (this.currentChatThreadId == this.ChatThread.ChatId
&& this.currentWorkspaceId == this.ChatThread.WorkspaceId)
return;
this.currentChatThreadId = this.ChatThread.ChatId;
this.currentWorkspaceId = this.ChatThread.WorkspaceId;
var loadedWorkspaceName = await WorkspaceBehaviour.LoadWorkspaceNameAsync(this.ChatThread.WorkspaceId);
// Only notify the parent when the name actually changed to prevent
// an infinite render loop: WorkspaceName → UpdateWorkspaceName →
// StateHasChanged → re-render → OnParametersSetAsync → WorkspaceName → ...
if (this.currentWorkspaceName != loadedWorkspaceName)
{
this.currentWorkspaceName = loadedWorkspaceName;
this.WorkspaceName(this.currentWorkspaceName);
}
}
private bool IsProviderSelected => this.Provider.UsedLLMProvider != LLMProviders.NONE;
@@ -428,8 +472,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
if(!this.ChatThread.IsLLMProviderAllowed(this.Provider))
return;
// We need to blur the focus away from the input field
// to be able to clear the field:
// Blur the focus away from the input field to be able to clear it:
await this.inputField.BlurAsync();
// Create a new chat thread if necessary:
@@ -520,8 +563,10 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
// Clear the input field:
await this.inputField.FocusAsync();
this.userInput = string.Empty;
this.chatDocumentPaths.Clear();
await this.inputField.BlurAsync();
// Enable the stream state for the chat component:
@@ -583,9 +628,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
// the workspace gets updated automatically when the chat is saved.
//
if (this.Workspaces is not null)
await this.Workspaces.StoreChat(this.ChatThread);
await this.Workspaces.StoreChatAsync(this.ChatThread);
else
await WorkspaceBehaviour.StoreChat(this.ChatThread);
await WorkspaceBehaviour.StoreChatAsync(this.ChatThread);
this.hasUnsavedChanges = false;
}
@@ -621,9 +666,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
chatPath = Path.Join(SettingsManager.DataDirectory, "workspaces", this.ChatThread.WorkspaceId.ToString(), this.ChatThread.ChatId.ToString());
if(this.Workspaces is null)
await WorkspaceBehaviour.DeleteChat(this.DialogService, this.ChatThread.WorkspaceId, this.ChatThread.ChatId, askForConfirmation: false);
await WorkspaceBehaviour.DeleteChatAsync(this.DialogService, this.ChatThread.WorkspaceId, this.ChatThread.ChatId, askForConfirmation: false);
else
await this.Workspaces.DeleteChat(chatPath, askForConfirmation: false, unloadChat: true);
await this.Workspaces.DeleteChatAsync(chatPath, askForConfirmation: false, unloadChat: true);
}
//
@@ -665,6 +710,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
// to reset the chat thread:
//
this.ChatThread = null;
this.currentChatThreadId = Guid.Empty;
this.currentWorkspaceId = Guid.Empty;
this.currentWorkspaceName = string.Empty;
this.WorkspaceName(this.currentWorkspaceName);
@@ -739,13 +785,13 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
return;
// Delete the chat from the current workspace or the temporary storage:
await WorkspaceBehaviour.DeleteChat(this.DialogService, this.ChatThread!.WorkspaceId, this.ChatThread.ChatId, askForConfirmation: false);
await WorkspaceBehaviour.DeleteChatAsync(this.DialogService, this.ChatThread!.WorkspaceId, this.ChatThread.ChatId, askForConfirmation: false);
this.ChatThread!.WorkspaceId = workspaceId;
await this.SaveThread();
this.currentWorkspaceId = this.ChatThread.WorkspaceId;
this.currentWorkspaceName = await WorkspaceBehaviour.LoadWorkspaceName(this.ChatThread.WorkspaceId);
this.currentWorkspaceName = await WorkspaceBehaviour.LoadWorkspaceNameAsync(this.ChatThread.WorkspaceId);
this.WorkspaceName(this.currentWorkspaceName);
}
@@ -758,12 +804,14 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
if (this.ChatThread is not null)
{
this.currentWorkspaceId = this.ChatThread.WorkspaceId;
this.currentWorkspaceName = await WorkspaceBehaviour.LoadWorkspaceName(this.ChatThread.WorkspaceId);
this.currentWorkspaceName = await WorkspaceBehaviour.LoadWorkspaceNameAsync(this.ChatThread.WorkspaceId);
this.WorkspaceName(this.currentWorkspaceName);
this.currentChatThreadId = this.ChatThread.ChatId;
this.dataSourceSelectionComponent?.ChangeOptionWithoutSaving(this.ChatThread.DataSourceOptions, this.ChatThread.AISelectedDataSources);
}
else
{
this.currentChatThreadId = Guid.Empty;
this.currentWorkspaceId = Guid.Empty;
this.currentWorkspaceName = string.Empty;
this.WorkspaceName(this.currentWorkspaceName);
@@ -785,6 +833,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
this.isStreaming = false;
this.hasUnsavedChanges = false;
this.userInput = string.Empty;
this.currentChatThreadId = Guid.Empty;
this.currentWorkspaceId = Guid.Empty;
this.currentWorkspaceName = string.Empty;