Browse Source

Merge pull request #576 from ionite34/moar-backport

Moar backport
pull/530/head v2.9.2
JT 8 months ago committed by GitHub
parent
commit
233eba2fce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 14
      CHANGELOG.md
  2. 2
      StabilityMatrix.Avalonia/App.axaml
  3. 3
      StabilityMatrix.Avalonia/DesignData/DesignData.cs
  4. 8
      StabilityMatrix.Avalonia/Models/IInfinitelyScroll.cs
  5. 178
      StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitAiBrowserViewModel.cs
  6. 6
      StabilityMatrix.Avalonia/ViewModels/CheckpointManager/CheckpointFile.cs
  7. 5
      StabilityMatrix.Avalonia/ViewModels/Dialogs/NewOneClickInstallViewModel.cs
  8. 7
      StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs
  9. 19
      StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectModelVersionViewModel.cs
  10. 78
      StabilityMatrix.Avalonia/Views/CivitAiBrowserPage.axaml
  11. 16
      StabilityMatrix.Avalonia/Views/CivitAiBrowserPage.axaml.cs
  12. 39
      StabilityMatrix.Avalonia/Views/Dialogs/NewOneClickInstallDialog.axaml
  13. 4
      StabilityMatrix.Avalonia/Views/MainWindow.axaml
  14. 4
      StabilityMatrix.Core/Models/Api/CivitMetadata.cs
  15. 3
      StabilityMatrix.Core/Models/Api/CivitModelStats.cs
  16. 3
      StabilityMatrix.Core/Models/Api/CivitModelVersion.cs
  17. 26
      StabilityMatrix.Core/Models/Api/CivitModelsRequest.cs
  18. 12
      StabilityMatrix.Core/Models/PackageDifficulty.cs
  19. 2
      StabilityMatrix.Core/Models/PackageModification/IPackageModificationRunner.cs
  20. 2
      StabilityMatrix.Core/Models/PackageModification/PackageModificationRunner.cs
  21. 2
      StabilityMatrix.Core/Models/Packages/ComfyUI.cs
  22. 2
      StabilityMatrix.Core/Models/Packages/SDWebForge.cs

14
CHANGELOG.md

@ -5,6 +5,20 @@ 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/), 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). and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html).
## v2.9.2
### Changed
- Due to changes with the CivitAI API, you can no longer select a specific page in the CivitAI Model Browser
- Due to the above API changes, new pages are now loaded via "infinite scrolling"
### Fixed
- Fixed models not being removed from the installed models cache when deleting them from the Checkpoints page
- Fixed model download location options for VAEs in the CivitAI Model Browser
- Fixed One-Click install progress dialog not disappearing after completion
- Fixed ComfyUI with Inference pop-up during one-click install appearing below the visible scroll area
- Fixed no packages being available for one-click install on PCs without a GPU
- Fixed missing ratings on some models in the CivitAI Model Browser
- Fixed missing favorite count in the CivitAI Model Browser
- Fixed recommended models not showing all SDXL models
## v2.9.1 ## v2.9.1
### Added ### Added
- Fixed [#498](https://github.com/LykosAI/StabilityMatrix/issues/498) Added "Pony" category to CivitAI Model Browser - Fixed [#498](https://github.com/LykosAI/StabilityMatrix/issues/498) Added "Pony" category to CivitAI Model Browser

2
StabilityMatrix.Avalonia/App.axaml

@ -4,6 +4,7 @@
xmlns:local="using:StabilityMatrix.Avalonia" xmlns:local="using:StabilityMatrix.Avalonia"
xmlns:idcr="using:Dock.Avalonia.Controls.Recycling" xmlns:idcr="using:Dock.Avalonia.Controls.Recycling"
xmlns:styling="clr-namespace:FluentAvalonia.Styling;assembly=FluentAvalonia" xmlns:styling="clr-namespace:FluentAvalonia.Styling;assembly=FluentAvalonia"
xmlns:controls="clr-namespace:Avalonia.Labs.Controls;assembly=Avalonia.Labs.Controls"
Name="Stability Matrix" Name="Stability Matrix"
RequestedThemeVariant="Dark"> RequestedThemeVariant="Dark">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. --> <!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
@ -79,6 +80,7 @@
<StyleInclude Source="Controls/Inference/FreeUCard.axaml"/> <StyleInclude Source="Controls/Inference/FreeUCard.axaml"/>
<StyleInclude Source="Controls/Inference/ControlNetCard.axaml"/> <StyleInclude Source="Controls/Inference/ControlNetCard.axaml"/>
<StyleInclude Source="Controls/Inference/PromptExpansionCard.axaml"/> <StyleInclude Source="Controls/Inference/PromptExpansionCard.axaml"/>
<controls:ControlThemes/>
<Style Selector="DockControl"> <Style Selector="DockControl">
<Setter Property="(DockProperties.ControlRecycling)" Value="{StaticResource ControlRecyclingKey}" /> <Setter Property="(DockProperties.ControlRecycling)" Value="{StaticResource ControlRecyclingKey}" />

3
StabilityMatrix.Avalonia/DesignData/DesignData.cs

@ -404,7 +404,8 @@ public static class DesignData
new PackageInstallProgressItemViewModel( new PackageInstallProgressItemViewModel(
new PackageModificationRunner new PackageModificationRunner
{ {
CurrentProgress = new ProgressReport(0.5f, "Installing package...") CurrentProgress = new ProgressReport(0.5f, "Installing package..."),
ModificationCompleteMessage = "Done"
} }
) )
} }

8
StabilityMatrix.Avalonia/Models/IInfinitelyScroll.cs

