using System.Text.Json; namespace VideoDownloader.Browser; public sealed record ArchivedTab(string Title, string Url, DateTime ClosedAt); /// T3-2: 탭 아카이브 — 닫힘 이력 영속(최대 500, 같은 URL 연속은 제목 갱신), 검색·복구 지원. public sealed class TabArchive { private const int Cap = 500; private readonly string _path; private readonly object _gate = new(); private List _items; public TabArchive(string path) { _path = path; try { if (File.Exists(path)) _items = JsonSerializer.Deserialize>(File.ReadAllText(path)) ?? new(); } catch { } _items ??= new List(); } public void Add(string title, string url) { if (string.IsNullOrWhiteSpace(url)) return; lock (_gate) { _items.RemoveAll(t => t.Url.Equals(url, StringComparison.OrdinalIgnoreCase)); _items.Insert(0, new ArchivedTab(title, url, DateTime.Now)); if (_items.Count > Cap) _items.RemoveRange(Cap, _items.Count - Cap); try { Directory.CreateDirectory(Path.GetDirectoryName(_path)!); File.WriteAllText(_path, JsonSerializer.Serialize(_items)); } catch { } } } public List Search(string query) { lock (_gate) { var q = query?.Trim() ?? ""; return _items.Where(t => q.Length == 0 || t.Title.Contains(q, StringComparison.OrdinalIgnoreCase) || t.Url.Contains(q, StringComparison.OrdinalIgnoreCase)).ToList(); } } public void Remove(string url) { lock (_gate) { _items.RemoveAll(t => t.Url.Equals(url, StringComparison.OrdinalIgnoreCase)); try { File.WriteAllText(_path, JsonSerializer.Serialize(_items)); } catch { } } } }