AX Agent 진행 메시지 폭·정렬 및 인코딩 표시 문제 수정
- 앱 생성 진행/도구/완료 카드에 전용 최대폭을 도입하고 좌측 정렬로 통일 - 라이브 진행 카드와 검증 게이트 문구에서 깨져 보이던 문자열을 정상화 - build_run/process 도구가 Windows 기본 출력 인코딩을 우선 사용하도록 조정 - README와 DEVELOPMENT 문서에 2026-04-16 00:57 (KST) 기준 이력 반영 검증: - dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify_agent_ui_layout_encoding\ -p:IntermediateOutputPath=obj\verify_agent_ui_layout_encoding\ (경고 0 / 오류 0) - dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "ChatWindowSlashPolicyTests|AgentLoopCodeQualityTests" -p:OutputPath=bin\verify_agent_ui_layout_encoding_tests\ -p:IntermediateOutputPath=obj\verify_agent_ui_layout_encoding_tests\ (통과 194)
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
# AX Commander
|
||||
|
||||
- 업데이트: 2026-04-16 00:57 (KST)
|
||||
- AX Agent 앱 생성 메시지의 가로 폭과 정렬을 다시 다듬었습니다. `src/AxCopilot/Views/ChatWindow.ResponsePresentation.cs`에 `GetAgentEventMaxWidth()`를 추가해 앱이 그리는 진행/도구/완료 카드 폭만 별도로 줄였고, `src/AxCopilot/Views/ChatWindow.AgentEventRendering.cs`, `src/AxCopilot/Views/ChatWindow.V2LiveProgressPresentation.cs`, `src/AxCopilot/Views/ChatWindow.V2AgentEventPresentation.cs`는 해당 폭을 사용하면서 중앙 정렬 대신 좌측 기준으로 붙도록 맞췄습니다.
|
||||
- 라이브 진행 카드와 검증 게이트 문구에서 깨져 보이던 문자열도 함께 정리했습니다. `src/AxCopilot/Services/Agent/AgentLoopTransitions.Verification.cs`의 검증/재시도 안내 문구를 정상 한국어로 교체했고, `src/AxCopilot/Views/ChatWindow.V2LiveProgressPresentation.cs`, `src/AxCopilot/Views/ChatWindow.V2AgentEventPresentation.cs`에는 런타임에 보이는 상태 문구를 안정적인 문자열로 다시 연결했습니다.
|
||||
- 프로세스 출력 인코딩은 `src/AxCopilot/Services/Agent/BuildRunTool.cs`, `src/AxCopilot/Services/Agent/ProcessTool.cs`에서 Windows 기본 출력 인코딩을 우선 사용하도록 바꿔 build/test나 셸 실행 결과가 UTF-8 고정 디코딩 때문에 깨질 가능성을 줄였습니다.
|
||||
- 검증:
|
||||
- `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_agent_ui_layout_encoding\\ -p:IntermediateOutputPath=obj\\verify_agent_ui_layout_encoding\\` 경고 0 / 오류 0
|
||||
- `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "ChatWindowSlashPolicyTests|AgentLoopCodeQualityTests" -p:OutputPath=bin\\verify_agent_ui_layout_encoding_tests\\ -p:IntermediateOutputPath=obj\\verify_agent_ui_layout_encoding_tests\\` 통과 194
|
||||
|
||||
- 업데이트: 2026-04-16 00:01 (KST)
|
||||
- AX Agent 스크롤 맨아래 이동 FAB가 입력창 아래로 잘려 보이던 위치 문제를 조정했습니다. `src/AxCopilot/Views/ChatWindow.xaml`에서 버튼을 `Grid.RowSpan="2"`로 옮겨 메시지 영역과 입력 바를 함께 기준으로 잡고, 기본 하단 여백도 더 넉넉하게 올렸습니다.
|
||||
- `src/AxCopilot/Views/ChatWindow.xaml.cs`에는 `UpdateScrollToBottomFabPosition()`을 추가해 `ComposerShell` 높이, 입력창 크기 변화, 창 리사이즈에 맞춰 버튼 하단 여백을 자동 계산하도록 연결했습니다. 이제 입력창이 커지거나 상태 행 높이가 변해도 FAB가 그 위에 안전하게 떠 있습니다.
|
||||
|
||||
@@ -1739,3 +1739,10 @@ UI ?遺우쁽????域뱀뮆???귐뗫솯?醫딆춦 ???袁る퓮 ?臾믩씜 ??疫
|
||||
- 검증:
|
||||
- `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_ppt_manifest_quality\\ -p:IntermediateOutputPath=obj\\verify_ppt_manifest_quality\\` 경고 0 / 오류 0
|
||||
- `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "PptQualityGatePolicyTests|PptxTemplateManifestCatalogTests|PptxSkillTemplatePackTests|PptxSkillTemplateDiagnosticsTests|PptxSkillAutoRepairTests|PptxSkillGoldenDeckTests" -p:OutputPath=bin\\verify_ppt_manifest_quality_tests\\ -p:IntermediateOutputPath=obj\\verify_ppt_manifest_quality_tests\\` 통과 15
|
||||
업데이트: 2026-04-16 00:57 (KST)
|
||||
- AX Agent 앱 생성 메시지 전용 폭을 분리했습니다. `src/AxCopilot/Views/ChatWindow.ResponsePresentation.cs`에 `GetAgentEventMaxWidth()`를 추가하고, `src/AxCopilot/Views/ChatWindow.AgentEventRendering.cs`, `src/AxCopilot/Views/ChatWindow.V2LiveProgressPresentation.cs`, `src/AxCopilot/Views/ChatWindow.V2AgentEventPresentation.cs`가 같은 값을 사용하도록 맞춰 진행 카드/도구 카드/완료 카드가 본문 전체 폭을 과하게 점유하지 않게 했습니다.
|
||||
- 라이브 진행 카드와 하단 상태 카드의 정렬도 함께 다듬었습니다. V2 라이브 컨테이너와 완료 카드 정렬을 좌측 기준으로 통일하고, 상태 텍스트의 `TextAlignment`를 왼쪽으로 고정해 요약/상세 줄이 카드 중앙이 아니라 본문 축에서 바로 읽히도록 정리했습니다.
|
||||
- 깨진 안내 문구는 두 경로에서 정리했습니다. `src/AxCopilot/Services/Agent/AgentLoopTransitions.Verification.cs`의 검증 게이트/재시도 이벤트 메시지를 정상 한국어로 교체했고, `src/AxCopilot/Views/ChatWindow.V2LiveProgressPresentation.cs`, `src/AxCopilot/Views/ChatWindow.V2AgentEventPresentation.cs`에는 런타임에 다시 덮어쓰는 안전한 상태 문구를 추가해 기존 깨진 문자열이 그대로 노출되지 않게 했습니다.
|
||||
- 프로세스 출력 인코딩은 `src/AxCopilot/Services/Agent/BuildRunTool.cs`, `src/AxCopilot/Services/Agent/ProcessTool.cs`에서 UTF-8 고정 대신 Windows 기본 출력 인코딩을 우선 사용하도록 조정했습니다. 한국어 콘솔 출력이 UTF-8로 강제 디코딩되며 깨질 수 있던 경로를 줄이기 위한 수정입니다.
|
||||
- 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_agent_ui_layout_encoding\\ -p:IntermediateOutputPath=obj\\verify_agent_ui_layout_encoding\\` 경고 0 / 오류 0
|
||||
- 검증: `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "ChatWindowSlashPolicyTests|AgentLoopCodeQualityTests" -p:OutputPath=bin\\verify_agent_ui_layout_encoding_tests\\ -p:IntermediateOutputPath=obj\\verify_agent_ui_layout_encoding_tests\\` 통과 194
|
||||
|
||||
@@ -28,8 +28,8 @@ public partial class AgentLoopService
|
||||
taskPolicy)
|
||||
});
|
||||
EmitEvent(AgentEventType.Thinking, "", highImpactCodeChange
|
||||
? "怨좎쁺??肄붾뱶 蹂寃쎌쑝濡?遺꾨쪟??李몄“ 寃利앷낵 build/test 寃利앹쓣 ???꾧꺽?섍쾶 ?댁뼱媛묐땲??"
|
||||
: "肄붾뱶 蹂寃???build/test/diff 寃利앹쓣 ?댁뼱媛묐땲??");
|
||||
? "고영향 코드 변경으로 분류되어 참조 검증과 build/test 검증을 더 엄격하게 이어갑니다."
|
||||
: "코드 변경 이후 build/test/diff 검증을 이어갑니다.");
|
||||
}
|
||||
else if (HasCodeVerificationEvidenceAfterLastModification(messages, requireHighImpactCodeVerification))
|
||||
{
|
||||
@@ -81,12 +81,12 @@ public partial class AgentLoopService
|
||||
{
|
||||
Role = "user",
|
||||
Content = requireHighImpactCodeVerification
|
||||
? "[System:CodeQualityGate] 怨듭슜/?듭떖 肄붾뱶 蹂寃??댄썑 寃利?洹쇨굅媛 遺議깊빀?덈떎. 醫낅즺?섏? 留먭퀬 file_read, grep/glob, git diff, build/test源뚯? ?뺤씤???ㅼ뿉留?留덈Т由ы븯?몄슂."
|
||||
: "[System:CodeQualityGate] 留덉?留?肄붾뱶 ?섏젙 ?댄썑 build/test/file_read/diff 洹쇨굅媛 遺議깊빀?덈떎. 醫낅즺?섏? 留먭퀬 寃利?洹쇨굅瑜?蹂닿컯???ㅼ뿉留?留덈Т由ы븯?몄슂."
|
||||
? "[System:CodeQualityGate] 공용/고영향 코드 변경 이후 검증 근거가 부족합니다. 종료하지 말고 file_read, grep/glob, git diff, build/test까지 확인한 뒤에만 마무리하세요."
|
||||
: "[System:CodeQualityGate] 마지막 코드 수정 이후 build/test/file_read/diff 근거가 부족합니다. 종료하지 말고 검증 근거를 보강한 뒤에만 마무리하세요."
|
||||
});
|
||||
EmitEvent(AgentEventType.Thinking, "", requireHighImpactCodeVerification
|
||||
? "?듭떖 肄붾뱶 蹂寃쎌쓽 寃利?洹쇨굅媛 遺議깊빐 異붽? 寃利앹쓣 吏꾪뻾?⑸땲??.."
|
||||
: "肄붾뱶 寃곌낵 寃利?洹쇨굅媛 遺議깊빐 異붽? 寃利앹쓣 吏꾪뻾?⑸땲??..");
|
||||
? "고영향 코드 변경의 검증 근거가 부족해 추가 검증을 진행합니다..."
|
||||
: "코드 결과 검증 근거가 부족해 추가 검증을 진행합니다...");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -101,9 +101,9 @@ public partial class AgentLoopService
|
||||
messages.Add(new ChatMessage
|
||||
{
|
||||
Role = "user",
|
||||
Content = "[System:HighImpactBuildTestGate] ?듭떖 肄붾뱶 蹂寃쎌엯?덈떎. 醫낅즺?섏? 留먭퀬 build_run怨?test_loop瑜?紐⑤몢 ?ㅽ뻾???깃났 洹쇨굅瑜??뺣낫???ㅼ뿉留?留덈Т由ы븯?몄슂."
|
||||
Content = "[System:HighImpactBuildTestGate] 고영향 코드 변경입니다. 종료하지 말고 build_run과 test_loop를 모두 실행해 성공 근거를 확보한 뒤에만 마무리하세요."
|
||||
});
|
||||
EmitEvent(AgentEventType.Thinking, "", "?듭떖 蹂寃쎌씠??build+test ?깃났 洹쇨굅瑜?紐⑤몢 ?뺣낫???뚭퉴吏 吏꾪뻾?⑸땲??..");
|
||||
EmitEvent(AgentEventType.Thinking, "", "고영향 변경이라 build와 test 성공 근거를 모두 확보할 때까지 진행합니다...");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ public partial class AgentLoopService
|
||||
Role = "user",
|
||||
Content = BuildFinalReportQualityPrompt(taskPolicy, requireHighImpactCodeVerification)
|
||||
});
|
||||
EmitEvent(AgentEventType.Thinking, "", "理쒖쥌 蹂닿퀬??蹂寃승룰?利씲룸━?ㅽ겕 ?붿빟??遺議깊빐 ??踰????뺣━?⑸땲??..");
|
||||
EmitEvent(AgentEventType.Thinking, "", "최종 보고에 변경·검증·리스크 요약이 부족해 한 번 더 정리합니다...");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ public partial class AgentLoopService
|
||||
EmitEvent(
|
||||
AgentEventType.Thinking,
|
||||
"",
|
||||
$"?꾨줈?앺듃 援ъ“媛 ?됰㈃?곸쑝濡??앹꽦???대뜑 ?덉씠?꾩썐??癒쇱? ?뺣━?⑸땲??({runState.ProjectLayoutGateRetry}/1)");
|
||||
$"프로젝트 구조가 평면적으로 생성되어 폴더 레이아웃을 먼저 정리합니다. ({runState.ProjectLayoutGateRetry}/1)");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -238,9 +238,9 @@ public partial class AgentLoopService
|
||||
messages.Add(new ChatMessage
|
||||
{
|
||||
Role = "user",
|
||||
Content = "[System:CodeDiffGate] 肄붾뱶 蹂寃??댄썑 diff 洹쇨굅媛 遺議깊빀?덈떎. git_tool ?꾧뎄濡?蹂寃??뚯씪怨??듭떖 diff瑜?癒쇱? ?뺤씤?섍퀬 ?붿빟?섏꽭?? 吏湲?利됱떆 git_tool ?꾧뎄瑜??몄텧?섏꽭??"
|
||||
Content = "[System:CodeDiffGate] 코드 변경 이후 diff 근거가 부족합니다. git_tool로 변경 파일과 핵심 diff를 먼저 확인하고 요약하세요. 지금 즉시 git_tool을 호출하세요."
|
||||
});
|
||||
EmitEvent(AgentEventType.Thinking, "", "肄붾뱶 diff 洹쇨굅媛 遺議깊빐 git diff 寃利앹쓣 異붽??⑸땲??..");
|
||||
EmitEvent(AgentEventType.Thinking, "", "코드 diff 근거가 부족해 git diff 검증을 추가합니다...");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ public partial class AgentLoopService
|
||||
Role = "user",
|
||||
Content = BuildRecentExecutionEvidencePrompt(taskPolicy)
|
||||
});
|
||||
EmitEvent(AgentEventType.Thinking, "", "理쒓렐 ?섏젙 ?댄썑 ?ㅽ뻾 洹쇨굅媛 遺議깊빐 build/test ?ш?利앹쓣 ?섑뻾?⑸땲??..");
|
||||
EmitEvent(AgentEventType.Thinking, "", "최근 수정 이후 실행 근거가 부족해 build/test 재검증을 진행합니다...");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -314,7 +314,7 @@ public partial class AgentLoopService
|
||||
Role = "user",
|
||||
Content = BuildExecutionSuccessGatePrompt(taskPolicy)
|
||||
});
|
||||
EmitEvent(AgentEventType.Thinking, "", "?ㅽ뙣???ㅽ뻾 洹쇨굅留??덉뼱 build/test ?깃났 寃곌낵瑜??ㅼ떆 寃利앺빀?덈떎...");
|
||||
EmitEvent(AgentEventType.Thinking, "", "실패한 실행 근거만 있어 build/test 성공 결과를 다시 검증합니다...");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -354,7 +354,7 @@ public partial class AgentLoopService
|
||||
Role = "user",
|
||||
Content = BuildTerminalEvidenceGatePrompt(taskPolicy, lastArtifactFilePath)
|
||||
});
|
||||
EmitEvent(AgentEventType.Thinking, "", $"醫낅즺 ???ㅽ뻾 利앷굅媛 遺議깊빐 蹂닿컯 ?④퀎瑜?吏꾪뻾?⑸땲??({runState.TerminalEvidenceGateRetry}/{retryMax})");
|
||||
EmitEvent(AgentEventType.Thinking, "", $"종료 전 실행 근거가 부족해 보강 단계를 진행합니다. ({runState.TerminalEvidenceGateRetry}/{retryMax})");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,8 +143,8 @@ public class BuildRunTool : IAgentTool
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
StandardOutputEncoding = Encoding.UTF8,
|
||||
StandardErrorEncoding = Encoding.UTF8,
|
||||
StandardOutputEncoding = ResolveProcessOutputEncoding(),
|
||||
StandardErrorEncoding = ResolveProcessOutputEncoding(),
|
||||
};
|
||||
|
||||
using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||||
@@ -186,6 +186,9 @@ public class BuildRunTool : IAgentTool
|
||||
|
||||
private record ProjectInfo(string Type, string Marker, string BuildCommand, string TestCommand, string RunCommand, string LintCommand, string FormatCommand);
|
||||
|
||||
private static Encoding ResolveProcessOutputEncoding()
|
||||
=> OperatingSystem.IsWindows() ? Encoding.Default : Encoding.UTF8;
|
||||
|
||||
private static ProjectInfo? DetectProjectType(string dir)
|
||||
{
|
||||
if (Directory.GetFiles(dir, "*.sln").Length > 0)
|
||||
|
||||
@@ -75,8 +75,8 @@ public class ProcessTool : IAgentTool
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true,
|
||||
StandardOutputEncoding = Encoding.UTF8,
|
||||
StandardErrorEncoding = Encoding.UTF8,
|
||||
StandardOutputEncoding = ResolveProcessOutputEncoding(),
|
||||
StandardErrorEncoding = ResolveProcessOutputEncoding(),
|
||||
};
|
||||
|
||||
// 작업 폴더 설정
|
||||
@@ -130,4 +130,7 @@ public class ProcessTool : IAgentTool
|
||||
return ToolResult.Fail($"명령 실행 실패: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static Encoding ResolveProcessOutputEncoding()
|
||||
=> OperatingSystem.IsWindows() ? Encoding.Default : Encoding.UTF8;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public partial class ChatWindow
|
||||
bool liveWaitingStyle = false)
|
||||
{
|
||||
var liveAccentColor = ResolveLiveProgressAccentColor(accentBrush);
|
||||
var pillMaxWidth = GetMessageMaxWidth();
|
||||
var pillMaxWidth = GetAgentEventMaxWidth();
|
||||
return new Border
|
||||
{
|
||||
Background = liveWaitingStyle
|
||||
@@ -72,7 +72,7 @@ public partial class ChatWindow
|
||||
CornerRadius = new CornerRadius(8),
|
||||
Padding = new Thickness(10, 6, 10, 6),
|
||||
Margin = new Thickness(0, 4, 0, 4),
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
MaxWidth = pillMaxWidth,
|
||||
Child = new Grid
|
||||
{
|
||||
@@ -280,7 +280,7 @@ public partial class ChatWindow
|
||||
&& !string.Equals(body, toolLabel, StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(body, headerText, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
var msgMaxWidth = GetMessageMaxWidth();
|
||||
var msgMaxWidth = GetAgentEventMaxWidth();
|
||||
var stack = new StackPanel
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
@@ -514,7 +514,7 @@ public partial class ChatWindow
|
||||
var hintBg = TryFindResource("HintBackground") as Brush
|
||||
?? new SolidColorBrush(Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF));
|
||||
|
||||
var msgMaxWidth = GetMessageMaxWidth();
|
||||
var msgMaxWidth = GetAgentEventMaxWidth();
|
||||
var container = new StackPanel
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
@@ -1685,7 +1685,7 @@ public partial class ChatWindow
|
||||
}
|
||||
|
||||
// ── Claude Code 스타일: 접히는 한 줄 행 (배너 이벤트도 동일 형식) ──
|
||||
var msgMaxWidth2 = GetMessageMaxWidth();
|
||||
var msgMaxWidth2 = GetAgentEventMaxWidth();
|
||||
|
||||
// 헤더 텍스트 구성: label + fileName (접기 화살표가 별도 표시되므로 statusPrefix 불필요)
|
||||
var bannerFileName = !string.IsNullOrWhiteSpace(evt.FilePath)
|
||||
@@ -1953,7 +1953,7 @@ public partial class ChatWindow
|
||||
// ── 요약 카드 ──
|
||||
if (!string.IsNullOrWhiteSpace(evt.Summary) && evt.Summary != "에이전트 작업 완료")
|
||||
{
|
||||
var msgMaxWidth = GetMessageMaxWidth();
|
||||
var msgMaxWidth = GetAgentEventMaxWidth();
|
||||
var hintBg = TryFindResource("HintBackground") as Brush ?? BrushFromHex("#F8FAFC");
|
||||
var borderColor = TryFindResource("BorderColor") as Brush ?? BrushFromHex("#E2E8F0");
|
||||
var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.Black;
|
||||
@@ -1967,7 +1967,7 @@ public partial class ChatWindow
|
||||
Padding = new Thickness(16, 12, 16, 12),
|
||||
Margin = new Thickness(0, 6, 0, 2),
|
||||
MaxWidth = msgMaxWidth,
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
};
|
||||
|
||||
var cardStack = new StackPanel();
|
||||
|
||||
@@ -227,6 +227,21 @@ public partial class ChatWindow
|
||||
return Math.Clamp(maxW, 320, 1040);
|
||||
}
|
||||
|
||||
private double GetAgentEventMaxWidth()
|
||||
{
|
||||
var baseWidth = GetMessageMaxWidth();
|
||||
var reservedRightGap = baseWidth switch
|
||||
{
|
||||
>= 980 => 190,
|
||||
>= 860 => 160,
|
||||
>= 720 => 132,
|
||||
_ => 92,
|
||||
};
|
||||
|
||||
var reducedWidth = baseWidth - reservedRightGap;
|
||||
return Math.Clamp(reducedWidth, 280, Math.Min(900, baseWidth));
|
||||
}
|
||||
|
||||
private bool UpdateResponsiveChatLayout()
|
||||
{
|
||||
var viewportWidth = MessageList?.ActualWidth ?? 0;
|
||||
@@ -269,10 +284,10 @@ public partial class ChatWindow
|
||||
|
||||
private StackPanel CreateStreamingContainer(out TextBlock streamText)
|
||||
{
|
||||
var msgMaxWidth = GetMessageMaxWidth();
|
||||
var msgMaxWidth = GetAgentEventMaxWidth();
|
||||
var container = new StackPanel
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
Width = msgMaxWidth,
|
||||
MaxWidth = msgMaxWidth,
|
||||
Margin = new Thickness(0, 3, 0, 3),
|
||||
|
||||
@@ -36,7 +36,7 @@ public partial class ChatWindow
|
||||
?? new SolidColorBrush(Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF));
|
||||
var accentBrush = TryFindResource("AccentColor") as Brush ?? Brushes.CornflowerBlue;
|
||||
|
||||
var msgMaxWidth = GetMessageMaxWidth();
|
||||
var msgMaxWidth = GetAgentEventMaxWidth();
|
||||
var (icon, iconColor) = GetV2ToolIcon(toolCall.ToolName);
|
||||
var elapsed = NormalizeProgressElapsedMs(toolResult.ElapsedMs);
|
||||
var elapsedText = elapsed > 0 ? $"{elapsed / 1000.0:F1}s" : "";
|
||||
@@ -45,7 +45,7 @@ public partial class ChatWindow
|
||||
// 외부 컨테이너 — 왼쪽 세로선 + 카드
|
||||
var outerGrid = new Grid
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
Width = msgMaxWidth,
|
||||
MaxWidth = msgMaxWidth,
|
||||
Margin = new Thickness(0, 2, 0, 2),
|
||||
@@ -300,7 +300,7 @@ public partial class ChatWindow
|
||||
private UIElement CreateV2ThinkingBlock(AgentEvent agentEvent)
|
||||
{
|
||||
var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray;
|
||||
var msgMaxWidth = GetMessageMaxWidth();
|
||||
var msgMaxWidth = GetAgentEventMaxWidth();
|
||||
|
||||
var summary = AgentProgressSummarySanitizer.NormalizeThinkingSummary(
|
||||
agentEvent.Summary,
|
||||
@@ -318,7 +318,7 @@ public partial class ChatWindow
|
||||
|
||||
var outerGrid = new Grid
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
Width = msgMaxWidth,
|
||||
MaxWidth = msgMaxWidth,
|
||||
Margin = new Thickness(0, 2, 0, 2),
|
||||
@@ -382,12 +382,12 @@ public partial class ChatWindow
|
||||
var hintBg = TryFindResource("HintBackground") as Brush
|
||||
?? new SolidColorBrush(Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF));
|
||||
|
||||
var msgMaxWidth = GetMessageMaxWidth();
|
||||
var msgMaxWidth = GetAgentEventMaxWidth();
|
||||
var (icon, iconColor) = GetV2ToolIcon(agentEvent.ToolName);
|
||||
|
||||
var outerGrid = new Grid
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
Width = msgMaxWidth,
|
||||
MaxWidth = msgMaxWidth,
|
||||
Margin = new Thickness(0, 2, 0, 2),
|
||||
@@ -460,7 +460,7 @@ public partial class ChatWindow
|
||||
/// <summary>V2: 작업 완료 배너</summary>
|
||||
private UIElement CreateV2CompleteBanner(AgentEvent agentEvent)
|
||||
{
|
||||
var msgMaxWidth = GetMessageMaxWidth();
|
||||
var msgMaxWidth = GetAgentEventMaxWidth();
|
||||
var elapsed = NormalizeProgressElapsedMs(agentEvent.ElapsedMs);
|
||||
var elapsedText = elapsed > 0 ? $" · {elapsed / 1000.0:F1}s" : "";
|
||||
|
||||
@@ -473,6 +473,14 @@ public partial class ChatWindow
|
||||
tokenInfo = $" · {string.Join("/", parts)} 토큰";
|
||||
}
|
||||
|
||||
if (agentEvent.InputTokens > 0 || agentEvent.OutputTokens > 0)
|
||||
{
|
||||
var localizedParts = new System.Collections.Generic.List<string>();
|
||||
if (agentEvent.InputTokens > 0) localizedParts.Add($"\uC785\uB825 {agentEvent.InputTokens:N0}");
|
||||
if (agentEvent.OutputTokens > 0) localizedParts.Add($"\uCD9C\uB825 {agentEvent.OutputTokens:N0}");
|
||||
tokenInfo = $" \u00B7 {string.Join("/", localizedParts)} \uD1A0\uD070";
|
||||
}
|
||||
|
||||
var banner = new Border
|
||||
{
|
||||
Background = new SolidColorBrush(Color.FromArgb(0x24, 0x66, 0xBB, 0x6A)),
|
||||
@@ -481,7 +489,7 @@ public partial class ChatWindow
|
||||
CornerRadius = new CornerRadius(8),
|
||||
Padding = new Thickness(14, 8, 14, 8),
|
||||
Margin = new Thickness(0, 6, 0, 4),
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
MaxWidth = msgMaxWidth,
|
||||
};
|
||||
|
||||
@@ -502,6 +510,8 @@ public partial class ChatWindow
|
||||
Foreground = new SolidColorBrush(Color.FromRgb(0x66, 0xBB, 0x6A)),
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
});
|
||||
if (sp.Children.Count > 1 && sp.Children[1] is TextBlock completeText)
|
||||
completeText.Text = $"\uC791\uC5C5 \uC644\uB8CC{elapsedText}{tokenInfo}";
|
||||
banner.Child = sp;
|
||||
return banner;
|
||||
}
|
||||
@@ -509,7 +519,7 @@ public partial class ChatWindow
|
||||
/// <summary>V2: 에러 배너</summary>
|
||||
private UIElement CreateV2ErrorBanner(AgentEvent agentEvent)
|
||||
{
|
||||
var msgMaxWidth = GetMessageMaxWidth();
|
||||
var msgMaxWidth = GetAgentEventMaxWidth();
|
||||
var banner = new Border
|
||||
{
|
||||
Background = new SolidColorBrush(Color.FromArgb(0x24, 0xEF, 0x53, 0x50)),
|
||||
@@ -518,7 +528,7 @@ public partial class ChatWindow
|
||||
CornerRadius = new CornerRadius(8),
|
||||
Padding = new Thickness(14, 8, 14, 8),
|
||||
Margin = new Thickness(0, 6, 0, 4),
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
MaxWidth = msgMaxWidth,
|
||||
};
|
||||
|
||||
@@ -540,6 +550,8 @@ public partial class ChatWindow
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
});
|
||||
if (sp.Children.Count > 1 && sp.Children[1] is TextBlock errorText)
|
||||
errorText.Text = $"\uC624\uB958 \uBC1C\uC0DD: {agentEvent.Summary ?? "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958"}";
|
||||
banner.Child = sp;
|
||||
return banner;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public partial class ChatWindow
|
||||
_v2LiveToolCards.Clear();
|
||||
_v2LastLiveToolCallId = null;
|
||||
|
||||
var msgMaxWidth = GetMessageMaxWidth();
|
||||
var msgMaxWidth = GetAgentEventMaxWidth();
|
||||
var accentBrush = TryFindResource("AccentColor") as Brush ?? Brushes.CornflowerBlue;
|
||||
var accentColor = ResolveLiveProgressAccentColor(accentBrush);
|
||||
var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray;
|
||||
@@ -40,7 +40,7 @@ public partial class ChatWindow
|
||||
|
||||
_v2LiveContainer = new StackPanel
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
Width = msgMaxWidth,
|
||||
MaxWidth = msgMaxWidth,
|
||||
Margin = new Thickness(0, 4, 0, 6),
|
||||
@@ -99,6 +99,7 @@ public partial class ChatWindow
|
||||
FontSize = 14,
|
||||
FontWeight = FontWeights.SemiBold,
|
||||
Foreground = primaryText,
|
||||
TextAlignment = TextAlignment.Left,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
LineHeight = 20,
|
||||
};
|
||||
@@ -107,6 +108,7 @@ public partial class ChatWindow
|
||||
FontSize = 11,
|
||||
Foreground = secondaryText,
|
||||
Opacity = 0.88,
|
||||
TextAlignment = TextAlignment.Left,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
LineHeight = 18,
|
||||
Margin = new Thickness(0, 4, 0, 0),
|
||||
@@ -116,6 +118,7 @@ public partial class ChatWindow
|
||||
FontSize = 10,
|
||||
Foreground = secondaryText,
|
||||
Opacity = 0.72,
|
||||
TextAlignment = TextAlignment.Left,
|
||||
Margin = new Thickness(0, 8, 0, 0),
|
||||
};
|
||||
|
||||
@@ -127,6 +130,7 @@ public partial class ChatWindow
|
||||
CornerRadius = new CornerRadius(12),
|
||||
Padding = new Thickness(14, 12, 14, 12),
|
||||
Margin = new Thickness(0, 4, 0, 6),
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
Child = new StackPanel
|
||||
{
|
||||
Children =
|
||||
@@ -148,7 +152,7 @@ public partial class ChatWindow
|
||||
{
|
||||
if (_v2LiveElapsedText == null) return;
|
||||
var sec = (int)(DateTime.UtcNow - _v2LiveStartTime).TotalSeconds;
|
||||
_v2LiveElapsedText.Text = sec > 0 ? $"{sec}초 경과" : "";
|
||||
_v2LiveElapsedText.Text = sec > 0 ? $"{sec}\uCD08 \uACBD\uACFC" : string.Empty;
|
||||
};
|
||||
_v2LiveElapsedTimer.Start();
|
||||
}
|
||||
@@ -160,7 +164,7 @@ public partial class ChatWindow
|
||||
|
||||
var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray;
|
||||
var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.White;
|
||||
var msgMaxWidth = GetMessageMaxWidth();
|
||||
var msgMaxWidth = GetAgentEventMaxWidth();
|
||||
UpdateV2LiveStatusCardFromEvent(agentEvent);
|
||||
|
||||
switch (agentEvent.Type)
|
||||
@@ -441,7 +445,7 @@ public partial class ChatWindow
|
||||
return;
|
||||
|
||||
var narrative = AgentStatusNarrativeCatalog.BuildFromEvent(agentEvent, _activeTab);
|
||||
var statusSummaryMaxLength = GetMessageMaxWidth() > 900 ? 260 : 180;
|
||||
var statusSummaryMaxLength = GetAgentEventMaxWidth() > 780 ? 260 : 180;
|
||||
var normalizedThinking = agentEvent.Type == AgentEventType.Thinking
|
||||
? AgentProgressSummarySanitizer.NormalizeThinkingSummary(
|
||||
agentEvent.Summary,
|
||||
@@ -459,9 +463,10 @@ public partial class ChatWindow
|
||||
if (_v2LiveStatusText == null || _v2LiveStatusDetailText == null || _v2LiveStatusMetaText == null)
|
||||
return;
|
||||
|
||||
_v2LiveStatusText.Text = string.IsNullOrWhiteSpace(message)
|
||||
? "작업을 이어가고 있습니다..."
|
||||
var normalizedMessage = string.IsNullOrWhiteSpace(message)
|
||||
? "\uC791\uC5C5\uC744 \uC774\uC5B4\uAC00\uACE0 \uC788\uC2B5\uB2C8\uB2E4..."
|
||||
: message;
|
||||
_v2LiveStatusText.Text = normalizedMessage;
|
||||
|
||||
_v2LiveStatusDetailText.Text = detail ?? string.Empty;
|
||||
_v2LiveStatusDetailText.Visibility = string.IsNullOrWhiteSpace(detail)
|
||||
|
||||
Reference in New Issue
Block a user