Families.AutoWriter 1.0.0.0
Families 콘텐츠를 반복 작업 없이 작성·등록하도록 돕는 자동화 작성 도구입니다.
로딩중...
검색중...
일치하는것 없음
MainWindow.xaml.cs
이 파일의 문서화 페이지로 가기
1using System.Windows;
2using System.Windows.Controls;
3using System.Windows.Controls.Primitives;
4using System.Windows.Input;
5using System.Windows.Media;
7using Microsoft.Web.WebView2.Wpf;
8
9namespace FamiliesAutoWriter;
10
19public partial class MainWindow : Window
20{
21 // ── 탭 상태 ─────────────────────────────────────────────────
30 private sealed class BrowserTab
31 {
40 public required string Id { get; init; }
49 public required string Label { get; set; }
58 public required WebView2 WebView { get; init; }
67 public required WriterSession Session { get; init; }
76 public ToggleButton? HeaderBtn { get; set; }
85 public bool IsReady { get; set; }
94 public string CurrentUrl { get; set; } = "https://claude.ai/new";
95 }
96
105 private readonly List<BrowserTab> _tabs = [];
123 private int _tabSeq = 0;
124
133 public MainWindow()
134 {
135 InitializeComponent();
136 Loaded += OnLoaded;
137 Closed += OnClosed;
138 }
139
140 // ── 초기화 ───────────────────────────────────────────────────
165 private void OnLoaded(object sender, RoutedEventArgs e)
166 {
167 NewTabNameBox.Focus();
168 }
169
194 private void OnClosed(object? sender, EventArgs e)
195 {
196 foreach (var tab in _tabs)
197 tab.Session.StopAutomation();
198 }
199
200 // ── 탭 생성 ─────────────────────────────────────────────────
249 private async Task<BrowserTab> CreateTabAsync(string label, string url, string? initialPrompt = null, SessionMode mode = SessionMode.Travel)
250 {
251 var vm = (MainViewModel)DataContext;
252 var session = new WriterSession(initialPrompt) { Mode = mode };
253
254 var wv = new WebView2 { Visibility = Visibility.Hidden };
255 WebViewHost.Children.Add(wv);
256
257 var tab = new BrowserTab
258 {
259 Id = $"tab{++_tabSeq}",
260 Label = label,
261 WebView = wv,
262 Session = session,
263 CurrentUrl = url,
264 };
265 session.NavigateTab = signal => HandleNavigateTab(tab, signal);
266 _tabs.Add(tab);
267 vm.Sessions.Add(session);
268
269 // 탭 헤더 버튼 생성
270 var btnPanel = new StackPanel { Orientation = Orientation.Horizontal };
271 btnPanel.Children.Add(new TextBlock
272 {
273 Text = label,
274 VerticalAlignment = VerticalAlignment.Center,
275 Margin = new Thickness(0, 0, 6, 0),
276 });
277 var close = new Button
278 {
279 Content = "×",
280 Background = Brushes.Transparent,
281 BorderThickness = new Thickness(0),
282 Foreground = new SolidColorBrush(Color.FromRgb(0x94, 0xa3, 0xb8)),
283 FontSize = 13,
284 Cursor = Cursors.Hand,
285 Padding = new Thickness(0),
286 Width = 16,
287 };
288 close.Click += (_, _) => CloseTab(tab);
289 btnPanel.Children.Add(close);
290
291 var toggle = new ToggleButton
292 {
293 Content = btnPanel,
294 Style = (Style)Resources["TabBtn"],
295 Margin = new Thickness(0, 0, 3, 0),
296 Tag = tab,
297 };
298 toggle.Click += (_, _) => ActivateTab(tab);
299 tab.HeaderBtn = toggle;
300 TabHeaderPanel.Children.Add(toggle);
301
302 // WebView2 초기화
303 await wv.EnsureCoreWebView2Async();
304 tab.IsReady = true;
305 tab.Session.ExecuteScriptAsync = async script =>
306 tab.IsReady ? await wv.ExecuteScriptAsync(script) : null;
307
308 wv.CoreWebView2.SourceChanged += (_, _) =>
309 {
310 tab.CurrentUrl = wv.CoreWebView2.Source;
311 if (_activeTab == tab)
312 Dispatcher.Invoke(() => UrlBox.Text = tab.CurrentUrl);
313 };
314 wv.CoreWebView2.Navigate(url);
315
316 return tab;
317 }
318
319 // ── 탭 활성화 ────────────────────────────────────────────────
336 private void ActivateTab(BrowserTab tab)
337 {
338 _activeTab = tab;
339 var vm = (MainViewModel)DataContext;
340
341 foreach (var t in _tabs)
342 {
343 // Collapsed이면 숨겨진 WebView2에서 JS focus()가 안 됨 → Hidden으로 레이아웃 유지
344 t.WebView.Visibility = t == tab ? Visibility.Visible : Visibility.Hidden;
345 if (t.HeaderBtn != null)
346 t.HeaderBtn.IsChecked = t == tab;
347 }
348
349 UrlBox.Text = tab.CurrentUrl;
350
351 // 왼쪽 패널이 이 탭의 세션을 바라보도록 전환
352 vm.ActiveSession = tab.Session;
353
354 // 이 탭의 WebView 스크립트 실행 위임 갱신 (IsReady 여부에 따라)
355 tab.Session.ExecuteScriptAsync = async script =>
356 tab.IsReady ? await tab.WebView.ExecuteScriptAsync(script) : null;
357 }
358
359 // ── 탭 닫기 ─────────────────────────────────────────────────
376 private void CloseTab(BrowserTab tab)
377 {
378 if (_tabs.Count <= 1) return;
379
380 var vm = (MainViewModel)DataContext;
381 var idx = _tabs.IndexOf(tab);
383 _tabs.Remove(tab);
384 vm.Sessions.Remove(tab.Session);
385
386 if (tab.HeaderBtn != null)
387 TabHeaderPanel.Children.Remove(tab.HeaderBtn);
388 WebViewHost.Children.Remove(tab.WebView);
389
390 if (_activeTab == tab)
391 {
392 var next = idx < _tabs.Count ? _tabs[idx] : _tabs[^1];
393 ActivateTab(next);
394 }
395 }
396
397 // ── ViewModel(세션)에서 요청하는 탭 이동 ───────────────────
422 private void HandleNavigateTab(BrowserTab tab, string signal)
423 {
424 if (!tab.IsReady) return;
425
426 var currentUrl = tab.CurrentUrl;
427 string newUrl;
428 if (currentUrl.Contains("chatgpt.com", StringComparison.OrdinalIgnoreCase))
429 newUrl = "https://chatgpt.com/";
430 else if (currentUrl.Contains("gemini.google.com", StringComparison.OrdinalIgnoreCase))
431 newUrl = "https://gemini.google.com/app";
432 else
433 newUrl = "https://claude.ai/new";
434
435 Dispatcher.Invoke(() =>
436 {
437 tab.WebView.CoreWebView2.Navigate(newUrl);
438 tab.CurrentUrl = newUrl;
439 if (_activeTab == tab)
440 UrlBox.Text = newUrl;
441 });
442 }
443
444 // ── 탭 추가 버튼 / Enter키 ──────────────────────────────────
469 private async void OnAddTabClick(object sender, RoutedEventArgs e) => await AddTabFromInputAsync();
494 private async void OnNewTabNameKeyDown(object sender, KeyEventArgs e)
495 {
496 if (e.Key == Key.Enter) await AddTabFromInputAsync();
497 }
498
515 private async Task AddTabFromInputAsync()
516 {
517 var label = NewTabNameBox.Text.Trim();
518 if (string.IsNullOrEmpty(label)) label = $"탭 {_tabSeq + 1}";
519
520 var modeItem = NewTabModeBox.SelectedItem as System.Windows.Controls.ComboBoxItem;
521 var isCooking = modeItem?.Content?.ToString() == "요리";
522 var mode = isCooking ? SessionMode.Cooking : SessionMode.Travel;
523 var prompt = isCooking ? WriterSession.BuildCookingPrompt([]) : WriterSession.BuildTravelPrompt([]);
524
525 var tab = await CreateTabAsync(label, "https://claude.ai/new", prompt, mode);
526 ActivateTab(tab);
527
528 NewTabNameBox.Clear();
529 NewTabNameBox.Focus();
530 }
531
532 // ── URL 바 네비게이션 ────────────────────────────────────────
557 private void OnNavigateClick(object sender, RoutedEventArgs e) => NavigateCurrent();
582 private void OnUrlKeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) NavigateCurrent(); }
607 private void OnBackClick(object sender, RoutedEventArgs e) => _activeTab?.WebView.GoBack();
608
617 private void NavigateCurrent()
618 {
619 if (_activeTab == null || !_activeTab.IsReady) return;
620 var url = UrlBox.Text.Trim();
621 if (!url.StartsWith("http", StringComparison.OrdinalIgnoreCase)) url = "https://" + url;
622 UrlBox.Text = url;
623 _activeTab.WebView.CoreWebView2.Navigate(url);
624 }
625
626 // ── 빠른 이동 버튼 ──────────────────────────────────────────
651 private void OnClaudeClick(object sender, RoutedEventArgs e) => NavigateTo("https://claude.ai/new");
676 private void OnChatGptClick(object sender, RoutedEventArgs e) => NavigateTo("https://chatgpt.com/");
701 private void OnGeminiClick(object sender, RoutedEventArgs e) => NavigateTo("https://gemini.google.com/app");
702
719 private void NavigateTo(string url)
720 {
721 if (_activeTab == null || !_activeTab.IsReady) return;
722 UrlBox.Text = url;
723 _activeTab.WebView.CoreWebView2.Navigate(url);
724 }
725}
void OnUrlKeyDown(object sender, KeyEventArgs e)
void OnLoaded(object sender, RoutedEventArgs e)
void OnBackClick(object sender, RoutedEventArgs e)
async Task< BrowserTab > CreateTabAsync(string label, string url, string? initialPrompt=null, SessionMode mode=SessionMode.Travel)
async void OnAddTabClick(object sender, RoutedEventArgs e)
async void OnNewTabNameKeyDown(object sender, KeyEventArgs e)
void HandleNavigateTab(BrowserTab tab, string signal)
void CloseTab(BrowserTab tab)
void OnClaudeClick(object sender, RoutedEventArgs e)
void OnNavigateClick(object sender, RoutedEventArgs e)
void ActivateTab(BrowserTab tab)
readonly List< BrowserTab > _tabs
void OnClosed(object? sender, EventArgs e)
void OnChatGptClick(object sender, RoutedEventArgs e)
void OnGeminiClick(object sender, RoutedEventArgs e)
static string BuildTravelPrompt(IReadOnlyList< string > existingTitles)
static string BuildCookingPrompt(IReadOnlyList< string > existingTitles)