Browse Source

finished up install stuff, added to download/progressmanager, restore dialog, etc

pull/117/head
JT 1 year ago
parent
commit
aefef59546
  1. 4
      StabilityMatrix.Avalonia/App.axaml.cs
  2. 7
      StabilityMatrix.Avalonia/DesignData/DesignData.cs
  3. 11
      StabilityMatrix.Avalonia/ViewModels/Base/ConsoleProgressViewModel.cs
  4. 2
      StabilityMatrix.Avalonia/ViewModels/Base/ContentDialogProgressViewModelBase.cs
  5. 15
      StabilityMatrix.Avalonia/ViewModels/Base/ProgressItemViewModelBase.cs
  6. 3
      StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs
  7. 27
      StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageModificationDialogViewModel.cs
  8. 62
      StabilityMatrix.Avalonia/ViewModels/MainWindowViewModel.cs
  9. 7
      StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs
  10. 4
      StabilityMatrix.Avalonia/ViewModels/Progress/DownloadProgressItemViewModel.cs
  11. 75
      StabilityMatrix.Avalonia/ViewModels/Progress/PackageInstallProgressItemViewModel.cs
  12. 6
      StabilityMatrix.Avalonia/ViewModels/Progress/ProgressItemViewModel.cs
  13. 27
      StabilityMatrix.Avalonia/ViewModels/Progress/ProgressManagerViewModel.cs
  14. 22
      StabilityMatrix.Avalonia/Views/Dialogs/PackageModificationDialog.axaml
  15. 3
      StabilityMatrix.Avalonia/Views/MainWindow.axaml
  16. 7
      StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs
  17. 69
      StabilityMatrix.Avalonia/Views/ProgressManagerPage.axaml
  18. 44
      StabilityMatrix.Core/Helper/EventManager.cs
  19. 7
      StabilityMatrix.Core/Models/PackageModification/AddInstalledPackageStep.cs
  20. 2
      StabilityMatrix.Core/Models/PackageModification/IPackageModificationRunner.cs
  21. 13
      StabilityMatrix.Core/Models/PackageModification/PackageModificationRunner.cs
  22. 24
      StabilityMatrix.Core/Models/PackageModification/SetPackageInstallingStep.cs
  23. 6
      StabilityMatrix.Core/Models/Packages/VladAutomatic.cs
  24. 7
      StabilityMatrix.Core/Models/Progress/ProgressItem.cs
  25. 1
      StabilityMatrix.Core/Services/ISettingsManager.cs
  26. 1
      StabilityMatrix.Core/Services/SettingsManager.cs

4
StabilityMatrix.Avalonia/App.axaml.cs

@ -47,6 +47,7 @@ using StabilityMatrix.Avalonia.ViewModels.CheckpointBrowser;
using StabilityMatrix.Avalonia.ViewModels.CheckpointManager; using StabilityMatrix.Avalonia.ViewModels.CheckpointManager;
using StabilityMatrix.Avalonia.ViewModels.Dialogs; using StabilityMatrix.Avalonia.ViewModels.Dialogs;
using StabilityMatrix.Avalonia.ViewModels.PackageManager; using StabilityMatrix.Avalonia.ViewModels.PackageManager;
using StabilityMatrix.Avalonia.ViewModels.Progress;
using StabilityMatrix.Avalonia.Views; using StabilityMatrix.Avalonia.Views;
using StabilityMatrix.Avalonia.Views.Dialogs; using StabilityMatrix.Avalonia.Views.Dialogs;
using StabilityMatrix.Core.Api; using StabilityMatrix.Core.Api;
@ -346,7 +347,6 @@ public sealed class App : Application
services.AddSingleton<BasePackage, VoltaML>(); services.AddSingleton<BasePackage, VoltaML>();
services.AddSingleton<BasePackage, InvokeAI>(); services.AddSingleton<BasePackage, InvokeAI>();
services.AddSingleton<BasePackage, Fooocus>(); services.AddSingleton<BasePackage, Fooocus>();
//services.AddSingleton<BasePackage, KohyaSs>();
} }
private static IServiceCollection ConfigureServices() private static IServiceCollection ConfigureServices()
@ -372,7 +372,7 @@ public sealed class App : Application
services.AddSingleton<IUpdateHelper, UpdateHelper>(); services.AddSingleton<IUpdateHelper, UpdateHelper>();
services.AddSingleton<INavigationService, NavigationService>(); services.AddSingleton<INavigationService, NavigationService>();
services.AddSingleton<IModelIndexService, ModelIndexService>(); services.AddSingleton<IModelIndexService, ModelIndexService>();
services.AddSingleton<IPackageModificationRunner, PackageModificationRunner>(); services.AddTransient<IPackageModificationRunner, PackageModificationRunner>();
services.AddSingleton<ITrackedDownloadService, TrackedDownloadService>(); services.AddSingleton<ITrackedDownloadService, TrackedDownloadService>();
services.AddSingleton<IDisposable>( services.AddSingleton<IDisposable>(

7
StabilityMatrix.Avalonia/DesignData/DesignData.cs

@ -13,6 +13,7 @@ using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Avalonia.ViewModels.CheckpointBrowser; using StabilityMatrix.Avalonia.ViewModels.CheckpointBrowser;
using StabilityMatrix.Avalonia.ViewModels.CheckpointManager; using StabilityMatrix.Avalonia.ViewModels.CheckpointManager;
using StabilityMatrix.Avalonia.ViewModels.Dialogs; using StabilityMatrix.Avalonia.ViewModels.Dialogs;
using StabilityMatrix.Avalonia.ViewModels.Progress;
using StabilityMatrix.Core.Api; using StabilityMatrix.Core.Api;
using StabilityMatrix.Core.Database; using StabilityMatrix.Core.Database;
using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper;
@ -276,6 +277,12 @@ public static class DesignData
) )
), ),
new MockDownloadProgressItemViewModel("Test File 2.exe"), new MockDownloadProgressItemViewModel("Test File 2.exe"),
new PackageInstallProgressItemViewModel(
new PackageModificationRunner
{
CurrentProgress = new ProgressReport(0.5f, "Installing package...")
}
)
} }
); );

