Compare commits

...

8 Commits

Author SHA1 Message Date
f9d18fba08 AX Agent 폴더 데이터 활용 사용자 옵션 제거 및 자동 정책 고정
Some checks are pending
Release Gate / gate (push) Waiting to run
- 코워크/코드의 폴더 내 문서 활용을 사용자 설정에서 제거하고 탭별 자동 정책으로 고정
- ChatWindow 하단 데이터 활용 버튼과 AX Agent 내부 설정 row, 메인 설정/구형 설정창의 관련 UI 제거
- Chat/Cowork/Code 탭별 자동 데이터 활용 정책을 none/passive/active로 고정하고 저장 경로에서 사용자 선택값 반영 제거
- README와 DEVELOPMENT 문서에 변경 이력 및 적용 기준 반영
- 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify\ -p:IntermediateOutputPath=obj\verify\ (경고 0 / 오류 0)
2026-04-05 23:06:38 +09:00
f0af86cc1e AX Agent transcript 렌더 구조를 분리하고 권한/도구 결과 표시 체계를 정리한다
Some checks failed
Release Gate / gate (push) Has been cancelled
- ChatWindow.xaml.cs에 몰려 있던 의견 요청, 계획 승인, 작업 요약 렌더를 partial 파일로 분리해 transcript 책임을 낮췄다.

- PermissionRequestPresentationCatalog와 ToolResultPresentationCatalog를 추가해 권한 요청 및 도구 결과 badge를 타입별로 해석하도록 정리했다.

- AppStateService에 OperationalStatusPresentationState를 추가하고 상태선 계산을 presentation 계층으로 한 번 더 분리했다.

- README.md, docs/DEVELOPMENT.md, docs/claw-code-parity-plan.md에 2026-04-06 00:58 (KST) 기준 변경 내용을 반영했다.

- 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ (경고 0 / 오류 0)
2026-04-05 22:55:56 +09:00
13f0e23ed5 AX Agent 테마 프리셋 2종 추가
Some checks failed
Release Gate / gate (push) Has been cancelled
AX Agent 전용 테마를 재점검해 Nord와 Ember 프리셋을 새로 추가했습니다.

내부 설정의 테마 스타일 카드에서 새 프리셋을 바로 선택할 수 있게 연결했고 system, light, dark 모드 조합으로 동일하게 적용되도록 정리했으며 문서 이력과 빌드 검증도 함께 반영했습니다.
2026-04-05 22:41:15 +09:00
7cb27b70f8 내부 설정 훅 섹션 잘림 레이아웃 보정
Some checks failed
Release Gate / gate (push) Has been cancelled
AX Agent 내부 설정의 도구 훅 실행 타임아웃과 등록된 훅 영역에서 컨트롤이 잘리던 레이아웃을 조정했습니다.

슬라이더와 값 배지 컬럼 폭, 훅 추가 버튼 최소 폭과 여백을 넓혀 작은 폭에서도 텍스트와 컨트롤이 안정적으로 보이도록 정리했고 문서 이력과 빌드 검증도 함께 반영했습니다.
2026-04-05 22:37:11 +09:00
61f82bdd10 내부 설정 개발자 토글 저장 경로 복구
Some checks failed
Release Gate / gate (push) Has been cancelled
AX Agent 내부 설정 개발자 탭의 워크플로우 시각화, 전체 호출·토큰 합계 표시, 감사 로그 토글에 변경 이벤트를 연결했습니다.

오버레이 재동기화 시 기본값으로 되돌아가던 문제를 막고 즉시 저장되도록 보정했으며 문서 이력과 빌드 검증도 함께 반영했습니다.
2026-04-05 22:33:39 +09:00
fa349c2057 프리셋 선택 시 새 대화 중복 생성 방지
Some checks failed
Release Gate / gate (push) Has been cancelled
AX Agent 채팅과 코워크에서 메시지와 입력이 없는 fresh conversation에 프리셋만 선택해도 새 대화가 반복 생성되던 흐름을 수정했습니다.

기존 빈 대화가 있으면 해당 대화를 재사용하고 프리셋 메타데이터만 갱신하도록 정리했으며 문서 이력과 빌드 검증도 함께 반영했습니다.
2026-04-05 22:30:53 +09:00
be7328184a 코드 탭 폴더 데이터 활용 기본 허용 및 파일명 강조 적용
Some checks failed
Release Gate / gate (push) Has been cancelled
코드 탭에서는 폴더 내 데이터 활용을 항상 적극 활용으로 고정하고 하단 버튼과 내부 설정 옵션을 숨겨 사용 흐름을 단순화했습니다.

코워크와 코드 탭의 사용자 메시지도 파일 경로 강조 렌더러를 사용하도록 바꿔 폴더 하위 파일명 입력 시 파란색으로 표시되게 맞췄습니다.

README와 DEVELOPMENT 문서에 변경 이력을 반영했고 Release 빌드에서 경고 0 오류 0을 확인했습니다.
2026-04-05 22:28:29 +09:00
905ea41ed3 코드 탭 Git 브랜치 선택 UI 단순화
- 하단 Git 브랜치 버튼을 상태판형에서 브랜치 선택 버튼 형태로 정리\n- 브랜치 버튼 기본 노출에서 변경 파일/추가/삭제 수치를 숨기고 브랜치명 중심으로 단순화\n- README 및 DEVELOPMENT 문서 이력 갱신\n\n검증:\n- dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\\n- 경고 0 / 오류 0
2026-04-05 22:21:58 +09:00
19 changed files with 1406 additions and 1150 deletions

View File

@@ -7,6 +7,9 @@ Windows 전용 시맨틱 런처 & 워크스페이스 매니저
개발 참고: Claw Code 동등성 작업 추적 문서
`docs/claw-code-parity-plan.md`
- 업데이트: 2026-04-06 00:50 (KST)
- 채팅/코워크 프리셋 카드의 hover 설명 레이어를 카드 내부 오버레이 방식에서 안정적인 tooltip형 설명으로 바꿨습니다. 카드 배경/테두리만 반응하게 정리해 hover 시 반복 깜빡임을 줄였습니다.
- 업데이트: 2026-04-06 00:45 (KST)
- AX Agent 내부 설정 공통 탭에 `대화 스타일` 섹션 제목을 복구해, `문서 형태``디자인 스타일` 저장 항목이 명확히 보이도록 정리했습니다.
- AX Agent 내부 설정 스킬 탭의 `MCP 서버` 영역에 `서버 추가` 버튼을 복원했고, 목록 카드 안에서 `활성화/비활성화``삭제`까지 바로 관리할 수 있게 옮겼습니다.
@@ -1094,3 +1097,26 @@ MIT License
- `claw-code``SessionPreview`/`PreviewBox` 흐름을 참고해 AX Agent 프리뷰도 같은 시각 언어로 정리했다. 새 파일 [AgentPreviewSurfaceFactory.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/AgentPreviewSurfaceFactory.cs)를 추가해 권한 프리뷰 카드의 제목/요약/본문 박스 구조를 공통화했다.
- [PermissionRequestWindow.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/PermissionRequestWindow.cs)의 일반 프리뷰, 파일 편집 프리뷰, 파일 생성 2열 프리뷰를 이 공통 surface로 맞춰 `preview box` 언어를 통일했다.
- [ChatWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml), [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs)의 우측 파일 프리뷰 패널에는 파일명/경로/형식·크기 메타를 보여주는 헤더를 추가하고, 텍스트 프리뷰 본문도 별도 bordered preview box 안에 렌더되게 바꿨다.
- 업데이트: 2026-04-06 00:35 (KST)
- AX Agent 채팅/코워크 프리셋 카드에서 기본 ToolTip을 제거해 hover 시 깜빡이듯 반복되던 현상을 줄였습니다.
- 업데이트: 2026-04-06 00:42 (KST)
- 코드 탭 하단 Git 브랜치 버튼을 상태판 형태에서 단순한 브랜치 선택 버튼 형태로 정리했습니다.
- 업데이트: 2026-04-05 22:26 (KST)
- 코드 탭에서는 폴더 문서/파일을 기본 작업 전제로 삼도록 `폴더 내 데이터 활용`을 항상 `적극 활용(active)`으로 강제했다. 하단 채팅창의 데이터 활용 버튼은 코드 탭에서 숨기고, 내부 설정 오버레이의 같은 옵션도 코드 탭에서는 노출하지 않게 정리했다.
- 코워크/코드 탭의 사용자 메시지도 assistant 메시지와 같은 파일 경로 강조 렌더러를 쓰도록 바꿔, 폴더 하위 파일명이나 경로를 입력하면 채팅 본문에서 파란색으로 인식되게 맞췄다.
- 업데이트: 2026-04-05 22:29 (KST)
- AX Agent 채팅/코워크 프리셋을 선택할 때, 메시지도 입력도 없는 fresh conversation인데도 `새 대화`가 반복 생성되던 흐름을 보정했다. 이제 현재 대화가 이미 있으면 그 빈 대화에 프리셋만 적용하고, 실제 대화가 아예 없는 경우에만 새 대화를 만든다.
- 업데이트: 2026-04-05 22:32 (KST)
- AX Agent 내부 설정 개발자 탭의 `워크플로우 시각화`, `전체 호출·토큰 합계 표시`, `감사 로그` 토글이 누르자마자 꺼지는 문제를 수정했다. 각 토글의 변경 이벤트를 연결해 즉시 저장되도록 보정했다.
- 업데이트: 2026-04-05 22:36 (KST)
- AX Agent 내부 설정 `도구 훅 실행 타임아웃``등록된 훅` 영역에서 잘림이 보이던 레이아웃을 보정했다. 슬라이더/값 배지 컬럼 폭과 `훅 추가` 버튼 최소 폭을 넉넉히 늘려 텍스트와 컨트롤이 서로 밀리지 않게 정리했다.
- 업데이트: 2026-04-05 22:40 (KST)
- AX Agent 테마를 다시 점검해 기존 `Claw / Codex / Slate` 외에 `Nord`, `Ember` 2종을 추가했다. `Nord`는 차분한 블루그레이 업무형 톤, `Ember`는 따뜻한 앰버 문서 작업 톤으로 구성했다.
- 내부 설정 `테마 스타일` 카드에서도 새 프리셋을 바로 선택할 수 있게 연결했고, `system / light / dark` 모드 조합으로 같은 방식으로 적용되도록 정리했다.
- 업데이트: 2026-04-06 00:58 (KST)
- AX Agent transcript 품질 향상을 위해 렌더 책임을 실제로 분리했다. [ChatWindow.InlineInteractions.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.InlineInteractions.cs), [ChatWindow.TaskSummary.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.TaskSummary.cs)를 추가해 `의견 요청`, `계획 승인`, `작업 요약` UI 로직을 메인 [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs)에서 분리했다.
- [PermissionRequestPresentationCatalog.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/PermissionRequestPresentationCatalog.cs), [ToolResultPresentationCatalog.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/ToolResultPresentationCatalog.cs)를 추가해 권한 요청과 도구 결과를 `명령/네트워크/파일`, `성공/실패/거부/취소` 기준으로 나눠 transcript badge에 재사용하도록 정리했다.
- [AppStateService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/AppStateService.cs)에 `OperationalStatusPresentationState``GetOperationalStatusPresentation(...)`을 추가해 status/runtime summary 계산을 전용 요약 모델로 한 번 더 계층화했다. [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs)의 상태선 갱신은 이제 이 presentation summary를 소비한다.
- 업데이트: 2026-04-06 01:12 (KST)
- AX Agent 코워크/코드의 `폴더 내 문서 활용`을 사용자 옵션에서 제거했다. [ChatWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml), [SettingsWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/SettingsWindow.xaml), [AgentSettingsWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/AgentSettingsWindow.xaml) 에서 하단 버튼, 내부 설정 행, 구형 설정창 항목을 걷어냈다.
- 런타임은 옵션이 아닌 자동 정책으로 유지한다. [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs) 에서 채팅은 `none`, 코워크는 `passive`, 코드는 `active`를 자동 적용하고, 더 이상 오버레이 저장 시 `FolderDataUsage`를 사용자 선택값으로 저장하지 않는다.

