Browse Source

many change

- Added more metadata to the image dialog info flyout
- Added Restart button to console page
- Model download location selector now searches all subfolders
- Fixed Civitai model browser not showing images when "Show NSFW" is disabled
- Fixed crash when Installed Workflows page is opened with no Workflows folder
- Fixed progress bars not displaying properly during package installs & updates
pull/629/head
JT 8 months ago
parent
commit
04db64fcfb
  1. 13
      CHANGELOG.md
  2. 14
      StabilityMatrix.Avalonia/Assets/sitecustomize.py
  3. 2
      StabilityMatrix.Avalonia/DesignData/DesignData.cs
  4. 21
      StabilityMatrix.Avalonia/Models/PackageSteps/UnpackSiteCustomizeStep.cs
  5. 2
      StabilityMatrix.Avalonia/Services/INavigationService.cs
  6. 2
      StabilityMatrix.Avalonia/Services/NavigationService.cs
  7. 5
      StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CheckpointBrowserCardViewModel.cs
  8. 19
      StabilityMatrix.Avalonia/ViewModels/Dialogs/NewOneClickInstallViewModel.cs
  9. 6
      StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectModelVersionViewModel.cs
  10. 5
      StabilityMatrix.Avalonia/ViewModels/InstalledWorkflowsViewModel.cs
  11. 3
      StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageInstallDetailViewModel.cs
  12. 15
      StabilityMatrix.Avalonia/ViewModels/Progress/PackageInstallProgressItemViewModel.cs
  13. 10
      StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs
  14. 10
      StabilityMatrix.Avalonia/Views/ConsoleOutputPage.axaml
  15. 23
      StabilityMatrix.Avalonia/Views/Dialogs/ImageViewerDialog.axaml
  16. 11
      StabilityMatrix.Core/Extensions/ProgressExtensions.cs
  17. 14
      StabilityMatrix.Core/Models/Api/CivitImage.cs
  18. 2
      StabilityMatrix.Core/Models/PackageModification/InstallPackageStep.cs
  19. 9
      StabilityMatrix.Core/Models/PackageModification/UpdatePackageStep.cs
  20. 66
      StabilityMatrix.Core/Models/Progress/ProgressReport.cs

13
CHANGELOG.md

@ -5,7 +5,18 @@ 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.10.0-preview.1
## v2.10.0-pre.2
### Added
- Added more metadata to the image dialog info flyout
- Added Restart button to console page
### Changed
- Model download location selector now searches all subfolders
### Fixed
- Fixed Civitai model browser not showing images when "Show NSFW" is disabled
- Fixed crash when Installed Workflows page is opened with no Workflows folder
- Fixed progress bars not displaying properly during package installs & updates
## v2.10.0-pre.1
### Added
- Added OpenArt.AI workflow browser for ComfyUI workflows
- Added Output Sharing toggle in Advanced Options during install flow

14
StabilityMatrix.Avalonia/Assets/sitecustomize.py

@ -68,6 +68,20 @@ def _patch_rich_console():
pass
except Exception as e:
print("[sitecustomize error]:", e)
try:
from pip._vendor.rich import console
class _Console(console.Console):
@property
def is_terminal(self) -> bool:
return True
console.Console = _Console
except ImportError:
pass
except Exception as e:
print("[sitecustomize error]:", e)
_patch_rich_console()

2
StabilityMatrix.Avalonia/DesignData/DesignData.cs

