채팅 본문 폭과 진행 로그 줄바꿈 레이아웃 개선

채팅 본문과 process feed가 큰 창의 오른쪽 여백을 더 활용하도록 반응형 폭 계산을 조정했습니다. 본문 최대폭을 1040, 입력창 폭을 980 기준까지 확장하고 MessageList 최대폭을 함께 늘려 조기 말줄임이 덜 발생하도록 정리했습니다.

process feed 헤더, 실행 이력 요약, 라이브 thinking 줄은 줄바꿈 우선으로 바꾸고 필요한 미리보기 카드에만 ellipsis를 남겼습니다. 입력창 위 StreamMetricsLabel과 PulseDotBar 간격도 다시 맞춰 우측 시간·토큰 표시가 좌측 진행 문구 폭을 과하게 잠식하지 않도록 보정했습니다.

검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify_chat_width_wrap\ -p:IntermediateOutputPath=obj\verify_chat_width_wrap\ (경고 0 / 오류 0), dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter ChatStreamingUiPolicyTests^|ChatWindowSlashPolicyTests -p:OutputPath=bin\verify_chat_width_wrap_tests\ -p:IntermediateOutputPath=obj\verify_chat_width_wrap_tests\ (통과 74)
This commit is contained in:
2026-04-15 23:54:50 +09:00
parent 6810fb1954
commit 5161e46ac2
6 changed files with 63 additions and 26 deletions

View File

@@ -287,6 +287,7 @@ public partial class ChatWindow
MaxWidth = msgMaxWidth,
Margin = new Thickness(62, 1, 12, 1),
};
var headerTextMaxWidth = Math.Max(180, msgMaxWidth - 28);
if (liveWaitingStyle)
{
@@ -324,6 +325,8 @@ public partial class ChatWindow
FontSize = 12,
Foreground = secondaryText,
VerticalAlignment = VerticalAlignment.Center,
TextWrapping = TextWrapping.Wrap,
MaxWidth = headerTextMaxWidth,
});
stack.Children.Add(waitRow);
}
@@ -360,7 +363,8 @@ public partial class ChatWindow
FontSize = 12,
Foreground = headerColor,
VerticalAlignment = VerticalAlignment.Center,
TextTrimming = TextTrimming.CharacterEllipsis,
TextWrapping = TextWrapping.Wrap,
MaxWidth = headerTextMaxWidth,
});
stack.Children.Add(headerRow);
@@ -543,7 +547,8 @@ public partial class ChatWindow
FontSize = 12,
Foreground = secondaryText,
VerticalAlignment = VerticalAlignment.Center,
TextTrimming = TextTrimming.CharacterEllipsis,
TextWrapping = TextWrapping.Wrap,
MaxWidth = Math.Max(180, msgMaxWidth - 28),
});
container.Children.Add(headerRow);
@@ -568,7 +573,8 @@ public partial class ChatWindow
FontSize = 11,
Foreground = secondaryText,
Opacity = 0.75,
TextTrimming = TextTrimming.CharacterEllipsis,
TextWrapping = TextWrapping.Wrap,
MaxWidth = Math.Max(180, msgMaxWidth - 16),
Margin = new Thickness(0, 1, 0, 0),
};
if (!string.IsNullOrWhiteSpace(evt.FilePath))
@@ -1842,6 +1848,7 @@ public partial class ChatWindow
MaxWidth = msgMaxWidth2,
Margin = new Thickness(62, 1, 12, 1),
};
var headerTextMaxWidth2 = Math.Max(180, msgMaxWidth2 - 28);
if (!string.IsNullOrWhiteSpace(evt.RunId))
_runBannerAnchors[evt.RunId] = stack2;
@@ -1874,7 +1881,8 @@ public partial class ChatWindow
FontSize = 12,
Foreground = headerColor2,
VerticalAlignment = VerticalAlignment.Center,
TextTrimming = TextTrimming.CharacterEllipsis,
TextWrapping = TextWrapping.Wrap,
MaxWidth = headerTextMaxWidth2,
});
stack2.Children.Add(headerRow2);

View File