View File

@@ -4851,5 +4851,34 @@ ow + toggle ?쒓컖 ?몄뼱濡??ㅼ떆 ?뺣젹?덈떎.
- [PermissionRequestWindow.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/PermissionRequestWindow.cs) 의 `BuildPreviewCard`, `BuildFileEditPreviewCard`, `BuildFileWriteTwoColumnPreviewCard`는 이 helper를 쓰도록 정리해 권한 승인 프리뷰가 같은 preview 언어를 따르도록 맞췄다.
- [ChatWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml) 의 우측 프리뷰 패널 헤더를 `파일명 / 경로 / 형식·크기 메타` 구조로 재배치하고, [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs) 에 `SetPreviewHeader`, `SetPreviewHeaderState`를 추가해 현재 탭 파일 메타를 표시하도록 했다.
- 텍스트 프리뷰 본문도 bordered preview box 안에 렌더되게 바꿔, AX Agent 파일 프리뷰와 transcript/승인 프리뷰 사이 시각 언어를 더 가깝게 맞췄다.
- Document update: 2026-04-06 00:50 (KST) - Reworked Chat/Cowork preset hover behavior in `BuildTopicButtons()`. The previous inline hover label overlay was removed and replaced with a stable tooltip-style description so preset cards only change background/border on hover, eliminating the repeated flicker effect caused by overlay-style label toggling.
- Document update: 2026-04-06 00:45 (KST) - Restored the missing AX Agent internal-settings UX for conversation-style persistence by adding a visible `대화 스타일` section header above the existing `문서 형태` and `디자인 스타일` controls. The underlying save path already existed in `CmbOverlayDefaultOutputFormat_SelectionChanged` and `CmbOverlayDefaultMood_SelectionChanged`; this pass makes that persisted behavior explicit again in the overlay.
- Document update: 2026-04-06 00:45 (KST) - Restored MCP server management inside the AX Agent internal settings overlay. The skill tab now exposes `+ 서버 추가`, and overlay MCP cards support inline enable/disable and delete actions with immediate persistence through `PersistOverlaySettingsState(...)`.
- 업데이트: 2026-04-06 00:35 (KST)
- `ChatWindow.xaml.cs`의 프리셋 카드 hover 처리에서 WPF 기본 ToolTip을 제거했습니다. 이제 hover는 배경/테두리만 바뀌고 tooltip에 의한 enter/leave 반복이 줄어듭니다.
- 업데이트: 2026-04-06 00:42 (KST)
- `ChatWindow` 하단 Git 브랜치 UI를 단순화했습니다. 변경 파일 수/추가/삭제 수치는 기본 버튼에서는 숨기고 브랜치명 중심의 선택 버튼처럼 보이게 조정했습니다.
- 업데이트: 2026-04-05 22:26 (KST)
- [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs) 에 `IsFolderDataAlwaysEnabledTab()` helper를 추가하고, 코드 탭에서는 `_folderDataUsage`를 항상 `active`로 로드/저장하도록 고정했다. 이에 맞춰 `BtnDataUsage_Click`, `UpdateDataUsageUI()`, 오버레이의 `OverlayFolderDataUsageRow`, `BtnOverlayFolderDataUsage_Click`, `CmbOverlayFolderDataUsage_SelectionChanged`도 코드 탭에서는 숨김 또는 강제 active만 유지하게 정리했다.
- 같은 파일의 사용자 메시지 bubble 렌더에서 코워크/코드 탭은 plain `TextBlock` 대신 [MarkdownRenderer.Render](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/MarkdownRenderer.cs) 경로를 타도록 변경했다. 이로써 코워크/코드 사용자 입력 안의 파일명/파일 경로가 assistant 응답과 동일한 파란 강조 규칙을 쓰게 됐다.
- 업데이트: 2026-04-05 22:29 (KST)
- [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs) 의 `SelectTopic(...)`에서 fresh conversation이 이미 있는 경우에는 `StartNewConversation()`를 다시 호출하지 않도록 보정했다. 기존에는 메시지도 입력도 없는 빈 대화에서 프리셋만 눌러도 새 conversation이 계속 생성되어 좌측 목록에 `새 대화`가 누적될 수 있었다.
- 현재는 `_currentConversation` 존재 여부를 기준으로 빈 대화 재사용과 실제 신규 생성 경로를 분리해, 프리셋 클릭은 같은 새 대화 안에서 메타데이터만 갱신하도록 맞췄다.
- 업데이트: 2026-04-05 22:32 (KST)
- [ChatWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml) 의 `ChkOverlayWorkflowVisualizer`, `ChkOverlayShowTotalCallStats`, `ChkOverlayEnableAuditLog``Checked/Unchecked` 이벤트를 연결했다.
- [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs)에 각 토글 전용 저장 handler를 추가해, 개발자 탭 옵션이 눌린 직후 오버레이 재동기화로 기본값으로 되돌아가던 문제를 해결했다.
- 업데이트: 2026-04-05 22:36 (KST)
- [ChatWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml) 의 훅 설정 섹션에서 `도구 훅 스크립트 제한 시간` row 오른쪽 컬럼을 `120 -> 180`, 값 배지 컬럼을 `44 -> 56`으로 늘렸다.
- 같은 파일의 `등록된 훅` 헤더 우측 `훅 추가` 버튼은 좌측 여백과 최소 폭을 키워, 작은 창에서도 텍스트 잘림 없이 보이도록 정리했다.
- 업데이트: 2026-04-05 22:40 (KST)
- AX Agent 테마 리소스 구조를 다시 점검한 뒤 `Claw / Codex / Slate` 외에 `Nord`, `Ember` 프리셋을 추가했다. 새 리소스 파일은 [AgentNordLight.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Themes/AgentNordLight.xaml), [AgentNordDark.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Themes/AgentNordDark.xaml), [AgentNordSystem.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Themes/AgentNordSystem.xaml), [AgentEmberLight.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Themes/AgentEmberLight.xaml), [AgentEmberDark.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Themes/AgentEmberDark.xaml), [AgentEmberSystem.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Themes/AgentEmberSystem.xaml) 이다.
- [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs)의 `BuildAgentThemeDictionaryUri()`, `RefreshOverlayThemeCards()`에 새 프리셋 분기를 추가했고, [ChatWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml)의 `테마 스타일` 선택 카드에도 `Nord`, `Ember`를 노출했다.
- 업데이트: 2026-04-06 00:58 (KST)
- transcript renderer 분리 1차를 반영했다. [ChatWindow.InlineInteractions.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.InlineInteractions.cs)에 `ShowInlineUserAskAsync`, `CreatePlanDecisionCallback`, `ShowPlanButton`, `EnsurePlanViewerWindow` 계열을 옮기고, [ChatWindow.TaskSummary.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.TaskSummary.cs)에 `ShowTaskSummaryPopup`, `BuildTaskSummaryCard`를 옮겨 메인 [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs)의 transcript 책임을 줄였다.
- 권한/도구 결과 presentation catalog를 추가했다. [PermissionRequestPresentationCatalog.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/PermissionRequestPresentationCatalog.cs)는 `명령/네트워크/파일/일반` 권한 요청·허용 메타를, [ToolResultPresentationCatalog.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/ToolResultPresentationCatalog.cs)는 `success/error/reject/cancel` 기준의 도구 결과 badge 메타를 제공한다.
- [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs)의 `AddAgentEventBanner(...)`는 이제 이 catalog를 사용해 권한 요청과 도구 결과 badge를 결정한다.
- [AppStateService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/AppStateService.cs)에 `OperationalStatusPresentationState``GetOperationalStatusPresentation(...)`을 추가해 status line/runtime summary 계산을 presentation layer로 분리했다. `UpdateTaskSummaryIndicators()`는 presentation summary만 소비하도록 바뀌었다.
- 업데이트: 2026-04-06 01:12 (KST)
- 코워크/코드의 `폴더 내 문서 활용`은 사용자 제어 옵션에서 제거하고 탭별 자동 정책으로 고정했다. [ChatWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml) 에서 하단 데이터 활용 버튼과 AX Agent 내부 설정의 관련 row를 제거했고, [SettingsWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/SettingsWindow.xaml), [AgentSettingsWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/AgentSettingsWindow.xaml) 의 대응 UI도 정리했다.
- [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs) 는 이제 `GetAutomaticFolderDataUsage()`만 사용해 채팅=`none`, 코워크=`passive`, 코드=`active`를 적용한다. 오버레이 저장에서도 `llm.FolderDataUsage`를 더 이상 사용자 입력으로 덮어쓰지 않으며, UI 클릭/선택 변경 핸들러는 자동 정책 유지용 no-op 수준으로 축소했다.