11
StabilityMatrix.Avalonia/ViewModels/Base/ConsoleProgressViewModel.cs

@ -0,0 +1,11 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace StabilityMatrix.Avalonia.ViewModels.Base;
public partial class ConsoleProgressViewModel : ProgressViewModel
{
public ConsoleViewModel Console { get; } = new();
[ObservableProperty]
private bool closeWhenFinished;
}

2
StabilityMatrix.Avalonia/ViewModels/Base/ContentDialogProgressViewModelBase.cs

@ -3,7 +3,7 @@ using FluentAvalonia.UI.Controls;
namespace StabilityMatrix.Avalonia.ViewModels.Base; namespace StabilityMatrix.Avalonia.ViewModels.Base;
public class ContentDialogProgressViewModelBase : ProgressViewModel public class ContentDialogProgressViewModelBase : ConsoleProgressViewModel
{ {
public event EventHandler<ContentDialogResult>? PrimaryButtonClick; public event EventHandler<ContentDialogResult>? PrimaryButtonClick;
public event EventHandler<ContentDialogResult>? SecondaryButtonClick; public event EventHandler<ContentDialogResult>? SecondaryButtonClick;

15
StabilityMatrix.Avalonia/ViewModels/Base/ProgressItemViewModelBase.cs

@ -6,11 +6,16 @@ namespace StabilityMatrix.Avalonia.ViewModels.Base;
public abstract partial class ProgressItemViewModelBase : ViewModelBase public abstract partial class ProgressItemViewModelBase : ViewModelBase
{ {
[ObservableProperty] private Guid id; [ObservableProperty]
[ObservableProperty] private string? name; private Guid id;
[ObservableProperty] private bool failed;
[ObservableProperty]
private string? name;
[ObservableProperty]
private bool failed;
public virtual bool IsCompleted => Progress.Value >= 100 || Failed; public virtual bool IsCompleted => Progress.Value >= 100 || Failed;
public ProgressViewModel Progress { get; } = new(); public ContentDialogProgressViewModelBase Progress { get; } = new();
} }

3
StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs

@ -215,6 +215,8 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
return Task.CompletedTask; return Task.CompletedTask;
} }
var setPackageInstallingStep = new SetPackageInstallingStep(settingsManager, InstallName);
var installLocation = Path.Combine(settingsManager.LibraryDir, "Packages", InstallName); var installLocation = Path.Combine(settingsManager.LibraryDir, "Packages", InstallName);
var prereqStep = new SetupPrerequisitesStep(prerequisiteHelper, pyRunner); var prereqStep = new SetupPrerequisitesStep(prerequisiteHelper, pyRunner);
@ -270,6 +272,7 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
var steps = new List<IPackageStep> var steps = new List<IPackageStep>
{ {
setPackageInstallingStep,
prereqStep, prereqStep,
downloadStep, downloadStep,
installStep, installStep,

27
StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageModificationDialogViewModel.cs

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls.Notifications; using Avalonia.Controls.Notifications;
@ -25,25 +26,43 @@ public class PackageModificationDialogViewModel : ContentDialogProgressViewModel
this.packageModificationRunner = packageModificationRunner; this.packageModificationRunner = packageModificationRunner;
this.notificationService = notificationService; this.notificationService = notificationService;
this.steps = steps; this.steps = steps;
CloseWhenFinished = true;
} }
public ConsoleViewModel Console { get; } = new();
public override async Task OnLoadedAsync() public override async Task OnLoadedAsync()
{ {
await Console.Clear();
Console.Post(string.Join(Environment.NewLine, packageModificationRunner.ConsoleOutput));
// idk why this is getting called twice // idk why this is getting called twice
if (!packageModificationRunner.IsRunning) if (!packageModificationRunner.IsRunning)
{ {
EventManager.Instance.OnPackageInstallProgressAdded(packageModificationRunner);
packageModificationRunner.ProgressChanged += PackageModificationRunnerOnProgressChanged; packageModificationRunner.ProgressChanged += PackageModificationRunnerOnProgressChanged;
await packageModificationRunner.ExecuteSteps(steps.ToList()); await packageModificationRunner.ExecuteSteps(steps.ToList());
var packageName = string.Empty;
var addPackageStep = steps.FirstOrDefault(step => step is AddInstalledPackageStep);
if (addPackageStep != null)
{
addPackageStep
.GetType()
.GetProperty("newInstalledPackage")
?.GetValue(addPackageStep, null);
}
notificationService.Show( notificationService.Show(
"Package Install Completed", "Package Install Completed",
"Package install completed successfully.", "Package install completed successfully.",
NotificationType.Success NotificationType.Success
); );
OnCloseButtonClick(); EventManager.Instance.OnInstalledPackagesChanged();
if (CloseWhenFinished)
{
OnCloseButtonClick();
}
} }
} }

62
StabilityMatrix.Avalonia/ViewModels/MainWindowViewModel.cs

