[Phase47] 대형 파일 분할 리팩터링 3차 — 8개 신규 파셜 파일 생성
## 분할 대상 및 결과 ### ChatWindow.ResponseHandling.cs (741줄 → 269줄) - ChatWindow.StreamingUI.cs (303줄, 신규): CreateStreamingContainer, FinalizeStreamingContainer, ParseSuggestionChips, FormatTokenCount, EstimateTokenCount, StopGeneration - ChatWindow.ConversationExport.cs (188줄, 신규): ForkConversation, OpenCommandPalette, ExecuteCommand, ExportConversation, ExportToHtml ### ChatWindow.PreviewAndFiles.cs (709줄 → ~340줄) - ChatWindow.PreviewPopup.cs (~230줄, 신규): ShowPreviewTabContextMenu, OpenPreviewPopupWindow, _previewTabPopup 필드 ### HelpDetailWindow.xaml.cs (673줄 → 254줄) - HelpDetailWindow.Shortcuts.cs (168줄, 신규): BuildShortcutItems() 정적 메서드 (단축키 항목 160개+ 생성) - HelpDetailWindow.Navigation.cs (266줄, 신규): 테마 프로퍼티, BuildTopMenu/SwitchTopMenu, BuildCategoryBar, NavigateToPage, 이벤트 핸들러 - partial class 전환: `public partial class HelpDetailWindow : Window` ### SkillService.cs (661줄 → 386줄) - SkillService.Import.cs (203줄, 신규): ExportSkill, ImportSkills, MapToolNames — 가져오기/내보내기 섹션 - SkillDefinition.cs (81줄, 신규): SkillDefinition 클래스 독립 파일로 분리 (별도 최상위 클래스) - partial class 전환: `public static partial class SkillService` ## NEXT_ROADMAP.md Phase 46 완료 항목 추가 ## 빌드 결과: 경고 0, 오류 0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -459,251 +459,4 @@ public partial class ChatWindow
|
||||
System.Diagnostics.Debug.WriteLine($"외부 프로그램 실행 오류: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>프리뷰 탭 우클릭 컨텍스트 메뉴를 표시합니다.</summary>
|
||||
private Popup? _previewTabPopup;
|
||||
|
||||
private void ShowPreviewTabContextMenu(string filePath)
|
||||
{
|
||||
// 기존 팝업 닫기
|
||||
if (_previewTabPopup != null) _previewTabPopup.IsOpen = false;
|
||||
|
||||
var bg = ThemeResourceHelper.Background(this);
|
||||
var borderBrush = ThemeResourceHelper.Border(this);
|
||||
var primaryText = ThemeResourceHelper.Primary(this);
|
||||
var secondaryText = ThemeResourceHelper.Secondary(this);
|
||||
var hoverBg = ThemeResourceHelper.HoverBg(this);
|
||||
|
||||
var stack = new StackPanel();
|
||||
|
||||
void AddItem(string icon, string iconColor, string label, Action action)
|
||||
{
|
||||
var itemBorder = new Border
|
||||
{
|
||||
Background = Brushes.Transparent,
|
||||
CornerRadius = new CornerRadius(6),
|
||||
Padding = new Thickness(10, 7, 16, 7),
|
||||
Cursor = Cursors.Hand,
|
||||
};
|
||||
var sp = new StackPanel { Orientation = Orientation.Horizontal };
|
||||
sp.Children.Add(new TextBlock
|
||||
{
|
||||
Text = icon, FontFamily = ThemeResourceHelper.SegoeMdl2,
|
||||
FontSize = 12, Foreground = string.IsNullOrEmpty(iconColor)
|
||||
? secondaryText
|
||||
: ThemeResourceHelper.HexBrush(iconColor),
|
||||
VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0, 0, 8, 0),
|
||||
});
|
||||
sp.Children.Add(new TextBlock
|
||||
{
|
||||
Text = label, FontSize = 13, Foreground = primaryText,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
});
|
||||
itemBorder.Child = sp;
|
||||
itemBorder.MouseEnter += (s, _) => { if (s is Border b) b.Background = hoverBg; };
|
||||
itemBorder.MouseLeave += (s, _) => { if (s is Border b) b.Background = Brushes.Transparent; };
|
||||
itemBorder.MouseLeftButtonUp += (_, _) =>
|
||||
{
|
||||
_previewTabPopup!.IsOpen = false;
|
||||
action();
|
||||
};
|
||||
stack.Children.Add(itemBorder);
|
||||
}
|
||||
|
||||
void AddSeparator()
|
||||
{
|
||||
stack.Children.Add(new Border
|
||||
{
|
||||
Height = 1,
|
||||
Background = borderBrush,
|
||||
Margin = new Thickness(8, 3, 8, 3),
|
||||
});
|
||||
}
|
||||
|
||||
AddItem("\uE8A7", "#64B5F6", "외부 프로그램으로 열기", () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = filePath, UseShellExecute = true,
|
||||
});
|
||||
}
|
||||
catch (Exception) { /* 비핵심 작업 실패 — UI 차단 방지 */ }
|
||||
});
|
||||
|
||||
AddItem("\uE838", "#FFB74D", "파일 위치 열기", () =>
|
||||
{
|
||||
try { System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{filePath}\""); }
|
||||
catch (Exception) { /* 비핵심 작업 실패 — UI 차단 방지 */ }
|
||||
});
|
||||
|
||||
AddItem("\uE8A7", "#81C784", "별도 창에서 보기", () => OpenPreviewPopupWindow(filePath));
|
||||
|
||||
AddSeparator();
|
||||
|
||||
AddItem("\uE8C8", "", "경로 복사", () =>
|
||||
{
|
||||
try { Clipboard.SetText(filePath); } catch (Exception) { /* 클립보드 접근 실패 */ }
|
||||
});
|
||||
|
||||
AddSeparator();
|
||||
|
||||
AddItem("\uE711", "#EF5350", "이 탭 닫기", () => ClosePreviewTab(filePath));
|
||||
|
||||
if (_previewTabs.Count > 1)
|
||||
{
|
||||
AddItem("\uE8BB", "#EF5350", "다른 탭 모두 닫기", () =>
|
||||
{
|
||||
var keep = filePath;
|
||||
_previewTabs.RemoveAll(p => !string.Equals(p, keep, StringComparison.OrdinalIgnoreCase));
|
||||
_activePreviewTab = keep;
|
||||
RebuildPreviewTabs();
|
||||
LoadPreviewContent(keep);
|
||||
});
|
||||
}
|
||||
|
||||
var popupBorder = new Border
|
||||
{
|
||||
Background = bg,
|
||||
BorderBrush = borderBrush,
|
||||
BorderThickness = new Thickness(1),
|
||||
CornerRadius = new CornerRadius(12),
|
||||
Padding = new Thickness(4, 6, 4, 6),
|
||||
MinWidth = 180,
|
||||
Effect = new System.Windows.Media.Effects.DropShadowEffect
|
||||
{
|
||||
BlurRadius = 16, Opacity = 0.4, ShadowDepth = 4,
|
||||
Color = Colors.Black,
|
||||
},
|
||||
Child = stack,
|
||||
};
|
||||
|
||||
_previewTabPopup = new Popup
|
||||
{
|
||||
Child = popupBorder,
|
||||
Placement = PlacementMode.MousePoint,
|
||||
StaysOpen = false,
|
||||
AllowsTransparency = true,
|
||||
PopupAnimation = PopupAnimation.Fade,
|
||||
};
|
||||
_previewTabPopup.IsOpen = true;
|
||||
}
|
||||
|
||||
/// <summary>프리뷰를 별도 팝업 창에서 엽니다.</summary>
|
||||
private void OpenPreviewPopupWindow(string filePath)
|
||||
{
|
||||
if (!System.IO.File.Exists(filePath)) return;
|
||||
|
||||
var ext = System.IO.Path.GetExtension(filePath).ToLowerInvariant();
|
||||
var fileName = System.IO.Path.GetFileName(filePath);
|
||||
var bg = ThemeResourceHelper.Background(this);
|
||||
var fg = ThemeResourceHelper.Primary(this);
|
||||
|
||||
var win = new Window
|
||||
{
|
||||
Title = $"미리보기 — {fileName}",
|
||||
Width = 900,
|
||||
Height = 700,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterScreen,
|
||||
Background = bg,
|
||||
};
|
||||
|
||||
FrameworkElement content;
|
||||
|
||||
switch (ext)
|
||||
{
|
||||
case ".html":
|
||||
case ".htm":
|
||||
var wv = new Microsoft.Web.WebView2.Wpf.WebView2();
|
||||
wv.Loaded += async (_, _) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var env = await Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(
|
||||
userDataFolder: WebView2DataFolder);
|
||||
await wv.EnsureCoreWebView2Async(env);
|
||||
wv.Source = new Uri(filePath);
|
||||
}
|
||||
catch (Exception) { /* 비핵심 작업 실패 — UI 차단 방지 */ }
|
||||
};
|
||||
content = wv;
|
||||
break;
|
||||
|
||||
case ".md":
|
||||
var mdWv = new Microsoft.Web.WebView2.Wpf.WebView2();
|
||||
var mdMood = _selectedMood;
|
||||
mdWv.Loaded += async (_, _) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var env = await Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(
|
||||
userDataFolder: WebView2DataFolder);
|
||||
await mdWv.EnsureCoreWebView2Async(env);
|
||||
var mdSrc = System.IO.File.ReadAllText(filePath);
|
||||
if (mdSrc.Length > 100000) mdSrc = mdSrc[..100000];
|
||||
var html = Services.Agent.TemplateService.RenderMarkdownToHtml(mdSrc, mdMood);
|
||||
mdWv.NavigateToString(html);
|
||||
}
|
||||
catch (Exception) { /* 비핵심 작업 실패 — UI 차단 방지 */ }
|
||||
};
|
||||
content = mdWv;
|
||||
break;
|
||||
|
||||
case ".csv":
|
||||
var dg = new System.Windows.Controls.DataGrid
|
||||
{
|
||||
AutoGenerateColumns = true,
|
||||
IsReadOnly = true,
|
||||
Background = Brushes.Transparent,
|
||||
Foreground = Brushes.White,
|
||||
BorderThickness = new Thickness(0),
|
||||
FontSize = 12,
|
||||
};
|
||||
try
|
||||
{
|
||||
var lines = System.IO.File.ReadAllLines(filePath);
|
||||
if (lines.Length > 0)
|
||||
{
|
||||
var dt = new System.Data.DataTable();
|
||||
var headers = ParseCsvLine(lines[0]);
|
||||
foreach (var h in headers) dt.Columns.Add(h);
|
||||
for (int i = 1; i < Math.Min(lines.Length, 1001); i++)
|
||||
{
|
||||
var vals = ParseCsvLine(lines[i]);
|
||||
var row = dt.NewRow();
|
||||
for (int j = 0; j < Math.Min(vals.Length, dt.Columns.Count); j++)
|
||||
row[j] = vals[j];
|
||||
dt.Rows.Add(row);
|
||||
}
|
||||
dg.ItemsSource = dt.DefaultView;
|
||||
}
|
||||
}
|
||||
catch (Exception) { /* 비핵심 작업 실패 — UI 차단 방지 */ }
|
||||
content = dg;
|
||||
break;
|
||||
|
||||
default:
|
||||
var text = System.IO.File.ReadAllText(filePath);
|
||||
if (text.Length > 100000) text = text[..100000] + "\n\n... (이후 생략)";
|
||||
var sv = new ScrollViewer
|
||||
{
|
||||
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
|
||||
Padding = new Thickness(20),
|
||||
Content = new TextBlock
|
||||
{
|
||||
Text = text,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
FontFamily = ThemeResourceHelper.Consolas,
|
||||
FontSize = 13,
|
||||
Foreground = fg,
|
||||
},
|
||||
};
|
||||
content = sv;
|
||||
break;
|
||||
}
|
||||
|
||||
win.Content = content;
|
||||
win.Show();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user