View File

@@ -407,3 +407,15 @@
- `BuildTopicButtons()` rebuild frequency
- `OnAgentEvent` timeline churn during long Cowork/Code runs
- compact queue summary still needs one more pass to fully match `claw-code` footer minimalism
## Progress Notes
- 업데이트: 2026-04-06 00:58 (KST)
- transcript renderer 분리 1차 완료
- AX 적용: [ChatWindow.InlineInteractions.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.InlineInteractions.cs), [ChatWindow.TaskSummary.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.TaskSummary.cs)
- 완료 조건: `plan / ask / task-summary` 렌더 helper가 메인 `ChatWindow.xaml.cs` 밖으로 이동
- permission / tool-result presentation catalog 도입
- AX 적용: [PermissionRequestPresentationCatalog.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/PermissionRequestPresentationCatalog.cs), [ToolResultPresentationCatalog.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/ToolResultPresentationCatalog.cs)
- 완료 조건: `AddAgentEventBanner(...)`가 권한/도구 결과 badge 메타를 inline switch가 아니라 catalog에서 해석
- runtime summary 전용 계층 1차 반영
- AX 적용: [AppStateService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/AppStateService.cs)
- 완료 조건: 상태선 UI가 `OperationalStatusPresentationState`를 소비해 strip/runtime badge visibility를 계산

View File

@@ -0,0 +1,40 @@
namespace AxCopilot.Services.Agent;
internal sealed record PermissionRequestPresentation(
string Icon,
string Label,
string BackgroundHex,
string ForegroundHex);
internal static class PermissionRequestPresentationCatalog
{
public static PermissionRequestPresentation Resolve(string? toolName, bool pending)
{
var tool = toolName?.Trim().ToLowerInvariant() ?? "";
if (tool.Contains("process") || tool.Contains("bash") || tool.Contains("powershell"))
{
return pending
? new PermissionRequestPresentation("\uE756", "명령 권한 요청", "#FEF2F2", "#DC2626")
: new PermissionRequestPresentation("\uE73E", "명령 권한 허용", "#ECFDF5", "#059669");
}
if (tool.Contains("web") || tool.Contains("fetch") || tool.Contains("http"))
{
return pending
? new PermissionRequestPresentation("\uE774", "네트워크 권한 요청", "#FFF7ED", "#C2410C")
: new PermissionRequestPresentation("\uE73E", "네트워크 권한 허용", "#ECFDF5", "#059669");
}
if (tool.Contains("file"))
{
return pending
? new PermissionRequestPresentation("\uE8A5", "파일 권한 요청", "#FFF7ED", "#C2410C")
: new PermissionRequestPresentation("\uE73E", "파일 권한 허용", "#ECFDF5", "#059669");
}
return pending
? new PermissionRequestPresentation("\uE897", "권한 요청", "#FFF7ED", "#C2410C")
: new PermissionRequestPresentation("\uE73E", "권한 허용", "#ECFDF5", "#059669");
}
}

View File

@@ -0,0 +1,37 @@
namespace AxCopilot.Services.Agent;
internal sealed record ToolResultPresentation(
string Icon,
string Label,
string BackgroundHex,
string ForegroundHex,
string StatusKind);
internal static class ToolResultPresentationCatalog
{
public static ToolResultPresentation Resolve(AgentEvent evt, string fallbackLabel)
{
var summary = evt.Summary?.Trim() ?? "";
if (summary.Contains("취소", StringComparison.OrdinalIgnoreCase) ||
summary.Contains("중단", StringComparison.OrdinalIgnoreCase) ||
evt.Type == AgentEventType.StopRequested)
{
return new ToolResultPresentation("\uE711", "도구 취소", "#F8FAFC", "#475569", "cancel");
}
if (summary.Contains("거부", StringComparison.OrdinalIgnoreCase) ||
summary.Contains("반려", StringComparison.OrdinalIgnoreCase) ||
summary.Contains("권한 거부", StringComparison.OrdinalIgnoreCase))
{
return new ToolResultPresentation("\uE783", "도구 거부", "#FEF2F2", "#DC2626", "reject");
}
if (!evt.Success || evt.Type == AgentEventType.Error)
{
return new ToolResultPresentation("\uE783", "도구 실패", "#FEF2F2", "#DC2626", "error");
}
return new ToolResultPresentation("\uE73E", fallbackLabel, "#ECFDF5", "#16A34A", "success");
}
}

View File

@@ -158,6 +158,17 @@ public sealed class AppStateService
public string StripText { get; init; } = "";
}
public sealed class OperationalStatusPresentationState
{
public bool ShowRuntimeBadge { get; init; }
public string RuntimeLabel { get; init; } = "";
public bool ShowLastCompleted { get; init; }
public string LastCompletedText { get; init; } = "";
public bool ShowCompactStrip { get; init; }
public string StripKind { get; init; } = "none";
public string StripText { get; init; } = "";
}
public ChatSessionStateService? ChatSession { get; private set; }
public SkillCatalogState Skills { get; } = new();
public McpCatalogState Mcp { get; } = new();
@@ -620,6 +631,26 @@ public sealed class AppStateService
};
}
public OperationalStatusPresentationState GetOperationalStatusPresentation(string tab, bool hasLiveRuntimeActivity)
{
var status = GetOperationalStatus(tab);
var showCompactStrip = !string.Equals(tab, "Chat", StringComparison.OrdinalIgnoreCase)
&& (string.Equals(status.StripKind, "permission_waiting", StringComparison.OrdinalIgnoreCase)
|| string.Equals(status.StripKind, "failed_run", StringComparison.OrdinalIgnoreCase)
|| string.Equals(status.StripKind, "permission_denied", StringComparison.OrdinalIgnoreCase));
return new OperationalStatusPresentationState
{
ShowRuntimeBadge = status.ShowRuntimeBadge && hasLiveRuntimeActivity,
RuntimeLabel = status.RuntimeLabel,
ShowLastCompleted = status.ShowLastCompleted,
LastCompletedText = status.LastCompletedText,
ShowCompactStrip = showCompactStrip,
StripKind = showCompactStrip ? status.StripKind : "none",
StripText = showCompactStrip ? status.StripText : "",
};
}
public IReadOnlyList<DraftQueueItem> GetDraftQueueItems(string tab)
=> ChatSession?.GetDraftQueueItems(tab) ?? Array.Empty<DraftQueueItem>();

View File

