From 2998ceee2122c17630e633fc611dec9df8fec6f2 Mon Sep 17 00:00:00 2001
From: JT
Date: Sat, 10 Feb 2024 22:35:50 -0800
Subject: [PATCH 01/18] Added models & refit interface for openart stuff
---
StabilityMatrix.Core/Api/IOpenArtApi.cs | 17 ++++++++
.../Models/Api/OpenArt/NodesCount.cs | 15 +++++++
.../Models/Api/OpenArt/OpenArtCreator.cs | 24 ++++++++++++
.../Api/OpenArt/OpenArtDownloadRequest.cs | 15 +++++++
.../Api/OpenArt/OpenArtDownloadResponse.cs | 12 ++++++
.../Models/Api/OpenArt/OpenArtFeedRequest.cs | 18 +++++++++
.../Api/OpenArt/OpenArtSearchRequest.cs | 18 +++++++++
.../Api/OpenArt/OpenArtSearchResponse.cs | 15 +++++++
.../Models/Api/OpenArt/OpenArtSearchResult.cs | 39 +++++++++++++++++++
.../Models/Api/OpenArt/OpenArtStats.cs | 33 ++++++++++++++++
.../Models/Api/OpenArt/OpenArtThumbnail.cs | 15 +++++++
11 files changed, 221 insertions(+)
create mode 100644 StabilityMatrix.Core/Api/IOpenArtApi.cs
create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/NodesCount.cs
create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtCreator.cs
create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDownloadRequest.cs
create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDownloadResponse.cs
create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtFeedRequest.cs
create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchRequest.cs
create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResponse.cs
create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResult.cs
create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtStats.cs
create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtThumbnail.cs
diff --git a/StabilityMatrix.Core/Api/IOpenArtApi.cs b/StabilityMatrix.Core/Api/IOpenArtApi.cs
new file mode 100644
index 00000000..e13c8623
--- /dev/null
+++ b/StabilityMatrix.Core/Api/IOpenArtApi.cs
@@ -0,0 +1,17 @@
+using Refit;
+using StabilityMatrix.Core.Models.Api.OpenArt;
+
+namespace StabilityMatrix.Core.Api;
+
+[Headers("User-Agent: StabilityMatrix")]
+public interface IOpenArtApi
+{
+ [Get("/feed")]
+ Task GetFeedAsync([Query] OpenArtFeedRequest request);
+
+ [Get("/list")]
+ Task SearchAsync([Query] OpenArtFeedRequest request);
+
+ [Post("/download")]
+ Task DownloadWorkflowAsync([Body] OpenArtDownloadRequest request);
+}
diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/NodesCount.cs b/StabilityMatrix.Core/Models/Api/OpenArt/NodesCount.cs
new file mode 100644
index 00000000..2509dcd7
--- /dev/null
+++ b/StabilityMatrix.Core/Models/Api/OpenArt/NodesCount.cs
@@ -0,0 +1,15 @@
+using System.Text.Json.Serialization;
+
+namespace StabilityMatrix.Core.Models.Api.OpenArt;
+
+public class NodesCount
+{
+ [JsonPropertyName("total")]
+ public long Total { get; set; }
+
+ [JsonPropertyName("primitive")]
+ public long Primitive { get; set; }
+
+ [JsonPropertyName("custom")]
+ public long Custom { get; set; }
+}
diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtCreator.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtCreator.cs
new file mode 100644
index 00000000..1f16dc14
--- /dev/null
+++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtCreator.cs
@@ -0,0 +1,24 @@
+using System.Text.Json.Serialization;
+
+namespace StabilityMatrix.Core.Models.Api.OpenArt;
+
+public class OpenArtCreator
+{
+ [JsonPropertyName("uid")]
+ public string Uid { get; set; }
+
+ [JsonPropertyName("name")]
+ public string Name { get; set; }
+
+ [JsonPropertyName("bio")]
+ public string Bio { get; set; }
+
+ [JsonPropertyName("avatar")]
+ public Uri Avatar { get; set; }
+
+ [JsonPropertyName("username")]
+ public string Username { get; set; }
+
+ [JsonPropertyName("dev_profile_url")]
+ public Uri DevProfileUrl { get; set; }
+}
diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDownloadRequest.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDownloadRequest.cs
new file mode 100644
index 00000000..cdf07d27
--- /dev/null
+++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDownloadRequest.cs
@@ -0,0 +1,15 @@
+using System.Text.Json.Serialization;
+using Refit;
+
+namespace StabilityMatrix.Core.Models.Api.OpenArt;
+
+public class OpenArtDownloadRequest
+{
+ [AliasAs("workflow_id")]
+ [JsonPropertyName("workflow_id")]
+ public required string WorkflowId { get; set; }
+
+ [AliasAs("version_tag")]
+ [JsonPropertyName("version_tag")]
+ public string VersionTag { get; set; } = "latest";
+}
diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDownloadResponse.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDownloadResponse.cs
new file mode 100644
index 00000000..3cb61d79
--- /dev/null
+++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDownloadResponse.cs
@@ -0,0 +1,12 @@
+using System.Text.Json.Serialization;
+
+namespace StabilityMatrix.Core.Models.Api.OpenArt;
+
+public class OpenArtDownloadResponse
+{
+ [JsonPropertyName("filename")]
+ public string Filename { get; set; }
+
+ [JsonPropertyName("payload")]
+ public string Payload { get; set; }
+}
diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtFeedRequest.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtFeedRequest.cs
new file mode 100644
index 00000000..a3f334d9
--- /dev/null
+++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtFeedRequest.cs
@@ -0,0 +1,18 @@
+using Refit;
+
+namespace StabilityMatrix.Core.Models.Api.OpenArt;
+
+public class OpenArtFeedRequest
+{
+ [AliasAs("category")]
+ public string Category { get; set; }
+
+ [AliasAs("sort")]
+ public string Sort { get; set; }
+
+ [AliasAs("custom_node")]
+ public string CustomNode { get; set; }
+
+ [AliasAs("cursor")]
+ public string Cursor { get; set; }
+}
diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchRequest.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchRequest.cs
new file mode 100644
index 00000000..27d944e3
--- /dev/null
+++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchRequest.cs
@@ -0,0 +1,18 @@
+using Refit;
+
+namespace StabilityMatrix.Core.Models.Api.OpenArt;
+
+public class OpenArtSearchRequest
+{
+ [AliasAs("keyword")]
+ public required string Keyword { get; set; }
+
+ [AliasAs("pageSize")]
+ public int PageSize { get; set; } = 30;
+
+ ///
+ /// 0-based index of the page to retrieve
+ ///
+ [AliasAs("currentPage")]
+ public int CurrentPage { get; set; } = 0;
+}
diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResponse.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResponse.cs
new file mode 100644
index 00000000..c58e308a
--- /dev/null
+++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResponse.cs
@@ -0,0 +1,15 @@
+using System.Text.Json.Serialization;
+
+namespace StabilityMatrix.Core.Models.Api.OpenArt;
+
+public class OpenArtSearchResponse
+{
+ [JsonPropertyName("items")]
+ public IEnumerable Items { get; set; }
+
+ [JsonPropertyName("total")]
+ public int Total { get; set; }
+
+ [JsonPropertyName("nextCursor")]
+ public string? NextCursor { get; set; }
+}
diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResult.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResult.cs
new file mode 100644
index 00000000..7db9bb4a
--- /dev/null
+++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResult.cs
@@ -0,0 +1,39 @@
+using System.Text.Json.Serialization;
+
+namespace StabilityMatrix.Core.Models.Api.OpenArt;
+
+public class OpenArtSearchResult
+{
+ [JsonPropertyName("id")]
+ public string Id { get; set; }
+
+ [JsonPropertyName("creator")]
+ public OpenArtCreator Creator { get; set; }
+
+ [JsonPropertyName("updated_at")]
+ public DateTimeOffset UpdatedAt { get; set; }
+
+ [JsonPropertyName("stats")]
+ public OpenArtStats Stats { get; set; }
+
+ [JsonPropertyName("nodes_index")]
+ public IEnumerable NodesIndex { get; set; }
+
+ [JsonPropertyName("name")]
+ public string Name { get; set; }
+
+ [JsonPropertyName("description")]
+ public string Description { get; set; }
+
+ [JsonPropertyName("created_at")]
+ public DateTimeOffset CreatedAt { get; set; }
+
+ [JsonPropertyName("categories")]
+ public IEnumerable Categories { get; set; }
+
+ [JsonPropertyName("thumbnails")]
+ public IEnumerable Thumbnails { get; set; }
+
+ [JsonPropertyName("nodes_count")]
+ public NodesCount NodesCount { get; set; }
+}
diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtStats.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtStats.cs
new file mode 100644
index 00000000..381db723
--- /dev/null
+++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtStats.cs
@@ -0,0 +1,33 @@
+using System.Text.Json.Serialization;
+
+namespace StabilityMatrix.Core.Models.Api.OpenArt;
+
+public class OpenArtStats
+{
+ [JsonPropertyName("num_shares")]
+ public int NumShares { get; set; }
+
+ [JsonPropertyName("num_bookmarks")]
+ public int NumBookmarks { get; set; }
+
+ [JsonPropertyName("num_reviews")]
+ public int NumReviews { get; set; }
+
+ [JsonPropertyName("rating")]
+ public int Rating { get; set; }
+
+ [JsonPropertyName("num_comments")]
+ public int NumComments { get; set; }
+
+ [JsonPropertyName("num_likes")]
+ public int NumLikes { get; set; }
+
+ [JsonPropertyName("num_downloads")]
+ public int NumDownloads { get; set; }
+
+ [JsonPropertyName("num_runs")]
+ public int NumRuns { get; set; }
+
+ [JsonPropertyName("num_views")]
+ public int NumViews { get; set; }
+}
diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtThumbnail.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtThumbnail.cs
new file mode 100644
index 00000000..05bfc22f
--- /dev/null
+++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtThumbnail.cs
@@ -0,0 +1,15 @@
+using System.Text.Json.Serialization;
+
+namespace StabilityMatrix.Core.Models.Api.OpenArt;
+
+public class OpenArtThumbnail
+{
+ [JsonPropertyName("width")]
+ public int Width { get; set; }
+
+ [JsonPropertyName("url")]
+ public Uri Url { get; set; }
+
+ [JsonPropertyName("height")]
+ public int Height { get; set; }
+}
From c26960ce1f8457e6ed228dc6e4c687734c03c95c Mon Sep 17 00:00:00 2001
From: JT
Date: Sun, 11 Feb 2024 18:35:51 -0800
Subject: [PATCH 02/18] added basic card UI, searching, and infiniscroll
---
Avalonia.Gif/Avalonia.Gif.csproj | 2 +-
...tabilityMatrix.Avalonia.Diagnostics.csproj | 4 +-
StabilityMatrix.Avalonia/App.axaml.cs | 16 +-
.../Models/IInfinitelyScroll.cs | 8 +
.../StabilityMatrix.Avalonia.csproj | 15 +-
.../ViewModels/OpenArtBrowserViewModel.cs | 175 +++++++++
.../Views/OpenArtBrowserPage.axaml | 340 ++++++++++++++++++
.../Views/OpenArtBrowserPage.axaml.cs | 32 ++
StabilityMatrix.Core/Api/IOpenArtApi.cs | 2 +-
.../Models/Api/OpenArt/OpenArtCreator.cs | 2 +-
.../Models/Api/OpenArt/OpenArtDateTime.cs | 14 +
.../Models/Api/OpenArt/OpenArtFeedRequest.cs | 3 +
.../Models/Api/OpenArt/OpenArtSearchResult.cs | 2 +-
.../Models/Api/OpenArt/OpenArtStats.cs | 2 +-
.../Models/Packages/SDWebForge.cs | 13 +
.../StabilityMatrix.UITests.csproj | 2 +-
16 files changed, 614 insertions(+), 18 deletions(-)
create mode 100644 StabilityMatrix.Avalonia/Models/IInfinitelyScroll.cs
create mode 100644 StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs
create mode 100644 StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml
create mode 100644 StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml.cs
create mode 100644 StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDateTime.cs
diff --git a/Avalonia.Gif/Avalonia.Gif.csproj b/Avalonia.Gif/Avalonia.Gif.csproj
index 858b974e..6a363589 100644
--- a/Avalonia.Gif/Avalonia.Gif.csproj
+++ b/Avalonia.Gif/Avalonia.Gif.csproj
@@ -10,7 +10,7 @@
true
-
+
diff --git a/StabilityMatrix.Avalonia.Diagnostics/StabilityMatrix.Avalonia.Diagnostics.csproj b/StabilityMatrix.Avalonia.Diagnostics/StabilityMatrix.Avalonia.Diagnostics.csproj
index e635ad2f..71efd8bb 100644
--- a/StabilityMatrix.Avalonia.Diagnostics/StabilityMatrix.Avalonia.Diagnostics.csproj
+++ b/StabilityMatrix.Avalonia.Diagnostics/StabilityMatrix.Avalonia.Diagnostics.csproj
@@ -19,8 +19,8 @@
-
-
+
+
diff --git a/StabilityMatrix.Avalonia/App.axaml.cs b/StabilityMatrix.Avalonia/App.axaml.cs
index f1c10ddd..05e54065 100644
--- a/StabilityMatrix.Avalonia/App.axaml.cs
+++ b/StabilityMatrix.Avalonia/App.axaml.cs
@@ -333,7 +333,8 @@ public sealed class App : Application
provider.GetRequiredService(),
provider.GetRequiredService(),
provider.GetRequiredService(),
- provider.GetRequiredService()
+ provider.GetRequiredService(),
+ provider.GetRequiredService()
},
FooterPages = { provider.GetRequiredService() }
}
@@ -556,7 +557,7 @@ public sealed class App : Application
.ConfigureHttpClient(c =>
{
c.BaseAddress = new Uri("https://civitai.com");
- c.Timeout = TimeSpan.FromSeconds(15);
+ c.Timeout = TimeSpan.FromSeconds(30);
})
.AddPolicyHandler(retryPolicy);
@@ -565,7 +566,7 @@ public sealed class App : Application
.ConfigureHttpClient(c =>
{
c.BaseAddress = new Uri("https://civitai.com");
- c.Timeout = TimeSpan.FromSeconds(15);
+ c.Timeout = TimeSpan.FromSeconds(30);
})
.AddPolicyHandler(retryPolicy);
@@ -583,6 +584,15 @@ public sealed class App : Application
new TokenAuthHeaderHandler(serviceProvider.GetRequiredService())
);
+ services
+ .AddRefitClient(defaultRefitSettings)
+ .ConfigureHttpClient(c =>
+ {
+ c.BaseAddress = new Uri("https://openart.ai/api/public/workflows");
+ c.Timeout = TimeSpan.FromSeconds(30);
+ })
+ .AddPolicyHandler(retryPolicy);
+
// Add Refit client managers
services.AddHttpClient("A3Client").AddPolicyHandler(localTimeout.WrapAsync(localRetryPolicy));
diff --git a/StabilityMatrix.Avalonia/Models/IInfinitelyScroll.cs b/StabilityMatrix.Avalonia/Models/IInfinitelyScroll.cs
new file mode 100644
index 00000000..59373816
--- /dev/null
+++ b/StabilityMatrix.Avalonia/Models/IInfinitelyScroll.cs
@@ -0,0 +1,8 @@
+using System.Threading.Tasks;
+
+namespace StabilityMatrix.Avalonia.Models;
+
+public interface IInfinitelyScroll
+{
+ Task LoadNextPageAsync();
+}
diff --git a/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj b/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj
index 1190f686..63e08450 100644
--- a/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj
+++ b/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj
@@ -39,13 +39,14 @@
-
+
+
-
-
-
+
+
+
-
+
@@ -65,8 +66,8 @@
-
-
+
+
diff --git a/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs
new file mode 100644
index 00000000..c8463037
--- /dev/null
+++ b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs
@@ -0,0 +1,175 @@
+using System;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Threading.Tasks;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using DynamicData;
+using DynamicData.Binding;
+using FluentAvalonia.UI.Controls;
+using Refit;
+using StabilityMatrix.Avalonia.Models;
+using StabilityMatrix.Avalonia.Services;
+using StabilityMatrix.Avalonia.ViewModels.Base;
+using StabilityMatrix.Avalonia.Views;
+using StabilityMatrix.Core.Api;
+using StabilityMatrix.Core.Attributes;
+using StabilityMatrix.Core.Models.Api.OpenArt;
+using StabilityMatrix.Core.Processes;
+using Symbol = FluentIcons.Common.Symbol;
+using SymbolIconSource = FluentIcons.Avalonia.Fluent.SymbolIconSource;
+
+namespace StabilityMatrix.Avalonia.ViewModels;
+
+[View(typeof(OpenArtBrowserPage))]
+[Singleton]
+public partial class OpenArtBrowserViewModel(IOpenArtApi openArtApi, INotificationService notificationService)
+ : PageViewModelBase,
+ IInfinitelyScroll
+{
+ private const int PageSize = 20;
+
+ public override string Title => "Workflows";
+ public override IconSource IconSource => new SymbolIconSource { Symbol = Symbol.Whiteboard };
+
+ private SourceCache searchResultsCache = new(x => x.Id);
+
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(PageCount), nameof(CanGoBack), nameof(CanGoForward), nameof(CanGoToEnd))]
+ private OpenArtSearchResponse? latestSearchResponse;
+
+ [ObservableProperty]
+ private IObservableCollection searchResults =
+ new ObservableCollectionExtended();
+
+ [ObservableProperty]
+ private string searchQuery = string.Empty;
+
+ [ObservableProperty]
+ private bool isLoading;
+
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(InternalPageNumber), nameof(CanGoBack))]
+ private int displayedPageNumber = 1;
+
+ public int InternalPageNumber => DisplayedPageNumber - 1;
+
+ public int PageCount =>
+ Math.Max(
+ 1,
+ Convert.ToInt32(Math.Ceiling((LatestSearchResponse?.Total ?? 0) / Convert.ToDouble(PageSize)))
+ );
+
+ public bool CanGoBack => InternalPageNumber > 0;
+
+ public bool CanGoForward => PageCount > InternalPageNumber + 1;
+
+ public bool CanGoToEnd => PageCount > InternalPageNumber + 1;
+
+ protected override void OnInitialLoaded()
+ {
+ searchResultsCache.Connect().DeferUntilLoaded().Bind(SearchResults).Subscribe();
+ }
+
+ public override async Task OnLoadedAsync()
+ {
+ if (SearchResults.Any())
+ return;
+
+ await DoSearch();
+ }
+
+ [RelayCommand]
+ private async Task FirstPage()
+ {
+ DisplayedPageNumber = 1;
+ await DoSearch();
+ }
+
+ [RelayCommand]
+ private async Task PreviousPage()
+ {
+ DisplayedPageNumber--;
+ await DoSearch(InternalPageNumber);
+ }
+
+ [RelayCommand]
+ private async Task NextPage()
+ {
+ DisplayedPageNumber++;
+ await DoSearch(InternalPageNumber);
+ }
+
+ [RelayCommand]
+ private async Task LastPage()
+ {
+ DisplayedPageNumber = PageCount;
+ await DoSearch(PageCount - 1);
+ }
+
+ [Localizable(false)]
+ [RelayCommand]
+ private void OpenModel(OpenArtSearchResult workflow)
+ {
+ ProcessRunner.OpenUrl($"https://openart.ai/workflows/{workflow.Id}");
+ }
+
+ [RelayCommand]
+ private async Task SearchButton()
+ {
+ DisplayedPageNumber = 1;
+ await DoSearch();
+ }
+
+ private async Task DoSearch(int page = 0)
+ {
+ IsLoading = true;
+
+ try
+ {
+ var response = await openArtApi.SearchAsync(
+ new OpenArtSearchRequest
+ {
+ Keyword = SearchQuery,
+ PageSize = PageSize,
+ CurrentPage = page
+ }
+ );
+
+ searchResultsCache.EditDiff(response.Items, (a, b) => a.Id == b.Id);
+ LatestSearchResponse = response;
+ }
+ catch (ApiException e)
+ {
+ notificationService.Show("Error retrieving workflows", e.Message);
+ }
+ finally
+ {
+ IsLoading = false;
+ }
+ }
+
+ public async Task LoadNextPageAsync()
+ {
+ try
+ {
+ DisplayedPageNumber++;
+ var response = await openArtApi.SearchAsync(
+ new OpenArtSearchRequest
+ {
+ Keyword = SearchQuery,
+ PageSize = PageSize,
+ CurrentPage = InternalPageNumber
+ }
+ );
+
+ searchResultsCache.AddOrUpdate(response.Items);
+ LatestSearchResponse = response;
+ }
+ catch (ApiException e)
+ {
+ notificationService.Show("Unable to load the next page", e.Message);
+ }
+ }
+}
diff --git a/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml
new file mode 100644
index 00000000..8e7a621e
--- /dev/null
+++ b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml
@@ -0,0 +1,340 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml.cs b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml.cs
new file mode 100644
index 00000000..aceb5db4
--- /dev/null
+++ b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml.cs
@@ -0,0 +1,32 @@
+using System;
+using AsyncAwaitBestPractices;
+using Avalonia.Controls;
+using StabilityMatrix.Avalonia.Controls;
+using StabilityMatrix.Avalonia.Models;
+using StabilityMatrix.Core.Attributes;
+
+namespace StabilityMatrix.Avalonia.Views;
+
+[Singleton]
+public partial class OpenArtBrowserPage : UserControlBase
+{
+ public OpenArtBrowserPage()
+ {
+ InitializeComponent();
+ }
+
+ private void ScrollViewer_OnScrollChanged(object? sender, ScrollChangedEventArgs e)
+ {
+ if (sender is not ScrollViewer scrollViewer)
+ return;
+
+ if (scrollViewer.Offset.Y == 0)
+ return;
+
+ var isAtEnd = Math.Abs(scrollViewer.Offset.Y - scrollViewer.ScrollBarMaximum.Y) < 0.1f;
+ if (isAtEnd && DataContext is IInfinitelyScroll scroll)
+ {
+ scroll.LoadNextPageAsync().SafeFireAndForget();
+ }
+ }
+}
diff --git a/StabilityMatrix.Core/Api/IOpenArtApi.cs b/StabilityMatrix.Core/Api/IOpenArtApi.cs
index e13c8623..f7e9ae19 100644
--- a/StabilityMatrix.Core/Api/IOpenArtApi.cs
+++ b/StabilityMatrix.Core/Api/IOpenArtApi.cs
@@ -10,7 +10,7 @@ public interface IOpenArtApi
Task GetFeedAsync([Query] OpenArtFeedRequest request);
[Get("/list")]
- Task SearchAsync([Query] OpenArtFeedRequest request);
+ Task SearchAsync([Query] OpenArtSearchRequest request);
[Post("/download")]
Task DownloadWorkflowAsync([Body] OpenArtDownloadRequest request);
diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtCreator.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtCreator.cs
index 1f16dc14..173d1cef 100644
--- a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtCreator.cs
+++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtCreator.cs
@@ -20,5 +20,5 @@ public class OpenArtCreator
public string Username { get; set; }
[JsonPropertyName("dev_profile_url")]
- public Uri DevProfileUrl { get; set; }
+ public string DevProfileUrl { get; set; }
}
diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDateTime.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDateTime.cs
new file mode 100644
index 00000000..5f45a232
--- /dev/null
+++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtDateTime.cs
@@ -0,0 +1,14 @@
+using System.Text.Json.Serialization;
+
+namespace StabilityMatrix.Core.Models.Api.OpenArt;
+
+public class OpenArtDateTime
+{
+ [JsonPropertyName("_seconds")]
+ public long Seconds { get; set; }
+
+ public DateTimeOffset ToDateTimeOffset()
+ {
+ return DateTimeOffset.FromUnixTimeSeconds(Seconds);
+ }
+}
diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtFeedRequest.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtFeedRequest.cs
index a3f334d9..f8dd5255 100644
--- a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtFeedRequest.cs
+++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtFeedRequest.cs
@@ -2,6 +2,9 @@
namespace StabilityMatrix.Core.Models.Api.OpenArt;
+///
+/// Note that parameters Category, Custom Node and Sort should be used separately
+///
public class OpenArtFeedRequest
{
[AliasAs("category")]
diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResult.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResult.cs
index 7db9bb4a..9b007ced 100644
--- a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResult.cs
+++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtSearchResult.cs
@@ -32,7 +32,7 @@ public class OpenArtSearchResult
public IEnumerable Categories { get; set; }
[JsonPropertyName("thumbnails")]
- public IEnumerable Thumbnails { get; set; }
+ public List Thumbnails { get; set; }
[JsonPropertyName("nodes_count")]
public NodesCount NodesCount { get; set; }
diff --git a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtStats.cs b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtStats.cs
index 381db723..4afa6118 100644
--- a/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtStats.cs
+++ b/StabilityMatrix.Core/Models/Api/OpenArt/OpenArtStats.cs
@@ -14,7 +14,7 @@ public class OpenArtStats
public int NumReviews { get; set; }
[JsonPropertyName("rating")]
- public int Rating { get; set; }
+ public double Rating { get; set; }
[JsonPropertyName("num_comments")]
public int NumComments { get; set; }
diff --git a/StabilityMatrix.Core/Models/Packages/SDWebForge.cs b/StabilityMatrix.Core/Models/Packages/SDWebForge.cs
index 1e6fd16a..b58c7ec8 100644
--- a/StabilityMatrix.Core/Models/Packages/SDWebForge.cs
+++ b/StabilityMatrix.Core/Models/Packages/SDWebForge.cs
@@ -79,6 +79,19 @@ public class SDWebForge(
TorchVersion.Mps
};
+ public override Dictionary> SharedOutputFolders =>
+ new()
+ {
+ [SharedOutputType.Extras] = new[] { "output/extras-images" },
+ [SharedOutputType.Saved] = new[] { "log/images" },
+ [SharedOutputType.Img2Img] = new[] { "output/img2img-images" },
+ [SharedOutputType.Text2Img] = new[] { "output/txt2img-images" },
+ [SharedOutputType.Img2ImgGrids] = new[] { "output/img2img-grids" },
+ [SharedOutputType.Text2ImgGrids] = new[] { "output/txt2img-grids" }
+ };
+
+ public override string OutputFolderName => "output";
+
public override async Task InstallPackage(
string installLocation,
TorchVersion torchVersion,
diff --git a/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj b/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj
index 90484174..47cee6c9 100644
--- a/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj
+++ b/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj
@@ -15,7 +15,7 @@
-
+
From e87b5032beb11e3d2d57cddbe99de5ee7aa5d48c Mon Sep 17 00:00:00 2001
From: JT
Date: Sun, 11 Feb 2024 19:24:44 -0800
Subject: [PATCH 03/18] 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 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -204,40 +195,49 @@
-
-
-
+
+
+
+ Text="{x:Static lang:Resources.Label_Appearance}" />
-
-
-
+ Margin="8,0,8,4"
+ Header="{x:Static lang:Resources.Label_Theme}"
+ IconSource="WeatherMoon">
+
+
+
-
-
-
+ Header="{x:Static lang:Resources.Label_Language}"
+ IconSource="Character">
-
+
-
+
+
diff --git a/StabilityMatrix.Core/Models/Settings/Settings.cs b/StabilityMatrix.Core/Models/Settings/Settings.cs
index 50f34f11..fb72ac43 100644
--- a/StabilityMatrix.Core/Models/Settings/Settings.cs
+++ b/StabilityMatrix.Core/Models/Settings/Settings.cs
@@ -120,6 +120,7 @@ public class Settings
public Size InferenceImageSize { get; set; } = new(150, 190);
public Size OutputsImageSize { get; set; } = new(300, 300);
public HolidayMode HolidayModeSetting { get; set; } = HolidayMode.Automatic;
+ public bool IsWorkflowInfiniteScrollEnabled { get; set; } = true;
[JsonIgnore]
public bool IsHolidayModeActive =>
diff --git a/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj b/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj
index 47cee6c9..b1cc9ba9 100644
--- a/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj
+++ b/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj
@@ -19,6 +19,7 @@
+
From 9f430ecb652c9682d040253459f8e3b98893a0e2 Mon Sep 17 00:00:00 2001
From: JT
Date: Mon, 12 Feb 2024 21:43:38 -0800
Subject: [PATCH 04/18] settings n bug fixes
---
.../Languages/Resources.Designer.cs | 27 +++++++++++++++++++
.../Languages/Resources.resx | 9 +++++++
.../ViewModels/OpenArtBrowserViewModel.cs | 3 +++
.../Settings/MainSettingsViewModel.cs | 3 ++-
.../Views/OpenArtBrowserPage.axaml.cs | 13 +++++++--
.../Views/Settings/MainSettingsPage.axaml | 18 +++++++++++++
6 files changed, 70 insertions(+), 3 deletions(-)
diff --git a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
index 1ff7b216..58714c39 100644
--- a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
+++ b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
@@ -1391,6 +1391,15 @@ namespace StabilityMatrix.Avalonia.Languages {
}
}
+ ///
+ /// Looks up a localized string similar to Infinite Scrolling.
+ ///
+ public static string Label_InfiniteScrolling {
+ get {
+ return ResourceManager.GetString("Label_InfiniteScrolling", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Inner exception.
///
@@ -2408,6 +2417,24 @@ namespace StabilityMatrix.Avalonia.Languages {
}
}
+ ///
+ /// Looks up a localized string similar to Workflow Browser.
+ ///
+ public static string Label_WorkflowBrowser {
+ get {
+ return ResourceManager.GetString("Label_WorkflowBrowser", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Workflows.
+ ///
+ public static string Label_Workflows {
+ get {
+ return ResourceManager.GetString("Label_Workflows", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to You're up to date.
///
diff --git a/StabilityMatrix.Avalonia/Languages/Resources.resx b/StabilityMatrix.Avalonia/Languages/Resources.resx
index 45e700aa..09a64ca3 100644
--- a/StabilityMatrix.Avalonia/Languages/Resources.resx
+++ b/StabilityMatrix.Avalonia/Languages/Resources.resx
@@ -972,4 +972,13 @@
Select Download Location:
+
+ Workflows
+
+
+ Infinite Scrolling
+
+
+ Workflow Browser
+
diff --git a/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs
index 6f290e70..c983ef94 100644
--- a/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs
+++ b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs
@@ -151,6 +151,9 @@ public partial class OpenArtBrowserViewModel(IOpenArtApi openArtApi, INotificati
public async Task LoadNextPageAsync()
{
+ if (!CanGoForward)
+ return;
+
try
{
DisplayedPageNumber++;
diff --git a/StabilityMatrix.Avalonia/ViewModels/Settings/MainSettingsViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Settings/MainSettingsViewModel.cs
index 2a28e275..cf46f2c6 100644
--- a/StabilityMatrix.Avalonia/ViewModels/Settings/MainSettingsViewModel.cs
+++ b/StabilityMatrix.Avalonia/ViewModels/Settings/MainSettingsViewModel.cs
@@ -223,7 +223,8 @@ public partial class MainSettingsViewModel : PageViewModelBase
settingsManager.RelayPropertyFor(
this,
vm => vm.InfinitelyScrollWorkflowBrowser,
- settings => settings.IsWorkflowInfiniteScrollEnabled
+ settings => settings.IsWorkflowInfiniteScrollEnabled,
+ true
);
DebugThrowAsyncExceptionCommand.WithNotificationErrorHandler(notificationService, LogLevel.Warn);
diff --git a/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml.cs b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml.cs
index c80793c2..b6a615f3 100644
--- a/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml.cs
+++ b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml.cs
@@ -4,14 +4,18 @@ using Avalonia.Controls;
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Models;
using StabilityMatrix.Core.Attributes;
+using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Avalonia.Views;
[Singleton]
public partial class OpenArtBrowserPage : UserControlBase
{
- public OpenArtBrowserPage()
+ private readonly ISettingsManager settingsManager;
+
+ public OpenArtBrowserPage(ISettingsManager settingsManager)
{
+ this.settingsManager = settingsManager;
InitializeComponent();
}
@@ -24,7 +28,12 @@ public partial class OpenArtBrowserPage : UserControlBase
return;
var isAtEnd = Math.Abs(scrollViewer.Offset.Y - scrollViewer.ScrollBarMaximum.Y) < 1f;
- if (isAtEnd && DataContext is IInfinitelyScroll scroll)
+
+ if (
+ isAtEnd
+ && settingsManager.Settings.IsWorkflowInfiniteScrollEnabled
+ && DataContext is IInfinitelyScroll scroll
+ )
{
scroll.LoadNextPageAsync().SafeFireAndForget();
}
diff --git a/StabilityMatrix.Avalonia/Views/Settings/MainSettingsPage.axaml b/StabilityMatrix.Avalonia/Views/Settings/MainSettingsPage.axaml
index 2b846cc0..5fb2d8a4 100644
--- a/StabilityMatrix.Avalonia/Views/Settings/MainSettingsPage.axaml
+++ b/StabilityMatrix.Avalonia/Views/Settings/MainSettingsPage.axaml
@@ -196,6 +196,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
Date: Sun, 18 Feb 2024 02:22:05 -0800
Subject: [PATCH 05/18] just put the whole json in there (for now)
---
CHANGELOG.md | 3 +
.../DesignData/DesignData.cs | 56 +
.../Languages/Resources.Designer.cs | 27 +
.../Languages/Resources.resx | 9 +
.../Models/OpenArtCustomNode.cs | 10 +
.../CheckpointBrowserCardViewModel.cs | 20 +-
.../Dialogs/OpenArtWorkflowViewModel.cs | 93 +
.../ViewModels/OpenArtBrowserViewModel.cs | 47 +-
.../Views/Dialogs/OpenArtWorkflowDialog.axaml | 100 +
.../Dialogs/OpenArtWorkflowDialog.axaml.cs | 13 +
.../Views/OpenArtBrowserPage.axaml | 14 +-
StabilityMatrix.Core/Helper/Utilities.cs | 23 +-
.../Models/Api/OpenArt/OpenArtSearchResult.cs | 6 -
StabilityMatrix.Core/Models/ComfyNodeMap.cs | 8420 +++++++++++++++++
14 files changed, 8806 insertions(+), 35 deletions(-)
create mode 100644 StabilityMatrix.Avalonia/Models/OpenArtCustomNode.cs
create mode 100644 StabilityMatrix.Avalonia/ViewModels/Dialogs/OpenArtWorkflowViewModel.cs
create mode 100644 StabilityMatrix.Avalonia/Views/Dialogs/OpenArtWorkflowDialog.axaml
create mode 100644 StabilityMatrix.Avalonia/Views/Dialogs/OpenArtWorkflowDialog.axaml.cs
create mode 100644 StabilityMatrix.Core/Models/ComfyNodeMap.cs
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2e207ed4..7cefa26e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,8 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html).
## v2.9.0-dev.3
+### Added
+- Added OpenArt.AI workflow browser for ComfyUI workflows
### Fixed
- Fixed StableSwarmUI not installing properly on macOS
+- Fixed output sharing for Stable Diffusion WebUI Forge
## v2.9.0-dev.2
### Added
diff --git a/StabilityMatrix.Avalonia/DesignData/DesignData.cs b/StabilityMatrix.Avalonia/DesignData/DesignData.cs
index 77d5a384..31d10d8d 100644
--- a/StabilityMatrix.Avalonia/DesignData/DesignData.cs
+++ b/StabilityMatrix.Avalonia/DesignData/DesignData.cs
@@ -36,6 +36,7 @@ using StabilityMatrix.Core.Helper.Factory;
using StabilityMatrix.Core.Models;
using StabilityMatrix.Core.Models.Api;
using StabilityMatrix.Core.Models.Api.Comfy;
+using StabilityMatrix.Core.Models.Api.OpenArt;
using StabilityMatrix.Core.Models.Database;
using StabilityMatrix.Core.Models.FileInterfaces;
using StabilityMatrix.Core.Models.PackageModification;
@@ -51,6 +52,7 @@ using HuggingFacePageViewModel = StabilityMatrix.Avalonia.ViewModels.CheckpointB
namespace StabilityMatrix.Avalonia.DesignData;
+[Localizable(false)]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
public static class DesignData
{
@@ -1008,6 +1010,60 @@ The gallery images are often inpainted, but you will get something very similar
public static ControlNetCardViewModel ControlNetCardViewModel =>
DialogFactory.Get();
+ public static OpenArtWorkflowViewModel OpenArtWorkflowViewModel =>
+ new()
+ {
+ Workflow = new OpenArtSearchResult
+ {
+ Name = "Test Workflow",
+ Creator = new OpenArtCreator
+ {
+ Name = "Test Creator Name",
+ Username = "Test Creator Username"
+ },
+ Thumbnails =
+ [
+ new OpenArtThumbnail
+ {
+ Url = new Uri(
+ "https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/a318ac1f-3ad0-48ac-98cc-79126febcc17/width=1500"
+ )
+ }
+ ],
+ NodesIndex =
+ [
+ "Anything Everywhere",
+ "Reroute",
+ "Note",
+ ".",
+ "ComfyUI's ControlNet Auxiliary Preprocessors",
+ "DWPreprocessor",
+ "PixelPerfectResolution",
+ "AIO_Preprocessor",
+ ",",
+ "ComfyUI",
+ "PreviewImage",
+ "CLIPTextEncode",
+ "EmptyLatentImage",
+ "SplitImageWithAlpha",
+ "ControlNetApplyAdvanced",
+ "JoinImageWithAlpha",
+ "LatentUpscaleBy",
+ "VAEEncode",
+ "LoadImage",
+ "ControlNetLoader",
+ "CLIPVisionLoader",
+ "SaveImage",
+ ",",
+ "ComfyUI Impact Pack",
+ "SAMLoader",
+ "UltralyticsDetectorProvider",
+ "FaceDetailer",
+ ","
+ ]
+ }
+ };
+
public static string CurrentDirectory => Directory.GetCurrentDirectory();
public static Indexer Types { get; } = new();
diff --git a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
index 01451192..e96ebd24 100644
--- a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
+++ b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
@@ -374,6 +374,15 @@ namespace StabilityMatrix.Avalonia.Languages {
}
}
+ ///
+ /// Looks up a localized string similar to Open on OpenArt.
+ ///
+ public static string Action_OpenOnOpenArt {
+ get {
+ return ResourceManager.GetString("Action_OpenOnOpenArt", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Open Project....
///
@@ -1688,6 +1697,15 @@ namespace StabilityMatrix.Avalonia.Languages {
}
}
+ ///
+ /// Looks up a localized string similar to Node Details.
+ ///
+ public static string Label_NodeDetails {
+ get {
+ return ResourceManager.GetString("Label_NodeDetails", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to No extensions found..
///
@@ -2435,6 +2453,15 @@ namespace StabilityMatrix.Avalonia.Languages {
}
}
+ ///
+ /// Looks up a localized string similar to Workflow Description.
+ ///
+ public static string Label_WorkflowDescription {
+ get {
+ return ResourceManager.GetString("Label_WorkflowDescription", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Workflows.
///
diff --git a/StabilityMatrix.Avalonia/Languages/Resources.resx b/StabilityMatrix.Avalonia/Languages/Resources.resx
index e8f7ed3f..91666c7c 100644
--- a/StabilityMatrix.Avalonia/Languages/Resources.resx
+++ b/StabilityMatrix.Avalonia/Languages/Resources.resx
@@ -984,4 +984,13 @@
Config
+
+ Open on OpenArt
+
+
+ Node Details
+
+
+ Workflow Description
+
diff --git a/StabilityMatrix.Avalonia/Models/OpenArtCustomNode.cs b/StabilityMatrix.Avalonia/Models/OpenArtCustomNode.cs
new file mode 100644
index 00000000..b075758d
--- /dev/null
+++ b/StabilityMatrix.Avalonia/Models/OpenArtCustomNode.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+
+namespace StabilityMatrix.Avalonia.Models;
+
+public class OpenArtCustomNode
+{
+ public required string Title { get; set; }
+ public List Children { get; set; } = [];
+ public bool IsInstalled { get; set; }
+}
diff --git a/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CheckpointBrowserCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CheckpointBrowserCardViewModel.cs
index 148712c0..bcb2ae62 100644
--- a/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CheckpointBrowserCardViewModel.cs
+++ b/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CheckpointBrowserCardViewModel.cs
@@ -19,6 +19,7 @@ using StabilityMatrix.Avalonia.ViewModels.Dialogs;
using StabilityMatrix.Avalonia.Views.Dialogs;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Extensions;
+using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Models;
using StabilityMatrix.Core.Models.Api;
using StabilityMatrix.Core.Models.Database;
@@ -216,7 +217,7 @@ public partial class CheckpointBrowserCardViewModel : Base.ProgressViewModel
MaxDialogHeight = 950,
};
- var prunedDescription = PruneDescription(model);
+ var prunedDescription = Utilities.RemoveHtml(model.Description);
var viewModel = dialogFactory.Get();
viewModel.Dialog = dialog;
@@ -263,23 +264,6 @@ public partial class CheckpointBrowserCardViewModel : Base.ProgressViewModel
await DoImport(model, downloadPath, selectedVersion, selectedFile);
}
- private static string PruneDescription(CivitModel model)
- {
- var prunedDescription =
- model
- .Description?.Replace("
", $"{Environment.NewLine}{Environment.NewLine}")
- .Replace("
", $"{Environment.NewLine}{Environment.NewLine}")
- .Replace("
", $"{Environment.NewLine}{Environment.NewLine}")
- .Replace("", $"{Environment.NewLine}{Environment.NewLine}")
- .Replace("", $"{Environment.NewLine}{Environment.NewLine}")
- .Replace("", $"{Environment.NewLine}{Environment.NewLine}")
- .Replace("", $"{Environment.NewLine}{Environment.NewLine}")
- .Replace("", $"{Environment.NewLine}{Environment.NewLine}")
- .Replace("", $"{Environment.NewLine}{Environment.NewLine}") ?? string.Empty;
- prunedDescription = HtmlRegex().Replace(prunedDescription, string.Empty);
- return prunedDescription;
- }
-
private static async Task SaveCmInfo(
CivitModel model,
CivitModelVersion modelVersion,
diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/OpenArtWorkflowViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/OpenArtWorkflowViewModel.cs
new file mode 100644
index 00000000..94ebf773
--- /dev/null
+++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/OpenArtWorkflowViewModel.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+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.Models;
+using StabilityMatrix.Core.Models.Api.Comfy.Nodes;
+using StabilityMatrix.Core.Models.Api.OpenArt;
+
+namespace StabilityMatrix.Avalonia.ViewModels.Dialogs;
+
+[View(typeof(OpenArtWorkflowDialog))]
+[ManagedService]
+[Transient]
+public partial class OpenArtWorkflowViewModel : ContentDialogViewModelBase
+{
+ public required OpenArtSearchResult Workflow { get; init; }
+ public string? InstalledComfyPath { get; init; }
+
+ [ObservableProperty]
+ private ObservableCollection customNodes = [];
+
+ [ObservableProperty]
+ private string prunedDescription = string.Empty;
+
+ public override void OnLoaded()
+ {
+ CustomNodes = new ObservableCollection(ParseNodes(Workflow.NodesIndex.ToList()));
+ PrunedDescription = Utilities.RemoveHtml(Workflow.Description);
+ }
+
+ [Localizable(false)]
+ private List ParseNodes(List nodes)
+ {
+ var indexOfFirstDot = nodes.IndexOf(".");
+ if (indexOfFirstDot != -1)
+ {
+ nodes = nodes[(indexOfFirstDot + 1)..];
+ }
+
+ var installedNodes = new List();
+ if (!string.IsNullOrWhiteSpace(InstalledComfyPath))
+ {
+ installedNodes = Directory
+ .EnumerateDirectories(InstalledComfyPath)
+ .Select(
+ x => x.Split(Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries).Last()
+ )
+ .Where(x => ComfyNodeMap.Lookup.Values.FirstOrDefault(y => y.EndsWith(x)) != null)
+ .ToList();
+ }
+
+ var sections = new List();
+ OpenArtCustomNode? currentSection = null;
+
+ foreach (var node in nodes)
+ {
+ if (node is "." or ",")
+ {
+ currentSection = null; // End of the current section
+ continue;
+ }
+
+ if (currentSection == null)
+ {
+ currentSection = new OpenArtCustomNode
+ {
+ Title = node,
+ IsInstalled = installedNodes.Contains(node)
+ };
+ sections.Add(currentSection);
+ }
+ else
+ {
+ currentSection.Children.Add(node);
+ }
+ }
+
+ if (sections.FirstOrDefault(x => x.Title == "ComfyUI") != null)
+ {
+ sections = sections.Where(x => x.Title != "ComfyUI").ToList();
+ }
+
+ return sections;
+ }
+}
diff --git a/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs
index c983ef94..6897ee23 100644
--- a/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs
+++ b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs
@@ -1,5 +1,6 @@
using System;
using System.ComponentModel;
+using System.IO;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
@@ -8,14 +9,18 @@ using DynamicData;
using DynamicData.Binding;
using FluentAvalonia.UI.Controls;
using Refit;
+using StabilityMatrix.Avalonia.Controls;
+using StabilityMatrix.Avalonia.Languages;
using StabilityMatrix.Avalonia.Models;
using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels.Base;
+using StabilityMatrix.Avalonia.ViewModels.Dialogs;
using StabilityMatrix.Avalonia.Views;
using StabilityMatrix.Core.Api;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Models.Api.OpenArt;
using StabilityMatrix.Core.Processes;
+using StabilityMatrix.Core.Services;
using Symbol = FluentIcons.Common.Symbol;
using SymbolIconSource = FluentIcons.Avalonia.Fluent.SymbolIconSource;
@@ -23,16 +28,18 @@ namespace StabilityMatrix.Avalonia.ViewModels;
[View(typeof(OpenArtBrowserPage))]
[Singleton]
-public partial class OpenArtBrowserViewModel(IOpenArtApi openArtApi, INotificationService notificationService)
- : PageViewModelBase,
- IInfinitelyScroll
+public partial class OpenArtBrowserViewModel(
+ IOpenArtApi openArtApi,
+ INotificationService notificationService,
+ ISettingsManager settingsManager
+) : PageViewModelBase, IInfinitelyScroll
{
private const int PageSize = 20;
- public override string Title => "Workflows";
+ public override string Title => Resources.Label_Workflows;
public override IconSource IconSource => new SymbolIconSource { Symbol = Symbol.Whiteboard };
- private SourceCache searchResultsCache = new(x => x.Id);
+ private readonly SourceCache searchResultsCache = new(x => x.Id);
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(PageCount), nameof(CanGoBack), nameof(CanGoForward), nameof(CanGoToEnd))]
@@ -121,6 +128,36 @@ public partial class OpenArtBrowserViewModel(IOpenArtApi openArtApi, INotificati
await DoSearch();
}
+ [RelayCommand]
+ private async Task OpenWorkflow(OpenArtSearchResult workflow)
+ {
+ var existingComfy = settingsManager.Settings.InstalledPackages.FirstOrDefault(
+ x => x.PackageName is "ComfyUI"
+ );
+
+ var dialog = new BetterContentDialog
+ {
+ IsPrimaryButtonEnabled = true,
+ IsSecondaryButtonEnabled = true,
+ PrimaryButtonText = Resources.Action_Import,
+ SecondaryButtonText = Resources.Action_Cancel,
+ DefaultButton = ContentDialogButton.Primary,
+ IsFooterVisible = true,
+ MaxDialogWidth = 750,
+ MaxDialogHeight = 850,
+ CloseOnClickOutside = true,
+ Content = new OpenArtWorkflowViewModel
+ {
+ Workflow = workflow,
+ InstalledComfyPath = existingComfy is null
+ ? null
+ : Path.Combine(settingsManager.LibraryDir, existingComfy.LibraryPath!, "custom_nodes")
+ },
+ };
+
+ await dialog.ShowAsync();
+ }
+
private async Task DoSearch(int page = 0)
{
IsLoading = true;
diff --git a/StabilityMatrix.Avalonia/Views/Dialogs/OpenArtWorkflowDialog.axaml b/StabilityMatrix.Avalonia/Views/Dialogs/OpenArtWorkflowDialog.axaml
new file mode 100644
index 00000000..af014bf1
--- /dev/null
+++ b/StabilityMatrix.Avalonia/Views/Dialogs/OpenArtWorkflowDialog.axaml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/StabilityMatrix.Avalonia/Views/Dialogs/OpenArtWorkflowDialog.axaml.cs b/StabilityMatrix.Avalonia/Views/Dialogs/OpenArtWorkflowDialog.axaml.cs
new file mode 100644
index 00000000..e0ed1d00
--- /dev/null
+++ b/StabilityMatrix.Avalonia/Views/Dialogs/OpenArtWorkflowDialog.axaml.cs
@@ -0,0 +1,13 @@
+using StabilityMatrix.Avalonia.Controls;
+using StabilityMatrix.Core.Attributes;
+
+namespace StabilityMatrix.Avalonia.Views.Dialogs;
+
+[Transient]
+public partial class OpenArtWorkflowDialog : UserControlBase
+{
+ public OpenArtWorkflowDialog()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml
index 8e7a621e..75a6f54c 100644
--- a/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml
+++ b/StabilityMatrix.Avalonia/Views/OpenArtBrowserPage.axaml
@@ -66,6 +66,11 @@
+
+
+
-
-
-
+ CornerRadius="8"
+ Command="{StaticResource OpenWorkflowCommand}"
+ CommandParameter="{Binding }">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 8d59e1d9f2ac249542d61ffcdb90b48afbd0b4ee Mon Sep 17 00:00:00 2001
From: JT
Date: Sun, 24 Mar 2024 14:43:03 -0700
Subject: [PATCH 16/18] fix merge shenanigans
---
.../Models/Packages/Extensions/IPackageExtensionManager.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/StabilityMatrix.Core/Models/Packages/Extensions/IPackageExtensionManager.cs b/StabilityMatrix.Core/Models/Packages/Extensions/IPackageExtensionManager.cs
index cdd3e3cf..67131238 100644
--- a/StabilityMatrix.Core/Models/Packages/Extensions/IPackageExtensionManager.cs
+++ b/StabilityMatrix.Core/Models/Packages/Extensions/IPackageExtensionManager.cs
@@ -95,6 +95,7 @@ public interface IPackageExtensionManager
Task> GetInstalledExtensionsLiteAsync(
InstalledPackage installedPackage,
CancellationToken cancellationToken = default
+ );
///
/// Get updated info (version) for an installed extension.
From c217189ade08317445343f1fb8d3fe3b9d96392a Mon Sep 17 00:00:00 2001
From: JT
Date: Sun, 24 Mar 2024 16:30:02 -0700
Subject: [PATCH 17/18] changed how metadata is downloaded & added context
menus & localization stuff
---
.../DesignData/DesignData.cs | 20 +++-
.../Languages/Resources.Designer.cs | 63 ++++++++++
.../Languages/Resources.resx | 21 ++++
.../Models/OpenArtMetadata.cs | 22 ++--
.../ViewModels/InstalledWorkflowsViewModel.cs | 101 +++++++++++++++-
.../ViewModels/OpenArtBrowserViewModel.cs | 27 +++--
.../Views/InstalledWorkflowsPage.axaml | 111 ++++++++++++++----
.../Views/OpenArtBrowserPage.axaml | 17 ++-
StabilityMatrix.Core/Helper/EventManager.cs | 3 +
.../DownloadOpenArtWorkflowStep.cs | 12 +-
10 files changed, 325 insertions(+), 72 deletions(-)
diff --git a/StabilityMatrix.Avalonia/DesignData/DesignData.cs b/StabilityMatrix.Avalonia/DesignData/DesignData.cs
index 4a11a99e..3fc6455b 100644
--- a/StabilityMatrix.Avalonia/DesignData/DesignData.cs
+++ b/StabilityMatrix.Avalonia/DesignData/DesignData.cs
@@ -932,12 +932,20 @@ The gallery images are often inpainted, but you will get something very similar
{
new()
{
- Name = "Test Workflow",
- Creator = "Test Creator",
- ThumbnailUrls =
- [
- "https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/dd9b038c-bd15-43ab-86ab-66e145ad7ff2/width=512"
- ]
+ Workflow = new()
+ {
+ Name = "Test Workflow",
+ Creator = new OpenArtCreator { Name = "Test Creator" },
+ Thumbnails =
+ [
+ new OpenArtThumbnail
+ {
+ Url = new Uri(
+ "https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/dd9b038c-bd15-43ab-86ab-66e145ad7ff2/width=512"
+ )
+ }
+ ]
+ }
}
};
diff --git a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
index 5dd40cc8..388037ce 100644
--- a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
+++ b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
@@ -1319,6 +1319,15 @@ namespace StabilityMatrix.Avalonia.Languages {
}
}
+ ///
+ /// Looks up a localized string similar to Error retrieving workflows.
+ ///
+ public static string Label_ErrorRetrievingWorkflows {
+ get {
+ return ResourceManager.GetString("Label_ErrorRetrievingWorkflows", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Everything looks good!.
///
@@ -1355,6 +1364,15 @@ namespace StabilityMatrix.Avalonia.Languages {
}
}
+ ///
+ /// Looks up a localized string similar to Finished importing workflow and custom nodes.
+ ///
+ public static string Label_FinishedImportingWorkflow {
+ get {
+ return ResourceManager.GetString("Label_FinishedImportingWorkflow", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to First Page.
///
@@ -2606,6 +2624,24 @@ namespace StabilityMatrix.Avalonia.Languages {
}
}
+ ///
+ /// Looks up a localized string similar to Workflow Deleted.
+ ///
+ public static string Label_WorkflowDeleted {
+ get {
+ return ResourceManager.GetString("Label_WorkflowDeleted", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0} deleted successfully.
+ ///
+ public static string Label_WorkflowDeletedSuccessfully {
+ get {
+ return ResourceManager.GetString("Label_WorkflowDeletedSuccessfully", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Workflow Description.
///
@@ -2615,6 +2651,24 @@ namespace StabilityMatrix.Avalonia.Languages {
}
}
+ ///
+ /// Looks up a localized string similar to The workflow and custom nodes have been imported..
+ ///
+ public static string Label_WorkflowImportComplete {
+ get {
+ return ResourceManager.GetString("Label_WorkflowImportComplete", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Workflow Imported.
+ ///
+ public static string Label_WorkflowImported {
+ get {
+ return ResourceManager.GetString("Label_WorkflowImported", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Workflows.
///
@@ -2705,6 +2759,15 @@ namespace StabilityMatrix.Avalonia.Languages {
}
}
+ ///
+ /// Looks up a localized string similar to Installed Workflows.
+ ///
+ public static string TabLabel_InstalledWorkflows {
+ get {
+ return ResourceManager.GetString("TabLabel_InstalledWorkflows", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Add a package to get started!.
///
diff --git a/StabilityMatrix.Avalonia/Languages/Resources.resx b/StabilityMatrix.Avalonia/Languages/Resources.resx
index 368e2867..4c70a20c 100644
--- a/StabilityMatrix.Avalonia/Languages/Resources.resx
+++ b/StabilityMatrix.Avalonia/Languages/Resources.resx
@@ -1047,4 +1047,25 @@
Stability Matrix is already running
+
+ {0} deleted successfully
+
+
+ Workflow Deleted
+
+
+ Error retrieving workflows
+
+
+ Installed Workflows
+
+
+ Workflow Imported
+
+
+ Finished importing workflow and custom nodes
+
+
+ The workflow and custom nodes have been imported.
+
diff --git a/StabilityMatrix.Avalonia/Models/OpenArtMetadata.cs b/StabilityMatrix.Avalonia/Models/OpenArtMetadata.cs
index 72d09658..7620816c 100644
--- a/StabilityMatrix.Avalonia/Models/OpenArtMetadata.cs
+++ b/StabilityMatrix.Avalonia/Models/OpenArtMetadata.cs
@@ -2,27 +2,21 @@
using System.Linq;
using System.Text.Json.Serialization;
using Avalonia.Platform.Storage;
-using StabilityMatrix.Avalonia;
+using StabilityMatrix.Core.Models.Api.OpenArt;
-namespace StabilityMatrix.Core.Models.Api.OpenArt;
+namespace StabilityMatrix.Avalonia.Models;
public class OpenArtMetadata
{
- [JsonPropertyName("workflow_id")]
- public string? Id { get; set; }
-
- [JsonPropertyName("workflow_name")]
- public string? Name { get; set; }
-
- [JsonPropertyName("creator")]
- public string? Creator { get; set; }
-
- [JsonPropertyName("thumbnails")]
- public IEnumerable? ThumbnailUrls { get; set; }
+ [JsonPropertyName("sm_workflow_data")]
+ public OpenArtSearchResult? Workflow { get; set; }
[JsonIgnore]
- public string? FirstThumbnail => ThumbnailUrls?.FirstOrDefault();
+ public string? FirstThumbnail => Workflow?.Thumbnails?.Select(x => x.Url).FirstOrDefault()?.ToString();
[JsonIgnore]
public List? FilePath { get; set; }
+
+ [JsonIgnore]
+ public bool HasMetadata => Workflow?.Creator != null;
}
diff --git a/StabilityMatrix.Avalonia/ViewModels/InstalledWorkflowsViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/InstalledWorkflowsViewModel.cs
index 8a4c0e87..b765c797 100644
--- a/StabilityMatrix.Avalonia/ViewModels/InstalledWorkflowsViewModel.cs
+++ b/StabilityMatrix.Avalonia/ViewModels/InstalledWorkflowsViewModel.cs
@@ -1,28 +1,41 @@
using System;
using System.IO;
+using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
+using AsyncAwaitBestPractices;
using Avalonia.Controls;
using Avalonia.Platform.Storage;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using DynamicData;
using DynamicData.Binding;
+using FluentAvalonia.UI.Controls;
+using StabilityMatrix.Avalonia.Controls;
+using StabilityMatrix.Avalonia.Languages;
+using StabilityMatrix.Avalonia.Models;
+using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Avalonia.Views;
using StabilityMatrix.Core.Attributes;
+using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Models.Api.OpenArt;
+using StabilityMatrix.Core.Processes;
using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Avalonia.ViewModels;
[View(typeof(InstalledWorkflowsPage))]
[Singleton]
-public partial class InstalledWorkflowsViewModel(ISettingsManager settingsManager) : TabViewModelBase
+public partial class InstalledWorkflowsViewModel(
+ ISettingsManager settingsManager,
+ INotificationService notificationService
+) : TabViewModelBase, IDisposable
{
- public override string Header => "Installed Workflows";
+ public override string Header => Resources.TabLabel_InstalledWorkflows;
- private readonly SourceCache workflowsCache = new(x => x.Id);
+ private readonly SourceCache workflowsCache =
+ new(x => x.Workflow?.Id ?? Guid.NewGuid().ToString());
[ObservableProperty]
private IObservableCollection displayedWorkflows =
@@ -38,6 +51,7 @@ public partial class InstalledWorkflowsViewModel(ISettingsManager settingsManage
return;
await LoadInstalledWorkflowsAsync();
+ EventManager.Instance.WorkflowInstalled += OnWorkflowInstalled;
}
[RelayCommand]
@@ -58,12 +72,15 @@ public partial class InstalledWorkflowsViewModel(ISettingsManager settingsManage
var json = await File.ReadAllTextAsync(workflowPath);
var metadata = JsonSerializer.Deserialize(json);
- if (metadata?.Id == null)
+ if (metadata?.Workflow == null)
{
metadata = new OpenArtMetadata
{
- Id = Guid.NewGuid().ToString(),
- Name = Path.GetFileNameWithoutExtension(workflowPath)
+ Workflow = new OpenArtSearchResult
+ {
+ Id = Guid.NewGuid().ToString(),
+ Name = Path.GetFileNameWithoutExtension(workflowPath)
+ }
};
}
@@ -76,4 +93,76 @@ public partial class InstalledWorkflowsViewModel(ISettingsManager settingsManage
}
}
}
+
+ [RelayCommand]
+ private async Task OpenInExplorer(OpenArtMetadata metadata)
+ {
+ if (metadata.FilePath == null)
+ return;
+
+ var path = metadata.FilePath.FirstOrDefault()?.Path.ToString();
+ if (string.IsNullOrWhiteSpace(path))
+ return;
+
+ await ProcessRunner.OpenFileBrowser(path);
+ }
+
+ [RelayCommand]
+ private void OpenOnOpenArt(OpenArtMetadata metadata)
+ {
+ if (metadata.Workflow == null)
+ return;
+
+ ProcessRunner.OpenUrl($"https://openart.ai/workflows/{metadata.Workflow.Id}");
+ }
+
+ [RelayCommand]
+ private async Task DeleteAsync(OpenArtMetadata metadata)
+ {
+ var confirmationDialog = new BetterContentDialog
+ {
+ Title = Resources.Label_AreYouSure,
+ Content = Resources.Label_ActionCannotBeUndone,
+ PrimaryButtonText = Resources.Action_Delete,
+ SecondaryButtonText = Resources.Action_Cancel,
+ DefaultButton = ContentDialogButton.Primary,
+ IsSecondaryButtonEnabled = true,
+ };
+ var dialogResult = await confirmationDialog.ShowAsync();
+ if (dialogResult != ContentDialogResult.Primary)
+ return;
+
+ await using var delay = new MinimumDelay(200, 500);
+
+ var path = metadata?.FilePath?.FirstOrDefault()?.Path.ToString().Replace("file:///", "");
+ if (!string.IsNullOrWhiteSpace(path) && File.Exists(path))
+ {
+ await notificationService.TryAsync(
+ Task.Run(() => File.Delete(path)),
+ message: "Error deleting workflow"
+ );
+
+ var id = metadata?.Workflow?.Id;
+ if (!string.IsNullOrWhiteSpace(id))
+ {
+ workflowsCache.Remove(id);
+ }
+ }
+
+ notificationService.Show(
+ Resources.Label_WorkflowDeleted,
+ string.Format(Resources.Label_WorkflowDeletedSuccessfully, metadata?.Workflow?.Name)
+ );
+ }
+
+ private void OnWorkflowInstalled(object? sender, EventArgs e)
+ {
+ LoadInstalledWorkflowsAsync().SafeFireAndForget();
+ }
+
+ public void Dispose()
+ {
+ workflowsCache.Dispose();
+ EventManager.Instance.WorkflowInstalled -= OnWorkflowInstalled;
+ }
}
diff --git a/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs
index b1dbd39a..1b7ef2f1 100644
--- a/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs
+++ b/StabilityMatrix.Avalonia/ViewModels/OpenArtBrowserViewModel.cs
@@ -12,6 +12,7 @@ using DynamicData.Binding;
using FluentAvalonia.UI.Controls;
using Refit;
using StabilityMatrix.Avalonia.Controls;
+using StabilityMatrix.Avalonia.Languages;
using StabilityMatrix.Avalonia.Models;
using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels.Base;
@@ -182,10 +183,7 @@ public partial class OpenArtBrowserViewModel(
if (existingComfy == null || comfyPair == null)
{
- notificationService.Show(
- Resources.Label_ComfyRequiredTitle,
- "ComfyUI is required to import workflows from OpenArt"
- );
+ notificationService.Show(Resources.Label_ComfyRequiredTitle, Resources.Label_ComfyRequiredDetail);
return;
}
@@ -210,18 +208,29 @@ public partial class OpenArtBrowserViewModel(
var runner = new PackageModificationRunner
{
ShowDialogOnStart = true,
- ModificationCompleteTitle = "Workflow Imported",
- ModificationCompleteMessage = "Finished importing workflow and custom nodes"
+ ModificationCompleteTitle = Resources.Label_WorkflowImported,
+ ModificationCompleteMessage = Resources.Label_FinishedImportingWorkflow
};
EventManager.Instance.OnPackageInstallProgressAdded(runner);
await runner.ExecuteSteps(steps);
notificationService.Show(
- "Workflow Imported",
- "The workflow and custom nodes have been imported.",
+ Resources.Label_WorkflowImported,
+ Resources.Label_WorkflowImportComplete,
NotificationType.Success
);
+
+ EventManager.Instance.OnWorkflowInstalled();
+ }
+
+ [RelayCommand]
+ private void OpenOnOpenArt(OpenArtSearchResult? workflow)
+ {
+ if (workflow?.Id == null)
+ return;
+
+ ProcessRunner.OpenUrl($"https://openart.ai/workflows/{workflow.Id}");
}
private async Task DoSearch(int page = 0)
@@ -262,7 +271,7 @@ public partial class OpenArtBrowserViewModel(
}
catch (ApiException e)
{
- notificationService.Show("Error retrieving workflows", e.Message);
+ notificationService.Show(Resources.Label_ErrorRetrievingWorkflows, e.Message);
}
finally
{
diff --git a/StabilityMatrix.Avalonia/Views/InstalledWorkflowsPage.axaml b/StabilityMatrix.Avalonia/Views/InstalledWorkflowsPage.axaml
index 5c4d3b4b..7e569068 100644
--- a/StabilityMatrix.Avalonia/Views/InstalledWorkflowsPage.axaml
+++ b/StabilityMatrix.Avalonia/Views/InstalledWorkflowsPage.axaml
@@ -10,6 +10,11 @@
xmlns:controls1="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages"
xmlns:avalonia="https://github.com/projektanker/icons.avalonia"
+ xmlns:labs="clr-namespace:Avalonia.Labs.Controls;assembly=Avalonia.Labs.Controls"
+ xmlns:models="clr-namespace:StabilityMatrix.Avalonia.Models"
+ xmlns:helpers="clr-namespace:StabilityMatrix.Avalonia.Helpers"
+ xmlns:fluent="clr-namespace:FluentIcons.Avalonia.Fluent;assembly=FluentIcons.Avalonia.Fluent"
+ xmlns:input="clr-namespace:FluentAvalonia.UI.Input;assembly=FluentAvalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
d:DataContext="{x:Static designData:DesignData.InstalledWorkflowsViewModel}"
x:DataType="viewModels:InstalledWorkflowsViewModel"
@@ -22,7 +27,7 @@
-
+
+
+
+
+
+
-
+
-
+
+ Margin="8,0" />
+ VerticalAlignment="Center" />
@@ -96,7 +113,7 @@
-
+
+
+
+
+
+
+
+
-
-
+ Stretch="UniformToFill" />
+
+ Grid.RowSpan="2"
+ CornerRadius="8"
+ Width="330"
+ Height="400"
+ FontSize="100"
+ IsVisible="{Binding FirstThumbnail, Converter={x:Static ObjectConverters.IsNull}, FallbackValue=False}"
+ Value="fa-regular fa-file-code" />
+
-
+
+
+
+
+
+
+ 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 @@
-