diff --git a/README.md b/README.md index 7a014a8..27be34b 100644 --- a/README.md +++ b/README.md @@ -1248,3 +1248,7 @@ MIT License - IBM 연동형 vLLM 인증 경로를 점검한 결과, 기존 AX Agent는 등록 모델 인증 방식으로 `Bearer`와 `CP4D`만 지원하고 `IBM IAM` 토큰 교환은 지원하지 않았다. 이 때문에 IBM Cloud 계열 watsonx/vLLM 게이트웨이에 API 키를 직접 Bearer로 보내면 `인증 실패 - API 키가 유효하지 않습니다.` 오류가 발생할 수 있었다. - [IbmIamTokenService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/IbmIamTokenService.cs)를 추가하고 [LlmService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/LlmService.cs)에 `ibm_iam` 인증 타입을 연결해, 등록 모델의 API 키를 IBM IAM access token으로 교환한 뒤 Bearer 헤더에 넣도록 보강했다. - [ModelRegistrationDialog.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ModelRegistrationDialog.cs), [SettingsViewModel.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/ViewModels/SettingsViewModel.cs), [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs), [AppSettings.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Models/AppSettings.cs)도 함께 갱신해 등록 모델 인증 방식에 `IBM IAM (토큰 교환)`이 보이고 저장/표시되도록 맞췄다. +- 업데이트: 2026-04-06 15:26 (KST) + - AX Agent 창을 PC에서 드래그로 이동할 때 버벅이던 문제를 줄이기 위해 [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs)에 `WM_ENTERSIZEMOVE` / `WM_EXITSIZEMOVE` 기반 이동·리사이즈 감지를 추가했다. + - 이동/리사이즈 루프 중에는 창 루트를 `BitmapCache`로 묶고, `SizeChanged`에 연결돼 있던 `UpdateTopicPresetScrollMode()`, `UpdateResponsiveChatLayout()`, `RenderMessages()` 재계산은 잠시 지연시킨 뒤 루프 종료 시 한 번만 반영하도록 바꿨다. + - 이 변경으로 창을 끌 때마다 무거운 transcript/레이아웃이 반복 갱신되던 경로를 줄여, AX Agent 창 이동 체감 속도를 개선했다. diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 23b00ed..277d995 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -4960,3 +4960,5 @@ ow + toggle ?쒓컖 ?몄뼱濡??ㅼ떆 ?뺣젹?덈떎. - Document update: 2026-04-06 13:36 (KST) - Made file-backed transcript actions state-aware in `ChatWindow.AgentEventRendering.cs`. The inline preview button now changes label by context, such as `변경 확인`, `작성 내용 보기`, `부분 결과 보기`, `오류 파일 보기`, or `승인 전 미리보기`, instead of showing the same generic action for every case. - Document update: 2026-04-06 14:06 (KST) - Diagnosed IBM-connected vLLM authentication failures and confirmed AX only supported `bearer` and `cp4d` registered-model auth modes. Added `IbmIamTokenService.cs` and wired a new `ibm_iam` auth mode into `LlmService.cs` so IBM Cloud API keys are exchanged for IAM access tokens before being sent as Bearer credentials. - Document update: 2026-04-06 14:06 (KST) - Updated the registered-model schema and UI surfaces to expose the new auth mode. `AppSettings.cs`, `SettingsViewModel.cs`, `ModelRegistrationDialog.cs`, and the AX Agent overlay model list in `ChatWindow.xaml.cs` now save/display `IBM IAM` alongside existing `Bearer` and `CP4D` modes. +- Document update: 2026-04-06 15:26 (KST) - Improved AX Agent window drag performance by handling `WM_ENTERSIZEMOVE` / `WM_EXITSIZEMOVE` in `ChatWindow.xaml.cs`. The window now enters a lightweight move-size mode while being dragged or resized. +- Document update: 2026-04-06 15:26 (KST) - During move-size loops the root visual is temporarily cached with `BitmapCache`, and the expensive `UpdateTopicPresetScrollMode()`, `UpdateResponsiveChatLayout()`, and `RenderMessages()` refresh path is deferred until the move/resize operation ends. This reduces the “heavy” feeling when moving the AX Agent window on desktop PCs. diff --git a/src/AxCopilot/Views/ChatWindow.xaml.cs b/src/AxCopilot/Views/ChatWindow.xaml.cs index f51fcff..2d9e4d5 100644 --- a/src/AxCopilot/Views/ChatWindow.xaml.cs +++ b/src/AxCopilot/Views/ChatWindow.xaml.cs @@ -35,6 +35,9 @@ public partial class ChatWindow : Window private bool _isStreaming; private bool _sidebarVisible = true; private double _sidebarExpandedWidth = 262; + private bool _isInWindowMoveSizeLoop; + private bool _pendingResponsiveLayoutRefresh; + private CacheMode? _cachedRootCacheModeBeforeMove; private string _selectedCategory = ""; // "" = 전체 private readonly Dictionary _tabSelectedCategory = new(StringComparer.OrdinalIgnoreCase) { @@ -274,6 +277,9 @@ public partial class ChatWindow : Window KeyDown += ChatWindow_KeyDown; MouseMove += (_, _) => { + if (_isInWindowMoveSizeLoop) + return; + if (TokenUsagePopup?.IsOpen == true) CloseTokenUsagePopupIfIdle(); }; @@ -389,6 +395,12 @@ public partial class ChatWindow : Window }; SizeChanged += (_, _) => { + if (_isInWindowMoveSizeLoop) + { + _pendingResponsiveLayoutRefresh = true; + return; + } + UpdateTopicPresetScrollMode(); if (UpdateResponsiveChatLayout()) RenderMessages(preserveViewport: true); @@ -1067,6 +1079,15 @@ public partial class ChatWindow : Window private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { + if (msg == 0x0231) // WM_ENTERSIZEMOVE + { + BeginWindowMoveSizeLoop(); + } + else if (msg == 0x0232) // WM_EXITSIZEMOVE + { + EndWindowMoveSizeLoop(); + } + // WM_GETMINMAXINFO — 최대화 시 작업 표시줄 영역 확보 if (msg == 0x0024) { @@ -1087,6 +1108,42 @@ public partial class ChatWindow : Window return IntPtr.Zero; } + private void BeginWindowMoveSizeLoop() + { + if (_isInWindowMoveSizeLoop) + return; + + _isInWindowMoveSizeLoop = true; + _pendingResponsiveLayoutRefresh = false; + + if (Content is UIElement rootElement) + { + _cachedRootCacheModeBeforeMove = rootElement.CacheMode; + rootElement.CacheMode = new BitmapCache(); + } + } + + private void EndWindowMoveSizeLoop() + { + if (!_isInWindowMoveSizeLoop) + return; + + _isInWindowMoveSizeLoop = false; + + if (Content is UIElement rootElement) + rootElement.CacheMode = _cachedRootCacheModeBeforeMove; + + _cachedRootCacheModeBeforeMove = null; + + if (_pendingResponsiveLayoutRefresh) + { + _pendingResponsiveLayoutRefresh = false; + UpdateTopicPresetScrollMode(); + if (UpdateResponsiveChatLayout()) + RenderMessages(preserveViewport: true); + } + } + private void BtnMinimize_Click(object sender, RoutedEventArgs e) => WindowState = WindowState.Minimized; private void BtnMaximize_Click(object sender, RoutedEventArgs e) {