diff --git a/CHANGELOG.md b/CHANGELOG.md index f2f2c665..109e4009 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,14 +7,23 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2 ## v2.6.0 ### Added -- Added "Output Sharing" option for all packages in the three-dots menu on the Packages page +- Added **Output Sharing** option for all packages in the three-dots menu on the Packages page - This will link the package's output folders to the relevant subfolders in the "Outputs" directory - When a package only has a generic "outputs" folder, all generated images from that package will be linked to the "Outputs\Text2Img" folder when this option is enabled -- Added "Outputs" page for viewing generated images from any package, or the shared output folder +- Added **Outputs page** for viewing generated images from any package, or the shared output folder - Added [Stable Diffusion WebUI/UX](https://github.com/anapnoe/stable-diffusion-webui-ux) package +- Added [Stable Diffusion WebUI-DirectML](https://github.com/lshqqytiger/stable-diffusion-webui-directml) package +- Added GPU compatibility badges to the installers +- Added filtering of "incompatible" packages (ones that do not support your GPU) to all installers + - This can be overridden by checking the new "Show All Packages" checkbox +- Added more launch options for Fooocus, such as the `--preset` option ### Changed - If ComfyUI for Inference is chosen during the One-Click Installer, the Inference page will be opened after installation instead of the Launch page - Changed all package installs & updates to use git commands instead of downloading zip files +- The One-Click Installer now uses the new progress dialog with console +- NVIDIA GPU users will be updated to use CUDA 12.1 for ComfyUI & Fooocus packages for a slight performance improvement + - Update will occur the next time the package is updated, or on a fresh install + - Note: CUDA 12.1 is only available on Maxwell (GTX 900 series) and newer GPUs ### Fixed - Fixed crash when clicking Inference gallery image after the image is deleted externally in file explorer - Fixed Inference popup Install button not working on One-Click Installer diff --git a/StabilityMatrix.Avalonia/App.axaml.cs b/StabilityMatrix.Avalonia/App.axaml.cs index 57c6497c..b7c80921 100644 --- a/StabilityMatrix.Avalonia/App.axaml.cs +++ b/StabilityMatrix.Avalonia/App.axaml.cs @@ -444,9 +444,10 @@ public sealed class App : Application { services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/StabilityMatrix.Avalonia/DesignData/DesignData.cs b/StabilityMatrix.Avalonia/DesignData/DesignData.cs index 534d8d71..b8dc5560 100644 --- a/StabilityMatrix.Avalonia/DesignData/DesignData.cs +++ b/StabilityMatrix.Avalonia/DesignData/DesignData.cs @@ -33,6 +33,7 @@ using StabilityMatrix.Core.Models.Api; using StabilityMatrix.Core.Models.Api.Comfy; using StabilityMatrix.Core.Models.Database; using StabilityMatrix.Core.Models.PackageModification; +using StabilityMatrix.Core.Models.Packages; using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Python; using StabilityMatrix.Core.Services; @@ -175,9 +176,9 @@ public static class DesignData LaunchOptionsViewModel.UpdateFilterCards(); InstallerViewModel = Services.GetRequiredService(); - InstallerViewModel.AvailablePackages = packageFactory - .GetAllAvailablePackages() - .ToImmutableArray(); + InstallerViewModel.AvailablePackages = new ObservableCollectionExtended( + packageFactory.GetAllAvailablePackages() + ); InstallerViewModel.SelectedPackage = InstallerViewModel.AvailablePackages[0]; InstallerViewModel.ReleaseNotes = "## Release Notes\nThis is a test release note."; diff --git a/StabilityMatrix.Avalonia/ViewModels/Base/ContentDialogProgressViewModelBase.cs b/StabilityMatrix.Avalonia/ViewModels/Base/ContentDialogProgressViewModelBase.cs index b905bba6..a4ed0309 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Base/ContentDialogProgressViewModelBase.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Base/ContentDialogProgressViewModelBase.cs @@ -1,10 +1,14 @@ using System; +using CommunityToolkit.Mvvm.ComponentModel; using FluentAvalonia.UI.Controls; namespace StabilityMatrix.Avalonia.ViewModels.Base; -public class ContentDialogProgressViewModelBase : ConsoleProgressViewModel +public partial class ContentDialogProgressViewModelBase : ConsoleProgressViewModel { + [ObservableProperty] + private bool hideCloseButton; + public event EventHandler? PrimaryButtonClick; public event EventHandler? SecondaryButtonClick; public event EventHandler? CloseButtonClick; diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs index 14ddce18..54ba9a54 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs @@ -36,6 +36,7 @@ public partial class InstallerViewModel : ContentDialogViewModelBase private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private readonly ISettingsManager settingsManager; + private readonly IPackageFactory packageFactory; private readonly IPyRunner pyRunner; private readonly IDownloadService downloadService; private readonly INotificationService notificationService; @@ -47,15 +48,15 @@ public partial class InstallerViewModel : ContentDialogViewModelBase [ObservableProperty] private PackageVersion? selectedVersion; - [ObservableProperty] - private IReadOnlyList? availablePackages; - [ObservableProperty] private ObservableCollection? availableCommits; [ObservableProperty] private ObservableCollection? availableVersions; + [ObservableProperty] + private ObservableCollection availablePackages; + [ObservableProperty] private GitCommit? selectedCommit; @@ -69,6 +70,9 @@ public partial class InstallerViewModel : ContentDialogViewModelBase [NotifyPropertyChangedFor(nameof(CanInstall))] private bool showDuplicateWarning; + [ObservableProperty] + private bool showIncompatiblePackages; + [ObservableProperty] [NotifyPropertyChangedFor(nameof(CanInstall))] private string? installName; @@ -115,7 +119,6 @@ public partial class InstallerViewModel : ContentDialogViewModelBase public bool CanInstall => !string.IsNullOrWhiteSpace(InstallName) && !ShowDuplicateWarning && !IsLoading; - public ProgressViewModel InstallProgress { get; } = new(); public IEnumerable Steps { get; set; } public InstallerViewModel( @@ -128,22 +131,25 @@ public partial class InstallerViewModel : ContentDialogViewModelBase ) { this.settingsManager = settingsManager; + this.packageFactory = packageFactory; this.pyRunner = pyRunner; this.downloadService = downloadService; this.notificationService = notificationService; this.prerequisiteHelper = prerequisiteHelper; - // AvailablePackages and SelectedPackage + var filtered = packageFactory.GetAllAvailablePackages().Where(p => p.IsCompatible).ToList(); + AvailablePackages = new ObservableCollection( - packageFactory.GetAllAvailablePackages() + filtered.Any() ? filtered : packageFactory.GetAllAvailablePackages() ); - SelectedPackage = AvailablePackages[0]; + ShowIncompatiblePackages = !filtered.Any(); } public override void OnLoaded() { if (AvailablePackages == null) return; + IsReleaseMode = !SelectedPackage.ShouldIgnoreReleases; } @@ -309,6 +315,19 @@ public partial class InstallerViewModel : ContentDialogViewModelBase OnCloseButtonClick(); } + partial void OnShowIncompatiblePackagesChanged(bool value) + { + var filtered = packageFactory + .GetAllAvailablePackages() + .Where(p => ShowIncompatiblePackages || p.IsCompatible) + .ToList(); + + AvailablePackages = new ObservableCollection( + filtered.Any() ? filtered : packageFactory.GetAllAvailablePackages() + ); + SelectedPackage = AvailablePackages[0]; + } + private void UpdateSelectedVersionToLatestMain() { if (AvailableVersions is null) @@ -370,41 +389,33 @@ public partial class InstallerViewModel : ContentDialogViewModelBase // When changing branch / release modes, refresh // ReSharper disable once UnusedParameterInPartialMethod - partial void OnSelectedVersionTypeChanged(PackageVersionType value) => - OnSelectedPackageChanged(SelectedPackage); - - partial void OnSelectedPackageChanged(BasePackage value) + partial void OnSelectedVersionTypeChanged(PackageVersionType value) { - IsLoading = true; - ReleaseNotes = string.Empty; - AvailableVersions?.Clear(); - AvailableCommits?.Clear(); - - AvailableVersionTypes = SelectedPackage.ShouldIgnoreReleases - ? PackageVersionType.Commit - : PackageVersionType.GithubRelease | PackageVersionType.Commit; - SelectedSharedFolderMethod = SelectedPackage.RecommendedSharedFolderMethod; - SelectedTorchVersion = SelectedPackage.GetRecommendedTorchVersion(); - if (Design.IsDesignMode) + if (SelectedPackage is null || Design.IsDesignMode) return; Dispatcher.UIThread .InvokeAsync(async () => { Logger.Debug($"Release mode: {IsReleaseMode}"); - var versionOptions = await value.GetAllVersionOptions(); + var versionOptions = await SelectedPackage.GetAllVersionOptions(); AvailableVersions = IsReleaseMode ? new ObservableCollection(versionOptions.AvailableVersions) : new ObservableCollection(versionOptions.AvailableBranches); - SelectedVersion = AvailableVersions.First(x => !x.IsPrerelease); + SelectedVersion = AvailableVersions?.FirstOrDefault(x => !x.IsPrerelease); + if (SelectedVersion is null) + return; + ReleaseNotes = SelectedVersion.ReleaseNotesMarkdown; Logger.Debug($"Loaded release notes for {ReleaseNotes}"); if (!IsReleaseMode) { - var commits = (await value.GetAllCommits(SelectedVersion.TagName))?.ToList(); + var commits = ( + await SelectedPackage.GetAllCommits(SelectedVersion.TagName) + )?.ToList(); if (commits is null || commits.Count == 0) return; @@ -420,6 +431,29 @@ public partial class InstallerViewModel : ContentDialogViewModelBase .SafeFireAndForget(); } + partial void OnSelectedPackageChanged(BasePackage? value) + { + IsLoading = true; + ReleaseNotes = string.Empty; + AvailableVersions?.Clear(); + AvailableCommits?.Clear(); + + if (value == null) + return; + + AvailableVersionTypes = SelectedPackage.ShouldIgnoreReleases + ? PackageVersionType.Commit + : PackageVersionType.GithubRelease | PackageVersionType.Commit; + IsReleaseMode = !SelectedPackage.ShouldIgnoreReleases; + SelectedSharedFolderMethod = SelectedPackage.RecommendedSharedFolderMethod; + SelectedTorchVersion = SelectedPackage.GetRecommendedTorchVersion(); + SelectedVersionType = SelectedPackage.ShouldIgnoreReleases + ? PackageVersionType.Commit + : PackageVersionType.GithubRelease; + + OnSelectedVersionTypeChanged(SelectedVersionType); + } + partial void OnInstallNameChanged(string? value) { ShowDuplicateWarning = settingsManager.Settings.InstalledPackages.Any( @@ -430,7 +464,7 @@ public partial class InstallerViewModel : ContentDialogViewModelBase partial void OnSelectedVersionChanged(PackageVersion? value) { ReleaseNotes = value?.ReleaseNotesMarkdown ?? string.Empty; - if (value == null) + if (value == null || Design.IsDesignMode) return; SelectedCommit = null; diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs index c4e34c99..008da6eb 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; @@ -12,6 +13,7 @@ using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper.Factory; using StabilityMatrix.Core.Models; +using StabilityMatrix.Core.Models.PackageModification; using StabilityMatrix.Core.Models.Packages; using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Python; @@ -19,14 +21,13 @@ using StabilityMatrix.Core.Services; namespace StabilityMatrix.Avalonia.ViewModels.Dialogs; -public partial class OneClickInstallViewModel : ViewModelBase +public partial class OneClickInstallViewModel : ContentDialogViewModelBase { private readonly ISettingsManager settingsManager; private readonly IPackageFactory packageFactory; private readonly IPrerequisiteHelper prerequisiteHelper; private readonly ILogger logger; private readonly IPyRunner pyRunner; - private readonly ISharedFolders sharedFolders; private readonly INavigationService navigationService; private const string DefaultPackageName = "stable-diffusion-webui"; @@ -45,6 +46,9 @@ public partial class OneClickInstallViewModel : ViewModelBase [ObservableProperty] private bool isIndeterminate; + [ObservableProperty] + private bool showIncompatiblePackages; + [ObservableProperty] private ObservableCollection allPackages; @@ -55,17 +59,16 @@ public partial class OneClickInstallViewModel : ViewModelBase [NotifyPropertyChangedFor(nameof(IsProgressBarVisible))] private int oneClickInstallProgress; - public bool IsProgressBarVisible => OneClickInstallProgress > 0 || IsIndeterminate; - private bool isInferenceInstall; + public bool IsProgressBarVisible => OneClickInstallProgress > 0 || IsIndeterminate; + public OneClickInstallViewModel( ISettingsManager settingsManager, IPackageFactory packageFactory, IPrerequisiteHelper prerequisiteHelper, ILogger logger, IPyRunner pyRunner, - ISharedFolders sharedFolders, INavigationService navigationService ) { @@ -74,14 +77,21 @@ public partial class OneClickInstallViewModel : ViewModelBase this.prerequisiteHelper = prerequisiteHelper; this.logger = logger; this.pyRunner = pyRunner; - this.sharedFolders = sharedFolders; this.navigationService = navigationService; HeaderText = Resources.Text_WelcomeToStabilityMatrix; SubHeaderText = Resources.Text_OneClickInstaller_SubHeader; ShowInstallButton = true; + + var filteredPackages = this.packageFactory + .GetAllAvailablePackages() + .Where(p => p is { OfferInOneClickInstaller: true, IsCompatible: true }) + .ToList(); + AllPackages = new ObservableCollection( - this.packageFactory.GetAllAvailablePackages().Where(p => p.OfferInOneClickInstaller) + filteredPackages.Any() + ? filteredPackages + : this.packageFactory.GetAllAvailablePackages() ); SelectedPackage = AllPackages[0]; } @@ -115,35 +125,18 @@ public partial class OneClickInstallViewModel : ViewModelBase private async Task DoInstall() { - HeaderText = $"{Resources.Label_Installing} {SelectedPackage.DisplayName}"; - - var progressHandler = new Progress(progress => + var steps = new List { - SubHeaderText = $"{progress.Title} {progress.Percentage:N0}%"; - - IsIndeterminate = progress.IsIndeterminate; - OneClickInstallProgress = Convert.ToInt32(progress.Percentage); - }); - - await prerequisiteHelper.InstallAllIfNecessary(progressHandler); - - SubHeaderText = Resources.Progress_InstallingPrerequisites; - IsIndeterminate = true; - if (!PyRunner.PipInstalled) - { - await pyRunner.SetupPip(); - } - - if (!PyRunner.VenvInstalled) - { - await pyRunner.InstallPackage("virtualenv"); - } - IsIndeterminate = false; - - var libraryDir = settingsManager.LibraryDir; + new SetPackageInstallingStep(settingsManager, SelectedPackage.Name), + new SetupPrerequisitesStep(prerequisiteHelper, pyRunner) + }; // get latest version & download & install - var installLocation = Path.Combine(libraryDir, "Packages", SelectedPackage.Name); + var installLocation = Path.Combine( + settingsManager.LibraryDir, + "Packages", + SelectedPackage.Name + ); var downloadVersion = new DownloadPackageVersionOptions { IsLatest = true }; var installedVersion = new InstalledPackageVersion(); @@ -167,12 +160,29 @@ public partial class OneClickInstallViewModel : ViewModelBase } var torchVersion = SelectedPackage.GetRecommendedTorchVersion(); + var recommendedSharedFolderMethod = SelectedPackage.RecommendedSharedFolderMethod; + + var downloadStep = new DownloadPackageVersionStep( + SelectedPackage, + installLocation, + downloadVersion + ); + steps.Add(downloadStep); - await DownloadPackage(installLocation, downloadVersion); - await InstallPackage(installLocation, torchVersion, downloadVersion); + var installStep = new InstallPackageStep( + SelectedPackage, + torchVersion, + downloadVersion, + installLocation + ); + steps.Add(installStep); - var recommendedSharedFolderMethod = SelectedPackage.RecommendedSharedFolderMethod; - await SelectedPackage.SetupModelFolders(installLocation, recommendedSharedFolderMethod); + var setupModelFoldersStep = new SetupModelFoldersStep( + SelectedPackage, + recommendedSharedFolderMethod, + installLocation + ); + steps.Add(setupModelFoldersStep); var installedPackage = new InstalledPackage { @@ -186,15 +196,23 @@ public partial class OneClickInstallViewModel : ViewModelBase PreferredTorchVersion = torchVersion, PreferredSharedFolderMethod = recommendedSharedFolderMethod }; - await using var st = settingsManager.BeginTransaction(); - st.Settings.InstalledPackages.Add(installedPackage); - st.Settings.ActiveInstalledPackageId = installedPackage.Id; - EventManager.Instance.OnInstalledPackagesChanged(); - HeaderText = Resources.Progress_InstallationComplete; - SubSubHeaderText = string.Empty; - OneClickInstallProgress = 100; + var addInstalledPackageStep = new AddInstalledPackageStep( + settingsManager, + installedPackage + ); + steps.Add(addInstalledPackageStep); + + var runner = new PackageModificationRunner + { + ShowDialogOnStart = true, + HideCloseButton = true, + }; + EventManager.Instance.OnPackageInstallProgressAdded(runner); + await runner.ExecuteSteps(steps); + EventManager.Instance.OnInstalledPackagesChanged(); + HeaderText = $"{SelectedPackage.DisplayName} installed successfully"; for (var i = 3; i > 0; i--) { SubHeaderText = $"{Resources.Text_ProceedingToLaunchPage} ({i}s)"; @@ -209,45 +227,16 @@ public partial class OneClickInstallViewModel : ViewModelBase } } - private async Task DownloadPackage( - string installLocation, - DownloadPackageVersionOptions versionOptions - ) + partial void OnShowIncompatiblePackagesChanged(bool value) { - SubHeaderText = Resources.Progress_DownloadingPackage; + var filteredPackages = packageFactory + .GetAllAvailablePackages() + .Where(p => p.OfferInOneClickInstaller && (ShowIncompatiblePackages || p.IsCompatible)) + .ToList(); - var progress = new Progress(progress => - { - IsIndeterminate = progress.IsIndeterminate; - OneClickInstallProgress = Convert.ToInt32(progress.Percentage); - EventManager.Instance.OnGlobalProgressChanged(OneClickInstallProgress); - }); - - await SelectedPackage.DownloadPackage(installLocation, versionOptions, progress); - SubHeaderText = Resources.Progress_DownloadComplete; - OneClickInstallProgress = 100; - } - - private async Task InstallPackage( - string installLocation, - TorchVersion torchVersion, - DownloadPackageVersionOptions versionOptions - ) - { - var progress = new Progress(progress => - { - SubHeaderText = Resources.Progress_InstallingPackageRequirements; - IsIndeterminate = progress.IsIndeterminate; - OneClickInstallProgress = Convert.ToInt32(progress.Percentage); - EventManager.Instance.OnGlobalProgressChanged(OneClickInstallProgress); - }); - - await SelectedPackage.InstallPackage( - installLocation, - torchVersion, - versionOptions, - progress, - (output) => SubSubHeaderText = output.Text + AllPackages = new ObservableCollection( + filteredPackages.Any() ? filteredPackages : packageFactory.GetAllAvailablePackages() ); + SelectedPackage = AllPackages[0]; } } diff --git a/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs index 0e98284b..8caab91b 100644 --- a/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs @@ -43,6 +43,7 @@ namespace StabilityMatrix.Avalonia.ViewModels; public partial class OutputsPageViewModel : PageViewModelBase { private readonly ISettingsManager settingsManager; + private readonly IPackageFactory packageFactory; private readonly INotificationService notificationService; private readonly INavigationService navigationService; private readonly ILogger logger; @@ -76,7 +77,8 @@ public partial class OutputsPageViewModel : PageViewModelBase [ObservableProperty] private string searchQuery; - public bool CanShowOutputTypes => SelectedCategory.Name.Equals("Shared Output Folder"); + public bool CanShowOutputTypes => + SelectedCategory?.Name?.Equals("Shared Output Folder") ?? false; public string NumImagesSelected => NumItemsSelected == 1 @@ -92,6 +94,7 @@ public partial class OutputsPageViewModel : PageViewModelBase ) { this.settingsManager = settingsManager; + this.packageFactory = packageFactory; this.notificationService = notificationService; this.navigationService = navigationService; this.logger = logger; @@ -131,10 +134,17 @@ public partial class OutputsPageViewModel : PageViewModelBase { NumItemsSelected = Outputs.Count(o => o.IsSelected); }); + } - if (!settingsManager.IsLibraryDirSet || Design.IsDesignMode) + public override void OnLoaded() + { + if (Design.IsDesignMode) + return; + + if (!settingsManager.IsLibraryDirSet) return; + Directory.CreateDirectory(settingsManager.ImagesDirectory); var packageCategories = settingsManager.Settings.InstalledPackages .Where(x => !x.UseSharedOutputFolder) .Select(p => @@ -164,12 +174,6 @@ public partial class OutputsPageViewModel : PageViewModelBase SelectedCategory = Categories.First(); SelectedOutputType = SharedOutputType.All; SearchQuery = string.Empty; - } - - public override void OnLoaded() - { - if (Design.IsDesignMode) - return; var path = CanShowOutputTypes && SelectedOutputType != SharedOutputType.All diff --git a/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs index 22c4dd10..d68a7676 100644 --- a/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using AsyncAwaitBestPractices; using Avalonia.Controls; using Avalonia.Controls.Notifications; +using Avalonia.Controls.Primitives; using DynamicData; using DynamicData.Binding; using FluentAvalonia.UI.Controls; @@ -122,17 +123,18 @@ public partial class PackageManagerViewModel : PageViewModelBase public async Task ShowInstallDialog(BasePackage? selectedPackage = null) { var viewModel = dialogFactory.Get(); - viewModel.AvailablePackages = packageFactory.GetAllAvailablePackages().ToImmutableArray(); viewModel.SelectedPackage = selectedPackage ?? viewModel.AvailablePackages[0]; var dialog = new BetterContentDialog { MaxDialogWidth = 900, MinDialogWidth = 900, + FullSizeDesired = true, DefaultButton = ContentDialogButton.Close, IsPrimaryButtonEnabled = false, IsSecondaryButtonEnabled = false, IsFooterVisible = false, + ContentVerticalScrollBarVisibility = ScrollBarVisibility.Disabled, Content = new InstallerDialog { DataContext = viewModel } }; diff --git a/StabilityMatrix.Avalonia/ViewModels/Progress/PackageInstallProgressItemViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Progress/PackageInstallProgressItemViewModel.cs index dc410872..9cb0eb3d 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Progress/PackageInstallProgressItemViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Progress/PackageInstallProgressItemViewModel.cs @@ -17,7 +17,10 @@ public class PackageInstallProgressItemViewModel : ProgressItemViewModelBase private readonly IPackageModificationRunner packageModificationRunner; private BetterContentDialog? dialog; - public PackageInstallProgressItemViewModel(IPackageModificationRunner packageModificationRunner) + public PackageInstallProgressItemViewModel( + IPackageModificationRunner packageModificationRunner, + bool hideCloseButton = false + ) { this.packageModificationRunner = packageModificationRunner; Id = packageModificationRunner.Id; @@ -25,6 +28,7 @@ public class PackageInstallProgressItemViewModel : ProgressItemViewModelBase Progress.Value = packageModificationRunner.CurrentProgress.Percentage; Progress.Text = packageModificationRunner.ConsoleOutput.LastOrDefault(); Progress.IsIndeterminate = packageModificationRunner.CurrentProgress.IsIndeterminate; + Progress.HideCloseButton = hideCloseButton; Progress.Console.StartUpdates(); diff --git a/StabilityMatrix.Avalonia/ViewModels/Progress/ProgressManagerViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Progress/ProgressManagerViewModel.cs index 51b88020..fc0a8a41 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Progress/ProgressManagerViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Progress/ProgressManagerViewModel.cs @@ -120,19 +120,22 @@ public partial class ProgressManagerViewModel : PageViewModelBase } } - private async Task AddPackageInstall(IPackageModificationRunner packageModificationRunner) + private Task AddPackageInstall(IPackageModificationRunner packageModificationRunner) { if (ProgressItems.Any(vm => vm.Id == packageModificationRunner.Id)) { - return; + return Task.CompletedTask; } - var vm = new PackageInstallProgressItemViewModel(packageModificationRunner); + var vm = new PackageInstallProgressItemViewModel( + packageModificationRunner, + packageModificationRunner.HideCloseButton + ); ProgressItems.Add(vm); - if (packageModificationRunner.ShowDialogOnStart) - { - await vm.ShowProgressDialog(); - } + + return packageModificationRunner.ShowDialogOnStart + ? vm.ShowProgressDialog() + : Task.CompletedTask; } private void ShowFailedNotification(string title, string message) diff --git a/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml b/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml index 02316e69..513144cd 100644 --- a/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml +++ b/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml @@ -16,12 +16,12 @@ x:Class="StabilityMatrix.Avalonia.Views.Dialogs.InstallerDialog"> + RowDefinitions="Auto, Auto, Auto, Auto, *, Auto"> @@ -33,18 +33,123 @@ TextWrapping="Wrap" Foreground="{DynamicResource ThemeRedColor}" IsVisible="{Binding SelectedPackage.Disclaimer, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -61,7 +166,7 @@ - + - +