Reduced memory usage and fixed several memory leaks (#933)
Build and Release / Determine run mode (push) Has been cancelled
Build and Release / Read metadata (push) Has been cancelled
Build and Release / Sync Flatpak repo (push) Has been cancelled
Build and Release / Collect Flatpak artifacts (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
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, nsis) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Prepare & create release (push) Has been cancelled
Build and Release / Publish release (push) Has been cancelled

This commit is contained in:
Thorsten Sommer authored and GitHub committed 2026-08-23 21:15:37 +02:00
1 parent 902a01a4d0
commit 7d9a4f5ab1
51 files changed
+531 -85

No files matched your search

@@ -8,7 +8,7 @@ namespace AIStudio.Chat;
/// <summary>
/// The UI component for a chat content block, i.e., for any IContent.
/// </summary>
public partial class ContentBlockComponent : MSGComponentBase, IAsyncDisposable
public partial class ContentBlockComponent : MSGComponentBase
{
private const string CHAT_MATH_SYNC_FUNCTION = "chatMath.syncContainer";
private const string CHAT_MATH_DISPOSE_FUNCTION = "chatMath.disposeContainer";
@@ -601,16 +601,24 @@ public partial class ContentBlockComponent : MSGComponentBase, IAsyncDisposable
private async Task OpenAttachmentsDialog()
{
var result = await ReviewAttachmentsDialog.OpenDialogAsync(this.DialogService, this.Content.FileAttachments.ToHashSet());
this.Content.FileAttachments = result.ToList();
this.Content.FileAttachments = [.. result];
}
public async ValueTask DisposeAsync()
protected override async ValueTask DisposeResourcesAsync()
{
if (this.isDisposed)
return;
this.isDisposed = true;
//
// Our handlers close over this component, while the content belongs to the chat thread and
// outlives us. We only detach what is still ours, though: when this content is streaming
// again, another component has registered its own handlers in the meantime.
//
if (this.Content.StreamingDone == this.AfterStreaming)
this.Content.ResetStreamingHandlers();
await this.DisposeMathContainerIfNeededAsync();
this.Dispose();
}
}
+2 -2
View File
@@ -22,11 +22,11 @@ public sealed class ContentImage : IContent, IImageSource
/// <inheritdoc />
[JsonIgnore]
public Func<Task> StreamingDone { get; set; } = () => Task.CompletedTask;
public Func<Task> StreamingDone { get; set; } = IContent.NO_STREAMING_HANDLER;
/// <inheritdoc />
[JsonIgnore]
public Func<Task> StreamingEvent { get; set; } = () => Task.CompletedTask;
public Func<Task> StreamingEvent { get; set; } = IContent.NO_STREAMING_HANDLER;
/// <inheritdoc />
public List<Source> Sources { get; set; } = [];
+2 -2
View File
@@ -37,11 +37,11 @@ public sealed class ContentText : IContent
/// <inheritdoc />
[JsonIgnore]
public Func<Task> StreamingDone { get; set; } = () => Task.CompletedTask;
public Func<Task> StreamingDone { get; set; } = IContent.NO_STREAMING_HANDLER;
/// <inheritdoc />
[JsonIgnore]
public Func<Task> StreamingEvent { get; set; } = () => Task.CompletedTask;
public Func<Task> StreamingEvent { get; set; } = IContent.NO_STREAMING_HANDLER;
/// <inheritdoc />
public List<Source> Sources { get; set; } = [];
+5
View File
@@ -38,6 +38,11 @@ public interface IContent
[JsonIgnore]
public Func<Task> StreamingDone { get; set; }
/// <summary>
/// What a content does while nobody listens to its stream: nothing.
/// </summary>
public static readonly Func<Task> NO_STREAMING_HANDLER = () => Task.CompletedTask;
/// <summary>
/// The provided sources, if any.
/// </summary>
@@ -0,0 +1,20 @@
namespace AIStudio.Chat;
public static class IContentExtensions
{
/// <summary>
/// Detaches whoever listens to the stream of this content.
/// </summary>
/// <remarks>
/// The streaming handlers are closures over the component which registered them. A content
/// object belongs to the chat thread and therefore outlives every component which renders it,
/// so handlers left behind would keep those components alive for as long as the thread exists.
/// Whoever registers a handler calls this when it is no longer needed.
/// </remarks>
/// <param name="content">The content whose streaming handlers you want to detach.</param>
public static void ResetStreamingHandlers(this IContent content)
{
content.StreamingEvent = IContent.NO_STREAMING_HANDLER;
content.StreamingDone = IContent.NO_STREAMING_HANDLER;
}
}