@ -0,0 +1,8 @@
using System.Threading.Tasks;
namespace StabilityMatrix.Avalonia.Models;
public interface IInfinitelyScroll
{
Task LoadNextPageAsync();
}

178
StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitAiBrowserViewModel.cs

@ -1,13 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Reactive;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsyncAwaitBestPractices; using AsyncAwaitBestPractices;
using Avalonia.Collections; using Avalonia.Collections;
@ -15,12 +12,15 @@ using Avalonia.Controls;
using Avalonia.Controls.Notifications; using Avalonia.Controls.Notifications;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using DynamicData;
using DynamicData.Alias;
using DynamicData.Binding;
using LiteDB; using LiteDB;
using LiteDB.Async; using LiteDB.Async;
using NLog; using NLog;
using OneOf.Types;
using Refit; using Refit;
using StabilityMatrix.Avalonia.Languages; using StabilityMatrix.Avalonia.Languages;
using StabilityMatrix.Avalonia.Models;
using StabilityMatrix.Avalonia.Services; using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Avalonia.ViewModels.CheckpointManager; using StabilityMatrix.Avalonia.ViewModels.CheckpointManager;
@ -41,24 +41,24 @@ namespace StabilityMatrix.Avalonia.ViewModels.CheckpointBrowser;
[View(typeof(CivitAiBrowserPage))] [View(typeof(CivitAiBrowserPage))]
[Singleton] [Singleton]
public partial class CivitAiBrowserViewModel : TabViewModelBase public partial class CivitAiBrowserViewModel : TabViewModelBase, IInfinitelyScroll
{ {
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly ICivitApi civitApi; private readonly ICivitApi civitApi;
private readonly IDownloadService downloadService;
private readonly ISettingsManager settingsManager; private readonly ISettingsManager settingsManager;
private readonly ServiceManager<ViewModelBase> dialogFactory; private readonly ServiceManager<ViewModelBase> dialogFactory;
private readonly ILiteDbContext liteDbContext; private readonly ILiteDbContext liteDbContext;
private readonly INotificationService notificationService; private readonly INotificationService notificationService;
private const int MaxModelsPerPage = 20; private const int MaxModelsPerPage = 20;
private LRUCache< private LRUCache<
int /* model id */ int /* model id */
, ,
CheckpointBrowserCardViewModel CheckpointBrowserCardViewModel
> cache = new(50); > cache = new(150);
[ObservableProperty] [ObservableProperty]
private ObservableCollection<CheckpointBrowserCardViewModel>? modelCards; private ObservableCollection<CheckpointBrowserCardViewModel> modelCards = new();
[ObservableProperty] [ObservableProperty]
private DataGridCollectionView? modelCardsView; private DataGridCollectionView? modelCardsView;
@ -81,27 +81,9 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase
[ObservableProperty] [ObservableProperty]
private CivitModelType selectedModelType = CivitModelType.Checkpoint; private CivitModelType selectedModelType = CivitModelType.Checkpoint;
[ObservableProperty]
private int currentPageNumber;
[ObservableProperty]
private int totalPages;
[ObservableProperty] [ObservableProperty]
private bool hasSearched; private bool hasSearched;
[ObservableProperty]
private bool canGoToNextPage;
[ObservableProperty]
private bool canGoToPreviousPage;
[ObservableProperty]
private bool canGoToFirstPage;
[ObservableProperty]
private bool canGoToLastPage;
[ObservableProperty] [ObservableProperty]
private bool isIndeterminate; private bool isIndeterminate;
@ -117,7 +99,8 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase
[ObservableProperty] [ObservableProperty]
private bool showSantaHats = true; private bool showSantaHats = true;
private List<CheckpointBrowserCardViewModel> allModelCards = new(); [ObservableProperty]
private string? nextPageCursor;
public IEnumerable<CivitPeriod> AllCivitPeriods => public IEnumerable<CivitPeriod> AllCivitPeriods =>
Enum.GetValues(typeof(CivitPeriod)).Cast<CivitPeriod>(); Enum.GetValues(typeof(CivitPeriod)).Cast<CivitPeriod>();
@ -143,25 +126,11 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase
) )
{ {
this.civitApi = civitApi; this.civitApi = civitApi;
this.downloadService = downloadService;
this.settingsManager = settingsManager; this.settingsManager = settingsManager;
this.dialogFactory = dialogFactory; this.dialogFactory = dialogFactory;
this.liteDbContext = liteDbContext; this.liteDbContext = liteDbContext;
this.notificationService = notificationService; this.notificationService = notificationService;
CurrentPageNumber = 1;
CanGoToNextPage = true;
CanGoToLastPage = true;
Observable
.FromEventPattern<PropertyChangedEventArgs>(this, nameof(PropertyChanged))
.Where(x => x.EventArgs.PropertyName == nameof(CurrentPageNumber))
.Throttle(TimeSpan.FromMilliseconds(250))
.Select<EventPattern<PropertyChangedEventArgs>, int>(_ => CurrentPageNumber)
.Where(page => page <= TotalPages && page > 0)
.ObserveOn(SynchronizationContext.Current)
.Subscribe(_ => TrySearchAgain(false).SafeFireAndForget(), err => Logger.Error(err));
EventManager.Instance.NavigateAndFindCivitModelRequested += OnNavigateAndFindCivitModelRequested; EventManager.Instance.NavigateAndFindCivitModelRequested += OnNavigateAndFindCivitModelRequested;
} }
@ -171,7 +140,7 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase
return; return;
SearchQuery = $"$#{e}"; SearchQuery = $"$#{e}";
SearchModelsCommand.ExecuteAsync(null).SafeFireAndForget(); SearchModelsCommand.ExecuteAsync(false).SafeFireAndForget();
} }
public override void OnLoaded() public override void OnLoaded()
@ -223,7 +192,7 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase
/// <summary> /// <summary>
/// Background update task /// Background update task
/// </summary> /// </summary>
private async Task CivitModelQuery(CivitModelsRequest request) private async Task CivitModelQuery(CivitModelsRequest request, bool isInfiniteScroll = false)
{ {
var timer = Stopwatch.StartNew(); var timer = Stopwatch.StartNew();
var queryText = request.Query; var queryText = request.Query;
@ -276,15 +245,9 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase
} }
); );
if (cacheNew) UpdateModelCards(models, isInfiniteScroll);
{
Logger.Debug("New cache entry, updating model cards"); NextPageCursor = modelsResponse.Metadata?.NextCursor;
UpdateModelCards(models, modelsResponse.Metadata);
}
else
{
Logger.Debug("Cache entry already exists, not updating model cards");
}
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@ -327,7 +290,7 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase
/// <summary> /// <summary>
/// Updates model cards using api response object. /// Updates model cards using api response object.
/// </summary> /// </summary>
private void UpdateModelCards(IEnumerable<CivitModel>? models, CivitMetadata? metadata) private void UpdateModelCards(List<CivitModel>? models, bool addCards = false)
{ {
if (models is null) if (models is null)
{ {
@ -335,7 +298,7 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase
} }
else else
{ {
var updateCards = models var modelsToAdd = models
.Select(model => .Select(model =>
{ {
var cachedViewModel = cache.Get(model.Id); var cachedViewModel = cache.Get(model.Id);
@ -364,23 +327,34 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase
return newCard; return newCard;
}) })
.ToList(); .Where(FilterModelCardsPredicate);
allModelCards = updateCards;
var filteredCards = updateCards.Where(FilterModelCardsPredicate);
if (SortMode == CivitSortMode.Installed) if (SortMode == CivitSortMode.Installed)
{ {
filteredCards = filteredCards.OrderByDescending(x => x.UpdateCardText == "Update Available"); modelsToAdd = modelsToAdd.OrderByDescending(x => x.UpdateCardText == "Update Available");
} }
ModelCards = new ObservableCollection<CheckpointBrowserCardViewModel>(filteredCards); if (!addCards)
{
ModelCards = new ObservableCollection<CheckpointBrowserCardViewModel>(modelsToAdd);
}
else
{
foreach (var model in modelsToAdd)
{
if (
ModelCards.Contains(
model,
new PropertyComparer<CheckpointBrowserCardViewModel>(x => x.CivitModel.Id)
)
)
continue;
ModelCards.Add(model);
}
}
} }
TotalPages = metadata?.TotalPages ?? 1;
CanGoToFirstPage = CurrentPageNumber != 1;
CanGoToPreviousPage = CurrentPageNumber > 1;
CanGoToNextPage = CurrentPageNumber < TotalPages;
CanGoToLastPage = CurrentPageNumber != TotalPages;
// Status update // Status update
ShowMainLoadingSpinner = false; ShowMainLoadingSpinner = false;
IsIndeterminate = false; IsIndeterminate = false;
@ -390,27 +364,30 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase
private string previousSearchQuery = string.Empty; private string previousSearchQuery = string.Empty;
[RelayCommand] [RelayCommand]
private async Task SearchModels() private async Task SearchModels(bool isInfiniteScroll = false)
{ {
var timer = Stopwatch.StartNew(); var timer = Stopwatch.StartNew();
if (SearchQuery != previousSearchQuery) if (SearchQuery != previousSearchQuery || !isInfiniteScroll)
{ {
// Reset page number // Reset page number
CurrentPageNumber = 1;
previousSearchQuery = SearchQuery; previousSearchQuery = SearchQuery;
NextPageCursor = null;
} }
// Build request // Build request
var modelRequest = new CivitModelsRequest var modelRequest = new CivitModelsRequest
{ {
Limit = MaxModelsPerPage,
Nsfw = "true", // Handled by local view filter Nsfw = "true", // Handled by local view filter
Sort = SortMode, Sort = SortMode,
Period = SelectedPeriod, Period = SelectedPeriod
Page = CurrentPageNumber
}; };
if (NextPageCursor != null)
{
modelRequest.Cursor = NextPageCursor;
}
if (SelectedModelType != CivitModelType.All) if (SelectedModelType != CivitModelType.All)
{ {
modelRequest.Types = [SelectedModelType]; modelRequest.Types = [SelectedModelType];
@ -516,14 +493,15 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase
modelRequest.GetHashCode(), modelRequest.GetHashCode(),
elapsed.TotalSeconds elapsed.TotalSeconds
); );
UpdateModelCards(cachedQuery.Items, cachedQuery.Metadata); NextPageCursor = cachedQuery.Metadata?.NextCursor;
UpdateModelCards(cachedQuery.Items, isInfiniteScroll);
// Start remote query (background mode) // Start remote query (background mode)
// Skip when last query was less than 2 min ago // Skip when last query was less than 2 min ago
var timeSinceCache = DateTimeOffset.UtcNow - cachedQuery.InsertedAt; var timeSinceCache = DateTimeOffset.UtcNow - cachedQuery.InsertedAt;
if (timeSinceCache?.TotalMinutes >= 2) if (timeSinceCache?.TotalMinutes >= 2)
{ {
CivitModelQuery(modelRequest).SafeFireAndForget(); CivitModelQuery(modelRequest, isInfiniteScroll).SafeFireAndForget();
Logger.Debug( Logger.Debug(
"Cached query was more than 2 minutes ago ({Seconds:F0} s), updating cache with remote query", "Cached query was more than 2 minutes ago ({Seconds:F0} s), updating cache with remote query",
timeSinceCache.Value.TotalSeconds timeSinceCache.Value.TotalSeconds
@ -534,54 +512,23 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase
{ {
// Not cached, wait for remote query // Not cached, wait for remote query
ShowMainLoadingSpinner = true; ShowMainLoadingSpinner = true;
await CivitModelQuery(modelRequest); await CivitModelQuery(modelRequest, isInfiniteScroll);
} }
UpdateResultsText(); UpdateResultsText();
} }
public void FirstPage()
{
CurrentPageNumber = 1;
}
public void PreviousPage()
{
if (CurrentPageNumber == 1)
return;
CurrentPageNumber--;
}
public void NextPage()
{
if (CurrentPageNumber == TotalPages)
return;
CurrentPageNumber++;
}
public void LastPage()
{
CurrentPageNumber = TotalPages;
}
public void ClearSearchQuery() public void ClearSearchQuery()
{ {
SearchQuery = string.Empty; SearchQuery = string.Empty;
} }
partial void OnShowNsfwChanged(bool value) public async Task LoadNextPageAsync()
{ {
settingsManager.Transaction(s => s.ModelBrowserNsfwEnabled, value); if (NextPageCursor != null)
// ModelCardsView?.Refresh(); {
var updateCards = allModelCards.Where(FilterModelCardsPredicate); await SearchModelsCommand.ExecuteAsync(true);
ModelCards = new ObservableCollection<CheckpointBrowserCardViewModel>(updateCards); }
if (!HasSearched)
return;
UpdateResultsText();
} }
partial void OnSelectedPeriodChanged(CivitPeriod value) partial void OnSelectedPeriodChanged(CivitPeriod value)
@ -596,6 +543,7 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase
SelectedBaseModelType SelectedBaseModelType
) )
); );
NextPageCursor = null;
} }
partial void OnSortModeChanged(CivitSortMode value) partial void OnSortModeChanged(CivitSortMode value)
@ -610,6 +558,7 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase
SelectedBaseModelType SelectedBaseModelType
) )
); );
NextPageCursor = null;
} }
partial void OnSelectedModelTypeChanged(CivitModelType value) partial void OnSelectedModelTypeChanged(CivitModelType value)
@ -624,6 +573,7 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase
SelectedBaseModelType SelectedBaseModelType
) )
); );
NextPageCursor = null;
} }
partial void OnSelectedBaseModelTypeChanged(string value) partial void OnSelectedBaseModelTypeChanged(string value)
@ -638,6 +588,7 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase
value value
) )
); );
NextPageCursor = null;
} }
private async Task TrySearchAgain(bool shouldUpdatePageNumber = true) private async Task TrySearchAgain(bool shouldUpdatePageNumber = true)
@ -648,18 +599,17 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase
if (shouldUpdatePageNumber) if (shouldUpdatePageNumber)
{ {
CurrentPageNumber = 1; NextPageCursor = null;
} }
// execute command instead of calling method directly so that the IsRunning property gets updated // execute command instead of calling method directly so that the IsRunning property gets updated
await SearchModelsCommand.ExecuteAsync(null); await SearchModelsCommand.ExecuteAsync(false);
} }
private void UpdateResultsText() private void UpdateResultsText()
{ {
NoResultsFound = ModelCards?.Count <= 0; NoResultsFound = ModelCards?.Count <= 0;
NoResultsText = NoResultsText = "No results found";
allModelCards.Count > 0 ? $"{allModelCards.Count} results hidden by filters" : "No results found";
} }
public override string Header => Resources.Label_CivitAi; public override string Header => Resources.Label_CivitAi;

