빌드 부산물 추적 해제와 AX Agent 대기열·composer UI 정리
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
- .gitignore에 bin/obj/publish 및 IDE/OS/비밀정보 패턴 추가 - Git 인덱스에서 publish 및 src 하위 bin/obj 빌드 부산물 추적을 해제하여 저장소 노이즈를 정리 - DraftQueue를 실행 대기/최근 결과 섹션과 상태 요약 pill 구조로 재정리 - composer 상단 모델/컨텍스트/프리셋 줄과 하단 작업 위치 칩 UI를 더 평평한 시각 언어로 통일 - 워크스페이스·브랜치·워크트리 패널에 공통 row 및 요약 strip을 적용해 panel UX를 정돈 - README.md와 docs/DEVELOPMENT.md, docs/AGENT_ROADMAP.md, AGENTS.md 이력을 갱신 검증 - dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ - 경고 0개, 오류 0개
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using System.Windows.Data;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
@@ -60,6 +61,7 @@ public partial class SettingsWindow : Window
|
||||
BuildDockBarSettings();
|
||||
BuildTextActionCommandsPanel();
|
||||
MoveBlockSectionToEtc();
|
||||
BuildServiceModelPanels();
|
||||
|
||||
// 스킬이 아직 로드되지 않았으면 백그라운드에서 로드 후 UI 구성
|
||||
var app = System.Windows.Application.Current as App;
|
||||
@@ -83,15 +85,62 @@ public partial class SettingsWindow : Window
|
||||
ApplyAiEnabledState(app?.SettingsService?.Settings.AiEnabled ?? false, init: true);
|
||||
ApplyOperationModeState(app?.SettingsService?.Settings.OperationMode);
|
||||
InitializeDisplayModeUi();
|
||||
SyncAgentSelectionCards();
|
||||
};
|
||||
}
|
||||
|
||||
private void SyncAgentSelectionCards()
|
||||
{
|
||||
var service = (_vm.LlmService ?? "").Trim().ToLowerInvariant();
|
||||
if (AgentServiceCardOllama != null) AgentServiceCardOllama.IsChecked = service == "ollama";
|
||||
if (AgentServiceCardVllm != null) AgentServiceCardVllm.IsChecked = service == "vllm";
|
||||
if (AgentServiceCardGemini != null) AgentServiceCardGemini.IsChecked = service == "gemini";
|
||||
if (AgentServiceCardClaude != null) AgentServiceCardClaude.IsChecked = service == "claude";
|
||||
|
||||
var permission = Services.Agent.PermissionModeCatalog.NormalizeGlobalMode(_vm.DefaultAgentPermission);
|
||||
if (AgentPermissionCardAsk != null) AgentPermissionCardAsk.IsChecked = permission == "Ask";
|
||||
if (AgentPermissionCardPlan != null) AgentPermissionCardPlan.IsChecked = permission == "Plan";
|
||||
if (AgentPermissionCardAuto != null) AgentPermissionCardAuto.IsChecked = permission == "Auto";
|
||||
if (AgentPermissionCardDeny != null) AgentPermissionCardDeny.IsChecked = permission == "Deny";
|
||||
|
||||
var decision = (_vm.AgentDecisionLevel ?? "normal").Trim().ToLowerInvariant();
|
||||
if (AgentDecisionCardMinimal != null) AgentDecisionCardMinimal.IsChecked = decision == "minimal";
|
||||
if (AgentDecisionCardNormal != null) AgentDecisionCardNormal.IsChecked = decision == "normal";
|
||||
if (AgentDecisionCardDetailed != null) AgentDecisionCardDetailed.IsChecked = decision == "detailed";
|
||||
|
||||
var planMode = (_vm.PlanMode ?? "off").Trim().ToLowerInvariant();
|
||||
if (AgentPlanModeCardOff != null) AgentPlanModeCardOff.IsChecked = planMode == "off";
|
||||
if (AgentPlanModeCardAlways != null) AgentPlanModeCardAlways.IsChecked = planMode == "always";
|
||||
if (AgentPlanModeCardAuto != null) AgentPlanModeCardAuto.IsChecked = planMode == "auto";
|
||||
|
||||
var operationMode = OperationModePolicy.Normalize(_vm.OperationMode);
|
||||
if (AgentOperationModeInternal != null) AgentOperationModeInternal.IsChecked = operationMode == OperationModePolicy.InternalMode;
|
||||
if (AgentOperationModeExternal != null) AgentOperationModeExternal.IsChecked = operationMode == OperationModePolicy.ExternalMode;
|
||||
|
||||
var maxContextTokens = _vm.LlmMaxContextTokens;
|
||||
if (AgentContextTokens4K != null) AgentContextTokens4K.IsChecked = maxContextTokens <= 4096;
|
||||
if (AgentContextTokens16K != null) AgentContextTokens16K.IsChecked = maxContextTokens > 4096 && maxContextTokens <= 16384;
|
||||
if (AgentContextTokens64K != null) AgentContextTokens64K.IsChecked = maxContextTokens > 16384 && maxContextTokens <= 65536;
|
||||
if (AgentContextTokens256K != null) AgentContextTokens256K.IsChecked = maxContextTokens > 65536 && maxContextTokens <= 262144;
|
||||
if (AgentContextTokens1M != null) AgentContextTokens1M.IsChecked = maxContextTokens > 262144;
|
||||
|
||||
var retentionDays = _vm.LlmRetentionDays;
|
||||
if (AgentRetentionDays7 != null) AgentRetentionDays7.IsChecked = retentionDays == 7;
|
||||
if (AgentRetentionDays30 != null) AgentRetentionDays30.IsChecked = retentionDays == 30;
|
||||
if (AgentRetentionDays90 != null) AgentRetentionDays90.IsChecked = retentionDays == 90;
|
||||
if (AgentRetentionDaysUnlimited != null) AgentRetentionDaysUnlimited.IsChecked = retentionDays == 0;
|
||||
|
||||
var logLevel = (_vm.AgentLogLevel ?? "simple").Trim().ToLowerInvariant();
|
||||
if (AgentLogLevelSimple != null) AgentLogLevelSimple.IsChecked = logLevel == "simple";
|
||||
if (AgentLogLevelDetailed != null) AgentLogLevelDetailed.IsChecked = logLevel == "detailed";
|
||||
if (AgentLogLevelDebug != null) AgentLogLevelDebug.IsChecked = logLevel == "debug";
|
||||
}
|
||||
|
||||
private void InitializeDisplayModeUi()
|
||||
{
|
||||
var app = System.Windows.Application.Current as App;
|
||||
if (app?.SettingsService?.Settings?.Llm != null)
|
||||
app.SettingsService.Settings.Llm.AgentUiExpressionLevel = "rich";
|
||||
SetDisplayMode("rich", persist: false);
|
||||
var saved = app?.SettingsService?.Settings?.Llm?.AgentUiExpressionLevel;
|
||||
SetDisplayMode(saved ?? "balanced", persist: false);
|
||||
}
|
||||
|
||||
private void DisplayMode_Checked(object sender, RoutedEventArgs e)
|
||||
@@ -107,6 +156,19 @@ public partial class SettingsWindow : Window
|
||||
SetDisplayMode(next, persist: true);
|
||||
}
|
||||
|
||||
private void AgentDisplayMode_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_isDisplayModeSyncing) return;
|
||||
var rb = sender as RadioButton;
|
||||
var next = rb?.Name switch
|
||||
{
|
||||
"AgentDisplayModeRich" => "rich",
|
||||
"AgentDisplayModeSimple" => "simple",
|
||||
_ => "balanced",
|
||||
};
|
||||
SetDisplayMode(next, persist: true);
|
||||
}
|
||||
|
||||
private void SetDisplayMode(string mode, bool persist)
|
||||
{
|
||||
mode = NormalizeDisplayMode(mode);
|
||||
@@ -117,9 +179,15 @@ public partial class SettingsWindow : Window
|
||||
var rich = GetDisplayModeRadio("DisplayModeRich");
|
||||
var balanced = GetDisplayModeRadio("DisplayModeBalanced");
|
||||
var simple = GetDisplayModeRadio("DisplayModeSimple");
|
||||
var agentRich = GetDisplayModeRadio("AgentDisplayModeRich");
|
||||
var agentBalanced = GetDisplayModeRadio("AgentDisplayModeBalanced");
|
||||
var agentSimple = GetDisplayModeRadio("AgentDisplayModeSimple");
|
||||
if (rich != null) rich.IsChecked = mode == "rich";
|
||||
if (balanced != null) balanced.IsChecked = mode == "balanced";
|
||||
if (simple != null) simple.IsChecked = mode == "simple";
|
||||
if (agentRich != null) agentRich.IsChecked = mode == "rich";
|
||||
if (agentBalanced != null) agentBalanced.IsChecked = mode == "balanced";
|
||||
if (agentSimple != null) agentSimple.IsChecked = mode == "simple";
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -183,21 +251,17 @@ public partial class SettingsWindow : Window
|
||||
if (AgentTabCommon == null) return;
|
||||
|
||||
SetSubTabVisible(AgentTabCommon, true);
|
||||
SetSubTabVisible(AgentTabChat, mode != "simple");
|
||||
SetSubTabVisible(AgentTabChat, true);
|
||||
SetSubTabVisible(AgentTabCoworkCode, true);
|
||||
SetSubTabVisible(AgentTabCowork, mode == "rich");
|
||||
SetSubTabVisible(AgentTabCode, mode == "rich");
|
||||
SetSubTabVisible(AgentTabTools, mode != "simple");
|
||||
SetSubTabVisible(AgentTabEtc, mode != "simple");
|
||||
SetSubTabVisible(AgentTabDev, mode == "rich");
|
||||
SetSubTabVisible(AgentTabDev, true);
|
||||
SetSubTabVisible(AgentTabCowork, false);
|
||||
SetSubTabVisible(AgentTabCode, false);
|
||||
SetSubTabVisible(AgentTabTools, false);
|
||||
SetSubTabVisible(AgentTabEtc, false);
|
||||
|
||||
if (AgentTabCommon.IsChecked != true
|
||||
&& AgentTabChat.IsChecked != true
|
||||
&& AgentTabCoworkCode.IsChecked != true
|
||||
&& AgentTabCowork.IsChecked != true
|
||||
&& AgentTabCode.IsChecked != true
|
||||
&& AgentTabTools.IsChecked != true
|
||||
&& AgentTabEtc.IsChecked != true
|
||||
&& AgentTabDev.IsChecked != true)
|
||||
{
|
||||
AgentTabCommon.IsChecked = true;
|
||||
@@ -327,66 +391,66 @@ public partial class SettingsWindow : Window
|
||||
{
|
||||
("파일/검색", "\uE8B7", "#F59E0B", new[]
|
||||
{
|
||||
("file_read", "파일 읽기 — 텍스트/바이너리 파일의 내용을 읽습니다"),
|
||||
("file_write", "파일 쓰기 — 새 파일을 생성하거나 기존 파일을 덮어씁니다"),
|
||||
("file_edit", "파일 편집 — 줄 번호 기반으로 파일의 특정 부분을 수정합니다"),
|
||||
("glob", "파일 패턴 검색 — 글로브 패턴으로 파일을 찾습니다 (예: **/*.cs)"),
|
||||
("grep_tool", "텍스트 검색 — 정규식으로 파일 내용을 검색합니다"),
|
||||
("folder_map", "폴더 구조 — 프로젝트의 디렉토리 트리를 조회합니다"),
|
||||
("document_read", "문서 읽기 — PDF, DOCX 등 문서 파일의 텍스트를 추출합니다"),
|
||||
("file_read", "파일 읽기 ? 텍스트/바이너리 파일의 내용을 읽습니다"),
|
||||
("file_write", "파일 쓰기 ? 새 파일을 생성하거나 기존 파일을 덮어씁니다"),
|
||||
("file_edit", "파일 편집 ? 줄 번호 기반으로 파일의 특정 부분을 수정합니다"),
|
||||
("glob", "파일 패턴 검색 ? 글로브 패턴으로 파일을 찾습니다 (예: **/*.cs)"),
|
||||
("grep_tool", "텍스트 검색 ? 정규식으로 파일 내용을 검색합니다"),
|
||||
("folder_map", "폴더 구조 ? 프로젝트의 디렉토리 트리를 조회합니다"),
|
||||
("document_read", "문서 읽기 ? PDF, DOCX 등 문서 파일의 텍스트를 추출합니다"),
|
||||
}),
|
||||
("프로세스/빌드", "\uE756", "#06B6D4", new[]
|
||||
{
|
||||
("process", "프로세스 실행 — 외부 명령/프로그램을 실행합니다"),
|
||||
("build_run", "빌드/테스트 — 프로젝트 타입을 감지하여 빌드/테스트를 실행합니다"),
|
||||
("dev_env_detect", "개발 환경 감지 — IDE, 런타임, 빌드 도구를 자동으로 인식합니다"),
|
||||
("process", "프로세스 실행 ? 외부 명령/프로그램을 실행합니다"),
|
||||
("build_run", "빌드/테스트 ? 프로젝트 타입을 감지하여 빌드/테스트를 실행합니다"),
|
||||
("dev_env_detect", "개발 환경 감지 ? IDE, 런타임, 빌드 도구를 자동으로 인식합니다"),
|
||||
}),
|
||||
("코드 분석", "\uE943", "#818CF8", new[]
|
||||
{
|
||||
("search_codebase", "코드 키워드 검색 — TF-IDF 기반으로 관련 코드를 찾습니다"),
|
||||
("code_review", "AI 코드 리뷰 — Git diff 분석, 파일 정적 검사, PR 요약"),
|
||||
("lsp", "LSP 인텔리전스 — 정의 이동, 참조 검색, 심볼 목록 (C#/TS/Python)"),
|
||||
("test_loop", "테스트 루프 — 테스트 생성/실행/분석 자동화"),
|
||||
("git_tool", "Git 작업 — status, log, diff, commit 등 Git 명령 실행"),
|
||||
("snippet_runner", "코드 실행 — C#/Python/JavaScript 스니펫 즉시 실행"),
|
||||
("search_codebase", "코드 키워드 검색 ? TF-IDF 기반으로 관련 코드를 찾습니다"),
|
||||
("code_review", "AI 코드 리뷰 ? Git diff 분석, 파일 정적 검사, PR 요약"),
|
||||
("lsp", "LSP 인텔리전스 ? 정의 이동, 참조 검색, 심볼 목록 (C#/TS/Python)"),
|
||||
("test_loop", "테스트 루프 ? 테스트 생성/실행/분석 자동화"),
|
||||
("git_tool", "Git 작업 ? status, log, diff, commit 등 Git 명령 실행"),
|
||||
("snippet_runner", "코드 실행 ? C#/Python/JavaScript 스니펫 즉시 실행"),
|
||||
}),
|
||||
("문서 생성", "\uE8A5", "#34D399", new[]
|
||||
{
|
||||
("excel_create", "Excel 생성 — .xlsx 스프레드시트를 생성합니다"),
|
||||
("docx_create", "Word 생성 — .docx 문서를 생성합니다"),
|
||||
("csv_create", "CSV 생성 — CSV 파일을 생성합니다"),
|
||||
("markdown_create", "마크다운 생성 — .md 파일을 생성합니다"),
|
||||
("html_create", "HTML 생성 — HTML 파일을 생성합니다"),
|
||||
("chart_create", "차트 생성 — 데이터 시각화 차트를 생성합니다"),
|
||||
("batch_create", "배치 생성 — 스크립트 파일을 생성합니다"),
|
||||
("document_review", "문서 검증 — 문서 품질을 검사합니다"),
|
||||
("format_convert", "포맷 변환 — 문서 형식을 변환합니다"),
|
||||
("excel_create", "Excel 생성 ? .xlsx 스프레드시트를 생성합니다"),
|
||||
("docx_create", "Word 생성 ? .docx 문서를 생성합니다"),
|
||||
("csv_create", "CSV 생성 ? CSV 파일을 생성합니다"),
|
||||
("markdown_create", "마크다운 생성 ? .md 파일을 생성합니다"),
|
||||
("html_create", "HTML 생성 ? HTML 파일을 생성합니다"),
|
||||
("chart_create", "차트 생성 ? 데이터 시각화 차트를 생성합니다"),
|
||||
("batch_create", "배치 생성 ? 스크립트 파일을 생성합니다"),
|
||||
("document_review", "문서 검증 ? 문서 품질을 검사합니다"),
|
||||
("format_convert", "포맷 변환 ? 문서 형식을 변환합니다"),
|
||||
}),
|
||||
("데이터 처리", "\uE9F5", "#F59E0B", new[]
|
||||
{
|
||||
("json_tool", "JSON 처리 — JSON 파싱, 변환, 검증, 포맷팅"),
|
||||
("regex_tool", "정규식 — 정규식 테스트, 추출, 치환"),
|
||||
("diff_tool", "텍스트 비교 — 두 파일/텍스트 비교, 통합 diff 출력"),
|
||||
("base64_tool", "인코딩 — Base64/URL 인코딩, 디코딩"),
|
||||
("hash_tool", "해시 계산 — MD5/SHA256 해시 계산 (파일/텍스트)"),
|
||||
("datetime_tool", "날짜/시간 — 날짜 변환, 타임존, 기간 계산"),
|
||||
("json_tool", "JSON 처리 ? JSON 파싱, 변환, 검증, 포맷팅"),
|
||||
("regex_tool", "정규식 ? 정규식 테스트, 추출, 치환"),
|
||||
("diff_tool", "텍스트 비교 ? 두 파일/텍스트 비교, 통합 diff 출력"),
|
||||
("base64_tool", "인코딩 ? Base64/URL 인코딩, 디코딩"),
|
||||
("hash_tool", "해시 계산 ? MD5/SHA256 해시 계산 (파일/텍스트)"),
|
||||
("datetime_tool", "날짜/시간 ? 날짜 변환, 타임존, 기간 계산"),
|
||||
}),
|
||||
("시스템/환경", "\uE770", "#06B6D4", new[]
|
||||
{
|
||||
("clipboard_tool", "클립보드 — Windows 클립보드 읽기/쓰기 (텍스트/이미지)"),
|
||||
("notify_tool", "알림 — Windows 시스템 알림 전송"),
|
||||
("env_tool", "환경변수 — 환경변수 읽기/설정 (프로세스 범위)"),
|
||||
("zip_tool", "압축 — 파일 압축(zip) / 해제"),
|
||||
("http_tool", "HTTP — 로컬/사내 HTTP API 호출 (GET/POST)"),
|
||||
("sql_tool", "SQLite — SQLite DB 쿼리 실행 (로컬 파일)"),
|
||||
("clipboard_tool", "클립보드 ? Windows 클립보드 읽기/쓰기 (텍스트/이미지)"),
|
||||
("notify_tool", "알림 ? Windows 시스템 알림 전송"),
|
||||
("env_tool", "환경변수 ? 환경변수 읽기/설정 (프로세스 범위)"),
|
||||
("zip_tool", "압축 ? 파일 압축(zip) / 해제"),
|
||||
("http_tool", "HTTP ? 로컬/사내 HTTP API 호출 (GET/POST)"),
|
||||
("sql_tool", "SQLite ? SQLite DB 쿼리 실행 (로컬 파일)"),
|
||||
}),
|
||||
("에이전트", "\uE99A", "#F472B6", new[]
|
||||
{
|
||||
("spawn_agent", "서브에이전트 — 하위 작업을 병렬로 실행하는 서브에이전트를 생성합니다"),
|
||||
("wait_agents", "에이전트 대기 — 실행 중인 서브에이전트의 결과를 수집합니다"),
|
||||
("memory", "에이전트 메모리 — 프로젝트 규칙, 선호도를 저장/검색합니다"),
|
||||
("skill_manager", "스킬 관리 — 스킬 목록 조회, 상세 정보, 재로드"),
|
||||
("project_rules", "프로젝트 지침 — AGENTS.md 개발 지침을 읽고 씁니다"),
|
||||
("spawn_agent", "서브에이전트 ? 하위 작업을 병렬로 실행하는 서브에이전트를 생성합니다"),
|
||||
("wait_agents", "에이전트 대기 ? 실행 중인 서브에이전트의 결과를 수집합니다"),
|
||||
("memory", "에이전트 메모리 ? 프로젝트 규칙, 선호도를 저장/검색합니다"),
|
||||
("skill_manager", "스킬 관리 ? 스킬 목록 조회, 상세 정보, 재로드"),
|
||||
("project_rules", "프로젝트 지침 ? AGENTS.md 개발 지침을 읽고 씁니다"),
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -878,7 +942,7 @@ public partial class SettingsWindow : Window
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Child = new TextBlock
|
||||
{
|
||||
Text = "✓ 사용 가능",
|
||||
Text = "? 사용 가능",
|
||||
FontSize = 9,
|
||||
Foreground = new SolidColorBrush(Color.FromRgb(0x34, 0xD3, 0x99)),
|
||||
FontWeight = FontWeights.SemiBold,
|
||||
@@ -891,7 +955,7 @@ public partial class SettingsWindow : Window
|
||||
// 아래 줄: 설명 (뱃지와 구분되도록 위 여백 추가)
|
||||
row.Children.Add(new TextBlock
|
||||
{
|
||||
Text = $"{skill.Label} — {skill.Description}",
|
||||
Text = $"{skill.Label} ? {skill.Description}",
|
||||
FontSize = 11.5,
|
||||
Foreground = TryFindResource("SecondaryText") as Brush
|
||||
?? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#6666AA")),
|
||||
@@ -1061,7 +1125,7 @@ public partial class SettingsWindow : Window
|
||||
ChkDockAutoShow.Unchecked += (_, _) =>
|
||||
{
|
||||
launcher.DockBarAutoShow = false; svc.Save();
|
||||
// 끄기 시에는 설정만 저장 — 독 바를 토글하지 않음 (이미 표시 중이면 유지, 다음 재시작 시 안 뜸)
|
||||
// 끄기 시에는 설정만 저장 ? 독 바를 토글하지 않음 (이미 표시 중이면 유지, 다음 재시작 시 안 뜸)
|
||||
};
|
||||
|
||||
ChkDockRainbowGlow.IsChecked = launcher.DockBarRainbowGlow;
|
||||
@@ -1229,7 +1293,7 @@ public partial class SettingsWindow : Window
|
||||
var hoverBg = TryFindResource("ItemHoverBackground") as Brush ?? new SolidColorBrush(Color.FromArgb(0x22, 0xFF, 0xFF, 0xFF));
|
||||
var shadowColor = TryFindResource("ShadowColor") is Color sc ? sc : Colors.Black;
|
||||
|
||||
// 보관 기간 선택 팝업 — 커스텀 버튼으로 날짜 선택
|
||||
// 보관 기간 선택 팝업 ? 커스텀 버튼으로 날짜 선택
|
||||
var popup = new Window
|
||||
{
|
||||
WindowStyle = WindowStyle.None, AllowsTransparency = true, Background = Brushes.Transparent,
|
||||
@@ -1396,7 +1460,7 @@ public partial class SettingsWindow : Window
|
||||
|
||||
// ─── 핫키 (콤보박스 선택 방식) ──────────────────────────────────────────
|
||||
|
||||
/// <summary>이전 녹화기에서 호출되던 초기화 — 콤보박스 전환 후 무연산 (호환용)</summary>
|
||||
/// <summary>이전 녹화기에서 호출되던 초기화 ? 콤보박스 전환 후 무연산 (호환용)</summary>
|
||||
private void RefreshHotkeyBadges() { /* 콤보박스 SelectedValue 바인딩으로 대체 */ }
|
||||
|
||||
/// <summary>현재 핫키가 콤보박스 목록에 없으면 항목으로 추가합니다.</summary>
|
||||
@@ -1423,7 +1487,7 @@ public partial class SettingsWindow : Window
|
||||
HotkeyCombo.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
/// <summary>Window-level PreviewKeyDown — 핫키 녹화 제거 후 잔여 호출 보호</summary>
|
||||
/// <summary>Window-level PreviewKeyDown ? 핫키 녹화 제거 후 잔여 호출 보호</summary>
|
||||
private void Window_PreviewKeyDown(object sender, KeyEventArgs e) { }
|
||||
|
||||
/// <summary>WPF Key → HotkeyParser가 인식하는 문자열 이름.</summary>
|
||||
@@ -1444,11 +1508,11 @@ public partial class SettingsWindow : Window
|
||||
Key.Up => "Up",
|
||||
Key.Down => "Down",
|
||||
Key.Insert => "Insert",
|
||||
// A–Z
|
||||
// A?Z
|
||||
>= Key.A and <= Key.Z => key.ToString(),
|
||||
// 0–9 (메인 키보드)
|
||||
// 0?9 (메인 키보드)
|
||||
>= Key.D0 and <= Key.D9 => ((int)(key - Key.D0)).ToString(),
|
||||
// F1–F12
|
||||
// F1?F12
|
||||
>= Key.F1 and <= Key.F12 => key.ToString(),
|
||||
// 기호
|
||||
Key.OemTilde => "`",
|
||||
@@ -1549,7 +1613,7 @@ public partial class SettingsWindow : Window
|
||||
|
||||
var count = Services.Agent.SkillService.ImportSkills(dlg.FileName);
|
||||
if (count > 0)
|
||||
CustomMessageBox.Show($"스킬 {count}개를 성공적으로 가져왔습니<EFBFBD><EFBFBD>.\n스킬 목록이 갱신됩니다.", "스킬 가져오기");
|
||||
CustomMessageBox.Show($"스킬 {count}개를 성공적으로 가져왔습니??.\n스킬 목록이 갱신됩니다.", "스킬 가져오기");
|
||||
else
|
||||
CustomMessageBox.Show("스킬 가져오기에 실패했습니다.\nzip 파일에 .skill.md 또는 SKILL.md 파일이 포함되어야 합니다.", "스킬 가져오기");
|
||||
}
|
||||
@@ -1607,7 +1671,7 @@ public partial class SettingsWindow : Window
|
||||
{
|
||||
var cb = new CheckBox
|
||||
{
|
||||
Content = $"/{skill.Name} — {skill.Label}",
|
||||
Content = $"/{skill.Name} ? {skill.Label}",
|
||||
FontSize = 12.5,
|
||||
Foreground = fgBrush,
|
||||
Margin = new Thickness(0, 3, 0, 3),
|
||||
@@ -1710,6 +1774,7 @@ public partial class SettingsWindow : Window
|
||||
Cp4dPassword = Services.CryptoService.EncryptIfEnabled(dlg.Cp4dPassword, IsEncryptionEnabled),
|
||||
});
|
||||
BuildFallbackModelsPanel();
|
||||
BuildServiceModelPanels();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1738,6 +1803,7 @@ public partial class SettingsWindow : Window
|
||||
row.Cp4dUsername = dlg.Cp4dUsername;
|
||||
row.Cp4dPassword = Services.CryptoService.EncryptIfEnabled(dlg.Cp4dPassword, IsEncryptionEnabled);
|
||||
BuildFallbackModelsPanel();
|
||||
BuildServiceModelPanels();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1750,6 +1816,7 @@ public partial class SettingsWindow : Window
|
||||
{
|
||||
_vm.RegisteredModels.Remove(row);
|
||||
BuildFallbackModelsPanel();
|
||||
BuildServiceModelPanels();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1795,21 +1862,30 @@ public partial class SettingsWindow : Window
|
||||
private void AgentSubTab_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AgentPanelCommon == null) return; // 초기화 전 방어
|
||||
AgentPanelCommon.Visibility = AgentTabCommon.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
|
||||
AgentPanelChat.Visibility = AgentTabChat.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
|
||||
AgentPanelCoworkCode.Visibility = AgentTabCoworkCode.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
|
||||
AgentPanelCowork.Visibility = AgentTabCowork.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
|
||||
AgentPanelCode.Visibility = AgentTabCode.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
|
||||
var showCommon = AgentTabCommon.IsChecked == true;
|
||||
var showService = AgentTabChat.IsChecked == true;
|
||||
var showPermission = AgentTabCoworkCode.IsChecked == true;
|
||||
var showAdvanced = AgentTabDev.IsChecked == true;
|
||||
|
||||
AgentPanelCommon.Visibility = showCommon || showService ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (AgentCommonOverviewSection != null)
|
||||
AgentCommonOverviewSection.Visibility = showCommon ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (AgentCommonRuntimeSection != null)
|
||||
AgentCommonRuntimeSection.Visibility = showCommon ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (AgentServiceSection != null)
|
||||
AgentServiceSection.Visibility = showService ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
AgentPanelCoworkCode.Visibility = showPermission ? Visibility.Visible : Visibility.Collapsed;
|
||||
AgentPanelChat.Visibility = Visibility.Collapsed;
|
||||
AgentPanelCowork.Visibility = Visibility.Collapsed;
|
||||
AgentPanelCode.Visibility = Visibility.Collapsed;
|
||||
|
||||
if (AgentPanelDev != null)
|
||||
AgentPanelDev.Visibility = AgentTabDev.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
|
||||
AgentPanelDev.Visibility = showAdvanced ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (AgentPanelEtc != null)
|
||||
AgentPanelEtc.Visibility = AgentTabEtc.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
|
||||
AgentPanelEtc.Visibility = Visibility.Collapsed;
|
||||
if (AgentPanelTools != null)
|
||||
{
|
||||
var show = AgentTabTools.IsChecked == true;
|
||||
AgentPanelTools.Visibility = show ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (show) LoadToolCards();
|
||||
}
|
||||
AgentPanelTools.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
// ─── 도구 관리 카드 UI ──────────────────────────────────────────────
|
||||
@@ -2085,6 +2161,320 @@ public partial class SettingsWindow : Window
|
||||
SvcPanelVllm.Visibility = SvcTabVllm.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
|
||||
SvcPanelGemini.Visibility = SvcTabGemini.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
|
||||
SvcPanelSigmoid.Visibility = SvcTabSigmoid.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
|
||||
BuildServiceModelPanels();
|
||||
SyncAgentSelectionCards();
|
||||
}
|
||||
|
||||
private void AgentServiceCard_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!IsLoaded || sender is not RadioButton rb || rb.IsChecked != true) return;
|
||||
var service = rb.Name switch
|
||||
{
|
||||
"AgentServiceCardVllm" => "vllm",
|
||||
"AgentServiceCardGemini" => "gemini",
|
||||
"AgentServiceCardClaude" => "claude",
|
||||
_ => "ollama",
|
||||
};
|
||||
|
||||
_vm.LlmService = service;
|
||||
if (SvcTabOllama != null) SvcTabOllama.IsChecked = service == "ollama";
|
||||
if (SvcTabVllm != null) SvcTabVllm.IsChecked = service == "vllm";
|
||||
if (SvcTabGemini != null) SvcTabGemini.IsChecked = service == "gemini";
|
||||
if (SvcTabSigmoid != null) SvcTabSigmoid.IsChecked = service == "claude";
|
||||
BuildServiceModelPanels();
|
||||
}
|
||||
|
||||
private void AgentPermissionCard_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!IsLoaded || sender is not RadioButton rb || rb.IsChecked != true) return;
|
||||
_vm.DefaultAgentPermission = rb.Name switch
|
||||
{
|
||||
"AgentPermissionCardPlan" => "Plan",
|
||||
"AgentPermissionCardAuto" => "Auto",
|
||||
"AgentPermissionCardDeny" => "Deny",
|
||||
_ => "Ask",
|
||||
};
|
||||
}
|
||||
|
||||
private void AgentDecisionCard_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!IsLoaded || sender is not RadioButton rb || rb.IsChecked != true) return;
|
||||
_vm.AgentDecisionLevel = rb.Name switch
|
||||
{
|
||||
"AgentDecisionCardMinimal" => "minimal",
|
||||
"AgentDecisionCardDetailed" => "detailed",
|
||||
_ => "normal",
|
||||
};
|
||||
}
|
||||
|
||||
private void AgentPlanModeCard_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!IsLoaded || sender is not RadioButton rb || rb.IsChecked != true) return;
|
||||
_vm.PlanMode = rb.Name switch
|
||||
{
|
||||
"AgentPlanModeCardAlways" => "always",
|
||||
"AgentPlanModeCardAuto" => "auto",
|
||||
_ => "off",
|
||||
};
|
||||
}
|
||||
|
||||
private void AgentOperationModeCard_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!IsLoaded || sender is not RadioButton rb || rb.IsChecked != true)
|
||||
return;
|
||||
|
||||
var next = rb.Name == "AgentOperationModeExternal"
|
||||
? OperationModePolicy.ExternalMode
|
||||
: OperationModePolicy.InternalMode;
|
||||
|
||||
var app = System.Windows.Application.Current as App;
|
||||
var settings = app?.SettingsService?.Settings;
|
||||
if (settings == null)
|
||||
return;
|
||||
|
||||
var current = OperationModePolicy.Normalize(settings.OperationMode);
|
||||
if (string.Equals(next, current, StringComparison.OrdinalIgnoreCase))
|
||||
return;
|
||||
|
||||
var ok = PromptPasswordDialog(
|
||||
"운영 모드 변경 ? 비밀번호 확인",
|
||||
"\U0001f512 사내/사외 모드 변경",
|
||||
"비밀번호를 입력하세요:");
|
||||
if (!ok)
|
||||
{
|
||||
ApplyOperationModeState(settings.OperationMode);
|
||||
return;
|
||||
}
|
||||
|
||||
settings.OperationMode = next;
|
||||
_vm.OperationMode = next;
|
||||
app?.SettingsService?.Save();
|
||||
ApplyOperationModeState(next);
|
||||
}
|
||||
|
||||
private void AgentContextTokensCard_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!IsLoaded || sender is not RadioButton rb || rb.IsChecked != true)
|
||||
return;
|
||||
|
||||
_vm.LlmMaxContextTokens = rb.Name switch
|
||||
{
|
||||
"AgentContextTokens16K" => 16384,
|
||||
"AgentContextTokens64K" => 65536,
|
||||
"AgentContextTokens256K" => 262144,
|
||||
"AgentContextTokens1M" => 1_000_000,
|
||||
_ => 4096,
|
||||
};
|
||||
}
|
||||
|
||||
private void AgentRetentionDaysCard_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!IsLoaded || sender is not RadioButton rb || rb.IsChecked != true)
|
||||
return;
|
||||
|
||||
_vm.LlmRetentionDays = rb.Name switch
|
||||
{
|
||||
"AgentRetentionDays30" => 30,
|
||||
"AgentRetentionDays90" => 90,
|
||||
"AgentRetentionDaysUnlimited" => 0,
|
||||
_ => 7,
|
||||
};
|
||||
}
|
||||
|
||||
private void AgentLogLevelCard_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!IsLoaded || sender is not RadioButton rb || rb.IsChecked != true)
|
||||
return;
|
||||
|
||||
_vm.AgentLogLevel = rb.Name switch
|
||||
{
|
||||
"AgentLogLevelDetailed" => "detailed",
|
||||
"AgentLogLevelDebug" => "debug",
|
||||
_ => "simple",
|
||||
};
|
||||
}
|
||||
|
||||
private void BuildServiceModelPanels()
|
||||
{
|
||||
BuildRegisteredModelList("ollama", OllamaRegisteredModelsList);
|
||||
BuildRegisteredModelList("vllm", VllmRegisteredModelsList);
|
||||
BuildServiceModelChips("ollama", OllamaModelChipPanel, _vm.OllamaModel, value => _vm.OllamaModel = value);
|
||||
BuildServiceModelChips("vllm", VllmModelChipPanel, _vm.VllmModel, value => _vm.VllmModel = value);
|
||||
BuildPresetModelChips(
|
||||
GeminiModelChipPanel,
|
||||
_vm.GeminiModel,
|
||||
value => _vm.GeminiModel = value,
|
||||
new (string Id, string Label, string Hint)[]
|
||||
{
|
||||
("gemini-3.1-pro-preview", "Gemini 3.1 Pro", "최고 성능"),
|
||||
("gemini-3-flash", "Gemini 3 Flash", "빠른 응답"),
|
||||
("gemini-3.1-flash-lite-preview", "Gemini 3.1 Flash Lite", "경량"),
|
||||
("gemini-2.5-flash", "Gemini 2.5 Flash", "균형"),
|
||||
("gemini-2.5-flash-lite", "Gemini 2.5 Flash Lite", "저비용"),
|
||||
});
|
||||
BuildPresetModelChips(
|
||||
ClaudeModelChipPanel,
|
||||
_vm.ClaudeModel,
|
||||
value => _vm.ClaudeModel = value,
|
||||
new (string Id, string Label, string Hint)[]
|
||||
{
|
||||
(ProviderModelIds.SigmoidOpus46, "Claude Opus 4.6", "최고 성능"),
|
||||
(ProviderModelIds.SigmoidSonnet46, "Claude Sonnet 4.6", "균형"),
|
||||
(ProviderModelIds.SigmoidHaiku45_20251001, "Claude Haiku 4.5", "경량"),
|
||||
(ProviderModelIds.SigmoidSonnet45_20250929, "Claude Sonnet 4.5", "이전 안정판"),
|
||||
(ProviderModelIds.SigmoidOpus4_20250514, "Claude Opus 4", "이전 최고급"),
|
||||
});
|
||||
}
|
||||
|
||||
private void BuildRegisteredModelList(string service, ItemsControl? target)
|
||||
{
|
||||
if (target == null) return;
|
||||
var view = new ListCollectionView(_vm.RegisteredModels);
|
||||
view.Filter = item =>
|
||||
{
|
||||
if (item is not RegisteredModelRow row) return false;
|
||||
return string.Equals(row.Service, service, StringComparison.OrdinalIgnoreCase);
|
||||
};
|
||||
target.ItemsSource = view;
|
||||
}
|
||||
|
||||
private void BuildServiceModelChips(string service, WrapPanel? panel, string selectedValue, Action<string> applySelection)
|
||||
{
|
||||
if (panel == null)
|
||||
return;
|
||||
|
||||
panel.Children.Clear();
|
||||
var models = _vm.RegisteredModels
|
||||
.Where(row => string.Equals(row.Service, service, StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
|
||||
if (models.Count == 0)
|
||||
{
|
||||
panel.Children.Add(new Border
|
||||
{
|
||||
Background = TryFindResource("ItemBackground") as Brush ?? Brushes.WhiteSmoke,
|
||||
BorderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.LightGray,
|
||||
BorderThickness = new Thickness(1),
|
||||
CornerRadius = new CornerRadius(10),
|
||||
Padding = new Thickness(12, 8, 12, 8),
|
||||
Child = new TextBlock
|
||||
{
|
||||
Text = "등록된 모델이 없습니다. + 모델 추가로 먼저 등록하세요.",
|
||||
FontSize = 11.5,
|
||||
Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray,
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var model in models)
|
||||
{
|
||||
var isSelected = string.Equals(model.EncryptedModelName, selectedValue, StringComparison.OrdinalIgnoreCase);
|
||||
var chip = new Border
|
||||
{
|
||||
Cursor = Cursors.Hand,
|
||||
CornerRadius = new CornerRadius(10),
|
||||
BorderThickness = new Thickness(1),
|
||||
BorderBrush = isSelected
|
||||
? (TryFindResource("AccentColor") as Brush ?? Brushes.DodgerBlue)
|
||||
: (TryFindResource("BorderColor") as Brush ?? Brushes.LightGray),
|
||||
Background = isSelected
|
||||
? (TryFindResource("ItemHoverBackground") as Brush ?? Brushes.AliceBlue)
|
||||
: Brushes.Transparent,
|
||||
Padding = new Thickness(12, 9, 12, 9),
|
||||
Margin = new Thickness(0, 0, 8, 8),
|
||||
Child = new StackPanel
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new TextBlock
|
||||
{
|
||||
Text = string.IsNullOrWhiteSpace(model.Alias) ? "(이름 없음)" : model.Alias,
|
||||
FontSize = 12,
|
||||
FontWeight = isSelected ? FontWeights.SemiBold : FontWeights.Normal,
|
||||
Foreground = isSelected
|
||||
? (TryFindResource("AccentColor") as Brush ?? Brushes.DodgerBlue)
|
||||
: (TryFindResource("PrimaryText") as Brush ?? Brushes.Black),
|
||||
},
|
||||
new TextBlock
|
||||
{
|
||||
Text = string.IsNullOrWhiteSpace(model.Endpoint) ? "기본 서버 사용" : model.Endpoint,
|
||||
FontSize = 10.5,
|
||||
Margin = new Thickness(0, 3, 0, 0),
|
||||
Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var captured = model.EncryptedModelName;
|
||||
chip.MouseLeftButtonUp += (_, _) =>
|
||||
{
|
||||
applySelection(captured);
|
||||
BuildServiceModelPanels();
|
||||
};
|
||||
panel.Children.Add(chip);
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildPresetModelChips(
|
||||
WrapPanel? panel,
|
||||
string? selectedValue,
|
||||
Action<string> applySelection,
|
||||
IEnumerable<(string Id, string Label, string Hint)> models)
|
||||
{
|
||||
if (panel == null)
|
||||
return;
|
||||
|
||||
panel.Children.Clear();
|
||||
foreach (var model in models)
|
||||
{
|
||||
var isSelected = string.Equals(model.Id, selectedValue, StringComparison.OrdinalIgnoreCase);
|
||||
var chip = new Border
|
||||
{
|
||||
Cursor = Cursors.Hand,
|
||||
CornerRadius = new CornerRadius(12),
|
||||
BorderThickness = new Thickness(1),
|
||||
BorderBrush = isSelected
|
||||
? (TryFindResource("AccentColor") as Brush ?? Brushes.DodgerBlue)
|
||||
: (TryFindResource("BorderColor") as Brush ?? Brushes.LightGray),
|
||||
Background = isSelected
|
||||
? (TryFindResource("ItemHoverBackground") as Brush ?? Brushes.AliceBlue)
|
||||
: Brushes.Transparent,
|
||||
Padding = new Thickness(12, 9, 12, 9),
|
||||
Margin = new Thickness(0, 0, 8, 8),
|
||||
Child = new StackPanel
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new TextBlock
|
||||
{
|
||||
Text = model.Label,
|
||||
FontSize = 12,
|
||||
FontWeight = isSelected ? FontWeights.SemiBold : FontWeights.Normal,
|
||||
Foreground = isSelected
|
||||
? (TryFindResource("AccentColor") as Brush ?? Brushes.DodgerBlue)
|
||||
: (TryFindResource("PrimaryText") as Brush ?? Brushes.Black),
|
||||
},
|
||||
new TextBlock
|
||||
{
|
||||
Text = model.Hint,
|
||||
FontSize = 10.5,
|
||||
Margin = new Thickness(0, 3, 0, 0),
|
||||
Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var capturedId = model.Id;
|
||||
chip.MouseLeftButtonUp += (_, _) =>
|
||||
{
|
||||
applySelection(capturedId);
|
||||
BuildServiceModelPanels();
|
||||
};
|
||||
panel.Children.Add(chip);
|
||||
}
|
||||
}
|
||||
|
||||
private void ThemeCard_Click(object sender, RoutedEventArgs e)
|
||||
@@ -2115,7 +2505,7 @@ public partial class SettingsWindow : Window
|
||||
// 비밀번호 확인 다이얼로그
|
||||
var dlg = new Window
|
||||
{
|
||||
Title = "개발자 모드 — 비밀번호 확인",
|
||||
Title = "개발자 모드 ? 비밀번호 확인",
|
||||
Width = 340, SizeToContent = SizeToContent.Height,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
Owner = this, ResizeMode = ResizeMode.NoResize,
|
||||
@@ -2169,7 +2559,7 @@ public partial class SettingsWindow : Window
|
||||
|
||||
if (dlg.ShowDialog() != true)
|
||||
{
|
||||
// 비밀번호 실패/취소 — 체크 해제 + DevMode 강제 false
|
||||
// 비밀번호 실패/취소 ? 체크 해제 + DevMode 강제 false
|
||||
_vm.DevMode = false;
|
||||
cb.IsChecked = false;
|
||||
}
|
||||
@@ -2198,24 +2588,66 @@ public partial class SettingsWindow : Window
|
||||
{
|
||||
AiEnabledToggle.IsChecked = enabled;
|
||||
}
|
||||
// AX Agent 상세 설정은 전용 창으로 분리 (탭은 숨김)
|
||||
if (AgentAiEnabledToggle != null && AgentAiEnabledToggle.IsChecked != enabled)
|
||||
{
|
||||
AgentAiEnabledToggle.IsChecked = enabled;
|
||||
}
|
||||
if (AgentTabItem != null)
|
||||
AgentTabItem.Visibility = Visibility.Collapsed;
|
||||
AgentTabItem.Visibility = enabled ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
ApplyMainTabVisibility(NormalizeDisplayMode((System.Windows.Application.Current as App)?.SettingsService?.Settings?.Llm?.AgentUiExpressionLevel));
|
||||
}
|
||||
|
||||
public void SelectAgentSettingsTab()
|
||||
{
|
||||
if (AgentTabItem == null || MainSettingsTab == null || AgentTabItem.Visibility != Visibility.Visible)
|
||||
return;
|
||||
|
||||
MainSettingsTab.SelectedItem = AgentTabItem;
|
||||
if (AgentTabCommon != null)
|
||||
AgentTabCommon.IsChecked = true;
|
||||
AgentSubTab_Checked(this, new RoutedEventArgs());
|
||||
Activate();
|
||||
}
|
||||
|
||||
private void BtnAgentSettingsBack_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (MainSettingsTab == null)
|
||||
return;
|
||||
|
||||
var fallback = MainSettingsTab.Items
|
||||
.OfType<TabItem>()
|
||||
.FirstOrDefault(t => t != AgentTabItem && t.Visibility == Visibility.Visible);
|
||||
|
||||
if (fallback != null)
|
||||
MainSettingsTab.SelectedItem = fallback;
|
||||
}
|
||||
|
||||
private void BtnAgentShortcut_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SelectAgentSettingsTab();
|
||||
}
|
||||
|
||||
private void ApplyOperationModeState(string? mode)
|
||||
{
|
||||
if (OperationModeCombo == null)
|
||||
var normalized = OperationModePolicy.Normalize(mode);
|
||||
SyncOperationModeCombo(OperationModeCombo, normalized);
|
||||
if (AgentOperationModeInternal != null) AgentOperationModeInternal.IsChecked = normalized == OperationModePolicy.InternalMode;
|
||||
if (AgentOperationModeExternal != null) AgentOperationModeExternal.IsChecked = normalized == OperationModePolicy.ExternalMode;
|
||||
}
|
||||
|
||||
private static void SyncOperationModeCombo(ComboBox? combo, string normalized)
|
||||
{
|
||||
if (combo == null)
|
||||
return;
|
||||
|
||||
var normalized = OperationModePolicy.Normalize(mode);
|
||||
foreach (var item in OperationModeCombo.Items.OfType<ComboBoxItem>())
|
||||
foreach (var item in combo.Items.OfType<ComboBoxItem>())
|
||||
{
|
||||
var key = item.Tag as string;
|
||||
if (string.Equals(key, normalized, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!ReferenceEquals(OperationModeCombo.SelectedItem, item))
|
||||
OperationModeCombo.SelectedItem = item;
|
||||
if (!ReferenceEquals(combo.SelectedItem, item))
|
||||
combo.SelectedItem = item;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -2307,7 +2739,8 @@ public partial class SettingsWindow : Window
|
||||
private void AiEnabled_Changed(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!IsLoaded) return;
|
||||
var tryEnable = AiEnabledToggle?.IsChecked == true;
|
||||
var sourceToggle = sender as CheckBox;
|
||||
var tryEnable = sourceToggle?.IsChecked == true;
|
||||
|
||||
// 비활성화는 즉시 적용 (비밀번호 불필요)
|
||||
if (!tryEnable)
|
||||
@@ -2335,7 +2768,7 @@ public partial class SettingsWindow : Window
|
||||
|
||||
var dlg = new Window
|
||||
{
|
||||
Title = "AI 기능 활성화 — 비밀번호 확인",
|
||||
Title = "AI 기능 활성화 ? 비밀번호 확인",
|
||||
Width = 340, SizeToContent = SizeToContent.Height,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
Owner = this, ResizeMode = ResizeMode.NoResize,
|
||||
@@ -2399,8 +2832,9 @@ public partial class SettingsWindow : Window
|
||||
}
|
||||
else
|
||||
{
|
||||
// 취소/실패 — 토글 원상복구
|
||||
// 취소/실패 ? 토글 원상복구
|
||||
if (AiEnabledToggle != null) AiEnabledToggle.IsChecked = false;
|
||||
if (AgentAiEnabledToggle != null) AgentAiEnabledToggle.IsChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2421,7 +2855,7 @@ public partial class SettingsWindow : Window
|
||||
return;
|
||||
|
||||
var ok = PromptPasswordDialog(
|
||||
"운영 모드 변경 — 비밀번호 확인",
|
||||
"운영 모드 변경 ? 비밀번호 확인",
|
||||
"\U0001f512 사내/사외 모드 변경",
|
||||
"비밀번호를 입력하세요:");
|
||||
if (!ok)
|
||||
@@ -2449,7 +2883,7 @@ public partial class SettingsWindow : Window
|
||||
|
||||
var dlg = new Window
|
||||
{
|
||||
Title = "스텝 바이 스텝 승인 — 비밀번호 확인",
|
||||
Title = "스텝 바이 스텝 승인 ? 비밀번호 확인",
|
||||
Width = 340, SizeToContent = SizeToContent.Height,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
Owner = this, ResizeMode = ResizeMode.NoResize,
|
||||
@@ -2983,7 +3417,7 @@ public partial class SettingsWindow : Window
|
||||
if (section.Models != null && !string.IsNullOrEmpty(modelName) && !section.Models.Contains(modelName))
|
||||
section.Models.Add(modelName);
|
||||
}
|
||||
// 2) AppSettings의 RegisteredModels (기존 저장된 것 — ViewModel에 없는 경우 보완)
|
||||
// 2) AppSettings의 RegisteredModels (기존 저장된 것 ? ViewModel에 없는 경우 보완)
|
||||
foreach (var m in llm.RegisteredModels)
|
||||
{
|
||||
var svc = (m.Service ?? "").ToLowerInvariant();
|
||||
@@ -3013,7 +3447,7 @@ public partial class SettingsWindow : Window
|
||||
})
|
||||
if (!sections[3].Models.Contains(cm)) sections[3].Models.Add(cm);
|
||||
|
||||
// 렌더링 — 모델이 없는 섹션도 헤더는 표시
|
||||
// 렌더링 ? 모델이 없는 섹션도 헤더는 표시
|
||||
foreach (var (service, svcLabel, svcColor, models) in sections)
|
||||
{
|
||||
FallbackModelsPanel.Children.Add(new TextBlock
|
||||
@@ -3331,7 +3765,7 @@ public partial class SettingsWindow : Window
|
||||
|
||||
var result = CustomMessageBox.Show(
|
||||
"설정을 불러오면 현재 설정이 덮어씌워집니다.\n계속하시겠습니까?",
|
||||
"AX Copilot — 설정 불러오기",
|
||||
"AX Copilot ? 설정 불러오기",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Question);
|
||||
|
||||
@@ -3344,7 +3778,7 @@ public partial class SettingsWindow : Window
|
||||
var json = CryptoService.PortableDecrypt(fileContent);
|
||||
if (string.IsNullOrEmpty(json))
|
||||
{
|
||||
// 평문 JSON일 수 있음 — 직접 파싱 시도
|
||||
// 평문 JSON일 수 있음 ? 직접 파싱 시도
|
||||
try
|
||||
{
|
||||
var test = System.Text.Json.JsonSerializer.Deserialize<Models.AppSettings>(fileContent);
|
||||
@@ -3395,3 +3829,6 @@ public partial class SettingsWindow : Window
|
||||
base.OnClosed(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user