@ -11,6 +11,7 @@ using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Services; using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Avalonia.ViewModels.Dialogs; using StabilityMatrix.Avalonia.ViewModels.Dialogs;
using StabilityMatrix.Avalonia.ViewModels.Progress;
using StabilityMatrix.Avalonia.Views; using StabilityMatrix.Avalonia.Views;
using StabilityMatrix.Avalonia.Views.Dialogs; using StabilityMatrix.Avalonia.Views.Dialogs;
using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Attributes;
@ -27,13 +28,13 @@ public partial class MainWindowViewModel : ViewModelBase
private readonly ITrackedDownloadService trackedDownloadService; private readonly ITrackedDownloadService trackedDownloadService;
private readonly IDiscordRichPresenceService discordRichPresenceService; private readonly IDiscordRichPresenceService discordRichPresenceService;
public string Greeting => "Welcome to Avalonia!"; public string Greeting => "Welcome to Avalonia!";
[ObservableProperty] [ObservableProperty]
private PageViewModelBase? currentPage; private PageViewModelBase? currentPage;
[ObservableProperty] [ObservableProperty]
private object? selectedCategory; private object? selectedCategory;
[ObservableProperty] [ObservableProperty]
private List<PageViewModelBase> pages = new(); private List<PageViewModelBase> pages = new();
@ -44,16 +45,17 @@ public partial class MainWindowViewModel : ViewModelBase
public UpdateViewModel UpdateViewModel { get; init; } public UpdateViewModel UpdateViewModel { get; init; }
public MainWindowViewModel( public MainWindowViewModel(
ISettingsManager settingsManager, ISettingsManager settingsManager,
IDiscordRichPresenceService discordRichPresenceService, IDiscordRichPresenceService discordRichPresenceService,
ServiceManager<ViewModelBase> dialogFactory, ServiceManager<ViewModelBase> dialogFactory,
ITrackedDownloadService trackedDownloadService) ITrackedDownloadService trackedDownloadService
)
{ {
this.settingsManager = settingsManager; this.settingsManager = settingsManager;
this.dialogFactory = dialogFactory; this.dialogFactory = dialogFactory;
this.discordRichPresenceService = discordRichPresenceService; this.discordRichPresenceService = discordRichPresenceService;
this.trackedDownloadService = trackedDownloadService; this.trackedDownloadService = trackedDownloadService;
ProgressManagerViewModel = dialogFactory.Get<ProgressManagerViewModel>(); ProgressManagerViewModel = dialogFactory.Get<ProgressManagerViewModel>();
UpdateViewModel = dialogFactory.Get<UpdateViewModel>(); UpdateViewModel = dialogFactory.Get<UpdateViewModel>();
} }
@ -61,7 +63,7 @@ public partial class MainWindowViewModel : ViewModelBase
public override void OnLoaded() public override void OnLoaded()
{ {
base.OnLoaded(); base.OnLoaded();
// Set only if null, since this may be called again when content dialogs open // Set only if null, since this may be called again when content dialogs open
CurrentPage ??= Pages.FirstOrDefault(); CurrentPage ??= Pages.FirstOrDefault();
SelectedCategory ??= Pages.FirstOrDefault(); SelectedCategory ??= Pages.FirstOrDefault();
@ -72,24 +74,25 @@ public partial class MainWindowViewModel : ViewModelBase
await base.OnLoadedAsync(); await base.OnLoadedAsync();
// Skip if design mode // Skip if design mode
if (Design.IsDesignMode) return; if (Design.IsDesignMode)
return;
if (!await EnsureDataDirectory()) if (!await EnsureDataDirectory())
{ {
// False if user exited dialog, shutdown app // False if user exited dialog, shutdown app
App.Shutdown(); App.Shutdown();
return; return;
} }
// Initialize Discord Rich Presence (this needs LibraryDir so is set here) // Initialize Discord Rich Presence (this needs LibraryDir so is set here)
discordRichPresenceService.UpdateState(); discordRichPresenceService.UpdateState();
// Load in-progress downloads // Load in-progress downloads
ProgressManagerViewModel.AddDownloads(trackedDownloadService.Downloads); ProgressManagerViewModel.AddDownloads(trackedDownloadService.Downloads);
// Index checkpoints if we dont have // Index checkpoints if we dont have
Task.Run(() => settingsManager.IndexCheckpoints()).SafeFireAndForget(); Task.Run(() => settingsManager.IndexCheckpoints()).SafeFireAndForget();
if (!settingsManager.Settings.InstalledPackages.Any()) if (!settingsManager.Settings.InstalledPackages.Any())
{ {
var viewModel = dialogFactory.Get<OneClickInstallViewModel>(); var viewModel = dialogFactory.Get<OneClickInstallViewModel>();
@ -98,17 +101,15 @@ public partial class MainWindowViewModel : ViewModelBase
IsPrimaryButtonEnabled = false, IsPrimaryButtonEnabled = false,
IsSecondaryButtonEnabled = false, IsSecondaryButtonEnabled = false,
IsFooterVisible = false, IsFooterVisible = false,
Content = new OneClickInstallDialog Content = new OneClickInstallDialog { DataContext = viewModel },
{
DataContext = viewModel
},
}; };
EventManager.Instance.OneClickInstallFinished += (_, skipped) => EventManager.Instance.OneClickInstallFinished += (_, skipped) =>
{ {
dialog.Hide(); dialog.Hide();
if (skipped) return; if (skipped)
return;
EventManager.Instance.OnTeachingTooltipNeeded(); EventManager.Instance.OnTeachingTooltipNeeded();
}; };
@ -125,24 +126,25 @@ public partial class MainWindowViewModel : ViewModelBase
if (!settingsManager.TryFindLibrary()) if (!settingsManager.TryFindLibrary())
{ {
var result = await ShowSelectDataDirectoryDialog(); var result = await ShowSelectDataDirectoryDialog();
if (!result) return false; if (!result)
return false;
} }
// Try to find library again, should be found now // Try to find library again, should be found now
if (!settingsManager.TryFindLibrary()) if (!settingsManager.TryFindLibrary())
{ {
throw new Exception("Could not find library after setting path"); throw new Exception("Could not find library after setting path");
} }
// Tell LaunchPage to load any packages if they selected an existing directory // Tell LaunchPage to load any packages if they selected an existing directory
EventManager.Instance.OnInstalledPackagesChanged(); EventManager.Instance.OnInstalledPackagesChanged();
// Check if there are old packages, if so show migration dialog // Check if there are old packages, if so show migration dialog
// TODO: Migration dialog // TODO: Migration dialog
return true; return true;
} }
/// <summary> /// <summary>
/// Shows the select data directory dialog. /// Shows the select data directory dialog.
/// </summary> /// </summary>
@ -155,10 +157,7 @@ public partial class MainWindowViewModel : ViewModelBase
IsPrimaryButtonEnabled = false, IsPrimaryButtonEnabled = false,
IsSecondaryButtonEnabled = false, IsSecondaryButtonEnabled = false,
IsFooterVisible = false, IsFooterVisible = false,
Content = new SelectDataDirectoryDialog Content = new SelectDataDirectoryDialog { DataContext = viewModel }
{
DataContext = viewModel
}
}; };
var result = await dialog.ShowAsync(); var result = await dialog.ShowAsync();
@ -191,10 +190,7 @@ public partial class MainWindowViewModel : ViewModelBase
IsPrimaryButtonEnabled = false, IsPrimaryButtonEnabled = false,
IsSecondaryButtonEnabled = false, IsSecondaryButtonEnabled = false,
IsFooterVisible = false, IsFooterVisible = false,
Content = new UpdateDialog Content = new UpdateDialog { DataContext = viewModel }
{
DataContext = viewModel
}
}; };
await dialog.ShowAsync(); await dialog.ShowAsync();