6
StabilityMatrix.Avalonia/ViewModels/CheckpointManager/CheckpointFile.cs

@ -8,6 +8,7 @@ using Avalonia.Data;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Microsoft.Extensions.DependencyInjection;
using NLog; using NLog;
using StabilityMatrix.Avalonia.Languages; using StabilityMatrix.Avalonia.Languages;
using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.ViewModels.Base;
@ -136,6 +137,11 @@ public partial class CheckpointFile : ViewModelBase
{ {
await Task.Run(() => File.Delete(cmInfoPath)); await Task.Run(() => File.Delete(cmInfoPath));
} }
var settingsManager = App.Services.GetRequiredService<ISettingsManager>();
settingsManager.Transaction(s =>
{
s.InstalledModelHashes?.Remove(ConnectedModel.Hashes.BLAKE3);
});
} }
} }
catch (IOException ex) catch (IOException ex)

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

@ -85,6 +85,10 @@ public partial class NewOneClickInstallViewModel : ContentDialogViewModelBase
.Subscribe(); .Subscribe();
AllPackagesCache.AddOrUpdate(packageFactory.GetAllAvailablePackages()); AllPackagesCache.AddOrUpdate(packageFactory.GetAllAvailablePackages());
if (ShownPackages.Count > 0)
return;
ShowIncompatiblePackages = true;
} }
[RelayCommand] [RelayCommand]
@ -182,6 +186,7 @@ public partial class NewOneClickInstallViewModel : ContentDialogViewModelBase
{ {
ShowDialogOnStart = false, ShowDialogOnStart = false,
HideCloseButton = false, HideCloseButton = false,
ModificationCompleteMessage = $"{selectedPackage.DisplayName} installed successfully"
}; };
runner runner

