- claude-code 선택적 탐색 흐름을 참고해 Cowork/Code 시스템 프롬프트에서 folder_map 상시 선행 지시를 완화하고 glob/grep 기반 좁은 탐색을 우선하도록 조정함 - FolderMapTool 기본 depth를 2로, include_files 기본값을 false로 낮추고 MultiReadTool 최대 파일 수를 8개로 줄여 초기 과탐색 폭을 보수적으로 조정함 - AgentLoopExplorationPolicy partial을 추가해 탐색 범위 분류, broad-scan corrective hint, exploration_breadth 성능 로그를 연결함 - AgentLoopService에 탐색 범위 가이드 주입과 실행 중 탐색 폭 추적을 추가하고, 좁은 질문에서 반복적인 folder_map/대량 multi_read를 교정하도록 정리함 - DocxToHtmlConverter nullable 경고를 수정해 Release 빌드 경고 0 / 오류 0 기준을 다시 충족함 - README와 docs/DEVELOPMENT.md에 2026-04-09 10:36 (KST) 기준 개발 이력을 반영함
This commit is contained in:
@@ -51,185 +51,236 @@ public partial class ChatWindow
|
||||
?? 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 okBrush = BrushFromHex("#10B981");
|
||||
var dangerBrush = BrushFromHex("#EF4444");
|
||||
var selectedBg = new SolidColorBrush(Color.FromArgb(0x20, accentColor.R, accentColor.G, accentColor.B));
|
||||
|
||||
var container = new Border
|
||||
{
|
||||
Margin = new Thickness(40, 4, 90, 8),
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
MaxWidth = Math.Max(420, GetMessageMaxWidth() - 36),
|
||||
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(14),
|
||||
Padding = new Thickness(14, 12, 14, 12),
|
||||
CornerRadius = new CornerRadius(16),
|
||||
Padding = new Thickness(20, 18, 20, 18),
|
||||
};
|
||||
|
||||
var outer = new StackPanel();
|
||||
outer.Children.Add(new TextBlock
|
||||
{
|
||||
Text = "의견 요청",
|
||||
FontSize = 12.5,
|
||||
FontWeight = FontWeights.SemiBold,
|
||||
Foreground = primaryText,
|
||||
});
|
||||
|
||||
// ── 질문 텍스트 ──
|
||||
outer.Children.Add(new TextBlock
|
||||
{
|
||||
Text = question,
|
||||
Margin = new Thickness(0, 4, 0, 10),
|
||||
FontSize = 12.5,
|
||||
FontSize = 14,
|
||||
Foreground = primaryText,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
LineHeight = 20,
|
||||
LineHeight = 22,
|
||||
Margin = new Thickness(0, 0, 0, 14),
|
||||
});
|
||||
|
||||
Border? selectedOption = null;
|
||||
// ── 옵션 카드 목록 ──
|
||||
Border? selectedCard = null;
|
||||
TextBox? customInputBox = null;
|
||||
string selectedResponse = defaultValue;
|
||||
|
||||
var cardPanel = new StackPanel { Margin = new Thickness(0, 0, 0, 4) };
|
||||
|
||||
if (options.Count > 0)
|
||||
{
|
||||
var optionPanel = new WrapPanel
|
||||
for (int i = 0; i < options.Count; i++)
|
||||
{
|
||||
Margin = new Thickness(0, 0, 0, 10),
|
||||
ItemWidth = double.NaN,
|
||||
};
|
||||
var optionLabel = options[i].Trim();
|
||||
if (string.IsNullOrWhiteSpace(optionLabel)) continue;
|
||||
var optionNumber = i + 1;
|
||||
|
||||
foreach (var option in options.Where(static option => !string.IsNullOrWhiteSpace(option)))
|
||||
{
|
||||
var optionLabel = option.Trim();
|
||||
var optBorder = new Border
|
||||
var card = new Border
|
||||
{
|
||||
Background = Brushes.Transparent,
|
||||
BorderBrush = borderBrush,
|
||||
BorderThickness = new Thickness(1),
|
||||
CornerRadius = new CornerRadius(999),
|
||||
Padding = new Thickness(10, 6, 10, 6),
|
||||
Margin = new Thickness(0, 0, 8, 8),
|
||||
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 = optionLabel,
|
||||
FontSize = 12,
|
||||
Foreground = primaryText,
|
||||
Text = optionNumber.ToString(),
|
||||
FontSize = 11,
|
||||
FontWeight = FontWeights.Bold,
|
||||
Foreground = secondaryText,
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
},
|
||||
};
|
||||
Grid.SetColumn(badge, 1);
|
||||
cardGrid.Children.Add(badge);
|
||||
|
||||
optBorder.MouseEnter += (s, _) =>
|
||||
card.Child = cardGrid;
|
||||
|
||||
var capturedLabel = optionLabel;
|
||||
var capturedCard = card;
|
||||
card.MouseEnter += (_, _) =>
|
||||
{
|
||||
if (!ReferenceEquals(selectedOption, s))
|
||||
((Border)s).Background = hoverBg;
|
||||
if (!ReferenceEquals(selectedCard, capturedCard))
|
||||
capturedCard.Background = hoverBg;
|
||||
};
|
||||
optBorder.MouseLeave += (s, _) =>
|
||||
card.MouseLeave += (_, _) =>
|
||||
{
|
||||
if (!ReferenceEquals(selectedOption, s))
|
||||
((Border)s).Background = Brushes.Transparent;
|
||||
if (!ReferenceEquals(selectedCard, capturedCard))
|
||||
capturedCard.Background = Brushes.Transparent;
|
||||
};
|
||||
optBorder.MouseLeftButtonUp += (_, _) =>
|
||||
card.MouseLeftButtonUp += (_, _) =>
|
||||
{
|
||||
if (selectedOption != null)
|
||||
// 이전 선택 해제
|
||||
if (selectedCard != null)
|
||||
{
|
||||
selectedOption.Background = Brushes.Transparent;
|
||||
selectedOption.BorderBrush = borderBrush;
|
||||
selectedCard.Background = Brushes.Transparent;
|
||||
selectedCard.BorderBrush = borderBrush;
|
||||
}
|
||||
// 커스텀 입력 카드 선택 해제
|
||||
if (customInputBox != null)
|
||||
customInputBox.BorderBrush = borderBrush;
|
||||
|
||||
selectedOption = optBorder;
|
||||
selectedOption.Background = new SolidColorBrush(Color.FromArgb(0x18, accentColor.R, accentColor.G, accentColor.B));
|
||||
selectedOption.BorderBrush = accentBrush;
|
||||
selectedResponse = optionLabel;
|
||||
selectedCard = capturedCard;
|
||||
selectedCard.Background = selectedBg;
|
||||
selectedCard.BorderBrush = accentBrush;
|
||||
selectedResponse = capturedLabel;
|
||||
};
|
||||
|
||||
optionPanel.Children.Add(optBorder);
|
||||
cardPanel.Children.Add(card);
|
||||
}
|
||||
|
||||
outer.Children.Add(optionPanel);
|
||||
}
|
||||
|
||||
outer.Children.Add(new TextBlock
|
||||
// ── 마지막 카드: 직접 입력 (Claude Desktop 스타일) ──
|
||||
var customCard = new Border
|
||||
{
|
||||
Text = "직접 입력",
|
||||
FontSize = 11.5,
|
||||
Foreground = secondaryText,
|
||||
Background = Brushes.Transparent,
|
||||
BorderBrush = borderBrush,
|
||||
BorderThickness = new Thickness(1),
|
||||
CornerRadius = new CornerRadius(10),
|
||||
Padding = new Thickness(2),
|
||||
Margin = new Thickness(0, 0, 0, 6),
|
||||
});
|
||||
};
|
||||
|
||||
var inputBox = new TextBox
|
||||
customInputBox = new TextBox
|
||||
{
|
||||
Text = defaultValue,
|
||||
AcceptsReturn = true,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
MinHeight = 42,
|
||||
MaxHeight = 100,
|
||||
FontSize = 12.5,
|
||||
Padding = new Thickness(10, 8, 10, 8),
|
||||
MinHeight = 38,
|
||||
MaxHeight = 120,
|
||||
FontSize = 13,
|
||||
Padding = new Thickness(12, 8, 12, 8),
|
||||
Background = Brushes.Transparent,
|
||||
Foreground = primaryText,
|
||||
CaretBrush = primaryText,
|
||||
BorderBrush = borderBrush,
|
||||
BorderThickness = new Thickness(1),
|
||||
BorderThickness = new Thickness(0),
|
||||
};
|
||||
inputBox.TextChanged += (_, _) =>
|
||||
// 플레이스홀더
|
||||
if (string.IsNullOrWhiteSpace(defaultValue))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(inputBox.Text))
|
||||
customInputBox.Tag = "placeholder";
|
||||
customInputBox.Text = "직접 입력...";
|
||||
customInputBox.Foreground = secondaryText;
|
||||
}
|
||||
customInputBox.GotFocus += (_, _) =>
|
||||
{
|
||||
if (customInputBox.Tag?.ToString() == "placeholder")
|
||||
{
|
||||
selectedResponse = inputBox.Text.Trim();
|
||||
if (selectedOption != null)
|
||||
{
|
||||
selectedOption.Background = Brushes.Transparent;
|
||||
selectedOption.BorderBrush = borderBrush;
|
||||
selectedOption = null;
|
||||
}
|
||||
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();
|
||||
};
|
||||
outer.Children.Add(inputBox);
|
||||
|
||||
customCard.Child = customInputBox;
|
||||
cardPanel.Children.Add(customCard);
|
||||
outer.Children.Add(cardPanel);
|
||||
|
||||
// ── 하단 버튼 ──
|
||||
var buttonRow = new StackPanel
|
||||
{
|
||||
Orientation = Orientation.Horizontal,
|
||||
HorizontalAlignment = HorizontalAlignment.Right,
|
||||
Margin = new Thickness(0, 12, 0, 0),
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
Margin = new Thickness(0, 8, 0, 0),
|
||||
};
|
||||
|
||||
Border BuildActionButton(string label, Brush bg, Brush fg)
|
||||
var skipBtn = new Border
|
||||
{
|
||||
return new Border
|
||||
{
|
||||
Background = bg,
|
||||
BorderBrush = bg,
|
||||
BorderThickness = new Thickness(1),
|
||||
CornerRadius = new CornerRadius(999),
|
||||
Padding = new Thickness(12, 7, 12, 7),
|
||||
Margin = new Thickness(8, 0, 0, 0),
|
||||
Cursor = Cursors.Hand,
|
||||
Child = new TextBlock
|
||||
{
|
||||
Text = label,
|
||||
FontSize = 12,
|
||||
FontWeight = FontWeights.SemiBold,
|
||||
Foreground = fg,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
var cancelBtn = BuildActionButton("취소", Brushes.Transparent, dangerBrush);
|
||||
cancelBtn.BorderBrush = new SolidColorBrush(Color.FromArgb(0x40, 0xEF, 0x44, 0x44));
|
||||
cancelBtn.MouseLeftButtonUp += (_, _) =>
|
||||
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(cancelBtn);
|
||||
buttonRow.Children.Add(skipBtn);
|
||||
|
||||
var submitBtn = BuildActionButton("전달", okBrush, Brushes.White);
|
||||
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 = !string.IsNullOrWhiteSpace(inputBox.Text)
|
||||
? inputBox.Text.Trim()
|
||||
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;
|
||||
|
||||
@@ -237,8 +288,8 @@ public partial class ChatWindow
|
||||
tcs.TrySetResult(finalResponse);
|
||||
};
|
||||
buttonRow.Children.Add(submitBtn);
|
||||
|
||||
outer.Children.Add(buttonRow);
|
||||
|
||||
container.Child = outer;
|
||||
_userAskCard = container;
|
||||
|
||||
@@ -246,7 +297,5 @@ public partial class ChatWindow
|
||||
container.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(180)));
|
||||
AddTranscriptElement(container);
|
||||
ForceScrollToEnd();
|
||||
inputBox.Focus();
|
||||
inputBox.CaretIndex = inputBox.Text.Length;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user