7
StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs

@ -163,8 +163,6 @@ public partial class PackageManagerViewModel : PageViewModelBase
await dialog.ShowAsync(); await dialog.ShowAsync();
} }
await OnLoadedAsync();
} }
private IEnumerable<UnknownInstalledPackage> IndexUnknownPackages() private IEnumerable<UnknownInstalledPackage> IndexUnknownPackages()
@ -192,6 +190,11 @@ public partial class PackageManagerViewModel : PageViewModelBase
continue; continue;
} }
if (settingsManager.PackageInstallsInProgress.Contains(subDir.Name))
{
continue;
}
yield return UnknownInstalledPackage.FromDirectoryName(subDir.Name); yield return UnknownInstalledPackage.FromDirectoryName(subDir.Name);
} }
} }

4
StabilityMatrix.Avalonia/ViewModels/DownloadProgressItemViewModel.cs → StabilityMatrix.Avalonia/ViewModels/Progress/DownloadProgressItemViewModel.cs

@ -4,13 +4,13 @@ using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Core.Models; using StabilityMatrix.Core.Models;
using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Models.Progress;
namespace StabilityMatrix.Avalonia.ViewModels; namespace StabilityMatrix.Avalonia.ViewModels.Progress;
public class DownloadProgressItemViewModel : PausableProgressItemViewModelBase public class DownloadProgressItemViewModel : PausableProgressItemViewModelBase
{ {
private readonly TrackedDownload download; private readonly TrackedDownload download;
public DownloadProgressItemViewModel(TrackedDownload download) public DownloadProgressItemViewModel(TrackedDownload download)
{ {
this.download = download; this.download = download;

75
StabilityMatrix.Avalonia/ViewModels/Progress/PackageInstallProgressItemViewModel.cs

@ -0,0 +1,75 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using FluentAvalonia.UI.Controls;
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Avalonia.Views.Dialogs;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Models.PackageModification;
using StabilityMatrix.Core.Models.Progress;
namespace StabilityMatrix.Avalonia.ViewModels.Progress;
public partial class PackageInstallProgressItemViewModel : ProgressItemViewModelBase
{
private readonly IPackageModificationRunner packageModificationRunner;
private BetterContentDialog? dialog;
public PackageInstallProgressItemViewModel(IPackageModificationRunner packageModificationRunner)
{
this.packageModificationRunner = packageModificationRunner;
Id = packageModificationRunner.Id;
Name = packageModificationRunner.CurrentStep?.ProgressTitle;
Progress.Value = packageModificationRunner.CurrentProgress.Percentage;
Progress.Text = packageModificationRunner.ConsoleOutput.LastOrDefault();
Progress.IsIndeterminate = packageModificationRunner.CurrentProgress.IsIndeterminate;
Progress.Console.Post(
string.Join(Environment.NewLine, packageModificationRunner.ConsoleOutput)
);
packageModificationRunner.ProgressChanged += PackageModificationRunnerOnProgressChanged;
}
private void PackageModificationRunnerOnProgressChanged(object? sender, ProgressReport e)
{
Progress.Value = e.Percentage;
Progress.Text = e.Message;
Progress.IsIndeterminate = e.IsIndeterminate;
Name = packageModificationRunner.CurrentStep?.ProgressTitle;
if (string.IsNullOrWhiteSpace(e.Message) || e.Message.Equals("Downloading..."))
return;
Progress.Console.PostLine(e.Message);
EventManager.Instance.OnScrollToBottomRequested();
if (
e is { Message: not null, Percentage: >= 100 }
&& e.Message.Contains("Package Install Complete")
&& Progress.CloseWhenFinished
)
{
EventManager.Instance.OnInstalledPackagesChanged();
dialog?.Hide();
}
}
public async Task ShowProgressDialog()
{
Progress.CloseWhenFinished = true;
dialog = new BetterContentDialog
{
MaxDialogWidth = 900,
MinDialogWidth = 900,
DefaultButton = ContentDialogButton.Close,
IsPrimaryButtonEnabled = false,
IsSecondaryButtonEnabled = false,
IsFooterVisible = false,
Content = new PackageModificationDialog { DataContext = Progress }
};
EventManager.Instance.OnToggleProgressFlyout();
await dialog.ShowAsync();
}
}

6
StabilityMatrix.Avalonia/ViewModels/ProgressItemViewModel.cs → StabilityMatrix.Avalonia/ViewModels/Progress/ProgressItemViewModel.cs

@ -1,10 +1,8 @@
using System; using StabilityMatrix.Avalonia.ViewModels.Base;
using CommunityToolkit.Mvvm.ComponentModel;
using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Models.Progress;
namespace StabilityMatrix.Avalonia.ViewModels; namespace StabilityMatrix.Avalonia.ViewModels.Progress;
public class ProgressItemViewModel : ProgressItemViewModelBase public class ProgressItemViewModel : ProgressItemViewModelBase
{ {

27
StabilityMatrix.Avalonia/ViewModels/ProgressManagerViewModel.cs → StabilityMatrix.Avalonia/ViewModels/Progress/ProgressManagerViewModel.cs

@ -3,6 +3,7 @@ using System.Linq;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls.Notifications; using Avalonia.Controls.Notifications;
using Avalonia.Threading; using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using StabilityMatrix.Avalonia.Services; using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.ViewModels.Base;
@ -10,12 +11,13 @@ using StabilityMatrix.Avalonia.Views;
using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Models; using StabilityMatrix.Core.Models;
using StabilityMatrix.Core.Models.PackageModification;
using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Services; using StabilityMatrix.Core.Services;
using Symbol = FluentIcons.Common.Symbol; using Symbol = FluentIcons.Common.Symbol;
using SymbolIconSource = FluentIcons.FluentAvalonia.SymbolIconSource; using SymbolIconSource = FluentIcons.FluentAvalonia.SymbolIconSource;
namespace StabilityMatrix.Avalonia.ViewModels; namespace StabilityMatrix.Avalonia.ViewModels.Progress;
[View(typeof(ProgressManagerPage))] [View(typeof(ProgressManagerPage))]
public partial class ProgressManagerViewModel : PageViewModelBase public partial class ProgressManagerViewModel : PageViewModelBase
@ -24,9 +26,10 @@ public partial class ProgressManagerViewModel : PageViewModelBase
public override string Title => "Download Manager"; public override string Title => "Download Manager";
public override IconSource IconSource => new SymbolIconSource {Symbol = Symbol.ArrowCircleDown, IsFilled = true}; public override IconSource IconSource => new SymbolIconSource {Symbol = Symbol.ArrowCircleDown, IsFilled = true};
public AvaloniaList<ProgressItemViewModelBase> ProgressItems { get; } = new(); public AvaloniaList<ProgressItemViewModelBase> ProgressItems { get; } = new();
[ObservableProperty] private bool isOpen;
public ProgressManagerViewModel( public ProgressManagerViewModel(
ITrackedDownloadService trackedDownloadService, ITrackedDownloadService trackedDownloadService,
INotificationService notificationService) INotificationService notificationService)
@ -35,8 +38,15 @@ public partial class ProgressManagerViewModel : PageViewModelBase
// Attach to the event // Attach to the event
trackedDownloadService.DownloadAdded += TrackedDownloadService_OnDownloadAdded; trackedDownloadService.DownloadAdded += TrackedDownloadService_OnDownloadAdded;
EventManager.Instance.PackageInstallProgressAdded += InstanceOnPackageInstallProgressAdded;
EventManager.Instance.ToggleProgressFlyout += (_, _) => IsOpen = !IsOpen;
} }
private void InstanceOnPackageInstallProgressAdded(object? sender, IPackageModificationRunner runner)
{
AddPackageInstall(runner);
}
private void TrackedDownloadService_OnDownloadAdded(object? sender, TrackedDownload e) private void TrackedDownloadService_OnDownloadAdded(object? sender, TrackedDownload e)
{ {
var vm = new DownloadProgressItemViewModel(e); var vm = new DownloadProgressItemViewModel(e);
@ -89,6 +99,17 @@ public partial class ProgressManagerViewModel : PageViewModelBase
ProgressItems.Add(vm); ProgressItems.Add(vm);
} }
} }
private void AddPackageInstall(IPackageModificationRunner packageModificationRunner)
{
if (ProgressItems.Any(vm => vm.Id == packageModificationRunner.Id))
{
return;
}
var vm = new PackageInstallProgressItemViewModel(packageModificationRunner);
ProgressItems.Add(vm);
}
private void ShowFailedNotification(string title, string message) private void ShowFailedNotification(string title, string message)
{ {

22
StabilityMatrix.Avalonia/Views/Dialogs/PackageModificationDialog.axaml

@ -5,10 +5,11 @@
xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls" xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls"
xmlns:dialogs="clr-namespace:StabilityMatrix.Avalonia.ViewModels.Dialogs" xmlns:dialogs="clr-namespace:StabilityMatrix.Avalonia.ViewModels.Dialogs"
xmlns:avaloniaEdit="https://github.com/avaloniaui/avaloniaedit" xmlns:avaloniaEdit="https://github.com/avaloniaui/avaloniaedit"
xmlns:base="clr-namespace:StabilityMatrix.Avalonia.ViewModels.Base"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:DataType="dialogs:PackageModificationDialogViewModel" x:DataType="base:ContentDialogProgressViewModelBase"
x:Class="StabilityMatrix.Avalonia.Views.Dialogs.PackageModificationDialog"> x:Class="StabilityMatrix.Avalonia.Views.Dialogs.PackageModificationDialog">
<Grid Margin="8" RowDefinitions="Auto, Auto, Auto, *"> <Grid Margin="8" RowDefinitions="Auto, Auto, Auto, *, Auto, Auto">
<TextBlock Grid.Row="0" Text="{Binding Text}" <TextBlock Grid.Row="0" Text="{Binding Text}"
FontSize="16" FontSize="16"
TextAlignment="Center" TextAlignment="Center"
@ -17,16 +18,19 @@
<TextBlock Grid.Row="1" Text="{Binding Description}" <TextBlock Grid.Row="1" Text="{Binding Description}"
Margin="4" Margin="4"
TextWrapping="WrapWithOverflow" TextWrapping="WrapWithOverflow"
TextAlignment="Center"
IsVisible="{Binding !#Expander.IsExpanded}"/> IsVisible="{Binding !#Expander.IsExpanded}"/>
<ProgressBar Grid.Row="2" Value="{Binding Value}" <ProgressBar Grid.Row="2" Value="{Binding Value}"
Margin="8" Margin="8"
IsIndeterminate="{Binding IsIndeterminate}"/> IsIndeterminate="{Binding IsIndeterminate}"/>
<Expander Grid.Row="3" Header="More Details" x:Name="Expander"> <Expander Grid.Row="3"
Margin="8"
Header="More Details" x:Name="Expander">
<avaloniaEdit:TextEditor <avaloniaEdit:TextEditor
x:Name="Console" x:Name="Console"
Margin="8,8,16,10" Margin="8"
MaxHeight="400" MaxHeight="400"
DataContext="{Binding Console}" DataContext="{Binding Console}"
Document="{Binding Document}" Document="{Binding Document}"
@ -37,5 +41,15 @@
VerticalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
WordWrap="True" /> WordWrap="True" />
</Expander> </Expander>
<CheckBox Grid.Row="4" IsChecked="{Binding CloseWhenFinished}"
HorizontalAlignment="Center"
Margin="4" Content="Close dialog when finished"/>
<Button Grid.Row="5" Content="Close"
FontSize="20"
Margin="8"
HorizontalAlignment="Center"
Command="{Binding OnCloseButtonClick}"/>
</Grid> </Grid>
</controls:UserControlBase> </controls:UserControlBase>

3
StabilityMatrix.Avalonia/Views/MainWindow.axaml

@ -16,7 +16,6 @@
Width="1100" Width="1100"
Height="750" Height="750"
Title="Stability Matrix" Title="Stability Matrix"
FontFamily="San Francisco, Segoe UI, Helvetica Neue, Helvetica, Arial, sans-serif"
x:Class="StabilityMatrix.Avalonia.Views.MainWindow"> x:Class="StabilityMatrix.Avalonia.Views.MainWindow">
<Grid RowDefinitions="Auto,Auto,*"> <Grid RowDefinitions="Auto,Auto,*">
@ -119,7 +118,7 @@
<controls:BetterFlyout Placement="RightEdgeAlignedBottom" <controls:BetterFlyout Placement="RightEdgeAlignedBottom"
HorizontalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
Content="{Binding }" /> Content="{Binding }"/>
</ui:NavigationViewItem.ContextFlyout> </ui:NavigationViewItem.ContextFlyout>
<ui:NavigationViewItem.InfoBadge> <ui:NavigationViewItem.InfoBadge>
<ui:InfoBadge Value="{Binding ProgressItems.Count}" <ui:InfoBadge Value="{Binding ProgressItems.Count}"

7
StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs

@ -25,6 +25,7 @@ using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Services; using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels; using StabilityMatrix.Avalonia.ViewModels;
using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Processes; using StabilityMatrix.Core.Processes;
#if DEBUG #if DEBUG
using StabilityMatrix.Avalonia.Diagnostics.Views; using StabilityMatrix.Avalonia.Diagnostics.Views;
@ -38,6 +39,8 @@ public partial class MainWindow : AppWindowBase
private readonly INotificationService notificationService; private readonly INotificationService notificationService;
private readonly INavigationService navigationService; private readonly INavigationService navigationService;
private FlyoutBase? progressFlyout;
[DesignOnly(true)] [DesignOnly(true)]
[SuppressMessage("ReSharper", "UnusedMember.Global")] [SuppressMessage("ReSharper", "UnusedMember.Global")]
public MainWindow() public MainWindow()
@ -62,6 +65,8 @@ public partial class MainWindow : AppWindowBase
#endif #endif
TitleBar.ExtendsContentIntoTitleBar = true; TitleBar.ExtendsContentIntoTitleBar = true;
TitleBar.TitleBarHitTestType = TitleBarHitTestType.Complex; TitleBar.TitleBarHitTestType = TitleBarHitTestType.Complex;
EventManager.Instance.ToggleProgressFlyout += (_, _) => progressFlyout?.Hide();
} }
/// <inheritdoc /> /// <inheritdoc />
@ -231,6 +236,8 @@ public partial class MainWindow : AppWindowBase
var item = sender as NavigationViewItem; var item = sender as NavigationViewItem;
var flyout = item!.ContextFlyout; var flyout = item!.ContextFlyout;
flyout!.ShowAt(item); flyout!.ShowAt(item);
progressFlyout = flyout;
} }
private void FooterUpdateItem_OnTapped(object? sender, TappedEventArgs e) private void FooterUpdateItem_OnTapped(object? sender, TappedEventArgs e)

