using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; namespace AxCopilot.Views; public partial class ChatWindow { private async Task ShowInlineUserAskAsync(string question, List options, string defaultValue) { var tcs = new TaskCompletionSource(); await Dispatcher.InvokeAsync(() => { AddUserAskCard(question, options, defaultValue, tcs); }); var completed = await Task.WhenAny(tcs.Task, Task.Delay(TimeSpan.FromMinutes(5))); if (completed != tcs.Task) { await Dispatcher.InvokeAsync(RemoveUserAskCard); return null; } return await tcs.Task; } private void RemoveUserAskCard() { if (_userAskCard == null) return; RemoveTranscriptElement(_userAskCard); _userAskCard = null; } private void AddUserAskCard(string question, List options, string defaultValue, TaskCompletionSource tcs) { RemoveUserAskCard(); var accentBrush = TryFindResource("AccentColor") as Brush ?? Brushes.CornflowerBlue; var accentColor = ((SolidColorBrush)accentBrush).Color; var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.White; var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray; var borderBrush = TryFindResource("BorderColor") as Brush ?? new SolidColorBrush(Color.FromArgb(0x24, accentColor.R, accentColor.G, accentColor.B)); var itemBg = TryFindResource("ItemBackground") as Brush ?? new SolidColorBrush(Color.FromArgb(0x10, accentColor.R, accentColor.G, accentColor.B)); var hoverBg = TryFindResource("ItemHoverBackground") as Brush ?? new SolidColorBrush(Color.FromArgb(0x16, 0xFF, 0xFF, 0xFF)); var selectedBg = new SolidColorBrush(Color.FromArgb(0x20, accentColor.R, accentColor.G, accentColor.B)); var container = new Border { Margin = new Thickness(48, 6, 48, 10), HorizontalAlignment = HorizontalAlignment.Stretch, MaxWidth = Math.Max(540, GetMessageMaxWidth()), Background = itemBg, BorderBrush = borderBrush, BorderThickness = new Thickness(1), CornerRadius = new CornerRadius(16), Padding = new Thickness(20, 18, 20, 18), }; var outer = new StackPanel(); // ── 질문 텍스트 ── outer.Children.Add(new TextBlock { Text = question, FontSize = 14, Foreground = primaryText, TextWrapping = TextWrapping.Wrap, LineHeight = 22, Margin = new Thickness(0, 0, 0, 14), }); // ── 옵션 카드 목록 ── Border? selectedCard = null; TextBox? customInputBox = null; string selectedResponse = defaultValue; var cardPanel = new StackPanel { Margin = new Thickness(0, 0, 0, 4) }; if (options.Count > 0) { for (int i = 0; i < options.Count; i++) { var optionLabel = options[i].Trim(); if (string.IsNullOrWhiteSpace(optionLabel)) continue; var optionNumber = i + 1; var card = new Border { Background = Brushes.Transparent, BorderBrush = borderBrush, BorderThickness = new Thickness(1), CornerRadius = new CornerRadius(10), Padding = new Thickness(14, 10, 14, 10), Margin = new Thickness(0, 0, 0, 6), Cursor = Cursors.Hand, }; var cardGrid = new Grid(); cardGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); cardGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); var textStack = new StackPanel(); textStack.Children.Add(new TextBlock { Text = optionLabel, FontSize = 13, FontWeight = FontWeights.SemiBold, Foreground = primaryText, TextWrapping = TextWrapping.Wrap, }); Grid.SetColumn(textStack, 0); cardGrid.Children.Add(textStack); // 번호 배지 var badge = new Border { Width = 24, Height = 24, CornerRadius = new CornerRadius(12), Background = new SolidColorBrush(Color.FromArgb(0x20, 0xFF, 0xFF, 0xFF)), VerticalAlignment = VerticalAlignment.Center, Child = new TextBlock { Text = optionNumber.ToString(), FontSize = 11, FontWeight = FontWeights.Bold, Foreground = secondaryText, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center, }, }; Grid.SetColumn(badge, 1); cardGrid.Children.Add(badge); card.Child = cardGrid; var capturedLabel = optionLabel; var capturedCard = card; card.MouseEnter += (_, _) => { if (!ReferenceEquals(selectedCard, capturedCard)) capturedCard.Background = hoverBg; }; card.MouseLeave += (_, _) => { if (!ReferenceEquals(selectedCard, capturedCard)) capturedCard.Background = Brushes.Transparent; }; card.MouseLeftButtonUp += (_, _) => { // 이전 선택 해제 if (selectedCard != null) { selectedCard.Background = Brushes.Transparent; selectedCard.BorderBrush = borderBrush; } // 커스텀 입력 카드 선택 해제 if (customInputBox != null) customInputBox.BorderBrush = borderBrush; selectedCard = capturedCard; selectedCard.Background = selectedBg; selectedCard.BorderBrush = accentBrush; selectedResponse = capturedLabel; }; cardPanel.Children.Add(card); } } // ── 마지막 카드: 직접 입력 (Claude Desktop 스타일) ── var customCard = new Border { Background = Brushes.Transparent, BorderBrush = borderBrush, BorderThickness = new Thickness(1), CornerRadius = new CornerRadius(10), Padding = new Thickness(2), Margin = new Thickness(0, 0, 0, 6), }; customInputBox = new TextBox { Text = defaultValue, AcceptsReturn = true, TextWrapping = TextWrapping.Wrap, MinHeight = 38, MaxHeight = 120, FontSize = 13, Padding = new Thickness(12, 8, 12, 8), Background = Brushes.Transparent, Foreground = primaryText, CaretBrush = primaryText, BorderThickness = new Thickness(0), }; // 플레이스홀더 if (string.IsNullOrWhiteSpace(defaultValue)) { customInputBox.Tag = "placeholder"; customInputBox.Text = "직접 입력..."; customInputBox.Foreground = secondaryText; } customInputBox.GotFocus += (_, _) => { if (customInputBox.Tag?.ToString() == "placeholder") { customInputBox.Text = ""; customInputBox.Foreground = primaryText; customInputBox.Tag = null; } // 옵션 카드 선택 해제 + 커스텀 카드 활성화 if (selectedCard != null) { selectedCard.Background = Brushes.Transparent; selectedCard.BorderBrush = borderBrush; selectedCard = null; } customCard.BorderBrush = accentBrush; customCard.Background = selectedBg; }; customInputBox.TextChanged += (_, _) => { if (customInputBox.Tag?.ToString() != "placeholder" && !string.IsNullOrWhiteSpace(customInputBox.Text)) selectedResponse = customInputBox.Text.Trim(); }; customCard.Child = customInputBox; cardPanel.Children.Add(customCard); outer.Children.Add(cardPanel); // ── 하단 버튼 ── var buttonRow = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Left, Margin = new Thickness(0, 8, 0, 0), }; var skipBtn = new Border { Background = Brushes.Transparent, BorderBrush = borderBrush, BorderThickness = new Thickness(1), CornerRadius = new CornerRadius(8), Padding = new Thickness(16, 7, 16, 7), Margin = new Thickness(0, 0, 8, 0), Cursor = Cursors.Hand, Child = new TextBlock { Text = "건너뛰기", FontSize = 12.5, Foreground = secondaryText }, }; skipBtn.MouseEnter += (_, _) => skipBtn.Background = hoverBg; skipBtn.MouseLeave += (_, _) => skipBtn.Background = Brushes.Transparent; skipBtn.MouseLeftButtonUp += (_, _) => { RemoveUserAskCard(); tcs.TrySetResult(null); }; buttonRow.Children.Add(skipBtn); var submitBtn = new Border { Background = accentBrush, BorderThickness = new Thickness(0), CornerRadius = new CornerRadius(8), Padding = new Thickness(16, 7, 16, 7), Cursor = Cursors.Hand, Child = new TextBlock { Text = "제출", FontSize = 12.5, FontWeight = FontWeights.SemiBold, Foreground = Brushes.White }, }; submitBtn.MouseLeftButtonUp += (_, _) => { var finalResponse = (customInputBox.Tag?.ToString() != "placeholder" && !string.IsNullOrWhiteSpace(customInputBox.Text)) ? customInputBox.Text.Trim() : selectedResponse?.Trim(); if (string.IsNullOrWhiteSpace(finalResponse)) finalResponse = defaultValue?.Trim(); if (string.IsNullOrWhiteSpace(finalResponse)) return; RemoveUserAskCard(); tcs.TrySetResult(finalResponse); }; buttonRow.Children.Add(submitBtn); outer.Children.Add(buttonRow); container.Child = outer; _userAskCard = container; container.Opacity = 0; container.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(180))); AddTranscriptElement(container); ForceScrollToEnd(); } }