@@ -0,0 +1,17 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="LauncherBackground" Color="#1B1510"/>
<SolidColorBrush x:Key="ItemBackground" Color="#241C15"/>
<SolidColorBrush x:Key="ItemSelectedBackground" Color="#32261D"/>
<SolidColorBrush x:Key="ItemHoverBackground" Color="#3A2C21"/>
<SolidColorBrush x:Key="PrimaryText" Color="#F6EEE6"/>
<SolidColorBrush x:Key="SecondaryText" Color="#D0B9A6"/>
<SolidColorBrush x:Key="PlaceholderText" Color="#A68F7E"/>
<SolidColorBrush x:Key="AccentColor" Color="#F29A54"/>
<SolidColorBrush x:Key="SeparatorColor" Color="#4A382B"/>
<SolidColorBrush x:Key="HintBackground" Color="#2B2119"/>
<SolidColorBrush x:Key="HintText" Color="#F6C08A"/>
<SolidColorBrush x:Key="BorderColor" Color="#4C392B"/>
<SolidColorBrush x:Key="ScrollbarThumb" Color="#755B48"/>
<SolidColorBrush x:Key="ShadowColor" Color="#99000000"/>
</ResourceDictionary>

View File

@@ -0,0 +1,17 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="LauncherBackground" Color="#FAF6EF"/>
<SolidColorBrush x:Key="ItemBackground" Color="#FFFDFC"/>
<SolidColorBrush x:Key="ItemSelectedBackground" Color="#F3E5D5"/>
<SolidColorBrush x:Key="ItemHoverBackground" Color="#F6ECE0"/>
<SolidColorBrush x:Key="PrimaryText" Color="#2B2118"/>
<SolidColorBrush x:Key="SecondaryText" Color="#7A6758"/>
<SolidColorBrush x:Key="PlaceholderText" Color="#A08D7D"/>
<SolidColorBrush x:Key="AccentColor" Color="#C96B2C"/>
<SolidColorBrush x:Key="SeparatorColor" Color="#E7D8C9"/>
<SolidColorBrush x:Key="HintBackground" Color="#F7EBDD"/>
<SolidColorBrush x:Key="HintText" Color="#9A531E"/>
<SolidColorBrush x:Key="BorderColor" Color="#E4D4C4"/>
<SolidColorBrush x:Key="ScrollbarThumb" Color="#CDB8A6"/>
<SolidColorBrush x:Key="ShadowColor" Color="#22000000"/>
</ResourceDictionary>

View File

@@ -0,0 +1,17 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="LauncherBackground" Color="#FAF6EF"/>
<SolidColorBrush x:Key="ItemBackground" Color="#FFFDFC"/>
<SolidColorBrush x:Key="ItemSelectedBackground" Color="#F3E5D5"/>
<SolidColorBrush x:Key="ItemHoverBackground" Color="#F6ECE0"/>
<SolidColorBrush x:Key="PrimaryText" Color="#2B2118"/>
<SolidColorBrush x:Key="SecondaryText" Color="#7A6758"/>
<SolidColorBrush x:Key="PlaceholderText" Color="#A08D7D"/>
<SolidColorBrush x:Key="AccentColor" Color="#C96B2C"/>
<SolidColorBrush x:Key="SeparatorColor" Color="#E7D8C9"/>
<SolidColorBrush x:Key="HintBackground" Color="#F7EBDD"/>
<SolidColorBrush x:Key="HintText" Color="#9A531E"/>
<SolidColorBrush x:Key="BorderColor" Color="#E4D4C4"/>
<SolidColorBrush x:Key="ScrollbarThumb" Color="#CDB8A6"/>
<SolidColorBrush x:Key="ShadowColor" Color="#26000000"/>
</ResourceDictionary>

View File

@@ -0,0 +1,17 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="LauncherBackground" Color="#111827"/>
<SolidColorBrush x:Key="ItemBackground" Color="#182231"/>
<SolidColorBrush x:Key="ItemSelectedBackground" Color="#223247"/>
<SolidColorBrush x:Key="ItemHoverBackground" Color="#26384F"/>
<SolidColorBrush x:Key="PrimaryText" Color="#E8EEF5"/>
<SolidColorBrush x:Key="SecondaryText" Color="#AEBBCB"/>
<SolidColorBrush x:Key="PlaceholderText" Color="#8393A7"/>
<SolidColorBrush x:Key="AccentColor" Color="#66A3FF"/>
<SolidColorBrush x:Key="SeparatorColor" Color="#334155"/>
<SolidColorBrush x:Key="HintBackground" Color="#1C2A3B"/>
<SolidColorBrush x:Key="HintText" Color="#9CC3FF"/>
<SolidColorBrush x:Key="BorderColor" Color="#334155"/>
<SolidColorBrush x:Key="ScrollbarThumb" Color="#52647A"/>
<SolidColorBrush x:Key="ShadowColor" Color="#99000000"/>
</ResourceDictionary>

View File

@@ -0,0 +1,17 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="LauncherBackground" Color="#F4F7FB"/>
<SolidColorBrush x:Key="ItemBackground" Color="#FFFFFF"/>
<SolidColorBrush x:Key="ItemSelectedBackground" Color="#E6EDF7"/>
<SolidColorBrush x:Key="ItemHoverBackground" Color="#EEF3F9"/>
<SolidColorBrush x:Key="PrimaryText" Color="#1F2A37"/>
<SolidColorBrush x:Key="SecondaryText" Color="#64748B"/>
<SolidColorBrush x:Key="PlaceholderText" Color="#94A3B8"/>
<SolidColorBrush x:Key="AccentColor" Color="#2F6FBE"/>
<SolidColorBrush x:Key="SeparatorColor" Color="#D8E1EB"/>
<SolidColorBrush x:Key="HintBackground" Color="#EAF2FB"/>
<SolidColorBrush x:Key="HintText" Color="#215C9E"/>
<SolidColorBrush x:Key="BorderColor" Color="#D7E0EA"/>
<SolidColorBrush x:Key="ScrollbarThumb" Color="#B6C3D2"/>
<SolidColorBrush x:Key="ShadowColor" Color="#22000000"/>
</ResourceDictionary>

View File

@@ -0,0 +1,17 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="LauncherBackground" Color="#F4F7FB"/>
<SolidColorBrush x:Key="ItemBackground" Color="#FFFFFF"/>
<SolidColorBrush x:Key="ItemSelectedBackground" Color="#E6EDF7"/>
<SolidColorBrush x:Key="ItemHoverBackground" Color="#EEF3F9"/>
<SolidColorBrush x:Key="PrimaryText" Color="#1F2A37"/>
<SolidColorBrush x:Key="SecondaryText" Color="#64748B"/>
<SolidColorBrush x:Key="PlaceholderText" Color="#94A3B8"/>
<SolidColorBrush x:Key="AccentColor" Color="#2F6FBE"/>
<SolidColorBrush x:Key="SeparatorColor" Color="#D8E1EB"/>
<SolidColorBrush x:Key="HintBackground" Color="#EAF2FB"/>
<SolidColorBrush x:Key="HintText" Color="#215C9E"/>
<SolidColorBrush x:Key="BorderColor" Color="#D7E0EA"/>
<SolidColorBrush x:Key="ScrollbarThumb" Color="#B6C3D2"/>
<SolidColorBrush x:Key="ShadowColor" Color="#26000000"/>
</ResourceDictionary>

View File

@@ -391,7 +391,7 @@
Grid.Column="1"
Style="{StaticResource ToggleSwitch}"/>
</Grid>
<Grid Margin="0,8,0,0">
<Grid Margin="0,8,0,0" Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
@@ -533,32 +533,6 @@
Style="{StaticResource OutlineHoverBtn}"
Click="BtnReasoningMode_Click"/>
</Grid>
<Grid Margin="0,8,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<TextBlock Text="폴더 데이터 활용"
Foreground="{DynamicResource PrimaryText}"
VerticalAlignment="Center"/>
<Border Width="16" Height="16" CornerRadius="8" Background="{DynamicResource ItemHoverBackground}" Margin="6,0,0,0" Cursor="Help" VerticalAlignment="Center">
<TextBlock Text="?" FontSize="10" FontWeight="Bold" Foreground="{DynamicResource AccentColor}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Border.ToolTip>
<ToolTip Style="{StaticResource HelpTooltipStyle}">
<TextBlock TextWrapping="Wrap" Foreground="White" FontSize="12" LineHeight="18" MaxWidth="320">
현재 작업 폴더 파일과 문맥을 AI가 얼마나 적극적으로 참고할지 정합니다.
</TextBlock>
</ToolTip>
</Border.ToolTip>
</Border>
</StackPanel>
<Button x:Name="BtnFolderDataUsage"
Grid.Column="1"
MinWidth="120"
Style="{StaticResource OutlineHoverBtn}"
Click="BtnFolderDataUsage_Click"/>
</Grid>
<Grid Margin="0,8,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>

View File