69
StabilityMatrix.Avalonia/Views/ProgressManagerPage.axaml

@ -11,11 +11,12 @@
xmlns:ui="using:FluentAvalonia.UI.Controls" xmlns:ui="using:FluentAvalonia.UI.Controls"
xmlns:vm="clr-namespace:StabilityMatrix.Avalonia.ViewModels" xmlns:vm="clr-namespace:StabilityMatrix.Avalonia.ViewModels"
xmlns:vmBase="clr-namespace:StabilityMatrix.Avalonia.ViewModels.Base" xmlns:vmBase="clr-namespace:StabilityMatrix.Avalonia.ViewModels.Base"
xmlns:progress="clr-namespace:StabilityMatrix.Avalonia.ViewModels.Progress"
MaxHeight="250" MaxHeight="250"
d:DataContext="{x:Static mocks:DesignData.ProgressManagerViewModel}" d:DataContext="{x:Static mocks:DesignData.ProgressManagerViewModel}"
d:DesignHeight="250" d:DesignHeight="250"
d:DesignWidth="300" d:DesignWidth="300"
x:DataType="vm:ProgressManagerViewModel" x:DataType="progress:ProgressManagerViewModel"
mc:Ignorable="d"> mc:Ignorable="d">
<ScrollViewer> <ScrollViewer>
<Grid RowDefinitions="Auto, *"> <Grid RowDefinitions="Auto, *">
@ -117,7 +118,7 @@
</Border> </Border>
</DataTemplate> </DataTemplate>
<DataTemplate DataType="{x:Type vm:ProgressItemViewModel}"> <DataTemplate DataType="{x:Type progress:ProgressItemViewModel}">
<Border <Border
Margin="4" Margin="4"
Padding="8" Padding="8"
@ -164,6 +165,70 @@
</Grid> </Grid>
</Border> </Border>
</DataTemplate> </DataTemplate>
<DataTemplate DataType="{x:Type progress:PackageInstallProgressItemViewModel}">
<Border
Margin="4"
Padding="8"
Background="#22000000"
BorderBrush="#33000000"
BorderThickness="2"
CornerRadius="8">
<Grid ColumnDefinitions="*,Auto" RowDefinitions="*,Auto">
<StackPanel Grid.Row="0" Grid.Column="0">
<TextBlock Margin="0,0"
Text="{Binding Name, Mode=OneWay}" />
<!-- non-indeterminate progress -->
<TextBlock
Margin="0,4"
MaxWidth="300"
IsVisible="{Binding !Progress.IsIndeterminate}">
<Run Text="{Binding Progress.Text, Mode=OneWay}" />
<Run Text="{Binding Progress.Value, Mode=OneWay}" /><Run Text="%" />
</TextBlock>
<!-- indeterminate progress -->
<TextBlock
Margin="0,4"
MaxWidth="300"
IsVisible="{Binding Progress.IsIndeterminate}"
Text="{Binding Progress.Text, Mode=OneWay}" />
</StackPanel>
<!-- Buttons -->
<Button
Grid.Row="0"
Grid.Column="1"
Margin="8,0"
ToolTip.Tip="Show Progress Dialog"
Command="{Binding ShowProgressDialog}"
Classes="transparent-full">
<ui:SymbolIcon Symbol="Open" />
</Button>
<ProgressBar
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="0,8,0,4"
IsIndeterminate="{Binding Progress.IsIndeterminate}"
Value="{Binding Progress.Value, Mode=OneWay}">
<ProgressBar.Transitions>
<Transitions>
<DoubleTransition Property="Value" Duration="00:00:00.150">
<DoubleTransition.Easing>
<SineEaseInOut />
</DoubleTransition.Easing>
</DoubleTransition>
</Transitions>
</ProgressBar.Transitions>
</ProgressBar>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.DataTemplates> </ItemsControl.DataTemplates>
</ItemsControl> </ItemsControl>
</Grid> </Grid>