@@ -212,16 +212,19 @@ public partial class ChatWindow
/// <summary>채팅 본문 폭을 세 탭에서 동일한 기준으로 맞춥니다.</summary>
private double GetMessageMaxWidth()
{
if (_lastResponsiveMessageWidth >= 100)
return Math.Clamp(_lastResponsiveMessageWidth, 320, 1040);
var hostWidth = _lastResponsiveComposerWidth;
if (hostWidth < 100)
hostWidth = ComposerShell?.ActualWidth ?? 0;
if (hostWidth < 100)
hostWidth = MessageList?.ActualWidth ?? 0;
if (hostWidth < 100)
hostWidth = 1120;
hostWidth = 1280;
var maxW = hostWidth - 44;
return Math.Clamp(maxW, 320, 760);
var maxW = hostWidth - 24;
return Math.Clamp(maxW, 320, 1040);
}
private bool UpdateResponsiveChatLayout()
@@ -234,17 +237,17 @@ public partial class ChatWindow
// 메시지 축과 입력축이 같은 중심선을 공유하도록
// 본문 폭 상한을 조금 더 낮추고 창 폭 변화에 더 부드럽게 반응시킵니다.
var contentWidth = Math.Max(360, viewportWidth - 24);
// 고정 최대폭 — 큰 창에서 안정적, 작은 창에서만 축소
var messageWidth = Math.Min(contentWidth - 10, 800);
var composerWidth = Math.Min(contentWidth - 24, 760);
var contentWidth = Math.Max(360, viewportWidth - 12);
// 큰 창에서는 본문을 더 넓게 쓰고, 입력창은 약간만 좁게 유지합니다.
var messageWidth = Math.Min(contentWidth - 8, 1040);
var composerWidth = Math.Min(contentWidth - 12, 980);
var changed = false;
if (Math.Abs(_lastResponsiveMessageWidth - messageWidth) > 8)
{
_lastResponsiveMessageWidth = messageWidth;
if (MessageList != null)
MessageList.MaxWidth = messageWidth + 48;
MessageList.MaxWidth = messageWidth + 64;
if (EmptyState != null)
EmptyState.MaxWidth = messageWidth;
changed = true;

View File

@@ -338,18 +338,21 @@ public partial class ChatWindow
case AgentEventType.Thinking:
{
var thinkingSummaryMaxLength = msgMaxWidth > 900 ? 220 : 140;
var thinkText = AgentProgressSummarySanitizer.NormalizeThinkingSummary(
agentEvent.Summary,
agentEvent.ToolName,
maxLength: 100);
maxLength: thinkingSummaryMaxLength);
if (string.IsNullOrWhiteSpace(thinkText)) break;
var thinkRow = new StackPanel
var thinkRow = new Grid
{
Orientation = Orientation.Horizontal,
Margin = new Thickness(22, 2, 0, 2),
};
thinkRow.Children.Add(new TextBlock
thinkRow.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
thinkRow.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
var thinkIcon = new TextBlock
{
Text = "\uE915",
FontFamily = s_segoeIconFont,
@@ -357,17 +360,23 @@ public partial class ChatWindow
Foreground = new SolidColorBrush(Color.FromRgb(0x59, 0xA5, 0xF5)),
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0, 0, 4, 0),
});
thinkRow.Children.Add(new TextBlock
};
Grid.SetColumn(thinkIcon, 0);
thinkRow.Children.Add(thinkIcon);
var thinkLabel = new TextBlock
{
Text = thinkText,
FontSize = 10.5,
FontStyle = FontStyles.Italic,
Foreground = secondaryText,
Opacity = 0.82,
TextTrimming = TextTrimming.CharacterEllipsis,
MaxWidth = msgMaxWidth - 60,
});
TextWrapping = TextWrapping.Wrap,
MaxWidth = Math.Max(180, msgMaxWidth - 52),
VerticalAlignment = VerticalAlignment.Center,
};
Grid.SetColumn(thinkLabel, 1);
thinkRow.Children.Add(thinkLabel);
_v2LiveContainer.Children.Add(thinkRow);
AutoScrollIfNeeded();
break;
@@ -432,11 +441,12 @@ public partial class ChatWindow
return;
var narrative = AgentStatusNarrativeCatalog.BuildFromEvent(agentEvent, _activeTab);
var statusSummaryMaxLength = GetMessageMaxWidth() > 900 ? 260 : 180;
var normalizedThinking = agentEvent.Type == AgentEventType.Thinking
? AgentProgressSummarySanitizer.NormalizeThinkingSummary(
agentEvent.Summary,
agentEvent.ToolName,
maxLength: 160)
maxLength: statusSummaryMaxLength)
: string.Empty;
var message = string.IsNullOrWhiteSpace(normalizedThinking)
? narrative.Message

View File

@@ -2166,7 +2166,7 @@
<Border x:Name="ComposerShell" Grid.Row="4"
Margin="24,0,24,12"
Width="Auto"
MaxWidth="900"
MaxWidth="980"
HorizontalAlignment="Center"
VerticalAlignment="Bottom">
<StackPanel HorizontalAlignment="Stretch">
@@ -2181,9 +2181,9 @@
<Border x:Name="PulseDotBar"
Grid.Column="0"
Visibility="Collapsed"
HorizontalAlignment="Left"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Margin="6,0,0,0">
Margin="6,0,18,0">
<StackPanel>
<!-- 레거시 펄스 점 (코드비하인드 참조용, 화면에 표시 안 함) -->
<Ellipse x:Name="PulseDot1" Width="7" Height="7" Fill="{DynamicResource AccentColor}" Opacity="0.3" Visibility="Collapsed"/>
@@ -2262,7 +2262,7 @@
Visibility="Collapsed"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Margin="12,0,6,2"
Margin="18,0,0,2"
FontSize="11"
Foreground="{DynamicResource SecondaryText}"
Opacity="0.7"