diff --git a/VideoDownloader.App/AppPaths.cs b/VideoDownloader.App/AppPaths.cs
index aaf14f1..1c7f738 100644
--- a/VideoDownloader.App/AppPaths.cs
+++ b/VideoDownloader.App/AppPaths.cs
@@ -1,14 +1,7 @@
namespace VideoDownloader.App;
-///
-/// 앱 데이터 디렉터리 결정. 기본 %AppData%/VideoDownloader.
-/// 환경변수 VD_DATA_DIR 로 오버라이드 — E2E 테스트가 실사용자 프로필/설정/세션과
-/// 격리된 임시 디렉터리에서 실행되도록 한다(결정적 테스트를 위한 공식 후크).
-///
+/// 앱 데이터 디렉터리(CorePaths 위임) — VD_DATA_DIR 오버라이드 지원.
public static class AppPaths
{
- public static string DataDir =>
- Environment.GetEnvironmentVariable("VD_DATA_DIR") is { Length: > 0 } env
- ? env
- : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "VideoDownloader");
+ public static string DataDir => VideoDownloader.Core.Platform.CorePaths.DataDir;
}
diff --git a/VideoDownloader.App/MainWindow.xaml.cs b/VideoDownloader.App/MainWindow.xaml.cs
index 1f302d0..8362bb6 100644
--- a/VideoDownloader.App/MainWindow.xaml.cs
+++ b/VideoDownloader.App/MainWindow.xaml.cs
@@ -9,14 +9,14 @@ using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using Microsoft.Web.WebView2.Core;
-using VideoDownloader.App.Browser;
-using VideoDownloader.App.Browser.Tabs;
+using VideoDownloader.Browser;
+using VideoDownloader.Browser.Tabs;
using VideoDownloader.App.Core;
using VideoDownloader.Core.Detection;
using VideoDownloader.App.ViewModels;
using VideoDownloader.Core.Config;
using Wv2 = Microsoft.Web.WebView2.Wpf;
-using TabItem = VideoDownloader.App.Browser.Tabs.TabItem;
+using TabItem = VideoDownloader.Browser.Tabs.TabItem;
namespace VideoDownloader.App;
diff --git a/VideoDownloader.App/SettingsWindow.xaml.cs b/VideoDownloader.App/SettingsWindow.xaml.cs
index 34525e2..53602a1 100644
--- a/VideoDownloader.App/SettingsWindow.xaml.cs
+++ b/VideoDownloader.App/SettingsWindow.xaml.cs
@@ -4,7 +4,7 @@ using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using Microsoft.Win32;
-using VideoDownloader.App.Browser;
+using VideoDownloader.Browser;
using VideoDownloader.Core.Config;
using VideoDownloader.Core.Social;
diff --git a/VideoDownloader.App/VideoDownloader.App.csproj b/VideoDownloader.App/VideoDownloader.App.csproj
index 358ac62..d6a1ad1 100644
--- a/VideoDownloader.App/VideoDownloader.App.csproj
+++ b/VideoDownloader.App/VideoDownloader.App.csproj
@@ -21,6 +21,7 @@
+
diff --git a/VideoDownloader.App/Browser/BrowserModels.cs b/VideoDownloader.Browser/BrowserModels.cs
similarity index 76%
rename from VideoDownloader.App/Browser/BrowserModels.cs
rename to VideoDownloader.Browser/BrowserModels.cs
index b513cd4..60ed3b7 100644
--- a/VideoDownloader.App/Browser/BrowserModels.cs
+++ b/VideoDownloader.Browser/BrowserModels.cs
@@ -1,4 +1,4 @@
-namespace VideoDownloader.App.Browser;
+namespace VideoDownloader.Browser;
public record Bookmark(string Title, string Url);
diff --git a/VideoDownloader.App/Browser/BrowserStore.cs b/VideoDownloader.Browser/BrowserStore.cs
similarity index 96%
rename from VideoDownloader.App/Browser/BrowserStore.cs
rename to VideoDownloader.Browser/BrowserStore.cs
index 159dfd2..495291e 100644
--- a/VideoDownloader.App/Browser/BrowserStore.cs
+++ b/VideoDownloader.Browser/BrowserStore.cs
@@ -1,13 +1,13 @@
using System.Text.Json;
-namespace VideoDownloader.App.Browser;
+namespace VideoDownloader.Browser;
/// 즐겨찾기/히스토리 JSON 영속화 (%AppData%/VideoDownloader).
/// #5 저장/로드 실패를 조용 삼킴하지 않고 browserstore.log 에 기록하며,
/// 손상 파일은 백업 후 빈 목록을 반환(다음 저장이 원본을 덮어쓰는 데이터 손실 방지).
public static class BrowserStore
{
- private static string Dir => AppPaths.DataDir;
+ private static string Dir => VideoDownloader.Core.Platform.CorePaths.DataDir;
private static string BookmarksPath => Path.Combine(Dir, "bookmarks.json");
private static string HistoryPath => Path.Combine(Dir, "history.json");
private static readonly JsonSerializerOptions JsonOpts = new() { WriteIndented = true };
diff --git a/VideoDownloader.App/Browser/FindSession.cs b/VideoDownloader.Browser/FindSession.cs
similarity index 99%
rename from VideoDownloader.App/Browser/FindSession.cs
rename to VideoDownloader.Browser/FindSession.cs
index 59a31f5..0e37b27 100644
--- a/VideoDownloader.App/Browser/FindSession.cs
+++ b/VideoDownloader.Browser/FindSession.cs
@@ -1,6 +1,6 @@
using Microsoft.Web.WebView2.Core;
-namespace VideoDownloader.App.Browser;
+namespace VideoDownloader.Browser;
/// 네이티브 Find API 의 최소 추상화(단위 테스트용 fake 교체 가능).
public interface IFindApi
diff --git a/VideoDownloader.App/Browser/SearchEngine.cs b/VideoDownloader.Browser/SearchEngine.cs
similarity index 97%
rename from VideoDownloader.App/Browser/SearchEngine.cs
rename to VideoDownloader.Browser/SearchEngine.cs
index 8138bf3..3898191 100644
--- a/VideoDownloader.App/Browser/SearchEngine.cs
+++ b/VideoDownloader.Browser/SearchEngine.cs
@@ -1,4 +1,4 @@
-namespace VideoDownloader.App.Browser;
+namespace VideoDownloader.Browser;
/// 검색엔진 정의. QueryTemplate 의 {0} 이 URL 인코딩된 검색어로 치환된다.
public sealed record SearchEngine(string Name, string Keyword, string QueryTemplate);
diff --git a/VideoDownloader.App/Browser/SearchEngineResolver.cs b/VideoDownloader.Browser/SearchEngineResolver.cs
similarity index 98%
rename from VideoDownloader.App/Browser/SearchEngineResolver.cs
rename to VideoDownloader.Browser/SearchEngineResolver.cs
index a56e306..c7d9a73 100644
--- a/VideoDownloader.App/Browser/SearchEngineResolver.cs
+++ b/VideoDownloader.Browser/SearchEngineResolver.cs
@@ -1,6 +1,6 @@
using System.Text.RegularExpressions;
-namespace VideoDownloader.App.Browser;
+namespace VideoDownloader.Browser;
///
/// 주소창 입력을 URL 또는 검색 의도로 판별한다(T1-4).
diff --git a/VideoDownloader.App/Browser/SiteZoomStore.cs b/VideoDownloader.Browser/SiteZoomStore.cs
similarity index 98%
rename from VideoDownloader.App/Browser/SiteZoomStore.cs
rename to VideoDownloader.Browser/SiteZoomStore.cs
index 81f63f3..f064b83 100644
--- a/VideoDownloader.App/Browser/SiteZoomStore.cs
+++ b/VideoDownloader.Browser/SiteZoomStore.cs
@@ -1,7 +1,7 @@
using System.IO;
using System.Text.Json;
-namespace VideoDownloader.App.Browser;
+namespace VideoDownloader.Browser;
///
/// 사이트별 줌 배율 영속 저장소(T1-7). WebView2 는 사이트별 줌 영속을 보장하지 않아
diff --git a/VideoDownloader.App/Browser/Tabs/TabItem.cs b/VideoDownloader.Browser/Tabs/TabItem.cs
similarity index 82%
rename from VideoDownloader.App/Browser/Tabs/TabItem.cs
rename to VideoDownloader.Browser/Tabs/TabItem.cs
index 7bc220b..4d7c7ac 100644
--- a/VideoDownloader.App/Browser/Tabs/TabItem.cs
+++ b/VideoDownloader.Browser/Tabs/TabItem.cs
@@ -2,7 +2,7 @@ using System.ComponentModel;
using System.Runtime.CompilerServices;
using Microsoft.Web.WebView2.Wpf;
-namespace VideoDownloader.App.Browser.Tabs;
+namespace VideoDownloader.Browser.Tabs;
/// 탭 하나의 상태. View(WebView2 컨트롤)는 앱 레이어에서 주입한다(단위 테스트에선 null).
public sealed class TabItem : INotifyPropertyChanged
@@ -44,10 +44,13 @@ public sealed class TabItem : INotifyPropertyChanged
/// 닫힌 시점의 인덱스(닫힌 탭 복원용). 열려 있는 동안은 무의미.
internal int ClosedIndex { get; set; } = -1;
- // ---- 탭별 일시 상태(앱 레이어 전용) ----
- internal string? PendingHistoryUrl; // SourceChanged → 타이틀 확정 사이의 기록 대기 URL
- internal string? PendingSocialUrl; // 소셜 카드 제목 갱신 대기 URL
- internal FindSession? FindSession; // 탭별 페이지 내 검색 세션 (cv 바인딩)
+ // ---- 탭별 일시 상태(호스트 앱이 사용) ----
+ /// SourceChanged → 타이틀 확정 사이의 히스토리 기록 대기 URL.
+ public string? PendingHistoryUrl { get; set; }
+ /// 소셜 카드 제목 갱신 대기 URL.
+ public string? PendingSocialUrl { get; set; }
+ /// 탭별 페이지 내 검색 세션 (CoreWebView2 바인딩).
+ public FindSession? FindSession { get; set; }
public event PropertyChangedEventHandler? PropertyChanged;
diff --git a/VideoDownloader.App/Browser/Tabs/TabManager.cs b/VideoDownloader.Browser/Tabs/TabManager.cs
similarity index 99%
rename from VideoDownloader.App/Browser/Tabs/TabManager.cs
rename to VideoDownloader.Browser/Tabs/TabManager.cs
index 8cfde04..4f90806 100644
--- a/VideoDownloader.App/Browser/Tabs/TabManager.cs
+++ b/VideoDownloader.Browser/Tabs/TabManager.cs
@@ -1,6 +1,6 @@
using System.Collections.ObjectModel;
-namespace VideoDownloader.App.Browser.Tabs;
+namespace VideoDownloader.Browser.Tabs;
///
/// 탭 관리 순수 로직(Phase A). WebView2 생성은 주입된 팩토리/어태처가 담당해
diff --git a/VideoDownloader.App/Browser/Tabs/TabSessionStore.cs b/VideoDownloader.Browser/Tabs/TabSessionStore.cs
similarity index 96%
rename from VideoDownloader.App/Browser/Tabs/TabSessionStore.cs
rename to VideoDownloader.Browser/Tabs/TabSessionStore.cs
index c15c733..b193eee 100644
--- a/VideoDownloader.App/Browser/Tabs/TabSessionStore.cs
+++ b/VideoDownloader.Browser/Tabs/TabSessionStore.cs
@@ -1,7 +1,7 @@
using System.IO;
using System.Text.Json;
-namespace VideoDownloader.App.Browser.Tabs;
+namespace VideoDownloader.Browser.Tabs;
/// 세션 JSON 저장소(T1-10). 종료 시 탭 URL 저장, 시작 시 복원.
public static class TabSessionStore
diff --git a/VideoDownloader.Browser/VideoDownloader.Browser.csproj b/VideoDownloader.Browser/VideoDownloader.Browser.csproj
new file mode 100644
index 0000000..82186a3
--- /dev/null
+++ b/VideoDownloader.Browser/VideoDownloader.Browser.csproj
@@ -0,0 +1,21 @@
+
+
+ net8.0-windows
+ enable
+ enable
+ true
+ true
+ VideoDownloader.Browser
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/VideoDownloader.Core/Platform/CorePaths.cs b/VideoDownloader.Core/Platform/CorePaths.cs
new file mode 100644
index 0000000..435e94c
--- /dev/null
+++ b/VideoDownloader.Core/Platform/CorePaths.cs
@@ -0,0 +1,13 @@
+namespace VideoDownloader.Core.Platform;
+
+///
+/// 앱 데이터 디렉터리 결정(Core 레벨). 기본 %AppData%/VideoDownloader.
+/// VD_DATA_DIR 환경변수로 오버라이드 — E2E 테스트 격리 공식 후크.
+///
+public static class CorePaths
+{
+ public static string DataDir =>
+ Environment.GetEnvironmentVariable("VD_DATA_DIR") is { Length: > 0 } env
+ ? env
+ : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "VideoDownloader");
+}
diff --git a/VideoDownloader.Tests/Browser/FindSessionTests.cs b/VideoDownloader.Tests/Browser/FindSessionTests.cs
index 5d3c27b..1e7f9aa 100644
--- a/VideoDownloader.Tests/Browser/FindSessionTests.cs
+++ b/VideoDownloader.Tests/Browser/FindSessionTests.cs
@@ -1,4 +1,4 @@
-using VideoDownloader.App.Browser;
+using VideoDownloader.Browser;
namespace VideoDownloader.Tests.Browser;
diff --git a/VideoDownloader.Tests/Browser/SearchEngineResolverTests.cs b/VideoDownloader.Tests/Browser/SearchEngineResolverTests.cs
index 105bffc..bec1d41 100644
--- a/VideoDownloader.Tests/Browser/SearchEngineResolverTests.cs
+++ b/VideoDownloader.Tests/Browser/SearchEngineResolverTests.cs
@@ -1,4 +1,4 @@
-using VideoDownloader.App.Browser;
+using VideoDownloader.Browser;
namespace VideoDownloader.Tests.Browser;
diff --git a/VideoDownloader.Tests/Browser/SiteZoomStoreTests.cs b/VideoDownloader.Tests/Browser/SiteZoomStoreTests.cs
index 72f6f46..d8c8ddd 100644
--- a/VideoDownloader.Tests/Browser/SiteZoomStoreTests.cs
+++ b/VideoDownloader.Tests/Browser/SiteZoomStoreTests.cs
@@ -1,4 +1,4 @@
-using VideoDownloader.App.Browser;
+using VideoDownloader.Browser;
namespace VideoDownloader.Tests.Browser;
diff --git a/VideoDownloader.Tests/Browser/TabManagerTests.cs b/VideoDownloader.Tests/Browser/TabManagerTests.cs
index 8bc7125..1426b89 100644
--- a/VideoDownloader.Tests/Browser/TabManagerTests.cs
+++ b/VideoDownloader.Tests/Browser/TabManagerTests.cs
@@ -1,4 +1,4 @@
-using VideoDownloader.App.Browser.Tabs;
+using VideoDownloader.Browser.Tabs;
namespace VideoDownloader.Tests.Browser;
diff --git a/VideoDownloader.slnx b/VideoDownloader.slnx
index a6b90d2..6268e20 100644
--- a/VideoDownloader.slnx
+++ b/VideoDownloader.slnx
@@ -1,6 +1,7 @@
+