데스크톱 T3 잔여 완성 + 스타일 폴리시: 도메인 라우팅(WorkspaceRoutingRules 자동 워크스페이스 전환), 탭 아카이브(TabArchive 영속·팔레트 복구), 페이지 액션(흑백/세피아 필터), 노트 패널(NotesStore+플라이아웃), 컴팩트 모드(크롬 숨김+엣지 호버). 스타일: 한국어 폰트 폴백, 암시 폰트 통일, 다크 툴팁, 텍스트 대비 상향. UpdateNavState dispose 가드. 164/164 GREEN + 실창 스크린샷 검증

This commit is contained in:
Yun Chan 2026-08-16 08:46:54 +09:00
parent 04ff0ff93d
commit f380518919
12 changed files with 594 additions and 24 deletions

View file

@ -0,0 +1,65 @@
using System.Text.Json;
namespace VideoDownloader.Browser;
/// <summary>T3-1: 도메인 → 워크스페이스 자동 라우팅 규칙(Vivaldi 규칙/Arc ATC).
/// 주소창 이동 시 매칭되면 해당 워크스페이스로 자동 전환한다. JSON 영속.</summary>
public sealed class WorkspaceRoutingRules
{
private readonly string _path;
private Dictionary<string, string> _map = new(StringComparer.OrdinalIgnoreCase);
public WorkspaceRoutingRules(string path)
{
_path = path;
try
{
if (File.Exists(path))
_map = JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText(path)) ?? new();
}
catch { }
_map ??= new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
public IReadOnlyDictionary<string, string> Rules => _map;
public void Add(string domain, string workspaceName)
{
var d = domain.Trim().TrimStart('.', 'w', 'w', 'w', '.');
_map[d] = workspaceName;
Save();
}
public void Remove(string domain)
{
_map.Remove(domain.Trim());
Save();
}
/// <summary>URL 이 매칭되는 워크스페이스명(없으면 null). www/서브도메인 허용.</summary>
public string? Match(string? url)
{
if (string.IsNullOrWhiteSpace(url)) return null;
if (!Uri.TryCreate(url, UriKind.Absolute, out var u) ||
(u.Scheme != Uri.UriSchemeHttp && u.Scheme != Uri.UriSchemeHttps)) return null;
var host = u.Host;
if (host.StartsWith("www.", StringComparison.OrdinalIgnoreCase)) host = host[4..];
foreach (var (domain, ws) in _map)
{
if (host.Equals(domain, StringComparison.OrdinalIgnoreCase) ||
host.EndsWith("." + domain, StringComparison.OrdinalIgnoreCase))
return ws;
}
return null;
}
private void Save()
{
try
{
Directory.CreateDirectory(Path.GetDirectoryName(_path)!);
File.WriteAllText(_path, JsonSerializer.Serialize(_map));
}
catch { }
}
}