44
StabilityMatrix.Core/Helper/EventManager.cs

@ -1,4 +1,5 @@
using StabilityMatrix.Core.Models; using StabilityMatrix.Core.Models;
using StabilityMatrix.Core.Models.PackageModification;
using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Models.Update; using StabilityMatrix.Core.Models.Update;
@ -10,11 +11,8 @@ public class EventManager
{ {
public static EventManager Instance { get; } = new(); public static EventManager Instance { get; } = new();
private EventManager() private EventManager() { }
{
}
public event EventHandler<int>? GlobalProgressChanged; public event EventHandler<int>? GlobalProgressChanged;
public event EventHandler? InstalledPackagesChanged; public event EventHandler? InstalledPackagesChanged;
public event EventHandler<bool>? OneClickInstallFinished; public event EventHandler<bool>? OneClickInstallFinished;
@ -24,20 +22,42 @@ public class EventManager
public event EventHandler<Guid>? PackageLaunchRequested; public event EventHandler<Guid>? PackageLaunchRequested;
public event EventHandler? ScrollToBottomRequested; public event EventHandler? ScrollToBottomRequested;
public event EventHandler<ProgressItem>? ProgressChanged; public event EventHandler<ProgressItem>? ProgressChanged;
public event EventHandler<RunningPackageStatusChangedEventArgs>? RunningPackageStatusChanged; public event EventHandler<RunningPackageStatusChangedEventArgs>? RunningPackageStatusChanged;
public void OnGlobalProgressChanged(int progress) => GlobalProgressChanged?.Invoke(this, progress); public event EventHandler<IPackageModificationRunner>? PackageInstallProgressAdded;
public void OnInstalledPackagesChanged() => InstalledPackagesChanged?.Invoke(this, EventArgs.Empty); public event EventHandler? ToggleProgressFlyout;
public void OnOneClickInstallFinished(bool skipped) => OneClickInstallFinished?.Invoke(this, skipped);
public void OnGlobalProgressChanged(int progress) =>
GlobalProgressChanged?.Invoke(this, progress);
public void OnInstalledPackagesChanged() =>
InstalledPackagesChanged?.Invoke(this, EventArgs.Empty);
public void OnOneClickInstallFinished(bool skipped) =>
OneClickInstallFinished?.Invoke(this, skipped);
public void OnTeachingTooltipNeeded() => TeachingTooltipNeeded?.Invoke(this, EventArgs.Empty); public void OnTeachingTooltipNeeded() => TeachingTooltipNeeded?.Invoke(this, EventArgs.Empty);
public void OnDevModeSettingChanged(bool value) => DevModeSettingChanged?.Invoke(this, value); public void OnDevModeSettingChanged(bool value) => DevModeSettingChanged?.Invoke(this, value);
public void OnUpdateAvailable(UpdateInfo args) => UpdateAvailable?.Invoke(this, args); public void OnUpdateAvailable(UpdateInfo args) => UpdateAvailable?.Invoke(this, args);
public void OnPackageLaunchRequested(Guid packageId) => public void OnPackageLaunchRequested(Guid packageId) =>
PackageLaunchRequested?.Invoke(this, packageId); PackageLaunchRequested?.Invoke(this, packageId);
public void OnScrollToBottomRequested() => public void OnScrollToBottomRequested() =>
ScrollToBottomRequested?.Invoke(this, EventArgs.Empty); ScrollToBottomRequested?.Invoke(this, EventArgs.Empty);
public void OnProgressChanged(ProgressItem progress) =>
ProgressChanged?.Invoke(this, progress); public void OnProgressChanged(ProgressItem progress) => ProgressChanged?.Invoke(this, progress);
public void OnRunningPackageStatusChanged(PackagePair? currentPackagePair) => public void OnRunningPackageStatusChanged(PackagePair? currentPackagePair) =>
RunningPackageStatusChanged?.Invoke(this, new RunningPackageStatusChangedEventArgs(currentPackagePair)); RunningPackageStatusChanged?.Invoke(
this,
new RunningPackageStatusChangedEventArgs(currentPackagePair)
);
public void OnPackageInstallProgressAdded(IPackageModificationRunner runner) =>
PackageInstallProgressAdded?.Invoke(this, runner);
public void OnToggleProgressFlyout() => ToggleProgressFlyout?.Invoke(this, EventArgs.Empty);
} }

