Ionite
1 year ago
committed by
GitHub
33 changed files with 801 additions and 92 deletions
@ -0,0 +1,43 @@
|
||||
using StabilityMatrix.Helper; |
||||
|
||||
namespace StabilityMatrix.Tests.Helper; |
||||
|
||||
[TestClass] |
||||
public class EventManagerTests |
||||
{ |
||||
private EventManager eventManager; |
||||
|
||||
[TestInitialize] |
||||
public void TestInitialize() |
||||
{ |
||||
eventManager = EventManager.Instance; |
||||
} |
||||
|
||||
[TestMethod] |
||||
public void GlobalProgressChanged_ShouldBeInvoked() |
||||
{ |
||||
// Arrange |
||||
var progress = 0; |
||||
eventManager.GlobalProgressChanged += (sender, args) => progress = args; |
||||
|
||||
// Act |
||||
eventManager.OnGlobalProgressChanged(100); |
||||
|
||||
// Assert |
||||
Assert.AreEqual(100, progress); |
||||
} |
||||
|
||||
[TestMethod] |
||||
public void RequestPageChange_ShouldBeInvoked() |
||||
{ |
||||
// Arrange |
||||
var pageType = typeof(object); |
||||
eventManager.PageChangeRequested += (sender, args) => pageType = args; |
||||
|
||||
// Act |
||||
eventManager.RequestPageChange(typeof(int)); |
||||
|
||||
// Assert |
||||
Assert.AreEqual(typeof(int), pageType); |
||||
} |
||||
} |
@ -0,0 +1,43 @@
|
||||
using StabilityMatrix.Helper; |
||||
using StabilityMatrix.Models; |
||||
using StabilityMatrix.Models.Packages; |
||||
|
||||
namespace StabilityMatrix.Tests.Helper; |
||||
|
||||
[TestClass] |
||||
public class PackageFactoryTests |
||||
{ |
||||
private PackageFactory packageFactory; |
||||
private IEnumerable<BasePackage> fakeBasePackages; |
||||
|
||||
[TestInitialize] |
||||
public void Setup() |
||||
{ |
||||
fakeBasePackages = new List<BasePackage> |
||||
{ |
||||
new DankDiffusion() |
||||
}; |
||||
packageFactory = new PackageFactory(fakeBasePackages); |
||||
} |
||||
|
||||
[TestMethod] |
||||
public void GetAllAvailablePackages_ReturnsAllPackages() |
||||
{ |
||||
var result = packageFactory.GetAllAvailablePackages(); |
||||
Assert.AreEqual(1, result.Count()); |
||||
} |
||||
|
||||
[TestMethod] |
||||
public void FindPackageByName_ReturnsPackage() |
||||
{ |
||||
var result = packageFactory.FindPackageByName("dank-diffusion"); |
||||
Assert.IsNotNull(result); |
||||
} |
||||
|
||||
[TestMethod] |
||||
public void FindPackageByName_ReturnsNull() |
||||
{ |
||||
var result = packageFactory.FindPackageByName("not-a-package"); |
||||
Assert.IsNull(result); |
||||
} |
||||
} |
@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
||||
<PropertyGroup> |
||||
<TargetFramework>net6.0-windows10.0.17763.0</TargetFramework> |
||||
<ImplicitUsings>enable</ImplicitUsings> |
||||
<Nullable>enable</Nullable> |
||||
|
||||
<IsPackable>false</IsPackable> |
||||
<IsTestProject>true</IsTestProject> |
||||
</PropertyGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="DotNext" Version="4.12.0" /> |
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" /> |
||||
<PackageReference Include="Moq" Version="4.18.4" /> |
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" /> |
||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" /> |
||||
<PackageReference Include="coverlet.collector" Version="3.2.0" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<ProjectReference Include="..\StabilityMatrix\StabilityMatrix.csproj" /> |
||||
</ItemGroup> |
||||
|
||||
</Project> |
@ -0,0 +1 @@
|
||||
global using Microsoft.VisualStudio.TestTools.UnitTesting; |
@ -0,0 +1,66 @@
|
||||
using System.Threading.Tasks; |
||||
using System; |
||||
using System.Windows.Threading; |
||||
using Microsoft.Extensions.Logging; |
||||
using StabilityMatrix.Models; |
||||
using StabilityMatrix.ViewModels; |
||||
using Wpf.Ui.Common; |
||||
using Wpf.Ui.Contracts; |
||||
using Wpf.Ui.Controls; |
||||
using Wpf.Ui.Controls.IconElements; |
||||
|
||||
namespace StabilityMatrix.Helper; |
||||
|
||||
/// <summary> |
||||
/// Generic recoverable error handler using content dialogs. |
||||
/// </summary> |
||||
public class DialogErrorHandler : IDialogErrorHandler |
||||
{ |
||||
private readonly ISnackbarService snackbarService; |
||||
private readonly SnackbarViewModel snackbarViewModel; |
||||
|
||||
public DialogErrorHandler(ISnackbarService snackbarService, SnackbarViewModel snackbarViewModel) |
||||
{ |
||||
this.snackbarService = snackbarService; |
||||
this.snackbarViewModel = snackbarViewModel; |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Shows a generic error snackbar with the given message. |
||||
/// </summary> |
||||
public void ShowSnackbarAsync(string message, LogLevel level = LogLevel.Error, int timeoutMilliseconds = 5000) |
||||
{ |
||||
snackbarViewModel.SnackbarAppearance = level switch |
||||
{ |
||||
LogLevel.Error => ControlAppearance.Danger, |
||||
LogLevel.Warning => ControlAppearance.Caution, |
||||
LogLevel.Information => ControlAppearance.Info, |
||||
_ => ControlAppearance.Secondary |
||||
}; |
||||
snackbarService.Timeout = timeoutMilliseconds; |
||||
var icon = new SymbolIcon(SymbolRegular.ErrorCircle24); |
||||
snackbarService.ShowAsync("Error", message, icon, snackbarViewModel.SnackbarAppearance); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Attempt to run the given action, showing a generic error snackbar if it fails. |
||||
/// </summary> |
||||
public async Task<TaskResult<T>> TryAsync<T>(Task<T> task, string message, LogLevel level = LogLevel.Error, int timeoutMilliseconds = 5000) |
||||
{ |
||||
try |
||||
{ |
||||
return new TaskResult<T> |
||||
{ |
||||
Result = await task |
||||
}; |
||||
} |
||||
catch (Exception e) |
||||
{ |
||||
ShowSnackbarAsync(message, level, timeoutMilliseconds); |
||||
return new TaskResult<T> |
||||
{ |
||||
Exception = e |
||||
}; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,34 @@
|
||||
using System; |
||||
using StabilityMatrix.Models; |
||||
using StabilityMatrix.ViewModels; |
||||
using Wpf.Ui.Contracts; |
||||
|
||||
namespace StabilityMatrix.Helper; |
||||
|
||||
public class DialogFactory : IDialogFactory |
||||
{ |
||||
private readonly IContentDialogService contentDialogService; |
||||
private readonly LaunchOptionsDialogViewModel launchOptionsDialogViewModel; |
||||
private readonly ISettingsManager settingsManager; |
||||
|
||||
public DialogFactory(IContentDialogService contentDialogService, LaunchOptionsDialogViewModel launchOptionsDialogViewModel, ISettingsManager settingsManager) |
||||
{ |
||||
this.contentDialogService = contentDialogService; |
||||
this.launchOptionsDialogViewModel = launchOptionsDialogViewModel; |
||||
this.settingsManager = settingsManager; |
||||
} |
||||
|
||||
public LaunchOptionsDialog CreateLaunchOptionsDialog(BasePackage selectedPackage, InstalledPackage installedPackage) |
||||
{ |
||||
var definitions = selectedPackage.LaunchOptions; |
||||
launchOptionsDialogViewModel.SelectedPackage = selectedPackage; |
||||
launchOptionsDialogViewModel.Cards.Clear(); |
||||
// Create cards |
||||
launchOptionsDialogViewModel.CardsFromDefinitions(definitions); |
||||
// Load user settings |
||||
var userLaunchArgs = settingsManager.GetLaunchArgs(installedPackage.Id); |
||||
launchOptionsDialogViewModel.LoadFromLaunchArgs(userLaunchArgs); |
||||
|
||||
return new LaunchOptionsDialog(contentDialogService, launchOptionsDialogViewModel); |
||||
} |
||||
} |
@ -0,0 +1,18 @@
|
||||
using System.Threading.Tasks; |
||||
using Microsoft.Extensions.Logging; |
||||
using StabilityMatrix.Models; |
||||
|
||||
namespace StabilityMatrix.Helper; |
||||
|
||||
public interface IDialogErrorHandler |
||||
{ |
||||
/// <summary> |
||||
/// Shows a generic error snackbar with the given message. |
||||
/// </summary> |
||||
void ShowSnackbarAsync(string message, LogLevel level = LogLevel.Error, int timeoutMilliseconds = 5000); |
||||
|
||||
/// <summary> |
||||
/// Attempt to run the given action, showing a generic error snackbar if it fails. |
||||
/// </summary> |
||||
Task<TaskResult<T>> TryAsync<T>(Task<T> task, string message, LogLevel level = LogLevel.Error, int timeoutMilliseconds = 5000); |
||||
} |
@ -0,0 +1,8 @@
|
||||
using StabilityMatrix.Models; |
||||
|
||||
namespace StabilityMatrix.Helper; |
||||
|
||||
public interface IDialogFactory |
||||
{ |
||||
LaunchOptionsDialog CreateLaunchOptionsDialog(BasePackage selectedPackage, InstalledPackage installedPackage); |
||||
} |
@ -0,0 +1,47 @@
|
||||
using System.IO; |
||||
using System.Threading.Tasks; |
||||
|
||||
namespace StabilityMatrix; |
||||
|
||||
public interface IPyRunner |
||||
{ |
||||
/// <summary> |
||||
/// Initializes the Python runtime using the embedded dll. |
||||
/// Can be called with no effect after initialization. |
||||
/// </summary> |
||||
/// <exception cref="FileNotFoundException">Thrown if Python DLL not found.</exception> |
||||
Task Initialize(); |
||||
|
||||
/// <summary> |
||||
/// One-time setup for get-pip |
||||
/// </summary> |
||||
Task SetupPip(); |
||||
|
||||
/// <summary> |
||||
/// Install a Python package with pip |
||||
/// </summary> |
||||
Task InstallPackage(string package); |
||||
|
||||
/// <summary> |
||||
/// Evaluate Python expression and return its value as a string |
||||
/// </summary> |
||||
/// <param name="expression"></param> |
||||
Task<string> Eval(string expression); |
||||
|
||||
/// <summary> |
||||
/// Evaluate Python expression and return its value |
||||
/// </summary> |
||||
/// <param name="expression"></param> |
||||
Task<T> Eval<T>(string expression); |
||||
|
||||
/// <summary> |
||||
/// Execute Python code without returning a value |
||||
/// </summary> |
||||
/// <param name="code"></param> |
||||
Task Exec(string code); |
||||
|
||||
/// <summary> |
||||
/// Return the Python version as a PyVersionInfo struct |
||||
/// </summary> |
||||
Task<PyVersionInfo> GetVersionInfo(); |
||||
} |
@ -0,0 +1,59 @@
|
||||
<ui:ContentDialog |
||||
CloseButtonText="Close" |
||||
DialogHeight="600" |
||||
DialogWidth="600" |
||||
Loaded="LaunchOptionsDialog_OnLoaded" |
||||
Title="Launch Options" |
||||
d:DataContext="{d:DesignInstance Type=viewModels:LaunchOptionsDialogViewModel, |
||||
IsDesignTimeCreatable=True}" |
||||
d:DesignHeight="512" |
||||
d:DesignWidth="512" |
||||
mc:Ignorable="d" |
||||
ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}" |
||||
ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}" |
||||
x:Class="StabilityMatrix.LaunchOptionsDialog" |
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" |
||||
xmlns:local="clr-namespace:StabilityMatrix" |
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" |
||||
xmlns:models="clr-namespace:StabilityMatrix.Models" |
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml" |
||||
xmlns:viewModels="clr-namespace:StabilityMatrix.ViewModels" |
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
||||
|
||||
<ui:ContentDialog.Resources> |
||||
<Style BasedOn="{StaticResource {x:Type ui:ContentDialog}}" TargetType="{x:Type local:LaunchOptionsDialog}" /> |
||||
<DataTemplate DataType="{x:Type models:LaunchOptionCard}" x:Key="LaunchOptionCardDataTemplate"> |
||||
<ui:Card Margin="16,8,8,8"> |
||||
<StackPanel |
||||
HorizontalAlignment="Left" |
||||
Margin="8,0,8,0" |
||||
Orientation="Vertical"> |
||||
<TextBlock |
||||
FontSize="16" |
||||
FontWeight="Bold" |
||||
Margin="0,8" |
||||
Text="{Binding Title}" /> |
||||
<StackPanel Orientation="Horizontal"> |
||||
<ItemsControl ItemsSource="{Binding Options}"> |
||||
<ItemsControl.ItemTemplate> |
||||
<DataTemplate> |
||||
<CheckBox Content="{Binding Name}" IsChecked="{Binding Selected}" /> |
||||
</DataTemplate> |
||||
</ItemsControl.ItemTemplate> |
||||
</ItemsControl> |
||||
</StackPanel> |
||||
</StackPanel> |
||||
</ui:Card> |
||||
</DataTemplate> |
||||
</ui:ContentDialog.Resources> |
||||
|
||||
<Grid Height="700" Width="700"> |
||||
<ScrollViewer HorizontalScrollBarVisibility="Auto"> |
||||
<StackPanel Orientation="Vertical"> |
||||
<!-- Options cards --> |
||||
<ItemsControl ItemTemplate="{StaticResource LaunchOptionCardDataTemplate}" ItemsSource="{Binding Cards}" /> |
||||
</StackPanel> |
||||
</ScrollViewer> |
||||
</Grid> |
||||
</ui:ContentDialog> |
@ -0,0 +1,29 @@
|
||||
using System.Collections.Generic; |
||||
using System.Windows; |
||||
using System.Windows.Controls; |
||||
using System.Windows.Documents; |
||||
using StabilityMatrix.ViewModels; |
||||
using Wpf.Ui.Contracts; |
||||
using Wpf.Ui.Controls.ContentDialogControl; |
||||
|
||||
namespace StabilityMatrix; |
||||
|
||||
public partial class LaunchOptionsDialog : ContentDialog |
||||
{ |
||||
private readonly LaunchOptionsDialogViewModel viewModel; |
||||
|
||||
public List<string> AsLaunchArgs() => viewModel.AsLaunchArgs(); |
||||
|
||||
public LaunchOptionsDialog(IContentDialogService dialogService, LaunchOptionsDialogViewModel viewModel) : base( |
||||
dialogService.GetContentPresenter()) |
||||
{ |
||||
this.viewModel = viewModel; |
||||
InitializeComponent(); |
||||
DataContext = viewModel; |
||||
} |
||||
|
||||
private void LaunchOptionsDialog_OnLoaded(object sender, RoutedEventArgs e) |
||||
{ |
||||
viewModel.OnLoad(); |
||||
} |
||||
} |
@ -0,0 +1,10 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel; |
||||
|
||||
namespace StabilityMatrix.Models; |
||||
|
||||
public partial class LaunchOption : ObservableObject |
||||
{ |
||||
public string Name { get; set; } |
||||
|
||||
[ObservableProperty] private bool selected = false; |
||||
} |
@ -0,0 +1,24 @@
|
||||
using System.Collections.Generic; |
||||
using System.Collections.ObjectModel; |
||||
using System.Windows.Documents; |
||||
using ABI.Windows.Data.Xml.Dom; |
||||
using StabilityMatrix.Helper; |
||||
|
||||
namespace StabilityMatrix.Models; |
||||
|
||||
public class LaunchOptionCard |
||||
{ |
||||
public string Title { get; set; } |
||||
public string? Description { get; set; } |
||||
public ObservableCollection<LaunchOption> Options { get; set; } = new(); |
||||
|
||||
public LaunchOptionCard(LaunchOptionDefinition definition) |
||||
{ |
||||
Title = definition.Name; |
||||
foreach (var optionName in definition.Options) |
||||
{ |
||||
var option = new LaunchOption {Name = optionName}; |
||||
Options.Add(option); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic; |
||||
|
||||
namespace StabilityMatrix.Models; |
||||
|
||||
/// <summary> |
||||
/// Defines a launch option for a BasePackage. |
||||
/// </summary> |
||||
public class LaunchOptionDefinition |
||||
{ |
||||
public string Name { get; set; } |
||||
// Minimum number of selected options |
||||
public int? MinSelectedOptions { get; set; } |
||||
// Maximum number of selected options |
||||
public int? MaxSelectedOptions { get; set; } |
||||
// List of option flags like "--api", "--lowvram", etc. |
||||
public List<string> Options { get; set; } |
||||
} |
@ -0,0 +1,11 @@
|
||||
using System; |
||||
|
||||
namespace StabilityMatrix.Models; |
||||
|
||||
public class TaskResult<T> |
||||
{ |
||||
public T? Result { get; set; } |
||||
public Exception? Exception { get; set; } |
||||
|
||||
public bool IsSuccessful => Exception is null && Result != null; |
||||
} |
@ -0,0 +1,58 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Collections.ObjectModel; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
using CommunityToolkit.Mvvm.ComponentModel; |
||||
using StabilityMatrix.Models; |
||||
|
||||
namespace StabilityMatrix.ViewModels; |
||||
|
||||
public partial class LaunchOptionsDialogViewModel : ObservableObject |
||||
{ |
||||
public ObservableCollection<LaunchOptionCard> Cards { get; set; } = new(); |
||||
|
||||
[ObservableProperty] |
||||
private BasePackage? selectedPackage; |
||||
|
||||
/// <summary> |
||||
/// Export the current cards options to a list of strings |
||||
/// </summary> |
||||
public List<string> AsLaunchArgs() |
||||
{ |
||||
return ( |
||||
from card in Cards from option in card.Options |
||||
where option.Selected select option.Name).ToList(); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Create cards using definitions |
||||
/// </summary> |
||||
public void CardsFromDefinitions(List<LaunchOptionDefinition> definitions) |
||||
{ |
||||
foreach (var definition in definitions) |
||||
{ |
||||
Cards.Add(new LaunchOptionCard(definition)); |
||||
} |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Import the current cards options from a list of strings |
||||
/// </summary> |
||||
public void LoadFromLaunchArgs(IEnumerable<string> launchArgs) |
||||
{ |
||||
var launchArgsSet = new HashSet<string>(launchArgs); |
||||
foreach (var card in Cards) |
||||
{ |
||||
foreach (var option in card.Options) |
||||
{ |
||||
option.Selected = launchArgsSet.Contains(option.Name); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void OnLoad() |
||||
{ |
||||
Debug.WriteLine("In LaunchOptions OnLoad"); |
||||
} |
||||
} |
@ -0,0 +1,32 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel; |
||||
using CommunityToolkit.Mvvm.Input; |
||||
using Wpf.Ui.Common; |
||||
using Wpf.Ui.Contracts; |
||||
using Wpf.Ui.Controls; |
||||
using Wpf.Ui.Controls.IconElements; |
||||
using SymbolIcon = Wpf.Ui.Controls.IconElements.SymbolIcon; |
||||
|
||||
namespace StabilityMatrix.ViewModels; |
||||
|
||||
public partial class SnackbarViewModel : ObservableObject |
||||
{ |
||||
private readonly ISnackbarService snackbarService; |
||||
|
||||
[ObservableProperty] |
||||
private ControlAppearance snackbarAppearance = ControlAppearance.Secondary; |
||||
|
||||
[ObservableProperty] |
||||
private int snackbarTimeout = 2000; |
||||
|
||||
public SnackbarViewModel(ISnackbarService snackbarService) |
||||
{ |
||||
this.snackbarService = snackbarService; |
||||
} |
||||
|
||||
[RelayCommand] |
||||
private void OnOpenSnackbar(object sender) |
||||
{ |
||||
snackbarService.Timeout = SnackbarTimeout; |
||||
snackbarService.Show("Some title.", "Some message.", new SymbolIcon(SymbolRegular.Fluent24), SnackbarAppearance); |
||||
} |
||||
} |
Loading…
Reference in new issue