@@ -17,7 +17,6 @@ public partial class AgentSettingsWindow : Window
private readonly LlmSettings _llm;
private string _permissionMode = PermissionModeCatalog.Deny;
private string _reasoningMode = "detailed";
private string _folderDataUsage = "active";
private string _operationMode = OperationModePolicy.InternalMode;
private string _displayMode = "rich";
private string _defaultOutputFormat = "auto";
@@ -48,7 +47,6 @@ public partial class AgentSettingsWindow : Window
ModelInput.Text = _selectedModel;
_permissionMode = PermissionModeCatalog.NormalizeGlobalMode(_llm.FilePermission);
_reasoningMode = string.IsNullOrWhiteSpace(_llm.AgentDecisionLevel) ? "detailed" : _llm.AgentDecisionLevel;
_folderDataUsage = string.IsNullOrWhiteSpace(_llm.FolderDataUsage) ? "active" : _llm.FolderDataUsage;
_operationMode = OperationModePolicy.Normalize(_settings.Settings.OperationMode);
_displayMode = "rich";
_defaultOutputFormat = string.IsNullOrWhiteSpace(_llm.DefaultOutputFormat) ? "auto" : _llm.DefaultOutputFormat;
@@ -114,7 +112,6 @@ public partial class AgentSettingsWindow : Window
BtnOperationMode.Content = BuildOperationModeLabel(_operationMode);
BtnPermissionMode.Content = PermissionModeCatalog.ToDisplayLabel(_permissionMode);
BtnReasoningMode.Content = BuildReasoningModeLabel(_reasoningMode);
BtnFolderDataUsage.Content = BuildFolderDataUsageLabel(_folderDataUsage);
}
private void RefreshDisplayModeCards()
@@ -173,16 +170,6 @@ public partial class AgentSettingsWindow : Window
};
}
private static string BuildFolderDataUsageLabel(string mode)
{
return (mode ?? "none").ToLowerInvariant() switch
{
"active" => "적극 활용",
"passive" => "소극 활용",
_ => "활용하지 않음",
};
}
private static string BuildOutputFormatLabel(string format)
{
return (format ?? "auto").ToLowerInvariant() switch
@@ -469,17 +456,6 @@ public partial class AgentSettingsWindow : Window
RefreshModeLabels();
}
private void BtnFolderDataUsage_Click(object sender, RoutedEventArgs e)
{
_folderDataUsage = _folderDataUsage switch
{
"none" => "passive",
"passive" => "active",
_ => "none",
};
RefreshModeLabels();
}
private void BtnDefaultOutputFormat_Click(object sender, RoutedEventArgs e)
{
_defaultOutputFormat = (_defaultOutputFormat ?? "auto").ToLowerInvariant() switch
@@ -514,7 +490,6 @@ public partial class AgentSettingsWindow : Window
_llm.FilePermission = _permissionMode;
_llm.DefaultAgentPermission = _permissionMode;
_llm.AgentDecisionLevel = _reasoningMode;
_llm.FolderDataUsage = _folderDataUsage;
_llm.AgentUiExpressionLevel = "rich";
_llm.DefaultOutputFormat = _defaultOutputFormat;
_llm.DefaultMood = _defaultMood;

View File

@@ -0,0 +1,397 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using AxCopilot.Services.Agent;
namespace AxCopilot.Views;
public partial class ChatWindow
{
private async Task<string?> ShowInlineUserAskAsync(string question, List<string> options, string defaultValue)
{
var tcs = new TaskCompletionSource<string?>();
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;
MessagePanel.Children.Remove(_userAskCard);
_userAskCard = null;
}
private void AddUserAskCard(string question, List<string> options, string defaultValue, TaskCompletionSource<string?> 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 okBrush = BrushFromHex("#10B981");
var dangerBrush = BrushFromHex("#EF4444");
var container = new Border
{
Margin = new Thickness(40, 4, 90, 8),
HorizontalAlignment = HorizontalAlignment.Left,
MaxWidth = Math.Max(420, GetMessageMaxWidth() - 36),
Background = itemBg,
BorderBrush = borderBrush,
BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(14),
Padding = new Thickness(14, 12, 14, 12),
};
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,
Foreground = primaryText,
TextWrapping = TextWrapping.Wrap,
LineHeight = 20,
});
Border? selectedOption = null;
string selectedResponse = defaultValue;
if (options.Count > 0)
{
var optionPanel = new WrapPanel
{
Margin = new Thickness(0, 0, 0, 10),
ItemWidth = double.NaN,
};
foreach (var option in options.Where(static option => !string.IsNullOrWhiteSpace(option)))
{
var optionLabel = option.Trim();
var optBorder = 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),
Cursor = Cursors.Hand,
Child = new TextBlock
{
Text = optionLabel,
FontSize = 12,
Foreground = primaryText,
},
};
optBorder.MouseEnter += (s, _) =>
{
if (!ReferenceEquals(selectedOption, s))
((Border)s).Background = hoverBg;
};
optBorder.MouseLeave += (s, _) =>
{
if (!ReferenceEquals(selectedOption, s))
((Border)s).Background = Brushes.Transparent;
};
optBorder.MouseLeftButtonUp += (_, _) =>
{
if (selectedOption != null)
{
selectedOption.Background = Brushes.Transparent;
selectedOption.BorderBrush = borderBrush;
}
selectedOption = optBorder;
selectedOption.Background = new SolidColorBrush(Color.FromArgb(0x18, accentColor.R, accentColor.G, accentColor.B));
selectedOption.BorderBrush = accentBrush;
selectedResponse = optionLabel;
};
optionPanel.Children.Add(optBorder);
}
outer.Children.Add(optionPanel);
}
outer.Children.Add(new TextBlock
{
Text = "직접 입력",
FontSize = 11.5,
Foreground = secondaryText,
Margin = new Thickness(0, 0, 0, 6),
});
var inputBox = new TextBox
{
Text = defaultValue,
AcceptsReturn = true,
TextWrapping = TextWrapping.Wrap,
MinHeight = 42,
MaxHeight = 100,
FontSize = 12.5,
Padding = new Thickness(10, 8, 10, 8),
Background = Brushes.Transparent,
Foreground = primaryText,
CaretBrush = primaryText,
BorderBrush = borderBrush,
BorderThickness = new Thickness(1),
};
inputBox.TextChanged += (_, _) =>
{
if (!string.IsNullOrWhiteSpace(inputBox.Text))
{
selectedResponse = inputBox.Text.Trim();
if (selectedOption != null)
{
selectedOption.Background = Brushes.Transparent;
selectedOption.BorderBrush = borderBrush;
selectedOption = null;
}
}
};
outer.Children.Add(inputBox);
var buttonRow = new StackPanel
{
Orientation = Orientation.Horizontal,
HorizontalAlignment = HorizontalAlignment.Right,
Margin = new Thickness(0, 12, 0, 0),
};
Border BuildActionButton(string label, Brush bg, Brush fg)
{
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 += (_, _) =>
{
RemoveUserAskCard();
tcs.TrySetResult(null);
};
buttonRow.Children.Add(cancelBtn);
var submitBtn = BuildActionButton("전달", okBrush, Brushes.White);
submitBtn.MouseLeftButtonUp += (_, _) =>
{
var finalResponse = !string.IsNullOrWhiteSpace(inputBox.Text)
? inputBox.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)));
MessagePanel.Children.Add(container);
ForceScrollToEnd();
inputBox.Focus();
inputBox.CaretIndex = inputBox.Text.Length;
}
private Func<string, List<string>, Task<string?>> CreatePlanDecisionCallback()
{
return async (planSummary, options) =>
{
var tcs = new TaskCompletionSource<string?>();
var steps = TaskDecomposer.ExtractSteps(planSummary);
await Dispatcher.InvokeAsync(() =>
{
EnsurePlanViewerWindow();
_planViewerWindow?.LoadPlan(planSummary, steps, tcs);
ShowPlanButton(true);
AddDecisionButtons(tcs, options);
});
var completed = await Task.WhenAny(tcs.Task, Task.Delay(TimeSpan.FromMinutes(5)));
if (completed != tcs.Task)
{
await Dispatcher.InvokeAsync(() => _planViewerWindow?.Hide());
return "취소";
}
var result = await tcs.Task;
var agentDecision = result;
if (result == null)
{
agentDecision = _planViewerWindow?.BuildApprovedDecisionPayload(AgentLoopService.ApprovedPlanDecisionPrefix);
}
else if (!string.Equals(result, "취소", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(result, "승인", StringComparison.OrdinalIgnoreCase)
&& !string.IsNullOrWhiteSpace(result))
{
agentDecision = $"수정 요청: {result.Trim()}";
}
if (result == null)
{
await Dispatcher.InvokeAsync(() =>
{
_planViewerWindow?.SwitchToExecutionMode();
_planViewerWindow?.Hide();
});
}
else
{
await Dispatcher.InvokeAsync(() => _planViewerWindow?.Hide());
}
return agentDecision;
};
}
private void EnsurePlanViewerWindow()
{
if (_planViewerWindow != null && IsWindowAlive(_planViewerWindow))
return;
_planViewerWindow = new PlanViewerWindow(this);
_planViewerWindow.Closing += (_, e) =>
{
e.Cancel = true;
_planViewerWindow.Hide();
};
}
private void ShowPlanButton(bool show)
{
if (!show)
{
for (int i = MoodIconPanel.Children.Count - 1; i >= 0; i--)
{
if (MoodIconPanel.Children[i] is Border b && b.Tag?.ToString() == "PlanBtn")
{
if (i > 0 && MoodIconPanel.Children[i - 1] is Border sep && sep.Tag?.ToString() == "PlanSep")
MoodIconPanel.Children.RemoveAt(i - 1);
if (i < MoodIconPanel.Children.Count)
MoodIconPanel.Children.RemoveAt(Math.Min(i, MoodIconPanel.Children.Count - 1));
break;
}
}
return;
}
foreach (var child in MoodIconPanel.Children)
{
if (child is Border b && b.Tag?.ToString() == "PlanBtn")
return;
}
var separator = new Border
{
Width = 1,
Height = 18,
Background = TryFindResource("SeparatorColor") as Brush ?? Brushes.Gray,
Margin = new Thickness(4, 0, 4, 0),
VerticalAlignment = VerticalAlignment.Center,
Tag = "PlanSep",
};
MoodIconPanel.Children.Add(separator);
var planBtn = CreateFolderBarButton("\uE9D2", "계획", "실행 계획 보기", "#10B981");
planBtn.Tag = "PlanBtn";
planBtn.MouseLeftButtonUp += (_, e) =>
{
e.Handled = true;
if (_planViewerWindow != null && IsWindowAlive(_planViewerWindow))
{
_planViewerWindow.Show();
_planViewerWindow.Activate();
}
};
MoodIconPanel.Children.Add(planBtn);
}
private void UpdatePlanViewerStep(AgentEvent evt)
{
if (_planViewerWindow == null || !IsWindowAlive(_planViewerWindow))
return;
if (evt.StepCurrent > 0)
_planViewerWindow.UpdateCurrentStep(evt.StepCurrent - 1);
}
private void CompletePlanViewer()
{
if (_planViewerWindow != null && IsWindowAlive(_planViewerWindow))
_planViewerWindow.MarkComplete();
ShowPlanButton(false);
}
private static bool IsWindowAlive(Window? w)
{
if (w == null)
return false;
try
{
var _ = w.IsVisible;
return true;
}
catch
{
return false;
}
}
}