7
StabilityMatrix.Core/Models/PackageModification/AddInstalledPackageStep.cs

@ -19,10 +19,15 @@ public class AddInstalledPackageStep : IPackageStep
public async Task ExecuteAsync(IProgress<ProgressReport>? progress = null) public async Task ExecuteAsync(IProgress<ProgressReport>? progress = null)
{ {
if (!string.IsNullOrWhiteSpace(newInstalledPackage.DisplayName))
{
settingsManager.PackageInstallsInProgress.Remove(newInstalledPackage.DisplayName);
}
await using var transaction = settingsManager.BeginTransaction(); await using var transaction = settingsManager.BeginTransaction();
transaction.Settings.InstalledPackages.Add(newInstalledPackage); transaction.Settings.InstalledPackages.Add(newInstalledPackage);
transaction.Settings.ActiveInstalledPackageId = newInstalledPackage.Id; transaction.Settings.ActiveInstalledPackageId = newInstalledPackage.Id;
} }
public string ProgressTitle => "Finishing up..."; public string ProgressTitle => $"{newInstalledPackage.DisplayName} Installed";
} }

2
StabilityMatrix.Core/Models/PackageModification/IPackageModificationRunner.cs

@ -9,4 +9,6 @@ public interface IPackageModificationRunner
ProgressReport CurrentProgress { get; set; } ProgressReport CurrentProgress { get; set; }
IPackageStep? CurrentStep { get; set; } IPackageStep? CurrentStep { get; set; }
event EventHandler<ProgressReport>? ProgressChanged; event EventHandler<ProgressReport>? ProgressChanged;
List<string> ConsoleOutput { get; }
Guid Id { get; }
} }

