More UI cleanups

This commit is contained in:
Javier Calvarro Nelson
2025-12-11 13:58:44 +01:00
Unverified
parent 0c93762cb6
commit c0ce673354
4 changed files with 102 additions and 45 deletions
@@ -23,9 +23,10 @@ internal sealed class DefaultMessageTemplate : MessageTemplateBase
if (messageContext.ChatMessage is not null)
{
var getRenderContents = messageContext.RenderContents();
// Return a render fragment that checks visibility at render time
// This is important because message contents may change during streaming
return CreateRenderMessage(
messageContext.ChatMessage.Role,
messageContext.ChatMessage.MessageId,
messageContext.ChatMessage,
getRenderContents);
}
else
@@ -44,29 +45,61 @@ internal sealed class DefaultMessageTemplate : MessageTemplateBase
}
return CreateRenderMessage(
this._buffer[0].Role,
this._buffer[0].MessageId,
this._buffer[0],
getRenderContents);
}
}
/// <summary>
/// Checks if a message has any visible content that should be displayed.
/// Messages that only contain FunctionCallContent or FunctionResultContent
/// are internal tool messages and should not be rendered as chat bubbles.
/// </summary>
private static bool HasVisibleContent(ChatMessage message)
{
if (message.Contents.Count == 0)
{
return false;
}
foreach (var content in message.Contents)
{
// TextContent is visible
if (content is TextContent textContent && !string.IsNullOrWhiteSpace(textContent.Text))
{
return true;
}
// Other content types that are not function calls/results are visible
if (content is not FunctionCallContent && content is not FunctionResultContent)
{
return true;
}
}
return false;
}
private static RenderFragment CreateRenderMessage(
ChatRole role,
string? messageId,
ChatMessage message,
RenderFragment getRenderContents)
{
var roleClass = $"{role}-message";
var roleClass = $"{message.Role}-message";
return builder =>
{
// Check visibility at render time, not at template creation time
// This is important because message contents may change during streaming
if (!HasVisibleContent(message))
{
return; // Don't render messages without visible content
}
builder.OpenElement(0, "div");
if (!string.IsNullOrEmpty(messageId))
if (!string.IsNullOrEmpty(message.MessageId))
{
builder.AddAttribute(1, "id", messageId);
}
else
{
builder.AddAttribute(2, "class", $"chat-message {roleClass}");
builder.AddAttribute(1, "id", message.MessageId);
}
builder.AddAttribute(2, "class", $"chat-message {roleClass}");
builder.AddContent(3, getRenderContents);
builder.CloseElement();
};
@@ -79,15 +79,19 @@ internal sealed partial class MessageList : IComponent, IDisposable
// Track all render keys to detect duplicates
var allRenderKeys = new HashSet<string?>();
// Open container div for message list with flex layout
builder.OpenElement(0, "div");
builder.AddAttribute(1, "class", "messages-container");
foreach (var message in this.MessageListContext.AgentBoundaryContext.CompletedMessages)
{
var renderKey = GetUniqueRenderKey(message);
// Calling GetTemplate will stop template collection on the first message if it
// was still ongoing.
builder.OpenComponent<ContentBlock>(0);
builder.OpenComponent<ContentBlock>(2);
builder.SetKey(renderKey);
builder.AddComponentParameter(1, "ChildContent", this.MessageListContext.GetTemplate(message));
builder.AddComponentParameter(3, "ChildContent", this.MessageListContext.GetTemplate(message));
builder.CloseComponent();
}
@@ -95,11 +99,14 @@ internal sealed partial class MessageList : IComponent, IDisposable
{
var renderKey = GetUniqueRenderKey(message);
builder.OpenComponent<ContentBlock>(1);
builder.OpenComponent<ContentBlock>(4);
builder.SetKey(renderKey);
builder.AddComponentParameter(1, "ChildContent", this.MessageListContext.GetTemplate(message));
builder.AddComponentParameter(5, "ChildContent", this.MessageListContext.GetTemplate(message));
builder.CloseComponent();
}
// Close container div
builder.CloseElement();
}
/// <summary>
@@ -55,35 +55,12 @@
}
.chat-input-container {
padding: 1.5rem 2rem 2rem;
padding: 1rem 2rem 1.5rem;
border-top: 1px solid #e5e5e5;
background: white;
}
::deep .agent-suggestions {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
justify-content: flex-start;
margin-bottom: 0.75rem;
}
::deep .suggestion-button {
padding: 0.5rem 1rem;
background: white;
border: 1px solid #d1d1d1;
border-radius: 1rem;
cursor: pointer;
font-size: 0.875rem;
color: #424242;
transition: all 0.2s;
}
::deep .suggestion-button:hover {
background: #f0f0f0;
border-color: #0078d4;
color: #0078d4;
}
.agentic-chat-demo {
height: 100%;
@@ -92,6 +69,6 @@
}
.chat-container {
padding: 20px;
padding: 0 2rem 1.5rem;
border-top: 1px solid #e0e0e0;
}
@@ -92,7 +92,6 @@ h1:focus {
align-items: flex-end;
gap: 12px;
width: 100%;
padding: 16px;
background-color: #fff;
}
@@ -200,7 +199,6 @@ h1:focus {
.agent-loading-indicator {
display: flex;
align-items: flex-start;
padding: 12px 16px;
}
.agent-loading-indicator .agent-loading-dots {
@@ -222,7 +220,7 @@ h1:focus {
display: flex;
flex-wrap: wrap;
gap: 8px;
padding: 8px 16px;
margin-bottom: 12px;
}
.agent-suggestions .suggestion-button {
@@ -267,3 +265,45 @@ h1:focus {
border-top: 1px solid #e5e7eb;
background-color: #fff;
}
/* Chat message styles */
.messages-container {
display: flex;
flex-direction: column;
gap: 12px;
}
.chat-message {
display: flex;
flex-direction: column;
max-width: 80%;
padding: 12px 16px;
border-radius: 8px;
border: 1px solid #e5e7eb;
line-height: 1.5;
}
/* User messages - right aligned with blue background */
.chat-message.user-message {
align-self: flex-end;
background-color: #2563eb;
color: white;
border-color: #2563eb;
}
/* Assistant messages - left aligned with light gray background */
.chat-message.assistant-message {
align-self: flex-start;
background-color: #f3f4f6;
color: #1f2937;
border-color: #e5e7eb;
}
/* Tool messages - left aligned, styled similar to assistant */
.chat-message.tool-message {
align-self: flex-start;
background-color: #fef3c7;
color: #92400e;
border-color: #fcd34d;
font-size: 0.875rem;
}