View File

@@ -0,0 +1,358 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using AxCopilot.Models;
using AxCopilot.Services;
namespace AxCopilot.Views;
public partial class ChatWindow
{
private void RuntimeTaskSummary_Click(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
_taskSummaryTarget = sender as UIElement ?? RuntimeActivityBadge;
ShowTaskSummaryPopup();
}
private void ShowTaskSummaryPopup()
{
if (_taskSummaryTarget == null)
return;
if (_taskSummaryPopup != null)
_taskSummaryPopup.IsOpen = false;
var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.Black;
var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.DimGray;
var popupBackground = TryFindResource("LauncherBackground") as Brush ?? Brushes.White;
var panel = new StackPanel { Margin = new Thickness(2) };
panel.Children.Add(new TextBlock
{
Text = "작업 요약",
FontSize = 11,
FontWeight = FontWeights.SemiBold,
Foreground = primaryText,
Margin = new Thickness(8, 5, 8, 2),
});
panel.Children.Add(new TextBlock
{
Text = "현재 상태 요약",
FontSize = 8.5,
Foreground = secondaryText,
Margin = new Thickness(8, 0, 8, 5),
});
ChatConversation? currentConversation;
lock (_convLock) currentConversation = _currentConversation;
AddTaskSummaryObservabilitySections(panel, currentConversation);
if (!string.IsNullOrWhiteSpace(_appState.AgentRun.RunId))
{
var currentRun = new Border
{
Background = BrushFromHex("#F8FAFC"),
BorderBrush = BrushFromHex("#E2E8F0"),
BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(8),
Padding = new Thickness(8, 6, 8, 6),
Margin = new Thickness(6, 0, 6, 6),
Child = new StackPanel
{
Children =
{
new TextBlock
{
Text = $"실행 run {ShortRunId(_appState.AgentRun.RunId)}",
FontWeight = FontWeights.SemiBold,
Foreground = primaryText,
FontSize = 9.75,
},
new TextBlock
{
Text = $"{GetRunStatusLabel(_appState.AgentRun.Status)} · step {_appState.AgentRun.LastIteration}",
Margin = new Thickness(0, 2, 0, 0),
Foreground = GetRunStatusBrush(_appState.AgentRun.Status),
FontSize = 9,
},
new TextBlock
{
Text = string.IsNullOrWhiteSpace(_appState.AgentRun.Summary) ? "요약 없음" : _appState.AgentRun.Summary,
Margin = new Thickness(0, 3, 0, 0),
TextWrapping = TextWrapping.Wrap,
Foreground = Brushes.DimGray,
FontSize = 9,
}
}
}
};
panel.Children.Add(currentRun);
}
var recentAgentRuns = _appState.GetRecentAgentRuns(1);
if (recentAgentRuns.Count > 0)
{
panel.Children.Add(new TextBlock
{
Text = "마지막 실행",
FontSize = 9,
FontWeight = FontWeights.SemiBold,
Foreground = Brushes.DimGray,
Margin = new Thickness(8, 0, 8, 2),
});
foreach (var run in recentAgentRuns)
{
var runEvents = GetExecutionEventsForRun(run.RunId, 1);
var runFilePaths = GetExecutionEventFilePaths(run.RunId, 1);
var runDisplay = _appState.GetRunDisplay(run);
var runCardStack = new StackPanel
{
Children =
{
new TextBlock
{
Text = runDisplay.HeaderText,
FontWeight = FontWeights.SemiBold,
Foreground = GetRunStatusBrush(run.Status),
FontSize = 9.5,
},
new TextBlock
{
Text = runDisplay.MetaText,
Margin = new Thickness(0, 1, 0, 0),
Foreground = secondaryText,
FontSize = 8.25,
},
new TextBlock
{
Text = TruncateForStatus(runDisplay.SummaryText, 92),
Margin = new Thickness(0, 1.5, 0, 0),
TextWrapping = TextWrapping.Wrap,
Foreground = secondaryText,
FontSize = 8.5,
}
}
};
if (runEvents.Count > 0 || runFilePaths.Count > 0)
{
var activitySummary = new StackPanel();
activitySummary.Children.Add(new TextBlock
{
Text = $"로그 {runEvents.Count} · 파일 {runFilePaths.Count}",
FontSize = 8,
Foreground = secondaryText,
});
if (!string.IsNullOrWhiteSpace(run.RunId))
{
var capturedRunId = run.RunId;
var timelineButton = CreateTaskSummaryActionButton(
"타임라인",
"#F8FAFC",
"#CBD5E1",
"#334155",
(_, _) => ScrollToRunInTimeline(capturedRunId),
trailingMargin: false);
timelineButton.Margin = new Thickness(0, 5, 0, 0);
activitySummary.Children.Add(timelineButton);
}
runCardStack.Children.Add(new Border
{
Background = BrushFromHex("#F8FAFC"),
BorderBrush = BrushFromHex("#E2E8F0"),
BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(7),
Padding = new Thickness(6, 4, 6, 4),
Margin = new Thickness(0, 5, 0, 0),
Child = activitySummary
});
}
if (string.Equals(run.Status, "completed", StringComparison.OrdinalIgnoreCase))
{
var capturedRun = run;
var followUpButton = CreateTaskSummaryActionButton(
"후속 큐",
"#ECFDF5",
"#BBF7D0",
"#166534",
(_, _) => EnqueueFollowUpFromRun(capturedRun),
trailingMargin: false);
followUpButton.Margin = new Thickness(0, 6, 0, 0);
runCardStack.Children.Add(followUpButton);
}
if (string.Equals(run.Status, "failed", StringComparison.OrdinalIgnoreCase) && CanRetryCurrentConversation())
{
var retryButton = CreateTaskSummaryActionButton(
"다시 시도",
"#FEF2F2",
"#FCA5A5",
"#991B1B",
(_, _) =>
{
_taskSummaryPopup?.SetCurrentValue(Popup.IsOpenProperty, false);
RetryLastUserMessageFromConversation();
},
trailingMargin: false);
retryButton.Margin = new Thickness(0, 6, 0, 0);
runCardStack.Children.Add(retryButton);
}
panel.Children.Add(new Border
{
Background = popupBackground,
BorderBrush = BrushFromHex("#E5E7EB"),
BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(7),
Padding = new Thickness(7, 5, 7, 5),
Margin = new Thickness(6, 0, 6, 4),
Child = runCardStack
});
}
}
var activeTasks = FilterTaskSummaryItems(_appState.ActiveTasks).Take(3).ToList();
var recentTasks = FilterTaskSummaryItems(_appState.RecentTasks).Take(2).ToList();
foreach (var task in activeTasks)
panel.Children.Add(BuildTaskSummaryCard(task, active: true));
if (ShouldIncludeRecentTaskSummary(activeTasks))
{
foreach (var task in recentTasks)
panel.Children.Add(BuildTaskSummaryCard(task, active: false));
}
if (activeTasks.Count == 0 && recentTasks.Count == 0)
{
panel.Children.Add(new TextBlock
{
Text = "표시할 작업 이력이 없습니다.",
Margin = new Thickness(10, 2, 10, 8),
Foreground = secondaryText,
});
}
_taskSummaryPopup = new Popup
{
PlacementTarget = _taskSummaryTarget,
Placement = PlacementMode.Top,
AllowsTransparency = true,
StaysOpen = false,
PopupAnimation = PopupAnimation.Fade,
Child = new Border
{
Background = popupBackground,
BorderBrush = BrushFromHex("#E5E7EB"),
BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(12),
Padding = new Thickness(6),
Child = new ScrollViewer
{
Content = panel,
MaxHeight = 340,
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
}
}
};
_taskSummaryPopup.IsOpen = true;
}
private Border BuildTaskSummaryCard(TaskRunStore.TaskRun task, bool active)
{
var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.Black;
var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.DimGray;
var (kindIcon, kindColor) = GetTaskKindVisual(task.Kind);
var categoryLabel = GetTranscriptTaskCategory(task);
var displayTitle = string.Equals(task.Kind, "tool", StringComparison.OrdinalIgnoreCase)
|| string.Equals(task.Kind, "permission", StringComparison.OrdinalIgnoreCase)
|| string.Equals(task.Kind, "hook", StringComparison.OrdinalIgnoreCase)
? GetAgentItemDisplayName(task.Title)
: task.Title;
var taskStack = new StackPanel();
var headerRow = new StackPanel
{
Orientation = Orientation.Horizontal,
Margin = new Thickness(0, 0, 0, 2),
};
headerRow.Children.Add(new TextBlock
{
Text = kindIcon,
FontFamily = new FontFamily("Segoe MDL2 Assets"),
FontSize = 9.5,
Foreground = kindColor,
Margin = new Thickness(0, 0, 4, 0),
VerticalAlignment = VerticalAlignment.Center,
});
headerRow.Children.Add(new TextBlock
{
Text = active
? $"진행 중 · {displayTitle}"
: $"{GetTaskStatusLabel(task.Status)} · {displayTitle}",
FontSize = 9.5,
FontWeight = FontWeights.SemiBold,
Foreground = active ? primaryText : secondaryText,
TextWrapping = TextWrapping.Wrap,
});
taskStack.Children.Add(headerRow);
taskStack.Children.Add(new Border
{
Background = BrushFromHex("#F8FAFC"),
BorderBrush = BrushFromHex("#E5E7EB"),
BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(999),
Padding = new Thickness(6, 1, 6, 1),
Margin = new Thickness(0, 0, 0, 4),
HorizontalAlignment = HorizontalAlignment.Left,
Child = new TextBlock
{
Text = categoryLabel,
FontSize = 8,
FontWeight = FontWeights.SemiBold,
Foreground = secondaryText,
},
});
if (!string.IsNullOrWhiteSpace(task.Summary))
{
taskStack.Children.Add(new TextBlock
{
Text = TruncateForStatus(task.Summary, 96),
FontSize = 8.75,
Foreground = secondaryText,
TextWrapping = TextWrapping.Wrap,
});
}
var reviewChipRow = BuildReviewSignalChipRow(
kind: task.Kind,
toolName: task.Title,
title: displayTitle,
summary: task.Summary);
if (reviewChipRow != null)
taskStack.Children.Add(reviewChipRow);
var actionRow = BuildTaskSummaryActionRow(task, active);
if (actionRow != null)
taskStack.Children.Add(actionRow);
return new Border
{
Background = active ? BrushFromHex("#F8FAFC") : (TryFindResource("LauncherBackground") as Brush ?? Brushes.White),
BorderBrush = BrushFromHex("#E5E7EB"),
BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(7),
Padding = new Thickness(8, 5, 8, 5),
Margin = new Thickness(8, 0, 8, 4),
Child = taskStack
};
}
}

