сырой проект
This commit is contained in:
@@ -0,0 +1,15 @@
|
|||||||
|
<Application xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
x:Class="SpotXInstaller.App"
|
||||||
|
xmlns:local="using:SpotXInstaller"
|
||||||
|
RequestedThemeVariant="Default">
|
||||||
|
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||||
|
|
||||||
|
<Application.DataTemplates>
|
||||||
|
<local:ViewLocator />
|
||||||
|
</Application.DataTemplates>
|
||||||
|
|
||||||
|
<Application.Styles>
|
||||||
|
<FluentTheme />
|
||||||
|
</Application.Styles>
|
||||||
|
</Application>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using SpotXInstaller.ViewModels;
|
||||||
|
using SpotXInstaller.Views;
|
||||||
|
|
||||||
|
namespace SpotXInstaller;
|
||||||
|
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnFrameworkInitializationCompleted()
|
||||||
|
{
|
||||||
|
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
|
{
|
||||||
|
desktop.MainWindow = new MainWindow
|
||||||
|
{
|
||||||
|
DataContext = new MainViewModel(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnFrameworkInitializationCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,7 @@
|
|||||||
|
namespace SpotXInstaller.Models;
|
||||||
|
|
||||||
|
public sealed record InstallOptions(
|
||||||
|
bool BlockUpdates,
|
||||||
|
bool HidePodcasts,
|
||||||
|
bool HidePremium,
|
||||||
|
bool ClearCache);
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace SpotXInstaller;
|
||||||
|
|
||||||
|
sealed class Program
|
||||||
|
{
|
||||||
|
// Initialization code. Don't use any Avalonia, third-party APIs or any
|
||||||
|
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
||||||
|
// yet and stuff might break.
|
||||||
|
[STAThread]
|
||||||
|
public static void Main(string[] args) => BuildAvaloniaApp()
|
||||||
|
.StartWithClassicDesktopLifetime(args);
|
||||||
|
|
||||||
|
// Avalonia configuration, don't remove; also used by visual designer.
|
||||||
|
public static AppBuilder BuildAvaloniaApp()
|
||||||
|
=> AppBuilder.Configure<App>()
|
||||||
|
.UsePlatformDetect()
|
||||||
|
#if DEBUG
|
||||||
|
.WithDeveloperTools()
|
||||||
|
#endif
|
||||||
|
.WithInterFont()
|
||||||
|
.LogToTrace();
|
||||||
|
}
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using SpotXInstaller.Models;
|
||||||
|
|
||||||
|
namespace SpotXInstaller.Services;
|
||||||
|
|
||||||
|
public sealed class InstallerService
|
||||||
|
{
|
||||||
|
private const string BashUrl = "https://api.kirieska.xyz/files/SPOTX/spotx.sh";
|
||||||
|
private const string PsUrl = "https://api.kirieska.xyz/files/SPOTX/run.ps1";
|
||||||
|
private const string PsUninstallUrl = "https://api.kirieska.xyz/files/SPOTX/uninstall.ps1";
|
||||||
|
|
||||||
|
public Task<int> InstallAsync(InstallOptions o,
|
||||||
|
IProgress<(int Percent, string Status)> progress,
|
||||||
|
IProgress<string> log, CancellationToken ct = default)
|
||||||
|
=> RunAsync(o, isUninstall: false, progress, log, ct);
|
||||||
|
|
||||||
|
public Task<int> UninstallAsync(
|
||||||
|
IProgress<(int Percent, string Status)> progress,
|
||||||
|
IProgress<string> log, CancellationToken ct = default)
|
||||||
|
=> RunAsync(new InstallOptions(false, false, false, false),
|
||||||
|
isUninstall: true, progress, log, ct);
|
||||||
|
|
||||||
|
private async Task<int> RunAsync(InstallOptions o, bool isUninstall,
|
||||||
|
IProgress<(int, string)> progress, IProgress<string> log, CancellationToken ct)
|
||||||
|
{
|
||||||
|
bool isWin = OperatingSystem.IsWindows();
|
||||||
|
string url = isUninstall
|
||||||
|
? (isWin ? PsUninstallUrl : BashUrl)
|
||||||
|
: (isWin ? PsUrl : BashUrl);
|
||||||
|
string ext = isWin ? ".ps1" : ".sh";
|
||||||
|
string scriptPath = Path.Combine(Path.GetTempPath(), $"spotx_{Guid.NewGuid():N}{ext}");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// === ШАГ 1 (0–20%): скачивание ===
|
||||||
|
progress.Report((3, "Загрузка скрипта..."));
|
||||||
|
using (var http = new HttpClient { Timeout = TimeSpan.FromSeconds(60) })
|
||||||
|
{
|
||||||
|
http.DefaultRequestHeaders.UserAgent.ParseAdd("SpotXInstaller/1.0");
|
||||||
|
byte[] data = await http.GetByteArrayAsync(url, ct).ConfigureAwait(false);
|
||||||
|
await File.WriteAllBytesAsync(scriptPath, data, ct).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
progress.Report((20, "Скрипт загружен"));
|
||||||
|
|
||||||
|
if (!isWin)
|
||||||
|
{
|
||||||
|
progress.Report((25, "Установка прав на исполнение..."));
|
||||||
|
await ExecAsync("/bin/chmod", new[] { "+x", scriptPath }, _ => { }, ct)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
progress.Report((30, isUninstall ? "Восстановление..." : "Запуск установки..."));
|
||||||
|
|
||||||
|
// === ШАГ 2: маппинг флагов ===
|
||||||
|
string file;
|
||||||
|
var argv = new List<string>();
|
||||||
|
|
||||||
|
if (isWin)
|
||||||
|
{
|
||||||
|
file = "powershell.exe";
|
||||||
|
argv.AddRange(new[]
|
||||||
|
{
|
||||||
|
"-NoProfile", "-ExecutionPolicy", "Bypass",
|
||||||
|
"-File", scriptPath
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isUninstall)
|
||||||
|
{
|
||||||
|
// uninstall.ps1 не принимает флагов — просто отрабатывает
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (o.BlockUpdates) argv.Add("-block_update_on");
|
||||||
|
if (o.HidePodcasts) argv.Add("-podcasts_off");
|
||||||
|
if (o.HidePremium) argv.Add("-adsections_off");
|
||||||
|
argv.Add("-no_pause"); // не ждать Enter в конце
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
file = "/bin/bash";
|
||||||
|
argv.Add(scriptPath);
|
||||||
|
|
||||||
|
if (isUninstall)
|
||||||
|
{
|
||||||
|
argv.Add("--uninstall");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (o.BlockUpdates) argv.Add("-B");
|
||||||
|
if (o.HidePodcasts) argv.Add("-h");
|
||||||
|
if (o.HidePremium) argv.Add("-e");
|
||||||
|
if (o.ClearCache) argv.Add("-c");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// === ШАГ 3 (30–95%): запуск + построчный стрим ===
|
||||||
|
int cur = 30;
|
||||||
|
var gate = new object();
|
||||||
|
|
||||||
|
int exit = await ExecAsync(file, argv.ToArray(), line =>
|
||||||
|
{
|
||||||
|
log.Report(line);
|
||||||
|
lock (gate)
|
||||||
|
{
|
||||||
|
cur = Math.Min(cur + 2, 95);
|
||||||
|
progress.Report((cur, isUninstall ? "Восстановление..." : "Установка..."));
|
||||||
|
}
|
||||||
|
}, ct).ConfigureAwait(false);
|
||||||
|
|
||||||
|
progress.Report((100, "Готово"));
|
||||||
|
return exit;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try { if (File.Exists(scriptPath)) File.Delete(scriptPath); } catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Task<int> ExecAsync(string file, string[] argv,
|
||||||
|
Action<string> onLine, CancellationToken ct)
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
var psi = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = file,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true,
|
||||||
|
};
|
||||||
|
foreach (var a in argv) psi.ArgumentList.Add(a);
|
||||||
|
|
||||||
|
var proc = new Process { StartInfo = psi, EnableRaisingEvents = true };
|
||||||
|
proc.OutputDataReceived += (_, e) => { if (e.Data is { } s) onLine(s); };
|
||||||
|
proc.ErrorDataReceived += (_, e) => { if (e.Data is { } s) onLine(s); };
|
||||||
|
proc.Exited += (_, _) =>
|
||||||
|
{
|
||||||
|
try { tcs.TrySetResult(proc.ExitCode); }
|
||||||
|
finally { proc.Dispose(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
using var reg = ct.Register(() =>
|
||||||
|
{
|
||||||
|
try { if (!proc.HasExited) proc.Kill(entireProcessTree: true); }
|
||||||
|
catch { }
|
||||||
|
});
|
||||||
|
|
||||||
|
proc.Start();
|
||||||
|
proc.BeginOutputReadLine();
|
||||||
|
proc.BeginErrorReadLine();
|
||||||
|
return tcs.Task;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Models\" />
|
||||||
|
<AvaloniaResource Include="Assets\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Avalonia" Version="12.1.2" />
|
||||||
|
<PackageReference Include="Avalonia.Desktop" Version="12.1.2" />
|
||||||
|
<PackageReference Include="Avalonia.Themes.Fluent" Version="12.1.2" />
|
||||||
|
<PackageReference Include="Avalonia.Fonts.Inter" Version="12.1.2" />
|
||||||
|
<PackageReference Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3">
|
||||||
|
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
|
||||||
|
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Templates;
|
||||||
|
using SpotXInstaller.ViewModels;
|
||||||
|
|
||||||
|
namespace SpotXInstaller;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Given a view model, returns the corresponding view if possible.
|
||||||
|
/// </summary>
|
||||||
|
[RequiresUnreferencedCode(
|
||||||
|
"Default implementation of ViewLocator involves reflection which may be trimmed away.",
|
||||||
|
Url = "https://docs.avaloniaui.net/docs/concepts/view-locator")]
|
||||||
|
public class ViewLocator : IDataTemplate
|
||||||
|
{
|
||||||
|
public Control? Build(object? param)
|
||||||
|
{
|
||||||
|
if (param is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var name = param.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
|
||||||
|
var type = Type.GetType(name);
|
||||||
|
|
||||||
|
if (type != null)
|
||||||
|
{
|
||||||
|
return (Control)Activator.CreateInstance(type)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TextBlock { Text = "Not Found: " + name };
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Match(object? data)
|
||||||
|
{
|
||||||
|
return data is ViewModelBase;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using SpotXInstaller.Models;
|
||||||
|
using SpotXInstaller.Services;
|
||||||
|
|
||||||
|
namespace SpotXInstaller.ViewModels;
|
||||||
|
|
||||||
|
public partial class MainViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
private readonly InstallerService _installer = new();
|
||||||
|
private CancellationTokenSource? _cts;
|
||||||
|
|
||||||
|
// --- СОСТОЯНИЯ ЭКРАНОВ ---
|
||||||
|
[ObservableProperty] private bool _isStepLoading = true;
|
||||||
|
[ObservableProperty] private bool _isStepHome;
|
||||||
|
[ObservableProperty] private bool _isStepSettings;
|
||||||
|
[ObservableProperty] private bool _isStepProgress;
|
||||||
|
|
||||||
|
// --- POPUP ---
|
||||||
|
[ObservableProperty] private bool _showPopup;
|
||||||
|
[ObservableProperty] private string _popupTitle = "";
|
||||||
|
[ObservableProperty] private string _popupMessage = "";
|
||||||
|
[ObservableProperty] private string _popupColor = "#8B5CF6";
|
||||||
|
|
||||||
|
// --- ГАЛОЧКИ ---
|
||||||
|
[ObservableProperty] private bool _blockUpdates = true;
|
||||||
|
[ObservableProperty] private bool _hidePodcasts = true;
|
||||||
|
[ObservableProperty] private bool _hidePremium = true;
|
||||||
|
[ObservableProperty] private bool _clearCache;
|
||||||
|
|
||||||
|
// --- ПРОГРЕСС ---
|
||||||
|
[ObservableProperty] private double _progressValue;
|
||||||
|
[ObservableProperty] private string _progressStatus = "Подготовка...";
|
||||||
|
[ObservableProperty] private string _miniLog = "";
|
||||||
|
|
||||||
|
public MainViewModel() => _ = BootAsync();
|
||||||
|
|
||||||
|
private async Task BootAsync()
|
||||||
|
{
|
||||||
|
await Task.Delay(3000);
|
||||||
|
SwitchTo(home: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SwitchTo(bool loading = false, bool home = false, bool settings = false, bool progress = false)
|
||||||
|
{
|
||||||
|
IsStepLoading = loading;
|
||||||
|
IsStepHome = home;
|
||||||
|
IsStepSettings = settings;
|
||||||
|
IsStepProgress = progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand] private void GoToSettings() => SwitchTo(settings: true);
|
||||||
|
[RelayCommand] private void GoBackToHome() => SwitchTo(home: true);
|
||||||
|
[RelayCommand] private void ClosePopup() => ShowPopup = false;
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private async Task StartInstallAsync()
|
||||||
|
{
|
||||||
|
await RunInstallerAsync(isUninstall: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private async Task StartUninstallAsync()
|
||||||
|
{
|
||||||
|
await RunInstallerAsync(isUninstall: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RunInstallerAsync(bool isUninstall)
|
||||||
|
{
|
||||||
|
ProgressValue = 0;
|
||||||
|
MiniLog = "";
|
||||||
|
ProgressStatus = isUninstall ? "Подготовка восстановления..." : "Подготовка установки...";
|
||||||
|
SwitchTo(progress: true);
|
||||||
|
|
||||||
|
_cts?.Dispose();
|
||||||
|
_cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var progress = new Progress<(int Percent, string Status)>(p =>
|
||||||
|
{
|
||||||
|
ProgressValue = p.Percent;
|
||||||
|
ProgressStatus = p.Status;
|
||||||
|
});
|
||||||
|
var log = new Progress<string>(l => MiniLog = l);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int code = isUninstall
|
||||||
|
? await _installer.UninstallAsync(progress, log, _cts.Token)
|
||||||
|
: await _installer.InstallAsync(
|
||||||
|
new InstallOptions(BlockUpdates, HidePodcasts, HidePremium, ClearCache),
|
||||||
|
progress, log, _cts.Token);
|
||||||
|
|
||||||
|
SwitchTo(home: true);
|
||||||
|
|
||||||
|
if (code == 0)
|
||||||
|
ShowNotification(
|
||||||
|
isUninstall ? "ГОТОВО" : "УСПЕХ",
|
||||||
|
isUninstall
|
||||||
|
? "Оригинальный клиент Spotify восстановлен."
|
||||||
|
: "SpotX успешно установлен.",
|
||||||
|
isUninstall ? "#8B5CF6" : "#00E676");
|
||||||
|
else
|
||||||
|
ShowNotification("ОШИБКА",
|
||||||
|
$"Скрипт завершился с кодом {code}.\n\n{MiniLog[..Math.Min(200, MiniLog.Length)]}",
|
||||||
|
"#FF3B30");
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
SwitchTo(home: true);
|
||||||
|
ShowNotification("ОТМЕНЕНО", "Операция прервана.", "#FFB300");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
SwitchTo(home: true);
|
||||||
|
ShowNotification("ОШИБКА", ex.Message, "#FF3B30");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowNotification(string title, string message, string hexColor)
|
||||||
|
{
|
||||||
|
PopupTitle = title;
|
||||||
|
PopupMessage = message;
|
||||||
|
PopupColor = hexColor;
|
||||||
|
ShowPopup = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
namespace SpotXInstaller.ViewModels;
|
||||||
|
|
||||||
|
public abstract class ViewModelBase : ObservableObject
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,312 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:vm="using:SpotXInstaller.ViewModels"
|
||||||
|
x:Class="SpotXInstaller.Views.MainWindow"
|
||||||
|
x:DataType="vm:MainViewModel"
|
||||||
|
Icon="/Assets/avalonia-logo.ico"
|
||||||
|
Title="SpotX Installer"
|
||||||
|
Width="700" Height="500"
|
||||||
|
CanResize="False"
|
||||||
|
Background="#050505"
|
||||||
|
Foreground="White"
|
||||||
|
WindowStartupLocation="CenterScreen">
|
||||||
|
|
||||||
|
<Design.DataContext><vm:MainViewModel /></Design.DataContext>
|
||||||
|
|
||||||
|
<Window.Styles>
|
||||||
|
<!-- === ACCENT BUTTON === -->
|
||||||
|
<Style Selector="Button.accent">
|
||||||
|
<Setter Property="Background" Value="#8B5CF6"/>
|
||||||
|
<Setter Property="Foreground" Value="White"/>
|
||||||
|
<Setter Property="CornerRadius" Value="10"/>
|
||||||
|
<Setter Property="Padding" Value="24,16"/>
|
||||||
|
<Setter Property="FontWeight" Value="Bold"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.accent:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="#A78BFA"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.accent:pressed /template/ ContentPresenter#PART_ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="#7C3AED"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- === GHOST BUTTON === -->
|
||||||
|
<Style Selector="Button.ghost">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
<Setter Property="Foreground" Value="#B8B8B8"/>
|
||||||
|
<Setter Property="BorderBrush" Value="#26262B"/>
|
||||||
|
<Setter Property="BorderThickness" Value="1"/>
|
||||||
|
<Setter Property="CornerRadius" Value="10"/>
|
||||||
|
<Setter Property="Padding" Value="20,14"/>
|
||||||
|
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||||
|
<Setter Property="FontSize" Value="13"/>
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.ghost:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="#101014"/>
|
||||||
|
<Setter Property="BorderBrush" Value="#8B5CF6"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- === WHITE BUTTON === -->
|
||||||
|
<Style Selector="Button.white">
|
||||||
|
<Setter Property="Background" Value="White"/>
|
||||||
|
<Setter Property="Foreground" Value="Black"/>
|
||||||
|
<Setter Property="CornerRadius" Value="10"/>
|
||||||
|
<Setter Property="Padding" Value="34,16"/>
|
||||||
|
<Setter Property="FontWeight" Value="Black"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.white:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="#E6E6E6"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- === BACK BUTTON === -->
|
||||||
|
<Style Selector="Button.back">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
<Setter Property="Foreground" Value="#8A8A8A"/>
|
||||||
|
<Setter Property="Padding" Value="10,6"/>
|
||||||
|
<Setter Property="FontSize" Value="13"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.back:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="#101014"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- === PREMIUM CHECKBOX === -->
|
||||||
|
<Style Selector="CheckBox.accent">
|
||||||
|
<Setter Property="Foreground" Value="#DDDDDD"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Grid ColumnDefinitions="Auto,*" Background="Transparent">
|
||||||
|
<Border Name="Box" Width="20" Height="20" CornerRadius="6"
|
||||||
|
BorderBrush="#2A2A2E" BorderThickness="1.5" Background="#0D0D10">
|
||||||
|
<Path Name="Check"
|
||||||
|
Data="M 5,10 L 9,14 L 15,6"
|
||||||
|
Stroke="White" StrokeThickness="2"
|
||||||
|
StrokeLineCap="Round" StrokeJoin="Round"
|
||||||
|
IsVisible="False"/>
|
||||||
|
</Border>
|
||||||
|
<ContentPresenter Name="PART_ContentPresenter"
|
||||||
|
Grid.Column="1" Margin="14,0,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Content="{TemplateBinding Content}"
|
||||||
|
ContentTemplate="{TemplateBinding ContentTemplate}"/>
|
||||||
|
</Grid>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="CheckBox.accent:checked /template/ Border#Box">
|
||||||
|
<Setter Property="Background" Value="#8B5CF6"/>
|
||||||
|
<Setter Property="BorderBrush" Value="#8B5CF6"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="CheckBox.accent:checked /template/ Path#Check">
|
||||||
|
<Setter Property="IsVisible" Value="True"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="CheckBox.accent:pointerover /template/ Border#Box">
|
||||||
|
<Setter Property="BorderBrush" Value="#8B5CF6"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- === PROGRESSBAR === -->
|
||||||
|
<Style Selector="ProgressBar.accent">
|
||||||
|
<Setter Property="Height" Value="6"/>
|
||||||
|
<Setter Property="CornerRadius" Value="3"/>
|
||||||
|
<Setter Property="Background" Value="#141418"/>
|
||||||
|
<Setter Property="Foreground" Value="#8B5CF6"/>
|
||||||
|
</Style>
|
||||||
|
</Window.Styles>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<!-- 1. SPLASH -->
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<Grid IsVisible="{Binding IsStepLoading}" Background="#050507" ZIndex="10">
|
||||||
|
<Grid Width="500" Height="260" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||||
|
|
||||||
|
<Border Width="390" Height="110" CornerRadius="55" Background="#8B5CF6" Opacity="0.08"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||||
|
<Border.Styles>
|
||||||
|
<Style Selector="Border">
|
||||||
|
<Style.Animations>
|
||||||
|
<Animation Duration="0:0:2" IterationCount="Infinite">
|
||||||
|
<KeyFrame Cue="0%"><Setter Property="Opacity" Value="0.05"/></KeyFrame>
|
||||||
|
<KeyFrame Cue="50%"><Setter Property="Opacity" Value="0.15"/></KeyFrame>
|
||||||
|
<KeyFrame Cue="100%"><Setter Property="Opacity" Value="0.05"/></KeyFrame>
|
||||||
|
</Animation>
|
||||||
|
</Style.Animations>
|
||||||
|
</Style>
|
||||||
|
</Border.Styles>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||||
|
<TextBlock Text="KIRIESKA" FontSize="60" FontWeight="Black" Foreground="#18181D"/>
|
||||||
|
<Border HorizontalAlignment="Left" ClipToBounds="True">
|
||||||
|
<Border.Styles>
|
||||||
|
<Style Selector="Border">
|
||||||
|
<Style.Animations>
|
||||||
|
<Animation Duration="0:0:2.5" FillMode="Forward">
|
||||||
|
<KeyFrame Cue="0%"><Setter Property="Width" Value="0"/></KeyFrame>
|
||||||
|
<KeyFrame Cue="100%"><Setter Property="Width" Value="400"/></KeyFrame>
|
||||||
|
</Animation>
|
||||||
|
</Style.Animations>
|
||||||
|
</Style>
|
||||||
|
</Border.Styles>
|
||||||
|
<TextBlock Text="KIRIESKA" FontSize="60" FontWeight="Black" Foreground="#A78BFA"/>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<TextBlock Text="INITIALIZING SYSTEM" FontSize="10" FontWeight="SemiBold"
|
||||||
|
Foreground="#666670" LetterSpacing="3"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,48"/>
|
||||||
|
|
||||||
|
<Border Width="280" Height="2" Background="#18181D" CornerRadius="1"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,30">
|
||||||
|
<Border Height="2" Background="#8B5CF6" CornerRadius="1" HorizontalAlignment="Left">
|
||||||
|
<Border.Styles>
|
||||||
|
<Style Selector="Border">
|
||||||
|
<Style.Animations>
|
||||||
|
<Animation Duration="0:0:2.5" FillMode="Forward">
|
||||||
|
<KeyFrame Cue="0%"><Setter Property="Width" Value="0"/></KeyFrame>
|
||||||
|
<KeyFrame Cue="100%"><Setter Property="Width" Value="280"/></KeyFrame>
|
||||||
|
</Animation>
|
||||||
|
</Style.Animations>
|
||||||
|
</Style>
|
||||||
|
</Border.Styles>
|
||||||
|
</Border>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Ellipse Width="5" Height="5" Fill="#8B5CF6" HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Bottom" Margin="0,0,0,18">
|
||||||
|
<Ellipse.Styles>
|
||||||
|
<Style Selector="Ellipse">
|
||||||
|
<Style.Animations>
|
||||||
|
<Animation Duration="0:0:1" IterationCount="Infinite">
|
||||||
|
<KeyFrame Cue="0%"><Setter Property="Opacity" Value="0.2"/></KeyFrame>
|
||||||
|
<KeyFrame Cue="50%"><Setter Property="Opacity" Value="1"/></KeyFrame>
|
||||||
|
<KeyFrame Cue="100%"><Setter Property="Opacity" Value="0.2"/></KeyFrame>
|
||||||
|
</Animation>
|
||||||
|
</Style.Animations>
|
||||||
|
</Style>
|
||||||
|
</Ellipse.Styles>
|
||||||
|
</Ellipse>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<!-- 2. HOME -->
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<Grid IsVisible="{Binding IsStepHome}" Margin="40">
|
||||||
|
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Width="340" Spacing="14">
|
||||||
|
<TextBlock Text="SpotX Installer" FontSize="38" FontWeight="Black"
|
||||||
|
HorizontalAlignment="Center" LetterSpacing="-0.5"/>
|
||||||
|
<TextBlock Text="Продвинутый патч для десктопного Spotify."
|
||||||
|
FontSize="14" Foreground="#666666"
|
||||||
|
HorizontalAlignment="Center" Margin="0,0,0,36"/>
|
||||||
|
|
||||||
|
<Button Content="НАЧАТЬ УСТАНОВКУ"
|
||||||
|
Command="{Binding GoToSettingsCommand}"
|
||||||
|
Classes="accent"
|
||||||
|
HorizontalAlignment="Stretch"/>
|
||||||
|
|
||||||
|
<Button Content="УДАЛИТЬ / ВОССТАНОВИТЬ"
|
||||||
|
Command="{Binding StartUninstallCommand}"
|
||||||
|
Classes="ghost"
|
||||||
|
HorizontalAlignment="Stretch"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<!-- 3. SETTINGS -->
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<Grid IsVisible="{Binding IsStepSettings}" Margin="40">
|
||||||
|
<Button Content="← Назад" Command="{Binding GoBackToHomeCommand}"
|
||||||
|
Classes="back"
|
||||||
|
HorizontalAlignment="Left" VerticalAlignment="Top"/>
|
||||||
|
|
||||||
|
<StackPanel Margin="0,70,0,0" Spacing="4">
|
||||||
|
<TextBlock Text="Параметры" FontSize="30" FontWeight="Bold"/>
|
||||||
|
<TextBlock Text="Выберите, что изменить в Spotify"
|
||||||
|
FontSize="13" Foreground="#666666" Margin="0,4,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Spacing="18" Margin="0,150,0,0">
|
||||||
|
<CheckBox Classes="accent" Content="Блокировать автоматические обновления Spotify"
|
||||||
|
IsChecked="{Binding BlockUpdates}"/>
|
||||||
|
<CheckBox Classes="accent" Content="Скрыть раздел с подкастами"
|
||||||
|
IsChecked="{Binding HidePodcasts}"/>
|
||||||
|
<CheckBox Classes="accent" Content="Удалить кнопки покупки Premium"
|
||||||
|
IsChecked="{Binding HidePremium}"/>
|
||||||
|
<CheckBox Classes="accent" Content="Очистить кэш перед установкой"
|
||||||
|
IsChecked="{Binding ClearCache}"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<Button Content="УСТАНОВИТЬ" Command="{Binding StartInstallCommand}"
|
||||||
|
Classes="white"
|
||||||
|
HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<!-- 4. PROGRESS -->
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<Grid IsVisible="{Binding IsStepProgress}" Background="#050505" ZIndex="5">
|
||||||
|
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||||
|
Width="480" Spacing="26">
|
||||||
|
|
||||||
|
<TextBlock Text="УСТАНОВКА" FontSize="11" FontWeight="Bold"
|
||||||
|
Foreground="#8B5CF6" LetterSpacing="4"
|
||||||
|
HorizontalAlignment="Center"/>
|
||||||
|
|
||||||
|
<TextBlock Text="{Binding ProgressStatus}" FontSize="22" FontWeight="Bold"
|
||||||
|
HorizontalAlignment="Center" TextAlignment="Center"
|
||||||
|
TextWrapping="Wrap"/>
|
||||||
|
|
||||||
|
<ProgressBar Value="{Binding ProgressValue}"
|
||||||
|
Minimum="0" Maximum="100"
|
||||||
|
Classes="accent" Width="480"/>
|
||||||
|
|
||||||
|
<TextBlock Text="{Binding ProgressValue, StringFormat='{}{0:F0}%'}"
|
||||||
|
FontSize="12" Foreground="#666666"
|
||||||
|
HorizontalAlignment="Center"/>
|
||||||
|
|
||||||
|
<Border Background="#0A0A0C" BorderBrush="#141418" BorderThickness="1"
|
||||||
|
CornerRadius="10" Padding="16,12" Width="480" MinHeight="42">
|
||||||
|
<TextBlock Text="{Binding MiniLog}"
|
||||||
|
FontFamily="Consolas,Menlo,DejaVu Sans Mono,monospace"
|
||||||
|
FontSize="11" Foreground="#888888"
|
||||||
|
TextTrimming="CharacterEllipsis" TextWrapping="NoWrap"/>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<!-- 5. POPUP -->
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<Grid IsVisible="{Binding ShowPopup}" Background="#D9000000" ZIndex="20">
|
||||||
|
<Border Width="420" Padding="32" Background="#101014"
|
||||||
|
BorderBrush="#22222A" BorderThickness="1"
|
||||||
|
CornerRadius="16"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||||
|
<StackPanel Spacing="14">
|
||||||
|
<TextBlock Text="{Binding PopupTitle}"
|
||||||
|
Foreground="{Binding PopupColor}"
|
||||||
|
FontSize="18" FontWeight="Black" LetterSpacing="1"/>
|
||||||
|
|
||||||
|
<TextBlock Text="{Binding PopupMessage}"
|
||||||
|
Foreground="#CCCCCC" FontSize="14"
|
||||||
|
TextWrapping="Wrap" LineHeight="20"/>
|
||||||
|
|
||||||
|
<Button Content="ОК" Command="{Binding ClosePopupCommand}"
|
||||||
|
Classes="accent" Padding="28,10" FontSize="13"
|
||||||
|
HorizontalAlignment="Right" Margin="0,8,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace SpotXInstaller.Views;
|
||||||
|
|
||||||
|
public partial class MainWindow : Window
|
||||||
|
{
|
||||||
|
public MainWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:vm="using:SpotXInstaller.ViewModels"
|
||||||
|
x:Class="SpotXInstaller.Views.MainWindow"
|
||||||
|
x:DataType="vm:MainViewModel"
|
||||||
|
Icon="/Assets/avalonia-logo.ico"
|
||||||
|
Title="SpotX Installer"
|
||||||
|
Width="700"
|
||||||
|
Height="500"
|
||||||
|
CanResize="False"
|
||||||
|
Background="#050505"
|
||||||
|
Foreground="White"
|
||||||
|
WindowStartupLocation="CenterScreen">
|
||||||
|
|
||||||
|
<Design.DataContext>
|
||||||
|
<vm:MainViewModel />
|
||||||
|
</Design.DataContext>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<!-- 1. LOADING SCREEN -->
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<Grid IsVisible="{Binding IsStepLoading}" Background="#050507" ZIndex="10">
|
||||||
|
<Grid Width="500" Height="260" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||||
|
|
||||||
|
<!-- Glow (Свечение на фоне) -->
|
||||||
|
<Border Width="390" Height="110" CornerRadius="55" Background="#8B5CF6" Opacity="0.08"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||||
|
<Border.Styles>
|
||||||
|
<Style Selector="Border">
|
||||||
|
<Style.Animations>
|
||||||
|
<Animation Duration="0:0:2" IterationCount="Infinite">
|
||||||
|
<KeyFrame Cue="0%"><Setter Property="Opacity" Value="0.05" /></KeyFrame>
|
||||||
|
<KeyFrame Cue="50%"><Setter Property="Opacity" Value="0.15" /></KeyFrame>
|
||||||
|
<KeyFrame Cue="100%"><Setter Property="Opacity" Value="0.05" /></KeyFrame>
|
||||||
|
</Animation>
|
||||||
|
</Style.Animations>
|
||||||
|
</Style>
|
||||||
|
</Border.Styles>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- ========================================================== -->
|
||||||
|
<!-- ИДЕАЛЬНОЕ НАЛОЖЕНИЕ ТЕКСТА (БЕЗ МАРДЖИНОВ И СМЕЩЕНИЙ) -->
|
||||||
|
<!-- ========================================================== -->
|
||||||
|
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||||
|
<!-- Background text (Темный) -->
|
||||||
|
<TextBlock Text="KIRIESKA" FontSize="60" FontWeight="Black" Foreground="#18181D" />
|
||||||
|
|
||||||
|
<!-- Animated text (Фиолетовый) -->
|
||||||
|
<!-- Он лежит в том же Grid, поэтому координаты букв совпадут на 100% -->
|
||||||
|
<Border HorizontalAlignment="Left" ClipToBounds="True">
|
||||||
|
<Border.Styles>
|
||||||
|
<Style Selector="Border">
|
||||||
|
<Style.Animations>
|
||||||
|
<Animation Duration="0:0:2.5" FillMode="Forward">
|
||||||
|
<KeyFrame Cue="0%"><Setter Property="Width" Value="0" /></KeyFrame>
|
||||||
|
<!-- Ставлю 400, чтобы ширины анимации точно хватило на всё слово на любом шрифте -->
|
||||||
|
<KeyFrame Cue="100%"><Setter Property="Width" Value="400" /></KeyFrame>
|
||||||
|
</Animation>
|
||||||
|
</Style.Animations>
|
||||||
|
</Style>
|
||||||
|
</Border.Styles>
|
||||||
|
<TextBlock Text="KIRIESKA" FontSize="60" FontWeight="Black" Foreground="#A78BFA" />
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<!-- Status -->
|
||||||
|
<TextBlock Text="INITIALIZING SYSTEM" FontSize="10" FontWeight="SemiBold" Foreground="#666670"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,48" />
|
||||||
|
|
||||||
|
<!-- Progress background -->
|
||||||
|
<Border Width="280" Height="2" Background="#18181D" CornerRadius="1"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,30">
|
||||||
|
<!-- Progress -->
|
||||||
|
<Border Height="2" Background="#8B5CF6" CornerRadius="1" HorizontalAlignment="Left">
|
||||||
|
<Border.Styles>
|
||||||
|
<Style Selector="Border">
|
||||||
|
<Style.Animations>
|
||||||
|
<Animation Duration="0:0:2.5" FillMode="Forward">
|
||||||
|
<KeyFrame Cue="0%"><Setter Property="Width" Value="0" /></KeyFrame>
|
||||||
|
<KeyFrame Cue="100%"><Setter Property="Width" Value="280" /></KeyFrame>
|
||||||
|
</Animation>
|
||||||
|
</Style.Animations>
|
||||||
|
</Style>
|
||||||
|
</Border.Styles>
|
||||||
|
</Border>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Loading dot -->
|
||||||
|
<Ellipse Width="5" Height="5" Fill="#8B5CF6" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,18">
|
||||||
|
<Ellipse.Styles>
|
||||||
|
<Style Selector="Ellipse">
|
||||||
|
<Style.Animations>
|
||||||
|
<Animation Duration="0:0:1" IterationCount="Infinite">
|
||||||
|
<KeyFrame Cue="0%"><Setter Property="Opacity" Value="0.2" /></KeyFrame>
|
||||||
|
<KeyFrame Cue="50%"><Setter Property="Opacity" Value="1" /></KeyFrame>
|
||||||
|
<KeyFrame Cue="100%"><Setter Property="Opacity" Value="0.2" /></KeyFrame>
|
||||||
|
</Animation>
|
||||||
|
</Style.Animations>
|
||||||
|
</Style>
|
||||||
|
</Ellipse.Styles>
|
||||||
|
</Ellipse>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<!-- 2. HOME -->
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<Grid IsVisible="{Binding IsStepHome}" Margin="40">
|
||||||
|
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Width="320" Spacing="12">
|
||||||
|
<TextBlock Text="SpotX Installer" FontSize="36" FontWeight="Black" HorizontalAlignment="Center" />
|
||||||
|
<TextBlock Text="Продвинутый патч для десктопного Spotify." FontSize="14" Foreground="#666666"
|
||||||
|
HorizontalAlignment="Center" Margin="0,0,0,30" />
|
||||||
|
|
||||||
|
<Button Content="НАЧАТЬ УСТАНОВКУ" Command="{Binding GoToSettingsCommand}"
|
||||||
|
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
|
||||||
|
Background="#8B5CF6" Foreground="White" FontWeight="Bold" FontSize="14" Padding="15" />
|
||||||
|
|
||||||
|
<Button Content="УДАЛИТЬ / ВОССТАНОВИТЬ" Command="{Binding StartUninstallCommand}"
|
||||||
|
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
|
||||||
|
Background="Transparent" Foreground="#A0A0A0" BorderBrush="#333333" BorderThickness="1"
|
||||||
|
FontWeight="Bold" FontSize="12" Padding="12" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<!-- 3. SETTINGS -->
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<Grid IsVisible="{Binding IsStepSettings}" Margin="40">
|
||||||
|
<Button Content="← Назад" Command="{Binding GoBackToHomeCommand}" Background="Transparent"
|
||||||
|
Foreground="#777777" HorizontalAlignment="Left" VerticalAlignment="Top" />
|
||||||
|
|
||||||
|
<TextBlock Text="Параметры" FontSize="28" FontWeight="Bold" Margin="0,45,0,0" />
|
||||||
|
|
||||||
|
<StackPanel Spacing="15" Margin="0,105,0,0">
|
||||||
|
<CheckBox Content="Блокировать автоматические обновления Spotify" IsChecked="{Binding BlockUpdates}" FontSize="14" />
|
||||||
|
<CheckBox Content="Скрыть раздел с подкастами" IsChecked="{Binding HidePodcasts}" FontSize="14" />
|
||||||
|
<CheckBox Content="Удалить кнопки покупки Premium" IsChecked="{Binding HidePremium}" FontSize="14" />
|
||||||
|
<CheckBox Content="Очистить кэш перед установкой" IsChecked="{Binding ClearCache}" FontSize="14" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<Button Content="УСТАНОВИТЬ" Command="{Binding StartInstallCommand}" HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Bottom" Background="White" Foreground="Black" FontWeight="Black"
|
||||||
|
FontSize="14" Padding="30,15" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<!-- 4. INSTALLATION PROGRESS -->
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<Grid IsVisible="{Binding IsStepProgress}" Background="#050505" ZIndex="5">
|
||||||
|
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="20">
|
||||||
|
<ProgressBar Width="200" Height="4" IsIndeterminate="True" Foreground="#8B5CF6" Background="#18181D"/>
|
||||||
|
<TextBlock Text="Выполнение скриптов..." Foreground="#888888" FontSize="14" HorizontalAlignment="Center" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<!-- 5. POPUP NOTIFICATION -->
|
||||||
|
<!-- ================================================= -->
|
||||||
|
<Grid IsVisible="{Binding ShowPopup}" Background="#D9000000" ZIndex="20">
|
||||||
|
<Border Width="400" Padding="30" Background="#111111" BorderBrush="#2A2A2A" BorderThickness="1"
|
||||||
|
CornerRadius="12" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="{Binding PopupTitle}" Foreground="{Binding PopupColor}" FontSize="18"
|
||||||
|
FontWeight="Black" Margin="0,0,0,10" />
|
||||||
|
|
||||||
|
<TextBlock Text="{Binding PopupMessage}" Foreground="#CCCCCC" FontSize="14"
|
||||||
|
TextWrapping="Wrap" Margin="0,0,0,25" />
|
||||||
|
|
||||||
|
<Button Content="ОК" Command="{Binding ClosePopupCommand}" HorizontalAlignment="Right"
|
||||||
|
Background="#333333" Foreground="White" FontWeight="Bold" Padding="25,10" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<!-- This manifest is used on Windows only.
|
||||||
|
Don't remove it as it might cause problems with window transparency and embedded controls.
|
||||||
|
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="SpotXInstaller.Desktop"/>
|
||||||
|
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!-- A list of the Windows versions that this application has been tested on
|
||||||
|
and is designed to work with. Uncomment the appropriate elements
|
||||||
|
and Windows will automatically select the most compatible environment. -->
|
||||||
|
|
||||||
|
<!-- Windows 10 -->
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
</assembly>
|
||||||
Reference in New Issue
Block a user