From 2998ceee2122c17630e633fc611dec9df8fec6f2 Mon Sep 17 00:00:00 2001 From: JT Date: Sat, 10 Feb 2024 22:35:50 -0800 Subject: [PATCH 01/34] Added models & refit interface for openart stuff --- StabilityMatrix.Core/Api/IOpenArtApi.cs | 17 ++++++++ .../Models/Api/OpenArt/NodesCount.cs | 15 +++++++ .../Models/Api/OpenArt/OpenArtCreator.cs | 24 ++++++++++++ .../Api/OpenArt/OpenArtDownloadRequest.cs | 15 +++++++ .../Api/OpenArt/OpenArtDownloadResponse.cs | 12 ++++++ .../Models/Api/OpenArt/OpenArtFeedRequest.cs | 18 +++++++++ .../Api/OpenArt/OpenArtSearchRequest.cs | 18 +++++++++ .../Api/OpenArt/OpenArtSearchResponse.cs | 15 +++++++ .../Models/Api/OpenArt/OpenArtSearchResult.cs | 39 +++++++++++++++++++ .../Models/Api/OpenArt/OpenArtStats.cs | 33 ++++++++++++++++ .../Models/Api/OpenArt/OpenArtThumbnail.cs | 15 +++++++ 11 files changed, 221 insertions(+) create mode 100644 StabilityMatrix.Core/Api/IOpenArtApi.cs create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/NodesCount.cs create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtCreator.cs create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDownloadRequest.cs create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDownloadResponse.cs create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtFeedRequest.cs create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchRequest.cs create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResponse.cs create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResult.cs create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtStats.cs create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtThumbnail.cs diff --git a/StabilityMatrix.Core/Api/IOpenArtApi.cs b/StabilityMatrix.Core/Api/IOpenArtApi.cs new file mode 100644 index 00000000..e13c8623 --- /dev/null +++ b/StabilityMatrix.Core/Api/IOpenArtApi.cs @@ -0,0 +1,17 @@ +using Refit; +using StabilityMatrix.Core.Models.Api.OpenArt; + +namespace StabilityMatrix.Core.Api; + +[Headers("User-Agent: StabilityMatrix")] +public interface IOpenArtApi +{ + [Get("/feed")] + Task GetFeedAsync([Query] OpenArtFeedRequest request); + + [Get("/list")] + Task SearchAsync([Query] OpenArtFeedRequest request); + + [Post("/download")] + Task DownloadWorkflowAsync([Body] OpenArtDownloadRequest request); +} diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/NodesCount.cs b/StabilityMatrix.Core/Models/Api/OpenArt/NodesCount.cs new file mode 100644 index 00000000..2509dcd7 --- /dev/null +++ b/StabilityMatrix.Core/Models/Api/OpenArt/NodesCount.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Core.Models.Api.OpenArt; + +public class NodesCount +{ + [JsonPropertyName("total")] + public long Total { get; set; } + + [JsonPropertyName("primitive")] + public long Primitive { get; set; } + + [JsonPropertyName("custom")] + public long Custom { get; set; } +} diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtCreator.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtCreator.cs new file mode 100644 index 00000000..1f16dc14 --- /dev/null +++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtCreator.cs @@ -0,0 +1,24 @@ +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Core.Models.Api.OpenArt; + +public class OpenArtCreator +{ + [JsonPropertyName("uid")] + public string Uid { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("bio")] + public string Bio { get; set; } + + [JsonPropertyName("avatar")] + public Uri Avatar { get; set; } + + [JsonPropertyName("username")] + public string Username { get; set; } + + [JsonPropertyName("dev_profile_url")] + public Uri DevProfileUrl { get; set; } +} diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDownloadRequest.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDownloadRequest.cs new file mode 100644 index 00000000..cdf07d27 --- /dev/null +++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDownloadRequest.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; +using Refit; + +namespace StabilityMatrix.Core.Models.Api.OpenArt; + +public class OpenArtDownloadRequest +{ + [AliasAs("workflow_id")] + [JsonPropertyName("workflow_id")] + public required string WorkflowId { get; set; } + + [AliasAs("version_tag")] + [JsonPropertyName("version_tag")] + public string VersionTag { get; set; } = "latest"; +} diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDownloadResponse.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDownloadResponse.cs new file mode 100644 index 00000000..3cb61d79 --- /dev/null +++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDownloadResponse.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Core.Models.Api.OpenArt; + +public class OpenArtDownloadResponse +{ + [JsonPropertyName("filename")] + public string Filename { get; set; } + + [JsonPropertyName("payload")] + public string Payload { get; set; } +} diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtFeedRequest.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtFeedRequest.cs new file mode 100644 index 00000000..a3f334d9 --- /dev/null +++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtFeedRequest.cs @@ -0,0 +1,18 @@ +using Refit; + +namespace StabilityMatrix.Core.Models.Api.OpenArt; + +public class OpenArtFeedRequest +{ + [AliasAs("category")] + public string Category { get; set; } + + [AliasAs("sort")] + public string Sort { get; set; } + + [AliasAs("custom_node")] + public string CustomNode { get; set; } + + [AliasAs("cursor")] + public string Cursor { get; set; } +} diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchRequest.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchRequest.cs new file mode 100644 index 00000000..27d944e3 --- /dev/null +++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchRequest.cs @@ -0,0 +1,18 @@ +using Refit; + +namespace StabilityMatrix.Core.Models.Api.OpenArt; + +public class OpenArtSearchRequest +{ + [AliasAs("keyword")] + public required string Keyword { get; set; } + + [AliasAs("pageSize")] + public int PageSize { get; set; } = 30; + + /// + /// 0-based index of the page to retrieve + /// + [AliasAs("currentPage")] + public int CurrentPage { get; set; } = 0; +} diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResponse.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResponse.cs new file mode 100644 index 00000000..c58e308a --- /dev/null +++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResponse.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Core.Models.Api.OpenArt; + +public class OpenArtSearchResponse +{ + [JsonPropertyName("items")] + public IEnumerable Items { get; set; } + + [JsonPropertyName("total")] + public int Total { get; set; } + + [JsonPropertyName("nextCursor")] + public string? NextCursor { get; set; } +} diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResult.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResult.cs new file mode 100644 index 00000000..7db9bb4a --- /dev/null +++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResult.cs @@ -0,0 +1,39 @@ +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Core.Models.Api.OpenArt; + +public class OpenArtSearchResult +{ + [JsonPropertyName("id")] + public string Id { get; set; } + + [JsonPropertyName("creator")] + public OpenArtCreator Creator { get; set; } + + [JsonPropertyName("updated_at")] + public DateTimeOffset UpdatedAt { get; set; } + + [JsonPropertyName("stats")] + public OpenArtStats Stats { get; set; } + + [JsonPropertyName("nodes_index")] + public IEnumerable NodesIndex { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("description")] + public string Description { get; set; } + + [JsonPropertyName("created_at")] + public DateTimeOffset CreatedAt { get; set; } + + [JsonPropertyName("categories")] + public IEnumerable Categories { get; set; } + + [JsonPropertyName("thumbnails")] + public IEnumerable Thumbnails { get; set; } + + [JsonPropertyName("nodes_count")] + public NodesCount NodesCount { get; set; } +} diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtStats.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtStats.cs new file mode 100644 index 00000000..381db723 --- /dev/null +++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtStats.cs @@ -0,0 +1,33 @@ +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Core.Models.Api.OpenArt; + +public class OpenArtStats +{ + [JsonPropertyName("num_shares")] + public int NumShares { get; set; } + + [JsonPropertyName("num_bookmarks")] + public int NumBookmarks { get; set; } + + [JsonPropertyName("num_reviews")] + public int NumReviews { get; set; } + + [JsonPropertyName("rating")] + public int Rating { get; set; } + + [JsonPropertyName("num_comments")] + public int NumComments { get; set; } + + [JsonPropertyName("num_likes")] + public int NumLikes { get; set; } + + [JsonPropertyName("num_downloads")] + public int NumDownloads { get; set; } + + [JsonPropertyName("num_runs")] + public int NumRuns { get; set; } + + [JsonPropertyName("num_views")] + public int NumViews { get; set; } +} diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtThumbnail.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtThumbnail.cs new file mode 100644 index 00000000..05bfc22f --- /dev/null +++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtThumbnail.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Core.Models.Api.OpenArt; + +public class OpenArtThumbnail +{ + [JsonPropertyName("width")] + public int Width { get; set; } + + [JsonPropertyName("url")] + public Uri Url { get; set; } + + [JsonPropertyName("height")] + public int Height { get; set; } +} From c26960ce1f8457e6ed228dc6e4c687734c03c95c Mon Sep 17 00:00:00 2001 From: JT Date: Sun, 11 Feb 2024 18:35:51 -0800 Subject: [PATCH 02/34] added basic card UI, searching, and infiniscroll --- Avalonia.Gif/Avalonia.Gif.csproj | 2 +- ...tabilityMatrix.Avalonia.Diagnostics.csproj | 4 +- StabilityMatrix.Avalonia/App.axaml.cs | 16 +- .../Models/IInfinitelyScroll.cs | 8 + .../StabilityMatrix.Avalonia.csproj | 15 +- .../ViewModels/OpenArtBrowserViewModel.cs | 175 +++++++++ .../Views/OpenArtBrowserPage.axaml | 340 ++++++++++++++++++ .../Views/OpenArtBrowserPage.axaml.cs | 32 ++ StabilityMatrix.Core/Api/IOpenArtApi.cs | 2 +- .../Models/Api/OpenArt/OpenArtCreator.cs | 2 +- .../Models/Api/OpenArt/OpenArtDateTime.cs | 14 + .../Models/Api/OpenArt/OpenArtFeedRequest.cs | 3 + .../Models/Api/OpenArt/OpenArtSearchResult.cs | 2 +- .../Models/Api/OpenArt/OpenArtStats.cs | 2 +- .../Models/Packages/SDWebForge.cs | 13 + .../StabilityMatrix.UITests.csproj | 2 +- 16 files changed, 614 insertions(+), 18 deletions(-) create mode 100644 StabilityMatrix.Avalonia/Models/IInfinitelyScroll.cs create mode 100644 StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs create mode 100644 StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml create mode 100644 StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml.cs create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDateTime.cs diff --git a/Avalonia.Gif/Avalonia.Gif.csproj b/Avalonia.Gif/Avalonia.Gif.csproj index 858b974e..6a363589 100644 --- a/Avalonia.Gif/Avalonia.Gif.csproj +++ b/Avalonia.Gif/Avalonia.Gif.csproj @@ -10,7 +10,7 @@ true - + diff --git a/StabilityMatrix.Avalonia.Diagnostics/StabilityMatrix.Avalonia.Diagnostics.csproj b/StabilityMatrix.Avalonia.Diagnostics/StabilityMatrix.Avalonia.Diagnostics.csproj index e635ad2f..71efd8bb 100644 --- a/StabilityMatrix.Avalonia.Diagnostics/StabilityMatrix.Avalonia.Diagnostics.csproj +++ b/StabilityMatrix.Avalonia.Diagnostics/StabilityMatrix.Avalonia.Diagnostics.csproj @@ -19,8 +19,8 @@ - - + + diff --git a/StabilityMatrix.Avalonia/App.axaml.cs b/StabilityMatrix.Avalonia/App.axaml.cs index f1c10ddd..05e54065 100644 --- a/StabilityMatrix.Avalonia/App.axaml.cs +++ b/StabilityMatrix.Avalonia/App.axaml.cs @@ -333,7 +333,8 @@ public sealed class App : Application provider.GetRequiredService(), provider.GetRequiredService(), provider.GetRequiredService(), - provider.GetRequiredService() + provider.GetRequiredService(), + provider.GetRequiredService() }, FooterPages = { provider.GetRequiredService() } } @@ -556,7 +557,7 @@ public sealed class App : Application .ConfigureHttpClient(c => { c.BaseAddress = new Uri("https://civitai.com"); - c.Timeout = TimeSpan.FromSeconds(15); + c.Timeout = TimeSpan.FromSeconds(30); }) .AddPolicyHandler(retryPolicy); @@ -565,7 +566,7 @@ public sealed class App : Application .ConfigureHttpClient(c => { c.BaseAddress = new Uri("https://civitai.com"); - c.Timeout = TimeSpan.FromSeconds(15); + c.Timeout = TimeSpan.FromSeconds(30); }) .AddPolicyHandler(retryPolicy); @@ -583,6 +584,15 @@ public sealed class App : Application new TokenAuthHeaderHandler(serviceProvider.GetRequiredService()) ); + services + .AddRefitClient(defaultRefitSettings) + .ConfigureHttpClient(c => + { + c.BaseAddress = new Uri("https://openart.ai/api/public/workflows"); + c.Timeout = TimeSpan.FromSeconds(30); + }) + .AddPolicyHandler(retryPolicy); + // Add Refit client managers services.AddHttpClient("A3Client").AddPolicyHandler(localTimeout.WrapAsync(localRetryPolicy)); diff --git a/StabilityMatrix.Avalonia/Models/IInfinitelyScroll.cs b/StabilityMatrix.Avalonia/Models/IInfinitelyScroll.cs new file mode 100644 index 00000000..59373816 --- /dev/null +++ b/StabilityMatrix.Avalonia/Models/IInfinitelyScroll.cs @@ -0,0 +1,8 @@ +using System.Threading.Tasks; + +namespace StabilityMatrix.Avalonia.Models; + +public interface IInfinitelyScroll +{ + Task LoadNextPageAsync(); +} diff --git a/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj b/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj index 1190f686..63e08450 100644 --- a/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj +++ b/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj @@ -39,13 +39,14 @@ - + + - - - + + + - + @@ -65,8 +66,8 @@ - - + + diff --git a/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs new file mode 100644 index 00000000..c8463037 --- /dev/null +++ b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using DynamicData; +using DynamicData.Binding; +using FluentAvalonia.UI.Controls; +using Refit; +using StabilityMatrix.Avalonia.Models; +using StabilityMatrix.Avalonia.Services; +using StabilityMatrix.Avalonia.ViewModels.Base; +using StabilityMatrix.Avalonia.Views; +using StabilityMatrix.Core.Api; +using StabilityMatrix.Core.Attributes; +using StabilityMatrix.Core.Models.Api.OpenArt; +using StabilityMatrix.Core.Processes; +using Symbol = FluentIcons.Common.Symbol; +using SymbolIconSource = FluentIcons.Avalonia.Fluent.SymbolIconSource; + +namespace StabilityMatrix.Avalonia.ViewModels; + +[View(typeof(OpenArtBrowserPage))] +[Singleton] +public partial class OpenArtBrowserViewModel(IOpenArtApi openArtApi, INotificationService notificationService) + : PageViewModelBase, + IInfinitelyScroll +{ + private const int PageSize = 20; + + public override string Title => "Workflows"; + public override IconSource IconSource => new SymbolIconSource { Symbol = Symbol.Whiteboard }; + + private SourceCache searchResultsCache = new(x => x.Id); + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(PageCount), nameof(CanGoBack), nameof(CanGoForward), nameof(CanGoToEnd))] + private OpenArtSearchResponse? latestSearchResponse; + + [ObservableProperty] + private IObservableCollection searchResults = + new ObservableCollectionExtended(); + + [ObservableProperty] + private string searchQuery = string.Empty; + + [ObservableProperty] + private bool isLoading; + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(InternalPageNumber), nameof(CanGoBack))] + private int displayedPageNumber = 1; + + public int InternalPageNumber => DisplayedPageNumber - 1; + + public int PageCount => + Math.Max( + 1, + Convert.ToInt32(Math.Ceiling((LatestSearchResponse?.Total ?? 0) / Convert.ToDouble(PageSize))) + ); + + public bool CanGoBack => InternalPageNumber > 0; + + public bool CanGoForward => PageCount > InternalPageNumber + 1; + + public bool CanGoToEnd => PageCount > InternalPageNumber + 1; + + protected override void OnInitialLoaded() + { + searchResultsCache.Connect().DeferUntilLoaded().Bind(SearchResults).Subscribe(); + } + + public override async Task OnLoadedAsync() + { + if (SearchResults.Any()) + return; + + await DoSearch(); + } + + [RelayCommand] + private async Task FirstPage() + { + DisplayedPageNumber = 1; + await DoSearch(); + } + + [RelayCommand] + private async Task PreviousPage() + { + DisplayedPageNumber--; + await DoSearch(InternalPageNumber); + } + + [RelayCommand] + private async Task NextPage() + { + DisplayedPageNumber++; + await DoSearch(InternalPageNumber); + } + + [RelayCommand] + private async Task LastPage() + { + DisplayedPageNumber = PageCount; + await DoSearch(PageCount - 1); + } + + [Localizable(false)] + [RelayCommand] + private void OpenModel(OpenArtSearchResult workflow) + { + ProcessRunner.OpenUrl($"https://openart.ai/workflows/{workflow.Id}"); + } + + [RelayCommand] + private async Task SearchButton() + { + DisplayedPageNumber = 1; + await DoSearch(); + } + + private async Task DoSearch(int page = 0) + { + IsLoading = true; + + try + { + var response = await openArtApi.SearchAsync( + new OpenArtSearchRequest + { + Keyword = SearchQuery, + PageSize = PageSize, + CurrentPage = page + } + ); + + searchResultsCache.EditDiff(response.Items, (a, b) => a.Id == b.Id); + LatestSearchResponse = response; + } + catch (ApiException e) + { + notificationService.Show("Error retrieving workflows", e.Message); + } + finally + { + IsLoading = false; + } + } + + public async Task LoadNextPageAsync() + { + try + { + DisplayedPageNumber++; + var response = await openArtApi.SearchAsync( + new OpenArtSearchRequest + { + Keyword = SearchQuery, + PageSize = PageSize, + CurrentPage = InternalPageNumber + } + ); + + searchResultsCache.AddOrUpdate(response.Items); + LatestSearchResponse = response; + } + catch (ApiException e) + { + notificationService.Show("Unable to load the next page", e.Message); + } + } +} diff --git a/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml new file mode 100644 index 00000000..8e7a621e --- /dev/null +++ b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml @@ -0,0 +1,340 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml.cs b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml.cs new file mode 100644 index 00000000..aceb5db4 --- /dev/null +++ b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml.cs @@ -0,0 +1,32 @@ +using System; +using AsyncAwaitBestPractices; +using Avalonia.Controls; +using StabilityMatrix.Avalonia.Controls; +using StabilityMatrix.Avalonia.Models; +using StabilityMatrix.Core.Attributes; + +namespace StabilityMatrix.Avalonia.Views; + +[Singleton] +public partial class OpenArtBrowserPage : UserControlBase +{ + public OpenArtBrowserPage() + { + InitializeComponent(); + } + + private void ScrollViewer_OnScrollChanged(object? sender, ScrollChangedEventArgs e) + { + if (sender is not ScrollViewer scrollViewer) + return; + + if (scrollViewer.Offset.Y == 0) + return; + + var isAtEnd = Math.Abs(scrollViewer.Offset.Y - scrollViewer.ScrollBarMaximum.Y) < 0.1f; + if (isAtEnd && DataContext is IInfinitelyScroll scroll) + { + scroll.LoadNextPageAsync().SafeFireAndForget(); + } + } +} diff --git a/StabilityMatrix.Core/Api/IOpenArtApi.cs b/StabilityMatrix.Core/Api/IOpenArtApi.cs index e13c8623..f7e9ae19 100644 --- a/StabilityMatrix.Core/Api/IOpenArtApi.cs +++ b/StabilityMatrix.Core/Api/IOpenArtApi.cs @@ -10,7 +10,7 @@ public interface IOpenArtApi Task GetFeedAsync([Query] OpenArtFeedRequest request); [Get("/list")] - Task SearchAsync([Query] OpenArtFeedRequest request); + Task SearchAsync([Query] OpenArtSearchRequest request); [Post("/download")] Task DownloadWorkflowAsync([Body] OpenArtDownloadRequest request); diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtCreator.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtCreator.cs index 1f16dc14..173d1cef 100644 --- a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtCreator.cs +++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtCreator.cs @@ -20,5 +20,5 @@ public class OpenArtCreator public string Username { get; set; } [JsonPropertyName("dev_profile_url")] - public Uri DevProfileUrl { get; set; } + public string DevProfileUrl { get; set; } } diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDateTime.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDateTime.cs new file mode 100644 index 00000000..5f45a232 --- /dev/null +++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDateTime.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Core.Models.Api.OpenArt; + +public class OpenArtDateTime +{ + [JsonPropertyName("_seconds")] + public long Seconds { get; set; } + + public DateTimeOffset ToDateTimeOffset() + { + return DateTimeOffset.FromUnixTimeSeconds(Seconds); + } +} diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtFeedRequest.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtFeedRequest.cs index a3f334d9..f8dd5255 100644 --- a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtFeedRequest.cs +++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtFeedRequest.cs @@ -2,6 +2,9 @@ namespace StabilityMatrix.Core.Models.Api.OpenArt; +/// +/// Note that parameters Category, Custom Node and Sort should be used separately +/// public class OpenArtFeedRequest { [AliasAs("category")] diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResult.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResult.cs index 7db9bb4a..9b007ced 100644 --- a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResult.cs +++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResult.cs @@ -32,7 +32,7 @@ public class OpenArtSearchResult public IEnumerable Categories { get; set; } [JsonPropertyName("thumbnails")] - public IEnumerable Thumbnails { get; set; } + public List Thumbnails { get; set; } [JsonPropertyName("nodes_count")] public NodesCount NodesCount { get; set; } diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtStats.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtStats.cs index 381db723..4afa6118 100644 --- a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtStats.cs +++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtStats.cs @@ -14,7 +14,7 @@ public class OpenArtStats public int NumReviews { get; set; } [JsonPropertyName("rating")] - public int Rating { get; set; } + public double Rating { get; set; } [JsonPropertyName("num_comments")] public int NumComments { get; set; } diff --git a/StabilityMatrix.Core/Models/Packages/SDWebForge.cs b/StabilityMatrix.Core/Models/Packages/SDWebForge.cs index 1e6fd16a..b58c7ec8 100644 --- a/StabilityMatrix.Core/Models/Packages/SDWebForge.cs +++ b/StabilityMatrix.Core/Models/Packages/SDWebForge.cs @@ -79,6 +79,19 @@ public class SDWebForge( TorchVersion.Mps }; + public override Dictionary> SharedOutputFolders => + new() + { + [SharedOutputType.Extras] = new[] { "output/extras-images" }, + [SharedOutputType.Saved] = new[] { "log/images" }, + [SharedOutputType.Img2Img] = new[] { "output/img2img-images" }, + [SharedOutputType.Text2Img] = new[] { "output/txt2img-images" }, + [SharedOutputType.Img2ImgGrids] = new[] { "output/img2img-grids" }, + [SharedOutputType.Text2ImgGrids] = new[] { "output/txt2img-grids" } + }; + + public override string OutputFolderName => "output"; + public override async Task InstallPackage( string installLocation, TorchVersion torchVersion, diff --git a/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj b/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj index 90484174..47cee6c9 100644 --- a/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj +++ b/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj @@ -15,7 +15,7 @@ - + From e87b5032beb11e3d2d57cddbe99de5ee7aa5d48c Mon Sep 17 00:00:00 2001 From: JT Date: Sun, 11 Feb 2024 19:24:44 -0800 Subject: [PATCH 03/34] rearrange settings & add System.Drawing.Common reference? --- .../StabilityMatrix.Avalonia.csproj | 1 + .../ViewModels/OpenArtBrowserViewModel.cs | 1 - .../Settings/MainSettingsViewModel.cs | 9 + .../Views/OpenArtBrowserPage.axaml.cs | 2 +- .../Views/Settings/MainSettingsPage.axaml | 188 +++++++++--------- .../Models/Settings/Settings.cs | 1 + .../StabilityMatrix.UITests.csproj | 1 + 7 files changed, 107 insertions(+), 96 deletions(-) diff --git a/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj b/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj index 63e08450..fa070fbd 100644 --- a/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj +++ b/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj @@ -95,6 +95,7 @@ + diff --git a/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs index c8463037..6f290e70 100644 --- a/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Threading.Tasks; diff --git a/StabilityMatrix.Avalonia/ViewModels/Settings/MainSettingsViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Settings/MainSettingsViewModel.cs index 55b6c896..2a28e275 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Settings/MainSettingsViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Settings/MainSettingsViewModel.cs @@ -129,6 +129,9 @@ public partial class MainSettingsViewModel : PageViewModelBase [ObservableProperty] private HolidayMode holidayModeSetting; + [ObservableProperty] + private bool infinitelyScrollWorkflowBrowser; + #region System Info private static Lazy> GpuInfosLazy { get; } = @@ -217,6 +220,12 @@ public partial class MainSettingsViewModel : PageViewModelBase settings => settings.HolidayModeSetting ); + settingsManager.RelayPropertyFor( + this, + vm => vm.InfinitelyScrollWorkflowBrowser, + settings => settings.IsWorkflowInfiniteScrollEnabled + ); + DebugThrowAsyncExceptionCommand.WithNotificationErrorHandler(notificationService, LogLevel.Warn); hardwareInfoUpdateTimer.Tick += OnHardwareInfoUpdateTimerTick; diff --git a/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml.cs b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml.cs index aceb5db4..c80793c2 100644 --- a/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml.cs +++ b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml.cs @@ -23,7 +23,7 @@ public partial class OpenArtBrowserPage : UserControlBase if (scrollViewer.Offset.Y == 0) return; - var isAtEnd = Math.Abs(scrollViewer.Offset.Y - scrollViewer.ScrollBarMaximum.Y) < 0.1f; + var isAtEnd = Math.Abs(scrollViewer.Offset.Y - scrollViewer.ScrollBarMaximum.Y) < 1f; if (isAtEnd && DataContext is IInfinitelyScroll scroll) { scroll.LoadNextPageAsync().SafeFireAndForget(); diff --git a/StabilityMatrix.Avalonia/Views/Settings/MainSettingsPage.axaml b/StabilityMatrix.Avalonia/Views/Settings/MainSettingsPage.axaml index 369c3f9a..2b846cc0 100644 --- a/StabilityMatrix.Avalonia/Views/Settings/MainSettingsPage.axaml +++ b/StabilityMatrix.Avalonia/Views/Settings/MainSettingsPage.axaml @@ -45,77 +45,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + diff --git a/StabilityMatrix.Avalonia/Views/InstalledWorkflowsPage.axaml.cs b/StabilityMatrix.Avalonia/Views/InstalledWorkflowsPage.axaml.cs new file mode 100644 index 00000000..191916d2 --- /dev/null +++ b/StabilityMatrix.Avalonia/Views/InstalledWorkflowsPage.axaml.cs @@ -0,0 +1,13 @@ +using StabilityMatrix.Avalonia.Controls; +using StabilityMatrix.Core.Attributes; + +namespace StabilityMatrix.Avalonia.Views; + +[Singleton] +public partial class InstalledWorkflowsPage : UserControlBase +{ + public InstalledWorkflowsPage() + { + InitializeComponent(); + } +} diff --git a/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml index 75a6f54c..5f776195 100644 --- a/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml +++ b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml @@ -82,7 +82,7 @@ CornerRadius="8"> - + @@ -243,13 +243,8 @@ - - - + Margin="8,8,8,0"> + + diff --git a/StabilityMatrix.Avalonia/Views/WorkflowsPage.axaml.cs b/StabilityMatrix.Avalonia/Views/WorkflowsPage.axaml.cs new file mode 100644 index 00000000..443070e9 --- /dev/null +++ b/StabilityMatrix.Avalonia/Views/WorkflowsPage.axaml.cs @@ -0,0 +1,13 @@ +using StabilityMatrix.Avalonia.Controls; +using StabilityMatrix.Core.Attributes; + +namespace StabilityMatrix.Avalonia.Views; + +[Singleton] +public partial class WorkflowsPage : UserControlBase +{ + public WorkflowsPage() + { + InitializeComponent(); + } +} diff --git a/StabilityMatrix.Core/Models/PackageModification/DownloadOpenArtWorkflowStep.cs b/StabilityMatrix.Core/Models/PackageModification/DownloadOpenArtWorkflowStep.cs index dbfa17f9..42553180 100644 --- a/StabilityMatrix.Core/Models/PackageModification/DownloadOpenArtWorkflowStep.cs +++ b/StabilityMatrix.Core/Models/PackageModification/DownloadOpenArtWorkflowStep.cs @@ -24,6 +24,14 @@ public class DownloadOpenArtWorkflowStep( var jsonObject = JsonNode.Parse(workflowData.Payload) as JsonObject; jsonObject?.Add("workflow_id", workflow.Id); + jsonObject?.Add("workflow_name", workflow.Name); + jsonObject?.Add("creator", workflow.Creator.Username); + var thumbs = new JsonArray(); + foreach (var thumb in workflow.Thumbnails) + { + thumbs.Add(thumb.Url); + } + jsonObject?.Add("thumbnails", thumbs); await File.WriteAllTextAsync(filePath, JsonSerializer.Serialize(jsonObject)).ConfigureAwait(false); From fbc8111952d2960f34253be2276acef27158c3b8 Mon Sep 17 00:00:00 2001 From: JT Date: Fri, 23 Feb 2024 00:56:32 -0800 Subject: [PATCH 09/34] call feed endpoint if no search query & added sort for feed --- .../ViewModels/OpenArtBrowserViewModel.cs | 127 +++++++++++++++--- .../Views/InstalledWorkflowsPage.axaml | 9 +- .../Views/OpenArtBrowserPage.axaml | 15 ++- 3 files changed, 124 insertions(+), 27 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs index bb744ff2..e7d54266 100644 --- a/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs @@ -1,7 +1,9 @@ using System; +using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Threading.Tasks; +using AsyncAwaitBestPractices; using Avalonia.Controls.Notifications; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; @@ -69,21 +71,32 @@ public partial class OpenArtBrowserViewModel( Convert.ToInt32(Math.Ceiling((LatestSearchResponse?.Total ?? 0) / Convert.ToDouble(PageSize))) ); - public bool CanGoBack => InternalPageNumber > 0; + public bool CanGoBack => + string.IsNullOrWhiteSpace(LatestSearchResponse?.NextCursor) && InternalPageNumber > 0; - public bool CanGoForward => PageCount > InternalPageNumber + 1; + public bool CanGoForward => + !string.IsNullOrWhiteSpace(LatestSearchResponse?.NextCursor) || PageCount > InternalPageNumber + 1; - public bool CanGoToEnd => PageCount > InternalPageNumber + 1; + public bool CanGoToEnd => + string.IsNullOrWhiteSpace(LatestSearchResponse?.NextCursor) && PageCount > InternalPageNumber + 1; + + public IEnumerable AllSortModes => ["Trending", "Latest", "Most Downloaded", "Most Liked"]; + + [ObservableProperty] + private string? selectedSortMode; protected override void OnInitialLoaded() { searchResultsCache.Connect().DeferUntilLoaded().Bind(SearchResults).Subscribe(); + SelectedSortMode = AllSortModes.First(); } [RelayCommand] private async Task FirstPage() { DisplayedPageNumber = 1; + searchResultsCache.Clear(); + await DoSearch(); } @@ -91,20 +104,32 @@ public partial class OpenArtBrowserViewModel( private async Task PreviousPage() { DisplayedPageNumber--; + searchResultsCache.Clear(); + await DoSearch(InternalPageNumber); } [RelayCommand] private async Task NextPage() { - DisplayedPageNumber++; + if (string.IsNullOrWhiteSpace(LatestSearchResponse?.NextCursor)) + { + DisplayedPageNumber++; + } + + searchResultsCache.Clear(); await DoSearch(InternalPageNumber); } [RelayCommand] private async Task LastPage() { - DisplayedPageNumber = PageCount; + if (string.IsNullOrWhiteSpace(LatestSearchResponse?.NextCursor)) + { + DisplayedPageNumber = PageCount; + } + + searchResultsCache.Clear(); await DoSearch(PageCount - 1); } @@ -119,6 +144,9 @@ public partial class OpenArtBrowserViewModel( private async Task SearchButton() { DisplayedPageNumber = 1; + LatestSearchResponse = null; + searchResultsCache.Clear(); + await DoSearch(); } @@ -202,16 +230,34 @@ public partial class OpenArtBrowserViewModel( try { - var response = await openArtApi.SearchAsync( - new OpenArtSearchRequest + OpenArtSearchResponse? response = null; + if (string.IsNullOrWhiteSpace(SearchQuery)) + { + var request = new OpenArtFeedRequest { Sort = GetSortMode(SelectedSortMode) }; + if (!string.IsNullOrWhiteSpace(LatestSearchResponse?.NextCursor)) { - Keyword = SearchQuery, - PageSize = PageSize, - CurrentPage = page + request.Cursor = LatestSearchResponse.NextCursor; } - ); - searchResultsCache.EditDiff(response.Items, (a, b) => a.Id == b.Id); + response = await openArtApi.GetFeedAsync(request); + } + else + { + response = await openArtApi.SearchAsync( + new OpenArtSearchRequest + { + Keyword = SearchQuery, + PageSize = PageSize, + CurrentPage = page + } + ); + } + + foreach (var item in response.Items) + { + searchResultsCache.AddOrUpdate(item); + } + LatestSearchResponse = response; } catch (ApiException e) @@ -224,6 +270,17 @@ public partial class OpenArtBrowserViewModel( } } + partial void OnSelectedSortModeChanged(string? value) + { + if (value is null || SearchResults.Count == 0) + return; + + searchResultsCache.Clear(); + LatestSearchResponse = null; + + DoSearch().SafeFireAndForget(); + } + public async Task LoadNextPageAsync() { if (!CanGoForward) @@ -231,17 +288,35 @@ public partial class OpenArtBrowserViewModel( try { - DisplayedPageNumber++; - var response = await openArtApi.SearchAsync( - new OpenArtSearchRequest + OpenArtSearchResponse? response = null; + if (string.IsNullOrWhiteSpace(SearchQuery)) + { + var request = new OpenArtFeedRequest { Sort = GetSortMode(SelectedSortMode) }; + if (!string.IsNullOrWhiteSpace(LatestSearchResponse?.NextCursor)) { - Keyword = SearchQuery, - PageSize = PageSize, - CurrentPage = InternalPageNumber + request.Cursor = LatestSearchResponse.NextCursor; } - ); - searchResultsCache.AddOrUpdate(response.Items); + response = await openArtApi.GetFeedAsync(request); + } + else + { + DisplayedPageNumber++; + response = await openArtApi.SearchAsync( + new OpenArtSearchRequest + { + Keyword = SearchQuery, + PageSize = PageSize, + CurrentPage = InternalPageNumber + } + ); + } + + foreach (var item in response.Items) + { + searchResultsCache.AddOrUpdate(item); + } + LatestSearchResponse = response; } catch (ApiException e) @@ -249,4 +324,16 @@ public partial class OpenArtBrowserViewModel( notificationService.Show("Unable to load the next page", e.Message); } } + + private static string GetSortMode(string? sortMode) + { + return sortMode switch + { + "Trending" => "trending", + "Latest" => "latest", + "Most Downloaded" => "most_downloaded", + "Most Liked" => "most_liked", + _ => "trending" + }; + } } diff --git a/StabilityMatrix.Avalonia/Views/InstalledWorkflowsPage.axaml b/StabilityMatrix.Avalonia/Views/InstalledWorkflowsPage.axaml index 15eec425..5c4d3b4b 100644 --- a/StabilityMatrix.Avalonia/Views/InstalledWorkflowsPage.axaml +++ b/StabilityMatrix.Avalonia/Views/InstalledWorkflowsPage.axaml @@ -78,12 +78,11 @@ - + + VerticalAlignment="Center"/> diff --git a/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml index 5f776195..2d377dd1 100644 --- a/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml +++ b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml @@ -243,8 +243,8 @@ - + + + + Date: Fri, 23 Feb 2024 00:57:55 -0800 Subject: [PATCH 10/34] remove windows references --- .../ViewModels/InstalledWorkflowsViewModel.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/InstalledWorkflowsViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/InstalledWorkflowsViewModel.cs index ad514b6c..8a4c0e87 100644 --- a/StabilityMatrix.Avalonia/ViewModels/InstalledWorkflowsViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/InstalledWorkflowsViewModel.cs @@ -1,12 +1,9 @@ using System; -using System.Collections.Generic; using System.IO; using System.Text.Json; using System.Threading.Tasks; using Avalonia.Controls; -using Avalonia.Input; using Avalonia.Platform.Storage; -using Avalonia.Xaml.Interactions.DragAndDrop; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using DynamicData; @@ -16,8 +13,6 @@ using StabilityMatrix.Avalonia.Views; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Models.Api.OpenArt; using StabilityMatrix.Core.Services; -using Windows.Storage; -using IStorageFile = Avalonia.Platform.Storage.IStorageFile; namespace StabilityMatrix.Avalonia.ViewModels; From 92620dc8940b456551ea21912070f50a424af621 Mon Sep 17 00:00:00 2001 From: Ionite Date: Tue, 27 Feb 2024 15:39:34 -0500 Subject: [PATCH 11/34] Fix duplicate def --- StabilityMatrix.Core/Models/Packages/SDWebForge.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/StabilityMatrix.Core/Models/Packages/SDWebForge.cs b/StabilityMatrix.Core/Models/Packages/SDWebForge.cs index 3ff4b3cb..c603532c 100644 --- a/StabilityMatrix.Core/Models/Packages/SDWebForge.cs +++ b/StabilityMatrix.Core/Models/Packages/SDWebForge.cs @@ -104,19 +104,6 @@ public class SDWebForge( TorchVersion.Mps }; - public override Dictionary> SharedOutputFolders => - new() - { - [SharedOutputType.Extras] = new[] { "output/extras-images" }, - [SharedOutputType.Saved] = new[] { "log/images" }, - [SharedOutputType.Img2Img] = new[] { "output/img2img-images" }, - [SharedOutputType.Text2Img] = new[] { "output/txt2img-images" }, - [SharedOutputType.Img2ImgGrids] = new[] { "output/img2img-grids" }, - [SharedOutputType.Text2ImgGrids] = new[] { "output/txt2img-grids" } - }; - - public override string OutputFolderName => "output"; - public override async Task InstallPackage( string installLocation, TorchVersion torchVersion, From df1f188fe60fa06147c8a302b9b25fdda03e52c0 Mon Sep 17 00:00:00 2001 From: Ionite Date: Tue, 27 Feb 2024 15:39:51 -0500 Subject: [PATCH 12/34] Add GetInstalledExtensionsLiteAsync to interface --- .../Packages/Extensions/IPackageExtensionManager.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/StabilityMatrix.Core/Models/Packages/Extensions/IPackageExtensionManager.cs b/StabilityMatrix.Core/Models/Packages/Extensions/IPackageExtensionManager.cs index 76903233..20d37365 100644 --- a/StabilityMatrix.Core/Models/Packages/Extensions/IPackageExtensionManager.cs +++ b/StabilityMatrix.Core/Models/Packages/Extensions/IPackageExtensionManager.cs @@ -89,6 +89,14 @@ public interface IPackageExtensionManager CancellationToken cancellationToken = default ); + /// + /// Like , but does not check version. + /// + Task> GetInstalledExtensionsLiteAsync( + InstalledPackage installedPackage, + CancellationToken cancellationToken = default + ); + /// /// Install an extension to the provided package. /// From 0c30b4ba328e5853c2fadfaf3066d07f99380ab6 Mon Sep 17 00:00:00 2001 From: Ionite Date: Tue, 27 Feb 2024 15:58:40 -0500 Subject: [PATCH 13/34] Fix openart extension install --- .../Dialogs/OpenArtWorkflowViewModel.cs | 58 ++++++++++++++++--- .../ViewModels/OpenArtBrowserViewModel.cs | 25 ++++---- 2 files changed, 60 insertions(+), 23 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/OpenArtWorkflowViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/OpenArtWorkflowViewModel.cs index 869521b2..f565b896 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/OpenArtWorkflowViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/OpenArtWorkflowViewModel.cs @@ -11,6 +11,7 @@ using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Models; using StabilityMatrix.Core.Models.Api.OpenArt; +using StabilityMatrix.Core.Models.Packages.Extensions; namespace StabilityMatrix.Avalonia.ViewModels.Dialogs; @@ -28,6 +29,8 @@ public partial class OpenArtWorkflowViewModel : ContentDialogViewModelBase [ObservableProperty] private string prunedDescription = string.Empty; + public List MissingNodes { get; } = []; + public override async Task OnLoadedAsync() { CustomNodes = new ObservableCollection( @@ -45,16 +48,44 @@ public partial class OpenArtWorkflowViewModel : ContentDialogViewModelBase nodes = nodes[(indexOfFirstDot + 1)..]; } - var installedNodes = new List(); - if (InstalledComfy != null) + var installedNodesNames = new HashSet(); + var nameToManifestNodes = new Dictionary(); + + if (InstalledComfy?.BasePackage.ExtensionManager is { } extensionManager) { - installedNodes = ( - await InstalledComfy.BasePackage.ExtensionManager?.GetInstalledExtensionsAsync( - InstalledComfy.InstalledPackage - ) - ) - .Select(x => x.PrimaryPath?.Name) + var installedNodes = ( + await extensionManager.GetInstalledExtensionsLiteAsync(InstalledComfy.InstalledPackage) + ).ToList(); + + var manifestExtensionsMap = await extensionManager.GetManifestExtensionsMapAsync( + extensionManager.GetManifests(InstalledComfy.InstalledPackage) + ); + + // Add manifestExtensions definition to installedNodes if matching git repository url + installedNodes = installedNodes + .Select(installedNode => + { + if ( + installedNode.GitRepositoryUrl is not null + && manifestExtensionsMap.TryGetValue( + installedNode.GitRepositoryUrl, + out var manifestExtension + ) + ) + { + installedNode = installedNode with { Definition = manifestExtension }; + } + + return installedNode; + }) .ToList(); + + // There may be duplicate titles, deduplicate by using the first one + nameToManifestNodes = manifestExtensionsMap + .GroupBy(x => x.Value.Title) + .ToDictionary(x => x.Key, x => x.First().Value); + + installedNodesNames = installedNodes.Select(x => x.Title).ToHashSet(); } var sections = new List(); @@ -73,8 +104,17 @@ public partial class OpenArtWorkflowViewModel : ContentDialogViewModelBase currentSection = new OpenArtCustomNode { Title = node, - IsInstalled = installedNodes.Contains(node) + IsInstalled = installedNodesNames.Contains(node) }; + + // Add missing nodes to the list + if ( + !currentSection.IsInstalled && nameToManifestNodes.TryGetValue(node, out var manifestNode) + ) + { + MissingNodes.Add(manifestNode); + } + sections.Add(currentSection); } else diff --git a/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs index e7d54266..cf9321c8 100644 --- a/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs @@ -23,7 +23,6 @@ using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper.Factory; using StabilityMatrix.Core.Models.Api.OpenArt; using StabilityMatrix.Core.Models.PackageModification; -using StabilityMatrix.Core.Models.Packages.Extensions; using StabilityMatrix.Core.Processes; using StabilityMatrix.Core.Services; using Resources = StabilityMatrix.Avalonia.Languages.Resources; @@ -189,23 +188,21 @@ public partial class OpenArtBrowserViewModel( return; } + if (vm.MissingNodes is not { Count: > 0 } missingNodes) + { + // Skip if no missing nodes + return; + } + var extensionManager = comfyPair.BasePackage.ExtensionManager!; - var extensions = ( - await extensionManager.GetManifestExtensionsAsync( - extensionManager.GetManifests(comfyPair.InstalledPackage) - ) - ).ToList(); - var steps = vm.CustomNodes.Where(x => x.IsInstalled is false) - .Select(node => extensions.FirstOrDefault(x => x.Title == node.Title)) - .OfType() - .Select( + List steps = + [ + new DownloadOpenArtWorkflowStep(openArtApi, vm.Workflow, settingsManager), + ..missingNodes.Select( extension => new InstallExtensionStep(extensionManager, comfyPair.InstalledPackage, extension) ) - .Cast() - .ToList(); - - steps.Add(new DownloadOpenArtWorkflowStep(openArtApi, vm.Workflow, settingsManager)); + ]; var runner = new PackageModificationRunner { From e62546691af605c368133c9ed42f678a1e633827 Mon Sep 17 00:00:00 2001 From: Ionite Date: Tue, 27 Feb 2024 16:05:25 -0500 Subject: [PATCH 14/34] Fix no download when no missing nodes --- .../ViewModels/OpenArtBrowserViewModel.cs | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs index cf9321c8..7cbbbd39 100644 --- a/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs @@ -188,22 +188,24 @@ public partial class OpenArtBrowserViewModel( return; } - if (vm.MissingNodes is not { Count: > 0 } missingNodes) - { - // Skip if no missing nodes - return; - } - - var extensionManager = comfyPair.BasePackage.ExtensionManager!; - List steps = [ - new DownloadOpenArtWorkflowStep(openArtApi, vm.Workflow, settingsManager), - ..missingNodes.Select( - extension => new InstallExtensionStep(extensionManager, comfyPair.InstalledPackage, extension) - ) + new DownloadOpenArtWorkflowStep(openArtApi, vm.Workflow, settingsManager) ]; + // Add install steps if missing nodes + if (vm.MissingNodes is { Count: > 0 } missingNodes) + { + var extensionManager = comfyPair.BasePackage.ExtensionManager!; + + steps.AddRange( + missingNodes.Select( + extension => + new InstallExtensionStep(extensionManager, comfyPair.InstalledPackage, extension) + ) + ); + } + var runner = new PackageModificationRunner { ShowDialogOnStart = true, From 1ea83f30f9dc42d5f5014ba6c9b46b445d4fee5f Mon Sep 17 00:00:00 2001 From: Ionite Date: Tue, 27 Feb 2024 17:46:18 -0500 Subject: [PATCH 15/34] Add install node option and target package selection --- .../DesignData/DesignData.cs | 2 +- .../Dialogs/OpenArtWorkflowViewModel.cs | 51 ++++- .../ViewModels/OpenArtBrowserViewModel.cs | 9 +- .../Views/Dialogs/OpenArtWorkflowDialog.axaml | 210 ++++++++++-------- .../Models/Settings/Settings.cs | 13 ++ 5 files changed, 187 insertions(+), 98 deletions(-) diff --git a/StabilityMatrix.Avalonia/DesignData/DesignData.cs b/StabilityMatrix.Avalonia/DesignData/DesignData.cs index 392c2ade..73b1bcba 100644 --- a/StabilityMatrix.Avalonia/DesignData/DesignData.cs +++ b/StabilityMatrix.Avalonia/DesignData/DesignData.cs @@ -1033,7 +1033,7 @@ The gallery images are often inpainted, but you will get something very similar DialogFactory.Get(); public static OpenArtWorkflowViewModel OpenArtWorkflowViewModel => - new() + new(Services.GetRequiredService(), Services.GetRequiredService()) { Workflow = new OpenArtSearchResult { diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/OpenArtWorkflowViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/OpenArtWorkflowViewModel.cs index f565b896..d6d519bf 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/OpenArtWorkflowViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/OpenArtWorkflowViewModel.cs @@ -3,25 +3,30 @@ using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Threading.Tasks; +using AsyncAwaitBestPractices; using CommunityToolkit.Mvvm.ComponentModel; using StabilityMatrix.Avalonia.Models; using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.Views.Dialogs; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Helper; +using StabilityMatrix.Core.Helper.Factory; using StabilityMatrix.Core.Models; using StabilityMatrix.Core.Models.Api.OpenArt; using StabilityMatrix.Core.Models.Packages.Extensions; +using StabilityMatrix.Core.Services; namespace StabilityMatrix.Avalonia.ViewModels.Dialogs; [View(typeof(OpenArtWorkflowDialog))] [ManagedService] [Transient] -public partial class OpenArtWorkflowViewModel : ContentDialogViewModelBase +public partial class OpenArtWorkflowViewModel( + ISettingsManager settingsManager, + IPackageFactory packageFactory +) : ContentDialogViewModelBase { public required OpenArtSearchResult Workflow { get; init; } - public PackagePair? InstalledComfy { get; init; } [ObservableProperty] private ObservableCollection customNodes = []; @@ -29,16 +34,50 @@ public partial class OpenArtWorkflowViewModel : ContentDialogViewModelBase [ObservableProperty] private string prunedDescription = string.Empty; + [ObservableProperty] + private bool installRequiredNodes = true; + + [ObservableProperty] + private InstalledPackage? selectedPackage; + + public PackagePair? SelectedPackagePair => + SelectedPackage is { } package ? packageFactory.GetPackagePair(package) : null; + + public IEnumerable AvailablePackages => + settingsManager.Settings.InstalledPackages.Where(package => package.PackageName == "ComfyUI"); + public List MissingNodes { get; } = []; public override async Task OnLoadedAsync() { + if (settingsManager.Settings.PreferredWorkflowPackage is { } preferredPackage) + { + SelectedPackage = preferredPackage; + } + else + { + SelectedPackage = AvailablePackages.FirstOrDefault(); + } + CustomNodes = new ObservableCollection( await ParseNodes(Workflow.NodesIndex.ToList()) ); PrunedDescription = Utilities.RemoveHtml(Workflow.Description); } + partial void OnSelectedPackageChanged(InstalledPackage? oldValue, InstalledPackage? newValue) + { + if (oldValue is null) + return; + + settingsManager.Transaction(settings => + { + settings.PreferredWorkflowPackage = newValue; + }); + + OnLoadedAsync().SafeFireAndForget(); + } + [Localizable(false)] private async Task> ParseNodes(List nodes) { @@ -51,14 +90,16 @@ public partial class OpenArtWorkflowViewModel : ContentDialogViewModelBase var installedNodesNames = new HashSet(); var nameToManifestNodes = new Dictionary(); - if (InstalledComfy?.BasePackage.ExtensionManager is { } extensionManager) + var packagePair = SelectedPackagePair; + + if (packagePair?.BasePackage.ExtensionManager is { } extensionManager) { var installedNodes = ( - await extensionManager.GetInstalledExtensionsLiteAsync(InstalledComfy.InstalledPackage) + await extensionManager.GetInstalledExtensionsLiteAsync(packagePair.InstalledPackage) ).ToList(); var manifestExtensionsMap = await extensionManager.GetManifestExtensionsMapAsync( - extensionManager.GetManifests(InstalledComfy.InstalledPackage) + extensionManager.GetManifests(packagePair.InstalledPackage) ); // Add manifestExtensions definition to installedNodes if matching git repository url diff --git a/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs index 7cbbbd39..b1dbd39a 100644 --- a/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs @@ -35,7 +35,8 @@ public partial class OpenArtBrowserViewModel( IOpenArtApi openArtApi, INotificationService notificationService, ISettingsManager settingsManager, - IPackageFactory packageFactory + IPackageFactory packageFactory, + ServiceManager vmFactory ) : TabViewModelBase, IInfinitelyScroll { private const int PageSize = 20; @@ -158,7 +159,7 @@ public partial class OpenArtBrowserViewModel( var comfyPair = packageFactory.GetPackagePair(existingComfy); - var vm = new OpenArtWorkflowViewModel { Workflow = workflow, InstalledComfy = comfyPair }; + var vm = new OpenArtWorkflowViewModel(settingsManager, packageFactory) { Workflow = workflow }; var dialog = new BetterContentDialog { @@ -193,8 +194,8 @@ public partial class OpenArtBrowserViewModel( new DownloadOpenArtWorkflowStep(openArtApi, vm.Workflow, settingsManager) ]; - // Add install steps if missing nodes - if (vm.MissingNodes is { Count: > 0 } missingNodes) + // Add install steps if missing nodes and preferred + if (vm is { InstallRequiredNodes: true, MissingNodes: { Count: > 0 } missingNodes }) { var extensionManager = comfyPair.BasePackage.ExtensionManager!; diff --git a/StabilityMatrix.Avalonia/Views/Dialogs/OpenArtWorkflowDialog.axaml b/StabilityMatrix.Avalonia/Views/Dialogs/OpenArtWorkflowDialog.axaml index af014bf1..ec043e94 100644 --- a/StabilityMatrix.Avalonia/Views/Dialogs/OpenArtWorkflowDialog.axaml +++ b/StabilityMatrix.Avalonia/Views/Dialogs/OpenArtWorkflowDialog.axaml @@ -1,99 +1,133 @@ - - - - - - + + + + + + - - - + + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StabilityMatrix.Core/Models/Settings/Settings.cs b/StabilityMatrix.Core/Models/Settings/Settings.cs index fb72ac43..e8556ba8 100644 --- a/StabilityMatrix.Core/Models/Settings/Settings.cs +++ b/StabilityMatrix.Core/Models/Settings/Settings.cs @@ -34,6 +34,19 @@ public class Settings set => ActiveInstalledPackageId = value?.Id; } + [JsonPropertyName("PreferredWorkflowPackage")] + public Guid? PreferredWorkflowPackageId { get; set; } + + [JsonIgnore] + public InstalledPackage? PreferredWorkflowPackage + { + get => + PreferredWorkflowPackageId == null + ? null + : InstalledPackages.FirstOrDefault(x => x.Id == PreferredWorkflowPackageId); + set => PreferredWorkflowPackageId = value?.Id; + } + public bool HasSeenWelcomeNotification { get; set; } public List? PathExtensions { get; set; } public string? WebApiHost { get; set; } From b28aef1e7bbb996e01c48f2d949fbd4c50c92e38 Mon Sep 17 00:00:00 2001 From: JT Date: Tue, 12 Mar 2024 21:46:18 -0700 Subject: [PATCH 16/34] Update for headless kohya cli arg --- .../Models/Packages/KohyaSs.cs | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/StabilityMatrix.Core/Models/Packages/KohyaSs.cs b/StabilityMatrix.Core/Models/Packages/KohyaSs.cs index ee197ac2..594f95ec 100644 --- a/StabilityMatrix.Core/Models/Packages/KohyaSs.cs +++ b/StabilityMatrix.Core/Models/Packages/KohyaSs.cs @@ -132,32 +132,19 @@ public class KohyaSs( await venvRunner.Setup(true, onConsoleOutput).ConfigureAwait(false); // Extra dep needed before running setup since v23.0.x - await venvRunner.PipInstall("packaging").ConfigureAwait(false); + await venvRunner.PipInstall(["rich", "packaging"]).ConfigureAwait(false); if (Compat.IsWindows) { - var setupSmPath = Path.Combine(installLocation, "setup", "setup_sm.py"); - var setupText = """ - import setup_windows - import setup_common - - setup_common.install_requirements('requirements_windows_torch2.txt', check_no_verify_flag=False) - setup_windows.sync_bits_and_bytes_files() - setup_common.configure_accelerate(run_accelerate=False) - """; - await File.WriteAllTextAsync(setupSmPath, setupText).ConfigureAwait(false); - // Install - await venvRunner.CustomInstall("setup/setup_sm.py", onConsoleOutput).ConfigureAwait(false); - await venvRunner.PipInstall("bitsandbytes-windows").ConfigureAwait(false); + await venvRunner + .CustomInstall("setup/setup_windows.py --headless", onConsoleOutput) + .ConfigureAwait(false); } else if (Compat.IsLinux) { await venvRunner - .CustomInstall( - "setup/setup_linux.py --platform-requirements-file=requirements_linux.txt --no_run_accelerate", - onConsoleOutput - ) + .CustomInstall("setup/setup_linux.py --headless", onConsoleOutput) .ConfigureAwait(false); } } From 76cf8fbd41be62778dae0a55d2bc51a6306b2521 Mon Sep 17 00:00:00 2001 From: JT Date: Fri, 15 Mar 2024 18:04:55 -0700 Subject: [PATCH 17/34] Fix star ratings for civitai stuff (cherry picked from commit 38013c16d6665f3401c891de8225306a84942446) --- .../ViewModels/Dialogs/RecommendedModelsViewModel.cs | 6 ++++-- StabilityMatrix.Avalonia/Views/CivitAiBrowserPage.axaml | 6 +++--- .../Views/Dialogs/RecommendedModelsDialog.axaml | 4 ++-- StabilityMatrix.Core/Models/Api/CivitModel.cs | 7 ++++--- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelsViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelsViewModel.cs index 41750364..e037408e 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelsViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelsViewModel.cs @@ -97,9 +97,11 @@ public partial class RecommendedModelsViewModel : ContentDialogViewModelBase new RecommendedModelItemViewModel { ModelVersion = model.ModelVersions.First( - x => !x.BaseModel.Contains("Turbo", StringComparison.OrdinalIgnoreCase) + x => + !x.BaseModel.Contains("Turbo", StringComparison.OrdinalIgnoreCase) + && !x.BaseModel.Contains("Lightning", StringComparison.OrdinalIgnoreCase) ), - Author = $"by {model.Creator.Username}", + Author = $"by {model.Creator?.Username}", CivitModel = model } ) diff --git a/StabilityMatrix.Avalonia/Views/CivitAiBrowserPage.axaml b/StabilityMatrix.Avalonia/Views/CivitAiBrowserPage.axaml index ff25d806..ad14d1a4 100644 --- a/StabilityMatrix.Avalonia/Views/CivitAiBrowserPage.axaml +++ b/StabilityMatrix.Avalonia/Views/CivitAiBrowserPage.axaml @@ -319,11 +319,11 @@ Background="#66000000" FontSize="16" Foreground="{DynamicResource ThemeEldenRingOrangeColor}" - Value="{Binding CivitModel.Stats.Rating}" /> + Value="{Binding CivitModel.ModelVersionStats.Rating}" /> @@ -336,7 +336,7 @@ + Text="{Binding CivitModel.ModelVersionStats.FavoriteCount, Converter={StaticResource KiloFormatterConverter}}" /> + Value="{Binding ModelVersion.Stats.Rating}" /> diff --git a/StabilityMatrix.Core/Models/Api/CivitModel.cs b/StabilityMatrix.Core/Models/Api/CivitModel.cs index 26e1afb2..be381d0c 100644 --- a/StabilityMatrix.Core/Models/Api/CivitModel.cs +++ b/StabilityMatrix.Core/Models/Api/CivitModel.cs @@ -48,9 +48,7 @@ public class CivitModel var latestVersion = ModelVersions?.FirstOrDefault(); if (latestVersion?.Files != null && latestVersion.Files.Any()) { - var latestModelFile = latestVersion.Files.FirstOrDefault( - x => x.Type == CivitFileType.Model - ); + var latestModelFile = latestVersion.Files.FirstOrDefault(x => x.Type == CivitFileType.Model); kbs = latestModelFile?.SizeKb ?? 0; } fullFilesSize = new FileSizeType(kbs); @@ -65,4 +63,7 @@ public class CivitModel ModelVersions != null && ModelVersions.Any() ? ModelVersions[0].BaseModel?.Replace("SD", "").Trim() : string.Empty; + + public CivitModelStats ModelVersionStats => + ModelVersions != null && ModelVersions.Any() ? ModelVersions[0].Stats : new CivitModelStats(); } From cbc37fb4669baeb1eb2e9d17f4b7cc8c658efdb1 Mon Sep 17 00:00:00 2001 From: JT Date: Sat, 16 Mar 2024 19:17:29 -0700 Subject: [PATCH 18/34] backport some bug fixes from dev --- CHANGELOG.md | 14 ++ StabilityMatrix.Avalonia/App.axaml | 2 + .../DesignData/DesignData.cs | 3 +- .../Models/IInfinitelyScroll.cs | 8 + .../CivitAiBrowserViewModel.cs | 178 +++++++----------- .../CheckpointManager/CheckpointFile.cs | 6 + .../Dialogs/NewOneClickInstallViewModel.cs | 5 + .../Dialogs/OneClickInstallViewModel.cs | 7 +- .../Dialogs/SelectModelVersionViewModel.cs | 19 +- .../Views/CivitAiBrowserPage.axaml | 82 +++----- .../Views/CivitAiBrowserPage.axaml.cs | 16 +- .../Dialogs/NewOneClickInstallDialog.axaml | 39 ++++ .../Views/MainWindow.axaml | 4 +- .../Models/Api/CivitMetadata.cs | 14 +- .../Models/Api/CivitModelStats.cs | 5 +- .../Models/Api/CivitModelVersion.cs | 21 ++- .../Models/Api/CivitModelsRequest.cs | 60 +++--- .../Models/PackageDifficulty.cs | 12 +- .../IPackageModificationRunner.cs | 2 +- .../PackageModificationRunner.cs | 2 +- .../Models/Packages/ComfyUI.cs | 2 +- .../Models/Packages/SDWebForge.cs | 2 + 22 files changed, 262 insertions(+), 241 deletions(-) create mode 100644 StabilityMatrix.Avalonia/Models/IInfinitelyScroll.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 25b313af..90a0f515 100644 --- a/CHANGELOG.md +++ b/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/), and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html). +## v2.9.2 +### Changed +- Due to changes on 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 ### Added - Fixed [#498](https://github.com/LykosAI/StabilityMatrix/issues/498) Added "Pony" category to CivitAI Model Browser diff --git a/StabilityMatrix.Avalonia/App.axaml b/StabilityMatrix.Avalonia/App.axaml index 1f039a8a..cbe8b808 100644 --- a/StabilityMatrix.Avalonia/App.axaml +++ b/StabilityMatrix.Avalonia/App.axaml @@ -4,6 +4,7 @@ xmlns:local="using:StabilityMatrix.Avalonia" xmlns:idcr="using:Dock.Avalonia.Controls.Recycling" xmlns:styling="clr-namespace:FluentAvalonia.Styling;assembly=FluentAvalonia" + xmlns:controls="clr-namespace:Avalonia.Labs.Controls;assembly=Avalonia.Labs.Controls" Name="Stability Matrix" RequestedThemeVariant="Dark"> @@ -79,6 +80,7 @@ + + + + + + + + + + + + - + - + + Margin="8,0" /> + VerticalAlignment="Center" /> @@ -96,7 +113,7 @@ - + + + + + + + + + + + + + + + + + + + + + Text="{Binding Workflow.Name}" + ToolTip.Tip="{Binding Workflow.Name}" /> diff --git a/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml index 2d377dd1..867d2a62 100644 --- a/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml +++ b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml @@ -12,6 +12,7 @@ xmlns:avalonia="https://github.com/projektanker/icons.avalonia" xmlns:input="clr-namespace:FluentAvalonia.UI.Input;assembly=FluentAvalonia" xmlns:viewModels="clr-namespace:StabilityMatrix.Avalonia.ViewModels" + xmlns:labs="clr-namespace:Avalonia.Labs.Controls;assembly=Avalonia.Labs.Controls" x:DataType="viewModels:OpenArtBrowserViewModel" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="StabilityMatrix.Avalonia.Views.OpenArtBrowserPage"> @@ -23,7 +24,7 @@ - + diff --git a/StabilityMatrix.Avalonia/Controls/Inference/ImageFolderCard.axaml b/StabilityMatrix.Avalonia/Controls/Inference/ImageFolderCard.axaml index 86c40520..67b75973 100644 --- a/StabilityMatrix.Avalonia/Controls/Inference/ImageFolderCard.axaml +++ b/StabilityMatrix.Avalonia/Controls/Inference/ImageFolderCard.axaml @@ -9,6 +9,7 @@ xmlns:ui="using:FluentAvalonia.UI.Controls" xmlns:vmInference="clr-namespace:StabilityMatrix.Avalonia.ViewModels.Inference" xmlns:scroll="clr-namespace:StabilityMatrix.Avalonia.Controls.Scroll" + xmlns:labs="clr-namespace:Avalonia.Labs.Controls;assembly=Avalonia.Labs.Controls" x:DataType="vmInference:ImageFolderCardViewModel"> @@ -195,12 +196,11 @@ - + Stretch="UniformToFill" /> @@ -115,12 +116,11 @@ - + Source="{Binding Uri}" + Stretch="Uniform" /> diff --git a/StabilityMatrix.Avalonia/Models/ImageSource.cs b/StabilityMatrix.Avalonia/Models/ImageSource.cs index 973b0bf6..0da5749d 100644 --- a/StabilityMatrix.Avalonia/Models/ImageSource.cs +++ b/StabilityMatrix.Avalonia/Models/ImageSource.cs @@ -58,6 +58,8 @@ public record ImageSource : IDisposable, ITemplateKey Bitmap = bitmap; } + public Uri? Uri => LocalFile?.FullPath != null ? new Uri(LocalFile.FullPath) : RemoteUrl; + /// public ImageSourceTemplateType TemplateKey { get; private set; } diff --git a/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj b/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj index cda7adcd..b3fba009 100644 --- a/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj +++ b/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj @@ -39,17 +39,17 @@ - - - - - - + + + + + + - + - - + + @@ -58,10 +58,10 @@ - - - - + + + + @@ -78,25 +78,25 @@ - + - + - + - - + + - + diff --git a/StabilityMatrix.Avalonia/Styles/ControlThemes/BetterComboBoxStyles.axaml b/StabilityMatrix.Avalonia/Styles/ControlThemes/BetterComboBoxStyles.axaml index 61d77d82..652a4ade 100644 --- a/StabilityMatrix.Avalonia/Styles/ControlThemes/BetterComboBoxStyles.axaml +++ b/StabilityMatrix.Avalonia/Styles/ControlThemes/BetterComboBoxStyles.axaml @@ -6,7 +6,8 @@ xmlns:mocks="using:StabilityMatrix.Avalonia.DesignData" xmlns:models="clr-namespace:StabilityMatrix.Core.Models;assembly=StabilityMatrix.Core" xmlns:sg="clr-namespace:SpacedGridControl.Avalonia;assembly=SpacedGridControl.Avalonia" - xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"> + xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" + xmlns:labs="clr-namespace:Avalonia.Labs.Controls;assembly=Avalonia.Labs.Controls"> @@ -42,15 +43,14 @@ ColumnSpacing="6" RowSpacing="0"> - + Stretch="UniformToFill"/> - - - - - + Stretch="UniformToFill"/> - - - - - + Stretch="UniformToFill"> + { NumItemsSelected = Outputs.Count(o => o.IsSelected); + Console.WriteLine($"Subscribe called"); }); categoriesCache.Connect().DeferUntilLoaded().Bind(Categories).Subscribe(); @@ -395,12 +396,13 @@ public partial class OutputsPageViewModel : PageViewModelBase var selected = Outputs.Where(o => o.IsSelected).ToList(); Debug.Assert(selected.Count == NumItemsSelected); + + var imagesToRemove = new List(); foreach (var output in selected) { // Delete the file var imageFile = new FilePath(output.ImageFile.AbsolutePath); var result = await notificationService.TryAsync(imageFile.DeleteAsync()); - if (!result.IsSuccessful) { continue; @@ -413,15 +415,10 @@ public partial class OutputsPageViewModel : PageViewModelBase await notificationService.TryAsync(sideCar.DeleteAsync()); } - OutputsCache.Remove(output.ImageFile); - - // Invalidate cache - if (ImageLoader.AsyncImageLoader is FallbackRamCachedWebImageLoader loader) - { - loader.RemoveAllNamesFromCache(imageFile.Name); - } + imagesToRemove.Add(output.ImageFile); } + OutputsCache.Remove(imagesToRemove); NumItemsSelected = 0; ClearSelection(); } diff --git a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs index 93f3534a..71d624ec 100644 --- a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs @@ -54,7 +54,7 @@ public partial class PackageCardViewModel( private InstalledPackage? package; [ObservableProperty] - private string? cardImageSource; + private Uri? cardImageSource; [ObservableProperty] private bool isUpdateAvailable; @@ -127,7 +127,7 @@ public partial class PackageCardViewModel( ) { IsUnknownPackage = true; - CardImageSource = ""; + CardImageSource = null; InstalledVersion = "Unknown"; } else @@ -135,7 +135,7 @@ public partial class PackageCardViewModel( IsUnknownPackage = false; var basePackage = packageFactory[value.PackageName]; - CardImageSource = basePackage?.PreviewImageUri.ToString() ?? Assets.NoImage.ToString(); + CardImageSource = basePackage?.PreviewImageUri ?? Assets.NoImage; InstalledVersion = value.Version?.DisplayVersion ?? "Unknown"; CanUseConfigMethod = basePackage?.AvailableSharedFolderMethods.Contains(SharedFolderMethod.Configuration) ?? false; diff --git a/StabilityMatrix.Avalonia/Views/CheckpointsPage.axaml b/StabilityMatrix.Avalonia/Views/CheckpointsPage.axaml index c2831986..bbb08dae 100644 --- a/StabilityMatrix.Avalonia/Views/CheckpointsPage.axaml +++ b/StabilityMatrix.Avalonia/Views/CheckpointsPage.axaml @@ -13,6 +13,7 @@ xmlns:api="clr-namespace:StabilityMatrix.Core.Models.Api;assembly=StabilityMatrix.Core" xmlns:generic="clr-namespace:System.Collections.Generic;assembly=System.Collections" xmlns:converters="clr-namespace:StabilityMatrix.Avalonia.Converters" + xmlns:labs="clr-namespace:Avalonia.Labs.Controls;assembly=Avalonia.Labs.Controls" d:DataContext="{x:Static mocks:DesignData.CheckpointsPageViewModel}" x:CompileBindings="True" x:DataType="vm:CheckpointsPageViewModel" @@ -138,7 +139,7 @@ TextWrapping="WrapWithOverflow" IsVisible="{Binding IsConnectedModel}" /> - - + - @@ -60,7 +60,7 @@ @@ -103,7 +103,7 @@ CommandParameter="{Binding CivitModel}" IsEnabled="{Binding !IsImporting}"> - --> - - + - + Source="{Binding CivitModel.Creator.Image}"> + - - - @@ -52,7 +52,7 @@ @@ -121,6 +121,7 @@ CornerRadius="16" Margin="4" Grid.Row="0" + ClipToBounds="True" HorizontalAlignment="Left" VerticalAlignment="Bottom"> @@ -142,14 +143,16 @@ Classes="transparent" Padding="10,4"> - + Source="{Binding Creator.Avatar}"> + diff --git a/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallBrowserView.axaml.cs b/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallBrowserView.axaml.cs index 5abbe50b..08436b3b 100644 --- a/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallBrowserView.axaml.cs +++ b/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallBrowserView.axaml.cs @@ -1,4 +1,6 @@ -using Avalonia.Input; +using System.Diagnostics; +using Avalonia.Input; +using Avalonia.Labs.Controls; using StabilityMatrix.Avalonia.Controls; using StabilityMatrix.Avalonia.ViewModels.PackageManager; using StabilityMatrix.Core.Attributes; @@ -20,4 +22,9 @@ public partial class PackageInstallBrowserView : UserControlBase vm.ClearSearchQuery(); } } + + private void AsyncImage_OnFailed(object? sender, AsyncImage.AsyncImageFailedEventArgs e) + { + Debug.WriteLine($"Failed to load image: {e.ErrorException?.Message}"); + } } diff --git a/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallDetailView.axaml b/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallDetailView.axaml index 91b2e671..7bf74c46 100644 --- a/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallDetailView.axaml +++ b/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallDetailView.axaml @@ -2,7 +2,6 @@ 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:viewModels="clr-namespace:StabilityMatrix.Avalonia.ViewModels" xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls" xmlns:designData="clr-namespace:StabilityMatrix.Avalonia.DesignData" xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages" @@ -10,24 +9,26 @@ xmlns:controls1="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:models="clr-namespace:StabilityMatrix.Core.Models;assembly=StabilityMatrix.Core" xmlns:database="clr-namespace:StabilityMatrix.Core.Models.Database;assembly=StabilityMatrix.Core" - xmlns:extensions="clr-namespace:StabilityMatrix.Core.Models.Packages.Extensions;assembly=StabilityMatrix.Core" xmlns:packageManager="clr-namespace:StabilityMatrix.Avalonia.ViewModels.PackageManager" + xmlns:labs="clr-namespace:Avalonia.Labs.Controls;assembly=Avalonia.Labs.Controls" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:DataType="packageManager:PackageInstallDetailViewModel" x:CompileBindings="True" d:DataContext="{x:Static designData:DesignData.PackageInstallDetailViewModel}" x:Class="StabilityMatrix.Avalonia.Views.PackageManager.PackageInstallDetailView"> - + - + Margin="16, 16, 8, 8" + CornerRadius="8" + Source="{Binding SelectedPackage.PreviewImageUri}"> + + diff --git a/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml b/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml index 81c912e6..ee2099d9 100644 --- a/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml +++ b/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml @@ -13,6 +13,7 @@ xmlns:converters="clr-namespace:StabilityMatrix.Avalonia.Converters" xmlns:avalonia="clr-namespace:SpacedGridControl.Avalonia;assembly=SpacedGridControl.Avalonia" xmlns:markupExtensions="clr-namespace:StabilityMatrix.Avalonia.MarkupExtensions" + xmlns:labs="clr-namespace:Avalonia.Labs.Controls;assembly=Avalonia.Labs.Controls" mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="450" x:DataType="viewModels:PackageManagerViewModel" x:CompileBindings="True" @@ -44,15 +45,13 @@ - diff --git a/StabilityMatrix.Core/StabilityMatrix.Core.csproj b/StabilityMatrix.Core/StabilityMatrix.Core.csproj index de135217..5005b1bd 100644 --- a/StabilityMatrix.Core/StabilityMatrix.Core.csproj +++ b/StabilityMatrix.Core/StabilityMatrix.Core.csproj @@ -17,8 +17,8 @@ - - + + @@ -27,10 +27,10 @@ - + - + @@ -45,7 +45,7 @@ - + @@ -53,7 +53,7 @@ - + diff --git a/StabilityMatrix.Tests/StabilityMatrix.Tests.csproj b/StabilityMatrix.Tests/StabilityMatrix.Tests.csproj index 6283704a..257b8f93 100644 --- a/StabilityMatrix.Tests/StabilityMatrix.Tests.csproj +++ b/StabilityMatrix.Tests/StabilityMatrix.Tests.csproj @@ -17,18 +17,18 @@ - + - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj b/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj index d4c1e79e..eb592e08 100644 --- a/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj +++ b/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj @@ -15,16 +15,16 @@ - + - - + + - + runtime; build; native; contentfiles; analyzers; buildtransitive From b2a76056afd02b244143b5a3f47fd14568dde92b Mon Sep 17 00:00:00 2001 From: JT Date: Mon, 25 Mar 2024 23:30:57 -0700 Subject: [PATCH 26/34] phrasing --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2787d790..8f66aa16 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Multi-Platform Package Manager and Inference UI for Stable Diffusion - Embedded Git and Python dependencies, with no need for either to be globally installed - Fully portable - move Stability Matrix's Data Directory to a new drive or computer at any time -### ✨ Inference - A Reimagined Stable Diffusion Interface, Built-In to Stability Matrix +### ✨ Inference - A Reimagined Interface for Stable Diffusion, Built-In to Stability Matrix - Powerful auto-completion and syntax highlighting using a formal language grammar - Workspaces open in tabs that save and load from `.smproj` project files From 3af08e04f3d021c08a34c000ebe382475dcd24db Mon Sep 17 00:00:00 2001 From: JT Date: Mon, 25 Mar 2024 23:36:58 -0700 Subject: [PATCH 27/34] more chagenlog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba1236d8..b1487a44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,8 +10,10 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2 ### Added - Added OpenArt.AI workflow browser for ComfyUI workflows ### Changed -- Updated to Avalonia 11.0.10 - Changed to a new image control for pages with many images +- (Internal) Updated to Avalonia 11.0.10 +### Fixed +- Improved performance when deleting many images from the Outputs page ## v2.10.0-dev.3 ### Added From 73771a29c16c11f298ac200dd05be4b0490df90c Mon Sep 17 00:00:00 2001 From: JT Date: Mon, 25 Mar 2024 23:44:34 -0700 Subject: [PATCH 28/34] change PackageInstallDetailView back to BetterAdvancedImage since the image should be cached from the previous page --- .../Views/PackageManager/PackageInstallDetailView.axaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallDetailView.axaml b/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallDetailView.axaml index 7bf74c46..84499d89 100644 --- a/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallDetailView.axaml +++ b/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallDetailView.axaml @@ -19,7 +19,7 @@ - - + Date: Mon, 25 Mar 2024 23:46:33 -0700 Subject: [PATCH 29/34] remove console write --- StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs index df39dd3d..da48a2e8 100644 --- a/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs @@ -145,7 +145,6 @@ public partial class OutputsPageViewModel : PageViewModelBase .Subscribe(_ => { NumItemsSelected = Outputs.Count(o => o.IsSelected); - Console.WriteLine($"Subscribe called"); }); categoriesCache.Connect().DeferUntilLoaded().Bind(Categories).Subscribe(); From 597c4c9aec7f956b614ed922931fa6c989f18885 Mon Sep 17 00:00:00 2001 From: JT Date: Tue, 26 Mar 2024 00:44:23 -0700 Subject: [PATCH 30/34] jsonignore uri & remove unused event handler --- StabilityMatrix.Avalonia/Models/ImageSource.cs | 1 + .../PackageManager/PackageInstallBrowserView.axaml.cs | 9 +-------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/StabilityMatrix.Avalonia/Models/ImageSource.cs b/StabilityMatrix.Avalonia/Models/ImageSource.cs index 0da5749d..331a9c09 100644 --- a/StabilityMatrix.Avalonia/Models/ImageSource.cs +++ b/StabilityMatrix.Avalonia/Models/ImageSource.cs @@ -58,6 +58,7 @@ public record ImageSource : IDisposable, ITemplateKey Bitmap = bitmap; } + [JsonIgnore] public Uri? Uri => LocalFile?.FullPath != null ? new Uri(LocalFile.FullPath) : RemoteUrl; /// diff --git a/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallBrowserView.axaml.cs b/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallBrowserView.axaml.cs index 08436b3b..5abbe50b 100644 --- a/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallBrowserView.axaml.cs +++ b/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallBrowserView.axaml.cs @@ -1,6 +1,4 @@ -using System.Diagnostics; -using Avalonia.Input; -using Avalonia.Labs.Controls; +using Avalonia.Input; using StabilityMatrix.Avalonia.Controls; using StabilityMatrix.Avalonia.ViewModels.PackageManager; using StabilityMatrix.Core.Attributes; @@ -22,9 +20,4 @@ public partial class PackageInstallBrowserView : UserControlBase vm.ClearSearchQuery(); } } - - private void AsyncImage_OnFailed(object? sender, AsyncImage.AsyncImageFailedEventArgs e) - { - Debug.WriteLine($"Failed to load image: {e.ErrorException?.Message}"); - } } From 870aa20a80e4b48768020535549277fd17dd73e1 Mon Sep 17 00:00:00 2001 From: JT Date: Mon, 1 Apr 2024 21:41:39 -0700 Subject: [PATCH 31/34] output sharing by default & invokeai fixes & some torch version updates --- CHANGELOG.md | 6 + .../Dialogs/RecommendedModelsViewModel.cs | 2 +- .../PackageInstallDetailViewModel.cs | 15 +- .../Views/MainWindow.axaml | 4 +- .../Views/MainWindow.axaml.cs | 5 + .../PackageInstallDetailView.axaml | 128 +++++++++--------- .../Views/PackageManagerPage.axaml | 3 +- .../SetupOutputSharingStep.cs | 15 ++ .../Models/Packages/ComfyUI.cs | 5 +- .../Models/Packages/InvokeAI.cs | 57 +------- .../Models/Packages/RuinedFooocus.cs | 8 +- StabilityMatrix.Core/Python/PyVenvRunner.cs | 2 +- 12 files changed, 123 insertions(+), 127 deletions(-) create mode 100644 StabilityMatrix.Core/Models/PackageModification/SetupOutputSharingStep.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index b1487a44..cea82fd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,17 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2 ## v2.10.0-preview.1 ### Added - Added OpenArt.AI workflow browser for ComfyUI workflows +- Added Output Sharing toggle in Advanced Options during install flow ### Changed - Changed to a new image control for pages with many images +- Removed Symlink option for InvokeAI due to changes with InvokeAI v4.0+ +- Output sharing is now enabled by default for new installations - (Internal) Updated to Avalonia 11.0.10 ### Fixed - Improved performance when deleting many images from the Outputs page +- Fixed ComfyUI torch downgrading to 2.1.2 when updating +- Fixed [#529](https://github.com/LykosAI/StabilityMatrix/issues/529) - OneTrainer requesting input during update +- Fixed "Could not find entry point for InvokeAI" error on InvokeAI v4.0+ ## v2.10.0-dev.3 ### Added diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelsViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelsViewModel.cs index e037408e..a2aa480e 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelsViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelsViewModel.cs @@ -75,7 +75,7 @@ public partial class RecommendedModelsViewModel : ContentDialogViewModelBase CivitModels .Connect() .DeferUntilLoaded() - .Filter(f => f.ModelVersion.BaseModel == "SDXL 1.0") + .Filter(f => f.ModelVersion.BaseModel == "SDXL 1.0" || f.ModelVersion.BaseModel == "Pony") .Bind(SdxlModels) .Subscribe(); } diff --git a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageInstallDetailViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageInstallDetailViewModel.cs index 9172d5c5..4e9b7fa2 100644 --- a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageInstallDetailViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageInstallDetailViewModel.cs @@ -85,6 +85,9 @@ public partial class PackageInstallDetailViewModel( [ObservableProperty] private GitCommit? selectedCommit; + [ObservableProperty] + private bool isOutputSharingEnabled = true; + [ObservableProperty] private bool canInstall; @@ -97,6 +100,8 @@ public partial class PackageInstallDetailViewModel( OnInstallNameChanged(InstallName); + CanInstall = false; + SelectedTorchVersion = SelectedPackage.GetRecommendedTorchVersion(); SelectedSharedFolderMethod = SelectedPackage.RecommendedSharedFolderMethod; @@ -224,6 +229,8 @@ public partial class PackageInstallDetailViewModel( installLocation ); + var setupOutputSharingStep = new SetupOutputSharingStep(SelectedPackage, installLocation); + var package = new InstalledPackage { DisplayName = InstallName, @@ -234,7 +241,8 @@ public partial class PackageInstallDetailViewModel( LaunchCommand = SelectedPackage.LaunchCommand, LastUpdateCheck = DateTimeOffset.Now, PreferredTorchVersion = SelectedTorchVersion, - PreferredSharedFolderMethod = SelectedSharedFolderMethod + PreferredSharedFolderMethod = SelectedSharedFolderMethod, + UseSharedOutputFolder = IsOutputSharingEnabled }; var addInstalledPackageStep = new AddInstalledPackageStep(settingsManager, package); @@ -249,6 +257,11 @@ public partial class PackageInstallDetailViewModel( addInstalledPackageStep }; + if (IsOutputSharingEnabled) + { + steps.Insert(steps.IndexOf(addInstalledPackageStep), setupOutputSharingStep); + } + var packageName = SelectedPackage.Name; var runner = new PackageModificationRunner diff --git a/StabilityMatrix.Avalonia/Views/MainWindow.axaml b/StabilityMatrix.Avalonia/Views/MainWindow.axaml index 75613afc..306a1a37 100644 --- a/StabilityMatrix.Avalonia/Views/MainWindow.axaml +++ b/StabilityMatrix.Avalonia/Views/MainWindow.axaml @@ -70,11 +70,13 @@ Grid.RowSpan="2" Name="NavigationView" ItemInvoked="NavigationView_OnItemInvoked" + BackRequested="NavigationView_OnBackRequested" PaneDisplayMode="Left" IsPaneOpen="False" OpenPaneLength="{Binding PaneWidth}" IsSettingsVisible="False" - IsBackEnabled="False" + IsBackEnabled="True" + IsBackButtonVisible="True" MenuItemsSource="{Binding Pages, Mode=OneWay}" FooterMenuItemsSource="{Binding FooterPages, Mode=OneWay}" SelectedItem="{Binding SelectedCategory}"> diff --git a/StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs b/StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs index c720f37b..eba477f8 100644 --- a/StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs +++ b/StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs @@ -446,4 +446,9 @@ public partial class MainWindow : AppWindowBase e.Handled = true; navigationService.GoBack(); } + + private void NavigationView_OnBackRequested(object? sender, NavigationViewBackRequestedEventArgs e) + { + navigationService.GoBack(); + } } diff --git a/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallDetailView.axaml b/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallDetailView.axaml index 84499d89..594b03eb 100644 --- a/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallDetailView.axaml +++ b/StabilityMatrix.Avalonia/Views/PackageManager/PackageInstallDetailView.axaml @@ -16,7 +16,7 @@ x:CompileBindings="True" d:DataContext="{x:Static designData:DesignData.PackageInstallDetailViewModel}" x:Class="StabilityMatrix.Avalonia.Views.PackageManager.PackageInstallDetailView"> - + - + - + + Content="{Binding SelectedPackage.LicenseType}" /> - + - - - + + +