데스크톱 T3 잔여 완성 + 스타일 폴리시: 도메인 라우팅(WorkspaceRoutingRules 자동 워크스페이스 전환), 탭 아카이브(TabArchive 영속·팔레트 복구), 페이지 액션(흑백/세피아 필터), 노트 패널(NotesStore+플라이아웃), 컴팩트 모드(크롬 숨김+엣지 호버). 스타일: 한국어 폰트 폴백, 암시 폰트 통일, 다크 툴팁, 텍스트 대비 상향. UpdateNavState dispose 가드. 164/164 GREEN + 실창 스크린샷 검증
This commit is contained in:
parent
04ff0ff93d
commit
f380518919
12 changed files with 594 additions and 24 deletions
|
|
@ -48,8 +48,11 @@ public sealed class CommandPalette
|
|||
public void Execute(PaletteCommand command) => command.Execute();
|
||||
}
|
||||
|
||||
/// <summary>사이트별 부스트 설정(T2-7, Zen/Arc Boosts 패턴).</summary>
|
||||
public sealed record BoostSettings(bool ForceDark, string CustomCss);
|
||||
/// <summary>페이지 액션 필터(T3-6, Vivaldi Page Actions — Boost 통합).</summary>
|
||||
public enum PageFilter { None, Grayscale, Sepia }
|
||||
|
||||
/// <summary>사이트별 부스트 설정(T2-7, Zen/Arc Boosts 패턴) + 페이지 액션 필터(T3-6).</summary>
|
||||
public sealed record BoostSettings(bool ForceDark, string CustomCss, PageFilter Filter = PageFilter.None);
|
||||
|
||||
/// <summary>도메인별 부스트 영속 저장.</summary>
|
||||
public sealed class BoostStore
|
||||
|
|
@ -77,11 +80,11 @@ public sealed class BoostStore
|
|||
return host != null && _map.TryGetValue(host, out var v) ? v : new BoostSettings(false, "");
|
||||
}
|
||||
|
||||
public void Set(string? url, bool forceDark, string css)
|
||||
public void Set(string? url, bool forceDark, string css, PageFilter filter = PageFilter.None)
|
||||
{
|
||||
var host = Normalize(url);
|
||||
if (host == null) return;
|
||||
_map[host] = new BoostSettings(forceDark, css);
|
||||
_map[host] = new BoostSettings(forceDark, css, filter);
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(_path)!);
|
||||
|
|
@ -110,7 +113,7 @@ public static class BoostCssBuilder
|
|||
{
|
||||
public static string BuildScript(string? url, BoostSettings settings)
|
||||
{
|
||||
if (!settings.ForceDark && string.IsNullOrWhiteSpace(settings.CustomCss)) return "";
|
||||
if (!settings.ForceDark && settings.Filter == PageFilter.None && string.IsNullOrWhiteSpace(settings.CustomCss)) return "";
|
||||
var css = "";
|
||||
if (settings.ForceDark)
|
||||
{
|
||||
|
|
@ -119,6 +122,14 @@ html { filter: invert(1) hue-rotate(180deg); background: #fff; }
|
|||
img, video, picture, canvas, svg, iframe, embed, object { filter: invert(1) hue-rotate(180deg); }
|
||||
""";
|
||||
}
|
||||
else if (settings.Filter == PageFilter.Grayscale) // T3-6 페이지 액션
|
||||
{
|
||||
css += "html { filter: grayscale(1); }";
|
||||
}
|
||||
else if (settings.Filter == PageFilter.Sepia)
|
||||
{
|
||||
css += "html { filter: sepia(0.7); }";
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(settings.CustomCss)) css += settings.CustomCss + "\n";
|
||||
var js = $$"""
|
||||
(function(){ try { var s = document.createElement('style'); s.id = 'vd-boost'; s.textContent = {{JsonSerializer.Serialize(css)}}; (document.head || document.documentElement).appendChild(s); } catch(e){} })();
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@
|
|||
</DataTemplate>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid x:Name="MainGrid">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
|
|
@ -459,7 +459,7 @@
|
|||
<!-- ===== 상태 표시줄 ===== -->
|
||||
<Border x:Name="StatusBar" Grid.Row="4" Background="{StaticResource PanelBg}"
|
||||
BorderBrush="{StaticResource BorderSubtle}" BorderThickness="0,1,0,0" Padding="16,5">
|
||||
<TextBlock Name="StatusText" Foreground="{StaticResource TextMuted}" FontSize="11"
|
||||
<TextBlock Name="StatusText" Foreground="{StaticResource TextSecondary}" FontSize="11.5"
|
||||
TextTrimming="CharacterEllipsis" Text="준비"/>
|
||||
</Border>
|
||||
|
||||
|
|
@ -550,6 +550,26 @@
|
|||
</Popup>
|
||||
|
||||
<!-- ===== 다운로드 플라이아웃 (T1-5) ===== -->
|
||||
<!-- T3-12 노트 패널 -->
|
||||
<Popup Name="NotesPopup" PlacementTarget="{Binding ElementName=AddressBox}"
|
||||
Placement="Bottom" HorizontalOffset="-40" VerticalOffset="6"
|
||||
StaysOpen="False" AllowsTransparency="True" PopupAnimation="Fade">
|
||||
<Border Background="{StaticResource CardBg}" BorderBrush="{StaticResource BorderBrush}"
|
||||
BorderThickness="1" CornerRadius="12" Padding="10" Width="420" MaxHeight="460">
|
||||
<StackPanel>
|
||||
<DockPanel Margin="6,2,6,8">
|
||||
<TextBlock Text="노트" Foreground="{StaticResource TextPrimary}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button Style="{StaticResource CardActionButton}" Content="현재 페이지 추가" Click="NotesAddPage_Click" Margin="0,0,6,0"/>
|
||||
<Button Style="{StaticResource CardActionButton}" Content="삭제" Click="NotesDelete_Click"/>
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
<ListBox Name="NotesList" Background="Transparent" BorderThickness="0" Height="340"
|
||||
Foreground="{StaticResource TextPrimary}" FontSize="12"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Popup>
|
||||
|
||||
<!-- T3-14 추적 방지 실드 팝업 -->
|
||||
<Popup Name="ShieldPopup" PlacementTarget="{Binding ElementName=ShieldBtn}"
|
||||
Placement="Bottom" HorizontalOffset="-10" VerticalOffset="6"
|
||||
|
|
|
|||
|
|
@ -50,6 +50,15 @@ public partial class MainWindow : Window
|
|||
// Phase C: 팔레트/부스트/워크스페이스/레이아웃
|
||||
private readonly CommandPalette _palette = new();
|
||||
private BoostStore _boostStore = null!;
|
||||
private WorkspaceRoutingRules _routingRules = null!; // T3-1
|
||||
private TabArchive _tabArchive = null!; // T3-2
|
||||
internal TabArchive Archive => _tabArchive; // E2E 검증용
|
||||
internal BoostStore BoostStore => _boostStore; // E2E 검증용
|
||||
internal NotesStore Notes => _notes; // E2E 검증용
|
||||
private NotesStore _notes = null!; // T3-12
|
||||
private string? _currentWorkspaceName; // T3-1 라우팅 기준
|
||||
private bool _compactMode; // T3-13
|
||||
private bool _chromeRevealed; // T3-13 호버 복원 상태
|
||||
private List<WorkspaceSnapshot> _workspaces = new();
|
||||
private bool _verticalTabs;
|
||||
private TabItem? _splitTab;
|
||||
|
|
@ -101,6 +110,9 @@ public partial class MainWindow : Window
|
|||
_resolver = new SearchEngineResolver(_config.SearchEngine);
|
||||
_zoomStore = new SiteZoomStore(Path.Combine(AppDataDir, "sitezoom.json"));
|
||||
_boostStore = new BoostStore(Path.Combine(AppDataDir, "boost.json"));
|
||||
_routingRules = new WorkspaceRoutingRules(Path.Combine(AppDataDir, "routing.json")); // T3-1
|
||||
_tabArchive = new TabArchive(Path.Combine(AppDataDir, "archive.json")); // T3-2
|
||||
_notes = new NotesStore(Path.Combine(AppDataDir, "notes.json")); // T3-12
|
||||
try
|
||||
{
|
||||
var wsPath = Path.Combine(AppDataDir, "workspaces.json");
|
||||
|
|
@ -434,16 +446,22 @@ public partial class MainWindow : Window
|
|||
private void OnTabRemoved(TabItem tab)
|
||||
{
|
||||
if (ReferenceEquals(_splitTab, tab)) ExitSplit();
|
||||
_tabArchive.Add(tab.Title, tab.Url); // T3-2 닫힘 → 아카이브(영속, 500 상한)
|
||||
_views?.RemoveView(tab);
|
||||
}
|
||||
|
||||
private void UpdateNavState()
|
||||
{
|
||||
var cv = Cv;
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
NavBack.IsEnabled = cv?.CanGoBack == true;
|
||||
NavForward.IsEnabled = cv?.CanGoForward == true;
|
||||
try
|
||||
{
|
||||
var cv = Cv; // dispose 이후 이벤트 경로 방어
|
||||
NavBack.IsEnabled = cv?.CanGoBack == true;
|
||||
NavForward.IsEnabled = cv?.CanGoForward == true;
|
||||
}
|
||||
catch (ObjectDisposedException) { }
|
||||
catch (InvalidOperationException) { }
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
@ -717,6 +735,16 @@ public partial class MainWindow : Window
|
|||
// T1-4: URL/검색어 판별 + 기본 엔진·키워드 전환(g, yt, ...)은 리졸버가 담당
|
||||
var intent = _resolver.Resolve(url);
|
||||
if (intent == null) return;
|
||||
|
||||
// T3-1: 도메인 라우팅 규칙 — 대상 워크스페이스가 다르면 자동 전환 후 이동(Arc ATC 패턴)
|
||||
var routeTo = _routingRules.Match(intent.Target);
|
||||
if (routeTo != null && routeTo != _currentWorkspaceName &&
|
||||
_workspaces.FirstOrDefault(w => w.Name == routeTo) is { } wsTarget)
|
||||
{
|
||||
SwitchWorkspace(wsTarget);
|
||||
_currentWorkspaceName = routeTo;
|
||||
Log($"라우팅 규칙 → 워크스페이스 '{routeTo}' 전환");
|
||||
}
|
||||
Cv?.Navigate(intent.Target);
|
||||
}
|
||||
|
||||
|
|
@ -778,6 +806,8 @@ public partial class MainWindow : Window
|
|||
else if (mods == (ModifierKeys.Control | ModifierKeys.Shift) && e.Key == Key.D) { ToggleBoostDark(); e.Handled = true; }
|
||||
else if (mods == (ModifierKeys.Control | ModifierKeys.Shift) && e.Key == Key.S) { _ = TakeScreenshot(); e.Handled = true; }
|
||||
else if (mods == (ModifierKeys.Control | ModifierKeys.Shift) && e.Key == Key.N) { OpenInPrivateTab(); e.Handled = true; }
|
||||
else if (mods == (ModifierKeys.Control | ModifierKeys.Shift) && e.Key == Key.M) { ToggleCompactMode(); e.Handled = true; } // T3-13
|
||||
else if (mods == (ModifierKeys.Control | ModifierKeys.Alt) && e.Key == Key.N) { ToggleNotes(); e.Handled = true; } // T3-12
|
||||
else if (mods == (ModifierKeys.Alt | ModifierKeys.Control) && e.Key == Key.V) { ToggleVerticalTabs(); e.Handled = true; }
|
||||
else if (mods == (ModifierKeys.Alt | ModifierKeys.Control) && e.Key == Key.H) { ToggleSplit(); e.Handled = true; }
|
||||
else if (mods == ModifierKeys.Control && e.Key == Key.U) { ViewSource(); e.Handled = true; }
|
||||
|
|
@ -1243,6 +1273,20 @@ public partial class MainWindow : Window
|
|||
_palette.Register("미디어 재생목록", "playlist play media 음악 영상", () => { PlaylistPopup.IsOpen = true; });
|
||||
_palette.Register("재생목록 재생", "playlist play start", OpenPlayer);
|
||||
_palette.Register("기기 연결 (QR 페어링)", "remote pair qr mobile phone", ShowPairing);
|
||||
// ---- T3 잔여(데스크톱) ----
|
||||
_palette.Register("이 사이트 흑백", "grayscale filter page action 흑백", () => ApplyPageFilter(PageFilter.Grayscale)); // T3-6
|
||||
_palette.Register("이 사이트 세피아", "sepia filter page action 세피아", () => ApplyPageFilter(PageFilter.Sepia));
|
||||
_palette.Register("페이지 액션 끄기", "filter off reset", () => ApplyPageFilter(PageFilter.None));
|
||||
_palette.Register("노트 패널", "notes note 메모", ToggleNotes); // T3-12
|
||||
_palette.Register("노트: 현재 페이지 추가", "note add", () => NotesAddPage_Click(this, new RoutedEventArgs()));
|
||||
_palette.Register("컴팩트 모드 토글", "compact chrome hide ui", ToggleCompactMode); // T3-13
|
||||
_palette.RegisterDynamic(() => _workspaces // T3-1: 사이트→워크스페이스 규칙
|
||||
.Select(w => new PaletteCommand($"라우팅 규칙: 이 사이트 → '{w.Name}'", "routing rule workspace",
|
||||
() => AddRoutingRuleForCurrentSite(w.Name))).ToList());
|
||||
_palette.RegisterDynamic(() => _tabArchive.Search("") // T3-2: 아카이브 복구
|
||||
.Take(8)
|
||||
.Select(a => new PaletteCommand($"아카이브에서 열기: {a.Title}", $"archive reopen {a.Url}",
|
||||
() => { var t = _tabs.NewTab(); Cv2Navigate(t, a.Url); })).ToList());
|
||||
_palette.Register("원격 접근 켜기/끄기", "remote server toggle lan", async () => await ToggleRemoteAccess());
|
||||
_palette.RegisterSeparator();
|
||||
_palette.RegisterDynamic(() => _tabs.Tabs
|
||||
|
|
@ -1304,6 +1348,112 @@ public partial class MainWindow : Window
|
|||
Log(!cur.ForceDark ? "이 사이트 강제 다크 켜짐" : "이 사이트 강제 다크 꺼짐");
|
||||
}
|
||||
|
||||
// ---- T3-6 페이지 액션(흑백/세피아 — Boost 통합) ----
|
||||
internal void ApplyPageFilter(PageFilter filter)
|
||||
{
|
||||
var url = Cv?.Source;
|
||||
if (string.IsNullOrEmpty(url)) { Log("페이지가 없습니다"); return; }
|
||||
var cur = _boostStore.Get(url);
|
||||
_boostStore.Set(url, cur.ForceDark, cur.CustomCss, filter);
|
||||
_views?.ApplyBoostNow(_tabs.ActiveTab!);
|
||||
Log(filter switch
|
||||
{
|
||||
PageFilter.Grayscale => "이 사이트 흑백 적용",
|
||||
PageFilter.Sepia => "이 사이트 세피아 적용",
|
||||
_ => "페이지 액션 해제",
|
||||
});
|
||||
}
|
||||
|
||||
// ---- T3-1 도메인 라우팅 규칙 ----
|
||||
private void AddRoutingRuleForCurrentSite(string workspaceName)
|
||||
{
|
||||
var url = Cv?.Source;
|
||||
if (string.IsNullOrWhiteSpace(url) || !Uri.TryCreate(url, UriKind.Absolute, out var u))
|
||||
{ Log("페이지가 없습니다"); return; }
|
||||
var host = u.Host;
|
||||
if (host.StartsWith("www.", StringComparison.OrdinalIgnoreCase)) host = host[4..];
|
||||
_routingRules.Add(host, workspaceName);
|
||||
Log($"라우팅 규칙 추가: {host} → '{workspaceName}' (다음 이동부터 자동 전환)");
|
||||
}
|
||||
|
||||
// ---- T3-2 아카이브 복구 보조 ----
|
||||
private static void Cv2Navigate(TabItem tab, string url)
|
||||
{
|
||||
try { tab.View?.CoreWebView2?.Navigate(url); } catch { }
|
||||
}
|
||||
|
||||
// ---- T3-12 노트 패널 ----
|
||||
internal void ToggleNotes()
|
||||
{
|
||||
RefreshNotesList();
|
||||
NotesPopup.IsOpen = true;
|
||||
}
|
||||
|
||||
private void RefreshNotesList()
|
||||
{
|
||||
NotesList.ItemsSource = _notes.List()
|
||||
.Select(n => $"[{n.CreatedAt:MM-dd HH:mm}] {n.Title} — {n.Body}")
|
||||
.ToList();
|
||||
NotesList.DisplayMemberPath = null;
|
||||
}
|
||||
|
||||
internal void NotesAddPage_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var url = Cv?.Source ?? "";
|
||||
var title = _tabs.ActiveTab?.Title ?? url;
|
||||
_notes.Add(title, url);
|
||||
Log("노트에 페이지 추가됨");
|
||||
RefreshNotesList();
|
||||
}
|
||||
|
||||
private void NotesDelete_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var idx = NotesList.SelectedIndex;
|
||||
var list = _notes.List();
|
||||
if (idx >= 0 && idx < list.Count)
|
||||
{
|
||||
_notes.Delete(list[idx].Id);
|
||||
RefreshNotesList();
|
||||
}
|
||||
}
|
||||
|
||||
// ---- T3-13 컴팩트 모드(크롬 숨김 + 상단 엣지 호버 복원) ----
|
||||
internal void ToggleCompactMode()
|
||||
{
|
||||
_compactMode = !_compactMode;
|
||||
_chromeRevealed = false;
|
||||
ApplyChromeVisibility();
|
||||
Log(_compactMode ? "컴팩트 모드: 크롤 숨김 (상단 가장자리에 마우스를 올리면 복원)" : "컴팩트 모드 해제");
|
||||
}
|
||||
|
||||
private void ApplyChromeVisibility()
|
||||
{
|
||||
var chromeRows = new[] { 0, 1, 2 }; // 툴바/북마크바/탭스트립
|
||||
foreach (var i in chromeRows)
|
||||
MainGrid.RowDefinitions[i].Height = (_compactMode && !_chromeRevealed) ? new GridLength(0) : GridLength.Auto;
|
||||
if (!_compactMode) _chromeRevealed = false;
|
||||
}
|
||||
|
||||
protected override void OnPreviewMouseMove(System.Windows.Input.MouseEventArgs e)
|
||||
{
|
||||
if (_compactMode)
|
||||
{
|
||||
var p = e.GetPosition(this);
|
||||
var wantReveal = p.Y <= 4; // 상단 엣지 4px
|
||||
if (wantReveal != _chromeRevealed)
|
||||
{
|
||||
_chromeRevealed = wantReveal;
|
||||
ApplyChromeVisibility();
|
||||
}
|
||||
else if (_chromeRevealed && p.Y > 120) // 크롬 영역 벗어나면 다시 숨김
|
||||
{
|
||||
_chromeRevealed = false;
|
||||
ApplyChromeVisibility();
|
||||
}
|
||||
}
|
||||
base.OnPreviewMouseMove(e);
|
||||
}
|
||||
|
||||
// ---- T2-9 스크린샷 ----
|
||||
private async Task TakeScreenshot()
|
||||
{
|
||||
|
|
@ -1490,7 +1640,7 @@ public partial class MainWindow : Window
|
|||
if (cv == null) return;
|
||||
var tiles = _bookmarks.Take(18).Select(b =>
|
||||
$"<a href='{System.Net.WebUtility.HtmlEncode(b.Url)}' style='display:block;text-decoration:none;background:#262736;color:#e6e6ec;border-radius:12px;padding:18px 12px;text-align:center;font-size:12.5px;min-width:110px' onmouseover='this.style.background=#2f3042' onmouseout='this.style.background=#262736'><div style='font-size:26px;margin-bottom:8px'>🌐</div><div style='overflow:hidden;text-overflow:ellipsis;white-space:nowrap'>{System.Net.WebUtility.HtmlEncode(b.Title)}</div></a>").ToList();
|
||||
var dial = "<!DOCTYPE html><html><head><meta charset='utf-8'><style>body{background:#14151d;color:#e6e6ec;font-family:'Segoe UI',sans-serif;margin:0;padding:48px 24px}h1{font-size:20px;text-align:center;margin-bottom:28px;font-weight:600}.grid{display:flex;flex-wrap:wrap;gap:14px;justify-content:center;max-width:760px;margin:0 auto}.hint{text-align:center;color:#8a8aa0;font-size:12px;margin-top:36px}a{color:inherit}</style></head><body><h1>새 탭</h1><div class='grid'>" + string.Join("", tiles) + $"</div><div class='hint'>주소창에 검색어나 URL 을 입력하세요 · 즐겨찾기 {_bookmarks.Count}개 중 {tiles.Count}개 표시</div></body></html>";
|
||||
var dial = "<!DOCTYPE html><html><head><meta charset='utf-8'><style>body{background:#14151d;color:#e6e6ec;font-family:'Segoe UI Variable Text','Segoe UI','Malgun Gothic',sans-serif;margin:0;padding:48px 24px}h1{font-size:20px;text-align:center;margin-bottom:28px;font-weight:600}.grid{display:flex;flex-wrap:wrap;gap:14px;justify-content:center;max-width:760px;margin:0 auto}.hint{text-align:center;color:#9aa0b4;font-size:12.5px;margin-top:36px}a{color:inherit}</style></head><body><h1>새 탭</h1><div class='grid'>" + string.Join("", tiles) + $"</div><div class='hint'>주소창에 검색어나 URL 을 입력하세요 · 즐겨찾기 {_bookmarks.Count}개 중 {tiles.Count}개 표시</div></body></html>";
|
||||
cv.NavigateToString(dial);
|
||||
}
|
||||
|
||||
|
|
@ -1575,6 +1725,7 @@ public partial class MainWindow : Window
|
|||
{
|
||||
ExitSplit();
|
||||
_tabs.SwitchWorkspace(ws);
|
||||
_currentWorkspaceName = ws.Name; // T3-1 라우팅 기준
|
||||
Log($"워크스페이스 '{ws.Name}' 전환 ({ws.Urls.Count} 탭)");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@
|
|||
<SolidColorBrush x:Key="GlassBg" Color="#1A1A24"/>
|
||||
<SolidColorBrush x:Key="CardBg" Color="#1C1C26"/>
|
||||
<SolidColorBrush x:Key="CardBgHover" Color="#242430"/>
|
||||
<SolidColorBrush x:Key="BorderBrush" Color="#2C2C3A"/>
|
||||
<SolidColorBrush x:Key="BorderBrush" Color="#343442"/>
|
||||
<SolidColorBrush x:Key="BorderSubtle" Color="#20202B"/>
|
||||
<SolidColorBrush x:Key="TextPrimary" Color="#F2F2F6"/>
|
||||
<SolidColorBrush x:Key="TextSecondary" Color="#9B9BAC"/>
|
||||
<SolidColorBrush x:Key="TextMuted" Color="#62626E"/>
|
||||
<SolidColorBrush x:Key="TextSecondary" Color="#A9B0C5"/>
|
||||
<SolidColorBrush x:Key="TextMuted" Color="#7C8296"/>
|
||||
<SolidColorBrush x:Key="TrackBrush" Color="#262631"/>
|
||||
<SolidColorBrush x:Key="SuccessBrush" Color="#34D399"/>
|
||||
<SolidColorBrush x:Key="ErrorBrush" Color="#F87171"/>
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@
|
|||
<SolidColorBrush x:Key="BorderBrush" Color="#D5D9E1"/>
|
||||
<SolidColorBrush x:Key="BorderSubtle" Color="#E3E6EC"/>
|
||||
<SolidColorBrush x:Key="TextPrimary" Color="#1B1C24"/>
|
||||
<SolidColorBrush x:Key="TextSecondary" Color="#565A6A"/>
|
||||
<SolidColorBrush x:Key="TextMuted" Color="#8A8E9C"/>
|
||||
<SolidColorBrush x:Key="TextSecondary" Color="#4A4E5E"/>
|
||||
<SolidColorBrush x:Key="TextMuted" Color="#767B8C"/>
|
||||
<SolidColorBrush x:Key="TrackBrush" Color="#DBDFE6"/>
|
||||
<SolidColorBrush x:Key="SuccessBrush" Color="#15A34A"/>
|
||||
<SolidColorBrush x:Key="ErrorBrush" Color="#DC2626"/>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,23 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<!-- 폰트 (테마 무관) -->
|
||||
<FontFamily x:Key="UiFont">Segoe UI Variable Text, Segoe UI</FontFamily>
|
||||
<!-- 폰트 (테마 무관) — 한국어 폴백 포함(맑은 고딕) -->
|
||||
<FontFamily x:Key="UiFont">Segoe UI Variable Text, Segoe UI, Malgun Gothic</FontFamily>
|
||||
<FontFamily x:Key="IconFont">Segoe Fluent Icons</FontFamily>
|
||||
|
||||
<!-- 암시 스타일: 모든 텍스트가 UI 폰트로 통일(Foreground 는 상속 존중) -->
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="FontFamily" Value="{StaticResource UiFont}"/>
|
||||
</Style>
|
||||
<Style TargetType="ToolTip">
|
||||
<Setter Property="FontFamily" Value="{StaticResource UiFont}"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource TextPrimary}"/>
|
||||
<Setter Property="Background" Value="{StaticResource CardBg}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Padding" Value="10,6"/>
|
||||
</Style>
|
||||
|
||||
<!-- ===== Primary 버튼 ===== -->
|
||||
<Style x:Key="PrimaryButton" TargetType="Button">
|
||||
<Setter Property="FontFamily" Value="{StaticResource UiFont}"/>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue