using AIStudio.Tools; using Microsoft.AspNetCore.Components; namespace AIStudio.Chat; /// /// The UI component for a chat content block, i.e., for any IContent. /// public partial class ContentBlockComponent : ComponentBase { /// /// The role of the chat content block. /// [Parameter] public ChatRole Role { get; init; } = ChatRole.NONE; /// /// The content. /// [Parameter] public IContent Content { get; init; } = new ContentText(); /// /// The content type. /// [Parameter] public ContentType Type { get; init; } = ContentType.NONE; /// /// When was the content created? /// [Parameter] public DateTimeOffset Time { get; init; } [Inject] private Rust Rust { get; init; } = null!; [Inject] private IJSRuntime JsRuntime { get; init; } = null!; [Inject] private ISnackbar Snackbar { get; init; } = null!; private bool HideContent { get; set; } #region Overrides of ComponentBase protected override async Task OnInitializedAsync() { // Register the streaming events: this.Content.StreamingDone = this.AfterStreaming; this.Content.StreamingEvent = () => this.InvokeAsync(this.StateHasChanged); await base.OnInitializedAsync(); } /// /// Gets called when the content stream ended. /// private async Task AfterStreaming() { // Might be called from a different thread, so we need to invoke the UI thread: await this.InvokeAsync(() => { // // Issue we try to solve: When the content changes during streaming, // Blazor might fail to see all changes made to the render tree. // This happens mostly when Markdown code blocks are streamed. // // Hide the content for a short time: this.HideContent = true; // Let Blazor update the UI, i.e., to see the render tree diff: this.StateHasChanged(); // Show the content again: this.HideContent = false; // Let Blazor update the UI, i.e., to see the render tree diff: this.StateHasChanged(); }); } #endregion /// /// Copy this block's content to the clipboard. /// private async Task CopyToClipboard() { switch (this.Type) { case ContentType.TEXT: var textContent = (ContentText) this.Content; await this.Rust.CopyText2Clipboard(this.JsRuntime, this.Snackbar, textContent.Text); break; default: this.Snackbar.Add("Cannot copy this content type to clipboard!", Severity.Error, config => { config.Icon = Icons.Material.Filled.ContentCopy; config.IconSize = Size.Large; config.IconColor = Color.Error; }); break; } } }