From 2998ceee2122c17630e633fc611dec9df8fec6f2 Mon Sep 17 00:00:00 2001 From: JT Date: Sat, 10 Feb 2024 22:35:50 -0800 Subject: [PATCH 001/130] 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 002/130] 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 003/130] 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 009/130] 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 010/130] 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 49273b049a143bb4ba8d5979b9b9ea3bf6db1f62 Mon Sep 17 00:00:00 2001 From: JT Date: Sun, 25 Feb 2024 16:54:25 -0800 Subject: [PATCH 011/130] wip start of multi-package running stuff --- .../Services/RunningPackageService.cs | 135 ++++++++++++++++++ .../PackageManager/PackageCardViewModel.cs | 54 ++++--- .../PackageInstallDetailViewModel.cs | 2 +- .../ViewModels/RunningPackageViewModel.cs | 13 ++ .../Views/ConsoleOutputPage.axaml | 22 +++ .../Views/ConsoleOutputPage.axaml.cs | 13 ++ .../Helper/Factory/IPackageFactory.cs | 1 + .../Helper/Factory/PackageFactory.cs | 33 ++++- 8 files changed, 241 insertions(+), 32 deletions(-) create mode 100644 StabilityMatrix.Avalonia/Services/RunningPackageService.cs create mode 100644 StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs create mode 100644 StabilityMatrix.Avalonia/Views/ConsoleOutputPage.axaml create mode 100644 StabilityMatrix.Avalonia/Views/ConsoleOutputPage.axaml.cs diff --git a/StabilityMatrix.Avalonia/Services/RunningPackageService.cs b/StabilityMatrix.Avalonia/Services/RunningPackageService.cs new file mode 100644 index 00000000..d112a71f --- /dev/null +++ b/StabilityMatrix.Avalonia/Services/RunningPackageService.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Immutable; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using Avalonia.Controls.Notifications; +using CommunityToolkit.Mvvm.ComponentModel; +using Microsoft.Extensions.Logging; +using StabilityMatrix.Avalonia.Models; +using StabilityMatrix.Avalonia.ViewModels; +using StabilityMatrix.Core.Attributes; +using StabilityMatrix.Core.Extensions; +using StabilityMatrix.Core.Helper; +using StabilityMatrix.Core.Helper.Factory; +using StabilityMatrix.Core.Models; +using StabilityMatrix.Core.Models.FileInterfaces; +using StabilityMatrix.Core.Models.Packages; +using StabilityMatrix.Core.Python; +using StabilityMatrix.Core.Services; + +namespace StabilityMatrix.Avalonia.Services; + +[Singleton] +public partial class RunningPackageService( + ILogger logger, + IPackageFactory packageFactory, + INotificationService notificationService, + ISettingsManager settingsManager, + IPyRunner pyRunner +) : ObservableObject +{ + [ObservableProperty] + private ObservableDictionary runningPackages = []; + + public async Task StartPackage(InstalledPackage installedPackage, string? command = null) + { + var activeInstallName = installedPackage.PackageName; + var basePackage = string.IsNullOrWhiteSpace(activeInstallName) + ? null + : packageFactory.GetNewBasePackage(installedPackage); + + if (basePackage == null) + { + logger.LogWarning( + "During launch, package name '{PackageName}' did not match a definition", + activeInstallName + ); + + notificationService.Show( + new Notification( + "Package name invalid", + "Install package name did not match a definition. Please reinstall and let us know about this issue.", + NotificationType.Error + ) + ); + return null; + } + + // If this is the first launch (LaunchArgs is null), + // load and save a launch options dialog vm + // so that dynamic initial values are saved. + if (installedPackage.LaunchArgs == null) + { + var definitions = basePackage.LaunchOptions; + // Create config cards and save them + var cards = LaunchOptionCard + .FromDefinitions(definitions, Array.Empty()) + .ToImmutableArray(); + + var args = cards.SelectMany(c => c.Options).ToList(); + + logger.LogDebug( + "Setting initial launch args: {Args}", + string.Join(", ", args.Select(o => o.ToArgString()?.ToRepr())) + ); + + settingsManager.SaveLaunchArgs(installedPackage.Id, args); + } + + if (basePackage is not StableSwarm) + { + await pyRunner.Initialize(); + } + + // Get path from package + var packagePath = new DirectoryPath(settingsManager.LibraryDir, installedPackage.LibraryPath!); + + if (basePackage is not StableSwarm) + { + // Unpack sitecustomize.py to venv + await UnpackSiteCustomize(packagePath.JoinDir("venv")); + } + + // Clear console and start update processing + var console = new ConsoleViewModel(); + console.StartUpdates(); + + // Update shared folder links (in case library paths changed) + await basePackage.UpdateModelFolders( + packagePath, + installedPackage.PreferredSharedFolderMethod ?? basePackage.RecommendedSharedFolderMethod + ); + + // Load user launch args from settings and convert to string + var userArgs = installedPackage.LaunchArgs ?? []; + var userArgsString = string.Join(" ", userArgs.Select(opt => opt.ToArgString())); + + // Join with extras, if any + userArgsString = string.Join(" ", userArgsString, basePackage.ExtraLaunchArguments); + + // Use input command if provided, otherwise use package launch command + command ??= basePackage.LaunchCommand; + + await basePackage.RunPackage(packagePath, command, userArgsString, o => console.Post(o)); + var runningPackage = new PackagePair(installedPackage, basePackage); + + EventManager.Instance.OnRunningPackageStatusChanged(runningPackage); + + var viewModel = new RunningPackageViewModel(runningPackage, console); + RunningPackages.Add(runningPackage.InstalledPackage.Id, viewModel); + + return runningPackage.InstalledPackage.Id; + } + + public RunningPackageViewModel? GetRunningPackageViewModel(Guid id) => + RunningPackages.TryGetValue(id, out var vm) ? vm : null; + + private static async Task UnpackSiteCustomize(DirectoryPath venvPath) + { + var sitePackages = venvPath.JoinDir(PyVenvRunner.RelativeSitePackagesPath); + var file = sitePackages.JoinFile("sitecustomize.py"); + file.Directory?.Create(); + await Assets.PyScriptSiteCustomize.ExtractTo(file, true); + } +} diff --git a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs index ba815fbf..20f97b72 100644 --- a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs @@ -26,7 +26,6 @@ using StabilityMatrix.Core.Models; using StabilityMatrix.Core.Models.FileInterfaces; using StabilityMatrix.Core.Models.PackageModification; using StabilityMatrix.Core.Models.Packages; -using StabilityMatrix.Core.Models.Settings; using StabilityMatrix.Core.Processes; using StabilityMatrix.Core.Services; @@ -34,15 +33,16 @@ namespace StabilityMatrix.Avalonia.ViewModels.PackageManager; [ManagedService] [Transient] -public partial class PackageCardViewModel : ProgressViewModel +public partial class PackageCardViewModel( + ILogger logger, + IPackageFactory packageFactory, + INotificationService notificationService, + ISettingsManager settingsManager, + INavigationService navigationService, + ServiceManager vmFactory, + RunningPackageService runningPackageService +) : ProgressViewModel { - private readonly ILogger logger; - private readonly IPackageFactory packageFactory; - private readonly INotificationService notificationService; - private readonly ISettingsManager settingsManager; - private readonly INavigationService navigationService; - private readonly ServiceManager vmFactory; - [ObservableProperty] private InstalledPackage? package; @@ -82,23 +82,6 @@ public partial class PackageCardViewModel : ProgressViewModel [ObservableProperty] private bool canUseExtensions; - public PackageCardViewModel( - ILogger logger, - IPackageFactory packageFactory, - INotificationService notificationService, - ISettingsManager settingsManager, - INavigationService navigationService, - ServiceManager vmFactory - ) - { - this.logger = logger; - this.packageFactory = packageFactory; - this.notificationService = notificationService; - this.settingsManager = settingsManager; - this.navigationService = navigationService; - this.vmFactory = vmFactory; - } - partial void OnPackageChanged(InstalledPackage? value) { if (string.IsNullOrWhiteSpace(value?.PackageName)) @@ -163,15 +146,26 @@ public partial class PackageCardViewModel : ProgressViewModel } } - public void Launch() + public async Task Launch() { if (Package == null) return; - settingsManager.Transaction(s => s.ActiveInstalledPackageId = Package.Id); + var packageId = await runningPackageService.StartPackage(Package); + + if (packageId != null) + { + var vm = runningPackageService.GetRunningPackageViewModel(packageId.Value); + if (vm != null) + { + navigationService.NavigateTo(vm, new BetterDrillInNavigationTransition()); + } + } - navigationService.NavigateTo(new BetterDrillInNavigationTransition()); - EventManager.Instance.OnPackageLaunchRequested(Package.Id); + // settingsManager.Transaction(s => s.ActiveInstalledPackageId = Package.Id); + // + // navigationService.NavigateTo(new BetterDrillInNavigationTransition()); + // EventManager.Instance.OnPackageLaunchRequested(Package.Id); } public async Task Uninstall() diff --git a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageInstallDetailViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageInstallDetailViewModel.cs index 71e14b31..9172d5c5 100644 --- a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageInstallDetailViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageInstallDetailViewModel.cs @@ -281,7 +281,7 @@ public partial class PackageInstallDetailViewModel( SelectedVersion = !IsReleaseMode ? AvailableVersions?.FirstOrDefault(x => x.TagName == SelectedPackage.MainBranch) ?? AvailableVersions?.FirstOrDefault() - : AvailableVersions?.FirstOrDefault(); + : AvailableVersions?.FirstOrDefault(v => !v.IsPrerelease); CanInstall = !ShowDuplicateWarning; } diff --git a/StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs new file mode 100644 index 00000000..d2beffe6 --- /dev/null +++ b/StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs @@ -0,0 +1,13 @@ +using StabilityMatrix.Avalonia.ViewModels.Base; +using StabilityMatrix.Avalonia.Views; +using StabilityMatrix.Core.Attributes; +using StabilityMatrix.Core.Models; + +namespace StabilityMatrix.Avalonia.ViewModels; + +[View(typeof(ConsoleOutputPage))] +public class RunningPackageViewModel(PackagePair runningPackage, ConsoleViewModel console) : ViewModelBase +{ + public PackagePair RunningPackage { get; } = runningPackage; + public ConsoleViewModel Console { get; } = console; +} diff --git a/StabilityMatrix.Avalonia/Views/ConsoleOutputPage.axaml b/StabilityMatrix.Avalonia/Views/ConsoleOutputPage.axaml new file mode 100644 index 00000000..8d8bdbc2 --- /dev/null +++ b/StabilityMatrix.Avalonia/Views/ConsoleOutputPage.axaml @@ -0,0 +1,22 @@ + + + diff --git a/StabilityMatrix.Avalonia/Views/ConsoleOutputPage.axaml.cs b/StabilityMatrix.Avalonia/Views/ConsoleOutputPage.axaml.cs new file mode 100644 index 00000000..1c7fdae6 --- /dev/null +++ b/StabilityMatrix.Avalonia/Views/ConsoleOutputPage.axaml.cs @@ -0,0 +1,13 @@ +using StabilityMatrix.Avalonia.Controls; +using StabilityMatrix.Core.Attributes; + +namespace StabilityMatrix.Avalonia.Views; + +[Transient] +public partial class ConsoleOutputPage : UserControlBase +{ + public ConsoleOutputPage() + { + InitializeComponent(); + } +} diff --git a/StabilityMatrix.Core/Helper/Factory/IPackageFactory.cs b/StabilityMatrix.Core/Helper/Factory/IPackageFactory.cs index b088c3bf..ccd855f8 100644 --- a/StabilityMatrix.Core/Helper/Factory/IPackageFactory.cs +++ b/StabilityMatrix.Core/Helper/Factory/IPackageFactory.cs @@ -10,4 +10,5 @@ public interface IPackageFactory BasePackage? this[string packageName] { get; } PackagePair? GetPackagePair(InstalledPackage? installedPackage); IEnumerable GetPackagesByType(PackageType packageType); + BasePackage GetNewBasePackage(InstalledPackage installedPackage); } diff --git a/StabilityMatrix.Core/Helper/Factory/PackageFactory.cs b/StabilityMatrix.Core/Helper/Factory/PackageFactory.cs index a31bca2b..0e959e89 100644 --- a/StabilityMatrix.Core/Helper/Factory/PackageFactory.cs +++ b/StabilityMatrix.Core/Helper/Factory/PackageFactory.cs @@ -1,22 +1,53 @@ using StabilityMatrix.Core.Attributes; +using StabilityMatrix.Core.Helper.Cache; using StabilityMatrix.Core.Models; using StabilityMatrix.Core.Models.Packages; +using StabilityMatrix.Core.Services; namespace StabilityMatrix.Core.Helper.Factory; [Singleton(typeof(IPackageFactory))] public class PackageFactory : IPackageFactory { + private readonly IGithubApiCache githubApiCache; + private readonly ISettingsManager settingsManager; + private readonly IDownloadService downloadService; + private readonly IPrerequisiteHelper prerequisiteHelper; + /// /// Mapping of package.Name to package /// private readonly Dictionary basePackages; - public PackageFactory(IEnumerable basePackages) + public PackageFactory( + IEnumerable basePackages, + IGithubApiCache githubApiCache, + ISettingsManager settingsManager, + IDownloadService downloadService, + IPrerequisiteHelper prerequisiteHelper + ) { + this.githubApiCache = githubApiCache; + this.settingsManager = settingsManager; + this.downloadService = downloadService; + this.prerequisiteHelper = prerequisiteHelper; this.basePackages = basePackages.ToDictionary(x => x.Name); } + public BasePackage GetNewBasePackage(InstalledPackage installedPackage) + { + return installedPackage.PackageName switch + { + "ComfyUI" => new ComfyUI(githubApiCache, settingsManager, downloadService, prerequisiteHelper), + "Fooocus" => new Fooocus(githubApiCache, settingsManager, downloadService, prerequisiteHelper), + "stable-diffusion-webui" + => new A3WebUI(githubApiCache, settingsManager, downloadService, prerequisiteHelper), + "Fooocus-ControlNet-SDXL" + => new FocusControlNet(githubApiCache, settingsManager, downloadService, prerequisiteHelper), + _ => throw new ArgumentOutOfRangeException() + }; + } + public IEnumerable GetAllAvailablePackages() { return basePackages.Values.OrderBy(p => p.InstallerSortOrder).ThenBy(p => p.DisplayName); From fb8f06d6117f1e1f1a205670348a3ca51857c85d Mon Sep 17 00:00:00 2001 From: JT Date: Mon, 26 Feb 2024 02:34:18 -0800 Subject: [PATCH 012/130] nav headers! and buttons! --- .../DesignData/DesignData.cs | 13 +++ .../Services/RunningPackageService.cs | 4 +- .../Styles/ThemeColors.axaml | 1 + .../ViewModels/NewPackageManagerViewModel.cs | 4 + .../PackageManager/PackageCardViewModel.cs | 101 +++++++++++++++++- .../ViewModels/RunningPackageViewModel.cs | 9 +- .../Views/PackageManagerPage.axaml | 80 +++++++++----- 7 files changed, 177 insertions(+), 35 deletions(-) diff --git a/StabilityMatrix.Avalonia/DesignData/DesignData.cs b/StabilityMatrix.Avalonia/DesignData/DesignData.cs index 77d5a384..ee43cb1d 100644 --- a/StabilityMatrix.Avalonia/DesignData/DesignData.cs +++ b/StabilityMatrix.Avalonia/DesignData/DesignData.cs @@ -98,6 +98,19 @@ public static class DesignData }, LibraryPath = $"Packages{Path.DirectorySeparatorChar}example-webui", LastUpdateCheck = DateTimeOffset.Now + }, + new() + { + Id = Guid.NewGuid(), + DisplayName = "Running Comfy", + PackageName = "ComfyUI", + Version = new InstalledPackageVersion + { + InstalledBranch = "master", + InstalledCommitSha = "abc12uwu345568972abaedf7g7e679a98879e879f87ga8" + }, + LibraryPath = $"Packages{Path.DirectorySeparatorChar}example-webui", + LastUpdateCheck = DateTimeOffset.Now } }, ActiveInstalledPackageId = activePackageId diff --git a/StabilityMatrix.Avalonia/Services/RunningPackageService.cs b/StabilityMatrix.Avalonia/Services/RunningPackageService.cs index d112a71f..44f6bf56 100644 --- a/StabilityMatrix.Avalonia/Services/RunningPackageService.cs +++ b/StabilityMatrix.Avalonia/Services/RunningPackageService.cs @@ -32,7 +32,7 @@ public partial class RunningPackageService( [ObservableProperty] private ObservableDictionary runningPackages = []; - public async Task StartPackage(InstalledPackage installedPackage, string? command = null) + public async Task StartPackage(InstalledPackage installedPackage, string? command = null) { var activeInstallName = installedPackage.PackageName; var basePackage = string.IsNullOrWhiteSpace(activeInstallName) @@ -119,7 +119,7 @@ public partial class RunningPackageService( var viewModel = new RunningPackageViewModel(runningPackage, console); RunningPackages.Add(runningPackage.InstalledPackage.Id, viewModel); - return runningPackage.InstalledPackage.Id; + return runningPackage; } public RunningPackageViewModel? GetRunningPackageViewModel(Guid id) => diff --git a/StabilityMatrix.Avalonia/Styles/ThemeColors.axaml b/StabilityMatrix.Avalonia/Styles/ThemeColors.axaml index dbca9287..963fb0f7 100644 --- a/StabilityMatrix.Avalonia/Styles/ThemeColors.axaml +++ b/StabilityMatrix.Avalonia/Styles/ThemeColors.axaml @@ -21,6 +21,7 @@ #00BCD4 #009688 #2C582C + #2C582C #3A783C #4BA04F #AA4BA04F diff --git a/StabilityMatrix.Avalonia/ViewModels/NewPackageManagerViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/NewPackageManagerViewModel.cs index d900f2bc..2170f6bf 100644 --- a/StabilityMatrix.Avalonia/ViewModels/NewPackageManagerViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/NewPackageManagerViewModel.cs @@ -58,6 +58,10 @@ public partial class NewPackageManagerViewModel : PageViewModelBase { CurrentPagePath.Add(value); } + else if (value is RunningPackageViewModel) + { + CurrentPagePath.Add(value); + } else { CurrentPagePath.Clear(); diff --git a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs index 20f97b72..45cd863d 100644 --- a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs @@ -1,11 +1,14 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; +using AsyncAwaitBestPractices; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Notifications; using Avalonia.Controls.Primitives; +using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using FluentAvalonia.UI.Controls; @@ -38,11 +41,13 @@ public partial class PackageCardViewModel( IPackageFactory packageFactory, INotificationService notificationService, ISettingsManager settingsManager, - INavigationService navigationService, + INavigationService navigationService, ServiceManager vmFactory, RunningPackageService runningPackageService ) : ProgressViewModel { + private string webUiUrl = string.Empty; + [ObservableProperty] private InstalledPackage? package; @@ -82,6 +87,12 @@ public partial class PackageCardViewModel( [ObservableProperty] private bool canUseExtensions; + [ObservableProperty] + private bool isRunning; + + [ObservableProperty] + private bool showWebUiButton; + partial void OnPackageChanged(InstalledPackage? value) { if (string.IsNullOrWhiteSpace(value?.PackageName)) @@ -115,6 +126,12 @@ public partial class PackageCardViewModel( public override async Task OnLoadedAsync() { + if (Design.IsDesignMode && Package?.DisplayName == "Running Comfy") + { + IsRunning = true; + ShowWebUiButton = true; + } + if (Design.IsDesignMode || !settingsManager.IsLibraryDirSet || Package is not { } currentPackage) return; @@ -151,14 +168,19 @@ public partial class PackageCardViewModel( if (Package == null) return; - var packageId = await runningPackageService.StartPackage(Package); + var packagePair = await runningPackageService.StartPackage(Package); - if (packageId != null) + if (packagePair != null) { - var vm = runningPackageService.GetRunningPackageViewModel(packageId.Value); + IsRunning = true; + + packagePair.BasePackage.Exited += BasePackageOnExited; + packagePair.BasePackage.StartupComplete += RunningPackageOnStartupComplete; + + var vm = runningPackageService.GetRunningPackageViewModel(packagePair.InstalledPackage.Id); if (vm != null) { - navigationService.NavigateTo(vm, new BetterDrillInNavigationTransition()); + navigationService.NavigateTo(vm, new BetterEntranceNavigationTransition()); } } @@ -168,6 +190,69 @@ public partial class PackageCardViewModel( // EventManager.Instance.OnPackageLaunchRequested(Package.Id); } + public void NavToConsole() + { + if (Package == null) + return; + + var vm = runningPackageService.GetRunningPackageViewModel(Package.Id); + if (vm != null) + { + navigationService.NavigateTo(vm, new BetterEntranceNavigationTransition()); + } + } + + public void LaunchWebUi() + { + if (string.IsNullOrEmpty(webUiUrl)) + return; + + notificationService.TryAsync( + Task.Run(() => ProcessRunner.OpenUrl(webUiUrl)), + "Failed to open URL", + $"{webUiUrl}" + ); + } + + private void BasePackageOnExited(object? sender, int exitCode) + { + EventManager.Instance.OnRunningPackageStatusChanged(null); + Dispatcher + .UIThread.InvokeAsync(async () => + { + logger.LogTrace("Process exited ({Code}) at {Time:g}", exitCode, DateTimeOffset.Now); + + // Need to wait for streams to finish before detaching handlers + if (sender is BaseGitPackage { VenvRunner: not null } package) + { + var process = package.VenvRunner.Process; + if (process is not null) + { + // Max 5 seconds + var ct = new CancellationTokenSource(5000).Token; + try + { + await process.WaitUntilOutputEOF(ct); + } + catch (OperationCanceledException e) + { + logger.LogWarning("Waiting for process EOF timed out: {Message}", e.Message); + } + } + } + + // Detach handlers + if (sender is BasePackage basePackage) + { + basePackage.Exited -= BasePackageOnExited; + basePackage.StartupComplete -= RunningPackageOnStartupComplete; + } + + IsRunning = false; + }) + .SafeFireAndForget(); + } + public async Task Uninstall() { if (Package?.LibraryPath == null) @@ -559,4 +644,10 @@ public partial class PackageCardViewModel( IsSharedModelConfig = false; } } + + private void RunningPackageOnStartupComplete(object? sender, string e) + { + webUiUrl = e.Replace("0.0.0.0", "127.0.0.1"); + ShowWebUiButton = !string.IsNullOrWhiteSpace(webUiUrl); + } } diff --git a/StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs index d2beffe6..6b928e8a 100644 --- a/StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs @@ -1,13 +1,18 @@ -using StabilityMatrix.Avalonia.ViewModels.Base; +using FluentAvalonia.UI.Controls; +using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.Views; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Models; +using SymbolIconSource = FluentIcons.Avalonia.Fluent.SymbolIconSource; namespace StabilityMatrix.Avalonia.ViewModels; [View(typeof(ConsoleOutputPage))] -public class RunningPackageViewModel(PackagePair runningPackage, ConsoleViewModel console) : ViewModelBase +public class RunningPackageViewModel(PackagePair runningPackage, ConsoleViewModel console) : PageViewModelBase { public PackagePair RunningPackage { get; } = runningPackage; public ConsoleViewModel Console { get; } = console; + + public override string Title => RunningPackage.InstalledPackage.PackageName ?? "Running Package"; + public override IconSource IconSource => new SymbolIconSource(); } diff --git a/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml b/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml index 456c3697..9416415d 100644 --- a/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml +++ b/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml @@ -10,11 +10,17 @@ xmlns:icons="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia" xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages" xmlns:system="clr-namespace:System;assembly=System.Runtime" + xmlns:converters="clr-namespace:StabilityMatrix.Avalonia.Converters" + xmlns:avalonia="clr-namespace:SpacedGridControl.Avalonia;assembly=SpacedGridControl.Avalonia" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:DataType="viewModels:PackageManagerViewModel" x:CompileBindings="True" d:DataContext="{x:Static designData:DesignData.PackageManagerViewModel}" x:Class="StabilityMatrix.Avalonia.Views.PackageManagerPage"> + + + + @@ -27,6 +33,13 @@ + + + + + + + @@ -221,13 +234,19 @@ Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" - IsVisible="{Binding IsUpdateAvailable}" - ColumnDefinitions="*, *"> + IsVisible="{Binding !IsUnknownPackage}" + ColumnDefinitions="*,Auto"> + + - - - + + ? DevModeSettingChanged; public event EventHandler? UpdateAvailable; public event EventHandler? PackageLaunchRequested; + public event EventHandler? PackageRelaunchRequested; public event EventHandler? ScrollToBottomRequested; public event EventHandler? ProgressChanged; public event EventHandler? RunningPackageStatusChanged; @@ -100,4 +101,7 @@ public class EventManager public void OnRecommendedModelsDialogClosed() => RecommendedModelsDialogClosed?.Invoke(this, EventArgs.Empty); + + public void OnPackageRelaunchRequested(InstalledPackage package) => + PackageRelaunchRequested?.Invoke(this, package); } From 92620dc8940b456551ea21912070f50a424af621 Mon Sep 17 00:00:00 2001 From: Ionite Date: Tue, 27 Feb 2024 15:39:34 -0500 Subject: [PATCH 014/130] 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 015/130] 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 016/130] 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 017/130] 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 018/130] 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 45f48ce36f6d51835a1abbb000a258df73010a2b Mon Sep 17 00:00:00 2001 From: JT Date: Tue, 27 Feb 2024 23:59:06 -0800 Subject: [PATCH 019/130] fix inference launch connection stuff to work with multi-package --- .../Services/RunningPackageService.cs | 4 - .../InferenceConnectionHelpViewModel.cs | 15 +-- .../ViewModels/InferenceViewModel.cs | 112 ++++++++++++------ .../PackageManager/PackageCardViewModel.cs | 65 ++++++++-- .../ViewModels/RunningPackageViewModel.cs | 19 ++- .../Helper/Factory/PackageFactory.cs | 45 ++++++- 6 files changed, 195 insertions(+), 65 deletions(-) diff --git a/StabilityMatrix.Avalonia/Services/RunningPackageService.cs b/StabilityMatrix.Avalonia/Services/RunningPackageService.cs index 89f0d903..d3cb114f 100644 --- a/StabilityMatrix.Avalonia/Services/RunningPackageService.cs +++ b/StabilityMatrix.Avalonia/Services/RunningPackageService.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Immutable; -using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; using Avalonia.Controls.Notifications; @@ -10,7 +9,6 @@ using StabilityMatrix.Avalonia.Models; using StabilityMatrix.Avalonia.ViewModels; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Extensions; -using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper.Factory; using StabilityMatrix.Core.Models; using StabilityMatrix.Core.Models.FileInterfaces; @@ -114,8 +112,6 @@ public partial class RunningPackageService( await basePackage.RunPackage(packagePath, command, userArgsString, o => console.Post(o)); var runningPackage = new PackagePair(installedPackage, basePackage); - EventManager.Instance.OnRunningPackageStatusChanged(runningPackage); - var viewModel = new RunningPackageViewModel( settingsManager, notificationService, diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/InferenceConnectionHelpViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/InferenceConnectionHelpViewModel.cs index fafd1206..4f271517 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/InferenceConnectionHelpViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/InferenceConnectionHelpViewModel.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Threading.Tasks; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; @@ -30,6 +31,7 @@ public partial class InferenceConnectionHelpViewModel : ContentDialogViewModelBa private readonly ISettingsManager settingsManager; private readonly INavigationService navigationService; private readonly IPackageFactory packageFactory; + private readonly RunningPackageService runningPackageService; [ObservableProperty] private string title = "Hello"; @@ -58,12 +60,14 @@ public partial class InferenceConnectionHelpViewModel : ContentDialogViewModelBa public InferenceConnectionHelpViewModel( ISettingsManager settingsManager, INavigationService navigationService, - IPackageFactory packageFactory + IPackageFactory packageFactory, + RunningPackageService runningPackageService ) { this.settingsManager = settingsManager; this.navigationService = navigationService; this.packageFactory = packageFactory; + this.runningPackageService = runningPackageService; // Get comfy type installed packages var comfyPackages = this.settingsManager.Settings.InstalledPackages.Where( @@ -122,14 +126,11 @@ public partial class InferenceConnectionHelpViewModel : ContentDialogViewModelBa /// Request launch of the selected package /// [RelayCommand] - private void LaunchSelectedPackage() + private async Task LaunchSelectedPackage() { - if (SelectedPackage?.Id is { } id) + if (SelectedPackage is not null) { - Dispatcher.UIThread.Post(() => - { - EventManager.Instance.OnPackageLaunchRequested(id); - }); + await runningPackageService.StartPackage(SelectedPackage); } } diff --git a/StabilityMatrix.Avalonia/ViewModels/InferenceViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/InferenceViewModel.cs index c36fa128..42378ce2 100644 --- a/StabilityMatrix.Avalonia/ViewModels/InferenceViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/InferenceViewModel.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.ObjectModel; +using System.Collections.Specialized; using System.Linq; using System.Reactive.Linq; using System.Text.Json; @@ -51,6 +53,8 @@ public partial class InferenceViewModel : PageViewModelBase, IAsyncDisposable private readonly ServiceManager vmFactory; private readonly IModelIndexService modelIndexService; private readonly ILiteDbContext liteDbContext; + private readonly RunningPackageService runningPackageService; + private Guid? selectedPackageId; public override string Title => "Inference"; public override IconSource IconSource => @@ -86,6 +90,8 @@ public partial class InferenceViewModel : PageViewModelBase, IAsyncDisposable public bool IsComfyRunning => RunningPackage?.BasePackage is ComfyUI; + private IDisposable? onStartupComplete; + public InferenceViewModel( ServiceManager vmFactory, INotificationService notificationService, @@ -93,6 +99,7 @@ public partial class InferenceViewModel : PageViewModelBase, IAsyncDisposable ISettingsManager settingsManager, IModelIndexService modelIndexService, ILiteDbContext liteDbContext, + RunningPackageService runningPackageService, SharedState sharedState ) { @@ -101,12 +108,13 @@ public partial class InferenceViewModel : PageViewModelBase, IAsyncDisposable this.settingsManager = settingsManager; this.modelIndexService = modelIndexService; this.liteDbContext = liteDbContext; + this.runningPackageService = runningPackageService; ClientManager = inferenceClientManager; SharedState = sharedState; // Keep RunningPackage updated with the current package pair - EventManager.Instance.RunningPackageStatusChanged += OnRunningPackageStatusChanged; + runningPackageService.RunningPackages.CollectionChanged += RunningPackagesOnCollectionChanged; // "Send to Inference" EventManager.Instance.InferenceTextToImageRequested += OnInferenceTextToImageRequested; @@ -118,54 +126,77 @@ public partial class InferenceViewModel : PageViewModelBase, IAsyncDisposable MenuOpenProjectCommand.WithConditionalNotificationErrorHandler(notificationService); } + private void DisconnectFromComfy() + { + RunningPackage = null; + + // Cancel any pending connection + if (ConnectCancelCommand.CanExecute(null)) + { + ConnectCancelCommand.Execute(null); + } + onStartupComplete?.Dispose(); + onStartupComplete = null; + IsWaitingForConnection = false; + + // Disconnect + Logger.Trace("On package close - disconnecting"); + DisconnectCommand.Execute(null); + } + /// /// Updates the RunningPackage property when the running package changes. /// Also starts a connection to the backend if a new ComfyUI package is running. /// And disconnects if the package is closed. /// - private void OnRunningPackageStatusChanged(object? sender, RunningPackageStatusChangedEventArgs e) + private void RunningPackagesOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { - RunningPackage = e.CurrentPackagePair; + if ( + e.NewItems?.OfType>().Select(x => x.Value) + is not { } newItems + ) + { + if (RunningPackage != null) + { + DisconnectFromComfy(); + } + return; + } - IDisposable? onStartupComplete = null; + var comfyViewModel = newItems.FirstOrDefault( + vm => + vm.RunningPackage.InstalledPackage.Id == selectedPackageId + || vm.RunningPackage.BasePackage is ComfyUI + ); - Dispatcher.UIThread.Post(() => + if (comfyViewModel is null && RunningPackage?.BasePackage is ComfyUI) { - if (e.CurrentPackagePair?.BasePackage is ComfyUI package) - { - IsWaitingForConnection = true; - onStartupComplete = Observable - .FromEventPattern(package, nameof(package.StartupComplete)) - .Take(1) - .Subscribe(_ => + DisconnectFromComfy(); + } + else if (comfyViewModel != null && RunningPackage == null) + { + IsWaitingForConnection = true; + RunningPackage = comfyViewModel.RunningPackage; + onStartupComplete = Observable + .FromEventPattern( + comfyViewModel.RunningPackage.BasePackage, + nameof(comfyViewModel.RunningPackage.BasePackage.StartupComplete) + ) + .Take(1) + .Subscribe(_ => + { + Dispatcher.UIThread.Post(() => { - Dispatcher.UIThread.Post(() => + if (ConnectCommand.CanExecute(null)) { - if (ConnectCommand.CanExecute(null)) - { - Logger.Trace("On package launch - starting connection"); - ConnectCommand.Execute(null); - } - IsWaitingForConnection = false; - }); - }); - } - else - { - // Cancel any pending connection - if (ConnectCancelCommand.CanExecute(null)) - { - ConnectCancelCommand.Execute(null); - } - onStartupComplete?.Dispose(); - onStartupComplete = null; - IsWaitingForConnection = false; + Logger.Trace("On package launch - starting connection"); + ConnectCommand.Execute(null); + } - // Disconnect - Logger.Trace("On package close - disconnecting"); - DisconnectCommand.Execute(null); - } - }); + IsWaitingForConnection = false; + }); + }); + } } public override void OnLoaded() @@ -390,7 +421,12 @@ public partial class InferenceViewModel : PageViewModelBase, IAsyncDisposable private async Task ShowConnectionHelp() { var vm = vmFactory.Get(); - await vm.CreateDialog().ShowAsync(); + var result = await vm.CreateDialog().ShowAsync(); + + if (result != ContentDialogResult.Primary) + return; + + selectedPackageId = vm.SelectedPackage?.Id; } /// diff --git a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs index 2e8bf0a9..614fef81 100644 --- a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -36,15 +37,7 @@ namespace StabilityMatrix.Avalonia.ViewModels.PackageManager; [ManagedService] [Transient] -public partial class PackageCardViewModel( - ILogger logger, - IPackageFactory packageFactory, - INotificationService notificationService, - ISettingsManager settingsManager, - INavigationService navigationService, - ServiceManager vmFactory, - RunningPackageService runningPackageService -) : ProgressViewModel +public partial class PackageCardViewModel : ProgressViewModel { private string webUiUrl = string.Empty; @@ -93,6 +86,59 @@ public partial class PackageCardViewModel( [ObservableProperty] private bool showWebUiButton; + private readonly ILogger logger; + private readonly IPackageFactory packageFactory; + private readonly INotificationService notificationService; + private readonly ISettingsManager settingsManager; + private readonly INavigationService navigationService; + private readonly ServiceManager vmFactory; + private readonly RunningPackageService runningPackageService; + + /// + public PackageCardViewModel( + ILogger logger, + IPackageFactory packageFactory, + INotificationService notificationService, + ISettingsManager settingsManager, + INavigationService navigationService, + ServiceManager vmFactory, + RunningPackageService runningPackageService + ) + { + this.logger = logger; + this.packageFactory = packageFactory; + this.notificationService = notificationService; + this.settingsManager = settingsManager; + this.navigationService = navigationService; + this.vmFactory = vmFactory; + this.runningPackageService = runningPackageService; + + runningPackageService.RunningPackages.CollectionChanged += RunningPackagesOnCollectionChanged; + } + + private void RunningPackagesOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + if ( + e.NewItems?.OfType>().Select(x => x.Value) + is not { } newItems + ) + return; + + var runningViewModel = newItems.FirstOrDefault( + x => x.RunningPackage.InstalledPackage.Id == Package?.Id + ); + if (runningViewModel is not null) + { + IsRunning = true; + runningViewModel.RunningPackage.BasePackage.Exited += BasePackageOnExited; + runningViewModel.RunningPackage.BasePackage.StartupComplete += RunningPackageOnStartupComplete; + } + else if (runningViewModel is null && IsRunning) + { + IsRunning = false; + } + } + partial void OnPackageChanged(InstalledPackage? value) { if (string.IsNullOrWhiteSpace(value?.PackageName)) @@ -232,7 +278,6 @@ public partial class PackageCardViewModel( private void BasePackageOnExited(object? sender, int exitCode) { - EventManager.Instance.OnRunningPackageStatusChanged(null); Dispatcher .UIThread.InvokeAsync(async () => { diff --git a/StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs index 2c188633..2758269f 100644 --- a/StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs @@ -1,7 +1,5 @@ using System; using System.Threading.Tasks; -using Avalonia.Threading; -using AvaloniaEdit; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using FluentAvalonia.UI.Controls; @@ -19,7 +17,7 @@ using SymbolIconSource = FluentIcons.Avalonia.Fluent.SymbolIconSource; namespace StabilityMatrix.Avalonia.ViewModels; [View(typeof(ConsoleOutputPage))] -public partial class RunningPackageViewModel : PageViewModelBase +public partial class RunningPackageViewModel : PageViewModelBase, IDisposable, IAsyncDisposable { private readonly INotificationService notificationService; private string webUiUrl = string.Empty; @@ -51,7 +49,7 @@ public partial class RunningPackageViewModel : PageViewModelBase Console = console; Console.Document.LineCountChanged += DocumentOnLineCountChanged; RunningPackage.BasePackage.StartupComplete += BasePackageOnStartupComplete; - runningPackage.BasePackage.Exited += BasePackageOnExited; + RunningPackage.BasePackage.Exited += BasePackageOnExited; settingsManager.RelayPropertyFor( this, @@ -65,6 +63,9 @@ public partial class RunningPackageViewModel : PageViewModelBase { IsRunning = false; ShowWebUiButton = false; + Console.Document.LineCountChanged -= DocumentOnLineCountChanged; + RunningPackage.BasePackage.StartupComplete -= BasePackageOnStartupComplete; + RunningPackage.BasePackage.Exited -= BasePackageOnExited; } private void BasePackageOnStartupComplete(object? sender, string url) @@ -123,4 +124,14 @@ public partial class RunningPackageViewModel : PageViewModelBase } } } + + public void Dispose() + { + Console.Dispose(); + } + + public async ValueTask DisposeAsync() + { + await Console.DisposeAsync(); + } } diff --git a/StabilityMatrix.Core/Helper/Factory/PackageFactory.cs b/StabilityMatrix.Core/Helper/Factory/PackageFactory.cs index 0e959e89..3e1ccee2 100644 --- a/StabilityMatrix.Core/Helper/Factory/PackageFactory.cs +++ b/StabilityMatrix.Core/Helper/Factory/PackageFactory.cs @@ -2,6 +2,7 @@ using StabilityMatrix.Core.Helper.Cache; using StabilityMatrix.Core.Models; using StabilityMatrix.Core.Models.Packages; +using StabilityMatrix.Core.Python; using StabilityMatrix.Core.Services; namespace StabilityMatrix.Core.Helper.Factory; @@ -13,6 +14,7 @@ public class PackageFactory : IPackageFactory private readonly ISettingsManager settingsManager; private readonly IDownloadService downloadService; private readonly IPrerequisiteHelper prerequisiteHelper; + private readonly IPyRunner pyRunner; /// /// Mapping of package.Name to package @@ -24,13 +26,15 @@ public class PackageFactory : IPackageFactory IGithubApiCache githubApiCache, ISettingsManager settingsManager, IDownloadService downloadService, - IPrerequisiteHelper prerequisiteHelper + IPrerequisiteHelper prerequisiteHelper, + IPyRunner pyRunner ) { this.githubApiCache = githubApiCache; this.settingsManager = settingsManager; this.downloadService = downloadService; this.prerequisiteHelper = prerequisiteHelper; + this.pyRunner = pyRunner; this.basePackages = basePackages.ToDictionary(x => x.Name); } @@ -44,7 +48,44 @@ public class PackageFactory : IPackageFactory => new A3WebUI(githubApiCache, settingsManager, downloadService, prerequisiteHelper), "Fooocus-ControlNet-SDXL" => new FocusControlNet(githubApiCache, settingsManager, downloadService, prerequisiteHelper), - _ => throw new ArgumentOutOfRangeException() + "Fooocus-MRE" + => new FooocusMre(githubApiCache, settingsManager, downloadService, prerequisiteHelper), + "InvokeAI" => new InvokeAI(githubApiCache, settingsManager, downloadService, prerequisiteHelper), + "kohya_ss" + => new KohyaSs( + githubApiCache, + settingsManager, + downloadService, + prerequisiteHelper, + pyRunner + ), + "OneTrainer" + => new OneTrainer(githubApiCache, settingsManager, downloadService, prerequisiteHelper), + "RuinedFooocus" + => new RuinedFooocus(githubApiCache, settingsManager, downloadService, prerequisiteHelper), + "stable-diffusion-webui-forge" + => new SDWebForge(githubApiCache, settingsManager, downloadService, prerequisiteHelper), + "stable-diffusion-webui-directml" + => new StableDiffusionDirectMl( + githubApiCache, + settingsManager, + downloadService, + prerequisiteHelper + ), + "stable-diffusion-webui-ux" + => new StableDiffusionUx( + githubApiCache, + settingsManager, + downloadService, + prerequisiteHelper + ), + "StableSwarmUI" + => new StableSwarm(githubApiCache, settingsManager, downloadService, prerequisiteHelper), + "automatic" + => new VladAutomatic(githubApiCache, settingsManager, downloadService, prerequisiteHelper), + "voltaML-fast-stable-diffusion" + => new VoltaML(githubApiCache, settingsManager, downloadService, prerequisiteHelper), + _ => throw new ArgumentOutOfRangeException(nameof(installedPackage)) }; } From 91fb39f3669e642c965bc4006ca6aea215f1426d Mon Sep 17 00:00:00 2001 From: JT Date: Wed, 28 Feb 2024 00:20:57 -0800 Subject: [PATCH 020/130] fix some process exit stuff --- .../Services/RunningPackageService.cs | 2 + .../PackageManager/PackageCardViewModel.cs | 60 +++++++++---------- .../ViewModels/RunningPackageViewModel.cs | 21 ++++--- 3 files changed, 43 insertions(+), 40 deletions(-) diff --git a/StabilityMatrix.Avalonia/Services/RunningPackageService.cs b/StabilityMatrix.Avalonia/Services/RunningPackageService.cs index d3cb114f..96f1d719 100644 --- a/StabilityMatrix.Avalonia/Services/RunningPackageService.cs +++ b/StabilityMatrix.Avalonia/Services/RunningPackageService.cs @@ -27,6 +27,7 @@ public partial class RunningPackageService( IPyRunner pyRunner ) : ObservableObject { + // 🤔 what if we put the ConsoleViewModel inside the BasePackage? 🤔 [ObservableProperty] private ObservableDictionary runningPackages = []; @@ -115,6 +116,7 @@ public partial class RunningPackageService( var viewModel = new RunningPackageViewModel( settingsManager, notificationService, + this, runningPackage, console ); diff --git a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs index 614fef81..8c68505c 100644 --- a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs @@ -37,7 +37,15 @@ namespace StabilityMatrix.Avalonia.ViewModels.PackageManager; [ManagedService] [Transient] -public partial class PackageCardViewModel : ProgressViewModel +public partial class PackageCardViewModel( + ILogger logger, + IPackageFactory packageFactory, + INotificationService notificationService, + ISettingsManager settingsManager, + INavigationService navigationService, + ServiceManager vmFactory, + RunningPackageService runningPackageService +) : ProgressViewModel { private string webUiUrl = string.Empty; @@ -86,36 +94,6 @@ public partial class PackageCardViewModel : ProgressViewModel [ObservableProperty] private bool showWebUiButton; - private readonly ILogger logger; - private readonly IPackageFactory packageFactory; - private readonly INotificationService notificationService; - private readonly ISettingsManager settingsManager; - private readonly INavigationService navigationService; - private readonly ServiceManager vmFactory; - private readonly RunningPackageService runningPackageService; - - /// - public PackageCardViewModel( - ILogger logger, - IPackageFactory packageFactory, - INotificationService notificationService, - ISettingsManager settingsManager, - INavigationService navigationService, - ServiceManager vmFactory, - RunningPackageService runningPackageService - ) - { - this.logger = logger; - this.packageFactory = packageFactory; - this.notificationService = notificationService; - this.settingsManager = settingsManager; - this.navigationService = navigationService; - this.vmFactory = vmFactory; - this.runningPackageService = runningPackageService; - - runningPackageService.RunningPackages.CollectionChanged += RunningPackagesOnCollectionChanged; - } - private void RunningPackagesOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { if ( @@ -167,6 +145,9 @@ public partial class PackageCardViewModel : ProgressViewModel UseSharedOutput = Package?.UseSharedOutputFolder ?? false; CanUseSharedOutput = basePackage?.SharedOutputFolders != null; CanUseExtensions = basePackage?.SupportsExtensions ?? false; + + runningPackageService.RunningPackages.CollectionChanged += RunningPackagesOnCollectionChanged; + EventManager.Instance.PackageRelaunchRequested += InstanceOnPackageRelaunchRequested; } } @@ -190,8 +171,6 @@ public partial class PackageCardViewModel : ProgressViewModel if (Design.IsDesignMode || !settingsManager.IsLibraryDirSet || Package is not { } currentPackage) return; - EventManager.Instance.PackageRelaunchRequested += InstanceOnPackageRelaunchRequested; - if ( packageFactory.FindPackageByName(currentPackage.PackageName) is { } basePackage @@ -217,12 +196,27 @@ public partial class PackageCardViewModel : ProgressViewModel } IsUpdateAvailable = await HasUpdate(); + + if ( + Package != null + && !IsRunning + && runningPackageService.RunningPackages.TryGetValue(Package.Id, out var runningPackageVm) + ) + { + IsRunning = true; + runningPackageVm.RunningPackage.BasePackage.Exited += BasePackageOnExited; + runningPackageVm.RunningPackage.BasePackage.StartupComplete += + RunningPackageOnStartupComplete; + webUiUrl = runningPackageVm.WebUiUrl; + ShowWebUiButton = !string.IsNullOrWhiteSpace(webUiUrl); + } } } public override void OnUnloaded() { EventManager.Instance.PackageRelaunchRequested -= InstanceOnPackageRelaunchRequested; + runningPackageService.RunningPackages.CollectionChanged -= RunningPackagesOnCollectionChanged; } public async Task Launch() diff --git a/StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs index 2758269f..3b879d95 100644 --- a/StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs @@ -20,7 +20,7 @@ namespace StabilityMatrix.Avalonia.ViewModels; public partial class RunningPackageViewModel : PageViewModelBase, IDisposable, IAsyncDisposable { private readonly INotificationService notificationService; - private string webUiUrl = string.Empty; + private readonly RunningPackageService runningPackageService; public PackagePair RunningPackage { get; } public ConsoleViewModel Console { get; } @@ -33,6 +33,9 @@ public partial class RunningPackageViewModel : PageViewModelBase, IDisposable, I [ObservableProperty] private bool showWebUiButton; + [ObservableProperty] + private string webUiUrl = string.Empty; + [ObservableProperty] private bool isRunning = true; @@ -40,11 +43,14 @@ public partial class RunningPackageViewModel : PageViewModelBase, IDisposable, I public RunningPackageViewModel( ISettingsManager settingsManager, INotificationService notificationService, + RunningPackageService runningPackageService, PackagePair runningPackage, ConsoleViewModel console ) { this.notificationService = notificationService; + this.runningPackageService = runningPackageService; + RunningPackage = runningPackage; Console = console; Console.Document.LineCountChanged += DocumentOnLineCountChanged; @@ -66,12 +72,13 @@ public partial class RunningPackageViewModel : PageViewModelBase, IDisposable, I Console.Document.LineCountChanged -= DocumentOnLineCountChanged; RunningPackage.BasePackage.StartupComplete -= BasePackageOnStartupComplete; RunningPackage.BasePackage.Exited -= BasePackageOnExited; + runningPackageService.RunningPackages.Remove(RunningPackage.InstalledPackage.Id); } private void BasePackageOnStartupComplete(object? sender, string url) { - webUiUrl = url.Replace("0.0.0.0", "127.0.0.1"); - ShowWebUiButton = !string.IsNullOrWhiteSpace(webUiUrl); + WebUiUrl = url.Replace("0.0.0.0", "127.0.0.1"); + ShowWebUiButton = !string.IsNullOrWhiteSpace(WebUiUrl); } private void DocumentOnLineCountChanged(object? sender, EventArgs e) @@ -91,7 +98,7 @@ public partial class RunningPackageViewModel : PageViewModelBase, IDisposable, I [RelayCommand] private async Task Stop() { - await RunningPackage.BasePackage.WaitForShutdown(); + await runningPackageService.StopPackage(RunningPackage.InstalledPackage.Id); Console.PostLine($"{Environment.NewLine}Stopped process at {DateTimeOffset.Now}"); await Console.StopUpdatesAsync(); IsRunning = false; @@ -100,13 +107,13 @@ public partial class RunningPackageViewModel : PageViewModelBase, IDisposable, I [RelayCommand] private void LaunchWebUi() { - if (string.IsNullOrEmpty(webUiUrl)) + if (string.IsNullOrEmpty(WebUiUrl)) return; notificationService.TryAsync( - Task.Run(() => ProcessRunner.OpenUrl(webUiUrl)), + Task.Run(() => ProcessRunner.OpenUrl(WebUiUrl)), "Failed to open URL", - $"{webUiUrl}" + $"{WebUiUrl}" ); } From 7e7b0148aa98671ae83ccc54a9c0c4455f5fd432 Mon Sep 17 00:00:00 2001 From: JT Date: Wed, 28 Feb 2024 00:22:08 -0800 Subject: [PATCH 021/130] "fix" tests --- .../Helper/PackageFactoryTests.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/StabilityMatrix.Tests/Helper/PackageFactoryTests.cs b/StabilityMatrix.Tests/Helper/PackageFactoryTests.cs index 4163922d..b4920bdb 100644 --- a/StabilityMatrix.Tests/Helper/PackageFactoryTests.cs +++ b/StabilityMatrix.Tests/Helper/PackageFactoryTests.cs @@ -8,31 +8,28 @@ public class PackageFactoryTests { private PackageFactory packageFactory = null!; private IEnumerable fakeBasePackages = null!; - + [TestInitialize] public void Setup() { - fakeBasePackages = new List - { - new DankDiffusion(null!, null!, null!, null!) - }; - packageFactory = new PackageFactory(fakeBasePackages); + fakeBasePackages = new List { new DankDiffusion(null!, null!, null!, null!) }; + packageFactory = new PackageFactory(fakeBasePackages, null, null, null, null, null); } - + [TestMethod] public void GetAllAvailablePackages_ReturnsAllPackages() { var result = packageFactory.GetAllAvailablePackages(); Assert.AreEqual(1, result.Count()); } - + [TestMethod] public void FindPackageByName_ReturnsPackage() { var result = packageFactory.FindPackageByName("dank-diffusion"); Assert.IsNotNull(result); } - + [TestMethod] public void FindPackageByName_ReturnsNull() { From e1816f71c6e09491e36383456c6c53e9ba1fdf35 Mon Sep 17 00:00:00 2001 From: Ionite Date: Thu, 29 Feb 2024 23:38:32 -0500 Subject: [PATCH 022/130] Add GetOptionalNodeOptionNames apis --- StabilityMatrix.Core/Inference/ComfyClient.cs | 15 +++++++++++++++ .../Models/Api/Comfy/ComfyInputInfo.cs | 17 ++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/StabilityMatrix.Core/Inference/ComfyClient.cs b/StabilityMatrix.Core/Inference/ComfyClient.cs index 72ae9a27..9e7ab6bf 100644 --- a/StabilityMatrix.Core/Inference/ComfyClient.cs +++ b/StabilityMatrix.Core/Inference/ComfyClient.cs @@ -460,6 +460,21 @@ public class ComfyClient : InferenceClientBase return info.Input.GetRequiredValueAsNestedList(optionName); } + /// + /// Get a list of strings representing available optional options of a given node + /// + public async Task?> GetOptionalNodeOptionNamesAsync( + string nodeName, + string optionName, + CancellationToken cancellationToken = default + ) + { + var response = await comfyApi.GetObjectInfo(nodeName, cancellationToken).ConfigureAwait(false); + + var info = response[nodeName]; + return info.Input.GetOptionalValueAsNestedList(optionName); + } + protected override void Dispose(bool disposing) { if (isDisposed) diff --git a/StabilityMatrix.Core/Models/Api/Comfy/ComfyInputInfo.cs b/StabilityMatrix.Core/Models/Api/Comfy/ComfyInputInfo.cs index ecd04374..60072ac4 100644 --- a/StabilityMatrix.Core/Models/Api/Comfy/ComfyInputInfo.cs +++ b/StabilityMatrix.Core/Models/Api/Comfy/ComfyInputInfo.cs @@ -9,13 +9,24 @@ public class ComfyInputInfo [JsonPropertyName("required")] public Dictionary? Required { get; set; } + [JsonPropertyName("optional")] + public Dictionary? Optional { get; set; } + public List? GetRequiredValueAsNestedList(string key) { var value = Required?[key]; - if (value is null) return null; - var nested = value.Deserialize>>(); - + var nested = value?.Deserialize>>(); + return nested?.SelectMany(x => x).ToList(); } + + public List? GetOptionalValueAsNestedList(string key) + { + var value = Optional?[key]; + + var nested = value?.Deserialize()?[0].Deserialize>(); + + return nested; + } } From a0221c7d91da9c21192323240866c2859e957966 Mon Sep 17 00:00:00 2001 From: Ionite Date: Thu, 29 Feb 2024 23:38:50 -0500 Subject: [PATCH 023/130] Add Inference_Core_AIO_Preprocessor support --- .../Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs b/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs index 286dff0b..f408dd96 100644 --- a/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs +++ b/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs @@ -342,6 +342,20 @@ public class ComfyNodeBuilder public bool LogPrompt { get; init; } } + [TypedNodeOptions( + Name = "Inference_Core_AIO_Preprocessor", + RequiredExtensions = ["https://github.com/LykosAI/ComfyUI-Inference-Core-Nodes"] + )] + public record AIOPreprocessor : ComfyTypedNodeBase + { + public required ImageNodeConnection Image { get; init; } + + public required string Preprocessor { get; init; } + + [Range(64, 2048)] + public int Resolution { get; init; } = 512; + } + public ImageNodeConnection Lambda_LatentToImage(LatentNodeConnection latent, VAENodeConnection vae) { var name = GetUniqueName("VAEDecode"); From 4cccde8a4b846b81f3669fef78352b4b5ed87a9e Mon Sep 17 00:00:00 2001 From: Ionite Date: Thu, 29 Feb 2024 23:39:18 -0500 Subject: [PATCH 024/130] Implement preprocessor support for ControlNet module --- .../Controls/Inference/ControlNetCard.axaml | 10 ++++----- .../DesignData/MockInferenceClientManager.cs | 3 +++ .../Services/IInferenceClientManager.cs | 1 + .../Services/InferenceClientManager.cs | 19 ++++++++++++++++ .../Inference/ControlNetCardViewModel.cs | 7 +----- .../Inference/Modules/ControlNetModule.cs | 22 +++++++++++++++---- 6 files changed, 46 insertions(+), 16 deletions(-) diff --git a/StabilityMatrix.Avalonia/Controls/Inference/ControlNetCard.axaml b/StabilityMatrix.Avalonia/Controls/Inference/ControlNetCard.axaml index b43ea05b..cb3b8567 100644 --- a/StabilityMatrix.Avalonia/Controls/Inference/ControlNetCard.axaml +++ b/StabilityMatrix.Avalonia/Controls/Inference/ControlNetCard.axaml @@ -47,18 +47,16 @@ DataContext="{Binding SelectImageCardViewModel}" FontSize="13" /> - + + Theme="{StaticResource FAComboBoxHybridModelTheme}"/> Schedulers { get; } = new ObservableCollectionExtended(ComfyScheduler.Defaults); + public IObservableCollection Preprocessors { get; } = + new ObservableCollectionExtended(); + [ObservableProperty] [NotifyPropertyChangedFor(nameof(CanUserConnect))] private bool isConnected; diff --git a/StabilityMatrix.Avalonia/Services/IInferenceClientManager.cs b/StabilityMatrix.Avalonia/Services/IInferenceClientManager.cs index fc134c74..c4b990b3 100644 --- a/StabilityMatrix.Avalonia/Services/IInferenceClientManager.cs +++ b/StabilityMatrix.Avalonia/Services/IInferenceClientManager.cs @@ -44,6 +44,7 @@ public interface IInferenceClientManager : IDisposable, INotifyPropertyChanged, IObservableCollection Samplers { get; } IObservableCollection Upscalers { get; } IObservableCollection Schedulers { get; } + IObservableCollection Preprocessors { get; } Task CopyImageToInputAsync(FilePath imageFile, CancellationToken cancellationToken = default); diff --git a/StabilityMatrix.Avalonia/Services/InferenceClientManager.cs b/StabilityMatrix.Avalonia/Services/InferenceClientManager.cs index 5ad092af..892031bf 100644 --- a/StabilityMatrix.Avalonia/Services/InferenceClientManager.cs +++ b/StabilityMatrix.Avalonia/Services/InferenceClientManager.cs @@ -101,6 +101,11 @@ public partial class InferenceClientManager : ObservableObject, IInferenceClient public IObservableCollection Schedulers { get; } = new ObservableCollectionExtended(); + public IObservableCollection Preprocessors { get; } = + new ObservableCollectionExtended(); + + private readonly SourceCache preprocessorsSource = new(p => p.GetId()); + public InferenceClientManager( ILogger logger, IApiFactory apiFactory, @@ -166,6 +171,8 @@ public partial class InferenceClientManager : ObservableObject, IInferenceClient schedulersSource.Connect().DeferUntilLoaded().Bind(Schedulers).Subscribe(); + preprocessorsSource.Connect().DeferUntilLoaded().Bind(Preprocessors).Subscribe(); + settingsManager.RegisterOnLibraryDirSet(_ => { Dispatcher.UIThread.Post(ResetSharedProperties, DispatcherPriority.Background); @@ -270,6 +277,18 @@ public partial class InferenceClientManager : ObservableObject, IInferenceClient }); logger.LogTrace("Loaded scheduler methods: {@Schedulers}", schedulerNames); } + + // Add preprocessor names from Inference_Core_AIO_Preprocessor node (might not exist if no extension) + if ( + await Client.GetOptionalNodeOptionNamesAsync("Inference_Core_AIO_Preprocessor", "preprocessor") is + { } preprocessorNames + ) + { + preprocessorsSource.EditDiff( + preprocessorNames.Select(HybridModelFile.FromRemote), + HybridModelFile.Comparer + ); + } } /// diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/ControlNetCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/ControlNetCardViewModel.cs index 496e90fc..3cc9fd8d 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/ControlNetCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/ControlNetCardViewModel.cs @@ -1,5 +1,4 @@ -using System; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; @@ -9,11 +8,7 @@ using StabilityMatrix.Avalonia.Services; using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.ViewModels.Dialogs; using StabilityMatrix.Core.Attributes; -using StabilityMatrix.Core.Extensions; -using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Models; -using StabilityMatrix.Core.Models.FileInterfaces; -using StabilityMatrix.Core.Services; namespace StabilityMatrix.Avalonia.ViewModels.Inference; diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ControlNetModule.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ControlNetModule.cs index 23260daf..f681f053 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ControlNetModule.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ControlNetModule.cs @@ -39,7 +39,7 @@ public class ControlNetModule : ModuleBase { var card = GetCard(); - var imageLoad = e.Nodes.AddTypedNode( + var image = e.Nodes.AddTypedNode( new ComfyNodeBuilder.LoadImage { Name = e.Nodes.GetUniqueName("ControlNet_LoadImage"), @@ -47,7 +47,21 @@ public class ControlNetModule : ModuleBase card.SelectImageCardViewModel.ImageSource?.GetHashGuidFileNameCached("Inference") ?? throw new ValidationException("No ImageSource") } - ); + ).Output1; + + if (card.SelectedPreprocessor is { } preprocessor && preprocessor.RelativePath != "none") + { + var aioPreprocessor = e.Nodes.AddTypedNode( + new ComfyNodeBuilder.AIOPreprocessor + { + Name = e.Nodes.GetUniqueName("ControlNet_Preprocessor"), + Image = image, + Preprocessor = preprocessor.RelativePath + } + ); + + image = aioPreprocessor.Output; + } var controlNetLoader = e.Nodes.AddTypedNode( new ComfyNodeBuilder.ControlNetLoader @@ -62,7 +76,7 @@ public class ControlNetModule : ModuleBase new ComfyNodeBuilder.ControlNetApplyAdvanced { Name = e.Nodes.GetUniqueName("ControlNetApply"), - Image = imageLoad.Output1, + Image = image, ControlNet = controlNetLoader.Output, Positive = e.Temp.Conditioning?.Positive ?? throw new ArgumentException("No Conditioning"), Negative = e.Temp.Conditioning?.Negative ?? throw new ArgumentException("No Conditioning"), @@ -81,7 +95,7 @@ public class ControlNetModule : ModuleBase new ComfyNodeBuilder.ControlNetApplyAdvanced { Name = e.Nodes.GetUniqueName("Refiner_ControlNetApply"), - Image = imageLoad.Output1, + Image = image, ControlNet = controlNetLoader.Output, Positive = e.Temp.RefinerConditioning.Positive, Negative = e.Temp.RefinerConditioning.Negative, From deb86fef8345bd7c65c3fdd319913f64e2556c84 Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 1 Mar 2024 16:42:21 -0500 Subject: [PATCH 025/130] Add ExtensionSpecifier parsing --- .../Attributes/TypedNodeOptionsAttribute.cs | 6 + .../Models/Api/Comfy/Nodes/NodeDictionary.cs | 13 ++- .../Packages/Extensions/ExtensionSpecifier.cs | 105 ++++++++++++++++++ 3 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 StabilityMatrix.Core/Models/Packages/Extensions/ExtensionSpecifier.cs diff --git a/StabilityMatrix.Core/Attributes/TypedNodeOptionsAttribute.cs b/StabilityMatrix.Core/Attributes/TypedNodeOptionsAttribute.cs index f496abc4..44d019fd 100644 --- a/StabilityMatrix.Core/Attributes/TypedNodeOptionsAttribute.cs +++ b/StabilityMatrix.Core/Attributes/TypedNodeOptionsAttribute.cs @@ -1,4 +1,5 @@ using StabilityMatrix.Core.Models.Api.Comfy.Nodes; +using StabilityMatrix.Core.Models.Packages.Extensions; namespace StabilityMatrix.Core.Attributes; @@ -11,4 +12,9 @@ public class TypedNodeOptionsAttribute : Attribute public string? Name { get; init; } public string[]? RequiredExtensions { get; init; } + + public IEnumerable GetRequiredExtensionSpecifiers() + { + return RequiredExtensions?.Select(ExtensionSpecifier.Parse) ?? Enumerable.Empty(); + } } diff --git a/StabilityMatrix.Core/Models/Api/Comfy/Nodes/NodeDictionary.cs b/StabilityMatrix.Core/Models/Api/Comfy/Nodes/NodeDictionary.cs index 5fbe4464..da088767 100644 --- a/StabilityMatrix.Core/Models/Api/Comfy/Nodes/NodeDictionary.cs +++ b/StabilityMatrix.Core/Models/Api/Comfy/Nodes/NodeDictionary.cs @@ -1,10 +1,12 @@ using System.ComponentModel; using System.Reflection; using System.Text.Json.Serialization; +using KGySoft.CoreLibraries; using OneOf; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Models.Api.Comfy.NodeTypes; +using StabilityMatrix.Core.Models.Packages.Extensions; namespace StabilityMatrix.Core.Models.Api.Comfy.Nodes; @@ -19,7 +21,10 @@ public class NodeDictionary : Dictionary /// When inserting TypedNodes, this holds a mapping of ClassType to required extensions /// [JsonIgnore] - public Dictionary ClassTypeRequiredExtensions { get; } = new(); + public Dictionary ClassTypeRequiredExtensions { get; } = new(); + + public IEnumerable RequiredExtensions => + ClassTypeRequiredExtensions.Values.SelectMany(x => x); /// /// Finds a unique node name given a base name, by appending _2, _3, etc. @@ -63,7 +68,11 @@ public class NodeDictionary : Dictionary { if (options.RequiredExtensions != null) { - ClassTypeRequiredExtensions[namedNode.ClassType] = options.RequiredExtensions; + ClassTypeRequiredExtensions.AddOrUpdate( + namedNode.ClassType, + _ => options.GetRequiredExtensionSpecifiers().ToArray(), + (_, specifiers) => options.GetRequiredExtensionSpecifiers().Concat(specifiers).ToArray() + ); } } diff --git a/StabilityMatrix.Core/Models/Packages/Extensions/ExtensionSpecifier.cs b/StabilityMatrix.Core/Models/Packages/Extensions/ExtensionSpecifier.cs new file mode 100644 index 00000000..13003ca0 --- /dev/null +++ b/StabilityMatrix.Core/Models/Packages/Extensions/ExtensionSpecifier.cs @@ -0,0 +1,105 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; +using JetBrains.Annotations; +using Semver; +using StabilityMatrix.Core.Processes; + +namespace StabilityMatrix.Core.Models.Packages.Extensions; + +/// +/// Extension specifier with optional version constraints. +/// +[PublicAPI] +public partial class ExtensionSpecifier +{ + public required string Name { get; init; } + + public string? Constraint { get; init; } + + public string? Version { get; init; } + + public string? VersionConstraint => Constraint is null || Version is null ? null : Constraint + Version; + + public bool TryGetSemVersionRange([NotNullWhen(true)] out SemVersionRange? semVersionRange) + { + if (!string.IsNullOrEmpty(VersionConstraint)) + { + return SemVersionRange.TryParse( + VersionConstraint, + SemVersionRangeOptions.Loose, + out semVersionRange + ); + } + + semVersionRange = null; + return false; + } + + public static ExtensionSpecifier Parse(string value) + { + TryParse(value, true, out var packageSpecifier); + + return packageSpecifier!; + } + + public static bool TryParse(string value, [NotNullWhen(true)] out ExtensionSpecifier? extensionSpecifier) + { + return TryParse(value, false, out extensionSpecifier); + } + + private static bool TryParse( + string value, + bool throwOnFailure, + [NotNullWhen(true)] out ExtensionSpecifier? packageSpecifier + ) + { + var match = ExtensionSpecifierRegex().Match(value); + if (!match.Success) + { + if (throwOnFailure) + { + throw new ArgumentException($"Invalid extension specifier: {value}"); + } + + packageSpecifier = null; + return false; + } + + packageSpecifier = new ExtensionSpecifier + { + Name = match.Groups["extension_name"].Value, + Constraint = match.Groups["version_constraint"].Value, + Version = match.Groups["version"].Value + }; + + return true; + } + + /// + public override string ToString() + { + return Name + VersionConstraint; + } + + public static implicit operator Argument(ExtensionSpecifier specifier) + { + return specifier.VersionConstraint is null + ? new Argument(specifier.Name) + : new Argument((specifier.Name, specifier.VersionConstraint)); + } + + public static implicit operator ExtensionSpecifier(string specifier) + { + return Parse(specifier); + } + + /// + /// Regex to match a pip package specifier. + /// + [GeneratedRegex( + @"(?\S+)\s*(?==|>=|<=|>|<|~=|!=)?\s*(?[a-zA-Z0-9_.]+)?", + RegexOptions.CultureInvariant, + 5000 + )] + private static partial Regex ExtensionSpecifierRegex(); +} From 3653fb475e38a17dd5162d86edbd963ae67e788b Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 1 Mar 2024 16:43:27 -0500 Subject: [PATCH 026/130] Fix missing node causing connection error --- StabilityMatrix.Core/Inference/ComfyClient.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/StabilityMatrix.Core/Inference/ComfyClient.cs b/StabilityMatrix.Core/Inference/ComfyClient.cs index 9e7ab6bf..3f3b894e 100644 --- a/StabilityMatrix.Core/Inference/ComfyClient.cs +++ b/StabilityMatrix.Core/Inference/ComfyClient.cs @@ -471,8 +471,9 @@ public class ComfyClient : InferenceClientBase { var response = await comfyApi.GetObjectInfo(nodeName, cancellationToken).ConfigureAwait(false); - var info = response[nodeName]; - return info.Input.GetOptionalValueAsNestedList(optionName); + var info = response.GetValueOrDefault(nodeName); + + return info?.Input.GetOptionalValueAsNestedList(optionName); } protected override void Dispose(bool disposing) From ecc3b7da2dc38ae1bdee70198ea0f95833627157 Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 1 Mar 2024 16:43:51 -0500 Subject: [PATCH 027/130] Add extension manager update installed version info function --- .../Extensions/GitPackageExtensionManager.cs | 25 +++++++++++++++++++ .../Extensions/IPackageExtensionManager.cs | 7 ++++++ 2 files changed, 32 insertions(+) diff --git a/StabilityMatrix.Core/Models/Packages/Extensions/GitPackageExtensionManager.cs b/StabilityMatrix.Core/Models/Packages/Extensions/GitPackageExtensionManager.cs index 1aac49f1..3cc5e17b 100644 --- a/StabilityMatrix.Core/Models/Packages/Extensions/GitPackageExtensionManager.cs +++ b/StabilityMatrix.Core/Models/Packages/Extensions/GitPackageExtensionManager.cs @@ -201,6 +201,31 @@ public abstract partial class GitPackageExtensionManager(IPrerequisiteHelper pre return extensions; } + public virtual async Task GetInstalledExtensionInfoAsync( + InstalledPackageExtension installedExtension + ) + { + if (installedExtension.PrimaryPath is not DirectoryPath extensionDirectory) + { + return installedExtension; + } + + // Get git version + var version = await prerequisiteHelper + .GetGitRepositoryVersion(extensionDirectory) + .ConfigureAwait(false); + + return installedExtension with + { + Version = new PackageExtensionVersion + { + Tag = version.Tag, + Branch = version.Branch, + CommitSha = version.CommitSha + } + }; + } + /// public virtual async Task InstallExtensionAsync( PackageExtension extension, diff --git a/StabilityMatrix.Core/Models/Packages/Extensions/IPackageExtensionManager.cs b/StabilityMatrix.Core/Models/Packages/Extensions/IPackageExtensionManager.cs index 76903233..45f56017 100644 --- a/StabilityMatrix.Core/Models/Packages/Extensions/IPackageExtensionManager.cs +++ b/StabilityMatrix.Core/Models/Packages/Extensions/IPackageExtensionManager.cs @@ -89,6 +89,13 @@ public interface IPackageExtensionManager CancellationToken cancellationToken = default ); + /// + /// Get updated info (version) for an installed extension. + /// + Task GetInstalledExtensionInfoAsync( + InstalledPackageExtension installedExtension + ); + /// /// Install an extension to the provided package. /// From 2303759eeccebe5a5ee69c1b6b00623347d168ca Mon Sep 17 00:00:00 2001 From: JT Date: Sat, 2 Mar 2024 03:44:35 -0800 Subject: [PATCH 028/130] redesigned packages page & made it the first tab & removed old launch page from tabs --- StabilityMatrix.Avalonia/App.axaml.cs | 3 +- .../Styles/ButtonStyles.axaml | 73 +++ .../CheckpointManager/CheckpointFolder.cs | 12 +- .../PackageManager/PackageCardViewModel.cs | 73 ++- .../ViewModels/RunningPackageViewModel.cs | 11 +- .../Views/ConsoleOutputPage.axaml | 8 +- .../PackageInstallBrowserView.axaml | 276 ++++---- .../Views/PackageManagerPage.axaml | 607 +++++++++--------- .../Models/DownloadPackageVersionOptions.cs | 3 + 9 files changed, 621 insertions(+), 445 deletions(-) diff --git a/StabilityMatrix.Avalonia/App.axaml.cs b/StabilityMatrix.Avalonia/App.axaml.cs index f1c10ddd..014d25d8 100644 --- a/StabilityMatrix.Avalonia/App.axaml.cs +++ b/StabilityMatrix.Avalonia/App.axaml.cs @@ -328,9 +328,8 @@ public sealed class App : Application { Pages = { - provider.GetRequiredService(), - provider.GetRequiredService(), provider.GetRequiredService(), + provider.GetRequiredService(), provider.GetRequiredService(), provider.GetRequiredService(), provider.GetRequiredService() diff --git a/StabilityMatrix.Avalonia/Styles/ButtonStyles.axaml b/StabilityMatrix.Avalonia/Styles/ButtonStyles.axaml index b0af1750..0b8d75f8 100644 --- a/StabilityMatrix.Avalonia/Styles/ButtonStyles.axaml +++ b/StabilityMatrix.Avalonia/Styles/ButtonStyles.axaml @@ -99,6 +99,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + - - - - - - - - - - - + + + - - - - - - - - - + + + + + + + + + + diff --git a/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml b/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml index d1460a42..999625ac 100644 --- a/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml +++ b/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml @@ -12,12 +12,13 @@ xmlns:system="clr-namespace:System;assembly=System.Runtime" xmlns:converters="clr-namespace:StabilityMatrix.Avalonia.Converters" xmlns:avalonia="clr-namespace:SpacedGridControl.Avalonia;assembly=SpacedGridControl.Avalonia" - mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" + xmlns:markupExtensions="clr-namespace:StabilityMatrix.Avalonia.MarkupExtensions" + mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="450" x:DataType="viewModels:PackageManagerViewModel" x:CompileBindings="True" d:DataContext="{x:Static designData:DesignData.PackageManagerViewModel}" x:Class="StabilityMatrix.Avalonia.Views.PackageManagerPage"> - + @@ -32,216 +33,34 @@ - - + + - - - - - - - - - - - - + - - @@ -252,114 +71,328 @@ TextWrapping="Wrap" Text="{x:Static lang:Resources.Label_UnknownPackage}" /> + + + + + - - - - + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + IsVisible="{Binding IsProgressVisible}" + RowDefinitions="Auto, *"> + !string.IsNullOrWhiteSpace(VersionTag) ? VersionTag : $"{BranchName}@{CommitHash?[..7]}"; } From d329f37e9d5cc66c893eba3cbfc394890d205b8e Mon Sep 17 00:00:00 2001 From: Ionite Date: Sat, 2 Mar 2024 14:55:21 -0500 Subject: [PATCH 029/130] Add version requirements for nodes --- .../Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs b/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs index f408dd96..b54547f4 100644 --- a/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs +++ b/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs @@ -332,7 +332,7 @@ public class ComfyNodeBuilder [TypedNodeOptions( Name = "Inference_Core_PromptExpansion", - RequiredExtensions = ["https://github.com/LykosAI/ComfyUI-Inference-Core-Nodes"] + RequiredExtensions = ["https://github.com/LykosAI/ComfyUI-Inference-Core-Nodes >= 0.2.0"] )] public record PromptExpansion : ComfyTypedNodeBase { @@ -344,7 +344,7 @@ public class ComfyNodeBuilder [TypedNodeOptions( Name = "Inference_Core_AIO_Preprocessor", - RequiredExtensions = ["https://github.com/LykosAI/ComfyUI-Inference-Core-Nodes"] + RequiredExtensions = ["https://github.com/LykosAI/ComfyUI-Inference-Core-Nodes >= 0.2.0"] )] public record AIOPreprocessor : ComfyTypedNodeBase { From 96c30f5d407f18abb291b999021ea85a0ff32f93 Mon Sep 17 00:00:00 2001 From: Ionite Date: Sat, 2 Mar 2024 14:56:07 -0500 Subject: [PATCH 030/130] Add out of date extension checks --- .../Base/InferenceGenerationViewModelBase.cs | 69 +++++++++++++++---- 1 file changed, 55 insertions(+), 14 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Base/InferenceGenerationViewModelBase.cs b/StabilityMatrix.Avalonia/ViewModels/Base/InferenceGenerationViewModelBase.cs index 2d70b02e..63622a72 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Base/InferenceGenerationViewModelBase.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Base/InferenceGenerationViewModelBase.cs @@ -17,9 +17,9 @@ using CommunityToolkit.Mvvm.Input; using ExifLibrary; using FluentAvalonia.UI.Controls; using Microsoft.Extensions.DependencyInjection; -using Nito.Disposables.Internals; using NLog; using Refit; +using Semver; using SkiaSharp; using StabilityMatrix.Avalonia.Extensions; using StabilityMatrix.Avalonia.Helpers; @@ -643,12 +643,10 @@ public abstract partial class InferenceGenerationViewModelBase { // Get prompt required extensions // Just static for now but could do manifest lookup when we support custom workflows - var requiredExtensions = nodeDictionary - .ClassTypeRequiredExtensions.Values.SelectMany(x => x) - .ToHashSet(); + var requiredExtensionSpecifiers = nodeDictionary.RequiredExtensions.ToList(); // Skip if no extensions required - if (requiredExtensions.Count == 0) + if (requiredExtensionSpecifiers.Count == 0) { return true; } @@ -661,20 +659,63 @@ public abstract partial class InferenceGenerationViewModelBase await ((GitPackageExtensionManager)manager).GetInstalledExtensionsLiteAsync( localPackagePair.InstalledPackage ) - ).ToImmutableArray(); + ).ToList(); + + var localExtensionsByGitUrl = localExtensions + .Where(ext => ext.GitRepositoryUrl is not null) + .ToDictionary(ext => ext.GitRepositoryUrl!, ext => ext); + + var requiredExtensionReferences = requiredExtensionSpecifiers + .Select(specifier => specifier.Name) + .ToHashSet(); + + var missingExtensions = new List(); + var outOfDateExtensions = + new List<(ExtensionSpecifier Specifier, InstalledPackageExtension Installed)>(); + + // Check missing extensions and out of date extensions + foreach (var specifier in requiredExtensionSpecifiers) + { + if (!localExtensionsByGitUrl.TryGetValue(specifier.Name, out var localExtension)) + { + missingExtensions.Add(specifier); + continue; + } - var missingExtensions = requiredExtensions - .Except(localExtensions.Select(ext => ext.GitRepositoryUrl).WhereNotNull()) - .ToImmutableArray(); + // Check if constraint is specified + if (specifier.Constraint is not null && specifier.TryGetSemVersionRange(out var semVersionRange)) + { + // Get version to compare + localExtension = await manager.GetInstalledExtensionInfoAsync(localExtension); + + // Try to parse local tag to semver + if ( + localExtension.Version?.Tag is not null + && SemVersion.TryParse( + localExtension.Version.Tag, + SemVersionStyles.AllowV, + out var localSemVersion + ) + ) + { + // Check if not satisfied + if (!semVersionRange.Contains(localSemVersion)) + { + outOfDateExtensions.Add((specifier, localExtension)); + } + } + } + } - if (missingExtensions.Length == 0) + if (missingExtensions.Count == 0 && outOfDateExtensions.Count == 0) { return true; } var dialog = DialogHelper.CreateMarkdownDialog( $"#### The following extensions are required for this workflow:\n" - + $"{string.Join("\n- ", missingExtensions)}", + + $"{string.Join("\n- ", missingExtensions.Select(ext => ext.Name))}" + + $"{string.Join("\n- ", outOfDateExtensions.Select(pair => $"{pair.Item1.Name} {pair.Specifier.Constraint} {pair.Specifier.Version} (Current Version: {pair.Installed.Version?.Tag})"))}", "Install Required Extensions?" ); @@ -692,13 +733,13 @@ public abstract partial class InferenceGenerationViewModelBase var steps = new List(); - foreach (var missingExtensionUrl in missingExtensions) + foreach (var missingExtension in missingExtensions) { - if (!manifestExtensionsMap.TryGetValue(missingExtensionUrl, out var extension)) + if (!manifestExtensionsMap.TryGetValue(missingExtension.Name, out var extension)) { Logger.Warn( "Extension {MissingExtensionUrl} not found in manifests", - missingExtensionUrl + missingExtension.Name ); continue; } From 842ab35e7d9f9eef0e39d393ff97142618f2f4c3 Mon Sep 17 00:00:00 2001 From: Ionite Date: Sat, 2 Mar 2024 22:41:57 -0500 Subject: [PATCH 031/130] Add Inference ControlNet preprocessor dimensions --- .../Controls/Inference/ControlNetCard.axaml | 43 ++++++++++++++++++- .../Inference/ControlNetCardViewModel.cs | 26 ++++++++++- .../Inference/Modules/ControlNetModule.cs | 4 +- 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/StabilityMatrix.Avalonia/Controls/Inference/ControlNetCard.axaml b/StabilityMatrix.Avalonia/Controls/Inference/ControlNetCard.axaml index cb3b8567..b376b780 100644 --- a/StabilityMatrix.Avalonia/Controls/Inference/ControlNetCard.axaml +++ b/StabilityMatrix.Avalonia/Controls/Inference/ControlNetCard.axaml @@ -36,12 +36,12 @@ + + + + + + + + (); + + // Update our width and height when the image changes + SelectImageCardViewModel + .WhenPropertyChanged(card => card.CurrentBitmapSize) + .Subscribe(propertyValue => + { + if (!propertyValue.Value.IsEmpty) + { + Width = propertyValue.Value.Width; + Height = propertyValue.Value.Height; + } + }); } [RelayCommand] diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ControlNetModule.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ControlNetModule.cs index f681f053..e4845fc6 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ControlNetModule.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ControlNetModule.cs @@ -56,7 +56,9 @@ public class ControlNetModule : ModuleBase { Name = e.Nodes.GetUniqueName("ControlNet_Preprocessor"), Image = image, - Preprocessor = preprocessor.RelativePath + Preprocessor = preprocessor.RelativePath, + // Use width if valid, else default of 512 + Resolution = card.Width is <= 2048 and > 0 ? card.Width : 512 } ); From 8f53c281b3abde9fcc061881443429f9addf921b Mon Sep 17 00:00:00 2001 From: JT Date: Sun, 3 Mar 2024 02:25:49 -0800 Subject: [PATCH 032/130] updated one-click stuff & confirm exit dialog for multi-package --- .../Languages/Resources.Designer.cs | 36 +++++++++++++++ .../Languages/Resources.resx | 12 +++++ .../Base/InferenceGenerationViewModelBase.cs | 12 ++--- .../Base/InferenceTabViewModelBase.cs | 16 ++----- .../InferenceImageToImageViewModel.cs | 20 +++++--- .../InferenceImageToVideoViewModel.cs | 5 +- .../InferenceImageUpscaleViewModel.cs | 15 ++++-- .../InferenceTextToImageViewModel.cs | 5 +- .../ViewModels/MainWindowViewModel.cs | 8 ---- .../ViewModels/PackageManagerViewModel.cs | 12 ++--- .../Views/MainWindow.axaml.cs | 46 +++++++++++++++++-- .../Views/PackageManagerPage.axaml | 26 ++++++++++- .../Views/PackageManagerPage.axaml.cs | 33 ++++++++++++- 13 files changed, 194 insertions(+), 52 deletions(-) diff --git a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs index c56d773b..4802a3a5 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs +++ b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs @@ -959,6 +959,24 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to Confirm Exit. + /// + public static string Label_ConfirmExit { + get { + return ResourceManager.GetString("Label_ConfirmExit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to exit? This will also close any currently running packages.. + /// + public static string Label_ConfirmExitDetail { + get { + return ResourceManager.GetString("Label_ConfirmExitDetail", resourceCulture); + } + } + /// /// Looks up a localized string similar to Confirm Password. /// @@ -1004,6 +1022,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to Console. + /// + public static string Label_Console { + get { + return ResourceManager.GetString("Label_Console", resourceCulture); + } + } + /// /// Looks up a localized string similar to This will move all generated images from the selected packages to the Consolidated directory of the shared outputs folder. This action cannot be undone.. /// @@ -2399,6 +2426,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to Web UI. + /// + public static string Label_WebUi { + get { + return ResourceManager.GetString("Label_WebUi", resourceCulture); + } + } + /// /// Looks up a localized string similar to Width. /// diff --git a/StabilityMatrix.Avalonia/Languages/Resources.resx b/StabilityMatrix.Avalonia/Languages/Resources.resx index a2129eaf..83430fee 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.resx @@ -978,4 +978,16 @@ Auto-Scroll to End + + Confirm Exit + + + Are you sure you want to exit? This will also close any currently running packages. + + + Console + + + Web UI + diff --git a/StabilityMatrix.Avalonia/ViewModels/Base/InferenceGenerationViewModelBase.cs b/StabilityMatrix.Avalonia/ViewModels/Base/InferenceGenerationViewModelBase.cs index 2d70b02e..9fe9cb40 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Base/InferenceGenerationViewModelBase.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Base/InferenceGenerationViewModelBase.cs @@ -59,6 +59,7 @@ public abstract partial class InferenceGenerationViewModelBase private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private readonly ISettingsManager settingsManager; + private readonly RunningPackageService runningPackageService; private readonly INotificationService notificationService; private readonly ServiceManager vmFactory; @@ -79,12 +80,14 @@ public abstract partial class InferenceGenerationViewModelBase ServiceManager vmFactory, IInferenceClientManager inferenceClientManager, INotificationService notificationService, - ISettingsManager settingsManager + ISettingsManager settingsManager, + RunningPackageService runningPackageService ) : base(notificationService) { this.notificationService = notificationService; this.settingsManager = settingsManager; + this.runningPackageService = runningPackageService; this.vmFactory = vmFactory; ClientManager = inferenceClientManager; @@ -722,15 +725,12 @@ public abstract partial class InferenceGenerationViewModelBase return; // Restart Package - // TODO: This should be handled by some DI package manager service - var launchPage = App.Services.GetRequiredService(); - try { await Dispatcher.UIThread.InvokeAsync(async () => { - await launchPage.Stop(); - await launchPage.LaunchAsync(); + await runningPackageService.StopPackage(localPackagePair.InstalledPackage.Id); + await runningPackageService.StartPackage(localPackagePair.InstalledPackage); }); } catch (Exception e) diff --git a/StabilityMatrix.Avalonia/ViewModels/Base/InferenceTabViewModelBase.cs b/StabilityMatrix.Avalonia/ViewModels/Base/InferenceTabViewModelBase.cs index 63e5b131..57e6bf47 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Base/InferenceTabViewModelBase.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Base/InferenceTabViewModelBase.cs @@ -98,11 +98,7 @@ public abstract partial class InferenceTabViewModelBase protected async Task SaveViewState() { var eventArgs = new SaveViewStateEventArgs(); - saveViewStateRequestedEventManager?.RaiseEvent( - this, - eventArgs, - nameof(SaveViewStateRequested) - ); + saveViewStateRequestedEventManager?.RaiseEvent(this, eventArgs, nameof(SaveViewStateRequested)); if (eventArgs.StateTask is not { } stateTask) { @@ -128,7 +124,7 @@ public abstract partial class InferenceTabViewModelBase // TODO: Dock reset not working, using this hack for now to get a new view var navService = App.Services.GetRequiredService>(); - navService.NavigateTo(new SuppressNavigationTransitionInfo()); + navService.NavigateTo(new SuppressNavigationTransitionInfo()); ((IPersistentViewProvider)this).AttachedPersistentView = null; navService.NavigateTo(new BetterEntranceNavigationTransition()); } @@ -157,9 +153,7 @@ public abstract partial class InferenceTabViewModelBase if (result == ContentDialogResult.Primary && textFields[0].Text is { } json) { - LoadViewState( - new LoadViewStateEventArgs { State = new ViewState { DockLayout = json } } - ); + LoadViewState(new LoadViewStateEventArgs { State = new ViewState { DockLayout = json } }); } } @@ -226,9 +220,7 @@ public abstract partial class InferenceTabViewModelBase if (this is IParametersLoadableState paramsLoadableVm) { - Dispatcher.UIThread.Invoke( - () => paramsLoadableVm.LoadStateFromParameters(parameters) - ); + Dispatcher.UIThread.Invoke(() => paramsLoadableVm.LoadStateFromParameters(parameters)); } else { diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceImageToImageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceImageToImageViewModel.cs index 50c4734f..77c5230f 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceImageToImageViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceImageToImageViewModel.cs @@ -27,9 +27,17 @@ public class InferenceImageToImageViewModel : InferenceTextToImageViewModel IInferenceClientManager inferenceClientManager, INotificationService notificationService, ISettingsManager settingsManager, - IModelIndexService modelIndexService + IModelIndexService modelIndexService, + RunningPackageService runningPackageService ) - : base(notificationService, inferenceClientManager, settingsManager, vmFactory, modelIndexService) + : base( + notificationService, + inferenceClientManager, + settingsManager, + vmFactory, + modelIndexService, + runningPackageService + ) { SelectImageCardViewModel = vmFactory.Get(); @@ -77,12 +85,12 @@ public class InferenceImageToImageViewModel : InferenceTextToImageViewModel var mainImages = SelectImageCardViewModel.GetInputImages(); var samplerImages = SamplerCardViewModel - .ModulesCardViewModel - .Cards - .OfType() + .ModulesCardViewModel.Cards.OfType() .SelectMany(m => m.GetInputImages()); - var moduleImages = ModulesCardViewModel.Cards.OfType().SelectMany(m => m.GetInputImages()); + var moduleImages = ModulesCardViewModel + .Cards.OfType() + .SelectMany(m => m.GetInputImages()); return mainImages.Concat(samplerImages).Concat(moduleImages); } diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceImageToVideoViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceImageToVideoViewModel.cs index b57e3406..035774c1 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceImageToVideoViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceImageToVideoViewModel.cs @@ -65,9 +65,10 @@ public partial class InferenceImageToVideoViewModel IInferenceClientManager inferenceClientManager, ISettingsManager settingsManager, ServiceManager vmFactory, - IModelIndexService modelIndexService + IModelIndexService modelIndexService, + RunningPackageService runningPackageService ) - : base(vmFactory, inferenceClientManager, notificationService, settingsManager) + : base(vmFactory, inferenceClientManager, notificationService, settingsManager, runningPackageService) { this.notificationService = notificationService; this.modelIndexService = modelIndexService; diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceImageUpscaleViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceImageUpscaleViewModel.cs index 2ca1dbd8..540de0d9 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceImageUpscaleViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceImageUpscaleViewModel.cs @@ -59,9 +59,10 @@ public class InferenceImageUpscaleViewModel : InferenceGenerationViewModelBase INotificationService notificationService, IInferenceClientManager inferenceClientManager, ISettingsManager settingsManager, - ServiceManager vmFactory + ServiceManager vmFactory, + RunningPackageService runningPackageService ) - : base(vmFactory, inferenceClientManager, notificationService, settingsManager) + : base(vmFactory, inferenceClientManager, notificationService, settingsManager, runningPackageService) { this.notificationService = notificationService; @@ -142,7 +143,10 @@ public class InferenceImageUpscaleViewModel : InferenceGenerationViewModelBase } /// - protected override async Task GenerateImageImpl(GenerateOverrides overrides, CancellationToken cancellationToken) + protected override async Task GenerateImageImpl( + GenerateOverrides overrides, + CancellationToken cancellationToken + ) { if (!ClientManager.IsConnected) { @@ -169,7 +173,10 @@ public class InferenceImageUpscaleViewModel : InferenceGenerationViewModelBase Client = ClientManager.Client, Nodes = buildPromptArgs.Builder.ToNodeDictionary(), OutputNodeNames = buildPromptArgs.Builder.Connections.OutputNodeNames.ToArray(), - Parameters = new GenerationParameters { ModelName = UpscalerCardViewModel.SelectedUpscaler?.Name, }, + Parameters = new GenerationParameters + { + ModelName = UpscalerCardViewModel.SelectedUpscaler?.Name, + }, Project = InferenceProjectDocument.FromLoadable(this) }; diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceTextToImageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceTextToImageViewModel.cs index 12d0d5fc..a1924559 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceTextToImageViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceTextToImageViewModel.cs @@ -59,9 +59,10 @@ public class InferenceTextToImageViewModel : InferenceGenerationViewModelBase, I IInferenceClientManager inferenceClientManager, ISettingsManager settingsManager, ServiceManager vmFactory, - IModelIndexService modelIndexService + IModelIndexService modelIndexService, + RunningPackageService runningPackageService ) - : base(vmFactory, inferenceClientManager, notificationService, settingsManager) + : base(vmFactory, inferenceClientManager, notificationService, settingsManager, runningPackageService) { this.notificationService = notificationService; this.modelIndexService = modelIndexService; diff --git a/StabilityMatrix.Avalonia/ViewModels/MainWindowViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/MainWindowViewModel.cs index 5aec5637..718cd574 100644 --- a/StabilityMatrix.Avalonia/ViewModels/MainWindowViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/MainWindowViewModel.cs @@ -139,14 +139,6 @@ public partial class MainWindowViewModel : ViewModelBase Content = new NewOneClickInstallDialog { DataContext = viewModel }, }; - EventManager.Instance.OneClickInstallFinished += (_, skipped) => - { - if (skipped) - return; - - EventManager.Instance.OnTeachingTooltipNeeded(); - }; - var firstDialogResult = await dialog.ShowAsync(App.TopLevel); if (firstDialogResult != ContentDialogResult.Primary) diff --git a/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs index 6066fd02..df3c33a8 100644 --- a/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs @@ -6,8 +6,6 @@ using System.Linq; using System.Threading.Tasks; using AsyncAwaitBestPractices; using Avalonia.Controls; -using Avalonia.Controls.Notifications; -using Avalonia.Controls.Primitives; using Avalonia.Threading; using CommunityToolkit.Mvvm.Input; using DynamicData; @@ -15,18 +13,14 @@ using DynamicData.Binding; using FluentAvalonia.UI.Controls; using Microsoft.Extensions.Logging; using StabilityMatrix.Avalonia.Animations; -using StabilityMatrix.Avalonia.Controls; using StabilityMatrix.Avalonia.Services; using StabilityMatrix.Avalonia.ViewModels.Base; -using StabilityMatrix.Avalonia.ViewModels.Dialogs; using StabilityMatrix.Avalonia.ViewModels.PackageManager; using StabilityMatrix.Avalonia.Views; -using StabilityMatrix.Avalonia.Views.Dialogs; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Models; using StabilityMatrix.Core.Models.FileInterfaces; -using StabilityMatrix.Core.Models.PackageModification; using StabilityMatrix.Core.Models.Packages; using StabilityMatrix.Core.Services; using Symbol = FluentIcons.Common.Symbol; @@ -84,6 +78,7 @@ public partial class PackageManagerViewModel : PageViewModelBase this.logger = logger; EventManager.Instance.InstalledPackagesChanged += OnInstalledPackagesChanged; + EventManager.Instance.OneClickInstallFinished += OnOneClickInstallFinished; var installed = installedPackages.Connect(); var unknown = unknownInstalledPackages.Connect(); @@ -107,6 +102,11 @@ public partial class PackageManagerViewModel : PageViewModelBase timer.Tick += async (_, _) => await CheckPackagesForUpdates(); } + private void OnOneClickInstallFinished(object? sender, bool e) + { + OnLoadedAsync().SafeFireAndForget(); + } + public void SetPackages(IEnumerable packages) { installedPackages.Edit(s => s.Load(packages)); diff --git a/StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs b/StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs index 614754a6..c720f37b 100644 --- a/StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs +++ b/StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Reactive.Linq; using System.Threading; +using AsyncAwaitBestPractices; using AsyncImageLoader; using Avalonia; using Avalonia.Controls; @@ -193,13 +194,52 @@ public partial class MainWindow : AppWindowBase protected override void OnClosing(WindowClosingEventArgs e) { // Show confirmation if package running - var launchPageViewModel = App.Services.GetRequiredService(); - - launchPageViewModel.OnMainWindowClosing(e); + var runningPackageService = App.Services.GetRequiredService(); + if ( + runningPackageService.RunningPackages.Count > 0 + && e.CloseReason is WindowCloseReason.WindowClosing + ) + { + e.Cancel = true; + + var dialog = CreateExitConfirmDialog(); + Dispatcher + .UIThread.InvokeAsync(async () => + { + if ( + (TaskDialogStandardResult)await dialog.ShowAsync(true) == TaskDialogStandardResult.Yes + ) + { + App.Services.GetRequiredService().Hide(); + App.Shutdown(); + } + }) + .SafeFireAndForget(); + } base.OnClosing(e); } + private static TaskDialog CreateExitConfirmDialog() + { + var dialog = DialogHelper.CreateTaskDialog( + Languages.Resources.Label_ConfirmExit, + Languages.Resources.Label_ConfirmExitDetail + ); + + dialog.ShowProgressBar = false; + dialog.FooterVisibility = TaskDialogFooterVisibility.Never; + + dialog.Buttons = new List + { + new("Exit", TaskDialogStandardResult.Yes), + TaskDialogButton.CancelButton + }; + dialog.Buttons[0].IsDefault = true; + + return dialog; + } + /// protected override void OnClosed(EventArgs e) { diff --git a/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml b/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml index 999625ac..81c912e6 100644 --- a/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml +++ b/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml @@ -26,6 +26,7 @@ @@ -284,6 +285,17 @@ + + + - + - - - - + @@ -161,7 +136,8 @@ - + - + From d5f726d9d66ef7c0874162115dba2f60223cbba1 Mon Sep 17 00:00:00 2001 From: Ionite Date: Wed, 6 Mar 2024 19:51:02 -0500 Subject: [PATCH 039/130] Add DebugExtractImagePromptsToTxt Command --- .../Settings/MainSettingsViewModel.cs | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Settings/MainSettingsViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Settings/MainSettingsViewModel.cs index 642d5d5a..8fdfe91d 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Settings/MainSettingsViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Settings/MainSettingsViewModel.cs @@ -777,7 +777,8 @@ public partial class MainSettingsViewModel : PageViewModelBase new CommandItem(DebugExtractDmgCommand), new CommandItem(DebugShowNativeNotificationCommand), new CommandItem(DebugClearImageCacheCommand), - new CommandItem(DebugGCCollectCommand) + new CommandItem(DebugGCCollectCommand), + new CommandItem(DebugExtractImagePromptsToTxtCommand) ]; [RelayCommand] @@ -907,6 +908,45 @@ public partial class MainSettingsViewModel : PageViewModelBase GC.Collect(); } + [RelayCommand] + private async Task DebugExtractImagePromptsToTxt() + { + // Choose images + var provider = App.StorageProvider; + var files = await provider.OpenFilePickerAsync(new FilePickerOpenOptions { AllowMultiple = true }); + + if (files.Count == 0) + return; + + var images = await Task.Run( + () => files.Select(f => LocalImageFile.FromPath(f.TryGetLocalPath()!)).ToList() + ); + + var successfulFiles = new List(); + + foreach (var localImage in images) + { + var imageFile = new FilePath(localImage.AbsolutePath); + + // Write a txt with the same name as the image + var txtFile = imageFile.WithName(imageFile.NameWithoutExtension + ".txt"); + + // Read metadata + if (localImage.GenerationParameters?.PositivePrompt is { } positivePrompt) + { + await File.WriteAllTextAsync(txtFile, positivePrompt); + + successfulFiles.Add(localImage); + } + } + + notificationService.Show( + "Extracted prompts", + $"Extracted prompts from {successfulFiles.Count}/{images.Count} images.", + NotificationType.Success + ); + } + #endregion #region Info Section From 09c72677b7547f294d5af39bed675882c4660963 Mon Sep 17 00:00:00 2001 From: JT Date: Wed, 6 Mar 2024 19:00:09 -0800 Subject: [PATCH 040/130] fix crash when directory not exist --- StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs index 0728a792..a2685a24 100644 --- a/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs @@ -596,6 +596,10 @@ public partial class OutputsPageViewModel : PageViewModelBase private ObservableCollection GetSubfolders(string strPath) { var subfolders = new ObservableCollection(); + + if (!Directory.Exists(strPath)) + return subfolders; + var directories = Directory.EnumerateDirectories(strPath, "*", SearchOption.TopDirectoryOnly); foreach (var dir in directories) From b394e5b701c94c933d0039cc006ce95548dd356b Mon Sep 17 00:00:00 2001 From: JT Date: Wed, 6 Mar 2024 21:36:32 -0800 Subject: [PATCH 041/130] faster outputs page with Task.Run & avalonia.labs async image --- StabilityMatrix.Avalonia/App.axaml | 2 + .../SelectableImageButton.axaml | 5 +- .../SelectableImageButton.cs | 8 ++- .../ViewModels/OutputsPageViewModel.cs | 70 ++++++++++++++----- .../Views/OutputsPage.axaml | 43 ++++++++++-- .../Models/Database/LocalImageFile.cs | 2 + 6 files changed, 100 insertions(+), 30 deletions(-) diff --git a/StabilityMatrix.Avalonia/App.axaml b/StabilityMatrix.Avalonia/App.axaml index a1bf280c..fa669962 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"> @@ -80,6 +81,7 @@ + + + + @@ -161,13 +190,15 @@ - + - + @@ -177,7 +208,7 @@ ImageHeight="{Binding $parent[ItemsRepeater].((vm:OutputsPageViewModel)DataContext).ImageSize.Height}" ImageWidth="{Binding $parent[ItemsRepeater].((vm:OutputsPageViewModel)DataContext).ImageSize.Width}" IsSelected="{Binding IsSelected}" - Source="{Binding ImageFile.AbsolutePath}"> + Source="{Binding ImageFile.AbsolutePathUriString}"> public string FileNameWithoutExtension => Path.GetFileNameWithoutExtension(AbsolutePath); + public Uri AbsolutePathUriString => new($"file://{AbsolutePath}"); + public (string? Parameters, string? ParametersJson, string? SMProject, string? ComfyNodes) ReadMetadata() { if (AbsolutePath.EndsWith("webp")) From aed6e79957c3cf1d54a1baa94910164f44f7a1fe Mon Sep 17 00:00:00 2001 From: JT Date: Wed, 6 Mar 2024 21:47:59 -0800 Subject: [PATCH 042/130] chagenlog --- CHANGELOG.md | 4 ++++ StabilityMatrix.Avalonia/Views/OutputsPage.axaml | 7 +------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3acb129..01d5ba5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to Stability Matrix will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html). +## v2.10.0-dev.1 +### Changed +- Changed the Outputs Page to use a TreeView for the directory selection instead of a dropdown selector + ## v2.9.0 ### Added - Added new package: [StableSwarmUI](https://github.com/Stability-AI/StableSwarmUI) by Stability AI diff --git a/StabilityMatrix.Avalonia/Views/OutputsPage.axaml b/StabilityMatrix.Avalonia/Views/OutputsPage.axaml index 46b81990..b679e541 100644 --- a/StabilityMatrix.Avalonia/Views/OutputsPage.axaml +++ b/StabilityMatrix.Avalonia/Views/OutputsPage.axaml @@ -54,12 +54,7 @@ - - + Date: Wed, 6 Mar 2024 21:50:22 -0800 Subject: [PATCH 043/130] moar chagenlog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01d5ba5a..f1472e89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2 ## v2.10.0-dev.1 ### Changed +- Revamped the Packages page to enable running multiple packages at the same time - Changed the Outputs Page to use a TreeView for the directory selection instead of a dropdown selector +### Removed +- Removed the main Launch page, as it is no longer needed with the new Packages page ## v2.9.0 ### Added From 6fb5b0cb17514bf2902238f7afcad7215d67dd7f Mon Sep 17 00:00:00 2001 From: JT Date: Wed, 6 Mar 2024 22:01:40 -0800 Subject: [PATCH 044/130] add the treeview back --- .../Views/OutputsPage.axaml | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/StabilityMatrix.Avalonia/Views/OutputsPage.axaml b/StabilityMatrix.Avalonia/Views/OutputsPage.axaml index dcc05e1c..ad145027 100644 --- a/StabilityMatrix.Avalonia/Views/OutputsPage.axaml +++ b/StabilityMatrix.Avalonia/Views/OutputsPage.axaml @@ -16,8 +16,6 @@ xmlns:selectableImageCard="clr-namespace:StabilityMatrix.Avalonia.Controls.SelectableImageCard" xmlns:ui="using:FluentAvalonia.UI.Controls" xmlns:vm="clr-namespace:StabilityMatrix.Avalonia.ViewModels" - xmlns:converters="clr-namespace:StabilityMatrix.Avalonia.Converters" - xmlns:system="clr-namespace:System;assembly=System.Runtime" d:DataContext="{x:Static mocks:DesignData.OutputsPageViewModel}" d:DesignHeight="650" d:DesignWidth="800" @@ -25,20 +23,29 @@ x:DataType="vm:OutputsPageViewModel" Focusable="True" mc:Ignorable="d"> - - - - loading - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - + - + + 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 095/130] 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 096/130] 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 097/130] 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 098/130] 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 099/130] 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 100/130] 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}" /> - + - - - + + + public double? Progress { get; init; } = 0; + /// /// Current progress count. /// public ulong? Current { get; init; } = 0; + /// /// Total progress count. /// public ulong? Total { get; init; } = 0; public string? Title { get; init; } public string? Message { get; init; } + public ProcessOutput? ProcessOutput { get; init; } public bool IsIndeterminate { get; init; } = false; - public float Percentage => (float) Math.Ceiling(Math.Clamp(Progress ?? 0, 0, 1) * 100); + public float Percentage => (float)Math.Ceiling(Math.Clamp(Progress ?? 0, 0, 1) * 100); public ProgressType Type { get; init; } = ProgressType.Generic; - - public ProgressReport(double progress, string? title = null, string? message = null, bool isIndeterminate = false, ProgressType type = ProgressType.Generic) + + public ProgressReport( + double progress, + string? title = null, + string? message = null, + bool isIndeterminate = false, + ProgressType type = ProgressType.Generic + ) { Progress = progress; Title = title; @@ -28,32 +39,53 @@ public record struct ProgressReport IsIndeterminate = isIndeterminate; Type = type; } - - public ProgressReport(ulong current, ulong total, string? title = null, string? message = null, bool isIndeterminate = false, ProgressType type = ProgressType.Generic) + + public ProgressReport( + ulong current, + ulong total, + string? title = null, + string? message = null, + bool isIndeterminate = false, + ProgressType type = ProgressType.Generic + ) { Current = current; Total = total; - Progress = (double) current / total; + Progress = (double)current / total; Title = title; Message = message; IsIndeterminate = isIndeterminate; Type = type; } - - public ProgressReport(int current, int total, string? title = null, string? message = null, bool isIndeterminate = false, ProgressType type = ProgressType.Generic) + + public ProgressReport( + int current, + int total, + string? title = null, + string? message = null, + bool isIndeterminate = false, + ProgressType type = ProgressType.Generic + ) { - if (current < 0) throw new ArgumentOutOfRangeException(nameof(current), "Current progress cannot negative."); - if (total < 0) throw new ArgumentOutOfRangeException(nameof(total), "Total progress cannot be negative."); - Current = (ulong) current; - Total = (ulong) total; - Progress = (double) current / total; + if (current < 0) + throw new ArgumentOutOfRangeException(nameof(current), "Current progress cannot negative."); + if (total < 0) + throw new ArgumentOutOfRangeException(nameof(total), "Total progress cannot be negative."); + Current = (ulong)current; + Total = (ulong)total; + Progress = (double)current / total; Title = title; Message = message; IsIndeterminate = isIndeterminate; Type = type; } - - public ProgressReport(ulong current, string? title = null, string? message = null, ProgressType type = ProgressType.Generic) + + public ProgressReport( + ulong current, + string? title = null, + string? message = null, + ProgressType type = ProgressType.Generic + ) { Current = current; Title = title; @@ -61,6 +93,6 @@ public record struct ProgressReport IsIndeterminate = true; Type = type; } - + // Implicit conversion from action } From 61abe7a1f62d71123db03866788448952dbca7ec Mon Sep 17 00:00:00 2001 From: JT Date: Tue, 2 Apr 2024 23:09:00 -0700 Subject: [PATCH 103/130] use same nsfwlevel --- .../ViewModels/Dialogs/SelectModelVersionViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectModelVersionViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectModelVersionViewModel.cs index 0b84de65..338aa20d 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectModelVersionViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectModelVersionViewModel.cs @@ -99,7 +99,7 @@ public partial class SelectModelVersionViewModel( var allImages = value ?.ModelVersion ?.Images - ?.Where(img => nsfwEnabled || img.NsfwLevel <= 2) + ?.Where(img => nsfwEnabled || img.NsfwLevel <= 1) ?.Select(x => new ImageSource(x.Url)) .ToList(); From af89e07c01868420d900c633f2177455e0209357 Mon Sep 17 00:00:00 2001 From: Ionite Date: Wed, 3 Apr 2024 20:32:30 -0400 Subject: [PATCH 104/130] Add LayerDiffuseCard --- StabilityMatrix.Avalonia/App.axaml | 1 + .../Controls/Inference/LayerDiffuseCard.axaml | 48 +++++++++++++++++++ .../Inference/LayerDiffuseCard.axaml.cs | 7 +++ .../DesignData/DesignData.cs | 3 ++ 4 files changed, 59 insertions(+) create mode 100644 StabilityMatrix.Avalonia/Controls/Inference/LayerDiffuseCard.axaml create mode 100644 StabilityMatrix.Avalonia/Controls/Inference/LayerDiffuseCard.axaml.cs diff --git a/StabilityMatrix.Avalonia/App.axaml b/StabilityMatrix.Avalonia/App.axaml index fa669962..643c72b3 100644 --- a/StabilityMatrix.Avalonia/App.axaml +++ b/StabilityMatrix.Avalonia/App.axaml @@ -81,6 +81,7 @@ + + diff --git a/StabilityMatrix.Avalonia/Controls/Inference/LayerDiffuseCard.axaml.cs b/StabilityMatrix.Avalonia/Controls/Inference/LayerDiffuseCard.axaml.cs new file mode 100644 index 00000000..a8973a3f --- /dev/null +++ b/StabilityMatrix.Avalonia/Controls/Inference/LayerDiffuseCard.axaml.cs @@ -0,0 +1,7 @@ +using Avalonia.Controls.Primitives; +using StabilityMatrix.Core.Attributes; + +namespace StabilityMatrix.Avalonia.Controls; + +[Transient] +public class LayerDiffuseCard : TemplatedControl; diff --git a/StabilityMatrix.Avalonia/DesignData/DesignData.cs b/StabilityMatrix.Avalonia/DesignData/DesignData.cs index df122b14..f2aa68cb 100644 --- a/StabilityMatrix.Avalonia/DesignData/DesignData.cs +++ b/StabilityMatrix.Avalonia/DesignData/DesignData.cs @@ -921,6 +921,9 @@ The gallery images are often inpainted, but you will get something very similar vm.IsBatchIndexEnabled = true; }); + public static LayerDiffuseCardViewModel LayerDiffuseCardViewModel => + DialogFactory.Get(); + public static IList SampleCompletionData => new List { From 92d42b3c53077b705673859c73fdd62c2111f42d Mon Sep 17 00:00:00 2001 From: Ionite Date: Wed, 3 Apr 2024 20:32:59 -0400 Subject: [PATCH 105/130] PreOutputActions support for ModuleApplyStepEventArgs --- .../Models/Inference/ModuleApplyStepEventArgs.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/StabilityMatrix.Avalonia/Models/Inference/ModuleApplyStepEventArgs.cs b/StabilityMatrix.Avalonia/Models/Inference/ModuleApplyStepEventArgs.cs index f4bd13fb..487d39b7 100644 --- a/StabilityMatrix.Avalonia/Models/Inference/ModuleApplyStepEventArgs.cs +++ b/StabilityMatrix.Avalonia/Models/Inference/ModuleApplyStepEventArgs.cs @@ -28,6 +28,16 @@ public class ModuleApplyStepEventArgs : EventArgs public List<(string SourcePath, string DestinationRelativePath)> FilesToTransfer { get; init; } = []; + public List> PreOutputActions { get; init; } = []; + + public void InvokeAllPreOutputActions() + { + foreach (var action in PreOutputActions) + { + action(this); + } + } + /// /// Creates a new with the given . /// From cefb08e4da98248c979f0271a35bcdd9e249878d Mon Sep 17 00:00:00 2001 From: Ionite Date: Wed, 3 Apr 2024 20:33:18 -0400 Subject: [PATCH 106/130] Add LayerDiffuseModule --- .../ViewModels/Base/LoadableViewModelBase.cs | 3 +++ .../Inference/Modules/LayerDiffuseModule.cs | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 StabilityMatrix.Avalonia/ViewModels/Inference/Modules/LayerDiffuseModule.cs diff --git a/StabilityMatrix.Avalonia/ViewModels/Base/LoadableViewModelBase.cs b/StabilityMatrix.Avalonia/ViewModels/Base/LoadableViewModelBase.cs index b06e9a68..ed787354 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Base/LoadableViewModelBase.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Base/LoadableViewModelBase.cs @@ -11,6 +11,7 @@ using NLog; using StabilityMatrix.Avalonia.Models; using StabilityMatrix.Avalonia.ViewModels.Inference; using StabilityMatrix.Avalonia.ViewModels.Inference.Modules; +using StabilityMatrix.Core.Models.Inference; namespace StabilityMatrix.Avalonia.ViewModels.Base; @@ -20,12 +21,14 @@ namespace StabilityMatrix.Avalonia.ViewModels.Base; [JsonDerivedType(typeof(UpscalerCardViewModel), UpscalerCardViewModel.ModuleKey)] [JsonDerivedType(typeof(ControlNetCardViewModel), ControlNetCardViewModel.ModuleKey)] [JsonDerivedType(typeof(PromptExpansionCardViewModel), PromptExpansionCardViewModel.ModuleKey)] +[JsonDerivedType(typeof(LayerDiffuseCardViewModel), LayerDiffuseCardViewModel.ModuleKey)] [JsonDerivedType(typeof(FreeUModule))] [JsonDerivedType(typeof(HiresFixModule))] [JsonDerivedType(typeof(UpscalerModule))] [JsonDerivedType(typeof(ControlNetModule))] [JsonDerivedType(typeof(SaveImageModule))] [JsonDerivedType(typeof(PromptExpansionModule))] +[JsonDerivedType(typeof(LayerDiffuseModule))] public abstract class LoadableViewModelBase : ViewModelBase, IJsonLoadableState { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/LayerDiffuseModule.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/LayerDiffuseModule.cs new file mode 100644 index 00000000..11a70114 --- /dev/null +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/LayerDiffuseModule.cs @@ -0,0 +1,26 @@ +using StabilityMatrix.Avalonia.Models.Inference; +using StabilityMatrix.Avalonia.Services; +using StabilityMatrix.Avalonia.ViewModels.Base; +using StabilityMatrix.Core.Attributes; + +namespace StabilityMatrix.Avalonia.ViewModels.Inference.Modules; + +[ManagedService] +[Transient] +public class LayerDiffuseModule : ModuleBase +{ + /// + public LayerDiffuseModule(ServiceManager vmFactory) + : base(vmFactory) + { + Title = "Layer Diffuse"; + AddCards(vmFactory.Get()); + } + + /// + protected override void OnApplyStep(ModuleApplyStepEventArgs e) + { + var card = GetCard(); + card.ApplyStep(e); + } +} From 51b6e1be367efdee89158a0fa8ba9a3823555b9a Mon Sep 17 00:00:00 2001 From: Ionite Date: Wed, 3 Apr 2024 20:34:07 -0400 Subject: [PATCH 107/130] Add LayerDiffuseCard ViewModel --- .../Inference/LayerDiffuseCardViewModel.cs | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 StabilityMatrix.Avalonia/ViewModels/Inference/LayerDiffuseCardViewModel.cs diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/LayerDiffuseCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/LayerDiffuseCardViewModel.cs new file mode 100644 index 00000000..04cb8b80 --- /dev/null +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/LayerDiffuseCardViewModel.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using CommunityToolkit.Mvvm.ComponentModel; +using KGySoft.CoreLibraries; +using StabilityMatrix.Avalonia.Controls; +using StabilityMatrix.Avalonia.Models.Inference; +using StabilityMatrix.Avalonia.ViewModels.Base; +using StabilityMatrix.Core.Attributes; +using StabilityMatrix.Core.Models.Api.Comfy.Nodes; +using StabilityMatrix.Core.Models.Inference; + +namespace StabilityMatrix.Avalonia.ViewModels.Inference; + +[Transient] +[ManagedService] +[View(typeof(LayerDiffuseCard))] +public partial class LayerDiffuseCardViewModel : LoadableViewModelBase, IComfyStep +{ + public const string ModuleKey = "LayerDiffuse"; + + [ObservableProperty] + private LayerDiffuseMode selectedMode = LayerDiffuseMode.None; + + public IEnumerable AvailableModes => Enum.GetValues(); + + [ObservableProperty] + [NotifyDataErrorInfo] + [Required] + [Range(-1d, 3d)] + private double weight = 1; + + /// + public void ApplyStep(ModuleApplyStepEventArgs e) + { + if (SelectedMode == LayerDiffuseMode.None) + return; + + foreach (var modelConnections in e.Temp.Models.Values) + { + var layerDiffuseApply = e.Nodes.AddTypedNode( + new ComfyNodeBuilder.LayeredDiffusionApply + { + Name = e.Nodes.GetUniqueName($"LayerDiffuseApply_{modelConnections.Name}"), + Model = modelConnections.Model, + Config = "SD15, Attention Injection, attn_sharing", + Weight = Weight, + } + ); + + modelConnections.Model = layerDiffuseApply.Output; + } + + // Add pre output action + e.PreOutputActions.Add(applyArgs => + { + // Use last latent for decode + var latent = + applyArgs.Builder.Connections.LastPrimaryLatent + ?? throw new InvalidOperationException("Connections.LastPrimaryLatent not set"); + + // Convert primary to image if not already + var primaryImage = applyArgs.Builder.GetPrimaryAsImage(); + applyArgs.Builder.Connections.Primary = primaryImage; + + // Add a Layer Diffuse Decode + var decode = applyArgs.Nodes.AddTypedNode( + new ComfyNodeBuilder.LayeredDiffusionDecodeRgba + { + Name = applyArgs.Nodes.GetUniqueName("LayerDiffuseDecode"), + Samples = latent, + Images = primaryImage, + SdVersion = "SD15", + } + ); + + // Set primary to decode output + applyArgs.Builder.Connections.Primary = decode.Output; + }); + } +} From 52b32f922722106b7238130596979945ee6f8bc4 Mon Sep 17 00:00:00 2001 From: Ionite Date: Wed, 3 Apr 2024 20:34:22 -0400 Subject: [PATCH 108/130] Add LayerDiffuseModule as Sampler addon option --- .../ViewModels/Inference/SamplerCardViewModel.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs index 8890a514..664ab0c8 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs @@ -109,7 +109,12 @@ public partial class SamplerCardViewModel : LoadableViewModelBase, IParametersLo ModulesCardViewModel = vmFactory.Get(modulesCard => { modulesCard.Title = Resources.Label_Addons; - modulesCard.AvailableModules = [typeof(FreeUModule), typeof(ControlNetModule)]; + modulesCard.AvailableModules = + [ + typeof(FreeUModule), + typeof(ControlNetModule), + typeof(LayerDiffuseModule) + ]; }); } From 255b1feb66fb4bff45dd267554489ef7920fe9c9 Mon Sep 17 00:00:00 2001 From: Ionite Date: Wed, 3 Apr 2024 20:34:32 -0400 Subject: [PATCH 109/130] Add LayerDiffuseMode --- .../Models/Inference/LayerDiffuseMode.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 StabilityMatrix.Core/Models/Inference/LayerDiffuseMode.cs diff --git a/StabilityMatrix.Core/Models/Inference/LayerDiffuseMode.cs b/StabilityMatrix.Core/Models/Inference/LayerDiffuseMode.cs new file mode 100644 index 00000000..af243113 --- /dev/null +++ b/StabilityMatrix.Core/Models/Inference/LayerDiffuseMode.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; + +namespace StabilityMatrix.Core.Models.Inference; + +public enum LayerDiffuseMode +{ + /// + /// The layer diffuse mode is not set. + /// + [Display(Name = "None")] + None, + + /// + /// Generate foreground only with transparency. + /// + [Display(Name = "Generate Foreground with Transparency")] + GenerateForegroundWithTransparency, +} From d6920be164d3d562bc127f5846615ac7e5868bbb Mon Sep 17 00:00:00 2001 From: Ionite Date: Wed, 3 Apr 2024 20:34:48 -0400 Subject: [PATCH 110/130] Add Nodes for LayerDiffuse --- .../Api/Comfy/Nodes/ComfyNodeBuilder.cs | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs b/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs index c9a378b6..a1b79df5 100644 --- a/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs +++ b/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs @@ -383,6 +383,39 @@ public class ComfyNodeBuilder public int BatchSize { get; init; } = 1; } + [TypedNodeOptions( + Name = "Inference_Core_LayeredDiffusionApply", + RequiredExtensions = ["https://github.com/LykosAI/ComfyUI-Inference-Core-Nodes >= 0.4.0"] + )] + public record LayeredDiffusionApply : ComfyTypedNodeBase + { + public required ModelNodeConnection Model { get; init; } + + public required string Config { get; init; } + + [Range(-1d, 3d)] + public double Weight { get; init; } = 1.0; + } + + [TypedNodeOptions( + Name = "Inference_Core_LayeredDiffusionDecodeRGBA", + RequiredExtensions = ["https://github.com/LykosAI/ComfyUI-Inference-Core-Nodes >= 0.4.0"] + )] + public record LayeredDiffusionDecodeRgba : ComfyTypedNodeBase + { + public required LatentNodeConnection Samples { get; init; } + + public required ImageNodeConnection Images { get; init; } + + /// + /// Either "SD15" or "SDXL" + /// + public required string SdVersion { get; init; } + + [Range(1, 4096)] + public int SubBatchSize { get; init; } = 16; + } + public ImageNodeConnection Lambda_LatentToImage(LatentNodeConnection latent, VAENodeConnection vae) { var name = GetUniqueName("VAEDecode"); @@ -867,7 +900,26 @@ public class ComfyNodeBuilder set => SamplerTemporaryArgs["Base"] = value; } - public PrimaryNodeConnection? Primary { get; set; } + /// + /// The last primary set latent value, updated when is set to a latent value. + /// + public LatentNodeConnection? LastPrimaryLatent { get; private set; } + + private PrimaryNodeConnection? primary; + + public PrimaryNodeConnection? Primary + { + get => primary; + set + { + if (value?.IsT0 == true) + { + LastPrimaryLatent = value.AsT0; + } + primary = value; + } + } + public VAENodeConnection? PrimaryVAE { get; set; } public Size PrimarySize { get; set; } From 395d9c3963389442a52968268f33d40bd0c7b869 Mon Sep 17 00:00:00 2001 From: Ionite Date: Wed, 3 Apr 2024 22:00:59 -0400 Subject: [PATCH 111/130] Add PreOutputAction support for Text2Img and Img2Img --- .../Inference/InferenceImageToImageViewModel.cs | 16 ++++++++++------ .../Inference/InferenceTextToImageViewModel.cs | 14 +++++++++----- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceImageToImageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceImageToImageViewModel.cs index 77c5230f..7600384a 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceImageToImageViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceImageToImageViewModel.cs @@ -56,26 +56,30 @@ public class InferenceImageToImageViewModel : InferenceTextToImageViewModel _ => Convert.ToUInt64(SeedCardViewModel.Seed) }; - BatchSizeCardViewModel.ApplyStep(args); + var applyArgs = args.ToModuleApplyStepEventArgs(); + + BatchSizeCardViewModel.ApplyStep(applyArgs); // Load models - ModelCardViewModel.ApplyStep(args); + ModelCardViewModel.ApplyStep(applyArgs); // Setup image latent source - SelectImageCardViewModel.ApplyStep(args); + SelectImageCardViewModel.ApplyStep(applyArgs); // Prompts and loras - PromptCardViewModel.ApplyStep(args); + PromptCardViewModel.ApplyStep(applyArgs); // Setup Sampler and Refiner if enabled - SamplerCardViewModel.ApplyStep(args); + SamplerCardViewModel.ApplyStep(applyArgs); // Apply module steps foreach (var module in ModulesCardViewModel.Cards.OfType()) { - module.ApplyStep(args); + module.ApplyStep(applyArgs); } + applyArgs.InvokeAllPreOutputActions(); + builder.SetupOutputImage(); } diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceTextToImageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceTextToImageViewModel.cs index a1924559..04e46f05 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceTextToImageViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceTextToImageViewModel.cs @@ -131,10 +131,12 @@ public class InferenceTextToImageViewModel : InferenceGenerationViewModelBase, I _ => Convert.ToUInt64(SeedCardViewModel.Seed) }; - BatchSizeCardViewModel.ApplyStep(args); + var applyArgs = args.ToModuleApplyStepEventArgs(); + + BatchSizeCardViewModel.ApplyStep(applyArgs); // Load models - ModelCardViewModel.ApplyStep(args); + ModelCardViewModel.ApplyStep(applyArgs); // Setup empty latent builder.SetupEmptyLatentSource( @@ -145,17 +147,19 @@ public class InferenceTextToImageViewModel : InferenceGenerationViewModelBase, I ); // Prompts and loras - PromptCardViewModel.ApplyStep(args); + PromptCardViewModel.ApplyStep(applyArgs); // Setup Sampler and Refiner if enabled - SamplerCardViewModel.ApplyStep(args); + SamplerCardViewModel.ApplyStep(applyArgs); // Hires fix if enabled foreach (var module in ModulesCardViewModel.Cards.OfType()) { - module.ApplyStep(args); + module.ApplyStep(applyArgs); } + applyArgs.InvokeAllPreOutputActions(); + builder.SetupOutputImage(); } From ff26180e8bd69ba562583dd9305c031c572889c1 Mon Sep 17 00:00:00 2001 From: Ionite Date: Thu, 4 Apr 2024 23:08:05 -0400 Subject: [PATCH 112/130] Apply post install dep updates for ComfyUI ext update --- .../Models/Packages/ComfyUI.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/StabilityMatrix.Core/Models/Packages/ComfyUI.cs b/StabilityMatrix.Core/Models/Packages/ComfyUI.cs index 19843e75..c620182c 100644 --- a/StabilityMatrix.Core/Models/Packages/ComfyUI.cs +++ b/StabilityMatrix.Core/Models/Packages/ComfyUI.cs @@ -511,6 +511,32 @@ public class ComfyUI( } } + /// + public override async Task UpdateExtensionAsync( + InstalledPackageExtension installedExtension, + InstalledPackage installedPackage, + PackageExtensionVersion? version = null, + IProgress? progress = null, + CancellationToken cancellationToken = default + ) + { + await base.UpdateExtensionAsync( + installedExtension, + installedPackage, + version, + progress, + cancellationToken + ) + .ConfigureAwait(false); + + cancellationToken.ThrowIfCancellationRequested(); + + var installedDirs = installedExtension.Paths.OfType().Where(dir => dir.Exists); + + await PostInstallAsync(installedPackage, installedDirs, progress, cancellationToken) + .ConfigureAwait(false); + } + /// public override async Task InstallExtensionAsync( PackageExtension extension, @@ -539,6 +565,20 @@ public class ComfyUI( .Select(path => cloneRoot.JoinDir(path!)) .Where(dir => dir.Exists); + await PostInstallAsync(installedPackage, installedDirs, progress, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Runs post install / update tasks (i.e. install.py, requirements.txt) + /// + private async Task PostInstallAsync( + InstalledPackage installedPackage, + IEnumerable installedDirs, + IProgress? progress = null, + CancellationToken cancellationToken = default + ) + { foreach (var installedDir in installedDirs) { cancellationToken.ThrowIfCancellationRequested(); From 6621a8ce58db7787bf05de841e57891e0ba887b0 Mon Sep 17 00:00:00 2001 From: Ionite Date: Thu, 4 Apr 2024 23:12:24 -0400 Subject: [PATCH 113/130] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa0e8fc8..38097f46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2 - Fixed Civitai model browser not showing images when "Show NSFW" is disabled - Fixed crash when Installed Workflows page is opened with no Workflows folder - Fixed progress bars not displaying properly during package installs & updates +- Fixed ComfyUI extension updates not running install.py / updating requirements.txt ## v2.10.0-pre.1 ### Added From 660dad42454c54016fa41fc961068471567aca7b Mon Sep 17 00:00:00 2001 From: Ionite Date: Thu, 4 Apr 2024 23:22:39 -0400 Subject: [PATCH 114/130] Add layer diffuse selection for SD1.5 / SDXL --- .../Inference/LayerDiffuseCardViewModel.cs | 22 +++++++++++++++++-- .../Api/Comfy/Nodes/ComfyNodeBuilder.cs | 6 +++++ .../Models/Inference/LayerDiffuseMode.cs | 12 +++++++--- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/LayerDiffuseCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/LayerDiffuseCardViewModel.cs index 04cb8b80..865a4c74 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/LayerDiffuseCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/LayerDiffuseCardViewModel.cs @@ -37,6 +37,24 @@ public partial class LayerDiffuseCardViewModel : LoadableViewModelBase, IComfySt if (SelectedMode == LayerDiffuseMode.None) return; + var sdType = SelectedMode switch + { + LayerDiffuseMode.GenerateForegroundWithTransparencySD15 => "SD15", + LayerDiffuseMode.GenerateForegroundWithTransparencySDXL => "SDXL", + LayerDiffuseMode.None => throw new ArgumentOutOfRangeException(), + _ => throw new ArgumentOutOfRangeException() + }; + + // Choose config based on mode + var config = SelectedMode switch + { + LayerDiffuseMode.GenerateForegroundWithTransparencySD15 + => "SD15, Attention Injection, attn_sharing", + LayerDiffuseMode.GenerateForegroundWithTransparencySDXL => "SDXL, Conv Injection", + LayerDiffuseMode.None => throw new ArgumentOutOfRangeException(), + _ => throw new ArgumentOutOfRangeException() + }; + foreach (var modelConnections in e.Temp.Models.Values) { var layerDiffuseApply = e.Nodes.AddTypedNode( @@ -44,7 +62,7 @@ public partial class LayerDiffuseCardViewModel : LoadableViewModelBase, IComfySt { Name = e.Nodes.GetUniqueName($"LayerDiffuseApply_{modelConnections.Name}"), Model = modelConnections.Model, - Config = "SD15, Attention Injection, attn_sharing", + Config = config, Weight = Weight, } ); @@ -71,7 +89,7 @@ public partial class LayerDiffuseCardViewModel : LoadableViewModelBase, IComfySt Name = applyArgs.Nodes.GetUniqueName("LayerDiffuseDecode"), Samples = latent, Images = primaryImage, - SdVersion = "SD15", + SdVersion = sdType } ); diff --git a/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs b/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs index a1b79df5..df14284a 100644 --- a/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs +++ b/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs @@ -391,6 +391,12 @@ public class ComfyNodeBuilder { public required ModelNodeConnection Model { get; init; } + /// + /// Available configs: + /// SD15, Attention Injection, attn_sharing + /// SDXL, Conv Injection + /// SDXL, Attention Injection + /// public required string Config { get; init; } [Range(-1d, 3d)] diff --git a/StabilityMatrix.Core/Models/Inference/LayerDiffuseMode.cs b/StabilityMatrix.Core/Models/Inference/LayerDiffuseMode.cs index af243113..35621f60 100644 --- a/StabilityMatrix.Core/Models/Inference/LayerDiffuseMode.cs +++ b/StabilityMatrix.Core/Models/Inference/LayerDiffuseMode.cs @@ -11,8 +11,14 @@ public enum LayerDiffuseMode None, /// - /// Generate foreground only with transparency. + /// Generate foreground only with transparency. SD1.5 /// - [Display(Name = "Generate Foreground with Transparency")] - GenerateForegroundWithTransparency, + [Display(Name = "(SD 1.5) Generate Foreground with Transparency")] + GenerateForegroundWithTransparencySD15, + + /// + /// Generate foreground only with transparency. SDXL + /// + [Display(Name = "(SDXL) Generate Foreground with Transparency")] + GenerateForegroundWithTransparencySDXL, } From 2d95adbda7f7786e155bb7e9bb7aa3debb1de536 Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 5 Apr 2024 02:17:56 -0400 Subject: [PATCH 115/130] Fix dmg build script --- .github/workflows/release.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ffad36eb..320b8bc9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -257,13 +257,11 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: '20.x' + node-version: '20.11.x' - name: Install dependencies for dmg creation - run: > - npm install --global create-dmg - brew install graphicsmagick imagemagick - + run: brew install graphicsmagick imagemagick && npm install --global create-dmg + - name: Create dmg working-directory: signing run: > From 026b9d0cb1bb440af39f20ea5e85252d9ba0557e Mon Sep 17 00:00:00 2001 From: JT Date: Sat, 6 Apr 2024 13:34:14 -0700 Subject: [PATCH 116/130] Only download images from civitai with Type == "image" since they finally added that property --- CHANGELOG.md | 4 ++++ .../Services/ModelDownloadLinkHandler.cs | 5 ++++- .../CheckpointBrowserCardViewModel.cs | 9 +++++++-- .../ViewModels/CheckpointManager/CheckpointFolder.cs | 2 +- .../Dialogs/RecommendedModelItemViewModel.cs | 4 ++-- .../ViewModels/Dialogs/RecommendedModelsViewModel.cs | 5 ++++- .../Dialogs/SelectModelVersionViewModel.cs | 2 +- StabilityMatrix.Core/Models/Api/CivitImage.cs | 3 +++ .../Services/MetadataImportService.cs | 12 +++++++++--- 9 files changed, 35 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38097f46..73eb4c18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to Stability Matrix will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html). +## v2.10.0 +### Fixed +- Fixed [#559](https://github.com/LykosAI/StabilityMatrix/issues/559) - "Unable to load bitmap from provided data" error in Checkpoints page + ## v2.10.0-pre.2 ### Added - Added more metadata to the image dialog info flyout diff --git a/StabilityMatrix.Avalonia/Services/ModelDownloadLinkHandler.cs b/StabilityMatrix.Avalonia/Services/ModelDownloadLinkHandler.cs index 326d715c..62145809 100644 --- a/StabilityMatrix.Avalonia/Services/ModelDownloadLinkHandler.cs +++ b/StabilityMatrix.Avalonia/Services/ModelDownloadLinkHandler.cs @@ -229,7 +229,10 @@ public class ModelDownloadLinkHandler( return null; } - var image = modelVersion.Images[0]; + var image = modelVersion.Images.FirstOrDefault(x => x.Type == "image"); + if (image is null) + return null; + var imageExtension = Path.GetExtension(image.Url).TrimStart('.'); if (imageExtension is "jpg" or "jpeg" or "png") { diff --git a/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CheckpointBrowserCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CheckpointBrowserCardViewModel.cs index 341a3913..7edf9920 100644 --- a/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CheckpointBrowserCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CheckpointBrowserCardViewModel.cs @@ -159,7 +159,9 @@ public partial class CheckpointBrowserCardViewModel : Base.ProgressViewModel // Try to find a valid image var image = images - ?.Where(img => LocalModelFile.SupportedImageExtensions.Any(img.Url.Contains)) + ?.Where( + img => LocalModelFile.SupportedImageExtensions.Any(img.Url.Contains) && img.Type == "image" + ) .FirstOrDefault(image => nsfwEnabled || image.NsfwLevel <= 1); if (image != null) { @@ -295,7 +297,10 @@ public partial class CheckpointBrowserCardViewModel : Base.ProgressViewModel return null; } - var image = modelVersion.Images[0]; + var image = modelVersion.Images.FirstOrDefault(x => x.Type == "image"); + if (image is null) + return null; + var imageExtension = Path.GetExtension(image.Url).TrimStart('.'); if (imageExtension is "jpg" or "jpeg" or "png") { diff --git a/StabilityMatrix.Avalonia/ViewModels/CheckpointManager/CheckpointFolder.cs b/StabilityMatrix.Avalonia/ViewModels/CheckpointManager/CheckpointFolder.cs index e11cccdc..39408129 100644 --- a/StabilityMatrix.Avalonia/ViewModels/CheckpointManager/CheckpointFolder.cs +++ b/StabilityMatrix.Avalonia/ViewModels/CheckpointManager/CheckpointFolder.cs @@ -500,7 +500,7 @@ public partial class CheckpointFolder : ViewModelBase await modelInfo.SaveJsonToDirectory(DirectoryPath, modelFileName); // If available, save thumbnail - var image = version.Images?.FirstOrDefault(); + var image = version.Images?.FirstOrDefault(x => x.Type == "image"); if (image != null) { var imageExt = Path.GetExtension(image.Url).TrimStart('.'); diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelItemViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelItemViewModel.cs index 3de1d5da..501fa5db 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelItemViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelItemViewModel.cs @@ -22,9 +22,9 @@ public partial class RecommendedModelItemViewModel : ViewModelBase private CivitModel civitModel; public Uri ThumbnailUrl => - ModelVersion.Images?.FirstOrDefault()?.Url == null + ModelVersion.Images?.FirstOrDefault(x => x.Type == "image")?.Url == null ? Assets.NoImage - : new Uri(ModelVersion.Images.First().Url); + : new Uri(ModelVersion.Images.First(x => x.Type == "image").Url); [RelayCommand] public void ToggleSelection() => IsSelected = !IsSelected; diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelsViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelsViewModel.cs index a2aa480e..5eeaeb6a 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelsViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelsViewModel.cs @@ -206,7 +206,10 @@ public partial class RecommendedModelsViewModel : ContentDialogViewModelBase return null; } - var image = modelVersion.Images[0]; + var image = modelVersion.Images.FirstOrDefault(x => x.Type == "image"); + if (image is null) + return null; + var imageExtension = Path.GetExtension(image.Url).TrimStart('.'); if (imageExtension is "jpg" or "jpeg" or "png") { diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectModelVersionViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectModelVersionViewModel.cs index 338aa20d..8327e996 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectModelVersionViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectModelVersionViewModel.cs @@ -99,7 +99,7 @@ public partial class SelectModelVersionViewModel( var allImages = value ?.ModelVersion ?.Images - ?.Where(img => nsfwEnabled || img.NsfwLevel <= 1) + ?.Where(img => img.Type == "image" && (nsfwEnabled || img.NsfwLevel <= 1)) ?.Select(x => new ImageSource(x.Url)) .ToList(); diff --git a/StabilityMatrix.Core/Models/Api/CivitImage.cs b/StabilityMatrix.Core/Models/Api/CivitImage.cs index 82aadce2..c29e3bac 100644 --- a/StabilityMatrix.Core/Models/Api/CivitImage.cs +++ b/StabilityMatrix.Core/Models/Api/CivitImage.cs @@ -19,5 +19,8 @@ public class CivitImage [JsonPropertyName("hash")] public string Hash { get; set; } + [JsonPropertyName("type")] + public string Type { get; set; } + // TODO: "meta" ( object? ) } diff --git a/StabilityMatrix.Core/Services/MetadataImportService.cs b/StabilityMatrix.Core/Services/MetadataImportService.cs index d31a603d..21ce5a2c 100644 --- a/StabilityMatrix.Core/Services/MetadataImportService.cs +++ b/StabilityMatrix.Core/Services/MetadataImportService.cs @@ -107,7 +107,9 @@ public class MetadataImportService( .ConfigureAwait(false); var image = modelVersion.Images?.FirstOrDefault( - img => LocalModelFile.SupportedImageExtensions.Contains(Path.GetExtension(img.Url)) + img => + LocalModelFile.SupportedImageExtensions.Contains(Path.GetExtension(img.Url)) + && img.Type == "image" ); if (image == null) { @@ -201,7 +203,9 @@ public class MetadataImportService( .ConfigureAwait(false); var image = modelVersion.Images?.FirstOrDefault( - img => LocalModelFile.SupportedImageExtensions.Contains(Path.GetExtension(img.Url)) + img => + LocalModelFile.SupportedImageExtensions.Contains(Path.GetExtension(img.Url)) + && img.Type == "image" ); if (image == null) continue; @@ -264,7 +268,9 @@ public class MetadataImportService( .ConfigureAwait(false); var image = modelVersion.Images?.FirstOrDefault( - img => LocalModelFile.SupportedImageExtensions.Contains(Path.GetExtension(img.Url)) + img => + LocalModelFile.SupportedImageExtensions.Contains(Path.GetExtension(img.Url)) + && img.Type == "image" ); if (image == null) From e5d215463e626d285f54dba694292880af7788ad Mon Sep 17 00:00:00 2001 From: JT Date: Sat, 6 Apr 2024 17:10:20 -0700 Subject: [PATCH 117/130] updated spanish/french/turkish translations --- CHANGELOG.md | 2 + .../Languages/Resources.es.resx | 62 +++++++++++++++++++ .../Languages/Resources.fr-FR.resx | 50 ++++++++++++++- .../Languages/Resources.tr-TR.resx | 60 ++++++++++++++++++ 4 files changed, 173 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73eb4c18..7456d558 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html). ## v2.10.0 +### Changed +- Updated translations for French, Spanish, and Turkish ### Fixed - Fixed [#559](https://github.com/LykosAI/StabilityMatrix/issues/559) - "Unable to load bitmap from provided data" error in Checkpoints page diff --git a/StabilityMatrix.Avalonia/Languages/Resources.es.resx b/StabilityMatrix.Avalonia/Languages/Resources.es.resx index 5efb7a15..28b0dae1 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.es.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.es.resx @@ -963,4 +963,66 @@ Mientras se instala tu paquete, aquí hay algunos modelos que recomendamos para ayudarte a comenzar. + + Notificaciones + + + Ninguno + + + Se Requiere ComfyUI + + + Se requiere ComfyUI para instalar este paquete. ¿Quieres instalarlo ahora? + + + Por favor, seleccione una ubicación de descarga. + + + Seleccione la Ubicación de Descarga: + + + Configuración + This is used in inference when models have a yaml config file + + + Desplazamiento Automático hasta el Final + + + Confirmar Salida + + + ¿Seguro que quieres salir? Esto también cerrará cualquier paquete que se esté ejecutando actualmente. + + + Consola + + + Interfaz Web + This will be used on a button to launch the web ui + + + Paquetes + + + Esta acción no se puede deshacer. + + + ¿Estás seguro de que deseas eliminar {0} imágenes? + + + Estamos verificando algunas especificaciones de hardware para determinar la compatibilidad. + + + ¡Todo parece estar bien! + + + Recomendamos una GPU con soporte CUDA para obtener la mejor experiencia. Puedes continuar sin una, pero es posible que algunos paquetes no funcionen y la inferencia pueda ser más lenta. + + + Checkpoints + + + Navegador de Modelos + \ No newline at end of file diff --git a/StabilityMatrix.Avalonia/Languages/Resources.fr-FR.resx b/StabilityMatrix.Avalonia/Languages/Resources.fr-FR.resx index 6e195f4d..f205b67c 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.fr-FR.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.fr-FR.resx @@ -598,7 +598,7 @@ Informations sur la version de Python - Redémarrage + Redémarrer Confirmer la suppression @@ -930,4 +930,52 @@ Pendant que votre paquet s'installe, voici quelques modèles que nous recommandons pour vous aidez à démarrer. + + Notifications + + + Aucun + + + ComfyUI est requis + + + ComfyUI est requis pour installer ce paquet. Voulez vous l'installer maintenant ? + + + Merci de sélectionner un dossier de téléchargement. + + + Sélectionner un dossier de téléchargement: + + + Défiler automatiquement à la fin + + + Confirmer la sortie + + + Êtes vous certain de vouloir quitter ? Cela va fermer toutes les instances actuellement lancées. + + + Console + + + Paquets + + + Cette action ne peut être annulée. + + + Êtes vous certain de vouloir supprimer les {0} images? + + + Nous vérifions quelques spécifications matérielles pour vérifier la compatibilité. + + + Tout à l'air bon! + + + Nous recommandons un GPU avec prise en charge CUDA pour une meilleure expérience. Vous pouvez continuer sans en avoir un, mais certains packages peuvent ne pas fonctionner et l'inférence peut être plus lente. + \ No newline at end of file diff --git a/StabilityMatrix.Avalonia/Languages/Resources.tr-TR.resx b/StabilityMatrix.Avalonia/Languages/Resources.tr-TR.resx index 8d2e1d7f..5b6064d2 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.tr-TR.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.tr-TR.resx @@ -961,4 +961,64 @@ Paketiniz yüklenirken başlamanıza yardımcı olması için önerdiğimiz bazı modeller aşağıda verilmiştir. + + Bildirimler + + + Hiçbiri + + + ComfyUI Gerekli + + + Bu paketi kurmak için ComfyUI gereklidir. Şimdi yüklemek ister misiniz? + + + Lütfen bir indirme konumu seçin. + + + İndirme Konumunu Seçin: + + + Yapılandırma + + + Otomatik Sona Kaydır + + + Çıkışı Onayla + + + Çıkmak istediğine emin misin? Bu aynı zamanda şu anda çalışan tüm paketleri de kapatacaktır. + + + Konsol + + + Web arayüzü + + + Paketler + + + Bu eylem geri alınamaz. + + + {0} görseli silmek istediğinizden emin misiniz? + + + Uyumluluğu belirlemek için bazı donanım özelliklerini kontrol ediyoruz. + + + Her şey iyi gözüküyor! + + + En iyi deneyim için CUDA destekli bir GPU öneriyoruz. Olmadan da devam edebilirsiniz ancak bazı paketler çalışmayabilir ve çıkarımlar daha yavaş olabilir. + + + Kontrol noktaları + + + Model Tarayıcı + \ No newline at end of file From 612509f4ff59e6ff634e86f87dfec3a8696682e5 Mon Sep 17 00:00:00 2001 From: JT Date: Sun, 7 Apr 2024 11:25:21 -0700 Subject: [PATCH 118/130] Fixed incorrect output path for A3WebUI & made package launch update output links --- CHANGELOG.md | 1 + .../Services/RunningPackageService.cs | 5 +++++ StabilityMatrix.Core/Models/Packages/A3WebUI.cs | 17 +++++++++++------ .../Models/Packages/SDWebForge.cs | 5 ----- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7456d558..0d4dcaaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2 - Updated translations for French, Spanish, and Turkish ### Fixed - Fixed [#559](https://github.com/LykosAI/StabilityMatrix/issues/559) - "Unable to load bitmap from provided data" error in Checkpoints page +- Fixed [#522](https://github.com/LykosAI/StabilityMatrix/issues/522) - Incorrect output directory path for latest Auto1111 ## v2.10.0-pre.2 ### Added diff --git a/StabilityMatrix.Avalonia/Services/RunningPackageService.cs b/StabilityMatrix.Avalonia/Services/RunningPackageService.cs index 96f1d719..38db4dc3 100644 --- a/StabilityMatrix.Avalonia/Services/RunningPackageService.cs +++ b/StabilityMatrix.Avalonia/Services/RunningPackageService.cs @@ -100,6 +100,11 @@ public partial class RunningPackageService( installedPackage.PreferredSharedFolderMethod ?? basePackage.RecommendedSharedFolderMethod ); + if (installedPackage.UseSharedOutputFolder) + { + await basePackage.SetupOutputFolderLinks(installedPackage.FullPath!); + } + // Load user launch args from settings and convert to string var userArgs = installedPackage.LaunchArgs ?? []; var userArgsString = string.Join(" ", userArgs.Select(opt => opt.ToArgString())); diff --git a/StabilityMatrix.Core/Models/Packages/A3WebUI.cs b/StabilityMatrix.Core/Models/Packages/A3WebUI.cs index f823efeb..9635cecc 100644 --- a/StabilityMatrix.Core/Models/Packages/A3WebUI.cs +++ b/StabilityMatrix.Core/Models/Packages/A3WebUI.cs @@ -72,12 +72,12 @@ public class A3WebUI( public override Dictionary>? SharedOutputFolders => new() { - [SharedOutputType.Extras] = new[] { "outputs/extras-images" }, + [SharedOutputType.Extras] = new[] { "output/extras-images" }, [SharedOutputType.Saved] = new[] { "log/images" }, - [SharedOutputType.Img2Img] = new[] { "outputs/img2img-images" }, - [SharedOutputType.Text2Img] = new[] { "outputs/txt2img-images" }, - [SharedOutputType.Img2ImgGrids] = new[] { "outputs/img2img-grids" }, - [SharedOutputType.Text2ImgGrids] = new[] { "outputs/txt2img-grids" } + [SharedOutputType.Img2Img] = new[] { "output/img2img-images" }, + [SharedOutputType.Text2Img] = new[] { "output/txt2img-images" }, + [SharedOutputType.Img2ImgGrids] = new[] { "output/img2img-grids" }, + [SharedOutputType.Text2ImgGrids] = new[] { "output/txt2img-grids" } }; [SuppressMessage("ReSharper", "ArrangeObjectCreationWhenTypeNotEvident")] @@ -184,7 +184,7 @@ public class A3WebUI( public override string MainBranch => "master"; - public override string OutputFolderName => "outputs"; + public override string OutputFolderName => "output"; public override IPackageExtensionManager ExtensionManager => new A3WebUiExtensionManager(this); @@ -294,6 +294,11 @@ public class A3WebUI( VenvRunner.RunDetached(args.TrimEnd(), HandleConsoleOutput, OnExit); } + public override string? ExtraLaunchArguments { get; set; } = + settingsManager.IsLibraryDirSet + ? $"--gradio-allowed-path \"{settingsManager.ImagesDirectory}\"" + : string.Empty; + private class A3WebUiExtensionManager(A3WebUI package) : GitPackageExtensionManager(package.PrerequisiteHelper) { diff --git a/StabilityMatrix.Core/Models/Packages/SDWebForge.cs b/StabilityMatrix.Core/Models/Packages/SDWebForge.cs index 69a501d9..f64a4197 100644 --- a/StabilityMatrix.Core/Models/Packages/SDWebForge.cs +++ b/StabilityMatrix.Core/Models/Packages/SDWebForge.cs @@ -178,9 +178,4 @@ public class SDWebForge( await venvRunner.PipInstall(pipArgs, onConsoleOutput).ConfigureAwait(false); progress?.Report(new ProgressReport(1f, "Install complete", isIndeterminate: false)); } - - public override string? ExtraLaunchArguments { get; set; } = - settingsManager.IsLibraryDirSet - ? $"--gradio-allowed-path \"{settingsManager.ImagesDirectory}\"" - : string.Empty; } From 4e42eee757f6a68d8eb3eb7c2be1f206da16e020 Mon Sep 17 00:00:00 2001 From: Ionite Date: Sun, 7 Apr 2024 17:37:47 -0400 Subject: [PATCH 119/130] Add default env vars, set SETUPTOOLS_USE_DISTUTILS=stdlib --- .../ViewModels/Settings/MainSettingsViewModel.cs | 4 ++-- StabilityMatrix.Core/Models/Settings/Settings.cs | 13 ++++++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Settings/MainSettingsViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Settings/MainSettingsViewModel.cs index 0da8ab4c..c02c396b 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Settings/MainSettingsViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Settings/MainSettingsViewModel.cs @@ -365,7 +365,7 @@ public partial class MainSettingsViewModel : PageViewModelBase var viewModel = dialogFactory.Get(); // Load current settings - var current = settingsManager.Settings.EnvironmentVariables ?? new Dictionary(); + var current = settingsManager.Settings.UserEnvironmentVariables ?? new Dictionary(); viewModel.EnvVars = new ObservableCollection( current.Select(kvp => new EnvVarKeyPair(kvp.Key, kvp.Value)) ); @@ -385,7 +385,7 @@ public partial class MainSettingsViewModel : PageViewModelBase .EnvVars.Where(kvp => !string.IsNullOrWhiteSpace(kvp.Key)) .GroupBy(kvp => kvp.Key, StringComparer.Ordinal) .ToDictionary(g => g.Key, g => g.First().Value, StringComparer.Ordinal); - settingsManager.Transaction(s => s.EnvironmentVariables = newEnvVars); + settingsManager.Transaction(s => s.UserEnvironmentVariables = newEnvVars); } } diff --git a/StabilityMatrix.Core/Models/Settings/Settings.cs b/StabilityMatrix.Core/Models/Settings/Settings.cs index bf5e080f..01e22fd2 100644 --- a/StabilityMatrix.Core/Models/Settings/Settings.cs +++ b/StabilityMatrix.Core/Models/Settings/Settings.cs @@ -110,7 +110,18 @@ public class Settings public bool IsDiscordRichPresenceEnabled { get; set; } - public Dictionary? EnvironmentVariables { get; set; } + [JsonIgnore] + public Dictionary DefaultEnvironmentVariables { get; } = + new() { ["SETUPTOOLS_USE_DISTUTILS"] = "stdlib" }; + + [JsonPropertyName("EnvironmentVariables")] + public Dictionary? UserEnvironmentVariables { get; set; } + + [JsonIgnore] + public IReadOnlyDictionary EnvironmentVariables => + DefaultEnvironmentVariables + .Concat(UserEnvironmentVariables ?? []) + .ToDictionary(x => x.Key, x => x.Value); public HashSet? InstalledModelHashes { get; set; } = new(); From 43f3c28b9a9cf8ee60c168572b550fbb8e2210d7 Mon Sep 17 00:00:00 2001 From: Ionite Date: Mon, 8 Apr 2024 00:16:40 -0400 Subject: [PATCH 120/130] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d4dcaaf..91ae423d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2 ### Fixed - Fixed [#559](https://github.com/LykosAI/StabilityMatrix/issues/559) - "Unable to load bitmap from provided data" error in Checkpoints page - Fixed [#522](https://github.com/LykosAI/StabilityMatrix/issues/522) - Incorrect output directory path for latest Auto1111 +- Fixed CLIP Install errors due to setuptools distutils conflict, added default environment variable setting `SETUPTOOLS_USE_DISTUTILS=stdlib` ## v2.10.0-pre.2 ### Added From d211db88ab9bbd521953af99b81b8d75913ae6db Mon Sep 17 00:00:00 2001 From: JT Date: Mon, 8 Apr 2024 18:02:13 -0700 Subject: [PATCH 121/130] Chunk installed model requests with more than 100 installed models cuz Civit doesn't paginate that --- CHANGELOG.md | 1 + .../CivitAiBrowserViewModel.cs | 42 ++++++++++++++++--- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d4dcaaf..8cb19019 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2 ### Fixed - Fixed [#559](https://github.com/LykosAI/StabilityMatrix/issues/559) - "Unable to load bitmap from provided data" error in Checkpoints page - Fixed [#522](https://github.com/LykosAI/StabilityMatrix/issues/522) - Incorrect output directory path for latest Auto1111 +- Fixed Civitai model browser error when sorting by Installed with more than 100 installed models ## v2.10.0-pre.2 ### Added diff --git a/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitAiBrowserViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitAiBrowserViewModel.cs index 4754397d..ebf5401a 100644 --- a/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitAiBrowserViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitAiBrowserViewModel.cs @@ -196,10 +196,40 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase, IInfinitelyScro { var timer = Stopwatch.StartNew(); var queryText = request.Query; + var models = new List(); + CivitModelsResponse? modelsResponse = null; try { - var modelsResponse = await civitApi.GetModels(request); - var models = modelsResponse.Items; + if (!string.IsNullOrWhiteSpace(request.CommaSeparatedModelIds)) + { + // count IDs + var ids = request.CommaSeparatedModelIds.Split(','); + if (ids.Length > 100) + { + var idChunks = ids.Chunk(100); + foreach (var chunk in idChunks) + { + request.CommaSeparatedModelIds = string.Join(",", chunk); + var chunkModelsResponse = await civitApi.GetModels(request); + + if (chunkModelsResponse.Items != null) + { + models.AddRange(chunkModelsResponse.Items); + } + } + } + else + { + modelsResponse = await civitApi.GetModels(request); + models = modelsResponse.Items; + } + } + else + { + modelsResponse = await civitApi.GetModels(request); + models = modelsResponse.Items; + } + if (models is null) { Logger.Debug( @@ -241,13 +271,13 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase, IInfinitelyScro InsertedAt = DateTimeOffset.UtcNow, Request = request, Items = models, - Metadata = modelsResponse.Metadata + Metadata = modelsResponse?.Metadata } ); UpdateModelCards(models, isInfiniteScroll); - NextPageCursor = modelsResponse.Metadata?.NextCursor; + NextPageCursor = modelsResponse?.Metadata?.NextCursor; } catch (OperationCanceledException) { @@ -428,12 +458,14 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase, IInfinitelyScro { var connectedModels = CheckpointFile .GetAllCheckpointFiles(settingsManager.ModelsDirectory) - .Where(c => c.IsConnectedModel); + .Where(c => c.IsConnectedModel) + .ToList(); modelRequest.CommaSeparatedModelIds = string.Join( ",", connectedModels.Select(c => c.ConnectedModel!.ModelId).GroupBy(m => m).Select(g => g.First()) ); + modelRequest.Sort = null; modelRequest.Period = null; } From 96f507619c7c2794bfb010cf7cd6a9aa56e4a557 Mon Sep 17 00:00:00 2001 From: JT Date: Mon, 8 Apr 2024 20:28:17 -0700 Subject: [PATCH 122/130] release changelog --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97092981..2d35c902 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,13 +6,35 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html). ## v2.10.0 +### Added +- Added Reference-Only mode for Inference ControlNet, used for guiding the sampler with an image without a pretrained model. Part of the latent and attention layers will be connected to the reference image, similar to Image to Image or Inpainting. +- Inference ControlNet module now supports over 42 preprocessors, a new button next to the preprocessors dropdown allows previewing the output of the selected preprocessor on the image. +- Added resolution selection for Inference ControlNet module, this controls preprocessor resolution too. +- Added support for deep links from the new Stability Matrix Chrome extension +- Added OpenArt.AI workflow browser for ComfyUI workflows +- Added more metadata to the image dialog info flyout +- Added Output Sharing toggle in Advanced Options during install flow ### Changed +- Revamped the Packages page to enable running multiple packages at the same time +- Changed the Outputs Page to use a TreeView for the directory selection instead of a dropdown selector +- Model download location selector now searches all subfolders +- Inference Primary Sampler Addons (i.e. ControlNet, FreeU) are now inherited by Hires Fix Samplers, this can be overriden from the Hires Fix module's settings menu by disabling the "Inherit Primary Sampler Addons" option. +- Revisited the way images are loaded on the outputs page, with improvements to loading speed & not freezing the UI while loading - Updated translations for French, Spanish, and Turkish +- Changed to a new image control for pages with many images +- (Internal) Updated to Avalonia 11.0.10 ### Fixed - Fixed [#559](https://github.com/LykosAI/StabilityMatrix/issues/559) - "Unable to load bitmap from provided data" error in Checkpoints page - Fixed [#522](https://github.com/LykosAI/StabilityMatrix/issues/522) - Incorrect output directory path for latest Auto1111 - Fixed Civitai model browser error when sorting by Installed with more than 100 installed models - Fixed CLIP Install errors due to setuptools distutils conflict, added default environment variable setting `SETUPTOOLS_USE_DISTUTILS=stdlib` +- Fixed progress bars not displaying properly during package installs & updates +- Fixed ComfyUI extension updates not running install.py / updating requirements.txt +- 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 +### Removed +- Removed the main Launch page, as it is no longer needed with the new Packages page ## v2.10.0-pre.2 ### Added @@ -40,6 +62,9 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2 - 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+ +- Fixed Inference HiresFix module "Inherit Primary Sampler Addons" setting not effectively disabling when unchecked +- Fixed model download location options for VAEs in the CivitAI Model Browser + ## v2.10.0-dev.3 ### Added From 8ba15c73a1290b593aca642795248efdb7796daa Mon Sep 17 00:00:00 2001 From: JT Date: Mon, 8 Apr 2024 20:29:25 -0700 Subject: [PATCH 123/130] oops wrong section --- CHANGELOG.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d35c902..14157ffd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,13 +26,15 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2 ### Fixed - Fixed [#559](https://github.com/LykosAI/StabilityMatrix/issues/559) - "Unable to load bitmap from provided data" error in Checkpoints page - Fixed [#522](https://github.com/LykosAI/StabilityMatrix/issues/522) - Incorrect output directory path for latest Auto1111 +- Fixed [#529](https://github.com/LykosAI/StabilityMatrix/issues/529) - OneTrainer requesting input during update - Fixed Civitai model browser error when sorting by Installed with more than 100 installed models - Fixed CLIP Install errors due to setuptools distutils conflict, added default environment variable setting `SETUPTOOLS_USE_DISTUTILS=stdlib` - Fixed progress bars not displaying properly during package installs & updates - Fixed ComfyUI extension updates not running install.py / updating requirements.txt - 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 Inference HiresFix module "Inherit Primary Sampler Addons" setting not effectively disabling when unchecked +- Fixed model download location options for VAEs in the CivitAI Model Browser ### Removed - Removed the main Launch page, as it is no longer needed with the new Packages page @@ -62,9 +64,6 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2 - 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+ -- Fixed Inference HiresFix module "Inherit Primary Sampler Addons" setting not effectively disabling when unchecked -- Fixed model download location options for VAEs in the CivitAI Model Browser - ## v2.10.0-dev.3 ### Added From 2a22a43cc8e3f6015e8a3f2349c9a786c4902923 Mon Sep 17 00:00:00 2001 From: JT Date: Wed, 10 Apr 2024 18:51:20 -0700 Subject: [PATCH 124/130] Fixed double 2.9.2 changelog --- CHANGELOG.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5648ef9f..d14f30f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -111,15 +111,6 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2 - Fixed images not appearing in Civitai Model Browser when "Show NSFW" was disabled - Fixed [#556](https://github.com/LykosAI/StabilityMatrix/issues/556) - "Could not find entry point for InvokeAI" error -## v2.9.2 -### Changed -- Model download location selector now searches all subfolders -### Fixed -- Fixed Civitai model browser not showing images when "Show NSFW" is disabled -- Fixed crash when Installed Workflows page is opened with no Workflows folder -- Fixed progress bars not displaying properly during package installs & updates -- Fixed ComfyUI extension updates not running install.py / updating requirements.txt - ## v2.9.2 ### Changed - Due to changes with the CivitAI API, you can no longer select a specific page in the CivitAI Model Browser From 8e1c00312c48ce57c11345f9381b5030ce6c2dbe Mon Sep 17 00:00:00 2001 From: Ionite Date: Wed, 10 Apr 2024 21:53:59 -0400 Subject: [PATCH 125/130] Add Inference prompt help tooltip --- .../Controls/Inference/PromptCard.axaml | 30 ++++++++++++------- .../Controls/Inference/PromptCard.axaml.cs | 2 +- .../Languages/Resources.Designer.cs | 9 ++++++ .../Languages/Resources.resx | 3 ++ .../Inference/PromptCardViewModel.cs | 30 +++++++++++++++++++ .../Models/Settings/TeachingTip.cs | 1 + 6 files changed, 64 insertions(+), 11 deletions(-) diff --git a/StabilityMatrix.Avalonia/Controls/Inference/PromptCard.axaml b/StabilityMatrix.Avalonia/Controls/Inference/PromptCard.axaml index 7c53337d..12423e48 100644 --- a/StabilityMatrix.Avalonia/Controls/Inference/PromptCard.axaml +++ b/StabilityMatrix.Avalonia/Controls/Inference/PromptCard.axaml @@ -8,6 +8,8 @@ xmlns:icons="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia" xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData" xmlns:vmInference="clr-namespace:StabilityMatrix.Avalonia.ViewModels.Inference" + xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" + xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages" x:DataType="vmInference:PromptCardViewModel"> @@ -56,21 +58,29 @@ HorizontalAlignment="Right" Orientation="Horizontal"> -