From b9cb81e9a9fc9f5335f2af60e5e9eeaa759d07a0 Mon Sep 17 00:00:00 2001 From: JT Date: Sun, 27 Aug 2023 00:23:10 -0700 Subject: [PATCH 01/15] install/update/versioning refactor & new install dialog layout wip --- .../DesignData/DesignData.cs | 12 +- .../ViewModels/Dialogs/InstallerViewModel.cs | 83 ++-- .../Dialogs/OneClickInstallViewModel.cs | 39 +- .../Dialogs/PackageImportViewModel.cs | 49 +- .../PackageManager/PackageCardViewModel.cs | 6 +- .../Views/Dialogs/InstallerDialog.axaml | 425 ++++++++---------- .../Models/DownloadPackageVersionOptions.cs | 8 + .../Models/InstalledPackage.cs | 44 +- .../Models/InstalledPackageVersion.cs | 20 + .../Models/Packages/A3WebUI.cs | 10 +- .../Models/Packages/BaseGitPackage.cs | 128 +++--- .../Models/Packages/BasePackage.cs | 25 +- .../Models/Packages/ComfyUI.cs | 18 +- .../Models/Packages/DankDiffusion.cs | 5 - .../Models/Packages/Fooocus.cs | 6 +- .../Models/Packages/InvokeAI.cs | 46 +- .../Models/Packages/PackageVersionOptions.cs | 7 + .../Models/Packages/UnknownPackage.cs | 11 +- .../Models/Packages/VladAutomatic.cs | 65 +-- .../Models/Packages/VoltaML.cs | 8 +- .../Models/SharedFolderMethod.cs | 8 + .../Services/ISettingsManager.cs | 2 +- .../Services/SettingsManager.cs | 8 +- .../Services/TrackedDownloadService.cs | 4 +- 24 files changed, 556 insertions(+), 481 deletions(-) create mode 100644 StabilityMatrix.Core/Models/DownloadPackageVersionOptions.cs create mode 100644 StabilityMatrix.Core/Models/InstalledPackageVersion.cs create mode 100644 StabilityMatrix.Core/Models/Packages/PackageVersionOptions.cs create mode 100644 StabilityMatrix.Core/Models/SharedFolderMethod.cs diff --git a/StabilityMatrix.Avalonia/DesignData/DesignData.cs b/StabilityMatrix.Avalonia/DesignData/DesignData.cs index b3b3379e..7d378412 100644 --- a/StabilityMatrix.Avalonia/DesignData/DesignData.cs +++ b/StabilityMatrix.Avalonia/DesignData/DesignData.cs @@ -54,9 +54,11 @@ public static class DesignData { Id = activePackageId, DisplayName = "My Installed Package", - DisplayVersion = "v1.0.0", PackageName = "stable-diffusion-webui", - PackageVersion = "v1.0.0", + Version = new InstalledPackageVersion + { + InstalledReleaseVersion = "v1.0.0" + }, LibraryPath = $"Packages{Path.DirectorySeparatorChar}example-webui", LastUpdateCheck = DateTimeOffset.Now }, @@ -65,7 +67,11 @@ public static class DesignData Id = Guid.NewGuid(), DisplayName = "Dank Diffusion", PackageName = "ComfyUI", - DisplayVersion = "main@ab73d4a", + Version = new InstalledPackageVersion + { + InstalledBranch = "master", + InstalledCommitSha = "abc12uwu345568972abaedf7g7e679a98879e879f87ga8" + }, LibraryPath = $"Packages{Path.DirectorySeparatorChar}example-webui", LastUpdateCheck = DateTimeOffset.Now } diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs index d9f99a7b..f44ca8bf 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs @@ -42,14 +42,13 @@ public partial class InstallerViewModel : ContentDialogViewModelBase [ObservableProperty] private BasePackage selectedPackage; [ObservableProperty] private PackageVersion? selectedVersion; - [ObservableProperty] private IReadOnlyList? availablePackages; [ObservableProperty] private ObservableCollection? availableCommits; [ObservableProperty] private ObservableCollection? availableVersions; - [ObservableProperty] private GitCommit? selectedCommit; - [ObservableProperty] private string? releaseNotes; + [ObservableProperty] private string latestVersionText = string.Empty; + [ObservableProperty] private bool isAdvancedMode; // Version types (release or commit) [ObservableProperty] @@ -107,23 +106,19 @@ public partial class InstallerViewModel : ContentDialogViewModelBase // Check for updates try { + var versionOptions = await SelectedPackage.GetAllVersionOptions(); if (IsReleaseMode) { - var versions = (await SelectedPackage.GetAllVersions()).ToList(); - AvailableVersions = new ObservableCollection(versions); + AvailableVersions = + new ObservableCollection(versionOptions.AvailableVersions); if (!AvailableVersions.Any()) return; SelectedVersion = AvailableVersions[0]; } else { - var branches = (await SelectedPackage.GetAllBranches()).ToList(); - AvailableVersions = new ObservableCollection(branches.Select(b => - new PackageVersion - { - TagName = b.Name, - ReleaseNotesMarkdown = b.Commit.Label - })); + AvailableVersions = + new ObservableCollection(versionOptions.AvailableBranches); UpdateSelectedVersionToLatestMain(); } @@ -173,9 +168,7 @@ public partial class InstallerViewModel : ContentDialogViewModelBase try { await InstallGitIfNecessary(); - - SelectedPackage.InstallLocation = Path.Combine( - settingsManager.LibraryDir, "Packages", InstallName); + var installLocation = Path.Combine(settingsManager.LibraryDir, "Packages", InstallName); if (!PyRunner.PipInstalled || !PyRunner.VenvInstalled) { @@ -193,45 +186,41 @@ public partial class InstallerViewModel : ContentDialogViewModelBase } } - string version; + var downloadOptions = new DownloadPackageVersionOptions(); + var installedVersion = new InstalledPackageVersion(); if (IsReleaseMode) { - version = SelectedVersion?.TagName ?? - throw new NullReferenceException("Selected version is null"); - - await DownloadPackage(version, false, null); + downloadOptions.VersionTag = SelectedVersion?.TagName ?? + throw new NullReferenceException("Selected version is null"); + installedVersion.InstalledReleaseVersion = downloadOptions.VersionTag; } else { - version = SelectedCommit?.Sha ?? + downloadOptions.CommitHash = SelectedCommit?.Sha ?? throw new NullReferenceException("Selected commit is null"); - - await DownloadPackage(version, true, SelectedVersion!.TagName); + installedVersion.InstalledBranch = SelectedVersion?.TagName ?? + throw new NullReferenceException("Selected version is null"); + installedVersion.InstalledCommitSha = downloadOptions.CommitHash; } - await InstallPackage(); + await DownloadPackage(installLocation, downloadOptions); + await InstallPackage(installLocation); InstallProgress.Text = "Setting up shared folder links..."; - await SelectedPackage.SetupModelFolders(SelectedPackage.InstallLocation); - //sharedFolders.SetupLinksForPackage(SelectedPackage, SelectedPackage.InstallLocation); + await SelectedPackage.SetupModelFolders(installLocation); InstallProgress.Text = "Done"; InstallProgress.IsIndeterminate = false; InstallProgress.Value = 100; EventManager.Instance.OnGlobalProgressChanged(100); - var branch = SelectedVersionType == PackageVersionType.GithubRelease ? - null : SelectedVersion!.TagName; - var package = new InstalledPackage { DisplayName = InstallName, LibraryPath = Path.Combine("Packages", InstallName), Id = Guid.NewGuid(), PackageName = SelectedPackage.Name, - PackageVersion = version, - DisplayVersion = GetDisplayVersion(version, branch), - InstalledBranch = branch, + Version = installedVersion, LaunchCommand = SelectedPackage.LaunchCommand, LastUpdateCheck = DateTimeOffset.Now }; @@ -270,22 +259,22 @@ public partial class InstallerViewModel : ContentDialogViewModelBase { return branch == null ? version : $"{branch}@{version[..7]}"; } - - private Task DownloadPackage(string version, bool isCommitHash, string? branch) + + private async Task DownloadPackage(string installLocation, DownloadPackageVersionOptions downloadOptions) { InstallProgress.Text = "Downloading package..."; - + var progress = new Progress(progress => { InstallProgress.IsIndeterminate = progress.IsIndeterminate; InstallProgress.Value = progress.Percentage; EventManager.Instance.OnGlobalProgressChanged((int) progress.Percentage); }); - - return SelectedPackage.DownloadPackage(version, isCommitHash, branch, progress); + + await SelectedPackage.DownloadPackage(installLocation, downloadOptions, progress); } - private async Task InstallPackage() + private async Task InstallPackage(string installLocation) { InstallProgress.Text = "Installing package..."; SelectedPackage.ConsoleOutput += SelectedPackageOnConsoleOutput; @@ -298,7 +287,7 @@ public partial class InstallerViewModel : ContentDialogViewModelBase EventManager.Instance.OnGlobalProgressChanged((int) progress.Percentage); }); - await SelectedPackage.InstallPackage(progress); + await SelectedPackage.InstallPackage(installLocation, progress); } finally { @@ -360,20 +349,23 @@ public partial class InstallerViewModel : ContentDialogViewModelBase AvailableVersionTypes = SelectedPackage.ShouldIgnoreReleases ? PackageVersionType.Commit : PackageVersionType.GithubRelease | PackageVersionType.Commit; + + IsReleaseMode = !SelectedPackage.ShouldIgnoreReleases; if (Design.IsDesignMode) return; Dispatcher.UIThread.InvokeAsync(async () => { Logger.Debug($"Release mode: {IsReleaseMode}"); - var versions = (await value.GetAllVersions(IsReleaseMode)).ToList(); + var versionOptions = await value.GetAllVersionOptions(); - if (!versions.Any()) return; + AvailableVersions = IsReleaseModeAvailable + ? new ObservableCollection(versionOptions.AvailableVersions) + : new ObservableCollection(versionOptions.AvailableBranches); - AvailableVersions = new ObservableCollection(versions); - Logger.Debug($"Available versions: {string.Join(", ", AvailableVersions)}"); SelectedVersion = AvailableVersions[0]; - ReleaseNotes = versions.First().ReleaseNotesMarkdown; + ReleaseNotes = SelectedVersion.ReleaseNotesMarkdown; + Logger.Debug($"Loaded release notes for {ReleaseNotes}"); if (!IsReleaseMode) @@ -387,6 +379,9 @@ public partial class InstallerViewModel : ContentDialogViewModelBase } InstallName = SelectedPackage.DisplayName; + LatestVersionText = IsReleaseMode + ? $"Latest version: {SelectedVersion.TagName}" + : $"Branch: {SelectedVersion.TagName}"; }).SafeFireAndForget(); } diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs index e2ff39cb..8c0df723 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs @@ -107,16 +107,30 @@ public partial class OneClickInstallViewModel : ViewModelBase // get latest version & download & install SubHeaderText = "Getting latest version..."; - var latestVersion = await SelectedPackage.GetLatestVersion(); - SelectedPackage.InstallLocation = - Path.Combine(libraryDir, "Packages", SelectedPackage.Name); + var installLocation = Path.Combine(libraryDir, "Packages", SelectedPackage.Name); SelectedPackage.ConsoleOutput += (_, output) => SubSubHeaderText = output.Text; + + var downloadVersion = new DownloadPackageVersionOptions(); + var installedVersion = new InstalledPackageVersion(); + + var versionOptions = await SelectedPackage.GetAllVersionOptions(); + if (versionOptions.AvailableVersions != null && versionOptions.AvailableVersions.Any()) + { + downloadVersion.VersionTag = + versionOptions.AvailableVersions.First().TagName; + installedVersion.InstalledReleaseVersion = downloadVersion.VersionTag; + } + else + { + downloadVersion.BranchName = await SelectedPackage.GetLatestVersion(); + installedVersion.InstalledBranch = downloadVersion.BranchName; + } - await DownloadPackage(latestVersion); - await InstallPackage(); + await DownloadPackage(installLocation, downloadVersion); + await InstallPackage(installLocation); SubHeaderText = "Setting up shared folder links..."; - await SelectedPackage.SetupModelFolders(SelectedPackage.InstallLocation); + await SelectedPackage.SetupModelFolders(installLocation); var installedPackage = new InstalledPackage { @@ -124,8 +138,7 @@ public partial class OneClickInstallViewModel : ViewModelBase LibraryPath = Path.Combine("Packages", SelectedPackage.Name), Id = Guid.NewGuid(), PackageName = SelectedPackage.Name, - PackageVersion = latestVersion, - DisplayVersion = latestVersion, + Version = installedVersion, LaunchCommand = SelectedPackage.LaunchCommand, LastUpdateCheck = DateTimeOffset.Now }; @@ -148,7 +161,7 @@ public partial class OneClickInstallViewModel : ViewModelBase EventManager.Instance.OnOneClickInstallFinished(false); } - private async Task DownloadPackage(string version) + private async Task DownloadPackage(string installLocation, DownloadPackageVersionOptions versionOptions) { SubHeaderText = "Downloading package..."; @@ -158,13 +171,13 @@ public partial class OneClickInstallViewModel : ViewModelBase OneClickInstallProgress = Convert.ToInt32(progress.Percentage); EventManager.Instance.OnGlobalProgressChanged(OneClickInstallProgress); }); - - await SelectedPackage.DownloadPackage(version, false, version, progress); + + await SelectedPackage.DownloadPackage(installLocation, versionOptions, progress); SubHeaderText = "Download Complete"; OneClickInstallProgress = 100; } - private async Task InstallPackage() + private async Task InstallPackage(string installLocation) { SelectedPackage.ConsoleOutput += (_, output) => SubSubHeaderText = output.Text; SubHeaderText = "Downloading and installing package requirements..."; @@ -177,6 +190,6 @@ public partial class OneClickInstallViewModel : ViewModelBase EventManager.Instance.OnGlobalProgressChanged(OneClickInstallProgress); }); - await SelectedPackage.InstallPackage(progress); + await SelectedPackage.InstallPackage(installLocation, progress); } } diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageImportViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageImportViewModel.cs index 428f5635..7edbceaf 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageImportViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageImportViewModel.cs @@ -77,23 +77,19 @@ public partial class PackageImportViewModel : ContentDialogViewModelBase // Populate available versions try { + var versionOptions = await SelectedBasePackage.GetAllVersionOptions(); if (IsReleaseMode) { - var versions = (await SelectedBasePackage.GetAllVersions()).ToList(); - AvailableVersions = new ObservableCollection(versions); + AvailableVersions = + new ObservableCollection(versionOptions.AvailableVersions); if (!AvailableVersions.Any()) return; SelectedVersion = AvailableVersions[0]; } else { - var branches = (await SelectedBasePackage.GetAllBranches()).ToList(); - AvailableVersions = new ObservableCollection(branches.Select(b => - new PackageVersion - { - TagName = b.Name, - ReleaseNotesMarkdown = b.Commit.Label - })); + AvailableVersions = + new ObservableCollection(versionOptions.AvailableBranches); UpdateSelectedVersionToLatestMain(); } } @@ -134,20 +130,19 @@ public partial class PackageImportViewModel : ContentDialogViewModelBase AvailableVersions?.Clear(); AvailableCommits?.Clear(); - AvailableVersionTypes = SelectedBasePackage.ShouldIgnoreReleases - ? PackageVersionType.Commit - : PackageVersionType.GithubRelease | PackageVersionType.Commit; + AvailableVersionTypes = SelectedBasePackage.AvailableVersionTypes; if (Design.IsDesignMode) return; Dispatcher.UIThread.InvokeAsync(async () => { Logger.Debug($"Release mode: {IsReleaseMode}"); - var versions = (await value.GetAllVersions(IsReleaseMode)).ToList(); - - if (!versions.Any()) return; + var versionOptions = await value.GetAllVersionOptions(); - AvailableVersions = new ObservableCollection(versions); + AvailableVersions = IsReleaseModeAvailable + ? new ObservableCollection(versionOptions.AvailableVersions) + : new ObservableCollection(versionOptions.AvailableBranches); + Logger.Debug($"Available versions: {string.Join(", ", AvailableVersions)}"); SelectedVersion = AvailableVersions[0]; @@ -188,30 +183,30 @@ public partial class PackageImportViewModel : ContentDialogViewModelBase if (SelectedBasePackage is null || PackagePath is null) return; - string version; + var version = new InstalledPackageVersion(); if (IsReleaseMode) { - version = SelectedVersion?.TagName ?? - throw new NullReferenceException("Selected version is null"); + version.InstalledReleaseVersion = SelectedVersion?.TagName ?? + throw new NullReferenceException( + "Selected version is null"); } else { - version = SelectedCommit?.Sha ?? - throw new NullReferenceException("Selected commit is null"); + version.InstalledBranch = SelectedVersion?.TagName ?? + throw new NullReferenceException( + "Selected version is null"); + version.InstalledCommitSha = SelectedCommit?.Sha ?? + throw new NullReferenceException( + "Selected commit is null"); } - var branch = SelectedVersionType == PackageVersionType.GithubRelease ? - null : SelectedVersion!.TagName; - var package = new InstalledPackage { Id = Guid.NewGuid(), DisplayName = PackagePath.Name, PackageName = SelectedBasePackage.Name, LibraryPath = $"Packages{Path.DirectorySeparatorChar}{PackagePath.Name}", - PackageVersion = version, - DisplayVersion = GetDisplayVersion(version, branch), - InstalledBranch = branch, + Version = version, LaunchCommand = SelectedBasePackage.LaunchCommand, LastUpdateCheck = DateTimeOffset.Now, }; diff --git a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs index 04f33294..2398a06d 100644 --- a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs @@ -74,7 +74,7 @@ public partial class PackageCardViewModel : ProgressViewModel var basePackage = packageFactory[value.PackageName]; CardImageSource = basePackage?.PreviewImageUri.ToString() ?? Assets.NoImage.ToString(); - InstalledVersion = value.DisplayVersion ?? "Unknown"; + InstalledVersion = value.Version?.DisplayVersion ?? "Unknown"; } } @@ -168,8 +168,6 @@ public partial class PackageCardViewModel : ProgressViewModel try { - basePackage.InstallLocation = Package.FullPath!; - var progress = new Progress(progress => { var percent = Convert.ToInt32(progress.Percentage); @@ -195,7 +193,7 @@ public partial class PackageCardViewModel : ProgressViewModel Package.UpdateAvailable = false; } IsUpdateAvailable = false; - InstalledVersion = Package.DisplayVersion ?? "Unknown"; + InstalledVersion = updateResult.DisplayVersion ?? "Unknown"; EventManager.Instance.OnProgressChanged(new ProgressItem(progressId, packageName, diff --git a/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml b/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml index 36961405..5a4b63f9 100644 --- a/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml +++ b/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml @@ -1,249 +1,204 @@  + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls" + xmlns:ui="using:FluentAvalonia.UI.Controls" + xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData" + xmlns:dialogs="clr-namespace:StabilityMatrix.Avalonia.ViewModels.Dialogs" + xmlns:packages="clr-namespace:StabilityMatrix.Core.Models.Packages;assembly=StabilityMatrix.Core" + xmlns:models="clr-namespace:StabilityMatrix.Core.Models;assembly=StabilityMatrix.Core" + xmlns:database="clr-namespace:StabilityMatrix.Core.Models.Database;assembly=StabilityMatrix.Core" + x:DataType="dialogs:InstallerViewModel" + mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="700" + d:DataContext="{x:Static mocks:DesignData.InstallerViewModel}" + x:Class="StabilityMatrix.Avalonia.Views.Dialogs.InstallerDialog"> - - - - - - - - - - - - - - - - - - - - + Foreground="{DynamicResource ThemeRedColor}" + Margin="0,8,8,8" + TextAlignment="Left" + TextWrapping="Wrap"> + + + + + - - - - + + + + + + + + + + + - - - + + + + + string.IsNullOrWhiteSpace(Sha) ? string.Empty : Sha[..7]; } From bb686a581f4981562643403051fb1aa634eba74d Mon Sep 17 00:00:00 2001 From: JT Date: Thu, 31 Aug 2023 11:27:30 -0700 Subject: [PATCH 03/15] Added SharedFolderMethod & TorchVersion options to Advanced Install Options --- StabilityMatrix.Avalonia/App.axaml.cs | 6 +- .../Controls/BetterContentDialog.cs | 6 + .../ContentDialogProgressViewModelBase.cs | 26 ++ .../ContentDialogViewModelBase.cs | 3 +- .../ViewModels/Dialogs/EnvVarsViewModel.cs | 1 + .../ViewModels/Dialogs/InstallerViewModel.cs | 222 ++++++----------- .../Dialogs/LaunchOptionsViewModel.cs | 1 + .../Dialogs/OneClickInstallViewModel.cs | 17 +- .../Dialogs/PackageImportViewModel.cs | 8 +- .../PackageModificationDialogViewModel.cs | 60 +++++ .../Dialogs/SelectDataDirectoryViewModel.cs | 1 + .../Dialogs/SelectModelVersionViewModel.cs | 1 + .../ViewModels/Dialogs/UpdateViewModel.cs | 1 + .../ViewModels/LaunchPageViewModel.cs | 3 +- .../PackageManager/PackageCardViewModel.cs | 7 +- .../ViewModels/PackageManagerViewModel.cs | 33 ++- .../Views/Dialogs/InstallerDialog.axaml | 234 +++++++++--------- .../Dialogs/PackageModificationDialog.axaml | 41 +++ .../PackageModificationDialog.axaml.cs | 49 ++++ StabilityMatrix.Core/Helper/SharedFolders.cs | 5 +- .../Models/InstalledPackage.cs | 15 +- .../AddInstalledPackageStep.cs | 26 ++ .../DownloadPackageVersionStep.cs | 24 ++ .../IPackageModificationRunner.cs | 12 + .../PackageModification/InstallPackageStep.cs | 33 +++ .../PackageModificationRunner.cs | 31 +++ .../Models/PackageModification/PackageStep.cs | 9 + .../SetupModelFoldersStep.cs | 28 +++ .../SetupPrerequisitesStep.cs | 43 ++++ StabilityMatrix.Core/Models/PackageVersion.cs | 1 + .../Models/Packages/A3WebUI.cs | 97 +++++--- .../Models/Packages/BaseGitPackage.cs | 45 ++-- .../Models/Packages/BasePackage.cs | 93 ++++++- .../Models/Packages/ComfyUI.cs | 124 +++++++--- .../Models/Packages/DankDiffusion.cs | 13 +- .../Models/Packages/Fooocus.cs | 68 ++++- .../Models/Packages/InvokeAI.cs | 120 +++++---- .../Models/Packages/UnknownPackage.cs | 26 +- .../Models/Packages/VladAutomatic.cs | 151 ++++++++--- .../Models/Packages/VoltaML.cs | 34 ++- .../Models/SharedFolderMethod.cs | 2 +- StabilityMatrix.Core/Models/TorchVersion.cs | 11 + .../StabilityMatrix.Core.csproj | 1 + 43 files changed, 1230 insertions(+), 502 deletions(-) create mode 100644 StabilityMatrix.Avalonia/ViewModels/Base/ContentDialogProgressViewModelBase.cs rename StabilityMatrix.Avalonia/ViewModels/{Dialogs => Base}/ContentDialogViewModelBase.cs (88%) create mode 100644 StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageModificationDialogViewModel.cs create mode 100644 StabilityMatrix.Avalonia/Views/Dialogs/PackageModificationDialog.axaml create mode 100644 StabilityMatrix.Avalonia/Views/Dialogs/PackageModificationDialog.axaml.cs create mode 100644 StabilityMatrix.Core/Models/PackageModification/AddInstalledPackageStep.cs create mode 100644 StabilityMatrix.Core/Models/PackageModification/DownloadPackageVersionStep.cs create mode 100644 StabilityMatrix.Core/Models/PackageModification/IPackageModificationRunner.cs create mode 100644 StabilityMatrix.Core/Models/PackageModification/InstallPackageStep.cs create mode 100644 StabilityMatrix.Core/Models/PackageModification/PackageModificationRunner.cs create mode 100644 StabilityMatrix.Core/Models/PackageModification/PackageStep.cs create mode 100644 StabilityMatrix.Core/Models/PackageModification/SetupModelFoldersStep.cs create mode 100644 StabilityMatrix.Core/Models/PackageModification/SetupPrerequisitesStep.cs create mode 100644 StabilityMatrix.Core/Models/TorchVersion.cs diff --git a/StabilityMatrix.Avalonia/App.axaml.cs b/StabilityMatrix.Avalonia/App.axaml.cs index 78baebba..2a5f4e8e 100644 --- a/StabilityMatrix.Avalonia/App.axaml.cs +++ b/StabilityMatrix.Avalonia/App.axaml.cs @@ -53,6 +53,7 @@ using StabilityMatrix.Core.Helper.Cache; using StabilityMatrix.Core.Helper.Factory; using StabilityMatrix.Core.Models.Api; using StabilityMatrix.Core.Models.Configs; +using StabilityMatrix.Core.Models.PackageModification; using StabilityMatrix.Core.Models.Packages; using StabilityMatrix.Core.Models.Settings; using StabilityMatrix.Core.Python; @@ -322,6 +323,7 @@ public sealed class App : Application services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + //services.AddSingleton(); } private static IServiceCollection ConfigureServices() @@ -346,6 +348,7 @@ public sealed class App : Application services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(provider => @@ -391,7 +394,8 @@ public sealed class App : Application // if (string.IsNullOrWhiteSpace(githubApiKey)) // return client; // - // client.Credentials = new Credentials(githubApiKey); + // client.Credentials = + // new Credentials(""); return client; }); diff --git a/StabilityMatrix.Avalonia/Controls/BetterContentDialog.cs b/StabilityMatrix.Avalonia/Controls/BetterContentDialog.cs index 732715a3..43d1a5d6 100644 --- a/StabilityMatrix.Avalonia/Controls/BetterContentDialog.cs +++ b/StabilityMatrix.Avalonia/Controls/BetterContentDialog.cs @@ -156,6 +156,12 @@ public class BetterContentDialog : ContentDialog viewModel.SecondaryButtonClick += OnDialogButtonClick; viewModel.CloseButtonClick += OnDialogButtonClick; } + else if ((Content as Control)?.DataContext is ContentDialogProgressViewModelBase progressViewModel) + { + progressViewModel.PrimaryButtonClick += OnDialogButtonClick; + progressViewModel.SecondaryButtonClick += OnDialogButtonClick; + progressViewModel.CloseButtonClick += OnDialogButtonClick; + } // If commands provided, bind OnCanExecuteChanged to hide buttons // otherwise link visibility to IsEnabled diff --git a/StabilityMatrix.Avalonia/ViewModels/Base/ContentDialogProgressViewModelBase.cs b/StabilityMatrix.Avalonia/ViewModels/Base/ContentDialogProgressViewModelBase.cs new file mode 100644 index 00000000..eb775e18 --- /dev/null +++ b/StabilityMatrix.Avalonia/ViewModels/Base/ContentDialogProgressViewModelBase.cs @@ -0,0 +1,26 @@ +using System; +using FluentAvalonia.UI.Controls; + +namespace StabilityMatrix.Avalonia.ViewModels.Base; + +public class ContentDialogProgressViewModelBase : ProgressViewModel +{ + public event EventHandler? PrimaryButtonClick; + public event EventHandler? SecondaryButtonClick; + public event EventHandler? CloseButtonClick; + + public virtual void OnPrimaryButtonClick() + { + PrimaryButtonClick?.Invoke(this, ContentDialogResult.Primary); + } + + public virtual void OnSecondaryButtonClick() + { + SecondaryButtonClick?.Invoke(this, ContentDialogResult.Secondary); + } + + public virtual void OnCloseButtonClick() + { + CloseButtonClick?.Invoke(this, ContentDialogResult.None); + } +} diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/ContentDialogViewModelBase.cs b/StabilityMatrix.Avalonia/ViewModels/Base/ContentDialogViewModelBase.cs similarity index 88% rename from StabilityMatrix.Avalonia/ViewModels/Dialogs/ContentDialogViewModelBase.cs rename to StabilityMatrix.Avalonia/ViewModels/Base/ContentDialogViewModelBase.cs index 61a139f1..c82b6721 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/ContentDialogViewModelBase.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Base/ContentDialogViewModelBase.cs @@ -1,8 +1,7 @@ using System; using FluentAvalonia.UI.Controls; -using StabilityMatrix.Avalonia.ViewModels.Base; -namespace StabilityMatrix.Avalonia.ViewModels.Dialogs; +namespace StabilityMatrix.Avalonia.ViewModels.Base; public class ContentDialogViewModelBase : ViewModelBase { diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/EnvVarsViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/EnvVarsViewModel.cs index 52b255f5..5c96fa80 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/EnvVarsViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/EnvVarsViewModel.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using Avalonia.Collections; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Models; diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs index da6f0671..808bb078 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs @@ -17,10 +17,12 @@ using FluentAvalonia.UI.Controls; using NLog; using StabilityMatrix.Avalonia.Controls; using StabilityMatrix.Avalonia.Services; +using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper.Factory; using StabilityMatrix.Core.Models; using StabilityMatrix.Core.Models.Database; +using StabilityMatrix.Core.Models.PackageModification; using StabilityMatrix.Core.Models.Packages; using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Processes; @@ -49,6 +51,13 @@ public partial class InstallerViewModel : ContentDialogViewModelBase [ObservableProperty] private string? releaseNotes; [ObservableProperty] private string latestVersionText = string.Empty; [ObservableProperty] private bool isAdvancedMode; + [ObservableProperty] private bool showDuplicateWarning; + [ObservableProperty] private string? installName; + [ObservableProperty] private SharedFolderMethod selectedSharedFolderMethod; + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(ShowTorchVersionOptions))] + private TorchVersion selectedTorchVersion; // Version types (release or commit) [ObservableProperty] @@ -60,20 +69,19 @@ public partial class InstallerViewModel : ContentDialogViewModelBase [NotifyPropertyChangedFor(nameof(IsReleaseModeAvailable))] private PackageVersionType availableVersionTypes = PackageVersionType.GithubRelease | PackageVersionType.Commit; + + public string ReleaseLabelText => IsReleaseMode ? "Version" : "Branch"; public bool IsReleaseMode { get => SelectedVersionType == PackageVersionType.GithubRelease; set => SelectedVersionType = value ? PackageVersionType.GithubRelease : PackageVersionType.Commit; } - public bool IsReleaseModeAvailable => AvailableVersionTypes.HasFlag(PackageVersionType.GithubRelease); + public bool ShowTorchVersionOptions => SelectedTorchVersion != TorchVersion.None; - [ObservableProperty] private bool showDuplicateWarning; - - [ObservableProperty] private string? installName; - - public Base.ProgressViewModel InstallProgress { get; } = new(); + public ProgressViewModel InstallProgress { get; } = new(); + public IEnumerable Steps { get; set; } public InstallerViewModel( ISettingsManager settingsManager, @@ -113,7 +121,7 @@ public partial class InstallerViewModel : ContentDialogViewModelBase new ObservableCollection(versionOptions.AvailableVersions); if (!AvailableVersions.Any()) return; - SelectedVersion = AvailableVersions[0]; + SelectedVersion = AvailableVersions.First(x => !x.IsPrerelease); } else { @@ -136,9 +144,6 @@ public partial class InstallerViewModel : ContentDialogViewModelBase var result = await notificationService.TryAsync(ActuallyInstall(), "Could not install package"); if (result.IsSuccessful) { - notificationService.Show(new Notification( - $"Package {SelectedPackage.Name} installed successfully!", - "Success", NotificationType.Success)); OnPrimaryButtonClick(); } else @@ -156,83 +161,72 @@ public partial class InstallerViewModel : ContentDialogViewModelBase } } - private async Task ActuallyInstall() + private Task ActuallyInstall() { if (string.IsNullOrWhiteSpace(InstallName)) { notificationService.Show(new Notification("Package name is empty", "Please enter a name for the package", NotificationType.Error)); - return; + return Task.CompletedTask; } - try + var installLocation = Path.Combine(settingsManager.LibraryDir, "Packages", InstallName); + var prereqStep = new SetupPrerequisitesStep(prerequisiteHelper, pyRunner); + + var downloadOptions = new DownloadPackageVersionOptions(); + var installedVersion = new InstalledPackageVersion(); + if (IsReleaseMode) { - await InstallGitIfNecessary(); - var installLocation = Path.Combine(settingsManager.LibraryDir, "Packages", InstallName); - - if (!PyRunner.PipInstalled || !PyRunner.VenvInstalled) - { - InstallProgress.Text = "Installing dependencies..."; - InstallProgress.IsIndeterminate = true; - await pyRunner.Initialize(); - - if (!PyRunner.PipInstalled) - { - await pyRunner.SetupPip(); - } - if (!PyRunner.VenvInstalled) - { - await pyRunner.InstallPackage("virtualenv"); - } - } + downloadOptions.VersionTag = SelectedVersion?.TagName ?? + throw new NullReferenceException("Selected version is null"); + installedVersion.InstalledReleaseVersion = downloadOptions.VersionTag; + } + else + { + downloadOptions.CommitHash = SelectedCommit?.Sha ?? + throw new NullReferenceException("Selected commit is null"); + installedVersion.InstalledBranch = SelectedVersion?.TagName ?? + throw new NullReferenceException("Selected version is null"); + installedVersion.InstalledCommitSha = downloadOptions.CommitHash; + } - var downloadOptions = new DownloadPackageVersionOptions(); - var installedVersion = new InstalledPackageVersion(); - if (IsReleaseMode) - { - downloadOptions.VersionTag = SelectedVersion?.TagName ?? - throw new NullReferenceException("Selected version is null"); - installedVersion.InstalledReleaseVersion = downloadOptions.VersionTag; - } - else - { - downloadOptions.CommitHash = SelectedCommit?.Sha ?? - throw new NullReferenceException("Selected commit is null"); - installedVersion.InstalledBranch = SelectedVersion?.TagName ?? - throw new NullReferenceException("Selected version is null"); - installedVersion.InstalledCommitSha = downloadOptions.CommitHash; - } - - await DownloadPackage(installLocation, downloadOptions); - await InstallPackage(installLocation); + var downloadStep = + new DownloadPackageVersionStep(SelectedPackage, installLocation, downloadOptions); + var installStep = new InstallPackageStep(SelectedPackage, SelectedTorchVersion, installLocation); + var setupModelFoldersStep = new SetupModelFoldersStep(SelectedPackage, + SelectedSharedFolderMethod, installLocation); - InstallProgress.Text = "Setting up shared folder links..."; - await SelectedPackage.SetupModelFolders(installLocation); - - InstallProgress.Text = "Done"; - InstallProgress.IsIndeterminate = false; - InstallProgress.Value = 100; - EventManager.Instance.OnGlobalProgressChanged(100); + var package = new InstalledPackage + { + DisplayName = InstallName, + LibraryPath = Path.Combine("Packages", InstallName), + Id = Guid.NewGuid(), + PackageName = SelectedPackage.Name, + Version = installedVersion, + LaunchCommand = SelectedPackage.LaunchCommand, + LastUpdateCheck = DateTimeOffset.Now, + PreferredTorchVersion = SelectedTorchVersion, + PreferredSharedFolderMethod = SelectedSharedFolderMethod + }; - var package = new InstalledPackage - { - DisplayName = InstallName, - LibraryPath = Path.Combine("Packages", InstallName), - Id = Guid.NewGuid(), - PackageName = SelectedPackage.Name, - Version = installedVersion, - LaunchCommand = SelectedPackage.LaunchCommand, - LastUpdateCheck = DateTimeOffset.Now - }; - await using var st = settingsManager.BeginTransaction(); - st.Settings.InstalledPackages.Add(package); - st.Settings.ActiveInstalledPackageId = package.Id; - } - finally + var addInstalledPackageStep = new AddInstalledPackageStep(settingsManager, package); + + var steps = new List { - InstallProgress.Value = 0; - InstallProgress.IsIndeterminate = false; - } + prereqStep, + downloadStep, + installStep, + setupModelFoldersStep, + addInstalledPackageStep + }; + + Steps = steps; + return Task.CompletedTask; + } + + public void Cancel() + { + OnCloseButtonClick(); } private void UpdateSelectedVersionToLatestMain() @@ -255,51 +249,6 @@ public partial class InstallerViewModel : ContentDialogViewModelBase } } - private static string GetDisplayVersion(string version, string? branch) - { - return branch == null ? version : $"{branch}@{version[..7]}"; - } - - private async Task DownloadPackage(string installLocation, DownloadPackageVersionOptions downloadOptions) - { - InstallProgress.Text = "Downloading package..."; - - var progress = new Progress(progress => - { - InstallProgress.IsIndeterminate = progress.IsIndeterminate; - InstallProgress.Value = progress.Percentage; - EventManager.Instance.OnGlobalProgressChanged((int) progress.Percentage); - }); - - await SelectedPackage.DownloadPackage(installLocation, downloadOptions, progress); - } - - private async Task InstallPackage(string installLocation) - { - InstallProgress.Text = "Installing package..."; - SelectedPackage.ConsoleOutput += SelectedPackageOnConsoleOutput; - try - { - var progress = new Progress(progress => - { - InstallProgress.IsIndeterminate = progress.IsIndeterminate; - InstallProgress.Value = progress.Percentage; - EventManager.Instance.OnGlobalProgressChanged((int) progress.Percentage); - }); - - await SelectedPackage.InstallPackage(installLocation, progress); - } - finally - { - SelectedPackage.ConsoleOutput -= SelectedPackageOnConsoleOutput; - } - } - - private void SelectedPackageOnConsoleOutput(object? sender, ProcessOutput e) - { - InstallProgress.Description = e.Text; - } - [RelayCommand] private async Task ShowPreview() { @@ -350,6 +299,9 @@ public partial class InstallerViewModel : ContentDialogViewModelBase ? PackageVersionType.Commit : PackageVersionType.GithubRelease | PackageVersionType.Commit; + SelectedSharedFolderMethod = SelectedPackage.RecommendedSharedFolderMethod; + SelectedTorchVersion = SelectedPackage.GetRecommendedTorchVersion(); + if (Design.IsDesignMode) return; Dispatcher.UIThread.InvokeAsync(async () => @@ -361,7 +313,7 @@ public partial class InstallerViewModel : ContentDialogViewModelBase ? new ObservableCollection(versionOptions.AvailableVersions) : new ObservableCollection(versionOptions.AvailableBranches); - SelectedVersion = AvailableVersions[0]; + SelectedVersion = AvailableVersions.First(x => !x.IsPrerelease); ReleaseNotes = SelectedVersion.ReleaseNotesMarkdown; Logger.Debug($"Loaded release notes for {ReleaseNotes}"); @@ -383,34 +335,6 @@ public partial class InstallerViewModel : ContentDialogViewModelBase }).SafeFireAndForget(); } - private async Task InstallGitIfNecessary() - { - var progressHandler = new Progress(progress => - { - if (progress.Message != null && progress.Message.Contains("Downloading")) - { - InstallProgress.Text = $"Downloading prerequisites... {progress.Percentage:N0}%"; - } - else if (progress.Type == ProgressType.Extract) - { - InstallProgress.Text = $"Installing git... {progress.Percentage:N0}%"; - } - else if (progress.Title != null && progress.Title.Contains("Unpacking")) - { - InstallProgress.Text = $"Unpacking resources... {progress.Percentage:N0}%"; - } - else - { - InstallProgress.Text = progress.Message; - } - - InstallProgress.IsIndeterminate = progress.IsIndeterminate; - InstallProgress.Value = Convert.ToInt32(progress.Percentage); - }); - - await prerequisiteHelper.InstallAllIfNecessary(progressHandler); - } - partial void OnInstallNameChanged(string? value) { ShowDuplicateWarning = diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/LaunchOptionsViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/LaunchOptionsViewModel.cs index 9979fd6f..a4d2e4a6 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/LaunchOptionsViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/LaunchOptionsViewModel.cs @@ -8,6 +8,7 @@ using System.Threading; using CommunityToolkit.Mvvm.ComponentModel; using FuzzySharp; using Microsoft.Extensions.Logging; +using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.Views.Dialogs; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Helper.Cache; diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs index 8c0df723..40aff597 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs @@ -126,11 +126,14 @@ public partial class OneClickInstallViewModel : ViewModelBase installedVersion.InstalledBranch = downloadVersion.BranchName; } - await DownloadPackage(installLocation, downloadVersion); - await InstallPackage(installLocation); + var torchVersion = SelectedPackage.GetRecommendedTorchVersion(); + await DownloadPackage(installLocation, downloadVersion); + await InstallPackage(installLocation, torchVersion); + SubHeaderText = "Setting up shared folder links..."; - await SelectedPackage.SetupModelFolders(installLocation); + var recommendedSharedFolderMethod = SelectedPackage.RecommendedSharedFolderMethod; + await SelectedPackage.SetupModelFolders(installLocation, recommendedSharedFolderMethod); var installedPackage = new InstalledPackage { @@ -140,7 +143,9 @@ public partial class OneClickInstallViewModel : ViewModelBase PackageName = SelectedPackage.Name, Version = installedVersion, LaunchCommand = SelectedPackage.LaunchCommand, - LastUpdateCheck = DateTimeOffset.Now + LastUpdateCheck = DateTimeOffset.Now, + PreferredTorchVersion = torchVersion, + PreferredSharedFolderMethod = recommendedSharedFolderMethod }; await using var st = settingsManager.BeginTransaction(); st.Settings.InstalledPackages.Add(installedPackage); @@ -177,7 +182,7 @@ public partial class OneClickInstallViewModel : ViewModelBase OneClickInstallProgress = 100; } - private async Task InstallPackage(string installLocation) + private async Task InstallPackage(string installLocation, TorchVersion torchVersion) { SelectedPackage.ConsoleOutput += (_, output) => SubSubHeaderText = output.Text; SubHeaderText = "Downloading and installing package requirements..."; @@ -190,6 +195,6 @@ public partial class OneClickInstallViewModel : ViewModelBase EventManager.Instance.OnGlobalProgressChanged(OneClickInstallProgress); }); - await SelectedPackage.InstallPackage(installLocation, progress); + await SelectedPackage.InstallPackage(installLocation, torchVersion, progress); } } diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageImportViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageImportViewModel.cs index 7edbceaf..8e8e4db3 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageImportViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageImportViewModel.cs @@ -10,6 +10,7 @@ using Avalonia.Controls; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; using NLog; +using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.Views.Dialogs; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Helper.Factory; @@ -200,6 +201,8 @@ public partial class PackageImportViewModel : ContentDialogViewModelBase "Selected commit is null"); } + var torchVersion = SelectedBasePackage.GetRecommendedTorchVersion(); + var sharedFolderRecommendation = SelectedBasePackage.RecommendedSharedFolderMethod; var package = new InstalledPackage { Id = Guid.NewGuid(), @@ -209,6 +212,8 @@ public partial class PackageImportViewModel : ContentDialogViewModelBase Version = version, LaunchCommand = SelectedBasePackage.LaunchCommand, LastUpdateCheck = DateTimeOffset.Now, + PreferredTorchVersion = torchVersion, + PreferredSharedFolderMethod = sharedFolderRecommendation }; // Recreate venv if it's a BaseGitPackage @@ -218,7 +223,8 @@ public partial class PackageImportViewModel : ContentDialogViewModelBase } // Reconfigure shared links - await SelectedBasePackage.UpdateModelFolders(PackagePath); + var recommendedSharedFolderMethod = SelectedBasePackage.RecommendedSharedFolderMethod; + await SelectedBasePackage.UpdateModelFolders(PackagePath, recommendedSharedFolderMethod); settingsManager.Transaction(s => s.InstalledPackages.Add(package)); } diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageModificationDialogViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageModificationDialogViewModel.cs new file mode 100644 index 00000000..86a940cb --- /dev/null +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageModificationDialogViewModel.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Avalonia.Controls.Notifications; +using StabilityMatrix.Avalonia.Services; +using StabilityMatrix.Avalonia.ViewModels.Base; +using StabilityMatrix.Core.Helper; +using StabilityMatrix.Core.Models.PackageModification; +using StabilityMatrix.Core.Models.Progress; + +namespace StabilityMatrix.Avalonia.ViewModels.Dialogs; + +public class PackageModificationDialogViewModel : ContentDialogProgressViewModelBase +{ + private readonly IPackageModificationRunner packageModificationRunner; + private readonly INotificationService notificationService; + private readonly IEnumerable steps; + + public PackageModificationDialogViewModel(IPackageModificationRunner packageModificationRunner, + INotificationService notificationService, IEnumerable steps) + { + this.packageModificationRunner = packageModificationRunner; + this.notificationService = notificationService; + this.steps = steps; + } + + public ConsoleViewModel Console { get; } = new(); + + public override async Task OnLoadedAsync() + { + // idk why this is getting called twice + if (!packageModificationRunner.IsRunning) + { + packageModificationRunner.ProgressChanged += PackageModificationRunnerOnProgressChanged; + await packageModificationRunner.ExecuteSteps(steps.ToList()); + + notificationService.Show("Package Install Completed", + "Package install completed successfully.", NotificationType.Success); + + OnCloseButtonClick(); + } + } + + private void PackageModificationRunnerOnProgressChanged(object? sender, ProgressReport e) + { + Text = string.IsNullOrWhiteSpace(e.Title) + ? packageModificationRunner.CurrentStep?.ProgressTitle + : e.Title; + + Value = e.Percentage; + Description = e.Message; + IsIndeterminate = e.IsIndeterminate; + + if (string.IsNullOrWhiteSpace(e.Message) || e.Message.Equals("Downloading...")) + return; + + Console.PostLine(e.Message); + EventManager.Instance.OnScrollToBottomRequested(); + } +} diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectDataDirectoryViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectDataDirectoryViewModel.cs index 25e8928e..fcdcf647 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectDataDirectoryViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectDataDirectoryViewModel.cs @@ -9,6 +9,7 @@ using Avalonia.Platform.Storage; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using NLog; +using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.Views.Dialogs; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Helper; diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectModelVersionViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectModelVersionViewModel.cs index 4d643c26..1c8a5107 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectModelVersionViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectModelVersionViewModel.cs @@ -9,6 +9,7 @@ using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; using FluentAvalonia.UI.Controls; using StabilityMatrix.Avalonia.Models; +using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Core.Services; namespace StabilityMatrix.Avalonia.ViewModels.Dialogs; diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/UpdateViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/UpdateViewModel.cs index 22a6b9ab..7f76be2f 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/UpdateViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/UpdateViewModel.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using AsyncAwaitBestPractices; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.Views.Dialogs; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Helper; diff --git a/StabilityMatrix.Avalonia/ViewModels/LaunchPageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/LaunchPageViewModel.cs index 1b8d3131..1ecbca3f 100644 --- a/StabilityMatrix.Avalonia/ViewModels/LaunchPageViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/LaunchPageViewModel.cs @@ -233,7 +233,8 @@ public partial class LaunchPageViewModel : PageViewModelBase, IDisposable, IAsyn Console.StartUpdates(); // Update shared folder links (in case library paths changed) - await basePackage.UpdateModelFolders(packagePath); + await basePackage.UpdateModelFolders(packagePath, + activeInstall.PreferredSharedFolderMethod ?? basePackage.RecommendedSharedFolderMethod); // Load user launch args from settings and convert to string var userArgs = settingsManager.GetLaunchArgs(activeInstall.Id); diff --git a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs index 2398a06d..257ef71f 100644 --- a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs @@ -180,8 +180,11 @@ public partial class PackageCardViewModel : ProgressViewModel EventManager.Instance.OnProgressChanged(new ProgressItem(progressId, packageName, progress)); }); - - var updateResult = await basePackage.Update(Package, progress); + + var torchVersion = Package.PreferredTorchVersion ?? + basePackage.GetRecommendedTorchVersion(); + + var updateResult = await basePackage.Update(Package, torchVersion, progress); settingsManager.UpdatePackageVersionNumber(Package.Id, updateResult); notificationService.Show("Update complete", diff --git a/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs index ac98e1b6..82c4db59 100644 --- a/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs @@ -21,6 +21,7 @@ using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper.Factory; using StabilityMatrix.Core.Models; using StabilityMatrix.Core.Models.FileInterfaces; +using StabilityMatrix.Core.Models.PackageModification; using StabilityMatrix.Core.Services; using Symbol = FluentIcons.Common.Symbol; using SymbolIconSource = FluentIcons.FluentAvalonia.SymbolIconSource; @@ -37,6 +38,8 @@ public partial class PackageManagerViewModel : PageViewModelBase private readonly ISettingsManager settingsManager; private readonly IPackageFactory packageFactory; private readonly ServiceManager dialogFactory; + private readonly IPackageModificationRunner packageModificationRunner; + private readonly INotificationService notificationService; public override string Title => "Packages"; public override IconSource IconSource => @@ -61,12 +64,16 @@ public partial class PackageManagerViewModel : PageViewModelBase public PackageManagerViewModel( ISettingsManager settingsManager, IPackageFactory packageFactory, - ServiceManager dialogFactory + ServiceManager dialogFactory, + IPackageModificationRunner packageModificationRunner, + INotificationService notificationService ) { this.settingsManager = settingsManager; this.packageFactory = packageFactory; this.dialogFactory = dialogFactory; + this.packageModificationRunner = packageModificationRunner; + this.notificationService = notificationService; EventManager.Instance.InstalledPackagesChanged += OnInstalledPackagesChanged; @@ -115,7 +122,7 @@ public partial class PackageManagerViewModel : PageViewModelBase var dialog = new BetterContentDialog { - MaxDialogWidth = 1100, + MaxDialogWidth = 900, MinDialogWidth = 900, DefaultButton = ContentDialogButton.Close, IsPrimaryButtonEnabled = false, @@ -124,7 +131,27 @@ public partial class PackageManagerViewModel : PageViewModelBase Content = new InstallerDialog { DataContext = viewModel } }; - await dialog.ShowAsync(); + var result = await dialog.ShowAsync(); + if (result == ContentDialogResult.Primary) + { + var steps = viewModel.Steps; + var packageModificationDialogViewModel = + new PackageModificationDialogViewModel(packageModificationRunner, notificationService, steps); + + dialog = new BetterContentDialog + { + MaxDialogWidth = 900, + MinDialogWidth = 900, + DefaultButton = ContentDialogButton.Close, + IsPrimaryButtonEnabled = false, + IsSecondaryButtonEnabled = false, + IsFooterVisible = false, + Content = new PackageModificationDialog {DataContext = packageModificationDialogViewModel} + }; + + await dialog.ShowAsync(); + } + await OnLoadedAsync(); } diff --git a/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml b/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml index cdb8e15f..0128c3a7 100644 --- a/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml +++ b/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml @@ -9,8 +9,6 @@ xmlns:packages="clr-namespace:StabilityMatrix.Core.Models.Packages;assembly=StabilityMatrix.Core" xmlns:models="clr-namespace:StabilityMatrix.Core.Models;assembly=StabilityMatrix.Core" xmlns:database="clr-namespace:StabilityMatrix.Core.Models.Database;assembly=StabilityMatrix.Core" - xmlns:avalonia="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia" - xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages" x:DataType="dialogs:InstallerViewModel" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="700" d:DataContext="{x:Static mocks:DesignData.InstallerViewModel}" @@ -19,18 +17,6 @@ - - - - - - - - - - - - + ColumnDefinitions="Auto, Auto, Auto, Auto"> - - + + + + + + + + + + + + + + + + + diff --git a/StabilityMatrix.Core/Helper/EventManager.cs b/StabilityMatrix.Core/Helper/EventManager.cs index 025f7de5..8c90c373 100644 --- a/StabilityMatrix.Core/Helper/EventManager.cs +++ b/StabilityMatrix.Core/Helper/EventManager.cs @@ -1,4 +1,5 @@ using StabilityMatrix.Core.Models; +using StabilityMatrix.Core.Models.PackageModification; using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Models.Update; @@ -10,11 +11,8 @@ public class EventManager { public static EventManager Instance { get; } = new(); - private EventManager() - { + private EventManager() { } - } - public event EventHandler? GlobalProgressChanged; public event EventHandler? InstalledPackagesChanged; public event EventHandler? OneClickInstallFinished; @@ -24,20 +22,42 @@ public class EventManager public event EventHandler? PackageLaunchRequested; public event EventHandler? ScrollToBottomRequested; public event EventHandler? ProgressChanged; - public event EventHandler? RunningPackageStatusChanged; - - 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 event EventHandler? RunningPackageStatusChanged; + + public event EventHandler? PackageInstallProgressAdded; + public event EventHandler? ToggleProgressFlyout; + + 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 OnDevModeSettingChanged(bool value) => DevModeSettingChanged?.Invoke(this, value); + public void OnUpdateAvailable(UpdateInfo args) => UpdateAvailable?.Invoke(this, args); + public void OnPackageLaunchRequested(Guid packageId) => PackageLaunchRequested?.Invoke(this, packageId); + public void OnScrollToBottomRequested() => 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) => - 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); } diff --git a/StabilityMatrix.Core/Models/PackageModification/AddInstalledPackageStep.cs b/StabilityMatrix.Core/Models/PackageModification/AddInstalledPackageStep.cs index 1b431c16..434e06a0 100644 --- a/StabilityMatrix.Core/Models/PackageModification/AddInstalledPackageStep.cs +++ b/StabilityMatrix.Core/Models/PackageModification/AddInstalledPackageStep.cs @@ -19,10 +19,15 @@ public class AddInstalledPackageStep : IPackageStep public async Task ExecuteAsync(IProgress? progress = null) { + if (!string.IsNullOrWhiteSpace(newInstalledPackage.DisplayName)) + { + settingsManager.PackageInstallsInProgress.Remove(newInstalledPackage.DisplayName); + } + await using var transaction = settingsManager.BeginTransaction(); transaction.Settings.InstalledPackages.Add(newInstalledPackage); transaction.Settings.ActiveInstalledPackageId = newInstalledPackage.Id; } - public string ProgressTitle => "Finishing up..."; + public string ProgressTitle => $"{newInstalledPackage.DisplayName} Installed"; } diff --git a/StabilityMatrix.Core/Models/PackageModification/IPackageModificationRunner.cs b/StabilityMatrix.Core/Models/PackageModification/IPackageModificationRunner.cs index a39dfae2..c5d6d008 100644 --- a/StabilityMatrix.Core/Models/PackageModification/IPackageModificationRunner.cs +++ b/StabilityMatrix.Core/Models/PackageModification/IPackageModificationRunner.cs @@ -9,4 +9,6 @@ public interface IPackageModificationRunner ProgressReport CurrentProgress { get; set; } IPackageStep? CurrentStep { get; set; } event EventHandler? ProgressChanged; + List ConsoleOutput { get; } + Guid Id { get; } } diff --git a/StabilityMatrix.Core/Models/PackageModification/PackageModificationRunner.cs b/StabilityMatrix.Core/Models/PackageModification/PackageModificationRunner.cs index 0f884fee..48990902 100644 --- a/StabilityMatrix.Core/Models/PackageModification/PackageModificationRunner.cs +++ b/StabilityMatrix.Core/Models/PackageModification/PackageModificationRunner.cs @@ -6,9 +6,14 @@ public class PackageModificationRunner : IPackageModificationRunner { public async Task ExecuteSteps(IReadOnlyList steps) { - var progress = new Progress(report => + IProgress progress = new Progress(report => { CurrentProgress = report; + if (!string.IsNullOrWhiteSpace(report.Message)) + { + ConsoleOutput.Add(report.Message); + } + OnProgressChanged(report); }); @@ -19,12 +24,18 @@ public class PackageModificationRunner : IPackageModificationRunner await step.ExecuteAsync(progress).ConfigureAwait(false); } + progress.Report( + new ProgressReport(1f, message: "Package Install Complete", isIndeterminate: false) + ); + IsRunning = false; } public bool IsRunning { get; set; } public ProgressReport CurrentProgress { get; set; } public IPackageStep? CurrentStep { get; set; } + public List ConsoleOutput { get; } = new(); + public Guid Id { get; } = Guid.NewGuid(); public event EventHandler? ProgressChanged; diff --git a/StabilityMatrix.Core/Models/PackageModification/SetPackageInstallingStep.cs b/StabilityMatrix.Core/Models/PackageModification/SetPackageInstallingStep.cs new file mode 100644 index 00000000..9867fd50 --- /dev/null +++ b/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? progress = null) + { + settingsManager.PackageInstallsInProgress.Add(packageName); + return Task.CompletedTask; + } + + public string ProgressTitle => "Starting Package Installation"; +} diff --git a/StabilityMatrix.Core/Models/Packages/VladAutomatic.cs b/StabilityMatrix.Core/Models/Packages/VladAutomatic.cs index c363c7c9..e98b367c 100644 --- a/StabilityMatrix.Core/Models/Packages/VladAutomatic.cs +++ b/StabilityMatrix.Core/Models/Packages/VladAutomatic.cs @@ -101,6 +101,12 @@ public class VladAutomatic : BaseGitPackage Options = new() { "--lowvram", "--medvram" } }, new() + { + Name = "Auto-Launch Web UI", + Type = LaunchOptionType.Bool, + Options = new() { "--autolaunch" } + }, + new() { Name = "Force use of Intel OneAPI XPU backend", Type = LaunchOptionType.Bool, diff --git a/StabilityMatrix.Core/Models/Progress/ProgressItem.cs b/StabilityMatrix.Core/Models/Progress/ProgressItem.cs index 244c7d04..dd3d7551 100644 --- a/StabilityMatrix.Core/Models/Progress/ProgressItem.cs +++ b/StabilityMatrix.Core/Models/Progress/ProgressItem.cs @@ -1,3 +1,8 @@ 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 +); diff --git a/StabilityMatrix.Core/Services/ISettingsManager.cs b/StabilityMatrix.Core/Services/ISettingsManager.cs index aec86465..0bd1030a 100644 --- a/StabilityMatrix.Core/Services/ISettingsManager.cs +++ b/StabilityMatrix.Core/Services/ISettingsManager.cs @@ -15,6 +15,7 @@ public interface ISettingsManager string ModelsDirectory { get; } string DownloadsDirectory { get; } Settings Settings { get; } + List PackageInstallsInProgress { get; set; } event EventHandler? LibraryDirChanged; event EventHandler? SettingsPropertyChanged; diff --git a/StabilityMatrix.Core/Services/SettingsManager.cs b/StabilityMatrix.Core/Services/SettingsManager.cs index 076f1034..43fddf82 100644 --- a/StabilityMatrix.Core/Services/SettingsManager.cs +++ b/StabilityMatrix.Core/Services/SettingsManager.cs @@ -56,6 +56,7 @@ public class SettingsManager : ISettingsManager private string SettingsPath => Path.Combine(LibraryDir, "settings.json"); public string ModelsDirectory => Path.Combine(LibraryDir, "Models"); public string DownloadsDirectory => Path.Combine(LibraryDir, ".downloads"); + public List PackageInstallsInProgress { get; set; } = new(); public Settings Settings { get; private set; } = new(); From 28dd51844c282a3e47464772a893dc3af58e77bd Mon Sep 17 00:00:00 2001 From: JT Date: Wed, 6 Sep 2023 23:32:13 -0700 Subject: [PATCH 08/15] Forgot the stupid task file --- .husky/task-runner.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.husky/task-runner.json b/.husky/task-runner.json index 8201d1d3..cedfb34b 100644 --- a/.husky/task-runner.json +++ b/.husky/task-runner.json @@ -11,7 +11,7 @@ "name": "Run xamlstyler", "group": "pre-commit", "command": "dotnet", - "args": [ "xstyler", "${staged}" ], + "args": [ "xstyler", "-f", "${staged}" ], "include": [ "**/*.axaml" ] } ] From 3f53bc16090e3b6e2e408fb075c0fe416677dc91 Mon Sep 17 00:00:00 2001 From: JT Date: Thu, 7 Sep 2023 00:33:55 -0700 Subject: [PATCH 09/15] Fix merge weirdness --- .config/dotnet-tools.json | 8 +- .github/workflows/release.yml | 23 +-- CHANGELOG.md | 11 +- StabilityMatrix.Avalonia/Assets.cs | 146 ++++++++++++------ StabilityMatrix.Avalonia/Program.cs | 118 ++++++++------ .../ViewModels/Dialogs/UpdateViewModel.cs | 11 +- 6 files changed, 205 insertions(+), 112 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index b1761c61..5b4bb470 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -13,6 +13,12 @@ "commands": [ "xstyler" ] + }, + "csharpier": { + "version": "0.25.0", + "commands": [ + "dotnet-csharpier" + ] } } -} \ No newline at end of file +} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 80287394..80d6cdd1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,5 +1,8 @@ name: Release +permissions: + contents: write + on: workflow_dispatch: inputs: @@ -82,20 +85,7 @@ jobs: path: ${{ env.out-name }} - name: Create Sentry release - if: ${{ github.event_name == 'release' || github.event.inputs.sentry-release == 'true' }} - uses: getsentry/action-release@v1 - env: - MAKE_SENTRY_RELEASE: ${{ secrets.SENTRY_PROJECT != '' }} - SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - SENTRY_ORG: ${{ secrets.SENTRY_ORG }} - SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} - with: - environment: production - ignore_missing: true - version: StabilityMatrix.Avalonia@${{ env.GIT_TAG_NAME }} - - - name: Create Sentry release - if: ${{ github.event_name == 'workflow_dispatch' }} + if: ${{ github.event.inputs.sentry-release == 'true' }} uses: getsentry/action-release@v1 env: MAKE_SENTRY_RELEASE: ${{ secrets.SENTRY_PROJECT != '' }} @@ -105,8 +95,9 @@ jobs: with: environment: production ignore_missing: true + ignore_empty: true version: StabilityMatrix.Avalonia@${{ github.event.inputs.version }} - + release-windows: name: Release (win-x64) @@ -206,4 +197,4 @@ jobs: tag_name: v${{ github.event.inputs.version }} body: ${{ steps.release_notes.outputs.release_notes }} draft: ${{ github.event.inputs.github-release-draft == 'true' }} - prerelease: ${{ github.event.inputs.github-release-prerelease == 'true' }} + prerelease: ${{ github.event.inputs.github-release-prerelease == 'true' }} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 16b32fcb..f764e7ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,16 @@ All notable changes to Stability Matrix will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html). -## v2.3.4 +## v2.4.0 ### Changed -- Revamped Package Installer dialog with some more advanced options +- Revamped package installer + - Added "advanced options" section for commit, shared folder method, and pytorch options + - Can be run in the background + - Shows progress in the Downloads tab + +## v2.3.4 +### Fixed +- Fixed [#108](https://github.com/LykosAI/StabilityMatrix/issues/108) - (Linux) Fixed permission error on updates [#103](https://github.com/LykosAI/StabilityMatrix/pull/103) ## v2.3.3 ### Fixed diff --git a/StabilityMatrix.Avalonia/Assets.cs b/StabilityMatrix.Avalonia/Assets.cs index 8e11c868..6b06b2ea 100644 --- a/StabilityMatrix.Avalonia/Assets.cs +++ b/StabilityMatrix.Avalonia/Assets.cs @@ -12,73 +12,133 @@ internal static class Assets { public static AvaloniaResource AppIcon { get; } = new("avares://StabilityMatrix.Avalonia/Assets/Icon.ico"); - + public static AvaloniaResource AppIconPng { get; } = new("avares://StabilityMatrix.Avalonia/Assets/Icon.png"); - + /// /// Fixed image for models with no images. /// public static Uri NoImage { get; } = new("avares://StabilityMatrix.Avalonia/Assets/noimage.png"); - - public static AvaloniaResource LicensesJson => new( - "avares://StabilityMatrix.Avalonia/Assets/licenses.json"); + + public static AvaloniaResource LicensesJson => + new("avares://StabilityMatrix.Avalonia/Assets/licenses.json"); + + private const UnixFileMode unix755 = + UnixFileMode.UserRead + | UnixFileMode.UserWrite + | UnixFileMode.UserExecute + | UnixFileMode.GroupRead + | UnixFileMode.GroupExecute + | UnixFileMode.OtherRead + | UnixFileMode.OtherExecute; [SupportedOSPlatform("windows")] [SupportedOSPlatform("linux")] [SupportedOSPlatform("macos")] - public static AvaloniaResource SevenZipExecutable => Compat.Switch( - (PlatformKind.Windows, - new AvaloniaResource("avares://StabilityMatrix.Avalonia/Assets/win-x64/7za.exe")), - (PlatformKind.Linux | PlatformKind.X64, - new AvaloniaResource("avares://StabilityMatrix.Avalonia/Assets/linux-x64/7zzs", (UnixFileMode) 0777)), - (PlatformKind.MacOS | PlatformKind.Arm, - new AvaloniaResource("avares://StabilityMatrix.Avalonia/Assets/macos-arm64/7zz", (UnixFileMode) 0x777))); - + public static AvaloniaResource SevenZipExecutable => + Compat.Switch( + ( + PlatformKind.Windows, + new AvaloniaResource("avares://StabilityMatrix.Avalonia/Assets/win-x64/7za.exe") + ), + ( + PlatformKind.Linux | PlatformKind.X64, + new AvaloniaResource( + "avares://StabilityMatrix.Avalonia/Assets/linux-x64/7zzs", + unix755 + ) + ), + ( + PlatformKind.MacOS | PlatformKind.Arm, + new AvaloniaResource( + "avares://StabilityMatrix.Avalonia/Assets/macos-arm64/7zz", + unix755 + ) + ) + ); + [SupportedOSPlatform("windows")] [SupportedOSPlatform("linux")] [SupportedOSPlatform("macos")] - public static AvaloniaResource SevenZipLicense => Compat.Switch( - (PlatformKind.Windows, new AvaloniaResource("avares://StabilityMatrix.Avalonia/Assets/win-x64/7za - LICENSE.txt")), - (PlatformKind.Linux | PlatformKind.X64, new AvaloniaResource("avares://StabilityMatrix.Avalonia/Assets/linux-x64/7zzs - LICENSE.txt")), - (PlatformKind.MacOS | PlatformKind.Arm, new AvaloniaResource("avares://StabilityMatrix.Avalonia/Assets/macos-arm64/7zz - LICENSE.txt"))); + public static AvaloniaResource SevenZipLicense => + Compat.Switch( + ( + PlatformKind.Windows, + new AvaloniaResource( + "avares://StabilityMatrix.Avalonia/Assets/win-x64/7za - LICENSE.txt" + ) + ), + ( + PlatformKind.Linux | PlatformKind.X64, + new AvaloniaResource( + "avares://StabilityMatrix.Avalonia/Assets/linux-x64/7zzs - LICENSE.txt" + ) + ), + ( + PlatformKind.MacOS | PlatformKind.Arm, + new AvaloniaResource( + "avares://StabilityMatrix.Avalonia/Assets/macos-arm64/7zz - LICENSE.txt" + ) + ) + ); + + public static AvaloniaResource PyScriptSiteCustomize => + new("avares://StabilityMatrix.Avalonia/Assets/sitecustomize.py"); - public static AvaloniaResource PyScriptSiteCustomize => new( - "avares://StabilityMatrix.Avalonia/Assets/sitecustomize.py"); - [SupportedOSPlatform("windows")] - public static AvaloniaResource PyScriptGetPip => new( - "avares://StabilityMatrix.Avalonia/Assets/win-x64/get-pip.pyc"); - + public static AvaloniaResource PyScriptGetPip => + new("avares://StabilityMatrix.Avalonia/Assets/win-x64/get-pip.pyc"); + [SupportedOSPlatform("windows")] - public static IEnumerable<(AvaloniaResource resource, string relativePath)> PyModuleVenv => + public static IEnumerable<(AvaloniaResource resource, string relativePath)> PyModuleVenv => FindAssets("win-x64/venv/"); - + [SupportedOSPlatform("windows")] [SupportedOSPlatform("linux")] [SupportedOSPlatform("macos")] - public static RemoteResource PythonDownloadUrl => Compat.Switch( - (PlatformKind.Windows | PlatformKind.X64, new RemoteResource( - new Uri("https://www.python.org/ftp/python/3.10.11/python-3.10.11-embed-amd64.zip"), - "608619f8619075629c9c69f361352a0da6ed7e62f83a0e19c63e0ea32eb7629d")), - (PlatformKind.Linux | PlatformKind.X64, new RemoteResource( - new Uri("https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.10.11+20230507-x86_64-unknown-linux-gnu-install_only.tar.gz"), - "c5bcaac91bc80bfc29cf510669ecad12d506035ecb3ad85ef213416d54aecd79")), - (PlatformKind.MacOS | PlatformKind.Arm, new RemoteResource( - new Uri("https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.10.11+20230507-aarch64-apple-darwin-install_only.tar.gz"), - "8348bc3c2311f94ec63751fb71bd0108174be1c4def002773cf519ee1506f96f"))); - - public static Uri DiscordServerUrl { get; } = - new("https://discord.com/invite/TUrgfECxHz"); - - public static Uri PatreonUrl { get; } = - new("https://patreon.com/StabilityMatrix"); - + public static RemoteResource PythonDownloadUrl => + Compat.Switch( + ( + PlatformKind.Windows | PlatformKind.X64, + new RemoteResource( + new Uri( + "https://www.python.org/ftp/python/3.10.11/python-3.10.11-embed-amd64.zip" + ), + "608619f8619075629c9c69f361352a0da6ed7e62f83a0e19c63e0ea32eb7629d" + ) + ), + ( + PlatformKind.Linux | PlatformKind.X64, + new RemoteResource( + new Uri( + "https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.10.11+20230507-x86_64-unknown-linux-gnu-install_only.tar.gz" + ), + "c5bcaac91bc80bfc29cf510669ecad12d506035ecb3ad85ef213416d54aecd79" + ) + ), + ( + PlatformKind.MacOS | PlatformKind.Arm, + new RemoteResource( + new Uri( + "https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.10.11+20230507-aarch64-apple-darwin-install_only.tar.gz" + ), + "8348bc3c2311f94ec63751fb71bd0108174be1c4def002773cf519ee1506f96f" + ) + ) + ); + + public static Uri DiscordServerUrl { get; } = new("https://discord.com/invite/TUrgfECxHz"); + + public static Uri PatreonUrl { get; } = new("https://patreon.com/StabilityMatrix"); + /// /// Yield AvaloniaResources given a relative directory path within the 'Assets' folder. /// - public static IEnumerable<(AvaloniaResource resource, string relativePath)> FindAssets(string relativeAssetPath) + public static IEnumerable<(AvaloniaResource resource, string relativePath)> FindAssets( + string relativeAssetPath + ) { var baseUri = new Uri("avares://StabilityMatrix.Avalonia/Assets/"); var targetUri = new Uri(baseUri, relativeAssetPath); diff --git a/StabilityMatrix.Avalonia/Program.cs b/StabilityMatrix.Avalonia/Program.cs index c693b224..b0021802 100644 --- a/StabilityMatrix.Avalonia/Program.cs +++ b/StabilityMatrix.Avalonia/Program.cs @@ -31,9 +31,9 @@ namespace StabilityMatrix.Avalonia; public class Program { public static AppArgs Args { get; } = new(); - + public static bool IsDebugBuild { get; private set; } - + // 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. @@ -47,28 +47,30 @@ public class Program Args.ResetWindowPosition = args.Contains("--reset-window-position"); SetDebugBuild(); - + HandleUpdateReplacement(); - - var infoVersion = Assembly.GetExecutingAssembly() - .GetCustomAttribute()?.InformationalVersion; + + var infoVersion = Assembly + .GetExecutingAssembly() + .GetCustomAttribute() + ?.InformationalVersion; Compat.AppVersion = SemVersion.Parse(infoVersion ?? "0.0.0", SemVersionStyles.Strict); - + // Configure exception dialog for unhandled exceptions if (!Debugger.IsAttached || Args.DebugExceptionDialog) { AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; } - + // Configure Sentry if (!Args.NoSentry && (!Debugger.IsAttached || Args.DebugSentry)) { ConfigureSentry(); } - + BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); } - + // Avalonia configuration, don't remove; also used by visual designer. public static AppBuilder BuildAvaloniaApp() { @@ -76,24 +78,23 @@ public class Program // Use our custom image loader for custom local load error handling ImageLoader.AsyncImageLoader.Dispose(); ImageLoader.AsyncImageLoader = new FallbackRamCachedWebImageLoader(); - - return AppBuilder.Configure() - .UsePlatformDetect() - .WithInterFont() - .LogToTrace(); + + return AppBuilder.Configure().UsePlatformDetect().WithInterFont().LogToTrace(); } private static void HandleUpdateReplacement() { // Check if we're in the named update folder or the legacy update folder for 1.2.0 -> 2.0.0 - if (Compat.AppCurrentDir is {Name: UpdateHelper.UpdateFolderName} or {Name: "Update"}) + if (Compat.AppCurrentDir is { Name: UpdateHelper.UpdateFolderName } or { Name: "Update" }) { var parentDir = Compat.AppCurrentDir.Parent; - if (parentDir is null) + if (parentDir is null) return; - + var retryDelays = Backoff.DecorrelatedJitterBackoffV2( - TimeSpan.FromMilliseconds(350), retryCount: 5); + TimeSpan.FromMilliseconds(350), + retryCount: 5 + ); foreach (var delay in retryDelays) { @@ -103,16 +104,25 @@ public class Program try { currentExe.CopyTo(targetExe, true); - + // Ensure permissions are set for unix if (Compat.IsUnix) { - File.SetUnixFileMode(targetExe, (UnixFileMode) 0x755); + File.SetUnixFileMode( + targetExe, // 0755 + UnixFileMode.UserRead + | UnixFileMode.UserWrite + | UnixFileMode.UserExecute + | UnixFileMode.GroupRead + | UnixFileMode.GroupExecute + | UnixFileMode.OtherRead + | UnixFileMode.OtherExecute + ); } - + // Start the new app Process.Start(targetExe); - + // Shutdown the current app Environment.Exit(0); } @@ -122,7 +132,7 @@ public class Program } } } - + // Delete update folder if it exists in current directory var updateDir = UpdateHelper.UpdateFolder; if (updateDir.Exists) @@ -138,12 +148,13 @@ public class Program } } } - + private static void ConfigureSentry() { SentrySdk.Init(o => { - o.Dsn = "https://eac7a5ea065d44cf9a8565e0f1817da2@o4505314753380352.ingest.sentry.io/4505314756067328"; + o.Dsn = + "https://eac7a5ea065d44cf9a8565e0f1817da2@o4505314753380352.ingest.sentry.io/4505314756067328"; o.StackTraceMode = StackTraceMode.Enhanced; o.TracesSampleRate = 1.0; o.IsGlobalModeEnabled = true; @@ -156,47 +167,56 @@ public class Program #endif }); } - - private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + + private static void CurrentDomain_UnhandledException( + object sender, + UnhandledExceptionEventArgs e + ) { - if (e.ExceptionObject is not Exception ex) return; - + if (e.ExceptionObject is not Exception ex) + return; + var logger = LogManager.GetCurrentClassLogger(); logger.Fatal(ex, "Unhandled {Type}: {Message}", ex.GetType().Name, ex.Message); - + if (SentrySdk.IsEnabled) { SentrySdk.CaptureException(ex); } - - if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime) + + if ( + Application.Current?.ApplicationLifetime + is IClassicDesktopStyleApplicationLifetime lifetime + ) { var dialog = new ExceptionDialog { - DataContext = new ExceptionViewModel - { - Exception = ex - } + DataContext = new ExceptionViewModel { Exception = ex } }; - + var mainWindow = lifetime.MainWindow; // We can only show dialog if main window exists, and is visible - if (mainWindow is {PlatformImpl: not null, IsVisible: true}) + if (mainWindow is { PlatformImpl: not null, IsVisible: true }) { // Configure for dialog mode dialog.ShowAsDialog = true; dialog.WindowStartupLocation = WindowStartupLocation.CenterOwner; - + // Show synchronously without blocking UI thread // https://github.com/AvaloniaUI/Avalonia/issues/4810#issuecomment-704259221 var cts = new CancellationTokenSource(); - - dialog.ShowDialog(mainWindow).ContinueWith(_ => - { - cts.Cancel(); - ExitWithException(ex); - }, TaskScheduler.FromCurrentSynchronizationContext()); - + + dialog + .ShowDialog(mainWindow) + .ContinueWith( + _ => + { + cts.Cancel(); + ExitWithException(ex); + }, + TaskScheduler.FromCurrentSynchronizationContext() + ); + Dispatcher.UIThread.MainLoop(cts.Token); } else @@ -205,9 +225,9 @@ public class Program var cts = new CancellationTokenSource(); // Exit on token cancellation cts.Token.Register(() => ExitWithException(ex)); - + dialog.ShowWithCts(cts); - + Dispatcher.UIThread.MainLoop(cts.Token); } } diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/UpdateViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/UpdateViewModel.cs index 4eb562a8..3c7cfef0 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/UpdateViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/UpdateViewModel.cs @@ -114,7 +114,16 @@ public partial class UpdateViewModel : ContentDialogViewModelBase // On unix, we need to set the executable bit if (Compat.IsUnix) { - File.SetUnixFileMode(UpdateHelper.ExecutablePath, (UnixFileMode)0x755); + File.SetUnixFileMode( + UpdateHelper.ExecutablePath, // 0755 + UnixFileMode.UserRead + | UnixFileMode.UserWrite + | UnixFileMode.UserExecute + | UnixFileMode.GroupRead + | UnixFileMode.GroupExecute + | UnixFileMode.OtherRead + | UnixFileMode.OtherExecute + ); } UpdateText = "Update complete. Restarting Stability Matrix in 3 seconds..."; From 2d6fa76383117dc94ac92b64fc6c878bfb6b7448 Mon Sep 17 00:00:00 2001 From: JT Date: Thu, 7 Sep 2023 00:34:34 -0700 Subject: [PATCH 10/15] add back csharpierrc --- .config/.csharpierrc.json | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .config/.csharpierrc.json diff --git a/.config/.csharpierrc.json b/.config/.csharpierrc.json new file mode 100644 index 00000000..c821bbeb --- /dev/null +++ b/.config/.csharpierrc.json @@ -0,0 +1,4 @@ +{ + "printWidth": 120, + "preprocessorSymbolSets": ["", "DEBUG", "DEBUG,CODE_STYLE"] +} From ce85dc1f502c2ace341423e19d50d8a22b729014 Mon Sep 17 00:00:00 2001 From: JT Date: Thu, 7 Sep 2023 00:41:11 -0700 Subject: [PATCH 11/15] Remove package name lookup thing --- .../Dialogs/PackageModificationDialogViewModel.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageModificationDialogViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageModificationDialogViewModel.cs index c2a65a4d..40acfa52 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageModificationDialogViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageModificationDialogViewModel.cs @@ -41,16 +41,6 @@ public class PackageModificationDialogViewModel : ContentDialogProgressViewModel packageModificationRunner.ProgressChanged += PackageModificationRunnerOnProgressChanged; 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( "Package Install Completed", "Package install completed successfully.", From ff240ca1d09dc97cba97dd66f436098b0d81b656 Mon Sep 17 00:00:00 2001 From: JT Date: Thu, 7 Sep 2023 17:38:54 -0700 Subject: [PATCH 12/15] Remove PackageModificationDialogViewModel & call Console.StartUpdates --- StabilityMatrix.Avalonia/App.axaml.cs | 3 + .../PackageModificationDialogViewModel.cs | 75 ------------------- .../ViewModels/PackageManagerViewModel.cs | 33 +++----- .../PackageInstallProgressItemViewModel.cs | 8 +- .../Progress/ProgressManagerViewModel.cs | 53 +++++++++---- .../Views/Dialogs/InstallerDialog.axaml | 1 + .../Dialogs/PackageModificationDialog.axaml | 1 + 7 files changed, 57 insertions(+), 117 deletions(-) delete mode 100644 StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageModificationDialogViewModel.cs diff --git a/StabilityMatrix.Avalonia/App.axaml.cs b/StabilityMatrix.Avalonia/App.axaml.cs index 57c82ddb..5e37768d 100644 --- a/StabilityMatrix.Avalonia/App.axaml.cs +++ b/StabilityMatrix.Avalonia/App.axaml.cs @@ -605,6 +605,9 @@ public sealed class App : Application builder.ForLogger("System.*").WriteToNil(NLog.LogLevel.Warn); builder.ForLogger("Microsoft.*").WriteToNil(NLog.LogLevel.Warn); builder.ForLogger("Microsoft.Extensions.Http.*").WriteToNil(NLog.LogLevel.Warn); + builder + .ForLogger("StabilityMatrix.Avalonia.ViewModels.ConsoleViewModel") + .WriteToNil(NLog.LogLevel.Debug); builder.ForLogger().FilterMinLevel(NLog.LogLevel.Trace).WriteTo(debugTarget); builder.ForLogger().FilterMinLevel(NLog.LogLevel.Debug).WriteTo(fileTarget); diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageModificationDialogViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageModificationDialogViewModel.cs deleted file mode 100644 index 40acfa52..00000000 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageModificationDialogViewModel.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Avalonia.Controls.Notifications; -using StabilityMatrix.Avalonia.Services; -using StabilityMatrix.Avalonia.ViewModels.Base; -using StabilityMatrix.Core.Helper; -using StabilityMatrix.Core.Models.PackageModification; -using StabilityMatrix.Core.Models.Progress; - -namespace StabilityMatrix.Avalonia.ViewModels.Dialogs; - -public class PackageModificationDialogViewModel : ContentDialogProgressViewModelBase -{ - private readonly IPackageModificationRunner packageModificationRunner; - private readonly INotificationService notificationService; - private readonly IEnumerable steps; - - public PackageModificationDialogViewModel( - IPackageModificationRunner packageModificationRunner, - INotificationService notificationService, - IEnumerable steps - ) - { - this.packageModificationRunner = packageModificationRunner; - this.notificationService = notificationService; - this.steps = steps; - CloseWhenFinished = true; - } - - public override async Task OnLoadedAsync() - { - await Console.Clear(); - Console.Post(string.Join(Environment.NewLine, packageModificationRunner.ConsoleOutput)); - - // idk why this is getting called twice - if (!packageModificationRunner.IsRunning) - { - EventManager.Instance.OnPackageInstallProgressAdded(packageModificationRunner); - packageModificationRunner.ProgressChanged += PackageModificationRunnerOnProgressChanged; - await packageModificationRunner.ExecuteSteps(steps.ToList()); - - notificationService.Show( - "Package Install Completed", - "Package install completed successfully.", - NotificationType.Success - ); - - EventManager.Instance.OnInstalledPackagesChanged(); - - if (CloseWhenFinished) - { - OnCloseButtonClick(); - } - } - } - - private void PackageModificationRunnerOnProgressChanged(object? sender, ProgressReport e) - { - Text = string.IsNullOrWhiteSpace(e.Title) - ? packageModificationRunner.CurrentStep?.ProgressTitle - : e.Title; - - Value = e.Percentage; - Description = e.Message; - IsIndeterminate = e.IsIndeterminate; - - if (string.IsNullOrWhiteSpace(e.Message) || e.Message.Equals("Downloading...")) - return; - - Console.PostLine(e.Message); - EventManager.Instance.OnScrollToBottomRequested(); - } -} diff --git a/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs index 10d937a0..5007bef5 100644 --- a/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Threading.Tasks; using AsyncAwaitBestPractices; using Avalonia.Controls; +using Avalonia.Controls.Notifications; using DynamicData; using DynamicData.Binding; using FluentAvalonia.UI.Controls; @@ -38,7 +39,6 @@ public partial class PackageManagerViewModel : PageViewModelBase private readonly ISettingsManager settingsManager; private readonly IPackageFactory packageFactory; private readonly ServiceManager dialogFactory; - private readonly IPackageModificationRunner packageModificationRunner; private readonly INotificationService notificationService; public override string Title => "Packages"; @@ -65,14 +65,12 @@ public partial class PackageManagerViewModel : PageViewModelBase ISettingsManager settingsManager, IPackageFactory packageFactory, ServiceManager dialogFactory, - IPackageModificationRunner packageModificationRunner, INotificationService notificationService ) { this.settingsManager = settingsManager; this.packageFactory = packageFactory; this.dialogFactory = dialogFactory; - this.packageModificationRunner = packageModificationRunner; this.notificationService = notificationService; EventManager.Instance.InstalledPackagesChanged += OnInstalledPackagesChanged; @@ -140,28 +138,17 @@ public partial class PackageManagerViewModel : PageViewModelBase var result = await dialog.ShowAsync(); if (result == ContentDialogResult.Primary) { + var runner = new PackageModificationRunner(); var steps = viewModel.Steps; - var packageModificationDialogViewModel = new PackageModificationDialogViewModel( - packageModificationRunner, - notificationService, - steps - ); - dialog = new BetterContentDialog - { - MaxDialogWidth = 900, - MinDialogWidth = 900, - DefaultButton = ContentDialogButton.Close, - IsPrimaryButtonEnabled = false, - IsSecondaryButtonEnabled = false, - IsFooterVisible = false, - Content = new PackageModificationDialog - { - DataContext = packageModificationDialogViewModel - } - }; - - await dialog.ShowAsync(); + EventManager.Instance.OnPackageInstallProgressAdded(runner); + await runner.ExecuteSteps(steps.ToList()); + EventManager.Instance.OnInstalledPackagesChanged(); + notificationService.Show( + "Package Install Complete", + $"{viewModel.InstallName} installed successfully", + NotificationType.Success + ); } } diff --git a/StabilityMatrix.Avalonia/ViewModels/Progress/PackageInstallProgressItemViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Progress/PackageInstallProgressItemViewModel.cs index 1422a7f3..c4b5f845 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Progress/PackageInstallProgressItemViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Progress/PackageInstallProgressItemViewModel.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Threading.Tasks; +using Avalonia.Threading; using FluentAvalonia.UI.Controls; using StabilityMatrix.Avalonia.Controls; using StabilityMatrix.Avalonia.ViewModels.Base; @@ -25,6 +26,8 @@ public partial class PackageInstallProgressItemViewModel : ProgressItemViewModel Progress.Text = packageModificationRunner.ConsoleOutput.LastOrDefault(); Progress.IsIndeterminate = packageModificationRunner.CurrentProgress.IsIndeterminate; + Progress.Console.StartUpdates(); + Progress.Console.Post( string.Join(Environment.NewLine, packageModificationRunner.ConsoleOutput) ); @@ -39,7 +42,7 @@ public partial class PackageInstallProgressItemViewModel : ProgressItemViewModel Progress.IsIndeterminate = e.IsIndeterminate; Name = packageModificationRunner.CurrentStep?.ProgressTitle; - if (string.IsNullOrWhiteSpace(e.Message) || e.Message.Equals("Downloading...")) + if (string.IsNullOrWhiteSpace(e.Message) || e.Message.Contains("Downloading...")) return; Progress.Console.PostLine(e.Message); @@ -51,8 +54,7 @@ public partial class PackageInstallProgressItemViewModel : ProgressItemViewModel && Progress.CloseWhenFinished ) { - EventManager.Instance.OnInstalledPackagesChanged(); - dialog?.Hide(); + Dispatcher.UIThread.Post(() => dialog?.Hide()); } } diff --git a/StabilityMatrix.Avalonia/ViewModels/Progress/ProgressManagerViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Progress/ProgressManagerViewModel.cs index 506d3126..1ede1406 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Progress/ProgressManagerViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Progress/ProgressManagerViewModel.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; +using AsyncAwaitBestPractices; using Avalonia.Collections; using Avalonia.Controls.Notifications; using Avalonia.Threading; @@ -23,45 +25,55 @@ namespace StabilityMatrix.Avalonia.ViewModels.Progress; public partial class ProgressManagerViewModel : PageViewModelBase { private readonly INotificationService notificationService; - + 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 ProgressItems { get; } = new(); - [ObservableProperty] private bool isOpen; + [ObservableProperty] + private bool isOpen; public ProgressManagerViewModel( ITrackedDownloadService trackedDownloadService, - INotificationService notificationService) + INotificationService notificationService + ) { this.notificationService = notificationService; - + // Attach to the event trackedDownloadService.DownloadAdded += TrackedDownloadService_OnDownloadAdded; EventManager.Instance.PackageInstallProgressAdded += InstanceOnPackageInstallProgressAdded; EventManager.Instance.ToggleProgressFlyout += (_, _) => IsOpen = !IsOpen; } - private void InstanceOnPackageInstallProgressAdded(object? sender, IPackageModificationRunner runner) + private void InstanceOnPackageInstallProgressAdded( + object? sender, + IPackageModificationRunner runner + ) { - AddPackageInstall(runner); + AddPackageInstall(runner).SafeFireAndForget(); } private void TrackedDownloadService_OnDownloadAdded(object? sender, TrackedDownload e) { var vm = new DownloadProgressItemViewModel(e); - + // Attach notification handlers e.ProgressStateChanged += (s, state) => { var download = s as TrackedDownload; - + switch (state) { case ProgressState.Success: Dispatcher.UIThread.Post(() => { - notificationService.Show("Download Completed", $"Download of {e.FileName} completed successfully.", NotificationType.Success); + notificationService.Show( + "Download Completed", + $"Download of {e.FileName} completed successfully.", + NotificationType.Success + ); }); break; case ProgressState.Failed: @@ -72,18 +84,26 @@ public partial class ProgressManagerViewModel : PageViewModelBase } Dispatcher.UIThread.Post(() => { - notificationService.ShowPersistent("Download Failed", $"Download of {e.FileName} failed: {msg}", NotificationType.Error); + notificationService.ShowPersistent( + "Download Failed", + $"Download of {e.FileName} failed: {msg}", + NotificationType.Error + ); }); break; case ProgressState.Cancelled: Dispatcher.UIThread.Post(() => { - notificationService.Show("Download Cancelled", $"Download of {e.FileName} was cancelled.", NotificationType.Warning); + notificationService.Show( + "Download Cancelled", + $"Download of {e.FileName} was cancelled.", + NotificationType.Warning + ); }); break; } }; - + ProgressItems.Add(vm); } @@ -100,7 +120,7 @@ public partial class ProgressManagerViewModel : PageViewModelBase } } - private void AddPackageInstall(IPackageModificationRunner packageModificationRunner) + private async Task AddPackageInstall(IPackageModificationRunner packageModificationRunner) { if (ProgressItems.Any(vm => vm.Id == packageModificationRunner.Id)) { @@ -109,13 +129,14 @@ public partial class ProgressManagerViewModel : PageViewModelBase var vm = new PackageInstallProgressItemViewModel(packageModificationRunner); ProgressItems.Add(vm); + await vm.ShowProgressDialog(); } - + private void ShowFailedNotification(string title, string message) { notificationService.ShowPersistent(title, message, NotificationType.Error); } - + public void StartEventListener() { EventManager.Instance.ProgressChanged += OnProgressChanged; diff --git a/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml b/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml index 0128c3a7..261111ac 100644 --- a/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml +++ b/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml @@ -197,6 +197,7 @@ Content="Install" Command="{Binding InstallCommand}" FontSize="20" + IsEnabled="{Binding !ShowDuplicateWarning}" HorizontalAlignment="Center" Classes="success" Margin="4,0,8,0" diff --git a/StabilityMatrix.Avalonia/Views/Dialogs/PackageModificationDialog.axaml b/StabilityMatrix.Avalonia/Views/Dialogs/PackageModificationDialog.axaml index fabaefd7..aa6c4018 100644 --- a/StabilityMatrix.Avalonia/Views/Dialogs/PackageModificationDialog.axaml +++ b/StabilityMatrix.Avalonia/Views/Dialogs/PackageModificationDialog.axaml @@ -12,6 +12,7 @@ From 16299d46393fa563d25773cf87a387816d674280 Mon Sep 17 00:00:00 2001 From: Ionite Date: Thu, 7 Sep 2023 20:39:52 -0400 Subject: [PATCH 13/15] Use conditional logger for ConsoleViewModel --- .../ViewModels/ConsoleViewModel.cs | 199 +++++++++++------- 1 file changed, 119 insertions(+), 80 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/ConsoleViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/ConsoleViewModel.cs index c957f9a4..b4cb1283 100644 --- a/StabilityMatrix.Avalonia/ViewModels/ConsoleViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/ConsoleViewModel.cs @@ -20,18 +20,21 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private bool isDisposed; - + // Queue for console updates private BufferBlock buffer = new(); + // Task that updates the console (runs on UI thread) private Task? updateTask; + // Cancellation token source for updateTask private CancellationTokenSource? updateCts; - + public bool IsUpdatesRunning => updateTask?.IsCompleted == false; - - [ObservableProperty] private TextDocument document = new(); - + + [ObservableProperty] + private TextDocument document = new(); + /// /// Current offset for write operations. /// @@ -47,13 +50,13 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis /// // ReSharper disable once MemberCanBePrivate.Global public TimeSpan WriteCursorLockTimeout { get; init; } = TimeSpan.FromMilliseconds(100); - + /// /// Gets a cancellation token using the cursor lock timeout /// - private CancellationToken WriteCursorLockTimeoutToken => + private CancellationToken WriteCursorLockTimeoutToken => new CancellationTokenSource(WriteCursorLockTimeout).Token; - + /// /// Event invoked when an ApcMessage of type Input is received. /// @@ -72,7 +75,7 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis updateCts = new CancellationTokenSource(); updateTask = Dispatcher.UIThread.InvokeAsync(ConsoleUpdateLoop, DispatcherPriority.Send); } - + /// /// Cancels the update task and waits for it to complete. /// @@ -97,7 +100,7 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis // Cancel update task updateCts?.Cancel(); updateCts = null; - + // Wait for update task if (updateTask is not null) { @@ -106,7 +109,7 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis } Logger.Trace($"Stopped console updates with {buffer.Count} buffer items remaining"); } - + /// /// Clears the console and sets a new buffer. /// This also resets the write cursor to 0. @@ -121,7 +124,7 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis buffer.Complete(); buffer = new BufferBlock(); } - + /// /// Resets the write cursor to be equal to the document length. /// @@ -129,20 +132,23 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis { using (await writeCursorLock.LockAsync(WriteCursorLockTimeoutToken)) { - Debug.WriteLine($"Reset cursor to end: ({writeCursor} -> {Document.TextLength})"); + Logger.ConditionalTrace( + $"Reset cursor to end: ({writeCursor} -> {Document.TextLength})" + ); writeCursor = Document.TextLength; } DebugPrintDocument(); } - + private async Task ConsoleUpdateLoop() { // This must be run in the UI thread Dispatcher.UIThread.VerifyAccess(); // Get cancellation token - var ct = updateCts?.Token - ?? throw new InvalidOperationException("Update cancellation token must be set"); + var ct = + updateCts?.Token + ?? throw new InvalidOperationException("Update cancellation token must be set"); try { @@ -161,16 +167,19 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis } var outputType = output.IsStdErr ? "stderr" : "stdout"; - Debug.WriteLine($"Processing: [{outputType}] (Text = {output.Text.ToRepr()}, " + - $"Raw = {output.RawText?.ToRepr()}, " + - $"CarriageReturn = {output.CarriageReturn}, " + - $"CursorUp = {output.CursorUp}, " + - $"AnsiCommand = {output.AnsiCommand})"); - + Logger.ConditionalTrace( + $"Processing: [{outputType}] (Text = {output.Text.ToRepr()}, " + + $"Raw = {output.RawText?.ToRepr()}, " + + $"CarriageReturn = {output.CarriageReturn}, " + + $"CursorUp = {output.CursorUp}, " + + $"AnsiCommand = {output.AnsiCommand})" + ); + // Link the cancellation token to the write cursor lock timeout var linkedCt = CancellationTokenSource - .CreateLinkedTokenSource(ct, WriteCursorLockTimeoutToken).Token; - + .CreateLinkedTokenSource(ct, WriteCursorLockTimeoutToken) + .Token; + using (await writeCursorLock.LockAsync(linkedCt)) { ConsoleUpdateOne(output); @@ -184,7 +193,10 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis catch (Exception e) { // Log other errors and continue here to not crash the UI thread - Logger.Error(e, $"Unexpected error in console update loop: {e.GetType().Name} {e.Message}"); + Logger.Error( + e, + $"Unexpected error in console update loop: {e.GetType().Name} {e.Message}" + ); } } @@ -196,7 +208,7 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis private void ConsoleUpdateOne(ProcessOutput output) { Debug.Assert(Dispatcher.UIThread.CheckAccess()); - + // Check for Apc messages if (output.ApcMessage is not null) { @@ -209,21 +221,23 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis // Ignore further processing return; } - + // If we have a carriage return, // start current write at the beginning of the current line if (output.CarriageReturn > 0) { var currentLine = Document.GetLineByOffset(writeCursor); - + // Get the start of current line as new write cursor var lineStartOffset = currentLine.Offset; - + // See if we need to move the cursor if (lineStartOffset == writeCursor) { - Debug.WriteLine($"Cursor already at start for carriage return " + - $"(offset = {lineStartOffset}, line = {currentLine.LineNumber})"); + Logger.ConditionalTrace( + $"Cursor already at start for carriage return " + + $"(offset = {lineStartOffset}, line = {currentLine.LineNumber})" + ); } else { @@ -232,19 +246,21 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis var lineEndOffset = currentLine.EndOffset; var lineLength = lineEndOffset - lineStartOffset; Document.Remove(lineStartOffset, lineLength); - - Debug.WriteLine($"Moving cursor to start for carriage return " + - $"({writeCursor} -> {lineStartOffset})"); + + Logger.ConditionalTrace( + $"Moving cursor to start for carriage return " + + $"({writeCursor} -> {lineStartOffset})" + ); writeCursor = lineStartOffset; } } - + // Write new text if (!string.IsNullOrEmpty(output.Text)) { DirectWriteLinesToConsole(output.Text); } - + // Handle cursor movements if (output.CursorUp > 0) { @@ -254,22 +270,27 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis if (currentLocation.Line == 1) { // We are already on the first line, ignore - Debug.WriteLine($"Cursor up: Already on first line"); + Logger.ConditionalTrace($"Cursor up: Already on first line"); } else { // We want to move up one line - var targetLocation = new TextLocation(currentLocation.Line - 1, currentLocation.Column); + var targetLocation = new TextLocation( + currentLocation.Line - 1, + currentLocation.Column + ); var targetOffset = Document.GetOffset(targetLocation); // Update cursor to target offset - Debug.WriteLine($"Cursor up: Moving (line {currentLocation.Line}, {writeCursor})" + - $" -> (line {targetLocation.Line}, {targetOffset})"); + Logger.ConditionalTrace( + $"Cursor up: Moving (line {currentLocation.Line}, {writeCursor})" + + $" -> (line {targetLocation.Line}, {targetOffset})" + ); writeCursor = targetOffset; } } - + // Handle erase commands, different to cursor move as they don't move the cursor // We'll insert blank spaces instead if (output.AnsiCommand.HasFlag(AnsiCommand.EraseLine)) @@ -278,9 +299,11 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis var currentLine = Document.GetLineByOffset(writeCursor); // Make some spaces to insert var spaces = new string(' ', currentLine.Length); - + // Insert the text - Debug.WriteLine($"Erasing line {currentLine.LineNumber}: (length = {currentLine.Length})"); + Logger.ConditionalTrace( + $"Erasing line {currentLine.LineNumber}: (length = {currentLine.Length})" + ); using (Document.RunUpdate()) { Document.Replace(currentLine.Offset, currentLine.Length, spaces); @@ -289,7 +312,7 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis DebugPrintDocument(); } - + /// /// Write text potentially containing line breaks to the console. /// This call will hold a upgradeable read lock @@ -298,7 +321,7 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis { // When our cursor is not at end, newlines should be interpreted as commands to // move cursor forward to the next linebreak instead of inserting a newline. - + // If text contains no newlines, we can just call DirectWriteToConsole // Also if cursor is equal to document length if (!text.Contains(Environment.NewLine) || writeCursor == Document.TextLength) @@ -306,7 +329,7 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis DirectWriteToConsole(text); return; } - + // Otherwise we need to handle how linebreaks are treated // Split text into lines var lines = text.Split(Environment.NewLine).ToList(); @@ -315,29 +338,32 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis { // Insert text DirectWriteToConsole(lineText); - + // Set cursor to start of next line, if we're not already there var currentLine = Document.GetLineByOffset(writeCursor); // If next line is available, move cursor to start of next line if (currentLine.LineNumber < Document.LineCount) { var nextLine = Document.GetLineByNumber(currentLine.LineNumber + 1); - Debug.WriteLine($"Moving cursor to start of next line " + - $"({writeCursor} -> {nextLine.Offset})"); + Logger.ConditionalTrace( + $"Moving cursor to start of next line " + + $"({writeCursor} -> {nextLine.Offset})" + ); writeCursor = nextLine.Offset; } else { // Otherwise move to end of current line, and direct insert a newline var lineEndOffset = currentLine.EndOffset; - Debug.WriteLine($"Moving cursor to end of current line " + - $"({writeCursor} -> {lineEndOffset})"); + Logger.ConditionalTrace( + $"Moving cursor to end of current line " + $"({writeCursor} -> {lineEndOffset})" + ); writeCursor = lineEndOffset; DirectWriteToConsole(Environment.NewLine); } } } - + /// /// Write text to the console, does not handle newlines. /// This should probably only be used by @@ -352,10 +378,11 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis if (replaceLength > 0) { var newText = text[..replaceLength]; - Debug.WriteLine( - $"Replacing: (cursor = {writeCursor}, length = {replaceLength}, " + - $"text = {Document.GetText(writeCursor, replaceLength).ToRepr()} " + - $"-> {newText.ToRepr()})"); + Logger.ConditionalTrace( + $"Replacing: (cursor = {writeCursor}, length = {replaceLength}, " + + $"text = {Document.GetText(writeCursor, replaceLength).ToRepr()} " + + $"-> {newText.ToRepr()})" + ); Document.Replace(writeCursor, replaceLength, newText); writeCursor += replaceLength; @@ -366,8 +393,9 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis if (remainingLength > 0) { var textToInsert = text[replaceLength..]; - Debug.WriteLine($"Inserting: (cursor = {writeCursor}, " + - $"text = {textToInsert.ToRepr()})"); + Logger.ConditionalTrace( + $"Inserting: (cursor = {writeCursor}, " + $"text = {textToInsert.ToRepr()})" + ); Document.Insert(writeCursor, textToInsert); writeCursor += textToInsert.Length; @@ -382,6 +410,9 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis [Conditional("DEBUG")] private void DebugPrintDocument() { + if (!Logger.IsTraceEnabled) + return; + var text = Document.Text; // Add a number for each line // Add an arrow line for where the cursor is, for example (cursor on offset 3): @@ -390,7 +421,7 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis // ~~~~~~~^ (3) // 2 | This is the second line // - + var lines = text.Split(Environment.NewLine).ToList(); var numberPadding = lines.Count.ToString().Length; for (var i = 0; i < lines.Count; i++) @@ -399,11 +430,11 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis } var cursorLine = Document.GetLineByOffset(writeCursor); var cursorLineOffset = writeCursor - cursorLine.Offset; - + // Need to account for padding + line number + space + cursor line offset var linePadding = numberPadding + 3 + cursorLineOffset; var cursorLineArrow = new string('~', linePadding) + $"^ ({writeCursor})"; - + // If more than line count, append to end if (cursorLine.LineNumber >= lines.Count) { @@ -413,13 +444,13 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis { lines.Insert(cursorLine.LineNumber, cursorLineArrow); } - + var textWithCursor = string.Join(Environment.NewLine, lines); - - Debug.WriteLine("[Current Document]"); - Debug.WriteLine(textWithCursor); + + Logger.ConditionalTrace("[Current Document]"); + Logger.ConditionalTrace(textWithCursor); } - + /// /// Posts an update to the console /// Safe to call on non-UI threads @@ -433,9 +464,10 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis return; } // Otherwise, use manual update one + Logger.Debug("Synchronous post update to console: {@Output}", output); Dispatcher.UIThread.Post(() => ConsoleUpdateOne(output)); } - + /// /// Posts an update to the console. /// Helper for calling Post(ProcessOutput) with strings @@ -444,7 +476,7 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis { Post(new ProcessOutput { Text = text }); } - + /// /// Posts an update to the console. /// Equivalent to Post(text + Environment.NewLine) @@ -456,20 +488,22 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis public void Dispose() { - if (isDisposed) return; - + if (isDisposed) + return; + updateCts?.Cancel(); updateCts?.Dispose(); updateCts = null; - + buffer.Complete(); - + if (updateTask is not null) { + Logger.Debug("Shutting down console update task"); + try { - updateTask.WaitWithoutException( - new CancellationTokenSource(1000).Token); + updateTask.WaitWithoutException(new CancellationTokenSource(1000).Token); updateTask.Dispose(); updateTask = null; } @@ -484,27 +518,32 @@ public partial class ConsoleViewModel : ObservableObject, IDisposable, IAsyncDis } isDisposed = true; - + GC.SuppressFinalize(this); } - + public async ValueTask DisposeAsync() { - if (isDisposed) return; - + if (isDisposed) + return; + updateCts?.Cancel(); updateCts?.Dispose(); updateCts = null; - + if (updateTask is not null) { + Logger.Debug("Waiting for console update task shutdown..."); + await updateTask; updateTask.Dispose(); updateTask = null; + + Logger.Debug("Console update task shutdown complete"); } isDisposed = true; - + GC.SuppressFinalize(this); } } From 523e57534548364b9ad15ec1351bf07ed291fa68 Mon Sep 17 00:00:00 2001 From: JT Date: Thu, 7 Sep 2023 18:16:26 -0700 Subject: [PATCH 14/15] Fix text display & change line offset for scrollToBottomRequested in PackageModificationDialog --- .../ViewModels/Dialogs/InstallerViewModel.cs | 12 ++++++++---- .../Views/Dialogs/InstallerDialog.axaml | 2 +- .../Views/Dialogs/PackageModificationDialog.axaml | 4 ++-- .../Views/Dialogs/PackageModificationDialog.axaml.cs | 2 +- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs index 68c9b96b..cda3e11a 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs @@ -69,9 +69,11 @@ public partial class InstallerViewModel : ContentDialogViewModelBase private bool isAdvancedMode; [ObservableProperty] + [NotifyPropertyChangedFor(nameof(CanInstall))] private bool showDuplicateWarning; [ObservableProperty] + [NotifyPropertyChangedFor(nameof(CanInstall))] private string? installName; [ObservableProperty] @@ -108,6 +110,8 @@ public partial class InstallerViewModel : ContentDialogViewModelBase AvailableVersionTypes.HasFlag(PackageVersionType.GithubRelease); public bool ShowTorchVersionOptions => SelectedTorchVersion != TorchVersion.None; + public bool CanInstall => !string.IsNullOrWhiteSpace(InstallName) && !ShowDuplicateWarning; + public ProgressViewModel InstallProgress { get; } = new(); public IEnumerable Steps { get; set; } @@ -303,7 +307,7 @@ public partial class InstallerViewModel : ContentDialogViewModelBase version ??= AvailableVersions.FirstOrDefault(x => x.TagName == "main"); // If still not found, just use the first one - version ??= AvailableVersions[0]; + version ??= AvailableVersions.FirstOrDefault(); SelectedVersion = version; } @@ -388,14 +392,14 @@ public partial class InstallerViewModel : ContentDialogViewModelBase return; AvailableCommits = new ObservableCollection(commits); - SelectedCommit = AvailableCommits[0]; + SelectedCommit = AvailableCommits.FirstOrDefault(); UpdateSelectedVersionToLatestMain(); } InstallName = SelectedPackage.DisplayName; LatestVersionText = IsReleaseMode - ? $"Latest version: {SelectedVersion.TagName}" - : $"Branch: {SelectedVersion.TagName}"; + ? $"Latest version: {SelectedVersion?.TagName}" + : $"Branch: {SelectedVersion?.TagName}"; }) .SafeFireAndForget(); } diff --git a/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml b/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml index 261111ac..995eb4f2 100644 --- a/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml +++ b/StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml @@ -197,7 +197,7 @@ Content="Install" Command="{Binding InstallCommand}" FontSize="20" - IsEnabled="{Binding !ShowDuplicateWarning}" + IsEnabled="{Binding CanInstall}" HorizontalAlignment="Center" Classes="success" Margin="4,0,8,0" diff --git a/StabilityMatrix.Avalonia/Views/Dialogs/PackageModificationDialog.axaml b/StabilityMatrix.Avalonia/Views/Dialogs/PackageModificationDialog.axaml index aa6c4018..308ae39e 100644 --- a/StabilityMatrix.Avalonia/Views/Dialogs/PackageModificationDialog.axaml +++ b/StabilityMatrix.Avalonia/Views/Dialogs/PackageModificationDialog.axaml @@ -13,8 +13,9 @@ + HorizontalAlignment="Stretch"/> diff --git a/StabilityMatrix.Avalonia/Views/Dialogs/PackageModificationDialog.axaml.cs b/StabilityMatrix.Avalonia/Views/Dialogs/PackageModificationDialog.axaml.cs index 16a59a56..2dd5053f 100644 --- a/StabilityMatrix.Avalonia/Views/Dialogs/PackageModificationDialog.axaml.cs +++ b/StabilityMatrix.Avalonia/Views/Dialogs/PackageModificationDialog.axaml.cs @@ -43,7 +43,7 @@ public partial class PackageModificationDialog : UserControlBase var editor = this.FindControl("Console"); if (editor?.Document == null) return; - var line = Math.Max(editor.Document.LineCount - 5, 1); + var line = Math.Max(editor.Document.LineCount - 1, 1); editor.ScrollToLine(line); }); }; From b5cb6d654af55c37d9f4e7e6b6ed7cf8b0126e90 Mon Sep 17 00:00:00 2001 From: JT Date: Thu, 7 Sep 2023 21:19:34 -0700 Subject: [PATCH 15/15] Fixed weird shared progress event --- .../Dialogs/OneClickInstallViewModel.cs | 9 +++-- .../ViewModels/LaunchPageViewModel.cs | 6 ++-- .../PackageInstallProgressItemViewModel.cs | 5 +-- .../Views/ProgressManagerPage.axaml | 6 ++-- .../PackageModification/InstallPackageStep.cs | 10 ++++-- .../Models/Packages/A3WebUI.cs | 26 ++++++++------- .../Models/Packages/BaseGitPackage.cs | 7 ++-- .../Models/Packages/BasePackage.cs | 33 +++++++++++-------- .../Models/Packages/ComfyUI.cs | 26 ++++++++------- .../Models/Packages/DankDiffusion.cs | 8 ++++- .../Models/Packages/Fooocus.cs | 12 ++++--- .../Models/Packages/InvokeAI.cs | 27 ++++++++------- .../Models/Packages/UnknownPackage.cs | 14 ++++++-- .../Models/Packages/VladAutomatic.cs | 21 +++++++----- .../Models/Packages/VoltaML.cs | 10 +++--- 15 files changed, 135 insertions(+), 85 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs index 72782cc8..3ba570c4 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs @@ -126,7 +126,6 @@ public partial class OneClickInstallViewModel : ViewModelBase // get latest version & download & install SubHeaderText = "Getting latest version..."; var installLocation = Path.Combine(libraryDir, "Packages", SelectedPackage.Name); - SelectedPackage.ConsoleOutput += (_, output) => SubSubHeaderText = output.Text; var downloadVersion = new DownloadPackageVersionOptions(); var installedVersion = new InstalledPackageVersion(); @@ -204,7 +203,6 @@ public partial class OneClickInstallViewModel : ViewModelBase private async Task InstallPackage(string installLocation, TorchVersion torchVersion) { - SelectedPackage.ConsoleOutput += (_, output) => SubSubHeaderText = output.Text; SubHeaderText = "Downloading and installing package requirements..."; var progress = new Progress(progress => @@ -215,6 +213,11 @@ public partial class OneClickInstallViewModel : ViewModelBase EventManager.Instance.OnGlobalProgressChanged(OneClickInstallProgress); }); - await SelectedPackage.InstallPackage(installLocation, torchVersion, progress); + await SelectedPackage.InstallPackage( + installLocation, + torchVersion, + progress, + (output) => SubSubHeaderText = output.Text + ); } } diff --git a/StabilityMatrix.Avalonia/ViewModels/LaunchPageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/LaunchPageViewModel.cs index 3d3f99f1..0d5bef52 100644 --- a/StabilityMatrix.Avalonia/ViewModels/LaunchPageViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/LaunchPageViewModel.cs @@ -255,7 +255,6 @@ public partial class LaunchPageViewModel : PageViewModelBase, IDisposable, IAsyn // Unpack sitecustomize.py to venv await UnpackSiteCustomize(packagePath.JoinDir("venv")); - basePackage.ConsoleOutput += OnProcessOutputReceived; basePackage.Exited += OnProcessExited; basePackage.StartupComplete += RunningPackageOnStartupComplete; @@ -280,7 +279,7 @@ public partial class LaunchPageViewModel : PageViewModelBase, IDisposable, IAsyn // Use input command if provided, otherwise use package launch command command ??= basePackage.LaunchCommand; - await basePackage.RunPackage(packagePath, command, userArgsString); + await basePackage.RunPackage(packagePath, command, userArgsString, OnProcessOutputReceived); RunningPackage = basePackage; EventManager.Instance.OnRunningPackageStatusChanged( @@ -465,7 +464,6 @@ public partial class LaunchPageViewModel : PageViewModelBase, IDisposable, IAsyn // Detach handlers if (sender is BasePackage basePackage) { - basePackage.ConsoleOutput -= OnProcessOutputReceived; basePackage.Exited -= OnProcessExited; basePackage.StartupComplete -= RunningPackageOnStartupComplete; } @@ -485,7 +483,7 @@ public partial class LaunchPageViewModel : PageViewModelBase, IDisposable, IAsyn } // Callback for processes - private void OnProcessOutputReceived(object? sender, ProcessOutput output) + private void OnProcessOutputReceived(ProcessOutput output) { Console.Post(output); EventManager.Instance.OnScrollToBottomRequested(); diff --git a/StabilityMatrix.Avalonia/ViewModels/Progress/PackageInstallProgressItemViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Progress/PackageInstallProgressItemViewModel.cs index c4b5f845..70dadda1 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Progress/PackageInstallProgressItemViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Progress/PackageInstallProgressItemViewModel.cs @@ -12,7 +12,7 @@ using StabilityMatrix.Core.Models.Progress; namespace StabilityMatrix.Avalonia.ViewModels.Progress; -public partial class PackageInstallProgressItemViewModel : ProgressItemViewModelBase +public class PackageInstallProgressItemViewModel : ProgressItemViewModelBase { private readonly IPackageModificationRunner packageModificationRunner; private BetterContentDialog? dialog; @@ -38,8 +38,9 @@ public partial class PackageInstallProgressItemViewModel : ProgressItemViewModel private void PackageModificationRunnerOnProgressChanged(object? sender, ProgressReport e) { Progress.Value = e.Percentage; - Progress.Text = e.Message; + Progress.Description = e.Message; Progress.IsIndeterminate = e.IsIndeterminate; + Progress.Text = packageModificationRunner.CurrentStep?.ProgressTitle; Name = packageModificationRunner.CurrentStep?.ProgressTitle; if (string.IsNullOrWhiteSpace(e.Message) || e.Message.Contains("Downloading...")) diff --git a/StabilityMatrix.Avalonia/Views/ProgressManagerPage.axaml b/StabilityMatrix.Avalonia/Views/ProgressManagerPage.axaml index 81740823..53525002 100644 --- a/StabilityMatrix.Avalonia/Views/ProgressManagerPage.axaml +++ b/StabilityMatrix.Avalonia/Views/ProgressManagerPage.axaml @@ -178,14 +178,14 @@ + Text="{Binding Progress.Text, Mode=OneWay}" /> - + @@ -194,7 +194,7 @@ Margin="0,4" MaxWidth="300" IsVisible="{Binding Progress.IsIndeterminate}" - Text="{Binding Progress.Text, Mode=OneWay}" /> + Text="{Binding Progress.Description, Mode=OneWay}" /> diff --git a/StabilityMatrix.Core/Models/PackageModification/InstallPackageStep.cs b/StabilityMatrix.Core/Models/PackageModification/InstallPackageStep.cs index 58562368..b307e161 100644 --- a/StabilityMatrix.Core/Models/PackageModification/InstallPackageStep.cs +++ b/StabilityMatrix.Core/Models/PackageModification/InstallPackageStep.cs @@ -1,5 +1,6 @@ using StabilityMatrix.Core.Models.Packages; using StabilityMatrix.Core.Models.Progress; +using StabilityMatrix.Core.Processes; namespace StabilityMatrix.Core.Models.PackageModification; @@ -18,11 +19,14 @@ public class InstallPackageStep : IPackageStep public async Task ExecuteAsync(IProgress? progress = null) { - package.ConsoleOutput += (sender, output) => + void OnConsoleOutput(ProcessOutput output) { progress?.Report(new ProgressReport { IsIndeterminate = true, Message = output.Text }); - }; - await package.InstallPackage(installPath, torchVersion, progress).ConfigureAwait(false); + } + + await package + .InstallPackage(installPath, torchVersion, progress, OnConsoleOutput) + .ConfigureAwait(false); } public string ProgressTitle => "Installing package..."; diff --git a/StabilityMatrix.Core/Models/Packages/A3WebUI.cs b/StabilityMatrix.Core/Models/Packages/A3WebUI.cs index 7d1505a3..91b8e90b 100644 --- a/StabilityMatrix.Core/Models/Packages/A3WebUI.cs +++ b/StabilityMatrix.Core/Models/Packages/A3WebUI.cs @@ -144,7 +144,8 @@ public class A3WebUI : BaseGitPackage public override async Task InstallPackage( string installLocation, TorchVersion torchVersion, - IProgress? progress = null + IProgress? progress = null, + Action? onConsoleOutput = null ) { await base.InstallPackage(installLocation, torchVersion, progress).ConfigureAwait(false); @@ -158,16 +159,17 @@ public class A3WebUI : BaseGitPackage switch (torchVersion) { case TorchVersion.Cpu: - await InstallCpuTorch(venvRunner, progress).ConfigureAwait(false); + await InstallCpuTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false); break; case TorchVersion.Cuda: - await InstallCudaTorch(venvRunner, progress).ConfigureAwait(false); + await InstallCudaTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false); break; case TorchVersion.Rocm: - await InstallRocmTorch(venvRunner, progress).ConfigureAwait(false); + await InstallRocmTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false); break; case TorchVersion.DirectMl: - await InstallDirectMlTorch(venvRunner, progress).ConfigureAwait(false); + await InstallDirectMlTorch(venvRunner, progress, onConsoleOutput) + .ConfigureAwait(false); break; default: throw new ArgumentOutOfRangeException(nameof(torchVersion), torchVersion, null); @@ -179,7 +181,7 @@ public class A3WebUI : BaseGitPackage ); Logger.Info("Installing requirements_versions.txt"); await venvRunner - .PipInstall($"-r requirements_versions.txt", OnConsoleOutput) + .PipInstall($"-r requirements_versions.txt", onConsoleOutput) .ConfigureAwait(false); progress?.Report( @@ -203,14 +205,15 @@ public class A3WebUI : BaseGitPackage public override async Task RunPackage( string installedPackagePath, string command, - string arguments + string arguments, + Action? onConsoleOutput ) { await SetupVenv(installedPackagePath).ConfigureAwait(false); void HandleConsoleOutput(ProcessOutput s) { - OnConsoleOutput(s); + onConsoleOutput?.Invoke(s); if (!s.Text.Contains("Running on", StringComparison.OrdinalIgnoreCase)) return; @@ -231,17 +234,18 @@ public class A3WebUI : BaseGitPackage private async Task InstallRocmTorch( PyVenvRunner venvRunner, - IProgress? progress = null + IProgress? progress = null, + Action? onConsoleOutput = null ) { progress?.Report( new ProgressReport(-1f, "Installing PyTorch for ROCm", isIndeterminate: true) ); - await venvRunner.PipInstall("--upgrade pip wheel", OnConsoleOutput).ConfigureAwait(false); + await venvRunner.PipInstall("--upgrade pip wheel", onConsoleOutput).ConfigureAwait(false); await venvRunner - .PipInstall(PyVenvRunner.TorchPipInstallArgsRocm511, OnConsoleOutput) + .PipInstall(PyVenvRunner.TorchPipInstallArgsRocm511, onConsoleOutput) .ConfigureAwait(false); } } diff --git a/StabilityMatrix.Core/Models/Packages/BaseGitPackage.cs b/StabilityMatrix.Core/Models/Packages/BaseGitPackage.cs index 83e9cd15..b6d50e18 100644 --- a/StabilityMatrix.Core/Models/Packages/BaseGitPackage.cs +++ b/StabilityMatrix.Core/Models/Packages/BaseGitPackage.cs @@ -7,6 +7,7 @@ using StabilityMatrix.Core.Helper.Cache; using StabilityMatrix.Core.Models.Database; using StabilityMatrix.Core.Models.FileInterfaces; using StabilityMatrix.Core.Models.Progress; +using StabilityMatrix.Core.Processes; using StabilityMatrix.Core.Python; using StabilityMatrix.Core.Services; @@ -182,7 +183,8 @@ public abstract class BaseGitPackage : BasePackage public override async Task InstallPackage( string installLocation, TorchVersion torchVersion, - IProgress? progress = null + IProgress? progress = null, + Action? onConsoleOutput = null ) { await UnzipPackage(installLocation, progress).ConfigureAwait(false); @@ -269,7 +271,8 @@ public abstract class BaseGitPackage : BasePackage InstalledPackage installedPackage, TorchVersion torchVersion, IProgress? progress = null, - bool includePrerelease = false + bool includePrerelease = false, + Action? onConsoleOutput = null ) { if (installedPackage.Version == null) diff --git a/StabilityMatrix.Core/Models/Packages/BasePackage.cs b/StabilityMatrix.Core/Models/Packages/BasePackage.cs index e3706a46..264ed427 100644 --- a/StabilityMatrix.Core/Models/Packages/BasePackage.cs +++ b/StabilityMatrix.Core/Models/Packages/BasePackage.cs @@ -45,10 +45,16 @@ public abstract class BasePackage public abstract Task InstallPackage( string installLocation, TorchVersion torchVersion, - IProgress? progress = null + IProgress? progress = null, + Action? onConsoleOutput = null ); - public abstract Task RunPackage(string installedPackagePath, string command, string arguments); + public abstract Task RunPackage( + string installedPackagePath, + string command, + string arguments, + Action? onConsoleOutput + ); public abstract Task CheckForUpdates(InstalledPackage package); @@ -56,7 +62,8 @@ public abstract class BasePackage InstalledPackage installedPackage, TorchVersion torchVersion, IProgress? progress = null, - bool includePrerelease = false + bool includePrerelease = false, + Action? onConsoleOutput = null ); public virtual IEnumerable AvailableSharedFolderMethods => @@ -144,12 +151,9 @@ public abstract class BasePackage ); public abstract Task> GetAllBranches(); public abstract Task> GetAllReleases(); - public event EventHandler? ConsoleOutput; public event EventHandler? Exited; public event EventHandler? StartupComplete; - public void OnConsoleOutput(ProcessOutput output) => ConsoleOutput?.Invoke(this, output); - public void OnExit(int exitCode) => Exited?.Invoke(this, exitCode); public void OnStartupComplete(string url) => StartupComplete?.Invoke(this, url); @@ -161,7 +165,8 @@ public abstract class BasePackage protected async Task InstallCudaTorch( PyVenvRunner venvRunner, - IProgress? progress = null + IProgress? progress = null, + Action? onConsoleOutput = null ) { progress?.Report( @@ -169,14 +174,15 @@ public abstract class BasePackage ); await venvRunner - .PipInstall(PyVenvRunner.TorchPipInstallArgsCuda, OnConsoleOutput) + .PipInstall(PyVenvRunner.TorchPipInstallArgsCuda, onConsoleOutput) .ConfigureAwait(false); - await venvRunner.PipInstall("xformers", OnConsoleOutput).ConfigureAwait(false); + await venvRunner.PipInstall("xformers", onConsoleOutput).ConfigureAwait(false); } protected async Task InstallDirectMlTorch( PyVenvRunner venvRunner, - IProgress? progress = null + IProgress? progress = null, + Action? onConsoleOutput = null ) { progress?.Report( @@ -184,13 +190,14 @@ public abstract class BasePackage ); await venvRunner - .PipInstall(PyVenvRunner.TorchPipInstallArgsDirectML, OnConsoleOutput) + .PipInstall(PyVenvRunner.TorchPipInstallArgsDirectML, onConsoleOutput) .ConfigureAwait(false); } protected async Task InstallCpuTorch( PyVenvRunner venvRunner, - IProgress? progress = null + IProgress? progress = null, + Action? onConsoleOutput = null ) { progress?.Report( @@ -198,7 +205,7 @@ public abstract class BasePackage ); await venvRunner - .PipInstall(PyVenvRunner.TorchPipInstallArgsCpu, OnConsoleOutput) + .PipInstall(PyVenvRunner.TorchPipInstallArgsCpu, onConsoleOutput) .ConfigureAwait(false); } } diff --git a/StabilityMatrix.Core/Models/Packages/ComfyUI.cs b/StabilityMatrix.Core/Models/Packages/ComfyUI.cs index 486ed094..90e2dc91 100644 --- a/StabilityMatrix.Core/Models/Packages/ComfyUI.cs +++ b/StabilityMatrix.Core/Models/Packages/ComfyUI.cs @@ -106,7 +106,8 @@ public class ComfyUI : BaseGitPackage public override async Task InstallPackage( string installLocation, TorchVersion torchVersion, - IProgress? progress = null + IProgress? progress = null, + Action? onConsoleOutput = null ) { await base.InstallPackage(installLocation, torchVersion, progress).ConfigureAwait(false); @@ -121,16 +122,17 @@ public class ComfyUI : BaseGitPackage switch (torchVersion) { case TorchVersion.Cpu: - await InstallCpuTorch(venvRunner, progress).ConfigureAwait(false); + await InstallCpuTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false); break; case TorchVersion.Cuda: - await InstallCudaTorch(venvRunner, progress).ConfigureAwait(false); + await InstallCudaTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false); break; case TorchVersion.Rocm: - await InstallRocmTorch(venvRunner, progress).ConfigureAwait(false); + await InstallRocmTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false); break; case TorchVersion.DirectMl: - await InstallDirectMlTorch(venvRunner, progress).ConfigureAwait(false); + await InstallDirectMlTorch(venvRunner, progress, onConsoleOutput) + .ConfigureAwait(false); break; default: throw new ArgumentOutOfRangeException(nameof(torchVersion), torchVersion, null); @@ -141,7 +143,7 @@ public class ComfyUI : BaseGitPackage new ProgressReport(-1, "Installing Package Requirements", isIndeterminate: true) ); Logger.Info("Installing requirements.txt"); - await venvRunner.PipInstall($"-r requirements.txt", OnConsoleOutput).ConfigureAwait(false); + await venvRunner.PipInstall($"-r requirements.txt", onConsoleOutput).ConfigureAwait(false); progress?.Report( new ProgressReport(1, "Installing Package Requirements", isIndeterminate: false) @@ -175,14 +177,15 @@ public class ComfyUI : BaseGitPackage public override async Task RunPackage( string installedPackagePath, string command, - string arguments + string arguments, + Action? onConsoleOutput ) { await SetupVenv(installedPackagePath).ConfigureAwait(false); void HandleConsoleOutput(ProcessOutput s) { - OnConsoleOutput(s); + onConsoleOutput?.Invoke(s); if (s.Text.Contains("To see the GUI go to", StringComparison.OrdinalIgnoreCase)) { @@ -290,17 +293,18 @@ public class ComfyUI : BaseGitPackage private async Task InstallRocmTorch( PyVenvRunner venvRunner, - IProgress? progress = null + IProgress? progress = null, + Action? onConsoleOutput = null ) { progress?.Report( new ProgressReport(-1f, "Installing PyTorch for ROCm", isIndeterminate: true) ); - await venvRunner.PipInstall("--upgrade pip wheel", OnConsoleOutput).ConfigureAwait(false); + await venvRunner.PipInstall("--upgrade pip wheel", onConsoleOutput).ConfigureAwait(false); await venvRunner - .PipInstall(PyVenvRunner.TorchPipInstallArgsRocm542, OnConsoleOutput) + .PipInstall(PyVenvRunner.TorchPipInstallArgsRocm542, onConsoleOutput) .ConfigureAwait(false); } diff --git a/StabilityMatrix.Core/Models/Packages/DankDiffusion.cs b/StabilityMatrix.Core/Models/Packages/DankDiffusion.cs index 00b206a0..ea1e34b5 100644 --- a/StabilityMatrix.Core/Models/Packages/DankDiffusion.cs +++ b/StabilityMatrix.Core/Models/Packages/DankDiffusion.cs @@ -1,6 +1,7 @@ using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper.Cache; using StabilityMatrix.Core.Models.FileInterfaces; +using StabilityMatrix.Core.Processes; using StabilityMatrix.Core.Services; namespace StabilityMatrix.Core.Models.Packages; @@ -29,7 +30,12 @@ public class DankDiffusion : BaseGitPackage public override Uri PreviewImageUri { get; } - public override Task RunPackage(string installedPackagePath, string command, string arguments) + public override Task RunPackage( + string installedPackagePath, + string command, + string arguments, + Action? onConsoleOutput + ) { throw new NotImplementedException(); } diff --git a/StabilityMatrix.Core/Models/Packages/Fooocus.cs b/StabilityMatrix.Core/Models/Packages/Fooocus.cs index 9ff0c7d2..f5026ff1 100644 --- a/StabilityMatrix.Core/Models/Packages/Fooocus.cs +++ b/StabilityMatrix.Core/Models/Packages/Fooocus.cs @@ -94,7 +94,8 @@ public class Fooocus : BaseGitPackage public override async Task InstallPackage( string installLocation, TorchVersion torchVersion, - IProgress? progress = null + IProgress? progress = null, + Action? onConsoleOutput = null ) { await base.InstallPackage(installLocation, torchVersion, progress).ConfigureAwait(false); @@ -122,7 +123,7 @@ public class Fooocus : BaseGitPackage await venvRunner .PipInstall( $"torch==2.0.1 torchvision==0.15.2 --extra-index-url https://download.pytorch.org/whl/{torchVersionStr}", - OnConsoleOutput + onConsoleOutput ) .ConfigureAwait(false); @@ -130,21 +131,22 @@ public class Fooocus : BaseGitPackage new ProgressReport(-1f, "Installing requirements...", isIndeterminate: true) ); await venvRunner - .PipInstall("-r requirements_versions.txt", OnConsoleOutput) + .PipInstall("-r requirements_versions.txt", onConsoleOutput) .ConfigureAwait(false); } public override async Task RunPackage( string installedPackagePath, string command, - string arguments + string arguments, + Action? onConsoleOutput ) { await SetupVenv(installedPackagePath).ConfigureAwait(false); void HandleConsoleOutput(ProcessOutput s) { - OnConsoleOutput(s); + onConsoleOutput?.Invoke(s); if (s.Text.Contains("Use the app with", StringComparison.OrdinalIgnoreCase)) { diff --git a/StabilityMatrix.Core/Models/Packages/InvokeAI.cs b/StabilityMatrix.Core/Models/Packages/InvokeAI.cs index 5f71f3ba..c1f891a3 100644 --- a/StabilityMatrix.Core/Models/Packages/InvokeAI.cs +++ b/StabilityMatrix.Core/Models/Packages/InvokeAI.cs @@ -150,7 +150,8 @@ public class InvokeAI : BaseGitPackage public override async Task InstallPackage( string installLocation, TorchVersion torchVersion, - IProgress? progress = null + IProgress? progress = null, + Action? onConsoleOutput = null ) { // Setup venv @@ -186,10 +187,10 @@ public class InvokeAI : BaseGitPackage break; } - await venvRunner.PipInstall(pipCommandArgs, OnConsoleOutput).ConfigureAwait(false); + await venvRunner.PipInstall(pipCommandArgs, onConsoleOutput).ConfigureAwait(false); await venvRunner - .PipInstall("rich packaging python-dotenv", OnConsoleOutput) + .PipInstall("rich packaging python-dotenv", onConsoleOutput) .ConfigureAwait(false); progress?.Report(new ProgressReport(-1f, "Configuring InvokeAI", isIndeterminate: true)); @@ -198,7 +199,8 @@ public class InvokeAI : BaseGitPackage installLocation, "invokeai-configure", "--yes --skip-sd-weights", - false + false, + onConsoleOutput ) .ConfigureAwait(false); @@ -209,7 +211,8 @@ public class InvokeAI : BaseGitPackage InstalledPackage installedPackage, TorchVersion torchVersion, IProgress? progress = null, - bool includePrerelease = false + bool includePrerelease = false, + Action? onConsoleOutput = null ) { progress?.Report(new ProgressReport(-1f, "Setting up venv", isIndeterminate: true)); @@ -260,7 +263,7 @@ public class InvokeAI : BaseGitPackage break; } - await venvRunner.PipInstall(pipCommandArgs, OnConsoleOutput).ConfigureAwait(false); + await venvRunner.PipInstall(pipCommandArgs, onConsoleOutput).ConfigureAwait(false); progress?.Report(new ProgressReport(1f, "Done!", isIndeterminate: false)); @@ -276,8 +279,9 @@ public class InvokeAI : BaseGitPackage public override Task RunPackage( string installedPackagePath, string command, - string arguments - ) => RunInvokeCommand(installedPackagePath, command, arguments, true); + string arguments, + Action? onConsoleOutput + ) => RunInvokeCommand(installedPackagePath, command, arguments, true, onConsoleOutput); private async Task GetUpdateVersion( InstalledPackage installedPackage, @@ -304,7 +308,8 @@ public class InvokeAI : BaseGitPackage string installedPackagePath, string command, string arguments, - bool runDetached + bool runDetached, + Action? onConsoleOutput ) { await SetupVenv(installedPackagePath).ConfigureAwait(false); @@ -344,7 +349,7 @@ public class InvokeAI : BaseGitPackage { void HandleConsoleOutput(ProcessOutput s) { - OnConsoleOutput(s); + onConsoleOutput?.Invoke(s); if (!s.Text.Contains("running on", StringComparison.OrdinalIgnoreCase)) return; @@ -369,7 +374,7 @@ public class InvokeAI : BaseGitPackage var result = await VenvRunner .Run($"-c \"{code}\" {arguments}".TrimEnd()) .ConfigureAwait(false); - OnConsoleOutput(new ProcessOutput { Text = result.StandardOutput }); + onConsoleOutput?.Invoke(new ProcessOutput { Text = result.StandardOutput }); } } diff --git a/StabilityMatrix.Core/Models/Packages/UnknownPackage.cs b/StabilityMatrix.Core/Models/Packages/UnknownPackage.cs index cd22eec6..5be7baf0 100644 --- a/StabilityMatrix.Core/Models/Packages/UnknownPackage.cs +++ b/StabilityMatrix.Core/Models/Packages/UnknownPackage.cs @@ -2,6 +2,7 @@ using StabilityMatrix.Core.Models.Database; using StabilityMatrix.Core.Models.FileInterfaces; using StabilityMatrix.Core.Models.Progress; +using StabilityMatrix.Core.Processes; namespace StabilityMatrix.Core.Models.Packages; @@ -38,13 +39,19 @@ public class UnknownPackage : BasePackage public override Task InstallPackage( string installLocation, TorchVersion torchVersion, - IProgress? progress = null + IProgress? progress = null, + Action? onConsoleOutput = null ) { throw new NotImplementedException(); } - public override Task RunPackage(string installedPackagePath, string command, string arguments) + public override Task RunPackage( + string installedPackagePath, + string command, + string arguments, + Action? onConsoleOutput + ) { throw new NotImplementedException(); } @@ -102,7 +109,8 @@ public class UnknownPackage : BasePackage InstalledPackage installedPackage, TorchVersion torchVersion, IProgress? progress = null, - bool includePrerelease = false + bool includePrerelease = false, + Action? onConsoleOutput = null ) { throw new NotImplementedException(); diff --git a/StabilityMatrix.Core/Models/Packages/VladAutomatic.cs b/StabilityMatrix.Core/Models/Packages/VladAutomatic.cs index e98b367c..b34730e7 100644 --- a/StabilityMatrix.Core/Models/Packages/VladAutomatic.cs +++ b/StabilityMatrix.Core/Models/Packages/VladAutomatic.cs @@ -161,7 +161,8 @@ public class VladAutomatic : BaseGitPackage public override async Task InstallPackage( string installLocation, TorchVersion torchVersion, - IProgress? progress = null + IProgress? progress = null, + Action? onConsoleOutput = null ) { progress?.Report(new ProgressReport(-1f, "Installing package...", isIndeterminate: true)); @@ -177,23 +178,23 @@ public class VladAutomatic : BaseGitPackage // Run initial install case TorchVersion.Cuda: await venvRunner - .CustomInstall("launch.py --use-cuda --debug --test", OnConsoleOutput) + .CustomInstall("launch.py --use-cuda --debug --test", onConsoleOutput) .ConfigureAwait(false); break; case TorchVersion.Rocm: await venvRunner - .CustomInstall("launch.py --use-rocm --debug --test", OnConsoleOutput) + .CustomInstall("launch.py --use-rocm --debug --test", onConsoleOutput) .ConfigureAwait(false); break; case TorchVersion.DirectMl: await venvRunner - .CustomInstall("launch.py --use-directml --debug --test", OnConsoleOutput) + .CustomInstall("launch.py --use-directml --debug --test", onConsoleOutput) .ConfigureAwait(false); break; default: // CPU await venvRunner - .CustomInstall("launch.py --debug --test", OnConsoleOutput) + .CustomInstall("launch.py --debug --test", onConsoleOutput) .ConfigureAwait(false); break; } @@ -252,14 +253,15 @@ public class VladAutomatic : BaseGitPackage public override async Task RunPackage( string installedPackagePath, string command, - string arguments + string arguments, + Action? onConsoleOutput ) { await SetupVenv(installedPackagePath).ConfigureAwait(false); void HandleConsoleOutput(ProcessOutput s) { - OnConsoleOutput(s); + onConsoleOutput?.Invoke(s); if (s.Text.Contains("Running on local URL", StringComparison.OrdinalIgnoreCase)) { var regex = new Regex(@"(https?:\/\/)([^:\s]+):(\d+)"); @@ -287,7 +289,8 @@ public class VladAutomatic : BaseGitPackage InstalledPackage installedPackage, TorchVersion torchVersion, IProgress? progress = null, - bool includePrerelease = false + bool includePrerelease = false, + Action? onConsoleOutput = null ) { if (installedPackage.Version is null) @@ -313,7 +316,7 @@ public class VladAutomatic : BaseGitPackage venvRunner.EnvironmentVariables = SettingsManager.Settings.EnvironmentVariables; await venvRunner - .CustomInstall("launch.py --upgrade --test", OnConsoleOutput) + .CustomInstall("launch.py --upgrade --test", onConsoleOutput) .ConfigureAwait(false); try diff --git a/StabilityMatrix.Core/Models/Packages/VoltaML.cs b/StabilityMatrix.Core/Models/Packages/VoltaML.cs index 79daccc2..524e863d 100644 --- a/StabilityMatrix.Core/Models/Packages/VoltaML.cs +++ b/StabilityMatrix.Core/Models/Packages/VoltaML.cs @@ -136,7 +136,8 @@ public class VoltaML : BaseGitPackage public override async Task InstallPackage( string installLocation, TorchVersion torchVersion, - IProgress? progress = null + IProgress? progress = null, + Action? onConsoleOutput = null ) { await base.InstallPackage(installLocation, torchVersion, progress).ConfigureAwait(false); @@ -153,7 +154,7 @@ public class VoltaML : BaseGitPackage new ProgressReport(-1, "Installing Package Requirements", isIndeterminate: true) ); await venvRunner - .PipInstall("rich packaging python-dotenv", OnConsoleOutput) + .PipInstall("rich packaging python-dotenv", onConsoleOutput) .ConfigureAwait(false); progress?.Report( @@ -164,7 +165,8 @@ public class VoltaML : BaseGitPackage public override async Task RunPackage( string installedPackagePath, string command, - string arguments + string arguments, + Action? onConsoleOutput ) { await SetupVenv(installedPackagePath).ConfigureAwait(false); @@ -175,7 +177,7 @@ public class VoltaML : BaseGitPackage void HandleConsoleOutput(ProcessOutput s) { - OnConsoleOutput(s); + onConsoleOutput?.Invoke(s); if (s.Text.Contains("running on", StringComparison.OrdinalIgnoreCase)) {