Phase B (T1-5/T1-6/T2-2/T2-8/T2-12): download manager flyout w/ speed+pause/resume/cancel, bookmark bar, tab sleeping, tab search (open+closed), context menu w/ 'download video from this link' (79 tests GREEN)

This commit is contained in:
Yun Chan 2026-08-15 15:09:59 +09:00
parent af19dba8f4
commit 0caa1111e7
9 changed files with 763 additions and 3 deletions

View file

@ -0,0 +1,41 @@
using VideoDownloader.Browser.Tabs;
namespace VideoDownloader.App.Browser.Features;
/// <summary>
/// 탭 절전(T2-2, Edge Sleeping Tabs 패턴): 유휴 비활성 탭을 TrySuspendAsync 로
/// 서스펜드해 메모리를 회수한다. 실제 서스펜드 수행은 주입(suspendAction) —
/// WebView2 TrySuspendAsync 는 뷰가 숨겨진(비활성) 탭에서만 동작.
/// </summary>
public sealed class TabSuspendService
{
private readonly TimeSpan _idleTimeout;
private readonly Func<TabItem, bool> _suspendAction;
private readonly Func<TabItem, bool> _canSuspend;
public TabSuspendService(TimeSpan idleTimeout, Func<TabItem, bool> suspendAction, Func<TabItem, bool> canSuspend)
{
_idleTimeout = idleTimeout;
_suspendAction = suspendAction;
_canSuspend = canSuspend;
}
/// <summary>유휴 비활성 탭 서스펜드. 반환: 서스펜드된 탭 수.</summary>
public int SuspendIdleTabs(IEnumerable<TabItem> tabs, TabItem active, DateTime now)
{
var count = 0;
foreach (var tab in tabs)
{
if (ReferenceEquals(tab, active)) continue;
if (tab.IsSuspended) continue;
if (!_canSuspend(tab)) continue;
if (now - tab.LastActivated < _idleTimeout) continue;
try
{
if (_suspendAction(tab)) { tab.IsSuspended = true; count++; }
}
catch { /* 서스펜드 실패는 건너뜀 */ }
}
return count;
}
}