7
StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs

@ -192,7 +192,12 @@ public partial class OneClickInstallViewModel : ContentDialogViewModelBase
var addInstalledPackageStep = new AddInstalledPackageStep(settingsManager, installedPackage); var addInstalledPackageStep = new AddInstalledPackageStep(settingsManager, installedPackage);
steps.Add(addInstalledPackageStep); steps.Add(addInstalledPackageStep);
var runner = new PackageModificationRunner { ShowDialogOnStart = true, HideCloseButton = true, }; var runner = new PackageModificationRunner
{
ShowDialogOnStart = true,
HideCloseButton = true,
ModificationCompleteMessage = $"{SelectedPackage.DisplayName} installed successfully",
};
EventManager.Instance.OnPackageInstallProgressAdded(runner); EventManager.Instance.OnPackageInstallProgressAdded(runner);
await runner.ExecuteSteps(steps); await runner.ExecuteSteps(steps);

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

@ -91,7 +91,6 @@ public partial class SelectModelVersionViewModel(
{ {
SelectedVersionViewModel = Versions[0]; SelectedVersionViewModel = Versions[0];
CanGoToNextImage = true; CanGoToNextImage = true;
LoadInstallLocations();
} }
partial void OnSelectedVersionViewModelChanged(ModelVersionViewModel? value) partial void OnSelectedVersionViewModelChanged(ModelVersionViewModel? value)
@ -130,6 +129,8 @@ public partial class SelectModelVersionViewModel(
var canImport = true; var canImport = true;
if (settingsManager.IsLibraryDirSet) if (settingsManager.IsLibraryDirSet)
{ {
LoadInstallLocations();
var fileSizeBytes = value?.CivitFile.SizeKb * 1024; var fileSizeBytes = value?.CivitFile.SizeKb * 1024;
var freeSizeBytes = var freeSizeBytes =
SystemInfo.GetDiskFreeSpaceBytes(settingsManager.ModelsDirectory) ?? long.MaxValue; SystemInfo.GetDiskFreeSpaceBytes(settingsManager.ModelsDirectory) ?? long.MaxValue;
@ -151,9 +152,9 @@ public partial class SelectModelVersionViewModel(
IsImportEnabled = value?.CivitFile != null && canImport && !ShowEmptyPathWarning; IsImportEnabled = value?.CivitFile != null && canImport && !ShowEmptyPathWarning;
} }
partial void OnSelectedInstallLocationChanged(string value) partial void OnSelectedInstallLocationChanged(string? value)
{ {
if (value.Equals("Custom...", StringComparison.OrdinalIgnoreCase)) if (value?.Equals("Custom...", StringComparison.OrdinalIgnoreCase) is true)
{ {
Dispatcher.UIThread.InvokeAsync(SelectCustomFolder); Dispatcher.UIThread.InvokeAsync(SelectCustomFolder);
} }
@ -304,14 +305,16 @@ public partial class SelectModelVersionViewModel(
private void LoadInstallLocations() private void LoadInstallLocations()
{ {
var installLocations = new ObservableCollection<string>();
var rootModelsDirectory = new DirectoryPath(settingsManager.ModelsDirectory); var rootModelsDirectory = new DirectoryPath(settingsManager.ModelsDirectory);
var downloadDirectory = rootModelsDirectory.JoinDir( var downloadDirectory = rootModelsDirectory.JoinDir(
CivitModel.Type.ConvertTo<SharedFolderType>().GetStringValue() SelectedFile?.CivitFile.Type == CivitFileType.VAE
? SharedFolderType.VAE.GetStringValue()
: CivitModel.Type.ConvertTo<SharedFolderType>().GetStringValue()
); );
var installLocations = new ObservableCollection<string>
{ installLocations.Add(downloadDirectory.ToString().Replace(rootModelsDirectory, "Models"));
downloadDirectory.ToString().Replace(rootModelsDirectory, "Models")
};
foreach (var directory in downloadDirectory.EnumerateDirectories()) foreach (var directory in downloadDirectory.EnumerateDirectories())
{ {

78
StabilityMatrix.Avalonia/Views/CivitAiBrowserPage.axaml

@ -15,6 +15,8 @@
xmlns:converters="clr-namespace:StabilityMatrix.Avalonia.Converters" xmlns:converters="clr-namespace:StabilityMatrix.Avalonia.Converters"
xmlns:asyncImageLoader="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia" xmlns:asyncImageLoader="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia"
xmlns:helpers="clr-namespace:StabilityMatrix.Avalonia.Helpers" xmlns:helpers="clr-namespace:StabilityMatrix.Avalonia.Helpers"
xmlns:controls1="clr-namespace:Avalonia.Labs.Controls;assembly=Avalonia.Labs.Controls"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
d:DataContext="{x:Static designData:DesignData.CivitAiBrowserViewModel}" d:DataContext="{x:Static designData:DesignData.CivitAiBrowserViewModel}"
d:DesignHeight="700" d:DesignHeight="700"
d:DesignWidth="800" d:DesignWidth="800"
@ -70,6 +72,7 @@
</UserControl.Styles> </UserControl.Styles>
<UserControl.Resources> <UserControl.Resources>
<system:Boolean x:Key="False">False</system:Boolean>
<converters:KiloFormatterStringConverter x:Key="KiloFormatterConverter"/> <converters:KiloFormatterStringConverter x:Key="KiloFormatterConverter"/>
<DataTemplate x:Key="CivitModelTemplate" DataType="{x:Type checkpointBrowser:CheckpointBrowserCardViewModel}"> <DataTemplate x:Key="CivitModelTemplate" DataType="{x:Type checkpointBrowser:CheckpointBrowserCardViewModel}">
<Border <Border
@ -100,28 +103,27 @@
CommandParameter="{Binding CivitModel}" CommandParameter="{Binding CivitModel}"
IsEnabled="{Binding !IsImporting}"> IsEnabled="{Binding !IsImporting}">
<Grid RowDefinitions="*, Auto"> <Grid RowDefinitions="*, Auto">
<controls:BetterAdvancedImage Grid.Row="0" <controls1:AsyncImage Grid.Row="0"
CornerRadius="8" CornerRadius="8"
VerticalAlignment="Top" VerticalAlignment="Top"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Height="75" Height="75"
ZIndex="10" ZIndex="10"
IsVisible="{Binding ShowSantaHats}" IsVisible="{Binding ShowSantaHats, FallbackValue=False}"
Margin="0,8,0,0" Margin="0,8,0,0"
Source="avares://StabilityMatrix.Avalonia/Assets/santahat.png"> Source="avares://StabilityMatrix.Avalonia/Assets/santahat.png">
<!-- <controls:BetterAdvancedImage.RenderTransform> --> <!-- <controls:BetterAdvancedImage.RenderTransform> -->
<!-- <RotateTransform Angle="315"></RotateTransform> --> <!-- <RotateTransform Angle="315"></RotateTransform> -->
<!-- </controls:BetterAdvancedImage.RenderTransform> --> <!-- </controls:BetterAdvancedImage.RenderTransform> -->
</controls:BetterAdvancedImage> </controls1:AsyncImage>
<controls:BetterAdvancedImage <controls1:AsyncImage
Grid.Row="0" Grid.Row="0"
Grid.RowSpan="2" Grid.RowSpan="2"
CornerRadius="8" CornerRadius="8"
Width="330" Width="330"
Height="400" Height="400"
Source="{Binding CardImage}" Source="{Binding CardImage}"
Stretch="UniformToFill" Stretch="UniformToFill"/>
StretchDirection="Both"/>
<StackPanel <StackPanel
Grid.Row="0" Grid.Row="0"
@ -180,7 +182,7 @@
Classes="transparent" Classes="transparent"
Padding="10,4"> Padding="10,4">
<StackPanel Orientation="Horizontal" Spacing="6"> <StackPanel Orientation="Horizontal" Spacing="6">
<controls:BetterAdvancedImage <controls1:AsyncImage
Width="22" Width="22"
Height="22" Height="22"
Effect="{StaticResource ImageDropShadowEffect}" Effect="{StaticResource ImageDropShadowEffect}"
@ -336,7 +338,7 @@
<TextBlock <TextBlock
Margin="4,0" Margin="4,0"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{Binding CivitModel.ModelVersionStats.FavoriteCount, Converter={StaticResource KiloFormatterConverter}}" /> Text="{Binding CivitModel.Stats.ThumbsUpCount, Converter={StaticResource KiloFormatterConverter}}" />
<avalonia:Icon Margin="4,0" Value="fa-solid fa-download" /> <avalonia:Icon Margin="4,0" Value="fa-solid fa-download" />
<TextBlock <TextBlock
@ -402,6 +404,7 @@
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
Classes="accent" Classes="accent"
Command="{Binding SearchModelsCommand}" Command="{Binding SearchModelsCommand}"
CommandParameter="{StaticResource False}"
IsDefault="True"> IsDefault="True">
<Grid> <Grid>
<controls:ProgressRing <controls:ProgressRing
@ -473,56 +476,17 @@
</ItemsRepeater> </ItemsRepeater>
</ScrollViewer> </ScrollViewer>
<TextBlock <TextBlock Grid.Row="2" Text="End of results"
Grid.Row="2" TextAlignment="Center"
Margin="8,8" Margin="0,0,0,8">
VerticalAlignment="Center" <TextBlock.IsVisible>
Text="{x:Static lang:Resources.Label_DataProvidedByCivitAi}" /> <MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="HasSearched"/>
<StackPanel Grid.Row="2" <Binding Path="NextPageCursor"
HorizontalAlignment="Center" Converter="{x:Static StringConverters.IsNullOrEmpty}"/>
IsVisible="{Binding HasSearched}" </MultiBinding>
Margin="0,8,0,8" </TextBlock.IsVisible>
Orientation="Horizontal">
<Button
Margin="0,0,8,0"
Command="{Binding FirstPage}"
IsEnabled="{Binding CanGoToFirstPage}"
ToolTip.Tip="{x:Static lang:Resources.Label_FirstPage}">
<avalonia:Icon Value="fa-solid fa-backward-fast" />
</Button>
<Button
Margin="0,0,16,0"
Command="{Binding PreviousPage}"
IsEnabled="{Binding CanGoToPreviousPage}"
ToolTip.Tip="{x:Static lang:Resources.Label_PreviousPage}">
<avalonia:Icon Value="fa-solid fa-caret-left" />
</Button>
<TextBlock Margin="8,0,4,0" TextAlignment="Center"
Text="{x:Static lang:Resources.Label_Page}"
VerticalAlignment="Center"/>
<ui:NumberBox Value="{Binding CurrentPageNumber, FallbackValue=1}"
VerticalAlignment="Center"
SpinButtonPlacementMode="Hidden"
TextAlignment="Center"/>
<TextBlock Margin="4,0,8,0" VerticalAlignment="Center">
<Run Text="/"/>
<Run Text="{Binding TotalPages, FallbackValue=5}"/>
</TextBlock> </TextBlock>
<Button
Margin="16,0,8,0"
Command="{Binding NextPage}"
IsEnabled="{Binding CanGoToNextPage}"
ToolTip.Tip="{x:Static lang:Resources.Label_NextPage}">
<avalonia:Icon Value="fa-solid fa-caret-right" />
</Button>
<Button
Command="{Binding LastPage}"
IsEnabled="{Binding CanGoToLastPage}"
ToolTip.Tip="{x:Static lang:Resources.Label_LastPage}">
<avalonia:Icon Value="fa-solid fa-forward-fast" />
</Button>
</StackPanel>
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"

16
StabilityMatrix.Avalonia/Views/CivitAiBrowserPage.axaml.cs

@ -1,8 +1,11 @@
using System.Diagnostics; using System;
using System.Diagnostics;
using AsyncAwaitBestPractices;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using StabilityMatrix.Avalonia.Controls; using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Models;
using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Attributes;
using CivitAiBrowserViewModel = StabilityMatrix.Avalonia.ViewModels.CheckpointBrowser.CivitAiBrowserViewModel; using CivitAiBrowserViewModel = StabilityMatrix.Avalonia.ViewModels.CheckpointBrowser.CivitAiBrowserViewModel;
@ -26,8 +29,15 @@ public partial class CivitAiBrowserPage : UserControlBase
if (sender is not ScrollViewer scrollViewer) if (sender is not ScrollViewer scrollViewer)
return; return;
var isAtEnd = scrollViewer.Offset == scrollViewer.ScrollBarMaximum; if (scrollViewer.Offset.Y == 0)
Debug.WriteLine($"IsAtEnd: {isAtEnd}"); return;
var isAtEnd = Math.Abs(scrollViewer.Offset.Y - scrollViewer.ScrollBarMaximum.Y) < 1f;
if (isAtEnd && DataContext is IInfinitelyScroll scroll)
{
scroll.LoadNextPageAsync().SafeFireAndForget();
}
} }
private void InputElement_OnKeyDown(object? sender, KeyEventArgs e) private void InputElement_OnKeyDown(object? sender, KeyEventArgs e)

39
StabilityMatrix.Avalonia/Views/Dialogs/NewOneClickInstallDialog.axaml

@ -62,6 +62,27 @@
VerticalAlignment="Top"> VerticalAlignment="Top">
<controls:Card.Styles> <controls:Card.Styles>
<Style Selector="controls|Card[Tag=ReallyRecommended]">
<Setter Property="Background"
Value="{DynamicResource ThemeGreenColorTransparent}" />
<Setter Property="BorderBrush"
Value="{DynamicResource ThemeGreenColorTransparent}" />
<Style
Selector="^ /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground"
Value="{DynamicResource ButtonForeground}" />
</Style>
<Setter Property="Content">
<Template>
<TextBlock
FontWeight="Medium"
HorizontalAlignment="Center"
Text="Recommended"
TextAlignment="Center"
VerticalAlignment="Center" />
</Template>
</Setter>
</Style>
<Style Selector="controls|Card[Tag=Recommended]"> <Style Selector="controls|Card[Tag=Recommended]">
<Setter Property="Background" <Setter Property="Background"
Value="{DynamicResource ThemeGreenColorTransparent}" /> Value="{DynamicResource ThemeGreenColorTransparent}" />
@ -117,6 +138,24 @@
</Template> </Template>
</Setter> </Setter>
</Style> </Style>
<Style Selector="controls|Card[Tag=InferenceCompatible]">
<Setter Property="Background"
Value="{DynamicResource ThemePurpleColor}" />
<Setter Property="BorderBrush"
Value="{DynamicResource ThemePurpleColor}" />
<Setter Property="Foreground"
Value="White" />
<Setter Property="Content">
<Template>
<TextBlock
FontWeight="Medium"
HorizontalAlignment="Center"
Text="Inference Compatible"
TextAlignment="Center"
VerticalAlignment="Center" />
</Template>
</Setter>
</Style>
<Style Selector="controls|Card"> <Style Selector="controls|Card">
<Setter Property="Background" <Setter Property="Background"
Value="{DynamicResource ThemeDarkRedColor}" /> Value="{DynamicResource ThemeDarkRedColor}" />

4
StabilityMatrix.Avalonia/Views/MainWindow.axaml

@ -14,8 +14,8 @@
d:DataContext="{x:Static mocks:DesignData.MainWindowViewModel}" d:DataContext="{x:Static mocks:DesignData.MainWindowViewModel}"
x:DataType="vm:MainWindowViewModel" x:DataType="vm:MainWindowViewModel"
Icon="/Assets/Icon.ico" Icon="/Assets/Icon.ico"
Width="1100" Width="1300"
Height="750" Height="950"
BackRequested="TopLevel_OnBackRequested" BackRequested="TopLevel_OnBackRequested"
Title="Stability Matrix" Title="Stability Matrix"
DockProperties.IsDragEnabled="True" DockProperties.IsDragEnabled="True"

4
StabilityMatrix.Core/Models/Api/CivitMetadata.cs

@ -2,7 +2,6 @@
namespace StabilityMatrix.Core.Models.Api; namespace StabilityMatrix.Core.Models.Api;
public class CivitMetadata public class CivitMetadata
{ {
[JsonPropertyName("totalItems")] [JsonPropertyName("totalItems")]
@ -22,4 +21,7 @@ public class CivitMetadata
[JsonPropertyName("prevPage")] [JsonPropertyName("prevPage")]
public string? PrevPage { get; set; } public string? PrevPage { get; set; }
[JsonPropertyName("nextCursor")]
public string? NextCursor { get; set; }
} }

3
StabilityMatrix.Core/Models/Api/CivitModelStats.cs

@ -9,4 +9,7 @@ public class CivitModelStats : CivitStats
[JsonPropertyName("commentCount")] [JsonPropertyName("commentCount")]
public int CommentCount { get; set; } public int CommentCount { get; set; }
[JsonPropertyName("thumbsUpCount")]
public int ThumbsUpCount { get; set; }
} }

3
StabilityMatrix.Core/Models/Api/CivitModelVersion.cs

@ -33,4 +33,7 @@ public class CivitModelVersion
[JsonPropertyName("stats")] [JsonPropertyName("stats")]
public CivitModelStats Stats { get; set; } public CivitModelStats Stats { get; set; }
[JsonPropertyName("publishedAt")]
public DateTimeOffset? PublishedAt { get; set; }
} }

26
StabilityMatrix.Core/Models/Api/CivitModelsRequest.cs

@ -2,7 +2,6 @@
namespace StabilityMatrix.Core.Models.Api; namespace StabilityMatrix.Core.Models.Api;
public class CivitModelsRequest public class CivitModelsRequest
{ {
/// <summary> /// <summary>
@ -121,18 +120,21 @@ public class CivitModelsRequest
[AliasAs("ids")] [AliasAs("ids")]
public string CommaSeparatedModelIds { get; set; } public string CommaSeparatedModelIds { get; set; }
[AliasAs("cursor")]
public string? Cursor { get; set; }
public override string ToString() public override string ToString()
{ {
return $"Page: {Page}, " + return $"Page: {Page}, "
$"Query: {Query}, " + + $"Query: {Query}, "
$"Tag: {Tag}, " + + $"Tag: {Tag}, "
$"Username: {Username}, " + + $"Username: {Username}, "
$"Types: {Types}, " + + $"Types: {Types}, "
$"Sort: {Sort}, " + + $"Sort: {Sort}, "
$"Period: {Period}, " + + $"Period: {Period}, "
$"Rating: {Rating}, " + + $"Rating: {Rating}, "
$"Nsfw: {Nsfw}, " + + $"Nsfw: {Nsfw}, "
$"BaseModel: {BaseModel}, " + + $"BaseModel: {BaseModel}, "
$"CommaSeparatedModelIds: {CommaSeparatedModelIds}"; + $"CommaSeparatedModelIds: {CommaSeparatedModelIds}";
} }
} }

12
StabilityMatrix.Core/Models/PackageDifficulty.cs

@ -2,11 +2,13 @@
public enum PackageDifficulty public enum PackageDifficulty
{ {
ReallyRecommended = -1,
Recommended = 0, Recommended = 0,
Simple = 1, InferenceCompatible = 1,
Advanced = 2, Simple = 2,
Expert = 3, Advanced = 3,
Nightmare = 4, Expert = 4,
UltraNightmare = 5, Nightmare = 5,
UltraNightmare = 6,
Impossible = 999 Impossible = 999
} }

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

@ -32,7 +32,7 @@ public interface IPackageModificationRunner
string? ModificationCompleteTitle { get; init; } string? ModificationCompleteTitle { get; init; }
string? ModificationCompleteMessage { get; init; } string ModificationCompleteMessage { get; init; }
string? ModificationFailedTitle { get; init; } string? ModificationFailedTitle { get; init; }

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

@ -80,7 +80,7 @@ public class PackageModificationRunner : IPackageModificationRunner
public string? ModificationCompleteTitle { get; init; } = "Install Complete"; public string? ModificationCompleteTitle { get; init; } = "Install Complete";
public string? ModificationCompleteMessage { get; init; } public required string ModificationCompleteMessage { get; init; }
public string? ModificationFailedTitle { get; init; } = "Install Failed"; public string? ModificationFailedTitle { get; init; } = "Install Failed";

2
StabilityMatrix.Core/Models/Packages/ComfyUI.cs

@ -43,7 +43,7 @@ public class ComfyUI(
public override bool ShouldIgnoreReleases => true; public override bool ShouldIgnoreReleases => true;
public override bool IsInferenceCompatible => true; public override bool IsInferenceCompatible => true;
public override string OutputFolderName => "output"; public override string OutputFolderName => "output";
public override PackageDifficulty InstallerSortOrder => PackageDifficulty.Advanced; public override PackageDifficulty InstallerSortOrder => PackageDifficulty.InferenceCompatible;
public override SharedFolderMethod RecommendedSharedFolderMethod => SharedFolderMethod.Configuration; public override SharedFolderMethod RecommendedSharedFolderMethod => SharedFolderMethod.Configuration;

2
StabilityMatrix.Core/Models/Packages/SDWebForge.cs

@ -35,6 +35,8 @@ public class SDWebForge(
"https://github.com/lllyasviel/stable-diffusion-webui-forge/assets/19834515/ca5e05ed-bd86-4ced-8662-f41034648e8c" "https://github.com/lllyasviel/stable-diffusion-webui-forge/assets/19834515/ca5e05ed-bd86-4ced-8662-f41034648e8c"
); );
public override PackageDifficulty InstallerSortOrder => PackageDifficulty.ReallyRecommended;
public override string MainBranch => "main"; public override string MainBranch => "main";
public override bool ShouldIgnoreReleases => true; public override bool ShouldIgnoreReleases => true;
public override IPackageExtensionManager ExtensionManager => null; public override IPackageExtensionManager ExtensionManager => null;

Loading…
Cancel
Save