13
StabilityMatrix.Core/Models/PackageModification/PackageModificationRunner.cs

@ -6,9 +6,14 @@ public class PackageModificationRunner : IPackageModificationRunner
{ {
public async Task ExecuteSteps(IReadOnlyList<IPackageStep> steps) public async Task ExecuteSteps(IReadOnlyList<IPackageStep> steps)
{ {
var progress = new Progress<ProgressReport>(report => IProgress<ProgressReport> progress = new Progress<ProgressReport>(report =>
{ {
CurrentProgress = report; CurrentProgress = report;
if (!string.IsNullOrWhiteSpace(report.Message))
{
ConsoleOutput.Add(report.Message);
}
OnProgressChanged(report); OnProgressChanged(report);
}); });
@ -19,12 +24,18 @@ public class PackageModificationRunner : IPackageModificationRunner
await step.ExecuteAsync(progress).ConfigureAwait(false); await step.ExecuteAsync(progress).ConfigureAwait(false);
} }
progress.Report(
new ProgressReport(1f, message: "Package Install Complete", isIndeterminate: false)
);
IsRunning = false; IsRunning = false;
} }
public bool IsRunning { get; set; } public bool IsRunning { get; set; }
public ProgressReport CurrentProgress { get; set; } public ProgressReport CurrentProgress { get; set; }
public IPackageStep? CurrentStep { get; set; } public IPackageStep? CurrentStep { get; set; }
public List<string> ConsoleOutput { get; } = new();
public Guid Id { get; } = Guid.NewGuid();
public event EventHandler<ProgressReport>? ProgressChanged; public event EventHandler<ProgressReport>? ProgressChanged;

24
StabilityMatrix.Core/Models/PackageModification/SetPackageInstallingStep.cs

@ -0,0 +1,24 @@
using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Core.Models.PackageModification;
public class SetPackageInstallingStep : IPackageStep
{
private readonly ISettingsManager settingsManager;
private readonly string packageName;
public SetPackageInstallingStep(ISettingsManager settingsManager, string packageName)
{
this.settingsManager = settingsManager;
this.packageName = packageName;
}
public Task ExecuteAsync(IProgress<ProgressReport>? progress = null)
{
settingsManager.PackageInstallsInProgress.Add(packageName);
return Task.CompletedTask;
}
public string ProgressTitle => "Starting Package Installation";
}

6
StabilityMatrix.Core/Models/Packages/VladAutomatic.cs

@ -101,6 +101,12 @@ public class VladAutomatic : BaseGitPackage
Options = new() { "--lowvram", "--medvram" } Options = new() { "--lowvram", "--medvram" }
}, },
new() new()
{
Name = "Auto-Launch Web UI",
Type = LaunchOptionType.Bool,
Options = new() { "--autolaunch" }
},
new()
{ {
Name = "Force use of Intel OneAPI XPU backend", Name = "Force use of Intel OneAPI XPU backend",
Type = LaunchOptionType.Bool, Type = LaunchOptionType.Bool,

7
StabilityMatrix.Core/Models/Progress/ProgressItem.cs

@ -1,3 +1,8 @@
namespace StabilityMatrix.Core.Models.Progress; namespace StabilityMatrix.Core.Models.Progress;
public record ProgressItem(Guid ProgressId, string Name, ProgressReport Progress, bool Failed = false); public record ProgressItem(
Guid ProgressId,
string Name,
ProgressReport Progress,
bool Failed = false
);

1
StabilityMatrix.Core/Services/ISettingsManager.cs

@ -15,6 +15,7 @@ public interface ISettingsManager
string ModelsDirectory { get; } string ModelsDirectory { get; }
string DownloadsDirectory { get; } string DownloadsDirectory { get; }
Settings Settings { get; } Settings Settings { get; }
List<string> PackageInstallsInProgress { get; set; }
event EventHandler<string>? LibraryDirChanged; event EventHandler<string>? LibraryDirChanged;
event EventHandler<PropertyChangedEventArgs>? SettingsPropertyChanged; event EventHandler<PropertyChangedEventArgs>? SettingsPropertyChanged;

1
StabilityMatrix.Core/Services/SettingsManager.cs

@ -56,6 +56,7 @@ public class SettingsManager : ISettingsManager
private string SettingsPath => Path.Combine(LibraryDir, "settings.json"); private string SettingsPath => Path.Combine(LibraryDir, "settings.json");
public string ModelsDirectory => Path.Combine(LibraryDir, "Models"); public string ModelsDirectory => Path.Combine(LibraryDir, "Models");
public string DownloadsDirectory => Path.Combine(LibraryDir, ".downloads"); public string DownloadsDirectory => Path.Combine(LibraryDir, ".downloads");
public List<string> PackageInstallsInProgress { get; set; } = new();
public Settings Settings { get; private set; } = new(); public Settings Settings { get; private set; } = new();

Loading…
Cancel
Save