Skip to content
76 changes: 56 additions & 20 deletions AIDevGallery/Samples/Open Source Models/Language Models/Chat.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,50 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:shared="using:AIDevGallery.Samples.SharedCode"
xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters"
mc:Ignorable="d">
<Page.Resources>
<tkconverters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<shared:ChatTemplateSelector
x:Key="ChatTemplateSelector"
AssistantTemplate="{StaticResource AssistantDataTemplate}"
UserTemplate="{StaticResource UserDataTemplate}" />
<DataTemplate x:Key="UserDataTemplate" x:DataType="shared:Message">
<StackPanel
MaxWidth="486"
Padding="12"
HorizontalAlignment="Right"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="{StaticResource OverlayCornerRadius}"
Spacing="8">
<TextBlock
IsTextSelectionEnabled="True"
Text="{x:Bind Content, Mode=OneWay}"
TextWrapping="Wrap" />
<TextBlock
<StackPanel Spacing="4">
<StackPanel
MaxWidth="486"
Padding="12"
HorizontalAlignment="Right"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind MsgDateTime}" />
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="{StaticResource OverlayCornerRadius}"
Spacing="8">
<TextBlock
IsTextSelectionEnabled="True"
Text="{x:Bind Content, Mode=OneWay}"
TextWrapping="Wrap" />
<TextBlock
HorizontalAlignment="Right"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind MsgDateTime}" />
</StackPanel>
<HyperlinkButton x:Name="RewriteButton"
AutomationProperties.Name="Rewrite This Message"
ToolTipService.ToolTip="Edit and resend this message"
HorizontalAlignment="Right"
Margin="0,0,8,0"
Padding="4,2"
Click="RewriteBtn_Click"
Visibility="{x:Bind IsLastUserMessage, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
<StackPanel Orientation="Horizontal" Spacing="4">
<FontIcon FontSize="10"
Glyph="&#xE70F;" />
<TextBlock Text="Rewrite"
FontSize="11" />
</StackPanel>
</HyperlinkButton>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="AssistantDataTemplate" x:DataType="shared:Message">
Expand Down Expand Up @@ -74,12 +93,30 @@
</Page.Resources>
<Grid MaxWidth="1000">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>

<!-- Chat Actions Bar -->
<Grid Grid.Row="0" Margin="0,0,0,8">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Spacing="8">
<Button x:Name="ClearBtn"
AutomationProperties.Name="Clear Chat History"
Click="ClearBtn_Click"
ToolTipService.ToolTip="Clear all chat messages"
IsEnabled="False">
<StackPanel Orientation="Horizontal" Spacing="8">
<FontIcon FontSize="16" Glyph="&#xE74D;" />
<TextBlock Text="Clear Chat" />
</StackPanel>
</Button>
</StackPanel>
</Grid>