View File

@@ -1360,22 +1360,6 @@
</Border>
</Popup>
<!-- ── 데이터 활용 수준 팝업 ── -->
<Popup x:Name="DataUsagePopup" Grid.Row="4"
PlacementTarget="{Binding ElementName=BtnDataUsage}"
Placement="Top" StaysOpen="False"
AllowsTransparency="True" PopupAnimation="Fade">
<Border Background="{DynamicResource LauncherBackground}"
BorderBrush="{DynamicResource BorderColor}"
BorderThickness="1" CornerRadius="18"
Padding="10" MinWidth="300">
<Border.Effect>
<DropShadowEffect BlurRadius="22" ShadowDepth="0" Opacity="0.15"/>
</Border.Effect>
<StackPanel x:Name="DataUsageItems" Margin="2"/>
</Border>
</Popup>
<!-- ── 슬래시 명령어 팝업 ── -->
<Popup x:Name="SlashPopup" Grid.Row="4"
PlacementTarget="{Binding ElementName=InputBorder}"
@@ -2198,28 +2182,7 @@
<Border x:Name="FormatMoodSeparator" Grid.Column="4" Width="1" Height="18" Margin="4,0"
Background="{DynamicResource SeparatorColor}" Visibility="Collapsed"/>
<!-- 데이터 활용 메뉴 -->
<Border x:Name="BtnDataUsage" Grid.Column="5" Cursor="Hand"
Background="Transparent"
BorderBrush="Transparent"
BorderThickness="0"
Padding="10,5"
CornerRadius="8"
MouseLeftButtonUp="BtnDataUsage_Click"
ToolTip="폴더 데이터 활용 수준">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="DataUsageIcon" Text="&#xE9F5;" FontFamily="Segoe MDL2 Assets" FontSize="12"
Foreground="{DynamicResource SecondaryText}"
VerticalAlignment="Center" Margin="0,0,4,0"/>
<TextBlock x:Name="DataUsageLabel" Text="활용하지 않음" FontSize="12"
Foreground="{DynamicResource SecondaryText}"
VerticalAlignment="Center"/>
</StackPanel>
</Border>
<!-- 구분선 -->
<Border Grid.Column="6" Width="1" Height="18" Margin="4,0"
Background="{DynamicResource SeparatorColor}"/>
<!-- 폴더 데이터 활용은 코드/코워크 자동 정책으로만 동작 -->
<!-- 권한 메뉴 -->
<Button x:Name="BtnPermission" Grid.Column="7" Style="{StaticResource OutlineHoverBtn}"
@@ -2245,11 +2208,11 @@
Margin="2,0,0,0"
Visibility="Collapsed"
Click="BtnGitBranch_Click"
ToolTip="현재 Git 브랜치 상태">
ToolTip="Git 브랜치 선택">
<StackPanel Orientation="Horizontal">
<TextBlock Text="&#xE943;"
FontFamily="Segoe MDL2 Assets"
FontSize="12"
FontSize="12.5"
Foreground="{DynamicResource SecondaryText}"
VerticalAlignment="Center"
Margin="0,0,4,0"/>
@@ -2260,18 +2223,21 @@
VerticalAlignment="Center"/>
<TextBlock x:Name="GitBranchFilesText"
Text=""
Visibility="Collapsed"
FontSize="11"
Foreground="{DynamicResource SecondaryText}"
VerticalAlignment="Center"
Margin="6,0,0,0"/>
<TextBlock x:Name="GitBranchAddedText"
Text=""
Visibility="Collapsed"
FontSize="11"
Foreground="#16A34A"
VerticalAlignment="Center"
Margin="6,0,0,0"/>
<TextBlock x:Name="GitBranchDeletedText"
Text=""
Visibility="Collapsed"
FontSize="11"
Foreground="#DC2626"
VerticalAlignment="Center"
@@ -2650,6 +2616,30 @@
FontSize="12"
Foreground="{DynamicResource PrimaryText}"/>
</Border>
<Border x:Name="OverlayThemeStyleNordCard"
Cursor="Hand"
CornerRadius="8"
BorderThickness="1"
BorderBrush="{DynamicResource BorderColor}"
Padding="10,7"
Margin="0,0,8,8"
MouseLeftButtonUp="OverlayThemeStyleNordCard_MouseLeftButtonUp">
<TextBlock Text="Nord"
FontSize="12"
Foreground="{DynamicResource PrimaryText}"/>
</Border>
<Border x:Name="OverlayThemeStyleEmberCard"
Cursor="Hand"
CornerRadius="8"
BorderThickness="1"
BorderBrush="{DynamicResource BorderColor}"
Padding="10,7"
Margin="0,0,8,8"
MouseLeftButtonUp="OverlayThemeStyleEmberCard_MouseLeftButtonUp">
<TextBlock Text="Ember"
FontSize="12"
Foreground="{DynamicResource PrimaryText}"/>
</Border>
<Border x:Name="OverlayThemeStyleSlateCard"
Cursor="Hand"
CornerRadius="8"
@@ -3250,9 +3240,103 @@
Background="{DynamicResource LauncherBackground}"
BorderBrush="{DynamicResource BorderColor}"
BorderThickness="1"
Foreground="{DynamicResource PrimaryText}"
Foreground="{DynamicResource PrimaryText}"
FontSize="12"/>
</Grid>
<StackPanel Margin="0,2,0,12">
<TextBlock Text="대화 관리"
FontSize="13"
FontWeight="SemiBold"
Foreground="{DynamicResource PrimaryText}"/>
<TextBlock Text="대화 보관 기간과 저장 공간 정리를 여기서 바로 관리합니다."
Margin="0,4,0,0"
FontSize="11"
Foreground="{DynamicResource SecondaryText}"/>
</StackPanel>
<Grid Margin="0,0,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Margin="0,0,12,0">
<TextBlock Text="대화 보관 기간"
FontSize="12.5"
FontWeight="SemiBold"
Foreground="{DynamicResource PrimaryText}"/>
<TextBlock Text="설정 기간이 지나면 오래된 대화는 자동으로 정리됩니다."
Margin="0,4,0,0"
FontSize="11"
Foreground="{DynamicResource SecondaryText}"/>
</StackPanel>
<WrapPanel Grid.Column="1" HorizontalAlignment="Right">
<Button x:Name="BtnOverlayRetention7"
Content="7일"
Padding="10,6"
Margin="0,0,6,0"
Click="BtnOverlayRetention_Click"/>
<Button x:Name="BtnOverlayRetention30"
Content="30일"
Padding="10,6"
Margin="0,0,6,0"
Click="BtnOverlayRetention_Click"/>
<Button x:Name="BtnOverlayRetention90"
Content="90일"
Padding="10,6"
Margin="0,0,6,0"
Click="BtnOverlayRetention_Click"/>
<Button x:Name="BtnOverlayRetentionUnlimited"
Content="무제한"
Padding="10,6"
Click="BtnOverlayRetention_Click"/>
</WrapPanel>
</Grid>
<Border Background="{DynamicResource ItemBackground}"
BorderBrush="{DynamicResource BorderColor}"
BorderThickness="1"
CornerRadius="12"
Padding="14,12"
Margin="0,0,0,12">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel>
<TextBlock Text="저장 공간"
FontSize="12.5"
FontWeight="SemiBold"
Foreground="{DynamicResource PrimaryText}"/>
<TextBlock x:Name="OverlayStorageSummaryText"
Text="분석 중..."
Margin="0,6,0,0"
FontSize="12"
FontWeight="SemiBold"
Foreground="{DynamicResource PrimaryText}"/>
<TextBlock x:Name="OverlayStorageDriveText"
Margin="0,3,0,0"
FontSize="11"
Foreground="{DynamicResource SecondaryText}"/>
</StackPanel>
<StackPanel Grid.Column="1"
Orientation="Horizontal"
VerticalAlignment="Top">
<Button x:Name="BtnOverlayStorageRefresh"
Content="새로고침"
Padding="10,6"
Margin="0,0,8,0"
Click="BtnOverlayStorageRefresh_Click"/>
<Button x:Name="BtnOverlayDeleteAllConversations"
Content="대화 삭제"
Padding="10,6"
Margin="0,0,8,0"
Click="BtnOverlayDeleteAllConversations_Click"/>
<Button x:Name="BtnOverlayStorageCleanup"
Content="저장 공간 줄이기"
Padding="12,6"
Click="BtnOverlayStorageCleanup_Click"/>
</StackPanel>
</Grid>
</Border>
<Border x:Name="OverlayToggleImageInput" Style="{StaticResource OverlayAdvancedToggleRowStyle}">
<Grid>
<Grid.ColumnDefinitions>
@@ -3372,43 +3456,6 @@
<ComboBoxItem Content="모드 · 사외 모드" Tag="external"/>
</ComboBox>
</Grid>
<Grid x:Name="OverlayFolderDataUsageRow" Margin="0,8,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="폴더 데이터 활용"
Foreground="{DynamicResource PrimaryText}"
VerticalAlignment="Center"/>
<Border Style="{StaticResource OverlayHelpBadge}">
<TextBlock Text="?"
FontSize="10"
FontWeight="Bold"
Foreground="{DynamicResource AccentColor}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<Border.ToolTip>
<ToolTip Style="{StaticResource HelpTooltipStyle}">
<TextBlock TextWrapping="Wrap"
Foreground="White"
FontSize="12"
LineHeight="18"
MaxWidth="280">현재 폴더의 파일과 문맥을 응답에 얼마나 적극적으로 활용할지 정합니다. 적극 활용은 관련 파일을 더 넓게 참조하고, 활용하지 않음은 현재 입력 중심으로만 답합니다.</TextBlock>
</ToolTip>
</Border.ToolTip>
</Border>
</StackPanel>
<ComboBox x:Name="CmbOverlayFolderDataUsage"
Grid.Column="1"
MinWidth="140"
Style="{StaticResource OverlayComboBox}"
SelectionChanged="CmbOverlayFolderDataUsage_SelectionChanged">
<ComboBoxItem Content="데이터 · 활용하지 않음" Tag="none"/>
<ComboBoxItem Content="데이터 · 소극 활용" Tag="passive"/>
<ComboBoxItem Content="데이터 · 적극 활용" Tag="active"/>
</ComboBox>
</Grid>
<Grid x:Name="OverlayTlsRow" Visibility="Collapsed" Margin="0,0,0,0">
<CheckBox x:Name="OverlayHiddenTlsToggle" Visibility="Collapsed"/>
@@ -4037,7 +4084,9 @@
<CheckBox x:Name="ChkOverlayWorkflowVisualizer"
Grid.Column="1"
VerticalAlignment="Center"
Style="{StaticResource ToggleSwitch}"/>
Style="{StaticResource ToggleSwitch}"
Checked="ChkOverlayWorkflowVisualizer_Changed"
Unchecked="ChkOverlayWorkflowVisualizer_Changed"/>
</Grid>
</Border>
<Border x:Name="OverlayToggleShowTotalCallStats" Style="{StaticResource OverlayAdvancedToggleRowStyle}" Margin="0,8,0,0">
@@ -4057,7 +4106,9 @@
<CheckBox x:Name="ChkOverlayShowTotalCallStats"
Grid.Column="1"
VerticalAlignment="Center"
Style="{StaticResource ToggleSwitch}"/>
Style="{StaticResource ToggleSwitch}"
Checked="ChkOverlayShowTotalCallStats_Changed"
Unchecked="ChkOverlayShowTotalCallStats_Changed"/>
</Grid>
</Border>
<Border x:Name="OverlayToggleAuditLog" Style="{StaticResource OverlayAdvancedToggleRowStyle}" Margin="0,8,0,0">
@@ -4077,7 +4128,9 @@
<CheckBox x:Name="ChkOverlayEnableAuditLog"
Grid.Column="1"
VerticalAlignment="Center"
Style="{StaticResource ToggleSwitch}"/>
Style="{StaticResource ToggleSwitch}"
Checked="ChkOverlayEnableAuditLog_Changed"
Unchecked="ChkOverlayEnableAuditLog_Changed"/>
</Grid>
</Border>
<Grid x:Name="OverlayOpenAuditLogRow" Margin="0,8,0,0">
@@ -4750,11 +4803,11 @@
CornerRadius="12"
Padding="14,12"
Margin="0,8,0,10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="120"/>
</Grid.ColumnDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="180"/>
</Grid.ColumnDefinitions>
<StackPanel Margin="0,0,16,0">
<TextBlock Text="도구 훅 스크립트 제한 시간"
FontSize="12"
@@ -4769,7 +4822,7 @@
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="44"/>
<ColumnDefinition Width="56"/>
</Grid.ColumnDefinitions>
<Slider x:Name="SldOverlayToolHookTimeoutMs"
Minimum="3000"
@@ -4783,7 +4836,7 @@
<Border Grid.Column="1"
Margin="10,0,0,0"
Padding="8,4"
MinWidth="44"
MinWidth="56"
VerticalAlignment="Center"
Background="{DynamicResource LauncherBackground}"
BorderBrush="{DynamicResource BorderColor}"
@@ -4825,7 +4878,8 @@
BorderThickness="1"
CornerRadius="8"
Padding="10,6"
Margin="12,0,0,0"
Margin="16,0,0,0"
MinWidth="92"
MouseLeftButtonUp="OverlayAddHookBtn_MouseLeftButtonUp">
<StackPanel Orientation="Horizontal">
<TextBlock Text="&#xE710;"

File diff suppressed because it is too large Load Diff

View File

@@ -4957,22 +4957,6 @@
</Grid>
</Border>
<TextBlock Style="{StaticResource SectionHeader}" Text="폴더 데이터 활용"/>
<Border Style="{StaticResource SettingsRow}">
<Grid>
<StackPanel HorizontalAlignment="Left">
<TextBlock Style="{StaticResource RowLabel}" Text="기본 데이터 활용 수준"/>
<TextBlock Style="{StaticResource RowHint}" Text="작업 폴더 내 문서/데이터를 보고서 작성에 활용하는 수준입니다."/>
</StackPanel>
<ComboBox HorizontalAlignment="Right" VerticalAlignment="Center"
Width="180" SelectedValue="{Binding FolderDataUsage, Mode=TwoWay}"
SelectedValuePath="Tag">
<ComboBoxItem Content="활용하지 않음" Tag="none"/>
<ComboBoxItem Content="소극 활용 (요청 시)" Tag="passive"/>
<ComboBoxItem Content="적극 활용 (자동 탐색)" Tag="active"/>
</ComboBox>
</Grid>
</Border>
<!-- ── 품질 검증 ── -->
<TextBlock Style="{StaticResource SectionHeader}" Text="품질 검증"/>