// Preview-only renderer. Uses real WPF controls and synthetic data; no app settings are loaded. using System; using System.Globalization; using System.IO; using System.Reflection; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Markup; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Threading; using SbLauncher; public static class RenderLightThemes { sealed class Variant { public string Id, Title, Description; public string[] Background; public string Colors; public Variant(string id, string title, string description, string[] background, string colors) { Id = id; Title = title; Description = description; Background = background; Colors = colors; } } static readonly Variant[] Variants = { new Variant("01-silver", "01 Серебро", "Нейтральный фон · графитовый акцент", new[] { "#F3F4F6", "#E8EBEF", "#EFF1F4" }, "Panel=#EBF8F9FB;Card=#FFFFFFFF;CardHover=#FFF7F9FC;CardSel=#FFE3EAF3;Stroke=#FFCDD3DC;GlassEdge=#FFD5DAE2;Text=#242B35;Muted=#606C7B;Accent=#405B80;AccentHover=#4E6D96;AccentDim=#344C6E;OnAccent=#FFFFFF;SoftBg=#FFE9EDF2;SoftHover=#FFF6F8FB;SwitchTrack=#C5CDD7;SwitchThumb=#FFFFFF;BadgeBg=#E0E6EE;PingBg=#EDF1F5;LogBg=#F8F9FB;LogText=#49586B;ScrollTrack=#18758499;ScrollThumb=#8895A6;ScrollThumbHover=#687A90;HeroRing=#905F7591;HeroRingDim=#407D8FA6;GlowA=#127689A2;GlowB=#108398B4"), new Variant("02-frost", "02 Лёд", "Холодное стекло · синий акцент", new[] { "#EDF5FF", "#DDEAF9", "#EAF0FD" }, "Panel=#D9F9FCFF;Card=#EFFFFFFF;CardHover=#FFFFFFFF;CardSel=#FFDCEAFF;Stroke=#FFC4D3E6;GlassEdge=#FFE8F0FA;Text=#1F3351;Muted=#536A86;Accent=#2E6CCC;AccentHover=#3A7CE1;AccentDim=#245ABD;OnAccent=#FFFFFF;SoftBg=#DBF7FBFF;SoftHover=#FFFFFFFF;SwitchTrack=#A9BED8;SwitchThumb=#FFFFFF;BadgeBg=#DCE8F8;PingBg=#F4F8FE;LogBg=#EAF9FCFF;LogText=#405E82;ScrollTrack=#207A9BCA;ScrollThumb=#829CBD;ScrollThumbHover=#577CA8;HeroRing=#AC6593D3;HeroRingDim=#406C99D8;GlowA=#387FAFEE;GlowB=#247F91D6"), new Variant("03-linen", "03 Лён", "Тёплый молочный фон · бронзовый акцент", new[] { "#F7F3EC", "#EDE5D9", "#F5EFE6" }, "Panel=#EDFBF8F2;Card=#FFFFFDFA;CardHover=#FFFFFFFF;CardSel=#FFECE0CF;Stroke=#FFD9CEBE;GlassEdge=#FFE4DACD;Text=#352F28;Muted=#746858;Accent=#89603B;AccentHover=#9A7049;AccentDim=#714D2C;OnAccent=#FFFFFF;SoftBg=#FFF0E9DF;SoftHover=#FFFFFCF7;SwitchTrack=#CBBEAC;SwitchThumb=#FFFFFF;BadgeBg=#EDE2D2;PingBg=#F5F0E8;LogBg=#FCFAF5;LogText=#6B5A46;ScrollTrack=#209A866C;ScrollThumb=#A69680;ScrollThumbHover=#857359;HeroRing=#9CA18765;HeroRingDim=#40AE9370;GlowA=#209E875F;GlowB=#16C8AE80"), new Variant("04-sage", "04 Шалфей", "Серо-зелёный фон · хвойный акцент", new[] { "#F0F5F1", "#DFE9E2", "#EBF1E9" }, "Panel=#E8F7FBF7;Card=#FFFDFEFC;CardHover=#FFFFFFFF;CardSel=#FFDDECE2;Stroke=#FFC5D4CB;GlassEdge=#FFD5E1D7;Text=#25372F;Muted=#5C7064;Accent=#37715B;AccentHover=#46846C;AccentDim=#295C49;OnAccent=#FFFFFF;SoftBg=#FFE5EEE7;SoftHover=#FFF6FCF7;SwitchTrack=#B5C9BC;SwitchThumb=#FFFFFF;BadgeBg=#DCE9DF;PingBg=#EDF4EE;LogBg=#F7FBF7;LogText=#486757;ScrollTrack=#20829A8A;ScrollThumb=#869E8E;ScrollThumbHover=#608570;HeroRing=#9C729B83;HeroRingDim=#407FA78D;GlowA=#207DAF93;GlowB=#2084A26C") }; [STAThread] public static int Main(string[] args) { try { string source = Path.GetFullPath(args[0]); string output = Path.GetFullPath(args[1]); Directory.CreateDirectory(output); var app = new Application { ShutdownMode = ShutdownMode.OnExplicitShutdown }; var screenshots = new BitmapSource[Variants.Length]; for (int i = 0; i < Variants.Length; i++) { Variant variant = Variants[i]; var window = (Window)XamlReader.Parse(File.ReadAllText(Path.Combine(source, "Ui.xaml"))); var palette = (ResourceDictionary)window.Resources["Theme.Light"]; var background = new LinearGradientBrush { StartPoint = new Point(0, 0), EndPoint = new Point(1, 1) }; for (int j = 0; j < 3; j++) background.GradientStops.Add(new GradientStop(Color(variant.Background[j]), j * 0.5)); palette["Bg"] = background; foreach (string token in variant.Colors.Split(';')) { string[] pair = token.Split('='); if (!palette.Contains(pair[0])) throw new InvalidOperationException("Unknown token: " + pair[0]); if (pair[0].StartsWith("Glow")) { Color center = Color(pair[1]); palette[pair[0]] = new RadialGradientBrush(center, System.Windows.Media.Color.FromArgb(0, center.R, center.G, center.B)); } else palette[pair[0]] = new SolidColorBrush(Color(pair[1])); } palette["Ok"] = new SolidColorBrush(Color("#207553")); palette["Warn"] = new SolidColorBrush(Color("#95620D")); palette["Bad"] = new SolidColorBrush(Color("#BD3848")); palette["Idle"] = palette["Muted"]; palette["Na"] = palette["Muted"]; palette["ShadowOpacity"] = 0.10; File.WriteAllText(Path.Combine(output, variant.Id + ".xaml"), XamlWriter.Save(palette)); MainVm vm = CreateDemo(); window.DataContext = vm; vm.ThemeApplier = delegate(string theme) { Theme.Apply(window, theme); }; vm.SetTheme(Theme.Light); ((Image)window.FindName("LogoImg")).Source = new BitmapImage(new Uri(Path.Combine(source, "logo.png"))); var root = (FrameworkElement)window.Content; root.Measure(new Size(window.Width, window.Height)); root.Arrange(new Rect(0, 0, window.Width, window.Height)); root.UpdateLayout(); Pump(); root.UpdateLayout(); Call("UpdateSwitch", window.FindName("WireguardSwitch"), false); Call("MoveServerSelection", window.FindName("ServerList"), window.FindName("ServerListHost"), window.FindName("ServerSelectionWindow"), false); vm.ShowSettings = true; Call("UpdateTabs", window, vm, false); root.UpdateLayout(); screenshots[i] = Render(root, 1120, 790, 1.5); Save(screenshots[i], Path.Combine(output, variant.Id + ".png")); vm.ShowSettings = false; Call("UpdateTabs", window, vm, false); root.UpdateLayout(); Save(Render(root, 1120, 790, 1.5), Path.Combine(output, variant.Id + "-log.png")); vm.DisposeTimers(); window.Close(); Console.WriteLine("Rendered " + variant.Id); } Board(screenshots, Path.Combine(output, "comparison.png")); app.Shutdown(); return 0; } catch (Exception ex) { Console.Error.WriteLine(ex); return 1; } } static MainVm CreateDemo() { var vm = new MainVm(); string[] codes = { "FI", "EE", "DE" }; string[] titles = { "Finland", "Estonia", "Germany" }; string[] pings = { "28 ms", "41 ms", "96 ms" }; for (int i = 0; i < 3; i++) { string host = codes[i].ToLowerInvariant() + ".example.com"; vm.Servers.Add(new ServerVm { Code = codes[i], Title = titles[i], Subtitle = "VLESS · " + host + ":443", PingText = pings[i], PingKey = i == 2 ? "Warn" : "Ok", Cfg = new ServerCfg { Code = codes[i], Name = titles[i], Server = host, Port = 443, Protocol = "vless" } }); } vm.Servers.Add(new ServerVm { Code = "RU", Title = "Только WireGuard", IsWgOnly = true, Subtitle = "выход внутри РФ · 198.51.100.10", PingText = "н/д", PingKey = "Na" }); vm.Selected = vm.Servers[0]; vm.LogText = "12:00:00 демонстрация интерфейса — sing-box не запускался"; return vm; } static void Call(string method, params object[] args) { typeof(SbLauncher.App).GetMethod(method, BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, args); } static void Pump() { var frame = new DispatcherFrame(); Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(delegate { frame.Continue = false; })); Dispatcher.PushFrame(frame); } static Color Color(string hex) { return (Color)ColorConverter.ConvertFromString(hex); } static BitmapSource Render(Visual visual, int width, int height, double scale) { var bitmap = new RenderTargetBitmap((int)(width * scale), (int)(height * scale), 96 * scale, 96 * scale, PixelFormats.Pbgra32); bitmap.Render(visual); bitmap.Freeze(); return bitmap; } static void Save(BitmapSource bitmap, string path) { var encoder = new PngBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(bitmap)); using (var stream = File.Create(path)) encoder.Save(stream); } static void Board(BitmapSource[] screenshots, string path) { var visual = new DrawingVisual(); RenderOptions.SetBitmapScalingMode(visual, BitmapScalingMode.HighQuality); using (DrawingContext dc = visual.RenderOpen()) { dc.DrawRectangle(new SolidColorBrush(Color("#E5E7EB")), null, new Rect(0, 0, 2336, 1840)); for (int i = 0; i < screenshots.Length; i++) { double x = 32 + (i % 2) * 1152; double y = 28 + (i / 2) * 902; DrawText(dc, Variants[i].Title, x, y, 30, "#29323D", true); DrawText(dc, Variants[i].Description, x, y + 43, 21, "#566373", false); dc.DrawImage(screenshots[i], new Rect(x, y + 83, 1120, 790)); } } Save(Render(visual, 2336, 1840, 1), path); } static void DrawText(DrawingContext dc, string text, double x, double y, double size, string color, bool bold) { var face = new Typeface(new FontFamily("Segoe UI"), FontStyles.Normal, bold ? FontWeights.SemiBold : FontWeights.Normal, FontStretches.Normal); dc.DrawText(new FormattedText(text, CultureInfo.GetCultureInfo("ru-RU"), FlowDirection.LeftToRight, face, size, new SolidColorBrush(Color(color))), new Point(x, y)); } }