<ListView
x:Name="InvertedListView"
Grid.Row="1"
Padding="-12,0,-12,24"
ItemTemplateSelector="{StaticResource ChatTemplateSelector}"
ItemsSource="{x:Bind Messages}"
Expand All @@ -96,14 +133,13 @@
</Style>
</ListView.ItemContainerStyle>
</ListView>
<Grid Grid.Row="1" ColumnSpacing="8">
<Grid Grid.Row="2" ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox
x:Name="InputBox"
Grid.Row="1"
AutomationProperties.Name="Prompt input"
KeyUp="TextBox_KeyUp"
PreviewKeyDown="TextBox_PreviewKeyDown"
Expand All @@ -113,7 +149,7 @@
AcceptsReturn="True"
MaxHeight="148"
ScrollViewer.VerticalScrollBarVisibility="Auto"/>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="8">
<Button x:Name="SendBtn"
AutomationProperties.Name="Send Message"
Click="SendBtn_Click"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace AIDevGallery.Samples.OpenSourceModels.LanguageModels;
Scenario = ScenarioType.TextChat,
NugetPackageReferences = [
"CommunityToolkit.Mvvm",
"CommunityToolkit.WinUI.Converters",
"Microsoft.Extensions.AI"
],
SharedCode = [
Expand Down Expand Up @@ -73,6 +74,8 @@ protected override async Task LoadModelAsync(SampleNavigationParameters samplePa
private void Page_Loaded()
{
InputBox.Focus(FocusState.Programmatic);
UpdateRewriteButtonState();
UpdateClearButtonState();
}

// </exclude>
Expand Down Expand Up @@ -142,8 +145,10 @@ private void AddMessage(string text)
}

Messages.Add(new Message(text.Trim(), DateTime.Now, ChatRole.User));
UpdateRewriteButtonState();
UpdateClearButtonState();
var contentStartedBeingGenerated = false; // <exclude-line>
NarratorHelper.Announce(InputBox, "Generating response, please wait.", "ChatWaitAnnouncementActivityId"); // <exclude-line>>
NarratorHelper.Announce(InputBox, "Generating response, please wait.", "ChatWaitAnnouncementActivityId"); // <exclude-line>
SendSampleInteractedEvent("AddMessage"); // <exclude-line>

Task.Run(async () =>
Expand All @@ -158,6 +163,7 @@ private void AddMessage(string text)
DispatcherQueue.TryEnqueue(() =>
{
Messages.Add(responseMessage);
UpdateClearButtonState();
StopBtn.Visibility = Visibility.Visible;
InputBox.IsEnabled = false;
InputBox.PlaceholderText = "Please wait for the response to complete before entering a new prompt";
Expand Down Expand Up @@ -336,6 +342,18 @@ private void StopBtn_Click(object sender, RoutedEventArgs e)
CancelResponse();
}

private void ClearBtn_Click(object sender, RoutedEventArgs e)
{
// Cancel any ongoing response generation before clearing chat
CancelResponse();
ClearChat();
}

private void RewriteBtn_Click(object sender, RoutedEventArgs e)
{
RewriteLastMessage();
}

private void InputBox_TextChanged(object sender, TextChangedEventArgs e)
{
SendBtn.IsEnabled = !string.IsNullOrWhiteSpace(InputBox.Text);
Expand All @@ -347,6 +365,44 @@ private void EnableInputBoxWithPlaceholder()
InputBox.PlaceholderText = "Enter your prompt (Press Shift + Enter to insert a newline)";
}

private void ClearChat()
{
Messages.Clear();
UpdateRewriteButtonState();
UpdateClearButtonState();
SendSampleInteractedEvent("ClearChat"); // <exclude-line>
}

private void RewriteLastMessage()
{
var lastUserMessage = Messages.LastOrDefault(m => m.Role == ChatRole.User);
if (lastUserMessage != null)
{
InputBox.Text = lastUserMessage.Content;
InputBox.Focus(FocusState.Programmatic);

InputBox.SelectionStart = InputBox.Text.Length;
InputBox.SelectionLength = 0;
SendSampleInteractedEvent("RewriteLastMessage"); // <exclude-line>
}
}

private void UpdateRewriteButtonState()
{
foreach (var message in Messages.Where(m => m.Role == ChatRole.User))
{
message.IsLastUserMessage = false;
}

var lastUserMessage = Messages.LastOrDefault(m => m.Role == ChatRole.User);
lastUserMessage?.IsLastUserMessage = true;
}

private void UpdateClearButtonState()
{
ClearBtn.IsEnabled = Messages.Count > 0;
}

private void InvertedListView_Loaded(object sender, RoutedEventArgs e)
{
scrollViewer = FindElement<ScrollViewer>(InvertedListView);
Expand Down
3 changes: 3 additions & 0 deletions AIDevGallery/Samples/SharedCode/IChatClient/Message.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ internal partial class Message : ObservableObject

public ChatRole Role { get; set; }

[ObservableProperty]
public partial bool IsLastUserMessage { get; set; }

public Message(string content, DateTime dateTime, ChatRole role)
{
Content = content;
Expand Down