mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2025-02-05 14:29:07 +00:00
211 lines
6.2 KiB
C#
211 lines
6.2 KiB
C#
using AIStudio.Settings;
|
|
|
|
using Microsoft.AspNetCore.Components;
|
|
|
|
using RustService = AIStudio.Tools.RustService;
|
|
|
|
namespace AIStudio.Chat;
|
|
|
|
/// <summary>
|
|
/// The UI component for a chat content block, i.e., for any IContent.
|
|
/// </summary>
|
|
public partial class ContentBlockComponent : ComponentBase
|
|
{
|
|
/// <summary>
|
|
/// The role of the chat content block.
|
|
/// </summary>
|
|
[Parameter]
|
|
public ChatRole Role { get; init; } = ChatRole.NONE;
|
|
|
|
/// <summary>
|
|
/// The content.
|
|
/// </summary>
|
|
[Parameter]
|
|
public IContent Content { get; init; } = new ContentText();
|
|
|
|
/// <summary>
|
|
/// The content type.
|
|
/// </summary>
|
|
[Parameter]
|
|
public ContentType Type { get; init; } = ContentType.NONE;
|
|
|
|
/// <summary>
|
|
/// When was the content created?
|
|
/// </summary>
|
|
[Parameter]
|
|
public DateTimeOffset Time { get; init; }
|
|
|
|
/// <summary>
|
|
/// Optional CSS classes.
|
|
/// </summary>
|
|
[Parameter]
|
|
public string Class { get; set; } = string.Empty;
|
|
|
|
[Parameter]
|
|
public bool IsLastContentBlock { get; set; } = false;
|
|
|
|
[Parameter]
|
|
public bool IsSecondToLastBlock { get; set; } = false;
|
|
|
|
[Parameter]
|
|
public Func<IContent, Task>? RemoveBlockFunc { get; set; }
|
|
|
|
[Parameter]
|
|
public Func<IContent, Task>? RegenerateFunc { get; set; }
|
|
|
|
[Parameter]
|
|
public Func<IContent, Task>? EditLastBlockFunc { get; set; }
|
|
|
|
[Parameter]
|
|
public Func<IContent, Task>? EditLastUserBlockFunc { get; set; }
|
|
|
|
[Parameter]
|
|
public Func<bool> RegenerateEnabled { get; set; } = () => false;
|
|
|
|
[Inject]
|
|
private RustService RustService { get; init; } = null!;
|
|
|
|
[Inject]
|
|
private ISnackbar Snackbar { get; init; } = null!;
|
|
|
|
[Inject]
|
|
private SettingsManager SettingsManager { get; init; } = null!;
|
|
|
|
[Inject]
|
|
private IDialogService DialogService { 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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets called when the content stream ended.
|
|
/// </summary>
|
|
private async Task AfterStreaming()
|
|
{
|
|
// Might be called from a different thread, so we need to invoke the UI thread:
|
|
await this.InvokeAsync(async () =>
|
|
{
|
|
//
|
|
// 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();
|
|
|
|
// Inform the chat that the streaming is done:
|
|
await MessageBus.INSTANCE.SendMessage<bool>(this, Event.CHAT_STREAMING_DONE);
|
|
});
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Copy this block's content to the clipboard.
|
|
/// </summary>
|
|
private async Task CopyToClipboard()
|
|
{
|
|
switch (this.Type)
|
|
{
|
|
case ContentType.TEXT:
|
|
var textContent = (ContentText) this.Content;
|
|
await this.RustService.CopyText2Clipboard(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;
|
|
}
|
|
}
|
|
|
|
private string CardClasses => $"my-2 rounded-lg {this.Class}";
|
|
|
|
private CodeBlockTheme CodeColorPalette => this.SettingsManager.IsDarkMode ? CodeBlockTheme.Dark : CodeBlockTheme.Default;
|
|
|
|
private async Task RemoveBlock()
|
|
{
|
|
if (this.RemoveBlockFunc is null)
|
|
return;
|
|
|
|
var remove = await this.DialogService.ShowMessageBox(
|
|
"Remove Message",
|
|
"Do you really want to remove this message?",
|
|
"Yes, remove it",
|
|
"No, keep it");
|
|
|
|
if (remove.HasValue && remove.Value)
|
|
await this.RemoveBlockFunc(this.Content);
|
|
}
|
|
|
|
private async Task RegenerateBlock()
|
|
{
|
|
if (this.RegenerateFunc is null)
|
|
return;
|
|
|
|
if(this.Role is not ChatRole.AI)
|
|
return;
|
|
|
|
var regenerate = await this.DialogService.ShowMessageBox(
|
|
"Regenerate Message",
|
|
"Do you really want to regenerate this message?",
|
|
"Yes, regenerate it",
|
|
"No, keep it");
|
|
|
|
if (regenerate.HasValue && regenerate.Value)
|
|
await this.RegenerateFunc(this.Content);
|
|
}
|
|
|
|
private async Task EditLastBlock()
|
|
{
|
|
if (this.EditLastBlockFunc is null)
|
|
return;
|
|
|
|
if(this.Role is not ChatRole.USER)
|
|
return;
|
|
|
|
await this.EditLastBlockFunc(this.Content);
|
|
}
|
|
|
|
private async Task EditLastUserBlock()
|
|
{
|
|
if (this.EditLastUserBlockFunc is null)
|
|
return;
|
|
|
|
if(this.Role is not ChatRole.USER)
|
|
return;
|
|
|
|
var edit = await this.DialogService.ShowMessageBox(
|
|
"Edit Message",
|
|
"Do you really want to edit this message? In order to edit this message, the AI response will be deleted.",
|
|
"Yes, remove the AI response and edit it",
|
|
"No, keep it");
|
|
|
|
if (edit.HasValue && edit.Value)
|
|
await this.EditLastUserBlockFunc(this.Content);
|
|
}
|
|
} |