@ -360,7 +360,7 @@ public static class DesignData
{
new()
{
Nsfw = "None",
NsfwLevel = 1,
Url =
"https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/"
+ "78fd2a0a-42b6-42b0-9815-81cb11bb3d05/00009-2423234823.jpeg"

21
StabilityMatrix.Avalonia/Models/PackageSteps/UnpackSiteCustomizeStep.cs

@ -0,0 +1,21 @@
using System;
using System.Threading.Tasks;
using StabilityMatrix.Core.Models.FileInterfaces;
using StabilityMatrix.Core.Models.PackageModification;
using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Python;
namespace StabilityMatrix.Avalonia.Models.PackageSteps;
public class UnpackSiteCustomizeStep(DirectoryPath venvPath) : IPackageStep
{
public async Task ExecuteAsync(IProgress<ProgressReport>? progress = null)
{
var sitePackages = venvPath.JoinDir(PyVenvRunner.RelativeSitePackagesPath);
var file = sitePackages.JoinFile("sitecustomize.py");
file.Directory?.Create();
await Assets.PyScriptSiteCustomize.ExtractTo(file);
}
public string ProgressTitle => "Unpacking prerequisites...";
}

2
StabilityMatrix.Avalonia/Services/INavigationService.cs

@ -37,4 +37,6 @@ public interface INavigationService<[SuppressMessage("ReSharper", "UnusedTypePar
void NavigateTo(ViewModelBase viewModel, NavigationTransitionInfo? transitionInfo = null);
bool GoBack();
bool CanGoBack { get; }
}

2
StabilityMatrix.Avalonia/Services/NavigationService.cs

@ -188,4 +188,6 @@ public class NavigationService<T> : INavigationService<T>
_frame.GoBack();
return true;
}
public bool CanGoBack => _frame?.CanGoBack ?? false;
}

5
StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CheckpointBrowserCardViewModel.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
@ -159,7 +160,7 @@ public partial class CheckpointBrowserCardViewModel : Base.ProgressViewModel
// Try to find a valid image
var image = images
?.Where(img => LocalModelFile.SupportedImageExtensions.Any(img.Url.Contains))
.FirstOrDefault(image => nsfwEnabled || image.Nsfw == "None");
.FirstOrDefault(image => nsfwEnabled || image.NsfwLevel <= 1);
if (image != null)
{
CardImage = new Uri(image.Url);
@ -256,7 +257,7 @@ public partial class CheckpointBrowserCardViewModel : Base.ProgressViewModel
{
var subFolder =
viewModel?.SelectedInstallLocation
?? Path.Combine("Models", model.Type.ConvertTo<SharedFolderType>().GetStringValue());
?? Path.Combine(@"Models", model.Type.ConvertTo<SharedFolderType>().GetStringValue());
downloadPath = Path.Combine(settingsManager.LibraryDir, subFolder);
}

19
StabilityMatrix.Avalonia/ViewModels/Dialogs/NewOneClickInstallViewModel.cs

@ -12,6 +12,7 @@ using DynamicData;
using DynamicData.Binding;
using Microsoft.Extensions.Logging;
using StabilityMatrix.Avalonia.Extensions;
using StabilityMatrix.Avalonia.Models.PackageSteps;
using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Core.Attributes;
@ -107,18 +108,19 @@ public partial class NewOneClickInstallViewModel : ContentDialogViewModelBase
{
Task.Run(async () =>
{
var installLocation = Path.Combine(
settingsManager.LibraryDir,
"Packages",
selectedPackage.Name
);
var steps = new List<IPackageStep>
{
new SetPackageInstallingStep(settingsManager, selectedPackage.Name),
new SetupPrerequisitesStep(prerequisiteHelper, pyRunner, selectedPackage)
new SetupPrerequisitesStep(prerequisiteHelper, pyRunner, selectedPackage),
};
// get latest version & download & install
var installLocation = Path.Combine(
settingsManager.LibraryDir,
"Packages",
selectedPackage.Name
);
if (Directory.Exists(installLocation))
{
var installPath = new DirectoryPath(installLocation);
@ -148,6 +150,11 @@ public partial class NewOneClickInstallViewModel : ContentDialogViewModelBase
);
steps.Add(downloadStep);
var unpackSiteCustomizeStep = new UnpackSiteCustomizeStep(
Path.Combine(installLocation, "venv")
);
steps.Add(unpackSiteCustomizeStep);
var installStep = new InstallPackageStep(
selectedPackage,
torchVersion,

6
StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectModelVersionViewModel.cs

@ -99,7 +99,7 @@ public partial class SelectModelVersionViewModel(
var allImages = value
?.ModelVersion
?.Images
?.Where(img => nsfwEnabled || img.Nsfw == "None")
?.Where(img => nsfwEnabled || img.NsfwLevel <= 2)
?.Select(x => new ImageSource(x.Url))
.ToList();
@ -316,7 +316,9 @@ public partial class SelectModelVersionViewModel(
installLocations.Add(downloadDirectory.ToString().Replace(rootModelsDirectory, "Models"));
foreach (var directory in downloadDirectory.EnumerateDirectories())
foreach (
var directory in downloadDirectory.EnumerateDirectories(searchOption: SearchOption.AllDirectories)
)
{
installLocations.Add(directory.ToString().Replace(rootModelsDirectory, "Models"));
}

5
StabilityMatrix.Avalonia/ViewModels/InstalledWorkflowsViewModel.cs

@ -59,6 +59,11 @@ public partial class InstalledWorkflowsViewModel(
{
workflowsCache.Clear();
if (!Directory.Exists(settingsManager.WorkflowDirectory))
{
Directory.CreateDirectory(settingsManager.WorkflowDirectory);
}
foreach (
var workflowPath in Directory.EnumerateFiles(
settingsManager.WorkflowDirectory,

3
StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageInstallDetailViewModel.cs

@ -14,6 +14,7 @@ using Microsoft.Extensions.Logging;
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Extensions;
using StabilityMatrix.Avalonia.Languages;
using StabilityMatrix.Avalonia.Models.PackageSteps;
using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Core.Attributes;
@ -188,6 +189,7 @@ public partial class PackageInstallDetailViewModel(
}
var prereqStep = new SetupPrerequisitesStep(prerequisiteHelper, pyRunner, SelectedPackage);
var unpackSiteCustomizeStep = new UnpackSiteCustomizeStep(Path.Combine(installLocation, "venv"));
var downloadOptions = new DownloadPackageVersionOptions();
var installedVersion = new InstalledPackageVersion();
@ -252,6 +254,7 @@ public partial class PackageInstallDetailViewModel(
setPackageInstallingStep,
prereqStep,
downloadStep,
unpackSiteCustomizeStep,
installStep,
setupModelFoldersStep,
addInstalledPackageStep

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

@ -49,10 +49,21 @@ public class PackageInstallProgressItemViewModel : ProgressItemViewModelBase
Name = packageModificationRunner.CurrentStep?.ProgressTitle;
Failed = packageModificationRunner.Failed;
if (string.IsNullOrWhiteSpace(e.Message) || e.Message.Contains("Downloading..."))
if (e.ProcessOutput == null && string.IsNullOrWhiteSpace(e.Message))
return;
Progress.Console.PostLine(e.Message);
if (!string.IsNullOrWhiteSpace(e.Message) && e.Message.Contains("Downloading..."))
return;
if (e.ProcessOutput != null)
{
Progress.Console.Post(e.ProcessOutput.Value);
}
else
{
Progress.Console.PostLine(e.Message);
}
EventManager.Instance.OnScrollToBottomRequested();
if (

10
StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs

@ -104,6 +104,14 @@ public partial class RunningPackageViewModel : PageViewModelBase, IDisposable, I
}
}
[RelayCommand]
private async Task Restart()
{
await Stop();
await Task.Delay(100);
LaunchPackage();
}
[RelayCommand]
private void LaunchPackage()
{
@ -113,10 +121,10 @@ public partial class RunningPackageViewModel : PageViewModelBase, IDisposable, I
[RelayCommand]
private async Task Stop()
{
IsRunning = false;
await runningPackageService.StopPackage(RunningPackage.InstalledPackage.Id);
Console.PostLine($"{Environment.NewLine}Stopped process at {DateTimeOffset.Now}");
await Console.StopUpdatesAsync();
IsRunning = false;
}
[RelayCommand]

10
StabilityMatrix.Avalonia/Views/ConsoleOutputPage.axaml

@ -34,8 +34,16 @@
Command="{Binding LaunchPackageCommand}"
VerticalAlignment="Center"
Label="{x:Static lang:Resources.Action_Launch}" />
<ui:CommandBarButton
IconSource="Refresh"
Margin="4,0,0,0"
Classes="info"
IsVisible="{Binding IsRunning}"
Command="{Binding RestartCommand}"
VerticalAlignment="Center"
Label="{x:Static lang:Resources.Action_Restart}" />
<ui:CommandBarSeparator Margin="6,0,4,0"/>
<ui:CommandBarSeparator Margin="16,0,16,0"/>
<ui:CommandBarToggleButton
VerticalAlignment="Center"

23
StabilityMatrix.Avalonia/Views/Dialogs/ImageViewerDialog.axaml

@ -128,7 +128,7 @@
PlacementMargin="16,0,16,0"
TailVisibility="Collapsed"
Target="{Binding #InfoButton}">
<StackPanel Spacing="4" Margin="4,0" DataContext="{Binding LocalImageFile.GenerationParameters}">
<StackPanel Spacing="8" Margin="4,0" DataContext="{Binding LocalImageFile.GenerationParameters}">
<Grid RowDefinitions="Auto,*">
<TextBlock Text="Prompt" Theme="{DynamicResource BodyStrongTextBlockStyle}" />
<SelectableTextBlock
@ -150,6 +150,27 @@
Text="{Binding ModelName}" />
</Grid>
<Grid RowDefinitions="Auto,*">
<TextBlock Text="Sampler" Theme="{DynamicResource BodyStrongTextBlockStyle}" />
<SelectableTextBlock
Grid.Row="1"
Text="{Binding Sampler}" />
</Grid>
<Grid RowDefinitions="Auto,*">
<TextBlock Text="Steps" Theme="{DynamicResource BodyStrongTextBlockStyle}" />
<SelectableTextBlock
Grid.Row="1"
Text="{Binding Steps}" />
</Grid>
<Grid RowDefinitions="Auto,*">
<TextBlock Text="{x:Static lang:Resources.Label_CFGScale}" Theme="{DynamicResource BodyStrongTextBlockStyle}" />
<SelectableTextBlock
Grid.Row="1"
Text="{Binding CfgScale}" />
</Grid>
<Grid RowDefinitions="Auto,*">
<TextBlock Text="Seed" Theme="{DynamicResource BodyStrongTextBlockStyle}" />
<SelectableTextBlock

11
StabilityMatrix.Core/Extensions/ProgressExtensions.cs

@ -7,16 +7,19 @@ namespace StabilityMatrix.Core.Extensions;
public static class ProgressExtensions
{
[return: NotNullIfNotNull(nameof(progress))]
public static Action<ProcessOutput>? AsProcessOutputHandler(
this IProgress<ProgressReport>? progress
)
public static Action<ProcessOutput>? AsProcessOutputHandler(this IProgress<ProgressReport>? progress)
{
return progress == null
? null
: output =>
{
progress.Report(
new ProgressReport { IsIndeterminate = true, Message = output.Text }
new ProgressReport
{
IsIndeterminate = true,
Message = output.Text,
ProcessOutput = output
}
);
};
}

14
StabilityMatrix.Core/Models/Api/CivitImage.cs

@ -6,18 +6,18 @@ public class CivitImage
{
[JsonPropertyName("url")]
public string Url { get; set; }
[JsonPropertyName("nsfw")]
public string Nsfw { get; set; }
[JsonPropertyName("nsfwLevel")]
public int? NsfwLevel { get; set; }
[JsonPropertyName("width")]
public int Width { get; set; }
[JsonPropertyName("height")]
public int Height { get; set; }
[JsonPropertyName("hash")]
public string Hash { get; set; }
// TODO: "meta" ( object? )
}

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

@ -31,7 +31,7 @@ public class InstallPackageStep : IPackageStep
{
void OnConsoleOutput(ProcessOutput output)
{
progress?.Report(new ProgressReport { IsIndeterminate = true, Message = output.Text });
progress?.Report(new ProgressReport { IsIndeterminate = true, ProcessOutput = output });
}
await package

9
StabilityMatrix.Core/Models/PackageModification/UpdatePackageStep.cs

@ -31,7 +31,14 @@ public class UpdatePackageStep : IPackageStep
void OnConsoleOutput(ProcessOutput output)
{
progress?.Report(new ProgressReport { IsIndeterminate = true, Message = output.Text });
progress?.Report(
new ProgressReport
{
IsIndeterminate = true,
Message = output.Text,
ProcessOutput = output
}
);
}
var updateResult = await basePackage

66
StabilityMatrix.Core/Models/Progress/ProgressReport.cs

@ -1,4 +1,6 @@
namespace StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Processes;
namespace StabilityMatrix.Core.Models.Progress;
public record struct ProgressReport
{
@ -6,21 +8,30 @@ public record struct ProgressReport
/// Progress value as percentage between 0 and 1.
/// </summary>
public double? Progress { get; init; } = 0;
/// <summary>
/// Current progress count.
/// </summary>
public ulong? Current { get; init; } = 0;
/// <summary>
/// Total progress count.
/// </summary>
public ulong? Total { get; init; } = 0;
public string? Title { get; init; }
public string? Message { get; init; }
public ProcessOutput? ProcessOutput { get; init; }
public bool IsIndeterminate { get; init; } = false;
public float Percentage => (float) Math.Ceiling(Math.Clamp(Progress ?? 0, 0, 1) * 100);
public float Percentage => (float)Math.Ceiling(Math.Clamp(Progress ?? 0, 0, 1) * 100);
public ProgressType Type { get; init; } = ProgressType.Generic;
public ProgressReport(double progress, string? title = null, string? message = null, bool isIndeterminate = false, ProgressType type = ProgressType.Generic)
public ProgressReport(
double progress,
string? title = null,
string? message = null,
bool isIndeterminate = false,
ProgressType type = ProgressType.Generic
)
{
Progress = progress;
Title = title;
@ -28,32 +39,53 @@ public record struct ProgressReport
IsIndeterminate = isIndeterminate;
Type = type;
}
public ProgressReport(ulong current, ulong total, string? title = null, string? message = null, bool isIndeterminate = false, ProgressType type = ProgressType.Generic)
public ProgressReport(
ulong current,
ulong total,
string? title = null,
string? message = null,
bool isIndeterminate = false,
ProgressType type = ProgressType.Generic
)
{
Current = current;
Total = total;
Progress = (double) current / total;
Progress = (double)current / total;
Title = title;
Message = message;
IsIndeterminate = isIndeterminate;
Type = type;
}
public ProgressReport(int current, int total, string? title = null, string? message = null, bool isIndeterminate = false, ProgressType type = ProgressType.Generic)
public ProgressReport(
int current,
int total,
string? title = null,
string? message = null,
bool isIndeterminate = false,
ProgressType type = ProgressType.Generic
)
{
if (current < 0) throw new ArgumentOutOfRangeException(nameof(current), "Current progress cannot negative.");
if (total < 0) throw new ArgumentOutOfRangeException(nameof(total), "Total progress cannot be negative.");
Current = (ulong) current;
Total = (ulong) total;
Progress = (double) current / total;
if (current < 0)
throw new ArgumentOutOfRangeException(nameof(current), "Current progress cannot negative.");
if (total < 0)
throw new ArgumentOutOfRangeException(nameof(total), "Total progress cannot be negative.");
Current = (ulong)current;
Total = (ulong)total;
Progress = (double)current / total;
Title = title;
Message = message;
IsIndeterminate = isIndeterminate;
Type = type;
}
public ProgressReport(ulong current, string? title = null, string? message = null, ProgressType type = ProgressType.Generic)
public ProgressReport(
ulong current,
string? title = null,
string? message = null,
ProgressType type = ProgressType.Generic
)
{
Current = current;
Title = title;
@ -61,6 +93,6 @@ public record struct ProgressReport
IsIndeterminate = true;
Type = type;
}
// Implicit conversion from action
}

Loading…
Cancel
Save